1 /* 2 * Copyright 2020 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 8 #ifndef SkSVGTextPriv_DEFINED 9 #define SkSVGTextPriv_DEFINED 10 11 #include "modules/skshaper/include/SkShaper.h" 12 #include "modules/svg/include/SkSVGRenderContext.h" 13 #include "modules/svg/include/SkSVGText.h" 14 #include "src/core/SkTLazy.h" 15 16 #include <functional> 17 #include <tuple> 18 19 class SkContourMeasure; 20 struct SkRSXform; 21 22 // SkSVGTextContext is responsible for sequencing input text chars into "chunks". 23 // A single text chunk can span multiple structural elements (<text>, <tspan>, etc), 24 // and per [1] new chunks are emitted 25 // 26 // a) for each top level text element (<text>, <textPath>) 27 // b) whenever a character with an explicit absolute position is encountered 28 // 29 // The implementation queues shaped run data until a full text chunk is resolved, at which 30 // point we have enough information to perform final alignment and rendering. 31 // 32 // [1] https://www.w3.org/TR/SVG11/text.html#TextLayoutIntroduction 33 class SkSVGTextContext final : SkShaper::RunHandler { 34 public: 35 using ShapedTextCallback = std::function<void(const SkSVGRenderContext&, 36 const sk_sp<SkTextBlob>&, 37 const SkPaint*, 38 const SkPaint*)>; 39 40 // Helper for encoding optional positional attributes. 41 class PosAttrs { 42 public: 43 // TODO: rotate 44 enum Attr : size_t { 45 kX = 0, 46 kY = 1, 47 kDx = 2, 48 kDy = 3, 49 kRotate = 4, 50 }; 51 52 float operator[](Attr a) const { return fStorage[a]; } 53 float& operator[](Attr a) { return fStorage[a]; } 54 has(Attr a)55 bool has(Attr a) const { return fStorage[a] != kNone; } hasAny()56 bool hasAny() const { 57 return this->has(kX) 58 || this->has(kY) 59 || this->has(kDx) 60 || this->has(kDy) 61 || this->has(kRotate); 62 } 63 setImplicitRotate(bool imp)64 void setImplicitRotate(bool imp) { fImplicitRotate = imp; } isImplicitRotate()65 bool isImplicitRotate() const { return fImplicitRotate; } 66 67 private: 68 static constexpr auto kNone = std::numeric_limits<float>::infinity(); 69 70 float fStorage[5] = { kNone, kNone, kNone, kNone, kNone }; 71 bool fImplicitRotate = false; 72 }; 73 74 // Helper for cascading position attribute resolution (x, y, dx, dy, rotate) [1]: 75 // - each text position element can specify an arbitrary-length attribute array 76 // - for each character, we look up a given attribute first in its local attribute array, 77 // then in the ancestor chain (cascading/fallback) - and return the first value encountered. 78 // - the lookup is based on character index relative to the text content subtree 79 // (i.e. the index crosses chunk boundaries) 80 // 81 // [1] https://www.w3.org/TR/SVG11/text.html#TSpanElementXAttribute 82 class ScopedPosResolver { 83 public: 84 ScopedPosResolver(const SkSVGTextContainer&, const SkSVGLengthContext&, SkSVGTextContext*, 85 size_t); 86 87 ScopedPosResolver(const SkSVGTextContainer&, const SkSVGLengthContext&, SkSVGTextContext*); 88 89 ~ScopedPosResolver(); 90 91 PosAttrs resolve(size_t charIndex) const; 92 93 private: 94 SkSVGTextContext* fTextContext; 95 const ScopedPosResolver* fParent; // parent resolver (fallback) 96 const size_t fCharIndexOffset; // start index for the current resolver 97 const std::vector<float> fX, 98 fY, 99 fDx, 100 fDy; 101 const std::vector<float>& fRotate; 102 103 // cache for the last known index with explicit positioning 104 mutable size_t fLastPosIndex = std::numeric_limits<size_t>::max(); 105 106 }; 107 108 SkSVGTextContext(const SkSVGRenderContext&, 109 const ShapedTextCallback&, 110 const SkSVGTextPath* = nullptr); 111 ~SkSVGTextContext() override; 112 113 // Shape and queue codepoints for final alignment. 114 void shapeFragment(const SkString&, const SkSVGRenderContext&, SkSVGXmlSpace); 115 116 // Perform final adjustments and push shaped blobs to the callback. 117 void flushChunk(const SkSVGRenderContext& ctx); 118 getCallback()119 const ShapedTextCallback& getCallback() const { return fCallback; } 120 121 private: 122 struct PositionAdjustment { 123 SkVector offset; 124 float rotation; 125 }; 126 127 struct ShapeBuffer { 128 SkSTArray<128, char , true> fUtf8; 129 // per-utf8-char cumulative pos adjustments 130 SkSTArray<128, PositionAdjustment, true> fUtf8PosAdjust; 131 reserveShapeBuffer132 void reserve(size_t size) { 133 fUtf8.reserve_back(SkToInt(size)); 134 fUtf8PosAdjust.reserve_back(SkToInt(size)); 135 } 136 resetShapeBuffer137 void reset() { 138 fUtf8.reset(); 139 fUtf8PosAdjust.reset(); 140 } 141 142 void append(SkUnichar, PositionAdjustment); 143 }; 144 145 struct RunRec { 146 SkFont font; 147 std::unique_ptr<SkPaint> fillPaint, 148 strokePaint; 149 std::unique_ptr<SkGlyphID[]> glyphs; // filled by SkShaper 150 std::unique_ptr<SkPoint[]> glyphPos; // filled by SkShaper 151 std::unique_ptr<PositionAdjustment[]> glyhPosAdjust; // deferred positioning adjustments 152 size_t glyphCount; 153 SkVector advance; 154 }; 155 156 // Caches path information to accelerate position lookups. 157 class PathData { 158 public: 159 PathData(const SkSVGRenderContext&, const SkSVGTextPath&); 160 161 SkMatrix getMatrixAt(float offset) const; 162 length()163 float length() const { return fLength; } 164 165 private: 166 std::vector<sk_sp<SkContourMeasure>> fContours; 167 float fLength = 0; // total path length 168 }; 169 170 void shapePendingBuffer(const SkFont&); 171 172 SkRSXform computeGlyphXform(SkGlyphID, const SkFont&, const SkPoint& glyph_pos, 173 const PositionAdjustment&) const; 174 175 // SkShaper callbacks beginLine()176 void beginLine() override {} runInfo(const RunInfo &)177 void runInfo(const RunInfo&) override {} commitRunInfo()178 void commitRunInfo() override {} 179 Buffer runBuffer(const RunInfo& ri) override; 180 void commitRunBuffer(const RunInfo& ri) override; commitLine()181 void commitLine() override {} 182 183 // http://www.w3.org/TR/SVG11/text.html#TextLayout 184 const SkSVGRenderContext& fRenderContext; // original render context 185 const ShapedTextCallback& fCallback; 186 const std::unique_ptr<SkShaper> fShaper; 187 std::vector<RunRec> fRuns; 188 const ScopedPosResolver* fPosResolver = nullptr; 189 std::unique_ptr<PathData> fPathData; 190 191 // shaper state 192 ShapeBuffer fShapeBuffer; 193 std::vector<uint32_t> fShapeClusterBuffer; 194 195 // chunk state 196 SkPoint fChunkPos = {0,0}; // current text chunk position 197 SkVector fChunkAdvance = {0,0}; // cumulative advance 198 float fChunkAlignmentFactor; // current chunk alignment 199 200 // tracks the global text subtree char index (cross chunks). Used for position resolution. 201 size_t fCurrentCharIndex = 0; 202 203 // cached for access from SkShaper callbacks. 204 SkTLazy<SkPaint> fCurrentFill; 205 SkTLazy<SkPaint> fCurrentStroke; 206 207 bool fPrevCharSpace = true; // WS filter state 208 }; 209 210 #endif // SkSVGTextPriv_DEFINED 211