[svg] xml:space support
Add xml:space attribute and implement related white space filters. (https://www.w3.org/TR/SVG11/text.html#WhiteSpace) Bug: skia:10840 Change-Id: I52fda50fae1cd7cf8b0dd7c1a2ee2e667ffa947b Reviewed-on: https://skia-review.googlesource.com/c/skia/+/342299 Reviewed-by: Tyler Denniston <tdenniston@google.com> Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: Florin Malita <fmalita@google.com>
This commit is contained in:
parent
a4bd478bc2
commit
9c1f1be078
@ -132,6 +132,9 @@ public:
|
||||
return fFontMgr ? fFontMgr : SkFontMgr::RefDefault();
|
||||
}
|
||||
|
||||
SkSVGXmlSpace getXmlSpace() const { return fXmlSpace; }
|
||||
void setXmlSpace(SkSVGXmlSpace xs) { fXmlSpace = xs; }
|
||||
|
||||
private:
|
||||
// Stack-only
|
||||
void* operator new(size_t) = delete;
|
||||
@ -156,6 +159,8 @@ private:
|
||||
// clipPath, if present for the current context (not inherited).
|
||||
SkTLazy<SkPath> fClipPath;
|
||||
|
||||
SkSVGXmlSpace fXmlSpace = SkSVGXmlSpace::kDefault;
|
||||
|
||||
const SkSVGNode* fNode;
|
||||
};
|
||||
|
||||
|
@ -18,11 +18,14 @@ public:
|
||||
SVG_ATTR(X, SkSVGLength, SkSVGLength(0))
|
||||
SVG_ATTR(Y, SkSVGLength, SkSVGLength(0))
|
||||
|
||||
SVG_ATTR(XmlSpace, SkSVGXmlSpace, SkSVGXmlSpace::kDefault)
|
||||
|
||||
protected:
|
||||
explicit SkSVGTextContainer(SkSVGTag t) : INHERITED(t) {}
|
||||
|
||||
private:
|
||||
void appendChild(sk_sp<SkSVGNode>) final;
|
||||
bool onPrepareToRender(SkSVGRenderContext*) const final;
|
||||
|
||||
bool parseAndSetAttribute(const char*, const char*) override;
|
||||
|
||||
|
@ -652,4 +652,9 @@ struct SkSVGFeTurbulenceType {
|
||||
explicit SkSVGFeTurbulenceType(Type type) : fType(type) {}
|
||||
};
|
||||
|
||||
enum class SkSVGXmlSpace {
|
||||
kDefault,
|
||||
kPreserve,
|
||||
};
|
||||
|
||||
#endif // SkSVGTypes_DEFINED
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "modules/skshaper/include/SkShaper.h"
|
||||
#include "modules/svg/include/SkSVGRenderContext.h"
|
||||
#include "modules/svg/include/SkSVGValue.h"
|
||||
#include "src/utils/SkUTF.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -118,7 +119,63 @@ public:
|
||||
|
||||
// Queues codepoints for rendering.
|
||||
void appendFragment(const SkString& txt, const SkSVGRenderContext& ctx) {
|
||||
// TODO: xml::space filtering
|
||||
// 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 {
|
||||
// Remove all newline chars.
|
||||
if (ch == '\n') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Convert tab chars to space.
|
||||
if (ch == '\t') {
|
||||
ch = ' ';
|
||||
}
|
||||
|
||||
// Consolidate contiguous space chars and strip leading spaces (fPrevCharSpace
|
||||
// starts off as true).
|
||||
if (fPrevCharSpace && ch == ' ') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// TODO: Strip trailing WS? Doing this across chunks would require another buffering
|
||||
// layer. In general, trailing WS should have no rendering side effects. Skipping
|
||||
// for now.
|
||||
return ch;
|
||||
};
|
||||
auto filterWSPreserve = [](SkUnichar ch) -> SkUnichar {
|
||||
// Convert newline and tab chars to space.
|
||||
if (ch == '\n' || ch == '\t') {
|
||||
ch = ' ';
|
||||
}
|
||||
return ch;
|
||||
};
|
||||
|
||||
const auto xmlSpace = ctx.getXmlSpace();
|
||||
|
||||
SkSTArray<128, char, true> filtered;
|
||||
filtered.reserve_back(SkToInt(txt.size()));
|
||||
|
||||
const char* ch_ptr = txt.c_str();
|
||||
const char* ch_end = ch_ptr + txt.size();
|
||||
|
||||
while (ch_ptr < ch_end) {
|
||||
auto ch = SkUTF::NextUTF8(&ch_ptr, ch_end);
|
||||
ch = (xmlSpace == SkSVGXmlSpace::kDefault)
|
||||
? filterWSDefault(ch)
|
||||
: filterWSPreserve(ch);
|
||||
|
||||
if (ch < 0) {
|
||||
// invalid utf or char filtered out
|
||||
continue;
|
||||
}
|
||||
|
||||
char utf8_buf[SkUTF::kMaxBytesInUTF8Sequence];
|
||||
filtered.push_back_n(SkToInt(SkUTF::ToUTF8(ch, utf8_buf)), utf8_buf);
|
||||
|
||||
fPrevCharSpace = (ch == ' ');
|
||||
}
|
||||
|
||||
// TODO: absolute positioned chars => chunk breaks
|
||||
|
||||
// Stash paints for access from SkShaper callbacks.
|
||||
@ -129,7 +186,7 @@ public:
|
||||
const auto LTR = true;
|
||||
|
||||
// Initiate shaping: this will generate a series of runs via callbacks.
|
||||
fShaper->shape(txt.c_str(), txt.size(), ResolveFont(ctx), LTR, SK_ScalarMax, this);
|
||||
fShaper->shape(filtered.data(), filtered.size(), ResolveFont(ctx), LTR, SK_ScalarMax, this);
|
||||
}
|
||||
|
||||
// Perform actual rendering for queued codepoints.
|
||||
@ -214,6 +271,8 @@ private:
|
||||
// cached for access from SkShaper callbacks.
|
||||
const SkPaint* fCurrentFill;
|
||||
const SkPaint* fCurrentStroke;
|
||||
|
||||
bool fPrevCharSpace = true; // WS filter state
|
||||
};
|
||||
|
||||
void SkSVGTextContainer::appendChild(sk_sp<SkSVGNode> child) {
|
||||
@ -229,10 +288,27 @@ void SkSVGTextContainer::appendChild(sk_sp<SkSVGNode> child) {
|
||||
}
|
||||
}
|
||||
|
||||
bool SkSVGTextContainer::onPrepareToRender(SkSVGRenderContext* ctx) const {
|
||||
ctx->setXmlSpace(this->getXmlSpace());
|
||||
return this->INHERITED::onPrepareToRender(ctx);
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/SVG11/text.html#WhiteSpace
|
||||
template <>
|
||||
bool SkSVGAttributeParser::parse(SkSVGXmlSpace* xs) {
|
||||
static constexpr std::tuple<const char*, SkSVGXmlSpace> gXmlSpaceMap[] = {
|
||||
{"default" , SkSVGXmlSpace::kDefault },
|
||||
{"preserve", SkSVGXmlSpace::kPreserve},
|
||||
};
|
||||
|
||||
return this->parseEnumMap(gXmlSpaceMap, xs) && this->parseEOSToken();
|
||||
}
|
||||
|
||||
bool SkSVGTextContainer::parseAndSetAttribute(const char* name, const char* value) {
|
||||
return INHERITED::parseAndSetAttribute(name, value) ||
|
||||
this->setX(SkSVGAttributeParser::parse<SkSVGLength>("x", name, value)) ||
|
||||
this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", name, value));
|
||||
this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", name, value)) ||
|
||||
this->setXmlSpace(SkSVGAttributeParser::parse<SkSVGXmlSpace>("xml:space", name, value));
|
||||
}
|
||||
|
||||
void SkSVGText::onRender(const SkSVGRenderContext& ctx) const {
|
||||
|
Loading…
Reference in New Issue
Block a user