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