Index: tools/using_skia_and_harfbuzz.cpp |
diff --git a/tools/using_skia_and_harfbuzz.cpp b/tools/using_skia_and_harfbuzz.cpp |
index 01a5b026895e4f5c009b246d9db96c253ffc0537..782103465eaea72916a629e7a6ddff80c3a04f9b 100644 |
--- a/tools/using_skia_and_harfbuzz.cpp |
+++ b/tools/using_skia_and_harfbuzz.cpp |
@@ -12,327 +12,203 @@ |
#include <cstdlib> |
#include <iostream> |
#include <map> |
-#include <string> |
#include <sstream> |
- |
-#include <hb-ot.h> |
+#include <string> |
+#include <vector> |
#include "SkCanvas.h" |
#include "SkDocument.h" |
+#include "SkShaper.h" |
#include "SkStream.h" |
#include "SkTextBlob.h" |
#include "SkTypeface.h" |
-struct BaseOption { |
- std::string selector; |
- std::string description; |
- virtual void set(std::string _value) = 0; |
- virtual std::string valueToString() = 0; |
+// Options ///////////////////////////////////////////////////////////////////// |
- BaseOption(std::string _selector, std::string _description) : |
- selector(_selector), |
- description(_description) {} |
- |
- virtual ~BaseOption() {} |
-}; |
+struct BaseOption { |
+ std::string selector; |
+ std::string description; |
+ virtual void set(std::string _value) = 0; |
+ virtual std::string valueToString() = 0; |
-template <class T> struct Option : BaseOption { |
- T value; |
- Option(std::string selector, std::string description, T defaultValue) : |
- BaseOption(selector, description), |
- value(defaultValue) {} |
-}; |
+ BaseOption(std::string _selector, std::string _description) |
+ : selector(_selector), description(_description) {} |
-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) {} |
-}; |
+ virtual ~BaseOption() {} |
-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) {} |
+ static void Init(const std::vector<BaseOption*> &, int argc, char **argv); |
}; |
-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) {} |
+template <class T> |
+struct Option : BaseOption { |
+ T value; |
+ Option(std::string selector, std::string description, T defaultValue) |
+ : BaseOption(selector, description), value(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", ""); |
- 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) { |
+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; |
+ 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); |
} |
- 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 |
+} |
+ |
+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) {} |
}; |
-const double FONT_SIZE_SCALE = 64.0f; |
+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) {} |
+}; |
-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; |
+// Config ////////////////////////////////////////////////////////////////////// |
- Face(sk_sp<SkTypeface> skiaTypeface) : fSkiaTypeface(std::move(skiaTypeface)) { |
- int index; |
- std::unique_ptr<SkStreamAsset> asset(fSkiaTypeface->openStream(&index)); |
- size_t size = asset->getLength(); |
- // TODO(halcanary): avoid this malloc and copy. |
- char* buffer = (char*)malloc(size); |
- asset->read(buffer, size); |
- hb_blob_t* blob = hb_blob_create(buffer, |
- size, |
- HB_MEMORY_MODE_READONLY, |
- nullptr, |
- free); |
- 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); |
- } |
- Face(const char* path, int index) { |
- // fairly portable mmap impl |
- auto data = SkData::MakeFromFileName(path); |
- assert(data); |
- if (!data) { return; } |
- fSkiaTypeface = SkTypeface::MakeFromStream(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; |
+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", 1.5f); |
+ 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); |
} |
- hb_face_set_index(face, (unsigned)index); |
- hb_face_set_upem(face, fSkiaTypeface->getUnitsPerEm()); |
- fHarfBuzzFace.reset(face); |
- } |
}; |
+// Placement /////////////////////////////////////////////////////////////////// |
+ |
class Placement { |
- public: |
- Placement(Config &_config, SkWStream* outputStream) : config(_config) { |
- const std::string& font_file = config.font_file->value; |
- if (font_file.size() > 0) { |
- face = new Face(font_file.c_str(), 0 /* index */); |
- } else { |
- face = new Face(SkTypeface::MakeDefault()); |
+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(config->font_size.value); |
} |
- 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); |
- 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); |
+ void WriteLine(const SkShaper& shaper, const char *text, size_t textBytes) { |
+ if (!pageCanvas || current_y > config->page_height.value) { |
+ if (pageCanvas) { |
+ document->endPage(); |
+ } |
+ pageCanvas = document->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; |
+ } |
+ SkTextBlobBuilder textBlobBuilder; |
+ shaper.shape(&textBlobBuilder, glyph_paint, text, textBytes, SkPoint{0, 0}); |
+ sk_sp<const SkTextBlob> blob(textBlobBuilder.build()); |
+ pageCanvas->drawTextBlob(blob.get(), current_x, current_y, glyph_paint); |
- // 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(); |
+ // Advance to the next line. |
+ current_y += config->line_spacing_ratio.value * config->font_size.value; |
} |
- } |
- |
- 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); |
+ const Config* config; |
+ SkDocument *document; |
+ SkCanvas *pageCanvas; |
+ SkPaint white_paint; |
+ SkPaint glyph_paint; |
+ double current_x; |
+ double current_y; |
+}; |
- 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 |
+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(); |
+ bool pdfa = false; |
+ #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; |
+ pdfa = true; |
+ #endif |
+ return SkDocument::MakePDF(wStream, SK_ScalarDefaultRasterDPI, pdf_info, |
+ nullptr, pdfa); |
+} |
-int main(int argc, char** argv) { |
+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()); |
- Placement placement(config, new SkFILEWStream(config.output_file_name->value.c_str())); |
+ 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()); |
for (std::string line; std::getline(std::cin, line);) { |
- placement.WriteLine(line.c_str()); |
+ placement.WriteLine(shaper, line.c_str(), line.size()); |
} |
- placement.Close(); |
+ doc->close(); |
return 0; |
} |