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