Flutter Engine
The Flutter Engine
Classes | Public Types | Public Member Functions | List of all members
skia::textlayout::TextWrapper Class Reference

#include <TextWrapper.h>

Public Types

using AddLineToParagraph = std::function< void(TextRange textExcludingSpaces, TextRange text, TextRange textIncludingNewlines, ClusterRange clusters, ClusterRange clustersWithGhosts, SkScalar AddLineToParagraph, size_t startClip, size_t endClip, SkVector offset, SkVector advance, InternalLineMetrics metrics, bool addEllipsis)>
 

Public Member Functions

 TextWrapper ()
 
void breakTextIntoLines (ParagraphImpl *parent, SkScalar maxWidth, const AddLineToParagraph &addLine)
 
SkScalar height () const
 
SkScalar minIntrinsicWidth () const
 
SkScalar maxIntrinsicWidth () const
 
bool exceededMaxLines () const
 

Detailed Description

Definition at line 14 of file TextWrapper.h.

Member Typedef Documentation

◆ AddLineToParagraph

using skia::textlayout::TextWrapper::AddLineToParagraph = std::function<void(TextRange textExcludingSpaces, TextRange text, TextRange textIncludingNewlines, ClusterRange clusters, ClusterRange clustersWithGhosts, SkScalar AddLineToParagraph, size_t startClip, size_t endClip, SkVector offset, SkVector advance, InternalLineMetrics metrics, bool addEllipsis)>

Definition at line 170 of file TextWrapper.h.

Constructor & Destructor Documentation

◆ TextWrapper()

skia::textlayout::TextWrapper::TextWrapper ( )
inline

Definition at line 164 of file TextWrapper.h.

164 {
165 fLineNumber = 1;
166 fHardLineBreak = false;
167 fExceededMaxLines = false;
168 }

Member Function Documentation

◆ breakTextIntoLines()

void skia::textlayout::TextWrapper::breakTextIntoLines ( ParagraphImpl parent,
SkScalar  maxWidth,
const AddLineToParagraph addLine 
)

Definition at line 277 of file TextWrapper.cpp.

279 {
280 fHeight = 0;
281 fMinIntrinsicWidth = std::numeric_limits<SkScalar>::min();
282 fMaxIntrinsicWidth = std::numeric_limits<SkScalar>::min();
283
284 auto span = parent->clusters();
285 if (span.empty()) {
286 return;
287 }
288 auto maxLines = parent->paragraphStyle().getMaxLines();
289 auto align = parent->paragraphStyle().effective_align();
290 auto unlimitedLines = maxLines == std::numeric_limits<size_t>::max();
291 auto endlessLine = !SkIsFinite(maxWidth);
292 auto hasEllipsis = parent->paragraphStyle().ellipsized();
293
294 auto disableFirstAscent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableFirstAscent;
295 auto disableLastDescent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableLastDescent;
296 bool firstLine = true; // We only interested in fist line if we have to disable the first ascent
297
298 SkScalar softLineMaxIntrinsicWidth = 0;
299 fEndLine = TextStretch(span.begin(), span.begin(), parent->strutForceHeight());
300 auto end = span.end() - 1;
301 auto start = span.begin();
302 InternalLineMetrics maxRunMetrics;
303 bool needEllipsis = false;
304 while (fEndLine.endCluster() != end) {
305
306 this->lookAhead(maxWidth, end, parent->getApplyRoundingHack());
307
308 auto lastLine = (hasEllipsis && unlimitedLines) || fLineNumber >= maxLines;
309 needEllipsis = hasEllipsis && !endlessLine && lastLine;
310
311 this->moveForward(needEllipsis);
312 needEllipsis &= fEndLine.endCluster() < end - 1; // Only if we have some text to ellipsize
313
314 // Do not trim end spaces on the naturally last line of the left aligned text
315 this->trimEndSpaces(align);
316
317 // For soft line breaks add to the line all the spaces next to it
318 Cluster* startLine;
319 size_t pos;
320 SkScalar widthWithSpaces;
321 std::tie(startLine, pos, widthWithSpaces) = this->trimStartSpaces(end);
322
323 if (needEllipsis && !fHardLineBreak) {
324 // This is what we need to do to preserve a space before the ellipsis
325 fEndLine.restoreBreak();
326 widthWithSpaces = fEndLine.widthWithGhostSpaces();
327 }
328
329 // If the line is empty with the hard line break, let's take the paragraph font (flutter???)
330 if (fEndLine.metrics().isClean()) {
331 fEndLine.setMetrics(parent->getEmptyMetrics());
332 }
333
334 // Deal with placeholder clusters == runs[@size==1]
335 Run* lastRun = nullptr;
336 for (auto cluster = fEndLine.startCluster(); cluster <= fEndLine.endCluster(); ++cluster) {
337 auto r = cluster->runOrNull();
338 if (r == lastRun) {
339 continue;
340 }
341 lastRun = r;
342 if (lastRun->placeholderStyle() != nullptr) {
343 SkASSERT(lastRun->size() == 1);
344 // Update the placeholder metrics so we can get the placeholder positions later
345 // and the line metrics (to make sure the placeholder fits)
346 lastRun->updateMetrics(&fEndLine.metrics());
347 }
348 }
349
350 // Before we update the line metrics with struts,
351 // let's save it for GetRectsForRange(RectHeightStyle::kMax)
352 maxRunMetrics = fEndLine.metrics();
353 maxRunMetrics.fForceStrut = false;
354
355 // TODO: keep start/end/break info for text and runs but in a better way that below
356 TextRange textExcludingSpaces(fEndLine.startCluster()->textRange().start, fEndLine.endCluster()->textRange().end);
357 TextRange text(fEndLine.startCluster()->textRange().start, fEndLine.breakCluster()->textRange().start);
358 TextRange textIncludingNewlines(fEndLine.startCluster()->textRange().start, startLine->textRange().start);
359 if (startLine == end) {
360 textIncludingNewlines.end = parent->text().size();
361 text.end = parent->text().size();
362 }
363 ClusterRange clusters(fEndLine.startCluster() - start, fEndLine.endCluster() - start + 1);
364 ClusterRange clustersWithGhosts(fEndLine.startCluster() - start, startLine - start);
365
366 if (disableFirstAscent && firstLine) {
367 fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent;
368 }
369 if (disableLastDescent && (lastLine || (startLine == end && !fHardLineBreak ))) {
370 fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent;
371 }
372
373 if (parent->strutEnabled()) {
374 // Make sure font metrics are not less than the strut
375 parent->strutMetrics().updateLineMetrics(fEndLine.metrics());
376 }
377
378 SkScalar lineHeight = fEndLine.metrics().height();
379 firstLine = false;
380
381 if (fEndLine.empty()) {
382 // Correct text and clusters (make it empty for an empty line)
383 textExcludingSpaces.end = textExcludingSpaces.start;
384 clusters.end = clusters.start;
385 }
386
387 // In case of a force wrapping we don't have a break cluster and have to use the end cluster
388 text.end = std::max(text.end, textExcludingSpaces.end);
389
390 addLine(textExcludingSpaces,
391 text,
392 textIncludingNewlines, clusters, clustersWithGhosts, widthWithSpaces,
393 fEndLine.startPos(),
394 fEndLine.endPos(),
395 SkVector::Make(0, fHeight),
396 SkVector::Make(fEndLine.width(), lineHeight),
397 fEndLine.metrics(),
398 needEllipsis && !fHardLineBreak);
399
400 softLineMaxIntrinsicWidth += widthWithSpaces;
401
402 fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth);
403 if (fHardLineBreak) {
404 softLineMaxIntrinsicWidth = 0;
405 }
406 // Start a new line
407 fHeight += lineHeight;
408 if (!fHardLineBreak || startLine != end) {
409 fEndLine.clean();
410 }
411 fEndLine.startFrom(startLine, pos);
412 parent->fMaxWidthWithTrailingSpaces = std::max(parent->fMaxWidthWithTrailingSpaces, widthWithSpaces);
413
414 if (hasEllipsis && unlimitedLines) {
415 // There is one case when we need an ellipsis on a separate line
416 // after a line break when width is infinite
417 if (!fHardLineBreak) {
418 break;
419 }
420 } else if (lastLine) {
421 // There is nothing more to draw
422 fHardLineBreak = false;
423 break;
424 }
425
426 ++fLineNumber;
427 }
428
429 // We finished formatting the text but we need to scan the rest for some numbers
430 // TODO: make it a case of a normal flow
431 if (fEndLine.endCluster() != nullptr) {
432 auto lastWordLength = 0.0f;
433 auto cluster = fEndLine.endCluster();
434 while (cluster != end || cluster->endPos() < end->endPos()) {
435 fExceededMaxLines = true;
436 if (cluster->isHardBreak()) {
437 // Hard line break ends the word and the line
438 fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth);
439 softLineMaxIntrinsicWidth = 0;
440 fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength);
441 lastWordLength = 0;
442 } else if (cluster->isWhitespaceBreak()) {
443 // Whitespaces end the word
444 softLineMaxIntrinsicWidth += cluster->width();
445 fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength);
446 lastWordLength = 0;
447 } else if (cluster->run().isPlaceholder()) {
448 // Placeholder ends the previous word and creates a separate one
449 fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength);
450 // Placeholder width now counts in fMinIntrinsicWidth
451 softLineMaxIntrinsicWidth += cluster->width();
452 fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, cluster->width());
453 lastWordLength = 0;
454 } else {
455 // Nothing out of ordinary - just add this cluster to the word and to the line
456 softLineMaxIntrinsicWidth += cluster->width();
457 lastWordLength += cluster->width();
458 }
459 ++cluster;
460 }
461 fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength);
462 fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth);
463
464 if (parent->lines().empty()) {
465 // In case we could not place even a single cluster on the line
466 if (disableFirstAscent) {
467 fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent;
468 }
469 if (disableLastDescent && !fHardLineBreak) {
470 fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent;
471 }
472 fHeight = std::max(fHeight, fEndLine.metrics().height());
473 }
474 }
475
476 if (fHardLineBreak) {
477 if (disableLastDescent) {
478 fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent;
479 }
480
481 // Last character is a line break
482 if (parent->strutEnabled()) {
483 // Make sure font metrics are not less than the strut
484 parent->strutMetrics().updateLineMetrics(fEndLine.metrics());
485 }
486
487 ClusterRange clusters(fEndLine.breakCluster() - start, fEndLine.endCluster() - start);
488 addLine(fEndLine.breakCluster()->textRange(),
489 fEndLine.breakCluster()->textRange(),
490 fEndLine.endCluster()->textRange(),
491 clusters,
492 clusters,
493 0,
494 0,
495 0,
496 SkVector::Make(0, fHeight),
497 SkVector::Make(0, fEndLine.metrics().height()),
498 fEndLine.metrics(),
499 needEllipsis);
500 fHeight += fEndLine.metrics().height();
501 parent->lines().back().setMaxRunMetrics(maxRunMetrics);
502 }
503
504 if (parent->lines().empty()) {
505 return;
506 }
507 // Correct line metric styles for the first and for the last lines if needed
508 if (disableFirstAscent) {
509 parent->lines().front().setAscentStyle(LineMetricStyle::Typographic);
510 }
511 if (disableLastDescent) {
512 parent->lines().back().setDescentStyle(LineMetricStyle::Typographic);
513 }
514}
SkPoint pos
#define SkASSERT(cond)
Definition: SkAssert.h:116
static bool SkIsFinite(T x, Pack... values)
float SkScalar
Definition: extension.cpp:12
glong glong end
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
std::u16string text
SkRange< size_t > ClusterRange
Definition: Run.h:36
SkRange< size_t > TextRange
Definition: TextStyle.h:337
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173

◆ exceededMaxLines()

bool skia::textlayout::TextWrapper::exceededMaxLines ( ) const
inline

Definition at line 189 of file TextWrapper.h.

189{ return fExceededMaxLines; }

◆ height()

SkScalar skia::textlayout::TextWrapper::height ( ) const
inline

Definition at line 186 of file TextWrapper.h.

186{ return fHeight; }

◆ maxIntrinsicWidth()

SkScalar skia::textlayout::TextWrapper::maxIntrinsicWidth ( ) const
inline

Definition at line 188 of file TextWrapper.h.

188{ return fMaxIntrinsicWidth; }

◆ minIntrinsicWidth()

SkScalar skia::textlayout::TextWrapper::minIntrinsicWidth ( ) const
inline

Definition at line 187 of file TextWrapper.h.

187{ return fMinIntrinsicWidth; }

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