Flutter Engine
The Flutter Engine
SkSVGText.cpp
Go to the documentation of this file.
1/*
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
9
10#include <limits>
11
14#include "include/core/SkFont.h"
24#include "src/base/SkUTF.h"
26
27using namespace skia_private;
28
29namespace {
30
31static SkFont ResolveFont(const SkSVGRenderContext& ctx) {
32 auto weight = [](const SkSVGFontWeight& w) {
33 switch (w.type()) {
48 SkASSERT(false);
50 }
51 }
53 };
54
55 auto slant = [](const SkSVGFontStyle& s) {
56 switch (s.type()) {
61 SkASSERT(false);
63 }
64 }
66 };
67
68 const auto& family = ctx.presentationContext().fInherited.fFontFamily->family();
69 const SkFontStyle style(weight(*ctx.presentationContext().fInherited.fFontWeight),
72
73 const auto size =
76
77 // TODO: we likely want matchFamilyStyle here, but switching away from legacyMakeTypeface
78 // changes all the results when using the default fontmgr.
79 auto tf = ctx.fontMgr()->legacyMakeTypeface(family.c_str(), style);
80 if (!tf) {
81 tf = ctx.fontMgr()->legacyMakeTypeface(nullptr, style);
82 }
83 SkASSERT(tf);
84 SkFont font(std::move(tf), size);
85 font.setHinting(SkFontHinting::kNone);
86 font.setSubpixel(true);
87 font.setLinearMetrics(true);
88 font.setBaselineSnap(false);
90
91 return font;
92}
93
94static std::vector<float> ResolveLengths(const SkSVGLengthContext& lctx,
95 const std::vector<SkSVGLength>& lengths,
97 std::vector<float> resolved;
98 resolved.reserve(lengths.size());
99
100 for (const auto& l : lengths) {
101 resolved.push_back(lctx.resolve(l, lt));
102 }
103
104 return resolved;
105}
106
107static float ComputeAlignmentFactor(const SkSVGPresentationContext& pctx) {
108 switch (pctx.fInherited.fTextAnchor->type()) {
109 case SkSVGTextAnchor::Type::kStart : return 0.0f;
110 case SkSVGTextAnchor::Type::kMiddle: return -0.5f;
111 case SkSVGTextAnchor::Type::kEnd : return -1.0f;
113 SkASSERT(false);
114 return 0.0f;
115 }
117}
118
119} // namespace
120
122 const SkSVGLengthContext& lctx,
123 SkSVGTextContext* tctx,
124 size_t charIndexOffset)
125 : fTextContext(tctx)
126 , fParent(tctx->fPosResolver)
127 , fCharIndexOffset(charIndexOffset)
128 , fX(ResolveLengths(lctx, txt.getX(), SkSVGLengthContext::LengthType::kHorizontal))
129 , fY(ResolveLengths(lctx, txt.getY(), SkSVGLengthContext::LengthType::kVertical))
130 , fDx(ResolveLengths(lctx, txt.getDx(), SkSVGLengthContext::LengthType::kHorizontal))
131 , fDy(ResolveLengths(lctx, txt.getDy(), SkSVGLengthContext::LengthType::kVertical))
132 , fRotate(txt.getRotate())
133{
134 fTextContext->fPosResolver = this;
135}
136
138 const SkSVGLengthContext& lctx,
139 SkSVGTextContext* tctx)
140 : ScopedPosResolver(txt, lctx, tctx, tctx->fCurrentCharIndex) {}
141
143 fTextContext->fPosResolver = fParent;
144}
145
147 PosAttrs attrs;
148
149 if (charIndex < fLastPosIndex) {
150 SkASSERT(charIndex >= fCharIndexOffset);
151 const auto localCharIndex = charIndex - fCharIndexOffset;
152
153 const auto hasAllLocal = localCharIndex < fX.size() &&
154 localCharIndex < fY.size() &&
155 localCharIndex < fDx.size() &&
156 localCharIndex < fDy.size() &&
157 localCharIndex < fRotate.size();
158 if (!hasAllLocal && fParent) {
159 attrs = fParent->resolve(charIndex);
160 }
161
162 if (localCharIndex < fX.size()) {
163 attrs[PosAttrs::kX] = fX[localCharIndex];
164 }
165 if (localCharIndex < fY.size()) {
166 attrs[PosAttrs::kY] = fY[localCharIndex];
167 }
168 if (localCharIndex < fDx.size()) {
169 attrs[PosAttrs::kDx] = fDx[localCharIndex];
170 }
171 if (localCharIndex < fDy.size()) {
172 attrs[PosAttrs::kDy] = fDy[localCharIndex];
173 }
174
175 // Rotation semantics are interestingly different [1]:
176 //
177 // - values are not cumulative
178 // - if explicit values are present at any level in the ancestor chain, those take
179 // precedence (closest ancestor)
180 // - last specified value applies to all remaining chars (closest ancestor)
181 // - these rules apply at node scope (not chunk scope)
182 //
183 // This means we need to discriminate between explicit rotation (rotate value provided for
184 // current char) and implicit rotation (ancestor has some values - but not for the requested
185 // char - we use the last specified value).
186 //
187 // [1] https://www.w3.org/TR/SVG11/text.html#TSpanElementRotateAttribute
188 if (!fRotate.empty()) {
189 if (localCharIndex < fRotate.size()) {
190 // Explicit rotation value overrides anything in the ancestor chain.
191 attrs[PosAttrs::kRotate] = fRotate[localCharIndex];
192 attrs.setImplicitRotate(false);
193 } else if (!attrs.has(PosAttrs::kRotate) || attrs.isImplicitRotate()){
194 // Local implicit rotation (last specified value) overrides ancestor implicit
195 // rotation.
196 attrs[PosAttrs::kRotate] = fRotate.back();
197 attrs.setImplicitRotate(true);
198 }
199 }
200
201 if (!attrs.hasAny()) {
202 // Once we stop producing explicit position data, there is no reason to
203 // continue trying for higher indices. We can suppress future lookups.
204 fLastPosIndex = charIndex;
205 }
206 }
207
208 return attrs;
209}
210
211void SkSVGTextContext::ShapeBuffer::append(SkUnichar ch, PositionAdjustment pos) {
212 // relative pos adjustments are cumulative
213 if (!fUtf8PosAdjust.empty()) {
214 pos.offset += fUtf8PosAdjust.back().offset;
215 }
216
217 char utf8_buf[SkUTF::kMaxBytesInUTF8Sequence];
218 const auto utf8_len = SkToInt(SkUTF::ToUTF8(ch, utf8_buf));
219 fUtf8 .push_back_n(utf8_len, utf8_buf);
220 fUtf8PosAdjust.push_back_n(utf8_len, pos);
221}
222
223void SkSVGTextContext::shapePendingBuffer(const SkSVGRenderContext& ctx, const SkFont& font) {
224 const char* utf8 = fShapeBuffer.fUtf8.data();
225 size_t utf8Bytes = fShapeBuffer.fUtf8.size();
226
227 std::unique_ptr<SkShaper::FontRunIterator> font_runs =
229 if (!font_runs) {
230 return;
231 }
232 if (!fForcePrimitiveShaping) {
233 // Try to use the passed in shaping callbacks to shape, for example, using harfbuzz and ICU.
234 const uint8_t defaultLTR = 0;
235 std::unique_ptr<SkShaper::BiDiRunIterator> bidi =
236 ctx.makeBidiRunIterator(utf8, utf8Bytes, defaultLTR);
237 std::unique_ptr<SkShaper::LanguageRunIterator> language =
239 std::unique_ptr<SkShaper::ScriptRunIterator> script = ctx.makeScriptRunIterator(utf8, utf8Bytes);
240
241 if (bidi && script && language) {
242 fShaper->shape(utf8,
243 utf8Bytes,
244 *font_runs,
245 *bidi,
246 *script,
247 *language,
248 nullptr,
249 0,
251 this);
252 fShapeBuffer.reset();
253 return;
254 } // If any of the callbacks fail, we'll fallback to the primitive shaping.
255 }
256
257 // bidi, script, and lang are all unused so we can construct them with empty data.
258 SkShaper::TrivialBiDiRunIterator trivial_bidi{0, 0};
259 SkShaper::TrivialScriptRunIterator trivial_script{0, 0};
260 SkShaper::TrivialLanguageRunIterator trivial_lang{nullptr, 0};
261 fShaper->shape(utf8,
262 utf8Bytes,
263 *font_runs,
264 trivial_bidi,
265 trivial_script,
266 trivial_lang,
267 nullptr,
268 0,
270 this);
271 fShapeBuffer.reset();
272}
273
275 const ShapedTextCallback& cb,
276 const SkSVGTextPath* tpath)
277 : fRenderContext(ctx)
278 , fCallback(cb)
279 , fShaper(ctx.makeShaper())
280 , fChunkAlignmentFactor(ComputeAlignmentFactor(ctx.presentationContext())) {
281 // If the shaper callback returns null, fallback to the primitive shaper and
282 // signal that we should not use the other callbacks in shapePendingBuffer
283 if (!fShaper) {
285 fForcePrimitiveShaping = true;
286 }
287 if (tpath) {
288 fPathData = std::make_unique<PathData>(ctx, *tpath);
289
290 // https://www.w3.org/TR/SVG11/text.html#TextPathElementStartOffsetAttribute
291 auto resolve_offset = [this](const SkSVGLength& offset) {
293 // "If a <length> other than a percentage is given, then the ‘startOffset’
294 // represents a distance along the path measured in the current user coordinate
295 // system."
296 return fRenderContext.lengthContext()
298 }
299
300 // "If a percentage is given, then the ‘startOffset’ represents a percentage distance
301 // along the entire path."
302 return offset.value() * fPathData->length() / 100;
303 };
304
305 // startOffset acts as an initial absolute position
306 fChunkPos.fX = resolve_offset(tpath->getStartOffset());
307 }
308}
309
311 this->flushChunk(fRenderContext);
312}
313
315 SkSVGXmlSpace xs) {
316 // https://www.w3.org/TR/SVG11/text.html#WhiteSpace
317 // https://www.w3.org/TR/2008/REC-xml-20081126/#NT-S
318 auto filterWSDefault = [this](SkUnichar ch) -> SkUnichar {
319 // Remove all newline chars.
320 if (ch == '\n') {
321 return -1;
322 }
323
324 // Convert tab chars to space.
325 if (ch == '\t') {
326 ch = ' ';
327 }
328
329 // Consolidate contiguous space chars and strip leading spaces (fPrevCharSpace
330 // starts off as true).
331 if (fPrevCharSpace && ch == ' ') {
332 return -1;
333 }
334
335 // TODO: Strip trailing WS? Doing this across chunks would require another buffering
336 // layer. In general, trailing WS should have no rendering side effects. Skipping
337 // for now.
338 return ch;
339 };
340 auto filterWSPreserve = [](SkUnichar ch) -> SkUnichar {
341 // Convert newline and tab chars to space.
342 if (ch == '\n' || ch == '\t') {
343 ch = ' ';
344 }
345 return ch;
346 };
347
348 // Stash paints for access from SkShaper callbacks.
349 fCurrentFill = ctx.fillPaint();
350 fCurrentStroke = ctx.strokePaint();
351
352 const auto font = ResolveFont(ctx);
353 fShapeBuffer.reserve(txt.size());
354
355 const char* ch_ptr = txt.c_str();
356 const char* ch_end = ch_ptr + txt.size();
357
358 while (ch_ptr < ch_end) {
359 auto ch = SkUTF::NextUTF8(&ch_ptr, ch_end);
360 ch = (xs == SkSVGXmlSpace::kDefault)
361 ? filterWSDefault(ch)
362 : filterWSPreserve(ch);
363
364 if (ch < 0) {
365 // invalid utf or char filtered out
366 continue;
367 }
368
369 SkASSERT(fPosResolver);
370 const auto pos = fPosResolver->resolve(fCurrentCharIndex++);
371
372 // Absolute position adjustments define a new chunk.
373 // (https://www.w3.org/TR/SVG11/text.html#TextLayoutIntroduction)
374 if (pos.has(PosAttrs::kX) || pos.has(PosAttrs::kY)) {
375 this->shapePendingBuffer(ctx, font);
376 this->flushChunk(ctx);
377
378 // New chunk position.
379 if (pos.has(PosAttrs::kX)) {
380 fChunkPos.fX = pos[PosAttrs::kX];
381 }
382 if (pos.has(PosAttrs::kY)) {
383 fChunkPos.fY = pos[PosAttrs::kY];
384 }
385 }
386
387 fShapeBuffer.append(ch, {
388 {
391 },
393 });
394
395 fPrevCharSpace = (ch == ' ');
396 }
397
398 this->shapePendingBuffer(ctx, font);
399
400 // Note: at this point we have shaped and buffered RunRecs for the current fragment.
401 // The active text chunk continues until an explicit or implicit flush.
402}
403
404SkSVGTextContext::PathData::PathData(const SkSVGRenderContext& ctx, const SkSVGTextPath& tpath)
405{
406 const auto ref = ctx.findNodeById(tpath.getHref());
407 if (!ref) {
408 return;
409 }
410
411 SkContourMeasureIter cmi(ref->asPath(ctx), false);
412 while (sk_sp<SkContourMeasure> contour = cmi.next()) {
413 fLength += contour->length();
414 fContours.push_back(std::move(contour));
415 }
416}
417
418SkMatrix SkSVGTextContext::PathData::getMatrixAt(float offset) const {
419 if (offset >= 0) {
420 for (const auto& contour : fContours) {
421 const auto contour_len = contour->length();
422 if (offset < contour_len) {
423 SkMatrix m;
424 return contour->getMatrix(offset, &m) ? m : SkMatrix::I();
425 }
426 offset -= contour_len;
427 }
428 }
429
430 // Quick & dirty way to "skip" rendering of glyphs off path.
431 return SkMatrix::Translate(std::numeric_limits<float>::infinity(),
432 std::numeric_limits<float>::infinity());
433}
434
435SkRSXform SkSVGTextContext::computeGlyphXform(SkGlyphID glyph, const SkFont& font,
436 const SkPoint& glyph_pos,
437 const PositionAdjustment& pos_adjust) const {
438 SkPoint pos = fChunkPos + glyph_pos + pos_adjust.offset + fChunkAdvance * fChunkAlignmentFactor;
439 if (!fPathData) {
440 return SkRSXform::MakeFromRadians(/*scale=*/ 1, pos_adjust.rotation, pos.fX, pos.fY, 0, 0);
441 }
442
443 // We're in a textPath scope, reposition the glyph on path.
444 // (https://www.w3.org/TR/SVG11/text.html#TextpathLayoutRules)
445
446 // Path positioning is based on the glyph center (horizontal component).
447 float glyph_width;
448 font.getWidths(&glyph, 1, &glyph_width);
449 auto path_offset = pos.fX + glyph_width * .5f;
450
451 // In addition to the path matrix, the final glyph matrix also includes:
452 //
453 // -- vertical position adjustment "dy" ("dx" is factored into path_offset)
454 // -- glyph origin adjustment (undoing the glyph center offset above)
455 // -- explicit rotation adjustment (composing with the path glyph rotation)
456 const auto m = fPathData->getMatrixAt(path_offset) *
457 SkMatrix::Translate(-glyph_width * .5f, pos_adjust.offset.fY) *
458 SkMatrix::RotateRad(pos_adjust.rotation);
459
460 return SkRSXform::Make(m.getScaleX(), m.getSkewY(), m.getTranslateX(), m.getTranslateY());
461}
462
464 SkTextBlobBuilder blobBuilder;
465
466 for (const auto& run : fRuns) {
467 const auto& buf = blobBuilder.allocRunRSXform(run.font, SkToInt(run.glyphCount));
468 std::copy(run.glyphs.get(), run.glyphs.get() + run.glyphCount, buf.glyphs);
469 for (size_t i = 0; i < run.glyphCount; ++i) {
470 buf.xforms()[i] = this->computeGlyphXform(run.glyphs[i],
471 run.font,
472 run.glyphPos[i],
473 run.glyhPosAdjust[i]);
474 }
475
476 fCallback(ctx, blobBuilder.make(), run.fillPaint.get(), run.strokePaint.get());
477 }
478
479 fChunkPos += fChunkAdvance;
480 fChunkAdvance = {0,0};
481 fChunkAlignmentFactor = ComputeAlignmentFactor(ctx.presentationContext());
482
483 fRuns.clear();
484}
485
486SkShaper::RunHandler::Buffer SkSVGTextContext::runBuffer(const RunInfo& ri) {
487 SkASSERT(ri.glyphCount);
488
489 fRuns.push_back({
490 ri.fFont,
491 fCurrentFill.isValid() ? std::make_unique<SkPaint>(*fCurrentFill) : nullptr,
492 fCurrentStroke.isValid() ? std::make_unique<SkPaint>(*fCurrentStroke) : nullptr,
493 std::make_unique<SkGlyphID[] >(ri.glyphCount),
494 std::make_unique<SkPoint[] >(ri.glyphCount),
495 std::make_unique<PositionAdjustment[]>(ri.glyphCount),
496 ri.glyphCount,
497 ri.fAdvance,
498 });
499
500 // Ensure sufficient space to temporarily fetch cluster information.
501 fShapeClusterBuffer.resize(std::max(fShapeClusterBuffer.size(), ri.glyphCount));
502
503 return {
504 fRuns.back().glyphs.get(),
505 fRuns.back().glyphPos.get(),
506 nullptr,
507 fShapeClusterBuffer.data(),
508 fChunkAdvance,
509 };
510}
511
512void SkSVGTextContext::commitRunBuffer(const RunInfo& ri) {
513 const auto& current_run = fRuns.back();
514
515 // stash position adjustments
516 for (size_t i = 0; i < ri.glyphCount; ++i) {
517 const auto utf8_index = fShapeClusterBuffer[i];
518 current_run.glyhPosAdjust[i] = fShapeBuffer.fUtf8PosAdjust[SkToInt(utf8_index)];
519 }
520
521 fChunkAdvance += ri.fAdvance;
522}
523
524void SkSVGTextContext::commitLine() {
525 if (!fShapeBuffer.fUtf8PosAdjust.empty()) {
526 // Offset adjustments are cumulative - only advance the current chunk with the last value.
527 fChunkAdvance += fShapeBuffer.fUtf8PosAdjust.back().offset;
528 }
529}
530
532 SkSVGXmlSpace xs) const {
533 // N.B.: unlike regular elements, text fragments do not establish a new OBB scope -- they
534 // always defer to the root <text> element for OBB resolution.
535 SkSVGRenderContext localContext(ctx);
536
537 if (this->onPrepareToRender(&localContext)) {
538 this->onShapeText(localContext, tctx, xs);
539 }
540}
541
542SkPath SkSVGTextFragment::onAsPath(const SkSVGRenderContext&) const {
543 // TODO
544 return SkPath();
545}
546
548 // Only allow text content child nodes.
549 switch (child->tag()) {
552 case SkSVGTag::kTSpan:
553 fChildren.push_back(
554 sk_sp<SkSVGTextFragment>(static_cast<SkSVGTextFragment*>(child.release())));
555 break;
556 default:
557 break;
558 }
559}
560
562 SkSVGXmlSpace) const {
563 SkASSERT(tctx);
564
565 const SkSVGTextContext::ScopedPosResolver resolver(*this, ctx.lengthContext(), tctx);
566
567 for (const auto& frag : fChildren) {
568 // Containers always override xml:space with the local value.
569 frag->renderText(ctx, tctx, this->getXmlSpace());
570 }
571}
572
573// https://www.w3.org/TR/SVG11/text.html#WhiteSpace
574template <>
576 static constexpr std::tuple<const char*, SkSVGXmlSpace> gXmlSpaceMap[] = {
577 {"default" , SkSVGXmlSpace::kDefault },
578 {"preserve", SkSVGXmlSpace::kPreserve},
579 };
580
581 return this->parseEnumMap(gXmlSpaceMap, xs) && this->parseEOSToken();
582}
583
586 this->setX(SkSVGAttributeParser::parse<std::vector<SkSVGLength>>("x", name, value)) ||
587 this->setY(SkSVGAttributeParser::parse<std::vector<SkSVGLength>>("y", name, value)) ||
588 this->setDx(SkSVGAttributeParser::parse<std::vector<SkSVGLength>>("dx", name, value)) ||
589 this->setDy(SkSVGAttributeParser::parse<std::vector<SkSVGLength>>("dy", name, value)) ||
590 this->setRotate(SkSVGAttributeParser::parse<std::vector<SkSVGNumberType>>("rotate",
591 name,
592 value)) ||
593 this->setXmlSpace(SkSVGAttributeParser::parse<SkSVGXmlSpace>("xml:space", name, value));
594}
595
596void SkSVGTextLiteral::onShapeText(const SkSVGRenderContext& ctx, SkSVGTextContext* tctx,
597 SkSVGXmlSpace xs) const {
598 SkASSERT(tctx);
599
600 tctx->shapeFragment(this->getText(), ctx, xs);
601}
602
603void SkSVGText::onRender(const SkSVGRenderContext& ctx) const {
604 const SkSVGTextContext::ShapedTextCallback render_text = [](const SkSVGRenderContext& ctx,
605 const sk_sp<SkTextBlob>& blob,
606 const SkPaint* fill,
607 const SkPaint* stroke) {
608 if (fill) {
609 ctx.canvas()->drawTextBlob(blob, 0, 0, *fill);
610 }
611 if (stroke) {
612 ctx.canvas()->drawTextBlob(blob, 0, 0, *stroke);
613 }
614 };
615
616 // Root <text> nodes establish a text layout context.
617 SkSVGTextContext tctx(ctx, render_text);
618
619 this->onShapeText(ctx, &tctx, this->getXmlSpace());
620}
621
622SkRect SkSVGText::onObjectBoundingBox(const SkSVGRenderContext& ctx) const {
624
626 [&bounds](const SkSVGRenderContext& ctx, const sk_sp<SkTextBlob>& blob, const SkPaint*,
627 const SkPaint*) {
628 if (!blob) {
629 return;
630 }
631
632 AutoSTArray<64, SkRect> glyphBounds;
633
634 for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) {
635 glyphBounds.reset(SkToInt(it.glyphCount()));
636 it.font().getBounds(it.glyphs(), it.glyphCount(), glyphBounds.get(), nullptr);
637
639 SkMatrix m;
640 for (uint32_t i = 0; i < it.glyphCount(); ++i) {
641 m.setRSXform(it.xforms()[i]);
642 bounds.join(m.mapRect(glyphBounds[i]));
643 }
644 }
645 };
646
647 {
649 this->onShapeText(ctx, &tctx, this->getXmlSpace());
650 }
651
652 return bounds;
653}
654
655SkPath SkSVGText::onAsPath(const SkSVGRenderContext& ctx) const {
657
659 [&builder](const SkSVGRenderContext& ctx, const sk_sp<SkTextBlob>& blob, const SkPaint*,
660 const SkPaint*) {
661 if (!blob) {
662 return;
663 }
664
665 for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) {
666 struct GetPathsCtx {
668 const SkRSXform* xform;
669 } get_paths_ctx {builder, it.xforms()};
670
671 it.font().getPaths(it.glyphs(), it.glyphCount(), [](const SkPath* path,
672 const SkMatrix& matrix,
673 void* raw_ctx) {
674 auto* get_paths_ctx = static_cast<GetPathsCtx*>(raw_ctx);
675 const auto& glyph_rsx = *get_paths_ctx->xform++;
676
677 if (!path) {
678 return;
679 }
680
681 SkMatrix glyph_matrix;
682 glyph_matrix.setRSXform(glyph_rsx);
683 glyph_matrix.preConcat(matrix);
684
685 get_paths_ctx->builder.addPath(path->makeTransform(glyph_matrix));
686 }, &get_paths_ctx);
687 }
688 };
689
690 {
691 SkSVGTextContext tctx(ctx, as_path);
692 this->onShapeText(ctx, &tctx, this->getXmlSpace());
693 }
694
695 auto path = builder.detach();
696 this->mapToParent(&path);
697
698 return path;
699}
700
701void SkSVGTextPath::onShapeText(const SkSVGRenderContext& ctx, SkSVGTextContext* parent_tctx,
702 SkSVGXmlSpace xs) const {
703 SkASSERT(parent_tctx);
704
705 // textPath nodes establish a new text layout context.
706 SkSVGTextContext tctx(ctx, parent_tctx->getCallback(), this);
707
708 this->INHERITED::onShapeText(ctx, &tctx, xs);
709}
710
711bool SkSVGTextPath::parseAndSetAttribute(const char* name, const char* value) {
713 this->setHref(SkSVGAttributeParser::parse<SkSVGIRI>("xlink:href", name, value)) ||
714 this->setStartOffset(SkSVGAttributeParser::parse<SkSVGLength>("startOffset", name, value));
715}
SkPoint pos
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kNone
glyph outlines unchanged
SkSVGXmlSpace
Definition: SkSVGTypes.h:714
#define SkDegreesToRadians(degrees)
Definition: SkScalar.h:77
#define SK_ScalarMax
Definition: SkScalar.h:24
static void copy(void *dst, const uint8_t *src, int width, int bpp, int deltaSrc, int offset, const SkPMColor ctable[])
Definition: SkSwizzler.cpp:31
constexpr int SkToInt(S x)
Definition: SkTo.h:29
int32_t SkUnichar
Definition: SkTypes.h:175
uint16_t SkGlyphID
Definition: SkTypes.h:179
void drawTextBlob(const SkTextBlob *blob, SkScalar x, SkScalar y, const SkPaint &paint)
Definition: SkCanvas.cpp:2484
sk_sp< SkTypeface > legacyMakeTypeface(const char familyName[], SkFontStyle style) const
Definition: SkFontMgr.cpp:150
@ kSemiBold_Weight
Definition: SkFontStyle.h:25
@ kExtraBold_Weight
Definition: SkFontStyle.h:27
@ kExtraLight_Weight
Definition: SkFontStyle.h:21
Definition: SkFont.h:35
@ kAntiAlias
may have transparent pixels on glyph edges
static SkMatrix RotateRad(SkScalar rad)
Definition: SkMatrix.h:114
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.h:91
SkMatrix & setRSXform(const SkRSXform &rsxForm)
Definition: SkMatrix.cpp:420
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
SkMatrix & preConcat(const SkMatrix &other)
Definition: SkMatrix.cpp:674
Definition: SkPath.h:59
bool parse(SkSVGIntegerType *v)
const SkString & family() const
Definition: SkSVGTypes.h:496
const SkSVGLength & size() const
Definition: SkSVGTypes.h:545
SkScalar resolve(const SkSVGLength &, LengthType) const
virtual bool parseAndSetAttribute(const char *name, const char *value)
Definition: SkSVGNode.cpp:90
SkSVGTag tag() const
Definition: SkSVGNode.h:106
SkTLazy< SkPaint > fillPaint() const
BorrowedNode findNodeById(const SkSVGIRI &) const
sk_sp< SkFontMgr > fontMgr() const
const SkSVGPresentationContext & presentationContext() const
std::unique_ptr< SkShaper::ScriptRunIterator > makeScriptRunIterator(const char *utf8, size_t utf8Bytes) const
std::unique_ptr< SkShaper::BiDiRunIterator > makeBidiRunIterator(const char *utf8, size_t utf8Bytes, uint8_t bidiLevel) const
SkCanvas * canvas() const
const SkSVGLengthContext & lengthContext() const
SkTLazy< SkPaint > strokePaint() const
Type type() const
Definition: SkSVGTypes.h:628
void onShapeText(const SkSVGRenderContext &, SkSVGTextContext *, SkSVGXmlSpace) const override
Definition: SkSVGText.cpp:561
void appendChild(sk_sp< SkSVGNode >) final
Definition: SkSVGText.cpp:547
bool parseAndSetAttribute(const char *, const char *) override
Definition: SkSVGText.cpp:584
void setImplicitRotate(bool imp)
Definition: SkSVGTextPriv.h:65
bool has(Attr a) const
Definition: SkSVGTextPriv.h:56
ScopedPosResolver(const SkSVGTextContainer &, const SkSVGLengthContext &, SkSVGTextContext *, size_t)
Definition: SkSVGText.cpp:121
PosAttrs resolve(size_t charIndex) const
Definition: SkSVGText.cpp:146
const ShapedTextCallback & getCallback() const
void flushChunk(const SkSVGRenderContext &ctx)
Definition: SkSVGText.cpp:463
void shapeFragment(const SkString &, const SkSVGRenderContext &, SkSVGXmlSpace)
Definition: SkSVGText.cpp:314
std::function< void(const SkSVGRenderContext &, const sk_sp< SkTextBlob > &, const SkPaint *, const SkPaint *)> ShapedTextCallback
Definition: SkSVGTextPriv.h:39
SkSVGTextContext(const SkSVGRenderContext &, const ShapedTextCallback &, const SkSVGTextPath *=nullptr)
Definition: SkSVGText.cpp:274
~SkSVGTextContext() override
Definition: SkSVGText.cpp:310
void renderText(const SkSVGRenderContext &, SkSVGTextContext *, SkSVGXmlSpace) const
Definition: SkSVGText.cpp:531
virtual void onShapeText(const SkSVGRenderContext &, SkSVGTextContext *, SkSVGXmlSpace) const =0
bool onPrepareToRender(SkSVGRenderContext *) const override
void mapToParent(SkPath *) const
static std::unique_ptr< FontRunIterator > MakeFontMgrRunIterator(const char *utf8, size_t utf8Bytes, const SkFont &font, sk_sp< SkFontMgr > fallback)
Definition: SkShaper.cpp:187
static std::unique_ptr< LanguageRunIterator > MakeStdLanguageRunIterator(const char *utf8, size_t utf8Bytes)
Definition: SkShaper.cpp:204
bool isValid() const
Definition: SkTLazy.h:77
const RunBuffer & allocRunRSXform(const SkFont &font, int count)
Definition: SkTextBlob.cpp:560
sk_sp< SkTextBlob > make()
Definition: SkTextBlob.cpp:617
T * release()
Definition: SkRefCnt.h:324
void reset(int count)
Definition: SkTemplates.h:195
static void append(char **dst, size_t *count, const char *src, size_t n)
Definition: editor.cpp:211
struct MyStruct s
uint8_t value
static float max(float r, float g, float b)
Definition: hsl.cpp:49
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
Optional< SkRect > bounds
Definition: SkRecords.h:189
SKSHAPER_API std::unique_ptr< SkShaper > PrimitiveText()
SK_SPI size_t ToUTF8(SkUnichar uni, char utf8[kMaxBytesInUTF8Sequence]=nullptr)
constexpr unsigned kMaxBytesInUTF8Sequence
Definition: SkUTF.h:59
SK_SPI SkUnichar NextUTF8(const char **ptr, const char *end)
Definition: SkUTF.cpp:118
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
static SkRect compute_bounds(const SkPoint *points, int count)
Definition: dl_vertices.cc:88
font
Font Metadata and Metrics.
Definition: run.py:1
SkScalar w
SeparatedVector2 offset
float fX
x-axis value
Definition: SkPoint_impl.h:164
void offset(float dx, float dy)
Definition: SkPoint_impl.h:269
float fY
y-axis value
Definition: SkPoint_impl.h:165
static SkRSXform Make(SkScalar scos, SkScalar ssin, SkScalar tx, SkScalar ty)
Definition: SkRSXform.h:24
static SkRSXform MakeFromRadians(SkScalar scale, SkScalar radians, SkScalar tx, SkScalar ty, SkScalar ax, SkScalar ay)
Definition: SkRSXform.h:35
static constexpr SkRect MakeEmpty()
Definition: SkRect.h:595
SkSVGProperty< SkSVGFontSize, true > fFontSize
SkSVGProperty< SkSVGFontFamily, true > fFontFamily
SkSVGProperty< SkSVGFontWeight, true > fFontWeight
SkSVGProperty< SkSVGTextAnchor, true > fTextAnchor
SkSVGProperty< SkSVGFontStyle, true > fFontStyle
SkSVGPresentationAttributes fInherited