blob: a4e3e4975e5d44cb1a4adc1cb3b3370be9205fd8 [file] [log] [blame]
Ben Wagnerc912d612018-02-15 10:20:04 -05001/*
2 * Copyright 2014 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#include "Resources.h"
9#include "SkAdvancedTypefaceMetrics.h"
10#include "SkBitmap.h"
11#include "SkCanvas.h"
12#include "SkColor.h"
13#include "SkData.h"
14#include "SkEncodedImageFormat.h"
15#include "SkFontDescriptor.h"
16#include "SkFontStyle.h"
17#include "SkGeometry.h"
18#include "SkGlyph.h"
19#include "SkImage.h"
20#include "SkImageInfo.h"
21#include "SkMask.h"
22#include "SkMatrix.h"
23#include "SkNoDrawCanvas.h"
24#include "SkOTUtils.h"
25#include "SkPaintPriv.h"
26#include "SkPath.h"
27#include "SkPathPriv.h"
28#include "SkPathEffect.h"
29#include "SkPathOps.h"
30#include "SkPixmap.h"
31#include "SkPointPriv.h"
32#include "SkRRect.h"
33#include "SkSVGDOM.h"
34#include "SkScalerContext.h"
35#include "SkSize.h"
36#include "SkStream.h"
37#include "SkSurface.h"
38#include "SkTestSVGTypeface.h"
39#include "SkTDArray.h"
40#include "SkTemplates.h"
41#include "SkUtils.h"
42
43#include <utility>
44
45class SkDescriptor;
46
47SkTestSVGTypeface::SkTestSVGTypeface(const char* name,
48 int upem,
49 const SkPaint::FontMetrics& fontMetrics,
50 const SkSVGTestTypefaceGlyphData* data, int dataCount,
51 const SkFontStyle& style)
52 : SkTypeface(style, false)
53 , fName(name)
54 , fUpem(upem)
55 , fFontMetrics(fontMetrics)
56 , fGlyphs(dataCount)
57{
58 for (int i = 0; i < dataCount; ++i) {
59 const SkSVGTestTypefaceGlyphData& datum = data[i];
60 std::unique_ptr<SkStreamAsset> stream = GetResourceAsStream(datum.fSvgResourcePath);
61 fCMap.set(datum.fUnicode, i);
62 if (!stream) {
63 fGlyphs.emplace_back(nullptr, datum);
64 continue;
65 }
66 sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromStream(*stream.get());
67 if (!svg) {
68 fGlyphs.emplace_back(nullptr, datum);
69 continue;
70 }
71
72 const SkSize& sz = svg->containerSize();
73 if (sz.isEmpty()) {
74 fGlyphs.emplace_back(nullptr, datum);
75 continue;
76 }
77
78 fGlyphs.emplace_back(std::move(svg), datum);
79 }
80}
81
82SkTestSVGTypeface::~SkTestSVGTypeface() {}
83
84SkTestSVGTypeface::Glyph::Glyph(sk_sp<SkSVGDOM> svg, const SkSVGTestTypefaceGlyphData& data)
85 : fSvg(std::move(svg)), fOrigin(data.fOrigin), fAdvance(data.fAdvance) {}
86SkTestSVGTypeface::Glyph::~Glyph() {}
87
88void SkTestSVGTypeface::getAdvance(SkGlyph* glyph) const {
89 SkGlyphID glyphID = glyph->getGlyphID();
90 glyphID = glyphID < fGlyphs.count() ? glyphID : 0;
91
92 glyph->fAdvanceX = fGlyphs[glyphID].fAdvance;
93 glyph->fAdvanceY = 0;
94}
95
96void SkTestSVGTypeface::getFontMetrics(SkPaint::FontMetrics* metrics) const {
97 *metrics = fFontMetrics;
98}
99
100void SkTestSVGTypeface::getPath(SkGlyphID glyphID, SkPath* path) const {
101 path->reset();
102}
103
104void SkTestSVGTypeface::onFilterRec(SkScalerContextRec* rec) const {
105 rec->setHinting(SkPaint::kNo_Hinting);
106}
107
108std::unique_ptr<SkAdvancedTypefaceMetrics> SkTestSVGTypeface::onGetAdvancedMetrics() const {
109 std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
110 info->fFontName.set(fName);
111 int glyphCount = this->onCountGlyphs();
112
113 SkTDArray<SkUnichar>& toUnicode = info->fGlyphToUnicode;
114 toUnicode.setCount(glyphCount);
115 SkASSERT(glyphCount == SkToInt(fGlyphs.count()));
116 fCMap.foreach([&toUnicode](const SkUnichar& c, const SkGlyphID& g) {
117 toUnicode[g] = c;
118 });
119 return info;
120}
121
122void SkTestSVGTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const {
123 desc->setFamilyName(fName.c_str());
124 desc->setStyle(this->fontStyle());
125 *isLocal = false;
126}
127
128int SkTestSVGTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
129 uint16_t glyphs[], int glyphCount) const {
130 auto utf8 = (const char*)chars;
131 auto utf16 = (const uint16_t*)chars;
132 auto utf32 = (const SkUnichar*)chars;
133
134 for (int i = 0; i < glyphCount; i++) {
135 SkUnichar ch;
136 switch (encoding) {
137 case kUTF8_Encoding: ch = SkUTF8_NextUnichar(&utf8 ); break;
138 case kUTF16_Encoding: ch = SkUTF16_NextUnichar(&utf16); break;
139 case kUTF32_Encoding: ch = *utf32++; break;
140 }
141 if (glyphs) {
142 SkGlyphID* g = fCMap.find(ch);
143 glyphs[i] = g ? *g : 0;
144 }
145 }
146 return glyphCount;
147}
148
149void SkTestSVGTypeface::onGetFamilyName(SkString* familyName) const {
150 *familyName = fName;
151}
152
153SkTypeface::LocalizedStrings* SkTestSVGTypeface::onCreateFamilyNameIterator() const {
154 SkString familyName(fName);
155 SkString language("und"); //undetermined
156 return new SkOTUtils::LocalizedStrings_SingleName(familyName, language);
157}
158
159class SkTestSVGScalerContext : public SkScalerContext {
160public:
161 SkTestSVGScalerContext(sk_sp<SkTestSVGTypeface> face, const SkScalerContextEffects& effects,
162 const SkDescriptor* desc)
163 : SkScalerContext(std::move(face), effects, desc)
164 {
165 fRec.getSingleMatrix(&fMatrix);
166 SkScalar upem = this->geTestSVGTypeface()->fUpem;
167 fMatrix.preScale(1.f/upem, 1.f/upem);
168 }
169
170protected:
171 SkTestSVGTypeface* geTestSVGTypeface() const {
172 return static_cast<SkTestSVGTypeface*>(this->getTypeface());
173 }
174
175 unsigned generateGlyphCount() override {
176 return this->geTestSVGTypeface()->onCountGlyphs();
177 }
178
179 uint16_t generateCharToGlyph(SkUnichar u) override {
180 uint16_t g;
181 (void) this->geTestSVGTypeface()->onCharsToGlyphs(&u, SkTypeface::kUTF32_Encoding, &g, 1);
182 return g;
183 }
184
185 void generateAdvance(SkGlyph* glyph) override {
186 this->geTestSVGTypeface()->getAdvance(glyph);
187
188 const SkVector advance = fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX),
189 SkFloatToScalar(glyph->fAdvanceY));
190 glyph->fAdvanceX = SkScalarToFloat(advance.fX);
191 glyph->fAdvanceY = SkScalarToFloat(advance.fY);
192 }
193
194 void generateMetrics(SkGlyph* glyph) override {
195 SkGlyphID glyphID = glyph->getGlyphID();
196 glyphID = glyphID < this->geTestSVGTypeface()->fGlyphs.count() ? glyphID : 0;
197
198 glyph->zeroMetrics();
199 glyph->fMaskFormat = SkMask::kARGB32_Format;
200 this->generateAdvance(glyph);
201
202 SkTestSVGTypeface::Glyph& glyphData = this->geTestSVGTypeface()->fGlyphs[glyphID];
203 if (!glyphData.fSvg) {
204 return;
205 }
206
207 SkSize containerSize = glyphData.fSvg->containerSize();
208 SkRect newBounds = SkRect::MakeXYWH(glyphData.fOrigin.fX, -glyphData.fOrigin.fY,
209 containerSize.fWidth, containerSize.fHeight);
210 fMatrix.mapRect(&newBounds);
211 SkScalar dx = SkFixedToScalar(glyph->getSubXFixed());
212 SkScalar dy = SkFixedToScalar(glyph->getSubYFixed());
213 newBounds.offset(dx, dy);
214
215 SkIRect ibounds;
216 newBounds.roundOut(&ibounds);
217 glyph->fLeft = ibounds.fLeft;
218 glyph->fTop = ibounds.fTop;
219 glyph->fWidth = ibounds.width();
220 glyph->fHeight = ibounds.height();
221 }
222
223 void generateImage(const SkGlyph& glyph) override {
224 SkGlyphID glyphID = glyph.getGlyphID();
225 glyphID = glyphID < this->geTestSVGTypeface()->fGlyphs.count() ? glyphID : 0;
226
227 SkBitmap bm;
228 // TODO: this should be SkImageInfo::MakeS32 when that passes all the tests.
229 bm.installPixels(SkImageInfo::MakeN32(glyph.fWidth, glyph.fHeight, kPremul_SkAlphaType),
230 glyph.fImage, glyph.rowBytes());
231 bm.eraseColor(0);
232
233 SkTestSVGTypeface::Glyph& glyphData = this->geTestSVGTypeface()->fGlyphs[glyphID];
234
235 SkScalar dx = SkFixedToScalar(glyph.getSubXFixed());
236 SkScalar dy = SkFixedToScalar(glyph.getSubYFixed());
237
238 SkCanvas canvas(bm);
239 canvas.translate(-glyph.fLeft, -glyph.fTop);
240 canvas.translate(dx, dy);
241 canvas.concat(fMatrix);
242 canvas.translate(glyphData.fOrigin.fX, -glyphData.fOrigin.fY);
243
244 if (glyphData.fSvg) {
245 glyphData.fSvg->render(&canvas);
246 }
247 }
248
249 void generatePath(SkGlyphID glyph, SkPath* path) override {
250 this->geTestSVGTypeface()->getPath(glyph, path);
251 path->transform(fMatrix);
252 }
253
254 void generateFontMetrics(SkPaint::FontMetrics* metrics) override {
255 this->geTestSVGTypeface()->getFontMetrics(metrics);
256 SkPaintPriv::ScaleFontMetrics(metrics, fMatrix.getScaleY());
257 }
258
259private:
260 SkMatrix fMatrix;
261};
262
263SkScalerContext* SkTestSVGTypeface::onCreateScalerContext(
264 const SkScalerContextEffects& e, const SkDescriptor* desc) const
265{
266 return new SkTestSVGScalerContext(sk_ref_sp(const_cast<SkTestSVGTypeface*>(this)), e, desc);
267}
268
269// Recommended that the first four be .notdef, .null, CR, space
270constexpr const static SkSVGTestTypefaceGlyphData gGlyphs[] = {
271 {"fonts/svg/notdef.svg", {100,800}, 800, 0x0}, // .notdef
272 {"fonts/svg/empty.svg", {0,0}, 800, 0x0020}, // space
273 {"fonts/svg/diamond.svg", {100, 800}, 800, 0x2662}, // ♢
274 {"fonts/svg/smile.svg", {0,800}, 800, 0x1F600}, // 😀
275};
276
277sk_sp<SkTestSVGTypeface> SkTestSVGTypeface::Default() {
278 SkPaint::FontMetrics metrics;
279 metrics.fFlags = SkPaint::FontMetrics::kUnderlineThicknessIsValid_Flag |
280 SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag |
281 SkPaint::FontMetrics::kStrikeoutThicknessIsValid_Flag |
282 SkPaint::FontMetrics::kStrikeoutPositionIsValid_Flag;
283 metrics.fTop = -800;
284 metrics.fAscent = -800;
285 metrics.fDescent = 200;
286 metrics.fBottom = 200;
287 metrics.fLeading = 100;
288 metrics.fAvgCharWidth = 1000;
289 metrics.fMaxCharWidth = 1000;
290 metrics.fXMin = 0;
291 metrics.fXMax = 1000;
292 metrics.fXHeight = 500;
293 metrics.fCapHeight = 700;
294 metrics.fUnderlineThickness = 40;
295 metrics.fUnderlinePosition = 20;
296 metrics.fStrikeoutThickness = 20;
297 metrics.fStrikeoutPosition = -400;
298 return sk_make_sp<SkTestSVGTypeface>("Emoji", 1000, metrics, gGlyphs, SK_ARRAY_COUNT(gGlyphs),
299 SkFontStyle::Normal());
300}
301
302void SkTestSVGTypeface::exportTtxCommon(SkWStream* out, const char* type,
303 const SkTArray<GlyfInfo>* glyfInfo) const
304{
305 int totalGlyphs = fGlyphs.count();
306 out->writeText(" <GlyphOrder>\n");
307 for (int i = 0; i < fGlyphs.count(); ++i) {
308 out->writeText(" <GlyphID name=\"glyf");
309 out->writeHexAsText(i, 4);
310 out->writeText("\"/>\n");
311 }
312 if (glyfInfo) {
313 for (int i = 0; i < fGlyphs.count(); ++i) {
314 for (int j = 0; j < (*glyfInfo)[i].fLayers.count(); ++j) {
315 out->writeText(" <GlyphID name=\"glyf");
316 out->writeHexAsText(i, 4);
317 out->writeText("l");
318 out->writeHexAsText(j, 4);
319 out->writeText("\"/>\n");
320 ++totalGlyphs;
321 }
322 }
323 }
324 out->writeText(" </GlyphOrder>\n");
325
326 out->writeText(" <head>\n");
327 out->writeText(" <tableVersion value=\"1.0\"/>\n");
328 out->writeText(" <fontRevision value=\"1.0\"/>\n");
329 out->writeText(" <checkSumAdjustment value=\"0xa9c3274\"/>\n");
330 out->writeText(" <magicNumber value=\"0x5f0f3cf5\"/>\n");
331 out->writeText(" <flags value=\"00000000 00011011\"/>\n");
332 out->writeText(" <unitsPerEm value=\"");
333 out->writeDecAsText(fUpem);
334 out->writeText("\"/>\n");
335 out->writeText(" <created value=\"Thu Feb 15 12:55:49 2018\"/>\n");
336 out->writeText(" <modified value=\"Thu Feb 15 12:55:49 2018\"/>\n");
337 // TODO: not recalculated for bitmap fonts?
338 out->writeText(" <xMin value=\"");
339 out->writeScalarAsText(fFontMetrics.fXMin);
340 out->writeText("\"/>\n");
341 out->writeText(" <yMin value=\"");
342 out->writeScalarAsText(-fFontMetrics.fBottom);
343 out->writeText("\"/>\n");
344 out->writeText(" <xMax value=\"");
345 out->writeScalarAsText(fFontMetrics.fXMax);
346 out->writeText("\"/>\n");
347 out->writeText(" <yMax value=\"");
348 out->writeScalarAsText(-fFontMetrics.fTop);
349 out->writeText("\"/>\n");
350
351 char macStyle[16] = {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
352 if (this->fontStyle().weight() >= SkFontStyle::Bold().weight()) {
353 macStyle[0xF - 0x0] = '1'; // Bold
354 }
355 switch (this->fontStyle().slant()) {
356 case SkFontStyle::kUpright_Slant:
357 break;
358 case SkFontStyle::kItalic_Slant:
359 macStyle[0xF - 0x1] = '1'; // Italic
360 break;
361 case SkFontStyle::kOblique_Slant:
362 macStyle[0xF - 0x1] = '1'; // Italic
363 break;
364 default:
365 SK_ABORT("Unknown slant.");
366 }
367 if (this->fontStyle().width() <= SkFontStyle::kCondensed_Width) {
368 macStyle[0xF - 0x5] = '1'; // Condensed
369 } else if (this->fontStyle().width() >= SkFontStyle::kExpanded_Width) {
370 macStyle[0xF - 0x6] = '1'; // Extended
371 }
372 out->writeText(" <macStyle value=\"");
373 out->write(macStyle, 8);
374 out->writeText(" ");
375 out->write(macStyle + 8, 8);
376 out->writeText("\"/>\n");
377 out->writeText(" <lowestRecPPEM value=\"8\"/>\n");
378 out->writeText(" <fontDirectionHint value=\"2\"/>\n");
379 out->writeText(" <indexToLocFormat value=\"0\"/>\n");
380 out->writeText(" <glyphDataFormat value=\"0\"/>\n");
381 out->writeText(" </head>\n");
382
383 out->writeText(" <hhea>\n");
384 out->writeText(" <tableVersion value=\"0x00010000\"/>\n");
385 out->writeText(" <ascent value=\"");
386 out->writeDecAsText(-fFontMetrics.fAscent);
387 out->writeText("\"/>\n");
388 out->writeText(" <descent value=\"");
389 out->writeDecAsText(-fFontMetrics.fDescent);
390 out->writeText("\"/>\n");
391 out->writeText(" <lineGap value=\"");
392 out->writeDecAsText(fFontMetrics.fLeading);
393 out->writeText("\"/>\n");
394 out->writeText(" <advanceWidthMax value=\"0\"/>\n");
395 out->writeText(" <minLeftSideBearing value=\"0\"/>\n");
396 out->writeText(" <minRightSideBearing value=\"0\"/>\n");
397 out->writeText(" <xMaxExtent value=\"");
398 out->writeScalarAsText(fFontMetrics.fXMax - fFontMetrics.fXMin);
399 out->writeText("\"/>\n");
400 out->writeText(" <caretSlopeRise value=\"1\"/>\n");
401 out->writeText(" <caretSlopeRun value=\"0\"/>\n");
402 out->writeText(" <caretOffset value=\"0\"/>\n");
403 out->writeText(" <reserved0 value=\"0\"/>\n");
404 out->writeText(" <reserved1 value=\"0\"/>\n");
405 out->writeText(" <reserved2 value=\"0\"/>\n");
406 out->writeText(" <reserved3 value=\"0\"/>\n");
407 out->writeText(" <metricDataFormat value=\"0\"/>\n");
408 out->writeText(" <numberOfHMetrics value=\"0\"/>\n");
409 out->writeText(" </hhea>\n");
410
411 // Some of this table is going to be re-calculated, but we have to write it out anyway.
412 out->writeText(" <maxp>\n");
413 out->writeText(" <tableVersion value=\"0x10000\"/>\n");
414 out->writeText(" <numGlyphs value=\"");
415 out->writeDecAsText(totalGlyphs);
416 out->writeText("\"/>\n");
417 out->writeText(" <maxPoints value=\"4\"/>\n");
418 out->writeText(" <maxContours value=\"1\"/>\n");
419 out->writeText(" <maxCompositePoints value=\"0\"/>\n");
420 out->writeText(" <maxCompositeContours value=\"0\"/>\n");
421 out->writeText(" <maxZones value=\"1\"/>\n");
422 out->writeText(" <maxTwilightPoints value=\"0\"/>\n");
423 out->writeText(" <maxStorage value=\"0\"/>\n");
424 out->writeText(" <maxFunctionDefs value=\"10\"/>\n");
425 out->writeText(" <maxInstructionDefs value=\"0\"/>\n");
426 out->writeText(" <maxStackElements value=\"512\"/>\n");
427 out->writeText(" <maxSizeOfInstructions value=\"24\"/>\n");
428 out->writeText(" <maxComponentElements value=\"0\"/>\n");
429 out->writeText(" <maxComponentDepth value=\"0\"/>\n");
430 out->writeText(" </maxp>\n");
431
432 out->writeText(" <OS_2>\n");
433 out->writeText(" <version value=\"4\"/>\n");
434 out->writeText(" <xAvgCharWidth value=\"");
435 out->writeScalarAsText(fFontMetrics.fAvgCharWidth);
436 out->writeText("\"/>\n");
437 out->writeText(" <usWeightClass value=\"");
438 out->writeDecAsText(this->fontStyle().weight());
439 out->writeText("\"/>\n");
440 out->writeText(" <usWidthClass value=\"");
441 out->writeDecAsText(this->fontStyle().width());
442 out->writeText("\"/>\n");
443 out->writeText(" <fsType value=\"00000000 00000000\"/>\n");
444 out->writeText(" <ySubscriptXSize value=\"665\"/>\n");
445 out->writeText(" <ySubscriptYSize value=\"716\"/>\n");
446 out->writeText(" <ySubscriptXOffset value=\"0\"/>\n");
447 out->writeText(" <ySubscriptYOffset value=\"143\"/>\n");
448 out->writeText(" <ySuperscriptXSize value=\"665\"/>\n");
449 out->writeText(" <ySuperscriptYSize value=\"716\"/>\n");
450 out->writeText(" <ySuperscriptXOffset value=\"0\"/>\n");
451 out->writeText(" <ySuperscriptYOffset value=\"491\"/>\n");
452 out->writeText(" <yStrikeoutSize value=\"");
453 out->writeScalarAsText(fFontMetrics.fStrikeoutThickness);
454 out->writeText("\"/>\n");
455 out->writeText(" <yStrikeoutPosition value=\"");
456 out->writeScalarAsText(-fFontMetrics.fStrikeoutPosition);
457 out->writeText("\"/>\n");
458 out->writeText(" <sFamilyClass value=\"0\"/>\n");
459 out->writeText(" <panose>\n");
460 out->writeText(" <bFamilyType value=\"0\"/>\n");
461 out->writeText(" <bSerifStyle value=\"0\"/>\n");
462 out->writeText(" <bWeight value=\"0\"/>\n");
463 out->writeText(" <bProportion value=\"0\"/>\n");
464 out->writeText(" <bContrast value=\"0\"/>\n");
465 out->writeText(" <bStrokeVariation value=\"0\"/>\n");
466 out->writeText(" <bArmStyle value=\"0\"/>\n");
467 out->writeText(" <bLetterForm value=\"0\"/>\n");
468 out->writeText(" <bMidline value=\"0\"/>\n");
469 out->writeText(" <bXHeight value=\"0\"/>\n");
470 out->writeText(" </panose>\n");
471 out->writeText(" <ulUnicodeRange1 value=\"00000000 00000000 00000000 00000001\"/>\n");
472 out->writeText(" <ulUnicodeRange2 value=\"00010000 00000000 00000000 00000000\"/>\n");
473 out->writeText(" <ulUnicodeRange3 value=\"00000000 00000000 00000000 00000000\"/>\n");
474 out->writeText(" <ulUnicodeRange4 value=\"00000000 00000000 00000000 00000000\"/>\n");
475 out->writeText(" <achVendID value=\"Skia\"/>\n");
476 char fsSelection[16] = {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
477 fsSelection[0xF - 0x7] = '1'; // Use typo metrics
478 if (this->fontStyle().weight() >= SkFontStyle::Bold().weight()) {
479 fsSelection[0xF - 0x5] = '1'; // Bold
480 }
481 switch (this->fontStyle().slant()) {
482 case SkFontStyle::kUpright_Slant:
483 if (this->fontStyle().weight() < SkFontStyle::Bold().weight()) {
484 fsSelection[0xF - 0x6] = '1'; // Not bold or italic, is regular
485 }
486 break;
487 case SkFontStyle::kItalic_Slant:
488 fsSelection[0xF - 0x0] = '1'; // Italic
489 break;
490 case SkFontStyle::kOblique_Slant:
491 fsSelection[0xF - 0x0] = '1'; // Italic
492 fsSelection[0xF - 0x9] = '1'; // Oblique
493 break;
494 default:
495 SK_ABORT("Unknown slant.");
496 }
497 out->writeText(" <fsSelection value=\"");
498 out->write(fsSelection, 8);
499 out->writeText(" ");
500 out->write(fsSelection + 8, 8);
501 out->writeText("\"/>\n");
502 out->writeText(" <usFirstCharIndex value=\"0\"/>\n");
503 out->writeText(" <usLastCharIndex value=\"0\"/>\n");
504 out->writeText(" <sTypoAscender value=\"");
505 out->writeScalarAsText(-fFontMetrics.fAscent);
506 out->writeText("\"/>\n");
507 out->writeText(" <sTypoDescender value=\"");
508 out->writeScalarAsText(-fFontMetrics.fDescent);
509 out->writeText("\"/>\n");
510 out->writeText(" <sTypoLineGap value=\"");
511 out->writeScalarAsText(fFontMetrics.fLeading);
512 out->writeText("\"/>\n");
513 out->writeText(" <usWinAscent value=\"");
514 out->writeScalarAsText(-fFontMetrics.fAscent);
515 out->writeText("\"/>\n");
516 out->writeText(" <usWinDescent value=\"");
517 out->writeScalarAsText(fFontMetrics.fDescent);
518 out->writeText("\"/>\n");
519 out->writeText(" <ulCodePageRange1 value=\"00000000 00000000 00000000 00000000\"/>\n");
520 out->writeText(" <ulCodePageRange2 value=\"00000000 00000000 00000000 00000000\"/>\n");
521 out->writeText(" <sxHeight value=\"");
522 out->writeScalarAsText(fFontMetrics.fXHeight);
523 out->writeText("\"/>\n");
524 out->writeText(" <sCapHeight value=\"");
525 out->writeScalarAsText(fFontMetrics.fCapHeight);
526 out->writeText("\"/>\n");
527 out->writeText(" <usDefaultChar value=\"0\"/>\n");
528 out->writeText(" <usBreakChar value=\"32\"/>\n");
529 out->writeText(" <usMaxContext value=\"0\"/>\n");
530 out->writeText(" </OS_2>\n");
531
532 out->writeText(" <hmtx>\n");
533 for (int i = 0; i < fGlyphs.count(); ++i) {
534 out->writeText(" <mtx name=\"glyf");
535 out->writeHexAsText(i, 4);
536 out->writeText("\" width=\"");
537 out->writeDecAsText(fGlyphs[i].fAdvance);
538 out->writeText("\" lsb=\"");
539 int lsb = fGlyphs[i].fOrigin.fX;
540 if (glyfInfo) {
541 lsb += (*glyfInfo)[i].fBounds.fLeft;
542 }
543 out->writeDecAsText(lsb);
544 out->writeText("\"/>\n");
545 }
546 if (glyfInfo) {
547 for (int i = 0; i < fGlyphs.count(); ++i) {
548 for (int j = 0; j < (*glyfInfo)[i].fLayers.count(); ++j) {
549 out->writeText(" <mtx name=\"glyf");
550 out->writeHexAsText(i, 4);
551 out->writeText("l");
552 out->writeHexAsText(j, 4);
553 out->writeText("\" width=\"");
554 out->writeDecAsText(fGlyphs[i].fAdvance);
555 out->writeText("\" lsb=\"");
556 int32_t lsb = fGlyphs[i].fOrigin.fX + (*glyfInfo)[i].fLayers[j].fBounds.fLeft;
557 out->writeDecAsText(lsb);
558 out->writeText("\"/>\n");
559 }
560 }
561 }
562 out->writeText(" </hmtx>\n");
563
564 bool hasNonBMP = false;
565 out->writeText(" <cmap>\n");
566 out->writeText(" <tableVersion version=\"0\"/>\n");
567 out->writeText(" <cmap_format_4 platformID=\"3\" platEncID=\"1\" language=\"0\">\n");
568 fCMap.foreach([&out, &hasNonBMP](const SkUnichar& c, const SkGlyphID& g) {
569 if (0xFFFF < c) {
570 hasNonBMP = true;
571 return;
572 }
573 out->writeText(" <map code=\"0x");
574 out->writeHexAsText(c, 4);
575 out->writeText("\" name=\"glyf");
576 out->writeHexAsText(g, 4);
577 out->writeText("\"/>\n");
578 });
579 out->writeText(" </cmap_format_4>\n");
580 if (hasNonBMP) {
581 out->writeText(" <cmap_format_12 platformID=\"3\" platEncID=\"10\" format=\"12\" reserved=\"0\" length=\"1\" language=\"0\" nGroups=\"0\">\n");
582 fCMap.foreach([&out](const SkUnichar& c, const SkGlyphID& g) {
583 out->writeText(" <map code=\"0x");
584 out->writeHexAsText(c, 6);
585 out->writeText("\" name=\"glyf");
586 out->writeHexAsText(g, 4);
587 out->writeText("\"/>\n");
588 });
589 out->writeText(" </cmap_format_12>\n");
590 }
591 out->writeText(" </cmap>\n");
592
593 out->writeText(" <name>\n");
594 out->writeText(" <namerecord nameID=\"1\" platformID=\"3\" platEncID=\"1\" langID=\"0x409\">\n");
595 out->writeText(" ");
596 out->writeText(fName.c_str());
597 out->writeText(" ");
598 out->writeText(type);
599 out->writeText("\n");
600 out->writeText(" </namerecord>\n");
601 out->writeText(" <namerecord nameID=\"2\" platformID=\"3\" platEncID=\"1\" langID=\"0x409\">\n");
602 out->writeText(" Regular\n");
603 out->writeText(" </namerecord>\n");
604 out->writeText(" </name>\n");
605
606 out->writeText(" <post>\n");
607 out->writeText(" <formatType value=\"3.0\"/>\n");
608 out->writeText(" <italicAngle value=\"0.0\"/>\n");
609 out->writeText(" <underlinePosition value=\"");
610 out->writeScalarAsText(fFontMetrics.fUnderlinePosition);
611 out->writeText("\"/>\n");
612 out->writeText(" <underlineThickness value=\"");
613 out->writeScalarAsText(fFontMetrics.fUnderlineThickness);
614 out->writeText("\"/>\n");
615 out->writeText(" <isFixedPitch value=\"0\"/>\n");
616 out->writeText(" <minMemType42 value=\"0\"/>\n");
617 out->writeText(" <maxMemType42 value=\"0\"/>\n");
618 out->writeText(" <minMemType1 value=\"0\"/>\n");
619 out->writeText(" <maxMemType1 value=\"0\"/>\n");
620 out->writeText(" </post>\n");
621}
622
623void SkTestSVGTypeface::exportTtxCbdt(SkWStream* out) const {
624 out->writeText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
625 out->writeText("<ttFont sfntVersion=\"\\x00\\x01\\x00\\x00\" ttLibVersion=\"3.19\">\n");
626 this->exportTtxCommon(out, "CBDT");
627
628 int strikeSizes[3] = { 16, 64, 128 };
629
630 SkPaint paint;
631 paint.setTypeface(sk_ref_sp(const_cast<SkTestSVGTypeface*>(this)));
632 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
633
634 out->writeText(" <CBDT>\n");
635 out->writeText(" <header version=\"2.0\"/>\n");
636 for (size_t strikeIndex = 0; strikeIndex < SK_ARRAY_COUNT(strikeSizes); ++strikeIndex) {
637 paint.setTextSize(strikeSizes[strikeIndex]);
638 out->writeText(" <strikedata index=\"");
639 out->writeDecAsText(strikeIndex);
640 out->writeText("\">\n");
641 for (int i = 0; i < fGlyphs.count(); ++i) {
642 SkGlyphID gid = i;
643 SkScalar advance;
644 SkRect bounds;
645 paint.getTextWidths(&gid, sizeof(gid), &advance, &bounds);
646 SkIRect ibounds = bounds.roundOut();
647 if (ibounds.isEmpty()) {
648 continue;
649 }
650 SkImageInfo image_info = SkImageInfo::MakeN32Premul(ibounds.width(), ibounds.height());
651 sk_sp<SkSurface> surface(SkSurface::MakeRaster(image_info));
652 SkASSERT(surface);
653 SkCanvas* canvas = surface->getCanvas();
654 canvas->clear(0);
655 SkPixmap pix;
656 surface->peekPixels(&pix);
657 canvas->drawText(&gid, sizeof(gid), -bounds.fLeft, -bounds.fTop, paint);
658 canvas->flush();
659 sk_sp<SkImage> image = surface->makeImageSnapshot();
660 sk_sp<SkData> data = image->encodeToData(SkEncodedImageFormat::kPNG, 100);
661
662 out->writeText(" <cbdt_bitmap_format_17 name=\"glyf");
663 out->writeHexAsText(i, 4);
664 out->writeText("\">\n");
665 out->writeText(" <SmallGlyphMetrics>\n");
666 out->writeText(" <height value=\"");
667 out->writeDecAsText(image->height());
668 out->writeText("\"/>\n");
669 out->writeText(" <width value=\"");
670 out->writeDecAsText(image->width());
671 out->writeText("\"/>\n");
672 out->writeText(" <BearingX value=\"");
673 out->writeDecAsText(bounds.fLeft);
674 out->writeText("\"/>\n");
675 out->writeText(" <BearingY value=\"");
676 out->writeScalarAsText(-bounds.fTop);
677 out->writeText("\"/>\n");
678 out->writeText(" <Advance value=\"");
679 out->writeScalarAsText(advance);
680 out->writeText("\"/>\n");
681 out->writeText(" </SmallGlyphMetrics>\n");
682 out->writeText(" <rawimagedata>");
683 uint8_t const * bytes = data->bytes();
684 for (size_t i = 0; i < data->size(); ++i) {
685 if ((i % 0x10) == 0x0) {
686 out->writeText("\n ");
687 } else if (((i - 1) % 0x4) == 0x3) {
688 out->writeText(" ");
689 }
690 out->writeHexAsText(bytes[i], 2);
691 }
692 out->writeText("\n");
693 out->writeText(" </rawimagedata>\n");
694 out->writeText(" </cbdt_bitmap_format_17>\n");
695 }
696 out->writeText(" </strikedata>\n");
697 }
698 out->writeText(" </CBDT>\n");
699
700 SkPaint::FontMetrics fm;
701 out->writeText(" <CBLC>\n");
702 out->writeText(" <header version=\"2.0\"/>\n");
703 for (size_t strikeIndex = 0; strikeIndex < SK_ARRAY_COUNT(strikeSizes); ++strikeIndex) {
704 paint.setTextSize(strikeSizes[strikeIndex]);
705 paint.getFontMetrics(&fm);
706 out->writeText(" <strike index=\"");
707 out->writeDecAsText(strikeIndex);
708 out->writeText("\">\n");
709 out->writeText(" <bitmapSizeTable>\n");
710 out->writeText(" <sbitLineMetrics direction=\"hori\">\n");
711 out->writeText(" <ascender value=\"");
712 out->writeScalarAsText(-fm.fTop);
713 out->writeText("\"/>\n");
714 out->writeText(" <descender value=\"");
715 out->writeScalarAsText(-fm.fBottom);
716 out->writeText("\"/>\n");
717 out->writeText(" <widthMax value=\"");
718 out->writeScalarAsText(fm.fXMax - fm.fXMin);
719 out->writeText("\"/>\n");
720 out->writeText(" <caretSlopeNumerator value=\"0\"/>\n");
721 out->writeText(" <caretSlopeDenominator value=\"0\"/>\n");
722 out->writeText(" <caretOffset value=\"0\"/>\n");
723 out->writeText(" <minOriginSB value=\"0\"/>\n");
724 out->writeText(" <minAdvanceSB value=\"0\"/>\n");
725 out->writeText(" <maxBeforeBL value=\"0\"/>\n");
726 out->writeText(" <minAfterBL value=\"0\"/>\n");
727 out->writeText(" <pad1 value=\"0\"/>\n");
728 out->writeText(" <pad2 value=\"0\"/>\n");
729 out->writeText(" </sbitLineMetrics>\n");
730 out->writeText(" <sbitLineMetrics direction=\"vert\">\n");
731 out->writeText(" <ascender value=\"");
732 out->writeScalarAsText(-fm.fTop);
733 out->writeText("\"/>\n");
734 out->writeText(" <descender value=\"");
735 out->writeScalarAsText(-fm.fBottom);
736 out->writeText("\"/>\n");
737 out->writeText(" <widthMax value=\"");
738 out->writeScalarAsText(fm.fXMax - fm.fXMin);
739 out->writeText("\"/>\n");
740 out->writeText(" <caretSlopeNumerator value=\"0\"/>\n");
741 out->writeText(" <caretSlopeDenominator value=\"0\"/>\n");
742 out->writeText(" <caretOffset value=\"0\"/>\n");
743 out->writeText(" <minOriginSB value=\"0\"/>\n");
744 out->writeText(" <minAdvanceSB value=\"0\"/>\n");
745 out->writeText(" <maxBeforeBL value=\"0\"/>\n");
746 out->writeText(" <minAfterBL value=\"0\"/>\n");
747 out->writeText(" <pad1 value=\"0\"/>\n");
748 out->writeText(" <pad2 value=\"0\"/>\n");
749 out->writeText(" </sbitLineMetrics>\n");
750 out->writeText(" <colorRef value=\"0\"/>\n");
751 out->writeText(" <startGlyphIndex value=\"1\"/>\n");
752 out->writeText(" <endGlyphIndex value=\"1\"/>\n");
753 out->writeText(" <ppemX value=\"");
754 out->writeDecAsText(strikeSizes[strikeIndex]);
755 out->writeText("\"/>\n");
756 out->writeText(" <ppemY value=\"");
757 out->writeDecAsText(strikeSizes[strikeIndex]);
758 out->writeText("\"/>\n");
759 out->writeText(" <bitDepth value=\"32\"/>\n");
760 out->writeText(" <flags value=\"1\"/>\n");
761 out->writeText(" </bitmapSizeTable>\n");
762 out->writeText(" <eblc_index_sub_table_1 imageFormat=\"17\" firstGlyphIndex=\"1\" lastGlyphIndex=\"1\">\n");
763 for (int i = 0; i < fGlyphs.count(); ++i) {
764 SkGlyphID gid = i;
765 SkRect bounds;
766 paint.getTextWidths(&gid, sizeof(gid), nullptr, &bounds);
767 if (bounds.isEmpty()) {
768 continue;
769 }
770 out->writeText(" <glyphLoc name=\"glyf");
771 out->writeHexAsText(i, 4);
772 out->writeText("\"/>\n");
773 }
774 out->writeText(" </eblc_index_sub_table_1>\n");
775 out->writeText(" </strike>\n");
776 }
777 out->writeText(" </CBLC>\n");
778
779 out->writeText("</ttFont>\n");
780}
781
782/**
783 * UnitsPerEm is generally 1000 here. Versions of macOS older than 10.13
784 * have problems in CoreText determining the glyph bounds of bitmap glyphs
785 * with unitsPerEm set to 1024 or numbers not divisible by 100 when the
786 * contour is not closed. The bounds of sbix fonts on macOS appear to be those
787 * of the outline in the 'glyf' table. If this countour is closed it will be
788 * drawn, as the 'glyf' outline is to be drawn on top of any bitmap. (There is
789 * a bit which is supposed to control this, but it cannot be relied on.) So
790 * make the glyph contour ta degenerate line with points at the edge of the
791 * bounding box of the glyph.
792 */
793void SkTestSVGTypeface::exportTtxSbix(SkWStream* out) const {
794 out->writeText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
795 out->writeText("<ttFont sfntVersion=\"\\x00\\x01\\x00\\x00\" ttLibVersion=\"3.19\">\n");
796 this->exportTtxCommon(out, "sbix");
797
798 SkPaint paint;
799 paint.setTypeface(sk_ref_sp(const_cast<SkTestSVGTypeface*>(this)));
800 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
801
802 out->writeText(" <glyf>\n");
803 for (int i = 0; i < fGlyphs.count(); ++i) {
804 const SkTestSVGTypeface::Glyph& glyphData = this->fGlyphs[i];
805
806 SkSize containerSize = glyphData.fSvg ? glyphData.fSvg->containerSize()
807 : SkSize::MakeEmpty();
808 SkRect bounds = SkRect::MakeXYWH(glyphData.fOrigin.fX, -glyphData.fOrigin.fY,
809 containerSize.fWidth, containerSize.fHeight);
810 SkIRect ibounds = bounds.roundOut();
811 out->writeText(" <TTGlyph name=\"glyf");
812 out->writeHexAsText(i, 4);
813 out->writeText("\" xMin=\"");
814 out->writeDecAsText(ibounds.fLeft);
815 out->writeText("\" yMin=\"");
816 out->writeDecAsText(-ibounds.fBottom);
817 out->writeText("\" xMax=\"");
818 out->writeDecAsText(ibounds.fRight);
819 out->writeText("\" yMax=\"");
820 out->writeDecAsText(-ibounds.fTop);
821 out->writeText("\">\n");
822 out->writeText(" <contour>\n");
823 out->writeText(" <pt x=\"");
824 out->writeDecAsText(ibounds.fLeft);
825 out->writeText("\" y=\"");
826 out->writeDecAsText(-ibounds.fBottom);
827 out->writeText("\" on=\"1\"/>\n");
828 out->writeText(" </contour>\n");
829 out->writeText(" <contour>\n");
830 out->writeText(" <pt x=\"");
831 out->writeDecAsText(ibounds.fRight);
832 out->writeText("\" y=\"");
833 out->writeDecAsText(-ibounds.fTop);
834 out->writeText("\" on=\"1\"/>\n");
835 out->writeText(" </contour>\n");
836 out->writeText(" <instructions/>\n");
837 out->writeText(" </TTGlyph>\n");
838 }
839 out->writeText(" </glyf>\n");
840
841 // The loca table will be re-calculated, but if we don't write one we don't get one.
842 out->writeText(" <loca/>\n");
843
844 int strikeSizes[3] = { 16, 64, 128 };
845
846 out->writeText(" <sbix>\n");
847 out->writeText(" <version value=\"1\"/>\n");
848 out->writeText(" <flags value=\"00000000 00000001\"/>\n");
849 for (size_t strikeIndex = 0; strikeIndex < SK_ARRAY_COUNT(strikeSizes); ++strikeIndex) {
850 paint.setTextSize(strikeSizes[strikeIndex]);
851 out->writeText(" <strike>\n");
852 out->writeText(" <ppem value=\"");
853 out->writeDecAsText(strikeSizes[strikeIndex]);
854 out->writeText("\"/>\n");
855 out->writeText(" <resolution value=\"72\"/>\n");
856 for (int i = 0; i < fGlyphs.count(); ++i) {
857 SkGlyphID gid = i;
858 SkScalar advance;
859 SkRect bounds;
860 paint.getTextWidths(&gid, sizeof(gid), &advance, &bounds);
861 SkIRect ibounds = bounds.roundOut();
862 if (ibounds.isEmpty()) {
863 continue;
864 }
865 SkImageInfo image_info = SkImageInfo::MakeN32Premul(ibounds.width(), ibounds.height());
866 sk_sp<SkSurface> surface(SkSurface::MakeRaster(image_info));
867 SkASSERT(surface);
868 SkCanvas* canvas = surface->getCanvas();
869 canvas->clear(0);
870 SkPixmap pix;
871 surface->peekPixels(&pix);
872 canvas->drawText(&gid, sizeof(gid), -bounds.fLeft, -bounds.fTop, paint);
873 canvas->flush();
874 sk_sp<SkImage> image = surface->makeImageSnapshot();
875 sk_sp<SkData> data = image->encodeToData(SkEncodedImageFormat::kPNG, 100);
876
877 out->writeText(" <glyph name=\"glyf");
878 out->writeHexAsText(i, 4);
879 out->writeText("\" graphicType=\"png \" originOffsetX=\"");
880 out->writeDecAsText(bounds.fLeft);
881 out->writeText("\" originOffsetY=\"");
882 out->writeScalarAsText(bounds.fBottom);
883 out->writeText("\">\n");
884
885 out->writeText(" <hexdata>");
886 uint8_t const * bytes = data->bytes();
887 for (size_t i = 0; i < data->size(); ++i) {
888 if ((i % 0x10) == 0x0) {
889 out->writeText("\n ");
890 } else if (((i - 1) % 0x4) == 0x3) {
891 out->writeText(" ");
892 }
893 out->writeHexAsText(bytes[i], 2);
894 }
895 out->writeText("\n");
896 out->writeText(" </hexdata>\n");
897 out->writeText(" </glyph>\n");
898 }
899 out->writeText(" </strike>\n");
900 }
901 out->writeText(" </sbix>\n");
902 out->writeText("</ttFont>\n");
903}
904
905namespace {
906
907void convert_noninflect_cubic_to_quads(const SkPoint p[4],
908 SkScalar toleranceSqd,
909 SkTArray<SkPoint, true>* quads,
910 int sublevel = 0)
911{
912 // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is
913 // p[2]. Point d is always p[3]. Point c is p[2] unless p[2] == p[3], in which case it is p[1].
914
915 SkVector ab = p[1] - p[0];
916 SkVector dc = p[2] - p[3];
917
918 if (SkPointPriv::LengthSqd(ab) < SK_ScalarNearlyZero) {
919 if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
920 SkPoint* degQuad = quads->push_back_n(3);
921 degQuad[0] = p[0];
922 degQuad[1] = p[0];
923 degQuad[2] = p[3];
924 return;
925 }
926 ab = p[2] - p[0];
927 }
928 if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
929 dc = p[1] - p[3];
930 }
931
932 static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2;
933 static const int kMaxSubdivs = 10;
934
935 ab.scale(kLengthScale);
936 dc.scale(kLengthScale);
937
938 // e0 and e1 are extrapolations along vectors ab and dc.
939 SkVector c0 = p[0];
940 c0 += ab;
941 SkVector c1 = p[3];
942 c1 += dc;
943
944 SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : SkPointPriv::DistanceToSqd(c0, c1);
945 if (dSqd < toleranceSqd) {
946 SkPoint cAvg = c0;
947 cAvg += c1;
948 cAvg.scale(SK_ScalarHalf);
949
950 SkPoint* pts = quads->push_back_n(3);
951 pts[0] = p[0];
952 pts[1] = cAvg;
953 pts[2] = p[3];
954 return;
955 }
956 SkPoint choppedPts[7];
957 SkChopCubicAtHalf(p, choppedPts);
958 convert_noninflect_cubic_to_quads(choppedPts + 0, toleranceSqd, quads, sublevel + 1);
959 convert_noninflect_cubic_to_quads(choppedPts + 3, toleranceSqd, quads, sublevel + 1);
960}
961
962void convertCubicToQuads(const SkPoint p[4], SkScalar tolScale, SkTArray<SkPoint, true>* quads) {
963 if (!p[0].isFinite() || !p[1].isFinite() || !p[2].isFinite() || !p[3].isFinite()) {
964 return;
965 }
966 SkPoint chopped[10];
967 int count = SkChopCubicAtInflections(p, chopped);
968
969 const SkScalar tolSqd = SkScalarSquare(tolScale);
970
971 for (int i = 0; i < count; ++i) {
972 SkPoint* cubic = chopped + 3*i;
973 convert_noninflect_cubic_to_quads(cubic, tolSqd, quads);
974 }
975}
976
977void path_to_quads(const SkPath& path, SkPath* quadPath) {
978 quadPath->reset();
979 SkTArray<SkPoint, true> qPts;
980 SkAutoConicToQuads converter;
981 const SkPoint* quadPts;
982 SkPath::RawIter iter(path);
983 uint8_t verb;
984 SkPoint pts[4];
985 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
986 switch (verb) {
987 case SkPath::kMove_Verb:
988 quadPath->moveTo(pts[0].fX, pts[0].fY);
989 break;
990 case SkPath::kLine_Verb:
991 quadPath->lineTo(pts[1].fX, pts[1].fY);
992 break;
993 case SkPath::kQuad_Verb:
994 quadPath->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
995 break;
996 case SkPath::kCubic_Verb:
997 qPts.reset();
998 convertCubicToQuads(pts, SK_Scalar1, &qPts);
999 for (int i = 0; i < qPts.count(); i += 3) {
1000 quadPath->quadTo(qPts[i+1].fX, qPts[i+1].fY, qPts[i+2].fX, qPts[i+2].fY);
1001 }
1002 break;
1003 case SkPath::kConic_Verb:
1004 quadPts = converter.computeQuads(pts, iter.conicWeight(), SK_Scalar1);
1005 for (int i = 0; i < converter.countQuads(); ++i) {
1006 quadPath->quadTo(quadPts[i*2+1].fX, quadPts[i*2+1].fY,
1007 quadPts[i*2+2].fX, quadPts[i*2+2].fY);
1008 }
1009 break;
1010 case SkPath::kClose_Verb:
1011 quadPath->close();
1012 break;
1013 default:
1014 SkDEBUGFAIL("bad verb");
1015 return;
1016 }
1017 }
1018}
1019
1020class SkCOLRCanvas : public SkNoDrawCanvas {
1021public:
1022 SkCOLRCanvas(SkRect glyphBounds, SkGlyphID glyphId,
1023 SkTestSVGTypeface::GlyfInfo* glyf, SkTHashMap<SkColor, int>* colors,
1024 SkWStream* out)
1025 : SkNoDrawCanvas(glyphBounds.roundOut().width(), glyphBounds.roundOut().height())
1026 , fOut(out)
1027 , fGlyphId(glyphId)
1028 , fBaselineOffset(glyphBounds.top())
1029 , fLayerId(0)
1030 , fGlyf(glyf)
1031 , fColors(colors)
1032 { }
1033
1034 void writePoint(SkScalar x, SkScalar y, bool on) {
1035 fOut->writeText(" <pt x=\"");
1036 fOut->writeDecAsText(SkScalarRoundToInt(x));
1037 fOut->writeText("\" y=\"");
1038 fOut->writeDecAsText(SkScalarRoundToInt(y));
1039 fOut->writeText("\" on=\"");
1040 fOut->write8(on ? '1' : '0');
1041 fOut->writeText("\"/>\n");
1042 }
1043 SkIRect writePath(const SkPath& path, bool layer) {
1044 // Convert to quads.
1045 SkPath quads;
1046 path_to_quads(path, &quads);
1047
1048 SkRect bounds = quads.computeTightBounds();
1049 SkIRect ibounds = bounds.roundOut();
1050 // The bounds will be re-calculated anyway.
1051 fOut->writeText(" <TTGlyph name=\"glyf");
1052 fOut->writeHexAsText(fGlyphId, 4);
1053 if (layer) {
1054 fOut->writeText("l");
1055 fOut->writeHexAsText(fLayerId, 4);
1056 }
1057 fOut->writeText("\" xMin=\"");
1058 fOut->writeDecAsText(ibounds.fLeft);
1059 fOut->writeText("\" yMin=\"");
1060 fOut->writeDecAsText(ibounds.fTop);
1061 fOut->writeText("\" xMax=\"");
1062 fOut->writeDecAsText(ibounds.fRight);
1063 fOut->writeText("\" yMax=\"");
1064 fOut->writeDecAsText(ibounds.fBottom);
1065 fOut->writeText("\">\n");
1066
1067 SkPath::RawIter iter(quads);
1068 uint8_t verb;
1069 SkPoint pts[4];
1070 bool contourOpen = false;
1071 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1072 switch (verb) {
1073 case SkPath::kMove_Verb:
1074 if (contourOpen) {
1075 fOut->writeText(" </contour>\n");
1076 contourOpen = false;
1077 }
1078 break;
1079 case SkPath::kLine_Verb:
1080 if (!contourOpen) {
1081 fOut->writeText(" <contour>\n");
1082 this->writePoint(pts[0].fX, pts[0].fY, true);
1083 contourOpen = true;
1084 }
1085 this->writePoint(pts[1].fX, pts[1].fY, true);
1086 break;
1087 case SkPath::kQuad_Verb:
1088 if (!contourOpen) {
1089 fOut->writeText(" <contour>\n");
1090 this->writePoint(pts[0].fX, pts[0].fY, true);
1091 contourOpen = true;
1092 }
1093 this->writePoint(pts[1].fX, pts[1].fY, false);
1094 this->writePoint(pts[2].fX, pts[2].fY, true);
1095 break;
1096 case SkPath::kClose_Verb:
1097 if (contourOpen) {
1098 fOut->writeText(" </contour>\n");
1099 contourOpen = false;
1100 }
1101 break;
1102 default:
1103 SkDEBUGFAIL("bad verb");
1104 return ibounds;
1105 }
1106 }
1107 if (contourOpen) {
1108 fOut->writeText(" </contour>\n");
1109 }
1110
1111 // Required to write out an instructions tag.
1112 fOut->writeText(" <instructions/>\n");
1113 fOut->writeText(" </TTGlyph>\n");
1114 return ibounds;
1115 }
1116
1117 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
1118 SkPath path;
1119 path.addRect(rect);
1120 this->drawPath(path, paint);
1121 }
1122
1123 void onDrawOval(const SkRect& oval, const SkPaint& paint) override {
1124 SkPath path;
1125 path.addOval(oval);
1126 this->drawPath(path, paint);
1127 }
1128
1129 void onDrawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
1130 const SkPaint& paint) override
1131 {
1132 SkPath path;
1133 bool fillNoPathEffect = SkPaint::kFill_Style == paint.getStyle() && !paint.getPathEffect();
1134 SkPathPriv::CreateDrawArcPath(&path, oval, startAngle, sweepAngle, useCenter,
1135 fillNoPathEffect);
1136 this->drawPath(path, paint);
1137 }
1138
1139 void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
1140 SkPath path;
1141 path.addRRect(rrect);
1142 this->drawPath(path, paint);
1143 }
1144
1145 void onDrawPath(const SkPath& platonicPath, const SkPaint& originalPaint) override {
1146 SkPaint paint = originalPaint;
1147 SkPath path = platonicPath;
1148
1149 // Apply the path effect.
1150 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
1151 bool fill = paint.getFillPath(path, &path);
1152
1153 paint.setPathEffect(nullptr);
1154 if (fill) {
1155 paint.setStyle(SkPaint::kFill_Style);
1156 } else {
1157 paint.setStyle(SkPaint::kStroke_Style);
1158 paint.setStrokeWidth(0);
1159 }
1160 }
1161
1162 // Apply the matrix.
1163 SkMatrix m = this->getTotalMatrix();
1164 // If done to the canvas then everything would get clipped out.
1165 m.postTranslate(0, fBaselineOffset); // put the baseline at 0
1166 m.postScale(1, -1); // and flip it since OpenType is y-up.
1167 path.transform(m);
1168
1169 // While creating the default glyf, union with dark colors and intersect with bright colors.
1170 SkColor color = paint.getColor();
1171 if ((SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color)) / 3 > 0x20) {
1172 fBasePath.add(path, SkPathOp::kDifference_SkPathOp);
1173 } else {
1174 fBasePath.add(path, SkPathOp::kUnion_SkPathOp);
1175 }
1176
1177 SkIRect bounds = this->writePath(path, true);
1178
1179 // The CPAL table has the concept of a 'current color' which is index 0xFFFF.
1180 // Mark any layer drawn in 'currentColor' as having this special index.
1181 // The value of 'currentColor' here should a color which causes this layer to union into the
1182 // default glyf.
1183 constexpr SkColor currentColor = 0xFF2B0000;
1184
1185 int colorIndex;
1186 if (color == currentColor) {
1187 colorIndex = 0xFFFF;
1188 } else {
1189 int* colorIndexPtr = fColors->find(color);
1190 if (colorIndexPtr) {
1191 colorIndex = *colorIndexPtr;
1192 } else {
1193 colorIndex = fColors->count();
1194 fColors->set(color, colorIndex);
1195 }
1196 }
1197 fGlyf->fLayers.emplace_back(colorIndex, bounds);
1198
1199 ++fLayerId;
1200 }
1201
1202 void finishGlyph() {
1203 SkPath baseGlyph;
1204 fBasePath.resolve(&baseGlyph);
1205 fGlyf->fBounds = this->writePath(baseGlyph, false);
1206 }
1207
1208private:
1209 SkWStream * const fOut;
1210 SkGlyphID fGlyphId;
1211 SkScalar fBaselineOffset;
1212 int fLayerId;
1213 SkOpBuilder fBasePath;
1214 SkTestSVGTypeface::GlyfInfo* fGlyf;
1215 SkTHashMap<SkColor, int>* fColors;
1216};
1217
1218} // namespace
1219
1220void SkTestSVGTypeface::exportTtxColr(SkWStream* out) const {
1221 out->writeText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1222 out->writeText("<ttFont sfntVersion=\"\\x00\\x01\\x00\\x00\" ttLibVersion=\"3.19\">\n");
1223
1224 SkTHashMap<SkColor, int> colors;
1225 SkTArray<GlyfInfo> glyfInfos(fGlyphs.count());
1226
1227 // Need to know all the glyphs up front for the common tables.
1228 SkDynamicMemoryWStream glyfOut;
1229 glyfOut.writeText(" <glyf>\n");
1230 for (int i = 0; i < fGlyphs.count(); ++i) {
1231 const SkTestSVGTypeface::Glyph& glyphData = this->fGlyphs[i];
1232
1233 SkSize containerSize = glyphData.fSvg ? glyphData.fSvg->containerSize()
1234 : SkSize::MakeEmpty();
1235 SkRect bounds = SkRect::MakeXYWH(glyphData.fOrigin.fX, -glyphData.fOrigin.fY,
1236 containerSize.fWidth, containerSize.fHeight);
1237 SkCOLRCanvas canvas(bounds, i, &glyfInfos.emplace_back(), &colors, &glyfOut);
1238 if (glyphData.fSvg) {
1239 glyphData.fSvg->render(&canvas);
1240 }
1241 canvas.finishGlyph();
1242 }
1243 glyfOut.writeText(" </glyf>\n");
1244
1245 this->exportTtxCommon(out, "COLR", &glyfInfos);
1246
1247 // The loca table will be re-calculated, but if we don't write one we don't get one.
1248 out->writeText(" <loca/>\n");
1249
1250 std::unique_ptr<SkStreamAsset> glyfStream = glyfOut.detachAsStream();
1251 out->writeStream(glyfStream.get(), glyfStream->getLength());
1252
1253 out->writeText(" <COLR>\n");
1254 out->writeText(" <version value=\"0\"/>\n");
1255 for (int i = 0; i < fGlyphs.count(); ++i) {
1256 if (glyfInfos[i].fBounds.isEmpty() || glyfInfos[i].fLayers.empty()) {
1257 continue;
1258 }
1259 out->writeText(" <ColorGlyph name=\"glyf");
1260 out->writeHexAsText(i, 4);
1261 out->writeText("\">\n");
1262 for (int j = 0; j < glyfInfos[i].fLayers.count(); ++j) {
1263 const int colorIndex = glyfInfos[i].fLayers[j].fLayerColorIndex;
1264 out->writeText(" <layer colorID=\"");
1265 out->writeDecAsText(colorIndex);
1266 out->writeText("\" name=\"glyf");
1267 out->writeHexAsText(i, 4);
1268 out->writeText("l");
1269 out->writeHexAsText(j, 4);
1270 out->writeText("\"/>\n");
1271 }
1272 out->writeText(" </ColorGlyph>\n");
1273 }
1274 out->writeText(" </COLR>\n");
1275
1276 // The colors must be written in order, the 'index' is ignored by ttx.
1277 SkAutoTMalloc<SkColor> colorsInOrder(colors.count());
1278 colors.foreach([&colorsInOrder](const SkColor& c, const int* i) {
1279 colorsInOrder[*i] = c;
1280 });
1281 out->writeText(" <CPAL>\n");
1282 out->writeText(" <version value=\"0\"/>\n");
1283 out->writeText(" <numPaletteEntries value=\"");
1284 out->writeDecAsText(colors.count());
1285 out->writeText("\"/>\n");
1286 out->writeText(" <palette index=\"0\">\n");
1287 for (int i = 0; i < colors.count(); ++i) {
1288 SkColor c = colorsInOrder[i];
1289 out->writeText(" <color index=\"");
1290 out->writeDecAsText(i);
1291 out->writeText("\" value=\"#");
1292 out->writeHexAsText(SkColorGetR(c), 2);
1293 out->writeHexAsText(SkColorGetG(c), 2);
1294 out->writeHexAsText(SkColorGetB(c), 2);
1295 out->writeHexAsText(SkColorGetA(c), 2);
1296 out->writeText("\"/>\n");
1297 }
1298 out->writeText(" </palette>\n");
1299 out->writeText(" </CPAL>\n");
1300
1301 out->writeText("</ttFont>\n");
1302}