From 264182c3f7d282e57a4b1d46fde3ef702b81c5c3 Mon Sep 17 00:00:00 2001 From: Herb Derby Date: Tue, 29 May 2018 15:53:40 -0400 Subject: Make SkShaper a module Change-Id: I3709e49ba865f14260660cc07a762b9ac837cb3c Reviewed-on: https://skia-review.googlesource.com/130602 Reviewed-by: Herb Derby Commit-Queue: Herb Derby --- tools/using_skia_and_harfbuzz.cpp | 222 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 tools/using_skia_and_harfbuzz.cpp (limited to 'tools/using_skia_and_harfbuzz.cpp') 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 +#include +#include +#include +#include +#include +#include + +#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 &, int argc, char **argv); +}; + +template +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 &option_list, + int argc, char **argv) { + std::map 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 { + 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(selector, description, defaultValue) {} +}; + +struct StringOption : Option { + 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(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{ + &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 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 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 doc = MakePDFDocument(config, &wStream); + assert(doc); + Placement placement(&config, doc.get()); + + const std::string &font_file = config.font_file.value; + sk_sp 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; +} -- cgit v1.2.3