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