Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(151)

Side by Side Diff: tools/using_skia_and_harfbuzz.cpp

Issue 2138133002: tools/SkShaper: factor shaping out of using_skia_and_harfbuzz (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: 2016-07-11 (Monday) 15:42:21 EDT Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/SkShaper.cpp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2016 Google Inc. 2 * Copyright 2016 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 // This sample progam demonstrates how to use Skia and HarfBuzz to 8 // This sample progam demonstrates how to use Skia and HarfBuzz to
9 // produce a PDF file from UTF-8 text in stdin. 9 // produce a PDF file from UTF-8 text in stdin.
10 10
11 #include <cassert> 11 #include <cassert>
12 #include <cstdlib> 12 #include <cstdlib>
13 #include <iostream> 13 #include <iostream>
14 #include <map> 14 #include <map>
15 #include <sstream>
15 #include <string> 16 #include <string>
16 #include <sstream> 17 #include <vector>
17
18 #include <hb-ot.h>
19 18
20 #include "SkCanvas.h" 19 #include "SkCanvas.h"
21 #include "SkDocument.h" 20 #include "SkDocument.h"
21 #include "SkShaper.h"
22 #include "SkStream.h" 22 #include "SkStream.h"
23 #include "SkTextBlob.h" 23 #include "SkTextBlob.h"
24 #include "SkTypeface.h" 24 #include "SkTypeface.h"
25 25
26 // Options /////////////////////////////////////////////////////////////////////
27
26 struct BaseOption { 28 struct BaseOption {
27 std::string selector; 29 std::string selector;
28 std::string description; 30 std::string description;
29 virtual void set(std::string _value) = 0; 31 virtual void set(std::string _value) = 0;
30 virtual std::string valueToString() = 0; 32 virtual std::string valueToString() = 0;
31 33
32 BaseOption(std::string _selector, std::string _description) : 34 BaseOption(std::string _selector, std::string _description)
33 selector(_selector), 35 : selector(_selector), description(_description) {}
34 description(_description) {} 36
35 37 virtual ~BaseOption() {}
36 virtual ~BaseOption() {} 38
37 }; 39 static void Init(const std::vector<BaseOption*> &, int argc, char **argv);
38 40 };
39 template <class T> struct Option : BaseOption { 41
40 T value; 42 template <class T>
41 Option(std::string selector, std::string description, T defaultValue) : 43 struct Option : BaseOption {
42 BaseOption(selector, description), 44 T value;
43 value(defaultValue) {} 45 Option(std::string selector, std::string description, T defaultValue)
44 }; 46 : BaseOption(selector, description), value(defaultValue) {}
47 };
48
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 }
45 78
46 struct DoubleOption : Option<double> { 79 struct DoubleOption : Option<double> {
47 virtual void set(std::string _value) { 80 virtual void set(std::string _value) { value = atof(_value.c_str()); }
48 value = atof(_value.c_str()); 81 virtual std::string valueToString() {
49 } 82 std::ostringstream stm;
50 virtual std::string valueToString() { 83 stm << value;
51 std::ostringstream stm; 84 return stm.str();
52 stm << value; 85 }
53 return stm.str(); 86 DoubleOption(std::string selector,
54 } 87 std::string description,
55 DoubleOption(std::string selector, std::string description, double defaultValu e) : 88 double defaultValue)
56 Option<double>(selector, description, defaultValue) {} 89 : Option<double>(selector, description, defaultValue) {}
57 }; 90 };
58 91
59 struct SkStringOption : Option<SkString> { 92 struct StringOption : Option<std::string> {
60 virtual void set(std::string _value) { 93 virtual void set(std::string _value) { value = _value; }
61 value = _value.c_str(); 94 virtual std::string valueToString() { return value; }
62 } 95 StringOption(std::string selector,
63 virtual std::string valueToString() { 96 std::string description,
64 return value.c_str(); 97 std::string defaultValue)
65 } 98 : Option<std::string>(selector, description, defaultValue) {}
66 SkStringOption(std::string selector, std::string description, SkString default Value) : 99 };
67 Option<SkString>(selector, description, defaultValue) {} 100
68 }; 101 // Config //////////////////////////////////////////////////////////////////////
69
70 struct StdStringOption : Option<std::string> {
71 virtual void set(std::string _value) {
72 value = _value;
73 }
74 virtual std::string valueToString() {
75 return value;
76 }
77 StdStringOption(std::string selector, std::string description, std::string def aultValue) :
78 Option<std::string>(selector, description, defaultValue) {}
79 };
80 102
81 struct Config { 103 struct Config {
82 DoubleOption *page_width = new DoubleOption("-w", "Page width", 600.0f); 104 DoubleOption page_width = DoubleOption("-w", "Page width", 600.0f);
83 DoubleOption *page_height = new DoubleOption("-h", "Page height", 800.0f); 105 DoubleOption page_height = DoubleOption("-h", "Page height", 800.0f);
84 SkStringOption *title = new SkStringOption("-t", "PDF title", SkString("---")) ; 106 StringOption title = StringOption("-t", "PDF title", "---");
85 SkStringOption *author = new SkStringOption("-a", "PDF author", SkString("---" )); 107 StringOption author = StringOption("-a", "PDF author", "---");
86 SkStringOption *subject = new SkStringOption("-k", "PDF subject", SkString("-- -")); 108 StringOption subject = StringOption("-k", "PDF subject", "---");
87 SkStringOption *keywords = new SkStringOption("-c", "PDF keywords", SkString(" ---")); 109 StringOption keywords = StringOption("-c", "PDF keywords", "---");
88 SkStringOption *creator = new SkStringOption("-t", "PDF creator", SkString("-- -")); 110 StringOption creator = StringOption("-t", "PDF creator", "---");
89 StdStringOption *font_file = new StdStringOption("-f", ".ttf font file", ""); 111 StringOption font_file = StringOption("-f", ".ttf font file", "");
90 DoubleOption *font_size = new DoubleOption("-z", "Font size", 8.0f); 112 DoubleOption font_size = DoubleOption("-z", "Font size", 8.0f);
91 DoubleOption *left_margin = new DoubleOption("-m", "Left margin", 20.0f); 113 DoubleOption left_margin = DoubleOption("-m", "Left margin", 20.0f);
92 DoubleOption *line_spacing_ratio = new DoubleOption("-h", "Line spacing ratio" , 1.5f); 114 DoubleOption line_spacing_ratio =
93 StdStringOption *output_file_name = new StdStringOption("-o", ".pdf output fil e name", "out-skiahf.pdf"); 115 DoubleOption("-h", "Line spacing ratio", 1.5f);
94 116 StringOption output_file_name =
95 std::map<std::string, BaseOption*> options = { 117 StringOption("-o", ".pdf output file name", "out-skiahf.pdf");
96 { page_width->selector, page_width }, 118
97 { page_height->selector, page_height }, 119 Config(int argc, char **argv) {
98 { title->selector, title }, 120 BaseOption::Init(std::vector<BaseOption*>{
99 { author->selector, author }, 121 &page_width, &page_height, &title, &author, &subject,
100 { subject->selector, subject }, 122 &keywords, &creator, &font_file, &font_size, &left_margin,
101 { keywords->selector, keywords }, 123 &line_spacing_ratio, &output_file_name}, argc, argv);
102 { creator->selector, creator }, 124 }
103 { font_file->selector, font_file }, 125 };
104 { font_size->selector, font_size }, 126
105 { left_margin->selector, left_margin }, 127 // Placement ///////////////////////////////////////////////////////////////////
106 { line_spacing_ratio->selector, line_spacing_ratio }, 128
107 { output_file_name->selector, output_file_name }, 129 class Placement {
108 }; 130 public:
109 131 Placement(const Config* conf, SkDocument *doc)
110 Config(int argc, char **argv) { 132 : config(conf), document(doc), pageCanvas(nullptr) {
111 for (int i = 1; i < argc; i++) { 133 white_paint.setColor(SK_ColorWHITE);
112 std::string option_selector(argv[i]); 134 glyph_paint.setColor(SK_ColorBLACK);
113 auto it = options.find(option_selector); 135 glyph_paint.setFlags(SkPaint::kAntiAlias_Flag |
114 if (it != options.end()) { 136 SkPaint::kSubpixelText_Flag);
115 if (i >= argc) { 137 glyph_paint.setTextSize(config->font_size.value);
116 break; 138 }
139
140 void WriteLine(const SkShaper& shaper, const char *text, size_t textBytes) {
141 if (!pageCanvas || current_y > config->page_height.value) {
142 if (pageCanvas) {
143 document->endPage();
144 }
145 pageCanvas = document->beginPage(config->page_width.value,
146 config->page_height.value);
147 pageCanvas->drawPaint(white_paint);
148 current_x = config->left_margin.value;
149 current_y = config->line_spacing_ratio.value * config->font_size.val ue;
117 } 150 }
118 const char *option_value = argv[i + 1]; 151 SkTextBlobBuilder textBlobBuilder;
119 it->second->set(option_value); 152 shaper.shape(&textBlobBuilder, glyph_paint, text, textBytes, SkPoint{0, 0});
120 i++; 153 sk_sp<const SkTextBlob> blob(textBlobBuilder.build());
121 } else { 154 pageCanvas->drawTextBlob(blob.get(), current_x, current_y, glyph_paint);
122 printf("Ignoring unrecognized option: %s.\n", argv[i]); 155
123 printf("Usage: %s {option value}\n", argv[0]); 156 // Advance to the next line.
124 printf("\tTakes text from stdin and produces pdf file.\n"); 157 current_y += config->line_spacing_ratio.value * config->font_size.value;
125 printf("Supported options:\n"); 158 }
126 for (auto it = options.begin(); it != options.end(); ++it) { 159
127 printf("\t%s\t%s (%s)\n", it->first.c_str(), 160 private:
128 it->second->description.c_str(), 161 const Config* config;
129 it->second->valueToString().c_str()); 162 SkDocument *document;
130 } 163 SkCanvas *pageCanvas;
131 exit(-1); 164 SkPaint white_paint;
132 } 165 SkPaint glyph_paint;
133 } 166 double current_x;
134 } // end of Config::Config 167 double current_y;
135 }; 168 };
136 169
137 const double FONT_SIZE_SCALE = 64.0f; 170 ////////////////////////////////////////////////////////////////////////////////
138 171
139 struct Face { 172 static sk_sp<SkDocument> MakePDFDocument(const Config &config,
140 struct HBFDel { void operator()(hb_face_t* f) { hb_face_destroy(f); } }; 173 SkWStream *wStream) {
141 std::unique_ptr<hb_face_t, HBFDel> fHarfBuzzFace; 174 SkDocument::PDFMetadata pdf_info;
142 sk_sp<SkTypeface> fSkiaTypeface; 175 pdf_info.fTitle = config.title.value.c_str();
143 176 pdf_info.fAuthor = config.author.value.c_str();
144 Face(sk_sp<SkTypeface> skiaTypeface) : fSkiaTypeface(std::move(skiaTypeface)) { 177 pdf_info.fSubject = config.subject.value.c_str();
145 int index; 178 pdf_info.fKeywords = config.keywords.value.c_str();
146 std::unique_ptr<SkStreamAsset> asset(fSkiaTypeface->openStream(&index)); 179 pdf_info.fCreator = config.creator.value.c_str();
147 size_t size = asset->getLength(); 180 bool pdfa = false;
148 // TODO(halcanary): avoid this malloc and copy. 181 #if 0
149 char* buffer = (char*)malloc(size); 182 SkTime::DateTime now;
150 asset->read(buffer, size); 183 SkTime::GetDateTime(&now);
151 hb_blob_t* blob = hb_blob_create(buffer, 184 pdf_info.fCreation.fEnabled = true;
152 size, 185 pdf_info.fCreation.fDateTime = now;
153 HB_MEMORY_MODE_READONLY, 186 pdf_info.fModified.fEnabled = true;
154 nullptr, 187 pdf_info.fModified.fDateTime = now;
155 free); 188 pdfa = true;
156 assert(blob); 189 #endif
157 hb_blob_make_immutable(blob); 190 return SkDocument::MakePDF(wStream, SK_ScalarDefaultRasterDPI, pdf_info,
158 hb_face_t* face = hb_face_create(blob, (unsigned)index); 191 nullptr, pdfa);
159 hb_blob_destroy(blob); 192 }
160 assert(face); 193
161 if (!face) { 194 int main(int argc, char **argv) {
162 fSkiaTypeface.reset(); 195 Config config(argc, argv);
163 return; 196 SkFILEWStream wStream(config.output_file_name.value.c_str());
164 } 197 sk_sp<SkDocument> doc = MakePDFDocument(config, &wStream);
165 hb_face_set_index(face, (unsigned)index); 198 assert(doc);
166 hb_face_set_upem(face, fSkiaTypeface->getUnitsPerEm()); 199 Placement placement(&config, doc.get());
167 fHarfBuzzFace.reset(face); 200
168 } 201 const std::string &font_file = config.font_file.value;
169 Face(const char* path, int index) { 202 sk_sp<SkTypeface> typeface;
170 // fairly portable mmap impl
171 auto data = SkData::MakeFromFileName(path);
172 assert(data);
173 if (!data) { return; }
174 fSkiaTypeface = SkTypeface::MakeFromStream(new SkMemoryStream(data), index);
175 assert(fSkiaTypeface);
176 if (!fSkiaTypeface) { return; }
177 auto destroy = [](void *d) { static_cast<SkData*>(d)->unref(); };
178 const char* bytes = (const char*)data->data();
179 unsigned int size = (unsigned int)data->size();
180 hb_blob_t* blob = hb_blob_create(bytes,
181 size,
182 HB_MEMORY_MODE_READONLY,
183 data.release(),
184 destroy);
185 assert(blob);
186 hb_blob_make_immutable(blob);
187 hb_face_t* face = hb_face_create(blob, (unsigned)index);
188 hb_blob_destroy(blob);
189 assert(face);
190 if (!face) {
191 fSkiaTypeface.reset();
192 return;
193 }
194 hb_face_set_index(face, (unsigned)index);
195 hb_face_set_upem(face, fSkiaTypeface->getUnitsPerEm());
196 fHarfBuzzFace.reset(face);
197 }
198 };
199
200 class Placement {
201 public:
202 Placement(Config &_config, SkWStream* outputStream) : config(_config) {
203 const std::string& font_file = config.font_file->value;
204 if (font_file.size() > 0) { 203 if (font_file.size() > 0) {
205 face = new Face(font_file.c_str(), 0 /* index */); 204 typeface = SkTypeface::MakeFromFile(font_file.c_str(), 0 /* index */);
206 } else { 205 }
207 face = new Face(SkTypeface::MakeDefault()); 206 SkShaper shaper(typeface);
208 } 207 assert(shaper.good());
209 hb_font = hb_font_create(face->fHarfBuzzFace.get());
210
211 hb_font_set_scale(hb_font,
212 FONT_SIZE_SCALE * config.font_size->value,
213 FONT_SIZE_SCALE * config.font_size->value);
214 hb_ot_font_set_funcs(hb_font);
215
216 SkDocument::PDFMetadata pdf_info;
217 pdf_info.fTitle = config.title->value;
218 pdf_info.fAuthor = config.author->value;
219 pdf_info.fSubject = config.subject->value;
220 pdf_info.fKeywords = config.keywords->value;
221 pdf_info.fCreator = config.creator->value;
222 SkTime::DateTime now;
223 SkTime::GetDateTime(&now);
224 pdf_info.fCreation.fEnabled = true;
225 pdf_info.fCreation.fDateTime = now;
226 pdf_info.fModified.fEnabled = true;
227 pdf_info.fModified.fDateTime = now;
228 pdfDocument = SkDocument::MakePDF(outputStream, SK_ScalarDefaultRasterDPI,
229 pdf_info, nullptr, true);
230 assert(pdfDocument);
231
232 white_paint.setColor(SK_ColorWHITE);
233
234 glyph_paint.setFlags(
235 SkPaint::kAntiAlias_Flag |
236 SkPaint::kSubpixelText_Flag); // ... avoid waggly text when rotating.
237 glyph_paint.setColor(SK_ColorBLACK);
238 glyph_paint.setTextSize(config.font_size->value);
239 glyph_paint.setTypeface(face->fSkiaTypeface);
240 glyph_paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
241
242 NewPage();
243 } // end of Placement
244
245 ~Placement() {
246 delete face;
247 hb_font_destroy (hb_font);
248 }
249
250 void WriteLine(const char *text) {
251 /* Create hb-buffer and populate. */
252 hb_buffer_t *hb_buffer = hb_buffer_create ();
253 hb_buffer_add_utf8 (hb_buffer, text, -1, 0, -1);
254 hb_buffer_guess_segment_properties (hb_buffer);
255
256 /* Shape it! */
257 hb_shape (hb_font, hb_buffer, NULL, 0);
258
259 DrawGlyphs(hb_buffer);
260
261 hb_buffer_destroy (hb_buffer);
262
263 // Advance to the next line.
264 current_y += config.line_spacing_ratio->value * config.font_size->value;
265 if (current_y > config.page_height->value) {
266 pdfDocument->endPage();
267 NewPage();
268 }
269 }
270
271 bool Close() {
272 return pdfDocument->close();
273 }
274
275 private:
276 Config config;
277
278 Face *face;
279
280 hb_font_t *hb_font;
281
282 sk_sp<SkDocument> pdfDocument;
283
284 SkCanvas* pageCanvas;
285
286 SkPaint white_paint;
287 SkPaint glyph_paint;
288
289 double current_x;
290 double current_y;
291
292 void NewPage() {
293 pageCanvas = pdfDocument->beginPage(config.page_width->value, config.page_he ight->value);
294
295 pageCanvas->drawPaint(white_paint);
296
297 current_x = config.left_margin->value;
298 current_y = config.line_spacing_ratio->value * config.font_size->value;
299 }
300
301 bool DrawGlyphs(hb_buffer_t *hb_buffer) {
302 SkTextBlobBuilder textBlobBuilder;
303 unsigned len = hb_buffer_get_length (hb_buffer);
304 if (len == 0) {
305 return true;
306 }
307 hb_glyph_info_t *info = hb_buffer_get_glyph_infos (hb_buffer, NULL);
308 hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (hb_buffer, NULL);
309 auto runBuffer = textBlobBuilder.allocRunPos(glyph_paint, len);
310
311 double x = 0;
312 double y = 0;
313 for (unsigned int i = 0; i < len; i++)
314 {
315 runBuffer.glyphs[i] = info[i].codepoint;
316 reinterpret_cast<SkPoint*>(runBuffer.pos)[i] = SkPoint::Make(
317 x + pos[i].x_offset / FONT_SIZE_SCALE,
318 y - pos[i].y_offset / FONT_SIZE_SCALE);
319 x += pos[i].x_advance / FONT_SIZE_SCALE;
320 y += pos[i].y_advance / FONT_SIZE_SCALE;
321 }
322
323 pageCanvas->drawTextBlob(textBlobBuilder.build(), current_x, current_y, glyp h_paint);
324 return true;
325 } // end of DrawGlyphs
326 }; // end of Placement class
327
328 int main(int argc, char** argv) {
329 Config config(argc, argv);
330
331 Placement placement(config, new SkFILEWStream(config.output_file_name->value .c_str()));
332 for (std::string line; std::getline(std::cin, line);) { 208 for (std::string line; std::getline(std::cin, line);) {
333 placement.WriteLine(line.c_str()); 209 placement.WriteLine(shaper, line.c_str(), line.size());
334 } 210 }
335 placement.Close(); 211
336 212 doc->close();
337 return 0; 213 return 0;
338 } 214 }
OLDNEW
« no previous file with comments | « tools/SkShaper.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698