MCPcopy Create free account
hub / github.com/crosspoint-reader/crosspoint-reader / computeLineBreaks

Method computeLineBreaks

lib/Epub/Epub/ParsedText.cpp:547–660  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

545}
546
547std::vector<size_t> ParsedText::computeLineBreaks(const GfxRenderer& renderer, const int fontId, const int pageWidth,
548 std::vector<uint16_t>& wordWidths, std::vector<bool>& continuesVec,
549 std::vector<bool>& noSpaceBeforeVec) {
550 if (words.empty()) {
551 return {};
552 }
553
554 const int firstLineIndent = resolveFirstLineIndent(true, renderer, fontId);
555
556 // Ensure any word that would overflow even as the first entry on a line is split using fallback hyphenation.
557 for (size_t i = 0; i < wordWidths.size(); ++i) {
558 // First word needs to fit in reduced width if there's an indent
559 const int effectiveWidth = i == 0 ? pageWidth - firstLineIndent : pageWidth;
560 while (wordWidths[i] > effectiveWidth) {
561 if (!hyphenateWordAtIndex(i, effectiveWidth, renderer, fontId, wordWidths, /*allowFallbackBreaks=*/true)) {
562 break;
563 }
564 }
565 }
566
567 const size_t totalWordCount = words.size();
568
569 // DP table to store the minimum badness (cost) of lines starting at index i
570 std::vector<int> dp(totalWordCount);
571 // 'ans[i]' stores the index 'j' of the *last word* in the optimal line starting at 'i'
572 std::vector<size_t> ans(totalWordCount);
573
574 // Base Case
575 dp[totalWordCount - 1] = 0;
576 ans[totalWordCount - 1] = totalWordCount - 1;
577
578 for (int i = totalWordCount - 2; i >= 0; --i) {
579 int currlen = 0;
580 dp[i] = MAX_COST;
581
582 // First line has reduced width due to text-indent
583 const int effectivePageWidth = i == 0 ? pageWidth - firstLineIndent : pageWidth;
584
585 for (size_t j = i; j < totalWordCount; ++j) {
586 // Add space before word j, unless it's the first word on the line or a continuation
587 int gap = 0;
588 if (j > static_cast<size_t>(i) && noSpaceBeforeVec[j]) {
589 gap = 0;
590 } else if (j > static_cast<size_t>(i) && !continuesVec[j]) {
591 gap =
592 renderer.getSpaceAdvance(fontId, lastCodepoint(words[j - 1]), firstCodepoint(words[j]), wordStyles[j - 1]);
593 } else if (j > static_cast<size_t>(i) && continuesVec[j]) {
594 // Cross-boundary kerning for continuation words (e.g. nonbreaking spaces, attached punctuation)
595 gap = renderer.getKerning(fontId, lastCodepoint(words[j - 1]), firstCodepoint(words[j]), wordStyles[j - 1]);
596 }
597 currlen += wordWidths[j] + gap;
598
599 if (currlen > effectivePageWidth) {
600 break;
601 }
602
603 // Cannot break after word j if the next word attaches to it (continuation group)
604 if (j + 1 < totalWordCount && continuesVec[j + 1]) {

Callers

nothing calls this directly

Calls 7

lastCodepointFunction · 0.85
firstCodepointFunction · 0.85
getSpaceAdvanceMethod · 0.80
push_backMethod · 0.80
emptyMethod · 0.45
sizeMethod · 0.45
getKerningMethod · 0.45

Tested by

no test coverage detected