12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442 |
- /*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "tools/fonts/TestSVGTypeface.h"
- #ifdef SK_XML
- #include "experimental/svg/model/SkSVGDOM.h"
- #include "include/core/SkBitmap.h"
- #include "include/core/SkCanvas.h"
- #include "include/core/SkColor.h"
- #include "include/core/SkData.h"
- #include "include/core/SkEncodedImageFormat.h"
- #include "include/core/SkFontStyle.h"
- #include "include/core/SkImage.h"
- #include "include/core/SkImageInfo.h"
- #include "include/core/SkMatrix.h"
- #include "include/core/SkPath.h"
- #include "include/core/SkPathEffect.h"
- #include "include/core/SkPixmap.h"
- #include "include/core/SkRRect.h"
- #include "include/core/SkSize.h"
- #include "include/core/SkStream.h"
- #include "include/core/SkSurface.h"
- #include "include/pathops/SkPathOps.h"
- #include "include/private/SkTDArray.h"
- #include "include/private/SkTemplates.h"
- #include "include/utils/SkNoDrawCanvas.h"
- #include "src/core/SkAdvancedTypefaceMetrics.h"
- #include "src/core/SkFontDescriptor.h"
- #include "src/core/SkFontPriv.h"
- #include "src/core/SkGeometry.h"
- #include "src/core/SkGlyph.h"
- #include "src/core/SkMask.h"
- #include "src/core/SkPaintPriv.h"
- #include "src/core/SkPathPriv.h"
- #include "src/core/SkPointPriv.h"
- #include "src/core/SkScalerContext.h"
- #include "src/core/SkUtils.h"
- #include "src/sfnt/SkOTUtils.h"
- #include "tools/Resources.h"
- #include <utility>
- class SkDescriptor;
- TestSVGTypeface::TestSVGTypeface(const char* name,
- int upem,
- const SkFontMetrics& fontMetrics,
- SkSpan<const SkSVGTestTypefaceGlyphData> data,
- const SkFontStyle& style)
- : SkTypeface(style, false)
- , fName(name)
- , fUpem(upem)
- , fFontMetrics(fontMetrics)
- , fGlyphs(new Glyph[data.size()])
- , fGlyphCount(data.size()) {
- for (size_t i = 0; i < data.size(); ++i) {
- const SkSVGTestTypefaceGlyphData& datum = data[i];
- fCMap.set(datum.fUnicode, i);
- fGlyphs[i].fAdvance = datum.fAdvance;
- fGlyphs[i].fOrigin = datum.fOrigin;
- fGlyphs[i].fResourcePath = datum.fSvgResourcePath;
- }
- }
- template <typename Fn>
- void TestSVGTypeface::Glyph::withSVG(Fn&& fn) const {
- SkAutoMutexExclusive lock(fSvgMutex);
- if (!fParsedSvg) {
- fParsedSvg = true;
- std::unique_ptr<SkStreamAsset> stream = GetResourceAsStream(fResourcePath);
- if (!stream) {
- return;
- }
- sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromStream(*stream.get());
- if (!svg) {
- return;
- }
- if (svg->containerSize().isEmpty()) {
- return;
- }
- fSvg = std::move(svg);
- }
- if (fSvg) {
- fn(*fSvg);
- }
- }
- SkSize TestSVGTypeface::Glyph::size() const {
- SkSize size = SkSize::MakeEmpty();
- this->withSVG([&](const SkSVGDOM& svg){
- size = svg.containerSize();
- });
- return size;
- }
- void TestSVGTypeface::Glyph::render(SkCanvas* canvas) const {
- this->withSVG([&](const SkSVGDOM& svg){
- svg.render(canvas);
- });
- }
- TestSVGTypeface::~TestSVGTypeface() {}
- TestSVGTypeface::Glyph::Glyph() : fOrigin{0, 0}, fAdvance(0) {}
- TestSVGTypeface::Glyph::~Glyph() {}
- void TestSVGTypeface::getAdvance(SkGlyph* glyph) const {
- SkGlyphID glyphID = glyph->getGlyphID();
- glyphID = glyphID < fGlyphCount ? glyphID : 0;
- glyph->fAdvanceX = fGlyphs[glyphID].fAdvance;
- glyph->fAdvanceY = 0;
- }
- void TestSVGTypeface::getFontMetrics(SkFontMetrics* metrics) const { *metrics = fFontMetrics; }
- void TestSVGTypeface::onFilterRec(SkScalerContextRec* rec) const {
- rec->setHinting(SkFontHinting::kNone);
- }
- void TestSVGTypeface::getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const {
- SkDEBUGCODE(unsigned glyphCount = this->countGlyphs());
- fCMap.foreach ([=](const SkUnichar& c, const SkGlyphID& g) {
- SkASSERT(g < glyphCount);
- glyphToUnicode[g] = c;
- });
- }
- std::unique_ptr<SkAdvancedTypefaceMetrics> TestSVGTypeface::onGetAdvancedMetrics() const {
- std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
- info->fFontName = fName;
- return info;
- }
- void TestSVGTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const {
- desc->setFamilyName(fName.c_str());
- desc->setStyle(this->fontStyle());
- *isLocal = false;
- }
- void TestSVGTypeface::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
- for (int i = 0; i < count; i++) {
- SkGlyphID* g = fCMap.find(uni[i]);
- glyphs[i] = g ? *g : 0;
- }
- }
- void TestSVGTypeface::onGetFamilyName(SkString* familyName) const { *familyName = fName; }
- SkTypeface::LocalizedStrings* TestSVGTypeface::onCreateFamilyNameIterator() const {
- SkString familyName(fName);
- SkString language("und"); // undetermined
- return new SkOTUtils::LocalizedStrings_SingleName(familyName, language);
- }
- class SkTestSVGScalerContext : public SkScalerContext {
- public:
- SkTestSVGScalerContext(sk_sp<TestSVGTypeface> face,
- const SkScalerContextEffects& effects,
- const SkDescriptor* desc)
- : SkScalerContext(std::move(face), effects, desc) {
- fRec.getSingleMatrix(&fMatrix);
- SkScalar upem = this->getTestSVGTypeface()->fUpem;
- fMatrix.preScale(1.f / upem, 1.f / upem);
- }
- protected:
- TestSVGTypeface* getTestSVGTypeface() const {
- return static_cast<TestSVGTypeface*>(this->getTypeface());
- }
- unsigned generateGlyphCount() override { return this->getTestSVGTypeface()->countGlyphs(); }
- bool generateAdvance(SkGlyph* glyph) override {
- this->getTestSVGTypeface()->getAdvance(glyph);
- const SkVector advance =
- fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX), SkFloatToScalar(glyph->fAdvanceY));
- glyph->fAdvanceX = SkScalarToFloat(advance.fX);
- glyph->fAdvanceY = SkScalarToFloat(advance.fY);
- return true;
- }
- void generateMetrics(SkGlyph* glyph) override {
- SkGlyphID glyphID = glyph->getGlyphID();
- glyphID = glyphID < this->getTestSVGTypeface()->fGlyphCount ? glyphID : 0;
- glyph->zeroMetrics();
- glyph->fMaskFormat = SkMask::kARGB32_Format;
- this->generateAdvance(glyph);
- TestSVGTypeface::Glyph& glyphData = this->getTestSVGTypeface()->fGlyphs[glyphID];
- SkSize containerSize = glyphData.size();
- SkRect newBounds = SkRect::MakeXYWH(glyphData.fOrigin.fX,
- -glyphData.fOrigin.fY,
- containerSize.fWidth,
- containerSize.fHeight);
- fMatrix.mapRect(&newBounds);
- SkScalar dx = SkFixedToScalar(glyph->getSubXFixed());
- SkScalar dy = SkFixedToScalar(glyph->getSubYFixed());
- newBounds.offset(dx, dy);
- SkIRect ibounds;
- newBounds.roundOut(&ibounds);
- glyph->fLeft = ibounds.fLeft;
- glyph->fTop = ibounds.fTop;
- glyph->fWidth = ibounds.width();
- glyph->fHeight = ibounds.height();
- }
- void generateImage(const SkGlyph& glyph) override {
- SkGlyphID glyphID = glyph.getGlyphID();
- glyphID = glyphID < this->getTestSVGTypeface()->fGlyphCount ? glyphID : 0;
- SkBitmap bm;
- // TODO: this should be SkImageInfo::MakeS32 when that passes all the tests.
- bm.installPixels(SkImageInfo::MakeN32(glyph.fWidth, glyph.fHeight, kPremul_SkAlphaType),
- glyph.fImage,
- glyph.rowBytes());
- bm.eraseColor(0);
- TestSVGTypeface::Glyph& glyphData = this->getTestSVGTypeface()->fGlyphs[glyphID];
- SkScalar dx = SkFixedToScalar(glyph.getSubXFixed());
- SkScalar dy = SkFixedToScalar(glyph.getSubYFixed());
- SkCanvas canvas(bm);
- canvas.translate(-glyph.fLeft, -glyph.fTop);
- canvas.translate(dx, dy);
- canvas.concat(fMatrix);
- canvas.translate(glyphData.fOrigin.fX, -glyphData.fOrigin.fY);
- glyphData.render(&canvas);
- }
- bool generatePath(SkGlyphID glyph, SkPath* path) override {
- path->reset();
- return false;
- }
- void generateFontMetrics(SkFontMetrics* metrics) override {
- this->getTestSVGTypeface()->getFontMetrics(metrics);
- SkFontPriv::ScaleFontMetrics(metrics, fMatrix.getScaleY());
- }
- private:
- SkMatrix fMatrix;
- };
- SkScalerContext* TestSVGTypeface::onCreateScalerContext(const SkScalerContextEffects& e,
- const SkDescriptor* desc) const {
- return new SkTestSVGScalerContext(sk_ref_sp(const_cast<TestSVGTypeface*>(this)), e, desc);
- }
- sk_sp<TestSVGTypeface> TestSVGTypeface::Default() {
- // Recommended that the first four be .notdef, .null, CR, space
- constexpr const static SkSVGTestTypefaceGlyphData glyphs[] = {
- {"fonts/svg/notdef.svg", {100, 800}, 800, 0x0}, // .notdef
- {"fonts/svg/empty.svg", {0, 0}, 800, 0x0020}, // space
- {"fonts/svg/diamond.svg", {100, 800}, 800, 0x2662}, // ♢
- {"fonts/svg/smile.svg", {0, 800}, 800, 0x1F600}, // 😀
- };
- SkFontMetrics metrics;
- metrics.fFlags = SkFontMetrics::kUnderlineThicknessIsValid_Flag |
- SkFontMetrics::kUnderlinePositionIsValid_Flag |
- SkFontMetrics::kStrikeoutThicknessIsValid_Flag |
- SkFontMetrics::kStrikeoutPositionIsValid_Flag;
- metrics.fTop = -800;
- metrics.fAscent = -800;
- metrics.fDescent = 200;
- metrics.fBottom = 200;
- metrics.fLeading = 100;
- metrics.fAvgCharWidth = 1000;
- metrics.fMaxCharWidth = 1000;
- metrics.fXMin = 0;
- metrics.fXMax = 1000;
- metrics.fXHeight = 500;
- metrics.fCapHeight = 700;
- metrics.fUnderlineThickness = 40;
- metrics.fUnderlinePosition = 20;
- metrics.fStrikeoutThickness = 20;
- metrics.fStrikeoutPosition = -400;
- class DefaultTypeface : public TestSVGTypeface {
- using TestSVGTypeface::TestSVGTypeface;
- bool getPathOp(SkColor color, SkPathOp* op) const override {
- if ((SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color)) / 3 > 0x20) {
- *op = SkPathOp::kDifference_SkPathOp;
- } else {
- *op = SkPathOp::kUnion_SkPathOp;
- }
- return true;
- }
- };
- return sk_make_sp<DefaultTypeface>("Emoji",
- 1000,
- metrics,
- SkMakeSpan(glyphs),
- SkFontStyle::Normal());
- }
- sk_sp<TestSVGTypeface> TestSVGTypeface::Planets() {
- // Recommended that the first four be .notdef, .null, CR, space
- constexpr const static SkSVGTestTypefaceGlyphData glyphs[] = {
- {"fonts/svg/planets/pluto.svg", {0, 20}, 60, 0x0}, // .notdef
- {"fonts/svg/empty.svg", {0, 0}, 400, 0x0020}, // space
- {"fonts/svg/planets/mercury.svg", {0, 45}, 120, 0x263F}, // ☿
- {"fonts/svg/planets/venus.svg", {0, 100}, 240, 0x2640}, // ♀
- {"fonts/svg/planets/earth.svg", {0, 100}, 240, 0x2641}, // ♁
- {"fonts/svg/planets/mars.svg", {0, 50}, 130, 0x2642}, // ♂
- {"fonts/svg/planets/jupiter.svg", {0, 1000}, 2200, 0x2643}, // ♃
- {"fonts/svg/planets/saturn.svg", {-300, 1500}, 2600, 0x2644}, // ♄
- {"fonts/svg/planets/uranus.svg", {0, 375}, 790, 0x2645}, // ♅
- {"fonts/svg/planets/neptune.svg", {0, 350}, 740, 0x2646}, // ♆
- };
- SkFontMetrics metrics;
- metrics.fFlags = SkFontMetrics::kUnderlineThicknessIsValid_Flag |
- SkFontMetrics::kUnderlinePositionIsValid_Flag |
- SkFontMetrics::kStrikeoutThicknessIsValid_Flag |
- SkFontMetrics::kStrikeoutPositionIsValid_Flag;
- metrics.fTop = -1500;
- metrics.fAscent = -200;
- metrics.fDescent = 50;
- metrics.fBottom = 1558;
- metrics.fLeading = 10;
- metrics.fAvgCharWidth = 200;
- metrics.fMaxCharWidth = 200;
- metrics.fXMin = -300;
- metrics.fXMax = 2566;
- metrics.fXHeight = 100;
- metrics.fCapHeight = 180;
- metrics.fUnderlineThickness = 8;
- metrics.fUnderlinePosition = 2;
- metrics.fStrikeoutThickness = 2;
- metrics.fStrikeoutPosition = -80;
- class PlanetTypeface : public TestSVGTypeface {
- using TestSVGTypeface::TestSVGTypeface;
- bool getPathOp(SkColor color, SkPathOp* op) const override {
- *op = SkPathOp::kUnion_SkPathOp;
- return true;
- }
- };
- return sk_make_sp<PlanetTypeface>("Planets",
- 200,
- metrics,
- SkMakeSpan(glyphs),
- SkFontStyle::Normal());
- }
- void TestSVGTypeface::exportTtxCommon(SkWStream* out,
- const char* type,
- const SkTArray<GlyfInfo>* glyfInfo) const {
- int totalGlyphs = fGlyphCount;
- out->writeText(" <GlyphOrder>\n");
- for (int i = 0; i < fGlyphCount; ++i) {
- out->writeText(" <GlyphID name=\"glyf");
- out->writeHexAsText(i, 4);
- out->writeText("\"/>\n");
- }
- if (glyfInfo) {
- for (int i = 0; i < fGlyphCount; ++i) {
- for (int j = 0; j < (*glyfInfo)[i].fLayers.count(); ++j) {
- out->writeText(" <GlyphID name=\"glyf");
- out->writeHexAsText(i, 4);
- out->writeText("l");
- out->writeHexAsText(j, 4);
- out->writeText("\"/>\n");
- ++totalGlyphs;
- }
- }
- }
- out->writeText(" </GlyphOrder>\n");
- out->writeText(" <head>\n");
- out->writeText(" <tableVersion value=\"1.0\"/>\n");
- out->writeText(" <fontRevision value=\"1.0\"/>\n");
- out->writeText(" <checkSumAdjustment value=\"0xa9c3274\"/>\n");
- out->writeText(" <magicNumber value=\"0x5f0f3cf5\"/>\n");
- out->writeText(" <flags value=\"00000000 00011011\"/>\n");
- out->writeText(" <unitsPerEm value=\"");
- out->writeDecAsText(fUpem);
- out->writeText("\"/>\n");
- out->writeText(" <created value=\"Thu Feb 15 12:55:49 2018\"/>\n");
- out->writeText(" <modified value=\"Thu Feb 15 12:55:49 2018\"/>\n");
- // TODO: not recalculated for bitmap fonts?
- out->writeText(" <xMin value=\"");
- out->writeScalarAsText(fFontMetrics.fXMin);
- out->writeText("\"/>\n");
- out->writeText(" <yMin value=\"");
- out->writeScalarAsText(-fFontMetrics.fBottom);
- out->writeText("\"/>\n");
- out->writeText(" <xMax value=\"");
- out->writeScalarAsText(fFontMetrics.fXMax);
- out->writeText("\"/>\n");
- out->writeText(" <yMax value=\"");
- out->writeScalarAsText(-fFontMetrics.fTop);
- out->writeText("\"/>\n");
- char macStyle[16] = {
- '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'};
- if (this->fontStyle().weight() >= SkFontStyle::Bold().weight()) {
- macStyle[0xF - 0x0] = '1'; // Bold
- }
- switch (this->fontStyle().slant()) {
- case SkFontStyle::kUpright_Slant: break;
- case SkFontStyle::kItalic_Slant:
- macStyle[0xF - 0x1] = '1'; // Italic
- break;
- case SkFontStyle::kOblique_Slant:
- macStyle[0xF - 0x1] = '1'; // Italic
- break;
- default: SK_ABORT("Unknown slant.");
- }
- if (this->fontStyle().width() <= SkFontStyle::kCondensed_Width) {
- macStyle[0xF - 0x5] = '1'; // Condensed
- } else if (this->fontStyle().width() >= SkFontStyle::kExpanded_Width) {
- macStyle[0xF - 0x6] = '1'; // Extended
- }
- out->writeText(" <macStyle value=\"");
- out->write(macStyle, 8);
- out->writeText(" ");
- out->write(macStyle + 8, 8);
- out->writeText("\"/>\n");
- out->writeText(" <lowestRecPPEM value=\"8\"/>\n");
- out->writeText(" <fontDirectionHint value=\"2\"/>\n");
- out->writeText(" <indexToLocFormat value=\"0\"/>\n");
- out->writeText(" <glyphDataFormat value=\"0\"/>\n");
- out->writeText(" </head>\n");
- out->writeText(" <hhea>\n");
- out->writeText(" <tableVersion value=\"0x00010000\"/>\n");
- out->writeText(" <ascent value=\"");
- out->writeDecAsText(-fFontMetrics.fAscent);
- out->writeText("\"/>\n");
- out->writeText(" <descent value=\"");
- out->writeDecAsText(-fFontMetrics.fDescent);
- out->writeText("\"/>\n");
- out->writeText(" <lineGap value=\"");
- out->writeDecAsText(fFontMetrics.fLeading);
- out->writeText("\"/>\n");
- out->writeText(" <advanceWidthMax value=\"0\"/>\n");
- out->writeText(" <minLeftSideBearing value=\"0\"/>\n");
- out->writeText(" <minRightSideBearing value=\"0\"/>\n");
- out->writeText(" <xMaxExtent value=\"");
- out->writeScalarAsText(fFontMetrics.fXMax - fFontMetrics.fXMin);
- out->writeText("\"/>\n");
- out->writeText(" <caretSlopeRise value=\"1\"/>\n");
- out->writeText(" <caretSlopeRun value=\"0\"/>\n");
- out->writeText(" <caretOffset value=\"0\"/>\n");
- out->writeText(" <reserved0 value=\"0\"/>\n");
- out->writeText(" <reserved1 value=\"0\"/>\n");
- out->writeText(" <reserved2 value=\"0\"/>\n");
- out->writeText(" <reserved3 value=\"0\"/>\n");
- out->writeText(" <metricDataFormat value=\"0\"/>\n");
- out->writeText(" <numberOfHMetrics value=\"0\"/>\n");
- out->writeText(" </hhea>\n");
- // Some of this table is going to be re-calculated, but we have to write it out anyway.
- out->writeText(" <maxp>\n");
- out->writeText(" <tableVersion value=\"0x10000\"/>\n");
- out->writeText(" <numGlyphs value=\"");
- out->writeDecAsText(totalGlyphs);
- out->writeText("\"/>\n");
- out->writeText(" <maxPoints value=\"4\"/>\n");
- out->writeText(" <maxContours value=\"1\"/>\n");
- out->writeText(" <maxCompositePoints value=\"0\"/>\n");
- out->writeText(" <maxCompositeContours value=\"0\"/>\n");
- out->writeText(" <maxZones value=\"1\"/>\n");
- out->writeText(" <maxTwilightPoints value=\"0\"/>\n");
- out->writeText(" <maxStorage value=\"0\"/>\n");
- out->writeText(" <maxFunctionDefs value=\"10\"/>\n");
- out->writeText(" <maxInstructionDefs value=\"0\"/>\n");
- out->writeText(" <maxStackElements value=\"512\"/>\n");
- out->writeText(" <maxSizeOfInstructions value=\"24\"/>\n");
- out->writeText(" <maxComponentElements value=\"0\"/>\n");
- out->writeText(" <maxComponentDepth value=\"0\"/>\n");
- out->writeText(" </maxp>\n");
- out->writeText(" <OS_2>\n");
- out->writeText(" <version value=\"4\"/>\n");
- out->writeText(" <xAvgCharWidth value=\"");
- out->writeScalarAsText(fFontMetrics.fAvgCharWidth);
- out->writeText("\"/>\n");
- out->writeText(" <usWeightClass value=\"");
- out->writeDecAsText(this->fontStyle().weight());
- out->writeText("\"/>\n");
- out->writeText(" <usWidthClass value=\"");
- out->writeDecAsText(this->fontStyle().width());
- out->writeText("\"/>\n");
- out->writeText(" <fsType value=\"00000000 00000000\"/>\n");
- out->writeText(" <ySubscriptXSize value=\"665\"/>\n");
- out->writeText(" <ySubscriptYSize value=\"716\"/>\n");
- out->writeText(" <ySubscriptXOffset value=\"0\"/>\n");
- out->writeText(" <ySubscriptYOffset value=\"143\"/>\n");
- out->writeText(" <ySuperscriptXSize value=\"665\"/>\n");
- out->writeText(" <ySuperscriptYSize value=\"716\"/>\n");
- out->writeText(" <ySuperscriptXOffset value=\"0\"/>\n");
- out->writeText(" <ySuperscriptYOffset value=\"491\"/>\n");
- out->writeText(" <yStrikeoutSize value=\"");
- out->writeScalarAsText(fFontMetrics.fStrikeoutThickness);
- out->writeText("\"/>\n");
- out->writeText(" <yStrikeoutPosition value=\"");
- out->writeScalarAsText(-fFontMetrics.fStrikeoutPosition);
- out->writeText("\"/>\n");
- out->writeText(" <sFamilyClass value=\"0\"/>\n");
- out->writeText(" <panose>\n");
- out->writeText(" <bFamilyType value=\"0\"/>\n");
- out->writeText(" <bSerifStyle value=\"0\"/>\n");
- out->writeText(" <bWeight value=\"0\"/>\n");
- out->writeText(" <bProportion value=\"0\"/>\n");
- out->writeText(" <bContrast value=\"0\"/>\n");
- out->writeText(" <bStrokeVariation value=\"0\"/>\n");
- out->writeText(" <bArmStyle value=\"0\"/>\n");
- out->writeText(" <bLetterForm value=\"0\"/>\n");
- out->writeText(" <bMidline value=\"0\"/>\n");
- out->writeText(" <bXHeight value=\"0\"/>\n");
- out->writeText(" </panose>\n");
- out->writeText(" <ulUnicodeRange1 value=\"00000000 00000000 00000000 00000001\"/>\n");
- out->writeText(" <ulUnicodeRange2 value=\"00010000 00000000 00000000 00000000\"/>\n");
- out->writeText(" <ulUnicodeRange3 value=\"00000000 00000000 00000000 00000000\"/>\n");
- out->writeText(" <ulUnicodeRange4 value=\"00000000 00000000 00000000 00000000\"/>\n");
- out->writeText(" <achVendID value=\"Skia\"/>\n");
- char fsSelection[16] = {
- '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'};
- fsSelection[0xF - 0x7] = '1'; // Use typo metrics
- if (this->fontStyle().weight() >= SkFontStyle::Bold().weight()) {
- fsSelection[0xF - 0x5] = '1'; // Bold
- }
- switch (this->fontStyle().slant()) {
- case SkFontStyle::kUpright_Slant:
- if (this->fontStyle().weight() < SkFontStyle::Bold().weight()) {
- fsSelection[0xF - 0x6] = '1'; // Not bold or italic, is regular
- }
- break;
- case SkFontStyle::kItalic_Slant:
- fsSelection[0xF - 0x0] = '1'; // Italic
- break;
- case SkFontStyle::kOblique_Slant:
- fsSelection[0xF - 0x0] = '1'; // Italic
- fsSelection[0xF - 0x9] = '1'; // Oblique
- break;
- default: SK_ABORT("Unknown slant.");
- }
- out->writeText(" <fsSelection value=\"");
- out->write(fsSelection, 8);
- out->writeText(" ");
- out->write(fsSelection + 8, 8);
- out->writeText("\"/>\n");
- out->writeText(" <usFirstCharIndex value=\"0\"/>\n");
- out->writeText(" <usLastCharIndex value=\"0\"/>\n");
- out->writeText(" <sTypoAscender value=\"");
- out->writeScalarAsText(-fFontMetrics.fAscent);
- out->writeText("\"/>\n");
- out->writeText(" <sTypoDescender value=\"");
- out->writeScalarAsText(-fFontMetrics.fDescent);
- out->writeText("\"/>\n");
- out->writeText(" <sTypoLineGap value=\"");
- out->writeScalarAsText(fFontMetrics.fLeading);
- out->writeText("\"/>\n");
- out->writeText(" <usWinAscent value=\"");
- out->writeScalarAsText(-fFontMetrics.fAscent);
- out->writeText("\"/>\n");
- out->writeText(" <usWinDescent value=\"");
- out->writeScalarAsText(fFontMetrics.fDescent);
- out->writeText("\"/>\n");
- out->writeText(" <ulCodePageRange1 value=\"00000000 00000000 00000000 00000000\"/>\n");
- out->writeText(" <ulCodePageRange2 value=\"00000000 00000000 00000000 00000000\"/>\n");
- out->writeText(" <sxHeight value=\"");
- out->writeScalarAsText(fFontMetrics.fXHeight);
- out->writeText("\"/>\n");
- out->writeText(" <sCapHeight value=\"");
- out->writeScalarAsText(fFontMetrics.fCapHeight);
- out->writeText("\"/>\n");
- out->writeText(" <usDefaultChar value=\"0\"/>\n");
- out->writeText(" <usBreakChar value=\"32\"/>\n");
- out->writeText(" <usMaxContext value=\"0\"/>\n");
- out->writeText(" </OS_2>\n");
- out->writeText(" <hmtx>\n");
- for (int i = 0; i < fGlyphCount; ++i) {
- out->writeText(" <mtx name=\"glyf");
- out->writeHexAsText(i, 4);
- out->writeText("\" width=\"");
- out->writeDecAsText(fGlyphs[i].fAdvance);
- out->writeText("\" lsb=\"");
- int lsb = fGlyphs[i].fOrigin.fX;
- if (glyfInfo) {
- lsb += (*glyfInfo)[i].fBounds.fLeft;
- }
- out->writeDecAsText(lsb);
- out->writeText("\"/>\n");
- }
- if (glyfInfo) {
- for (int i = 0; i < fGlyphCount; ++i) {
- for (int j = 0; j < (*glyfInfo)[i].fLayers.count(); ++j) {
- out->writeText(" <mtx name=\"glyf");
- out->writeHexAsText(i, 4);
- out->writeText("l");
- out->writeHexAsText(j, 4);
- out->writeText("\" width=\"");
- out->writeDecAsText(fGlyphs[i].fAdvance);
- out->writeText("\" lsb=\"");
- int32_t lsb = fGlyphs[i].fOrigin.fX + (*glyfInfo)[i].fLayers[j].fBounds.fLeft;
- out->writeDecAsText(lsb);
- out->writeText("\"/>\n");
- }
- }
- }
- out->writeText(" </hmtx>\n");
- bool hasNonBMP = false;
- out->writeText(" <cmap>\n");
- out->writeText(" <tableVersion version=\"0\"/>\n");
- out->writeText(" <cmap_format_4 platformID=\"3\" platEncID=\"1\" language=\"0\">\n");
- fCMap.foreach ([&out, &hasNonBMP](const SkUnichar& c, const SkGlyphID& g) {
- if (0xFFFF < c) {
- hasNonBMP = true;
- return;
- }
- out->writeText(" <map code=\"0x");
- out->writeHexAsText(c, 4);
- out->writeText("\" name=\"glyf");
- out->writeHexAsText(g, 4);
- out->writeText("\"/>\n");
- });
- out->writeText(" </cmap_format_4>\n");
- if (hasNonBMP) {
- out->writeText(
- " <cmap_format_12 platformID=\"3\" platEncID=\"10\" format=\"12\" "
- "reserved=\"0\" length=\"1\" language=\"0\" nGroups=\"0\">\n");
- fCMap.foreach ([&out](const SkUnichar& c, const SkGlyphID& g) {
- out->writeText(" <map code=\"0x");
- out->writeHexAsText(c, 6);
- out->writeText("\" name=\"glyf");
- out->writeHexAsText(g, 4);
- out->writeText("\"/>\n");
- });
- out->writeText(" </cmap_format_12>\n");
- }
- out->writeText(" </cmap>\n");
- out->writeText(" <name>\n");
- out->writeText(
- " <namerecord nameID=\"1\" platformID=\"3\" platEncID=\"1\" langID=\"0x409\">\n");
- out->writeText(" ");
- out->writeText(fName.c_str());
- out->writeText(" ");
- out->writeText(type);
- out->writeText("\n");
- out->writeText(" </namerecord>\n");
- out->writeText(
- " <namerecord nameID=\"2\" platformID=\"3\" platEncID=\"1\" langID=\"0x409\">\n");
- out->writeText(" Regular\n");
- out->writeText(" </namerecord>\n");
- out->writeText(" </name>\n");
- out->writeText(" <post>\n");
- out->writeText(" <formatType value=\"3.0\"/>\n");
- out->writeText(" <italicAngle value=\"0.0\"/>\n");
- out->writeText(" <underlinePosition value=\"");
- out->writeScalarAsText(fFontMetrics.fUnderlinePosition);
- out->writeText("\"/>\n");
- out->writeText(" <underlineThickness value=\"");
- out->writeScalarAsText(fFontMetrics.fUnderlineThickness);
- out->writeText("\"/>\n");
- out->writeText(" <isFixedPitch value=\"0\"/>\n");
- out->writeText(" <minMemType42 value=\"0\"/>\n");
- out->writeText(" <maxMemType42 value=\"0\"/>\n");
- out->writeText(" <minMemType1 value=\"0\"/>\n");
- out->writeText(" <maxMemType1 value=\"0\"/>\n");
- out->writeText(" </post>\n");
- }
- void TestSVGTypeface::exportTtxCbdt(SkWStream* out, SkSpan<unsigned> strikeSizes) const {
- SkPaint paint;
- SkFont font;
- font.setTypeface(sk_ref_sp(const_cast<TestSVGTypeface*>(this)));
- SkString name;
- this->getFamilyName(&name);
- // The CBDT/CBLC format is quite restrictive. Only write strikes which fully fit.
- SkSTArray<8, int> goodStrikeSizes;
- for (size_t strikeIndex = 0; strikeIndex < strikeSizes.size(); ++strikeIndex) {
- font.setSize(strikeSizes[strikeIndex]);
- // CBLC limits
- SkFontMetrics fm;
- font.getMetrics(&fm);
- if (!SkTFitsIn<int8_t>((int)(-fm.fTop)) || !SkTFitsIn<int8_t>((int)(-fm.fBottom)) ||
- !SkTFitsIn<uint8_t>((int)(fm.fXMax - fm.fXMin))) {
- SkDebugf("Metrics too big cbdt font size %f for %s.\n", font.getSize(), name.c_str());
- continue;
- }
- // CBDT limits
- auto exceedsCbdtLimits = [&]() {
- for (int i = 0; i < fGlyphCount; ++i) {
- SkGlyphID gid = i;
- SkScalar advance;
- SkRect bounds;
- font.getWidthsBounds(&gid, 1, &advance, &bounds, nullptr);
- SkIRect ibounds = bounds.roundOut();
- if (!SkTFitsIn<int8_t>(ibounds.fLeft) || !SkTFitsIn<int8_t>(ibounds.fTop) ||
- !SkTFitsIn<uint8_t>(ibounds.width()) || !SkTFitsIn<uint8_t>(ibounds.height()) ||
- !SkTFitsIn<uint8_t>((int)advance)) {
- return true;
- }
- }
- return false;
- };
- if (exceedsCbdtLimits()) {
- SkDebugf("Glyphs too big cbdt font size %f for %s.\n", font.getSize(), name.c_str());
- continue;
- }
- goodStrikeSizes.emplace_back(strikeSizes[strikeIndex]);
- }
- if (goodStrikeSizes.empty()) {
- SkDebugf("No strike size fit for cbdt font for %s.\n", name.c_str());
- return;
- }
- out->writeText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
- out->writeText("<ttFont sfntVersion=\"\\x00\\x01\\x00\\x00\" ttLibVersion=\"3.19\">\n");
- this->exportTtxCommon(out, "CBDT");
- out->writeText(" <CBDT>\n");
- out->writeText(" <header version=\"2.0\"/>\n");
- for (size_t strikeIndex = 0; strikeIndex < goodStrikeSizes.size(); ++strikeIndex) {
- font.setSize(goodStrikeSizes[strikeIndex]);
- out->writeText(" <strikedata index=\"");
- out->writeDecAsText(strikeIndex);
- out->writeText("\">\n");
- for (int i = 0; i < fGlyphCount; ++i) {
- SkGlyphID gid = i;
- SkScalar advance;
- SkRect bounds;
- font.getWidthsBounds(&gid, 1, &advance, &bounds, nullptr);
- SkIRect ibounds = bounds.roundOut();
- if (ibounds.isEmpty()) {
- continue;
- }
- SkImageInfo image_info = SkImageInfo::MakeN32Premul(ibounds.width(), ibounds.height());
- sk_sp<SkSurface> surface(SkSurface::MakeRaster(image_info));
- SkASSERT(surface);
- SkCanvas* canvas = surface->getCanvas();
- canvas->clear(0);
- SkPixmap pix;
- surface->peekPixels(&pix);
- canvas->drawSimpleText(&gid,
- sizeof(gid),
- SkTextEncoding::kGlyphID,
- -bounds.fLeft,
- -bounds.fTop,
- font,
- paint);
- surface->flush();
- sk_sp<SkImage> image = surface->makeImageSnapshot();
- sk_sp<SkData> data = image->encodeToData(SkEncodedImageFormat::kPNG, 100);
- out->writeText(" <cbdt_bitmap_format_17 name=\"glyf");
- out->writeHexAsText(i, 4);
- out->writeText("\">\n");
- out->writeText(" <SmallGlyphMetrics>\n");
- out->writeText(" <height value=\"");
- out->writeDecAsText(image->height());
- out->writeText("\"/>\n");
- out->writeText(" <width value=\"");
- out->writeDecAsText(image->width());
- out->writeText("\"/>\n");
- out->writeText(" <BearingX value=\"");
- out->writeDecAsText(ibounds.fLeft);
- out->writeText("\"/>\n");
- out->writeText(" <BearingY value=\"");
- out->writeDecAsText(-ibounds.fTop);
- out->writeText("\"/>\n");
- out->writeText(" <Advance value=\"");
- out->writeDecAsText((int)advance);
- out->writeText("\"/>\n");
- out->writeText(" </SmallGlyphMetrics>\n");
- out->writeText(" <rawimagedata>");
- uint8_t const* bytes = data->bytes();
- for (size_t i = 0; i < data->size(); ++i) {
- if ((i % 0x10) == 0x0) {
- out->writeText("\n ");
- } else if (((i - 1) % 0x4) == 0x3) {
- out->writeText(" ");
- }
- out->writeHexAsText(bytes[i], 2);
- }
- out->writeText("\n");
- out->writeText(" </rawimagedata>\n");
- out->writeText(" </cbdt_bitmap_format_17>\n");
- }
- out->writeText(" </strikedata>\n");
- }
- out->writeText(" </CBDT>\n");
- SkFontMetrics fm;
- out->writeText(" <CBLC>\n");
- out->writeText(" <header version=\"2.0\"/>\n");
- for (size_t strikeIndex = 0; strikeIndex < goodStrikeSizes.size(); ++strikeIndex) {
- font.setSize(goodStrikeSizes[strikeIndex]);
- font.getMetrics(&fm);
- out->writeText(" <strike index=\"");
- out->writeDecAsText(strikeIndex);
- out->writeText("\">\n");
- out->writeText(" <bitmapSizeTable>\n");
- out->writeText(" <sbitLineMetrics direction=\"hori\">\n");
- out->writeText(" <ascender value=\"");
- out->writeDecAsText((int)(-fm.fTop));
- out->writeText("\"/>\n");
- out->writeText(" <descender value=\"");
- out->writeDecAsText((int)(-fm.fBottom));
- out->writeText("\"/>\n");
- out->writeText(" <widthMax value=\"");
- out->writeDecAsText((int)(fm.fXMax - fm.fXMin));
- out->writeText("\"/>\n");
- out->writeText(" <caretSlopeNumerator value=\"0\"/>\n");
- out->writeText(" <caretSlopeDenominator value=\"0\"/>\n");
- out->writeText(" <caretOffset value=\"0\"/>\n");
- out->writeText(" <minOriginSB value=\"0\"/>\n");
- out->writeText(" <minAdvanceSB value=\"0\"/>\n");
- out->writeText(" <maxBeforeBL value=\"0\"/>\n");
- out->writeText(" <minAfterBL value=\"0\"/>\n");
- out->writeText(" <pad1 value=\"0\"/>\n");
- out->writeText(" <pad2 value=\"0\"/>\n");
- out->writeText(" </sbitLineMetrics>\n");
- out->writeText(" <sbitLineMetrics direction=\"vert\">\n");
- out->writeText(" <ascender value=\"");
- out->writeDecAsText((int)(-fm.fTop));
- out->writeText("\"/>\n");
- out->writeText(" <descender value=\"");
- out->writeDecAsText((int)(-fm.fBottom));
- out->writeText("\"/>\n");
- out->writeText(" <widthMax value=\"");
- out->writeDecAsText((int)(fm.fXMax - fm.fXMin));
- out->writeText("\"/>\n");
- out->writeText(" <caretSlopeNumerator value=\"0\"/>\n");
- out->writeText(" <caretSlopeDenominator value=\"0\"/>\n");
- out->writeText(" <caretOffset value=\"0\"/>\n");
- out->writeText(" <minOriginSB value=\"0\"/>\n");
- out->writeText(" <minAdvanceSB value=\"0\"/>\n");
- out->writeText(" <maxBeforeBL value=\"0\"/>\n");
- out->writeText(" <minAfterBL value=\"0\"/>\n");
- out->writeText(" <pad1 value=\"0\"/>\n");
- out->writeText(" <pad2 value=\"0\"/>\n");
- out->writeText(" </sbitLineMetrics>\n");
- out->writeText(" <colorRef value=\"0\"/>\n");
- out->writeText(" <startGlyphIndex value=\"1\"/>\n");
- out->writeText(" <endGlyphIndex value=\"1\"/>\n");
- out->writeText(" <ppemX value=\"");
- out->writeDecAsText(goodStrikeSizes[strikeIndex]);
- out->writeText("\"/>\n");
- out->writeText(" <ppemY value=\"");
- out->writeDecAsText(goodStrikeSizes[strikeIndex]);
- out->writeText("\"/>\n");
- out->writeText(" <bitDepth value=\"32\"/>\n");
- out->writeText(" <flags value=\"1\"/>\n");
- out->writeText(" </bitmapSizeTable>\n");
- out->writeText(
- " <eblc_index_sub_table_1 imageFormat=\"17\" firstGlyphIndex=\"1\" "
- "lastGlyphIndex=\"1\">\n");
- for (int i = 0; i < fGlyphCount; ++i) {
- SkGlyphID gid = i;
- SkRect bounds;
- font.getBounds(&gid, 1, &bounds, nullptr);
- if (bounds.isEmpty()) {
- continue;
- }
- out->writeText(" <glyphLoc name=\"glyf");
- out->writeHexAsText(i, 4);
- out->writeText("\"/>\n");
- }
- out->writeText(" </eblc_index_sub_table_1>\n");
- out->writeText(" </strike>\n");
- }
- out->writeText(" </CBLC>\n");
- out->writeText("</ttFont>\n");
- }
- /**
- * UnitsPerEm is generally 1000 here. Versions of macOS older than 10.13
- * have problems in CoreText determining the glyph bounds of bitmap glyphs
- * with unitsPerEm set to 1024 or numbers not divisible by 100 when the
- * contour is not closed. The bounds of sbix fonts on macOS appear to be those
- * of the outline in the 'glyf' table. If this countour is closed it will be
- * drawn, as the 'glyf' outline is to be drawn on top of any bitmap. (There is
- * a bit which is supposed to control this, but it cannot be relied on.) So
- * make the glyph contour a degenerate line with points at the edge of the
- * bounding box of the glyph.
- */
- void TestSVGTypeface::exportTtxSbix(SkWStream* out, SkSpan<unsigned> strikeSizes) const {
- out->writeText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
- out->writeText("<ttFont sfntVersion=\"\\x00\\x01\\x00\\x00\" ttLibVersion=\"3.19\">\n");
- this->exportTtxCommon(out, "sbix");
- SkPaint paint;
- SkFont font;
- font.setTypeface(sk_ref_sp(const_cast<TestSVGTypeface*>(this)));
- out->writeText(" <glyf>\n");
- for (int i = 0; i < fGlyphCount; ++i) {
- const TestSVGTypeface::Glyph& glyphData = this->fGlyphs[i];
- SkSize containerSize = glyphData.size();
- SkRect bounds = SkRect::MakeXYWH(glyphData.fOrigin.fX,
- -glyphData.fOrigin.fY,
- containerSize.fWidth,
- containerSize.fHeight);
- SkIRect ibounds = bounds.roundOut();
- out->writeText(" <TTGlyph name=\"glyf");
- out->writeHexAsText(i, 4);
- out->writeText("\" xMin=\"");
- out->writeDecAsText(ibounds.fLeft);
- out->writeText("\" yMin=\"");
- out->writeDecAsText(-ibounds.fBottom);
- out->writeText("\" xMax=\"");
- out->writeDecAsText(ibounds.fRight);
- out->writeText("\" yMax=\"");
- out->writeDecAsText(-ibounds.fTop);
- out->writeText("\">\n");
- out->writeText(" <contour>\n");
- out->writeText(" <pt x=\"");
- out->writeDecAsText(ibounds.fLeft);
- out->writeText("\" y=\"");
- out->writeDecAsText(-ibounds.fBottom);
- out->writeText("\" on=\"1\"/>\n");
- out->writeText(" </contour>\n");
- out->writeText(" <contour>\n");
- out->writeText(" <pt x=\"");
- out->writeDecAsText(ibounds.fRight);
- out->writeText("\" y=\"");
- out->writeDecAsText(-ibounds.fTop);
- out->writeText("\" on=\"1\"/>\n");
- out->writeText(" </contour>\n");
- out->writeText(" <instructions/>\n");
- out->writeText(" </TTGlyph>\n");
- }
- out->writeText(" </glyf>\n");
- // The loca table will be re-calculated, but if we don't write one we don't get one.
- out->writeText(" <loca/>\n");
- out->writeText(" <sbix>\n");
- out->writeText(" <version value=\"1\"/>\n");
- out->writeText(" <flags value=\"00000000 00000001\"/>\n");
- for (size_t strikeIndex = 0; strikeIndex < strikeSizes.size(); ++strikeIndex) {
- font.setSize(strikeSizes[strikeIndex]);
- out->writeText(" <strike>\n");
- out->writeText(" <ppem value=\"");
- out->writeDecAsText(strikeSizes[strikeIndex]);
- out->writeText("\"/>\n");
- out->writeText(" <resolution value=\"72\"/>\n");
- for (int i = 0; i < fGlyphCount; ++i) {
- SkGlyphID gid = i;
- SkScalar advance;
- SkRect bounds;
- font.getWidthsBounds(&gid, 1, &advance, &bounds, nullptr);
- SkIRect ibounds = bounds.roundOut();
- if (ibounds.isEmpty()) {
- continue;
- }
- SkImageInfo image_info = SkImageInfo::MakeN32Premul(ibounds.width(), ibounds.height());
- sk_sp<SkSurface> surface(SkSurface::MakeRaster(image_info));
- SkASSERT(surface);
- SkCanvas* canvas = surface->getCanvas();
- canvas->clear(0);
- SkPixmap pix;
- surface->peekPixels(&pix);
- canvas->drawSimpleText(&gid,
- sizeof(gid),
- SkTextEncoding::kGlyphID,
- -bounds.fLeft,
- -bounds.fTop,
- font,
- paint);
- surface->flush();
- sk_sp<SkImage> image = surface->makeImageSnapshot();
- sk_sp<SkData> data = image->encodeToData(SkEncodedImageFormat::kPNG, 100);
- // The originOffset values are difficult to use as DirectWrite and FreeType interpret
- // the origin to be the initial glyph position on the baseline, but CoreGraphics
- // interprets the origin to be the lower left of the cbox of the outline in the 'glyf'
- // table.
- //#define SK_SBIX_LIKE_FT
- //#define SK_SBIX_LIKE_DW
- out->writeText(" <glyph name=\"glyf");
- out->writeHexAsText(i, 4);
- out->writeText("\" graphicType=\"png \" originOffsetX=\"");
- #if defined(SK_SBIX_LIKE_FT) || defined(SK_SBIX_LIKE_DW)
- out->writeDecAsText(bounds.fLeft);
- #else
- out->writeDecAsText(0);
- #endif
- // DirectWrite and CoreGraphics use positive values of originOffsetY to push the
- // image visually up (but from different origins).
- // FreeType uses positive values to push the image down.
- out->writeText("\" originOffsetY=\"");
- #if defined(SK_SBIX_LIKE_FT)
- out->writeScalarAsText(bounds.fBottom);
- #elif defined(SK_SBIX_LIKE_DW)
- out->writeScalarAsText(-bounds.fBottom);
- #else
- out->writeDecAsText(0);
- #endif
- out->writeText("\">\n");
- out->writeText(" <hexdata>");
- uint8_t const* bytes = data->bytes();
- for (size_t i = 0; i < data->size(); ++i) {
- if ((i % 0x10) == 0x0) {
- out->writeText("\n ");
- } else if (((i - 1) % 0x4) == 0x3) {
- out->writeText(" ");
- }
- out->writeHexAsText(bytes[i], 2);
- }
- out->writeText("\n");
- out->writeText(" </hexdata>\n");
- out->writeText(" </glyph>\n");
- }
- out->writeText(" </strike>\n");
- }
- out->writeText(" </sbix>\n");
- out->writeText("</ttFont>\n");
- }
- namespace {
- void convert_noninflect_cubic_to_quads(const SkPoint p[4],
- SkScalar toleranceSqd,
- SkTArray<SkPoint, true>* quads,
- int sublevel = 0) {
- // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is
- // 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].
- SkVector ab = p[1] - p[0];
- SkVector dc = p[2] - p[3];
- if (SkPointPriv::LengthSqd(ab) < SK_ScalarNearlyZero) {
- if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
- SkPoint* degQuad = quads->push_back_n(3);
- degQuad[0] = p[0];
- degQuad[1] = p[0];
- degQuad[2] = p[3];
- return;
- }
- ab = p[2] - p[0];
- }
- if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
- dc = p[1] - p[3];
- }
- static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2;
- static const int kMaxSubdivs = 10;
- ab.scale(kLengthScale);
- dc.scale(kLengthScale);
- // e0 and e1 are extrapolations along vectors ab and dc.
- SkVector c0 = p[0];
- c0 += ab;
- SkVector c1 = p[3];
- c1 += dc;
- SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : SkPointPriv::DistanceToSqd(c0, c1);
- if (dSqd < toleranceSqd) {
- SkPoint cAvg = c0;
- cAvg += c1;
- cAvg.scale(SK_ScalarHalf);
- SkPoint* pts = quads->push_back_n(3);
- pts[0] = p[0];
- pts[1] = cAvg;
- pts[2] = p[3];
- return;
- }
- SkPoint choppedPts[7];
- SkChopCubicAtHalf(p, choppedPts);
- convert_noninflect_cubic_to_quads(choppedPts + 0, toleranceSqd, quads, sublevel + 1);
- convert_noninflect_cubic_to_quads(choppedPts + 3, toleranceSqd, quads, sublevel + 1);
- }
- void convertCubicToQuads(const SkPoint p[4], SkScalar tolScale, SkTArray<SkPoint, true>* quads) {
- if (!p[0].isFinite() || !p[1].isFinite() || !p[2].isFinite() || !p[3].isFinite()) {
- return;
- }
- SkPoint chopped[10];
- int count = SkChopCubicAtInflections(p, chopped);
- const SkScalar tolSqd = SkScalarSquare(tolScale);
- for (int i = 0; i < count; ++i) {
- SkPoint* cubic = chopped + 3 * i;
- convert_noninflect_cubic_to_quads(cubic, tolSqd, quads);
- }
- }
- void path_to_quads(const SkPath& path, SkPath* quadPath) {
- quadPath->reset();
- SkTArray<SkPoint, true> qPts;
- SkAutoConicToQuads converter;
- const SkPoint* quadPts;
- SkPath::RawIter iter(path);
- uint8_t verb;
- SkPoint pts[4];
- while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
- switch (verb) {
- case SkPath::kMove_Verb: quadPath->moveTo(pts[0].fX, pts[0].fY); break;
- case SkPath::kLine_Verb: quadPath->lineTo(pts[1].fX, pts[1].fY); break;
- case SkPath::kQuad_Verb:
- quadPath->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
- break;
- case SkPath::kCubic_Verb:
- qPts.reset();
- convertCubicToQuads(pts, SK_Scalar1, &qPts);
- for (int i = 0; i < qPts.count(); i += 3) {
- quadPath->quadTo(
- qPts[i + 1].fX, qPts[i + 1].fY, qPts[i + 2].fX, qPts[i + 2].fY);
- }
- break;
- case SkPath::kConic_Verb:
- quadPts = converter.computeQuads(pts, iter.conicWeight(), SK_Scalar1);
- for (int i = 0; i < converter.countQuads(); ++i) {
- quadPath->quadTo(quadPts[i * 2 + 1].fX,
- quadPts[i * 2 + 1].fY,
- quadPts[i * 2 + 2].fX,
- quadPts[i * 2 + 2].fY);
- }
- break;
- case SkPath::kClose_Verb: quadPath->close(); break;
- default: SkDEBUGFAIL("bad verb"); return;
- }
- }
- }
- class SkCOLRCanvas : public SkNoDrawCanvas {
- public:
- SkCOLRCanvas(SkRect glyphBounds,
- const TestSVGTypeface& typeface,
- SkGlyphID glyphId,
- TestSVGTypeface::GlyfInfo* glyf,
- SkTHashMap<SkColor, int>* colors,
- SkWStream* out)
- : SkNoDrawCanvas(glyphBounds.roundOut().width(), glyphBounds.roundOut().height())
- , fBaselineOffset(glyphBounds.top())
- , fTypeface(typeface)
- , fGlyphId(glyphId)
- , fGlyf(glyf)
- , fColors(colors)
- , fOut(out)
- , fLayerId(0) {}
- void writePoint(SkScalar x, SkScalar y, bool on) {
- fOut->writeText(" <pt x=\"");
- fOut->writeDecAsText(SkScalarRoundToInt(x));
- fOut->writeText("\" y=\"");
- fOut->writeDecAsText(SkScalarRoundToInt(y));
- fOut->writeText("\" on=\"");
- fOut->write8(on ? '1' : '0');
- fOut->writeText("\"/>\n");
- }
- SkIRect writePath(const SkPath& path, bool layer) {
- // Convert to quads.
- SkPath quads;
- path_to_quads(path, &quads);
- SkRect bounds = quads.computeTightBounds();
- SkIRect ibounds = bounds.roundOut();
- // The bounds will be re-calculated anyway.
- fOut->writeText(" <TTGlyph name=\"glyf");
- fOut->writeHexAsText(fGlyphId, 4);
- if (layer) {
- fOut->writeText("l");
- fOut->writeHexAsText(fLayerId, 4);
- }
- fOut->writeText("\" xMin=\"");
- fOut->writeDecAsText(ibounds.fLeft);
- fOut->writeText("\" yMin=\"");
- fOut->writeDecAsText(ibounds.fTop);
- fOut->writeText("\" xMax=\"");
- fOut->writeDecAsText(ibounds.fRight);
- fOut->writeText("\" yMax=\"");
- fOut->writeDecAsText(ibounds.fBottom);
- fOut->writeText("\">\n");
- SkPath::RawIter iter(quads);
- uint8_t verb;
- SkPoint pts[4];
- bool contourOpen = false;
- while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
- switch (verb) {
- case SkPath::kMove_Verb:
- if (contourOpen) {
- fOut->writeText(" </contour>\n");
- contourOpen = false;
- }
- break;
- case SkPath::kLine_Verb:
- if (!contourOpen) {
- fOut->writeText(" <contour>\n");
- this->writePoint(pts[0].fX, pts[0].fY, true);
- contourOpen = true;
- }
- this->writePoint(pts[1].fX, pts[1].fY, true);
- break;
- case SkPath::kQuad_Verb:
- if (!contourOpen) {
- fOut->writeText(" <contour>\n");
- this->writePoint(pts[0].fX, pts[0].fY, true);
- contourOpen = true;
- }
- this->writePoint(pts[1].fX, pts[1].fY, false);
- this->writePoint(pts[2].fX, pts[2].fY, true);
- break;
- case SkPath::kClose_Verb:
- if (contourOpen) {
- fOut->writeText(" </contour>\n");
- contourOpen = false;
- }
- break;
- default: SkDEBUGFAIL("bad verb"); return ibounds;
- }
- }
- if (contourOpen) {
- fOut->writeText(" </contour>\n");
- }
- // Required to write out an instructions tag.
- fOut->writeText(" <instructions/>\n");
- fOut->writeText(" </TTGlyph>\n");
- return ibounds;
- }
- void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
- SkPath path;
- path.addRect(rect);
- this->drawPath(path, paint);
- }
- void onDrawOval(const SkRect& oval, const SkPaint& paint) override {
- SkPath path;
- path.addOval(oval);
- this->drawPath(path, paint);
- }
- void onDrawArc(const SkRect& oval,
- SkScalar startAngle,
- SkScalar sweepAngle,
- bool useCenter,
- const SkPaint& paint) override {
- SkPath path;
- bool fillNoPathEffect = SkPaint::kFill_Style == paint.getStyle() && !paint.getPathEffect();
- SkPathPriv::CreateDrawArcPath(
- &path, oval, startAngle, sweepAngle, useCenter, fillNoPathEffect);
- this->drawPath(path, paint);
- }
- void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
- SkPath path;
- path.addRRect(rrect);
- this->drawPath(path, paint);
- }
- void onDrawPath(const SkPath& platonicPath, const SkPaint& originalPaint) override {
- SkPaint paint = originalPaint;
- SkPath path = platonicPath;
- // Apply the path effect.
- if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
- bool fill = paint.getFillPath(path, &path);
- paint.setPathEffect(nullptr);
- if (fill) {
- paint.setStyle(SkPaint::kFill_Style);
- } else {
- paint.setStyle(SkPaint::kStroke_Style);
- paint.setStrokeWidth(0);
- }
- }
- // Apply the matrix.
- SkMatrix m = this->getTotalMatrix();
- // If done to the canvas then everything would get clipped out.
- m.postTranslate(0, fBaselineOffset); // put the baseline at 0
- m.postScale(1, -1); // and flip it since OpenType is y-up.
- path.transform(m);
- // While creating the default glyf, union with dark colors and intersect with bright colors.
- SkColor color = paint.getColor();
- SkPathOp op;
- if (fTypeface.getPathOp(color, &op)) {
- fBasePath.add(path, op);
- }
- SkIRect bounds = this->writePath(path, true);
- // The CPAL table has the concept of a 'current color' which is index 0xFFFF.
- // Mark any layer drawn in 'currentColor' as having this special index.
- // The value of 'currentColor' here should a color which causes this layer to union into the
- // default glyf.
- constexpr SkColor currentColor = 0xFF2B0000;
- int colorIndex;
- if (color == currentColor) {
- colorIndex = 0xFFFF;
- } else {
- int* colorIndexPtr = fColors->find(color);
- if (colorIndexPtr) {
- colorIndex = *colorIndexPtr;
- } else {
- colorIndex = fColors->count();
- fColors->set(color, colorIndex);
- }
- }
- fGlyf->fLayers.emplace_back(colorIndex, bounds);
- ++fLayerId;
- }
- void finishGlyph() {
- SkPath baseGlyph;
- fBasePath.resolve(&baseGlyph);
- fGlyf->fBounds = this->writePath(baseGlyph, false);
- }
- private:
- SkScalar fBaselineOffset;
- const TestSVGTypeface& fTypeface;
- SkGlyphID fGlyphId;
- TestSVGTypeface::GlyfInfo* fGlyf;
- SkTHashMap<SkColor, int>* fColors;
- SkWStream* const fOut;
- SkOpBuilder fBasePath;
- int fLayerId;
- };
- } // namespace
- void TestSVGTypeface::exportTtxColr(SkWStream* out) const {
- out->writeText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
- out->writeText("<ttFont sfntVersion=\"\\x00\\x01\\x00\\x00\" ttLibVersion=\"3.19\">\n");
- SkTHashMap<SkColor, int> colors;
- SkTArray<GlyfInfo> glyfInfos(fGlyphCount);
- // Need to know all the glyphs up front for the common tables.
- SkDynamicMemoryWStream glyfOut;
- glyfOut.writeText(" <glyf>\n");
- for (int i = 0; i < fGlyphCount; ++i) {
- const TestSVGTypeface::Glyph& glyphData = this->fGlyphs[i];
- SkSize containerSize = glyphData.size();
- SkRect bounds = SkRect::MakeXYWH(glyphData.fOrigin.fX,
- -glyphData.fOrigin.fY,
- containerSize.fWidth,
- containerSize.fHeight);
- SkCOLRCanvas canvas(bounds, *this, i, &glyfInfos.emplace_back(), &colors, &glyfOut);
- glyphData.render(&canvas);
- canvas.finishGlyph();
- }
- glyfOut.writeText(" </glyf>\n");
- this->exportTtxCommon(out, "COLR", &glyfInfos);
- // The loca table will be re-calculated, but if we don't write one we don't get one.
- out->writeText(" <loca/>\n");
- std::unique_ptr<SkStreamAsset> glyfStream = glyfOut.detachAsStream();
- out->writeStream(glyfStream.get(), glyfStream->getLength());
- out->writeText(" <COLR>\n");
- out->writeText(" <version value=\"0\"/>\n");
- for (int i = 0; i < fGlyphCount; ++i) {
- if (glyfInfos[i].fLayers.empty()) {
- continue;
- }
- if (glyfInfos[i].fBounds.isEmpty()) {
- SkDebugf("Glyph %d is empty but has layers.\n", i);
- }
- out->writeText(" <ColorGlyph name=\"glyf");
- out->writeHexAsText(i, 4);
- out->writeText("\">\n");
- for (int j = 0; j < glyfInfos[i].fLayers.count(); ++j) {
- const int colorIndex = glyfInfos[i].fLayers[j].fLayerColorIndex;
- out->writeText(" <layer colorID=\"");
- out->writeDecAsText(colorIndex);
- out->writeText("\" name=\"glyf");
- out->writeHexAsText(i, 4);
- out->writeText("l");
- out->writeHexAsText(j, 4);
- out->writeText("\"/>\n");
- }
- out->writeText(" </ColorGlyph>\n");
- }
- out->writeText(" </COLR>\n");
- // The colors must be written in order, the 'index' is ignored by ttx.
- SkAutoTMalloc<SkColor> colorsInOrder(colors.count());
- colors.foreach ([&colorsInOrder](const SkColor& c, const int* i) { colorsInOrder[*i] = c; });
- out->writeText(" <CPAL>\n");
- out->writeText(" <version value=\"0\"/>\n");
- out->writeText(" <numPaletteEntries value=\"");
- out->writeDecAsText(colors.count());
- out->writeText("\"/>\n");
- out->writeText(" <palette index=\"0\">\n");
- for (int i = 0; i < colors.count(); ++i) {
- SkColor c = colorsInOrder[i];
- out->writeText(" <color index=\"");
- out->writeDecAsText(i);
- out->writeText("\" value=\"#");
- out->writeHexAsText(SkColorGetR(c), 2);
- out->writeHexAsText(SkColorGetG(c), 2);
- out->writeHexAsText(SkColorGetB(c), 2);
- out->writeHexAsText(SkColorGetA(c), 2);
- out->writeText("\"/>\n");
- }
- out->writeText(" </palette>\n");
- out->writeText(" </CPAL>\n");
- out->writeText("</ttFont>\n");
- }
- #endif // SK_XML
|