1 /*
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 "tools/fonts/TestSVGTypeface.h"
9
10 #if defined(SK_ENABLE_SVG)
11
12 #include "include/core/SkBitmap.h"
13 #include "include/core/SkCanvas.h"
14 #include "include/core/SkColor.h"
15 #include "include/core/SkData.h"
16 #include "include/core/SkDrawable.h"
17 #include "include/core/SkEncodedImageFormat.h"
18 #include "include/core/SkFontStyle.h"
19 #include "include/core/SkImage.h"
20 #include "include/core/SkImageInfo.h"
21 #include "include/core/SkMatrix.h"
22 #include "include/core/SkPath.h"
23 #include "include/core/SkPathEffect.h"
24 #include "include/core/SkPathUtils.h"
25 #include "include/core/SkPixmap.h"
26 #include "include/core/SkRRect.h"
27 #include "include/core/SkSize.h"
28 #include "include/core/SkStream.h"
29 #include "include/core/SkSurface.h"
30 #include "include/pathops/SkPathOps.h"
31 #include "include/private/base/SkTDArray.h"
32 #include "include/private/base/SkTemplates.h"
33 #include "include/utils/SkNoDrawCanvas.h"
34 #include "modules/svg/include/SkSVGDOM.h"
35 #include "modules/svg/include/SkSVGNode.h"
36 #include "src/base/SkUtils.h"
37 #include "src/core/SkAdvancedTypefaceMetrics.h"
38 #include "src/core/SkFontDescriptor.h"
39 #include "src/core/SkFontPriv.h"
40 #include "src/core/SkGeometry.h"
41 #include "src/core/SkGlyph.h"
42 #include "src/core/SkMask.h"
43 #include "src/core/SkPaintPriv.h"
44 #include "src/core/SkPathPriv.h"
45 #include "src/core/SkPointPriv.h"
46 #include "src/core/SkScalerContext.h"
47 #include "src/sfnt/SkOTUtils.h"
48 #include "tools/Resources.h"
49
50 #include <utility>
51
52 using namespace skia_private;
53
54 class SkDescriptor;
55
TestSVGTypeface(const char * name,int upem,const SkFontMetrics & fontMetrics,SkSpan<const SkSVGTestTypefaceGlyphData> data,const SkFontStyle & style)56 TestSVGTypeface::TestSVGTypeface(const char* name,
57 int upem,
58 const SkFontMetrics& fontMetrics,
59 SkSpan<const SkSVGTestTypefaceGlyphData> data,
60 const SkFontStyle& style)
61 : SkTypeface(style, false)
62 , fName(name)
63 , fUpem(upem)
64 , fFontMetrics(fontMetrics)
65 , fGlyphs(new Glyph[data.size()])
66 , fGlyphCount(data.size()) {
67 for (size_t i = 0; i < data.size(); ++i) {
68 const SkSVGTestTypefaceGlyphData& datum = data[i];
69 fCMap.set(datum.fUnicode, i);
70 fGlyphs[i].fAdvance = datum.fAdvance;
71 fGlyphs[i].fOrigin = datum.fOrigin;
72 fGlyphs[i].fResourcePath = datum.fSvgResourcePath;
73 }
74 }
75
76 template <typename Fn>
withSVG(Fn && fn) const77 void TestSVGTypeface::Glyph::withSVG(Fn&& fn) const {
78 SkAutoMutexExclusive lock(fSvgMutex);
79
80 if (!fParsedSvg) {
81 fParsedSvg = true;
82
83 std::unique_ptr<SkStreamAsset> stream = GetResourceAsStream(fResourcePath);
84 if (!stream) {
85 return;
86 }
87
88 sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromStream(*stream);
89 if (!svg) {
90 return;
91 }
92
93 if (svg->containerSize().isEmpty()) {
94 return;
95 }
96
97 fSvg = std::move(svg);
98 }
99
100 if (fSvg) {
101 fn(*fSvg);
102 }
103 }
104
size() const105 SkSize TestSVGTypeface::Glyph::size() const {
106 SkSize size = SkSize::MakeEmpty();
107 this->withSVG([&](const SkSVGDOM& svg){
108 size = svg.containerSize();
109 });
110 return size;
111 }
112
render(SkCanvas * canvas) const113 void TestSVGTypeface::Glyph::render(SkCanvas* canvas) const {
114 this->withSVG([&](const SkSVGDOM& svg){
115 svg.render(canvas);
116 });
117 }
118
~TestSVGTypeface()119 TestSVGTypeface::~TestSVGTypeface() {}
120
Glyph()121 TestSVGTypeface::Glyph::Glyph() : fOrigin{0, 0}, fAdvance(0) {}
~Glyph()122 TestSVGTypeface::Glyph::~Glyph() {}
123
getAdvance(SkGlyph * glyph) const124 void TestSVGTypeface::getAdvance(SkGlyph* glyph) const {
125 SkGlyphID glyphID = glyph->getGlyphID();
126 glyphID = glyphID < fGlyphCount ? glyphID : 0;
127
128 glyph->fAdvanceX = fGlyphs[glyphID].fAdvance;
129 glyph->fAdvanceY = 0;
130 }
131
getFontMetrics(SkFontMetrics * metrics) const132 void TestSVGTypeface::getFontMetrics(SkFontMetrics* metrics) const { *metrics = fFontMetrics; }
133
onFilterRec(SkScalerContextRec * rec) const134 void TestSVGTypeface::onFilterRec(SkScalerContextRec* rec) const {
135 rec->setHinting(SkFontHinting::kNone);
136 }
137
getGlyphToUnicodeMap(SkUnichar * glyphToUnicode) const138 void TestSVGTypeface::getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const {
139 SkDEBUGCODE(unsigned glyphCount = this->countGlyphs());
140 fCMap.foreach ([=](const SkUnichar& c, const SkGlyphID& g) {
141 SkASSERT(g < glyphCount);
142 glyphToUnicode[g] = c;
143 });
144 }
145
onGetAdvancedMetrics() const146 std::unique_ptr<SkAdvancedTypefaceMetrics> TestSVGTypeface::onGetAdvancedMetrics() const {
147 std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
148 info->fFontName = fName;
149 return info;
150 }
151
onGetFontDescriptor(SkFontDescriptor * desc,bool * serialize) const152 void TestSVGTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const {
153 desc->setFamilyName(fName.c_str());
154 desc->setStyle(this->fontStyle());
155 *serialize = true;
156 }
157
onCharsToGlyphs(const SkUnichar uni[],int count,SkGlyphID glyphs[]) const158 void TestSVGTypeface::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
159 for (int i = 0; i < count; i++) {
160 SkGlyphID* g = fCMap.find(uni[i]);
161 glyphs[i] = g ? *g : 0;
162 }
163 }
164
onGetFamilyName(SkString * familyName) const165 void TestSVGTypeface::onGetFamilyName(SkString* familyName) const { *familyName = fName; }
166
onGetPostScriptName(SkString *) const167 bool TestSVGTypeface::onGetPostScriptName(SkString*) const { return false; }
168
onCreateFamilyNameIterator() const169 SkTypeface::LocalizedStrings* TestSVGTypeface::onCreateFamilyNameIterator() const {
170 SkString familyName(fName);
171 SkString language("und"); // undetermined
172 return new SkOTUtils::LocalizedStrings_SingleName(familyName, language);
173 }
174
175 class SkTestSVGScalerContext : public SkScalerContext {
176 public:
SkTestSVGScalerContext(sk_sp<TestSVGTypeface> face,const SkScalerContextEffects & effects,const SkDescriptor * desc)177 SkTestSVGScalerContext(sk_sp<TestSVGTypeface> face,
178 const SkScalerContextEffects& effects,
179 const SkDescriptor* desc)
180 : SkScalerContext(std::move(face), effects, desc) {
181 fRec.getSingleMatrix(&fMatrix);
182 SkScalar upem = this->getTestSVGTypeface()->fUpem;
183 fMatrix.preScale(1.f / upem, 1.f / upem);
184 }
185
186 protected:
getTestSVGTypeface() const187 TestSVGTypeface* getTestSVGTypeface() const {
188 return static_cast<TestSVGTypeface*>(this->getTypeface());
189 }
190
generateAdvance(SkGlyph * glyph)191 bool generateAdvance(SkGlyph* glyph) override {
192 this->getTestSVGTypeface()->getAdvance(glyph);
193
194 const SkVector advance =
195 fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX), SkFloatToScalar(glyph->fAdvanceY));
196 glyph->fAdvanceX = SkScalarToFloat(advance.fX);
197 glyph->fAdvanceY = SkScalarToFloat(advance.fY);
198 return true;
199 }
200
generateMetrics(SkGlyph * glyph,SkArenaAlloc * alloc)201 void generateMetrics(SkGlyph* glyph, SkArenaAlloc* alloc) override {
202 SkGlyphID glyphID = glyph->getGlyphID();
203 glyphID = glyphID < this->getTestSVGTypeface()->fGlyphCount ? glyphID : 0;
204
205 glyph->zeroMetrics();
206 glyph->fMaskFormat = SkMask::kARGB32_Format;
207 glyph->setPath(alloc, nullptr, false);
208 this->generateAdvance(glyph);
209
210 TestSVGTypeface::Glyph& glyphData = this->getTestSVGTypeface()->fGlyphs[glyphID];
211
212 SkSize containerSize = glyphData.size();
213 SkRect newBounds = SkRect::MakeXYWH(glyphData.fOrigin.fX,
214 -glyphData.fOrigin.fY,
215 containerSize.fWidth,
216 containerSize.fHeight);
217 fMatrix.mapRect(&newBounds);
218 SkScalar dx = SkFixedToScalar(glyph->getSubXFixed());
219 SkScalar dy = SkFixedToScalar(glyph->getSubYFixed());
220 newBounds.offset(dx, dy);
221
222 SkIRect ibounds;
223 newBounds.roundOut(&ibounds);
224 glyph->fLeft = ibounds.fLeft;
225 glyph->fTop = ibounds.fTop;
226 glyph->fWidth = ibounds.width();
227 glyph->fHeight = ibounds.height();
228 }
229
generateImage(const SkGlyph & glyph)230 void generateImage(const SkGlyph& glyph) override {
231 SkGlyphID glyphID = glyph.getGlyphID();
232 glyphID = glyphID < this->getTestSVGTypeface()->fGlyphCount ? glyphID : 0;
233
234 SkBitmap bm;
235 // TODO: this should be SkImageInfo::MakeS32 when that passes all the tests.
236 bm.installPixels(SkImageInfo::MakeN32(glyph.fWidth, glyph.fHeight, kPremul_SkAlphaType),
237 glyph.fImage,
238 glyph.rowBytes());
239 bm.eraseColor(0);
240
241 TestSVGTypeface::Glyph& glyphData = this->getTestSVGTypeface()->fGlyphs[glyphID];
242
243 SkScalar dx = SkFixedToScalar(glyph.getSubXFixed());
244 SkScalar dy = SkFixedToScalar(glyph.getSubYFixed());
245
246 SkCanvas canvas(bm);
247 canvas.translate(-glyph.fLeft, -glyph.fTop);
248 canvas.translate(dx, dy);
249 canvas.concat(fMatrix);
250 canvas.translate(glyphData.fOrigin.fX, -glyphData.fOrigin.fY);
251
252 glyphData.render(&canvas);
253 }
254
generatePath(const SkGlyph & glyph,SkPath * path)255 bool generatePath(const SkGlyph& glyph, SkPath* path) override {
256 // Should never get here since generateMetrics always sets the path to not exist.
257 SK_ABORT("Path requested, but it should have been indicated that there isn't one.");
258 path->reset();
259 return false;
260 }
261
262 struct SVGGlyphDrawable : public SkDrawable {
263 SkTestSVGScalerContext* fSelf;
264 SkGlyph fGlyph;
SVGGlyphDrawableSkTestSVGScalerContext::SVGGlyphDrawable265 SVGGlyphDrawable(SkTestSVGScalerContext* self, const SkGlyph& glyph)
266 : fSelf(self), fGlyph(glyph) {}
onGetBoundsSkTestSVGScalerContext::SVGGlyphDrawable267 SkRect onGetBounds() override { return fGlyph.rect(); }
onApproximateBytesUsedSkTestSVGScalerContext::SVGGlyphDrawable268 size_t onApproximateBytesUsed() override { return sizeof(SVGGlyphDrawable); }
269
onDrawSkTestSVGScalerContext::SVGGlyphDrawable270 void onDraw(SkCanvas* canvas) override {
271 SkGlyphID glyphID = fGlyph.getGlyphID();
272 glyphID = glyphID < fSelf->getTestSVGTypeface()->fGlyphCount ? glyphID : 0;
273
274 TestSVGTypeface::Glyph& glyphData = fSelf->getTestSVGTypeface()->fGlyphs[glyphID];
275
276 SkScalar dx = SkFixedToScalar(fGlyph.getSubXFixed());
277 SkScalar dy = SkFixedToScalar(fGlyph.getSubYFixed());
278
279 canvas->translate(dx, dy);
280 canvas->concat(fSelf->fMatrix);
281 canvas->translate(glyphData.fOrigin.fX, -glyphData.fOrigin.fY);
282
283 glyphData.render(canvas);
284 }
285 };
generateDrawable(const SkGlyph & glyph)286 sk_sp<SkDrawable> generateDrawable(const SkGlyph& glyph) override {
287 return sk_sp<SVGGlyphDrawable>(new SVGGlyphDrawable(this, glyph));
288 }
289
generateFontMetrics(SkFontMetrics * metrics)290 void generateFontMetrics(SkFontMetrics* metrics) override {
291 this->getTestSVGTypeface()->getFontMetrics(metrics);
292 SkFontPriv::ScaleFontMetrics(metrics, fMatrix.getScaleY());
293 }
294
295 private:
296 SkMatrix fMatrix;
297 };
298
onCreateScalerContext(const SkScalerContextEffects & e,const SkDescriptor * desc) const299 std::unique_ptr<SkScalerContext> TestSVGTypeface::onCreateScalerContext(
300 const SkScalerContextEffects& e, const SkDescriptor* desc) const
301 {
302 return std::make_unique<SkTestSVGScalerContext>(
303 sk_ref_sp(const_cast<TestSVGTypeface*>(this)), e, desc);
304 }
305
306 class DefaultTypeface : public TestSVGTypeface {
307 using TestSVGTypeface::TestSVGTypeface;
308
getPathOp(SkColor color,SkPathOp * op) const309 bool getPathOp(SkColor color, SkPathOp* op) const override {
310 if ((SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color)) / 3 > 0x20) {
311 *op = SkPathOp::kDifference_SkPathOp;
312 } else {
313 *op = SkPathOp::kUnion_SkPathOp;
314 }
315 return true;
316 }
317
318 static constexpr SkTypeface::FactoryId FactoryId = SkSetFourByteTag('d','s','v','g');
319 static constexpr const char gHeaderString[] = "SkTestSVGTypefaceDefault01";
320 static constexpr const size_t kHeaderSize = sizeof(gHeaderString);
321
onOpenStream(int * ttcIndex) const322 std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override {
323 SkDynamicMemoryWStream wstream;
324 wstream.write(gHeaderString, kHeaderSize);
325 return wstream.detachAsStream();
326 }
327
MakeFromStream(std::unique_ptr<SkStreamAsset> stream,const SkFontArguments &)328 static sk_sp<SkTypeface> MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
329 const SkFontArguments&) {
330 char header[kHeaderSize];
331 if (stream->read(header, kHeaderSize) != kHeaderSize ||
332 0 != memcmp(header, gHeaderString, kHeaderSize))
333 {
334 return nullptr;
335 }
336 return TestSVGTypeface::Default();
337 }
338
onGetFontDescriptor(SkFontDescriptor * desc,bool * serialize) const339 void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override {
340 TestSVGTypeface::onGetFontDescriptor(desc, serialize);
341 desc->setFactoryId(FactoryId);
342 }
343 public:
RegisterDefaultTypeface::Register344 struct Register { Register() { SkTypeface::Register(FactoryId, &MakeFromStream); } };
345 };
346 static DefaultTypeface::Register defaultTypefaceRegister;
Default()347 sk_sp<TestSVGTypeface> TestSVGTypeface::Default() {
348 // Recommended that the first four be .notdef, .null, CR, space
349 constexpr const static SkSVGTestTypefaceGlyphData glyphs[] = {
350 {"fonts/svg/notdef.svg", {100, 800}, 800, 0x0}, // .notdef
351 {"fonts/svg/empty.svg", {0, 0}, 800, 0x0020}, // space
352 {"fonts/svg/diamond.svg", {100, 800}, 800, 0x2662}, // ♢
353 {"fonts/svg/smile.svg", {0, 800}, 800, 0x1F600}, //
354 };
355 SkFontMetrics metrics;
356 metrics.fFlags = SkFontMetrics::kUnderlineThicknessIsValid_Flag |
357 SkFontMetrics::kUnderlinePositionIsValid_Flag |
358 SkFontMetrics::kStrikeoutThicknessIsValid_Flag |
359 SkFontMetrics::kStrikeoutPositionIsValid_Flag;
360 metrics.fTop = -800;
361 metrics.fAscent = -800;
362 metrics.fDescent = 200;
363 metrics.fBottom = 200;
364 metrics.fLeading = 100;
365 metrics.fAvgCharWidth = 1000;
366 metrics.fMaxCharWidth = 1000;
367 metrics.fXMin = 0;
368 metrics.fXMax = 1000;
369 metrics.fXHeight = 500;
370 metrics.fCapHeight = 700;
371 metrics.fUnderlineThickness = 40;
372 metrics.fUnderlinePosition = 20;
373 metrics.fStrikeoutThickness = 20;
374 metrics.fStrikeoutPosition = -400;
375
376 return sk_sp<TestSVGTypeface>(
377 new DefaultTypeface("Emoji", 1000, metrics, glyphs, SkFontStyle::Normal()));
378 }
379
380 class PlanetTypeface : public TestSVGTypeface {
381 using TestSVGTypeface::TestSVGTypeface;
382
getPathOp(SkColor color,SkPathOp * op) const383 bool getPathOp(SkColor color, SkPathOp* op) const override {
384 *op = SkPathOp::kUnion_SkPathOp;
385 return true;
386 }
387
388 static constexpr SkTypeface::FactoryId FactoryId = SkSetFourByteTag('p','s','v','g');
389 static constexpr const char gHeaderString[] = "SkTestSVGTypefacePlanet01";
390 static constexpr const size_t kHeaderSize = sizeof(gHeaderString);
391
onOpenStream(int * ttcIndex) const392 std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override {
393 SkDynamicMemoryWStream wstream;
394 wstream.write(gHeaderString, kHeaderSize);
395 return wstream.detachAsStream();
396 }
397
MakeFromStream(std::unique_ptr<SkStreamAsset> stream,const SkFontArguments &)398 static sk_sp<SkTypeface> MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
399 const SkFontArguments&) {
400 char header[kHeaderSize];
401 if (stream->read(header, kHeaderSize) != kHeaderSize ||
402 0 != memcmp(header, gHeaderString, kHeaderSize))
403 {
404 return nullptr;
405 }
406 return TestSVGTypeface::Planets();
407 }
408
onGetFontDescriptor(SkFontDescriptor * desc,bool * isLocal) const409 void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override {
410 TestSVGTypeface::onGetFontDescriptor(desc, isLocal);
411 desc->setFactoryId(FactoryId);
412 }
413 public:
RegisterPlanetTypeface::Register414 struct Register { Register() { SkTypeface::Register(FactoryId, &MakeFromStream); } };
415 };
416 static PlanetTypeface::Register planetTypefaceRegister;
Planets()417 sk_sp<TestSVGTypeface> TestSVGTypeface::Planets() {
418 // Recommended that the first four be .notdef, .null, CR, space
419 constexpr const static SkSVGTestTypefaceGlyphData glyphs[] = {
420 {"fonts/svg/planets/pluto.svg", {0, 20}, 60, 0x0}, // .notdef
421 {"fonts/svg/empty.svg", {0, 0}, 400, 0x0020}, // space
422 {"fonts/svg/planets/mercury.svg", {0, 45}, 120, 0x263F}, // ☿
423 {"fonts/svg/planets/venus.svg", {0, 100}, 240, 0x2640}, // ♀
424 {"fonts/svg/planets/earth.svg", {0, 100}, 240, 0x2641}, // ♁
425 {"fonts/svg/planets/mars.svg", {0, 50}, 130, 0x2642}, // ♂
426 {"fonts/svg/planets/jupiter.svg", {0, 1000}, 2200, 0x2643}, // ♃
427 {"fonts/svg/planets/saturn.svg", {-300, 1500}, 2600, 0x2644}, // ♄
428 {"fonts/svg/planets/uranus.svg", {0, 375}, 790, 0x2645}, // ♅
429 {"fonts/svg/planets/neptune.svg", {0, 350}, 740, 0x2646}, // ♆
430 };
431 SkFontMetrics metrics;
432 metrics.fFlags = SkFontMetrics::kUnderlineThicknessIsValid_Flag |
433 SkFontMetrics::kUnderlinePositionIsValid_Flag |
434 SkFontMetrics::kStrikeoutThicknessIsValid_Flag |
435 SkFontMetrics::kStrikeoutPositionIsValid_Flag;
436 metrics.fTop = -1500;
437 metrics.fAscent = -200;
438 metrics.fDescent = 50;
439 metrics.fBottom = 1558;
440 metrics.fLeading = 10;
441 metrics.fAvgCharWidth = 200;
442 metrics.fMaxCharWidth = 200;
443 metrics.fXMin = -300;
444 metrics.fXMax = 2566;
445 metrics.fXHeight = 100;
446 metrics.fCapHeight = 180;
447 metrics.fUnderlineThickness = 8;
448 metrics.fUnderlinePosition = 2;
449 metrics.fStrikeoutThickness = 2;
450 metrics.fStrikeoutPosition = -80;
451
452 return sk_sp<TestSVGTypeface>(
453 new PlanetTypeface("Planets", 200, metrics, glyphs, SkFontStyle::Normal()));
454 }
455
exportTtxCommon(SkWStream * out,const char * type,const SkTArray<GlyfInfo> * glyfInfo) const456 void TestSVGTypeface::exportTtxCommon(SkWStream* out,
457 const char* type,
458 const SkTArray<GlyfInfo>* glyfInfo) const {
459 int totalGlyphs = fGlyphCount;
460 out->writeText(" <GlyphOrder>\n");
461 for (int i = 0; i < fGlyphCount; ++i) {
462 out->writeText(" <GlyphID name=\"glyf");
463 out->writeHexAsText(i, 4);
464 out->writeText("\"/>\n");
465 }
466 if (glyfInfo) {
467 for (int i = 0; i < fGlyphCount; ++i) {
468 for (int j = 0; j < (*glyfInfo)[i].fLayers.size(); ++j) {
469 out->writeText(" <GlyphID name=\"glyf");
470 out->writeHexAsText(i, 4);
471 out->writeText("l");
472 out->writeHexAsText(j, 4);
473 out->writeText("\"/>\n");
474 ++totalGlyphs;
475 }
476 }
477 }
478 out->writeText(" </GlyphOrder>\n");
479
480 out->writeText(" <head>\n");
481 out->writeText(" <tableVersion value=\"1.0\"/>\n");
482 out->writeText(" <fontRevision value=\"1.0\"/>\n");
483 out->writeText(" <checkSumAdjustment value=\"0xa9c3274\"/>\n");
484 out->writeText(" <magicNumber value=\"0x5f0f3cf5\"/>\n");
485 out->writeText(" <flags value=\"00000000 00011011\"/>\n");
486 out->writeText(" <unitsPerEm value=\"");
487 out->writeDecAsText(fUpem);
488 out->writeText("\"/>\n");
489 out->writeText(" <created value=\"Thu Feb 15 12:55:49 2018\"/>\n");
490 out->writeText(" <modified value=\"Thu Feb 15 12:55:49 2018\"/>\n");
491 // TODO: not recalculated for bitmap fonts?
492 out->writeText(" <xMin value=\"");
493 out->writeScalarAsText(fFontMetrics.fXMin);
494 out->writeText("\"/>\n");
495 out->writeText(" <yMin value=\"");
496 out->writeScalarAsText(-fFontMetrics.fBottom);
497 out->writeText("\"/>\n");
498 out->writeText(" <xMax value=\"");
499 out->writeScalarAsText(fFontMetrics.fXMax);
500 out->writeText("\"/>\n");
501 out->writeText(" <yMax value=\"");
502 out->writeScalarAsText(-fFontMetrics.fTop);
503 out->writeText("\"/>\n");
504
505 char macStyle[16] = {
506 '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'};
507 if (this->fontStyle().weight() >= SkFontStyle::Bold().weight()) {
508 macStyle[0xF - 0x0] = '1'; // Bold
509 }
510 switch (this->fontStyle().slant()) {
511 case SkFontStyle::kUpright_Slant: break;
512 case SkFontStyle::kItalic_Slant:
513 macStyle[0xF - 0x1] = '1'; // Italic
514 break;
515 case SkFontStyle::kOblique_Slant:
516 macStyle[0xF - 0x1] = '1'; // Italic
517 break;
518 default: SK_ABORT("Unknown slant.");
519 }
520 if (this->fontStyle().width() <= SkFontStyle::kCondensed_Width) {
521 macStyle[0xF - 0x5] = '1'; // Condensed
522 } else if (this->fontStyle().width() >= SkFontStyle::kExpanded_Width) {
523 macStyle[0xF - 0x6] = '1'; // Extended
524 }
525 out->writeText(" <macStyle value=\"");
526 out->write(macStyle, 8);
527 out->writeText(" ");
528 out->write(macStyle + 8, 8);
529 out->writeText("\"/>\n");
530 out->writeText(" <lowestRecPPEM value=\"8\"/>\n");
531 out->writeText(" <fontDirectionHint value=\"2\"/>\n");
532 out->writeText(" <indexToLocFormat value=\"0\"/>\n");
533 out->writeText(" <glyphDataFormat value=\"0\"/>\n");
534 out->writeText(" </head>\n");
535
536 out->writeText(" <hhea>\n");
537 out->writeText(" <tableVersion value=\"0x00010000\"/>\n");
538 out->writeText(" <ascent value=\"");
539 out->writeDecAsText(-fFontMetrics.fAscent);
540 out->writeText("\"/>\n");
541 out->writeText(" <descent value=\"");
542 out->writeDecAsText(-fFontMetrics.fDescent);
543 out->writeText("\"/>\n");
544 out->writeText(" <lineGap value=\"");
545 out->writeDecAsText(fFontMetrics.fLeading);
546 out->writeText("\"/>\n");
547 out->writeText(" <advanceWidthMax value=\"0\"/>\n");
548 out->writeText(" <minLeftSideBearing value=\"0\"/>\n");
549 out->writeText(" <minRightSideBearing value=\"0\"/>\n");
550 out->writeText(" <xMaxExtent value=\"");
551 out->writeScalarAsText(fFontMetrics.fXMax - fFontMetrics.fXMin);
552 out->writeText("\"/>\n");
553 out->writeText(" <caretSlopeRise value=\"1\"/>\n");
554 out->writeText(" <caretSlopeRun value=\"0\"/>\n");
555 out->writeText(" <caretOffset value=\"0\"/>\n");
556 out->writeText(" <reserved0 value=\"0\"/>\n");
557 out->writeText(" <reserved1 value=\"0\"/>\n");
558 out->writeText(" <reserved2 value=\"0\"/>\n");
559 out->writeText(" <reserved3 value=\"0\"/>\n");
560 out->writeText(" <metricDataFormat value=\"0\"/>\n");
561 out->writeText(" <numberOfHMetrics value=\"0\"/>\n");
562 out->writeText(" </hhea>\n");
563
564 // Some of this table is going to be re-calculated, but we have to write it out anyway.
565 out->writeText(" <maxp>\n");
566 out->writeText(" <tableVersion value=\"0x10000\"/>\n");
567 out->writeText(" <numGlyphs value=\"");
568 out->writeDecAsText(totalGlyphs);
569 out->writeText("\"/>\n");
570 out->writeText(" <maxPoints value=\"4\"/>\n");
571 out->writeText(" <maxContours value=\"1\"/>\n");
572 out->writeText(" <maxCompositePoints value=\"0\"/>\n");
573 out->writeText(" <maxCompositeContours value=\"0\"/>\n");
574 out->writeText(" <maxZones value=\"1\"/>\n");
575 out->writeText(" <maxTwilightPoints value=\"0\"/>\n");
576 out->writeText(" <maxStorage value=\"0\"/>\n");
577 out->writeText(" <maxFunctionDefs value=\"10\"/>\n");
578 out->writeText(" <maxInstructionDefs value=\"0\"/>\n");
579 out->writeText(" <maxStackElements value=\"512\"/>\n");
580 out->writeText(" <maxSizeOfInstructions value=\"24\"/>\n");
581 out->writeText(" <maxComponentElements value=\"0\"/>\n");
582 out->writeText(" <maxComponentDepth value=\"0\"/>\n");
583 out->writeText(" </maxp>\n");
584
585 out->writeText(" <OS_2>\n");
586 out->writeText(" <version value=\"4\"/>\n");
587 out->writeText(" <xAvgCharWidth value=\"");
588 out->writeScalarAsText(fFontMetrics.fAvgCharWidth);
589 out->writeText("\"/>\n");
590 out->writeText(" <usWeightClass value=\"");
591 out->writeDecAsText(this->fontStyle().weight());
592 out->writeText("\"/>\n");
593 out->writeText(" <usWidthClass value=\"");
594 out->writeDecAsText(this->fontStyle().width());
595 out->writeText("\"/>\n");
596 out->writeText(" <fsType value=\"00000000 00000000\"/>\n");
597 out->writeText(" <ySubscriptXSize value=\"665\"/>\n");
598 out->writeText(" <ySubscriptYSize value=\"716\"/>\n");
599 out->writeText(" <ySubscriptXOffset value=\"0\"/>\n");
600 out->writeText(" <ySubscriptYOffset value=\"143\"/>\n");
601 out->writeText(" <ySuperscriptXSize value=\"665\"/>\n");
602 out->writeText(" <ySuperscriptYSize value=\"716\"/>\n");
603 out->writeText(" <ySuperscriptXOffset value=\"0\"/>\n");
604 out->writeText(" <ySuperscriptYOffset value=\"491\"/>\n");
605 out->writeText(" <yStrikeoutSize value=\"");
606 out->writeScalarAsText(fFontMetrics.fStrikeoutThickness);
607 out->writeText("\"/>\n");
608 out->writeText(" <yStrikeoutPosition value=\"");
609 out->writeScalarAsText(-fFontMetrics.fStrikeoutPosition);
610 out->writeText("\"/>\n");
611 out->writeText(" <sFamilyClass value=\"0\"/>\n");
612 out->writeText(" <panose>\n");
613 out->writeText(" <bFamilyType value=\"0\"/>\n");
614 out->writeText(" <bSerifStyle value=\"0\"/>\n");
615 out->writeText(" <bWeight value=\"0\"/>\n");
616 out->writeText(" <bProportion value=\"0\"/>\n");
617 out->writeText(" <bContrast value=\"0\"/>\n");
618 out->writeText(" <bStrokeVariation value=\"0\"/>\n");
619 out->writeText(" <bArmStyle value=\"0\"/>\n");
620 out->writeText(" <bLetterForm value=\"0\"/>\n");
621 out->writeText(" <bMidline value=\"0\"/>\n");
622 out->writeText(" <bXHeight value=\"0\"/>\n");
623 out->writeText(" </panose>\n");
624 out->writeText(" <ulUnicodeRange1 value=\"00000000 00000000 00000000 00000001\"/>\n");
625 out->writeText(" <ulUnicodeRange2 value=\"00010000 00000000 00000000 00000000\"/>\n");
626 out->writeText(" <ulUnicodeRange3 value=\"00000000 00000000 00000000 00000000\"/>\n");
627 out->writeText(" <ulUnicodeRange4 value=\"00000000 00000000 00000000 00000000\"/>\n");
628 out->writeText(" <achVendID value=\"Skia\"/>\n");
629 char fsSelection[16] = {
630 '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'};
631 fsSelection[0xF - 0x7] = '1'; // Use typo metrics
632 if (this->fontStyle().weight() >= SkFontStyle::Bold().weight()) {
633 fsSelection[0xF - 0x5] = '1'; // Bold
634 }
635 switch (this->fontStyle().slant()) {
636 case SkFontStyle::kUpright_Slant:
637 if (this->fontStyle().weight() < SkFontStyle::Bold().weight()) {
638 fsSelection[0xF - 0x6] = '1'; // Not bold or italic, is regular
639 }
640 break;
641 case SkFontStyle::kItalic_Slant:
642 fsSelection[0xF - 0x0] = '1'; // Italic
643 break;
644 case SkFontStyle::kOblique_Slant:
645 fsSelection[0xF - 0x0] = '1'; // Italic
646 fsSelection[0xF - 0x9] = '1'; // Oblique
647 break;
648 default: SK_ABORT("Unknown slant.");
649 }
650 out->writeText(" <fsSelection value=\"");
651 out->write(fsSelection, 8);
652 out->writeText(" ");
653 out->write(fsSelection + 8, 8);
654 out->writeText("\"/>\n");
655 out->writeText(" <usFirstCharIndex value=\"0\"/>\n");
656 out->writeText(" <usLastCharIndex value=\"0\"/>\n");
657 out->writeText(" <sTypoAscender value=\"");
658 out->writeScalarAsText(-fFontMetrics.fAscent);
659 out->writeText("\"/>\n");
660 out->writeText(" <sTypoDescender value=\"");
661 out->writeScalarAsText(-fFontMetrics.fDescent);
662 out->writeText("\"/>\n");
663 out->writeText(" <sTypoLineGap value=\"");
664 out->writeScalarAsText(fFontMetrics.fLeading);
665 out->writeText("\"/>\n");
666 out->writeText(" <usWinAscent value=\"");
667 out->writeScalarAsText(-fFontMetrics.fAscent);
668 out->writeText("\"/>\n");
669 out->writeText(" <usWinDescent value=\"");
670 out->writeScalarAsText(fFontMetrics.fDescent);
671 out->writeText("\"/>\n");
672 out->writeText(" <ulCodePageRange1 value=\"00000000 00000000 00000000 00000000\"/>\n");
673 out->writeText(" <ulCodePageRange2 value=\"00000000 00000000 00000000 00000000\"/>\n");
674 out->writeText(" <sxHeight value=\"");
675 out->writeScalarAsText(fFontMetrics.fXHeight);
676 out->writeText("\"/>\n");
677 out->writeText(" <sCapHeight value=\"");
678 out->writeScalarAsText(fFontMetrics.fCapHeight);
679 out->writeText("\"/>\n");
680 out->writeText(" <usDefaultChar value=\"0\"/>\n");
681 out->writeText(" <usBreakChar value=\"32\"/>\n");
682 out->writeText(" <usMaxContext value=\"0\"/>\n");
683 out->writeText(" </OS_2>\n");
684
685 out->writeText(" <hmtx>\n");
686 for (int i = 0; i < fGlyphCount; ++i) {
687 out->writeText(" <mtx name=\"glyf");
688 out->writeHexAsText(i, 4);
689 out->writeText("\" width=\"");
690 out->writeDecAsText(fGlyphs[i].fAdvance);
691 out->writeText("\" lsb=\"");
692 int lsb = fGlyphs[i].fOrigin.fX;
693 if (glyfInfo) {
694 lsb += (*glyfInfo)[i].fBounds.fLeft;
695 }
696 out->writeDecAsText(lsb);
697 out->writeText("\"/>\n");
698 }
699 if (glyfInfo) {
700 for (int i = 0; i < fGlyphCount; ++i) {
701 for (int j = 0; j < (*glyfInfo)[i].fLayers.size(); ++j) {
702 out->writeText(" <mtx name=\"glyf");
703 out->writeHexAsText(i, 4);
704 out->writeText("l");
705 out->writeHexAsText(j, 4);
706 out->writeText("\" width=\"");
707 out->writeDecAsText(fGlyphs[i].fAdvance);
708 out->writeText("\" lsb=\"");
709 int32_t lsb = fGlyphs[i].fOrigin.fX + (*glyfInfo)[i].fLayers[j].fBounds.fLeft;
710 out->writeDecAsText(lsb);
711 out->writeText("\"/>\n");
712 }
713 }
714 }
715 out->writeText(" </hmtx>\n");
716
717 bool hasNonBMP = false;
718 out->writeText(" <cmap>\n");
719 out->writeText(" <tableVersion version=\"0\"/>\n");
720 out->writeText(" <cmap_format_4 platformID=\"3\" platEncID=\"1\" language=\"0\">\n");
721 fCMap.foreach ([&out, &hasNonBMP](const SkUnichar& c, const SkGlyphID& g) {
722 if (0xFFFF < c) {
723 hasNonBMP = true;
724 return;
725 }
726 out->writeText(" <map code=\"0x");
727 out->writeHexAsText(c, 4);
728 out->writeText("\" name=\"glyf");
729 out->writeHexAsText(g, 4);
730 out->writeText("\"/>\n");
731 });
732 out->writeText(" </cmap_format_4>\n");
733 if (hasNonBMP) {
734 out->writeText(
735 " <cmap_format_12 platformID=\"3\" platEncID=\"10\" format=\"12\" "
736 "reserved=\"0\" length=\"1\" language=\"0\" nGroups=\"0\">\n");
737 fCMap.foreach ([&out](const SkUnichar& c, const SkGlyphID& g) {
738 out->writeText(" <map code=\"0x");
739 out->writeHexAsText(c, 6);
740 out->writeText("\" name=\"glyf");
741 out->writeHexAsText(g, 4);
742 out->writeText("\"/>\n");
743 });
744 out->writeText(" </cmap_format_12>\n");
745 }
746 out->writeText(" </cmap>\n");
747
748 out->writeText(" <name>\n");
749 out->writeText(
750 " <namerecord nameID=\"1\" platformID=\"3\" platEncID=\"1\" langID=\"0x409\">\n");
751 out->writeText(" ");
752 out->writeText(fName.c_str());
753 out->writeText(" ");
754 out->writeText(type);
755 out->writeText("\n");
756 out->writeText(" </namerecord>\n");
757 out->writeText(
758 " <namerecord nameID=\"2\" platformID=\"3\" platEncID=\"1\" langID=\"0x409\">\n");
759 out->writeText(" Regular\n");
760 out->writeText(" </namerecord>\n");
761 out->writeText(" </name>\n");
762
763 out->writeText(" <post>\n");
764 out->writeText(" <formatType value=\"3.0\"/>\n");
765 out->writeText(" <italicAngle value=\"0.0\"/>\n");
766 out->writeText(" <underlinePosition value=\"");
767 out->writeScalarAsText(fFontMetrics.fUnderlinePosition);
768 out->writeText("\"/>\n");
769 out->writeText(" <underlineThickness value=\"");
770 out->writeScalarAsText(fFontMetrics.fUnderlineThickness);
771 out->writeText("\"/>\n");
772 out->writeText(" <isFixedPitch value=\"0\"/>\n");
773 out->writeText(" <minMemType42 value=\"0\"/>\n");
774 out->writeText(" <maxMemType42 value=\"0\"/>\n");
775 out->writeText(" <minMemType1 value=\"0\"/>\n");
776 out->writeText(" <maxMemType1 value=\"0\"/>\n");
777 out->writeText(" </post>\n");
778 }
779
exportTtxCbdt(SkWStream * out,SkSpan<unsigned> strikeSizes) const780 void TestSVGTypeface::exportTtxCbdt(SkWStream* out, SkSpan<unsigned> strikeSizes) const {
781 SkPaint paint;
782 SkFont font;
783 font.setTypeface(sk_ref_sp(const_cast<TestSVGTypeface*>(this)));
784 SkString name;
785 this->getFamilyName(&name);
786
787 // The CBDT/CBLC format is quite restrictive. Only write strikes which fully fit.
788 SkSTArray<8, int> goodStrikeSizes;
789 for (size_t strikeIndex = 0; strikeIndex < strikeSizes.size(); ++strikeIndex) {
790 font.setSize(strikeSizes[strikeIndex]);
791
792 // CBLC limits
793 SkFontMetrics fm;
794 font.getMetrics(&fm);
795 if (!SkTFitsIn<int8_t>((int)(-fm.fTop)) || !SkTFitsIn<int8_t>((int)(-fm.fBottom)) ||
796 !SkTFitsIn<uint8_t>((int)(fm.fXMax - fm.fXMin))) {
797 SkDebugf("Metrics too big cbdt font size %f for %s.\n", font.getSize(), name.c_str());
798 continue;
799 }
800
801 // CBDT limits
802 auto exceedsCbdtLimits = [&]() {
803 for (int i = 0; i < fGlyphCount; ++i) {
804 SkGlyphID gid = i;
805 SkScalar advance;
806 SkRect bounds;
807 font.getWidthsBounds(&gid, 1, &advance, &bounds, nullptr);
808 SkIRect ibounds = bounds.roundOut();
809 if (!SkTFitsIn<int8_t>(ibounds.fLeft) || !SkTFitsIn<int8_t>(ibounds.fTop) ||
810 !SkTFitsIn<uint8_t>(ibounds.width()) || !SkTFitsIn<uint8_t>(ibounds.height()) ||
811 !SkTFitsIn<uint8_t>((int)advance)) {
812 return true;
813 }
814 }
815 return false;
816 };
817 if (exceedsCbdtLimits()) {
818 SkDebugf("Glyphs too big cbdt font size %f for %s.\n", font.getSize(), name.c_str());
819 continue;
820 }
821
822 goodStrikeSizes.emplace_back(strikeSizes[strikeIndex]);
823 }
824
825 if (goodStrikeSizes.empty()) {
826 SkDebugf("No strike size fit for cbdt font for %s.\n", name.c_str());
827 return;
828 }
829
830 out->writeText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
831 out->writeText("<ttFont sfntVersion=\"\\x00\\x01\\x00\\x00\" ttLibVersion=\"3.19\">\n");
832 this->exportTtxCommon(out, "CBDT");
833
834 out->writeText(" <CBDT>\n");
835 out->writeText(" <header version=\"2.0\"/>\n");
836 for (int strikeIndex = 0; strikeIndex < goodStrikeSizes.size(); ++strikeIndex) {
837 font.setSize(goodStrikeSizes[strikeIndex]);
838
839 out->writeText(" <strikedata index=\"");
840 out->writeDecAsText(strikeIndex);
841 out->writeText("\">\n");
842 for (int i = 0; i < fGlyphCount; ++i) {
843 SkGlyphID gid = i;
844 SkScalar advance;
845 SkRect bounds;
846 font.getWidthsBounds(&gid, 1, &advance, &bounds, nullptr);
847 SkIRect ibounds = bounds.roundOut();
848 if (ibounds.isEmpty()) {
849 continue;
850 }
851 SkImageInfo image_info = SkImageInfo::MakeN32Premul(ibounds.width(), ibounds.height());
852 sk_sp<SkSurface> surface(SkSurface::MakeRaster(image_info));
853 SkASSERT(surface);
854 SkCanvas* canvas = surface->getCanvas();
855 canvas->clear(0);
856 SkPixmap pix;
857 surface->peekPixels(&pix);
858 canvas->drawSimpleText(&gid,
859 sizeof(gid),
860 SkTextEncoding::kGlyphID,
861 -bounds.fLeft,
862 -bounds.fTop,
863 font,
864 paint);
865 surface->flushAndSubmit();
866 sk_sp<SkImage> image = surface->makeImageSnapshot();
867 sk_sp<SkData> data = image->encodeToData(SkEncodedImageFormat::kPNG, 100);
868
869 out->writeText(" <cbdt_bitmap_format_17 name=\"glyf");
870 out->writeHexAsText(i, 4);
871 out->writeText("\">\n");
872 out->writeText(" <SmallGlyphMetrics>\n");
873 out->writeText(" <height value=\"");
874 out->writeDecAsText(image->height());
875 out->writeText("\"/>\n");
876 out->writeText(" <width value=\"");
877 out->writeDecAsText(image->width());
878 out->writeText("\"/>\n");
879 out->writeText(" <BearingX value=\"");
880 out->writeDecAsText(ibounds.fLeft);
881 out->writeText("\"/>\n");
882 out->writeText(" <BearingY value=\"");
883 out->writeDecAsText(-ibounds.fTop);
884 out->writeText("\"/>\n");
885 out->writeText(" <Advance value=\"");
886 out->writeDecAsText((int)advance);
887 out->writeText("\"/>\n");
888 out->writeText(" </SmallGlyphMetrics>\n");
889 out->writeText(" <rawimagedata>");
890 uint8_t const* bytes = data->bytes();
891 for (size_t j = 0; j < data->size(); ++j) {
892 if ((j % 0x10) == 0x0) {
893 out->writeText("\n ");
894 } else if (((j - 1) % 0x4) == 0x3) {
895 out->writeText(" ");
896 }
897 out->writeHexAsText(bytes[j], 2);
898 }
899 out->writeText("\n");
900 out->writeText(" </rawimagedata>\n");
901 out->writeText(" </cbdt_bitmap_format_17>\n");
902 }
903 out->writeText(" </strikedata>\n");
904 }
905 out->writeText(" </CBDT>\n");
906
907 SkFontMetrics fm;
908 out->writeText(" <CBLC>\n");
909 out->writeText(" <header version=\"2.0\"/>\n");
910 for (int strikeIndex = 0; strikeIndex < goodStrikeSizes.size(); ++strikeIndex) {
911 font.setSize(goodStrikeSizes[strikeIndex]);
912 font.getMetrics(&fm);
913 out->writeText(" <strike index=\"");
914 out->writeDecAsText(strikeIndex);
915 out->writeText("\">\n");
916 out->writeText(" <bitmapSizeTable>\n");
917 out->writeText(" <sbitLineMetrics direction=\"hori\">\n");
918 out->writeText(" <ascender value=\"");
919 out->writeDecAsText((int)(-fm.fTop));
920 out->writeText("\"/>\n");
921 out->writeText(" <descender value=\"");
922 out->writeDecAsText((int)(-fm.fBottom));
923 out->writeText("\"/>\n");
924 out->writeText(" <widthMax value=\"");
925 out->writeDecAsText((int)(fm.fXMax - fm.fXMin));
926 out->writeText("\"/>\n");
927 out->writeText(" <caretSlopeNumerator value=\"0\"/>\n");
928 out->writeText(" <caretSlopeDenominator value=\"0\"/>\n");
929 out->writeText(" <caretOffset value=\"0\"/>\n");
930 out->writeText(" <minOriginSB value=\"0\"/>\n");
931 out->writeText(" <minAdvanceSB value=\"0\"/>\n");
932 out->writeText(" <maxBeforeBL value=\"0\"/>\n");
933 out->writeText(" <minAfterBL value=\"0\"/>\n");
934 out->writeText(" <pad1 value=\"0\"/>\n");
935 out->writeText(" <pad2 value=\"0\"/>\n");
936 out->writeText(" </sbitLineMetrics>\n");
937 out->writeText(" <sbitLineMetrics direction=\"vert\">\n");
938 out->writeText(" <ascender value=\"");
939 out->writeDecAsText((int)(-fm.fTop));
940 out->writeText("\"/>\n");
941 out->writeText(" <descender value=\"");
942 out->writeDecAsText((int)(-fm.fBottom));
943 out->writeText("\"/>\n");
944 out->writeText(" <widthMax value=\"");
945 out->writeDecAsText((int)(fm.fXMax - fm.fXMin));
946 out->writeText("\"/>\n");
947 out->writeText(" <caretSlopeNumerator value=\"0\"/>\n");
948 out->writeText(" <caretSlopeDenominator value=\"0\"/>\n");
949 out->writeText(" <caretOffset value=\"0\"/>\n");
950 out->writeText(" <minOriginSB value=\"0\"/>\n");
951 out->writeText(" <minAdvanceSB value=\"0\"/>\n");
952 out->writeText(" <maxBeforeBL value=\"0\"/>\n");
953 out->writeText(" <minAfterBL value=\"0\"/>\n");
954 out->writeText(" <pad1 value=\"0\"/>\n");
955 out->writeText(" <pad2 value=\"0\"/>\n");
956 out->writeText(" </sbitLineMetrics>\n");
957 out->writeText(" <colorRef value=\"0\"/>\n");
958 out->writeText(" <startGlyphIndex value=\"1\"/>\n");
959 out->writeText(" <endGlyphIndex value=\"1\"/>\n");
960 out->writeText(" <ppemX value=\"");
961 out->writeDecAsText(goodStrikeSizes[strikeIndex]);
962 out->writeText("\"/>\n");
963 out->writeText(" <ppemY value=\"");
964 out->writeDecAsText(goodStrikeSizes[strikeIndex]);
965 out->writeText("\"/>\n");
966 out->writeText(" <bitDepth value=\"32\"/>\n");
967 out->writeText(" <flags value=\"1\"/>\n");
968 out->writeText(" </bitmapSizeTable>\n");
969 out->writeText(
970 " <eblc_index_sub_table_1 imageFormat=\"17\" firstGlyphIndex=\"1\" "
971 "lastGlyphIndex=\"1\">\n");
972 for (int i = 0; i < fGlyphCount; ++i) {
973 SkGlyphID gid = i;
974 SkRect bounds;
975 font.getBounds(&gid, 1, &bounds, nullptr);
976 if (bounds.isEmpty()) {
977 continue;
978 }
979 out->writeText(" <glyphLoc name=\"glyf");
980 out->writeHexAsText(i, 4);
981 out->writeText("\"/>\n");
982 }
983 out->writeText(" </eblc_index_sub_table_1>\n");
984 out->writeText(" </strike>\n");
985 }
986 out->writeText(" </CBLC>\n");
987
988 out->writeText("</ttFont>\n");
989 }
990
991 /**
992 * UnitsPerEm is generally 1000 here. Versions of macOS older than 10.13
993 * have problems in CoreText determining the glyph bounds of bitmap glyphs
994 * with unitsPerEm set to 1024 or numbers not divisible by 100 when the
995 * contour is not closed. The bounds of sbix fonts on macOS appear to be those
996 * of the outline in the 'glyf' table. If this countour is closed it will be
997 * drawn, as the 'glyf' outline is to be drawn on top of any bitmap. (There is
998 * a bit which is supposed to control this, but it cannot be relied on.) So
999 * make the glyph contour a degenerate line with points at the edge of the
1000 * bounding box of the glyph.
1001 */
exportTtxSbix(SkWStream * out,SkSpan<unsigned> strikeSizes) const1002 void TestSVGTypeface::exportTtxSbix(SkWStream* out, SkSpan<unsigned> strikeSizes) const {
1003 out->writeText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1004 out->writeText("<ttFont sfntVersion=\"\\x00\\x01\\x00\\x00\" ttLibVersion=\"3.19\">\n");
1005 this->exportTtxCommon(out, "sbix");
1006
1007 SkPaint paint;
1008 SkFont font;
1009 font.setTypeface(sk_ref_sp(const_cast<TestSVGTypeface*>(this)));
1010
1011 out->writeText(" <glyf>\n");
1012 for (int i = 0; i < fGlyphCount; ++i) {
1013 const TestSVGTypeface::Glyph& glyphData = this->fGlyphs[i];
1014
1015 SkSize containerSize = glyphData.size();
1016 SkRect bounds = SkRect::MakeXYWH(glyphData.fOrigin.fX,
1017 -glyphData.fOrigin.fY,
1018 containerSize.fWidth,
1019 containerSize.fHeight);
1020 SkIRect ibounds = bounds.roundOut();
1021 out->writeText(" <TTGlyph name=\"glyf");
1022 out->writeHexAsText(i, 4);
1023 out->writeText("\" xMin=\"");
1024 out->writeDecAsText(ibounds.fLeft);
1025 out->writeText("\" yMin=\"");
1026 out->writeDecAsText(-ibounds.fBottom);
1027 out->writeText("\" xMax=\"");
1028 out->writeDecAsText(ibounds.fRight);
1029 out->writeText("\" yMax=\"");
1030 out->writeDecAsText(-ibounds.fTop);
1031 out->writeText("\">\n");
1032 out->writeText(" <contour>\n");
1033 out->writeText(" <pt x=\"");
1034 out->writeDecAsText(ibounds.fLeft);
1035 out->writeText("\" y=\"");
1036 out->writeDecAsText(-ibounds.fBottom);
1037 out->writeText("\" on=\"1\"/>\n");
1038 out->writeText(" </contour>\n");
1039 out->writeText(" <contour>\n");
1040 out->writeText(" <pt x=\"");
1041 out->writeDecAsText(ibounds.fRight);
1042 out->writeText("\" y=\"");
1043 out->writeDecAsText(-ibounds.fTop);
1044 out->writeText("\" on=\"1\"/>\n");
1045 out->writeText(" </contour>\n");
1046 out->writeText(" <instructions/>\n");
1047 out->writeText(" </TTGlyph>\n");
1048 }
1049 out->writeText(" </glyf>\n");
1050
1051 // The loca table will be re-calculated, but if we don't write one we don't get one.
1052 out->writeText(" <loca/>\n");
1053
1054 out->writeText(" <sbix>\n");
1055 out->writeText(" <version value=\"1\"/>\n");
1056 out->writeText(" <flags value=\"00000000 00000001\"/>\n");
1057 for (size_t strikeIndex = 0; strikeIndex < strikeSizes.size(); ++strikeIndex) {
1058 font.setSize(strikeSizes[strikeIndex]);
1059 out->writeText(" <strike>\n");
1060 out->writeText(" <ppem value=\"");
1061 out->writeDecAsText(strikeSizes[strikeIndex]);
1062 out->writeText("\"/>\n");
1063 out->writeText(" <resolution value=\"72\"/>\n");
1064 for (int i = 0; i < fGlyphCount; ++i) {
1065 SkGlyphID gid = i;
1066 SkScalar advance;
1067 SkRect bounds;
1068 font.getWidthsBounds(&gid, 1, &advance, &bounds, nullptr);
1069 SkIRect ibounds = bounds.roundOut();
1070 if (ibounds.isEmpty()) {
1071 continue;
1072 }
1073 SkImageInfo image_info = SkImageInfo::MakeN32Premul(ibounds.width(), ibounds.height());
1074 sk_sp<SkSurface> surface(SkSurface::MakeRaster(image_info));
1075 SkASSERT(surface);
1076 SkCanvas* canvas = surface->getCanvas();
1077 canvas->clear(0);
1078 SkPixmap pix;
1079 surface->peekPixels(&pix);
1080 canvas->drawSimpleText(&gid,
1081 sizeof(gid),
1082 SkTextEncoding::kGlyphID,
1083 -bounds.fLeft,
1084 -bounds.fTop,
1085 font,
1086 paint);
1087 surface->flushAndSubmit();
1088 sk_sp<SkImage> image = surface->makeImageSnapshot();
1089 sk_sp<SkData> data = image->encodeToData(SkEncodedImageFormat::kPNG, 100);
1090
1091 // The originOffset values are difficult to use as DirectWrite and FreeType interpret
1092 // the origin to be the initial glyph position on the baseline, but CoreGraphics
1093 // interprets the origin to be the lower left of the cbox of the outline in the 'glyf'
1094 // table.
1095 //#define SK_SBIX_LIKE_FT
1096 //#define SK_SBIX_LIKE_DW
1097 out->writeText(" <glyph name=\"glyf");
1098 out->writeHexAsText(i, 4);
1099 out->writeText("\" graphicType=\"png \" originOffsetX=\"");
1100 #if defined(SK_SBIX_LIKE_FT) || defined(SK_SBIX_LIKE_DW)
1101 out->writeDecAsText(bounds.fLeft);
1102 #else
1103 out->writeDecAsText(0);
1104 #endif
1105 // DirectWrite and CoreGraphics use positive values of originOffsetY to push the
1106 // image visually up (but from different origins).
1107 // FreeType uses positive values to push the image down.
1108 out->writeText("\" originOffsetY=\"");
1109 #if defined(SK_SBIX_LIKE_FT)
1110 out->writeScalarAsText(bounds.fBottom);
1111 #elif defined(SK_SBIX_LIKE_DW)
1112 out->writeScalarAsText(-bounds.fBottom);
1113 #else
1114 out->writeDecAsText(0);
1115 #endif
1116 out->writeText("\">\n");
1117
1118 out->writeText(" <hexdata>");
1119 uint8_t const* bytes = data->bytes();
1120 for (size_t j = 0; j < data->size(); ++j) {
1121 if ((j % 0x10) == 0x0) {
1122 out->writeText("\n ");
1123 } else if (((j - 1) % 0x4) == 0x3) {
1124 out->writeText(" ");
1125 }
1126 out->writeHexAsText(bytes[j], 2);
1127 }
1128 out->writeText("\n");
1129 out->writeText(" </hexdata>\n");
1130 out->writeText(" </glyph>\n");
1131 }
1132 out->writeText(" </strike>\n");
1133 }
1134 out->writeText(" </sbix>\n");
1135 out->writeText("</ttFont>\n");
1136 }
1137
1138 namespace {
1139
convert_noninflect_cubic_to_quads(const SkPoint p[4],SkScalar toleranceSqd,SkTArray<SkPoint,true> * quads,int sublevel=0)1140 void convert_noninflect_cubic_to_quads(const SkPoint p[4],
1141 SkScalar toleranceSqd,
1142 SkTArray<SkPoint, true>* quads,
1143 int sublevel = 0) {
1144 // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is
1145 // 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].
1146
1147 SkVector ab = p[1] - p[0];
1148 SkVector dc = p[2] - p[3];
1149
1150 if (SkPointPriv::LengthSqd(ab) < SK_ScalarNearlyZero) {
1151 if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
1152 SkPoint* degQuad = quads->push_back_n(3);
1153 degQuad[0] = p[0];
1154 degQuad[1] = p[0];
1155 degQuad[2] = p[3];
1156 return;
1157 }
1158 ab = p[2] - p[0];
1159 }
1160 if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
1161 dc = p[1] - p[3];
1162 }
1163
1164 static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2;
1165 static const int kMaxSubdivs = 10;
1166
1167 ab.scale(kLengthScale);
1168 dc.scale(kLengthScale);
1169
1170 // e0 and e1 are extrapolations along vectors ab and dc.
1171 SkVector c0 = p[0];
1172 c0 += ab;
1173 SkVector c1 = p[3];
1174 c1 += dc;
1175
1176 SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : SkPointPriv::DistanceToSqd(c0, c1);
1177 if (dSqd < toleranceSqd) {
1178 SkPoint cAvg = c0;
1179 cAvg += c1;
1180 cAvg.scale(SK_ScalarHalf);
1181
1182 SkPoint* pts = quads->push_back_n(3);
1183 pts[0] = p[0];
1184 pts[1] = cAvg;
1185 pts[2] = p[3];
1186 return;
1187 }
1188 SkPoint choppedPts[7];
1189 SkChopCubicAtHalf(p, choppedPts);
1190 convert_noninflect_cubic_to_quads(choppedPts + 0, toleranceSqd, quads, sublevel + 1);
1191 convert_noninflect_cubic_to_quads(choppedPts + 3, toleranceSqd, quads, sublevel + 1);
1192 }
1193
convertCubicToQuads(const SkPoint p[4],SkScalar tolScale,SkTArray<SkPoint,true> * quads)1194 void convertCubicToQuads(const SkPoint p[4], SkScalar tolScale, SkTArray<SkPoint, true>* quads) {
1195 if (!p[0].isFinite() || !p[1].isFinite() || !p[2].isFinite() || !p[3].isFinite()) {
1196 return;
1197 }
1198 SkPoint chopped[10];
1199 int count = SkChopCubicAtInflections(p, chopped);
1200
1201 const SkScalar tolSqd = SkScalarSquare(tolScale);
1202
1203 for (int i = 0; i < count; ++i) {
1204 SkPoint* cubic = chopped + 3 * i;
1205 convert_noninflect_cubic_to_quads(cubic, tolSqd, quads);
1206 }
1207 }
1208
path_to_quads(const SkPath & path,SkPath * quadPath)1209 void path_to_quads(const SkPath& path, SkPath* quadPath) {
1210 quadPath->reset();
1211 SkTArray<SkPoint, true> qPts;
1212 SkAutoConicToQuads converter;
1213 const SkPoint* quadPts;
1214 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
1215 switch (verb) {
1216 case SkPathVerb::kMove: quadPath->moveTo(pts[0].fX, pts[0].fY); break;
1217 case SkPathVerb::kLine: quadPath->lineTo(pts[1].fX, pts[1].fY); break;
1218 case SkPathVerb::kQuad:
1219 quadPath->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
1220 break;
1221 case SkPathVerb::kCubic:
1222 qPts.clear();
1223 convertCubicToQuads(pts, SK_Scalar1, &qPts);
1224 for (int i = 0; i < qPts.size(); i += 3) {
1225 quadPath->quadTo(
1226 qPts[i + 1].fX, qPts[i + 1].fY, qPts[i + 2].fX, qPts[i + 2].fY);
1227 }
1228 break;
1229 case SkPathVerb::kConic:
1230 quadPts = converter.computeQuads(pts, *w, SK_Scalar1);
1231 for (int i = 0; i < converter.countQuads(); ++i) {
1232 quadPath->quadTo(quadPts[i * 2 + 1].fX,
1233 quadPts[i * 2 + 1].fY,
1234 quadPts[i * 2 + 2].fX,
1235 quadPts[i * 2 + 2].fY);
1236 }
1237 break;
1238 case SkPathVerb::kClose: quadPath->close(); break;
1239 }
1240 }
1241 }
1242
1243 class SkCOLRCanvas : public SkNoDrawCanvas {
1244 public:
SkCOLRCanvas(SkRect glyphBounds,const TestSVGTypeface & typeface,SkGlyphID glyphId,TestSVGTypeface::GlyfInfo * glyf,SkTHashMap<SkColor,int> * colors,SkWStream * out)1245 SkCOLRCanvas(SkRect glyphBounds,
1246 const TestSVGTypeface& typeface,
1247 SkGlyphID glyphId,
1248 TestSVGTypeface::GlyfInfo* glyf,
1249 SkTHashMap<SkColor, int>* colors,
1250 SkWStream* out)
1251 : SkNoDrawCanvas(glyphBounds.roundOut().width(), glyphBounds.roundOut().height())
1252 , fBaselineOffset(glyphBounds.top())
1253 , fTypeface(typeface)
1254 , fGlyphId(glyphId)
1255 , fGlyf(glyf)
1256 , fColors(colors)
1257 , fOut(out)
1258 , fLayerId(0) {}
1259
writePoint(SkScalar x,SkScalar y,bool on)1260 void writePoint(SkScalar x, SkScalar y, bool on) {
1261 fOut->writeText(" <pt x=\"");
1262 fOut->writeDecAsText(SkScalarRoundToInt(x));
1263 fOut->writeText("\" y=\"");
1264 fOut->writeDecAsText(SkScalarRoundToInt(y));
1265 fOut->writeText("\" on=\"");
1266 fOut->write8(on ? '1' : '0');
1267 fOut->writeText("\"/>\n");
1268 }
writePath(const SkPath & path,bool layer)1269 SkIRect writePath(const SkPath& path, bool layer) {
1270 // Convert to quads.
1271 SkPath quads;
1272 path_to_quads(path, &quads);
1273
1274 SkRect bounds = quads.computeTightBounds();
1275 SkIRect ibounds = bounds.roundOut();
1276 // The bounds will be re-calculated anyway.
1277 fOut->writeText(" <TTGlyph name=\"glyf");
1278 fOut->writeHexAsText(fGlyphId, 4);
1279 if (layer) {
1280 fOut->writeText("l");
1281 fOut->writeHexAsText(fLayerId, 4);
1282 }
1283 fOut->writeText("\" xMin=\"");
1284 fOut->writeDecAsText(ibounds.fLeft);
1285 fOut->writeText("\" yMin=\"");
1286 fOut->writeDecAsText(ibounds.fTop);
1287 fOut->writeText("\" xMax=\"");
1288 fOut->writeDecAsText(ibounds.fRight);
1289 fOut->writeText("\" yMax=\"");
1290 fOut->writeDecAsText(ibounds.fBottom);
1291 fOut->writeText("\">\n");
1292
1293 bool contourOpen = false;
1294 for (auto [verb, pts, w] : SkPathPriv::Iterate(quads)) {
1295 switch (verb) {
1296 case SkPathVerb::kMove:
1297 if (contourOpen) {
1298 fOut->writeText(" </contour>\n");
1299 contourOpen = false;
1300 }
1301 break;
1302 case SkPathVerb::kLine:
1303 if (!contourOpen) {
1304 fOut->writeText(" <contour>\n");
1305 this->writePoint(pts[0].fX, pts[0].fY, true);
1306 contourOpen = true;
1307 }
1308 this->writePoint(pts[1].fX, pts[1].fY, true);
1309 break;
1310 case SkPathVerb::kQuad:
1311 if (!contourOpen) {
1312 fOut->writeText(" <contour>\n");
1313 this->writePoint(pts[0].fX, pts[0].fY, true);
1314 contourOpen = true;
1315 }
1316 this->writePoint(pts[1].fX, pts[1].fY, false);
1317 this->writePoint(pts[2].fX, pts[2].fY, true);
1318 break;
1319 case SkPathVerb::kClose:
1320 if (contourOpen) {
1321 fOut->writeText(" </contour>\n");
1322 contourOpen = false;
1323 }
1324 break;
1325 default: SkDEBUGFAIL("bad verb"); return ibounds;
1326 }
1327 }
1328 if (contourOpen) {
1329 fOut->writeText(" </contour>\n");
1330 }
1331
1332 // Required to write out an instructions tag.
1333 fOut->writeText(" <instructions/>\n");
1334 fOut->writeText(" </TTGlyph>\n");
1335 return ibounds;
1336 }
1337
onDrawRect(const SkRect & rect,const SkPaint & paint)1338 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
1339 SkPath path;
1340 path.addRect(rect);
1341 this->drawPath(path, paint);
1342 }
1343
onDrawOval(const SkRect & oval,const SkPaint & paint)1344 void onDrawOval(const SkRect& oval, const SkPaint& paint) override {
1345 SkPath path;
1346 path.addOval(oval);
1347 this->drawPath(path, paint);
1348 }
1349
onDrawArc(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,bool useCenter,const SkPaint & paint)1350 void onDrawArc(const SkRect& oval,
1351 SkScalar startAngle,
1352 SkScalar sweepAngle,
1353 bool useCenter,
1354 const SkPaint& paint) override {
1355 SkPath path;
1356 bool fillNoPathEffect = SkPaint::kFill_Style == paint.getStyle() && !paint.getPathEffect();
1357 SkPathPriv::CreateDrawArcPath(
1358 &path, oval, startAngle, sweepAngle, useCenter, fillNoPathEffect);
1359 this->drawPath(path, paint);
1360 }
1361
onDrawRRect(const SkRRect & rrect,const SkPaint & paint)1362 void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
1363 SkPath path;
1364 path.addRRect(rrect);
1365 this->drawPath(path, paint);
1366 }
1367
onDrawPath(const SkPath & platonicPath,const SkPaint & originalPaint)1368 void onDrawPath(const SkPath& platonicPath, const SkPaint& originalPaint) override {
1369 SkPaint paint = originalPaint;
1370 SkPath path = platonicPath;
1371
1372 // Apply the path effect.
1373 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
1374 bool fill = skpathutils::FillPathWithPaint(path, paint, &path);
1375
1376 paint.setPathEffect(nullptr);
1377 if (fill) {
1378 paint.setStyle(SkPaint::kFill_Style);
1379 } else {
1380 paint.setStyle(SkPaint::kStroke_Style);
1381 paint.setStrokeWidth(0);
1382 }
1383 }
1384
1385 // Apply the matrix.
1386 SkMatrix m = this->getTotalMatrix();
1387 // If done to the canvas then everything would get clipped out.
1388 m.postTranslate(0, fBaselineOffset); // put the baseline at 0
1389 m.postScale(1, -1); // and flip it since OpenType is y-up.
1390 path.transform(m);
1391
1392 // While creating the default glyf, union with dark colors and intersect with bright colors.
1393 SkColor color = paint.getColor();
1394 SkPathOp op;
1395 if (fTypeface.getPathOp(color, &op)) {
1396 fBasePath.add(path, op);
1397 }
1398 SkIRect bounds = this->writePath(path, true);
1399
1400 // The CPAL table has the concept of a 'current color' which is index 0xFFFF.
1401 // Mark any layer drawn in 'currentColor' as having this special index.
1402 // The value of 'currentColor' here should a color which causes this layer to union into the
1403 // default glyf.
1404 constexpr SkColor currentColor = 0xFF2B0000;
1405
1406 int colorIndex;
1407 if (color == currentColor) {
1408 colorIndex = 0xFFFF;
1409 } else {
1410 int* colorIndexPtr = fColors->find(color);
1411 if (colorIndexPtr) {
1412 colorIndex = *colorIndexPtr;
1413 } else {
1414 colorIndex = fColors->count();
1415 fColors->set(color, colorIndex);
1416 }
1417 }
1418 fGlyf->fLayers.emplace_back(colorIndex, bounds);
1419
1420 ++fLayerId;
1421 }
1422
finishGlyph()1423 void finishGlyph() {
1424 SkPath baseGlyph;
1425 fBasePath.resolve(&baseGlyph);
1426 fGlyf->fBounds = this->writePath(baseGlyph, false);
1427 }
1428
1429 private:
1430 SkScalar fBaselineOffset;
1431 const TestSVGTypeface& fTypeface;
1432 SkGlyphID fGlyphId;
1433 TestSVGTypeface::GlyfInfo* fGlyf;
1434 SkTHashMap<SkColor, int>* fColors;
1435 SkWStream* const fOut;
1436 SkOpBuilder fBasePath;
1437 int fLayerId;
1438 };
1439
1440 } // namespace
1441
exportTtxColr(SkWStream * out) const1442 void TestSVGTypeface::exportTtxColr(SkWStream* out) const {
1443 out->writeText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1444 out->writeText("<ttFont sfntVersion=\"\\x00\\x01\\x00\\x00\" ttLibVersion=\"3.19\">\n");
1445
1446 SkTHashMap<SkColor, int> colors;
1447 SkTArray<GlyfInfo> glyfInfos(fGlyphCount);
1448
1449 // Need to know all the glyphs up front for the common tables.
1450 SkDynamicMemoryWStream glyfOut;
1451 glyfOut.writeText(" <glyf>\n");
1452 for (int i = 0; i < fGlyphCount; ++i) {
1453 const TestSVGTypeface::Glyph& glyphData = this->fGlyphs[i];
1454
1455 SkSize containerSize = glyphData.size();
1456 SkRect bounds = SkRect::MakeXYWH(glyphData.fOrigin.fX,
1457 -glyphData.fOrigin.fY,
1458 containerSize.fWidth,
1459 containerSize.fHeight);
1460 SkCOLRCanvas canvas(bounds, *this, i, &glyfInfos.emplace_back(), &colors, &glyfOut);
1461 glyphData.render(&canvas);
1462 canvas.finishGlyph();
1463 }
1464 glyfOut.writeText(" </glyf>\n");
1465
1466 this->exportTtxCommon(out, "COLR", &glyfInfos);
1467
1468 // The loca table will be re-calculated, but if we don't write one we don't get one.
1469 out->writeText(" <loca/>\n");
1470
1471 std::unique_ptr<SkStreamAsset> glyfStream = glyfOut.detachAsStream();
1472 out->writeStream(glyfStream.get(), glyfStream->getLength());
1473
1474 out->writeText(" <COLR>\n");
1475 out->writeText(" <version value=\"0\"/>\n");
1476 for (int i = 0; i < fGlyphCount; ++i) {
1477 if (glyfInfos[i].fLayers.empty()) {
1478 continue;
1479 }
1480 if (glyfInfos[i].fBounds.isEmpty()) {
1481 SkDebugf("Glyph %d is empty but has layers.\n", i);
1482 }
1483 out->writeText(" <ColorGlyph name=\"glyf");
1484 out->writeHexAsText(i, 4);
1485 out->writeText("\">\n");
1486 for (int j = 0; j < glyfInfos[i].fLayers.size(); ++j) {
1487 const int colorIndex = glyfInfos[i].fLayers[j].fLayerColorIndex;
1488 out->writeText(" <layer colorID=\"");
1489 out->writeDecAsText(colorIndex);
1490 out->writeText("\" name=\"glyf");
1491 out->writeHexAsText(i, 4);
1492 out->writeText("l");
1493 out->writeHexAsText(j, 4);
1494 out->writeText("\"/>\n");
1495 }
1496 out->writeText(" </ColorGlyph>\n");
1497 }
1498 out->writeText(" </COLR>\n");
1499
1500 // The colors must be written in order, the 'index' is ignored by ttx.
1501 AutoTMalloc<SkColor> colorsInOrder(colors.count());
1502 colors.foreach ([&colorsInOrder](const SkColor& c, const int* i) { colorsInOrder[*i] = c; });
1503 out->writeText(" <CPAL>\n");
1504 out->writeText(" <version value=\"0\"/>\n");
1505 out->writeText(" <numPaletteEntries value=\"");
1506 out->writeDecAsText(colors.count());
1507 out->writeText("\"/>\n");
1508 out->writeText(" <palette index=\"0\">\n");
1509 for (int i = 0; i < colors.count(); ++i) {
1510 SkColor c = colorsInOrder[i];
1511 out->writeText(" <color index=\"");
1512 out->writeDecAsText(i);
1513 out->writeText("\" value=\"#");
1514 out->writeHexAsText(SkColorGetR(c), 2);
1515 out->writeHexAsText(SkColorGetG(c), 2);
1516 out->writeHexAsText(SkColorGetB(c), 2);
1517 out->writeHexAsText(SkColorGetA(c), 2);
1518 out->writeText("\"/>\n");
1519 }
1520 out->writeText(" </palette>\n");
1521 out->writeText(" </CPAL>\n");
1522
1523 out->writeText("</ttFont>\n");
1524 }
1525 #endif // SK_ENABLE_SVG
1526