[svg] Parse text attributes

Convert font-family, font-size, font-style and font-weight to
presentation attributes, and add parsing utils.

Bug: skia:10840
Change-Id: I1acdb59bc95fe46e67ed0f499dd0732420016663
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/328436
Commit-Queue: Florin Malita <fmalita@google.com>
Reviewed-by: Tyler Denniston <tdenniston@google.com>
diff --git a/modules/svg/include/SkSVGAttribute.h b/modules/svg/include/SkSVGAttribute.h
index 3c129d0..cd64e91 100644
--- a/modules/svg/include/SkSVGAttribute.h
+++ b/modules/svg/include/SkSVGAttribute.h
@@ -89,6 +89,11 @@
 
     SkTLazy<SkSVGColorType>  fColor;
 
+    SkTLazy<SkSVGFontFamily> fFontFamily;
+    SkTLazy<SkSVGFontStyle>  fFontStyle;
+    SkTLazy<SkSVGFontSize>   fFontSize;
+    SkTLazy<SkSVGFontWeight> fFontWeight;
+
     // TODO(tdenniston): add SkSVGStopColor
 
     // uninherited
diff --git a/modules/svg/include/SkSVGAttributeParser.h b/modules/svg/include/SkSVGAttributeParser.h
index 81ae1d3..1ac9aac 100644
--- a/modules/svg/include/SkSVGAttributeParser.h
+++ b/modules/svg/include/SkSVGAttributeParser.h
@@ -32,6 +32,11 @@
     bool parseVisibility(SkSVGVisibility*);
     bool parseDashArray(SkSVGDashArray*);
 
+    bool parseFontFamily(SkSVGFontFamily*);
+    bool parseFontSize(SkSVGFontSize*);
+    bool parseFontStyle(SkSVGFontStyle*);
+    bool parseFontWeight(SkSVGFontWeight*);
+
 private:
     // Stack-only
     void* operator new(size_t) = delete;
@@ -67,6 +72,9 @@
     template <typename Func, typename T>
     bool parseParenthesized(const char* prefix, Func, T* result);
 
+    template <typename T, typename TArray>
+    bool parseEnumMap(const TArray& arr, T* result);
+
     // The current position in the input string.
     const char* fCurPos;
 
diff --git a/modules/svg/include/SkSVGNode.h b/modules/svg/include/SkSVGNode.h
index 2e92c6b..8f493d2 100644
--- a/modules/svg/include/SkSVGNode.h
+++ b/modules/svg/include/SkSVGNode.h
@@ -38,6 +38,29 @@
     kUse
 };
 
+#define SVG_PRES_ATTR(attr_name, attr_type, attr_inherited)             \
+    const attr_type* get##attr_name() const {                           \
+        return fPresentationAttributes.f##attr_name.getMaybeNull();     \
+    }                                                                   \
+    void set##attr_name(const attr_type& v) {                           \
+        if (!attr_inherited || v.type() != attr_type::Type::kInherit) { \
+            fPresentationAttributes.f##attr_name.set(v);                \
+        } else {                                                        \
+            /* kInherited values are semantically equivalent to         \
+               the absence of a local presentation attribute.*/         \
+            fPresentationAttributes.f##attr_name.reset();               \
+        }                                                               \
+    }                                                                   \
+    void set##attr_name(attr_type&& v) {                                \
+        if (!attr_inherited || v.type() != attr_type::Type::kInherit) { \
+            fPresentationAttributes.f##attr_name.set(std::move(v));     \
+        } else {                                                        \
+            /* kInherited values are semantically equivalent to         \
+               the absence of a local presentation attribute.*/         \
+            fPresentationAttributes.f##attr_name.reset();               \
+        }                                                               \
+    }
+
 class SkSVGNode : public SkRefCnt {
 public:
     ~SkSVGNode() override;
@@ -70,6 +93,11 @@
     void setStrokeWidth(const SkSVGLength&);
     void setVisibility(const SkSVGVisibility&);
 
+    SVG_PRES_ATTR(FontFamily, SkSVGFontFamily, true)
+    SVG_PRES_ATTR(FontStyle , SkSVGFontStyle , true)
+    SVG_PRES_ATTR(FontSize  , SkSVGFontSize  , true)
+    SVG_PRES_ATTR(FontWeight, SkSVGFontWeight, true)
+
 protected:
     SkSVGNode(SkSVGTag);
 
diff --git a/modules/svg/include/SkSVGText.h b/modules/svg/include/SkSVGText.h
index 9336f73..55716cd 100644
--- a/modules/svg/include/SkSVGText.h
+++ b/modules/svg/include/SkSVGText.h
@@ -10,12 +10,12 @@
 
 #include "include/core/SkFont.h"
 #include "include/utils/SkTextUtils.h"
-#include "modules/svg/include/SkSVGShape.h"
+#include "modules/svg/include/SkSVGTransformableNode.h"
 #include "modules/svg/include/SkSVGTypes.h"
 
 class SkRRect;
 
-class SkSVGText final : public SkSVGShape {
+class SkSVGText final : public SkSVGTransformableNode {
  public:
   ~SkSVGText() override = default;
   static sk_sp<SkSVGText> Make() {
@@ -23,18 +23,14 @@
 
   void setX(const SkSVGLength&);
   void setY(const SkSVGLength&);
-  void setFontFamily(const SkSVGStringType&);
-  void setFontSize(const SkSVGLength&);
-  void setFontStyle(const SkSVGStringType&);
-  void setFontWeight(const SkSVGStringType&);
   void setText(const SkSVGStringType&);
   void setTextAnchor(const SkSVGStringType&);
 
  protected:
   void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
 
-  void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&,
-              SkPathFillType) const override;
+  void onRender(const SkSVGRenderContext&) const override;
+  void appendChild(sk_sp<SkSVGNode>) override;
 
   SkPath onAsPath(const SkSVGRenderContext&) const override;
 
@@ -42,16 +38,16 @@
 
  private:
   SkSVGText();
-  SkSVGLength fX = SkSVGLength(0);
-  SkSVGLength fY = SkSVGLength(0);
-  SkSVGStringType fText;
-  sk_sp<SkTypeface> fTypeface;
-  SkSVGLength fFontSize;
-  SkSVGStringType fFontFamily;
-  SkSVGStringType fFontStyle;
-  SkSVGStringType fFontWeight;
+
+  SkFont resolveFont(const SkSVGRenderContext&) const;
+
+  SkSVGLength        fX = SkSVGLength(0);
+  SkSVGLength        fY = SkSVGLength(0);
+  SkSVGStringType    fText;
+  sk_sp<SkTypeface>  fTypeface;
   SkTextUtils::Align fTextAlign = SkTextUtils::Align::kLeft_Align;
-  using INHERITED = SkSVGShape;
+
+  using INHERITED = SkSVGTransformableNode;
 };
 
 #endif  // SkSVGText_DEFINED
diff --git a/modules/svg/include/SkSVGTypes.h b/modules/svg/include/SkSVGTypes.h
index d4563d1..e0ea73e 100644
--- a/modules/svg/include/SkSVGTypes.h
+++ b/modules/svg/include/SkSVGTypes.h
@@ -308,4 +308,112 @@
     SkSVGColorType fColor;
 };
 
+class SkSVGFontFamily {
+public:
+    enum class Type {
+        kFamily,
+        kInherit,
+    };
+
+    SkSVGFontFamily() : fType(Type::kInherit) {}
+    explicit SkSVGFontFamily(const char family[])
+        : fType(Type::kFamily)
+        , fFamily(family) {}
+
+    bool operator==(const SkSVGFontFamily& other) const {
+        return fType == other.fType && fFamily == other.fFamily;
+    }
+    bool operator!=(const SkSVGFontFamily& other) const { return !(*this == other); }
+
+    Type type() const { return fType; }
+
+    const SkString& family() const { return fFamily; }
+
+private:
+    Type     fType;
+    SkString fFamily;
+};
+
+class SkSVGFontStyle {
+public:
+    enum class Type {
+        kNormal,
+        kItalic,
+        kOblique,
+        kInherit,
+    };
+
+    SkSVGFontStyle() : fType(Type::kInherit) {}
+    explicit SkSVGFontStyle(Type t) : fType(t) {}
+
+    bool operator==(const SkSVGFontStyle& other) const {
+        return fType == other.fType;
+    }
+    bool operator!=(const SkSVGFontStyle& other) const { return !(*this == other); }
+
+    Type type() const { return fType; }
+
+private:
+    Type fType;
+};
+
+class SkSVGFontSize {
+public:
+    enum class Type {
+        kLength,
+        kInherit,
+    };
+
+    SkSVGFontSize() : fType(Type::kInherit), fSize(0) {}
+    explicit SkSVGFontSize(const SkSVGLength& s)
+        : fType(Type::kLength)
+        , fSize(s) {}
+
+    bool operator==(const SkSVGFontSize& other) const {
+        return fType == other.fType && fSize == other.fSize;
+    }
+    bool operator!=(const SkSVGFontSize& other) const { return !(*this == other); }
+
+    Type type() const { return fType; }
+
+    const SkSVGLength& size() const { return fSize; }
+
+private:
+    Type        fType;
+    SkSVGLength fSize;
+};
+
+class SkSVGFontWeight {
+public:
+    enum class Type {
+        k100,
+        k200,
+        k300,
+        k400,
+        k500,
+        k600,
+        k700,
+        k800,
+        k900,
+        kNormal,
+        kBold,
+        kBolder,
+        kLighter,
+        kInherit,
+    };
+
+    SkSVGFontWeight() : fType(Type::kInherit) {}
+    explicit SkSVGFontWeight(Type t) : fType(t) {}
+
+    bool operator==(const SkSVGFontWeight& other) const {
+        return fType == other.fType;
+    }
+    bool operator!=(const SkSVGFontWeight& other) const { return !(*this == other); }
+
+    Type type() const { return fType; }
+
+private:
+    Type fType;
+};
+
 #endif // SkSVGTypes_DEFINED
diff --git a/modules/svg/include/SkSVGValue.h b/modules/svg/include/SkSVGValue.h
index 7b74cd0..0144a70 100644
--- a/modules/svg/include/SkSVGValue.h
+++ b/modules/svg/include/SkSVGValue.h
@@ -22,6 +22,10 @@
         kColor,
         kDashArray,
         kFillRule,
+        kFontFamily,
+        kFontSize,
+        kFontStyle,
+        kFontWeight,
         kLength,
         kLineCap,
         kLineJoin,
@@ -94,4 +98,9 @@
 using SkSVGVisibilityValue   = SkSVGWrapperValue<SkSVGVisibility   , SkSVGValue::Type::kVisibility>;
 using SkSVGDashArrayValue    = SkSVGWrapperValue<SkSVGDashArray    , SkSVGValue::Type::kDashArray >;
 
+using SkSVGFontFamilyValue   = SkSVGWrapperValue<SkSVGFontFamily   , SkSVGValue::Type::kFontFamily>;
+using SkSVGFontSizeValue     = SkSVGWrapperValue<SkSVGFontSize     , SkSVGValue::Type::kFontSize  >;
+using SkSVGFontStyleValue    = SkSVGWrapperValue<SkSVGFontStyle    , SkSVGValue::Type::kFontStyle >;
+using SkSVGFontWeightValue   = SkSVGWrapperValue<SkSVGFontWeight   , SkSVGValue::Type::kFontWeight>;
+
 #endif // SkSVGValue_DEFINED
diff --git a/modules/svg/src/SkSVGAttribute.cpp b/modules/svg/src/SkSVGAttribute.cpp
index 5bdb65b..0ccea49 100644
--- a/modules/svg/src/SkSVGAttribute.cpp
+++ b/modules/svg/src/SkSVGAttribute.cpp
@@ -28,5 +28,10 @@
 
     result.fColor.set(SkSVGColorType(SK_ColorBLACK));
 
+    result.fFontFamily.init("Sans");
+    result.fFontStyle.init(SkSVGFontStyle::Type::kNormal);
+    result.fFontSize.init(SkSVGLength(24));
+    result.fFontWeight.init(SkSVGFontWeight(SkSVGFontWeight::Type::kNormal));
+
     return result;
 }
diff --git a/modules/svg/src/SkSVGAttributeParser.cpp b/modules/svg/src/SkSVGAttributeParser.cpp
index 9914456..18acdf4 100644
--- a/modules/svg/src/SkSVGAttributeParser.cpp
+++ b/modules/svg/src/SkSVGAttributeParser.cpp
@@ -33,6 +33,17 @@
 
 }  // namespace
 
+template <typename T, typename TArray>
+bool SkSVGAttributeParser::parseEnumMap(const TArray& arr, T* result) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(arr); ++i) {
+        if (this->parseExpectedStringToken(std::get<0>(arr[i]))) {
+            *result = std::get<1>(arr[i]);
+            return true;
+        }
+    }
+    return false;
+}
+
 SkSVGAttributeParser::SkSVGAttributeParser(const char attributeString[])
     : fCurPos(attributeString) {}
 
@@ -710,3 +721,90 @@
 
     return parsedValue && this->parseEOSToken();
 }
+
+// https://www.w3.org/TR/SVG11/text.html#FontFamilyProperty
+bool SkSVGAttributeParser::parseFontFamily(SkSVGFontFamily* family) {
+    bool parsedValue = false;
+    if (this->parseExpectedStringToken("inherit")) {
+        *family = SkSVGFontFamily();
+        parsedValue = true;
+    } else {
+        // The spec allows specifying a comma-separated list for explicit fallback order.
+        // For now, we only use the first entry and rely on the font manager to handle fallback.
+        const auto* comma = strchr(fCurPos, ',');
+        auto family_name = comma ? SkString(fCurPos, comma - fCurPos)
+                                 : SkString(fCurPos);
+        *family = SkSVGFontFamily(family_name.c_str());
+        fCurPos += strlen(fCurPos);
+        parsedValue = true;
+    }
+
+    return parsedValue && this->parseEOSToken();
+}
+
+// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
+bool SkSVGAttributeParser::parseFontSize(SkSVGFontSize* size) {
+    bool parsedValue = false;
+    if (this->parseExpectedStringToken("inherit")) {
+        *size = SkSVGFontSize();
+        parsedValue = true;
+    } else {
+        SkSVGLength length;
+        if (this->parseLength(&length)) {
+            *size = SkSVGFontSize(length);
+            parsedValue = true;
+        }
+    }
+
+    return parsedValue && this->parseEOSToken();
+}
+
+// https://www.w3.org/TR/SVG11/text.html#FontStyleProperty
+bool SkSVGAttributeParser::parseFontStyle(SkSVGFontStyle* style) {
+    static constexpr std::tuple<const char*, SkSVGFontStyle::Type> gStyleMap[] = {
+        { "normal" , SkSVGFontStyle::Type::kNormal  },
+        { "italic" , SkSVGFontStyle::Type::kItalic  },
+        { "oblique", SkSVGFontStyle::Type::kOblique },
+        { "inherit", SkSVGFontStyle::Type::kInherit },
+    };
+
+    bool parsedValue = false;
+    SkSVGFontStyle::Type type;
+
+    if (this->parseEnumMap(gStyleMap, &type)) {
+        *style = SkSVGFontStyle(type);
+        parsedValue = true;
+    }
+
+    return parsedValue && this->parseEOSToken();
+}
+
+// https://www.w3.org/TR/SVG11/text.html#FontWeightProperty
+bool SkSVGAttributeParser::parseFontWeight(SkSVGFontWeight* weight) {
+    static constexpr std::tuple<const char*, SkSVGFontWeight::Type> gWeightMap[] = {
+        { "normal" , SkSVGFontWeight::Type::kNormal  },
+        { "bold"   , SkSVGFontWeight::Type::kBold    },
+        { "bolder" , SkSVGFontWeight::Type::kBolder  },
+        { "lighter", SkSVGFontWeight::Type::kLighter },
+        { "100"    , SkSVGFontWeight::Type::k100     },
+        { "200"    , SkSVGFontWeight::Type::k200     },
+        { "300"    , SkSVGFontWeight::Type::k300     },
+        { "400"    , SkSVGFontWeight::Type::k400     },
+        { "500"    , SkSVGFontWeight::Type::k500     },
+        { "600"    , SkSVGFontWeight::Type::k600     },
+        { "700"    , SkSVGFontWeight::Type::k700     },
+        { "800"    , SkSVGFontWeight::Type::k800     },
+        { "900"    , SkSVGFontWeight::Type::k900     },
+        { "inherit", SkSVGFontWeight::Type::kInherit },
+    };
+
+    bool parsedValue = false;
+    SkSVGFontWeight::Type type;
+
+    if (this->parseEnumMap(gWeightMap, &type)) {
+        *weight = SkSVGFontWeight(type);
+        parsedValue = true;
+    }
+
+    return parsedValue && this->parseEOSToken();
+}
diff --git a/modules/svg/src/SkSVGDOM.cpp b/modules/svg/src/SkSVGDOM.cpp
index c833a6a..8ab5ade 100644
--- a/modules/svg/src/SkSVGDOM.cpp
+++ b/modules/svg/src/SkSVGDOM.cpp
@@ -248,6 +248,54 @@
     return true;
 }
 
+bool SetFontFamilyAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
+                            const char* stringValue) {
+    SkSVGFontFamily family;
+    SkSVGAttributeParser parser(stringValue);
+    if (!parser.parseFontFamily(&family)) {
+        return false;
+    }
+
+    node->setAttribute(attr, SkSVGFontFamilyValue(family));
+    return true;
+}
+
+bool SetFontSizeAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
+                          const char* stringValue) {
+    SkSVGFontSize size;
+    SkSVGAttributeParser parser(stringValue);
+    if (!parser.parseFontSize(&size)) {
+        return false;
+    }
+
+    node->setAttribute(attr, SkSVGFontSizeValue(size));
+    return true;
+}
+
+bool SetFontStyleAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
+                           const char* stringValue) {
+    SkSVGFontStyle style;
+    SkSVGAttributeParser parser(stringValue);
+    if (!parser.parseFontStyle(&style)) {
+        return false;
+    }
+
+    node->setAttribute(attr, SkSVGFontStyleValue(style));
+    return true;
+}
+
+bool SetFontWeightAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
+                            const char* stringValue) {
+    SkSVGFontWeight weight;
+    SkSVGAttributeParser parser(stringValue);
+    if (!parser.parseFontWeight(&weight)) {
+        return false;
+    }
+
+    node->setAttribute(attr, SkSVGFontWeightValue(weight));
+    return true;
+}
+
 SkString TrimmedString(const char* first, const char* last) {
     SkASSERT(first);
     SkASSERT(last);
@@ -335,10 +383,10 @@
     { "fill"             , { SkSVGAttribute::kFill             , SetPaintAttribute        }},
     { "fill-opacity"     , { SkSVGAttribute::kFillOpacity      , SetNumberAttribute       }},
     { "fill-rule"        , { SkSVGAttribute::kFillRule         , SetFillRuleAttribute     }},
-    { "font-family"      , { SkSVGAttribute::kFontFamily       , SetStringAttribute       }},
-    { "font-size"        , { SkSVGAttribute::kFontSize         , SetLengthAttribute       }},
-    { "font-style"       , { SkSVGAttribute::kFontStyle        , SetStringAttribute       }},
-    { "font-weight"      , { SkSVGAttribute::kFontWeight       , SetStringAttribute       }},
+    { "font-family"      , { SkSVGAttribute::kFontFamily       , SetFontFamilyAttribute   }},
+    { "font-size"        , { SkSVGAttribute::kFontSize         , SetFontSizeAttribute     }},
+    { "font-style"       , { SkSVGAttribute::kFontStyle        , SetFontStyleAttribute    }},
+    { "font-weight"      , { SkSVGAttribute::kFontWeight       , SetFontWeightAttribute   }},
     // focal point x & y
     { "fx"               , { SkSVGAttribute::kFx               , SetLengthAttribute       }},
     { "fy"               , { SkSVGAttribute::kFy               , SetLengthAttribute       }},
@@ -404,8 +452,8 @@
     ConstructionContext(const ConstructionContext& other, const sk_sp<SkSVGNode>& newParent)
         : fParent(newParent.get()), fIDMapper(other.fIDMapper) {}
 
-    const SkSVGNode* fParent;
-    SkSVGIDMapper*   fIDMapper;
+    SkSVGNode*     fParent;
+    SkSVGIDMapper* fIDMapper;
 };
 
 bool set_string_attribute(const sk_sp<SkSVGNode>& node, const char* name, const char* value) {
@@ -452,7 +500,10 @@
 
     if (elemType == SkDOM::kText_Type) {
         SkASSERT(dom.countChildren(xmlNode) == 0);
-        // TODO: text handling
+        // TODO: add type conversion helper to SkSVGNode
+        if (ctx.fParent->tag() == SkSVGTag::kText) {
+            static_cast<SkSVGText*>(ctx.fParent)->setText(SkString(dom.getName(xmlNode)));
+        }
         return nullptr;
     }
 
diff --git a/modules/svg/src/SkSVGNode.cpp b/modules/svg/src/SkSVGNode.cpp
index d634722..ce05aa5 100644
--- a/modules/svg/src/SkSVGNode.cpp
+++ b/modules/svg/src/SkSVGNode.cpp
@@ -169,6 +169,26 @@
             this->setFillRule(*fillRule);
         }
         break;
+    case SkSVGAttribute::kFontFamily:
+        if (const SkSVGFontFamilyValue* family = v.as<SkSVGFontFamilyValue>()) {
+            this->setFontFamily(*family);
+        }
+        break;
+    case SkSVGAttribute::kFontSize:
+        if (const SkSVGFontSizeValue* size = v.as<SkSVGFontSizeValue>()) {
+            this->setFontSize(*size);
+        }
+        break;
+    case SkSVGAttribute::kFontStyle:
+        if (const SkSVGFontStyleValue* style = v.as<SkSVGFontStyleValue>()) {
+            this->setFontStyle(*style);
+        }
+        break;
+    case SkSVGAttribute::kFontWeight:
+        if (const SkSVGFontWeightValue* style = v.as<SkSVGFontWeightValue>()) {
+            this->setFontWeight(*style);
+        }
+        break;
     case SkSVGAttribute::kOpacity:
         if (const SkSVGNumberValue* opacity = v.as<SkSVGNumberValue>()) {
             this->setOpacity(*opacity);
diff --git a/modules/svg/src/SkSVGRenderContext.cpp b/modules/svg/src/SkSVGRenderContext.cpp
index 4607f67..759c5cf 100644
--- a/modules/svg/src/SkSVGRenderContext.cpp
+++ b/modules/svg/src/SkSVGRenderContext.cpp
@@ -274,6 +274,34 @@
     // Not part of the SkPaint state; applied via 'currentColor' color value
 }
 
+template <>
+void commitToPaint<SkSVGAttribute::kFontFamily>(const SkSVGPresentationAttributes&,
+                                                const SkSVGRenderContext&,
+                                                SkSVGPresentationContext*) {
+    // Not part of the SkPaint state; applied at render time.
+}
+
+template <>
+void commitToPaint<SkSVGAttribute::kFontSize>(const SkSVGPresentationAttributes&,
+                                              const SkSVGRenderContext&,
+                                              SkSVGPresentationContext*) {
+    // Not part of the SkPaint state; applied at render time.
+}
+
+template <>
+void commitToPaint<SkSVGAttribute::kFontStyle>(const SkSVGPresentationAttributes&,
+                                               const SkSVGRenderContext&,
+                                               SkSVGPresentationContext*) {
+    // Not part of the SkPaint state; applied at render time.
+}
+
+template <>
+void commitToPaint<SkSVGAttribute::kFontWeight>(const SkSVGPresentationAttributes&,
+                                                const SkSVGRenderContext&,
+                                                SkSVGPresentationContext*) {
+    // Not part of the SkPaint state; applied at render time.
+}
+
 }  // namespace
 
 SkSVGPresentationContext::SkSVGPresentationContext()
@@ -351,6 +379,10 @@
     ApplyLazyInheritedAttribute(Fill);
     ApplyLazyInheritedAttribute(FillOpacity);
     ApplyLazyInheritedAttribute(FillRule);
+    ApplyLazyInheritedAttribute(FontFamily);
+    ApplyLazyInheritedAttribute(FontSize);
+    ApplyLazyInheritedAttribute(FontStyle);
+    ApplyLazyInheritedAttribute(FontWeight);
     ApplyLazyInheritedAttribute(ClipRule);
     ApplyLazyInheritedAttribute(Stroke);
     ApplyLazyInheritedAttribute(StrokeDashOffset);
diff --git a/modules/svg/src/SkSVGText.cpp b/modules/svg/src/SkSVGText.cpp
index b2d8704..4dde8b8 100644
--- a/modules/svg/src/SkSVGText.cpp
+++ b/modules/svg/src/SkSVGText.cpp
@@ -8,6 +8,7 @@
 #include "modules/svg/include/SkSVGText.h"
 
 #include "include/core/SkCanvas.h"
+#include "include/core/SkFontMgr.h"
 #include "include/core/SkFontStyle.h"
 #include "include/core/SkString.h"
 #include "modules/svg/include/SkSVGRenderContext.h"
@@ -19,47 +20,6 @@
 
 void SkSVGText::setY(const SkSVGLength& y) { fY = y; }
 
-void SkSVGText::setFontFamily(const SkSVGStringType& font_family) {
-  if (fFontFamily != font_family) {
-    fFontFamily = font_family;
-
-    this->loadFont();
-  }
-}
-
-void SkSVGText::loadFont() {
-  SkFontStyle style;
-  if (fFontWeight.equals("bold")) {
-    if (fFontStyle.equals("italic")) {
-      style = SkFontStyle::BoldItalic();
-    } else {
-      style = SkFontStyle::Bold();
-    }
-  } else if (fFontStyle.equals("italic")) {
-    style = SkFontStyle::Italic();
-  }
-
-  fTypeface = SkTypeface::MakeFromName(fFontFamily.c_str(), style);
-}
-
-void SkSVGText::setFontSize(const SkSVGLength& size) { fFontSize = size; }
-
-void SkSVGText::setFontStyle(const SkSVGStringType& font_style) {
-  if (fFontStyle != font_style) {
-    fFontStyle = font_style;
-
-    this->loadFont();
-  }
-}
-
-void SkSVGText::setFontWeight(const SkSVGStringType& font_weight) {
-  if (fFontWeight != font_weight) {
-    fFontWeight = font_weight;
-
-    this->loadFont();
-  }
-}
-
 void SkSVGText::setText(const SkSVGStringType& text) { fText = text; }
 
 void SkSVGText::setTextAnchor(const SkSVGStringType& text_anchor) {
@@ -72,11 +32,79 @@
   }
 }
 
-void SkSVGText::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx,
-                       const SkPaint& paint, SkPathFillType) const {
-  SkFont font(fTypeface, fFontSize.value());
-  SkTextUtils::DrawString(canvas, fText.c_str(), fX.value(), fY.value(),
-                          font, paint, fTextAlign);
+SkFont SkSVGText::resolveFont(const SkSVGRenderContext& ctx) const {
+    auto weight = [](const SkSVGFontWeight& w) {
+        switch (w.type()) {
+            case SkSVGFontWeight::Type::k100:     return SkFontStyle::kThin_Weight;
+            case SkSVGFontWeight::Type::k200:     return SkFontStyle::kExtraLight_Weight;
+            case SkSVGFontWeight::Type::k300:     return SkFontStyle::kLight_Weight;
+            case SkSVGFontWeight::Type::k400:     return SkFontStyle::kNormal_Weight;
+            case SkSVGFontWeight::Type::k500:     return SkFontStyle::kMedium_Weight;
+            case SkSVGFontWeight::Type::k600:     return SkFontStyle::kSemiBold_Weight;
+            case SkSVGFontWeight::Type::k700:     return SkFontStyle::kBold_Weight;
+            case SkSVGFontWeight::Type::k800:     return SkFontStyle::kExtraBold_Weight;
+            case SkSVGFontWeight::Type::k900:     return SkFontStyle::kBlack_Weight;
+            case SkSVGFontWeight::Type::kNormal:  return SkFontStyle::kNormal_Weight;
+            case SkSVGFontWeight::Type::kBold:    return SkFontStyle::kBold_Weight;
+            case SkSVGFontWeight::Type::kBolder:  return SkFontStyle::kExtraBold_Weight;
+            case SkSVGFontWeight::Type::kLighter: return SkFontStyle::kLight_Weight;
+            case SkSVGFontWeight::Type::kInherit: {
+                SkASSERT(false);
+                return SkFontStyle::kNormal_Weight;
+            }
+        }
+        SkUNREACHABLE;
+    };
+
+    auto slant = [](const SkSVGFontStyle& s) {
+        switch (s.type()) {
+            case SkSVGFontStyle::Type::kNormal:  return SkFontStyle::kUpright_Slant;
+            case SkSVGFontStyle::Type::kItalic:  return SkFontStyle::kItalic_Slant;
+            case SkSVGFontStyle::Type::kOblique: return SkFontStyle::kOblique_Slant;
+            case SkSVGFontStyle::Type::kInherit: {
+                SkASSERT(false);
+                return SkFontStyle::kUpright_Slant;
+            }
+        }
+        SkUNREACHABLE;
+    };
+
+    const auto& family = ctx.presentationContext().fInherited.fFontFamily->family();
+    const SkFontStyle style(weight(*ctx.presentationContext().fInherited.fFontWeight),
+                            SkFontStyle::kNormal_Width,
+                            slant(*ctx.presentationContext().fInherited.fFontStyle));
+
+    const auto size =
+            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);
+    font.setHinting(SkFontHinting::kNone);
+    font.setSubpixel(true);
+    font.setLinearMetrics(true);
+    font.setBaselineSnap(false);
+    font.setEdging(SkFont::Edging::kAntiAlias);
+
+    return font;
+}
+
+void SkSVGText::onRender(const SkSVGRenderContext& ctx) const {
+    const auto font = this->resolveFont(ctx);
+
+    if (const SkPaint* fillPaint = ctx.fillPaint()) {
+        SkTextUtils::DrawString(ctx.canvas(), fText.c_str(), fX.value(), fY.value(), font,
+                                *fillPaint, fTextAlign);
+    }
+
+    if (const SkPaint* strokePaint = ctx.strokePaint()) {
+        SkTextUtils::DrawString(ctx.canvas(), fText.c_str(), fX.value(), fY.value(), font,
+                                *strokePaint, fTextAlign);
+    }
+}
+
+void SkSVGText::appendChild(sk_sp<SkSVGNode>) {
+    // TODO
 }
 
 SkPath SkSVGText::onAsPath(const SkSVGRenderContext& ctx) const {
@@ -106,25 +134,6 @@
         this->setTextAnchor(*text_anchor);
       }
       break;
-    case SkSVGAttribute::kFontFamily:
-      if (const auto* font_family = v.as<SkSVGStringValue>()) {
-        this->setFontFamily(*font_family);
-      }
-      break;
-    case SkSVGAttribute::kFontWeight:
-      if (const auto* font_weight = v.as<SkSVGStringValue>()) {
-        this->setFontWeight(*font_weight);
-      }
-      break;
-    case SkSVGAttribute::kFontSize:
-      if (const auto* font_size = v.as<SkSVGLengthValue>()) {
-        this->setFontSize(*font_size);
-      }
-      break;
-    case SkSVGAttribute::kFontStyle:
-      if (const auto* font_style = v.as<SkSVGStringValue>()) {
-        this->setFontStyle(*font_style);
-      }
       break;
     default:
       this->INHERITED::onSetAttribute(attr, v);