mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Reland "Track detailed LibTxt metrics with LineMetrics(#10127)" (flutter/engine#11064)
This commit is contained in:
parent
c5cac036ad
commit
3ec6774cea
@ -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
|
||||
|
||||
2
engine/src/flutter/third_party/txt/BUILD.gn
vendored
2
engine/src/flutter/third_party/txt/BUILD.gn
vendored
@ -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",
|
||||
|
||||
84
engine/src/flutter/third_party/txt/src/txt/line_metrics.h
vendored
Normal file
84
engine/src/flutter/third_party/txt/src/txt/line_metrics.h
vendored
Normal 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_
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
62
engine/src/flutter/third_party/txt/src/txt/run_metrics.h
vendored
Normal file
62
engine/src/flutter/third_party/txt/src/txt/run_metrics.h
vendored
Normal 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_
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user