diff options
author | halcanary <halcanary@google.com> | 2016-05-03 15:09:52 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-05-03 15:09:52 -0700 |
commit | 00d44e014c805d06a59a74cd744e24482c218e9b (patch) | |
tree | af206d235dc1ebaf1a00908ea6882192ea7d1a9b /tools/using_skia_and_harfbuzz.cpp | |
parent | 02125d10d5d021f1f3190ca95a3ef62b43349a64 (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.cpp | 310 |
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; +} |