Flutter Engine
paragraph_txt.h
Go to the documentation of this file.
1 /*
2  * Copyright 2017 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef LIB_TXT_SRC_PARAGRAPH_TXT_H_
18 #define LIB_TXT_SRC_PARAGRAPH_TXT_H_
19 
20 #include <set>
21 #include <utility>
22 #include <vector>
23 
24 #include "flutter/fml/compiler_specific.h"
25 #include "flutter/fml/macros.h"
26 #include "font_collection.h"
27 #include "line_metrics.h"
28 #include "minikin/LineBreaker.h"
29 #include "paint_record.h"
30 #include "paragraph.h"
31 #include "paragraph_style.h"
32 #include "placeholder_run.h"
33 #include "run_metrics.h"
34 #include "styled_runs.h"
35 #include "third_party/googletest/googletest/include/gtest/gtest_prod.h" // nogncheck
36 #include "third_party/skia/include/core/SkFontMetrics.h"
37 #include "third_party/skia/include/core/SkRect.h"
38 #include "utils/LinuxUtils.h"
39 #include "utils/MacUtils.h"
40 #include "utils/WindowsUtils.h"
41 
42 namespace txt {
43 
44 using GlyphID = uint32_t;
45 
46 // Constant with the unicode codepoint for the "Object replacement character".
47 // Used as a stand-in character for Placeholder boxes.
48 const int objReplacementChar = 0xFFFC;
49 // Constant with the unicode codepoint for the "Replacement character". This is
50 // the character that commonly renders as a black diamond with a white question
51 // mark. Used to replace non-placeholder instances of 0xFFFC in the text buffer.
52 const int replacementChar = 0xFFFD;
53 
54 // Paragraph provides Layout, metrics, and painting capabilities for text. Once
55 // a Paragraph is constructed with ParagraphBuilder::Build(), an example basic
56 // workflow can be this:
57 //
58 // std::unique_ptr<Paragraph> paragraph = paragraph_builder.Build();
59 // paragraph->Layout(<somewidthgoeshere>);
60 // paragraph->Paint(<someSkCanvas>, <xpos>, <ypos>);
61 class ParagraphTxt : public Paragraph {
62  public:
63  // Constructor. It is highly recommended to construct a paragraph with a
64  // ParagraphBuilder.
65  ParagraphTxt();
66 
67  virtual ~ParagraphTxt();
68 
69  // Minikin Layout doLayout() and LineBreaker addStyleRun() has an
70  // O(N^2) (according to benchmarks) time complexity where N is the total
71  // number of characters. However, this is not significant for reasonably sized
72  // paragraphs. It is currently recommended to break up very long paragraphs
73  // (10k+ characters) to ensure speedy layout.
74  virtual void Layout(double width) override;
75 
76  virtual void Paint(SkCanvas* canvas, double x, double y) override;
77 
78  // Getter for paragraph_style_.
79  const ParagraphStyle& GetParagraphStyle() const;
80 
81  // Returns the number of characters/unicode characters. AKA text_.size()
82  size_t TextSize() const;
83 
84  double GetHeight() override;
85 
86  double GetMaxWidth() override;
87 
88  double GetLongestLine() override;
89 
90  double GetAlphabeticBaseline() override;
91 
92  double GetIdeographicBaseline() override;
93 
94  double GetMaxIntrinsicWidth() override;
95 
96  // Currently, calculated similarly to as GetLayoutWidth(), however this is not
97  // necessarily 100% correct in all cases.
98  double GetMinIntrinsicWidth() override;
99 
100  std::vector<TextBox> GetRectsForRange(
101  size_t start,
102  size_t end,
103  RectHeightStyle rect_height_style,
104  RectWidthStyle rect_width_style) override;
105 
107  double dy) override;
108 
109  std::vector<Paragraph::TextBox> GetRectsForPlaceholders() override;
110 
111  Range<size_t> GetWordBoundary(size_t offset) override;
112 
113  // Returns the number of lines the paragraph takes up. If the text exceeds the
114  // amount width and maxlines provides, Layout() truncates the extra text from
115  // the layout and this will return the max lines allowed.
116  size_t GetLineCount();
117 
118  bool DidExceedMaxLines() override;
119 
120  // Gets the full vector of LineMetrics which includes detailed data on each
121  // line in the final layout.
122  std::vector<LineMetrics>& GetLineMetrics() override;
123 
124  // Sets the needs_layout_ to dirty. When Layout() is called, a new Layout will
125  // be performed when this is set to true. Can also be used to prevent a new
126  // Layout from being calculated by setting to false.
127  void SetDirty(bool dirty = true);
128 
129  private:
130  friend class ParagraphBuilderTxt;
131  FRIEND_TEST(ParagraphTest, SimpleParagraph);
132  FRIEND_TEST(ParagraphTest, SimpleParagraphSmall);
133  FRIEND_TEST(ParagraphTest, SimpleRedParagraph);
134  FRIEND_TEST(ParagraphTest, RainbowParagraph);
135  FRIEND_TEST(ParagraphTest, DefaultStyleParagraph);
136  FRIEND_TEST(ParagraphTest, BoldParagraph);
137  FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, LeftAlignParagraph);
138  FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, RightAlignParagraph);
139  FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, CenterAlignParagraph);
140  FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, JustifyAlignParagraph);
142  FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, InlinePlaceholderLongestLine);
143  FRIEND_TEST_LINUX_ONLY(ParagraphTest, JustifyRTLNewLine);
144  FRIEND_TEST(ParagraphTest, DecorationsParagraph);
145  FRIEND_TEST(ParagraphTest, ItalicsParagraph);
146  FRIEND_TEST(ParagraphTest, ChineseParagraph);
147  FRIEND_TEST(ParagraphTest, DISABLED_ArabicParagraph);
148  FRIEND_TEST(ParagraphTest, SpacingParagraph);
149  FRIEND_TEST(ParagraphTest, LongWordParagraph);
150  FRIEND_TEST_LINUX_ONLY(ParagraphTest, KernScaleParagraph);
151  FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, NewlineParagraph);
152  FRIEND_TEST_LINUX_ONLY(ParagraphTest, EmojiParagraph);
153  FRIEND_TEST_LINUX_ONLY(ParagraphTest, EmojiMultiLineRectsParagraph);
154  FRIEND_TEST(ParagraphTest, HyphenBreakParagraph);
155  FRIEND_TEST(ParagraphTest, RepeatLayoutParagraph);
156  FRIEND_TEST(ParagraphTest, Ellipsize);
157  FRIEND_TEST(ParagraphTest, UnderlineShiftParagraph);
158  FRIEND_TEST(ParagraphTest, WavyDecorationParagraph);
159  FRIEND_TEST(ParagraphTest, SimpleShadow);
160  FRIEND_TEST(ParagraphTest, ComplexShadow);
161  FRIEND_TEST(ParagraphTest, FontFallbackParagraph);
162  FRIEND_TEST(ParagraphTest, InlinePlaceholder0xFFFCParagraph);
163  FRIEND_TEST(ParagraphTest, FontFeaturesParagraph);
164  FRIEND_TEST(ParagraphTest, GetGlyphPositionAtCoordinateSegfault);
165  FRIEND_TEST(ParagraphTest, KhmerLineBreaker);
166  FRIEND_TEST(ParagraphTest, TextHeightBehaviorRectsParagraph);
167 
168  // Starting data to layout.
169  std::vector<uint16_t> text_;
170  // A vector of PlaceholderRuns, which detail the sizes, positioning and break
171  // behavior of the empty spaces to leave. Each placeholder span corresponds to
172  // a 0xFFFC (object replacement character) in text_, which indicates the
173  // position in the text where the placeholder will occur. There should be an
174  // equal number of 0xFFFC characters and elements in this vector.
175  std::vector<PlaceholderRun> inline_placeholders_;
176  // The indexes of the boxes that correspond to an inline placeholder.
177  std::vector<size_t> inline_placeholder_boxes_;
178  // The indexes of instances of 0xFFFC that correspond to placeholders. This is
179  // necessary since the user may pass in manually entered 0xFFFC values using
180  // AddText().
181  std::unordered_set<size_t> obj_replacement_char_indexes_;
182  StyledRuns runs_;
183  ParagraphStyle paragraph_style_;
184  std::shared_ptr<FontCollection> font_collection_;
185 
186  minikin::LineBreaker breaker_;
187  mutable std::unique_ptr<icu::BreakIterator> word_breaker_;
188 
189  std::vector<LineMetrics> line_metrics_;
190  size_t final_line_count_;
191  std::vector<double> line_widths_;
192 
193  // Stores the result of Layout().
194  std::vector<PaintRecord> records_;
195 
196  bool did_exceed_max_lines_;
197 
198  // Strut metrics of zero will have no effect on the layout.
199  struct StrutMetrics {
200  double ascent = 0; // Positive value to keep signs clear.
201  double descent = 0;
202  double leading = 0;
203  double half_leading = 0;
204  double line_height = 0;
205  bool force_strut = false;
206  };
207 
208  StrutMetrics strut_;
209 
210  // Overall left and right extremes over all lines.
211  double max_right_;
212  double min_left_;
213 
214  class BidiRun {
215  public:
216  // Constructs a BidiRun with is_ghost defaulted to false.
217  BidiRun(size_t s, size_t e, TextDirection d, const TextStyle& st)
218  : start_(s), end_(e), direction_(d), style_(&st), is_ghost_(false) {}
219 
220  // Constructs a BidiRun with a custom is_ghost flag.
221  BidiRun(size_t s,
222  size_t e,
223  TextDirection d,
224  const TextStyle& st,
225  bool is_ghost)
226  : start_(s), end_(e), direction_(d), style_(&st), is_ghost_(is_ghost) {}
227 
228  // Constructs a placeholder bidi run.
229  BidiRun(size_t s,
230  size_t e,
231  TextDirection d,
232  const TextStyle& st,
233  PlaceholderRun& placeholder)
234  : start_(s),
235  end_(e),
236  direction_(d),
237  style_(&st),
238  is_ghost_(false),
239  placeholder_run_(&placeholder) {}
240 
241  size_t start() const { return start_; }
242  size_t end() const { return end_; }
243  size_t size() const { return end_ - start_; }
244  TextDirection direction() const { return direction_; }
245  const TextStyle& style() const { return *style_; }
246  PlaceholderRun* placeholder_run() const { return placeholder_run_; }
247  bool is_rtl() const { return direction_ == TextDirection::rtl; }
248  // Tracks if the run represents trailing whitespace.
249  bool is_ghost() const { return is_ghost_; }
250  bool is_placeholder_run() const { return placeholder_run_ != nullptr; }
251 
252  private:
253  size_t start_, end_;
254  TextDirection direction_;
255  const TextStyle* style_;
256  bool is_ghost_;
257  PlaceholderRun* placeholder_run_ = nullptr;
258  };
259 
260  struct GlyphPosition {
261  Range<size_t> code_units;
262  Range<double> x_pos;
263 
264  GlyphPosition(double x_start,
265  double x_advance,
266  size_t code_unit_index,
267  size_t code_unit_width);
268 
269  void Shift(double delta);
270  };
271 
272  struct GlyphLine {
273  // Glyph positions sorted by x coordinate.
274  const std::vector<GlyphPosition> positions;
275  const size_t total_code_units;
276 
277  GlyphLine(std::vector<GlyphPosition>&& p, size_t tcu);
278  };
279 
280  struct CodeUnitRun {
281  // Glyph positions sorted by code unit index.
282  std::vector<GlyphPosition> positions;
283  Range<size_t> code_units;
284  Range<double> x_pos;
285  size_t line_number;
286  SkFontMetrics font_metrics;
287  const TextStyle* style;
288  TextDirection direction;
289  const PlaceholderRun* placeholder_run;
290 
291  CodeUnitRun(std::vector<GlyphPosition>&& p,
292  Range<size_t> cu,
293  Range<double> x,
294  size_t line,
295  const SkFontMetrics& metrics,
296  const TextStyle& st,
297  TextDirection dir,
298  const PlaceholderRun* placeholder);
299 
300  void Shift(double delta);
301  };
302 
303  // Holds the laid out x positions of each glyph.
304  std::vector<GlyphLine> glyph_lines_;
305 
306  // Holds the positions of each range of code units in the text.
307  // Sorted in code unit index order.
308  std::vector<CodeUnitRun> code_unit_runs_;
309  // Holds the positions of the inline placeholders.
310  std::vector<CodeUnitRun> inline_placeholder_code_unit_runs_;
311 
312  // The max width of the paragraph as provided in the most recent Layout()
313  // call.
314  double width_ = -1.0f;
315  double longest_line_ = -1.0f;
316  double max_intrinsic_width_ = 0;
317  double min_intrinsic_width_ = 0;
318  double alphabetic_baseline_ = FLT_MAX;
319  double ideographic_baseline_ = FLT_MAX;
320 
321  bool needs_layout_ = true;
322 
323  struct WaveCoordinates {
324  double x_start;
325  double y_start;
326  double x_end;
327  double y_end;
328 
329  WaveCoordinates(double x_s, double y_s, double x_e, double y_e)
330  : x_start(x_s), y_start(y_s), x_end(x_e), y_end(y_e) {}
331  };
332 
333  // Passes in the text and Styled Runs. text_ and runs_ will later be passed
334  // into breaker_ in InitBreaker(), which is called in Layout().
335  void SetText(std::vector<uint16_t> text, StyledRuns runs);
336 
337  void SetParagraphStyle(const ParagraphStyle& style);
338 
339  void SetFontCollection(std::shared_ptr<FontCollection> font_collection);
340 
341  void SetInlinePlaceholders(
342  std::vector<PlaceholderRun> inline_placeholders,
343  std::unordered_set<size_t> obj_replacement_char_indexes);
344 
345  // Break the text into lines.
346  bool ComputeLineBreaks();
347 
348  // Break the text into runs based on LTR/RTL text direction.
349  bool ComputeBidiRuns(std::vector<BidiRun>* result);
350 
351  // Calculates and populates strut based on paragraph_style_ strut info.
352  void ComputeStrut(StrutMetrics* strut, SkFont& font);
353 
354  // Adjusts the ascent and descent based on the existence and type of
355  // placeholder. This method sets the proper metrics to achieve the different
356  // PlaceholderAlignment options.
357  void ComputePlaceholder(PlaceholderRun* placeholder_run,
358  double& ascent,
359  double& descent);
360 
361  bool IsStrutValid() const;
362 
363  void UpdateLineMetrics(const SkFontMetrics& metrics,
364  const TextStyle& style,
365  double& max_ascent,
366  double& max_descent,
367  double& max_unscaled_ascent,
368  PlaceholderRun* placeholder_run,
369  size_t line_number,
370  size_t line_limit);
371 
372  // Calculate the starting X offset of a line based on the line's width and
373  // alignment.
374  double GetLineXOffset(double line_total_advance, bool justify_line);
375 
376  // Creates and draws the decorations onto the canvas.
377  void PaintDecorations(SkCanvas* canvas,
378  const PaintRecord& record,
379  SkPoint base_offset);
380 
381  // Computes the beziers for a wavy decoration. The results will be
382  // applied to path.
383  void ComputeWavyDecoration(SkPath& path,
384  double x,
385  double y,
386  double width,
387  double thickness);
388 
389  // Draws the background onto the canvas.
390  void PaintBackground(SkCanvas* canvas,
391  const PaintRecord& record,
392  SkPoint base_offset);
393 
394  // Draws the shadows onto the canvas.
395  void PaintShadow(SkCanvas* canvas, const PaintRecord& record, SkPoint offset);
396 
397  // Obtain a Minikin font collection matching this text style.
398  std::shared_ptr<minikin::FontCollection> GetMinikinFontCollectionForStyle(
399  const TextStyle& style);
400 
401  // Get a default SkTypeface for a text style.
402  sk_sp<SkTypeface> GetDefaultSkiaTypeface(const TextStyle& style);
403 
405 };
406 
407 } // namespace txt
408 
409 #endif // LIB_TXT_SRC_PARAGRAPH_TXT_H_
double GetAlphabeticBaseline() override
DEF_SWITCHES_START snapshot asset path
Definition: switches.h:32
std::vector< Paragraph::TextBox > GetRectsForPlaceholders() override
size_t TextSize() const
double GetMaxWidth() override
virtual ~ParagraphTxt()
uint32_t GlyphID
Definition: paragraph_txt.h:44
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
double GetMinIntrinsicWidth() override
virtual void Layout(double width) override
double GetLongestLine() override
std::vector< TextBox > GetRectsForRange(size_t start, size_t end, RectHeightStyle rect_height_style, RectWidthStyle rect_width_style) override
double GetHeight() override
#define FRIEND_TEST_LINUX_ONLY(SUITE, TEST_NAME)
Definition: LinuxUtils.h:38
const ParagraphStyle & GetParagraphStyle() const
const int objReplacementChar
Definition: paragraph_txt.h:48
#define FRIEND_TEST_WINDOWS_DISABLED(SUITE, TEST_NAME)
Definition: WindowsUtils.h:59
int32_t width
bool DidExceedMaxLines() override
double GetIdeographicBaseline() override
double GetMaxIntrinsicWidth() override
std::vector< LineMetrics > & GetLineMetrics() override
virtual void Paint(SkCanvas *canvas, double x, double y) override
PositionWithAffinity GetGlyphPositionAtCoordinate(double dx, double dy) override
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: macros.h:27
Range< size_t > GetWordBoundary(size_t offset) override
void SetDirty(bool dirty=true)
const int replacementChar
Definition: paragraph_txt.h:52