xref: /third_party/skia/modules/svg/src/SkSVGText.cpp (revision cb93a386)
1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2019 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "modules/svg/include/SkSVGText.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include <limits>
11cb93a386Sopenharmony_ci
12cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
13cb93a386Sopenharmony_ci#include "include/core/SkContourMeasure.h"
14cb93a386Sopenharmony_ci#include "include/core/SkFont.h"
15cb93a386Sopenharmony_ci#include "include/core/SkFontMgr.h"
16cb93a386Sopenharmony_ci#include "include/core/SkFontStyle.h"
17cb93a386Sopenharmony_ci#include "include/core/SkPathBuilder.h"
18cb93a386Sopenharmony_ci#include "include/core/SkRSXform.h"
19cb93a386Sopenharmony_ci#include "include/core/SkString.h"
20cb93a386Sopenharmony_ci#include "modules/skshaper/include/SkShaper.h"
21cb93a386Sopenharmony_ci#include "modules/svg/include/SkSVGRenderContext.h"
22cb93a386Sopenharmony_ci#include "modules/svg/include/SkSVGValue.h"
23cb93a386Sopenharmony_ci#include "modules/svg/src/SkSVGTextPriv.h"
24cb93a386Sopenharmony_ci#include "src/core/SkTextBlobPriv.h"
25cb93a386Sopenharmony_ci#include "src/utils/SkUTF.h"
26cb93a386Sopenharmony_ci
27cb93a386Sopenharmony_cinamespace {
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_cistatic SkFont ResolveFont(const SkSVGRenderContext& ctx) {
30cb93a386Sopenharmony_ci    auto weight = [](const SkSVGFontWeight& w) {
31cb93a386Sopenharmony_ci        switch (w.type()) {
32cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::k100:     return SkFontStyle::kThin_Weight;
33cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::k200:     return SkFontStyle::kExtraLight_Weight;
34cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::k300:     return SkFontStyle::kLight_Weight;
35cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::k400:     return SkFontStyle::kNormal_Weight;
36cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::k500:     return SkFontStyle::kMedium_Weight;
37cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::k600:     return SkFontStyle::kSemiBold_Weight;
38cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::k700:     return SkFontStyle::kBold_Weight;
39cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::k800:     return SkFontStyle::kExtraBold_Weight;
40cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::k900:     return SkFontStyle::kBlack_Weight;
41cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::kNormal:  return SkFontStyle::kNormal_Weight;
42cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::kBold:    return SkFontStyle::kBold_Weight;
43cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::kBolder:  return SkFontStyle::kExtraBold_Weight;
44cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::kLighter: return SkFontStyle::kLight_Weight;
45cb93a386Sopenharmony_ci            case SkSVGFontWeight::Type::kInherit: {
46cb93a386Sopenharmony_ci                SkASSERT(false);
47cb93a386Sopenharmony_ci                return SkFontStyle::kNormal_Weight;
48cb93a386Sopenharmony_ci            }
49cb93a386Sopenharmony_ci        }
50cb93a386Sopenharmony_ci        SkUNREACHABLE;
51cb93a386Sopenharmony_ci    };
52cb93a386Sopenharmony_ci
53cb93a386Sopenharmony_ci    auto slant = [](const SkSVGFontStyle& s) {
54cb93a386Sopenharmony_ci        switch (s.type()) {
55cb93a386Sopenharmony_ci            case SkSVGFontStyle::Type::kNormal:  return SkFontStyle::kUpright_Slant;
56cb93a386Sopenharmony_ci            case SkSVGFontStyle::Type::kItalic:  return SkFontStyle::kItalic_Slant;
57cb93a386Sopenharmony_ci            case SkSVGFontStyle::Type::kOblique: return SkFontStyle::kOblique_Slant;
58cb93a386Sopenharmony_ci            case SkSVGFontStyle::Type::kInherit: {
59cb93a386Sopenharmony_ci                SkASSERT(false);
60cb93a386Sopenharmony_ci                return SkFontStyle::kUpright_Slant;
61cb93a386Sopenharmony_ci            }
62cb93a386Sopenharmony_ci        }
63cb93a386Sopenharmony_ci        SkUNREACHABLE;
64cb93a386Sopenharmony_ci    };
65cb93a386Sopenharmony_ci
66cb93a386Sopenharmony_ci    const auto& family = ctx.presentationContext().fInherited.fFontFamily->family();
67cb93a386Sopenharmony_ci    const SkFontStyle style(weight(*ctx.presentationContext().fInherited.fFontWeight),
68cb93a386Sopenharmony_ci                            SkFontStyle::kNormal_Width,
69cb93a386Sopenharmony_ci                            slant(*ctx.presentationContext().fInherited.fFontStyle));
70cb93a386Sopenharmony_ci
71cb93a386Sopenharmony_ci    const auto size =
72cb93a386Sopenharmony_ci            ctx.lengthContext().resolve(ctx.presentationContext().fInherited.fFontSize->size(),
73cb93a386Sopenharmony_ci                                        SkSVGLengthContext::LengthType::kVertical);
74cb93a386Sopenharmony_ci
75cb93a386Sopenharmony_ci    // TODO: we likely want matchFamilyStyle here, but switching away from legacyMakeTypeface
76cb93a386Sopenharmony_ci    // changes all the results when using the default fontmgr.
77cb93a386Sopenharmony_ci    auto tf = ctx.fontMgr()->legacyMakeTypeface(family.c_str(), style);
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci    SkFont font(std::move(tf), size);
80cb93a386Sopenharmony_ci    font.setHinting(SkFontHinting::kNone);
81cb93a386Sopenharmony_ci    font.setSubpixel(true);
82cb93a386Sopenharmony_ci    font.setLinearMetrics(true);
83cb93a386Sopenharmony_ci    font.setBaselineSnap(false);
84cb93a386Sopenharmony_ci    font.setEdging(SkFont::Edging::kAntiAlias);
85cb93a386Sopenharmony_ci
86cb93a386Sopenharmony_ci    return font;
87cb93a386Sopenharmony_ci}
88cb93a386Sopenharmony_ci
89cb93a386Sopenharmony_cistatic std::vector<float> ResolveLengths(const SkSVGLengthContext& lctx,
90cb93a386Sopenharmony_ci                                         const std::vector<SkSVGLength>& lengths,
91cb93a386Sopenharmony_ci                                         SkSVGLengthContext::LengthType lt) {
92cb93a386Sopenharmony_ci    std::vector<float> resolved;
93cb93a386Sopenharmony_ci    resolved.reserve(lengths.size());
94cb93a386Sopenharmony_ci
95cb93a386Sopenharmony_ci    for (const auto& l : lengths) {
96cb93a386Sopenharmony_ci        resolved.push_back(lctx.resolve(l, lt));
97cb93a386Sopenharmony_ci    }
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_ci    return resolved;
100cb93a386Sopenharmony_ci}
101cb93a386Sopenharmony_ci
102cb93a386Sopenharmony_cistatic float ComputeAlignmentFactor(const SkSVGPresentationContext& pctx) {
103cb93a386Sopenharmony_ci    switch (pctx.fInherited.fTextAnchor->type()) {
104cb93a386Sopenharmony_ci    case SkSVGTextAnchor::Type::kStart : return  0.0f;
105cb93a386Sopenharmony_ci    case SkSVGTextAnchor::Type::kMiddle: return -0.5f;
106cb93a386Sopenharmony_ci    case SkSVGTextAnchor::Type::kEnd   : return -1.0f;
107cb93a386Sopenharmony_ci    case SkSVGTextAnchor::Type::kInherit:
108cb93a386Sopenharmony_ci        SkASSERT(false);
109cb93a386Sopenharmony_ci        return 0.0f;
110cb93a386Sopenharmony_ci    }
111cb93a386Sopenharmony_ci    SkUNREACHABLE;
112cb93a386Sopenharmony_ci}
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_ci} // namespace
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ciSkSVGTextContext::ScopedPosResolver::ScopedPosResolver(const SkSVGTextContainer& txt,
117cb93a386Sopenharmony_ci                                                       const SkSVGLengthContext& lctx,
118cb93a386Sopenharmony_ci                                                       SkSVGTextContext* tctx,
119cb93a386Sopenharmony_ci                                                       size_t charIndexOffset)
120cb93a386Sopenharmony_ci    : fTextContext(tctx)
121cb93a386Sopenharmony_ci    , fParent(tctx->fPosResolver)
122cb93a386Sopenharmony_ci    , fCharIndexOffset(charIndexOffset)
123cb93a386Sopenharmony_ci    , fX(ResolveLengths(lctx, txt.getX(), SkSVGLengthContext::LengthType::kHorizontal))
124cb93a386Sopenharmony_ci    , fY(ResolveLengths(lctx, txt.getY(), SkSVGLengthContext::LengthType::kVertical))
125cb93a386Sopenharmony_ci    , fDx(ResolveLengths(lctx, txt.getDx(), SkSVGLengthContext::LengthType::kHorizontal))
126cb93a386Sopenharmony_ci    , fDy(ResolveLengths(lctx, txt.getDy(), SkSVGLengthContext::LengthType::kVertical))
127cb93a386Sopenharmony_ci    , fRotate(txt.getRotate())
128cb93a386Sopenharmony_ci{
129cb93a386Sopenharmony_ci    fTextContext->fPosResolver = this;
130cb93a386Sopenharmony_ci}
131cb93a386Sopenharmony_ci
132cb93a386Sopenharmony_ciSkSVGTextContext::ScopedPosResolver::ScopedPosResolver(const SkSVGTextContainer& txt,
133cb93a386Sopenharmony_ci                                                       const SkSVGLengthContext& lctx,
134cb93a386Sopenharmony_ci                                                       SkSVGTextContext* tctx)
135cb93a386Sopenharmony_ci    : ScopedPosResolver(txt, lctx, tctx, tctx->fCurrentCharIndex) {}
136cb93a386Sopenharmony_ci
137cb93a386Sopenharmony_ciSkSVGTextContext::ScopedPosResolver::~ScopedPosResolver() {
138cb93a386Sopenharmony_ci    fTextContext->fPosResolver = fParent;
139cb93a386Sopenharmony_ci}
140cb93a386Sopenharmony_ci
141cb93a386Sopenharmony_ciSkSVGTextContext::PosAttrs SkSVGTextContext::ScopedPosResolver::resolve(size_t charIndex) const {
142cb93a386Sopenharmony_ci    PosAttrs attrs;
143cb93a386Sopenharmony_ci
144cb93a386Sopenharmony_ci    if (charIndex < fLastPosIndex) {
145cb93a386Sopenharmony_ci        SkASSERT(charIndex >= fCharIndexOffset);
146cb93a386Sopenharmony_ci        const auto localCharIndex = charIndex - fCharIndexOffset;
147cb93a386Sopenharmony_ci
148cb93a386Sopenharmony_ci        const auto hasAllLocal = localCharIndex < fX.size() &&
149cb93a386Sopenharmony_ci                                 localCharIndex < fY.size() &&
150cb93a386Sopenharmony_ci                                 localCharIndex < fDx.size() &&
151cb93a386Sopenharmony_ci                                 localCharIndex < fDy.size() &&
152cb93a386Sopenharmony_ci                                 localCharIndex < fRotate.size();
153cb93a386Sopenharmony_ci        if (!hasAllLocal && fParent) {
154cb93a386Sopenharmony_ci            attrs = fParent->resolve(charIndex);
155cb93a386Sopenharmony_ci        }
156cb93a386Sopenharmony_ci
157cb93a386Sopenharmony_ci        if (localCharIndex < fX.size()) {
158cb93a386Sopenharmony_ci            attrs[PosAttrs::kX] = fX[localCharIndex];
159cb93a386Sopenharmony_ci        }
160cb93a386Sopenharmony_ci        if (localCharIndex < fY.size()) {
161cb93a386Sopenharmony_ci            attrs[PosAttrs::kY] = fY[localCharIndex];
162cb93a386Sopenharmony_ci        }
163cb93a386Sopenharmony_ci        if (localCharIndex < fDx.size()) {
164cb93a386Sopenharmony_ci            attrs[PosAttrs::kDx] = fDx[localCharIndex];
165cb93a386Sopenharmony_ci        }
166cb93a386Sopenharmony_ci        if (localCharIndex < fDy.size()) {
167cb93a386Sopenharmony_ci            attrs[PosAttrs::kDy] = fDy[localCharIndex];
168cb93a386Sopenharmony_ci        }
169cb93a386Sopenharmony_ci
170cb93a386Sopenharmony_ci        // Rotation semantics are interestingly different [1]:
171cb93a386Sopenharmony_ci        //
172cb93a386Sopenharmony_ci        //   - values are not cumulative
173cb93a386Sopenharmony_ci        //   - if explicit values are present at any level in the ancestor chain, those take
174cb93a386Sopenharmony_ci        //     precedence (closest ancestor)
175cb93a386Sopenharmony_ci        //   - last specified value applies to all remaining chars (closest ancestor)
176cb93a386Sopenharmony_ci        //   - these rules apply at node scope (not chunk scope)
177cb93a386Sopenharmony_ci        //
178cb93a386Sopenharmony_ci        // This means we need to discriminate between explicit rotation (rotate value provided for
179cb93a386Sopenharmony_ci        // current char) and implicit rotation (ancestor has some values - but not for the requested
180cb93a386Sopenharmony_ci        // char - we use the last specified value).
181cb93a386Sopenharmony_ci        //
182cb93a386Sopenharmony_ci        // [1] https://www.w3.org/TR/SVG11/text.html#TSpanElementRotateAttribute
183cb93a386Sopenharmony_ci        if (!fRotate.empty()) {
184cb93a386Sopenharmony_ci            if (localCharIndex < fRotate.size()) {
185cb93a386Sopenharmony_ci                // Explicit rotation value overrides anything in the ancestor chain.
186cb93a386Sopenharmony_ci                attrs[PosAttrs::kRotate] = fRotate[localCharIndex];
187cb93a386Sopenharmony_ci                attrs.setImplicitRotate(false);
188cb93a386Sopenharmony_ci            } else if (!attrs.has(PosAttrs::kRotate) || attrs.isImplicitRotate()){
189cb93a386Sopenharmony_ci                // Local implicit rotation (last specified value) overrides ancestor implicit
190cb93a386Sopenharmony_ci                // rotation.
191cb93a386Sopenharmony_ci                attrs[PosAttrs::kRotate] = fRotate.back();
192cb93a386Sopenharmony_ci                attrs.setImplicitRotate(true);
193cb93a386Sopenharmony_ci            }
194cb93a386Sopenharmony_ci        }
195cb93a386Sopenharmony_ci
196cb93a386Sopenharmony_ci        if (!attrs.hasAny()) {
197cb93a386Sopenharmony_ci            // Once we stop producing explicit position data, there is no reason to
198cb93a386Sopenharmony_ci            // continue trying for higher indices.  We can suppress future lookups.
199cb93a386Sopenharmony_ci            fLastPosIndex = charIndex;
200cb93a386Sopenharmony_ci        }
201cb93a386Sopenharmony_ci    }
202cb93a386Sopenharmony_ci
203cb93a386Sopenharmony_ci    return attrs;
204cb93a386Sopenharmony_ci}
205cb93a386Sopenharmony_ci
206cb93a386Sopenharmony_civoid SkSVGTextContext::ShapeBuffer::append(SkUnichar ch, PositionAdjustment pos) {
207cb93a386Sopenharmony_ci    // relative pos adjustments are cumulative
208cb93a386Sopenharmony_ci    if (!fUtf8PosAdjust.empty()) {
209cb93a386Sopenharmony_ci        pos.offset += fUtf8PosAdjust.back().offset;
210cb93a386Sopenharmony_ci    }
211cb93a386Sopenharmony_ci
212cb93a386Sopenharmony_ci    char utf8_buf[SkUTF::kMaxBytesInUTF8Sequence];
213cb93a386Sopenharmony_ci    const auto utf8_len = SkToInt(SkUTF::ToUTF8(ch, utf8_buf));
214cb93a386Sopenharmony_ci    fUtf8         .push_back_n(utf8_len, utf8_buf);
215cb93a386Sopenharmony_ci    fUtf8PosAdjust.push_back_n(utf8_len, pos);
216cb93a386Sopenharmony_ci}
217cb93a386Sopenharmony_ci
218cb93a386Sopenharmony_civoid SkSVGTextContext::shapePendingBuffer(const SkFont& font) {
219cb93a386Sopenharmony_ci    // TODO: directionality hints?
220cb93a386Sopenharmony_ci    const auto LTR  = true;
221cb93a386Sopenharmony_ci
222cb93a386Sopenharmony_ci    // Initiate shaping: this will generate a series of runs via callbacks.
223cb93a386Sopenharmony_ci    fShaper->shape(fShapeBuffer.fUtf8.data(), fShapeBuffer.fUtf8.size(),
224cb93a386Sopenharmony_ci                   font, LTR, SK_ScalarMax, this);
225cb93a386Sopenharmony_ci    fShapeBuffer.reset();
226cb93a386Sopenharmony_ci}
227cb93a386Sopenharmony_ci
228cb93a386Sopenharmony_ciSkSVGTextContext::SkSVGTextContext(const SkSVGRenderContext& ctx, const ShapedTextCallback& cb,
229cb93a386Sopenharmony_ci                                   const SkSVGTextPath* tpath)
230cb93a386Sopenharmony_ci    : fRenderContext(ctx)
231cb93a386Sopenharmony_ci    , fCallback(cb)
232cb93a386Sopenharmony_ci    , fShaper(SkShaper::Make(ctx.fontMgr()))
233cb93a386Sopenharmony_ci    , fChunkAlignmentFactor(ComputeAlignmentFactor(ctx.presentationContext()))
234cb93a386Sopenharmony_ci{
235cb93a386Sopenharmony_ci    if (tpath) {
236cb93a386Sopenharmony_ci        fPathData = std::make_unique<PathData>(ctx, *tpath);
237cb93a386Sopenharmony_ci
238cb93a386Sopenharmony_ci        // https://www.w3.org/TR/SVG11/text.html#TextPathElementStartOffsetAttribute
239cb93a386Sopenharmony_ci        auto resolve_offset = [this](const SkSVGLength& offset) {
240cb93a386Sopenharmony_ci            if (offset.unit() != SkSVGLength::Unit::kPercentage) {
241cb93a386Sopenharmony_ci                // "If a <length> other than a percentage is given, then the ‘startOffset’
242cb93a386Sopenharmony_ci                // represents a distance along the path measured in the current user coordinate
243cb93a386Sopenharmony_ci                // system."
244cb93a386Sopenharmony_ci                return fRenderContext.lengthContext()
245cb93a386Sopenharmony_ci                                     .resolve(offset, SkSVGLengthContext::LengthType::kHorizontal);
246cb93a386Sopenharmony_ci            }
247cb93a386Sopenharmony_ci
248cb93a386Sopenharmony_ci            // "If a percentage is given, then the ‘startOffset’ represents a percentage distance
249cb93a386Sopenharmony_ci            // along the entire path."
250cb93a386Sopenharmony_ci            return offset.value() * fPathData->length() / 100;
251cb93a386Sopenharmony_ci        };
252cb93a386Sopenharmony_ci
253cb93a386Sopenharmony_ci        // startOffset acts as an initial absolute position
254cb93a386Sopenharmony_ci        fChunkPos.fX = resolve_offset(tpath->getStartOffset());
255cb93a386Sopenharmony_ci    }
256cb93a386Sopenharmony_ci}
257cb93a386Sopenharmony_ci
258cb93a386Sopenharmony_ciSkSVGTextContext::~SkSVGTextContext() {
259cb93a386Sopenharmony_ci    this->flushChunk(fRenderContext);
260cb93a386Sopenharmony_ci}
261cb93a386Sopenharmony_ci
262cb93a386Sopenharmony_civoid SkSVGTextContext::shapeFragment(const SkString& txt, const SkSVGRenderContext& ctx,
263cb93a386Sopenharmony_ci                                     SkSVGXmlSpace xs) {
264cb93a386Sopenharmony_ci    // https://www.w3.org/TR/SVG11/text.html#WhiteSpace
265cb93a386Sopenharmony_ci    // https://www.w3.org/TR/2008/REC-xml-20081126/#NT-S
266cb93a386Sopenharmony_ci    auto filterWSDefault = [this](SkUnichar ch) -> SkUnichar {
267cb93a386Sopenharmony_ci        // Remove all newline chars.
268cb93a386Sopenharmony_ci        if (ch == '\n') {
269cb93a386Sopenharmony_ci            return -1;
270cb93a386Sopenharmony_ci        }
271cb93a386Sopenharmony_ci
272cb93a386Sopenharmony_ci        // Convert tab chars to space.
273cb93a386Sopenharmony_ci        if (ch == '\t') {
274cb93a386Sopenharmony_ci            ch = ' ';
275cb93a386Sopenharmony_ci        }
276cb93a386Sopenharmony_ci
277cb93a386Sopenharmony_ci        // Consolidate contiguous space chars and strip leading spaces (fPrevCharSpace
278cb93a386Sopenharmony_ci        // starts off as true).
279cb93a386Sopenharmony_ci        if (fPrevCharSpace && ch == ' ') {
280cb93a386Sopenharmony_ci            return -1;
281cb93a386Sopenharmony_ci        }
282cb93a386Sopenharmony_ci
283cb93a386Sopenharmony_ci        // TODO: Strip trailing WS?  Doing this across chunks would require another buffering
284cb93a386Sopenharmony_ci        //   layer.  In general, trailing WS should have no rendering side effects. Skipping
285cb93a386Sopenharmony_ci        //   for now.
286cb93a386Sopenharmony_ci        return ch;
287cb93a386Sopenharmony_ci    };
288cb93a386Sopenharmony_ci    auto filterWSPreserve = [](SkUnichar ch) -> SkUnichar {
289cb93a386Sopenharmony_ci        // Convert newline and tab chars to space.
290cb93a386Sopenharmony_ci        if (ch == '\n' || ch == '\t') {
291cb93a386Sopenharmony_ci            ch = ' ';
292cb93a386Sopenharmony_ci        }
293cb93a386Sopenharmony_ci        return ch;
294cb93a386Sopenharmony_ci    };
295cb93a386Sopenharmony_ci
296cb93a386Sopenharmony_ci    // Stash paints for access from SkShaper callbacks.
297cb93a386Sopenharmony_ci    fCurrentFill   = ctx.fillPaint();
298cb93a386Sopenharmony_ci    fCurrentStroke = ctx.strokePaint();
299cb93a386Sopenharmony_ci
300cb93a386Sopenharmony_ci    const auto font = ResolveFont(ctx);
301cb93a386Sopenharmony_ci    fShapeBuffer.reserve(txt.size());
302cb93a386Sopenharmony_ci
303cb93a386Sopenharmony_ci    const char* ch_ptr = txt.c_str();
304cb93a386Sopenharmony_ci    const char* ch_end = ch_ptr + txt.size();
305cb93a386Sopenharmony_ci
306cb93a386Sopenharmony_ci    while (ch_ptr < ch_end) {
307cb93a386Sopenharmony_ci        auto ch = SkUTF::NextUTF8(&ch_ptr, ch_end);
308cb93a386Sopenharmony_ci        ch = (xs == SkSVGXmlSpace::kDefault)
309cb93a386Sopenharmony_ci                ? filterWSDefault(ch)
310cb93a386Sopenharmony_ci                : filterWSPreserve(ch);
311cb93a386Sopenharmony_ci
312cb93a386Sopenharmony_ci        if (ch < 0) {
313cb93a386Sopenharmony_ci            // invalid utf or char filtered out
314cb93a386Sopenharmony_ci            continue;
315cb93a386Sopenharmony_ci        }
316cb93a386Sopenharmony_ci
317cb93a386Sopenharmony_ci        SkASSERT(fPosResolver);
318cb93a386Sopenharmony_ci        const auto pos = fPosResolver->resolve(fCurrentCharIndex++);
319cb93a386Sopenharmony_ci
320cb93a386Sopenharmony_ci        // Absolute position adjustments define a new chunk.
321cb93a386Sopenharmony_ci        // (https://www.w3.org/TR/SVG11/text.html#TextLayoutIntroduction)
322cb93a386Sopenharmony_ci        if (pos.has(PosAttrs::kX) || pos.has(PosAttrs::kY)) {
323cb93a386Sopenharmony_ci            this->shapePendingBuffer(font);
324cb93a386Sopenharmony_ci            this->flushChunk(ctx);
325cb93a386Sopenharmony_ci
326cb93a386Sopenharmony_ci            // New chunk position.
327cb93a386Sopenharmony_ci            if (pos.has(PosAttrs::kX)) {
328cb93a386Sopenharmony_ci                fChunkPos.fX = pos[PosAttrs::kX];
329cb93a386Sopenharmony_ci            }
330cb93a386Sopenharmony_ci            if (pos.has(PosAttrs::kY)) {
331cb93a386Sopenharmony_ci                fChunkPos.fY = pos[PosAttrs::kY];
332cb93a386Sopenharmony_ci            }
333cb93a386Sopenharmony_ci        }
334cb93a386Sopenharmony_ci
335cb93a386Sopenharmony_ci        fShapeBuffer.append(ch, {
336cb93a386Sopenharmony_ci            {
337cb93a386Sopenharmony_ci                pos.has(PosAttrs::kDx) ? pos[PosAttrs::kDx] : 0,
338cb93a386Sopenharmony_ci                pos.has(PosAttrs::kDy) ? pos[PosAttrs::kDy] : 0,
339cb93a386Sopenharmony_ci            },
340cb93a386Sopenharmony_ci            pos.has(PosAttrs::kRotate) ? SkDegreesToRadians(pos[PosAttrs::kRotate]) : 0,
341cb93a386Sopenharmony_ci        });
342cb93a386Sopenharmony_ci
343cb93a386Sopenharmony_ci        fPrevCharSpace = (ch == ' ');
344cb93a386Sopenharmony_ci    }
345cb93a386Sopenharmony_ci
346cb93a386Sopenharmony_ci    this->shapePendingBuffer(font);
347cb93a386Sopenharmony_ci
348cb93a386Sopenharmony_ci    // Note: at this point we have shaped and buffered RunRecs for the current fragment.
349cb93a386Sopenharmony_ci    // The active text chunk continues until an explicit or implicit flush.
350cb93a386Sopenharmony_ci}
351cb93a386Sopenharmony_ci
352cb93a386Sopenharmony_ciSkSVGTextContext::PathData::PathData(const SkSVGRenderContext& ctx, const SkSVGTextPath& tpath)
353cb93a386Sopenharmony_ci{
354cb93a386Sopenharmony_ci    const auto ref = ctx.findNodeById(tpath.getHref());
355cb93a386Sopenharmony_ci    if (!ref) {
356cb93a386Sopenharmony_ci        return;
357cb93a386Sopenharmony_ci    }
358cb93a386Sopenharmony_ci
359cb93a386Sopenharmony_ci    SkContourMeasureIter cmi(ref->asPath(ctx), false);
360cb93a386Sopenharmony_ci    while (sk_sp<SkContourMeasure> contour = cmi.next()) {
361cb93a386Sopenharmony_ci        fLength += contour->length();
362cb93a386Sopenharmony_ci        fContours.push_back(std::move(contour));
363cb93a386Sopenharmony_ci    }
364cb93a386Sopenharmony_ci}
365cb93a386Sopenharmony_ci
366cb93a386Sopenharmony_ciSkMatrix SkSVGTextContext::PathData::getMatrixAt(float offset) const {
367cb93a386Sopenharmony_ci    if (offset >= 0) {
368cb93a386Sopenharmony_ci        for (const auto& contour : fContours) {
369cb93a386Sopenharmony_ci            const auto contour_len = contour->length();
370cb93a386Sopenharmony_ci            if (offset < contour_len) {
371cb93a386Sopenharmony_ci                SkMatrix m;
372cb93a386Sopenharmony_ci                return contour->getMatrix(offset, &m) ? m : SkMatrix::I();
373cb93a386Sopenharmony_ci            }
374cb93a386Sopenharmony_ci            offset -= contour_len;
375cb93a386Sopenharmony_ci        }
376cb93a386Sopenharmony_ci    }
377cb93a386Sopenharmony_ci
378cb93a386Sopenharmony_ci    // Quick & dirty way to "skip" rendering of glyphs off path.
379cb93a386Sopenharmony_ci    return SkMatrix::Translate(std::numeric_limits<float>::infinity(),
380cb93a386Sopenharmony_ci                               std::numeric_limits<float>::infinity());
381cb93a386Sopenharmony_ci}
382cb93a386Sopenharmony_ci
383cb93a386Sopenharmony_ciSkRSXform SkSVGTextContext::computeGlyphXform(SkGlyphID glyph, const SkFont& font,
384cb93a386Sopenharmony_ci                                              const SkPoint& glyph_pos,
385cb93a386Sopenharmony_ci                                              const PositionAdjustment& pos_adjust) const {
386cb93a386Sopenharmony_ci    SkPoint pos = fChunkPos + glyph_pos + pos_adjust.offset + fChunkAdvance * fChunkAlignmentFactor;
387cb93a386Sopenharmony_ci    if (!fPathData) {
388cb93a386Sopenharmony_ci        return SkRSXform::MakeFromRadians(/*scale=*/ 1, pos_adjust.rotation, pos.fX, pos.fY, 0, 0);
389cb93a386Sopenharmony_ci    }
390cb93a386Sopenharmony_ci
391cb93a386Sopenharmony_ci    // We're in a textPath scope, reposition the glyph on path.
392cb93a386Sopenharmony_ci    // (https://www.w3.org/TR/SVG11/text.html#TextpathLayoutRules)
393cb93a386Sopenharmony_ci
394cb93a386Sopenharmony_ci    // Path positioning is based on the glyph center (horizontal component).
395cb93a386Sopenharmony_ci    float glyph_width;
396cb93a386Sopenharmony_ci    font.getWidths(&glyph, 1, &glyph_width);
397cb93a386Sopenharmony_ci    auto path_offset = pos.fX + glyph_width * .5f;
398cb93a386Sopenharmony_ci
399cb93a386Sopenharmony_ci    // In addition to the path matrix, the final glyph matrix also includes:
400cb93a386Sopenharmony_ci    //
401cb93a386Sopenharmony_ci    //   -- vertical position adjustment "dy" ("dx" is factored into path_offset)
402cb93a386Sopenharmony_ci    //   -- glyph origin adjustment (undoing the glyph center offset above)
403cb93a386Sopenharmony_ci    //   -- explicit rotation adjustment (composing with the path glyph rotation)
404cb93a386Sopenharmony_ci    const auto m = fPathData->getMatrixAt(path_offset) *
405cb93a386Sopenharmony_ci            SkMatrix::Translate(-glyph_width * .5f, pos_adjust.offset.fY) *
406cb93a386Sopenharmony_ci            SkMatrix::RotateRad(pos_adjust.rotation);
407cb93a386Sopenharmony_ci
408cb93a386Sopenharmony_ci    return SkRSXform::Make(m.getScaleX(), m.getSkewY(), m.getTranslateX(), m.getTranslateY());
409cb93a386Sopenharmony_ci}
410cb93a386Sopenharmony_ci
411cb93a386Sopenharmony_civoid SkSVGTextContext::flushChunk(const SkSVGRenderContext& ctx) {
412cb93a386Sopenharmony_ci    SkTextBlobBuilder blobBuilder;
413cb93a386Sopenharmony_ci
414cb93a386Sopenharmony_ci    for (const auto& run : fRuns) {
415cb93a386Sopenharmony_ci        const auto& buf = blobBuilder.allocRunRSXform(run.font, SkToInt(run.glyphCount));
416cb93a386Sopenharmony_ci        std::copy(run.glyphs.get(), run.glyphs.get() + run.glyphCount, buf.glyphs);
417cb93a386Sopenharmony_ci        for (size_t i = 0; i < run.glyphCount; ++i) {
418cb93a386Sopenharmony_ci            buf.xforms()[i] = this->computeGlyphXform(run.glyphs[i],
419cb93a386Sopenharmony_ci                                                      run.font,
420cb93a386Sopenharmony_ci                                                      run.glyphPos[i],
421cb93a386Sopenharmony_ci                                                      run.glyhPosAdjust[i]);
422cb93a386Sopenharmony_ci        }
423cb93a386Sopenharmony_ci
424cb93a386Sopenharmony_ci        fCallback(ctx, blobBuilder.make(), run.fillPaint.get(), run.strokePaint.get());
425cb93a386Sopenharmony_ci    }
426cb93a386Sopenharmony_ci
427cb93a386Sopenharmony_ci    fChunkPos += fChunkAdvance;
428cb93a386Sopenharmony_ci    fChunkAdvance = {0,0};
429cb93a386Sopenharmony_ci    fChunkAlignmentFactor = ComputeAlignmentFactor(ctx.presentationContext());
430cb93a386Sopenharmony_ci
431cb93a386Sopenharmony_ci    fRuns.clear();
432cb93a386Sopenharmony_ci}
433cb93a386Sopenharmony_ci
434cb93a386Sopenharmony_ciSkShaper::RunHandler::Buffer SkSVGTextContext::runBuffer(const RunInfo& ri) {
435cb93a386Sopenharmony_ci    SkASSERT(ri.glyphCount);
436cb93a386Sopenharmony_ci
437cb93a386Sopenharmony_ci    fRuns.push_back({
438cb93a386Sopenharmony_ci        ri.fFont,
439cb93a386Sopenharmony_ci        fCurrentFill.isValid()   ? std::make_unique<SkPaint>(*fCurrentFill)   : nullptr,
440cb93a386Sopenharmony_ci        fCurrentStroke.isValid() ? std::make_unique<SkPaint>(*fCurrentStroke) : nullptr,
441cb93a386Sopenharmony_ci        std::make_unique<SkGlyphID[]         >(ri.glyphCount),
442cb93a386Sopenharmony_ci        std::make_unique<SkPoint[]           >(ri.glyphCount),
443cb93a386Sopenharmony_ci        std::make_unique<PositionAdjustment[]>(ri.glyphCount),
444cb93a386Sopenharmony_ci        ri.glyphCount,
445cb93a386Sopenharmony_ci        ri.fAdvance,
446cb93a386Sopenharmony_ci    });
447cb93a386Sopenharmony_ci
448cb93a386Sopenharmony_ci    // Ensure sufficient space to temporarily fetch cluster information.
449cb93a386Sopenharmony_ci    fShapeClusterBuffer.resize(std::max(fShapeClusterBuffer.size(), ri.glyphCount));
450cb93a386Sopenharmony_ci
451cb93a386Sopenharmony_ci    return {
452cb93a386Sopenharmony_ci        fRuns.back().glyphs.get(),
453cb93a386Sopenharmony_ci        fRuns.back().glyphPos.get(),
454cb93a386Sopenharmony_ci        nullptr,
455cb93a386Sopenharmony_ci        fShapeClusterBuffer.data(),
456cb93a386Sopenharmony_ci        fChunkAdvance,
457cb93a386Sopenharmony_ci    };
458cb93a386Sopenharmony_ci}
459cb93a386Sopenharmony_ci
460cb93a386Sopenharmony_civoid SkSVGTextContext::commitRunBuffer(const RunInfo& ri) {
461cb93a386Sopenharmony_ci    const auto& current_run = fRuns.back();
462cb93a386Sopenharmony_ci
463cb93a386Sopenharmony_ci    // stash position adjustments
464cb93a386Sopenharmony_ci    for (size_t i = 0; i < ri.glyphCount; ++i) {
465cb93a386Sopenharmony_ci        const auto utf8_index = fShapeClusterBuffer[i];
466cb93a386Sopenharmony_ci        current_run.glyhPosAdjust[i] = fShapeBuffer.fUtf8PosAdjust[SkToInt(utf8_index)];
467cb93a386Sopenharmony_ci    }
468cb93a386Sopenharmony_ci
469cb93a386Sopenharmony_ci    // Offset adjustments are cumulative - we only need to advance the current chunk
470cb93a386Sopenharmony_ci    // with the last value.
471cb93a386Sopenharmony_ci    fChunkAdvance += ri.fAdvance + fShapeBuffer.fUtf8PosAdjust.back().offset;
472cb93a386Sopenharmony_ci}
473cb93a386Sopenharmony_ci
474cb93a386Sopenharmony_civoid SkSVGTextFragment::renderText(const SkSVGRenderContext& ctx, SkSVGTextContext* tctx,
475cb93a386Sopenharmony_ci                                   SkSVGXmlSpace xs) const {
476cb93a386Sopenharmony_ci    // N.B.: unlike regular elements, text fragments do not establish a new OBB scope -- they
477cb93a386Sopenharmony_ci    // always defer to the root <text> element for OBB resolution.
478cb93a386Sopenharmony_ci    SkSVGRenderContext localContext(ctx);
479cb93a386Sopenharmony_ci
480cb93a386Sopenharmony_ci    if (this->onPrepareToRender(&localContext)) {
481cb93a386Sopenharmony_ci        this->onShapeText(localContext, tctx, xs);
482cb93a386Sopenharmony_ci    }
483cb93a386Sopenharmony_ci}
484cb93a386Sopenharmony_ci
485cb93a386Sopenharmony_ciSkPath SkSVGTextFragment::onAsPath(const SkSVGRenderContext&) const {
486cb93a386Sopenharmony_ci    // TODO
487cb93a386Sopenharmony_ci    return SkPath();
488cb93a386Sopenharmony_ci}
489cb93a386Sopenharmony_ci
490cb93a386Sopenharmony_civoid SkSVGTextContainer::appendChild(sk_sp<SkSVGNode> child) {
491cb93a386Sopenharmony_ci    // Only allow text content child nodes.
492cb93a386Sopenharmony_ci    switch (child->tag()) {
493cb93a386Sopenharmony_ci    case SkSVGTag::kTextLiteral:
494cb93a386Sopenharmony_ci    case SkSVGTag::kTextPath:
495cb93a386Sopenharmony_ci    case SkSVGTag::kTSpan:
496cb93a386Sopenharmony_ci        fChildren.push_back(
497cb93a386Sopenharmony_ci            sk_sp<SkSVGTextFragment>(static_cast<SkSVGTextFragment*>(child.release())));
498cb93a386Sopenharmony_ci        break;
499cb93a386Sopenharmony_ci    default:
500cb93a386Sopenharmony_ci        break;
501cb93a386Sopenharmony_ci    }
502cb93a386Sopenharmony_ci}
503cb93a386Sopenharmony_ci
504cb93a386Sopenharmony_ci
505cb93a386Sopenharmony_cistd::vector<sk_sp<SkSVGNode>> SkSVGTextContainer::getChild() {
506cb93a386Sopenharmony_ci    std::vector<sk_sp<SkSVGNode>> res;
507cb93a386Sopenharmony_ci    res.assign(fChildren.begin(),fChildren.end());
508cb93a386Sopenharmony_ci    return res;
509cb93a386Sopenharmony_ci}
510cb93a386Sopenharmony_ci
511cb93a386Sopenharmony_civoid SkSVGTextContainer::onShapeText(const SkSVGRenderContext& ctx, SkSVGTextContext* tctx,
512cb93a386Sopenharmony_ci                                     SkSVGXmlSpace) const {
513cb93a386Sopenharmony_ci    SkASSERT(tctx);
514cb93a386Sopenharmony_ci
515cb93a386Sopenharmony_ci    const SkSVGTextContext::ScopedPosResolver resolver(*this, ctx.lengthContext(), tctx);
516cb93a386Sopenharmony_ci
517cb93a386Sopenharmony_ci    for (const auto& frag : fChildren) {
518cb93a386Sopenharmony_ci        // Containers always override xml:space with the local value.
519cb93a386Sopenharmony_ci        frag->renderText(ctx, tctx, this->getXmlSpace());
520cb93a386Sopenharmony_ci    }
521cb93a386Sopenharmony_ci}
522cb93a386Sopenharmony_ci
523cb93a386Sopenharmony_ci// https://www.w3.org/TR/SVG11/text.html#WhiteSpace
524cb93a386Sopenharmony_citemplate <>
525cb93a386Sopenharmony_cibool SkSVGAttributeParser::parse(SkSVGXmlSpace* xs) {
526cb93a386Sopenharmony_ci    static constexpr std::tuple<const char*, SkSVGXmlSpace> gXmlSpaceMap[] = {
527cb93a386Sopenharmony_ci            {"default" , SkSVGXmlSpace::kDefault },
528cb93a386Sopenharmony_ci            {"preserve", SkSVGXmlSpace::kPreserve},
529cb93a386Sopenharmony_ci    };
530cb93a386Sopenharmony_ci
531cb93a386Sopenharmony_ci    return this->parseEnumMap(gXmlSpaceMap, xs) && this->parseEOSToken();
532cb93a386Sopenharmony_ci}
533cb93a386Sopenharmony_ci
534cb93a386Sopenharmony_cibool SkSVGTextContainer::parseAndSetAttribute(const char* name, const char* value) {
535cb93a386Sopenharmony_ci    return INHERITED::parseAndSetAttribute(name, value) ||
536cb93a386Sopenharmony_ci           this->setX(SkSVGAttributeParser::parse<std::vector<SkSVGLength>>("x", name, value)) ||
537cb93a386Sopenharmony_ci           this->setY(SkSVGAttributeParser::parse<std::vector<SkSVGLength>>("y", name, value)) ||
538cb93a386Sopenharmony_ci           this->setDx(SkSVGAttributeParser::parse<std::vector<SkSVGLength>>("dx", name, value)) ||
539cb93a386Sopenharmony_ci           this->setDy(SkSVGAttributeParser::parse<std::vector<SkSVGLength>>("dy", name, value)) ||
540cb93a386Sopenharmony_ci           this->setRotate(SkSVGAttributeParser::parse<std::vector<SkSVGNumberType>>("rotate",
541cb93a386Sopenharmony_ci                                                                                     name,
542cb93a386Sopenharmony_ci                                                                                     value)) ||
543cb93a386Sopenharmony_ci           this->setXmlSpace(SkSVGAttributeParser::parse<SkSVGXmlSpace>("xml:space", name, value));
544cb93a386Sopenharmony_ci}
545cb93a386Sopenharmony_ci
546cb93a386Sopenharmony_civoid SkSVGTextLiteral::onShapeText(const SkSVGRenderContext& ctx, SkSVGTextContext* tctx,
547cb93a386Sopenharmony_ci                                   SkSVGXmlSpace xs) const {
548cb93a386Sopenharmony_ci    SkASSERT(tctx);
549cb93a386Sopenharmony_ci
550cb93a386Sopenharmony_ci    tctx->shapeFragment(this->getText(), ctx, xs);
551cb93a386Sopenharmony_ci}
552cb93a386Sopenharmony_ci
553cb93a386Sopenharmony_civoid SkSVGText::onRender(const SkSVGRenderContext& ctx) const {
554cb93a386Sopenharmony_ci    const SkSVGTextContext::ShapedTextCallback render_text = [](const SkSVGRenderContext& ctx,
555cb93a386Sopenharmony_ci                                                                const sk_sp<SkTextBlob>& blob,
556cb93a386Sopenharmony_ci                                                                const SkPaint* fill,
557cb93a386Sopenharmony_ci                                                                const SkPaint* stroke) {
558cb93a386Sopenharmony_ci        if (fill) {
559cb93a386Sopenharmony_ci            ctx.canvas()->drawTextBlob(blob, 0, 0, *fill);
560cb93a386Sopenharmony_ci        }
561cb93a386Sopenharmony_ci        if (stroke) {
562cb93a386Sopenharmony_ci            ctx.canvas()->drawTextBlob(blob, 0, 0, *stroke);
563cb93a386Sopenharmony_ci        }
564cb93a386Sopenharmony_ci    };
565cb93a386Sopenharmony_ci
566cb93a386Sopenharmony_ci    // Root <text> nodes establish a text layout context.
567cb93a386Sopenharmony_ci    SkSVGTextContext tctx(ctx, render_text);
568cb93a386Sopenharmony_ci
569cb93a386Sopenharmony_ci    this->onShapeText(ctx, &tctx, this->getXmlSpace());
570cb93a386Sopenharmony_ci}
571cb93a386Sopenharmony_ci
572cb93a386Sopenharmony_ciSkRect SkSVGText::onObjectBoundingBox(const SkSVGRenderContext& ctx) const {
573cb93a386Sopenharmony_ci    SkRect bounds = SkRect::MakeEmpty();
574cb93a386Sopenharmony_ci
575cb93a386Sopenharmony_ci    const SkSVGTextContext::ShapedTextCallback compute_bounds =
576cb93a386Sopenharmony_ci        [&bounds](const SkSVGRenderContext& ctx, const sk_sp<SkTextBlob>& blob, const SkPaint*,
577cb93a386Sopenharmony_ci                  const SkPaint*) {
578cb93a386Sopenharmony_ci            if (!blob) {
579cb93a386Sopenharmony_ci                return;
580cb93a386Sopenharmony_ci            }
581cb93a386Sopenharmony_ci
582cb93a386Sopenharmony_ci            SkAutoSTArray<64, SkRect> glyphBounds;
583cb93a386Sopenharmony_ci
584cb93a386Sopenharmony_ci            for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) {
585cb93a386Sopenharmony_ci                glyphBounds.reset(SkToInt(it.glyphCount()));
586cb93a386Sopenharmony_ci                it.font().getBounds(it.glyphs(), it.glyphCount(), glyphBounds.get(), nullptr);
587cb93a386Sopenharmony_ci
588cb93a386Sopenharmony_ci                SkASSERT(it.positioning() == SkTextBlobRunIterator::kRSXform_Positioning);
589cb93a386Sopenharmony_ci                SkMatrix m;
590cb93a386Sopenharmony_ci                for (uint32_t i = 0; i < it.glyphCount(); ++i) {
591cb93a386Sopenharmony_ci                    m.setRSXform(it.xforms()[i]);
592cb93a386Sopenharmony_ci                    bounds.join(m.mapRect(glyphBounds[i]));
593cb93a386Sopenharmony_ci                }
594cb93a386Sopenharmony_ci            }
595cb93a386Sopenharmony_ci        };
596cb93a386Sopenharmony_ci
597cb93a386Sopenharmony_ci    {
598cb93a386Sopenharmony_ci        SkSVGTextContext tctx(ctx, compute_bounds);
599cb93a386Sopenharmony_ci        this->onShapeText(ctx, &tctx, this->getXmlSpace());
600cb93a386Sopenharmony_ci    }
601cb93a386Sopenharmony_ci
602cb93a386Sopenharmony_ci    return bounds;
603cb93a386Sopenharmony_ci}
604cb93a386Sopenharmony_ci
605cb93a386Sopenharmony_ciSkPath SkSVGText::onAsPath(const SkSVGRenderContext& ctx) const {
606cb93a386Sopenharmony_ci    SkPathBuilder builder;
607cb93a386Sopenharmony_ci
608cb93a386Sopenharmony_ci    const SkSVGTextContext::ShapedTextCallback as_path =
609cb93a386Sopenharmony_ci        [&builder](const SkSVGRenderContext& ctx, const sk_sp<SkTextBlob>& blob, const SkPaint*,
610cb93a386Sopenharmony_ci                   const SkPaint*) {
611cb93a386Sopenharmony_ci            if (!blob) {
612cb93a386Sopenharmony_ci                return;
613cb93a386Sopenharmony_ci            }
614cb93a386Sopenharmony_ci
615cb93a386Sopenharmony_ci            for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) {
616cb93a386Sopenharmony_ci                struct GetPathsCtx {
617cb93a386Sopenharmony_ci                    SkPathBuilder&   builder;
618cb93a386Sopenharmony_ci                    const SkRSXform* xform;
619cb93a386Sopenharmony_ci                } get_paths_ctx {builder, it.xforms()};
620cb93a386Sopenharmony_ci
621cb93a386Sopenharmony_ci                it.font().getPaths(it.glyphs(), it.glyphCount(), [](const SkPath* path,
622cb93a386Sopenharmony_ci                                                                    const SkMatrix& matrix,
623cb93a386Sopenharmony_ci                                                                    void* raw_ctx) {
624cb93a386Sopenharmony_ci                    auto* get_paths_ctx = static_cast<GetPathsCtx*>(raw_ctx);
625cb93a386Sopenharmony_ci                    const auto& glyph_rsx = *get_paths_ctx->xform++;
626cb93a386Sopenharmony_ci
627cb93a386Sopenharmony_ci                    if (!path) {
628cb93a386Sopenharmony_ci                        return;
629cb93a386Sopenharmony_ci                    }
630cb93a386Sopenharmony_ci
631cb93a386Sopenharmony_ci                    SkMatrix glyph_matrix;
632cb93a386Sopenharmony_ci                    glyph_matrix.setRSXform(glyph_rsx);
633cb93a386Sopenharmony_ci                    glyph_matrix.preConcat(matrix);
634cb93a386Sopenharmony_ci
635cb93a386Sopenharmony_ci                    get_paths_ctx->builder.addPath(path->makeTransform(glyph_matrix));
636cb93a386Sopenharmony_ci                }, &get_paths_ctx);
637cb93a386Sopenharmony_ci            }
638cb93a386Sopenharmony_ci        };
639cb93a386Sopenharmony_ci
640cb93a386Sopenharmony_ci    {
641cb93a386Sopenharmony_ci        SkSVGTextContext tctx(ctx, as_path);
642cb93a386Sopenharmony_ci        this->onShapeText(ctx, &tctx, this->getXmlSpace());
643cb93a386Sopenharmony_ci    }
644cb93a386Sopenharmony_ci
645cb93a386Sopenharmony_ci    auto path = builder.detach();
646cb93a386Sopenharmony_ci    this->mapToParent(&path);
647cb93a386Sopenharmony_ci
648cb93a386Sopenharmony_ci    return path;
649cb93a386Sopenharmony_ci}
650cb93a386Sopenharmony_ci
651cb93a386Sopenharmony_civoid SkSVGTextPath::onShapeText(const SkSVGRenderContext& ctx, SkSVGTextContext* parent_tctx,
652cb93a386Sopenharmony_ci                                 SkSVGXmlSpace xs) const {
653cb93a386Sopenharmony_ci    SkASSERT(parent_tctx);
654cb93a386Sopenharmony_ci
655cb93a386Sopenharmony_ci    // textPath nodes establish a new text layout context.
656cb93a386Sopenharmony_ci    SkSVGTextContext tctx(ctx, parent_tctx->getCallback(), this);
657cb93a386Sopenharmony_ci
658cb93a386Sopenharmony_ci    this->INHERITED::onShapeText(ctx, &tctx, xs);
659cb93a386Sopenharmony_ci}
660cb93a386Sopenharmony_ci
661cb93a386Sopenharmony_cibool SkSVGTextPath::parseAndSetAttribute(const char* name, const char* value) {
662cb93a386Sopenharmony_ci    return INHERITED::parseAndSetAttribute(name, value) ||
663cb93a386Sopenharmony_ci        this->setHref(SkSVGAttributeParser::parse<SkSVGIRI>("xlink:href", name, value)) ||
664cb93a386Sopenharmony_ci        this->setStartOffset(SkSVGAttributeParser::parse<SkSVGLength>("startOffset", name, value));
665cb93a386Sopenharmony_ci}
666