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