Flutter Engine
txt::ParagraphTxt Class Reference

#include <paragraph_txt.h>

Inheritance diagram for txt::ParagraphTxt:
txt::Paragraph

Public Member Functions

 ParagraphTxt ()
 
virtual ~ParagraphTxt ()
 
virtual void Layout (double width) override
 
virtual void Paint (SkCanvas *canvas, double x, double y) override
 
const ParagraphStyleGetParagraphStyle () const
 
size_t TextSize () const
 
double GetHeight () override
 
double GetMaxWidth () override
 
double GetLongestLine () override
 
double GetAlphabeticBaseline () override
 
double GetIdeographicBaseline () override
 
double GetMaxIntrinsicWidth () override
 
double GetMinIntrinsicWidth () override
 
std::vector< TextBoxGetRectsForRange (size_t start, size_t end, RectHeightStyle rect_height_style, RectWidthStyle rect_width_style) override
 
PositionWithAffinity GetGlyphPositionAtCoordinate (double dx, double dy) override
 
std::vector< Paragraph::TextBoxGetRectsForPlaceholders () override
 
Range< size_t > GetWordBoundary (size_t offset) override
 
size_t GetLineCount ()
 
bool DidExceedMaxLines () override
 
std::vector< LineMetrics > & GetLineMetrics () override
 
void SetDirty (bool dirty=true)
 
- Public Member Functions inherited from txt::Paragraph
virtual ~Paragraph ()=default
 

Friends

class ParagraphBuilderTxt
 

Additional Inherited Members

- Public Types inherited from txt::Paragraph
enum  Affinity {
  UPSTREAM,
  DOWNSTREAM
}
 
enum  RectHeightStyle {
  RectHeightStyle::kTight,
  RectHeightStyle::kMax,
  RectHeightStyle::kIncludeLineSpacingMiddle,
  RectHeightStyle::kIncludeLineSpacingTop,
  RectHeightStyle::kIncludeLineSpacingBottom,
  RectHeightStyle::kStrut
}
 
enum  RectWidthStyle {
  RectWidthStyle::kTight,
  RectWidthStyle::kMax
}
 

Detailed Description

Definition at line 61 of file paragraph_txt.h.

Constructor & Destructor Documentation

◆ ParagraphTxt()

txt::ParagraphTxt::ParagraphTxt ( )

Definition at line 231 of file paragraph_txt.cc.

References minikin::LineBreaker::addStyleRun(), txt::PlaceholderRun::alignment, txt::PlaceholderRun::baseline, txt::PlaceholderRun::baseline_offset, txt::ParagraphStyle::break_strategy, minikin::LineBreaker::buffer(), minikin::LineBreaker::computeBreaks(), txt::StyledRuns::Run::end, minikin::LineBreaker::finish(), FML_LOG, minikin::FakedFont::font, txt::TextStyle::font_families, txt::ParagraphStyle::force_strut_height, minikin::LineBreaker::getBreaks(), txt::StyledRuns::GetRun(), minikin::LineBreaker::getWidths(), txt::PlaceholderRun::height, minikin::isLineEndSpace(), txt::italic, txt::justify, txt::kAboveBaseline, txt::kAlphabetic, txt::kBaseline, txt::kBelowBaseline, txt::kBottom, txt::kIdeographic, txt::kMiddle, txt::kTop, txt::ltr, txt::objReplacementChar, minikin::LineBreaker::resize(), txt::rtl, minikin::LineBreaker::setCustomCharWidth(), SetDirty(), minikin::LineBreaker::setJustified(), minikin::LineBreaker::setLineWidths(), minikin::LineBreaker::setLocale(), minikin::LineBreaker::setStrategy(), minikin::LineBreaker::setText(), txt::StyledRuns::size(), txt::StyledRuns::Run::start, txt::ParagraphStyle::strut_enabled, txt::ParagraphStyle::strut_font_families, txt::ParagraphStyle::strut_font_size, txt::ParagraphStyle::strut_font_style, txt::ParagraphStyle::strut_font_weight, txt::ParagraphStyle::strut_half_leading, txt::ParagraphStyle::strut_has_height_override, txt::ParagraphStyle::strut_height, txt::ParagraphStyle::strut_leading, txt::StyledRuns::Run::style, txt::ParagraphStyle::text_align, txt::ParagraphStyle::text_direction, txt::PlaceholderRun::width, and ~ParagraphTxt().

231  {
232  breaker_.setLocale();
233 }

◆ ~ParagraphTxt()

txt::ParagraphTxt::~ParagraphTxt ( )
virtualdefault

Referenced by ParagraphTxt().

Member Function Documentation

◆ DidExceedMaxLines()

bool txt::ParagraphTxt::DidExceedMaxLines ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 2012 of file paragraph_txt.cc.

References FML_DCHECK.

2012  {
2013  FML_DCHECK(!needs_layout_) << "only valid after layout";
2014  return did_exceed_max_lines_;
2015 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetAlphabeticBaseline()

double txt::ParagraphTxt::GetAlphabeticBaseline ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1327 of file paragraph_txt.cc.

References FML_DCHECK.

1327  {
1328  FML_DCHECK(!needs_layout_) << "only valid after layout";
1329  // Currently -fAscent
1330  return alphabetic_baseline_;
1331 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetGlyphPositionAtCoordinate()

Paragraph::PositionWithAffinity txt::ParagraphTxt::GetGlyphPositionAtCoordinate ( double  dx,
double  dy 
)
overridevirtual

Implements txt::Paragraph.

Definition at line 1882 of file paragraph_txt.cc.

References txt::Paragraph::DOWNSTREAM, FML_DCHECK, height, if(), txt::ltr, txt::rtl, and txt::Paragraph::UPSTREAM.

1884  {
1885  FML_DCHECK(!needs_layout_) << "only valid after layout";
1886  if (final_line_count_ <= 0)
1887  return PositionWithAffinity(0, DOWNSTREAM);
1888 
1889  size_t y_index;
1890  for (y_index = 0; y_index < final_line_count_ - 1; ++y_index) {
1891  if (dy < line_metrics_[y_index].height)
1892  break;
1893  }
1894 
1895  const std::vector<GlyphPosition>& line_glyph_position =
1896  glyph_lines_[y_index].positions;
1897  if (line_glyph_position.empty()) {
1898  int line_start_index =
1899  std::accumulate(glyph_lines_.begin(), glyph_lines_.begin() + y_index, 0,
1900  [](const int a, const GlyphLine& b) {
1901  return a + static_cast<int>(b.total_code_units);
1902  });
1903  return PositionWithAffinity(line_start_index, DOWNSTREAM);
1904  }
1905 
1906  size_t x_index;
1907  const GlyphPosition* gp = nullptr;
1908  for (x_index = 0; x_index < line_glyph_position.size(); ++x_index) {
1909  double glyph_end = (x_index < line_glyph_position.size() - 1)
1910  ? line_glyph_position[x_index + 1].x_pos.start
1911  : line_glyph_position[x_index].x_pos.end;
1912  if (dx < glyph_end) {
1913  gp = &line_glyph_position[x_index];
1914  break;
1915  }
1916  }
1917 
1918  if (gp == nullptr) {
1919  gp = &line_glyph_position.back();
1920  }
1921 
1922  // Find the direction of the run that contains this glyph.
1923  TextDirection direction = TextDirection::ltr;
1924  for (const CodeUnitRun& run : code_unit_runs_) {
1925  if (gp->code_units.start >= run.code_units.start &&
1926  gp->code_units.end <= run.code_units.end) {
1927  direction = run.direction;
1928  break;
1929  }
1930  }
1931 
1932  double glyph_center = (gp->x_pos.start + gp->x_pos.end) / 2;
1933  if ((direction == TextDirection::ltr && dx < glyph_center) ||
1934  (direction == TextDirection::rtl && dx >= glyph_center)) {
1935  return PositionWithAffinity(gp->code_units.start, DOWNSTREAM);
1936  } else {
1937  return PositionWithAffinity(gp->code_units.end, UPSTREAM);
1938  }
1939 }
if(match !=nullptr)
Definition: fl_engine.cc:81
#define FML_DCHECK(condition)
Definition: logging.h:86
int32_t height

◆ GetHeight()

double txt::ParagraphTxt::GetHeight ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1354 of file paragraph_txt.cc.

References FML_DCHECK.

1354  {
1355  FML_DCHECK(!needs_layout_) << "only valid after layout";
1356  return final_line_count_ == 0 ? 0
1357  : line_metrics_[final_line_count_ - 1].height;
1358 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetIdeographicBaseline()

double txt::ParagraphTxt::GetIdeographicBaseline ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1333 of file paragraph_txt.cc.

References FML_DCHECK.

1333  {
1334  FML_DCHECK(!needs_layout_) << "only valid after layout";
1335  // TODO(garyq): Currently -fAscent + fUnderlinePosition. Verify this.
1336  return ideographic_baseline_;
1337 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetLineCount()

size_t txt::ParagraphTxt::GetLineCount ( )

Definition at line 2007 of file paragraph_txt.cc.

References FML_DCHECK.

2007  {
2008  FML_DCHECK(!needs_layout_) << "only valid after layout";
2009  return final_line_count_;
2010 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetLineMetrics()

std::vector< LineMetrics > & txt::ParagraphTxt::GetLineMetrics ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 2021 of file paragraph_txt.cc.

References FML_DCHECK.

2021  {
2022  FML_DCHECK(!needs_layout_) << "only valid after layout";
2023  return line_metrics_;
2024 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetLongestLine()

double txt::ParagraphTxt::GetLongestLine ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1365 of file paragraph_txt.cc.

References FML_DCHECK, minikin::FakedFont::font, txt::TextStyle::font_families, minikin::FontLanguageListCache::getById(), txt::TextStyle::locale, minikin::FontStyle::registerLanguageList(), and minikin::FontLanguages::size().

1365  {
1366  FML_DCHECK(!needs_layout_) << "only valid after layout";
1367  return longest_line_;
1368 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetMaxIntrinsicWidth()

double txt::ParagraphTxt::GetMaxIntrinsicWidth ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1339 of file paragraph_txt.cc.

References FML_DCHECK.

1339  {
1340  FML_DCHECK(!needs_layout_) << "only valid after layout";
1341  return max_intrinsic_width_;
1342 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetMaxWidth()

double txt::ParagraphTxt::GetMaxWidth ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1360 of file paragraph_txt.cc.

References FML_DCHECK.

1360  {
1361  FML_DCHECK(!needs_layout_) << "only valid after layout";
1362  return width_;
1363 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetMinIntrinsicWidth()

double txt::ParagraphTxt::GetMinIntrinsicWidth ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1344 of file paragraph_txt.cc.

References FML_DCHECK.

1344  {
1345  FML_DCHECK(!needs_layout_) << "only valid after layout";
1346  return min_intrinsic_width_;
1347 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetParagraphStyle()

const ParagraphStyle & txt::ParagraphTxt::GetParagraphStyle ( ) const

Definition at line 1323 of file paragraph_txt.cc.

1323  {
1324  return paragraph_style_;
1325 }

◆ GetRectsForPlaceholders()

std::vector< Paragraph::TextBox > txt::ParagraphTxt::GetRectsForPlaceholders ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1943 of file paragraph_txt.cc.

References FML_DCHECK, txt::left, and txt::right.

1943  {
1944  FML_DCHECK(!needs_layout_) << "only valid after layout";
1945  // Struct that holds calculated metrics for each line.
1946  struct LineBoxMetrics {
1947  std::vector<Paragraph::TextBox> boxes;
1948  // Per-line metrics for max and min coordinates for left and right boxes.
1949  // These metrics cannot be calculated in layout generically because of
1950  // selections that do not cover the whole line.
1951  SkScalar max_right = std::numeric_limits<SkScalar>::lowest();
1952  SkScalar min_left = std::numeric_limits<SkScalar>::max();
1953  };
1954 
1955  std::vector<Paragraph::TextBox> boxes;
1956 
1957  // Generate initial boxes and calculate metrics.
1958  for (const CodeUnitRun& run : inline_placeholder_code_unit_runs_) {
1959  // Check to see if we are finished.
1960  double baseline = line_metrics_[run.line_number].baseline;
1961  SkScalar top = baseline + run.font_metrics.fAscent;
1962  SkScalar bottom = baseline + run.font_metrics.fDescent;
1963 
1964  if (run.placeholder_run !=
1965  nullptr) { // Use inline placeholder size as height.
1966  top = baseline - run.placeholder_run->baseline_offset;
1967  bottom = baseline + run.placeholder_run->height -
1968  run.placeholder_run->baseline_offset;
1969  }
1970 
1971  // Calculate left and right.
1972  SkScalar left, right;
1973  left = run.x_pos.start;
1974  right = run.x_pos.end;
1975 
1976  boxes.emplace_back(SkRect::MakeLTRB(left, top, right, bottom),
1977  run.direction);
1978  }
1979  return boxes;
1980 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetRectsForRange()

std::vector< Paragraph::TextBox > txt::ParagraphTxt::GetRectsForRange ( size_t  start,
size_t  end,
RectHeightStyle  rect_height_style,
RectWidthStyle  rect_width_style 
)
overridevirtual

Implements txt::Paragraph.

Definition at line 1662 of file paragraph_txt.cc.

References txt::LineMetrics::ascent, txt::LineMetrics::baseline, txt::LineMetrics::descent, txt::Paragraph::TextBox::direction, txt::LineMetrics::end_including_newline, txt::LineMetrics::end_index, FML_DCHECK, height, txt::Paragraph::kIncludeLineSpacingBottom, txt::Paragraph::kIncludeLineSpacingMiddle, txt::Paragraph::kIncludeLineSpacingTop, txt::Paragraph::kMax, txt::Paragraph::kStrut, txt::Paragraph::kTight, txt::left, txt::ltr, txt::Paragraph::TextBox::rect, txt::right, txt::rtl, txt::LineMetrics::start_index, and txt::LineMetrics::unscaled_ascent.

1666  {
1667  FML_DCHECK(!needs_layout_) << "only valid after layout";
1668  // Struct that holds calculated metrics for each line.
1669  struct LineBoxMetrics {
1670  std::vector<Paragraph::TextBox> boxes;
1671  // Per-line metrics for max and min coordinates for left and right boxes.
1672  // These metrics cannot be calculated in layout generically because of
1673  // selections that do not cover the whole line.
1674  SkScalar max_right = std::numeric_limits<SkScalar>::lowest();
1675  SkScalar min_left = std::numeric_limits<SkScalar>::max();
1676  };
1677 
1678  std::map<size_t, LineBoxMetrics> line_box_metrics;
1679  // Text direction of the first line so we can extend the correct side for
1680  // RectWidthStyle::kMax.
1681  TextDirection first_line_dir = TextDirection::ltr;
1682  std::map<size_t, size_t> newline_x_positions;
1683 
1684  // Lines that are actually in the requested range.
1685  size_t max_line = 0;
1686  size_t min_line = INT_MAX;
1687  size_t glyph_length = 0;
1688 
1689  // Generate initial boxes and calculate metrics.
1690  for (const CodeUnitRun& run : code_unit_runs_) {
1691  // Check to see if we are finished.
1692  if (run.code_units.start >= end)
1693  break;
1694 
1695  // Update new line x position with the ending of last bidi run on the line
1696  newline_x_positions[run.line_number] =
1697  run.direction == TextDirection::ltr ? run.x_pos.end : run.x_pos.start;
1698 
1699  if (run.code_units.end <= start)
1700  continue;
1701 
1702  double baseline = line_metrics_[run.line_number].baseline;
1703  SkScalar top = baseline + run.font_metrics.fAscent;
1704  SkScalar bottom = baseline + run.font_metrics.fDescent;
1705 
1706  if (run.placeholder_run !=
1707  nullptr) { // Use inline placeholder size as height.
1708  top = baseline - run.placeholder_run->baseline_offset;
1709  bottom = baseline + run.placeholder_run->height -
1710  run.placeholder_run->baseline_offset;
1711  }
1712 
1713  max_line = std::max(run.line_number, max_line);
1714  min_line = std::min(run.line_number, min_line);
1715 
1716  // Calculate left and right.
1717  SkScalar left, right;
1718  if (run.code_units.start >= start && run.code_units.end <= end) {
1719  left = run.x_pos.start;
1720  right = run.x_pos.end;
1721  } else {
1722  left = SK_ScalarMax;
1723  right = SK_ScalarMin;
1724  for (const GlyphPosition& gp : run.positions) {
1725  if (gp.code_units.start >= start && gp.code_units.end <= end) {
1726  left = std::min(left, static_cast<SkScalar>(gp.x_pos.start));
1727  right = std::max(right, static_cast<SkScalar>(gp.x_pos.end));
1728  } else if (gp.code_units.end == end) {
1729  // Calculate left and right when we are at
1730  // the last position of a combining character.
1731  glyph_length = (gp.code_units.end - gp.code_units.start) - 1;
1732  if (gp.code_units.start ==
1733  std::max<size_t>(0, (start - glyph_length))) {
1734  left = std::min(left, static_cast<SkScalar>(gp.x_pos.start));
1735  right = std::max(right, static_cast<SkScalar>(gp.x_pos.end));
1736  }
1737  }
1738  }
1739  if (left == SK_ScalarMax || right == SK_ScalarMin)
1740  continue;
1741  }
1742  // Keep track of the min and max horizontal coordinates over all lines. Not
1743  // needed for kTight.
1744  if (rect_width_style == RectWidthStyle::kMax) {
1745  line_box_metrics[run.line_number].max_right =
1746  std::max(line_box_metrics[run.line_number].max_right, right);
1747  line_box_metrics[run.line_number].min_left =
1748  std::min(line_box_metrics[run.line_number].min_left, left);
1749  if (min_line == run.line_number) {
1750  first_line_dir = run.direction;
1751  }
1752  }
1753  line_box_metrics[run.line_number].boxes.emplace_back(
1754  SkRect::MakeLTRB(left, top, right, bottom), run.direction);
1755  }
1756 
1757  // Add empty rectangles representing any newline characters within the
1758  // range.
1759  for (size_t line_number = 0; line_number < line_metrics_.size();
1760  ++line_number) {
1761  LineMetrics& line = line_metrics_[line_number];
1762  if (line.start_index >= end)
1763  break;
1764  if (line.end_including_newline <= start)
1765  continue;
1766  if (line_box_metrics.find(line_number) == line_box_metrics.end()) {
1767  if (line.end_index != line.end_including_newline &&
1768  line.end_index >= start && line.end_including_newline <= end) {
1769  SkScalar x;
1770  auto it = newline_x_positions.find(line_number);
1771  if (it != newline_x_positions.end()) {
1772  x = it->second;
1773  } else {
1774  x = GetLineXOffset(0, false);
1775  }
1776  SkScalar top =
1777  (line_number > 0) ? line_metrics_[line_number - 1].height : 0;
1778  SkScalar bottom = line_metrics_[line_number].height;
1779  line_box_metrics[line_number].boxes.emplace_back(
1780  SkRect::MakeLTRB(x, top, x, bottom), TextDirection::ltr);
1781  }
1782  }
1783  }
1784 
1785  // "Post-process" metrics and aggregate final rects to return.
1786  std::vector<Paragraph::TextBox> boxes;
1787  for (const auto& kv : line_box_metrics) {
1788  // Handle rect_width_styles. We skip the last line because not everything is
1789  // selected.
1790 
1791  LineMetrics& line =
1792  line_metrics_[fmin(line_metrics_.size() - 1, fmax(0, kv.first))];
1793  if (rect_width_style == RectWidthStyle::kMax && kv.first != max_line) {
1794  if (line_box_metrics[kv.first].min_left > min_left_ &&
1795  (kv.first != min_line || first_line_dir == TextDirection::rtl)) {
1796  line_box_metrics[kv.first].boxes.emplace_back(
1797  SkRect::MakeLTRB(min_left_, line.baseline - line.unscaled_ascent,
1798  line_box_metrics[kv.first].min_left,
1799  line.baseline + line.descent),
1801  }
1802  if (line_box_metrics[kv.first].max_right < max_right_ &&
1803  (kv.first != min_line || first_line_dir == TextDirection::ltr)) {
1804  line_box_metrics[kv.first].boxes.emplace_back(
1805  SkRect::MakeLTRB(line_box_metrics[kv.first].max_right,
1806  line.baseline - line.unscaled_ascent, max_right_,
1807  line.baseline + line.descent),
1809  }
1810  }
1811 
1812  // Handle rect_height_styles. The height metrics used are all positive to
1813  // make the signage clear here.
1814  if (rect_height_style == RectHeightStyle::kTight) {
1815  // Ignore line max height and width and generate tight bounds.
1816  boxes.insert(boxes.end(), kv.second.boxes.begin(), kv.second.boxes.end());
1817  } else if (rect_height_style == RectHeightStyle::kMax) {
1818  for (const Paragraph::TextBox& box : kv.second.boxes) {
1819  boxes.emplace_back(
1820  SkRect::MakeLTRB(box.rect.fLeft, line.baseline - line.ascent,
1821  box.rect.fRight, line.baseline + line.descent),
1822  box.direction);
1823  }
1824  } else if (rect_height_style ==
1826  SkScalar adjusted_bottom = line.baseline + line.descent;
1827  if (kv.first < line_metrics_.size() - 1) {
1828  adjusted_bottom += (line_metrics_[kv.first + 1].ascent -
1829  line_metrics_[kv.first + 1].unscaled_ascent) /
1830  2;
1831  }
1832  SkScalar adjusted_top = line.baseline - line.unscaled_ascent;
1833  if (kv.first != 0) {
1834  adjusted_top -= (line.ascent - line.unscaled_ascent) / 2;
1835  }
1836  for (const Paragraph::TextBox& box : kv.second.boxes) {
1837  boxes.emplace_back(SkRect::MakeLTRB(box.rect.fLeft, adjusted_top,
1838  box.rect.fRight, adjusted_bottom),
1839  box.direction);
1840  }
1841  } else if (rect_height_style == RectHeightStyle::kIncludeLineSpacingTop) {
1842  for (const Paragraph::TextBox& box : kv.second.boxes) {
1843  SkScalar adjusted_top = kv.first == 0
1844  ? line.baseline - line.unscaled_ascent
1845  : line.baseline - line.ascent;
1846  boxes.emplace_back(
1847  SkRect::MakeLTRB(box.rect.fLeft, adjusted_top, box.rect.fRight,
1848  line.baseline + line.descent),
1849  box.direction);
1850  }
1851  } else if (rect_height_style ==
1853  for (const Paragraph::TextBox& box : kv.second.boxes) {
1854  SkScalar adjusted_bottom = line.baseline + line.descent;
1855  if (kv.first < line_metrics_.size() - 1) {
1856  adjusted_bottom += -line.unscaled_ascent + line.ascent;
1857  }
1858  boxes.emplace_back(
1859  SkRect::MakeLTRB(box.rect.fLeft,
1860  line.baseline - line.unscaled_ascent,
1861  box.rect.fRight, adjusted_bottom),
1862  box.direction);
1863  }
1864  } else if (rect_height_style == RectHeightStyle::kStrut) {
1865  if (IsStrutValid()) {
1866  for (const Paragraph::TextBox& box : kv.second.boxes) {
1867  boxes.emplace_back(
1868  SkRect::MakeLTRB(box.rect.fLeft, line.baseline - strut_.ascent,
1869  box.rect.fRight, line.baseline + strut_.descent),
1870  box.direction);
1871  }
1872  } else {
1873  // Fall back to tight bounds if the strut is invalid.
1874  boxes.insert(boxes.end(), kv.second.boxes.begin(),
1875  kv.second.boxes.end());
1876  }
1877  }
1878  }
1879  return boxes;
1880 }
#define FML_DCHECK(condition)
Definition: logging.h:86
int32_t height

◆ GetWordBoundary()

Paragraph::Range< size_t > txt::ParagraphTxt::GetWordBoundary ( size_t  offset)
overridevirtual

Implements txt::Paragraph.

Definition at line 1982 of file paragraph_txt.cc.

References FML_DCHECK.

1982  {
1983  FML_DCHECK(!needs_layout_) << "only valid after layout";
1984  if (text_.size() == 0)
1985  return Range<size_t>(0, 0);
1986 
1987  if (!word_breaker_) {
1988  UErrorCode status = U_ZERO_ERROR;
1989  word_breaker_.reset(
1990  icu::BreakIterator::createWordInstance(icu::Locale(), status));
1991  if (!U_SUCCESS(status))
1992  return Range<size_t>(0, 0);
1993  }
1994 
1995  icu::UnicodeString icu_text(false, text_.data(), text_.size());
1996  word_breaker_->setText(icu_text);
1997 
1998  int32_t prev_boundary = word_breaker_->preceding(offset + 1);
1999  int32_t next_boundary = word_breaker_->next();
2000  if (prev_boundary == icu::BreakIterator::DONE)
2001  prev_boundary = offset;
2002  if (next_boundary == icu::BreakIterator::DONE)
2003  next_boundary = offset;
2004  return Range<size_t>(prev_boundary, next_boundary);
2005 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ Layout()

void txt::ParagraphTxt::Layout ( double  width)
overridevirtual

Implements txt::Paragraph.

Definition at line 667 of file paragraph_txt.cc.

References txt::LineMetrics::ascent, txt::LineMetrics::baseline, txt::PlaceholderRun::baseline_offset, txt::center, txt::LineMetrics::descent, minikin::Layout::doLayout(), txt::ParagraphStyle::effective_align(), txt::ParagraphStyle::ellipsis, txt::ParagraphStyle::ellipsized(), txt::Paragraph::Range< T >::end, txt::LineMetrics::end_excluding_whitespace, txt::LineMetrics::end_index, FML_DCHECK, txt::TextStyle::font_size, minikin::Layout::getAdvance(), minikin::Layout::getAdvances(), minikin::Layout::getCharAdvance(), minikin::Layout::getGlyphCluster(), minikin::Layout::getGlyphId(), txt::ParagraphStyle::GetTextStyle(), minikin::Layout::getX(), minikin::Layout::getY(), txt::TextStyle::half_leading, txt::LineMetrics::hard_break, txt::TextStyle::has_height_override, txt::LineMetrics::height, txt::TextStyle::height, minikin::GraphemeBreak::isGraphemeBreak(), txt::justify, txt::kDisableFirstAscent, txt::kDisableLastDescent, txt::LineMetrics::left, txt::LineMetrics::line_number, txt::ltr, txt::ParagraphStyle::max_lines, minikin::Layout::measureText(), minikin::Layout::nGlyphs(), txt::objReplacementChar, txt::right, txt::rtl, txt::LineMetrics::run_metrics, txt::Paragraph::Range< T >::start, txt::LineMetrics::start_index, fml::swap(), txt::ParagraphStyle::text_align, txt::ParagraphStyle::text_direction, txt::ParagraphStyle::text_height_behavior, txt::ParagraphStyle::unlimited_lines(), txt::LineMetrics::unscaled_ascent, txt::LineMetrics::width, and txt::Paragraph::Range< T >::width().

667  {
668  double rounded_width = floor(width);
669  // Do not allow calling layout multiple times without changing anything.
670  if (!needs_layout_ && rounded_width == width_) {
671  return;
672  }
673 
674  width_ = rounded_width;
675 
676  needs_layout_ = false;
677 
678  records_.clear();
679  glyph_lines_.clear();
680  code_unit_runs_.clear();
681  inline_placeholder_code_unit_runs_.clear();
682  max_right_ = std::numeric_limits<double>::lowest();
683  min_left_ = std::numeric_limits<double>::max();
684  final_line_count_ = 0;
685 
686  if (!ComputeLineBreaks())
687  return;
688 
689  std::vector<BidiRun> bidi_runs;
690  if (!ComputeBidiRuns(&bidi_runs))
691  return;
692 
693  SkFont font;
694  font.setEdging(SkFont::Edging::kAntiAlias);
695  font.setSubpixel(true);
696  font.setHinting(SkFontHinting::kSlight);
697 
698  minikin::Layout layout;
699  SkTextBlobBuilder builder;
700  double y_offset = 0;
701  double prev_max_descent = 0;
702  double max_word_width = 0;
703 
704  // Compute strut minimums according to paragraph_style_.
705  ComputeStrut(&strut_, font);
706 
707  // Paragraph bounds tracking.
708  size_t line_limit =
709  std::min(paragraph_style_.max_lines, line_metrics_.size());
710  did_exceed_max_lines_ = (line_metrics_.size() > paragraph_style_.max_lines);
711 
712  size_t placeholder_run_index = 0;
713  for (size_t line_number = 0; line_number < line_limit; ++line_number) {
714  LineMetrics& line_metrics = line_metrics_[line_number];
715 
716  // Break the line into words if justification should be applied.
717  std::vector<Range<size_t>> words;
718  double word_gap_width = 0;
719  size_t word_index = 0;
720  bool justify_line =
721  (paragraph_style_.text_align == TextAlign::justify &&
722  line_number != line_limit - 1 && !line_metrics.hard_break);
723  FindWords(text_, line_metrics.start_index, line_metrics.end_index, &words);
724  if (justify_line) {
725  if (words.size() > 1) {
726  word_gap_width =
727  (width_ - line_widths_[line_number]) / (words.size() - 1);
728  }
729  }
730 
731  // Exclude trailing whitespace from justified lines so the last visible
732  // character in the line will be flush with the right margin.
733  size_t line_end_index =
734  (paragraph_style_.effective_align() == TextAlign::right ||
735  paragraph_style_.effective_align() == TextAlign::center ||
736  paragraph_style_.effective_align() == TextAlign::justify)
737  ? line_metrics.end_excluding_whitespace
738  : line_metrics.end_index;
739 
740  // Find the runs comprising this line.
741  std::vector<BidiRun> line_runs;
742  for (const BidiRun& bidi_run : bidi_runs) {
743  // A "ghost" run is a run that does not impact the layout, breaking,
744  // alignment, width, etc but is still "visible" through getRectsForRange.
745  // For example, trailing whitespace on centered text can be scrolled
746  // through with the caret but will not wrap the line.
747  //
748  // Here, we add an additional run for the whitespace, but dont
749  // let it impact metrics. After layout of the whitespace run, we do not
750  // add its width into the x-offset adjustment, effectively nullifying its
751  // impact on the layout.
752  std::unique_ptr<BidiRun> ghost_run = nullptr;
753  if (paragraph_style_.ellipsis.empty() &&
754  line_metrics.end_excluding_whitespace < line_metrics.end_index &&
755  bidi_run.start() <= line_metrics.end_index &&
756  bidi_run.end() > line_end_index) {
757  ghost_run = std::make_unique<BidiRun>(
758  std::max(bidi_run.start(), line_end_index),
759  std::min(bidi_run.end(), line_metrics.end_index),
760  bidi_run.direction(), bidi_run.style(), true);
761  }
762  // Include the ghost run before normal run if RTL
763  if (bidi_run.direction() == TextDirection::rtl && ghost_run != nullptr) {
764  line_runs.push_back(*ghost_run);
765  }
766  // Emplace a normal line run.
767  if (bidi_run.start() < line_end_index &&
768  bidi_run.end() > line_metrics.start_index) {
769  // The run is a placeholder run.
770  if (bidi_run.size() == 1 &&
771  text_[bidi_run.start()] == objReplacementChar &&
772  obj_replacement_char_indexes_.count(bidi_run.start()) != 0 &&
773  placeholder_run_index < inline_placeholders_.size()) {
774  line_runs.emplace_back(
775  std::max(bidi_run.start(), line_metrics.start_index),
776  std::min(bidi_run.end(), line_end_index), bidi_run.direction(),
777  bidi_run.style(), inline_placeholders_[placeholder_run_index]);
778  placeholder_run_index++;
779  } else {
780  line_runs.emplace_back(
781  std::max(bidi_run.start(), line_metrics.start_index),
782  std::min(bidi_run.end(), line_end_index), bidi_run.direction(),
783  bidi_run.style());
784  }
785  }
786  // Include the ghost run after normal run if LTR
787  if (bidi_run.direction() == TextDirection::ltr && ghost_run != nullptr) {
788  line_runs.push_back(*ghost_run);
789  }
790  }
791  bool line_runs_all_rtl =
792  line_runs.size() &&
793  std::accumulate(
794  line_runs.begin(), line_runs.end(), true,
795  [](const bool a, const BidiRun& b) { return a && b.is_rtl(); });
796  if (line_runs_all_rtl) {
797  std::reverse(words.begin(), words.end());
798  }
799 
800  std::vector<GlyphPosition> line_glyph_positions;
801  std::vector<CodeUnitRun> line_code_unit_runs;
802  std::vector<CodeUnitRun> line_inline_placeholder_code_unit_runs;
803 
804  double run_x_offset = 0;
805  double justify_x_offset = 0;
806  std::vector<PaintRecord> paint_records;
807 
808  for (auto line_run_it = line_runs.begin(); line_run_it != line_runs.end();
809  ++line_run_it) {
810  const BidiRun& run = *line_run_it;
811  minikin::FontStyle minikin_font;
812  minikin::MinikinPaint minikin_paint;
813  GetFontAndMinikinPaint(run.style(), &minikin_font, &minikin_paint);
814  font.setSize(run.style().font_size);
815 
816  std::shared_ptr<minikin::FontCollection> minikin_font_collection =
817  GetMinikinFontCollectionForStyle(run.style());
818  if (!minikin_font_collection) {
819  return;
820  }
821 
822  // Lay out this run.
823  uint16_t* text_ptr = text_.data();
824  size_t text_start = run.start();
825  size_t text_count = run.end() - run.start();
826  size_t text_size = text_.size();
827 
828  // Apply ellipsizing if the run was not completely laid out and this
829  // is the last line (or lines are unlimited).
830  const std::u16string& ellipsis = paragraph_style_.ellipsis;
831  std::vector<uint16_t> ellipsized_text;
832  if (ellipsis.length() && !isinf(width_) && !line_metrics.hard_break &&
833  line_run_it == line_runs.end() - 1 &&
834  (line_number == line_limit - 1 ||
835  paragraph_style_.unlimited_lines())) {
836  float ellipsis_width = layout.measureText(
837  reinterpret_cast<const uint16_t*>(ellipsis.data()), 0,
838  ellipsis.length(), ellipsis.length(), run.is_rtl(), minikin_font,
839  minikin_paint, minikin_font_collection, nullptr);
840 
841  std::vector<float> text_advances(text_count);
842  float text_width =
843  layout.measureText(text_ptr, text_start, text_count, text_.size(),
844  run.is_rtl(), minikin_font, minikin_paint,
845  minikin_font_collection, text_advances.data());
846 
847  // Truncate characters from the text until the ellipsis fits.
848  size_t truncate_count = 0;
849  while (truncate_count < text_count &&
850  run_x_offset + text_width + ellipsis_width > width_) {
851  text_width -= text_advances[text_count - truncate_count - 1];
852  truncate_count++;
853  }
854 
855  ellipsized_text.reserve(text_count - truncate_count +
856  ellipsis.length());
857  ellipsized_text.insert(ellipsized_text.begin(),
858  text_.begin() + run.start(),
859  text_.begin() + run.end() - truncate_count);
860  ellipsized_text.insert(ellipsized_text.end(), ellipsis.begin(),
861  ellipsis.end());
862  text_ptr = ellipsized_text.data();
863  text_start = 0;
864  text_count = ellipsized_text.size();
865  text_size = text_count;
866 
867  // If there is no line limit, then skip all lines after the ellipsized
868  // line.
869  if (paragraph_style_.unlimited_lines()) {
870  line_limit = line_number + 1;
871  did_exceed_max_lines_ = true;
872  }
873  }
874 
875  layout.doLayout(text_ptr, text_start, text_count, text_size, run.is_rtl(),
876  minikin_font, minikin_paint, minikin_font_collection);
877 
878  if (layout.nGlyphs() == 0)
879  continue;
880 
881  // When laying out RTL ghost runs, shift the run_x_offset here by the
882  // advance so that the ghost run is positioned to the left of the first
883  // real run of text in the line. However, since we do not want it to
884  // impact the layout of real text, this advance is subsequently added
885  // back into the run_x_offset after the ghost run positions have been
886  // calcuated and before the next real run of text is laid out, ensuring
887  // later runs are laid out in the same position as if there were no ghost
888  // run.
889  if (run.is_ghost() && run.is_rtl())
890  run_x_offset -= layout.getAdvance();
891 
892  std::vector<float> layout_advances(text_count);
893  layout.getAdvances(layout_advances.data());
894 
895  // Break the layout into blobs that share the same SkPaint parameters.
896  std::vector<Range<size_t>> glyph_blobs = GetLayoutTypefaceRuns(layout);
897 
898  double word_start_position = std::numeric_limits<double>::quiet_NaN();
899 
900  // Build a Skia text blob from each group of glyphs.
901  for (const Range<size_t>& glyph_blob : glyph_blobs) {
902  std::vector<GlyphPosition> glyph_positions;
903 
904  GetGlyphTypeface(layout, glyph_blob.start).apply(font);
905  const SkTextBlobBuilder::RunBuffer& blob_buffer =
906  builder.allocRunPos(font, glyph_blob.end - glyph_blob.start);
907 
908  double justify_x_offset_delta = 0;
909  for (size_t glyph_index = glyph_blob.start;
910  glyph_index < glyph_blob.end;) {
911  size_t cluster_start_glyph_index = glyph_index;
912  uint32_t cluster = layout.getGlyphCluster(cluster_start_glyph_index);
913  double glyph_x_offset;
914  // Add all the glyphs in this cluster to the text blob.
915  do {
916  size_t blob_index = glyph_index - glyph_blob.start;
917  blob_buffer.glyphs[blob_index] = layout.getGlyphId(glyph_index);
918 
919  size_t pos_index = blob_index * 2;
920  blob_buffer.pos[pos_index] = layout.getX(glyph_index) +
921  justify_x_offset +
922  justify_x_offset_delta;
923  blob_buffer.pos[pos_index + 1] = layout.getY(glyph_index);
924 
925  if (glyph_index == cluster_start_glyph_index)
926  glyph_x_offset = blob_buffer.pos[pos_index];
927 
928  glyph_index++;
929  } while (glyph_index < glyph_blob.end &&
930  layout.getGlyphCluster(glyph_index) == cluster);
931 
932  Range<int32_t> glyph_code_units(cluster, 0);
933  std::vector<size_t> grapheme_code_unit_counts;
934  if (run.is_rtl()) {
935  if (cluster_start_glyph_index > 0) {
936  glyph_code_units.end =
937  layout.getGlyphCluster(cluster_start_glyph_index - 1);
938  } else {
939  glyph_code_units.end = text_count;
940  }
941  grapheme_code_unit_counts.push_back(glyph_code_units.width());
942  } else {
943  if (glyph_index < layout.nGlyphs()) {
944  glyph_code_units.end = layout.getGlyphCluster(glyph_index);
945  } else {
946  glyph_code_units.end = text_count;
947  }
948 
949  // The glyph may be a ligature. Determine how many graphemes are
950  // joined into this glyph and how many input code units map to
951  // each grapheme.
952  size_t code_unit_count = 1;
953  for (int32_t offset = glyph_code_units.start + 1;
954  offset < glyph_code_units.end; ++offset) {
956  layout_advances.data(), text_ptr, text_start, text_count,
957  text_start + offset)) {
958  grapheme_code_unit_counts.push_back(code_unit_count);
959  code_unit_count = 1;
960  } else {
961  code_unit_count++;
962  }
963  }
964  grapheme_code_unit_counts.push_back(code_unit_count);
965  }
966  float glyph_advance;
967  if (run.is_placeholder_run()) {
968  // The placeholder run's layout should yield one glyph representing
969  // the object replacement character. Replace its width with the
970  // placeholder's width.
971  FML_DCHECK(layout.nGlyphs() == 1);
972  glyph_advance = run.placeholder_run()->width;
973  } else {
974  glyph_advance = layout.getCharAdvance(glyph_code_units.start);
975  }
976  float grapheme_advance =
977  glyph_advance / grapheme_code_unit_counts.size();
978 
979  glyph_positions.emplace_back(run_x_offset + glyph_x_offset,
980  grapheme_advance,
981  run.start() + glyph_code_units.start,
982  grapheme_code_unit_counts[0]);
983 
984  // Compute positions for the additional graphemes in the ligature.
985  for (size_t i = 1; i < grapheme_code_unit_counts.size(); ++i) {
986  glyph_positions.emplace_back(
987  glyph_positions.back().x_pos.end, grapheme_advance,
988  glyph_positions.back().code_units.start +
989  grapheme_code_unit_counts[i - 1],
990  grapheme_code_unit_counts[i]);
991  }
992 
993  bool at_word_start = false;
994  bool at_word_end = false;
995  if (word_index < words.size()) {
996  at_word_start =
997  words[word_index].start == run.start() + glyph_code_units.start;
998  at_word_end =
999  words[word_index].end == run.start() + glyph_code_units.end;
1000  if (line_runs_all_rtl) {
1001  std::swap(at_word_start, at_word_end);
1002  }
1003  }
1004 
1005  if (at_word_start) {
1006  word_start_position = run_x_offset + glyph_x_offset;
1007  }
1008 
1009  if (at_word_end) {
1010  if (justify_line) {
1011  justify_x_offset_delta += word_gap_width;
1012  }
1013  word_index++;
1014 
1015  if (!isnan(word_start_position)) {
1016  double word_width =
1017  glyph_positions.back().x_pos.end - word_start_position;
1018  max_word_width = std::max(word_width, max_word_width);
1019  word_start_position = std::numeric_limits<double>::quiet_NaN();
1020  }
1021  }
1022  } // for each in glyph_blob
1023 
1024  if (glyph_positions.empty())
1025  continue;
1026 
1027  // Store the font metrics and TextStyle in the LineMetrics for this line
1028  // to provide metrics upon user request. We index this RunMetrics
1029  // instance at `run.end() - 1` to allow map::lower_bound to access the
1030  // correct RunMetrics at any text index.
1031  size_t run_key = run.end() - 1;
1032  line_metrics.run_metrics.emplace(run_key, &run.style());
1033  SkFontMetrics* metrics =
1034  &line_metrics.run_metrics.at(run_key).font_metrics;
1035  font.getMetrics(metrics);
1036 
1037  Range<double> record_x_pos(
1038  glyph_positions.front().x_pos.start - run_x_offset,
1039  glyph_positions.back().x_pos.end - run_x_offset);
1040  paint_records.emplace_back(run.style(), SkPoint::Make(run_x_offset, 0),
1041  builder.make(), *metrics, line_number,
1042  record_x_pos.start, record_x_pos.end,
1043  run.is_ghost(), run.placeholder_run());
1044 
1045  justify_x_offset += justify_x_offset_delta;
1046 
1047  line_glyph_positions.insert(line_glyph_positions.end(),
1048  glyph_positions.begin(),
1049  glyph_positions.end());
1050 
1051  // Add a record of glyph positions sorted by code unit index.
1052  std::vector<GlyphPosition> code_unit_positions(glyph_positions);
1053  std::sort(code_unit_positions.begin(), code_unit_positions.end(),
1054  [](const GlyphPosition& a, const GlyphPosition& b) {
1055  return a.code_units.start < b.code_units.start;
1056  });
1057 
1058  double blob_x_pos_start = glyph_positions.front().x_pos.start;
1059  double blob_x_pos_end = glyph_positions.back().x_pos.end;
1060  line_code_unit_runs.emplace_back(
1061  std::move(code_unit_positions),
1062  Range<size_t>(run.start(), run.end()),
1063  Range<double>(blob_x_pos_start, blob_x_pos_end), line_number,
1064  *metrics, run.style(), run.direction(), run.placeholder_run());
1065 
1066  if (run.is_placeholder_run()) {
1067  line_inline_placeholder_code_unit_runs.push_back(
1068  line_code_unit_runs.back());
1069  }
1070 
1071  if (!run.is_ghost()) {
1072  min_left_ = std::min(min_left_, blob_x_pos_start);
1073  max_right_ = std::max(max_right_, blob_x_pos_end);
1074  }
1075  } // for each in glyph_blobs
1076 
1077  if (run.is_placeholder_run()) {
1078  run_x_offset += run.placeholder_run()->width;
1079  } else {
1080  // Do not increase x offset for LTR trailing ghost runs as it should not
1081  // impact the layout of visible glyphs. RTL tailing ghost runs have the
1082  // advance subtracted, so we do add the advance here to reset the
1083  // run_x_offset. We do keep the record though so GetRectsForRange() can
1084  // find metrics for trailing spaces.
1085  if (!run.is_ghost() || run.is_rtl()) {
1086  run_x_offset += layout.getAdvance();
1087  }
1088  }
1089  } // for each in line_runs
1090 
1091  // Adjust the glyph positions based on the alignment of the line.
1092  double line_x_offset = GetLineXOffset(run_x_offset, justify_line);
1093  if (line_x_offset) {
1094  for (CodeUnitRun& code_unit_run : line_code_unit_runs) {
1095  code_unit_run.Shift(line_x_offset);
1096  }
1097  for (CodeUnitRun& code_unit_run :
1098  line_inline_placeholder_code_unit_runs) {
1099  code_unit_run.Shift(line_x_offset);
1100  }
1101  for (GlyphPosition& position : line_glyph_positions) {
1102  position.Shift(line_x_offset);
1103  }
1104  }
1105 
1106  size_t next_line_start = (line_number < line_metrics_.size() - 1)
1107  ? line_metrics_[line_number + 1].start_index
1108  : text_.size();
1109  glyph_lines_.emplace_back(std::move(line_glyph_positions),
1110  next_line_start - line_metrics.start_index);
1111  code_unit_runs_.insert(code_unit_runs_.end(), line_code_unit_runs.begin(),
1112  line_code_unit_runs.end());
1113  inline_placeholder_code_unit_runs_.insert(
1114  inline_placeholder_code_unit_runs_.end(),
1115  line_inline_placeholder_code_unit_runs.begin(),
1116  line_inline_placeholder_code_unit_runs.end());
1117 
1118  // Calculate the amount to advance in the y direction. This is done by
1119  // computing the maximum ascent and descent with respect to the strut.
1120  double max_ascent = IsStrutValid() ? strut_.ascent + strut_.half_leading
1121  : std::numeric_limits<double>::lowest();
1122  double max_descent = IsStrutValid() ? strut_.descent + strut_.half_leading
1123  : std::numeric_limits<double>::lowest();
1124  double max_unscaled_ascent = 0;
1125  for (const PaintRecord& paint_record : paint_records) {
1126  UpdateLineMetrics(paint_record.metrics(), paint_record.style(),
1127  max_ascent, max_descent, max_unscaled_ascent,
1128  paint_record.GetPlaceholderRun(), line_number,
1129  line_limit);
1130  }
1131 
1132  // If no fonts were actually rendered, then compute a baseline based on the
1133  // font of the paragraph style.
1134  if (paint_records.empty()) {
1135  SkFontMetrics metrics;
1136  TextStyle style(paragraph_style_.GetTextStyle());
1137  font.setTypeface(GetDefaultSkiaTypeface(style));
1138  font.setSize(style.font_size);
1139  font.getMetrics(&metrics);
1140  UpdateLineMetrics(metrics, style, max_ascent, max_descent,
1141  max_unscaled_ascent, nullptr, line_number, line_limit);
1142  }
1143 
1144  // Calculate the baselines. This is only done on the first line.
1145  if (line_number == 0) {
1146  alphabetic_baseline_ = max_ascent;
1147  // TODO(garyq): Ideographic baseline is currently bottom of EM
1148  // box, which is not correct. This should be obtained from metrics.
1149  // Skia currently does not support various baselines.
1150  ideographic_baseline_ = (max_ascent + max_descent);
1151  }
1152 
1153  line_metrics.height =
1154  (line_number == 0 ? 0 : line_metrics_[line_number - 1].height) +
1155  round(max_ascent + max_descent);
1156  line_metrics.baseline = line_metrics.height - max_descent;
1157 
1158  y_offset += round(max_ascent + prev_max_descent);
1159  prev_max_descent = max_descent;
1160 
1161  line_metrics.line_number = line_number;
1162  line_metrics.ascent = max_ascent;
1163  line_metrics.descent = max_descent;
1164  line_metrics.unscaled_ascent = max_unscaled_ascent;
1165  line_metrics.width = line_widths_[line_number];
1166  line_metrics.left = line_x_offset;
1167 
1168  final_line_count_++;
1169 
1170  for (PaintRecord& paint_record : paint_records) {
1171  paint_record.SetOffset(
1172  SkPoint::Make(paint_record.offset().x() + line_x_offset, y_offset));
1173  records_.emplace_back(std::move(paint_record));
1174  }
1175  } // for each line_number
1176 
1177  if (paragraph_style_.max_lines == 1 ||
1178  (paragraph_style_.unlimited_lines() && paragraph_style_.ellipsized())) {
1179  min_intrinsic_width_ = max_intrinsic_width_;
1180  } else {
1181  min_intrinsic_width_ = std::min(max_word_width, max_intrinsic_width_);
1182  }
1183 
1184  std::sort(code_unit_runs_.begin(), code_unit_runs_.end(),
1185  [](const CodeUnitRun& a, const CodeUnitRun& b) {
1186  return a.code_units.start < b.code_units.start;
1187  });
1188 
1189  longest_line_ = max_right_ - min_left_;
1190 }
#define FML_DCHECK(condition)
Definition: logging.h:86
static bool isGraphemeBreak(const float *advances, const uint16_t *buf, size_t start, size_t count, size_t offset)
TextStyle GetTextStyle() const
float getAdvance() const
bool ellipsized() const
std::u16string ellipsis
TextAlign effective_align() const
float getCharAdvance(size_t i) const
Definition: Layout.h:113
size_t nGlyphs() const
void swap(scoped_nsprotocol< C > &p1, scoped_nsprotocol< C > &p2)
unsigned int getGlyphId(int i) const
float getY(int i) const
const int objReplacementChar
Definition: paragraph_txt.h:48
float getX(int i) const
void doLayout(const uint16_t *buf, size_t start, size_t count, size_t bufSize, bool isRtl, const FontStyle &style, const MinikinPaint &paint, const std::shared_ptr< FontCollection > &collection)
int32_t width
uint32_t getGlyphCluster(int i) const
bool unlimited_lines() const
static float measureText(const uint16_t *buf, size_t start, size_t count, size_t bufSize, bool isRtl, const FontStyle &style, const MinikinPaint &paint, const std::shared_ptr< FontCollection > &collection, float *advances)
void getAdvances(float *advances)

◆ Paint()

void txt::ParagraphTxt::Paint ( SkCanvas *  canvas,
double  x,
double  y 
)
overridevirtual

Implements txt::Paragraph.

Definition at line 1410 of file paragraph_txt.cc.

References txt::TextStyle::background, txt::TextStyle::color, txt::TextStyle::decoration, txt::TextStyle::decoration_color, txt::TextStyle::decoration_style, txt::TextStyle::decoration_thickness_multiplier, txt::TextStyle::font_size, txt::PaintRecord::GetRunWidth(), txt::TextStyle::has_background, if(), txt::PaintRecord::isGhost(), txt::kDashed, txt::kDotted, txt::kDouble, txt::kDoubleDecorationSpacing, txt::kLineThrough, txt::kNone, txt::kOverline, txt::kSolid, txt::kUnderline, txt::kWavy, txt::PaintRecord::metrics(), txt::PaintRecord::offset(), flutter::path, txt::PaintRecord::style(), txt::PaintRecord::text(), txt::TextStyle::text_shadows, width, txt::PaintRecord::x_end(), and txt::PaintRecord::x_start().

1410  {
1411  SkPoint base_offset = SkPoint::Make(x, y);
1412  SkPaint paint;
1413  // Paint the background first before painting any text to prevent
1414  // potential overlap.
1415  for (const PaintRecord& record : records_) {
1416  PaintBackground(canvas, record, base_offset);
1417  }
1418  for (const PaintRecord& record : records_) {
1419  if (record.style().has_foreground) {
1420  paint = record.style().foreground;
1421  } else {
1422  paint.reset();
1423  paint.setColor(record.style().color);
1424  }
1425  SkPoint offset = base_offset + record.offset();
1426  if (record.GetPlaceholderRun() == nullptr) {
1427  PaintShadow(canvas, record, offset);
1428  canvas->drawTextBlob(record.text(), offset.x(), offset.y(), paint);
1429  }
1430  PaintDecorations(canvas, record, base_offset);
1431  }
1432 }

◆ SetDirty()

void txt::ParagraphTxt::SetDirty ( bool  dirty = true)

Definition at line 2017 of file paragraph_txt.cc.

Referenced by ParagraphTxt().

2017  {
2018  needs_layout_ = dirty;
2019 }

◆ TextSize()

size_t txt::ParagraphTxt::TextSize ( ) const

Definition at line 1349 of file paragraph_txt.cc.

References FML_DCHECK.

1349  {
1350  FML_DCHECK(!needs_layout_) << "only valid after layout";
1351  return text_.size();
1352 }
#define FML_DCHECK(condition)
Definition: logging.h:86

Friends And Related Function Documentation

◆ ParagraphBuilderTxt

friend class ParagraphBuilderTxt
friend

Definition at line 130 of file paragraph_txt.h.


The documentation for this class was generated from the following files: