[svg] Refactor text rendering context plumbing
Instead of relying on RenderContext to pass text rendering options
downstack, introduce a dedicated virtual (onRenderText) and pass options
explicitly.
Root text nodes bridge from onRender() -> onRenderText().
This removes some complexity from RenderContext and incidentally fixes
xml:space = preserve (the value was being dropped during local ctx
copying).
Bug: skia:10840
Change-Id: Ic5fd9e0f9382f52f65108521574fcb2a422b97aa
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/344559
Reviewed-by: Tyler Denniston <tdenniston@google.com>
Commit-Queue: Florin Malita <fmalita@google.com>
diff --git a/modules/svg/src/SkSVGText.cpp b/modules/svg/src/SkSVGText.cpp
index a6ba964..4e2d91b 100644
--- a/modules/svg/src/SkSVGText.cpp
+++ b/modules/svg/src/SkSVGText.cpp
@@ -118,7 +118,7 @@
{}
// Queues codepoints for rendering.
- void appendFragment(const SkString& txt, const SkSVGRenderContext& ctx) {
+ void appendFragment(const SkString& txt, const SkSVGRenderContext& ctx, SkSVGXmlSpace xs) {
// https://www.w3.org/TR/SVG11/text.html#WhiteSpace
// https://www.w3.org/TR/2008/REC-xml-20081126/#NT-S
auto filterWSDefault = [this](SkUnichar ch) -> SkUnichar {
@@ -151,8 +151,6 @@
return ch;
};
- const auto xmlSpace = ctx.getXmlSpace();
-
SkSTArray<128, char, true> filtered;
filtered.reserve_back(SkToInt(txt.size()));
@@ -161,7 +159,7 @@
while (ch_ptr < ch_end) {
auto ch = SkUTF::NextUTF8(&ch_ptr, ch_end);
- ch = (xmlSpace == SkSVGXmlSpace::kDefault)
+ ch = (xs == SkSVGXmlSpace::kDefault)
? filterWSDefault(ch)
: filterWSPreserve(ch);
@@ -275,22 +273,40 @@
bool fPrevCharSpace = true; // WS filter state
};
+void SkSVGTextFragment::renderText(const SkSVGRenderContext& ctx, SkSVGTextContext* tctx,
+ SkSVGXmlSpace xs) const {
+ SkSVGRenderContext localContext(ctx, this);
+
+ if (this->onPrepareToRender(&localContext)) {
+ this->onRenderText(localContext, tctx, xs);
+ }
+}
+
+SkPath SkSVGTextFragment::onAsPath(const SkSVGRenderContext&) const {
+ // TODO
+ return SkPath();
+}
+
void SkSVGTextContainer::appendChild(sk_sp<SkSVGNode> child) {
// Only allow text nodes.
switch (child->tag()) {
case SkSVGTag::kText:
case SkSVGTag::kTextLiteral:
case SkSVGTag::kTSpan:
- this->INHERITED::appendChild(child);
+ fChildren.push_back(
+ sk_sp<SkSVGTextFragment>(static_cast<SkSVGTextFragment*>(child.release())));
break;
default:
break;
}
}
-bool SkSVGTextContainer::onPrepareToRender(SkSVGRenderContext* ctx) const {
- ctx->setXmlSpace(this->getXmlSpace());
- return this->INHERITED::onPrepareToRender(ctx);
+void SkSVGTextContainer::onRenderText(const SkSVGRenderContext& ctx, SkSVGTextContext* tctx,
+ SkSVGXmlSpace) const {
+ for (const auto& frag : fChildren) {
+ // Containers always override xml:space with the local value.
+ frag->renderText(ctx, tctx, this->getXmlSpace());
+ }
}
// https://www.w3.org/TR/SVG11/text.html#WhiteSpace
@@ -311,26 +327,18 @@
this->setXmlSpace(SkSVGAttributeParser::parse<SkSVGXmlSpace>("xml:space", name, value));
}
-void SkSVGText::onRender(const SkSVGRenderContext& ctx) const {
- // <text> establishes a new text layout context.
+void SkSVGTextContainer::onRender(const SkSVGRenderContext& ctx) const {
+ // Root text nodes establish a new text layout context.
SkSVGTextContext tctx(*this, ctx);
- SkSVGRenderContext local_ctx(ctx, tctx);
- this->INHERITED::onRender(local_ctx);
+ this->onRenderText(ctx, &tctx, this->getXmlSpace());
tctx.flushChunk(ctx);
}
-void SkSVGTextLiteral::onRender(const SkSVGRenderContext& ctx) const {
- auto* tctx = ctx.textContext();
- if (!tctx) {
- return;
- }
+void SkSVGTextLiteral::onRenderText(const SkSVGRenderContext& ctx, SkSVGTextContext* tctx,
+ SkSVGXmlSpace xs) const {
+ SkASSERT(tctx);
- tctx->appendFragment(this->getText(), ctx);
-}
-
-SkPath SkSVGTextLiteral::onAsPath(const SkSVGRenderContext&) const {
- // TODO
- return SkPath();
+ tctx->appendFragment(this->getText(), ctx, xs);
}