xref: /aosp_15_r20/external/skia/tools/using_skia_and_harfbuzz.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2016 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker // This sample progam demonstrates how to use Skia and HarfBuzz to
9*c8dee2aaSAndroid Build Coastguard Worker // produce a PDF file from UTF-8 text in stdin.
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include <cassert>
12*c8dee2aaSAndroid Build Coastguard Worker #include <cstdlib>
13*c8dee2aaSAndroid Build Coastguard Worker #include <iostream>
14*c8dee2aaSAndroid Build Coastguard Worker #include <map>
15*c8dee2aaSAndroid Build Coastguard Worker #include <sstream>
16*c8dee2aaSAndroid Build Coastguard Worker #include <string>
17*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
18*c8dee2aaSAndroid Build Coastguard Worker 
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTextBlob.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypeface.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/docs/SkPDFDocument.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/ports/SkFontMgr_empty.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skshaper/include/SkShaper_harfbuzz.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skshaper/include/SkShaper_skunicode.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skunicode/include/SkUnicode.h"
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker namespace {
get_unicode()30*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkUnicode> get_unicode() {
31*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_UNICODE_ICU_IMPLEMENTATION)
32*c8dee2aaSAndroid Build Coastguard Worker     auto unicode = SkUnicodes::ICU::Make();
33*c8dee2aaSAndroid Build Coastguard Worker     if (unicode) {
34*c8dee2aaSAndroid Build Coastguard Worker         return unicode;
35*c8dee2aaSAndroid Build Coastguard Worker     }
36*c8dee2aaSAndroid Build Coastguard Worker #endif
37*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGFAIL("Only ICU implementation of SkUnicode is supported");
38*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
39*c8dee2aaSAndroid Build Coastguard Worker }
40*c8dee2aaSAndroid Build Coastguard Worker 
41*c8dee2aaSAndroid Build Coastguard Worker } // namespace
42*c8dee2aaSAndroid Build Coastguard Worker 
43*c8dee2aaSAndroid Build Coastguard Worker // Options /////////////////////////////////////////////////////////////////////
44*c8dee2aaSAndroid Build Coastguard Worker 
45*c8dee2aaSAndroid Build Coastguard Worker struct BaseOption {
46*c8dee2aaSAndroid Build Coastguard Worker     std::string selector;
47*c8dee2aaSAndroid Build Coastguard Worker     std::string description;
48*c8dee2aaSAndroid Build Coastguard Worker     virtual void set(const std::string& _value) = 0;
49*c8dee2aaSAndroid Build Coastguard Worker     virtual std::string valueToString() = 0;
50*c8dee2aaSAndroid Build Coastguard Worker 
BaseOptionBaseOption51*c8dee2aaSAndroid Build Coastguard Worker     BaseOption(std::string _selector, std::string _description)
52*c8dee2aaSAndroid Build Coastguard Worker             : selector(std::move(_selector))
53*c8dee2aaSAndroid Build Coastguard Worker             , description(std::move(_description)) {}
54*c8dee2aaSAndroid Build Coastguard Worker 
~BaseOptionBaseOption55*c8dee2aaSAndroid Build Coastguard Worker     virtual ~BaseOption() {}
56*c8dee2aaSAndroid Build Coastguard Worker 
57*c8dee2aaSAndroid Build Coastguard Worker     static void Init(const std::vector<BaseOption*> &, int argc, char **argv);
58*c8dee2aaSAndroid Build Coastguard Worker };
59*c8dee2aaSAndroid Build Coastguard Worker 
60*c8dee2aaSAndroid Build Coastguard Worker template <class T>
61*c8dee2aaSAndroid Build Coastguard Worker struct Option : BaseOption {
62*c8dee2aaSAndroid Build Coastguard Worker     T value;
OptionOption63*c8dee2aaSAndroid Build Coastguard Worker     Option(std::string _selector, std::string _description, T defaultValue)
64*c8dee2aaSAndroid Build Coastguard Worker             : BaseOption(std::move(_selector), std::move(_description))
65*c8dee2aaSAndroid Build Coastguard Worker             , value(defaultValue) {}
66*c8dee2aaSAndroid Build Coastguard Worker };
67*c8dee2aaSAndroid Build Coastguard Worker 
Init(const std::vector<BaseOption * > & option_list,int argc,char ** argv)68*c8dee2aaSAndroid Build Coastguard Worker void BaseOption::Init(const std::vector<BaseOption*> &option_list,
69*c8dee2aaSAndroid Build Coastguard Worker                       int argc, char **argv) {
70*c8dee2aaSAndroid Build Coastguard Worker     std::map<std::string, BaseOption *> options;
71*c8dee2aaSAndroid Build Coastguard Worker     for (BaseOption *opt : option_list) {
72*c8dee2aaSAndroid Build Coastguard Worker         options[opt->selector] = opt;
73*c8dee2aaSAndroid Build Coastguard Worker     }
74*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 1; i < argc; i++) {
75*c8dee2aaSAndroid Build Coastguard Worker         std::string option_selector(argv[i]);
76*c8dee2aaSAndroid Build Coastguard Worker         auto it = options.find(option_selector);
77*c8dee2aaSAndroid Build Coastguard Worker         if (it != options.end()) {
78*c8dee2aaSAndroid Build Coastguard Worker             if (i >= argc) {
79*c8dee2aaSAndroid Build Coastguard Worker                 break;
80*c8dee2aaSAndroid Build Coastguard Worker             }
81*c8dee2aaSAndroid Build Coastguard Worker             const char *option_value = argv[i + 1];
82*c8dee2aaSAndroid Build Coastguard Worker             it->second->set(option_value);
83*c8dee2aaSAndroid Build Coastguard Worker             i++;
84*c8dee2aaSAndroid Build Coastguard Worker         } else {
85*c8dee2aaSAndroid Build Coastguard Worker             printf("Ignoring unrecognized option: %s.\n", argv[i]);
86*c8dee2aaSAndroid Build Coastguard Worker             printf("Usage: %s {option value}\n", argv[0]);
87*c8dee2aaSAndroid Build Coastguard Worker             printf("\tTakes text from stdin and produces pdf file.\n");
88*c8dee2aaSAndroid Build Coastguard Worker             printf("Supported options:\n");
89*c8dee2aaSAndroid Build Coastguard Worker             for (BaseOption *opt : option_list) {
90*c8dee2aaSAndroid Build Coastguard Worker                 printf("\t%s\t%s (%s)\n", opt->selector.c_str(),
91*c8dee2aaSAndroid Build Coastguard Worker                        opt->description.c_str(), opt->valueToString().c_str());
92*c8dee2aaSAndroid Build Coastguard Worker             }
93*c8dee2aaSAndroid Build Coastguard Worker             exit(-1);
94*c8dee2aaSAndroid Build Coastguard Worker         }
95*c8dee2aaSAndroid Build Coastguard Worker     }
96*c8dee2aaSAndroid Build Coastguard Worker }
97*c8dee2aaSAndroid Build Coastguard Worker 
98*c8dee2aaSAndroid Build Coastguard Worker struct DoubleOption : Option<double> {
setDoubleOption99*c8dee2aaSAndroid Build Coastguard Worker     void set(const std::string& _value) override { value = atof(_value.c_str()); }
valueToStringDoubleOption100*c8dee2aaSAndroid Build Coastguard Worker     std::string valueToString() override {
101*c8dee2aaSAndroid Build Coastguard Worker         std::ostringstream stm;
102*c8dee2aaSAndroid Build Coastguard Worker         stm << value;
103*c8dee2aaSAndroid Build Coastguard Worker         return stm.str();
104*c8dee2aaSAndroid Build Coastguard Worker     }
DoubleOptionDoubleOption105*c8dee2aaSAndroid Build Coastguard Worker     DoubleOption(std::string _selector, std::string _description, double defaultValue)
106*c8dee2aaSAndroid Build Coastguard Worker             : Option<double>(std::move(_selector),
107*c8dee2aaSAndroid Build Coastguard Worker                              std::move(_description),
108*c8dee2aaSAndroid Build Coastguard Worker                              std::move(defaultValue)) {}
109*c8dee2aaSAndroid Build Coastguard Worker };
110*c8dee2aaSAndroid Build Coastguard Worker 
111*c8dee2aaSAndroid Build Coastguard Worker struct StringOption : Option<std::string> {
setStringOption112*c8dee2aaSAndroid Build Coastguard Worker     void set(const std::string& _value) override { value = _value; }
valueToStringStringOption113*c8dee2aaSAndroid Build Coastguard Worker     std::string valueToString() override { return value; }
StringOptionStringOption114*c8dee2aaSAndroid Build Coastguard Worker     StringOption(std::string _selector, std::string _description, std::string defaultValue)
115*c8dee2aaSAndroid Build Coastguard Worker             : Option<std::string>(std::move(_selector),
116*c8dee2aaSAndroid Build Coastguard Worker                                   std::move(_description),
117*c8dee2aaSAndroid Build Coastguard Worker                                   std::move(defaultValue)) {}
118*c8dee2aaSAndroid Build Coastguard Worker };
119*c8dee2aaSAndroid Build Coastguard Worker 
120*c8dee2aaSAndroid Build Coastguard Worker // Config //////////////////////////////////////////////////////////////////////
121*c8dee2aaSAndroid Build Coastguard Worker 
122*c8dee2aaSAndroid Build Coastguard Worker struct Config {
123*c8dee2aaSAndroid Build Coastguard Worker     DoubleOption page_width = DoubleOption("-w", "Page width", 600.0f);
124*c8dee2aaSAndroid Build Coastguard Worker     DoubleOption page_height = DoubleOption("-h", "Page height", 800.0f);
125*c8dee2aaSAndroid Build Coastguard Worker     StringOption title = StringOption("-t", "PDF title", "---");
126*c8dee2aaSAndroid Build Coastguard Worker     StringOption author = StringOption("-a", "PDF author", "---");
127*c8dee2aaSAndroid Build Coastguard Worker     StringOption subject = StringOption("-k", "PDF subject", "---");
128*c8dee2aaSAndroid Build Coastguard Worker     StringOption keywords = StringOption("-c", "PDF keywords", "---");
129*c8dee2aaSAndroid Build Coastguard Worker     StringOption creator = StringOption("-t", "PDF creator", "---");
130*c8dee2aaSAndroid Build Coastguard Worker     StringOption font_file = StringOption("-f", ".ttf font file", "");
131*c8dee2aaSAndroid Build Coastguard Worker     DoubleOption font_size = DoubleOption("-z", "Font size", 8.0f);
132*c8dee2aaSAndroid Build Coastguard Worker     DoubleOption left_margin = DoubleOption("-m", "Left margin", 20.0f);
133*c8dee2aaSAndroid Build Coastguard Worker     DoubleOption line_spacing_ratio =
134*c8dee2aaSAndroid Build Coastguard Worker             DoubleOption("-h", "Line spacing ratio", 0.25f);
135*c8dee2aaSAndroid Build Coastguard Worker     StringOption output_file_name =
136*c8dee2aaSAndroid Build Coastguard Worker             StringOption("-o", ".pdf output file name", "out-skiahf.pdf");
137*c8dee2aaSAndroid Build Coastguard Worker 
ConfigConfig138*c8dee2aaSAndroid Build Coastguard Worker     Config(int argc, char **argv) {
139*c8dee2aaSAndroid Build Coastguard Worker         BaseOption::Init(std::vector<BaseOption*>{
140*c8dee2aaSAndroid Build Coastguard Worker                 &page_width, &page_height, &title, &author, &subject,
141*c8dee2aaSAndroid Build Coastguard Worker                 &keywords, &creator, &font_file, &font_size, &left_margin,
142*c8dee2aaSAndroid Build Coastguard Worker                 &line_spacing_ratio, &output_file_name}, argc, argv);
143*c8dee2aaSAndroid Build Coastguard Worker     }
144*c8dee2aaSAndroid Build Coastguard Worker };
145*c8dee2aaSAndroid Build Coastguard Worker 
146*c8dee2aaSAndroid Build Coastguard Worker // Placement ///////////////////////////////////////////////////////////////////
147*c8dee2aaSAndroid Build Coastguard Worker 
148*c8dee2aaSAndroid Build Coastguard Worker class Placement {
149*c8dee2aaSAndroid Build Coastguard Worker public:
Placement(const Config * conf,SkDocument * doc)150*c8dee2aaSAndroid Build Coastguard Worker     Placement(const Config* conf, SkDocument *doc)
151*c8dee2aaSAndroid Build Coastguard Worker         : config(conf), document(doc), pageCanvas(nullptr) {
152*c8dee2aaSAndroid Build Coastguard Worker         white_paint.setColor(SK_ColorWHITE);
153*c8dee2aaSAndroid Build Coastguard Worker         glyph_paint.setColor(SK_ColorBLACK);
154*c8dee2aaSAndroid Build Coastguard Worker         glyph_paint.setAntiAlias(true);
155*c8dee2aaSAndroid Build Coastguard Worker         font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
156*c8dee2aaSAndroid Build Coastguard Worker         font.setSubpixel(true);
157*c8dee2aaSAndroid Build Coastguard Worker         font.setSize(SkDoubleToScalar(config->font_size.value));
158*c8dee2aaSAndroid Build Coastguard Worker     }
159*c8dee2aaSAndroid Build Coastguard Worker 
WriteLine(const SkShaper & shaper,const char * text,size_t textBytes)160*c8dee2aaSAndroid Build Coastguard Worker     void WriteLine(const SkShaper& shaper, const char* text, size_t textBytes) {
161*c8dee2aaSAndroid Build Coastguard Worker         SkTextBlobBuilderRunHandler textBlobBuilder(text, {0, 0});
162*c8dee2aaSAndroid Build Coastguard Worker 
163*c8dee2aaSAndroid Build Coastguard Worker         const SkBidiIterator::Level defaultLevel = SkBidiIterator::kLTR;
164*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkShaper::BiDiRunIterator> bidi =
165*c8dee2aaSAndroid Build Coastguard Worker                 SkShapers::unicode::BidiRunIterator(get_unicode(), text, textBytes, defaultLevel);
166*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(bidi);
167*c8dee2aaSAndroid Build Coastguard Worker 
168*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkShaper::LanguageRunIterator> language =
169*c8dee2aaSAndroid Build Coastguard Worker                 SkShaper::MakeStdLanguageRunIterator(text, textBytes);
170*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(language);
171*c8dee2aaSAndroid Build Coastguard Worker 
172*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkShaper::ScriptRunIterator> script =
173*c8dee2aaSAndroid Build Coastguard Worker                 SkShapers::HB::ScriptRunIterator(text, textBytes);
174*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(script);
175*c8dee2aaSAndroid Build Coastguard Worker 
176*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkShaper::FontRunIterator> fontRuns =
177*c8dee2aaSAndroid Build Coastguard Worker                 SkShaper::MakeFontMgrRunIterator(text, textBytes, font, SkFontMgr::RefEmpty());
178*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fontRuns);
179*c8dee2aaSAndroid Build Coastguard Worker 
180*c8dee2aaSAndroid Build Coastguard Worker         const SkScalar width = config->page_width.value - 2 * config->left_margin.value;
181*c8dee2aaSAndroid Build Coastguard Worker         shaper.shape(text,
182*c8dee2aaSAndroid Build Coastguard Worker                      textBytes,
183*c8dee2aaSAndroid Build Coastguard Worker                      *fontRuns,
184*c8dee2aaSAndroid Build Coastguard Worker                      *bidi,
185*c8dee2aaSAndroid Build Coastguard Worker                      *script,
186*c8dee2aaSAndroid Build Coastguard Worker                      *language,
187*c8dee2aaSAndroid Build Coastguard Worker                      nullptr,
188*c8dee2aaSAndroid Build Coastguard Worker                      0,
189*c8dee2aaSAndroid Build Coastguard Worker                      width,
190*c8dee2aaSAndroid Build Coastguard Worker                      &textBlobBuilder);
191*c8dee2aaSAndroid Build Coastguard Worker         SkPoint endPoint = textBlobBuilder.endPoint();
192*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<const SkTextBlob> blob = textBlobBuilder.makeBlob();
193*c8dee2aaSAndroid Build Coastguard Worker         // If we don't have a page, or if we're not at the start of the page and the blob won't fit
194*c8dee2aaSAndroid Build Coastguard Worker         if (!pageCanvas ||
195*c8dee2aaSAndroid Build Coastguard Worker               (current_y > config->line_spacing_ratio.value * config->font_size.value &&
196*c8dee2aaSAndroid Build Coastguard Worker                current_y + endPoint.y() > config->page_height.value)
197*c8dee2aaSAndroid Build Coastguard Worker         ) {
198*c8dee2aaSAndroid Build Coastguard Worker             if (pageCanvas) {
199*c8dee2aaSAndroid Build Coastguard Worker                 document->endPage();
200*c8dee2aaSAndroid Build Coastguard Worker             }
201*c8dee2aaSAndroid Build Coastguard Worker             pageCanvas = document->beginPage(
202*c8dee2aaSAndroid Build Coastguard Worker                     SkDoubleToScalar(config->page_width.value),
203*c8dee2aaSAndroid Build Coastguard Worker                     SkDoubleToScalar(config->page_height.value));
204*c8dee2aaSAndroid Build Coastguard Worker             pageCanvas->drawPaint(white_paint);
205*c8dee2aaSAndroid Build Coastguard Worker             current_x = config->left_margin.value;
206*c8dee2aaSAndroid Build Coastguard Worker             current_y = config->line_spacing_ratio.value * config->font_size.value;
207*c8dee2aaSAndroid Build Coastguard Worker         }
208*c8dee2aaSAndroid Build Coastguard Worker         pageCanvas->drawTextBlob(
209*c8dee2aaSAndroid Build Coastguard Worker                 blob.get(), SkDoubleToScalar(current_x),
210*c8dee2aaSAndroid Build Coastguard Worker                 SkDoubleToScalar(current_y), glyph_paint);
211*c8dee2aaSAndroid Build Coastguard Worker         // Advance to the next line.
212*c8dee2aaSAndroid Build Coastguard Worker         current_y += endPoint.y() + config->line_spacing_ratio.value * config->font_size.value;
213*c8dee2aaSAndroid Build Coastguard Worker     }
214*c8dee2aaSAndroid Build Coastguard Worker 
215*c8dee2aaSAndroid Build Coastguard Worker private:
216*c8dee2aaSAndroid Build Coastguard Worker     const Config* config;
217*c8dee2aaSAndroid Build Coastguard Worker     SkDocument *document;
218*c8dee2aaSAndroid Build Coastguard Worker     SkCanvas *pageCanvas;
219*c8dee2aaSAndroid Build Coastguard Worker     SkPaint white_paint;
220*c8dee2aaSAndroid Build Coastguard Worker     SkPaint glyph_paint;
221*c8dee2aaSAndroid Build Coastguard Worker     SkFont font;
222*c8dee2aaSAndroid Build Coastguard Worker     double current_x;
223*c8dee2aaSAndroid Build Coastguard Worker     double current_y;
224*c8dee2aaSAndroid Build Coastguard Worker };
225*c8dee2aaSAndroid Build Coastguard Worker 
226*c8dee2aaSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
227*c8dee2aaSAndroid Build Coastguard Worker 
MakePDFDocument(const Config & config,SkWStream * wStream)228*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkDocument> MakePDFDocument(const Config &config, SkWStream *wStream) {
229*c8dee2aaSAndroid Build Coastguard Worker     SkPDF::Metadata pdf_info;
230*c8dee2aaSAndroid Build Coastguard Worker     pdf_info.fTitle = config.title.value.c_str();
231*c8dee2aaSAndroid Build Coastguard Worker     pdf_info.fAuthor = config.author.value.c_str();
232*c8dee2aaSAndroid Build Coastguard Worker     pdf_info.fSubject = config.subject.value.c_str();
233*c8dee2aaSAndroid Build Coastguard Worker     pdf_info.fKeywords = config.keywords.value.c_str();
234*c8dee2aaSAndroid Build Coastguard Worker     pdf_info.fCreator = config.creator.value.c_str();
235*c8dee2aaSAndroid Build Coastguard Worker     #if 0
236*c8dee2aaSAndroid Build Coastguard Worker         SkPDF::DateTime now;
237*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::GetDateTime(&now);
238*c8dee2aaSAndroid Build Coastguard Worker         pdf_info.fCreation = now;
239*c8dee2aaSAndroid Build Coastguard Worker         pdf_info.fModified = now;
240*c8dee2aaSAndroid Build Coastguard Worker         pdf_info.fPDFA = true;
241*c8dee2aaSAndroid Build Coastguard Worker     #endif
242*c8dee2aaSAndroid Build Coastguard Worker     return SkPDF::MakeDocument(wStream, pdf_info);
243*c8dee2aaSAndroid Build Coastguard Worker }
244*c8dee2aaSAndroid Build Coastguard Worker 
main(int argc,char ** argv)245*c8dee2aaSAndroid Build Coastguard Worker int main(int argc, char **argv) {
246*c8dee2aaSAndroid Build Coastguard Worker     Config config(argc, argv);
247*c8dee2aaSAndroid Build Coastguard Worker     SkFILEWStream wStream(config.output_file_name.value.c_str());
248*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkDocument> doc = MakePDFDocument(config, &wStream);
249*c8dee2aaSAndroid Build Coastguard Worker     assert(doc);
250*c8dee2aaSAndroid Build Coastguard Worker     Placement placement(&config, doc.get());
251*c8dee2aaSAndroid Build Coastguard Worker 
252*c8dee2aaSAndroid Build Coastguard Worker     const std::string &font_file = config.font_file.value;
253*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkTypeface> typeface;
254*c8dee2aaSAndroid Build Coastguard Worker     if (font_file.size() > 0) {
255*c8dee2aaSAndroid Build Coastguard Worker         // There are different font managers for different platforms. See include/ports
256*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<SkFontMgr> mgr = SkFontMgr_New_Custom_Empty();
257*c8dee2aaSAndroid Build Coastguard Worker         assert(mgr);
258*c8dee2aaSAndroid Build Coastguard Worker         typeface = mgr->makeFromFile(font_file.c_str(), 0 /* index */);
259*c8dee2aaSAndroid Build Coastguard Worker     }
260*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkShaper> shaper =
261*c8dee2aaSAndroid Build Coastguard Worker             SkShapers::HB::ShaperDrivenWrapper(get_unicode(), nullptr);
262*c8dee2aaSAndroid Build Coastguard Worker     assert(shaper);
263*c8dee2aaSAndroid Build Coastguard Worker     //SkString line("This is هذا هو الخط a line.");
264*c8dee2aaSAndroid Build Coastguard Worker     //SkString line("⁧This is a line هذا هو الخط.⁩");
265*c8dee2aaSAndroid Build Coastguard Worker     for (std::string line; std::getline(std::cin, line);) {
266*c8dee2aaSAndroid Build Coastguard Worker         placement.WriteLine(*shaper, line.c_str(), line.size());
267*c8dee2aaSAndroid Build Coastguard Worker     }
268*c8dee2aaSAndroid Build Coastguard Worker 
269*c8dee2aaSAndroid Build Coastguard Worker     doc->close();
270*c8dee2aaSAndroid Build Coastguard Worker     wStream.flush();
271*c8dee2aaSAndroid Build Coastguard Worker     return 0;
272*c8dee2aaSAndroid Build Coastguard Worker }
273