• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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