1 // Copyright 2019 Google LLC. 2 #ifndef Paragraph_DEFINED 3 #define Paragraph_DEFINED 4 5 #include "include/core/SkPath.h" 6 #ifdef ENABLE_TEXT_ENHANCE 7 #include "include/private/base/SkTArray.h" 8 #endif 9 #include "modules/skparagraph/include/FontCollection.h" 10 #include "modules/skparagraph/include/Metrics.h" 11 #include "modules/skparagraph/include/ParagraphStyle.h" 12 #ifdef ENABLE_TEXT_ENHANCE 13 #include "modules/skparagraph/include/TextLineBase.h" 14 #endif 15 #include "modules/skparagraph/include/TextStyle.h" 16 #include <unordered_set> 17 18 #ifdef ENABLE_TEXT_ENHANCE 19 #include "drawing.h" 20 #endif 21 22 class SkCanvas; 23 24 namespace skia { 25 namespace textlayout { 26 27 #ifdef ENABLE_TEXT_ENHANCE 28 enum InternalState { 29 kUnknown = 0, 30 kIndexed = 1, // Text is indexed 31 kShaped = 2, // Text is shaped 32 kLineBroken = 5, 33 kFormatted = 6, 34 kDrawn = 7 35 }; 36 37 enum class UtfEncodeType { 38 kUtf8, 39 kUtf16 40 }; 41 #endif 42 43 class ParagraphPainter; 44 45 class Paragraph { 46 47 public: 48 #ifdef ENABLE_TEXT_ENHANCE 49 Paragraph() = default; 50 #endif // ENABLE_TEXT_ENHANCE 51 52 Paragraph(ParagraphStyle style, sk_sp<FontCollection> fonts); 53 54 virtual ~Paragraph() = default; 55 getMaxWidth()56 SkScalar getMaxWidth() { return fWidth; } 57 getHeight()58 SkScalar getHeight() { return fHeight; } 59 getMinIntrinsicWidth()60 SkScalar getMinIntrinsicWidth() { return fMinIntrinsicWidth; } 61 getMaxIntrinsicWidth()62 SkScalar getMaxIntrinsicWidth() { return fMaxIntrinsicWidth; } 63 getAlphabeticBaseline()64 SkScalar getAlphabeticBaseline() { return fAlphabeticBaseline; } 65 getIdeographicBaseline()66 SkScalar getIdeographicBaseline() { return fIdeographicBaseline; } 67 getLongestLine()68 SkScalar getLongestLine() { return fLongestLine; } 69 #ifdef ENABLE_TEXT_ENHANCE getLongestLineWithIndent()70 SkScalar getLongestLineWithIndent() { return fLongestLineWithIndent; } 71 setLongestLineWithIndent(SkScalar longestLineWithIndent)72 void setLongestLineWithIndent(SkScalar longestLineWithIndent) 73 { 74 fLongestLineWithIndent = longestLineWithIndent; 75 } 76 getGlyphsBoundsTop()77 SkScalar getGlyphsBoundsTop() { return fGlyphsBoundsTop; } 78 getGlyphsBoundsBottom()79 SkScalar getGlyphsBoundsBottom() { return fGlyphsBoundsBottom; } 80 getGlyphsBoundsLeft()81 SkScalar getGlyphsBoundsLeft() { return fGlyphsBoundsLeft; } 82 getGlyphsBoundsRight()83 SkScalar getGlyphsBoundsRight() { return fGlyphsBoundsRight; } 84 getParagraphStyle()85 ParagraphStyle& getParagraphStyle() { return fParagraphStyle; } 86 87 virtual skia_private::TArray<Block, true>& exportTextStyles() = 0; 88 89 virtual void setState(InternalState state) = 0; 90 91 virtual InternalState getState() const = 0; 92 93 virtual void paint(ParagraphPainter* painter, RSPath* path, SkScalar hOffset, SkScalar vOffset) = 0; 94 95 virtual TextRange getEllipsisTextRange() = 0; 96 97 virtual std::vector<ParagraphPainter::PaintID> updateColor(size_t from, size_t to, SkColor color, 98 UtfEncodeType encodeType) = 0; 99 100 virtual std::vector<TextBlobRecordInfo> getTextBlobRecordInfo() = 0; 101 102 virtual bool hasSkipTextBlobDrawing() const = 0; 103 104 virtual void setSkipTextBlobDrawing(bool state) = 0; 105 106 virtual bool isRunCombinated() const = 0; 107 108 virtual bool canPaintAllText() const = 0; 109 110 virtual std::string GetDumpInfo() const = 0; 111 #endif 112 didExceedMaxLines()113 bool didExceedMaxLines() { return fExceededMaxLines; } 114 115 virtual void layout(SkScalar width) = 0; 116 117 virtual void paint(SkCanvas* canvas, SkScalar x, SkScalar y) = 0; 118 119 virtual void paint(ParagraphPainter* painter, SkScalar x, SkScalar y) = 0; 120 121 // Returns a vector of bounding boxes that enclose all text between 122 // start and end glyph indexes, including start and excluding end 123 virtual std::vector<TextBox> getRectsForRange(unsigned start, 124 unsigned end, 125 RectHeightStyle rectHeightStyle, 126 RectWidthStyle rectWidthStyle) = 0; 127 128 virtual std::vector<TextBox> getRectsForPlaceholders() = 0; 129 130 // Returns the index of the glyph that corresponds to the provided coordinate, 131 // with the top left corner as the origin, and +y direction as down 132 virtual PositionWithAffinity getGlyphPositionAtCoordinate(SkScalar dx, SkScalar dy) = 0; 133 134 // Finds the first and last glyphs that define a word containing 135 // the glyph at index offset 136 virtual SkRange<size_t> getWordBoundary(unsigned offset) = 0; 137 138 virtual void getLineMetrics(std::vector<LineMetrics>&) = 0; 139 140 virtual size_t lineNumber() = 0; 141 142 virtual void markDirty() = 0; 143 144 // This function will return the number of unresolved glyphs or 145 // -1 if not applicable (has not been shaped yet - valid case) 146 virtual int32_t unresolvedGlyphs() = 0; 147 virtual std::unordered_set<SkUnichar> unresolvedCodepoints() = 0; 148 149 // Experimental API that allows fast way to update some of "immutable" paragraph attributes 150 // but not the text itself 151 virtual void updateTextAlign(TextAlign textAlign) = 0; 152 virtual void updateFontSize(size_t from, size_t to, SkScalar fontSize) = 0; 153 virtual void updateForegroundPaint(size_t from, size_t to, SkPaint paint) = 0; 154 virtual void updateBackgroundPaint(size_t from, size_t to, SkPaint paint) = 0; 155 156 enum VisitorFlags { 157 kWhiteSpace_VisitorFlag = 1 << 0, 158 }; 159 struct VisitorInfo { 160 const SkFont& font; 161 SkPoint origin; 162 SkScalar advanceX; 163 int count; 164 const uint16_t* glyphs; // count values 165 const SkPoint* positions; // count values 166 const uint32_t* utf8Starts; // count+1 values 167 unsigned flags; 168 }; 169 170 // lineNumber begins at 0. If info is null, this signals the end of that line. 171 using Visitor = std::function<void(int lineNumber, const VisitorInfo*)>; 172 virtual void visit(const Visitor&) = 0; 173 174 struct ExtendedVisitorInfo { 175 const SkFont& font; 176 SkPoint origin; 177 SkSize advance; 178 int count; 179 const uint16_t* glyphs; // count values 180 SkPoint* positions; // count values 181 const SkRect* bounds; // count values 182 const uint32_t* utf8Starts; // count+1 values 183 unsigned flags; 184 }; 185 using ExtendedVisitor = std::function<void(int lineNumber, const ExtendedVisitorInfo*)>; 186 #ifndef ENABLE_TEXT_ENHANCE 187 virtual void extendedVisit(const ExtendedVisitor&) = 0; 188 189 /* Returns path for a given line 190 * 191 * @param lineNumber a line number 192 * @param dest a resulting path 193 * @return a number glyphs that could not be converted to path 194 */ 195 virtual int getPath(int lineNumber, SkPath* dest) = 0; 196 #endif 197 198 /* Returns path for a text blob 199 * 200 * @param textBlob a text blob 201 * @return a path 202 */ 203 static SkPath GetPath(SkTextBlob* textBlob); 204 205 /* Checks if a given text blob contains 206 * glyph with emoji 207 * 208 * @param textBlob a text blob 209 * @return true if there is such a glyph 210 */ 211 virtual bool containsEmoji(SkTextBlob* textBlob) = 0; 212 213 /* Checks if a given text blob contains colored font or bitmap 214 * 215 * @param textBlob a text blob 216 * @return true if there is such a glyph 217 */ 218 virtual bool containsColorFontOrBitmap(SkTextBlob* textBlob) = 0; 219 220 // Editing API 221 222 /* Finds the line number of the line that contains the given UTF-8 index. 223 * 224 * @param index a UTF-8 TextIndex into the paragraph 225 * @return the line number the glyph that corresponds to the 226 * given codeUnitIndex is in, or -1 if the codeUnitIndex 227 * is out of bounds, or when the glyph is truncated or 228 * ellipsized away. 229 */ 230 virtual int getLineNumberAt(TextIndex codeUnitIndex) const = 0; 231 232 /* Finds the line number of the line that contains the given UTF-16 index. 233 * 234 * @param index a UTF-16 offset into the paragraph 235 * @return the line number the glyph that corresponds to the 236 * given codeUnitIndex is in, or -1 if the codeUnitIndex 237 * is out of bounds, or when the glyph is truncated or 238 * ellipsized away. 239 */ 240 virtual int getLineNumberAtUTF16Offset(size_t codeUnitIndex) = 0; 241 242 /* Returns line metrics info for the line 243 * 244 * @param lineNumber a line number 245 * @param lineMetrics an address to return the info (in case of null just skipped) 246 * @return true if the line is found; false if not 247 */ 248 virtual bool getLineMetricsAt(int lineNumber, LineMetrics* lineMetrics) const = 0; 249 250 /* Returns the visible text on the line (excluding a possible ellipsis) 251 * 252 * @param lineNumber a line number 253 * @param includeSpaces indicates if the whitespaces should be included 254 * @return the range of the text that is shown in the line 255 */ 256 virtual TextRange getActualTextRange(int lineNumber, bool includeSpaces) const = 0; 257 258 struct GlyphClusterInfo { 259 SkRect fBounds; 260 TextRange fClusterTextRange; 261 TextDirection fGlyphClusterPosition; 262 }; 263 264 /** Finds a glyph cluster for text index 265 * 266 * @param codeUnitIndex a text index 267 * @param glyphInfo a glyph cluster info filled if not null 268 * @return true if glyph cluster was found; false if not 269 */ 270 virtual bool getGlyphClusterAt(TextIndex codeUnitIndex, GlyphClusterInfo* glyphInfo) = 0; 271 272 /** Finds the closest glyph cluster for a visual text position 273 * 274 * @param dx x coordinate 275 * @param dy y coordinate 276 * @param glyphInfo a glyph cluster info filled if not null 277 * @return true if glyph cluster was found; false if not 278 * (which usually means the paragraph is empty) 279 */ 280 virtual bool getClosestGlyphClusterAt(SkScalar dx, 281 SkScalar dy, 282 GlyphClusterInfo* glyphInfo) = 0; 283 284 // The glyph and grapheme cluster information assoicated with a unicode 285 // codepoint in the paragraph. 286 struct GlyphInfo { 287 SkRect fGraphemeLayoutBounds; 288 TextRange fGraphemeClusterTextRange; 289 TextDirection fDirection; 290 bool fIsEllipsis; 291 }; 292 293 /** Retrives the information associated with the glyph located at the given 294 * codeUnitIndex. 295 * 296 * @param codeUnitIndex a UTF-16 offset into the paragraph 297 * @param glyphInfo an optional GlyphInfo struct to hold the 298 * information associated with the glyph found at the 299 * given index 300 * @return false only if the offset is out of bounds 301 */ 302 virtual bool getGlyphInfoAtUTF16Offset(size_t codeUnitIndex, GlyphInfo* glyphInfo) = 0; 303 304 /** Finds the information associated with the closest glyph to the given 305 * paragraph coordinates. 306 * 307 * @param dx x coordinate 308 * @param dy y coordinate 309 * @param glyphInfo an optional GlyphInfo struct to hold the 310 * information associated with the glyph found. The 311 * text indices and text ranges are described using 312 * UTF-16 offsets 313 * @return true if a graphme cluster was found; false if not 314 * (which usually means the paragraph is empty) 315 */ 316 virtual bool getClosestUTF16GlyphInfoAt(SkScalar dx, SkScalar dy, GlyphInfo* glyphInfo) = 0; 317 318 #ifdef ENABLE_TEXT_ENHANCE 319 struct FontInfo { FontInfoFontInfo320 FontInfo(const RSFont font, const TextRange textRange) : fFont(font), fTextRange(textRange) {} 321 virtual ~FontInfo() = default; 322 FontInfo(const FontInfo& ) = default; 323 RSFont fFont; 324 TextRange fTextRange; 325 }; 326 #else 327 struct FontInfo { FontInfoFontInfo328 FontInfo(const SkFont& font, const TextRange textRange) 329 : fFont(font), fTextRange(textRange) {} 330 virtual ~FontInfo() = default; 331 FontInfo(const FontInfo& ) = default; 332 SkFont fFont; 333 TextRange fTextRange; 334 }; 335 336 /** Returns the font used to shape the text at the given UTF-16 offset. 337 * 338 * @param codeUnitIndex a UTF-16 offset in the paragraph 339 * @return font info or an empty font info if the text is not found 340 */ 341 virtual SkFont getFontAtUTF16Offset(size_t codeUnitIndex) = 0; 342 #endif 343 344 /** Returns the information about all the fonts used to shape the paragraph text 345 * 346 * @return a list of fonts and text ranges 347 */ 348 virtual std::vector<FontInfo> getFonts() const = 0; 349 350 #ifdef ENABLE_TEXT_ENHANCE 351 virtual RSFont getFontAt(TextIndex codeUnitIndex) const = 0; 352 353 virtual void setIndents(const std::vector<SkScalar>& indents) = 0; 354 virtual SkScalar detectIndents(size_t index) = 0; 355 356 virtual SkScalar getTextSplitRatio() const = 0; 357 virtual std::vector<std::unique_ptr<TextLineBase>> GetTextLines() = 0; 358 virtual std::unique_ptr<Paragraph> CloneSelf() = 0; 359 virtual size_t getUnicodeIndex(TextIndex index) const = 0; 360 virtual const std::vector<SkUnichar>& unicodeText() const = 0; 361 virtual std::unique_ptr<Paragraph> createCroppedCopy( 362 size_t startIndex, size_t count = std::numeric_limits<size_t>::max()) = 0; 363 virtual void initUnicodeText() = 0; 364 virtual size_t GetMaxLines() const = 0; 365 virtual SkIRect generatePaintRegion(SkScalar x, SkScalar y) = 0; 366 367 virtual RSFontMetrics measureText() = 0; 368 virtual bool GetLineFontMetrics(const size_t lineNumber, size_t& charNumber, 369 std::vector<RSFontMetrics>& fontMetrics) = 0; 370 #else 371 /** Returns the font that is used to shape the text at the position 372 * 373 * @param codeUnitIndex text index 374 * @return font info or an empty font info if the text is not found 375 */ 376 virtual SkFont getFontAt(TextIndex codeUnitIndex) const = 0; 377 #endif 378 379 protected: 380 sk_sp<FontCollection> fFontCollection; 381 ParagraphStyle fParagraphStyle; 382 383 // Things for Flutter 384 SkScalar fAlphabeticBaseline; 385 SkScalar fIdeographicBaseline; 386 SkScalar fHeight; 387 SkScalar fWidth; 388 SkScalar fMaxIntrinsicWidth; 389 SkScalar fMinIntrinsicWidth; 390 SkScalar fLongestLine; 391 #ifdef ENABLE_TEXT_ENHANCE 392 SkScalar fLongestLineWithIndent; 393 SkScalar fGlyphsBoundsTop; 394 SkScalar fGlyphsBoundsBottom; 395 SkScalar fGlyphsBoundsLeft; 396 SkScalar fGlyphsBoundsRight; 397 #endif 398 bool fExceededMaxLines; 399 }; 400 } // namespace textlayout 401 } // namespace skia 402 403 #endif // Paragraph_DEFINED 404