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