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