[svg] Allow clients to pass a custom SkFontMgr

Introduce a Builder helper and plumb the client-provided SkFontMgr for
font resolution.

Also clean up some of the legacy SkSVGDom factories.

Bug: skia:10840
Change-Id: I6e1eabe7c257cb75dfdb5bf67054f93f25769027
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/333577
Commit-Queue: Florin Malita <fmalita@google.com>
Reviewed-by: Tyler Denniston <tdenniston@google.com>
This commit is contained in:
Florin Malita 2020-11-10 15:24:59 -05:00 committed by Skia Commit-Bot
parent 24c18526a5
commit 7006e15df5
10 changed files with 77 additions and 75 deletions

View File

@ -8,6 +8,7 @@
#ifndef SkSVGDOM_DEFINED
#define SkSVGDOM_DEFINED
#include "include/core/SkFontMgr.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSize.h"
#include "include/private/SkTemplates.h"
@ -17,32 +18,43 @@ class SkCanvas;
class SkDOM;
class SkStream;
class SkSVGNode;
class SkSVGSVG;
class SkSVGDOM : public SkRefCnt {
public:
SkSVGDOM();
class Builder final {
public:
/**
* Specify a font manager for loading SVG fonts.
*/
Builder& setFontManager(sk_sp<SkFontMgr>);
static sk_sp<SkSVGDOM> MakeFromDOM(const SkDOM&);
static sk_sp<SkSVGDOM> MakeFromStream(SkStream&);
sk_sp<SkSVGDOM> make(SkStream&) const;
private:
sk_sp<SkFontMgr> fFontMgr;
};
static sk_sp<SkSVGDOM> MakeFromStream(SkStream& str) {
return Builder().make(str);
}
const SkSize& containerSize() const;
void setContainerSize(const SkSize&);
void setRoot(sk_sp<SkSVGNode>);
// Returns the node with the given id, or nullptr if not found.
sk_sp<SkSVGNode>* findNodeById(const char* id);
void render(SkCanvas*) const;
private:
SkSize intrinsicSize() const;
SkSVGDOM(sk_sp<SkSVGSVG>, sk_sp<SkFontMgr>, SkSVGIDMapper&&);
SkSize fContainerSize;
sk_sp<SkSVGNode> fRoot;
SkSVGIDMapper fIDMapper;
const sk_sp<SkSVGSVG> fRoot;
const sk_sp<SkFontMgr> fFontMgr;
const SkSVGIDMapper fIDMapper;
using INHERITED = SkRefCnt;
SkSize fContainerSize;
};
#endif // SkSVGDOM_DEFINED

View File

@ -8,6 +8,7 @@
#ifndef SkSVGRenderContext_DEFINED
#define SkSVGRenderContext_DEFINED
#include "include/core/SkFontMgr.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkRect.h"
@ -58,8 +59,9 @@ struct SkSVGPresentationContext {
class SkSVGRenderContext {
public:
SkSVGRenderContext(SkCanvas*, const SkSVGIDMapper&, const SkSVGLengthContext&,
const SkSVGPresentationContext&, const SkSVGNode*);
SkSVGRenderContext(SkCanvas*, const sk_sp<SkFontMgr>&, const SkSVGIDMapper&,
const SkSVGLengthContext&, const SkSVGPresentationContext&,
const SkSVGNode*);
SkSVGRenderContext(const SkSVGRenderContext&);
SkSVGRenderContext(const SkSVGRenderContext&, SkCanvas*);
SkSVGRenderContext(const SkSVGRenderContext&, const SkSVGNode*);
@ -123,6 +125,10 @@ public:
// The node being rendered (may be null).
const SkSVGNode* node() const { return fNode; }
sk_sp<SkFontMgr> fontMgr() const {
return fFontMgr ? fFontMgr : SkFontMgr::RefDefault();
}
private:
// Stack-only
void* operator new(size_t) = delete;
@ -134,6 +140,7 @@ private:
void applyClip(const SkSVGClip&);
void updatePaintsWithCurrentColor(const SkSVGPresentationAttributes&);
const sk_sp<SkFontMgr>& fFontMgr;
const SkSVGIDMapper& fIDMapper;
SkTCopyOnFirstWrite<SkSVGLengthContext> fLengthContext;
SkTCopyOnFirstWrite<SkSVGPresentationContext> fPresentationContext;

View File

@ -6,6 +6,7 @@
*/
#include "include/core/SkCanvas.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkString.h"
#include "include/private/SkTo.h"
#include "include/utils/SkParsePath.h"
@ -427,49 +428,44 @@ sk_sp<SkSVGNode> construct_svg_node(const SkDOM& dom, const ConstructionContext&
} // anonymous namespace
SkSVGDOM::SkSVGDOM()
: fContainerSize(SkSize::Make(0, 0)) {
SkSVGDOM::Builder& SkSVGDOM::Builder::setFontManager(sk_sp<SkFontMgr> fmgr) {
fFontMgr = std::move(fmgr);
return *this;
}
sk_sp<SkSVGDOM> SkSVGDOM::MakeFromDOM(const SkDOM& xmlDom) {
sk_sp<SkSVGDOM> dom = sk_make_sp<SkSVGDOM>();
ConstructionContext ctx(&dom->fIDMapper);
dom->fRoot = construct_svg_node(xmlDom, ctx, xmlDom.getRootNode());
// Reset the default container size to match the intrinsic SVG size.
dom->setContainerSize(dom->intrinsicSize());
return dom;
}
sk_sp<SkSVGDOM> SkSVGDOM::MakeFromStream(SkStream& svgStream) {
sk_sp<SkSVGDOM> SkSVGDOM::Builder::make(SkStream& str) const {
SkDOM xmlDom;
if (!xmlDom.build(svgStream)) {
if (!xmlDom.build(str)) {
return nullptr;
}
return MakeFromDOM(xmlDom);
SkSVGIDMapper mapper;
ConstructionContext ctx(&mapper);
auto root = construct_svg_node(xmlDom, ctx, xmlDom.getRootNode());
if (!root || root->tag() != SkSVGTag::kSvg) {
return nullptr;
}
return sk_sp<SkSVGDOM>(new SkSVGDOM(sk_sp<SkSVGSVG>(static_cast<SkSVGSVG*>(root.release())),
std::move(fFontMgr), std::move(mapper)));
}
SkSVGDOM::SkSVGDOM(sk_sp<SkSVGSVG> root, sk_sp<SkFontMgr> fmgr, SkSVGIDMapper&& mapper)
: fRoot(std::move(root))
, fFontMgr(std::move(fmgr))
, fIDMapper(std::move(mapper))
, fContainerSize(fRoot->intrinsicSize(SkSVGLengthContext(SkSize::Make(0, 0))))
{}
void SkSVGDOM::render(SkCanvas* canvas) const {
if (fRoot) {
SkSVGLengthContext lctx(fContainerSize);
SkSVGPresentationContext pctx;
fRoot->render(SkSVGRenderContext(canvas, fIDMapper, lctx, pctx, nullptr));
fRoot->render(SkSVGRenderContext(canvas, fFontMgr, fIDMapper, lctx, pctx, nullptr));
}
}
SkSize SkSVGDOM::intrinsicSize() const {
if (!fRoot || fRoot->tag() != SkSVGTag::kSvg) {
return SkSize::Make(0, 0);
}
// Intrinsic sizes are never relative, so the viewport size is irrelevant.
const SkSVGLengthContext lctx(SkSize::Make(0, 0));
return static_cast<const SkSVGSVG*>(fRoot.get())->intrinsicSize(lctx);
}
const SkSize& SkSVGDOM::containerSize() const {
return fContainerSize;
}
@ -484,10 +480,6 @@ sk_sp<SkSVGNode>* SkSVGDOM::findNodeById(const char* id) {
return this->fIDMapper.find(idStr);
}
void SkSVGDOM::setRoot(sk_sp<SkSVGNode> root) {
fRoot = std::move(root);
}
// TODO(fuego): move this to SkSVGNode or its own CU.
bool SkSVGNode::setAttribute(const char* attributeName, const char* attributeValue) {
return set_string_attribute(sk_ref_sp(this), attributeName, attributeValue);

View File

@ -325,8 +325,9 @@ SkSVGPresentationContext::SkSVGPresentationContext()
// Commit initial values to the paint cache.
SkCanvas fakeCanvas(0, 0);
SkSVGRenderContext fake(&fakeCanvas, SkSVGIDMapper(), SkSVGLengthContext(SkSize::Make(0, 0)),
*this, nullptr);
SkSVGRenderContext fake(&fakeCanvas, nullptr, SkSVGIDMapper(),
SkSVGLengthContext(SkSize::Make(0, 0)),
*this, nullptr);
commitToPaint<SkSVGAttribute::kFill>(fInherited, fake, this);
commitToPaint<SkSVGAttribute::kFillOpacity>(fInherited, fake, this);
@ -339,11 +340,13 @@ SkSVGPresentationContext::SkSVGPresentationContext()
}
SkSVGRenderContext::SkSVGRenderContext(SkCanvas* canvas,
const sk_sp<SkFontMgr>& fmgr,
const SkSVGIDMapper& mapper,
const SkSVGLengthContext& lctx,
const SkSVGPresentationContext& pctx,
const SkSVGNode* node)
: fIDMapper(mapper)
: fFontMgr(fmgr)
, fIDMapper(mapper)
, fLengthContext(lctx)
, fPresentationContext(pctx)
, fCanvas(canvas)
@ -352,6 +355,7 @@ SkSVGRenderContext::SkSVGRenderContext(SkCanvas* canvas,
SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other)
: SkSVGRenderContext(other.fCanvas,
other.fFontMgr,
other.fIDMapper,
*other.fLengthContext,
*other.fPresentationContext,
@ -359,6 +363,7 @@ SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other)
SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other, SkCanvas* canvas)
: SkSVGRenderContext(canvas,
other.fFontMgr,
other.fIDMapper,
*other.fLengthContext,
*other.fPresentationContext,
@ -366,6 +371,7 @@ SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other, SkCanvas
SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other, const SkSVGNode* node)
: SkSVGRenderContext(other.fCanvas,
other.fFontMgr,
other.fIDMapper,
*other.fLengthContext,
*other.fPresentationContext,

View File

@ -62,8 +62,11 @@ SkFont SkSVGText::resolveFont(const SkSVGRenderContext& ctx) const {
ctx.lengthContext().resolve(ctx.presentationContext().fInherited.fFontSize->size(),
SkSVGLengthContext::LengthType::kVertical);
// TODO: allow clients to pass an external fontmgr.
SkFont font(SkTypeface::MakeFromName(family.c_str(), style), size);
// TODO: we likely want matchFamilyStyle here, but switching away from legacyMakeTypeface
// changes all the results when using the default fontmgr.
auto tf = ctx.fontMgr()->legacyMakeTypeface(family.c_str(), style);
SkFont font(std::move(tf), size);
font.setHinting(SkFontHinting::kNone);
font.setSubpixel(true);
font.setLinearMetrics(true);

View File

@ -39,7 +39,10 @@ int main(int argc, char** argv) {
return 1;
}
auto svg_dom = SkSVGDOM::MakeFromStream(in);
auto svg_dom = SkSVGDOM::Builder()
.setFontManager(SkFontMgr::RefDefault())
.make(in);
if (!svg_dom) {
std::cerr << "Could not parse " << FLAGS_input[0] << "\n";
return 1;

View File

@ -47,13 +47,7 @@ private:
}
SkMemoryStream svgStream(std::move(data));
SkDOM xmlDom;
if (!xmlDom.build(svgStream)) {
SkDebugf("XML parsing failed: \"%s\"\n", fResource);
return;
}
fDom = SkSVGDOM::MakeFromDOM(xmlDom);
fDom = SkSVGDOM::MakeFromStream(svgStream);
if (fDom) {
fDom->setContainerSize(SkSize::Make(this->width(), this->height()));
}

View File

@ -33,13 +33,7 @@ protected:
return;
}
SkDOM xmlDom;
if (!xmlDom.build(svgStream)) {
SkDebugf("XML parsing failed: \"path\"\n", fPath.c_str());
return;
}
fDom = SkSVGDOM::MakeFromDOM(xmlDom);
fDom = SkSVGDOM::MakeFromStream(svgStream);
if (fDom) {
fDom->setContainerSize(SkSize::Make(this->width(), this->height()));
}

View File

@ -694,11 +694,7 @@ static sk_sp<SkPicture> create_warmup_skp() {
static sk_sp<SkPicture> create_skp_from_svg(SkStream* stream, const char* filename) {
#ifdef SK_XML
SkDOM xml;
if (!xml.build(*stream)) {
exitf(ExitErr::kData, "failed to parse xml in file %s", filename);
}
sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromDOM(xml);
sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromStream(*stream);
if (!svg) {
exitf(ExitErr::kData, "failed to build svg dom from file %s", filename);
}

View File

@ -28,12 +28,7 @@ sk_sp<BisectSlide> BisectSlide::Create(const char filepath[]) {
sk_sp<BisectSlide> bisect(new BisectSlide(filepath));
if (bisect->fFilePath.endsWith(".svg")) {
#ifdef SK_XML
SkDOM xml;
if (!xml.build(stream)) {
SkDebugf("BISECT: XML parsing failed: \"%s\"\n", filepath);
return nullptr;
}
sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromDOM(xml);
sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromStream(stream);
if (!svg) {
SkDebugf("BISECT: couldn't load svg at \"%s\"\n", filepath);
return nullptr;