Dillo v3.1.1-14-g8f67d6e0
Loading...
Searching...
No Matches
textblock_linebreaking.cc
Go to the documentation of this file.
1/*
2 * Dillo Widget
3 *
4 * Copyright 2005-2007, 2012-2014 Sebastian Geerken <sgeerken@dillo.org>
5 *
6 * (Parts of this file were originally part of textblock.cc.)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22
23#include "textblock.hh"
24#include "hyphenator.hh"
25#include "../lout/msg.h"
26#include "../lout/debug.hh"
27#include "../lout/misc.hh"
28
29#include <stdio.h>
30#include <math.h>
31
32using namespace lout;
33using namespace lout::misc;
34
35namespace dw {
36
38{
39 switch (badnessState) {
40 case NOT_STRETCHABLE:
41 return infLevel == INF_NOT_STRETCHABLE ? 1 : 0;
42
43 case QUITE_LOOSE:
44 return infLevel == INF_LARGE ? 1 : 0;
45
46 case BADNESS_VALUE:
47 return infLevel == INF_VALUE ? badness : 0;
48
49 case TOO_TIGHT:
50 return infLevel == INF_TOO_TIGHT ? 1 : 0;
51 }
52
53 // compiler happiness
55 return 0;
56}
57
59{
60 if (penalty[index] == INT_MIN)
61 return infLevel == INF_PENALTIES ? -1 : 0;
62 else if (penalty[index] == INT_MAX)
63 return infLevel == INF_PENALTIES ? 1 : 0;
64 else
65 return infLevel == INF_VALUE ? penalty[index] : 0;
66}
67
68void Textblock::BadnessAndPenalty::calcBadness (int totalWidth, int idealWidth,
69 int totalStretchability,
70 int totalShrinkability)
71{
72#ifdef DEBUG
73 this->totalWidth = totalWidth;
74 this->idealWidth = idealWidth;
75 this->totalStretchability = totalStretchability;
76 this->totalShrinkability = totalShrinkability;
77#endif
78
79 ratio = 0;
80
81 if (totalWidth == idealWidth) {
82 badnessState = BADNESS_VALUE;
83 badness = 0;
84 } else if (totalWidth < idealWidth) {
85 if (totalStretchability == 0)
86 badnessState = NOT_STRETCHABLE;
87 else {
88 ratio = 100 * (idealWidth - totalWidth) / totalStretchability;
89 if (ratio > 1024)
90 badnessState = QUITE_LOOSE;
91 else {
92 badnessState = BADNESS_VALUE;
93 badness = ratio * ratio * ratio;
94 }
95 }
96 } else { // if (totalWidth > idealWidth)
97 if (totalShrinkability == 0)
98 badnessState = TOO_TIGHT;
99 else {
100 // ratio is negative here
101 ratio = 100 * (idealWidth - totalWidth) / totalShrinkability;
102 if (ratio <= - 100)
103 badnessState = TOO_TIGHT;
104 else {
105 badnessState = BADNESS_VALUE;
106 badness = - ratio * ratio * ratio;
107 }
108 }
109 }
110}
111
130void Textblock::BadnessAndPenalty::setPenalties (int penalty1, int penalty2)
131{
132 // TODO Check here some cases, e.g. both or no penalty INT_MIN.
133 setSinglePenalty(0, penalty1);
134 setSinglePenalty(1, penalty2);
135}
136
138{
139 if (penalty == INT_MAX || penalty == INT_MIN)
140 this->penalty[index] = penalty;
141 else
142 // This factor consists of: (i) 100^3, since in calcBadness(), the
143 // ratio is multiplied with 100 (again, to use integer numbers for
144 // fractional numbers), and the badness (which has to be compared
145 // to the penalty!) is the third power or it; (ii) the denominator
146 // 100, of course, since 100 times the penalty is passed to this
147 // method.
148 this->penalty[index] = penalty * (100 * 100 * 100 / 100);
149}
150
152{
153 return
154 badnessState == NOT_STRETCHABLE || badnessState == QUITE_LOOSE ||
155 (badnessState == BADNESS_VALUE && ratio > 0);
156}
157
159{
160 return
161 badnessState == TOO_TIGHT || (badnessState == BADNESS_VALUE && ratio < 0);
162}
163
165{
166 return badnessState == TOO_TIGHT;
167}
168
169
171{
172 return penalty[penaltyIndex] == PENALTY_FORCE_BREAK;
173}
174
176{
177 return penalty[penaltyIndex] != PENALTY_PROHIBIT_BREAK;
178}
179
181 BadnessAndPenalty *other)
182{
183 for (int l = INF_MAX; l >= 0; l--) {
184 int thisValue = badnessValue (l) + penaltyValue (penaltyIndex, l);
185 int otherValue =
186 other->badnessValue (l) + other->penaltyValue (penaltyIndex, l);
187
188 if (thisValue != otherValue)
189 return thisValue - otherValue;
190 }
191
192 return 0;
193}
194
196{
197 switch (badnessState) {
198 case NOT_STRETCHABLE:
199 sb->append ("not stretchable");
200 break;
201
202 case TOO_TIGHT:
203 sb->append ("too tight");
204 break;
205
206 case QUITE_LOOSE:
207 sb->append ("quite loose (ratio = ");
208 sb->appendInt (ratio);
209 sb->append (")");
210 break;
211
212 case BADNESS_VALUE:
213 sb->appendInt (badness);
214 break;
215 }
216
217#ifdef DEBUG
218 sb->append (" [");
219 sb->appendInt (totalWidth);
220 sb->append (" + ");
221 sb->appendInt (totalStretchability);
222 sb->append (" - ");
223 sb->appendInt (totalShrinkability);
224 sb->append (" vs. ");
225 sb->appendInt (idealWidth);
226 sb->append ("]");
227#endif
228
229 sb->append (" + (");
230 for (int i = 0; i < 2; i++) {
231 if (penalty[i] == INT_MIN)
232 sb->append ("-inf");
233 else if (penalty[i] == INT_MAX)
234 sb->append ("inf");
235 else
236 sb->appendInt (penalty[i]);
237
238 if (i == 0)
239 sb->append (", ");
240 }
241 sb->append (")");
242}
243
244/*
245 * ...
246 *
247 * diff ...
248 */
249void Textblock::justifyLine (Line *line, int diff)
250{
251 DBG_OBJ_ENTER ("construct.line", 0, "justifyLine", "..., %d", diff);
252
253 // To avoid rounding errors, the calculation is based on accumulated
254 // values. See doc/rounding-errors.doc.
255
256 if (diff > 0) {
257 int spaceStretchabilitySum = 0;
258 for (int i = line->firstWord; i < line->lastWord; i++)
259 spaceStretchabilitySum += getSpaceStretchability(words->getRef(i));
260
261 if (spaceStretchabilitySum > 0) {
262 int spaceStretchabilityCum = 0;
263 int spaceDiffCum = 0;
264 for (int i = line->firstWord; i < line->lastWord; i++) {
265 Word *word = words->getRef (i);
266 spaceStretchabilityCum += getSpaceStretchability(word);
267 int spaceDiff =
268 spaceStretchabilityCum * diff / spaceStretchabilitySum
269 - spaceDiffCum;
270 spaceDiffCum += spaceDiff;
271
272 DBG_OBJ_MSGF ("construct.line", 1, "%d (of %d): diff = %d",
273 i, words->size (), spaceDiff);
274
275 word->effSpace = word->origSpace + spaceDiff;
276 }
277 }
278 } else if (diff < 0) {
279 int spaceShrinkabilitySum = 0;
280 for (int i = line->firstWord; i < line->lastWord; i++)
281 spaceShrinkabilitySum += getSpaceShrinkability(words->getRef(i));
282
283 if (spaceShrinkabilitySum > 0) {
284 int spaceShrinkabilityCum = 0;
285 int spaceDiffCum = 0;
286 for (int i = line->firstWord; i < line->lastWord; i++) {
287 Word *word = words->getRef (i);
288 spaceShrinkabilityCum += getSpaceShrinkability(word);
289 int spaceDiff =
290 spaceShrinkabilityCum * diff / spaceShrinkabilitySum
291 - spaceDiffCum;
292 spaceDiffCum += spaceDiff;
293
294 DBG_OBJ_MSGF ("construct.line", 1, "%d (of %d): diff = %d",
295 i, words->size (), spaceDiff);
296
297 word->effSpace = word->origSpace + spaceDiff;
298 }
299 }
300 }
301
302 DBG_OBJ_LEAVE ();
303}
304
305
306Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
307 int newLastOofPos, bool temporary,
308 int minHeight)
309{
310 DBG_OBJ_ENTER ("construct.line", 0, "addLine", "%d, %d, %d, %s, %d",
311 firstWord, lastWord, newLastOofPos,
312 temporary ? "true" : "false", minHeight);
313 DBG_OBJ_MSGF ("construct.line", 0, "=> %d", lines->size ());
314
315 int lineWidth;
316 if (lastWord >= firstWord) {
317 DBG_MSG_WORD ("construct.line", 1, "<i>first word:</i> ", firstWord, "");
318 DBG_MSG_WORD ("construct.line", 1, "<i>last word:</i> ", lastWord, "");
319
320 Word *lastWordOfLine = words->getRef(lastWord);
321 // Word::totalWidth includes the hyphen (which is what we want here).
322 lineWidth = lastWordOfLine->totalWidth;
323 DBG_OBJ_MSGF ("construct.line", 1, "lineWidth (from last word): %d",
324 lineWidth);
325 } else {
326 // empty line
327 lineWidth = 0;
328 DBG_OBJ_MSGF ("construct.line", 1, "lineWidth (empty line): %d",
329 lineWidth);
330 }
331
332 // "lineWidth" is relative to leftOffset, so we may have to add
333 // "line1OffsetEff" (remember: this is, for list items, negative).
334 if (lines->size () == 0) {
335 lineWidth += line1OffsetEff;
336 DBG_OBJ_MSGF ("construct.line", 1, "lineWidth (line1OffsetEff): %d",
337 lineWidth);
338 }
339
340 lines->increase ();
341 DBG_OBJ_SET_NUM ("lines.size", lines->size ());
342
343 if(!temporary) {
344 // If the last line was temporary, this will be temporary, too, even
345 // if not requested.
346 if (lines->size () == 1 || nonTemporaryLines == lines->size () - 1)
347 nonTemporaryLines = lines->size ();
348 }
349
350 DBG_OBJ_MSGF ("construct.line", 1, "nonTemporaryLines = %d",
352
353 int lineIndex = lines->size () - 1;
354 Line *line = lines->getRef (lineIndex);
355
356 line->firstWord = firstWord;
357 line->lastWord = lastWord;
358
359 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "firstWord", line->firstWord);
360 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "lastWord", line->lastWord);
361
362 line->borderAscent = line->contentAscent = 0;
363 line->borderDescent = line->contentDescent = 0;
364 line->marginAscent = 0;
365 line->marginDescent = 0;
366 line->breakSpace = 0;
367
368 bool regardBorder = mustBorderBeRegarded (line);
369 line->leftOffset = max (regardBorder ? newLineLeftBorder : 0,
371 + (lineIndex == 0 ? line1OffsetEff : 0));
372 line->rightOffset = max (regardBorder ? newLineRightBorder : 0,
373 boxRestWidth ());
374
375 DBG_OBJ_MSGF ("construct.line", 1,
376 "regardBorder = %s, newLineLeftBorder = %d, "
377 "newLineRightBorder = %d",
378 regardBorder ? "true" : "false", newLineLeftBorder,
380
381 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "leftOffset", line->leftOffset);
382 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "rightOffset",
383 line->rightOffset);
384
385 alignLine (lineIndex);
386 calcTextOffset (lineIndex, lineBreakWidth);
387
388 for (int i = line->firstWord; i < line->lastWord; i++) {
389 Word *word = words->getRef (i);
390 lineWidth += (word->effSpace - word->origSpace);
391 DBG_OBJ_MSGF ("construct.line", 1,
392 "lineWidth [corrected space (%d - %d) after word %d]: %d",
393 word->effSpace, word->origSpace, i, lineWidth);
394 }
395
396 // Until here, lineWidth refers does not include floats on the left
397 // side. To include left floats, so that maxLineWidth, and
398 // eventually the requisition, is correct, line->leftOffset (minus
399 // margin+border+padding) has to be added, which was calculated
400 // just before. The correction in sizeAllocateImpl() is irrelevant
401 // in this regard. Also, right floats are not regarded here, but in
402 // OutOfFlowMgr::getSize(),
403 lineWidth += (line->leftOffset - getStyle()->boxOffsetX ());
404
405 if (lines->size () == 1) {
406 // first line
407 line->maxLineWidth = lineWidth;
409 } else {
410 Line *prevLine = lines->getRef (lines->size () - 2);
411 line->maxLineWidth = max (lineWidth, prevLine->maxLineWidth);
414 }
415
416 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "maxLineWidth",
417 line->maxLineWidth);
418
419 for(int i = line->firstWord; i <= line->lastWord; i++)
420 accumulateWordForLine (lineIndex, i);
421
422 if (lines->size () == 1)
423 line->top = 0;
424 else {
425 // See comment in Line::totalHeight for collapsing of the
426 // margins of adjacent lines.
427 Line *prevLine = lines->getRef (lines->size () - 2);
428 line->top = prevLine->top
429 + prevLine->totalHeight (line->marginAscent - line->borderAscent);
430 }
431
432 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "top", line->top);
433
434 // Especially empty lines (possible when there are floats) have
435 // zero height, which may cause endless loops. For this reasons,
436 // the height should be positive (assuming the caller passed
437 // minHeight > 0).
438 line->borderAscent = max (line->borderAscent, minHeight);
439 line->marginAscent = max (line->marginAscent, minHeight);
440
441 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "borderAscent",
442 line->borderAscent);
443 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "borderDescent",
444 line->borderDescent);
445 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "marginAscent",
446 line->marginAscent);
447 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "marginDescent",
448 line->marginDescent);
449 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "contentAscent",
450 line->contentAscent);
451 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "contentDescent",
452 line->contentDescent);
453
454 mustQueueResize = true;
455 DBG_OBJ_SET_BOOL ("mustQueueResize", mustQueueResize);
456
457 int xWidget = line->textOffset;
458 int yLine = yOffsetOfLineCreated (line);
459 for (int i = firstWord; i <= lastWord; i++) {
460 Word *word = words->getRef (i);
461 if (word->wordImgRenderer)
462 word->wordImgRenderer->setData (xWidget, lines->size () - 1);
463 if (word->spaceImgRenderer)
464 word->spaceImgRenderer->setData (xWidget, lines->size () - 1);
465
467 Widget *widget = word->content.widgetReference->widget;
468 int oofmIndex = getWidgetOOFIndex (widget);
469 oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr (oofmIndex);
470 // See also Textblock::sizeAllocate, and notes there about
471 // vertical alignment. Calculating the vertical position
472 // should probably be centralized.
473 if (oofm) {
474 int xRel = xWidget;
475 int yRel = yLine + (line->borderAscent - word->size.ascent);
476 int xRef, yRef;
477 if (findSizeRequestReference (oofmIndex, &xRef, &yRef))
478 oofm->tellPosition2 (widget, xRef + xRel, yRef + yRel);
479 else
480 oofm->tellIncompletePosition2 (widget, this, xRel, yRel);
481 }
482 }
483
484 xWidget += word->size.width + word->effSpace;
485 }
486
488 max (line->lastOofRefPositionedBeforeThisLine, newLastOofPos);
489 DBG_OBJ_SET_NUM ("lastLine.lastOofRefPositionedBeforeThisLine",
491
492 initNewLine ();
493
494 DBG_OBJ_LEAVE ();
495 return line;
496}
497
498void Textblock::processWord (int wordIndex)
499{
500 DBG_OBJ_ENTER ("construct.all", 0, "processWord", "%d", wordIndex);
501 DBG_MSG_WORD ("construct.all", 1, "<i>processed word:</i>", wordIndex, "");
502
503 int diffWords = wordWrap (wordIndex, false);
504
505 if (diffWords == 0)
506 handleWordExtremes (wordIndex);
507 else {
508 // If wordWrap has called hyphenateWord here, this has an effect
509 // on the call of handleWordExtremes. To avoid adding values
510 // more than one time (original un-hyphenated word, plus all
511 // parts of the hyphenated word, except the first one), the
512 // whole paragraph is recalculated again.
513 //
514 // (Note: the hyphenated word is often *before* wordIndex, and
515 // it may be even more than one word, which makes it nearly
516 // impossible to reconstruct what has happend. Therefore, there
517 // is no simpler approach to handle this.)
518
519 DBG_OBJ_MSGF ("construct.paragraph", 1,
520 "word list has become longer by %d", diffWords);
521 DBG_MSG_WORD ("construct.all", 1, "<i>processed word now:</i>",
522 wordIndex, "");
523
524 int firstWord;
525 if (paragraphs->size() > 0) {
526 firstWord = paragraphs->getLastRef()->firstWord;
527 paragraphs->setSize (paragraphs->size() - 1);
528 DBG_OBJ_SET_NUM ("paragraphs.size", paragraphs->size ());
529 DBG_OBJ_MSG ("construct.paragraph", 1, "removing last paragraph");
530 } else
531 firstWord = 0;
532
533 int lastIndex = wordIndex + diffWords;
534 DBG_OBJ_MSGF ("construct.paragraph", 1,
535 "processing words again from %d to %d",
536 firstWord, lastIndex);
537
538 // Furthermore, some more words have to be processed, so we
539 // iterate until wordIndex + diffWords, not only
540 // wordIndex.
542 for (int i = firstWord; i <= lastIndex; i++)
545 }
546
547 DBG_OBJ_LEAVE ();
548}
549
550/*
551 * This method is called in two cases: (i) when a word is added
552 * (ii) when a page has to be (partially) rewrapped. It does word wrap,
553 * and adds new lines if necessary.
554 *
555 * Returns whether the words list has changed at, or before, the word
556 * index.
557 */
558int Textblock::wordWrap (int wordIndex, bool wrapAll)
559{
560 DBG_OBJ_ENTER ("construct.word", 0, "wordWrap", "%d, %s",
561 wordIndex, wrapAll ? "true" : "false");
562 DBG_MSG_WORD ("construct.word", 1, "<i>wrapped word:</i> ", wordIndex, "");
563
564 if (!wrapAll)
566
567 initLine1Offset (wordIndex);
568
569 Word *word = words->getRef (wordIndex);
570 word->effSpace = word->origSpace;
571
572 accumulateWordData (wordIndex);
573
574 int n;
576 n = wrapWordOofRef (wordIndex, wrapAll);
577 else
578 n = wrapWordInFlow (wordIndex, wrapAll);
579
580 DBG_OBJ_MSGF ("construct.word", 1, "=> %d", n);
581 DBG_OBJ_LEAVE ();
582
583 return n;
584}
585
586int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll)
587{
588 DBG_OBJ_ENTER ("construct.word", 0, "wrapWordInFlow", "%d, %s",
589 wordIndex, wrapAll ? "true" : "false");
590
591 Word *word = words->getRef (wordIndex);
592 int diffWords = 0;
593
594 int penaltyIndex = calcPenaltyIndexForNewLine ();
595
596 bool newLine;
597 do {
598 // This variable, thereWillBeMoreSpace, is set to true, if, due
599 // to floats, this line is smaller than following lines will be
600 // (and, at the end, there will be surely lines without
601 // floats). If this is the case, lines may, in an extreme case,
602 // be left empty.
603
604 // (In other cases, lines are never left empty, even if this means
605 // that the contents is wider than the line break width. Leaving
606 // lines empty does not make sense without floats, since there will
607 // be no possibility with more space anymore.)
608
609 bool regardBorder = mustBorderBeRegarded (lines->size ());
610 bool thereWillBeMoreSpace = regardBorder ?
612
613 DBG_OBJ_MSGF ("construct.word", 1,
614 "thereWillBeMoreSpace = %s ? %s || %s : false = %s",
615 regardBorder ? "true" : "false",
616 newLineHasFloatLeft ? "true" : "false",
617 newLineHasFloatRight ? "true" : "false",
618 thereWillBeMoreSpace ? "true" : "false");
619
620
621 bool tempNewLine = false;
622 int firstIndex =
623 lines->size() == 0 ? 0 : lines->getLastRef()->lastWord + 1;
624 int searchUntil;
625
626 if (wordIndex < firstIndex)
627 // Current word is already part of a line (ending with
628 // firstIndex - 1), so no new line has to be added.
629 newLine = false;
630 else if (wrapAll && wordIndex >= firstIndex &&
631 wordIndex == words->size() -1) {
632 newLine = true;
633 searchUntil = wordIndex;
634 tempNewLine = true;
635 DBG_OBJ_MSG ("construct.word", 1, "<b>new line:</b> last word");
636 } else if (wordIndex >= firstIndex &&
637 // TODO: lineMustBeBroken should be independent of
638 // the penalty index?
639 word->badnessAndPenalty.lineMustBeBroken (penaltyIndex)) {
640 newLine = true;
641 searchUntil = wordIndex;
642 DBG_OBJ_MSG ("construct.word", 1, "<b>new line:</b> forced break");
643 } else {
644 // Break the line when too tight, but only when there is a
645 // possible break point so far. (TODO: I've forgotten the
646 // original bug which is fixed by this.)
647
648 // Exception of the latter rule: thereWillBeMoreSpace; see
649 // above, where it is defined.
650
651 DBG_OBJ_MSGF ("construct.word", 1,
652 "possible line break between %d and %d?",
653 firstIndex, wordIndex - 1);
655
656 bool possibleLineBreak = false;
657 for (int i = firstIndex;
658 !(thereWillBeMoreSpace || possibleLineBreak)
659 && i <= wordIndex - 1;
660 i++) {
661 DBG_OBJ_MSGF ("construct.word", 2, "examining word %d", i);
662 if (words->getRef(i)->badnessAndPenalty
663 .lineCanBeBroken (penaltyIndex)) {
664 DBG_MSG_WORD ("construct.word", 2, "break possible for word:",
665 i, "");
666 possibleLineBreak = true;
667 }
668 }
669
671 DBG_OBJ_MSGF ("construct.word", 1, "=> %s",
672 possibleLineBreak ? "true" : "false");
673
674 DBG_OBJ_MSGF ("construct.word", 1, "word->... too tight: %s",
675 word->badnessAndPenalty.lineTooTight () ?
676 "true" : "false");
677
678 if ((thereWillBeMoreSpace || possibleLineBreak)
679 && word->badnessAndPenalty.lineTooTight ()) {
680 newLine = true;
681 searchUntil = wordIndex - 1;
682 DBG_OBJ_MSG ("construct.word", 1,
683 "<b>new line:</b> line too tight");
684 } else {
685 DBG_OBJ_MSG ("construct.word", 1, "no <b>new line</b>");
686 newLine = false;
687 }
688 }
689
690 if(!newLine && !wrapAll) {
691 // No new line is added. "mustQueueResize" must,
692 // nevertheless, be set, so that flush() will call
693 // queueResize(), and later sizeRequestImpl() is called,
694 // which then calls showMissingLines(), which eventually
695 // calls this method again, with wrapAll == true, so that
696 // newLine is calculated as "true".
697 mustQueueResize = true;
698 DBG_OBJ_SET_BOOL ("mustQueueResize", mustQueueResize);
699 }
700
701 PRINTF ("[%p] special case? newLine = %s, wrapAll = %s => "
702 "mustQueueResize = %s\n", this, newLine ? "true" : "false",
703 wrapAll ? "true" : "false", mustQueueResize ? "true" : "false");
704
705 if (newLine) {
706 accumulateWordData (wordIndex);
707
708 int wordIndexEnd = wordIndex;
709 int height = 1; // assumed by calcBorders before (see there)
710 int breakPos;
711 int lastFloatPos = lines->size() > 0 ?
712 lines->getLastRef()->lastOofRefPositionedBeforeThisLine : -1;
713 DBG_OBJ_MSGF ("construct.word", 2, "lastFloatPos = %d", lastFloatPos);
714
715 balanceBreakPosAndHeight (wordIndex, firstIndex, &searchUntil,
716 tempNewLine, penaltyIndex, true,
717 &thereWillBeMoreSpace, wrapAll,
718 &diffWords, &wordIndexEnd,
719 &lastFloatPos, regardBorder, &height,
720 &breakPos);
721
722 bool floatHandled;
723 int yNewLine = yOffsetOfLineToBeCreated ();
724
725 do {
726 DBG_OBJ_MSG ("construct.word", 1, "<i>floatHandled loop cycle</i>");
728
729 DBG_OBJ_MSGF ("construct.word", 2,
730 "breakPos = %d, height = %d, lastFloatPos = %d",
731 breakPos, height, lastFloatPos);
732
733 int startSearch = max (firstIndex, lastFloatPos + 1);
734 int newFloatPos = -1;
735
736 // Step 1: search for the next float.
737 DBG_OBJ_MSGF ("construct.word", 2, "searching from %d to %d",
738 startSearch, breakPos);
739 for (int i = startSearch; newFloatPos == -1 && i <= breakPos; i++) {
740 core::Content *content = &(words->getRef(i)->content);
741 if (content->type == core::Content::WIDGET_OOF_REF) {
742 for (int j = 0; newFloatPos == -1 && j < NUM_OOFM; j++) {
743 Widget *widget = content->widgetReference->widget;
744 if ((searchOutOfFlowMgr(j)->affectsLeftBorder(widget) ||
745 searchOutOfFlowMgr(j)->affectsRightBorder (widget)))
746 newFloatPos = i;
747 }
748 }
749 }
750
751 DBG_OBJ_MSGF ("construct.word", 2, "newFloatPos = %d", newFloatPos);
752
753 if (newFloatPos == -1)
754 floatHandled = false;
755 else {
756 floatHandled = true;
757
758 // Step 2: position the float and re-calculate the line.
759
760 // TODO "x" is not quite correct, but this does not matter
761 // (currently?).
762
763 lastFloatPos = newFloatPos;
764
765 Widget *widget =
766 words->getRef(lastFloatPos)->content.widgetReference->widget;
767 int oofmIndex = getWidgetOOFIndex (widget);
768 oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr (oofmIndex);
769 if (oofm && oofm->mayAffectBordersAtAll ()) {
770 int xRel = boxOffsetX (), yRel = yNewLine, xRef, yRef;
771 if (findSizeRequestReference (oofmIndex, &xRef, &yRef))
772 oofm->tellPosition1 (widget, xRef + xRel, yRef + yRel);
773 else
774 oofm->tellIncompletePosition1 (widget, this, xRel, yRel);
775 }
776
777 balanceBreakPosAndHeight (wordIndex, firstIndex, &searchUntil,
778 tempNewLine, penaltyIndex, false,
779 &thereWillBeMoreSpace, wrapAll,
780 &diffWords, &wordIndexEnd,
781 &lastFloatPos, regardBorder, &height,
782 &breakPos);
783 }
784
786 } while (floatHandled);
787
788 int minHeight;
789 if (firstIndex <= breakPos) {
790 // Not an empty line: calculate line height from contents.
791 minHeight = 1;
792 DBG_OBJ_MSGF ("construct.word", 1, "%d <= %d => minHeight = %d",
793 firstIndex, breakPos, minHeight);
794 } else {
795 // Empty line. Too avoid too many lines one pixel high, we
796 // use the float heights.
798 minHeight = max (min (newLineLeftFloatHeight,
800 1);
802 minHeight = max (newLineLeftFloatHeight, 1);
804 minHeight = max (newLineRightFloatHeight, 1);
805 else
806 // May this happen?
807 minHeight = 1;
808
809 DBG_OBJ_MSGF ("construct.word", 1,
810 "%d < %d => minHeight = %d (l: %s (%d), r: %s (%d))",
811 firstIndex, breakPos, minHeight,
816 }
817
818 addLine (firstIndex, breakPos, lastFloatPos, tempNewLine, minHeight);
819
820 DBG_OBJ_MSGF ("construct.word", 1,
821 "accumulating again from %d to %d",
822 breakPos + 1, wordIndexEnd);
823 for(int i = breakPos + 1; i <= wordIndexEnd; i++)
825
826 // update word pointer as hyphenateWord() can trigger a
827 // reorganization of the words structure
828 word = words->getRef (wordIndex);
829
830 penaltyIndex = calcPenaltyIndexForNewLine ();
831 }
832 } while (newLine);
833
834 if(word->content.type == core::Content::WIDGET_IN_FLOW) {
835 // Set parentRef for the child, when necessary.
836 //
837 // parentRef is set for the child already, when a line is
838 // added. There are a couple of different situations to
839 // consider, e.g. when called from showMissingLines(), this word
840 // may already have been added in a previous call. To make
841 // things simple, we just check whether this word is contained
842 // within any line, or still "missing".
843
844 int firstWordWithoutLine;
845 if (lines->size() == 0)
846 firstWordWithoutLine = 0;
847 else
848 firstWordWithoutLine = lines->getLastRef()->lastWord + 1;
849
850 if (wordIndex >= firstWordWithoutLine) {
851 word->content.widget->parentRef = makeParentRefInFlow (lines->size ());
852 DBG_OBJ_SET_NUM_O (word->content.widget, "parentRef",
853 word->content.widget->parentRef);
854 }
855 }
856
857 DBG_OBJ_LEAVE ();
858
859 return diffWords;
860}
861
862int Textblock::wrapWordOofRef (int wordIndex, bool wrapAll)
863{
864 DBG_OBJ_ENTER ("construct.word", 0, "wrapWordOofRef", "%d, %s",
865 wordIndex, wrapAll ? "true" : "false");
866
867 Word *word = words->getRef (wordIndex);
868 Widget *widget = word->content.widgetReference->widget;
869 int yNewLine = yOffsetOfLineToBeCreated ();
870
871 // Floats, which affect either border, are handled in wrapWordInFlow; this
872 // is rather for positioned elements (but only for completeness:
873 // tellPosition1 is not implemented for positioned elements).
874 int oofmIndex = getWidgetOOFIndex (widget);
875 oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr (oofmIndex);
876 DBG_OBJ_MSGF ("construct.word", 1, "parentRef = %d, oofm = %p",
877 widget->parentRef, oofm);
878 if (oofm && !oofm->mayAffectBordersAtAll ()) {
879 // TODO Again, "x" is not correct (see above).
880 int xRel = boxOffsetX (), yRel = yNewLine, xRef, yRef;
881 if (findSizeRequestReference (oofmIndex, &xRef, &yRef))
882 oofm->tellPosition1 (widget, xRef + xRel, yRef + yRel);
883 else
884 oofm->tellIncompletePosition1 (widget, this, xRel, yRel);
885 }
886
887 if(word->content.type == core::Content::WIDGET_OOF_REF) {
888 // Set parentRef for the referred widget. Cf. wrapWordInFlow.
889 int firstWordWithoutLine;
890 if (lines->size() == 0)
891 firstWordWithoutLine = 0;
892 else
893 firstWordWithoutLine = lines->getLastRef()->lastWord + 1;
894
895 if (wordIndex >= firstWordWithoutLine) {
896 word->content.widgetReference->parentRef =
897 makeParentRefInFlow (lines->size ());
898 DBG_SET_WORD (wordIndex);
899 }
900 }
901
902 DBG_OBJ_LEAVE ();
903
904 return 0; // Words list not changed.
905}
906
907// *height must be initialized, but not *breakPos.
908// *wordIndexEnd must be initialized (initially to wordIndex)
909void Textblock::balanceBreakPosAndHeight (int wordIndex, int firstIndex,
910 int *searchUntil, bool tempNewLine,
911 int penaltyIndex,
912 bool borderIsCalculated,
913 bool *thereWillBeMoreSpace,
914 bool wrapAll, int *diffWords,
915 int *wordIndexEnd, int *lastFloatPos,
916 bool regardBorder, int *height,
917 int *breakPos)
918{
919 DBG_OBJ_ENTER ("construct.word", 0, "balanceBreakPosAndHeight",
920 "%d, %d. %d, %s, %d, %s, ..., %s, ..., %d, %s, %d, ...",
921 wordIndex, firstIndex, *searchUntil,
922 tempNewLine ? "true" : "false", penaltyIndex,
923 borderIsCalculated ? "true" : "false",
924 wrapAll ? "true" : "false", *lastFloatPos,
925 regardBorder ? "true" : "false", *height);
926
927 // The height of this part of the line (until the new break
928 // position) may change with the break position, but the break
929 // position may depend on the height. We try to let these values
930 // converge.
931 //
932 // The height, as a function of the break position, is
933 // monotonically (but not strictly) increasing, since more words
934 // may make the line higher (but not flatter). The break position,
935 // as a function of the height, is, however, monotonically (but not
936 // strictly) *de*creasing, since flatter lines may fit easier
937 // between floats (although this is a rare case). So a convergence
938 // is not necessary.
939 //
940 // For this reason, we iterate only as long as the height does not
941 // increase again, and stop if it remains the same. As the minimum
942 // is 1, this approach will force the iteration to stop.
943 //
944 // (As a side effect, this will lead to a larger break position,
945 // and so place as much words as possible in the line.)
946
947 int runNo = 1;
948 while (true) {
949 if (!(borderIsCalculated && runNo == 1)) {
950 // borderIsCalculated is, of course, only valid in the first run
951 calcBorders (*lastFloatPos, *height);
952 *thereWillBeMoreSpace = regardBorder ?
954
955 for(int i = firstIndex; i <= *wordIndexEnd; i++)
957 }
958
959 DBG_OBJ_MSGF ("construct.word", 1, "thereWillBeMoreSpace = %s",
960 *thereWillBeMoreSpace ? "true" : "false");
961
962 int newBreakPos =
963 searchBreakPos (wordIndex, firstIndex, searchUntil,
964 tempNewLine, penaltyIndex, *thereWillBeMoreSpace,
965 wrapAll, diffWords, wordIndexEnd, lastFloatPos);
966 int newHeight = calcLinePartHeight (firstIndex, newBreakPos);
967
968 DBG_OBJ_MSGF ("construct.word", 1,
969 "runNo = %d, newBreakPos = %d, newHeight = %d",
970 runNo, newBreakPos, newHeight);
971 if (runNo == 1)
972 DBG_OBJ_MSGF ("construct.word", 1,
973 "old: height = %d, breakPos undefined", *height);
974 else
975 DBG_OBJ_MSGF ("construct.word", 1,
976 "old: height = %d, breakPos = %d", *height, *breakPos);
977
978 if (runNo != 1 /* Since *some* value are needed, the results
979 from the first run are never discarded. */
980 && newHeight >= *height) {
981 if (newHeight == *height) {
982 // newHeight == height: convergence, stop here. The new break
983 // position is, nevertheless, adopted.
984 DBG_OBJ_MSG ("construct.word", 1, "stopping, adopting new values");
985 *breakPos = newBreakPos;
986 } else
987 // newHeight > height: do not proceed, discard new values,
988 // which are less desirable than the old ones (see above).
989 DBG_OBJ_MSG ("construct.word", 1,
990 "stopping, discarding new values");
991 break;
992 } else {
993 DBG_OBJ_MSG ("construct.word", 1, "adopting new values, continuing");
994 *height = newHeight;
995 *breakPos = newBreakPos;
996 }
997
998 runNo++;
999 }
1000
1001 DBG_OBJ_LEAVE_VAL ("%d, %d, %d, %d",
1002 *searchUntil, *lastFloatPos, *height, *breakPos);
1003}
1004
1005// *wordIndexEnd must be initialized (initially to wordIndex)
1006int Textblock::searchBreakPos (int wordIndex, int firstIndex, int *searchUntil,
1007 bool tempNewLine, int penaltyIndex,
1008 bool thereWillBeMoreSpace, bool wrapAll,
1009 int *diffWords, int *wordIndexEnd,
1010 int *addIndex1)
1011{
1012 DBG_OBJ_ENTER ("construct.word", 0, "searchBreakPos",
1013 "%d, %d. %d, %s, %d, %s, %s, ...",
1014 wordIndex, firstIndex, *searchUntil,
1015 tempNewLine ? "true" : "false", penaltyIndex,
1016 thereWillBeMoreSpace ? "true" : "false",
1017 wrapAll ? "true" : "false");
1018
1019 DBG_MSG_WORD ("construct.word", 1, "<i>first word:</i> ", firstIndex, "");
1020
1021 int result;
1022 bool lineAdded;
1023
1024 do {
1025 DBG_OBJ_MSG ("construct.word", 1, "<i>searchBreakPos loop cycle</i>");
1027
1028 if (firstIndex > *searchUntil) {
1029 // empty line
1030 DBG_OBJ_MSG ("construct.word", 1, "empty line");
1031 assert (*searchUntil == firstIndex - 1);
1032 result = firstIndex - 1;
1033 lineAdded = true;
1034 } else if (thereWillBeMoreSpace &&
1035 words->getRef(firstIndex)->badnessAndPenalty.lineTooTight ()) {
1036 int hyphenatedWord = considerHyphenation (firstIndex, firstIndex);
1037
1038 DBG_IF_RTFL {
1039 StringBuffer sb;
1040 words->getRef(firstIndex)->badnessAndPenalty.intoStringBuffer (&sb);
1041 DBG_OBJ_MSGF ("construct.word", 1,
1042 "too tight: %s ... hyphenatedWord = %d",
1043 sb.getChars (), hyphenatedWord);
1044 }
1045
1046 if (hyphenatedWord == -1) {
1047 DBG_OBJ_MSG ("construct.word", 1, "... => empty line");
1048 result = firstIndex - 1;
1049 lineAdded = true;
1050 } else {
1051 DBG_OBJ_MSG ("construct.word", 1,
1052 "... => hyphenate word and try again");
1053 int n = hyphenateWord (hyphenatedWord, addIndex1);
1054 *searchUntil += n;
1055 if (hyphenatedWord <= wordIndex)
1056 *wordIndexEnd += n;
1057 DBG_OBJ_MSGF ("construct.word", 1, "new searchUntil = %d",
1058 *searchUntil);
1059
1060 lineAdded = false;
1061 }
1062 } else {
1063 DBG_OBJ_MSG ("construct.word", 1, "non-empty line");
1064
1065 int breakPos =
1066 searchMinBap (firstIndex, *searchUntil, penaltyIndex,
1067 thereWillBeMoreSpace, wrapAll);
1068 int hyphenatedWord = considerHyphenation (firstIndex, breakPos);
1069
1070 DBG_OBJ_MSGF ("construct.word", 1, "breakPos = %d", breakPos);
1071 DBG_MSG_WORD ("construct.word", 1, "<i>break at word:</i> ",
1072 breakPos, "");
1073 DBG_OBJ_MSGF ("construct.word", 1, "hyphenatedWord = %d",
1074 hyphenatedWord);
1075 if (hyphenatedWord != -1)
1076 DBG_MSG_WORD ("construct.word", 1,
1077 "<i>hyphenate at word:</i> ",
1078 hyphenatedWord, "");
1079
1080 if(hyphenatedWord == -1) {
1081 result = breakPos;
1082 lineAdded = true;
1083 } else {
1084 // TODO hyphenateWord() should return whether something
1085 // has changed at all. So that a second run, with
1086 // !word->canBeHyphenated, is unnecessary.
1087 // TODO Update: The return value of hyphenateWord() should
1088 // be checked.
1089 DBG_OBJ_MSGF ("construct.word", 1, "old searchUntil = %d",
1090 *searchUntil);
1091 int n = hyphenateWord (hyphenatedWord, addIndex1);
1092 *searchUntil += n;
1093 if (hyphenatedWord <= wordIndex)
1094 *wordIndexEnd += n;
1095 DBG_OBJ_MSGF ("construct.word", 1, "new searchUntil = %d",
1096 *searchUntil);
1097 lineAdded = false;
1098
1099 if (hyphenatedWord <= wordIndex)
1100 *diffWords += n;
1101
1102 DBG_OBJ_MSGF ("construct.word", 1,
1103 "accumulating again from %d to %d",
1104 breakPos + 1, *wordIndexEnd);
1105 for(int i = breakPos + 1; i <= *wordIndexEnd; i++)
1107 }
1108 }
1109
1110 DBG_OBJ_MSG_END ();
1111 } while(!lineAdded);
1112
1113 DBG_OBJ_LEAVE_VAL ("%d", result);
1114
1115 return result;
1116}
1117
1118int Textblock::searchMinBap (int firstWord, int lastWord, int penaltyIndex,
1119 bool thereWillBeMoreSpace, bool correctAtEnd)
1120{
1121 DBG_OBJ_ENTER ("construct.word", 0, "searchMinBap", "%d, %d, %d, %s, %s",
1122 firstWord, lastWord, penaltyIndex,
1123 thereWillBeMoreSpace ? "true" : "false",
1124 correctAtEnd ? "true" : "false");
1125
1126 int pos = -1;
1127
1129 for (int i = firstWord; i <= lastWord; i++) {
1130 Word *w = words->getRef(i);
1131
1132 DBG_IF_RTFL {
1133 StringBuffer sb;
1135 DBG_OBJ_MSGF ("construct.word", 2, "%d (of %d): b+p: %s",
1136 i, words->size (), sb.getChars ());
1137 DBG_MSG_WORD ("construct.word", 2, "(<i>i. e.:</i> ", i, ")");
1138 }
1139
1140 // "<=" instead of "<" in the next lines (see also
1141 // "correctedBap.compareTo ...) tends to result in more words
1142 // per line -- theoretically. Practically, the case "==" will
1143 // never occur.
1144 if (pos == -1 ||
1145 w->badnessAndPenalty.compareTo (penaltyIndex,
1146 &words->getRef(pos)
1147 ->badnessAndPenalty) <= 0)
1148 pos = i;
1149 }
1150 DBG_OBJ_MSG_END ();
1151
1152 DBG_OBJ_MSGF ("construct.word", 1, "found at %d", pos);
1153
1154 if (correctAtEnd && lastWord == words->size () - 1) {
1155 // Since no break and no space is added, the last word will have
1156 // a penalty of inf. Actually, it should be less, since it is
1157 // the last word. However, since more words may follow, the
1158 // penalty is not changed, but here, the search is corrected
1159 // (maybe only temporary).
1160
1161 // (Notice that it was once (temporally) set to -inf, not 0, but
1162 // this will make e.g. test/table-1.html not work.)
1163 Word *w = words->getRef (lastWord);
1164 BadnessAndPenalty correctedBap = w->badnessAndPenalty;
1165 correctedBap.setPenalty (0);
1166
1167 DBG_IF_RTFL {
1168 StringBuffer sb;
1169 correctedBap.intoStringBuffer (&sb);
1170 DBG_OBJ_MSGF ("construct.word", 1, "corrected b+p: %s",
1171 sb.getChars ());
1172 }
1173
1174 if (correctedBap.compareTo(penaltyIndex,
1175 &words->getRef(pos)->badnessAndPenalty) <= 0) {
1176 pos = lastWord;
1177 DBG_OBJ_MSGF ("construct.word", 1, "corrected: %d", pos);
1178 }
1179 }
1180
1181 DBG_OBJ_LEAVE ();
1182 return pos;
1183}
1184
1190int Textblock::considerHyphenation (int firstIndex, int breakPos)
1191{
1192 int hyphenatedWord = -1;
1193
1194 Word *wordBreak = words->getRef(breakPos);
1195 //printf ("[%p] line (broken at word %d): ", this, breakPos);
1196 //printWord (wordBreak);
1197 //printf ("\n");
1198
1199 // A tight line: maybe, after hyphenation, some parts of the last
1200 // word of this line can be put into the next line.
1201 if (wordBreak->badnessAndPenalty.lineTight ()) {
1202 // Sometimes, it is not the last word, which must be hyphenated,
1203 // but some word before. Here, we search for the first word
1204 // which can be hyphenated, *and* makes the line too tight.
1205 for (int i = breakPos; i >= firstIndex; i--) {
1206 Word *word1 = words->getRef (i);
1207 if (word1->badnessAndPenalty.lineTight () &&
1208 isHyphenationCandidate (word1))
1209 hyphenatedWord = i;
1210 }
1211 }
1212
1213 // A loose line: maybe, after hyphenation, some parts of the first
1214 // word of the next line can be put into this line.
1215 if (wordBreak->badnessAndPenalty.lineLoose () &&
1216 breakPos + 1 < words->size ()) {
1217 Word *word2 = words->getRef(breakPos + 1);
1218 if (isHyphenationCandidate (word2))
1219 hyphenatedWord = breakPos + 1;
1220 }
1221
1222 return hyphenatedWord;
1223}
1224
1226{
1227 return (word->flags & Word::CAN_BE_HYPHENATED) &&
1228 word->style->x_lang[0] &&
1229 isBreakAllowedInWord (word) &&
1230 word->content.type == core::Content::TEXT &&
1232}
1233
1234
1235int Textblock::calcLinePartHeight (int firstWord, int lastWord)
1236{
1237 int ascent = 0, descent = 0;
1238
1239 for (int i = firstWord; i <= lastWord; i++) {
1240 Word *word = words->getRef (i);
1241 ascent = max (ascent, word->size.ascent);
1242 descent = max (descent, word->size.descent);
1243 }
1244
1245 return max (ascent + descent, 1);
1246}
1247
1252{
1253 // TODO Overall, clarify penalty index.
1254
1255 DBG_OBJ_ENTER ("construct.paragraph", 0, "handleWordExtremes", "%d",
1256 wordIndex);
1257
1258 initLine1Offset (wordIndex);
1259
1260 Word *word = words->getRef (wordIndex);
1261 DBG_MSG_WORD ("construct.paragraph", 1,
1262 "<i>handled word:</i> ", wordIndex, "");
1263
1264 core::Extremes wordExtremes;
1265 getWordExtremes (word, &wordExtremes);
1266 DBG_OBJ_MSGF ("construct.paragraph", 1, "extremes: %d (%d) / %d (%d)",
1267 wordExtremes.minWidth, wordExtremes.minWidthIntrinsic,
1268 wordExtremes.maxWidth, wordExtremes.maxWidthIntrinsic);
1269
1270 if (wordIndex == 0) {
1271 wordExtremes.minWidth += line1OffsetEff;
1272 wordExtremes.minWidthIntrinsic += line1OffsetEff;
1273 wordExtremes.maxWidth += line1OffsetEff;
1274 wordExtremes.maxWidthIntrinsic += line1OffsetEff;
1275 }
1276
1277 if (paragraphs->size() == 0 ||
1278 words->getRef(paragraphs->getLastRef()->lastWord)
1279 ->badnessAndPenalty.lineMustBeBroken (1)) {
1280 // Add a new paragraph.
1281 paragraphs->increase ();
1282 DBG_OBJ_SET_NUM ("paragraphs.size", paragraphs->size ());
1283
1284 Paragraph *prevPar = paragraphs->size() == 1 ?
1285 NULL : paragraphs->getRef(paragraphs->size() - 2);
1286 Paragraph *par = paragraphs->getLastRef();
1287
1288 par->firstWord = par->lastWord = wordIndex;
1289 par->parMin = par->parMinIntrinsic = par->parMax = par->parMaxIntrinsic =
1290 par->parAdjustmentWidth = 0;
1291
1292 if (prevPar) {
1293 par->maxParMin = prevPar->maxParMin;
1294 par->maxParMinIntrinsic = prevPar->maxParMinIntrinsic;
1295 par->maxParMax = prevPar->maxParMax;
1296 par->maxParMaxIntrinsic = prevPar->maxParMaxIntrinsic;
1298 } else
1299 par->maxParMin = par->maxParMinIntrinsic = par->maxParMax =
1301
1302 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1, "maxParMin",
1303 par->maxParMin);
1304 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1,
1305 "maxParMinIntrinsic", par->maxParMinIntrinsic);
1306 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1, "maxParMax",
1307 par->maxParMax);
1308 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1,
1309 "maxParMaxIntrinsic", par->maxParMaxIntrinsic);
1310 }
1311
1312 Paragraph *lastPar = paragraphs->getLastRef();
1313
1314 int corrDiffMin, corrDiffMax;
1315 if (wordIndex - 1 >= lastPar->firstWord) {
1316 Word *lastWord = words->getRef (wordIndex - 1);
1317 if (lastWord->badnessAndPenalty.lineCanBeBroken (1) &&
1318 (lastWord->flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) == 0)
1319 corrDiffMin = 0;
1320 else
1321 corrDiffMin = lastWord->origSpace - lastWord->hyphenWidth;
1322
1323 corrDiffMax = lastWord->origSpace - lastWord->hyphenWidth;
1324 } else
1325 corrDiffMin = corrDiffMax = 0;
1326
1327 // Minimum: between two *possible* breaks.
1328 // Shrinkability could be considered, but really does not play a role.
1329 lastPar->parMin += wordExtremes.minWidth + word->hyphenWidth + corrDiffMin;
1330 lastPar->parMinIntrinsic +=
1331 wordExtremes.minWidthIntrinsic + word->hyphenWidth + corrDiffMin;
1332 lastPar->parAdjustmentWidth +=
1333 wordExtremes.adjustmentWidth + word->hyphenWidth + corrDiffMin;
1334 lastPar->maxParMin = max (lastPar->maxParMin, lastPar->parMin);
1335 lastPar->maxParMinIntrinsic =
1336 max (lastPar->maxParMinIntrinsic, lastPar->parMinIntrinsic);
1337 lastPar->maxParAdjustmentWidth =
1338 max (lastPar->maxParAdjustmentWidth, lastPar->parAdjustmentWidth);
1339
1340 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1, "parMin",
1341 lastPar->parMin);
1342 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1,
1343 "parMinIntrinsic", lastPar->parMinIntrinsic);
1344 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1, "maxParMin",
1345 lastPar->maxParMin);
1346 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1,
1347 "maxParMinIntrinsic", lastPar->maxParMinIntrinsic);
1348 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1,
1349 "parAdjustmentWidth", lastPar->parAdjustmentWidth);
1350 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1,
1351 "maxParAdjustmentWidth",
1352 lastPar->maxParAdjustmentWidth);
1353
1354 if (word->badnessAndPenalty.lineCanBeBroken (1) &&
1355 (word->flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) == 0) {
1356 lastPar->parMin = lastPar->parMinIntrinsic = lastPar->parAdjustmentWidth
1357 = 0;
1358
1359 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1, "parMin",
1360 lastPar->parMin);
1361 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1,
1362 "parMinIntrinsic", lastPar->parMinIntrinsic);
1363 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1,
1364 "parAdjustmentWidth",
1365 lastPar->parAdjustmentWidth);
1366 }
1367
1368 // Maximum: between two *necessary* breaks.
1369 lastPar->parMax += wordExtremes.maxWidth + word->hyphenWidth + corrDiffMax;
1370 lastPar->parMaxIntrinsic +=
1371 wordExtremes.maxWidthIntrinsic + word->hyphenWidth + corrDiffMax;
1372 lastPar->maxParMax = max (lastPar->maxParMax, lastPar->parMax);
1373 lastPar->maxParMaxIntrinsic =
1374 max (lastPar->maxParMaxIntrinsic, lastPar->parMaxIntrinsic);
1375
1376 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1, "parMax",
1377 lastPar->parMax);
1378 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1,
1379 "parMaxIntrinsic", lastPar->parMaxIntrinsic);
1380 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1, "maxParMax",
1381 lastPar->maxParMax);
1382 DBG_OBJ_ARRATTRSET_NUM ("paragraphs", paragraphs->size() - 1,
1383 "maxParMaxIntrinsic", lastPar->maxParMaxIntrinsic);
1384
1385 lastPar->lastWord = wordIndex;
1386 DBG_OBJ_LEAVE ();
1387}
1388
1393{
1394 if (paragraphs->size() > 0) {
1395 Word *word = words->getLastRef ();
1396 if (word->badnessAndPenalty.lineCanBeBroken (1) &&
1397 (word->flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) == 0) {
1398 Paragraph *lastPar = paragraphs->getLastRef();
1399 lastPar->parMin = lastPar->parMinIntrinsic =
1400 lastPar->parAdjustmentWidth = 0;
1401 PRINTF (" => corrected; parMin = %d\n",
1402 paragraphs->getLastRef()->parMin);
1403 }
1404 }
1405}
1406
1407
1408int Textblock::hyphenateWord (int wordIndex, int *addIndex1)
1409{
1410 Word *hyphenatedWord = words->getRef(wordIndex);
1411 char lang[3] = { hyphenatedWord->style->x_lang[0],
1412 hyphenatedWord->style->x_lang[1], 0 };
1413 Hyphenator *hyphenator = Hyphenator::getHyphenator (lang);
1414 PRINTF ("[%p] considering to hyphenate word %d, '%s', in language '%s'\n",
1415 this, wordIndex, words->getRef(wordIndex)->content.text, lang);
1416 int numBreaks;
1417 int *breakPos =
1418 hyphenator->hyphenateWord (layout->getPlatform (),
1419 hyphenatedWord->content.text, &numBreaks);
1420
1421 if (numBreaks > 0) {
1422 Word origWord = *hyphenatedWord;
1423
1424 core::Requisition wordSize[numBreaks + 1];
1425 calcTextSizes (origWord.content.text, strlen (origWord.content.text),
1426 origWord.style, numBreaks, breakPos, wordSize);
1427
1428 PRINTF ("[%p] %d words ...\n", this, words->size ());
1429 words->insert (wordIndex, numBreaks);
1430
1431 DBG_IF_RTFL {
1432 for (int i = wordIndex + numBreaks; i < words->size (); i++)
1433 DBG_SET_WORD (i);
1434 }
1435
1436 for (int i = 0; i < numBreaks; i++)
1437 initWord (wordIndex + i);
1438 PRINTF ("[%p] ... => %d words\n", this, words->size ());
1439
1440 moveWordIndices (wordIndex, numBreaks, addIndex1);
1441
1442 // Adjust anchor indexes.
1443 for (int i = 0; i < anchors->size (); i++) {
1444 Anchor *anchor = anchors->getRef (i);
1445 if (anchor->wordIndex > wordIndex)
1446 anchor->wordIndex += numBreaks;
1447 }
1448
1449 for (int i = 0; i < numBreaks + 1; i++) {
1450 Word *w = words->getRef (wordIndex + i);
1451 fillWord (wordIndex + i, wordSize[i].width, wordSize[i].ascent,
1452 wordSize[i].descent, false, origWord.style);
1453
1454 // TODO There should be a method fillText0.
1456
1457 int start = (i == 0 ? 0 : breakPos[i - 1]);
1458 int end = (i == numBreaks ?
1459 strlen (origWord.content.text) : breakPos[i]);
1460 w->content.text =
1461 layout->textZone->strndup (origWord.content.text + start,
1462 end - start);
1463
1464 // Note: there are numBreaks + 1 word parts.
1465 if (i == 0)
1466 w->flags |= Word::WORD_START;
1467 else
1468 w->flags &= ~Word::WORD_START;
1469
1470 if (i == numBreaks)
1471 w->flags |= Word::WORD_END;
1472 else
1473 w->flags &= ~Word::WORD_END;
1474
1475 if (i < numBreaks) {
1476 // TODO There should be a method fillHyphen.
1479 // "\xe2\x80\x90" is an unconditional hyphen.
1480 w->hyphenWidth =
1482 strlen (hyphenDrawChar));
1485 } else {
1486 if (origWord.content.space)
1487 fillSpace (wordIndex + i, origWord.spaceStyle);
1488 }
1489
1490 DBG_SET_WORD (wordIndex + i);
1491 }
1492
1493 // AccumulateWordData() will calculate the width, which depends
1494 // on the borders (possibly limited by floats), which depends on
1495 // the widgeds so far. For this reason, it is important to first
1496 // make all words consistent before calling
1497 // accumulateWordData(); therefore the second loop.
1498
1499 for (int i = 0; i < numBreaks + 1; i++)
1500 accumulateWordData (wordIndex + i);
1501
1502 PRINTF (" finished\n");
1503
1504 //delete origword->content.text; TODO: Via textZone?
1505 origWord.style->unref ();
1506 origWord.spaceStyle->unref ();
1507
1508 free (breakPos);
1509 } else
1510 words->getRef(wordIndex)->flags &= ~Word::CAN_BE_HYPHENATED;
1511
1512 return numBreaks;
1513}
1514
1515void Textblock::moveWordIndices (int wordIndex, int num, int *addIndex1)
1516{
1517 DBG_OBJ_ENTER ("construct.word", 0, "moveWordIndices", "%d, %d",
1518 wordIndex, num);
1519
1520 for (int i = 0; i < NUM_OOFM; i++)
1521 if (searchOutOfFlowMgr(i))
1522 searchOutOfFlowMgr(i)->moveExternalIndices (this, wordIndex, num);
1523
1524 for (int i = lines->size () - 1; i >= 0; i--) {
1525 Line *line = lines->getRef (i);
1526 if (line->lastOofRefPositionedBeforeThisLine < wordIndex) {
1527 // Since lastOofRefPositionedBeforeThisLine are ascending,
1528 // the search can be stopped here.
1529 DBG_OBJ_MSGF ("construct.word", 1,
1530 "lines[%d]->lastOofRef = %d < %d => stop",
1531 i, line->lastOofRefPositionedBeforeThisLine, wordIndex);
1532 break;
1533 } else {
1534 DBG_OBJ_MSGF ("construct.word", 1,
1535 "adding %d to lines[%d]->lastOofRef...: %d -> %d",
1539 }
1540 }
1541
1542 // Unlike the last line, the last paragraph is already constructed. (To
1543 // make sure we cover all cases, we iterate over the last paragraphs.)
1544 Paragraph *par;
1545 for (int parNo = paragraphs->size () - 1;
1546 parNo >= 0 &&
1547 (par = paragraphs->getRef(parNo)) && par->lastWord > wordIndex;
1548 parNo--) {
1549 par->lastWord += num;
1550 if (par->firstWord > wordIndex)
1551 par->firstWord += num;
1552 }
1553
1554 // Addiditional indices. When needed, the number can be extended.
1555 if (addIndex1 && *addIndex1 >= wordIndex)
1556 *addIndex1 += num;
1557
1558 DBG_OBJ_LEAVE ();
1559}
1560
1561void Textblock::accumulateWordForLine (int lineIndex, int wordIndex)
1562{
1563 DBG_OBJ_ENTER ("construct.line", 1, "accumulateWordForLine", "%d, %d",
1564 lineIndex, wordIndex);
1565 DBG_MSG_WORD ("construct.line", 2, "<i>word:</i> ", wordIndex, "");
1566
1567 Line *line = lines->getRef (lineIndex);
1568 Word *word = words->getRef (wordIndex);
1569
1570 int len = word->style->font->ascent;
1572 len += len / 2;
1573 line->contentAscent = max (line->contentAscent, len);
1574
1575 len = word->style->font->descent;
1576 if (word->style->valign == core::style::VALIGN_SUB)
1577 len += word->style->font->ascent / 3;
1578 line->contentDescent = max (line->contentDescent, len);
1579
1580 int borderAscent, borderDescent, marginAscent, marginDescent;
1581
1582 DBG_OBJ_MSGF ("construct.line", 2, "size.ascent = %d, size.descent = %d",
1583 word->size.ascent, word->size.descent);
1584
1586 // TODO Consider extraSpace?
1587 marginAscent = word->size.ascent;
1588 marginDescent = word->size.descent;
1589 borderAscent =
1590 marginAscent - word->content.widget->getStyle()->margin.top;
1591 borderDescent =
1592 marginDescent - word->content.widget->getStyle()->margin.bottom;
1593
1594 word->content.widget->parentRef = makeParentRefInFlow (lineIndex);
1595 DBG_OBJ_SET_NUM_O (word->content.widget, "parentRef",
1596 word->content.widget->parentRef);
1597 } else {
1598 borderAscent = marginAscent = word->size.ascent;
1599 borderDescent = marginDescent = word->size.descent;
1600
1601 if (word->content.type == core::Content::BREAK)
1602 line->breakSpace = max (word->content.breakSpace, line->breakSpace);
1603 else if (word->content.type == core::Content::WIDGET_OOF_REF) {
1605 makeParentRefInFlow (lineIndex);
1606 DBG_SET_WORD (wordIndex);
1607 }
1608 }
1609
1610 DBG_OBJ_MSGF ("construct.line", 2,
1611 "borderAscent = %d, borderDescent = %d, marginAscent = %d, "
1612 "marginDescent = %d",
1613 borderAscent, borderDescent, marginAscent, marginDescent);
1614
1615 line->borderAscent = max (line->borderAscent, borderAscent);
1616 line->borderDescent = max (line->borderDescent, borderDescent);
1617 line->marginAscent = max (line->marginAscent, marginAscent);
1618 line->marginDescent = max (line->marginDescent, marginDescent);
1619
1620 DBG_OBJ_LEAVE ();
1621}
1622
1624{
1625 DBG_OBJ_ENTER ("construct.word.accum", 1, "accumulateWordData", "%d",
1626 wordIndex);
1627 DBG_MSG_WORD ("construct.word.accum", 1, "<i>word:</i> ", wordIndex, "");
1628
1629 // Typically, the word in question is in the last line; in any case
1630 // quite at the end of the text, so that linear search is actually
1631 // the fastest option.
1632 int lineIndex = lines->size ();
1633 while (lineIndex > 0 && wordIndex <= lines->getRef(lineIndex - 1)->lastWord)
1634 lineIndex--;
1635
1636 int firstWordOfLine;
1637 if (lineIndex == 0)
1638 firstWordOfLine = 0;
1639 else
1640 firstWordOfLine = lines->getRef(lineIndex - 1)->lastWord + 1;
1641
1642 Word *word = words->getRef (wordIndex);
1643 DBG_OBJ_MSGF ("construct.word.accum", 2, "lineIndex = %d", lineIndex);
1644
1645 int lineBreakWidth = calcLineBreakWidth (lineIndex);
1646
1647 DBG_OBJ_MSGF ("construct.word.accum", 2,
1648 "(%s existing line %d starts with word %d; "
1649 "lineBreakWidth = %d)",
1650 lineIndex < lines->size () ? "already" : "not yet",
1651 lineIndex, firstWordOfLine, lineBreakWidth);
1652
1653 if (wordIndex == firstWordOfLine) {
1654 // first word of the (not neccessarily yet existing) line
1655 word->totalWidth = word->size.width + word->hyphenWidth;
1656 word->maxAscent = word->size.ascent;
1657 word->maxDescent = word->size.descent;
1658 word->totalSpaceStretchability = 0;
1659 word->totalSpaceShrinkability = 0;
1660
1661 DBG_OBJ_MSGF ("construct.word.accum", 1,
1662 "first word of line: words[%d].totalWidth = %d + %d = %d; "
1663 "maxAscent = %d, maxDescent = %d",
1664 wordIndex, word->size.width, word->hyphenWidth,
1665 word->totalWidth, word->maxAscent, word->maxDescent);
1666 } else {
1667 Word *prevWord = words->getRef (wordIndex - 1);
1668
1669 word->totalWidth = prevWord->totalWidth
1670 + prevWord->origSpace - prevWord->hyphenWidth
1671 + word->size.width + word->hyphenWidth;
1672 word->maxAscent = max (prevWord->maxAscent, word->size.ascent);
1673 word->maxDescent = max (prevWord->maxDescent, word->size.descent);
1677 prevWord->totalSpaceShrinkability + getSpaceShrinkability(prevWord);
1678
1679 DBG_OBJ_MSGF ("construct.word.accum", 1,
1680 "not first word of line: words[%d].totalWidth = %d + %d - "
1681 "%d + %d + %d = %d; maxAscent = max (%d, %d) = %d, "
1682 "maxDescent = max (%d, %d) = %d",
1683 wordIndex, prevWord->totalWidth, prevWord->origSpace,
1684 prevWord->hyphenWidth, word->size.width,
1685 word->hyphenWidth, word->totalWidth,
1686 prevWord->maxAscent, word->size.ascent, word->maxAscent,
1687 prevWord->maxDescent, word->size.descent, word->maxDescent);
1688 }
1689
1690 int totalStretchability =
1692 int totalShrinkability =
1694
1695 DBG_OBJ_MSGF ("construct.word.accum", 1,
1696 "totalStretchability = %d + ... = %d",
1697 word->totalSpaceStretchability, totalStretchability);
1698 DBG_OBJ_MSGF ("construct.word.accum", 1,
1699 "totalShrinkability = %d + ... = %d",
1700 word->totalSpaceShrinkability, totalShrinkability);
1701
1703 totalStretchability,
1704 totalShrinkability);
1705
1706 DBG_IF_RTFL {
1707 StringBuffer sb;
1709 DBG_OBJ_ARRATTRSET_SYM ("words", wordIndex, "badnessAndPenalty",
1710 sb.getChars ());
1711 }
1712
1713 DBG_OBJ_LEAVE ();
1714}
1715
1717{
1718 DBG_OBJ_ENTER ("construct.word.width", 1, "calcLineBreakWidth",
1719 "%d <i>of %d</i>", lineIndex, lines->size());
1720
1721 int lineBreakWidth = this->lineBreakWidth - leftInnerPadding;
1722 if (limitTextWidth &&
1723 layout->getUsesViewport () &&
1724 // margin/border/padding will be subtracted later, via OOFM.
1727 if (lineIndex == 0)
1729
1730 int leftBorder, rightBorder;
1731 if (mustBorderBeRegarded (lineIndex)) {
1732 leftBorder = newLineLeftBorder;
1733 rightBorder = newLineRightBorder;
1734 } else
1735 leftBorder = rightBorder = 0;
1736
1737 leftBorder = max (leftBorder, boxOffsetX());
1738 rightBorder = max (rightBorder, boxRestWidth());
1739
1740 lineBreakWidth -= (leftBorder + rightBorder);
1741
1742 DBG_OBJ_LEAVE_VAL ("%d - %d - (%d + %d) = %d",
1743 this->lineBreakWidth, leftInnerPadding, leftBorder,
1744 rightBorder, lineBreakWidth);
1745 return lineBreakWidth;
1746}
1747
1748void Textblock::initLine1Offset (int wordIndex)
1749{
1750 Word *word = words->getRef (wordIndex);
1751
1752 /* Test whether line1Offset can be used. */
1753 if (wordIndex == 0) {
1756 line1OffsetEff = 0;
1757 } else {
1758 int indent = 0;
1759
1761 word->content.widget->isBlockLevel()) {
1762 /* don't use text-indent when nesting blocks */
1763 } else {
1764 if (core::style::isPerLength(getStyle()->textIndent)) {
1766 (lineBreakWidth, getStyle()->textIndent);
1767 } else {
1768 indent = core::style::absLengthVal (getStyle()->textIndent);
1769 }
1770 }
1771 line1OffsetEff = line1Offset + indent;
1772 }
1773 }
1774}
1775
1781void Textblock::alignLine (int lineIndex)
1782{
1783 DBG_OBJ_ENTER ("construct.line", 0, "alignLine", "%d", lineIndex);
1784
1785 Line *line = lines->getRef (lineIndex);
1786
1787 for (int i = line->firstWord; i <= line->lastWord; i++)
1788 words->getRef(i)->effSpace = words->getRef(i)->origSpace;
1789
1790 // We are not interested in the alignment of floats etc.
1791 int firstWordNotOofRef = line->firstWord;
1792 while (firstWordNotOofRef <= line->lastWord &&
1793 words->getRef(firstWordNotOofRef)->content.type
1795 firstWordNotOofRef++;
1796
1797 if (firstWordNotOofRef <= line->lastWord) {
1798 Word *firstWord = words->getRef (firstWordNotOofRef);
1799
1800 if (firstWord->content.type != core::Content::BREAK) {
1801 Word *lastWord = words->getRef (line->lastWord);
1802 int lineBreakWidth =
1803 this->lineBreakWidth - (line->leftOffset + line->rightOffset);
1804
1805 switch (firstWord->style->textAlign) {
1807 DBG_OBJ_MSG ("construct.line", 1,
1808 "first word has 'text-align: left'");
1809 line->alignment = Line::LEFT;
1810 break;
1811 case core::style::TEXT_ALIGN_STRING: /* handled elsewhere (in the
1812 * future)? */
1813 DBG_OBJ_MSG ("construct.line", 1,
1814 "first word has 'text-align: string'");
1815 line->alignment = Line::LEFT;
1816 break;
1817 case core::style::TEXT_ALIGN_JUSTIFY: /* see some lines above */
1818 DBG_OBJ_MSG ("construct.line", 1,
1819 "first word has 'text-align: justify'");
1820 line->alignment = Line::LEFT;
1821 // Do not justify the last line of a paragraph (which ends on a
1822 // BREAK or with the last word of the page).
1823 if(!(lastWord->content.type == core::Content::BREAK ||
1824 line->lastWord == words->size () - 1) ||
1825 // In some cases, however, an unjustified line would be too wide:
1826 // when the line would be shrunken otherwise. (This solution is
1827 // far from perfect, but a better solution would make changes in
1828 // the line breaking algorithm necessary.)
1829 lineBreakWidth < lastWord->totalWidth)
1830 justifyLine (line, lineBreakWidth - lastWord->totalWidth);
1831 break;
1833 DBG_OBJ_MSG ("construct.line", 1,
1834 "first word has 'text-align: right'");
1835 line->alignment = Line::RIGHT;
1836 break;
1838 DBG_OBJ_MSG ("construct.line", 1,
1839 "first word has 'text-align: center'");
1840 line->alignment = Line::CENTER;
1841 break;
1842 default:
1843 // compiler happiness
1844 line->alignment = Line::LEFT;
1845 }
1846
1847 } else
1848 // empty line (only line break);
1849 line->alignment = Line::LEFT;
1850 } else
1851 // empty line (or only OOF references).
1852 line->alignment = Line::LEFT;
1853
1854 DBG_OBJ_LEAVE ();
1855}
1856
1857void Textblock::calcTextOffset (int lineIndex, int totalWidth)
1858{
1859 DBG_OBJ_ENTER ("construct.line", 0, "calcTextOffset", "%d, %d",
1860 lineIndex, totalWidth);
1861
1862 Line *line = lines->getRef (lineIndex);
1863 int lineWidth = line->firstWord <= line->lastWord ?
1864 words->getRef(line->lastWord)->totalWidth : 0;
1865
1866 switch (line->alignment) {
1867 case Line::LEFT:
1868 line->textOffset = line->leftOffset;
1869 DBG_OBJ_MSGF ("construct.line", 1, "left: textOffset = %d",
1870 line->textOffset);
1871 break;
1872
1873 case Line::RIGHT:
1874 line->textOffset = totalWidth - line->rightOffset - lineWidth;
1875 DBG_OBJ_MSGF ("construct.line", 1,
1876 "right: textOffset = %d - %d - %d = %d",
1877 totalWidth, line->rightOffset, lineWidth, line->textOffset);
1878 break;
1879
1880 case Line::CENTER:
1881 line->textOffset =
1882 (line->leftOffset + totalWidth - line->rightOffset - lineWidth) / 2;
1883 DBG_OBJ_MSGF ("construct.line", 1,
1884 "center: textOffset = (%d + %d - %d - %d) /2 = %d",
1885 line->leftOffset, totalWidth, line->rightOffset, lineWidth,
1886 line->textOffset);
1887 break;
1888
1889 default:
1891 break;
1892 }
1893
1894 // For large lines (images etc), which do not fit into the viewport:
1895 if (line->textOffset < line->leftOffset)
1896 line->textOffset = line->leftOffset;
1897
1898 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "textOffset", line->textOffset);
1899 DBG_OBJ_LEAVE ();
1900}
1901
1909{
1910 DBG_OBJ_ENTER0 ("construct.line", 0, "rewrap");
1911
1912 if (wrapRefLines == -1)
1913 DBG_OBJ_MSG ("construct.line", 0, "does not have to be rewrapped");
1914 else {
1915 // All lines up from wrapRef will be rebuild from the word list,
1916 // the line list up from this position is rebuild.
1917 lines->setSize (wrapRefLines);
1918 DBG_OBJ_SET_NUM ("lines.size", lines->size ());
1920
1921 initNewLine ();
1922
1923 int firstWord;
1924 if (lines->size () > 0) {
1925 Line *lastLine = lines->getLastRef();
1926 firstWord = lastLine->lastWord + 1;
1927 } else
1928 firstWord = 0;
1929
1930 DBG_OBJ_MSGF ("construct.line", 0, "starting with word %d", firstWord);
1931
1932 lastWordDrawn = min (lastWordDrawn, firstWord - 1);
1933 DBG_OBJ_SET_NUM ("lastWordDrawn", lastWordDrawn);
1934
1935 for (int i = firstWord; i < words->size (); i++) {
1936 Word *word = words->getRef (i);
1937
1938 switch (word->content.type) {
1940 calcSizeOfWidgetInFlow (i, word->content.widget, &word->size);
1942 break;
1943
1945 {
1946 int oofmIndex =
1948 oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr (oofmIndex);
1950 &(word->size));
1952 }
1953 break;
1954
1955 default:
1956 break;
1957 }
1958
1959 wordWrap (i, false);
1960
1961 // Somewhat historical, but still important, note:
1962 //
1963 // For the case that something else is done with this word, it
1964 // is important that wordWrap() may insert some new words; since
1965 // NotSoSimpleVector is used for the words list, the internal
1966 // structure may have changed, so getRef() must be called again.
1967 //
1968 // So this is necessary: word = words->getRef (i);
1969 }
1970
1971 // Next time, the page will not have to be rewrapped.
1972 wrapRefLines = -1;
1973 DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines);
1974 }
1975
1976 DBG_OBJ_LEAVE ();
1977}
1978
1983{
1984 DBG_OBJ_ENTER0 ("resize", 0, "fillParagraphs");
1985
1986 DBG_OBJ_MSGF ("resize", 1, "wrapRefParagraphs = %d", wrapRefParagraphs);
1987
1988 if (wrapRefParagraphs != -1) {
1989 // Notice that wrapRefParagraphs refers to the lines, not to the
1990 // paragraphs.
1991 int firstWordOfLine;
1992 if (lines->size () > 0 && wrapRefParagraphs > 0) {
1993 // Sometimes, wrapRefParagraphs is larger than lines->size(), due to
1994 // floats? (Has to be clarified.)
1995 int lineNo = min (wrapRefParagraphs, lines->size ()) - 1;
1996 firstWordOfLine = lines->getRef(lineNo)->lastWord + 1;
1997 } else
1998 firstWordOfLine = 0;
1999
2000 int parNo;
2001 if (paragraphs->size() > 0 &&
2002 firstWordOfLine > paragraphs->getLastRef()->firstWord)
2003 // A special case: the paragraphs list has been partly built, but
2004 // not yet the paragraph containing the word in question. In
2005 // this case, only the rest of the paragraphs list must be
2006 // constructed. (Without this check, findParagraphOfWord would
2007 // return -1 in this case, so that all paragraphs would be
2008 // rebuilt.)
2009 parNo = paragraphs->size ();
2010 else
2011 // If there are no paragraphs yet, findParagraphOfWord will return
2012 // -1: use 0 then instead.
2013 parNo = max (0, findParagraphOfWord (firstWordOfLine));
2014
2015 paragraphs->setSize (parNo);
2016 DBG_OBJ_SET_NUM ("paragraphs.size", paragraphs->size ());
2017
2018 int firstWord;
2019 if (paragraphs->size () > 0)
2020 firstWord = paragraphs->getLastRef()->lastWord + 1;
2021 else
2022 firstWord = 0;
2023
2024 DBG_OBJ_MSGF ("resize", 1, "firstWord = %d, words->size() = %d [before]",
2025 firstWord, words->size ());
2026
2027 for (int i = firstWord; i < words->size (); i++)
2029
2030 DBG_OBJ_MSGF ("resize", 1, "words->size() = %d [after]", words->size ());
2031
2032 wrapRefParagraphs = -1;
2033 DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs);
2034 }
2035
2036 DBG_OBJ_LEAVE ();
2037}
2038
2040{
2041 DBG_OBJ_ENTER0 ("construct.line", 0, "initNewLine");
2042
2043 calcBorders (lines->size() > 0 ?
2044 lines->getLastRef()->lastOofRefPositionedBeforeThisLine : -1,
2045 1);
2046
2048
2049 DBG_OBJ_SET_NUM ("newLineAscent", newLineAscent);
2050 DBG_OBJ_SET_NUM ("newLineDescent", newLineDescent);
2051
2052 DBG_OBJ_LEAVE ();
2053}
2054
2055void Textblock::calcBorders (int lastOofRef, int height)
2056{
2057 DBG_OBJ_ENTER ("construct.line", 0, "calcBorders", "%d, %d",
2058 lastOofRef, height);
2059
2063
2064 bool oofmDefined = false;
2065 for (int i = 0; i < NUM_OOFM && !oofmDefined; i++)
2067 oofmDefined = true;
2068
2069 if (oofmDefined) {
2070 int firstWordOfLine =
2071 lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0;
2072 int effOofRef = max (lastOofRef, firstWordOfLine - 1);
2073 int yRel = yOffsetOfLineToBeCreated (), yRef;
2074
2075 for (int i = 0; i < NUM_OOFM; i++) {
2076 oof::OutOfFlowMgr *oofm;
2077 if ((oofm = searchOutOfFlowMgr (i)) &&
2078 findSizeRequestReference (i, NULL, &yRef)) {
2079 // Consider the example:
2080 //
2081 // <div>
2082 // Some text A ...
2083 // <p> Some text B ... <img style="float:right" ...> </p>
2084 // Some more text C ...
2085 // </div>
2086 //
2087 // If the image is large enough, it should float around the last
2088 // paragraph, "Some more text C ...":
2089 //
2090 // Some more text A ...
2091 //
2092 // Some more ,---------.
2093 // text B ... | |
2094 // | <img> |
2095 // Some more | | <---- Consider this line!
2096 // text C ... '---------'
2097 //
2098 // Since this float is generated in the <p> element, not in the-
2099 // <div> element, and since they are represented by different
2100 // instances of dw::Textblock, lastOofRefPositionedBeforeThisLine,
2101 // and so lastOofRef, is -1 for the line marked with an arrow;
2102 // this would result in ignoring the float, because -1 is
2103 // equivalent to the very beginning of the <div> element ("Some
2104 // more text A ..."), which is not affected by the float.
2105 //
2106 // On the other hand, the only relevant values of
2107 // Line::lastOofRefPositionedBeforeThisLine are those greater
2108 // than the first word of the new line, so a solution is to use
2109 // the maximum of both.
2110
2111 int y = yRef + yRel;
2112 bool thisHasLeft, thisHasRight;
2113
2114 thisHasLeft = oofm->hasFloatLeft (y, height, this, effOofRef);
2116 thisHasRight = oofm->hasFloatRight (y, height, this, effOofRef);
2118
2119 // TODO "max" is not really correct for the heights. (Does
2120 // not matter, since only one, the float manager, returns
2121 // meaningful values.)
2122 if (thisHasLeft) {
2125 oofm->getLeftBorder (y, height, this, effOofRef)
2126 - getGeneratorX (i));
2129 oofm->getLeftFloatHeight (y, height, this, effOofRef));
2130 }
2131
2132 if (thisHasRight) {
2135 oofm->getRightBorder (y, height, this, effOofRef)
2136 - getGeneratorRest (i));
2139 oofm->getRightFloatHeight (y, height, this, effOofRef));
2140 }
2141
2142 DBG_OBJ_MSGF ("construct.line", 1,
2143 "OOFM #%d: %d * %d (%s) / %d * %d (%s), at %d (%d), "
2144 "until %d = max (%d, %d - 1)",
2146 newLineHasFloatLeft ? "true" : "false",
2148 newLineHasFloatRight ? "true" : "false",
2149 y, height, effOofRef, lastOofRef, firstWordOfLine);
2150 }
2151 }
2152 }
2153
2154 DBG_OBJ_SET_BOOL ("newLineHasFloatLeft", newLineHasFloatLeft);
2155 DBG_OBJ_SET_BOOL ("newLineHasFloatRight", newLineHasFloatRight);
2156 DBG_OBJ_SET_NUM ("newLineLeftBorder", newLineLeftBorder);
2157 DBG_OBJ_SET_NUM ("newLineRightBorder", newLineRightBorder);
2158 DBG_OBJ_SET_NUM ("newLineLeftFloatHeight", newLineLeftFloatHeight);
2159 DBG_OBJ_SET_NUM ("newLineRightFloatHeight", newLineRightFloatHeight);
2160
2161 DBG_OBJ_LEAVE ();
2162}
2163
2165{
2166 DBG_OBJ_ENTER0 ("construct.line", 0, "showMissingLines");
2167
2168 // "Temporary word": when the last word is an OOF reference, it is
2169 // not processed, and not part of any line. For this reason, we
2170 // introduce a "temporary word", which is in flow, after this last
2171 // OOF reference, and later removed again.
2172 bool tempWord = words->size () > 0 &&
2173 words->getLastRef()->content.type == core::Content::WIDGET_OOF_REF;
2174 int firstWordToWrap =
2175 lines->size () > 0 ? lines->getLastRef()->lastWord + 1 : 0;
2176
2177 DBG_OBJ_MSGF ("construct.line", 1,
2178 "words->size() = %d, firstWordToWrap = %d, tempWord = %s",
2179 words->size (), firstWordToWrap, tempWord ? "true" : "false");
2180
2181 if (tempWord) {
2182 core::Requisition size = { 0, 0, 0 };
2183 addText0 ("", 0, Word::WORD_START | Word::WORD_END, getStyle (), &size);
2184 }
2185
2186 for (int i = firstWordToWrap; i < words->size (); i++)
2187 wordWrap (i, true);
2188
2189 // Remove temporary word again. The only reference should be the line.
2190 if (tempWord) {
2191 cleanupWord (words->size () - 1);
2192 words->setSize (words->size () - 1);
2193 if (lines->getLastRef()->lastWord > words->size () - 1)
2194 lines->getLastRef()->lastWord = words->size () - 1;
2195 }
2196
2197 // The following old code should not be necessary anymore, after
2198 // the introduction of the "virtual word". Instead, test the
2199 // condition.
2200 assert (lines->size () == 0 ||
2201 lines->getLastRef()->lastWord == words->size () - 1);
2202 /*
2203 // In some cases, there are some words of type WIDGET_OOF_REF left, which
2204 // are not added to line, since addLine() is only called within
2205 // wrapWordInFlow(), but not within wrapWordOofRef(). The missing line
2206 // is created here, so it is ensured that the last line ends with the last
2207 // word.
2208
2209 int firstWordNotInLine =
2210 lines->size () > 0 ? lines->getLastRef()->lastWord + 1: 0;
2211 DBG_OBJ_MSGF ("construct.line", 1, "firstWordNotInLine = %d (of %d)",
2212 firstWordNotInLine, words->size ());
2213 if (firstWordNotInLine < words->size ())
2214 addLine (firstWordNotInLine, words->size () - 1, -1, true);
2215 */
2216
2217 DBG_OBJ_LEAVE ();
2218}
2219
2220
2222{
2223 DBG_OBJ_ENTER0 ("construct.line", 0, "removeTemporaryLines");
2224
2225 if (nonTemporaryLines < lines->size ()) {
2226 lines->setSize (nonTemporaryLines);
2227 DBG_OBJ_SET_NUM ("lines.size", lines->size ());
2228
2229 // For words which will be added, the values calculated before in
2230 // accumulateWordData() are wrong, so it is called again. (Actually, the
2231 // words from the first temporary line are correct, but for simplicity,
2232 // we re-calculate all.)
2233 int firstWord =
2234 lines->size () > 0 ? lines->getLastRef()->lastWord + 1 : 0;
2235 for (int i = firstWord; i < words->size (); i++)
2237 }
2238
2239 DBG_OBJ_LEAVE ();
2240}
2241
2243{
2245 return word->origSpace / 3;
2246 else
2247 return 0;
2248}
2249
2251{
2253 return word->origSpace / 2;
2254 else
2255 return 0;
2256
2257 // Alternative: return word->origSpace / 2;
2258}
2259
2261{
2262 return 0;
2263}
2264
2266{
2267 DBG_OBJ_ENTER ("construct.word.accum", 0, "getLineStretchability", "%d",
2268 lastWordIndex);
2269 DBG_MSG_WORD ("construct.word.accum", 1, "<i>last word:</i> ",
2270 lastWordIndex, "");
2271
2272 Word *lastWord = words->getRef (lastWordIndex);
2273 int str;
2274
2276 str = 0;
2277 DBG_OBJ_MSG ("construct.word.accum", 1, "justified => 0");
2278 } else {
2279 str = stretchabilityFactor * (lastWord->maxAscent
2280 + lastWord->maxDescent) / 100;
2281 DBG_OBJ_MSGF ("construct.word.accum", 1,
2282 "not justified => %d * (%d + %d) / 100 = %d",
2284 lastWord->maxDescent, str);
2285 }
2286
2287 DBG_OBJ_LEAVE ();
2288 return str;
2289
2290 // Alternative: return 0;
2291}
2292
2293} // namespace dw
static Hyphenator * getHyphenator(const char *language)
int * hyphenateWord(core::Platform *platform, const char *word, int *numBreaks)
Given a word, returns a list of the possible hyphenation points.
static bool isHyphenationCandidate(const char *word)
Simple test to avoid much costs.
int penaltyValue(int index, int infLevel)
enum dw::Textblock::BadnessAndPenalty::@31 badnessState
int compareTo(int penaltyIndex, BadnessAndPenalty *other)
void intoStringBuffer(lout::misc::StringBuffer *sb)
void calcBadness(int totalWidth, int idealWidth, int totalStretchability, int totalShrinkability)
void setPenalties(int penalty1, int penalty2)
Sets the penalty, multiplied by 100.
void setSinglePenalty(int index, int penalty)
void setData(int xWordWidget, int lineNo)
Definition textblock.cc:63
int findParagraphOfWord(int wordIndex)
Find the paragraph of word wordIndex.
void calcTextSizes(const char *text, size_t textLen, core::style::Style *style, int numBreaks, int *breakPos, core::Requisition *wordSize)
void initLine1Offset(int wordIndex)
int searchBreakPos(int wordIndex, int firstIndex, int *searchUntil, bool tempNewLine, int penaltyIndex, bool thereWillBeMoreSpace, bool wrapAll, int *diffWords, int *wordIndexEnd, int *addIndex1=NULL)
int newLineLeftFloatHeight
Definition textblock.hh:600
void accumulateWordData(int wordIndex)
void getWordExtremes(Word *word, core::Extremes *extremes)
Get the extremes of a word within a textblock.
Definition textblock.cc:457
lout::misc::NotSoSimpleVector< Word > * words
Definition textblock.hh:611
int wrapWordOofRef(int wordIndex, bool wrapAll)
void handleWordExtremes(int wordIndex)
Counter part to wordWrap(), but for extremes, not size calculation.
int yOffsetOfLineCreated(Line *line)
Includes margin, border, and padding.
void correctLastWordExtremes()
Called when something changed for the last word (space, hyphens etc.).
virtual int wordWrap(int wordIndex, bool wrapAll)
int getLineStretchability(int lastWordIndex)
bool isHyphenationCandidate(Word *word)
void processWord(int wordIndex)
Line * addLine(int firstWord, int lastWord, int newLastOofPos, bool temporary, int minHeight)
int considerHyphenation(int firstIndex, int breakPos)
Suggest a word to hyphenate, when breaking at breakPos is planned.
bool calcSizeOfWidgetInFlow(int wordIndex, Widget *widget, core::Requisition *size)
Calculate the size of a widget, and return whether it has to be positioned at the top of the line.
int getGeneratorX(int oofmIndex)
Return position relative to container, not regarding margin/border/padding, Called by OOFFloatsMgr to...
bool newLineHasFloatRight
Definition textblock.hh:595
void fillParagraphs()
Counter part to rewrap(), but for extremes, not size calculation.
void calcBorders(int lastOofRef, int height)
void rewrap()
Rewrap the page from the line from which this is necessary.
static const char * hyphenDrawChar
The character which is used to draw a hyphen at the end of a line, either caused by automatic hyphena...
Definition textblock.hh:296
void cleanupWord(int wordNo)
void calcTextOffset(int lineIndex, int totalWidth)
void balanceBreakPosAndHeight(int wordIndex, int firstIndex, int *searchUntil, bool tempNewLine, int penaltyIndex, bool borderIsCalculated, bool *thereWillBeMoreSpace, bool wrapAll, int *diffWords, int *wordIndexEnd, int *lastFloatPos, bool regardBorder, int *height, int *breakPos)
int newLineRightBorder
Definition textblock.hh:596
void accumulateWordForLine(int lineIndex, int wordIndex)
int newLineLeftBorder
Definition textblock.hh:596
bool isBreakAllowedInWord(Word *word)
Definition textblock.hh:677
int searchMinBap(int firstWord, int lastWordm, int penaltyIndex, bool thereWillBeMoreSpace, bool correctAtEnd)
int nonTemporaryLines
Definition textblock.hh:610
int getLineShrinkability(int lastWordIndex)
bool limitTextWidth
Definition textblock.hh:572
lout::misc::SimpleVector< Anchor > * anchors
Definition textblock.hh:612
static int getSpaceStretchability(struct Word *word)
int yOffsetOfLineToBeCreated(int *lastMargin=NULL)
Includes margin, border, and padding.
static int stretchabilityFactor
...
Definition textblock.hh:570
bool mustBorderBeRegarded(Line *line)
Of nested text blocks, only the most inner one must regard the borders of floats.
Definition textblock.hh:696
bool findSizeRequestReference(Widget *reference, int *xRef=NULL, int *yRef=NULL)
void moveWordIndices(int wordIndex, int num, int *addIndex1=NULL)
int getGeneratorRest(int oofmIndex)
static int penalties[PENALTY_NUM][2]
The penalties for hyphens and other, multiplied by 100.
Definition textblock.hh:565
int hyphenateWord(int wordIndex, int *addIndex1=NULL)
int calcLineBreakWidth(int lineIndex)
bool newLineHasFloatLeft
Definition textblock.hh:595
void addText0(const char *text, size_t len, short flags, core::style::Style *style, core::Requisition *size)
Add a word (without hyphens) to the page structure.
int wrapWordInFlow(int wordIndex, bool wrapAll)
bool mustQueueResize
Definition textblock.hh:557
int newLineRightFloatHeight
Definition textblock.hh:600
void fillSpace(int wordNo, core::style::Style *style)
int calcLinePartHeight(int firstWord, int lastWord)
void alignLine(int lineIndex)
Align the line.
void fillWord(int wordNo, int width, int ascent, int descent, short flags, core::style::Style *style)
int calcPenaltyIndexForNewLine()
Definition textblock.hh:747
void initWord(int wordNo)
Basic initialization, which is neccessary before fillWord.
int wrapRefParagraphs
Definition textblock.hh:583
lout::misc::SimpleVector< Paragraph > * paragraphs
Definition textblock.hh:609
void justifyLine(Line *line, int diff)
static int getSpaceShrinkability(struct Word *word)
bool ignoreLine1OffsetSometimes
Definition textblock.hh:555
lout::misc::SimpleVector< Line > * lines
Definition textblock.hh:608
lout::misc::ZoneAllocator * textZone
Definition layout.hh:276
int textWidth(style::Font *font, const char *text, int len)
Definition layout.hh:349
int getWidthViewport()
Definition layout.hh:285
Platform * getPlatform()
Definition layout.hh:342
bool getUsesViewport()
Definition layout.hh:284
Layout * layout
Definition widget.hh:189
int boxDiffWidth()
Definition widget.hh:461
int parentRef
This value is defined by the parent widget, and used for incremential resizing.
Definition widget.hh:175
style::Style * getStyle()
Definition widget.hh:448
int boxRestWidth()
Definition widget.hh:459
virtual bool isBlockLevel()
Definition widget.cc:1958
TextAlignType textAlign
Definition style.hh:542
OutOfFlowMgr * searchOutOfFlowMgr(int oofmIndex)
static int getOOFMIndex(Widget *widget)
int makeParentRefInFlow(int inFlowSubRef)
int getWidgetOOFIndex(Widget *widget)
Represents additional data for OOF containers.
virtual bool mayAffectBordersAtAll()=0
virtual bool hasFloatRight(int y, int h, OOFAwareWidget *lastGen, int lastExtIndex)=0
Return whether there is a float on the right side.
virtual bool hasFloatLeft(int y, int h, OOFAwareWidget *lastGen, int lastExtIndex)=0
Return whether there is a float on the left side.
virtual int getRightBorder(int y, int h, OOFAwareWidget *lastGen, int lastExtIndex)=0
Get the right border for the vertical position of y, for a height of h, based on floats; relative to ...
virtual int getLeftFloatHeight(int y, int h, OOFAwareWidget *lastGen, int lastExtIndex)=0
Assuming there is a float on the left side, return the rest height of it.
virtual void tellPosition1(core::Widget *widget, int x, int y)=0
Called before tellPosition2, see there for more.
virtual void tellIncompletePosition1(core::Widget *generator, core::Widget *widget, int x, int y)=0
virtual void tellIncompletePosition2(core::Widget *generator, core::Widget *widget, int x, int y)=0
virtual void calcWidgetRefSize(core::Widget *widget, core::Requisition *size)=0
virtual int getRightFloatHeight(int y, int h, OOFAwareWidget *lastGen, int lastExtIndex)=0
Assuming there is a float on the right side, return the rest height of it.
virtual void moveExternalIndices(OOFAwareWidget *generator, int oldStartIndex, int diff)=0
virtual void tellPosition2(core::Widget *widget, int x, int y)=0
Called after tellPosition1.
virtual int getLeftBorder(int y, int h, OOFAwareWidget *lastGen, int lastExtIndex)=0
Get the left border for the vertical position of y, for a height of h", based on floats; relative to ...
A class for fast concatenation of a large number of strings.
Definition misc.hh:566
void appendInt(int n)
Definition misc.hh:590
void append(const char *str)
Append a NUL-terminated string to the buffer, with copying.
Definition misc.hh:589
const char * getChars()
Return a NUL-terminated strings containing all appended strings.
Definition misc.cc:92
const char * strndup(const char *str, size_t t)
Definition misc.hh:678
#define DBG_IF_RTFL
Definition debug.hh:73
#define DBG_OBJ_ENTER0(aspect, prio, funname)
#define DBG_OBJ_SET_BOOL(var, val)
#define DBG_OBJ_MSG_END()
#define DBG_OBJ_MSGF(aspect, prio, fmt,...)
#define DBG_OBJ_SET_NUM(var, val)
#define DBG_OBJ_MSG(aspect, prio, msg)
#define DBG_OBJ_ENTER(aspect, prio, funname, fmt,...)
#define DBG_OBJ_LEAVE()
#define DBG_OBJ_MSG_START()
#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val)
#define DBG_OBJ_SET_NUM_O(obj, var, val)
#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val)
#define DBG_OBJ_LEAVE_VAL(fmt,...)
int multiplyWithPerLengthRounded(int x, Length l)
Like multiplyWithPerLength, but rounds to nearest integer instead of down.
Definition style.hh:483
bool isPerLength(Length l)
Returns true if l is a percentage.
Definition style.hh:445
int absLengthVal(Length l)
Returns the value of a length in pixels, as an integer.
Definition style.hh:451
Dw is in this namespace, or sub namespaces of this one.
Miscellaneous stuff, which does not fit anywhere else.
Definition misc.cc:31
T min(T a, T b)
Definition misc.hh:19
T max(T a, T b)
Definition misc.hh:20
void assertNotReached()
Definition misc.hh:35
const char * boolToStr(bool b)
Definition misc.hh:87
enum dw::Textblock::Line::@33 alignment
int lastOofRefPositionedBeforeThisLine
Definition textblock.hh:421
int totalHeight(int marginNextLine)
Returns the difference between two vertical lines positions: height of this line plus space below thi...
Definition textblock.hh:401
SpaceImgRenderer * spaceImgRenderer
Definition textblock.hh:499
core::style::Style * style
Definition textblock.hh:492
WordImgRenderer * wordImgRenderer
Definition textblock.hh:498
core::Requisition size
Definition textblock.hh:463
BadnessAndPenalty badnessAndPenalty
Definition textblock.hh:489
core::style::Style * spaceStyle
Definition textblock.hh:493
core::Content content
Definition textblock.hh:475
@ DIV_CHAR_AT_EOL
Must be drawn with a hyphen, when at the end of the line.
Definition textblock.hh:434
@ CAN_BE_HYPHENATED
Can be hyphenated automatically.
Definition textblock.hh:432
@ DRAW_AS_ONE_TEXT
This word must be drawn, together with the following word(s), by only one call of View::drawText(),...
Definition textblock.hh:442
@ WIDGET_OOF_REF
reference to a widget out of flow (OOF); this widget (containing this content) is only the generator ...
Definition types.hh:217
@ WIDGET_IN_FLOW
widget in normal flow, so that this widget (containing this content) is both container (parent) and g...
Definition types.hh:207
const char * text
Definition types.hh:236
Widget * widget
Definition types.hh:237
WidgetReference * widgetReference
Definition types.hh:238
#define PRINTF(fmt,...)
Definition textblock.hh:11
#define DBG_MSG_WORD(aspect, prio, prefix, n, suffix)
Definition textblock.hh:981
#define DBG_SET_WORD_SIZE(n)
Definition textblock.hh:971
#define DBG_SET_WORD(n)
Definition textblock.hh:934