• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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