25 #include "../lout/msg.h" 26 #include "../lout/misc.hh" 35 int Textblock::BadnessAndPenalty::badnessValue (
int infLevel)
37 switch (badnessState) {
39 return infLevel == INF_NOT_STRETCHABLE ? 1 : 0;
42 return infLevel == INF_LARGE ? 1 : 0;
45 return infLevel == INF_VALUE ? badness : 0;
48 return infLevel == INF_TOO_TIGHT ? 1 : 0;
56 int Textblock::BadnessAndPenalty::penaltyValue (
int index,
int infLevel)
58 if (penalty[index] == INT_MIN)
59 return infLevel == INF_PENALTIES ? -1 : 0;
60 else if (penalty[index] == INT_MAX)
61 return infLevel == INF_PENALTIES ? 1 : 0;
63 return infLevel == INF_VALUE ? penalty[index] : 0;
66 void Textblock::BadnessAndPenalty::calcBadness (
int totalWidth,
int idealWidth,
67 int totalStretchability,
68 int totalShrinkability)
71 this->totalWidth = totalWidth;
72 this->idealWidth = idealWidth;
73 this->totalStretchability = totalStretchability;
74 this->totalShrinkability = totalShrinkability;
79 if (totalWidth == idealWidth) {
80 badnessState = BADNESS_VALUE;
82 }
else if (totalWidth < idealWidth) {
83 if (totalStretchability == 0)
84 badnessState = NOT_STRETCHABLE;
86 ratio = 100 * (idealWidth - totalWidth) / totalStretchability;
88 badnessState = QUITE_LOOSE;
90 badnessState = BADNESS_VALUE;
91 badness = ratio * ratio * ratio;
95 if (totalShrinkability == 0)
96 badnessState = TOO_TIGHT;
99 ratio = 100 * (idealWidth - totalWidth) / totalShrinkability;
101 badnessState = TOO_TIGHT;
103 badnessState = BADNESS_VALUE;
104 badness = - ratio * ratio * ratio;
128 void Textblock::BadnessAndPenalty::setPenalties (
int penalty1,
int penalty2)
131 setSinglePenalty(0, penalty1);
132 setSinglePenalty(1, penalty2);
135 void Textblock::BadnessAndPenalty::setSinglePenalty (
int index,
int penalty)
137 if (penalty == INT_MAX || penalty == INT_MIN)
138 this->penalty[index] = penalty;
146 this->penalty[index] = penalty * (100 * 100 * 100 / 100);
149 bool Textblock::BadnessAndPenalty::lineLoose ()
152 badnessState == NOT_STRETCHABLE || badnessState == QUITE_LOOSE ||
153 (badnessState == BADNESS_VALUE && ratio > 0);
156 bool Textblock::BadnessAndPenalty::lineTight ()
159 badnessState == TOO_TIGHT || (badnessState == BADNESS_VALUE && ratio < 0);
162 bool Textblock::BadnessAndPenalty::lineTooTight ()
164 return badnessState == TOO_TIGHT;
168 bool Textblock::BadnessAndPenalty::lineMustBeBroken (
int penaltyIndex)
170 return penalty[penaltyIndex] == PENALTY_FORCE_BREAK;
173 bool Textblock::BadnessAndPenalty::lineCanBeBroken (
int penaltyIndex)
175 return penalty[penaltyIndex] != PENALTY_PROHIBIT_BREAK;
178 int Textblock::BadnessAndPenalty::compareTo (
int penaltyIndex,
181 for (
int l = INF_MAX; l >= 0; l--) {
182 int thisValue = badnessValue (l) + penaltyValue (penaltyIndex, l);
186 if (thisValue != otherValue)
187 return thisValue - otherValue;
193 void Textblock::BadnessAndPenalty::print ()
195 switch (badnessState) {
196 case NOT_STRETCHABLE:
197 printf (
"not stretchable");
201 printf (
"too tight");
205 printf (
"quite loose (ratio = %d)", ratio);
209 printf (
"%d", badness);
214 printf (
" [%d + %d - %d vs. %d]",
215 totalWidth, totalStretchability, totalShrinkability, idealWidth);
219 for (
int i = 0; i < 2; i++) {
220 if (penalty[i] == INT_MIN)
222 else if (penalty[i] == INT_MAX)
225 printf (
"%d", penalty[i]);
233 void Textblock::printWordShort (
Word *word)
236 case core::Content::TEXT:
239 case core::Content::WIDGET:
240 printf (
"<widget: %p (%s)>",
243 case core::Content::BREAK:
252 void Textblock::printWordFlags (
short flags)
254 printf (
"%s:%s:%s:%s:%s:%s:%s",
255 (flags & Word::CAN_BE_HYPHENATED) ?
"h?" :
"--",
256 (flags & Word::DIV_CHAR_AT_EOL) ?
"de" :
"--",
257 (flags & Word::PERM_DIV_CHAR) ?
"dp" :
"--",
258 (flags & Word::DRAW_AS_ONE_TEXT) ?
"t1" :
"--",
259 (flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) ?
"um" :
"--",
260 (flags & Word::WORD_START) ?
"st" :
"--",
261 (flags & Word::WORD_END) ?
"en" :
"--");
264 void Textblock::printWordWithFlags (
Word *word)
266 printWordShort (word);
267 printf (
" (flags = ");
268 printWordFlags (word->
flags);
272 void Textblock::printWord (
Word *word)
274 printWordWithFlags (word);
276 printf (
" [%d / %d + %d - %d => %d + %d - %d] => ",
278 getSpaceShrinkability(word), word->
totalWidth,
288 void Textblock::justifyLine (
Line *line,
int diff)
294 int spaceStretchabilitySum = 0;
295 for (
int i = line->
firstWord; i < line->lastWord; i++)
296 spaceStretchabilitySum += getSpaceStretchability(words->getRef(i));
298 if (spaceStretchabilitySum > 0) {
299 int spaceStretchabilityCum = 0;
300 int spaceDiffCum = 0;
301 for (
int i = line->
firstWord; i < line->lastWord; i++) {
302 Word *word = words->getRef (i);
303 spaceStretchabilityCum += getSpaceStretchability(word);
305 spaceStretchabilityCum * diff / spaceStretchabilitySum
307 spaceDiffCum += spaceDiff;
309 PRINTF (
" %d (of %d): diff = %d\n", i, words->size (),
315 }
else if (diff < 0) {
316 int spaceShrinkabilitySum = 0;
317 for (
int i = line->
firstWord; i < line->lastWord; i++)
318 spaceShrinkabilitySum += getSpaceShrinkability(words->getRef(i));
320 if (spaceShrinkabilitySum > 0) {
321 int spaceShrinkabilityCum = 0;
322 int spaceDiffCum = 0;
323 for (
int i = line->
firstWord; i < line->lastWord; i++) {
324 Word *word = words->getRef (i);
325 spaceShrinkabilityCum += getSpaceShrinkability(word);
327 spaceShrinkabilityCum * diff / spaceShrinkabilitySum
329 spaceDiffCum += spaceDiff;
341 PRINTF (
"[%p] ADD_LINE (%d, %d) => %d\n",
342 this, firstWord, lastWord, lines->size ());
344 Word *lastWordOfLine = words->getRef(lastWord);
349 if (lines->size () == 0)
350 lineWidth += line1OffsetEff;
352 int maxOfMinWidth, sumOfMaxWidth;
353 accumulateWordExtremes (firstWord, lastWord, &maxOfMinWidth,
356 PRINTF (
" words[%d]->totalWidth = %d\n", lastWord,
357 lastWordOfLine->totalWidth);
359 PRINTF (
"[%p] ##### LINE ADDED: %d, from %d to %d #####\n",
360 this, lines->size (), firstWord, lastWord);
366 if (lines->size () == 1 || nonTemporaryLines == lines->size () -1)
367 nonTemporaryLines = lines->size ();
370 PRINTF (
"nonTemporaryLines = %d\n", nonTemporaryLines);
372 int lineIndex = lines->size () - 1;
373 Line *line = lines->getRef (lineIndex);
383 alignLine (lineIndex);
384 for (
int i = line->
firstWord; i < line->lastWord; i++) {
385 Word *word = words->getRef (i);
389 if (lines->size () == 1) {
394 Line *prevLine = lines->getRef (lines->size () - 2);
400 for(
int i = line->
firstWord; i <= line->lastWord; i++)
401 accumulateWordForLine (lineIndex, i);
403 PRINTF (
" line[%d].top = %d\n", lines->size () - 1, line->
top);
404 PRINTF (
" line[%d].boxAscent = %d\n", lines->size () - 1, line->
boxAscent);
405 PRINTF (
" line[%d].boxDescent = %d\n",
407 PRINTF (
" line[%d].contentAscent = %d\n", lines->size () - 1,
409 PRINTF (
" line[%d].contentDescent = %d\n",
412 PRINTF (
" line[%d].maxLineWidth = %d\n",
415 mustQueueResize =
true;
424 int xWidget = lineXOffsetWidget(line);
425 for (
int i = firstWord; i <= lastWord; i++) {
426 Word *word = words->getRef (i);
437 void Textblock::accumulateWordExtremes (
int firstWord,
int lastWord,
438 int *maxOfMinWidth,
int *sumOfMaxWidth)
441 *maxOfMinWidth = *sumOfMaxWidth = 0;
443 for (
int i = firstWord; i <= lastWord; i++) {
444 Word *word = words->getRef (i);
445 bool atLastWord = i == lastWord;
448 getWordExtremes (word, &extremes);
455 *maxOfMinWidth =
misc::max (*maxOfMinWidth, parMin);
475 void Textblock::processWord (
int wordIndex)
477 bool wordListChanged = wordWrap (wordIndex,
false);
479 if (wordListChanged) {
487 if (paragraphs->size() > 0) {
488 firstWord = paragraphs->getLastRef()->firstWord;
489 paragraphs->setSize (paragraphs->size() - 1);
493 for (
int i = firstWord; i <= wordIndex - 1; i++)
494 handleWordExtremes (i);
497 handleWordExtremes (wordIndex);
508 bool Textblock::wordWrap (
int wordIndex,
bool wrapAll)
510 PRINTF (
"[%p] WORD_WRAP (%d, %s)\n",
511 this, wordIndex, wrapAll ?
"true" :
"false");
514 bool wordListChanged =
false;
517 removeTemporaryLines ();
519 initLine1Offset (wordIndex);
521 word = words->getRef (wordIndex);
524 accumulateWordData (wordIndex);
530 int penaltyIndex = calcPenaltyIndexForNewLine ();
534 bool tempNewLine =
false;
536 lines->size() == 0 ? 0 : lines->getLastRef()->lastWord + 1;
539 if (wrapAll && wordIndex >= firstIndex && wordIndex == words->size() -1) {
541 searchUntil = wordIndex;
543 PRINTF (
" NEW LINE: last word\n");
544 }
else if (wordIndex >= firstIndex &&
549 searchUntil = wordIndex;
550 PRINTF (
" NEW LINE: forced break\n");
555 bool possibleLineBreak =
false;
556 for (
int i = firstIndex; !possibleLineBreak && i <= wordIndex - 1; i++)
557 if (words->getRef(i)->badnessAndPenalty
558 .lineCanBeBroken (penaltyIndex))
559 possibleLineBreak =
true;
563 searchUntil = wordIndex - 1;
564 PRINTF (
" NEW LINE: line too tight\n");
569 if(!newLine && !wrapAll)
576 mustQueueResize =
true;
579 accumulateWordData (wordIndex);
580 int wordIndexEnd = wordIndex;
585 searchMinBap (firstIndex, searchUntil, penaltyIndex, wrapAll);
586 int hyphenatedWord = considerHyphenation (firstIndex, breakPos);
598 if(hyphenatedWord == -1) {
599 addLine (firstIndex, breakPos, tempNewLine);
600 PRINTF (
"[%p] new line %d (%s), from %d to %d\n",
601 this, lines->size() - 1,
602 tempNewLine ?
"temporally" :
"permanently",
603 firstIndex, breakPos);
605 penaltyIndex = calcPenaltyIndexForNewLine ();
611 PRINTF (
"[%p] old searchUntil = %d ...\n",
this, searchUntil);
612 int n = hyphenateWord (hyphenatedWord);
614 if (hyphenatedWord <= wordIndex)
616 PRINTF (
"[%p] -> new searchUntil = %d ...\n",
this, searchUntil);
621 word = words->getRef (wordIndex);
623 if (n > 0 && hyphenatedWord <= wordIndex)
624 wordListChanged =
true;
627 PRINTF (
"[%p] accumulating again from %d to %d\n",
628 this, breakPos + 1, wordIndexEnd);
629 for(
int i = breakPos + 1; i <= wordIndexEnd; i++)
630 accumulateWordData (i);
646 int firstWordWithoutLine;
647 if (lines->size() == 0)
648 firstWordWithoutLine = 0;
650 firstWordWithoutLine = lines->getLastRef()->lastWord + 1;
652 if (wordIndex >= firstWordWithoutLine) {
654 PRINTF (
"The %s %p is assigned parentRef = %d.\n",
660 return wordListChanged;
663 int Textblock::searchMinBap (
int firstWord,
int lastWord,
int penaltyIndex,
666 PRINTF (
" searching from %d to %d\n", firstWord, lastWord);
670 for (
int i = firstWord; i <= lastWord; i++) {
671 Word *w = words->getRef(i);
680 ->badnessAndPenalty) <= 0)
687 PRINTF (
" found at %d\n", pos);
689 if (correctAtEnd && lastWord == words->size () - 1) {
698 Word *w = words->getRef (lastWord);
707 &words->getRef(pos)->badnessAndPenalty) <= 0) {
709 PRINTF (
" corrected => %d\n", pos);
722 int Textblock::considerHyphenation (
int firstIndex,
int breakPos)
724 int hyphenatedWord = -1;
726 Word *wordBreak = words->getRef(breakPos);
737 for (
int i = breakPos; i >= firstIndex; i--) {
738 Word *word1 = words->getRef (i);
740 isHyphenationCandidate (word1))
748 breakPos + 1 < words->size ()) {
749 Word *word2 = words->getRef(breakPos + 1);
750 if (isHyphenationCandidate (word2))
751 hyphenatedWord = breakPos + 1;
754 return hyphenatedWord;
757 bool Textblock::isHyphenationCandidate (
Word *word)
759 return (word->
flags & Word::CAN_BE_HYPHENATED) &&
761 isBreakAllowed(word) &&
763 Hyphenator::isHyphenationCandidate (word->
content.
text);
770 void Textblock::handleWordExtremes (
int wordIndex)
774 Word *word = words->getRef (wordIndex);
776 getWordExtremes (word, &wordExtremes);
782 if (wordIndex == 0) {
783 wordExtremes.
minWidth += line1Offset;
784 wordExtremes.
maxWidth += line1Offset;
787 if (paragraphs->size() == 0 ||
788 words->getRef(paragraphs->getLastRef()->lastWord)
789 ->badnessAndPenalty.lineMustBeBroken (1)) {
791 paragraphs->increase ();
792 Paragraph *prevPar = paragraphs->size() == 1 ?
793 NULL : paragraphs->getRef(paragraphs->size() - 2);
794 Paragraph *par = paragraphs->getLastRef();
805 PRINTF (
" new par: %d\n", paragraphs->size() - 1);
808 PRINTF (
" last par: %d\n", paragraphs->size() - 1);
809 Paragraph *lastPar = paragraphs->getLastRef();
811 int corrDiffMin, corrDiffMax;
812 if (wordIndex - 1 >= lastPar->firstWord) {
813 Word *lastWord = words->getRef (wordIndex - 1);
815 (lastWord->
flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) == 0)
822 corrDiffMin = corrDiffMax = 0;
824 PRINTF (
" (lastPar from %d to %d; corrDiffMin = %d, corDiffMax = %d)\n",
825 lastPar->firstWord, lastPar->lastWord, corrDiffMin, corrDiffMax);
830 lastPar->maxParMin =
misc::max (lastPar->maxParMin, lastPar->parMin);
832 (word->
flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) == 0)
837 lastPar->maxParMax =
misc::max (lastPar->maxParMax, lastPar->parMax);
839 PRINTF (
" => parMin = %d (max = %d), parMax = %d (max = %d)\n",
840 lastPar->parMin, lastPar->maxParMin, lastPar->parMax,
843 lastPar->lastWord = wordIndex;
849 void Textblock::correctLastWordExtremes ()
851 if (paragraphs->size() > 0) {
852 Word *word = words->getLastRef ();
854 (word->
flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) == 0) {
855 paragraphs->getLastRef()->parMin = 0;
856 PRINTF (
" => corrected; parMin = %d\n",
857 paragraphs->getLastRef()->parMin);
863 int Textblock::hyphenateWord (
int wordIndex)
865 Word *hyphenatedWord = words->getRef(wordIndex);
868 Hyphenator *hyphenator = Hyphenator::getHyphenator (lang);
869 PRINTF (
"[%p] considering to hyphenate word %d, '%s', in language '%s'\n",
870 this, wordIndex, words->getRef(wordIndex)->content.text, lang);
877 Word origWord = *hyphenatedWord;
881 origWord.
style, numBreaks, breakPos, wordSize);
883 PRINTF (
"[%p] %d words ...\n",
this, words->size ());
884 words->insert (wordIndex, numBreaks);
885 for (
int i = 0; i < numBreaks; i++)
886 initWord (wordIndex + i);
887 PRINTF (
"[%p] ... => %d words\n",
this, words->size ());
890 for (
int i = 0; i < anchors->size (); i++) {
891 Anchor *anchor = anchors->getRef (i);
896 for (
int i = 0; i < numBreaks + 1; i++) {
897 Word *w = words->getRef (wordIndex + i);
898 fillWord (wordIndex + i, wordSize[i].width, wordSize[i].ascent,
899 wordSize[i].descent,
false, origWord.
style);
904 int start = (i == 0 ? 0 : breakPos[i - 1]);
905 int end = (i == numBreaks ?
908 layout->textZone->strndup (origWord.
content.
text + start,
914 w->
flags |= Word::WORD_START;
919 w->
flags |= Word::WORD_END;
926 penalties[PENALTY_HYPHEN][1]);
929 layout->textWidth (w->
style->
font, hyphenDrawChar,
930 strlen (hyphenDrawChar));
931 w->
flags |= (Word::DRAW_AS_ONE_TEXT | Word::DIV_CHAR_AT_EOL |
932 Word::UNBREAKABLE_FOR_MIN_WIDTH);
934 PRINTF (
" [%d] + hyphen\n", wordIndex + i);
937 fillSpace (wordIndex + i, origWord.
spaceStyle);
938 PRINTF (
" [%d] + space\n", wordIndex + i);
940 PRINTF (
" [%d] + nothing\n", wordIndex + i);
944 accumulateWordData (wordIndex + i);
959 words->getRef(wordIndex)->flags &= ~
Word::CAN_BE_HYPHENATED;
964 void Textblock::accumulateWordForLine (
int lineIndex,
int wordIndex)
966 Line *line = lines->getRef (lineIndex);
967 Word *word = words->getRef (wordIndex);
986 int collapseMarginTop = 0;
993 if (lines->size () == 1 &&
995 getStyle ()->borderWidth.top == 0 &&
996 getStyle ()->padding.top == 0) {
999 collapseMarginTop = getStyle ()->margin.top;
1007 - collapseMarginTop);
1022 void Textblock::accumulateWordData (
int wordIndex)
1027 int lineIndex = lines->size ();
1028 while (lineIndex > 0 && wordIndex <= lines->getRef(lineIndex - 1)->lastWord)
1031 int firstWordOfLine;
1033 firstWordOfLine = 0;
1035 firstWordOfLine = lines->getRef(lineIndex - 1)->lastWord + 1;
1037 Word *word = words->getRef (wordIndex);
1038 PRINTF (
"[%p] ACCUMULATE_WORD_DATA (%d); lineIndex = %d: ...\n",
1039 this, wordIndex, lineIndex);
1041 int availWidth = calcAvailWidth (lineIndex);
1043 PRINTF (
" (%s existing line %d starts with word %d)\n",
1044 lineIndex < lines->size () ?
"already" :
"not yet",
1045 lineIndex, firstWordOfLine);
1047 if (wordIndex == firstWordOfLine) {
1055 Word *prevWord = words->getRef (wordIndex - 1);
1068 int totalStretchability =
1070 int totalShrinkability =
1073 totalStretchability,
1074 totalShrinkability);
1081 int Textblock::calcAvailWidth (
int lineIndex)
1084 this->availWidth - getStyle()->boxDiffWidth() - innerPadding;
1085 if (limitTextWidth &&
1086 layout->getUsesViewport () &&
1087 availWidth > layout->getWidthViewport () - 10)
1088 availWidth = layout->getWidthViewport () - 10;
1090 availWidth -= line1OffsetEff;
1099 void Textblock::initLine1Offset (
int wordIndex)
1101 Word *word = words->getRef (wordIndex);
1104 if (wordIndex == 0) {
1105 if (ignoreLine1OffsetSometimes &&
1106 line1Offset + word->
size.
width > availWidth) {
1111 if (word->
content.
type == core::Content::WIDGET &&
1117 (this->availWidth, getStyle()->textIndent);
1122 line1OffsetEff = line1Offset + indent;
1132 void Textblock::alignLine (
int lineIndex)
1134 Line *line = lines->getRef (lineIndex);
1135 int availWidth = calcAvailWidth (lineIndex);
1139 for (
int i = line->
firstWord; i < line->lastWord; i++)
1140 words->getRef(i)->
origSpace = words->getRef(i)->effSpace;
1142 if (firstWord->
content.
type != core::Content::BREAK) {
1153 if(!(lastWord->
content.
type == core::Content::BREAK ||
1154 line->
lastWord == words->size () - 1) ||
1159 availWidth < lastWord->totalWidth)
1160 justifyLine (line, availWidth - lastWord->
totalWidth);
1185 void Textblock::rewrap ()
1187 PRINTF (
"[%p] REWRAP: wrapRef = %d\n",
this, wrapRef);
1189 if (wrapRefLines == -1)
1195 lines->setSize (wrapRefLines);
1196 nonTemporaryLines =
misc::min (nonTemporaryLines, wrapRefLines);
1199 if (lines->size () > 0)
1200 firstWord = lines->getLastRef()->lastWord + 1;
1204 for (
int i = firstWord; i < words->size (); i++) {
1205 Word *word = words->getRef (i);
1210 wordWrap (i,
false);
1229 void Textblock::fillParagraphs ()
1231 if (wrapRefParagraphs == -1)
1235 int firstWordOfLine;
1236 if (lines->size () > 0 && wrapRefParagraphs > 0)
1237 firstWordOfLine = lines->getRef(wrapRefParagraphs - 1)->lastWord + 1;
1239 firstWordOfLine = 0;
1242 if (paragraphs->size() > 0 &&
1243 firstWordOfLine > paragraphs->getLastRef()->firstWord)
1250 parNo = paragraphs->size ();
1254 parNo =
misc::max (0, findParagraphOfWord (firstWordOfLine));
1256 paragraphs->setSize (parNo);
1259 if (paragraphs->size () > 0)
1260 firstWord = paragraphs->getLastRef()->lastWord + 1;
1264 PRINTF (
"[%p] FILL_PARAGRAPHS: now %d paragraphs; starting from word %d\n",
1265 this, parNo, firstWord);
1267 for (
int i = firstWord; i < words->size (); i++)
1268 handleWordExtremes (i);
1270 wrapRefParagraphs = -1;
1273 void Textblock::showMissingLines ()
1275 int firstWordToWrap = lines->size () > 0 ?
1276 lines->getRef(lines->size () - 1)->lastWord + 1 : 0;
1277 PRINTF (
"[%p] SHOW_MISSING_LINES: wrap from %d to %d\n",
1278 this, firstWordToWrap, words->size () - 1);
1279 for (
int i = firstWordToWrap; i < words->size (); i++)
1284 void Textblock::removeTemporaryLines ()
1286 lines->setSize (nonTemporaryLines);
1289 int Textblock::getSpaceShrinkability(
struct Word *word)
1297 int Textblock::getSpaceStretchability(
struct Word *word)
1307 int Textblock::getLineShrinkability(
Word *lastWord)
1312 int Textblock::getLineStretchability(
Word *lastWord)
1317 return stretchabilityFactor * (lastWord->
maxAscent