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