1// Copyright 2019 Google LLC.
2#ifndef TextLine_DEFINED
3#define TextLine_DEFINED
4
5#include "include/ParagraphStyle.h"
6#include "include/core/SkPoint.h"
7#include "include/core/SkRect.h"
8#include "include/core/SkScalar.h"
9#include "include/private/SkBitmaskEnum.h" // IWYU pragma: keep
10#include "include/private/SkTArray.h"
11#include "modules/skparagraph/include/DartTypes.h"
12#include "modules/skparagraph/include/Metrics.h"
13#include "modules/skparagraph/include/ParagraphPainter.h"
14#include "modules/skparagraph/include/RunBase.h"
15#ifdef OHOS_SUPPORT
16#include "modules/skparagraph/include/TextLineBase.h"
17#endif
18#include "modules/skparagraph/include/TextStyle.h"
19#include "modules/skparagraph/src/Run.h"
20
21#include <stddef.h>
22#include <functional>
23#include <memory>
24#include <vector>
25
26class SkString;
27
28namespace skia {
29namespace textlayout {
30
31class ParagraphImpl;
32struct DecorationContext {
33    SkScalar thickness = 0.0f;
34    SkScalar underlinePosition = 0.0f;
35    SkScalar textBlobTop = 0.0f;
36};
37class TextLine {
38public:
39
40    struct ClipContext {
41      const Run* run;
42      size_t pos;
43      size_t size;
44      SkScalar fTextShift; // Shifts the text inside the run so it's placed at the right position
45      SkRect clip;
46      SkScalar fExcludedTrailingSpaces;
47      bool clippingNeeded;
48    };
49
50    struct PathParameters {
51        const RSPath* recordPath = nullptr;
52        SkScalar hOffset = 0;
53        SkScalar vOffset = 0;
54    } pathParameters;
55
56    enum TextAdjustment {
57        GlyphCluster = 0x01,    // All text producing glyphs pointing to the same ClusterIndex
58        GlyphemeCluster = 0x02, // base glyph + all attached diacritics
59        Grapheme = 0x04,        // Text adjusted to graphemes
60        GraphemeGluster = 0x05, // GlyphCluster & Grapheme
61    };
62
63#ifdef OHOS_SUPPORT
64    enum EllipsisReadStrategy {
65        DEFAULT = 0,            // default
66        READ_REPLACED_WORD = 1,  // read replaced word
67        READ_ELLIPSIS_WORD = 2, // read ellipsis word
68    };
69#endif
70
71    TextLine() = default;
72    TextLine(const TextLine&) = delete;
73    TextLine& operator=(const TextLine&) = delete;
74    TextLine(TextLine&&) = default;
75    TextLine& operator=(TextLine&&) = default;
76    ~TextLine() = default;
77
78    TextLine(ParagraphImpl* owner,
79             SkVector offset,
80             SkVector advance,
81             BlockRange blocks,
82             TextRange textExcludingSpaces,
83             TextRange text,
84             TextRange textIncludingNewlines,
85             ClusterRange clusters,
86             ClusterRange clustersWithGhosts,
87             SkScalar widthWithSpaces,
88             InternalLineMetrics sizes);
89
90    TextRange trimmedText() const { return fTextExcludingSpaces; }
91    TextRange textWithNewlines() const { return fTextIncludingNewlines; }
92    TextRange text() const { return fText; }
93    ClusterRange clusters() const { return fClusterRange; }
94    ClusterRange clustersWithSpaces() const { return fGhostClusterRange; }
95    Run* ellipsis() const { return fEllipsis.get(); }
96    InternalLineMetrics sizes() const { return fSizes; }
97    bool empty() const { return fTextExcludingSpaces.empty(); }
98
99    SkScalar spacesWidth() const { return fWidthWithSpaces - width(); }
100    SkScalar height() const { return fAdvance.fY; }
101    SkScalar width() const {
102        return fAdvance.fX + (fEllipsis != nullptr ? fEllipsis->fAdvance.fX : 0);
103    }
104    SkScalar widthWithoutEllipsis() const { return fAdvance.fX; }
105    SkScalar widthWithEllipsisSpaces() const {
106        return fWidthWithSpaces + (fEllipsis != nullptr ? fEllipsis->fAdvance.fX : 0);
107    }
108    SkVector offset() const;
109    void setLineOffsetX(SkScalar x) {
110        fOffset.set(x, fOffset.y());
111    }
112
113    SkScalar alphabeticBaseline() const { return fSizes.alphabeticBaseline(); }
114    SkScalar ideographicBaseline() const { return fSizes.ideographicBaseline(); }
115    SkScalar baseline() const { return fSizes.baseline(); }
116
117    using RunVisitor = std::function<bool(
118            const Run* run, SkScalar runOffset, TextRange textRange, SkScalar* width)>;
119
120#ifdef OHOS_SUPPORT
121    bool processEllipsisRun(bool& isAlreadyUseEllipsis,
122                            SkScalar& runOffset,
123                            EllipsisReadStrategy ellipsisReadStrategy,
124                            const RunVisitor& visitor,
125                            SkScalar& runWidthInLine) const;
126#endif
127
128#ifdef OHOS_SUPPORT
129    void iterateThroughVisualRuns(EllipsisReadStrategy ellipsisReadStrategy,
130                                  bool includingGhostSpaces,
131                                  const RunVisitor& runVisitor) const;
132#else
133    void iterateThroughVisualRuns(bool includingGhostSpaces, const RunVisitor& runVisitor) const;
134#endif
135    using RunStyleVisitor = std::function<void(
136            TextRange textRange, const TextStyle& style, const ClipContext& context)>;
137    SkScalar iterateThroughSingleRunByStyles(TextAdjustment textAdjustment,
138                                             const Run* run,
139                                             SkScalar runOffset,
140                                             TextRange textRange,
141                                             StyleType styleType,
142                                             const RunStyleVisitor& visitor) const;
143
144    using ClustersVisitor = std::function<bool(const Cluster* cluster, ClusterIndex index, bool ghost)>;
145    void iterateThroughClustersInGlyphsOrder(bool reverse,
146                                             bool includeGhosts,
147                                             const ClustersVisitor& visitor) const;
148
149    void format(TextAlign align, SkScalar maxWidth, EllipsisModal ellipsisModal);
150    SkScalar calculateSpacing(const Cluster prevCluster, const Cluster curCluster);
151    SkScalar autoSpacing();
152    void paint(ParagraphPainter* painter, SkScalar x, SkScalar y);
153    void paint(ParagraphPainter* painter, const RSPath* path, SkScalar hOffset, SkScalar vOffset);
154    void visit(SkScalar x, SkScalar y);
155    void ensureTextBlobCachePopulated();
156    void setParagraphImpl(ParagraphImpl* newpara) { fOwner = newpara; }
157    void setBlockRange(const BlockRange& blockRange) { fBlockRange = blockRange; }
158    void countWord(int& wordCount, bool& inWord);
159    void ellipsisNotFitProcess(EllipsisModal ellipsisModal);
160    void createTailEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr, WordBreakType wordBreakType);
161    void createHeadEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr);
162    // For testing internal structures
163    void scanStyles(StyleType style, const RunStyleVisitor& visitor);
164
165    void setMaxRunMetrics(const InternalLineMetrics& metrics) { fMaxRunMetrics = metrics; }
166    InternalLineMetrics getMaxRunMetrics() const { return fMaxRunMetrics; }
167
168    bool isFirstLine() const;
169    bool isLastLine() const;
170    void getRectsForRange(TextRange textRange,
171                          RectHeightStyle rectHeightStyle,
172                          RectWidthStyle rectWidthStyle,
173                          std::vector<TextBox>& boxes) const;
174    void getRectsForPlaceholders(std::vector<TextBox>& boxes);
175    PositionWithAffinity getGlyphPositionAtCoordinate(SkScalar dx);
176
177    ClipContext measureTextInsideOneRun(TextRange textRange,
178                                        const Run* run,
179                                        SkScalar runOffsetInLine,
180                                        SkScalar textOffsetInRunInLine,
181                                        bool includeGhostSpaces,
182                                        TextAdjustment textAdjustment) const;
183
184    LineMetrics getMetrics() const;
185
186    SkRect extendHeight(const ClipContext& context) const;
187
188    void shiftVertically(SkScalar shift) { fOffset.fY += shift; }
189
190    void setAscentStyle(LineMetricStyle style) { fAscentStyle = style; }
191    void setDescentStyle(LineMetricStyle style) { fDescentStyle = style; }
192
193    bool endsWithHardLineBreak() const;
194    std::unique_ptr<Run> shapeEllipsis(const SkString& ellipsis, const Cluster* cluster);
195    SkSTArray<1, size_t, true> getLineAllRuns() const { return fRunsInVisualOrder; };
196
197    size_t getGlyphCount() const;
198    std::vector<std::unique_ptr<RunBase>> getGlyphRuns() const;
199    TextLine CloneSelf();
200    TextRange getTextRangeReplacedByEllipsis() const { return fTextRangeReplacedByEllipsis; }
201    void setTextBlobCachePopulated(const bool textBlobCachePopulated) {
202        fTextBlobCachePopulated = textBlobCachePopulated;
203    }
204
205#ifdef OHOS_SUPPORT
206    std::unique_ptr<TextLineBase> createTruncatedLine(double width, EllipsisModal ellipsisMode,
207        const std::string& ellipsisStr);
208    double getTypographicBounds(double* ascent, double* descent, double* leading) const;
209    RSRect getImageBounds() const;
210    double getTrailingSpaceWidth() const;
211    int32_t getStringIndexForPosition(SkPoint point) const;
212    double getOffsetForStringIndex(int32_t index) const;
213    std::map<int32_t, double> getIndexAndOffsets(bool& isHardBreak) const;
214    double getAlignmentOffset(double alignmentFactor, double alignmentWidth) const;
215#endif
216
217private:
218    struct RoundRectAttr {
219        int styleId;
220        RectStyle roundRectStyle;
221        SkRect rect;
222    };
223
224    void justify(SkScalar maxWidth);
225
226    void buildTextBlob(TextRange textRange, const TextStyle& style, const ClipContext& context);
227    void paintBackground(ParagraphPainter* painter,
228                         SkScalar x,
229                         SkScalar y,
230                         TextRange textRange,
231                         const TextStyle& style,
232                         const ClipContext& context) const;
233    void paintRoundRect(ParagraphPainter* painter, SkScalar x, SkScalar y, const Run* run) const;
234    void paintShadow(ParagraphPainter* painter,
235                     SkScalar x,
236                     SkScalar y,
237                     TextRange textRange,
238                     const TextStyle& style,
239                     const ClipContext& context) const;
240    void paintDecorations(ParagraphPainter* painter,
241                          SkScalar x,
242                          SkScalar y,
243                          TextRange textRange,
244                          const TextStyle& style,
245                          const ClipContext& context) const;
246
247    void shiftCluster(const Cluster* cluster, SkScalar shift, SkScalar prevShift);
248    void spacingCluster(const Cluster* cluster, SkScalar spacing, SkScalar prevSpacing);
249    bool hasBackgroundRect(const RoundRectAttr& attr);
250    void computeRoundRect(int& index, int& preIndex, std::vector<Run*>& groupRuns, Run* run);
251    void prepareRoundRect();
252    SkScalar calculateThickness(const TextStyle& style, const ClipContext& context);
253#ifdef OHOS_SUPPORT
254    void measureTextWithSpacesAtTheEnd(ClipContext& context, bool includeGhostSpaces) const;
255    void computeNextPaintGlyphRange(ClipContext& context, const TextRange& lastGlyphRange, StyleType styleType) const;
256#endif
257
258    ParagraphImpl* fOwner;
259    BlockRange fBlockRange;
260    TextRange fTextExcludingSpaces;
261    TextRange fText;
262    TextRange fTextIncludingNewlines;
263    ClusterRange fClusterRange;
264    ClusterRange fGhostClusterRange;
265    // Avoid the malloc/free in the common case of one run per line
266    SkSTArray<1, size_t, true> fRunsInVisualOrder;
267    SkVector fAdvance;                  // Text size
268    SkVector fOffset;                   // Text position
269    SkScalar fShift;                    // Let right
270    SkScalar fWidthWithSpaces;
271    std::unique_ptr<Run> fEllipsis;     // In case the line ends with the ellipsis
272    TextRange fTextRangeReplacedByEllipsis;     // text range replaced by ellipsis
273    InternalLineMetrics fSizes;                 // Line metrics as a max of all run metrics and struts
274    InternalLineMetrics fMaxRunMetrics;         // No struts - need it for GetRectForRange(max height)
275    size_t fEllipsisIndex = EMPTY_INDEX;
276
277    bool fHasBackground;
278    bool fHasShadows;
279    bool fHasDecorations;
280    bool fIsArcText;
281    bool fArcTextState;
282    bool fLastClipRunLtr;
283
284    LineMetricStyle fAscentStyle;
285    LineMetricStyle fDescentStyle;
286
287    struct TextBlobRecord {
288        void paint(ParagraphPainter* painter, SkScalar x, SkScalar y);
289        void paint(ParagraphPainter* painter);
290
291#ifndef USE_SKIA_TXT
292        sk_sp<SkTextBlob> fBlob;
293#else
294        std::shared_ptr<RSTextBlob> fBlob;
295#endif
296        SkPoint fOffset = SkPoint::Make(0.0f, 0.0f);
297        ParagraphPainter::SkPaintOrID fPaint;
298        SkRect fBounds = SkRect::MakeEmpty();
299        bool fClippingNeeded = false;
300        SkRect fClipRect = SkRect::MakeEmpty();
301
302        // Extra fields only used for the (experimental) visitor
303        const Run* fVisitor_Run;
304        size_t     fVisitor_Pos;
305        size_t     fVisitor_Size;
306    };
307    bool fTextBlobCachePopulated;
308    DecorationContext fDecorationContext;
309    std::vector<RoundRectAttr> roundRectAttrs = {};
310#ifdef OHOS_SUPPORT
311    bool fIsTextLineEllipsisHeadModal = false;
312#endif
313public:
314    std::vector<TextBlobRecord> fTextBlobCache;
315};
316}  // namespace textlayout
317}  // namespace skia
318
319namespace sknonstd {
320    template <> struct is_bitmask_enum<skia::textlayout::TextLine::TextAdjustment> : std::true_type {};
321}  // namespace sknonstd
322
323#endif  // TextLine_DEFINED
324