blob: 4e9898d6d4a53d336baf5c02f3d26e511977d3a3 [file] [log] [blame]
Xavier Phane29cdaf2020-03-26 16:15:14 +00001/*
2 * Copyright 2019 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
Florin Malitab3418102020-10-15 18:10:29 -04008#include "modules/svg/include/SkSVGText.h"
Xavier Phane29cdaf2020-03-26 16:15:14 +00009
Xavier Phane29cdaf2020-03-26 16:15:14 +000010#include "include/core/SkCanvas.h"
Florin Malita512ff752020-12-06 11:50:52 -050011#include "include/core/SkFont.h"
Florin Malita39fe8c82020-10-20 10:43:03 -040012#include "include/core/SkFontMgr.h"
Tyler Freemane9663db2020-04-14 14:37:13 -070013#include "include/core/SkFontStyle.h"
14#include "include/core/SkString.h"
Florin Malita512ff752020-12-06 11:50:52 -050015#include "include/utils/SkTextUtils.h"
Florin Malitab3418102020-10-15 18:10:29 -040016#include "modules/svg/include/SkSVGRenderContext.h"
17#include "modules/svg/include/SkSVGValue.h"
Xavier Phane29cdaf2020-03-26 16:15:14 +000018
Florin Malita512ff752020-12-06 11:50:52 -050019namespace {
Xavier Phane29cdaf2020-03-26 16:15:14 +000020
Florin Malita512ff752020-12-06 11:50:52 -050021static SkFont ResolveFont(const SkSVGRenderContext& ctx) {
Florin Malita39fe8c82020-10-20 10:43:03 -040022 auto weight = [](const SkSVGFontWeight& w) {
23 switch (w.type()) {
24 case SkSVGFontWeight::Type::k100: return SkFontStyle::kThin_Weight;
25 case SkSVGFontWeight::Type::k200: return SkFontStyle::kExtraLight_Weight;
26 case SkSVGFontWeight::Type::k300: return SkFontStyle::kLight_Weight;
27 case SkSVGFontWeight::Type::k400: return SkFontStyle::kNormal_Weight;
28 case SkSVGFontWeight::Type::k500: return SkFontStyle::kMedium_Weight;
29 case SkSVGFontWeight::Type::k600: return SkFontStyle::kSemiBold_Weight;
30 case SkSVGFontWeight::Type::k700: return SkFontStyle::kBold_Weight;
31 case SkSVGFontWeight::Type::k800: return SkFontStyle::kExtraBold_Weight;
32 case SkSVGFontWeight::Type::k900: return SkFontStyle::kBlack_Weight;
33 case SkSVGFontWeight::Type::kNormal: return SkFontStyle::kNormal_Weight;
34 case SkSVGFontWeight::Type::kBold: return SkFontStyle::kBold_Weight;
35 case SkSVGFontWeight::Type::kBolder: return SkFontStyle::kExtraBold_Weight;
36 case SkSVGFontWeight::Type::kLighter: return SkFontStyle::kLight_Weight;
37 case SkSVGFontWeight::Type::kInherit: {
38 SkASSERT(false);
39 return SkFontStyle::kNormal_Weight;
40 }
41 }
42 SkUNREACHABLE;
43 };
44
45 auto slant = [](const SkSVGFontStyle& s) {
46 switch (s.type()) {
47 case SkSVGFontStyle::Type::kNormal: return SkFontStyle::kUpright_Slant;
48 case SkSVGFontStyle::Type::kItalic: return SkFontStyle::kItalic_Slant;
49 case SkSVGFontStyle::Type::kOblique: return SkFontStyle::kOblique_Slant;
50 case SkSVGFontStyle::Type::kInherit: {
51 SkASSERT(false);
52 return SkFontStyle::kUpright_Slant;
53 }
54 }
55 SkUNREACHABLE;
56 };
57
58 const auto& family = ctx.presentationContext().fInherited.fFontFamily->family();
59 const SkFontStyle style(weight(*ctx.presentationContext().fInherited.fFontWeight),
60 SkFontStyle::kNormal_Width,
61 slant(*ctx.presentationContext().fInherited.fFontStyle));
62
63 const auto size =
64 ctx.lengthContext().resolve(ctx.presentationContext().fInherited.fFontSize->size(),
65 SkSVGLengthContext::LengthType::kVertical);
66
Florin Malita7006e152020-11-10 15:24:59 -050067 // TODO: we likely want matchFamilyStyle here, but switching away from legacyMakeTypeface
68 // changes all the results when using the default fontmgr.
69 auto tf = ctx.fontMgr()->legacyMakeTypeface(family.c_str(), style);
70
71 SkFont font(std::move(tf), size);
Florin Malita39fe8c82020-10-20 10:43:03 -040072 font.setHinting(SkFontHinting::kNone);
73 font.setSubpixel(true);
74 font.setLinearMetrics(true);
75 font.setBaselineSnap(false);
76 font.setEdging(SkFont::Edging::kAntiAlias);
77
78 return font;
79}
80
Florin Malita512ff752020-12-06 11:50:52 -050081} // namespace
82
83struct SkSVGTextContext {
84 SkV2 currentPos; // current text position (http://www.w3.org/TR/SVG11/text.html#TextLayout)
85 size_t currentIndex; // current character index
86};
87
88void SkSVGTextContainer::appendChild(sk_sp<SkSVGNode> child) {
89 // Only allow text nodes.
90 switch (child->tag()) {
91 case SkSVGTag::kText:
92 case SkSVGTag::kTextLiteral:
93 case SkSVGTag::kTSpan:
94 this->INHERITED::appendChild(child);
95 break;
96 default:
97 break;
98 }
99}
100
101bool SkSVGTextContainer::parseAndSetAttribute(const char* name, const char* value) {
102 return INHERITED::parseAndSetAttribute(name, value) ||
103 this->setX(SkSVGAttributeParser::parse<SkSVGLength>("x", name, value)) ||
104 this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", name, value));
105}
106
Florin Malita39fe8c82020-10-20 10:43:03 -0400107void SkSVGText::onRender(const SkSVGRenderContext& ctx) const {
Florin Malita512ff752020-12-06 11:50:52 -0500108 // <text> establishes a new text layout context.
109 SkSVGTextContext tctx {
110 {
111 ctx.lengthContext().resolve(this->getX(), SkSVGLengthContext::LengthType::kHorizontal),
112 ctx.lengthContext().resolve(this->getY(), SkSVGLengthContext::LengthType::kVertical),
113 },
114 0
115 };
116
117 SkSVGRenderContext lctx(ctx, tctx);
118
119 return this->INHERITED::onRender(lctx);
120}
121
122void SkSVGTextLiteral::onRender(const SkSVGRenderContext& ctx) const {
123 auto* tctx = ctx.textContext();
124 if (!tctx) {
125 return;
126 }
127
128 const auto font = ResolveFont(ctx);
Florin Malita39fe8c82020-10-20 10:43:03 -0400129
Florin Malita056385b2020-10-27 22:57:56 -0400130 const auto text_align = [](const SkSVGTextAnchor& anchor) {
131 switch (anchor.type()) {
132 case SkSVGTextAnchor::Type::kStart : return SkTextUtils::Align::kLeft_Align;
133 case SkSVGTextAnchor::Type::kMiddle: return SkTextUtils::Align::kCenter_Align;
134 case SkSVGTextAnchor::Type::kEnd : return SkTextUtils::Align::kRight_Align;
135 case SkSVGTextAnchor::Type::kInherit:
136 SkASSERT(false);
137 return SkTextUtils::Align::kLeft_Align;
138 }
139 SkUNREACHABLE;
140 };
141
142 const auto align = text_align(*ctx.presentationContext().fInherited.fTextAnchor);
Florin Malita39fe8c82020-10-20 10:43:03 -0400143 if (const SkPaint* fillPaint = ctx.fillPaint()) {
Florin Malita512ff752020-12-06 11:50:52 -0500144 SkTextUtils::DrawString(ctx.canvas(), fText.c_str(),
145 tctx->currentPos.x, tctx->currentPos.y,
146 font, *fillPaint, align);
Florin Malita39fe8c82020-10-20 10:43:03 -0400147 }
148
149 if (const SkPaint* strokePaint = ctx.strokePaint()) {
Florin Malita512ff752020-12-06 11:50:52 -0500150 SkTextUtils::DrawString(ctx.canvas(), fText.c_str(),
151 tctx->currentPos.x, tctx->currentPos.y,
152 font, *strokePaint, align);
Florin Malita39fe8c82020-10-20 10:43:03 -0400153 }
154}
155
Florin Malita512ff752020-12-06 11:50:52 -0500156SkPath SkSVGTextLiteral::onAsPath(const SkSVGRenderContext&) const {
Florin Malita39fe8c82020-10-20 10:43:03 -0400157 // TODO
Florin Malita512ff752020-12-06 11:50:52 -0500158 return SkPath();
Xavier Phane29cdaf2020-03-26 16:15:14 +0000159}
160
Florin Malita512ff752020-12-06 11:50:52 -0500161SkSVGTextLiteral::~SkSVGTextLiteral() = default; // just to pin the vtable