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_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(icu::Locale(), nullptr);
233 }
void setLocale(const icu::Locale &locale, Hyphenator *hyphenator)

◆ ~ParagraphTxt()

txt::ParagraphTxt::~ParagraphTxt ( )
virtualdefault

Referenced by ParagraphTxt().

Member Function Documentation

◆ DidExceedMaxLines()

bool txt::ParagraphTxt::DidExceedMaxLines ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1979 of file paragraph_txt.cc.

References FML_DCHECK.

1979  {
1980  FML_DCHECK(!needs_layout_) << "only valid after layout";
1981  return did_exceed_max_lines_;
1982 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetAlphabeticBaseline()

double txt::ParagraphTxt::GetAlphabeticBaseline ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1294 of file paragraph_txt.cc.

References FML_DCHECK.

1294  {
1295  FML_DCHECK(!needs_layout_) << "only valid after layout";
1296  // Currently -fAscent
1297  return alphabetic_baseline_;
1298 }
#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 1849 of file paragraph_txt.cc.

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

1851  {
1852  FML_DCHECK(!needs_layout_) << "only valid after layout";
1853  if (final_line_count_ <= 0)
1854  return PositionWithAffinity(0, DOWNSTREAM);
1855 
1856  size_t y_index;
1857  for (y_index = 0; y_index < final_line_count_ - 1; ++y_index) {
1858  if (dy < line_metrics_[y_index].height)
1859  break;
1860  }
1861 
1862  const std::vector<GlyphPosition>& line_glyph_position =
1863  glyph_lines_[y_index].positions;
1864  if (line_glyph_position.empty()) {
1865  int line_start_index =
1866  std::accumulate(glyph_lines_.begin(), glyph_lines_.begin() + y_index, 0,
1867  [](const int a, const GlyphLine& b) {
1868  return a + static_cast<int>(b.total_code_units);
1869  });
1870  return PositionWithAffinity(line_start_index, DOWNSTREAM);
1871  }
1872 
1873  size_t x_index;
1874  const GlyphPosition* gp = nullptr;
1875  for (x_index = 0; x_index < line_glyph_position.size(); ++x_index) {
1876  double glyph_end = (x_index < line_glyph_position.size() - 1)
1877  ? line_glyph_position[x_index + 1].x_pos.start
1878  : line_glyph_position[x_index].x_pos.end;
1879  if (dx < glyph_end) {
1880  gp = &line_glyph_position[x_index];
1881  break;
1882  }
1883  }
1884 
1885  if (gp == nullptr) {
1886  const GlyphPosition& last_glyph = line_glyph_position.back();
1887  return PositionWithAffinity(last_glyph.code_units.end, UPSTREAM);
1888  }
1889 
1890  // Find the direction of the run that contains this glyph.
1891  TextDirection direction = TextDirection::ltr;
1892  for (const CodeUnitRun& run : code_unit_runs_) {
1893  if (gp->code_units.start >= run.code_units.start &&
1894  gp->code_units.end <= run.code_units.end) {
1895  direction = run.direction;
1896  break;
1897  }
1898  }
1899 
1900  double glyph_center = (gp->x_pos.start + gp->x_pos.end) / 2;
1901  if ((direction == TextDirection::ltr && dx < glyph_center) ||
1902  (direction == TextDirection::rtl && dx >= glyph_center)) {
1903  return PositionWithAffinity(gp->code_units.start, DOWNSTREAM);
1904  } else {
1905  return PositionWithAffinity(gp->code_units.end, UPSTREAM);
1906  }
1907 }
#define FML_DCHECK(condition)
Definition: logging.h:86
int32_t height
if(event->type==GDK_BUTTON_PRESS)
Definition: fl_view.cc:79

◆ GetHeight()

double txt::ParagraphTxt::GetHeight ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1321 of file paragraph_txt.cc.

References FML_DCHECK.

1321  {
1322  FML_DCHECK(!needs_layout_) << "only valid after layout";
1323  return final_line_count_ == 0 ? 0
1324  : line_metrics_[final_line_count_ - 1].height;
1325 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetIdeographicBaseline()

double txt::ParagraphTxt::GetIdeographicBaseline ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1300 of file paragraph_txt.cc.

References FML_DCHECK.

1300  {
1301  FML_DCHECK(!needs_layout_) << "only valid after layout";
1302  // TODO(garyq): Currently -fAscent + fUnderlinePosition. Verify this.
1303  return ideographic_baseline_;
1304 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetLineCount()

size_t txt::ParagraphTxt::GetLineCount ( )

Definition at line 1974 of file paragraph_txt.cc.

References FML_DCHECK.

1974  {
1975  FML_DCHECK(!needs_layout_) << "only valid after layout";
1976  return final_line_count_;
1977 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetLineMetrics()

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

Implements txt::Paragraph.

Definition at line 1988 of file paragraph_txt.cc.

References FML_DCHECK.

1988  {
1989  FML_DCHECK(!needs_layout_) << "only valid after layout";
1990  return line_metrics_;
1991 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetLongestLine()

double txt::ParagraphTxt::GetLongestLine ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1332 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().

1332  {
1333  FML_DCHECK(!needs_layout_) << "only valid after layout";
1334  return longest_line_;
1335 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetMaxIntrinsicWidth()

double txt::ParagraphTxt::GetMaxIntrinsicWidth ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1306 of file paragraph_txt.cc.

References FML_DCHECK.

1306  {
1307  FML_DCHECK(!needs_layout_) << "only valid after layout";
1308  return max_intrinsic_width_;
1309 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetMaxWidth()

double txt::ParagraphTxt::GetMaxWidth ( )
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  return width_;
1330 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetMinIntrinsicWidth()

double txt::ParagraphTxt::GetMinIntrinsicWidth ( )
overridevirtual

Implements txt::Paragraph.

Definition at line 1311 of file paragraph_txt.cc.

References FML_DCHECK.

1311  {
1312  FML_DCHECK(!needs_layout_) << "only valid after layout";
1313  return min_intrinsic_width_;
1314 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ GetParagraphStyle()

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

Definition at line 1290 of file paragraph_txt.cc.

1290  {
1291  return paragraph_style_;
1292 }

◆ GetRectsForPlaceholders()

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

Implements txt::Paragraph.

Definition at line 1911 of file paragraph_txt.cc.

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

1911  {
1912  FML_DCHECK(!needs_layout_) << "only valid after layout";
1913  // Struct that holds calculated metrics for each line.
1914  struct LineBoxMetrics {
1915  std::vector<Paragraph::TextBox> boxes;
1916  // Per-line metrics for max and min coordinates for left and right boxes.
1917  // These metrics cannot be calculated in layout generically because of
1918  // selections that do not cover the whole line.
1919  SkScalar max_right = FLT_MIN;
1920  SkScalar min_left = FLT_MAX;
1921  };
1922 
1923  std::vector<Paragraph::TextBox> boxes;
1924 
1925  // Generate initial boxes and calculate metrics.
1926  for (const CodeUnitRun& run : inline_placeholder_code_unit_runs_) {
1927  // Check to see if we are finished.
1928  double baseline = line_metrics_[run.line_number].baseline;
1929  SkScalar top = baseline + run.font_metrics.fAscent;
1930  SkScalar bottom = baseline + run.font_metrics.fDescent;
1931 
1932  if (run.placeholder_run !=
1933  nullptr) { // Use inline placeholder size as height.
1934  top = baseline - run.placeholder_run->baseline_offset;
1935  bottom = baseline + run.placeholder_run->height -
1936  run.placeholder_run->baseline_offset;
1937  }
1938 
1939  // Calculate left and right.
1940  SkScalar left, right;
1941  left = run.x_pos.start;
1942  right = run.x_pos.end;
1943 
1944  boxes.emplace_back(SkRect::MakeLTRB(left, top, right, bottom),
1945  run.direction);
1946  }
1947  return boxes;
1948 }
#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 1629 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.

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

References FML_DCHECK.

1950  {
1951  FML_DCHECK(!needs_layout_) << "only valid after layout";
1952  if (text_.size() == 0)
1953  return Range<size_t>(0, 0);
1954 
1955  if (!word_breaker_) {
1956  UErrorCode status = U_ZERO_ERROR;
1957  word_breaker_.reset(
1958  icu::BreakIterator::createWordInstance(icu::Locale(), status));
1959  if (!U_SUCCESS(status))
1960  return Range<size_t>(0, 0);
1961  }
1962 
1963  word_breaker_->setText(icu::UnicodeString(false, text_.data(), text_.size()));
1964 
1965  int32_t prev_boundary = word_breaker_->preceding(offset + 1);
1966  int32_t next_boundary = word_breaker_->next();
1967  if (prev_boundary == icu::BreakIterator::DONE)
1968  prev_boundary = offset;
1969  if (next_boundary == icu::BreakIterator::DONE)
1970  next_boundary = offset;
1971  return Range<size_t>(prev_boundary, next_boundary);
1972 }
#define FML_DCHECK(condition)
Definition: logging.h:86

◆ Layout()

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

Implements txt::Paragraph.

Definition at line 659 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, 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::LineMetrics::hard_break, txt::TextStyle::has_height_override, txt::TextStyle::height, txt::LineMetrics::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().

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

1377  {
1378  SkPoint base_offset = SkPoint::Make(x, y);
1379  SkPaint paint;
1380  // Paint the background first before painting any text to prevent
1381  // potential overlap.
1382  for (const PaintRecord& record : records_) {
1383  PaintBackground(canvas, record, base_offset);
1384  }
1385  for (const PaintRecord& record : records_) {
1386  if (record.style().has_foreground) {
1387  paint = record.style().foreground;
1388  } else {
1389  paint.reset();
1390  paint.setColor(record.style().color);
1391  }
1392  SkPoint offset = base_offset + record.offset();
1393  if (record.GetPlaceholderRun() == nullptr) {
1394  PaintShadow(canvas, record, offset);
1395  canvas->drawTextBlob(record.text(), offset.x(), offset.y(), paint);
1396  }
1397  PaintDecorations(canvas, record, base_offset);
1398  }
1399 }

◆ SetDirty()

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

Definition at line 1984 of file paragraph_txt.cc.

Referenced by ParagraphTxt().

1984  {
1985  needs_layout_ = dirty;
1986 }

◆ TextSize()

size_t txt::ParagraphTxt::TextSize ( ) const

Definition at line 1316 of file paragraph_txt.cc.

References FML_DCHECK.

1316  {
1317  FML_DCHECK(!needs_layout_) << "only valid after layout";
1318  return text_.size();
1319 }
#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: