• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkCanvas.h"
20 #include "SkDocument.h"
21 #include "SkShaper.h"
22 #include "SkStream.h"
23 #include "SkTextBlob.h"
24 #include "SkTypeface.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 
~BaseOptionBaseOption37     virtual ~BaseOption() {}
38 
39     static void Init(const std::vector<BaseOption*> &, int argc, char **argv);
40 };
41 
42 template <class T>
43 struct Option : BaseOption {
44     T value;
OptionOption45     Option(std::string selector, std::string description, T defaultValue)
46         : BaseOption(selector, description), value(defaultValue) {}
47 };
48 
Init(const std::vector<BaseOption * > & option_list,int argc,char ** argv)49 void BaseOption::Init(const std::vector<BaseOption*> &option_list,
50                       int argc, char **argv) {
51     std::map<std::string, BaseOption *> options;
52     for (BaseOption *opt : option_list) {
53         options[opt->selector] = opt;
54     }
55     for (int i = 1; i < argc; i++) {
56         std::string option_selector(argv[i]);
57         auto it = options.find(option_selector);
58         if (it != options.end()) {
59             if (i >= argc) {
60                 break;
61             }
62             const char *option_value = argv[i + 1];
63             it->second->set(option_value);
64             i++;
65         } else {
66             printf("Ignoring unrecognized option: %s.\n", argv[i]);
67             printf("Usage: %s {option value}\n", argv[0]);
68             printf("\tTakes text from stdin and produces pdf file.\n");
69             printf("Supported options:\n");
70             for (BaseOption *opt : option_list) {
71                 printf("\t%s\t%s (%s)\n", opt->selector.c_str(),
72                        opt->description.c_str(), opt->valueToString().c_str());
73             }
74             exit(-1);
75         }
76     }
77 }
78 
79 struct DoubleOption : Option<double> {
setDoubleOption80     virtual void set(std::string _value) { value = atof(_value.c_str()); }
valueToStringDoubleOption81     virtual std::string valueToString() {
82         std::ostringstream stm;
83         stm << value;
84         return stm.str();
85     }
DoubleOptionDoubleOption86     DoubleOption(std::string selector,
87                  std::string description,
88                  double defaultValue)
89         : Option<double>(selector, description, defaultValue) {}
90 };
91 
92 struct StringOption : Option<std::string> {
setStringOption93     virtual void set(std::string _value) { value = _value; }
valueToStringStringOption94     virtual std::string valueToString() { return value; }
StringOptionStringOption95     StringOption(std::string selector,
96                  std::string description,
97                  std::string defaultValue)
98         : Option<std::string>(selector, description, defaultValue) {}
99 };
100 
101 // Config //////////////////////////////////////////////////////////////////////
102 
103 struct Config {
104     DoubleOption page_width = DoubleOption("-w", "Page width", 600.0f);
105     DoubleOption page_height = DoubleOption("-h", "Page height", 800.0f);
106     StringOption title = StringOption("-t", "PDF title", "---");
107     StringOption author = StringOption("-a", "PDF author", "---");
108     StringOption subject = StringOption("-k", "PDF subject", "---");
109     StringOption keywords = StringOption("-c", "PDF keywords", "---");
110     StringOption creator = StringOption("-t", "PDF creator", "---");
111     StringOption font_file = StringOption("-f", ".ttf font file", "");
112     DoubleOption font_size = DoubleOption("-z", "Font size", 8.0f);
113     DoubleOption left_margin = DoubleOption("-m", "Left margin", 20.0f);
114     DoubleOption line_spacing_ratio =
115             DoubleOption("-h", "Line spacing ratio", 0.25f);
116     StringOption output_file_name =
117             StringOption("-o", ".pdf output file name", "out-skiahf.pdf");
118 
ConfigConfig119     Config(int argc, char **argv) {
120         BaseOption::Init(std::vector<BaseOption*>{
121                 &page_width, &page_height, &title, &author, &subject,
122                 &keywords, &creator, &font_file, &font_size, &left_margin,
123                 &line_spacing_ratio, &output_file_name}, argc, argv);
124     }
125 };
126 
127 // Placement ///////////////////////////////////////////////////////////////////
128 
129 class Placement {
130 public:
Placement(const Config * conf,SkDocument * doc)131     Placement(const Config* conf, SkDocument *doc)
132         : config(conf), document(doc), pageCanvas(nullptr) {
133         white_paint.setColor(SK_ColorWHITE);
134         glyph_paint.setColor(SK_ColorBLACK);
135         glyph_paint.setFlags(SkPaint::kAntiAlias_Flag |
136                              SkPaint::kSubpixelText_Flag);
137         glyph_paint.setTextSize(SkDoubleToScalar(config->font_size.value));
138     }
139 
WriteLine(const SkShaper & shaper,const char * text,size_t textBytes)140     void WriteLine(const SkShaper& shaper, const char *text, size_t textBytes) {
141         SkTextBlobBuilder textBlobBuilder;
142         SkPoint endPoint = shaper.shape(&textBlobBuilder, glyph_paint, text, textBytes, true,
143                                         SkPoint{0, 0},
144                                         config->page_width.value - 2*config->left_margin.value);
145         sk_sp<const SkTextBlob> blob = textBlobBuilder.make();
146         // If we don't have a page, or if we're not at the start of the page and the blob won't fit
147         if (!pageCanvas ||
148               (current_y > config->line_spacing_ratio.value * config->font_size.value &&
149                current_y + endPoint.y() > config->page_height.value)
150         ) {
151             if (pageCanvas) {
152                 document->endPage();
153             }
154             pageCanvas = document->beginPage(
155                     SkDoubleToScalar(config->page_width.value),
156                     SkDoubleToScalar(config->page_height.value));
157             pageCanvas->drawPaint(white_paint);
158             current_x = config->left_margin.value;
159             current_y = config->line_spacing_ratio.value * config->font_size.value;
160         }
161         pageCanvas->drawTextBlob(
162                 blob.get(), SkDoubleToScalar(current_x),
163                 SkDoubleToScalar(current_y), glyph_paint);
164         // Advance to the next line.
165         current_y += endPoint.y() + config->line_spacing_ratio.value * config->font_size.value;
166     }
167 
168 private:
169     const Config* config;
170     SkDocument *document;
171     SkCanvas *pageCanvas;
172     SkPaint white_paint;
173     SkPaint glyph_paint;
174     double current_x;
175     double current_y;
176 };
177 
178 ////////////////////////////////////////////////////////////////////////////////
179 
MakePDFDocument(const Config & config,SkWStream * wStream)180 static sk_sp<SkDocument> MakePDFDocument(const Config &config, SkWStream *wStream) {
181     SkDocument::PDFMetadata pdf_info;
182     pdf_info.fTitle = config.title.value.c_str();
183     pdf_info.fAuthor = config.author.value.c_str();
184     pdf_info.fSubject = config.subject.value.c_str();
185     pdf_info.fKeywords = config.keywords.value.c_str();
186     pdf_info.fCreator = config.creator.value.c_str();
187     #if 0
188         SkTime::DateTime now;
189         SkTime::GetDateTime(&now);
190         pdf_info.fCreation.fEnabled = true;
191         pdf_info.fCreation.fDateTime = now;
192         pdf_info.fModified.fEnabled = true;
193         pdf_info.fModified.fDateTime = now;
194         pdf_info.fPDFA = true;
195     #endif
196     return SkDocument::MakePDF(wStream, pdf_info);
197 }
198 
main(int argc,char ** argv)199 int main(int argc, char **argv) {
200     Config config(argc, argv);
201     SkFILEWStream wStream(config.output_file_name.value.c_str());
202     sk_sp<SkDocument> doc = MakePDFDocument(config, &wStream);
203     assert(doc);
204     Placement placement(&config, doc.get());
205 
206     const std::string &font_file = config.font_file.value;
207     sk_sp<SkTypeface> typeface;
208     if (font_file.size() > 0) {
209         typeface = SkTypeface::MakeFromFile(font_file.c_str(), 0 /* index */);
210     }
211     SkShaper shaper(typeface);
212     assert(shaper.good());
213     //SkString line("This is هذا هو الخط a line.");
214     //SkString line("⁧This is a line هذا هو الخط.⁩");
215     for (std::string line; std::getline(std::cin, line);) {
216         placement.WriteLine(shaper, line.c_str(), line.size());
217     }
218 
219     doc->close();
220     wStream.flush();
221     return 0;
222 }
223