aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/using_skia_and_harfbuzz.cpp
diff options
context:
space:
mode:
authorGravatar Herb Derby <herb@google.com>2018-05-29 15:53:40 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-05-30 04:04:07 +0000
commit264182c3f7d282e57a4b1d46fde3ef702b81c5c3 (patch)
tree85c68bfa138662fd60cc3ab3bcad26f83cb9b80d /tools/using_skia_and_harfbuzz.cpp
parent09725b0c3fd52b008743c63feb09ec1ab8bc2eea (diff)
Make SkShaper a module
Change-Id: I3709e49ba865f14260660cc07a762b9ac837cb3c Reviewed-on: https://skia-review.googlesource.com/130602 Reviewed-by: Herb Derby <herb@google.com> Commit-Queue: Herb Derby <herb@google.com>
Diffstat (limited to 'tools/using_skia_and_harfbuzz.cpp')
-rw-r--r--tools/using_skia_and_harfbuzz.cpp222
1 files changed, 222 insertions, 0 deletions
diff --git a/tools/using_skia_and_harfbuzz.cpp b/tools/using_skia_and_harfbuzz.cpp
new file mode 100644
index 0000000000..5a74866794
--- /dev/null
+++ b/tools/using_skia_and_harfbuzz.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This sample progam demonstrates how to use Skia and HarfBuzz to
+// produce a PDF file from UTF-8 text in stdin.
+
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "SkCanvas.h"
+#include "SkDocument.h"
+#include "SkShaper.h"
+#include "SkStream.h"
+#include "SkTextBlob.h"
+#include "SkTypeface.h"
+
+// Options /////////////////////////////////////////////////////////////////////
+
+struct BaseOption {
+ std::string selector;
+ std::string description;
+ virtual void set(std::string _value) = 0;
+ virtual std::string valueToString() = 0;
+
+ BaseOption(std::string _selector, std::string _description)
+ : selector(_selector), description(_description) {}
+
+ virtual ~BaseOption() {}
+
+ static void Init(const std::vector<BaseOption*> &, int argc, char **argv);
+};
+
+template <class T>
+struct Option : BaseOption {
+ T value;
+ Option(std::string selector, std::string description, T defaultValue)
+ : BaseOption(selector, description), value(defaultValue) {}
+};
+
+void BaseOption::Init(const std::vector<BaseOption*> &option_list,
+ int argc, char **argv) {
+ std::map<std::string, BaseOption *> options;
+ for (BaseOption *opt : option_list) {
+ options[opt->selector] = opt;
+ }
+ for (int i = 1; i < argc; i++) {
+ std::string option_selector(argv[i]);
+ auto it = options.find(option_selector);
+ if (it != options.end()) {
+ if (i >= argc) {
+ break;
+ }
+ const char *option_value = argv[i + 1];
+ it->second->set(option_value);
+ i++;
+ } else {
+ printf("Ignoring unrecognized option: %s.\n", argv[i]);
+ printf("Usage: %s {option value}\n", argv[0]);
+ printf("\tTakes text from stdin and produces pdf file.\n");
+ printf("Supported options:\n");
+ for (BaseOption *opt : option_list) {
+ printf("\t%s\t%s (%s)\n", opt->selector.c_str(),
+ opt->description.c_str(), opt->valueToString().c_str());
+ }
+ exit(-1);
+ }
+ }
+}
+
+struct DoubleOption : Option<double> {
+ virtual void set(std::string _value) { value = atof(_value.c_str()); }
+ virtual std::string valueToString() {
+ std::ostringstream stm;
+ stm << value;
+ return stm.str();
+ }
+ DoubleOption(std::string selector,
+ std::string description,
+ double defaultValue)
+ : Option<double>(selector, description, defaultValue) {}
+};
+
+struct StringOption : Option<std::string> {
+ virtual void set(std::string _value) { value = _value; }
+ virtual std::string valueToString() { return value; }
+ StringOption(std::string selector,
+ std::string description,
+ std::string defaultValue)
+ : Option<std::string>(selector, description, defaultValue) {}
+};
+
+// Config //////////////////////////////////////////////////////////////////////
+
+struct Config {
+ DoubleOption page_width = DoubleOption("-w", "Page width", 600.0f);
+ DoubleOption page_height = DoubleOption("-h", "Page height", 800.0f);
+ StringOption title = StringOption("-t", "PDF title", "---");
+ StringOption author = StringOption("-a", "PDF author", "---");
+ StringOption subject = StringOption("-k", "PDF subject", "---");
+ StringOption keywords = StringOption("-c", "PDF keywords", "---");
+ StringOption creator = StringOption("-t", "PDF creator", "---");
+ StringOption font_file = StringOption("-f", ".ttf font file", "");
+ DoubleOption font_size = DoubleOption("-z", "Font size", 8.0f);
+ DoubleOption left_margin = DoubleOption("-m", "Left margin", 20.0f);
+ DoubleOption line_spacing_ratio =
+ DoubleOption("-h", "Line spacing ratio", 0.25f);
+ StringOption output_file_name =
+ StringOption("-o", ".pdf output file name", "out-skiahf.pdf");
+
+ Config(int argc, char **argv) {
+ BaseOption::Init(std::vector<BaseOption*>{
+ &page_width, &page_height, &title, &author, &subject,
+ &keywords, &creator, &font_file, &font_size, &left_margin,
+ &line_spacing_ratio, &output_file_name}, argc, argv);
+ }
+};
+
+// Placement ///////////////////////////////////////////////////////////////////
+
+class Placement {
+public:
+ Placement(const Config* conf, SkDocument *doc)
+ : config(conf), document(doc), pageCanvas(nullptr) {
+ white_paint.setColor(SK_ColorWHITE);
+ glyph_paint.setColor(SK_ColorBLACK);
+ glyph_paint.setFlags(SkPaint::kAntiAlias_Flag |
+ SkPaint::kSubpixelText_Flag);
+ glyph_paint.setTextSize(SkDoubleToScalar(config->font_size.value));
+ }
+
+ void WriteLine(const SkShaper& shaper, const char *text, size_t textBytes) {
+ SkTextBlobBuilder textBlobBuilder;
+ SkPoint endPoint = shaper.shape(&textBlobBuilder, glyph_paint, text, textBytes, true,
+ SkPoint{0, 0},
+ config->page_width.value - 2*config->left_margin.value);
+ sk_sp<const SkTextBlob> blob = textBlobBuilder.make();
+ // If we don't have a page, or if we're not at the start of the page and the blob won't fit
+ if (!pageCanvas ||
+ (current_y > config->line_spacing_ratio.value * config->font_size.value &&
+ current_y + endPoint.y() > config->page_height.value)
+ ) {
+ if (pageCanvas) {
+ document->endPage();
+ }
+ pageCanvas = document->beginPage(
+ SkDoubleToScalar(config->page_width.value),
+ SkDoubleToScalar(config->page_height.value));
+ pageCanvas->drawPaint(white_paint);
+ current_x = config->left_margin.value;
+ current_y = config->line_spacing_ratio.value * config->font_size.value;
+ }
+ pageCanvas->drawTextBlob(
+ blob.get(), SkDoubleToScalar(current_x),
+ SkDoubleToScalar(current_y), glyph_paint);
+ // Advance to the next line.
+ current_y += endPoint.y() + config->line_spacing_ratio.value * config->font_size.value;
+ }
+
+private:
+ const Config* config;
+ SkDocument *document;
+ SkCanvas *pageCanvas;
+ SkPaint white_paint;
+ SkPaint glyph_paint;
+ double current_x;
+ double current_y;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+static sk_sp<SkDocument> MakePDFDocument(const Config &config, SkWStream *wStream) {
+ SkDocument::PDFMetadata pdf_info;
+ pdf_info.fTitle = config.title.value.c_str();
+ pdf_info.fAuthor = config.author.value.c_str();
+ pdf_info.fSubject = config.subject.value.c_str();
+ pdf_info.fKeywords = config.keywords.value.c_str();
+ pdf_info.fCreator = config.creator.value.c_str();
+ #if 0
+ SkTime::DateTime now;
+ SkTime::GetDateTime(&now);
+ pdf_info.fCreation.fEnabled = true;
+ pdf_info.fCreation.fDateTime = now;
+ pdf_info.fModified.fEnabled = true;
+ pdf_info.fModified.fDateTime = now;
+ pdf_info.fPDFA = true;
+ #endif
+ return SkDocument::MakePDF(wStream, pdf_info);
+}
+
+int main(int argc, char **argv) {
+ Config config(argc, argv);
+ SkFILEWStream wStream(config.output_file_name.value.c_str());
+ sk_sp<SkDocument> doc = MakePDFDocument(config, &wStream);
+ assert(doc);
+ Placement placement(&config, doc.get());
+
+ const std::string &font_file = config.font_file.value;
+ sk_sp<SkTypeface> typeface;
+ if (font_file.size() > 0) {
+ typeface = SkTypeface::MakeFromFile(font_file.c_str(), 0 /* index */);
+ }
+ SkShaper shaper(typeface);
+ assert(shaper.good());
+ //SkString line("This is هذا هو الخط a line.");
+ //SkString line("⁧This is a line هذا هو الخط.⁩");
+ for (std::string line; std::getline(std::cin, line);) {
+ placement.WriteLine(shaper, line.c_str(), line.size());
+ }
+
+ doc->close();
+ wStream.flush();
+ return 0;
+}