using_skia_and_harfbuzz.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. * Copyright 2016 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. // This sample progam demonstrates how to use Skia and HarfBuzz to
  8. // produce a PDF file from UTF-8 text in stdin.
  9. #include <cassert>
  10. #include <cstdlib>
  11. #include <iostream>
  12. #include <map>
  13. #include <sstream>
  14. #include <string>
  15. #include <vector>
  16. #include "include/core/SkCanvas.h"
  17. #include "include/core/SkStream.h"
  18. #include "include/core/SkTextBlob.h"
  19. #include "include/core/SkTypeface.h"
  20. #include "include/docs/SkPDFDocument.h"
  21. #include "modules/skshaper/include/SkShaper.h"
  22. // Options /////////////////////////////////////////////////////////////////////
  23. struct BaseOption {
  24. std::string selector;
  25. std::string description;
  26. virtual void set(std::string _value) = 0;
  27. virtual std::string valueToString() = 0;
  28. BaseOption(std::string _selector, std::string _description)
  29. : selector(_selector), description(_description) {}
  30. virtual ~BaseOption() {}
  31. static void Init(const std::vector<BaseOption*> &, int argc, char **argv);
  32. };
  33. template <class T>
  34. struct Option : BaseOption {
  35. T value;
  36. Option(std::string _selector, std::string _description, T defaultValue)
  37. : BaseOption(_selector, _description), value(defaultValue) {}
  38. };
  39. void BaseOption::Init(const std::vector<BaseOption*> &option_list,
  40. int argc, char **argv) {
  41. std::map<std::string, BaseOption *> options;
  42. for (BaseOption *opt : option_list) {
  43. options[opt->selector] = opt;
  44. }
  45. for (int i = 1; i < argc; i++) {
  46. std::string option_selector(argv[i]);
  47. auto it = options.find(option_selector);
  48. if (it != options.end()) {
  49. if (i >= argc) {
  50. break;
  51. }
  52. const char *option_value = argv[i + 1];
  53. it->second->set(option_value);
  54. i++;
  55. } else {
  56. printf("Ignoring unrecognized option: %s.\n", argv[i]);
  57. printf("Usage: %s {option value}\n", argv[0]);
  58. printf("\tTakes text from stdin and produces pdf file.\n");
  59. printf("Supported options:\n");
  60. for (BaseOption *opt : option_list) {
  61. printf("\t%s\t%s (%s)\n", opt->selector.c_str(),
  62. opt->description.c_str(), opt->valueToString().c_str());
  63. }
  64. exit(-1);
  65. }
  66. }
  67. }
  68. struct DoubleOption : Option<double> {
  69. virtual void set(std::string _value) { value = atof(_value.c_str()); }
  70. virtual std::string valueToString() {
  71. std::ostringstream stm;
  72. stm << value;
  73. return stm.str();
  74. }
  75. DoubleOption(std::string _selector,
  76. std::string _description,
  77. double defaultValue)
  78. : Option<double>(_selector, _description, defaultValue) {}
  79. };
  80. struct StringOption : Option<std::string> {
  81. virtual void set(std::string _value) { value = _value; }
  82. virtual std::string valueToString() { return value; }
  83. StringOption(std::string _selector,
  84. std::string _description,
  85. std::string defaultValue)
  86. : Option<std::string>(_selector, _description, defaultValue) {}
  87. };
  88. // Config //////////////////////////////////////////////////////////////////////
  89. struct Config {
  90. DoubleOption page_width = DoubleOption("-w", "Page width", 600.0f);
  91. DoubleOption page_height = DoubleOption("-h", "Page height", 800.0f);
  92. StringOption title = StringOption("-t", "PDF title", "---");
  93. StringOption author = StringOption("-a", "PDF author", "---");
  94. StringOption subject = StringOption("-k", "PDF subject", "---");
  95. StringOption keywords = StringOption("-c", "PDF keywords", "---");
  96. StringOption creator = StringOption("-t", "PDF creator", "---");
  97. StringOption font_file = StringOption("-f", ".ttf font file", "");
  98. DoubleOption font_size = DoubleOption("-z", "Font size", 8.0f);
  99. DoubleOption left_margin = DoubleOption("-m", "Left margin", 20.0f);
  100. DoubleOption line_spacing_ratio =
  101. DoubleOption("-h", "Line spacing ratio", 0.25f);
  102. StringOption output_file_name =
  103. StringOption("-o", ".pdf output file name", "out-skiahf.pdf");
  104. Config(int argc, char **argv) {
  105. BaseOption::Init(std::vector<BaseOption*>{
  106. &page_width, &page_height, &title, &author, &subject,
  107. &keywords, &creator, &font_file, &font_size, &left_margin,
  108. &line_spacing_ratio, &output_file_name}, argc, argv);
  109. }
  110. };
  111. // Placement ///////////////////////////////////////////////////////////////////
  112. class Placement {
  113. public:
  114. Placement(const Config* conf, SkDocument *doc)
  115. : config(conf), document(doc), pageCanvas(nullptr) {
  116. white_paint.setColor(SK_ColorWHITE);
  117. glyph_paint.setColor(SK_ColorBLACK);
  118. glyph_paint.setAntiAlias(true);
  119. font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
  120. font.setSubpixel(true);
  121. font.setSize(SkDoubleToScalar(config->font_size.value));
  122. }
  123. void WriteLine(const SkShaper& shaper, const char *text, size_t textBytes) {
  124. SkTextBlobBuilderRunHandler textBlobBuilder(text, {0, 0});
  125. shaper.shape(text, textBytes, font, true,
  126. config->page_width.value - 2*config->left_margin.value, &textBlobBuilder);
  127. SkPoint endPoint = textBlobBuilder.endPoint();
  128. sk_sp<const SkTextBlob> blob = textBlobBuilder.makeBlob();
  129. // If we don't have a page, or if we're not at the start of the page and the blob won't fit
  130. if (!pageCanvas ||
  131. (current_y > config->line_spacing_ratio.value * config->font_size.value &&
  132. current_y + endPoint.y() > config->page_height.value)
  133. ) {
  134. if (pageCanvas) {
  135. document->endPage();
  136. }
  137. pageCanvas = document->beginPage(
  138. SkDoubleToScalar(config->page_width.value),
  139. SkDoubleToScalar(config->page_height.value));
  140. pageCanvas->drawPaint(white_paint);
  141. current_x = config->left_margin.value;
  142. current_y = config->line_spacing_ratio.value * config->font_size.value;
  143. }
  144. pageCanvas->drawTextBlob(
  145. blob.get(), SkDoubleToScalar(current_x),
  146. SkDoubleToScalar(current_y), glyph_paint);
  147. // Advance to the next line.
  148. current_y += endPoint.y() + config->line_spacing_ratio.value * config->font_size.value;
  149. }
  150. private:
  151. const Config* config;
  152. SkDocument *document;
  153. SkCanvas *pageCanvas;
  154. SkPaint white_paint;
  155. SkPaint glyph_paint;
  156. SkFont font;
  157. double current_x;
  158. double current_y;
  159. };
  160. ////////////////////////////////////////////////////////////////////////////////
  161. static sk_sp<SkDocument> MakePDFDocument(const Config &config, SkWStream *wStream) {
  162. SkPDF::Metadata pdf_info;
  163. pdf_info.fTitle = config.title.value.c_str();
  164. pdf_info.fAuthor = config.author.value.c_str();
  165. pdf_info.fSubject = config.subject.value.c_str();
  166. pdf_info.fKeywords = config.keywords.value.c_str();
  167. pdf_info.fCreator = config.creator.value.c_str();
  168. #if 0
  169. SkTime::DateTime now;
  170. SkTime::GetDateTime(&now);
  171. pdf_info.fCreation = now;
  172. pdf_info.fModified = now;
  173. pdf_info.fPDFA = true;
  174. #endif
  175. return SkPDF::MakeDocument(wStream, pdf_info);
  176. }
  177. int main(int argc, char **argv) {
  178. Config config(argc, argv);
  179. SkFILEWStream wStream(config.output_file_name.value.c_str());
  180. sk_sp<SkDocument> doc = MakePDFDocument(config, &wStream);
  181. assert(doc);
  182. Placement placement(&config, doc.get());
  183. const std::string &font_file = config.font_file.value;
  184. sk_sp<SkTypeface> typeface;
  185. if (font_file.size() > 0) {
  186. typeface = SkTypeface::MakeFromFile(font_file.c_str(), 0 /* index */);
  187. }
  188. std::unique_ptr<SkShaper> shaper = SkShaper::Make();
  189. assert(shaper);
  190. //SkString line("This is هذا هو الخط a line.");
  191. //SkString line("⁧This is a line هذا هو الخط.⁩");
  192. for (std::string line; std::getline(std::cin, line);) {
  193. placement.WriteLine(*shaper, line.c_str(), line.size());
  194. }
  195. doc->close();
  196. wStream.flush();
  197. return 0;
  198. }