libtxt: refactoring of paragraph layout to improve text justification (#4178)
This changes Paragraph::Layout to iterate line-by-line through the text.
For each line, Layout scans for word boundaries and adds extra space between
words if the line is justified.
diff --git a/third_party/txt/src/txt/paragraph.cc b/third_party/txt/src/txt/paragraph.cc
index a953830..1b356b5 100644
--- a/third_party/txt/src/txt/paragraph.cc
+++ b/third_party/txt/src/txt/paragraph.cc
@@ -42,6 +42,11 @@
namespace txt {
namespace {
+struct Range {
+ Range(size_t s, size_t e) : start(s), end(e) {}
+ size_t start, end;
+};
+
const sk_sp<SkTypeface>& GetTypefaceForGlyph(const minikin::Layout& layout,
size_t index) {
const FontSkia* font = static_cast<const FontSkia*>(layout.getFont(index));
@@ -115,6 +120,26 @@
paint->setTextSize(style.font_size);
}
+void FindWords(const std::vector<uint16_t>& text,
+ size_t start,
+ size_t end,
+ std::vector<Range>* words) {
+ bool in_word = false;
+ size_t word_start;
+ for (size_t i = start; i < end; ++i) {
+ bool is_space = minikin::isWordSpace(text[i]);
+ if (!in_word && !is_space) {
+ word_start = i;
+ in_word = true;
+ } else if (in_word && is_space) {
+ words->emplace_back(word_start - start, i - start);
+ in_word = false;
+ }
+ }
+ if (in_word)
+ words->emplace_back(word_start - start, end - start);
+}
+
} // namespace
static const float kDoubleDecorationSpacing = 3.0f;
@@ -183,18 +208,6 @@
return true;
}
-void Paragraph::FillWhitespaceSet(size_t start,
- size_t end,
- hb_font_t* hb_font) {
- uint32_t unusedGlyph;
- for (size_t i = start; i < end; ++i) {
- if (minikin::isWordSpace(text_[i])) {
- hb_font_get_glyph(hb_font, text_[i], 0, &unusedGlyph);
- whitespace_set_.insert(unusedGlyph);
- }
- }
-}
-
void Paragraph::Layout(double width, bool force) {
// Do not allow calling layout multiple times without changing anything.
if (!needs_layout_ && width == width_ && !force) {
@@ -229,78 +242,85 @@
breaks_count_ = breaker_.computeBreaks();
const int* breaks = breaker_.getBreaks();
- // Create a copy of text_ to use locally so that any changes made to the
- // vector (such as removing newline characters) is not permanent.
- std::vector<uint16_t> text(text_);
-
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint.setSubpixelText(true);
- minikin::FontStyle font;
- minikin::MinikinPaint minikin_paint;
+ records_.clear();
+ line_heights_.clear();
+
minikin::Layout layout;
-
SkTextBlobBuilder builder;
+ size_t line_limit = std::min(paragraph_style_.max_lines, breaks_count_);
+ size_t run_index = 0;
+ double y_offset = 0;
+ double prev_max_descent = 0;
- // Reset member variables so Layout still works when called more than once
- lines_ = 0;
- line_heights_ = std::vector<double>();
- records_ = std::vector<PaintRecord>();
+ for (size_t line_number = 0; line_number < line_limit; ++line_number) {
+ size_t line_start = (line_number > 0) ? breaks[line_number - 1] : 0;
+ size_t line_end = breaks[line_number];
- glyph_position_x_ = std::vector<GlyphLine>();
- std::vector<GlyphPosition> glyph_single_line_position_x;
+ // Break the line into words if justification should be applied.
+ std::vector<Range> words;
+ double word_gap_width = 0;
+ size_t word_index = 0;
+ bool justify_line =
+ (paragraph_style_.text_align == TextAlign::justify &&
+ line_number != line_limit - 1 && text_[line_end - 1] != '\n');
+ if (justify_line) {
+ FindWords(text_, line_start, line_end, &words);
+ if (words.size() > 1) {
+ word_gap_width =
+ (width_ - breaker_.getWidths()[line_number]) / (words.size() - 1);
+ }
+ }
- SkScalar x_offset = GetLineXOffset(0);
- SkScalar y_offset = 0.0f;
- size_t break_index = 0;
- double max_line_spacing = 0.0f;
- double max_descent = 0.0f;
- double prev_max_descent = 0.0f;
- std::vector<SkScalar> x_queue;
- double justify_spacing = 0.0f;
- double current_x_position = 0.0f;
+ // Find the runs comprising this line.
+ std::vector<StyledRuns::Run> line_runs;
+ while (run_index < runs_.size()) {
+ StyledRuns::Run run = runs_.GetRun(run_index);
+ if (run.start >= line_end)
+ break;
+ line_runs.push_back(run);
+ if (run.end > line_end)
+ break;
+ run_index++;
+ }
- std::vector<const SkTextBlobBuilder::RunBuffer*> buffers;
- std::vector<size_t> buffer_sizes;
- int word_count = 0;
- size_t max_lines = paragraph_style_.max_lines;
+ std::vector<GlyphPosition> glyph_single_line_position_x;
+ double run_x_offset = GetLineXOffset(line_number);
+ std::vector<PaintRecord> paint_records;
- for (size_t run_index = 0; run_index < runs_.size(); ++run_index) {
- auto run = runs_.GetRun(run_index);
- bool is_newline = text_[run.start] == '\n' && run.end - run.start == 1;
- // Replace '\n' with a null character so that a 'missing glyph' box is not
- // drawn.
- if (is_newline)
- text[run.start] = '\0';
+ for (const StyledRuns::Run& run : line_runs) {
+ minikin::FontStyle font;
+ minikin::MinikinPaint minikin_paint;
+ GetFontAndMinikinPaint(run.style, &font, &minikin_paint);
+ GetPaint(run.style, &paint);
- GetFontAndMinikinPaint(run.style, &font, &minikin_paint);
- GetPaint(run.style, &paint);
-
- size_t layout_start = run.start;
- // Layout until the end of the run or too many lines.
- while (layout_start < run.end && lines_ < max_lines) {
- const size_t next_break = (break_index > breaks_count_ - 1)
- ? std::numeric_limits<size_t>::max()
- : breaks[break_index];
- const size_t layout_end = std::min(run.end, next_break);
-
- bool bidiFlags = paragraph_style_.rtl;
std::shared_ptr<minikin::FontCollection> minikin_font_collection =
font_collection_->GetMinikinFontCollectionForFamily(
run.style.font_family);
- uint16_t* text_ptr = text.data() + layout_start;
- size_t text_count = layout_end - layout_start;
- std::vector<uint16_t> ellipsized_text;
+ // Lay out this run.
+ size_t line_run_start = std::max(run.start, line_start);
+ size_t line_run_end = std::min(run.end, line_end);
+ uint16_t* text_ptr = text_.data() + line_run_start;
+ size_t text_count = line_run_end - line_run_start;
+ bool bidiFlags = paragraph_style_.rtl;
+
+ if (text_count == 0)
+ continue;
+ if (text_ptr[text_count - 1] == '\n')
+ text_count--;
// Apply ellipsizing if the run was not completely laid out and this
// is the last line (or lines are unlimited).
const std::u16string& ellipsis = paragraph_style_.ellipsis;
- if (ellipsis.length() && !isinf(width_) && run.end != layout_end &&
- (lines_ == max_lines - 1 ||
- max_lines == std::numeric_limits<size_t>::max())) {
+ std::vector<uint16_t> ellipsized_text;
+ if (ellipsis.length() && !isinf(width_) && run.end > line_end &&
+ (line_number == line_limit - 1 ||
+ paragraph_style_.max_lines == std::numeric_limits<size_t>::max())) {
float ellipsis_width = layout.measureText(
reinterpret_cast<const uint16_t*>(ellipsis.data()), 0,
ellipsis.length(), ellipsis.length(), bidiFlags, font,
@@ -308,8 +328,8 @@
std::vector<float> text_advances(text_count);
float text_width = layout.measureText(
- text.data() + layout_start, 0, text_count, text_count, bidiFlags,
- font, minikin_paint, minikin_font_collection, text_advances.data());
+ text_ptr, 0, text_count, text_count, bidiFlags, font, minikin_paint,
+ minikin_font_collection, text_advances.data());
// Truncate characters from the text until the ellipsis fits.
size_t truncate_count = 0;
@@ -322,8 +342,8 @@
ellipsized_text.reserve(text_count - truncate_count +
ellipsis.length());
ellipsized_text.insert(ellipsized_text.begin(),
- text.begin() + layout_start,
- text.begin() + layout_end - truncate_count);
+ text_.begin() + line_run_start,
+ text_.begin() + line_run_end - truncate_count);
ellipsized_text.insert(ellipsized_text.end(), ellipsis.begin(),
ellipsis.end());
text_ptr = ellipsized_text.data();
@@ -331,65 +351,37 @@
// If there is no line limit, then skip all lines after the ellipsized
// line.
- if (max_lines == std::numeric_limits<size_t>::max())
- max_lines = lines_ + 1;
+ if (paragraph_style_.max_lines == std::numeric_limits<size_t>::max())
+ line_limit = line_number + 1;
}
- // Minikin Layout doLayout() has an O(N^2) (according to
- // benchmarks) time complexity where N is the total number of characters.
- // However, this is not significant for reasonably sized paragraphs. It is
- // currently recommended to break up very long paragraphs (10k+
- // characters) to ensure speedy layout.
layout.doLayout(text_ptr, 0, text_count, text_count, bidiFlags, font,
minikin_paint, minikin_font_collection);
- FillWhitespaceSet(layout_start, layout_end,
- minikin::getHbFontLocked(layout.getFont(0)));
- const size_t glyph_count = layout.nGlyphs();
- size_t blob_start = 0;
+ // Break the layout into blobs that share the same SkPaint parameters.
+ std::vector<Range> glyph_blobs;
+ for (size_t blob_start = 0; blob_start < layout.nGlyphs();) {
+ size_t blob_len = GetBlobLength(layout, blob_start);
+ glyph_blobs.emplace_back(blob_start, blob_start + blob_len);
+ blob_start += blob_len;
+ }
+
+ double current_x_position = 0;
size_t code_unit_index = 0;
- // Each blob.
- buffers = std::vector<const SkTextBlobBuilder::RunBuffer*>();
- buffer_sizes = std::vector<size_t>();
- word_count = 0;
- double temp_line_spacing = 0;
- current_x_position = 0;
- while (blob_start < glyph_count) {
- const size_t blob_length = GetBlobLength(layout, blob_start);
- buffer_sizes.push_back(blob_length);
- // TODO(abarth): Precompute when we can use allocRunPosH.
- paint.setTypeface(GetTypefaceForGlyph(layout, blob_start));
+ for (const Range& glyph_blob : glyph_blobs) {
+ paint.setTypeface(GetTypefaceForGlyph(layout, glyph_blob.start));
+ const SkTextBlobBuilder::RunBuffer& blob_buffer =
+ builder.allocRunPos(paint, glyph_blob.end - glyph_blob.start);
- // Check if we should remove trailing whitespace of blobs.
- size_t trailing_length = 0;
- while ((paragraph_style_.text_align == TextAlign::center ||
- paragraph_style_.text_align == TextAlign::right) &&
- whitespace_set_.count(layout.getGlyphId(
- blob_start + blob_length - trailing_length - 1)) > 0 &&
- layout_end == next_break) {
- ++trailing_length;
- }
+ for (size_t glyph_index = glyph_blob.start;
+ glyph_index < glyph_blob.end; ++glyph_index) {
+ size_t blob_index = glyph_index - glyph_blob.start;
+ blob_buffer.glyphs[blob_index] = layout.getGlyphId(glyph_index);
- buffers.push_back(
- &builder.allocRunPos(paint, blob_length - trailing_length));
-
- // TODO(garyq): Implement RTL.
- // Each Glyph/Letter.
- bool whitespace_ended = true;
- float letter_spacing = 0;
- for (size_t blob_index = 0; blob_index < blob_length - trailing_length;
- ++blob_index) {
- const size_t glyph_index = blob_start + blob_index;
- buffers.back()->glyphs[blob_index] = layout.getGlyphId(glyph_index);
-
- const size_t pos_index = 2 * blob_index;
- // Extract the letter spacing by itself out of the minikin layout.
- letter_spacing = run.style.letter_spacing == 0
- ? 0
- : layout.getX(glyph_index) - current_x_position;
- buffers.back()->pos[pos_index] = current_x_position + letter_spacing;
- buffers.back()->pos[pos_index + 1] = layout.getY(glyph_index);
+ size_t pos_index = blob_index * 2;
+ blob_buffer.pos[pos_index] = current_x_position;
+ blob_buffer.pos[pos_index + 1] = layout.getY(glyph_index);
float glyph_advance = layout.getCharAdvance(code_unit_index);
@@ -411,7 +403,7 @@
float subglyph_advance =
glyph_advance / subglyph_code_unit_counts.size();
glyph_single_line_position_x.emplace_back(
- x_offset + current_x_position + letter_spacing, subglyph_advance,
+ run_x_offset + current_x_position, subglyph_advance,
subglyph_code_unit_counts[0]);
// Compute positions for the additional characters in the ligature.
@@ -423,91 +415,60 @@
current_x_position += glyph_advance;
- // Check if the current Glyph is a whitespace and handle multiple
- // whitespaces in a row.
- if (whitespace_set_.count(layout.getGlyphId(glyph_index)) > 0) {
- // Only increment word_count if it is the first in a series of
- // whitespaces.
- if (whitespace_ended) {
- ++word_count;
- }
- whitespace_ended = false;
- } else {
- whitespace_ended = true;
+ if (justify_line && word_index < words.size() &&
+ code_unit_index == words[word_index].end) {
+ current_x_position += word_gap_width;
+ word_index++;
}
}
- blob_start += blob_length;
}
- // TODO(abarth): We could keep the same SkTextBlobBuilder as long as the
- // color stayed the same.
SkPaint::FontMetrics metrics;
paint.getFontMetrics(&metrics);
- // Apply additional word spacing if the text is justified.
- if (paragraph_style_.text_align == TextAlign::justify &&
- buffer_sizes.size() > 0) {
- JustifyLine(buffers, buffer_sizes, word_count, justify_spacing);
- }
- records_.push_back(PaintRecord{run.style, builder.make(), metrics, lines_,
- layout.getAdvance()});
- // Must adjust each line to the largest text in the line, so cannot
- // directly push the offset property of PaintRecord until line is
- // finished.
- x_queue.push_back(x_offset);
+ paint_records.emplace_back(run.style, SkPoint::Make(run_x_offset, 0),
+ builder.make(), metrics, line_number,
+ current_x_position);
+ run_x_offset += current_x_position;
+ }
- temp_line_spacing = lines_ == 0 ? -metrics.fAscent * run.style.height
- : (-metrics.fAscent + metrics.fLeading) *
- run.style.height;
- if (max_line_spacing < temp_line_spacing) {
- max_line_spacing = temp_line_spacing;
- // Record the alphabetic_baseline_ and idegraphic_baseline_:
- if (lines_ == 0) {
- alphabetic_baseline_ = -metrics.fAscent * run.style.height;
+ double max_line_spacing = 0;
+ double max_descent = 0;
+ for (const PaintRecord& paint_record : paint_records) {
+ const SkPaint::FontMetrics& metrics = paint_record.metrics();
+ double style_height = paint_record.style().height;
+ double line_spacing =
+ (line_number == 0)
+ ? -metrics.fAscent * style_height
+ : (-metrics.fAscent + metrics.fLeading) * style_height;
+ if (line_spacing > max_line_spacing) {
+ max_line_spacing = line_spacing;
+ if (line_number == 0) {
+ alphabetic_baseline_ = line_spacing;
// TODO(garyq): Properly implement ideographic_baseline_.
ideographic_baseline_ =
- (metrics.fUnderlinePosition - metrics.fAscent) * run.style.height;
+ (metrics.fUnderlinePosition - metrics.fAscent) * style_height;
}
}
- temp_line_spacing = metrics.fDescent * run.style.height;
- if (max_descent < temp_line_spacing)
- max_descent = temp_line_spacing;
+ max_line_spacing = std::max(line_spacing, max_line_spacing);
- if (layout_end == next_break || is_newline) {
- y_offset += roundf(max_line_spacing + prev_max_descent);
- for (size_t i = 0; i < x_queue.size(); ++i) {
- PaintRecord& record = records_[records_.size() - x_queue.size() + i];
- record.SetOffset(SkPoint::Make(x_queue[i], y_offset));
- }
- x_queue.clear();
-
- line_heights_.push_back(
- (line_heights_.empty() ? 0 : line_heights_.back()) +
- roundf(max_line_spacing + max_descent));
- glyph_position_x_.emplace_back(std::move(glyph_single_line_position_x));
- glyph_single_line_position_x.clear();
-
- prev_max_descent = max_descent;
-
- // Reset Variables for next line.
- max_line_spacing = 0.0f;
- max_descent = 0.0f;
- current_x_position = 0.0f;
- break_index += 1;
- lines_++;
- x_offset = GetLineXOffset(lines_);
- } else {
- x_offset += layout.getAdvance();
- }
-
- layout_start = layout_end;
+ double descent = metrics.fDescent * style_height;
+ max_descent = std::max(descent, max_descent);
}
+
+ line_heights_.push_back((line_heights_.empty() ? 0 : line_heights_.back()) +
+ roundf(max_line_spacing + max_descent));
+ y_offset += roundf(max_line_spacing + prev_max_descent);
+ prev_max_descent = max_descent;
+
+ for (PaintRecord& paint_record : paint_records) {
+ paint_record.SetOffset(
+ SkPoint::Make(paint_record.offset().x(), y_offset));
+ records_.emplace_back(std::move(paint_record));
+ }
+
+ glyph_position_x_.emplace_back(std::move(glyph_single_line_position_x));
}
- // Remove justification on the last line.
- if (paragraph_style_.text_align == TextAlign::justify &&
- buffer_sizes.size() > 0) {
- JustifyLine(buffers, buffer_sizes, word_count, justify_spacing, -1);
- }
CalculateIntrinsicWidths();
breaker_.finish();
}
@@ -525,43 +486,6 @@
}
}
-// Amends the buffers to incorporate justification.
-void Paragraph::JustifyLine(
- std::vector<const SkTextBlobBuilder::RunBuffer*>& buffers,
- std::vector<size_t>& buffer_sizes,
- int word_count,
- double& justify_spacing,
- double multiplier) {
- // We will use the previous justification spacing when undoing justification.
- if (multiplier > 0) {
- justify_spacing =
- (width_ - breaker_.getWidths()[lines_]) / (word_count - 1);
- }
- word_count = 0;
- bool whitespace_ended = true;
- for (size_t i = 0; i < buffers.size(); ++i) {
- for (size_t glyph_index = 0; glyph_index < buffer_sizes[i]; ++glyph_index) {
- // Check if the current Glyph is a whitespace and handle multiple
- // whitespaces in a row.
- if (whitespace_set_.count(buffers[i]->glyphs[glyph_index]) > 0) {
- // Only increment word_count and add justification spacing to
- // whitespace if it is the first in a series of whitespaces.
- if (whitespace_ended) {
- ++word_count;
- buffers[i]->pos[glyph_index * 2] +=
- justify_spacing * multiplier * word_count;
- }
- whitespace_ended = false;
- } else {
- // Add justification spacing for all non-whitespace glyphs.
- buffers[i]->pos[glyph_index * 2] +=
- justify_spacing * multiplier * word_count;
- whitespace_ended = true;
- }
- }
- }
-}
-
const ParagraphStyle& Paragraph::GetParagraphStyle() const {
return paragraph_style_;
}
@@ -578,7 +502,7 @@
void Paragraph::CalculateIntrinsicWidths() {
max_intrinsic_width_ = 0;
- for (size_t i = 0; i < lines_; ++i) {
+ for (size_t i = 0; i < GetLineCount(); ++i) {
max_intrinsic_width_ += breaker_.getWidths()[i];
}
@@ -586,7 +510,7 @@
// intrinsic width. This is currently the longest line in the text after
// layout.
min_intrinsic_width_ = 0;
- for (size_t i = 0; i < lines_; ++i) {
+ for (size_t i = 0; i < GetLineCount(); ++i) {
min_intrinsic_width_ = fmax(min_intrinsic_width_, breaker_.getWidths()[i]);
}
@@ -666,7 +590,7 @@
double width = 0;
if (paragraph_style_.text_align == TextAlign::justify &&
- record.line() != lines_ - 1) {
+ record.line() != GetLineCount() - 1) {
width = width_;
} else {
width = record.GetRunWidth();
@@ -905,12 +829,12 @@
minikin::getNextWordBreakForCache(text_.data(), offset, text_.size()));
}
-int Paragraph::GetLineCount() const {
- return lines_;
+size_t Paragraph::GetLineCount() const {
+ return line_heights_.size();
}
bool Paragraph::DidExceedMaxLines() const {
- if (lines_ > paragraph_style_.max_lines)
+ if (GetLineCount() > paragraph_style_.max_lines)
return true;
return false;
}
diff --git a/third_party/txt/src/txt/paragraph.h b/third_party/txt/src/txt/paragraph.h
index f8caa40..3ad5199 100644
--- a/third_party/txt/src/txt/paragraph.h
+++ b/third_party/txt/src/txt/paragraph.h
@@ -71,6 +71,7 @@
//
// Layout calculates the positioning of all the glyphs. Must call this method
// before Painting and getting any statistics from this class.
+ void OldLayout(double width, bool force = false);
void Layout(double width, bool force = false);
// Paints the Laid out text onto the supplied SkCanvas at (x, y) offset from
@@ -138,7 +139,7 @@
// Returns the number of lines the paragraph takes up. If the text exceeds the
// amount width and maxlines provides, Layout() truncates the extra text from
// the layout and this will return the max lines allowed.
- int GetLineCount() const;
+ size_t GetLineCount() const;
// Checks if the layout extends past the maximum lines and had to be
// truncated.
@@ -212,13 +213,9 @@
// Holds the laid out x positions of each glyph.
std::vector<GlyphLine> glyph_position_x_;
- // Set of glyph IDs that correspond to whitespace.
- std::set<GlyphID> whitespace_set_;
-
// The max width of the paragraph as provided in the most recent Layout()
// call.
double width_ = -1.0f;
- size_t lines_ = 0;
double max_intrinsic_width_ = 0;
double min_intrinsic_width_ = 0;
double alphabetic_baseline_ = FLT_MAX;
@@ -253,22 +250,10 @@
std::unordered_map<std::string, std::shared_ptr<minikin::FontCollection>>&
collection_map);
- // Calculates the GlyphIDs of all whitespace characters present in the text
- // between start and end. THis is used to correctly add extra whitespace when
- // justifying.
- void FillWhitespaceSet(size_t start, size_t end, hb_font_t* hb_font);
-
// Calculate the starting X offset of a line based on the line's width and
// alignment.
double GetLineXOffset(size_t line);
- // Calculates and amends the layout for one line to be justified.
- void JustifyLine(std::vector<const SkTextBlobBuilder::RunBuffer*>& buffers,
- std::vector<size_t>& buffer_sizes,
- int word_count,
- double& justify_spacing,
- double multiplier = 1);
-
// Creates and draws the decorations onto the canvas.
void PaintDecorations(SkCanvas* canvas,
double x,
diff --git a/third_party/txt/tests/paragraph_unittests.cc b/third_party/txt/tests/paragraph_unittests.cc
index 6893d20..4e5e1c7 100644
--- a/third_party/txt/tests/paragraph_unittests.cc
+++ b/third_party/txt/tests/paragraph_unittests.cc
@@ -1260,7 +1260,7 @@
ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull);
ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style));
ASSERT_EQ(paragraph->records_[0].style().color, text_style.color);
- ASSERT_EQ(paragraph->GetLineCount(), 4);
+ ASSERT_EQ(paragraph->GetLineCount(), 4ull);
ASSERT_TRUE(Snapshot());
}
@@ -1446,7 +1446,7 @@
ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull);
ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style));
ASSERT_EQ(paragraph->records_[0].style().color, text_style.color);
- ASSERT_EQ(paragraph->GetLineCount(), 5);
+ ASSERT_EQ(paragraph->GetLineCount(), 5ull);
ASSERT_TRUE(Snapshot());
}
@@ -1490,7 +1490,7 @@
ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull);
ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style));
ASSERT_EQ(paragraph->records_[0].style().color, text_style.color);
- ASSERT_EQ(paragraph->GetLineCount(), 12);
+ ASSERT_EQ(paragraph->GetLineCount(), 12ull);
// Second Layout.
SetUp();
@@ -1506,7 +1506,7 @@
ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull);
ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style));
ASSERT_EQ(paragraph->records_[0].style().color, text_style.color);
- ASSERT_EQ(paragraph->GetLineCount(), 6);
+ ASSERT_EQ(paragraph->GetLineCount(), 6ull);
ASSERT_TRUE(Snapshot());
}