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