• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "paragraph_txt.h"
18 
19 #include <hb.h>
20 #include <minikin/Layout.h>
21 
22 #include <algorithm>
23 #include <limits>
24 #include <map>
25 #include <numeric>
26 #include <utility>
27 #include <vector>
28 
29 #include "flutter/fml/logging.h"
30 #include "font_collection.h"
31 #include "font_skia.h"
32 #include "minikin/FontLanguageListCache.h"
33 #include "minikin/GraphemeBreak.h"
34 #include "minikin/HbFontCache.h"
35 #include "minikin/LayoutUtils.h"
36 #include "minikin/LineBreaker.h"
37 #include "minikin/MinikinFont.h"
38 #include "third_party/skia/include/core/SkCanvas.h"
39 #include "third_party/skia/include/core/SkFont.h"
40 #include "third_party/skia/include/core/SkFontMetrics.h"
41 #include "third_party/skia/include/core/SkMaskFilter.h"
42 #include "third_party/skia/include/core/SkPaint.h"
43 #include "third_party/skia/include/core/SkTextBlob.h"
44 #include "third_party/skia/include/core/SkTypeface.h"
45 #include "third_party/skia/include/effects/SkDashPathEffect.h"
46 #include "third_party/skia/include/effects/SkDiscretePathEffect.h"
47 #include "unicode/ubidi.h"
48 #include "unicode/utf16.h"
49 
50 namespace txt {
51 namespace {
52 
53 class GlyphTypeface {
54  public:
GlyphTypeface(sk_sp<SkTypeface> typeface,minikin::FontFakery fakery)55   GlyphTypeface(sk_sp<SkTypeface> typeface, minikin::FontFakery fakery)
56       : typeface_(std::move(typeface)),
57         fake_bold_(fakery.isFakeBold()),
58         fake_italic_(fakery.isFakeItalic()) {}
59 
operator ==(GlyphTypeface & other)60   bool operator==(GlyphTypeface& other) {
61     return other.typeface_.get() == typeface_.get() &&
62            other.fake_bold_ == fake_bold_ && other.fake_italic_ == fake_italic_;
63   }
64 
operator !=(GlyphTypeface & other)65   bool operator!=(GlyphTypeface& other) { return !(*this == other); }
66 
apply(SkFont & font)67   void apply(SkFont& font) {
68     font.setTypeface(typeface_);
69     font.setEmbolden(fake_bold_);
70     font.setSkewX(fake_italic_ ? -SK_Scalar1 / 4 : 0);
71   }
72 
73  private:
74   sk_sp<SkTypeface> typeface_;
75   bool fake_bold_;
76   bool fake_italic_;
77 };
78 
GetGlyphTypeface(const minikin::Layout & layout,size_t index)79 GlyphTypeface GetGlyphTypeface(const minikin::Layout& layout, size_t index) {
80   const FontSkia* font = static_cast<const FontSkia*>(layout.getFont(index));
81   return GlyphTypeface(font->GetSkTypeface(), layout.getFakery(index));
82 }
83 
84 // Return ranges of text that have the same typeface in the layout.
GetLayoutTypefaceRuns(const minikin::Layout & layout)85 std::vector<Paragraph::Range<size_t>> GetLayoutTypefaceRuns(
86     const minikin::Layout& layout) {
87   std::vector<Paragraph::Range<size_t>> result;
88   if (layout.nGlyphs() == 0)
89     return result;
90   size_t run_start = 0;
91   GlyphTypeface run_typeface = GetGlyphTypeface(layout, run_start);
92   for (size_t i = 1; i < layout.nGlyphs(); ++i) {
93     GlyphTypeface typeface = GetGlyphTypeface(layout, i);
94     if (typeface != run_typeface) {
95       result.emplace_back(run_start, i);
96       run_start = i;
97       run_typeface = typeface;
98     }
99   }
100   result.emplace_back(run_start, layout.nGlyphs());
101   return result;
102 }
103 
GetWeight(const FontWeight weight)104 int GetWeight(const FontWeight weight) {
105   switch (weight) {
106     case FontWeight::w100:
107       return 1;
108     case FontWeight::w200:
109       return 2;
110     case FontWeight::w300:
111       return 3;
112     case FontWeight::w400:  // Normal.
113       return 4;
114     case FontWeight::w500:
115       return 5;
116     case FontWeight::w600:
117       return 6;
118     case FontWeight::w700:  // Bold.
119       return 7;
120     case FontWeight::w800:
121       return 8;
122     case FontWeight::w900:
123       return 9;
124     default:
125       return -1;
126   }
127 }
128 
GetWeight(const TextStyle & style)129 int GetWeight(const TextStyle& style) {
130   return GetWeight(style.font_weight);
131 }
132 
GetItalic(const TextStyle & style)133 bool GetItalic(const TextStyle& style) {
134   switch (style.font_style) {
135     case FontStyle::italic:
136       return true;
137     case FontStyle::normal:
138     default:
139       return false;
140   }
141 }
142 
GetMinikinFontStyle(const TextStyle & style)143 minikin::FontStyle GetMinikinFontStyle(const TextStyle& style) {
144   uint32_t language_list_id =
145       style.locale.empty()
146           ? minikin::FontLanguageListCache::kEmptyListId
147           : minikin::FontStyle::registerLanguageList(style.locale);
148   return minikin::FontStyle(language_list_id, 0, GetWeight(style),
149                             GetItalic(style));
150 }
151 
GetFontAndMinikinPaint(const TextStyle & style,minikin::FontStyle * font,minikin::MinikinPaint * paint)152 void GetFontAndMinikinPaint(const TextStyle& style,
153                             minikin::FontStyle* font,
154                             minikin::MinikinPaint* paint) {
155   *font = GetMinikinFontStyle(style);
156   paint->size = style.font_size;
157   // Divide by font size so letter spacing is pixels, not proportional to font
158   // size.
159   paint->letterSpacing = style.letter_spacing / style.font_size;
160   paint->wordSpacing = style.word_spacing;
161   paint->scaleX = 1.0f;
162   // Prevent spacing rounding in Minikin. This causes jitter when switching
163   // between same text content with different runs composing it, however, it
164   // also produces more accurate layouts.
165   paint->paintFlags |= minikin::LinearTextFlag;
166   paint->fontFeatureSettings = style.font_features.GetFeatureSettings();
167 }
168 
FindWords(const std::vector<uint16_t> & text,size_t start,size_t end,std::vector<Paragraph::Range<size_t>> * words)169 void FindWords(const std::vector<uint16_t>& text,
170                size_t start,
171                size_t end,
172                std::vector<Paragraph::Range<size_t>>* words) {
173   bool in_word = false;
174   size_t word_start;
175   for (size_t i = start; i < end; ++i) {
176     bool is_space = minikin::isWordSpace(text[i]);
177     if (!in_word && !is_space) {
178       word_start = i;
179       in_word = true;
180     } else if (in_word && is_space) {
181       words->emplace_back(word_start, i);
182       in_word = false;
183     }
184   }
185   if (in_word)
186     words->emplace_back(word_start, end);
187 }
188 
189 }  // namespace
190 
191 static const float kDoubleDecorationSpacing = 3.0f;
192 
GlyphPosition(double x_start,double x_advance,size_t code_unit_index,size_t code_unit_width,size_t cluster)193 ParagraphTxt::GlyphPosition::GlyphPosition(double x_start,
194                                            double x_advance,
195                                            size_t code_unit_index,
196                                            size_t code_unit_width,
197                                            size_t cluster)
198     : code_units(code_unit_index, code_unit_index + code_unit_width),
199       x_pos(x_start, x_start + x_advance),
200       cluster(cluster) {}
201 
Shift(double delta)202 void ParagraphTxt::GlyphPosition::Shift(double delta) {
203   x_pos.Shift(delta);
204 }
205 
GlyphLine(std::vector<GlyphPosition> && p,size_t tcu)206 ParagraphTxt::GlyphLine::GlyphLine(std::vector<GlyphPosition>&& p, size_t tcu)
207     : positions(std::move(p)), total_code_units(tcu) {}
208 
CodeUnitRun(std::vector<GlyphPosition> && p,Range<size_t> cu,Range<double> x,size_t line,const SkFontMetrics & metrics,TextDirection dir,const PlaceholderRun * placeholder)209 ParagraphTxt::CodeUnitRun::CodeUnitRun(std::vector<GlyphPosition>&& p,
210                                        Range<size_t> cu,
211                                        Range<double> x,
212                                        size_t line,
213                                        const SkFontMetrics& metrics,
214                                        TextDirection dir,
215                                        const PlaceholderRun* placeholder)
216     : positions(std::move(p)),
217       code_units(cu),
218       x_pos(x),
219       line_number(line),
220       font_metrics(metrics),
221       direction(dir),
222       placeholder_run(placeholder) {}
223 
Shift(double delta)224 void ParagraphTxt::CodeUnitRun::Shift(double delta) {
225   x_pos.Shift(delta);
226   for (GlyphPosition& position : positions)
227     position.Shift(delta);
228 }
229 
ParagraphTxt()230 ParagraphTxt::ParagraphTxt() {
231   breaker_.setLocale();
232 }
233 
234 ParagraphTxt::~ParagraphTxt() = default;
235 
SetText(std::vector<uint16_t> text,StyledRuns runs)236 void ParagraphTxt::SetText(std::vector<uint16_t> text, StyledRuns runs) {
237   needs_layout_ = true;
238   if (text.size() == 0)
239     return;
240   text_ = std::move(text);
241   runs_ = std::move(runs);
242 }
243 
SetInlinePlaceholders(std::vector<PlaceholderRun> inline_placeholders,std::unordered_set<size_t> obj_replacement_char_indexes)244 void ParagraphTxt::SetInlinePlaceholders(
245     std::vector<PlaceholderRun> inline_placeholders,
246     std::unordered_set<size_t> obj_replacement_char_indexes) {
247   needs_layout_ = true;
248   inline_placeholders_ = std::move(inline_placeholders);
249   obj_replacement_char_indexes_ = std::move(obj_replacement_char_indexes);
250 }
251 
ComputeLineBreaks()252 bool ParagraphTxt::ComputeLineBreaks() {
253   line_ranges_.clear();
254   line_widths_.clear();
255   max_intrinsic_width_ = 0;
256 
257   std::vector<size_t> newline_positions;
258   // Discover and add all hard breaks.
259   for (size_t i = 0; i < text_.size(); ++i) {
260     ULineBreak ulb = static_cast<ULineBreak>(
261         u_getIntPropertyValue(text_[i], UCHAR_LINE_BREAK));
262     if (ulb == U_LB_LINE_FEED || ulb == U_LB_MANDATORY_BREAK)
263       newline_positions.push_back(i);
264   }
265   // Break at the end of the paragraph.
266   newline_positions.push_back(text_.size());
267 
268   // Calculate and add any breaks due to a line being too long.
269   size_t run_index = 0;
270   size_t inline_placeholder_index = 0;
271   for (size_t newline_index = 0; newline_index < newline_positions.size();
272        ++newline_index) {
273     size_t block_start =
274         (newline_index > 0) ? newline_positions[newline_index - 1] + 1 : 0;
275     size_t block_end = newline_positions[newline_index];
276     size_t block_size = block_end - block_start;
277 
278     if (block_size == 0) {
279       line_ranges_.emplace_back(block_start, block_end, block_end,
280                                 block_end + 1, true);
281       line_widths_.push_back(0);
282       continue;
283     }
284 
285     // Setup breaker. We wait to set the line width in order to account for the
286     // widths of the inline placeholders, which are calcualted in the loop over
287     // the runs.
288     breaker_.setLineWidths(0.0f, 0, width_);
289     breaker_.setJustified(paragraph_style_.text_align == TextAlign::justify);
290     breaker_.setStrategy(paragraph_style_.break_strategy);
291     breaker_.setWordBreakType(paragraph_style_.word_break_type);
292     breaker_.resize(block_size);
293     memcpy(breaker_.buffer(), text_.data() + block_start,
294            block_size * sizeof(text_[0]));
295     breaker_.setText();
296     breaker_.setIndents(indents_);
297 
298     // Add the runs that include this line to the LineBreaker.
299     double block_total_width = 0;
300     while (run_index < runs_.size()) {
301       StyledRuns::Run run = runs_.GetRun(run_index);
302       if (run.start >= block_end)
303         break;
304       if (run.end < block_start) {
305         run_index++;
306         continue;
307       }
308 
309       minikin::FontStyle font;
310       minikin::MinikinPaint paint;
311       GetFontAndMinikinPaint(run.style, &font, &paint);
312       std::shared_ptr<minikin::FontCollection> collection =
313           GetMinikinFontCollectionForStyle(run.style);
314       if (collection == nullptr) {
315         FML_LOG(INFO) << "Could not find font collection for families \""
316                       << (run.style.font_families.empty()
317                               ? ""
318                               : run.style.font_families[0])
319                       << "\".";
320         return false;
321       }
322       size_t run_start = std::max(run.start, block_start) - block_start;
323       size_t run_end = std::min(run.end, block_end) - block_start;
324       bool isRtl = (paragraph_style_.text_direction == TextDirection::rtl);
325 
326       // Check if the run is an object replacement character-only run. We should
327       // leave space for inline placeholder and break around it if appropriate.
328       if (run.end - run.start == 1 &&
329           obj_replacement_char_indexes_.count(run.start) != 0 &&
330           text_[run.start] == objReplacementChar &&
331           inline_placeholder_index < inline_placeholders_.size()) {
332         // Is a inline placeholder run.
333         PlaceholderRun placeholder_run =
334             inline_placeholders_[inline_placeholder_index];
335         block_total_width += placeholder_run.width;
336 
337         // Inject custom width into minikin breaker. (Uses LibTxt-minikin
338         // patch).
339         breaker_.setCustomCharWidth(run_start, placeholder_run.width);
340 
341         // Called with nullptr as paint in order to use the custom widths passed
342         // above.
343         breaker_.addStyleRun(nullptr, collection, font, run_start, run_end,
344                              isRtl);
345         inline_placeholder_index++;
346       } else {
347         // Is a regular text run.
348         double run_width = breaker_.addStyleRun(&paint, collection, font,
349                                                 run_start, run_end, isRtl);
350         block_total_width += run_width;
351       }
352 
353       if (run.end > block_end)
354         break;
355       run_index++;
356     }
357     max_intrinsic_width_ = std::max(max_intrinsic_width_, block_total_width);
358 
359     size_t breaks_count = breaker_.computeBreaks();
360     const int* breaks = breaker_.getBreaks();
361     for (size_t i = 0; i < breaks_count; ++i) {
362       size_t break_start = (i > 0) ? breaks[i - 1] : 0;
363       size_t line_start = break_start + block_start;
364       size_t line_end = breaks[i] + block_start;
365       bool hard_break = i == breaks_count - 1;
366       size_t line_end_including_newline =
367           (hard_break && line_end < text_.size()) ? line_end + 1 : line_end;
368       size_t line_end_excluding_whitespace = line_end;
369       while (
370           line_end_excluding_whitespace > line_start &&
371           minikin::isLineEndSpace(text_[line_end_excluding_whitespace - 1])) {
372         line_end_excluding_whitespace--;
373       }
374       line_ranges_.emplace_back(line_start, line_end,
375                                 line_end_excluding_whitespace,
376                                 line_end_including_newline, hard_break);
377       line_widths_.push_back(breaker_.getWidths()[i]);
378     }
379 
380     breaker_.finish();
381   }
382 
383   return true;
384 }
385 
ComputeBidiRuns(std::vector<BidiRun> * result)386 bool ParagraphTxt::ComputeBidiRuns(std::vector<BidiRun>* result) {
387   if (text_.empty())
388     return true;
389 
390   auto ubidi_closer = [](UBiDi* b) { ubidi_close(b); };
391   std::unique_ptr<UBiDi, decltype(ubidi_closer)> bidi(ubidi_open(),
392                                                       ubidi_closer);
393   if (!bidi)
394     return false;
395 
396   UBiDiLevel paraLevel = (paragraph_style_.text_direction == TextDirection::rtl)
397                              ? UBIDI_RTL
398                              : UBIDI_LTR;
399   UErrorCode status = U_ZERO_ERROR;
400   ubidi_setPara(bidi.get(), reinterpret_cast<const UChar*>(text_.data()),
401                 text_.size(), paraLevel, nullptr, &status);
402   if (!U_SUCCESS(status))
403     return false;
404 
405   int32_t bidi_run_count = ubidi_countRuns(bidi.get(), &status);
406   if (!U_SUCCESS(status))
407     return false;
408 
409   // Detect if final trailing run is a single ambiguous whitespace.
410   // We want to bundle the final ambiguous whitespace with the preceding
411   // run in order to maintain logical typing behavior when mixing RTL and LTR
412   // text. We do not want this to be a true ghost run since the contrasting
413   // directionality causes the trailing space to not render at the visual end of
414   // the paragraph.
415   //
416   // This only applies to the final whitespace at the end as other whitespace is
417   // no longer ambiguous when surrounded by additional text.
418   bool has_trailing_whitespace = false;
419   int32_t bidi_run_start, bidi_run_length;
420   if (bidi_run_count > 1) {
421     ubidi_getVisualRun(bidi.get(), bidi_run_count - 1, &bidi_run_start,
422                        &bidi_run_length);
423     if (!U_SUCCESS(status))
424       return false;
425     if (bidi_run_length == 1) {
426       UChar32 last_char;
427       U16_GET(text_.data(), 0, bidi_run_start + bidi_run_length - 1,
428               static_cast<int>(text_.size()), last_char);
429       if (u_hasBinaryProperty(last_char, UCHAR_WHITE_SPACE)) {
430         has_trailing_whitespace = true;
431         bidi_run_count--;
432       }
433     }
434   }
435 
436   // Build a map of styled runs indexed by start position.
437   std::map<size_t, StyledRuns::Run> styled_run_map;
438   for (size_t i = 0; i < runs_.size(); ++i) {
439     StyledRuns::Run run = runs_.GetRun(i);
440     styled_run_map.emplace(std::make_pair(run.start, run));
441   }
442 
443   for (int32_t bidi_run_index = 0; bidi_run_index < bidi_run_count;
444        ++bidi_run_index) {
445     UBiDiDirection direction = ubidi_getVisualRun(
446         bidi.get(), bidi_run_index, &bidi_run_start, &bidi_run_length);
447     if (!U_SUCCESS(status))
448       return false;
449 
450     // Exclude the leading bidi control character if present.
451     UChar32 first_char;
452     U16_GET(text_.data(), 0, bidi_run_start, static_cast<int>(text_.size()),
453             first_char);
454     if (u_hasBinaryProperty(first_char, UCHAR_BIDI_CONTROL)) {
455       bidi_run_start++;
456       bidi_run_length--;
457     }
458     if (bidi_run_length == 0)
459       continue;
460 
461     // Exclude the trailing bidi control character if present.
462     UChar32 last_char;
463     U16_GET(text_.data(), 0, bidi_run_start + bidi_run_length - 1,
464             static_cast<int>(text_.size()), last_char);
465     if (u_hasBinaryProperty(last_char, UCHAR_BIDI_CONTROL)) {
466       bidi_run_length--;
467     }
468     if (bidi_run_length == 0)
469       continue;
470 
471     // Attach the final trailing whitespace as part of this run.
472     if (has_trailing_whitespace && bidi_run_index == bidi_run_count - 1) {
473       bidi_run_length++;
474     }
475 
476     size_t bidi_run_end = bidi_run_start + bidi_run_length;
477     TextDirection text_direction =
478         direction == UBIDI_RTL ? TextDirection::rtl : TextDirection::ltr;
479 
480     // Break this bidi run into chunks based on text style.
481     std::vector<BidiRun> chunks;
482     size_t chunk_start = bidi_run_start;
483     while (chunk_start < bidi_run_end) {
484       auto styled_run_iter = styled_run_map.upper_bound(chunk_start);
485       styled_run_iter--;
486       const StyledRuns::Run& styled_run = styled_run_iter->second;
487       size_t chunk_end = std::min(bidi_run_end, styled_run.end);
488       chunks.emplace_back(chunk_start, chunk_end, text_direction,
489                           styled_run.style);
490       chunk_start = chunk_end;
491     }
492 
493     if (text_direction == TextDirection::ltr) {
494       result->insert(result->end(), chunks.begin(), chunks.end());
495     } else {
496       result->insert(result->end(), chunks.rbegin(), chunks.rend());
497     }
498   }
499 
500   return true;
501 }
502 
IsStrutValid() const503 bool ParagraphTxt::IsStrutValid() const {
504   // Font size must be positive.
505   return (paragraph_style_.strut_enabled &&
506           paragraph_style_.strut_font_size >= 0);
507 }
508 
ComputeStrut(StrutMetrics * strut,SkFont & font)509 void ParagraphTxt::ComputeStrut(StrutMetrics* strut, SkFont& font) {
510   strut->ascent = 0;
511   strut->descent = 0;
512   strut->leading = 0;
513   strut->half_leading = 0;
514   strut->line_height = 0;
515   strut->force_strut = false;
516 
517   if (!IsStrutValid())
518     return;
519 
520   // force_strut makes all lines have exactly the strut metrics, and ignores all
521   // actual metrics. We only force the strut if the strut is non-zero and valid.
522   strut->force_strut = paragraph_style_.force_strut_height;
523   minikin::FontStyle minikin_font_style(
524       0, GetWeight(paragraph_style_.strut_font_weight),
525       paragraph_style_.strut_font_style == FontStyle::italic);
526 
527   std::shared_ptr<minikin::FontCollection> collection =
528       font_collection_->GetMinikinFontCollectionForFamilies(
529           paragraph_style_.strut_font_families, "");
530   if (!collection) {
531     return;
532   }
533   minikin::FakedFont faked_font = collection->baseFontFaked(minikin_font_style);
534 
535   if (faked_font.font != nullptr) {
536     SkString str;
537     static_cast<FontSkia*>(faked_font.font)
538         ->GetSkTypeface()
539         ->getFamilyName(&str);
540     font.setTypeface(static_cast<FontSkia*>(faked_font.font)->GetSkTypeface());
541     font.setSize(paragraph_style_.strut_font_size);
542     SkFontMetrics strut_metrics;
543     font.getMetrics(&strut_metrics);
544 
545     if (paragraph_style_.strut_has_height_override) {
546       double metrics_height = -strut_metrics.fAscent + strut_metrics.fDescent;
547       strut->ascent = (-strut_metrics.fAscent / metrics_height) *
548                       paragraph_style_.strut_height *
549                       paragraph_style_.strut_font_size;
550       strut->descent = (strut_metrics.fDescent / metrics_height) *
551                        paragraph_style_.strut_height *
552                        paragraph_style_.strut_font_size;
553       strut->leading =
554           // Zero leading if there is no user specified strut leading.
555           paragraph_style_.strut_leading < 0
556               ? 0
557               : (paragraph_style_.strut_leading *
558                  paragraph_style_.strut_font_size);
559     } else {
560       strut->ascent = -strut_metrics.fAscent;
561       strut->descent = strut_metrics.fDescent;
562       strut->leading =
563           // Use font's leading if there is no user specified strut leading.
564           paragraph_style_.strut_leading < 0
565               ? strut_metrics.fLeading
566               : (paragraph_style_.strut_leading *
567                  paragraph_style_.strut_font_size);
568     }
569     strut->half_leading = strut->leading / 2;
570     strut->line_height = strut->ascent + strut->descent + strut->leading;
571   }
572 }
573 
ComputePlaceholder(PlaceholderRun * placeholder_run,double & ascent,double & descent)574 void ParagraphTxt::ComputePlaceholder(PlaceholderRun* placeholder_run,
575                                       double& ascent,
576                                       double& descent) {
577   if (placeholder_run != nullptr) {
578     // Calculate how much to shift the ascent and descent to account
579     // for the baseline choice.
580     //
581     // TODO(garyq): implement for various baselines. Currently only
582     // supports for alphabetic and ideographic
583     double baseline_adjustment = 0;
584     switch (placeholder_run->baseline) {
585       case TextBaseline::kAlphabetic: {
586         baseline_adjustment = 0;
587         break;
588       }
589       case TextBaseline::kIdeographic: {
590         baseline_adjustment = -descent / 2;
591         break;
592       }
593     }
594     // Convert the ascent and descent from the font's to the placeholder
595     // rect's.
596     switch (placeholder_run->alignment) {
597       case PlaceholderAlignment::kBaseline: {
598         ascent = baseline_adjustment + placeholder_run->baseline_offset;
599         descent = -baseline_adjustment + placeholder_run->height -
600                   placeholder_run->baseline_offset;
601         break;
602       }
603       case PlaceholderAlignment::kAboveBaseline: {
604         ascent = baseline_adjustment + placeholder_run->height;
605         descent = -baseline_adjustment;
606         break;
607       }
608       case PlaceholderAlignment::kBelowBaseline: {
609         descent = baseline_adjustment + placeholder_run->height;
610         ascent = -baseline_adjustment;
611         break;
612       }
613       case PlaceholderAlignment::kTop: {
614         descent = placeholder_run->height - ascent;
615         break;
616       }
617       case PlaceholderAlignment::kBottom: {
618         ascent = placeholder_run->height - descent;
619         break;
620       }
621       case PlaceholderAlignment::kMiddle: {
622         double mid = (ascent - descent) / 2;
623         ascent = mid + placeholder_run->height / 2;
624         descent = -mid + placeholder_run->height / 2;
625         break;
626       }
627     }
628     placeholder_run->baseline_offset = ascent;
629   }
630 }
631 
632 // Implementation outline:
633 //
634 // -For each line:
635 //   -Compute Bidi runs, convert into line_runs (keeps in-line-range runs, adds
636 //   special runs)
637 //   -For each line_run (runs in the line):
638 //     -Calculate ellipsis
639 //     -Obtain font
640 //     -layout.doLayout(...), genereates glyph blobs
641 //     -For each glyph blob:
642 //       -Convert glyph blobs into pixel metrics/advances
643 //     -Store as paint records (for painting) and code unit runs (for metrics
644 //     and boxes).
645 //   -Apply letter spacing, alignment, justification, etc
646 //   -Calculate line vertical layout (ascent, descent, etc)
647 //   -Store per-line metrics
Layout(double width)648 void ParagraphTxt::Layout(double width) {
649   double rounded_width = floor(width);
650   // Do not allow calling layout multiple times without changing anything.
651   if (!needs_layout_ && rounded_width == width_) {
652     return;
653   }
654 
655   width_ = rounded_width;
656 
657   needs_layout_ = false;
658 
659   if (!ComputeLineBreaks())
660     return;
661 
662   std::vector<BidiRun> bidi_runs;
663   if (!ComputeBidiRuns(&bidi_runs))
664     return;
665 
666   SkFont font;
667   font.setEdging(SkFont::Edging::kAntiAlias);
668   font.setSubpixel(true);
669   font.setHinting(SkFontHinting::kSlight);
670 
671   records_.clear();
672   line_heights_.clear();
673   line_baselines_.clear();
674   glyph_lines_.clear();
675   code_unit_runs_.clear();
676   inline_placeholder_code_unit_runs_.clear();
677   line_max_spacings_.clear();
678   line_max_descent_.clear();
679   line_max_ascent_.clear();
680   max_right_ = FLT_MIN;
681   min_left_ = FLT_MAX;
682 
683   minikin::Layout layout;
684   SkTextBlobBuilder builder;
685   double y_offset = 0;
686   double prev_max_descent = 0;
687   double max_word_width = 0;
688 
689   // Compute strut minimums according to paragraph_style_.
690   ComputeStrut(&strut_, font);
691 
692   // Paragraph bounds tracking.
693   size_t line_limit = std::min(paragraph_style_.max_lines, line_ranges_.size());
694   did_exceed_max_lines_ = (line_ranges_.size() > paragraph_style_.max_lines);
695 
696   size_t placeholder_run_index = 0;
697   for (size_t line_number = 0; line_number < line_limit; ++line_number) {
698     const LineRange& line_range = line_ranges_[line_number];
699 
700     // Break the line into words if justification should be applied.
701     std::vector<Range<size_t>> words;
702     double word_gap_width = 0;
703     size_t word_index = 0;
704     bool justify_line =
705         (paragraph_style_.text_align == TextAlign::justify &&
706          line_number != line_limit - 1 && !line_range.hard_break);
707     FindWords(text_, line_range.start, line_range.end, &words);
708     if (justify_line) {
709       if (words.size() > 1) {
710         word_gap_width =
711             (width_ - line_widths_[line_number]) / (words.size() - 1);
712       }
713     }
714 
715     // Exclude trailing whitespace from justified lines so the last visible
716     // character in the line will be flush with the right margin.
717     size_t line_end_index =
718         (paragraph_style_.effective_align() == TextAlign::right ||
719          paragraph_style_.effective_align() == TextAlign::center ||
720          paragraph_style_.effective_align() == TextAlign::justify)
721             ? line_range.end_excluding_whitespace
722             : line_range.end;
723 
724     // Find the runs comprising this line.
725     std::vector<BidiRun> line_runs;
726     for (const BidiRun& bidi_run : bidi_runs) {
727       // A "ghost" run is a run that does not impact the layout, breaking,
728       // alignment, width, etc but is still "visible" through getRectsForRange.
729       // For example, trailing whitespace on centered text can be scrolled
730       // through with the caret but will not wrap the line.
731       //
732       // Here, we add an additional run for the whitespace, but dont
733       // let it impact metrics. After layout of the whitespace run, we do not
734       // add its width into the x-offset adjustment, effectively nullifying its
735       // impact on the layout.
736       std::unique_ptr<BidiRun> ghost_run = nullptr;
737       if (paragraph_style_.ellipsis.empty() &&
738           line_range.end_excluding_whitespace < line_range.end &&
739           bidi_run.start() <= line_range.end &&
740           bidi_run.end() > line_end_index) {
741         ghost_run = std::make_unique<BidiRun>(
742             std::max(bidi_run.start(), line_end_index),
743             std::min(bidi_run.end(), line_range.end), bidi_run.direction(),
744             bidi_run.style(), true);
745       }
746       // Include the ghost run before normal run if RTL
747       if (bidi_run.direction() == TextDirection::rtl && ghost_run != nullptr) {
748         line_runs.push_back(*ghost_run);
749       }
750       // Emplace a normal line run.
751       if (bidi_run.start() < line_end_index &&
752           bidi_run.end() > line_range.start) {
753         // The run is a placeholder run.
754         if (bidi_run.size() == 1 &&
755             text_[bidi_run.start()] == objReplacementChar &&
756             obj_replacement_char_indexes_.count(bidi_run.start()) != 0 &&
757             placeholder_run_index < inline_placeholders_.size()) {
758           line_runs.emplace_back(std::max(bidi_run.start(), line_range.start),
759                                  std::min(bidi_run.end(), line_end_index),
760                                  bidi_run.direction(), bidi_run.style(),
761                                  inline_placeholders_[placeholder_run_index]);
762           placeholder_run_index++;
763         } else {
764           line_runs.emplace_back(std::max(bidi_run.start(), line_range.start),
765                                  std::min(bidi_run.end(), line_end_index),
766                                  bidi_run.direction(), bidi_run.style());
767         }
768       }
769       // Include the ghost run after normal run if LTR
770       if (bidi_run.direction() == TextDirection::ltr && ghost_run != nullptr) {
771         line_runs.push_back(*ghost_run);
772       }
773     }
774     bool line_runs_all_rtl =
775         line_runs.size() &&
776         std::accumulate(
777             line_runs.begin(), line_runs.end(), true,
778             [](const bool a, const BidiRun& b) { return a && b.is_rtl(); });
779     if (line_runs_all_rtl) {
780       std::reverse(words.begin(), words.end());
781     }
782 
783     std::vector<GlyphPosition> line_glyph_positions;
784     std::vector<CodeUnitRun> line_code_unit_runs;
785     std::vector<CodeUnitRun> line_inline_placeholder_code_unit_runs;
786     double run_x_offset = 0;
787     double justify_x_offset = 0;
788     std::vector<PaintRecord> paint_records;
789 
790     for (auto line_run_it = line_runs.begin(); line_run_it != line_runs.end();
791          ++line_run_it) {
792       const BidiRun& run = *line_run_it;
793       minikin::FontStyle minikin_font;
794       minikin::MinikinPaint minikin_paint;
795       GetFontAndMinikinPaint(run.style(), &minikin_font, &minikin_paint);
796       font.setSize(run.style().font_size);
797 
798       std::shared_ptr<minikin::FontCollection> minikin_font_collection =
799           GetMinikinFontCollectionForStyle(run.style());
800 
801       // Lay out this run.
802       uint16_t* text_ptr = text_.data();
803       size_t text_start = run.start();
804       size_t text_count = run.end() - run.start();
805       size_t text_size = text_.size();
806 
807       // Apply ellipsizing if the run was not completely laid out and this
808       // is the last line (or lines are unlimited).
809       const std::u16string& ellipsis = paragraph_style_.ellipsis;
810       std::vector<uint16_t> ellipsized_text;
811       if (ellipsis.length() && !isinf(width_) && !line_range.hard_break &&
812           line_run_it == line_runs.end() - 1 &&
813           (line_number == line_limit - 1 ||
814            paragraph_style_.unlimited_lines())) {
815         float ellipsis_width = layout.measureText(
816             reinterpret_cast<const uint16_t*>(ellipsis.data()), 0,
817             ellipsis.length(), ellipsis.length(), run.is_rtl(), minikin_font,
818             minikin_paint, minikin_font_collection, nullptr);
819 
820         std::vector<float> text_advances(text_count);
821         float text_width =
822             layout.measureText(text_ptr, text_start, text_count, text_.size(),
823                                run.is_rtl(), minikin_font, minikin_paint,
824                                minikin_font_collection, text_advances.data());
825 
826         // Truncate characters from the text until the ellipsis fits.
827         size_t truncate_count = 0;
828         while (truncate_count < text_count &&
829                run_x_offset + text_width + ellipsis_width > width_) {
830           text_width -= text_advances[text_count - truncate_count - 1];
831           truncate_count++;
832         }
833 
834         ellipsized_text.reserve(text_count - truncate_count +
835                                 ellipsis.length());
836         ellipsized_text.insert(ellipsized_text.begin(),
837                                text_.begin() + run.start(),
838                                text_.begin() + run.end() - truncate_count);
839         ellipsized_text.insert(ellipsized_text.end(), ellipsis.begin(),
840                                ellipsis.end());
841         text_ptr = ellipsized_text.data();
842         text_start = 0;
843         text_count = ellipsized_text.size();
844         text_size = text_count;
845 
846         // If there is no line limit, then skip all lines after the ellipsized
847         // line.
848         if (paragraph_style_.unlimited_lines()) {
849           line_limit = line_number + 1;
850           did_exceed_max_lines_ = true;
851         }
852       }
853 
854       layout.doLayout(text_ptr, text_start, text_count, text_size, run.is_rtl(),
855                       minikin_font, minikin_paint, minikin_font_collection);
856 
857       if (layout.nGlyphs() == 0)
858         continue;
859 
860       // When laying out RTL ghost runs, shift the run_x_offset here by the
861       // advance so that the ghost run is positioned to the left of the first
862       // real run of text in the line. However, since we do not want it to
863       // impact the layout of real text, this advance is subsequently added
864       // back into the run_x_offset after the ghost run positions have been
865       // calcuated and before the next real run of text is laid out, ensuring
866       // later runs are laid out in the same position as if there were no ghost
867       // run.
868       if (run.is_ghost() && run.is_rtl())
869         run_x_offset -= layout.getAdvance();
870 
871       std::vector<float> layout_advances(text_count);
872       layout.getAdvances(layout_advances.data());
873 
874       // Break the layout into blobs that share the same SkPaint parameters.
875       std::vector<Range<size_t>> glyph_blobs = GetLayoutTypefaceRuns(layout);
876 
877       double word_start_position = std::numeric_limits<double>::quiet_NaN();
878 
879       // Build a Skia text blob from each group of glyphs.
880       for (const Range<size_t>& glyph_blob : glyph_blobs) {
881         std::vector<GlyphPosition> glyph_positions;
882 
883         GetGlyphTypeface(layout, glyph_blob.start).apply(font);
884         const SkTextBlobBuilder::RunBuffer& blob_buffer =
885             builder.allocRunPos(font, glyph_blob.end - glyph_blob.start);
886 
887         double justify_x_offset_delta = 0;
888         for (size_t glyph_index = glyph_blob.start;
889              glyph_index < glyph_blob.end;) {
890           size_t cluster_start_glyph_index = glyph_index;
891           uint32_t cluster = layout.getGlyphCluster(cluster_start_glyph_index);
892           double glyph_x_offset;
893           // Add all the glyphs in this cluster to the text blob.
894           do {
895             size_t blob_index = glyph_index - glyph_blob.start;
896             blob_buffer.glyphs[blob_index] = layout.getGlyphId(glyph_index);
897 
898             size_t pos_index = blob_index * 2;
899             blob_buffer.pos[pos_index] =
900                 layout.getX(glyph_index) + justify_x_offset_delta;
901             blob_buffer.pos[pos_index + 1] = layout.getY(glyph_index);
902 
903             if (glyph_index == cluster_start_glyph_index)
904               glyph_x_offset = blob_buffer.pos[pos_index];
905 
906             glyph_index++;
907           } while (glyph_index < glyph_blob.end &&
908                    layout.getGlyphCluster(glyph_index) == cluster);
909 
910           Range<int32_t> glyph_code_units(cluster, 0);
911           std::vector<size_t> grapheme_code_unit_counts;
912           if (run.is_rtl()) {
913             if (cluster_start_glyph_index > 0) {
914               glyph_code_units.end =
915                   layout.getGlyphCluster(cluster_start_glyph_index - 1);
916             } else {
917               glyph_code_units.end = text_count;
918             }
919             grapheme_code_unit_counts.push_back(glyph_code_units.width());
920           } else {
921             if (glyph_index < layout.nGlyphs()) {
922               glyph_code_units.end = layout.getGlyphCluster(glyph_index);
923             } else {
924               glyph_code_units.end = text_count;
925             }
926 
927             // The glyph may be a ligature.  Determine how many graphemes are
928             // joined into this glyph and how many input code units map to
929             // each grapheme.
930             size_t code_unit_count = 1;
931             for (int32_t offset = glyph_code_units.start + 1;
932                  offset < glyph_code_units.end; ++offset) {
933               if (minikin::GraphemeBreak::isGraphemeBreak(
934                       layout_advances.data(), text_ptr, text_start, text_count,
935                       offset)) {
936                 grapheme_code_unit_counts.push_back(code_unit_count);
937                 code_unit_count = 1;
938               } else {
939                 code_unit_count++;
940               }
941             }
942             grapheme_code_unit_counts.push_back(code_unit_count);
943           }
944           float glyph_advance = layout.getCharAdvance(glyph_code_units.start);
945           float grapheme_advance =
946               glyph_advance / grapheme_code_unit_counts.size();
947 
948           glyph_positions.emplace_back(run_x_offset + glyph_x_offset,
949                                        grapheme_advance,
950                                        run.start() + glyph_code_units.start,
951                                        grapheme_code_unit_counts[0], cluster);
952 
953           // Compute positions for the additional graphemes in the ligature.
954           for (size_t i = 1; i < grapheme_code_unit_counts.size(); ++i) {
955             glyph_positions.emplace_back(
956                 glyph_positions.back().x_pos.end, grapheme_advance,
957                 glyph_positions.back().code_units.start +
958                     grapheme_code_unit_counts[i - 1],
959                 grapheme_code_unit_counts[i], cluster);
960           }
961 
962           bool at_word_start = false;
963           bool at_word_end = false;
964           if (word_index < words.size()) {
965             at_word_start =
966                 words[word_index].start == run.start() + glyph_code_units.start;
967             at_word_end =
968                 words[word_index].end == run.start() + glyph_code_units.end;
969             if (line_runs_all_rtl) {
970               std::swap(at_word_start, at_word_end);
971             }
972           }
973 
974           if (at_word_start) {
975             word_start_position = run_x_offset + glyph_x_offset;
976           }
977 
978           if (at_word_end) {
979             if (justify_line) {
980               justify_x_offset_delta += word_gap_width;
981             }
982             word_index++;
983 
984             if (!isnan(word_start_position)) {
985               double word_width =
986                   glyph_positions.back().x_pos.end - word_start_position;
987               max_word_width = std::max(word_width, max_word_width);
988               word_start_position = std::numeric_limits<double>::quiet_NaN();
989             }
990           }
991         }  // for each in glyph_blob
992 
993         if (glyph_positions.empty())
994           continue;
995 
996         SkFontMetrics metrics;
997         font.getMetrics(&metrics);
998         Range<double> record_x_pos(
999             glyph_positions.front().x_pos.start - run_x_offset,
1000             glyph_positions.back().x_pos.end - run_x_offset);
1001         if (run.is_placeholder_run()) {
1002           paint_records.emplace_back(
1003               run.style(), SkPoint::Make(run_x_offset + justify_x_offset, 0),
1004               builder.make(), metrics, line_number, record_x_pos.start,
1005               record_x_pos.start + run.placeholder_run()->width, run.is_ghost(),
1006               run.placeholder_run());
1007           run_x_offset += run.placeholder_run()->width;
1008         } else {
1009           paint_records.emplace_back(
1010               run.style(), SkPoint::Make(run_x_offset + justify_x_offset, 0),
1011               builder.make(), metrics, line_number, record_x_pos.start,
1012               record_x_pos.end, run.is_ghost());
1013         }
1014         justify_x_offset += justify_x_offset_delta;
1015 
1016         line_glyph_positions.insert(line_glyph_positions.end(),
1017                                     glyph_positions.begin(),
1018                                     glyph_positions.end());
1019 
1020         // Add a record of glyph positions sorted by code unit index.
1021         std::vector<GlyphPosition> code_unit_positions(glyph_positions);
1022         std::sort(code_unit_positions.begin(), code_unit_positions.end(),
1023                   [](const GlyphPosition& a, const GlyphPosition& b) {
1024                     return a.code_units.start < b.code_units.start;
1025                   });
1026 
1027         line_code_unit_runs.emplace_back(
1028             std::move(code_unit_positions),
1029             Range<size_t>(run.start(), run.end()),
1030             Range<double>(glyph_positions.front().x_pos.start,
1031                           run.is_placeholder_run()
1032                               ? glyph_positions.back().x_pos.start +
1033                                     run.placeholder_run()->width
1034                               : glyph_positions.back().x_pos.end),
1035             line_number, metrics, run.direction(), run.placeholder_run());
1036         if (run.is_placeholder_run()) {
1037           line_inline_placeholder_code_unit_runs.push_back(
1038               line_code_unit_runs.back());
1039         }
1040 
1041         if (!run.is_ghost()) {
1042           min_left_ = std::min(min_left_, glyph_positions.front().x_pos.start);
1043           max_right_ = std::max(max_right_, glyph_positions.back().x_pos.end);
1044         }
1045       }  // for each in glyph_blobs
1046 
1047       // Do not increase x offset for LTR trailing ghost runs as it should not
1048       // impact the layout of visible glyphs. RTL tailing ghost runs have the
1049       // advance subtracted, so we do add the advance here to reset the
1050       // run_x_offset. We do keep the record though so GetRectsForRange() can
1051       // find metrics for trailing spaces.
1052       if ((!run.is_ghost() || run.is_rtl()) && !run.is_placeholder_run()) {
1053         run_x_offset += layout.getAdvance();
1054       }
1055     }  // for each in line_runs
1056 
1057     // Adjust the glyph positions based on the alignment of the line.
1058     double line_x_offset =
1059         GetLineXOffset(run_x_offset, line_number, line_limit);
1060     if (line_x_offset) {
1061       for (CodeUnitRun& code_unit_run : line_code_unit_runs) {
1062         code_unit_run.Shift(line_x_offset);
1063       }
1064       for (CodeUnitRun& code_unit_run :
1065            line_inline_placeholder_code_unit_runs) {
1066         code_unit_run.Shift(line_x_offset);
1067       }
1068       for (GlyphPosition& position : line_glyph_positions) {
1069         position.Shift(line_x_offset);
1070       }
1071     }
1072 
1073     size_t next_line_start = (line_number < line_ranges_.size() - 1)
1074                                  ? line_ranges_[line_number + 1].start
1075                                  : text_.size();
1076     glyph_lines_.emplace_back(std::move(line_glyph_positions),
1077                               next_line_start - line_range.start);
1078     code_unit_runs_.insert(code_unit_runs_.end(), line_code_unit_runs.begin(),
1079                            line_code_unit_runs.end());
1080     inline_placeholder_code_unit_runs_.insert(
1081         inline_placeholder_code_unit_runs_.end(),
1082         line_inline_placeholder_code_unit_runs.begin(),
1083         line_inline_placeholder_code_unit_runs.end());
1084 
1085     // Calculate the amount to advance in the y direction. This is done by
1086     // computing the maximum ascent and descent with respect to the strut.
1087     double max_ascent = strut_.ascent + strut_.half_leading;
1088     double max_descent = strut_.descent + strut_.half_leading;
1089     double max_unscaled_ascent = 0;
1090     auto update_line_metrics = [&](const SkFontMetrics& metrics,
1091                                    const TextStyle& style,
1092                                    PlaceholderRun* placeholder_run) {
1093       if (!strut_.force_strut) {
1094         double ascent;
1095         double descent;
1096         if (style.has_height_override) {
1097           // Scale the ascent and descent such that the sum of ascent and
1098           // descent is `fontsize * style.height * style.font_size`.
1099           double metrics_height = -metrics.fAscent + metrics.fDescent;
1100           ascent = (-metrics.fAscent / metrics_height) * style.height *
1101                    style.font_size;
1102           descent = (metrics.fDescent / metrics_height) * style.height *
1103                     style.font_size;
1104         } else {
1105           // Use the font-provided ascent, descent, and leading directly.
1106           ascent = (-metrics.fAscent + metrics.fLeading / 2);
1107           descent = (metrics.fDescent + metrics.fLeading / 2);
1108         }
1109         ComputePlaceholder(placeholder_run, ascent, descent);
1110 
1111         max_ascent = std::max(ascent, max_ascent);
1112         max_descent = std::max(descent, max_descent);
1113       }
1114 
1115       max_unscaled_ascent = std::max(placeholder_run == nullptr
1116                                          ? -metrics.fAscent
1117                                          : placeholder_run->baseline_offset,
1118                                      max_unscaled_ascent);
1119     };
1120     for (const PaintRecord& paint_record : paint_records) {
1121       update_line_metrics(paint_record.metrics(), paint_record.style(),
1122                           paint_record.GetPlaceholderRun());
1123     }
1124 
1125     // If no fonts were actually rendered, then compute a baseline based on the
1126     // font of the paragraph style.
1127     if (paint_records.empty()) {
1128       SkFontMetrics metrics;
1129       TextStyle style(paragraph_style_.GetTextStyle());
1130       font.setTypeface(GetDefaultSkiaTypeface(style));
1131       font.setSize(style.font_size);
1132       font.getMetrics(&metrics);
1133       update_line_metrics(metrics, style, nullptr);
1134     }
1135 
1136     // Calculate the baselines. This is only done on the first line.
1137     if (line_number == 0) {
1138       alphabetic_baseline_ = max_ascent;
1139       // TODO(garyq): Ideographic baseline is currently bottom of EM
1140       // box, which is not correct. This should be obtained from metrics.
1141       // Skia currently does not support various baselines.
1142       ideographic_baseline_ = (max_ascent + max_descent);
1143     }
1144 
1145     line_heights_.push_back((line_heights_.empty() ? 0 : line_heights_.back()) +
1146                             round(max_ascent + max_descent));
1147     line_baselines_.push_back(line_heights_.back() - max_descent);
1148     y_offset += round(max_ascent + prev_max_descent);
1149     prev_max_descent = max_descent;
1150 
1151     // The max line spacing and ascent have been multiplied by -1 to make math
1152     // in GetRectsForRange more logical/readable.
1153     line_max_spacings_.push_back(max_ascent);
1154     line_max_descent_.push_back(max_descent);
1155     line_max_ascent_.push_back(max_unscaled_ascent);
1156 
1157     for (PaintRecord& paint_record : paint_records) {
1158       paint_record.SetOffset(
1159           SkPoint::Make(paint_record.offset().x() + line_x_offset, y_offset));
1160       records_.emplace_back(std::move(paint_record));
1161     }
1162   }  // for each line_number
1163 
1164   if (paragraph_style_.max_lines == 1 ||
1165       (paragraph_style_.unlimited_lines() && paragraph_style_.ellipsized())) {
1166     min_intrinsic_width_ = max_intrinsic_width_;
1167   } else {
1168     min_intrinsic_width_ = std::min(max_word_width, max_intrinsic_width_);
1169   }
1170 
1171   std::sort(code_unit_runs_.begin(), code_unit_runs_.end(),
1172             [](const CodeUnitRun& a, const CodeUnitRun& b) {
1173               return a.code_units.start < b.code_units.start;
1174             });
1175 
1176   longest_line_ = max_right_ - min_left_;
1177 }
1178 
GetLineXOffset(double line_total_advance,size_t line_number,size_t line_limit)1179 double ParagraphTxt::GetLineXOffset(double line_total_advance,
1180                                     size_t line_number,
1181                                     size_t line_limit) {
1182   if (isinf(width_))
1183     return 0;
1184 
1185   TextAlign align = paragraph_style_.effective_align();
1186 
1187   if (align == TextAlign::right ||
1188       (align == TextAlign::justify &&
1189        paragraph_style_.text_direction == TextDirection::rtl &&
1190        line_number == line_limit - 1)) {
1191     return width_ - line_total_advance;
1192   } else if (align == TextAlign::center) {
1193     return (width_ - line_total_advance) / 2;
1194   } else {
1195     if (line_number < indents_.size()) {
1196       return indents_[line_number];
1197     } else {
1198       return indents_.size() > 0 ? indents_.back() : 0;
1199     }
1200   }
1201 }
1202 
GetParagraphStyle() const1203 const ParagraphStyle& ParagraphTxt::GetParagraphStyle() const {
1204   return paragraph_style_;
1205 }
1206 
GetAlphabeticBaseline()1207 double ParagraphTxt::GetAlphabeticBaseline() {
1208   // Currently -fAscent
1209   return alphabetic_baseline_;
1210 }
1211 
GetIdeographicBaseline()1212 double ParagraphTxt::GetIdeographicBaseline() {
1213   // TODO(garyq): Currently -fAscent + fUnderlinePosition. Verify this.
1214   return ideographic_baseline_;
1215 }
1216 
GetMaxIntrinsicWidth()1217 double ParagraphTxt::GetMaxIntrinsicWidth() {
1218   return max_intrinsic_width_;
1219 }
1220 
SetIndents(const std::vector<float> & indents)1221 void ParagraphTxt::SetIndents(const std::vector<float>& indents) {
1222   indents_ = indents;
1223 }
1224 
GetMinIntrinsicWidth()1225 double ParagraphTxt::GetMinIntrinsicWidth() {
1226   return min_intrinsic_width_;
1227 }
1228 
TextSize() const1229 size_t ParagraphTxt::TextSize() const {
1230   return text_.size();
1231 }
1232 
GetHeight()1233 double ParagraphTxt::GetHeight() {
1234   return line_heights_.size() ? line_heights_.back() : 0;
1235 }
1236 
GetMaxWidth()1237 double ParagraphTxt::GetMaxWidth() {
1238   return width_;
1239 }
1240 
GetLongestLine()1241 double ParagraphTxt::GetLongestLine() {
1242   return longest_line_;
1243 }
1244 
SetParagraphStyle(const ParagraphStyle & style)1245 void ParagraphTxt::SetParagraphStyle(const ParagraphStyle& style) {
1246   needs_layout_ = true;
1247   paragraph_style_ = style;
1248 }
1249 
SetFontCollection(std::shared_ptr<FontCollection> font_collection)1250 void ParagraphTxt::SetFontCollection(
1251     std::shared_ptr<FontCollection> font_collection) {
1252   font_collection_ = std::move(font_collection);
1253 }
1254 
1255 std::shared_ptr<minikin::FontCollection>
GetMinikinFontCollectionForStyle(const TextStyle & style)1256 ParagraphTxt::GetMinikinFontCollectionForStyle(const TextStyle& style) {
1257   std::string locale;
1258   if (!style.locale.empty()) {
1259     uint32_t language_list_id =
1260         minikin::FontStyle::registerLanguageList(style.locale);
1261     const minikin::FontLanguages& langs =
1262         minikin::FontLanguageListCache::getById(language_list_id);
1263     if (langs.size()) {
1264       locale = langs[0].getString();
1265     }
1266   }
1267 
1268   return font_collection_->GetMinikinFontCollectionForFamilies(
1269       style.font_families, locale);
1270 }
1271 
GetDefaultSkiaTypeface(const TextStyle & style)1272 sk_sp<SkTypeface> ParagraphTxt::GetDefaultSkiaTypeface(const TextStyle& style) {
1273   std::shared_ptr<minikin::FontCollection> collection =
1274       GetMinikinFontCollectionForStyle(style);
1275   if (!collection) {
1276     return nullptr;
1277   }
1278   minikin::FakedFont faked_font =
1279       collection->baseFontFaked(GetMinikinFontStyle(style));
1280   return static_cast<FontSkia*>(faked_font.font)->GetSkTypeface();
1281 }
1282 
1283 // The x,y coordinates will be the very top left corner of the rendered
1284 // paragraph.
Paint(SkCanvas * canvas,double x,double y)1285 void ParagraphTxt::Paint(SkCanvas* canvas, double x, double y) {
1286   SkPoint base_offset = SkPoint::Make(x, y);
1287   SkPaint paint;
1288   // Paint the background first before painting any text to prevent
1289   // potential overlap.
1290   for (const PaintRecord& record : records_) {
1291     PaintBackground(canvas, record, base_offset);
1292   }
1293   for (const PaintRecord& record : records_) {
1294     if (record.style().has_foreground) {
1295       paint = record.style().foreground;
1296     } else {
1297       paint.reset();
1298       paint.setColor(record.style().color);
1299     }
1300     SkPoint offset = base_offset + record.offset();
1301     if (record.GetPlaceholderRun() == nullptr) {
1302       PaintShadow(canvas, record, offset);
1303       canvas->drawTextBlob(record.text(), offset.x(), offset.y(), paint);
1304     }
1305     PaintDecorations(canvas, record, base_offset);
1306   }
1307 }
1308 
PaintDecorations(SkCanvas * canvas,const PaintRecord & record,SkPoint base_offset)1309 void ParagraphTxt::PaintDecorations(SkCanvas* canvas,
1310                                     const PaintRecord& record,
1311                                     SkPoint base_offset) {
1312   if (record.style().decoration == TextDecoration::kNone)
1313     return;
1314 
1315   if (record.isGhost())
1316     return;
1317 
1318   const SkFontMetrics& metrics = record.metrics();
1319   SkPaint paint;
1320   paint.setStyle(SkPaint::kStroke_Style);
1321   if (record.style().decoration_color == SK_ColorTRANSPARENT) {
1322     paint.setColor(record.style().color);
1323   } else {
1324     paint.setColor(record.style().decoration_color);
1325   }
1326   paint.setAntiAlias(true);
1327 
1328   // This is set to 2 for the double line style
1329   int decoration_count = 1;
1330 
1331   // Filled when drawing wavy decorations.
1332   SkPath path;
1333 
1334   double width = record.GetRunWidth();
1335 
1336   SkScalar underline_thickness;
1337   if ((metrics.fFlags &
1338        SkFontMetrics::FontMetricsFlags::kUnderlineThicknessIsValid_Flag) &&
1339       metrics.fUnderlineThickness > 0) {
1340     underline_thickness = metrics.fUnderlineThickness;
1341   } else {
1342     // Backup value if the fUnderlineThickness metric is not available:
1343     // Divide by 14pt as it is the default size.
1344     underline_thickness = record.style().font_size / 14.0f;
1345   }
1346   paint.setStrokeWidth(underline_thickness *
1347                        record.style().decoration_thickness_multiplier);
1348 
1349   SkPoint record_offset = base_offset + record.offset();
1350   SkScalar x = record_offset.x() + record.x_start();
1351   SkScalar y = record_offset.y();
1352 
1353   // Setup the decorations.
1354   switch (record.style().decoration_style) {
1355     case TextDecorationStyle::kSolid: {
1356       break;
1357     }
1358     case TextDecorationStyle::kDouble: {
1359       decoration_count = 2;
1360       break;
1361     }
1362     // Note: the intervals are scaled by the thickness of the line, so it is
1363     // possible to change spacing by changing the decoration_thickness
1364     // property of TextStyle.
1365     case TextDecorationStyle::kDotted: {
1366       // Divide by 14pt as it is the default size.
1367       const float scale = record.style().font_size / 14.0f;
1368       const SkScalar intervals[] = {1.0f * scale, 1.5f * scale, 1.0f * scale,
1369                                     1.5f * scale};
1370       size_t count = sizeof(intervals) / sizeof(intervals[0]);
1371       paint.setPathEffect(SkPathEffect::MakeCompose(
1372           SkDashPathEffect::Make(intervals, count, 0.0f),
1373           SkDiscretePathEffect::Make(0, 0)));
1374       break;
1375     }
1376     // Note: the intervals are scaled by the thickness of the line, so it is
1377     // possible to change spacing by changing the decoration_thickness
1378     // property of TextStyle.
1379     case TextDecorationStyle::kDashed: {
1380       // Divide by 14pt as it is the default size.
1381       const float scale = record.style().font_size / 14.0f;
1382       const SkScalar intervals[] = {4.0f * scale, 2.0f * scale, 4.0f * scale,
1383                                     2.0f * scale};
1384       size_t count = sizeof(intervals) / sizeof(intervals[0]);
1385       paint.setPathEffect(SkPathEffect::MakeCompose(
1386           SkDashPathEffect::Make(intervals, count, 0.0f),
1387           SkDiscretePathEffect::Make(0, 0)));
1388       break;
1389     }
1390     case TextDecorationStyle::kWavy: {
1391       ComputeWavyDecoration(
1392           path, x, y, width,
1393           underline_thickness * record.style().decoration_thickness_multiplier);
1394       break;
1395     }
1396   }
1397 
1398   // Draw the decorations.
1399   // Use a for loop for "kDouble" decoration style
1400   for (int i = 0; i < decoration_count; i++) {
1401     double y_offset = i * underline_thickness * kDoubleDecorationSpacing;
1402     double y_offset_original = y_offset;
1403     // Underline
1404     if (record.style().decoration & TextDecoration::kUnderline) {
1405       y_offset +=
1406           (metrics.fFlags &
1407            SkFontMetrics::FontMetricsFlags::kUnderlinePositionIsValid_Flag)
1408               ? metrics.fUnderlinePosition
1409               : underline_thickness;
1410       if (record.style().decoration_style != TextDecorationStyle::kWavy) {
1411         canvas->drawLine(x, y + y_offset, x + width, y + y_offset, paint);
1412       } else {
1413         SkPath offsetPath = path;
1414         offsetPath.offset(0, y_offset);
1415         canvas->drawPath(offsetPath, paint);
1416       }
1417       y_offset = y_offset_original;
1418     }
1419     // Overline
1420     if (record.style().decoration & TextDecoration::kOverline) {
1421       // We subtract fAscent here because for double overlines, we want the
1422       // second line to be above, not below the first.
1423       y_offset -= metrics.fAscent;
1424       if (record.style().decoration_style != TextDecorationStyle::kWavy) {
1425         canvas->drawLine(x, y - y_offset, x + width, y - y_offset, paint);
1426       } else {
1427         SkPath offsetPath = path;
1428         offsetPath.offset(0, -y_offset);
1429         canvas->drawPath(offsetPath, paint);
1430       }
1431       y_offset = y_offset_original;
1432     }
1433     // Strikethrough
1434     if (record.style().decoration & TextDecoration::kLineThrough) {
1435       if (metrics.fFlags &
1436           SkFontMetrics::FontMetricsFlags::kStrikeoutThicknessIsValid_Flag)
1437         paint.setStrokeWidth(metrics.fStrikeoutThickness *
1438                              record.style().decoration_thickness_multiplier);
1439       // Make sure the double line is "centered" vertically.
1440       y_offset += (decoration_count - 1.0) * underline_thickness *
1441                   kDoubleDecorationSpacing / -2.0;
1442       y_offset +=
1443           (metrics.fFlags &
1444            SkFontMetrics::FontMetricsFlags::kStrikeoutThicknessIsValid_Flag)
1445               ? metrics.fStrikeoutPosition
1446               // Backup value if the strikeoutposition metric is not
1447               // available:
1448               : metrics.fXHeight / -2.0;
1449       if (record.style().decoration_style != TextDecorationStyle::kWavy) {
1450         canvas->drawLine(x, y + y_offset, x + width, y + y_offset, paint);
1451       } else {
1452         SkPath offsetPath = path;
1453         offsetPath.offset(0, y_offset);
1454         canvas->drawPath(offsetPath, paint);
1455       }
1456       y_offset = y_offset_original;
1457     }
1458   }
1459 }
1460 
ComputeWavyDecoration(SkPath & path,double x,double y,double width,double thickness)1461 void ParagraphTxt::ComputeWavyDecoration(SkPath& path,
1462                                          double x,
1463                                          double y,
1464                                          double width,
1465                                          double thickness) {
1466   int wave_count = 0;
1467   double x_start = 0;
1468   // One full wavelength is 4 * thickness.
1469   double quarter = thickness;
1470   path.moveTo(x, y);
1471   double remaining = width;
1472   while (x_start + (quarter * 2) < width) {
1473     path.rQuadTo(quarter, wave_count % 2 == 0 ? -quarter : quarter, quarter * 2,
1474                  0);
1475     x_start += quarter * 2;
1476     remaining = width - x_start;
1477     ++wave_count;
1478   }
1479   // Manually add a final partial quad for the remaining width that do
1480   // not fit nicely into a half-wavelength.
1481   // The following math is based off of quadratic bezier equations:
1482   //
1483   //  * Let P(x) be the equation for the curve.
1484   //  * Let P0 = start, P1 = control point, P2 = end
1485   //  * P(x) = -2x^2 - 2x
1486   //  * P0 = (0, 0)
1487   //  * P1 = 2P(0.5) - 0.5 * P0 - 0.5 * P2
1488   //  * P2 = P(remaining / (wavelength / 2))
1489   //
1490   // Simplified implementation coursesy of @jim-flar at
1491   // https://github.com/flutter/engine/pull/9468#discussion_r297872739
1492   // Unsimplified original version at
1493   // https://github.com/flutter/engine/pull/9468#discussion_r297879129
1494 
1495   double x1 = remaining / 2;
1496   double y1 = remaining / 2 * (wave_count % 2 == 0 ? -1 : 1);
1497   double x2 = remaining;
1498   double y2 = (remaining - remaining * remaining / (quarter * 2)) *
1499               (wave_count % 2 == 0 ? -1 : 1);
1500   path.rQuadTo(x1, y1, x2, y2);
1501 }
1502 
PaintBackground(SkCanvas * canvas,const PaintRecord & record,SkPoint base_offset)1503 void ParagraphTxt::PaintBackground(SkCanvas* canvas,
1504                                    const PaintRecord& record,
1505                                    SkPoint base_offset) {
1506   if (!record.style().has_background)
1507     return;
1508 
1509   const SkFontMetrics& metrics = record.metrics();
1510   SkRect rect(SkRect::MakeLTRB(record.x_start(), metrics.fAscent,
1511                                record.x_end(), metrics.fDescent));
1512   rect.offset(base_offset + record.offset());
1513   canvas->drawRect(rect, record.style().background);
1514 }
1515 
PaintShadow(SkCanvas * canvas,const PaintRecord & record,SkPoint offset)1516 void ParagraphTxt::PaintShadow(SkCanvas* canvas,
1517                                const PaintRecord& record,
1518                                SkPoint offset) {
1519   if (record.style().text_shadows.size() == 0)
1520     return;
1521   for (TextShadow text_shadow : record.style().text_shadows) {
1522     if (!text_shadow.hasShadow()) {
1523       continue;
1524     }
1525 
1526     SkPaint paint;
1527     paint.setColor(text_shadow.color);
1528     if (text_shadow.blur_radius != 0.0) {
1529       paint.setMaskFilter(SkMaskFilter::MakeBlur(
1530           kNormal_SkBlurStyle, text_shadow.blur_radius, false));
1531     }
1532     canvas->drawTextBlob(record.text(), offset.x() + text_shadow.offset.x(),
1533                          offset.y() + text_shadow.offset.y(), paint);
1534   }
1535 }
1536 
GetRectsForRange(size_t start,size_t end,RectHeightStyle rect_height_style,RectWidthStyle rect_width_style)1537 std::vector<Paragraph::TextBox> ParagraphTxt::GetRectsForRange(
1538     size_t start,
1539     size_t end,
1540     RectHeightStyle rect_height_style,
1541     RectWidthStyle rect_width_style) {
1542   // Struct that holds calculated metrics for each line.
1543   struct LineBoxMetrics {
1544     std::vector<Paragraph::TextBox> boxes;
1545     // Per-line metrics for max and min coordinates for left and right boxes.
1546     // These metrics cannot be calculated in layout generically because of
1547     // selections that do not cover the whole line.
1548     SkScalar max_right = FLT_MIN;
1549     SkScalar min_left = FLT_MAX;
1550   };
1551 
1552   std::map<size_t, LineBoxMetrics> line_metrics;
1553   // Text direction of the first line so we can extend the correct side for
1554   // RectWidthStyle::kMax.
1555   TextDirection first_line_dir = TextDirection::ltr;
1556 
1557   // Lines that are actually in the requested range.
1558   size_t max_line = 0;
1559   size_t min_line = INT_MAX;
1560   size_t glyph_length = 0;
1561 
1562   // Generate initial boxes and calculate metrics.
1563   for (const CodeUnitRun& run : code_unit_runs_) {
1564     // Check to see if we are finished.
1565     if (run.code_units.start >= end)
1566       break;
1567     if (run.code_units.end <= start)
1568       continue;
1569 
1570     double baseline = line_baselines_[run.line_number];
1571     SkScalar top = baseline + run.font_metrics.fAscent;
1572     SkScalar bottom = baseline + run.font_metrics.fDescent;
1573 
1574     if (run.placeholder_run !=
1575         nullptr) {  // Use inline placeholder size as height.
1576       top = baseline - run.placeholder_run->baseline_offset;
1577       bottom = baseline + run.placeholder_run->height -
1578                run.placeholder_run->baseline_offset;
1579     }
1580 
1581     max_line = std::max(run.line_number, max_line);
1582     min_line = std::min(run.line_number, min_line);
1583 
1584     // Calculate left and right.
1585     SkScalar left, right;
1586     if (run.code_units.start >= start && run.code_units.end <= end) {
1587       left = run.x_pos.start;
1588       right = run.x_pos.end;
1589     } else {
1590       left = SK_ScalarMax;
1591       right = SK_ScalarMin;
1592       for (const GlyphPosition& gp : run.positions) {
1593         if (gp.code_units.start >= start && gp.code_units.end <= end) {
1594           left = std::min(left, static_cast<SkScalar>(gp.x_pos.start));
1595           right = std::max(right, static_cast<SkScalar>(gp.x_pos.end));
1596         } else if (gp.code_units.end == end) {
1597           // Calculate left and right when we are at
1598           // the last position of a combining character.
1599           glyph_length = (gp.code_units.end - gp.code_units.start) - 1;
1600           if (gp.code_units.start ==
1601               std::max<size_t>(0, (start - glyph_length))) {
1602             left = std::min(left, static_cast<SkScalar>(gp.x_pos.start));
1603             right = std::max(right, static_cast<SkScalar>(gp.x_pos.end));
1604           }
1605         }
1606       }
1607       if (left == SK_ScalarMax || right == SK_ScalarMin)
1608         continue;
1609     }
1610     // Keep track of the min and max horizontal coordinates over all lines. Not
1611     // needed for kTight.
1612     if (rect_width_style == RectWidthStyle::kMax) {
1613       line_metrics[run.line_number].max_right =
1614           std::max(line_metrics[run.line_number].max_right, right);
1615       line_metrics[run.line_number].min_left =
1616           std::min(line_metrics[run.line_number].min_left, left);
1617       if (min_line == run.line_number) {
1618         first_line_dir = run.direction;
1619       }
1620     }
1621     line_metrics[run.line_number].boxes.emplace_back(
1622         SkRect::MakeLTRB(left, top, right, bottom), run.direction);
1623   }
1624 
1625   // Add empty rectangles representing any newline characters within the
1626   // range.
1627   for (size_t line_number = 0; line_number < line_ranges_.size();
1628        ++line_number) {
1629     const LineRange& line = line_ranges_[line_number];
1630     if (line.start >= end)
1631       break;
1632     if (line.end_including_newline <= start)
1633       continue;
1634     if (line_metrics.find(line_number) == line_metrics.end()) {
1635       if (line.end != line.end_including_newline && line.end >= start &&
1636           line.end_including_newline <= end) {
1637         SkScalar x = line_widths_[line_number];
1638         // Move empty box to center if center aligned and is an empty line.
1639         if (x == 0 && !isinf(width_) &&
1640             paragraph_style_.effective_align() == TextAlign::center) {
1641           x = width_ / 2;
1642         }
1643         SkScalar top = (line_number > 0) ? line_heights_[line_number - 1] : 0;
1644         SkScalar bottom = line_heights_[line_number];
1645         line_metrics[line_number].boxes.emplace_back(
1646             SkRect::MakeLTRB(x, top, x, bottom), TextDirection::ltr);
1647       }
1648     }
1649   }
1650 
1651   // "Post-process" metrics and aggregate final rects to return.
1652   std::vector<Paragraph::TextBox> boxes;
1653   for (const auto& kv : line_metrics) {
1654     // Handle rect_width_styles. We skip the last line because not everything is
1655     // selected.
1656     if (rect_width_style == RectWidthStyle::kMax && kv.first != max_line) {
1657       if (line_metrics[kv.first].min_left > min_left_ &&
1658           (kv.first != min_line || first_line_dir == TextDirection::rtl)) {
1659         line_metrics[kv.first].boxes.emplace_back(
1660             SkRect::MakeLTRB(
1661                 min_left_,
1662                 line_baselines_[kv.first] - line_max_ascent_[kv.first],
1663                 line_metrics[kv.first].min_left,
1664                 line_baselines_[kv.first] + line_max_descent_[kv.first]),
1665             TextDirection::rtl);
1666       }
1667       if (line_metrics[kv.first].max_right < max_right_ &&
1668           (kv.first != min_line || first_line_dir == TextDirection::ltr)) {
1669         line_metrics[kv.first].boxes.emplace_back(
1670             SkRect::MakeLTRB(
1671                 line_metrics[kv.first].max_right,
1672                 line_baselines_[kv.first] - line_max_ascent_[kv.first],
1673                 max_right_,
1674                 line_baselines_[kv.first] + line_max_descent_[kv.first]),
1675             TextDirection::ltr);
1676       }
1677     }
1678 
1679     // Handle rect_height_styles. The height metrics used are all positive to
1680     // make the signage clear here.
1681     if (rect_height_style == RectHeightStyle::kTight) {
1682       // Ignore line max height and width and generate tight bounds.
1683       boxes.insert(boxes.end(), kv.second.boxes.begin(), kv.second.boxes.end());
1684     } else if (rect_height_style == RectHeightStyle::kMax) {
1685       for (const Paragraph::TextBox& box : kv.second.boxes) {
1686         boxes.emplace_back(
1687             SkRect::MakeLTRB(
1688                 box.rect.fLeft,
1689                 line_baselines_[kv.first] - line_max_ascent_[kv.first],
1690                 box.rect.fRight,
1691                 line_baselines_[kv.first] + line_max_descent_[kv.first]),
1692             box.direction);
1693       }
1694     } else if (rect_height_style ==
1695                RectHeightStyle::kIncludeLineSpacingMiddle) {
1696       SkScalar adjusted_bottom =
1697           line_baselines_[kv.first] + line_max_descent_[kv.first];
1698       if (kv.first < line_ranges_.size() - 1) {
1699         adjusted_bottom += (line_max_spacings_[kv.first + 1] -
1700                             line_max_ascent_[kv.first + 1]) /
1701                            2;
1702       }
1703       SkScalar adjusted_top =
1704           line_baselines_[kv.first] - line_max_ascent_[kv.first];
1705       if (kv.first != 0) {
1706         adjusted_top -=
1707             (line_max_spacings_[kv.first] - line_max_ascent_[kv.first]) / 2;
1708       }
1709       for (const Paragraph::TextBox& box : kv.second.boxes) {
1710         boxes.emplace_back(SkRect::MakeLTRB(box.rect.fLeft, adjusted_top,
1711                                             box.rect.fRight, adjusted_bottom),
1712                            box.direction);
1713       }
1714     } else if (rect_height_style == RectHeightStyle::kIncludeLineSpacingTop) {
1715       for (const Paragraph::TextBox& box : kv.second.boxes) {
1716         SkScalar adjusted_top =
1717             kv.first == 0
1718                 ? line_baselines_[kv.first] - line_max_ascent_[kv.first]
1719                 : line_baselines_[kv.first] - line_max_spacings_[kv.first];
1720         boxes.emplace_back(
1721             SkRect::MakeLTRB(
1722                 box.rect.fLeft, adjusted_top, box.rect.fRight,
1723                 line_baselines_[kv.first] + line_max_descent_[kv.first]),
1724             box.direction);
1725       }
1726     } else if (rect_height_style ==
1727                RectHeightStyle::kIncludeLineSpacingBottom) {
1728       for (const Paragraph::TextBox& box : kv.second.boxes) {
1729         SkScalar adjusted_bottom =
1730             line_baselines_[kv.first] + line_max_descent_[kv.first];
1731         if (kv.first < line_ranges_.size() - 1) {
1732           adjusted_bottom +=
1733               -line_max_ascent_[kv.first] + line_max_spacings_[kv.first];
1734         }
1735         boxes.emplace_back(SkRect::MakeLTRB(box.rect.fLeft,
1736                                             line_baselines_[kv.first] -
1737                                                 line_max_ascent_[kv.first],
1738                                             box.rect.fRight, adjusted_bottom),
1739                            box.direction);
1740       }
1741     } else if (rect_height_style == RectHeightStyle::kStrut) {
1742       if (IsStrutValid()) {
1743         for (const Paragraph::TextBox& box : kv.second.boxes) {
1744           boxes.emplace_back(
1745               SkRect::MakeLTRB(
1746                   box.rect.fLeft, line_baselines_[kv.first] - strut_.ascent,
1747                   box.rect.fRight, line_baselines_[kv.first] + strut_.descent),
1748               box.direction);
1749         }
1750       } else {
1751         // Fall back to tight bounds if the strut is invalid.
1752         boxes.insert(boxes.end(), kv.second.boxes.begin(),
1753                      kv.second.boxes.end());
1754       }
1755     }
1756   }
1757   return boxes;
1758 }
1759 
GetGlyphPositionAtCoordinate(double dx,double dy)1760 Paragraph::PositionWithAffinity ParagraphTxt::GetGlyphPositionAtCoordinate(
1761     double dx,
1762     double dy) {
1763   if (line_heights_.empty())
1764     return PositionWithAffinity(0, DOWNSTREAM);
1765 
1766   size_t y_index;
1767   for (y_index = 0; y_index < line_heights_.size() - 1; ++y_index) {
1768     if (dy < line_heights_[y_index])
1769       break;
1770   }
1771 
1772   const std::vector<GlyphPosition>& line_glyph_position =
1773       glyph_lines_[y_index].positions;
1774   if (line_glyph_position.empty()) {
1775     int line_start_index =
1776         std::accumulate(glyph_lines_.begin(), glyph_lines_.begin() + y_index, 0,
1777                         [](const int a, const GlyphLine& b) {
1778                           return a + static_cast<int>(b.total_code_units);
1779                         });
1780     return PositionWithAffinity(line_start_index, DOWNSTREAM);
1781   }
1782 
1783   size_t x_index;
1784   const GlyphPosition* gp = nullptr;
1785   const GlyphPosition* gp_cluster = nullptr;
1786   bool is_cluster_corection = false;
1787   for (x_index = 0; x_index < line_glyph_position.size(); ++x_index) {
1788     double glyph_end = (x_index < line_glyph_position.size() - 1)
1789                            ? line_glyph_position[x_index + 1].x_pos.start
1790                            : line_glyph_position[x_index].x_pos.end;
1791     if (gp_cluster == nullptr ||
1792         gp_cluster->cluster != line_glyph_position[x_index].cluster) {
1793       gp_cluster = &line_glyph_position[x_index];
1794     }
1795     if (dx < glyph_end) {
1796       // Check if the glyph position is part of a cluster. If it is,
1797       // we assign the cluster's root GlyphPosition to represent it.
1798       if (gp_cluster->cluster == line_glyph_position[x_index].cluster) {
1799         gp = gp_cluster;
1800         // Detect if the matching GlyphPosition was non-root for the cluster.
1801         if (gp_cluster != &line_glyph_position[x_index]) {
1802           is_cluster_corection = true;
1803         }
1804       } else {
1805         gp = &line_glyph_position[x_index];
1806       }
1807       break;
1808     }
1809   }
1810 
1811   if (gp == nullptr) {
1812     const GlyphPosition& last_glyph = line_glyph_position.back();
1813     return PositionWithAffinity(last_glyph.code_units.end, UPSTREAM);
1814   }
1815 
1816   // Find the direction of the run that contains this glyph.
1817   TextDirection direction = TextDirection::ltr;
1818   for (const CodeUnitRun& run : code_unit_runs_) {
1819     if (gp->code_units.start >= run.code_units.start &&
1820         gp->code_units.end <= run.code_units.end) {
1821       direction = run.direction;
1822       break;
1823     }
1824   }
1825 
1826   double glyph_center = (gp->x_pos.start + gp->x_pos.end) / 2;
1827   // We want to use the root cluster's start when the cluster
1828   // was corrected.
1829   // TODO(garyq): Detect if the position is in the middle of the cluster
1830   // and properly assign the start/end positions.
1831   if ((direction == TextDirection::ltr && dx < glyph_center) ||
1832       (direction == TextDirection::rtl && dx >= glyph_center) ||
1833       is_cluster_corection) {
1834     return PositionWithAffinity(gp->code_units.start, DOWNSTREAM);
1835   } else {
1836     return PositionWithAffinity(gp->code_units.end, UPSTREAM);
1837   }
1838 }
1839 
GetGlyphPositionAtCoordinateWithCluster(double dx,double dy)1840 Paragraph::PositionWithAffinity ParagraphTxt::GetGlyphPositionAtCoordinateWithCluster(
1841     double dx,
1842     double dy) {
1843   if (line_heights_.empty())
1844     return PositionWithAffinity(0, DOWNSTREAM);
1845 
1846   size_t y_index;
1847   for (y_index = 0; y_index < line_heights_.size() - 1; ++y_index) {
1848     if (dy < line_heights_[y_index])
1849       break;
1850   }
1851 
1852   const std::vector<GlyphPosition>& line_glyph_position =
1853       glyph_lines_[y_index].positions;
1854   if (line_glyph_position.empty()) {
1855     int line_start_index =
1856         std::accumulate(glyph_lines_.begin(), glyph_lines_.begin() + y_index, 0,
1857                         [](const int a, const GlyphLine& b) {
1858                           return a + static_cast<int>(b.total_code_units);
1859                         });
1860     return PositionWithAffinity(line_start_index, DOWNSTREAM);
1861   }
1862 
1863   size_t x_index;
1864   const GlyphPosition* gp = nullptr;
1865   for (x_index = 0; x_index < line_glyph_position.size(); ++x_index) {
1866     double glyph_end = (x_index < line_glyph_position.size() - 1)
1867                            ? line_glyph_position[x_index + 1].x_pos.start
1868                            : line_glyph_position[x_index].x_pos.end;
1869     if (dx < glyph_end) {
1870       gp = &line_glyph_position[x_index];
1871       break;
1872     }
1873   }
1874 
1875   if (gp == nullptr) {
1876     gp = &line_glyph_position.back();
1877   }
1878 
1879   bool is_cluster_start_setted = false;
1880   size_t cluster_start = 0;
1881   size_t cluster_end = 0;
1882   for (size_t index = 0; index < line_glyph_position.size(); ++index) {
1883     if (line_glyph_position[index].cluster == gp->cluster) {
1884       if (!is_cluster_start_setted) {
1885         cluster_start = index;
1886         cluster_end = index;
1887         is_cluster_start_setted = true;
1888       } else {
1889         cluster_end = index;
1890       }
1891     }
1892   }
1893 
1894   // Find the direction of the run that contains this glyph.
1895   TextDirection direction = TextDirection::ltr;
1896   for (const CodeUnitRun& run : code_unit_runs_) {
1897     if (gp->code_units.start >= run.code_units.start &&
1898         gp->code_units.end <= run.code_units.end) {
1899       direction = run.direction;
1900       break;
1901     }
1902   }
1903 
1904   double glyph_center = (gp->x_pos.start + gp->x_pos.end) / 2;
1905   if (cluster_start == cluster_end) {
1906     if ((direction == TextDirection::ltr && dx < glyph_center) ||
1907       (direction == TextDirection::rtl && dx >= glyph_center)) {
1908         return PositionWithAffinity(gp->code_units.start, DOWNSTREAM);
1909     } else {
1910       return PositionWithAffinity(gp->code_units.end, UPSTREAM);
1911     }
1912   } else {
1913     glyph_center = (line_glyph_position[cluster_start].x_pos.start + line_glyph_position[cluster_end].x_pos.end) / 2;
1914     if ((direction == TextDirection::ltr && dx < glyph_center) ||
1915       (direction == TextDirection::rtl && dx >= glyph_center)) {
1916         return PositionWithAffinity(line_glyph_position[cluster_start].code_units.start, DOWNSTREAM);
1917     } else {
1918       return PositionWithAffinity(line_glyph_position[cluster_end].code_units.end, UPSTREAM);
1919     }
1920   }
1921 }
1922 
1923 // We don't cache this because since this returns all boxes, it is usually
1924 // unnecessary to call this multiple times in succession.
GetRectsForPlaceholders()1925 std::vector<Paragraph::TextBox> ParagraphTxt::GetRectsForPlaceholders() {
1926   // Struct that holds calculated metrics for each line.
1927   struct LineBoxMetrics {
1928     std::vector<Paragraph::TextBox> boxes;
1929     // Per-line metrics for max and min coordinates for left and right boxes.
1930     // These metrics cannot be calculated in layout generically because of
1931     // selections that do not cover the whole line.
1932     SkScalar max_right = FLT_MIN;
1933     SkScalar min_left = FLT_MAX;
1934   };
1935 
1936   std::vector<Paragraph::TextBox> boxes;
1937 
1938   // Generate initial boxes and calculate metrics.
1939   for (const CodeUnitRun& run : inline_placeholder_code_unit_runs_) {
1940     // Check to see if we are finished.
1941     double baseline = line_baselines_[run.line_number];
1942     SkScalar top = baseline + run.font_metrics.fAscent;
1943     SkScalar bottom = baseline + run.font_metrics.fDescent;
1944 
1945     if (run.placeholder_run !=
1946         nullptr) {  // Use inline placeholder size as height.
1947       top = baseline - run.placeholder_run->baseline_offset;
1948       bottom = baseline + run.placeholder_run->height -
1949                run.placeholder_run->baseline_offset;
1950     }
1951 
1952     // Calculate left and right.
1953     SkScalar left, right;
1954     left = run.x_pos.start;
1955     right = run.x_pos.end;
1956 
1957     boxes.emplace_back(SkRect::MakeLTRB(left, top, right, bottom),
1958                        run.direction);
1959   }
1960   return boxes;
1961 }
1962 
GetWordBoundary(size_t offset)1963 Paragraph::Range<size_t> ParagraphTxt::GetWordBoundary(size_t offset) {
1964   if (text_.size() == 0)
1965     return Range<size_t>(0, 0);
1966 
1967   if (!word_breaker_) {
1968     UErrorCode status = U_ZERO_ERROR;
1969     word_breaker_.reset(
1970         icu::BreakIterator::createWordInstance(icu::Locale(), status));
1971     if (!U_SUCCESS(status))
1972       return Range<size_t>(0, 0);
1973   }
1974 
1975   word_breaker_->setText(icu::UnicodeString(false, text_.data(), text_.size()));
1976 
1977   int32_t prev_boundary = word_breaker_->preceding(offset + 1);
1978   int32_t next_boundary = word_breaker_->next();
1979   if (prev_boundary == icu::BreakIterator::DONE)
1980     prev_boundary = offset;
1981   if (next_boundary == icu::BreakIterator::DONE)
1982     next_boundary = offset;
1983   return Range<size_t>(prev_boundary, next_boundary);
1984 }
1985 
GetLineCount()1986 size_t ParagraphTxt::GetLineCount() {
1987   return line_heights_.size();
1988 }
1989 
DidExceedMaxLines()1990 bool ParagraphTxt::DidExceedMaxLines() {
1991   return did_exceed_max_lines_;
1992 }
1993 
SetDirty(bool dirty)1994 void ParagraphTxt::SetDirty(bool dirty) {
1995   needs_layout_ = dirty;
1996 }
1997 
1998 }  // namespace txt
1999