Reland "Track detailed LibTxt metrics with LineMetrics(#10127)" (flutter/engine#11064)

This commit is contained in:
Gary Qian 2019-08-22 16:00:45 -07:00 committed by GitHub
parent c5cac036ad
commit 3ec6774cea
8 changed files with 706 additions and 145 deletions

View File

@ -1142,6 +1142,7 @@ FILE: ../../../flutter/third_party/txt/src/txt/font_skia.cc
FILE: ../../../flutter/third_party/txt/src/txt/font_skia.h
FILE: ../../../flutter/third_party/txt/src/txt/font_style.h
FILE: ../../../flutter/third_party/txt/src/txt/font_weight.h
FILE: ../../../flutter/third_party/txt/src/txt/line_metrics.h
FILE: ../../../flutter/third_party/txt/src/txt/paint_record.cc
FILE: ../../../flutter/third_party/txt/src/txt/paint_record.h
FILE: ../../../flutter/third_party/txt/src/txt/paragraph.h
@ -1155,6 +1156,7 @@ FILE: ../../../flutter/third_party/txt/src/txt/paragraph_txt.cc
FILE: ../../../flutter/third_party/txt/src/txt/paragraph_txt.h
FILE: ../../../flutter/third_party/txt/src/txt/placeholder_run.cc
FILE: ../../../flutter/third_party/txt/src/txt/placeholder_run.h
FILE: ../../../flutter/third_party/txt/src/txt/run_metrics.h
FILE: ../../../flutter/third_party/txt/src/txt/styled_runs.cc
FILE: ../../../flutter/third_party/txt/src/txt/styled_runs.h
FILE: ../../../flutter/third_party/txt/src/txt/test_font_manager.cc

View File

@ -87,6 +87,7 @@ source_set("txt") {
"src/txt/font_skia.h",
"src/txt/font_style.h",
"src/txt/font_weight.h",
"src/txt/line_metrics.h",
"src/txt/paint_record.cc",
"src/txt/paint_record.h",
"src/txt/paragraph.h",
@ -101,6 +102,7 @@ source_set("txt") {
"src/txt/placeholder_run.cc",
"src/txt/placeholder_run.h",
"src/txt/platform.h",
"src/txt/run_metrics.h",
"src/txt/styled_runs.cc",
"src/txt/styled_runs.h",
"src/txt/test_font_manager.cc",

View File

@ -0,0 +1,84 @@
/*
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef LIB_TXT_SRC_LINE_METRICS_H_
#define LIB_TXT_SRC_LINE_METRICS_H_
#include <map>
#include <vector>
#include "run_metrics.h"
namespace txt {
class LineMetrics {
public:
// The following fields are used in the layout process itself.
// The indexes in the text buffer the line begins and ends.
size_t start_index = 0;
size_t end_index = 0;
size_t end_excluding_whitespace = 0;
size_t end_including_newline = 0;
bool hard_break = false;
// The following fields are tracked after or during layout to provide to
// the user as well as for computing bounding boxes.
// The final computed ascent and descent for the line. This can be impacted by
// the strut, height, scaling, as well as outlying runs that are very tall.
//
// The top edge is `baseline - ascent` and the bottom edge is `baseline +
// descent`. Ascent and descent are provided as positive numbers. Raw numbers
// for specific runs of text can be obtained in run_metrics_map. These values
// are the cumulative metrics for the entire line.
double ascent = 0.0;
double descent = 0.0;
double unscaled_ascent = 0.0;
// Height of the line.
double height = 0.0;
// Width of the line.
double width = 0.0;
// The left edge of the line. The right edge can be obtained with `left +
// width`
double left = 0.0;
// The y position of the baseline for this line from the top of the paragraph.
double baseline = 0.0;
// Zero indexed line number.
size_t line_number = 0;
// Mapping between text index ranges and the FontMetrics associated with
// them. The first run will be keyed under start_index. The metrics here
// are before layout and are the base values we calculate from.
std::map<size_t, RunMetrics> run_metrics;
LineMetrics();
LineMetrics(size_t start,
size_t end,
size_t end_excluding_whitespace,
size_t end_including_newline,
bool hard_break)
: start_index(start),
end_index(end),
end_excluding_whitespace(end_excluding_whitespace),
end_including_newline(end_including_newline),
hard_break(hard_break) {}
};
} // namespace txt
#endif // LIB_TXT_SRC_LINE_METRICS_H_

View File

@ -1,3 +1,4 @@
/*
* Copyright 2017 Google Inc.
*
@ -17,6 +18,7 @@
#ifndef LIB_TXT_SRC_PARAGRAPH_H_
#define LIB_TXT_SRC_PARAGRAPH_H_
#include "line_metrics.h"
#include "paragraph_style.h"
class SkCanvas;
@ -171,6 +173,8 @@ class Paragraph {
// Finds the first and last glyphs that define a word containing the glyph at
// index offset.
virtual Range<size_t> GetWordBoundary(size_t offset) = 0;
virtual std::vector<LineMetrics>& GetLineMetrics() = 0;
};
} // namespace txt

View File

@ -211,6 +211,7 @@ ParagraphTxt::CodeUnitRun::CodeUnitRun(std::vector<GlyphPosition>&& p,
Range<double> x,
size_t line,
const SkFontMetrics& metrics,
const TextStyle& st,
TextDirection dir,
const PlaceholderRun* placeholder)
: positions(std::move(p)),
@ -218,6 +219,7 @@ ParagraphTxt::CodeUnitRun::CodeUnitRun(std::vector<GlyphPosition>&& p,
x_pos(x),
line_number(line),
font_metrics(metrics),
style(&st),
direction(dir),
placeholder_run(placeholder) {}
@ -250,7 +252,7 @@ void ParagraphTxt::SetInlinePlaceholders(
}
bool ParagraphTxt::ComputeLineBreaks() {
line_ranges_.clear();
line_metrics_.clear();
line_widths_.clear();
max_intrinsic_width_ = 0;
@ -276,8 +278,8 @@ bool ParagraphTxt::ComputeLineBreaks() {
size_t block_size = block_end - block_start;
if (block_size == 0) {
line_ranges_.emplace_back(block_start, block_end, block_end,
block_end + 1, true);
line_metrics_.emplace_back(block_start, block_end, block_end,
block_end + 1, true);
line_widths_.push_back(0);
continue;
}
@ -369,9 +371,9 @@ bool ParagraphTxt::ComputeLineBreaks() {
minikin::isLineEndSpace(text_[line_end_excluding_whitespace - 1])) {
line_end_excluding_whitespace--;
}
line_ranges_.emplace_back(line_start, line_end,
line_end_excluding_whitespace,
line_end_including_newline, hard_break);
line_metrics_.emplace_back(line_start, line_end,
line_end_excluding_whitespace,
line_end_including_newline, hard_break);
line_widths_.push_back(breaker_.getWidths()[i]);
}
@ -654,6 +656,14 @@ void ParagraphTxt::Layout(double width) {
needs_layout_ = false;
records_.clear();
glyph_lines_.clear();
code_unit_runs_.clear();
inline_placeholder_code_unit_runs_.clear();
max_right_ = FLT_MIN;
min_left_ = FLT_MAX;
final_line_count_ = 0;
if (!ComputeLineBreaks())
return;
@ -666,18 +676,6 @@ void ParagraphTxt::Layout(double width) {
font.setSubpixel(true);
font.setHinting(SkFontHinting::kSlight);
records_.clear();
line_heights_.clear();
line_baselines_.clear();
glyph_lines_.clear();
code_unit_runs_.clear();
inline_placeholder_code_unit_runs_.clear();
line_max_spacings_.clear();
line_max_descent_.clear();
line_max_ascent_.clear();
max_right_ = FLT_MIN;
min_left_ = FLT_MAX;
minikin::Layout layout;
SkTextBlobBuilder builder;
double y_offset = 0;
@ -688,12 +686,13 @@ void ParagraphTxt::Layout(double width) {
ComputeStrut(&strut_, font);
// Paragraph bounds tracking.
size_t line_limit = std::min(paragraph_style_.max_lines, line_ranges_.size());
did_exceed_max_lines_ = (line_ranges_.size() > paragraph_style_.max_lines);
size_t line_limit =
std::min(paragraph_style_.max_lines, line_metrics_.size());
did_exceed_max_lines_ = (line_metrics_.size() > paragraph_style_.max_lines);
size_t placeholder_run_index = 0;
for (size_t line_number = 0; line_number < line_limit; ++line_number) {
const LineRange& line_range = line_ranges_[line_number];
LineMetrics& line_metrics = line_metrics_[line_number];
// Break the line into words if justification should be applied.
std::vector<Range<size_t>> words;
@ -701,8 +700,8 @@ void ParagraphTxt::Layout(double width) {
size_t word_index = 0;
bool justify_line =
(paragraph_style_.text_align == TextAlign::justify &&
line_number != line_limit - 1 && !line_range.hard_break);
FindWords(text_, line_range.start, line_range.end, &words);
line_number != line_limit - 1 && !line_metrics.hard_break);
FindWords(text_, line_metrics.start_index, line_metrics.end_index, &words);
if (justify_line) {
if (words.size() > 1) {
word_gap_width =
@ -716,8 +715,8 @@ void ParagraphTxt::Layout(double width) {
(paragraph_style_.effective_align() == TextAlign::right ||
paragraph_style_.effective_align() == TextAlign::center ||
paragraph_style_.effective_align() == TextAlign::justify)
? line_range.end_excluding_whitespace
: line_range.end;
? line_metrics.end_excluding_whitespace
: line_metrics.end_index;
// Find the runs comprising this line.
std::vector<BidiRun> line_runs;
@ -733,13 +732,13 @@ void ParagraphTxt::Layout(double width) {
// impact on the layout.
std::unique_ptr<BidiRun> ghost_run = nullptr;
if (paragraph_style_.ellipsis.empty() &&
line_range.end_excluding_whitespace < line_range.end &&
bidi_run.start() <= line_range.end &&
line_metrics.end_excluding_whitespace < line_metrics.end_index &&
bidi_run.start() <= line_metrics.end_index &&
bidi_run.end() > line_end_index) {
ghost_run = std::make_unique<BidiRun>(
std::max(bidi_run.start(), line_end_index),
std::min(bidi_run.end(), line_range.end), bidi_run.direction(),
bidi_run.style(), true);
std::min(bidi_run.end(), line_metrics.end_index),
bidi_run.direction(), bidi_run.style(), true);
}
// Include the ghost run before normal run if RTL
if (bidi_run.direction() == TextDirection::rtl && ghost_run != nullptr) {
@ -747,21 +746,22 @@ void ParagraphTxt::Layout(double width) {
}
// Emplace a normal line run.
if (bidi_run.start() < line_end_index &&
bidi_run.end() > line_range.start) {
bidi_run.end() > line_metrics.start_index) {
// The run is a placeholder run.
if (bidi_run.size() == 1 &&
text_[bidi_run.start()] == objReplacementChar &&
obj_replacement_char_indexes_.count(bidi_run.start()) != 0 &&
placeholder_run_index < inline_placeholders_.size()) {
line_runs.emplace_back(std::max(bidi_run.start(), line_range.start),
std::min(bidi_run.end(), line_end_index),
bidi_run.direction(), bidi_run.style(),
inline_placeholders_[placeholder_run_index]);
line_runs.emplace_back(
std::max(bidi_run.start(), line_metrics.start_index),
std::min(bidi_run.end(), line_end_index), bidi_run.direction(),
bidi_run.style(), inline_placeholders_[placeholder_run_index]);
placeholder_run_index++;
} else {
line_runs.emplace_back(std::max(bidi_run.start(), line_range.start),
std::min(bidi_run.end(), line_end_index),
bidi_run.direction(), bidi_run.style());
line_runs.emplace_back(
std::max(bidi_run.start(), line_metrics.start_index),
std::min(bidi_run.end(), line_end_index), bidi_run.direction(),
bidi_run.style());
}
}
// Include the ghost run after normal run if LTR
@ -781,6 +781,7 @@ void ParagraphTxt::Layout(double width) {
std::vector<GlyphPosition> line_glyph_positions;
std::vector<CodeUnitRun> line_code_unit_runs;
std::vector<CodeUnitRun> line_inline_placeholder_code_unit_runs;
double run_x_offset = 0;
double justify_x_offset = 0;
std::vector<PaintRecord> paint_records;
@ -806,7 +807,7 @@ void ParagraphTxt::Layout(double width) {
// is the last line (or lines are unlimited).
const std::u16string& ellipsis = paragraph_style_.ellipsis;
std::vector<uint16_t> ellipsized_text;
if (ellipsis.length() && !isinf(width_) && !line_range.hard_break &&
if (ellipsis.length() && !isinf(width_) && !line_metrics.hard_break &&
line_run_it == line_runs.end() - 1 &&
(line_number == line_limit - 1 ||
paragraph_style_.unlimited_lines())) {
@ -991,22 +992,30 @@ void ParagraphTxt::Layout(double width) {
if (glyph_positions.empty())
continue;
SkFontMetrics metrics;
font.getMetrics(&metrics);
// Store the font metrics and TextStyle in the LineMetrics for this line
// to provide metrics upon user request. We index this RunMetrics
// instance at `run.end() - 1` to allow map::lower_bound to access the
// correct RunMetrics at any text index.
size_t run_key = run.end() - 1;
line_metrics.run_metrics.emplace(run_key, &run.style());
SkFontMetrics* metrics =
&line_metrics.run_metrics.at(run_key).GetFontMetrics();
font.getMetrics(metrics);
Range<double> record_x_pos(
glyph_positions.front().x_pos.start - run_x_offset,
glyph_positions.back().x_pos.end - run_x_offset);
if (run.is_placeholder_run()) {
paint_records.emplace_back(
run.style(), SkPoint::Make(run_x_offset + justify_x_offset, 0),
builder.make(), metrics, line_number, record_x_pos.start,
builder.make(), *metrics, line_number, record_x_pos.start,
record_x_pos.start + run.placeholder_run()->width, run.is_ghost(),
run.placeholder_run());
run_x_offset += run.placeholder_run()->width;
} else {
paint_records.emplace_back(
run.style(), SkPoint::Make(run_x_offset + justify_x_offset, 0),
builder.make(), metrics, line_number, record_x_pos.start,
builder.make(), *metrics, line_number, record_x_pos.start,
record_x_pos.end, run.is_ghost());
}
justify_x_offset += justify_x_offset_delta;
@ -1030,7 +1039,9 @@ void ParagraphTxt::Layout(double width) {
? glyph_positions.back().x_pos.start +
run.placeholder_run()->width
: glyph_positions.back().x_pos.end),
line_number, metrics, run.direction(), run.placeholder_run());
line_number, *metrics, run.style(), run.direction(),
run.placeholder_run());
if (run.is_placeholder_run()) {
line_inline_placeholder_code_unit_runs.push_back(
line_code_unit_runs.back());
@ -1068,11 +1079,11 @@ void ParagraphTxt::Layout(double width) {
}
}
size_t next_line_start = (line_number < line_ranges_.size() - 1)
? line_ranges_[line_number + 1].start
size_t next_line_start = (line_number < line_metrics_.size() - 1)
? line_metrics_[line_number + 1].start_index
: text_.size();
glyph_lines_.emplace_back(std::move(line_glyph_positions),
next_line_start - line_range.start);
next_line_start - line_metrics.start_index);
code_unit_runs_.insert(code_unit_runs_.end(), line_code_unit_runs.begin(),
line_code_unit_runs.end());
inline_placeholder_code_unit_runs_.insert(
@ -1140,17 +1151,22 @@ void ParagraphTxt::Layout(double width) {
ideographic_baseline_ = (max_ascent + max_descent);
}
line_heights_.push_back((line_heights_.empty() ? 0 : line_heights_.back()) +
round(max_ascent + max_descent));
line_baselines_.push_back(line_heights_.back() - max_descent);
line_metrics.height =
(line_number == 0 ? 0 : line_metrics_[line_number - 1].height) +
round(max_ascent + max_descent);
line_metrics.baseline = line_metrics.height - max_descent;
y_offset += round(max_ascent + prev_max_descent);
prev_max_descent = max_descent;
// The max line spacing and ascent have been multiplied by -1 to make math
// in GetRectsForRange more logical/readable.
line_max_spacings_.push_back(max_ascent);
line_max_descent_.push_back(max_descent);
line_max_ascent_.push_back(max_unscaled_ascent);
line_metrics.line_number = line_number;
line_metrics.ascent = max_ascent;
line_metrics.descent = max_descent;
line_metrics.unscaled_ascent = max_unscaled_ascent;
line_metrics.width = line_widths_[line_number];
line_metrics.left = line_x_offset;
final_line_count_++;
for (PaintRecord& paint_record : paint_records) {
paint_record.SetOffset(
@ -1221,7 +1237,8 @@ size_t ParagraphTxt::TextSize() const {
}
double ParagraphTxt::GetHeight() {
return line_heights_.size() ? line_heights_.back() : 0;
return final_line_count_ == 0 ? 0
: line_metrics_[final_line_count_ - 1].height;
}
double ParagraphTxt::GetMaxWidth() {
@ -1539,7 +1556,7 @@ std::vector<Paragraph::TextBox> ParagraphTxt::GetRectsForRange(
SkScalar min_left = FLT_MAX;
};
std::map<size_t, LineBoxMetrics> line_metrics;
std::map<size_t, LineBoxMetrics> line_box_metrics;
// Text direction of the first line so we can extend the correct side for
// RectWidthStyle::kMax.
TextDirection first_line_dir = TextDirection::ltr;
@ -1557,7 +1574,7 @@ std::vector<Paragraph::TextBox> ParagraphTxt::GetRectsForRange(
if (run.code_units.end <= start)
continue;
double baseline = line_baselines_[run.line_number];
double baseline = line_metrics_[run.line_number].baseline;
SkScalar top = baseline + run.font_metrics.fAscent;
SkScalar bottom = baseline + run.font_metrics.fDescent;
@ -1600,39 +1617,40 @@ std::vector<Paragraph::TextBox> ParagraphTxt::GetRectsForRange(
// Keep track of the min and max horizontal coordinates over all lines. Not
// needed for kTight.
if (rect_width_style == RectWidthStyle::kMax) {
line_metrics[run.line_number].max_right =
std::max(line_metrics[run.line_number].max_right, right);
line_metrics[run.line_number].min_left =
std::min(line_metrics[run.line_number].min_left, left);
line_box_metrics[run.line_number].max_right =
std::max(line_box_metrics[run.line_number].max_right, right);
line_box_metrics[run.line_number].min_left =
std::min(line_box_metrics[run.line_number].min_left, left);
if (min_line == run.line_number) {
first_line_dir = run.direction;
}
}
line_metrics[run.line_number].boxes.emplace_back(
line_box_metrics[run.line_number].boxes.emplace_back(
SkRect::MakeLTRB(left, top, right, bottom), run.direction);
}
// Add empty rectangles representing any newline characters within the
// range.
for (size_t line_number = 0; line_number < line_ranges_.size();
for (size_t line_number = 0; line_number < line_metrics_.size();
++line_number) {
const LineRange& line = line_ranges_[line_number];
if (line.start >= end)
LineMetrics& line = line_metrics_[line_number];
if (line.start_index >= end)
break;
if (line.end_including_newline <= start)
continue;
if (line_metrics.find(line_number) == line_metrics.end()) {
if (line.end != line.end_including_newline && line.end >= start &&
line.end_including_newline <= end) {
if (line_box_metrics.find(line_number) == line_box_metrics.end()) {
if (line.end_index != line.end_including_newline &&
line.end_index >= start && line.end_including_newline <= end) {
SkScalar x = line_widths_[line_number];
// Move empty box to center if center aligned and is an empty line.
if (x == 0 && !isinf(width_) &&
paragraph_style_.effective_align() == TextAlign::center) {
x = width_ / 2;
}
SkScalar top = (line_number > 0) ? line_heights_[line_number - 1] : 0;
SkScalar bottom = line_heights_[line_number];
line_metrics[line_number].boxes.emplace_back(
SkScalar top =
(line_number > 0) ? line_metrics_[line_number - 1].height : 0;
SkScalar bottom = line_metrics_[line_number].height;
line_box_metrics[line_number].boxes.emplace_back(
SkRect::MakeLTRB(x, top, x, bottom), TextDirection::ltr);
}
}
@ -1640,28 +1658,27 @@ std::vector<Paragraph::TextBox> ParagraphTxt::GetRectsForRange(
// "Post-process" metrics and aggregate final rects to return.
std::vector<Paragraph::TextBox> boxes;
for (const auto& kv : line_metrics) {
for (const auto& kv : line_box_metrics) {
// Handle rect_width_styles. We skip the last line because not everything is
// selected.
LineMetrics& line =
line_metrics_[fmin(line_metrics_.size() - 1, fmax(0, kv.first))];
if (rect_width_style == RectWidthStyle::kMax && kv.first != max_line) {
if (line_metrics[kv.first].min_left > min_left_ &&
if (line_box_metrics[kv.first].min_left > min_left_ &&
(kv.first != min_line || first_line_dir == TextDirection::rtl)) {
line_metrics[kv.first].boxes.emplace_back(
SkRect::MakeLTRB(
min_left_,
line_baselines_[kv.first] - line_max_ascent_[kv.first],
line_metrics[kv.first].min_left,
line_baselines_[kv.first] + line_max_descent_[kv.first]),
line_box_metrics[kv.first].boxes.emplace_back(
SkRect::MakeLTRB(min_left_, line.baseline - line.unscaled_ascent,
line_box_metrics[kv.first].min_left,
line.baseline + line.descent),
TextDirection::rtl);
}
if (line_metrics[kv.first].max_right < max_right_ &&
if (line_box_metrics[kv.first].max_right < max_right_ &&
(kv.first != min_line || first_line_dir == TextDirection::ltr)) {
line_metrics[kv.first].boxes.emplace_back(
SkRect::MakeLTRB(
line_metrics[kv.first].max_right,
line_baselines_[kv.first] - line_max_ascent_[kv.first],
max_right_,
line_baselines_[kv.first] + line_max_descent_[kv.first]),
line_box_metrics[kv.first].boxes.emplace_back(
SkRect::MakeLTRB(line_box_metrics[kv.first].max_right,
line.baseline - line.unscaled_ascent, max_right_,
line.baseline + line.descent),
TextDirection::ltr);
}
}
@ -1674,27 +1691,22 @@ std::vector<Paragraph::TextBox> ParagraphTxt::GetRectsForRange(
} else if (rect_height_style == RectHeightStyle::kMax) {
for (const Paragraph::TextBox& box : kv.second.boxes) {
boxes.emplace_back(
SkRect::MakeLTRB(
box.rect.fLeft,
line_baselines_[kv.first] - line_max_ascent_[kv.first],
box.rect.fRight,
line_baselines_[kv.first] + line_max_descent_[kv.first]),
SkRect::MakeLTRB(box.rect.fLeft,
line.baseline - line.unscaled_ascent,
box.rect.fRight, line.baseline + line.descent),
box.direction);
}
} else if (rect_height_style ==
RectHeightStyle::kIncludeLineSpacingMiddle) {
SkScalar adjusted_bottom =
line_baselines_[kv.first] + line_max_descent_[kv.first];
if (kv.first < line_ranges_.size() - 1) {
adjusted_bottom += (line_max_spacings_[kv.first + 1] -
line_max_ascent_[kv.first + 1]) /
SkScalar adjusted_bottom = line.baseline + line.descent;
if (kv.first < line_metrics_.size() - 1) {
adjusted_bottom += (line_metrics_[kv.first + 1].ascent -
line_metrics_[kv.first + 1].unscaled_ascent) /
2;
}
SkScalar adjusted_top =
line_baselines_[kv.first] - line_max_ascent_[kv.first];
SkScalar adjusted_top = line.baseline - line.unscaled_ascent;
if (kv.first != 0) {
adjusted_top -=
(line_max_spacings_[kv.first] - line_max_ascent_[kv.first]) / 2;
adjusted_top -= (line.ascent - line.unscaled_ascent) / 2;
}
for (const Paragraph::TextBox& box : kv.second.boxes) {
boxes.emplace_back(SkRect::MakeLTRB(box.rect.fLeft, adjusted_top,
@ -1703,38 +1715,33 @@ std::vector<Paragraph::TextBox> ParagraphTxt::GetRectsForRange(
}
} else if (rect_height_style == RectHeightStyle::kIncludeLineSpacingTop) {
for (const Paragraph::TextBox& box : kv.second.boxes) {
SkScalar adjusted_top =
kv.first == 0
? line_baselines_[kv.first] - line_max_ascent_[kv.first]
: line_baselines_[kv.first] - line_max_spacings_[kv.first];
SkScalar adjusted_top = kv.first == 0
? line.baseline - line.unscaled_ascent
: line.baseline - line.ascent;
boxes.emplace_back(
SkRect::MakeLTRB(
box.rect.fLeft, adjusted_top, box.rect.fRight,
line_baselines_[kv.first] + line_max_descent_[kv.first]),
SkRect::MakeLTRB(box.rect.fLeft, adjusted_top, box.rect.fRight,
line.baseline + line.descent),
box.direction);
}
} else if (rect_height_style ==
RectHeightStyle::kIncludeLineSpacingBottom) {
for (const Paragraph::TextBox& box : kv.second.boxes) {
SkScalar adjusted_bottom =
line_baselines_[kv.first] + line_max_descent_[kv.first];
if (kv.first < line_ranges_.size() - 1) {
adjusted_bottom +=
-line_max_ascent_[kv.first] + line_max_spacings_[kv.first];
SkScalar adjusted_bottom = line.baseline + line.descent;
if (kv.first < line_metrics_.size() - 1) {
adjusted_bottom += -line.unscaled_ascent + line.ascent;
}
boxes.emplace_back(SkRect::MakeLTRB(box.rect.fLeft,
line_baselines_[kv.first] -
line_max_ascent_[kv.first],
box.rect.fRight, adjusted_bottom),
box.direction);
boxes.emplace_back(
SkRect::MakeLTRB(box.rect.fLeft,
line.baseline - line.unscaled_ascent,
box.rect.fRight, adjusted_bottom),
box.direction);
}
} else if (rect_height_style == RectHeightStyle::kStrut) {
if (IsStrutValid()) {
for (const Paragraph::TextBox& box : kv.second.boxes) {
boxes.emplace_back(
SkRect::MakeLTRB(
box.rect.fLeft, line_baselines_[kv.first] - strut_.ascent,
box.rect.fRight, line_baselines_[kv.first] + strut_.descent),
SkRect::MakeLTRB(box.rect.fLeft, line.baseline - strut_.ascent,
box.rect.fRight, line.baseline + strut_.descent),
box.direction);
}
} else {
@ -1750,12 +1757,12 @@ std::vector<Paragraph::TextBox> ParagraphTxt::GetRectsForRange(
Paragraph::PositionWithAffinity ParagraphTxt::GetGlyphPositionAtCoordinate(
double dx,
double dy) {
if (line_heights_.empty())
if (final_line_count_ <= 0)
return PositionWithAffinity(0, DOWNSTREAM);
size_t y_index;
for (y_index = 0; y_index < line_heights_.size() - 1; ++y_index) {
if (dy < line_heights_[y_index])
for (y_index = 0; y_index < final_line_count_ - 1; ++y_index) {
if (dy < line_metrics_[y_index].height)
break;
}
@ -1845,7 +1852,7 @@ std::vector<Paragraph::TextBox> ParagraphTxt::GetRectsForPlaceholders() {
// Generate initial boxes and calculate metrics.
for (const CodeUnitRun& run : inline_placeholder_code_unit_runs_) {
// Check to see if we are finished.
double baseline = line_baselines_[run.line_number];
double baseline = line_metrics_[run.line_number].baseline;
SkScalar top = baseline + run.font_metrics.fAscent;
SkScalar bottom = baseline + run.font_metrics.fDescent;
@ -1891,7 +1898,7 @@ Paragraph::Range<size_t> ParagraphTxt::GetWordBoundary(size_t offset) {
}
size_t ParagraphTxt::GetLineCount() {
return line_heights_.size();
return final_line_count_;
}
bool ParagraphTxt::DidExceedMaxLines() {
@ -1902,4 +1909,8 @@ void ParagraphTxt::SetDirty(bool dirty) {
needs_layout_ = dirty;
}
std::vector<LineMetrics>& ParagraphTxt::GetLineMetrics() {
return line_metrics_;
}
} // namespace txt

View File

@ -24,11 +24,13 @@
#include "flutter/fml/compiler_specific.h"
#include "flutter/fml/macros.h"
#include "font_collection.h"
#include "line_metrics.h"
#include "minikin/LineBreaker.h"
#include "paint_record.h"
#include "paragraph.h"
#include "paragraph_style.h"
#include "placeholder_run.h"
#include "run_metrics.h"
#include "styled_runs.h"
#include "third_party/googletest/googletest/include/gtest/gtest_prod.h" // nogncheck
#include "third_party/skia/include/core/SkFontMetrics.h"
@ -113,6 +115,10 @@ class ParagraphTxt : public Paragraph {
bool DidExceedMaxLines() override;
// Gets the full vector of LineMetrics which includes detailed data on each
// line in the final layout.
std::vector<LineMetrics>& GetLineMetrics() override;
// Sets the needs_layout_ to dirty. When Layout() is called, a new Layout will
// be performed when this is set to true. Can also be used to prevent a new
// Layout from being calculated by setting to false.
@ -150,6 +156,7 @@ class ParagraphTxt : public Paragraph {
FRIEND_TEST(ParagraphTest, FontFallbackParagraph);
FRIEND_TEST(ParagraphTest, InlinePlaceholder0xFFFCParagraph);
FRIEND_TEST(ParagraphTest, FontFeaturesParagraph);
FRIEND_TEST(ParagraphTest, GetGlyphPositionAtCoordinateSegfault);
// Starting data to layout.
std::vector<uint16_t> text_;
@ -172,26 +179,13 @@ class ParagraphTxt : public Paragraph {
minikin::LineBreaker breaker_;
mutable std::unique_ptr<icu::BreakIterator> word_breaker_;
struct LineRange {
LineRange(size_t s, size_t e, size_t eew, size_t ein, bool h)
: start(s),
end(e),
end_excluding_whitespace(eew),
end_including_newline(ein),
hard_break(h) {}
size_t start, end;
size_t end_excluding_whitespace;
size_t end_including_newline;
bool hard_break;
};
std::vector<LineRange> line_ranges_;
std::vector<LineMetrics> line_metrics_;
size_t final_line_count_;
std::vector<double> line_widths_;
// Stores the result of Layout().
std::vector<PaintRecord> records_;
std::vector<double> line_heights_;
std::vector<double> line_baselines_;
bool did_exceed_max_lines_;
// Strut metrics of zero will have no effect on the layout.
@ -206,11 +200,6 @@ class ParagraphTxt : public Paragraph {
StrutMetrics strut_;
// Metrics for use in GetRectsForRange(...);
// Per-line max metrics over all runs in a given line.
std::vector<SkScalar> line_max_spacings_;
std::vector<SkScalar> line_max_descent_;
std::vector<SkScalar> line_max_ascent_;
// Overall left and right extremes over all lines.
double max_right_;
double min_left_;
@ -293,6 +282,7 @@ class ParagraphTxt : public Paragraph {
Range<double> x_pos;
size_t line_number;
SkFontMetrics font_metrics;
const TextStyle* style;
TextDirection direction;
const PlaceholderRun* placeholder_run;
@ -301,6 +291,7 @@ class ParagraphTxt : public Paragraph {
Range<double> x,
size_t line,
const SkFontMetrics& metrics,
const TextStyle& st,
TextDirection dir,
const PlaceholderRun* placeholder);

View File

@ -0,0 +1,62 @@
/*
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef LIB_TXT_SRC_RUN_METRICS_H_
#define LIB_TXT_SRC_RUN_METRICS_H_
#include "text_style.h"
#include "third_party/skia/include/core/SkFontMetrics.h"
namespace txt {
// Contains the font metrics and TextStyle of a unique run.
class RunMetrics {
public:
RunMetrics(const TextStyle* style) : text_style_(style) {}
RunMetrics(const TextStyle* style, SkFontMetrics& metrics)
: text_style_(style), font_metrics_(metrics) {}
SkFontMetrics& GetFontMetrics() { return font_metrics_; }
const TextStyle& GetTextStyle() const { return *text_style_; }
private:
const TextStyle* text_style_;
// SkFontMetrics contains the following metrics:
//
// * Top distance to reserve above baseline
// * Ascent distance to reserve below baseline
// * Descent extent below baseline
// * Bottom extent below baseline
// * Leading distance to add between lines
// * AvgCharWidth average character width
// * MaxCharWidth maximum character width
// * XMin minimum x
// * XMax maximum x
// * XHeight height of lower-case 'x'
// * CapHeight height of an upper-case letter
// * UnderlineThickness underline thickness
// * UnderlinePosition underline position relative to baseline
// * StrikeoutThickness strikeout thickness
// * StrikeoutPosition strikeout position relative to baseline
SkFontMetrics font_metrics_;
};
} // namespace txt
#endif // LIB_TXT_SRC_RUN_METRICS_H_

View File

@ -71,6 +71,411 @@ TEST_F(ParagraphTest, SimpleParagraph) {
ASSERT_TRUE(Snapshot());
}
// It is possible for the line_metrics_ vector in paragraph to have an empty
// line at the end as a result of the line breaking algorithm. This causes
// the final_line_count_ to be one less than line metrics. This tests that we
// properly handle this case and do not segfault.
TEST_F(ParagraphTest, GetGlyphPositionAtCoordinateSegfault) {
const char* text = "Hello World\nText Dialog";
auto icu_text = icu::UnicodeString::fromUTF8(text);
std::u16string u16_text(icu_text.getBuffer(),
icu_text.getBuffer() + icu_text.length());
txt::ParagraphStyle paragraph_style;
txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());
txt::TextStyle text_style;
// We must supply a font here, as the default is Arial, and we do not
// include Arial in our test fonts as it is proprietary. We want it to
// be Arial default though as it is one of the most common fonts on host
// platforms. On real devices/apps, Arial should be able to be resolved.
text_style.font_families = std::vector<std::string>(1, "Roboto");
text_style.color = SK_ColorBLACK;
builder.PushStyle(text_style);
builder.AddText(u16_text);
builder.Pop();
auto paragraph = BuildParagraph(builder);
paragraph->Layout(GetTestCanvasWidth());
paragraph->Paint(GetCanvas(), 10.0, 15.0);
ASSERT_EQ(paragraph->final_line_count_, paragraph->line_metrics_.size());
ASSERT_EQ(paragraph->final_line_count_, 2ull);
ASSERT_EQ(paragraph->GetLineCount(), 2ull);
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(0.2, 0.2).position, 0ull);
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(20.2, 0.2).position, 3ull);
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(0.2, 20.2).position, 12ull);
// We artificially reproduce the conditions that cause segfaults in very
// specific circumstances in the wild. By adding this empty un-laid-out
// LineMetrics at the end, we force the case where final_line_count_
// represents the true number of lines whereas line_metrics_ has one
// extra empty one.
paragraph->line_metrics_.emplace_back(23, 24, 24, 24, true);
ASSERT_EQ(paragraph->final_line_count_, paragraph->line_metrics_.size() - 1);
ASSERT_EQ(paragraph->final_line_count_, 2ull);
ASSERT_EQ(paragraph->GetLineCount(), 2ull);
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(0.2, 20.2).position, 12ull);
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(0.2, 0.2).position, 0ull);
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(20.2, 0.2).position, 3ull);
paragraph->line_metrics_.emplace_back(24, 25, 25, 25, true);
ASSERT_EQ(paragraph->final_line_count_, paragraph->line_metrics_.size() - 2);
ASSERT_EQ(paragraph->final_line_count_, 2ull);
ASSERT_EQ(paragraph->GetLineCount(), 2ull);
ASSERT_TRUE(Snapshot());
}
TEST_F(ParagraphTest, LineMetricsParagraph1) {
const char* text = "Hello! What is going on?\nSecond line \nthirdline";
auto icu_text = icu::UnicodeString::fromUTF8(text);
std::u16string u16_text(icu_text.getBuffer(),
icu_text.getBuffer() + icu_text.length());
txt::ParagraphStyle paragraph_style;
txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());
txt::TextStyle text_style;
// We must supply a font here, as the default is Arial, and we do not
// include Arial in our test fonts as it is proprietary. We want it to
// be Arial default though as it is one of the most common fonts on host
// platforms. On real devices/apps, Arial should be able to be resolved.
text_style.font_families = std::vector<std::string>(1, "Roboto");
text_style.color = SK_ColorBLACK;
builder.PushStyle(text_style);
builder.AddText(u16_text);
builder.Pop();
auto paragraph = BuildParagraph(builder);
paragraph->Layout(GetTestCanvasWidth());
paragraph->Paint(GetCanvas(), 0, 0);
ASSERT_TRUE(Snapshot());
ASSERT_EQ(paragraph->GetLineMetrics().size(), 3ull);
ASSERT_EQ(paragraph->GetLineMetrics()[0].start_index, 0ull);
ASSERT_EQ(paragraph->GetLineMetrics()[0].end_index, 24ull);
ASSERT_EQ(paragraph->GetLineMetrics()[0].end_including_newline, 25ull);
ASSERT_EQ(paragraph->GetLineMetrics()[0].end_excluding_whitespace, 24ull);
ASSERT_EQ(paragraph->GetLineMetrics()[0].hard_break, true);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].ascent, 12.988281);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].descent, 3.4179688);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].width, 149.67578);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].left, 0.0);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].baseline, 12.582031);
ASSERT_EQ(paragraph->GetLineMetrics()[0].line_number, 0ull);
ASSERT_EQ(paragraph->GetLineMetrics()[0].run_metrics.size(), 1ull);
ASSERT_EQ(
paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[0].start_index)
->second.GetTextStyle()
.color,
SK_ColorBLACK);
ASSERT_EQ(
paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[0].start_index)
->second.GetTextStyle()
.font_families,
std::vector<std::string>(1, "Roboto"));
ASSERT_FLOAT_EQ(
paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[0].start_index)
->second.GetFontMetrics()
.fAscent,
-12.988281);
ASSERT_FLOAT_EQ(
paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[0].start_index)
->second.GetFontMetrics()
.fDescent,
3.4179688);
ASSERT_FLOAT_EQ(
paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[0].start_index)
->second.GetFontMetrics()
.fXHeight,
7.3964844);
ASSERT_FLOAT_EQ(
paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[0].start_index)
->second.GetFontMetrics()
.fLeading,
0);
ASSERT_FLOAT_EQ(
paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[0].start_index)
->second.GetFontMetrics()
.fTop,
-14.786133);
ASSERT_FLOAT_EQ(
paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[0].start_index)
->second.GetFontMetrics()
.fUnderlinePosition,
1.0253906);
ASSERT_EQ(paragraph->GetLineMetrics()[1].start_index, 25ull);
ASSERT_EQ(paragraph->GetLineMetrics()[1].end_index, 37ull);
ASSERT_EQ(paragraph->GetLineMetrics()[1].end_including_newline, 38ull);
ASSERT_EQ(paragraph->GetLineMetrics()[1].end_excluding_whitespace, 36ull);
ASSERT_EQ(paragraph->GetLineMetrics()[1].hard_break, true);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1].ascent, 12.988281);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1].descent, 3.4179688);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1].width, 72.039062);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1].left, 0.0);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1].baseline, 28.582031);
ASSERT_EQ(paragraph->GetLineMetrics()[1].line_number, 1ull);
ASSERT_EQ(paragraph->GetLineMetrics()[1].run_metrics.size(), 1ull);
ASSERT_EQ(
paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[1].start_index)
->second.GetTextStyle()
.color,
SK_ColorBLACK);
ASSERT_EQ(
paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[1].start_index)
->second.GetTextStyle()
.font_families,
std::vector<std::string>(1, "Roboto"));
ASSERT_FLOAT_EQ(
paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[1].start_index)
->second.GetFontMetrics()
.fAscent,
-12.988281);
ASSERT_FLOAT_EQ(
paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[1].start_index)
->second.GetFontMetrics()
.fDescent,
3.4179688);
ASSERT_FLOAT_EQ(
paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[1].start_index)
->second.GetFontMetrics()
.fXHeight,
7.3964844);
ASSERT_FLOAT_EQ(
paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[1].start_index)
->second.GetFontMetrics()
.fLeading,
0);
ASSERT_FLOAT_EQ(
paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[1].start_index)
->second.GetFontMetrics()
.fTop,
-14.786133);
ASSERT_FLOAT_EQ(
paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(paragraph->GetLineMetrics()[1].start_index)
->second.GetFontMetrics()
.fUnderlinePosition,
1.0253906);
}
TEST_F(ParagraphTest, LineMetricsParagraph2) {
const char* text = "test string alphabetic";
auto icu_text = icu::UnicodeString::fromUTF8(text);
std::u16string alphabetic(icu_text.getBuffer(),
icu_text.getBuffer() + icu_text.length());
const char* text2 = "测试中文日本語한국어";
auto icu_text2 = icu::UnicodeString::fromUTF8(text2);
std::u16string cjk(icu_text2.getBuffer(),
icu_text2.getBuffer() + icu_text2.length());
txt::ParagraphStyle paragraph_style;
txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());
txt::TextStyle text_style;
text_style.font_families = std::vector<std::string>(1, "Roboto");
text_style.font_families.push_back("Noto Sans CJK JP");
text_style.font_size = 27;
text_style.color = SK_ColorBLACK;
builder.PushStyle(text_style);
builder.AddText(alphabetic);
text_style.font_size = 24;
builder.PushStyle(text_style);
builder.AddText(cjk);
builder.Pop();
auto paragraph = BuildParagraph(builder);
paragraph->Layout(350);
paragraph->Paint(GetCanvas(), 0, 0);
ASSERT_TRUE(Snapshot());
ASSERT_EQ(paragraph->GetLineMetrics().size(), 2ull);
ASSERT_EQ(paragraph->GetLineMetrics()[0].start_index, 0ull);
ASSERT_EQ(paragraph->GetLineMetrics()[0].end_index, 26ull);
ASSERT_EQ(paragraph->GetLineMetrics()[0].end_including_newline, 26ull);
ASSERT_EQ(paragraph->GetLineMetrics()[0].end_excluding_whitespace, 26ull);
ASSERT_EQ(paragraph->GetLineMetrics()[0].hard_break, false);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].ascent, 27.84);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].descent, 7.6799998);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].width, 349.22266);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].left, 0.0);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0].baseline, 28.32);
ASSERT_EQ(paragraph->GetLineMetrics()[0].line_number, 0ull);
ASSERT_EQ(paragraph->GetLineMetrics()[0].run_metrics.size(), 2ull);
// First run
ASSERT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(2)
->second.GetTextStyle()
.font_size,
27);
ASSERT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(2)
->second.GetTextStyle()
.font_families,
text_style.font_families);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(2)
->second.GetFontMetrics()
.fAscent,
-25.048828);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(2)
->second.GetFontMetrics()
.fDescent,
6.5917969);
ASSERT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(21)
->second.GetTextStyle()
.font_size,
27);
ASSERT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(21)
->second.GetTextStyle()
.font_families,
text_style.font_families);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(21)
->second.GetFontMetrics()
.fAscent,
-25.048828);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(21)
->second.GetFontMetrics()
.fDescent,
6.5917969);
// Second run
ASSERT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(22)
->second.GetTextStyle()
.font_size,
24);
ASSERT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(22)
->second.GetTextStyle()
.font_families,
text_style.font_families);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(22)
->second.GetFontMetrics()
.fAscent,
-27.84);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(22)
->second.GetFontMetrics()
.fDescent,
7.6799998);
ASSERT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(24)
->second.GetTextStyle()
.font_size,
24);
ASSERT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(24)
->second.GetTextStyle()
.font_families,
text_style.font_families);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(24)
->second.GetFontMetrics()
.fAscent,
-27.84);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[0]
.run_metrics.lower_bound(24)
->second.GetFontMetrics()
.fDescent,
7.6799998);
ASSERT_EQ(paragraph->GetLineMetrics()[1].start_index, 26ull);
ASSERT_EQ(paragraph->GetLineMetrics()[1].end_index, 32ull);
ASSERT_EQ(paragraph->GetLineMetrics()[1].end_including_newline, 32ull);
ASSERT_EQ(paragraph->GetLineMetrics()[1].end_excluding_whitespace, 32ull);
ASSERT_EQ(paragraph->GetLineMetrics()[1].hard_break, true);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1].ascent, 27.84);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1].descent, 7.6799998);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1].width, 138.23438);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1].left, 0.0);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1].baseline, 64.32);
ASSERT_EQ(paragraph->GetLineMetrics()[1].line_number, 1ull);
ASSERT_EQ(paragraph->GetLineMetrics()[1].run_metrics.size(), 1ull);
// Indexing below the line will just resolve to the first run in the line.
ASSERT_EQ(paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(3)
->second.GetTextStyle()
.font_size,
24);
ASSERT_EQ(paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(3)
->second.GetTextStyle()
.font_families,
text_style.font_families);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(3)
->second.GetFontMetrics()
.fAscent,
-27.84);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(3)
->second.GetFontMetrics()
.fDescent,
7.6799998);
// Indexing within the line
ASSERT_EQ(paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(31)
->second.GetTextStyle()
.font_size,
24);
ASSERT_EQ(paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(31)
->second.GetTextStyle()
.font_families,
text_style.font_families);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(31)
->second.GetFontMetrics()
.fAscent,
-27.84);
ASSERT_FLOAT_EQ(paragraph->GetLineMetrics()[1]
.run_metrics.lower_bound(31)
->second.GetFontMetrics()
.fDescent,
7.6799998);
}
TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(InlinePlaceholderParagraph)) {
const char* text = "012 34";
auto icu_text = icu::UnicodeString::fromUTF8(text);