| // Copyright 2019 Google LLC | |
| #ifndef TextLine_DEFINED | |
| #define TextLine_DEFINED | |
| #include "include/core/SkPoint.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/core/SkScalar.h" | |
| #include "include/private/SkTArray.h" | |
| #include "modules/skparagraph/include/DartTypes.h" | |
| #include "modules/skparagraph/include/Metrics.h" | |
| #include "modules/skparagraph/include/ParagraphPainter.h" | |
| #include "modules/skparagraph/include/TextStyle.h" | |
| #include "modules/skparagraph/src/Run.h" | |
| #include "src/core/SkBitmaskEnum.h" | |
| #include <stddef.h> | |
| #include <functional> | |
| #include <memory> | |
| #include <vector> | |
| class SkString; | |
| namespace skia { | |
| namespace textlayout { | |
| class ParagraphImpl; | |
| class TextLine { | |
| public: | |
| struct ClipContext { | |
| const Run* run; | |
| size_t pos; | |
| size_t size; | |
| SkScalar fTextShift; // Shifts the text inside the run so it's placed at the right position | |
| SkRect clip; | |
| SkScalar fExcludedTrailingSpaces; | |
| bool clippingNeeded; | |
| }; | |
| enum TextAdjustment { | |
| GlyphCluster = 0x01, // All text producing glyphs pointing to the same ClusterIndex | |
| GlyphemeCluster = 0x02, // base glyph + all attached diacritics | |
| Grapheme = 0x04, // Text adjusted to graphemes | |
| GraphemeGluster = 0x05, // GlyphCluster & Grapheme | |
| }; | |
| TextLine() = default; | |
| TextLine(const TextLine&) = delete; | |
| TextLine& operator=(const TextLine&) = delete; | |
| TextLine(TextLine&&) = default; | |
| TextLine& operator=(TextLine&&) = default; | |
| ~TextLine() = default; | |
| TextLine(ParagraphImpl* owner, | |
| SkVector offset, | |
| SkVector advance, | |
| BlockRange blocks, | |
| TextRange textExcludingSpaces, | |
| TextRange text, | |
| TextRange textIncludingNewlines, | |
| ClusterRange clusters, | |
| ClusterRange clustersWithGhosts, | |
| SkScalar widthWithSpaces, | |
| InternalLineMetrics sizes); | |
| TextRange trimmedText() const { return fTextExcludingSpaces; } | |
| TextRange textWithNewlines() const { return fTextIncludingNewlines; } | |
| TextRange text() const { return fText; } | |
| ClusterRange clusters() const { return fClusterRange; } | |
| ClusterRange clustersWithSpaces() const { return fGhostClusterRange; } | |
| Run* ellipsis() const { return fEllipsis.get(); } | |
| Run* hyphen() const { return fHyphen.get(); } | |
| InternalLineMetrics sizes() const { return fSizes; } | |
| bool empty() const { return fTextExcludingSpaces.empty(); } | |
| SkScalar spacesWidth() const { return fWidthWithSpaces - width(); } | |
| SkScalar height() const { return fAdvance.fY; } | |
| SkScalar width() const { | |
| return fAdvance.fX + (fEllipsis != nullptr ? fEllipsis->fAdvance.fX : 0) + | |
| (fHyphen != nullptr ? fHyphen->fAdvance.fX : 0); | |
| } | |
| // Returns the bare advance of the line's source-text content, excluding both | |
| // line-end affixes: the ellipsis (if any) and the rendered soft hyphen (if any). | |
| // The name is historical -- it predates soft hyphen rendering -- but the value | |
| // is the source-text advance, i.e. width() minus any affix widths. | |
| SkScalar widthWithoutEllipsis() const { return fAdvance.fX; } | |
| SkVector offset() const; | |
| SkScalar alphabeticBaseline() const { return fSizes.alphabeticBaseline(); } | |
| SkScalar ideographicBaseline() const { return fSizes.ideographicBaseline(); } | |
| SkScalar baseline() const { return fSizes.baseline(); } | |
| using RunVisitor = std::function<bool( | |
| const Run* run, SkScalar runOffset, TextRange textRange, SkScalar* width)>; | |
| void iterateThroughVisualRuns(bool includingGhostSpaces, const RunVisitor& runVisitor) const; | |
| using RunStyleVisitor = std::function<void( | |
| TextRange textRange, const TextStyle& style, const ClipContext& context)>; | |
| SkScalar iterateThroughSingleRunByStyles(TextAdjustment textAdjustment, | |
| const Run* run, | |
| SkScalar runOffset, | |
| TextRange textRange, | |
| StyleType styleType, | |
| const RunStyleVisitor& visitor) const; | |
| using ClustersVisitor = std::function<bool(const Cluster* cluster, ClusterIndex index, bool ghost)>; | |
| void iterateThroughClustersInGlyphsOrder(bool reverse, | |
| bool includeGhosts, | |
| const ClustersVisitor& visitor) const; | |
| void format(TextAlign align, SkScalar maxWidth); | |
| void paint(ParagraphPainter* painter, SkScalar x, SkScalar y); | |
| void visit(SkScalar x, SkScalar y); | |
| void ensureTextBlobCachePopulated(); | |
| void createEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr); | |
| void createSoftHyphen(); | |
| // For testing internal structures | |
| void scanStyles(StyleType style, const RunStyleVisitor& visitor); | |
| void setMaxRunMetrics(const InternalLineMetrics& metrics) { fMaxRunMetrics = metrics; } | |
| InternalLineMetrics getMaxRunMetrics() const { return fMaxRunMetrics; } | |
| bool isFirstLine() const; | |
| bool isLastLine() const; | |
| void getRectsForRange(TextRange textRange, | |
| RectHeightStyle rectHeightStyle, | |
| RectWidthStyle rectWidthStyle, | |
| std::vector<TextBox>& boxes) const; | |
| void getRectsForPlaceholders(std::vector<TextBox>& boxes); | |
| PositionWithAffinity getGlyphPositionAtCoordinate(SkScalar dx); | |
| ClipContext measureTextInsideOneRun(TextRange textRange, | |
| const Run* run, | |
| SkScalar runOffsetInLine, | |
| SkScalar textOffsetInRunInLine, | |
| bool includeGhostSpaces, | |
| TextAdjustment textAdjustment) const; | |
| LineMetrics getMetrics() const; | |
| SkRect extendHeight(const ClipContext& context) const; | |
| void shiftVertically(SkScalar shift) { fOffset.fY += shift; } | |
| void setAscentStyle(LineMetricStyle style) { fAscentStyle = style; } | |
| void setDescentStyle(LineMetricStyle style) { fDescentStyle = style; } | |
| bool endsWithHardLineBreak() const; | |
| private: | |
| std::unique_ptr<Run> shapeEllipsis(const SkString& ellipsis, | |
| const Cluster* cluster, | |
| bool isHyphen = false); | |
| void justify(SkScalar maxWidth); | |
| void buildTextBlob(TextRange textRange, const TextStyle& style, const ClipContext& context); | |
| void paintBackground(ParagraphPainter* painter, | |
| SkScalar x, | |
| SkScalar y, | |
| TextRange textRange, | |
| const TextStyle& style, | |
| const ClipContext& context) const; | |
| void paintShadow(ParagraphPainter* painter, | |
| SkScalar x, | |
| SkScalar y, | |
| TextRange textRange, | |
| const TextStyle& style, | |
| const ClipContext& context) const; | |
| void paintDecorations(ParagraphPainter* painter, | |
| SkScalar x, | |
| SkScalar y, | |
| TextRange textRange, | |
| const TextStyle& style, | |
| const ClipContext& context) const; | |
| void shiftCluster(const Cluster* cluster, SkScalar shift, SkScalar prevShift); | |
| ParagraphImpl* fOwner; | |
| BlockRange fBlockRange; | |
| TextRange fTextExcludingSpaces; | |
| TextRange fText; | |
| TextRange fTextIncludingNewlines; | |
| ClusterRange fClusterRange; | |
| ClusterRange fGhostClusterRange; | |
| // Avoid the malloc/free in the common case of one run per line | |
| skia_private::STArray<1, size_t, true> fRunsInVisualOrder; | |
| SkVector fAdvance; // Text size | |
| SkVector fOffset; // Text position | |
| SkScalar fShift; // Let right | |
| SkScalar fWidthWithSpaces; | |
| std::unique_ptr<Run> fEllipsis; // In case the line ends with the ellipsis | |
| std::unique_ptr<Run> fHyphen; // Visible hyphen for soft hyphen line breaks | |
| InternalLineMetrics fSizes; // Line metrics as a max of all run metrics and struts | |
| InternalLineMetrics fMaxRunMetrics; // No struts - need it for GetRectForRange(max height) | |
| bool fHasBackground; | |
| bool fHasShadows; | |
| bool fHasDecorations; | |
| LineMetricStyle fAscentStyle; | |
| LineMetricStyle fDescentStyle; | |
| struct TextBlobRecord { | |
| void paint(ParagraphPainter* painter, SkScalar x, SkScalar y); | |
| sk_sp<SkTextBlob> fBlob; | |
| SkPoint fOffset = SkPoint::Make(0.0f, 0.0f); | |
| ParagraphPainter::SkPaintOrID fPaint; | |
| SkRect fBounds = SkRect::MakeEmpty(); | |
| bool fClippingNeeded = false; | |
| SkRect fClipRect = SkRect::MakeEmpty(); | |
| // Extra fields only used for the (experimental) visitor | |
| const Run* fVisitor_Run; | |
| size_t fVisitor_Pos; | |
| }; | |
| bool fTextBlobCachePopulated; | |
| public: | |
| std::vector<TextBlobRecord> fTextBlobCache; | |
| }; | |
| } // namespace textlayout | |
| } // namespace skia | |
| namespace sknonstd { | |
| template <> struct is_bitmask_enum<skia::textlayout::TextLine::TextAdjustment> : std::true_type {}; | |
| } // namespace sknonstd | |
| #endif // TextLine_DEFINED |