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