aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/using_skia_and_harfbuzz.cpp
diff options
context:
space:
mode:
authorGravatar halcanary <halcanary@google.com>2016-05-03 15:09:52 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-05-03 15:09:52 -0700
commit00d44e014c805d06a59a74cd744e24482c218e9b (patch)
treeaf206d235dc1ebaf1a00908ea6882192ea7d1a9b /tools/using_skia_and_harfbuzz.cpp
parent02125d10d5d021f1f3190ca95a3ef62b43349a64 (diff)
Start building HarfBuzz+Skia example
(prevent bitrot in example code) tools/using_skia_and_harfbuzz.cpp was written by aam@: https://github.com/aam/skiaex update HarfBuzz to latest release: 1.2.7 BUG=skia:4742 GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1940393002 Review-Url: https://codereview.chromium.org/1940393002
Diffstat (limited to 'tools/using_skia_and_harfbuzz.cpp')
-rw-r--r--tools/using_skia_and_harfbuzz.cpp310
1 files changed, 310 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..8ed213c46c
--- /dev/null
+++ b/tools/using_skia_and_harfbuzz.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCanvas.h"
+#include "SkDocument.h"
+#include "SkFontMgr.h"
+#include "SkGradientShader.h"
+#include "SkPaint.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkTextBlob.h"
+#include "SkTypeface.h"
+
+#include <hb.h>
+#include <hb-ot.h>
+
+#include <cassert>
+#include <iostream>
+#include <map>
+#include <string>
+
+
+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() {}
+};
+
+template <class T> struct Option : BaseOption {
+ T value;
+ Option(std::string selector, std::string description, T defaultValue) :
+ BaseOption(selector, description),
+ value(defaultValue) {}
+};
+
+struct DoubleOption : Option<double> {
+ virtual void set(std::string _value) {
+ value = atof(_value.c_str());
+ }
+ virtual std::string valueToString() {
+ return std::to_string(value);
+ }
+ DoubleOption(std::string selector, std::string description, double defaultValue) :
+ Option<double>(selector, description, defaultValue) {}
+};
+
+struct SkStringOption : Option<SkString> {
+ virtual void set(std::string _value) {
+ value = _value.c_str();
+ }
+ virtual std::string valueToString() {
+ return value.c_str();
+ }
+ SkStringOption(std::string selector, std::string description, SkString defaultValue) :
+ Option<SkString>(selector, description, defaultValue) {}
+};
+
+struct StdStringOption : Option<std::string> {
+ virtual void set(std::string _value) {
+ value = _value;
+ }
+ virtual std::string valueToString() {
+ return value;
+ }
+ StdStringOption(std::string selector, std::string description, std::string defaultValue) :
+ Option<std::string>(selector, description, defaultValue) {}
+};
+
+struct Config {
+ DoubleOption *page_width = new DoubleOption("-w", "Page width", 600.0f);
+ DoubleOption *page_height = new DoubleOption("-h", "Page height", 800.0f);
+ SkStringOption *title = new SkStringOption("-t", "PDF title", SkString("---"));
+ SkStringOption *author = new SkStringOption("-a", "PDF author", SkString("---"));
+ SkStringOption *subject = new SkStringOption("-k", "PDF subject", SkString("---"));
+ SkStringOption *keywords = new SkStringOption("-c", "PDF keywords", SkString("---"));
+ SkStringOption *creator = new SkStringOption("-t", "PDF creator", SkString("---"));
+ StdStringOption *font_file = new StdStringOption("-f", ".ttf font file", "fonts/DejaVuSans.ttf");
+ DoubleOption *font_size = new DoubleOption("-z", "Font size", 8.0f);
+ DoubleOption *left_margin = new DoubleOption("-m", "Left margin", 20.0f);
+ DoubleOption *line_spacing_ratio = new DoubleOption("-h", "Line spacing ratio", 1.5f);
+ StdStringOption *output_file_name = new StdStringOption("-o", ".pdf output file name", "out-skiahf.pdf");
+
+ std::map<std::string, BaseOption*> options = {
+ { page_width->selector, page_width },
+ { page_height->selector, page_height },
+ { title->selector, title },
+ { author->selector, author },
+ { subject->selector, subject },
+ { keywords->selector, keywords },
+ { creator->selector, creator },
+ { font_file->selector, font_file },
+ { font_size->selector, font_size },
+ { left_margin->selector, left_margin },
+ { line_spacing_ratio->selector, line_spacing_ratio },
+ { output_file_name->selector, output_file_name },
+ };
+
+ Config(int argc, char **argv) {
+ 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 (auto it = options.begin(); it != options.end(); ++it) {
+ printf("\t%s\t%s (%s)\n", it->first.c_str(),
+ it->second->description.c_str(),
+ it->second->valueToString().c_str());
+ }
+ exit(-1);
+ }
+ }
+ } // end of Config::Config
+};
+
+const double FONT_SIZE_SCALE = 64.0f;
+
+struct Face {
+ struct HBFDel { void operator()(hb_face_t* f) { hb_face_destroy(f); } };
+ std::unique_ptr<hb_face_t, HBFDel> fHarfBuzzFace;
+ sk_sp<SkTypeface> fSkiaTypeface;
+
+ Face(const char* path, int index) {
+ // fairly portable mmap impl
+ auto data = SkData::MakeFromFileName(path);
+ assert(data);
+ if (!data) { return; }
+ fSkiaTypeface.reset(
+ SkTypeface::CreateFromStream(
+ new SkMemoryStream(data), index));
+ assert(fSkiaTypeface);
+ if (!fSkiaTypeface) { return; }
+ auto destroy = [](void *d) { static_cast<SkData*>(d)->unref(); };
+ const char* bytes = (const char*)data->data();
+ unsigned int size = (unsigned int)data->size();
+ hb_blob_t* blob = hb_blob_create(bytes,
+ size,
+ HB_MEMORY_MODE_READONLY,
+ data.release(),
+ destroy);
+ assert(blob);
+ hb_blob_make_immutable(blob);
+ hb_face_t* face = hb_face_create(blob, (unsigned)index);
+ hb_blob_destroy(blob);
+ assert(face);
+ if (!face) {
+ fSkiaTypeface.reset();
+ return;
+ }
+ hb_face_set_index(face, (unsigned)index);
+ hb_face_set_upem(face, fSkiaTypeface->getUnitsPerEm());
+ fHarfBuzzFace.reset(face);
+ }
+};
+
+class Placement {
+ public:
+ Placement(Config &_config, SkWStream* outputStream) : config(_config) {
+ face = new Face(config.font_file->value.c_str(), 0 /* index */);
+ hb_font = hb_font_create(face->fHarfBuzzFace.get());
+
+ hb_font_set_scale(hb_font,
+ FONT_SIZE_SCALE * config.font_size->value,
+ FONT_SIZE_SCALE * config.font_size->value);
+ hb_ot_font_set_funcs(hb_font);
+
+ SkDocument::PDFMetadata pdf_info;
+ pdf_info.fTitle = config.title->value;
+ pdf_info.fAuthor = config.author->value;
+ pdf_info.fSubject = config.subject->value;
+ pdf_info.fKeywords = config.keywords->value;
+ pdf_info.fCreator = config.creator->value;
+ 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;
+ pdfDocument = SkDocument::MakePDF(outputStream, SK_ScalarDefaultRasterDPI,
+ pdf_info, nullptr, true);
+ assert(pdfDocument);
+
+ white_paint.setColor(SK_ColorWHITE);
+
+ glyph_paint.setFlags(
+ SkPaint::kAntiAlias_Flag |
+ SkPaint::kSubpixelText_Flag); // ... avoid waggly text when rotating.
+ glyph_paint.setColor(SK_ColorBLACK);
+ glyph_paint.setTextSize(config.font_size->value);
+ SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
+ glyph_paint.setTypeface(face->fSkiaTypeface);
+ glyph_paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+ NewPage();
+ } // end of Placement
+
+ ~Placement() {
+ delete face;
+ hb_font_destroy (hb_font);
+ }
+
+ void WriteLine(const char *text) {
+ /* Create hb-buffer and populate. */
+ hb_buffer_t *hb_buffer = hb_buffer_create ();
+ hb_buffer_add_utf8 (hb_buffer, text, -1, 0, -1);
+ hb_buffer_guess_segment_properties (hb_buffer);
+
+ /* Shape it! */
+ hb_shape (hb_font, hb_buffer, NULL, 0);
+
+ DrawGlyphs(hb_buffer);
+
+ hb_buffer_destroy (hb_buffer);
+
+ // Advance to the next line.
+ current_y += config.line_spacing_ratio->value * config.font_size->value;
+ if (current_y > config.page_height->value) {
+ pdfDocument->endPage();
+ NewPage();
+ }
+ }
+
+ bool Close() {
+ return pdfDocument->close();
+ }
+
+private:
+ Config config;
+
+ Face *face;
+
+ hb_font_t *hb_font;
+
+ sk_sp<SkDocument> pdfDocument;
+
+ SkCanvas* pageCanvas;
+
+ SkPaint white_paint;
+ SkPaint glyph_paint;
+
+ double current_x;
+ double current_y;
+
+ void NewPage() {
+ pageCanvas = pdfDocument->beginPage(config.page_width->value, 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;
+ }
+
+ bool DrawGlyphs(hb_buffer_t *hb_buffer) {
+ SkTextBlobBuilder textBlobBuilder;
+ unsigned len = hb_buffer_get_length (hb_buffer);
+ if (len == 0) {
+ return true;
+ }
+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos (hb_buffer, NULL);
+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (hb_buffer, NULL);
+ auto runBuffer = textBlobBuilder.allocRunPos(glyph_paint, len);
+
+ double x = 0;
+ double y = 0;
+ for (unsigned int i = 0; i < len; i++)
+ {
+ runBuffer.glyphs[i] = info[i].codepoint;
+ reinterpret_cast<SkPoint*>(runBuffer.pos)[i] = SkPoint::Make(
+ x + pos[i].x_offset / FONT_SIZE_SCALE,
+ y - pos[i].y_offset / FONT_SIZE_SCALE);
+ x += pos[i].x_advance / FONT_SIZE_SCALE;
+ y += pos[i].y_advance / FONT_SIZE_SCALE;
+ }
+
+ pageCanvas->drawTextBlob(textBlobBuilder.build(), current_x, current_y, glyph_paint);
+ return true;
+ } // end of DrawGlyphs
+}; // end of Placement class
+
+int main(int argc, char** argv) {
+ Config config(argc, argv);
+
+ Placement placement(config, new SkFILEWStream(config.output_file_name->value.c_str()));
+ for (std::string line; std::getline(std::cin, line);) {
+ placement.WriteLine(line.c_str());
+ }
+ placement.Close();
+
+ return 0;
+}