mirror of
https://github.com/encounter/engine.git
synced 2026-03-30 11:09:55 -07:00
Re-land "libtxt: exclude trailing whitespace from right-justified lines" (#5234)
If a line is right justified, then remove any trailing whitespace from the text range given to Minikin. Right justification shifts the line's glyphs by the layout advance computed by Minikin, and this advance should exclude whitespace so that the last visible character will be flush with the right margin. Also exclude trailing whitespace from center justified lines. Fixes https://github.com/flutter/flutter/issues/17502 Fixes https://github.com/flutter/flutter/issues/16333
This commit is contained in:
+1
-1
@@ -107,7 +107,7 @@ void LineBreaker::setIndents(const std::vector<float>& indents) {
|
||||
// end of line. It is the Unicode set:
|
||||
// [[:General_Category=Space_Separator:]-[:Line_Break=Glue:]], plus '\n'. Note:
|
||||
// all such characters are in the BMP, so it's ok to use code units for this.
|
||||
static bool isLineEndSpace(uint16_t c) {
|
||||
bool isLineEndSpace(uint16_t c) {
|
||||
return c == '\n' || c == ' ' || c == 0x1680 ||
|
||||
(0x2000 <= c && c <= 0x200A && c != 0x2007) || c == 0x205F ||
|
||||
c == 0x3000;
|
||||
|
||||
+2
@@ -49,6 +49,8 @@ enum HyphenationFrequency {
|
||||
kHyphenationFrequency_Full = 2
|
||||
};
|
||||
|
||||
bool isLineEndSpace(uint16_t c);
|
||||
|
||||
// TODO: want to generalize to be able to handle array of line widths
|
||||
class LineWidths {
|
||||
public:
|
||||
|
||||
Vendored
+27
-20
@@ -258,7 +258,8 @@ bool Paragraph::ComputeLineBreaks() {
|
||||
size_t block_size = block_end - block_start;
|
||||
|
||||
if (block_size == 0) {
|
||||
line_ranges_.emplace_back(block_start, block_end, block_end + 1, true);
|
||||
line_ranges_.emplace_back(block_start, block_end, block_end,
|
||||
block_end + 1, true);
|
||||
line_widths_.push_back(0);
|
||||
continue;
|
||||
}
|
||||
@@ -311,7 +312,14 @@ bool Paragraph::ComputeLineBreaks() {
|
||||
bool hard_break = i == breaks_count - 1;
|
||||
size_t line_end_including_newline =
|
||||
(hard_break && line_end < text_.size()) ? line_end + 1 : line_end;
|
||||
size_t line_end_excluding_whitespace = line_end;
|
||||
while (
|
||||
line_end_excluding_whitespace > 0 &&
|
||||
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_widths_.push_back(breaker_.getWidths()[i]);
|
||||
}
|
||||
@@ -462,13 +470,21 @@ void Paragraph::Layout(double width, bool force) {
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude trailing whitespace from right-justified lines so the last
|
||||
// visible character in the line will be flush with the right margin.
|
||||
size_t line_end_index =
|
||||
(paragraph_style_.effective_align() == TextAlign::right ||
|
||||
paragraph_style_.effective_align() == TextAlign::center)
|
||||
? line_range.end_excluding_whitespace
|
||||
: line_range.end;
|
||||
|
||||
// Find the runs comprising this line.
|
||||
std::vector<BidiRun> line_runs;
|
||||
for (const BidiRun& bidi_run : bidi_runs) {
|
||||
if (bidi_run.start() < line_range.end &&
|
||||
if (bidi_run.start() < line_end_index &&
|
||||
bidi_run.end() > line_range.start) {
|
||||
line_runs.emplace_back(std::max(bidi_run.start(), line_range.start),
|
||||
std::min(bidi_run.end(), line_range.end),
|
||||
std::min(bidi_run.end(), line_end_index),
|
||||
bidi_run.direction(), bidi_run.style());
|
||||
}
|
||||
}
|
||||
@@ -687,7 +703,7 @@ void Paragraph::Layout(double width, bool force) {
|
||||
}
|
||||
|
||||
// Adjust the glyph positions based on the alignment of the line.
|
||||
double line_x_offset = GetLineXOffset(line_number, run_x_offset);
|
||||
double line_x_offset = GetLineXOffset(run_x_offset);
|
||||
if (line_x_offset) {
|
||||
for (CodeUnitRun& code_unit_run : line_code_unit_runs) {
|
||||
for (GlyphPosition& position : code_unit_run.positions) {
|
||||
@@ -780,25 +796,16 @@ void Paragraph::Layout(double width, bool force) {
|
||||
});
|
||||
}
|
||||
|
||||
double Paragraph::GetLineXOffset(size_t line_number,
|
||||
double line_total_advance) {
|
||||
if (line_number >= line_widths_.size() || isinf(width_))
|
||||
double Paragraph::GetLineXOffset(double line_total_advance) {
|
||||
if (isinf(width_))
|
||||
return 0;
|
||||
|
||||
TextAlign align = paragraph_style_.text_align;
|
||||
TextDirection direction = paragraph_style_.text_direction;
|
||||
TextAlign align = paragraph_style_.effective_align();
|
||||
|
||||
if (align == TextAlign::right ||
|
||||
(align == TextAlign::start && direction == TextDirection::rtl) ||
|
||||
(align == TextAlign::end && direction == TextDirection::ltr)) {
|
||||
// If this line has a soft break, then use the line width calculated by the
|
||||
// line breaker because that width excludes the soft break's whitespace.
|
||||
double text_width = line_ranges_[line_number].hard_break
|
||||
? line_total_advance
|
||||
: line_widths_[line_number];
|
||||
return width_ - text_width;
|
||||
} else if (paragraph_style_.text_align == TextAlign::center) {
|
||||
return (width_ - line_widths_[line_number]) / 2;
|
||||
if (align == TextAlign::right) {
|
||||
return width_ - line_total_advance;
|
||||
} else if (align == TextAlign::center) {
|
||||
return (width_ - line_total_advance) / 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vendored
+8
-3
@@ -187,9 +187,14 @@ class Paragraph {
|
||||
mutable std::unique_ptr<icu::BreakIterator> word_breaker_;
|
||||
|
||||
struct LineRange {
|
||||
LineRange(size_t s, size_t e, size_t ewn, bool h)
|
||||
: start(s), end(e), end_including_newline(ewn), hard_break(h) {}
|
||||
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;
|
||||
};
|
||||
@@ -300,7 +305,7 @@ class Paragraph {
|
||||
|
||||
// Calculate the starting X offset of a line based on the line's width and
|
||||
// alignment.
|
||||
double GetLineXOffset(size_t line_number, double line_total_advance);
|
||||
double GetLineXOffset(double line_total_advance);
|
||||
|
||||
// Creates and draws the decorations onto the canvas.
|
||||
void PaintDecorations(SkCanvas* canvas, const PaintRecord& record);
|
||||
|
||||
+12
@@ -37,4 +37,16 @@ bool ParagraphStyle::ellipsized() const {
|
||||
return !ellipsis.empty();
|
||||
}
|
||||
|
||||
TextAlign ParagraphStyle::effective_align() const {
|
||||
if (text_align == TextAlign::start) {
|
||||
return (text_direction == TextDirection::ltr) ? TextAlign::left
|
||||
: TextAlign::right;
|
||||
} else if (text_align == TextAlign::end) {
|
||||
return (text_direction == TextDirection::ltr) ? TextAlign::right
|
||||
: TextAlign::left;
|
||||
} else {
|
||||
return text_align;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace txt
|
||||
|
||||
+3
@@ -66,6 +66,9 @@ class ParagraphStyle {
|
||||
|
||||
bool unlimited_lines() const;
|
||||
bool ellipsized() const;
|
||||
|
||||
// Return a text alignment value that is not dependent on the text direction.
|
||||
TextAlign effective_align() const;
|
||||
};
|
||||
|
||||
} // namespace txt
|
||||
|
||||
Reference in New Issue
Block a user