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