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