Dillo v3.2.0-88-g47ab7c70
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 happened. 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 = new core::Requisition[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 widgets 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 delete[] wordSize;
1510 } else {
1511 words->getRef(wordIndex)->flags &= ~Word::CAN_BE_HYPHENATED;
1512 }
1513
1514 return numBreaks;
1515}
1516
1517void Textblock::moveWordIndices (int wordIndex, int num, int *addIndex1)
1518{
1519 DBG_OBJ_ENTER ("construct.word", 0, "moveWordIndices", "%d, %d",
1520 wordIndex, num);
1521
1522 for (int i = 0; i < NUM_OOFM; i++)
1523 if (searchOutOfFlowMgr(i))
1524 searchOutOfFlowMgr(i)->moveExternalIndices (this, wordIndex, num);
1525
1526 for (int i = lines->size () - 1; i >= 0; i--) {
1527 Line *line = lines->getRef (i);
1528 if (line->lastOofRefPositionedBeforeThisLine < wordIndex) {
1529 // Since lastOofRefPositionedBeforeThisLine are ascending,
1530 // the search can be stopped here.
1531 DBG_OBJ_MSGF ("construct.word", 1,
1532 "lines[%d]->lastOofRef = %d < %d => stop",
1533 i, line->lastOofRefPositionedBeforeThisLine, wordIndex);
1534 break;
1535 } else {
1536 DBG_OBJ_MSGF ("construct.word", 1,
1537 "adding %d to lines[%d]->lastOofRef...: %d -> %d",
1541 }
1542 }
1543
1544 // Unlike the last line, the last paragraph is already constructed. (To
1545 // make sure we cover all cases, we iterate over the last paragraphs.)
1546 Paragraph *par;
1547 for (int parNo = paragraphs->size () - 1;
1548 parNo >= 0 &&
1549 (par = paragraphs->getRef(parNo)) && par->lastWord > wordIndex;
1550 parNo--) {
1551 par->lastWord += num;
1552 if (par->firstWord > wordIndex)
1553 par->firstWord += num;
1554 }
1555
1556 // Additional indices. When needed, the number can be extended.
1557 if (addIndex1 && *addIndex1 >= wordIndex)
1558 *addIndex1 += num;
1559
1560 DBG_OBJ_LEAVE ();
1561}
1562
1563void Textblock::accumulateWordForLine (int lineIndex, int wordIndex)
1564{
1565 DBG_OBJ_ENTER ("construct.line", 1, "accumulateWordForLine", "%d, %d",
1566 lineIndex, wordIndex);
1567 DBG_MSG_WORD ("construct.line", 2, "<i>word:</i> ", wordIndex, "");
1568
1569 Line *line = lines->getRef (lineIndex);
1570 Word *word = words->getRef (wordIndex);
1571
1572 int len = word->style->font->ascent;
1574 len += len / 2;
1575 line->contentAscent = max (line->contentAscent, len);
1576
1577 len = word->style->font->descent;
1578 if (word->style->valign == core::style::VALIGN_SUB)
1579 len += word->style->font->ascent / 3;
1580 line->contentDescent = max (line->contentDescent, len);
1581
1582 int borderAscent, borderDescent, marginAscent, marginDescent;
1583
1584 DBG_OBJ_MSGF ("construct.line", 2, "size.ascent = %d, size.descent = %d",
1585 word->size.ascent, word->size.descent);
1586
1588 // TODO Consider extraSpace?
1589 marginAscent = word->size.ascent;
1590 marginDescent = word->size.descent;
1591 borderAscent =
1592 marginAscent - word->content.widget->getStyle()->margin.top;
1593 borderDescent =
1594 marginDescent - word->content.widget->getStyle()->margin.bottom;
1595
1596 word->content.widget->parentRef = makeParentRefInFlow (lineIndex);
1597 DBG_OBJ_SET_NUM_O (word->content.widget, "parentRef",
1598 word->content.widget->parentRef);
1599 } else {
1600 borderAscent = marginAscent = word->size.ascent;
1601 borderDescent = marginDescent = word->size.descent;
1602
1603 if (word->content.type == core::Content::BREAK)
1604 line->breakSpace = max (word->content.breakSpace, line->breakSpace);
1605 else if (word->content.type == core::Content::WIDGET_OOF_REF) {
1607 makeParentRefInFlow (lineIndex);
1608 DBG_SET_WORD (wordIndex);
1609 }
1610 }
1611
1612 DBG_OBJ_MSGF ("construct.line", 2,
1613 "borderAscent = %d, borderDescent = %d, marginAscent = %d, "
1614 "marginDescent = %d",
1615 borderAscent, borderDescent, marginAscent, marginDescent);
1616
1617 line->borderAscent = max (line->borderAscent, borderAscent);
1618 line->borderDescent = max (line->borderDescent, borderDescent);
1619 line->marginAscent = max (line->marginAscent, marginAscent);
1620 line->marginDescent = max (line->marginDescent, marginDescent);
1621
1622 DBG_OBJ_LEAVE ();
1623}
1624
1626{
1627 DBG_OBJ_ENTER ("construct.word.accum", 1, "accumulateWordData", "%d",
1628 wordIndex);
1629 DBG_MSG_WORD ("construct.word.accum", 1, "<i>word:</i> ", wordIndex, "");
1630
1631 // Typically, the word in question is in the last line; in any case
1632 // quite at the end of the text, so that linear search is actually
1633 // the fastest option.
1634 int lineIndex = lines->size ();
1635 while (lineIndex > 0 && wordIndex <= lines->getRef(lineIndex - 1)->lastWord)
1636 lineIndex--;
1637
1638 int firstWordOfLine;
1639 if (lineIndex == 0)
1640 firstWordOfLine = 0;
1641 else
1642 firstWordOfLine = lines->getRef(lineIndex - 1)->lastWord + 1;
1643
1644 Word *word = words->getRef (wordIndex);
1645 DBG_OBJ_MSGF ("construct.word.accum", 2, "lineIndex = %d", lineIndex);
1646
1647 int lineBreakWidth = calcLineBreakWidth (lineIndex);
1648
1649 DBG_OBJ_MSGF ("construct.word.accum", 2,
1650 "(%s existing line %d starts with word %d; "
1651 "lineBreakWidth = %d)",
1652 lineIndex < lines->size () ? "already" : "not yet",
1653 lineIndex, firstWordOfLine, lineBreakWidth);
1654
1655 if (wordIndex == firstWordOfLine) {
1656 // first word of the (not necessarily yet existing) line
1657 word->totalWidth = word->size.width + word->hyphenWidth;
1658 word->maxAscent = word->size.ascent;
1659 word->maxDescent = word->size.descent;
1660 word->totalSpaceStretchability = 0;
1661 word->totalSpaceShrinkability = 0;
1662
1663 DBG_OBJ_MSGF ("construct.word.accum", 1,
1664 "first word of line: words[%d].totalWidth = %d + %d = %d; "
1665 "maxAscent = %d, maxDescent = %d",
1666 wordIndex, word->size.width, word->hyphenWidth,
1667 word->totalWidth, word->maxAscent, word->maxDescent);
1668 } else {
1669 Word *prevWord = words->getRef (wordIndex - 1);
1670
1671 word->totalWidth = prevWord->totalWidth
1672 + prevWord->origSpace - prevWord->hyphenWidth
1673 + word->size.width + word->hyphenWidth;
1674 word->maxAscent = max (prevWord->maxAscent, word->size.ascent);
1675 word->maxDescent = max (prevWord->maxDescent, word->size.descent);
1679 prevWord->totalSpaceShrinkability + getSpaceShrinkability(prevWord);
1680
1681 DBG_OBJ_MSGF ("construct.word.accum", 1,
1682 "not first word of line: words[%d].totalWidth = %d + %d - "
1683 "%d + %d + %d = %d; maxAscent = max (%d, %d) = %d, "
1684 "maxDescent = max (%d, %d) = %d",
1685 wordIndex, prevWord->totalWidth, prevWord->origSpace,
1686 prevWord->hyphenWidth, word->size.width,
1687 word->hyphenWidth, word->totalWidth,
1688 prevWord->maxAscent, word->size.ascent, word->maxAscent,
1689 prevWord->maxDescent, word->size.descent, word->maxDescent);
1690 }
1691
1692 int totalStretchability =
1694 int totalShrinkability =
1696
1697 DBG_OBJ_MSGF ("construct.word.accum", 1,
1698 "totalStretchability = %d + ... = %d",
1699 word->totalSpaceStretchability, totalStretchability);
1700 DBG_OBJ_MSGF ("construct.word.accum", 1,
1701 "totalShrinkability = %d + ... = %d",
1702 word->totalSpaceShrinkability, totalShrinkability);
1703
1705 totalStretchability,
1706 totalShrinkability);
1707
1708 DBG_IF_RTFL {
1709 StringBuffer sb;
1711 DBG_OBJ_ARRATTRSET_SYM ("words", wordIndex, "badnessAndPenalty",
1712 sb.getChars ());
1713 }
1714
1715 DBG_OBJ_LEAVE ();
1716}
1717
1719{
1720 DBG_OBJ_ENTER ("construct.word.width", 1, "calcLineBreakWidth",
1721 "%d <i>of %d</i>", lineIndex, lines->size());
1722
1723 int lineBreakWidth = this->lineBreakWidth - leftInnerPadding;
1724 if (limitTextWidth &&
1725 layout->getUsesViewport () &&
1726 // margin/border/padding will be subtracted later, via OOFM.
1729 if (lineIndex == 0)
1731
1732 int leftBorder, rightBorder;
1733 if (mustBorderBeRegarded (lineIndex)) {
1734 leftBorder = newLineLeftBorder;
1735 rightBorder = newLineRightBorder;
1736 } else
1737 leftBorder = rightBorder = 0;
1738
1739 leftBorder = max (leftBorder, boxOffsetX());
1740 rightBorder = max (rightBorder, boxRestWidth());
1741
1742 lineBreakWidth -= (leftBorder + rightBorder);
1743
1744 DBG_OBJ_LEAVE_VAL ("%d - %d - (%d + %d) = %d",
1745 this->lineBreakWidth, leftInnerPadding, leftBorder,
1746 rightBorder, lineBreakWidth);
1747 return lineBreakWidth;
1748}
1749
1750void Textblock::initLine1Offset (int wordIndex)
1751{
1752 Word *word = words->getRef (wordIndex);
1753
1754 /* Test whether line1Offset can be used. */
1755 if (wordIndex == 0) {
1758 line1OffsetEff = 0;
1759 } else {
1760 int indent = 0;
1761
1763 word->content.widget->isBlockLevel()) {
1764 /* don't use text-indent when nesting blocks */
1765 } else {
1766 if (core::style::isPerLength(getStyle()->textIndent)) {
1768 (lineBreakWidth, getStyle()->textIndent);
1769 } else {
1770 indent = core::style::absLengthVal (getStyle()->textIndent);
1771 }
1772 }
1773 line1OffsetEff = line1Offset + indent;
1774 }
1775 }
1776}
1777
1783void Textblock::alignLine (int lineIndex)
1784{
1785 DBG_OBJ_ENTER ("construct.line", 0, "alignLine", "%d", lineIndex);
1786
1787 Line *line = lines->getRef (lineIndex);
1788
1789 for (int i = line->firstWord; i <= line->lastWord; i++)
1790 words->getRef(i)->effSpace = words->getRef(i)->origSpace;
1791
1792 // We are not interested in the alignment of floats etc.
1793 int firstWordNotOofRef = line->firstWord;
1794 while (firstWordNotOofRef <= line->lastWord &&
1795 words->getRef(firstWordNotOofRef)->content.type
1797 firstWordNotOofRef++;
1798
1799 if (firstWordNotOofRef <= line->lastWord) {
1800 Word *firstWord = words->getRef (firstWordNotOofRef);
1801
1802 if (firstWord->content.type != core::Content::BREAK) {
1803 Word *lastWord = words->getRef (line->lastWord);
1804 int lineBreakWidth =
1805 this->lineBreakWidth - (line->leftOffset + line->rightOffset);
1806
1808
1814 if (firstWord->style->display == core::style::DISPLAY_INLINE ||
1820 alignStyle = getStyle()->textAlign;
1821 }
1822
1823 switch (alignStyle) {
1825 DBG_OBJ_MSG ("construct.line", 1,
1826 "first word has 'text-align: left'");
1827 line->alignment = Line::LEFT;
1828 break;
1829 case core::style::TEXT_ALIGN_STRING: /* handled elsewhere (in the
1830 * future)? */
1831 DBG_OBJ_MSG ("construct.line", 1,
1832 "first word has 'text-align: string'");
1833 line->alignment = Line::LEFT;
1834 break;
1835 case core::style::TEXT_ALIGN_JUSTIFY: /* see some lines above */
1836 DBG_OBJ_MSG ("construct.line", 1,
1837 "first word has 'text-align: justify'");
1838 line->alignment = Line::LEFT;
1839 // Do not justify the last line of a paragraph (which ends on a
1840 // BREAK or with the last word of the page).
1841 if(!(lastWord->content.type == core::Content::BREAK ||
1842 line->lastWord == words->size () - 1) ||
1843 // In some cases, however, an unjustified line would be too wide:
1844 // when the line would be shrunken otherwise. (This solution is
1845 // far from perfect, but a better solution would make changes in
1846 // the line breaking algorithm necessary.)
1847 lineBreakWidth < lastWord->totalWidth)
1848 justifyLine (line, lineBreakWidth - lastWord->totalWidth);
1849 break;
1851 DBG_OBJ_MSG ("construct.line", 1,
1852 "first word has 'text-align: right'");
1853 line->alignment = Line::RIGHT;
1854 break;
1856 DBG_OBJ_MSG ("construct.line", 1,
1857 "first word has 'text-align: center'");
1858 line->alignment = Line::CENTER;
1859 break;
1860 default:
1861 // compiler happiness
1862 line->alignment = Line::LEFT;
1863 }
1864
1865 } else
1866 // empty line (only line break);
1867 line->alignment = Line::LEFT;
1868 } else
1869 // empty line (or only OOF references).
1870 line->alignment = Line::LEFT;
1871
1872 DBG_OBJ_LEAVE ();
1873}
1874
1875void Textblock::calcTextOffset (int lineIndex, int totalWidth)
1876{
1877 DBG_OBJ_ENTER ("construct.line", 0, "calcTextOffset", "%d, %d",
1878 lineIndex, totalWidth);
1879
1880 Line *line = lines->getRef (lineIndex);
1881 int lineWidth = line->firstWord <= line->lastWord ?
1882 words->getRef(line->lastWord)->totalWidth : 0;
1883
1884 switch (line->alignment) {
1885 case Line::LEFT:
1886 line->textOffset = line->leftOffset;
1887 DBG_OBJ_MSGF ("construct.line", 1, "left: textOffset = %d",
1888 line->textOffset);
1889 break;
1890
1891 case Line::RIGHT:
1892 line->textOffset = totalWidth - line->rightOffset - lineWidth;
1893 DBG_OBJ_MSGF ("construct.line", 1,
1894 "right: textOffset = %d - %d - %d = %d",
1895 totalWidth, line->rightOffset, lineWidth, line->textOffset);
1896 break;
1897
1898 case Line::CENTER:
1899 line->textOffset =
1900 (line->leftOffset + totalWidth - line->rightOffset - lineWidth) / 2;
1901 DBG_OBJ_MSGF ("construct.line", 1,
1902 "center: textOffset = (%d + %d - %d - %d) /2 = %d",
1903 line->leftOffset, totalWidth, line->rightOffset, lineWidth,
1904 line->textOffset);
1905 break;
1906
1907 default:
1909 break;
1910 }
1911
1912 // For large lines (images etc), which do not fit into the viewport:
1913 if (line->textOffset < line->leftOffset)
1914 line->textOffset = line->leftOffset;
1915
1916 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "textOffset", line->textOffset);
1917 DBG_OBJ_LEAVE ();
1918}
1919
1927{
1928 DBG_OBJ_ENTER0 ("construct.line", 0, "rewrap");
1929
1930 if (wrapRefLines == -1)
1931 DBG_OBJ_MSG ("construct.line", 0, "does not have to be rewrapped");
1932 else {
1933 // All lines up from wrapRef will be rebuild from the word list,
1934 // the line list up from this position is rebuild.
1935 lines->setSize (wrapRefLines);
1936 DBG_OBJ_SET_NUM ("lines.size", lines->size ());
1938
1939 initNewLine ();
1940
1941 int firstWord;
1942 if (lines->size () > 0) {
1943 Line *lastLine = lines->getLastRef();
1944 firstWord = lastLine->lastWord + 1;
1945 } else
1946 firstWord = 0;
1947
1948 DBG_OBJ_MSGF ("construct.line", 0, "starting with word %d", firstWord);
1949
1950 lastWordDrawn = min (lastWordDrawn, firstWord - 1);
1951 DBG_OBJ_SET_NUM ("lastWordDrawn", lastWordDrawn);
1952
1953 for (int i = firstWord; i < words->size (); i++) {
1954 Word *word = words->getRef (i);
1955
1956 switch (word->content.type) {
1958 calcSizeOfWidgetInFlow (i, word->content.widget, &word->size);
1960 break;
1961
1963 {
1964 int oofmIndex =
1966 oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr (oofmIndex);
1968 &(word->size));
1970 }
1971 break;
1972
1973 default:
1974 break;
1975 }
1976
1977 wordWrap (i, false);
1978
1979 // Somewhat historical, but still important, note:
1980 //
1981 // For the case that something else is done with this word, it
1982 // is important that wordWrap() may insert some new words; since
1983 // NotSoSimpleVector is used for the words list, the internal
1984 // structure may have changed, so getRef() must be called again.
1985 //
1986 // So this is necessary: word = words->getRef (i);
1987 }
1988
1989 // Next time, the page will not have to be rewrapped.
1990 wrapRefLines = -1;
1991 DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines);
1992 }
1993
1994 DBG_OBJ_LEAVE ();
1995}
1996
2001{
2002 DBG_OBJ_ENTER0 ("resize", 0, "fillParagraphs");
2003
2004 DBG_OBJ_MSGF ("resize", 1, "wrapRefParagraphs = %d", wrapRefParagraphs);
2005
2006 if (wrapRefParagraphs != -1) {
2007 // Notice that wrapRefParagraphs refers to the lines, not to the
2008 // paragraphs.
2009 int firstWordOfLine;
2010 if (lines->size () > 0 && wrapRefParagraphs > 0) {
2011 // Sometimes, wrapRefParagraphs is larger than lines->size(), due to
2012 // floats? (Has to be clarified.)
2013 int lineNo = min (wrapRefParagraphs, lines->size ()) - 1;
2014 firstWordOfLine = lines->getRef(lineNo)->lastWord + 1;
2015 } else
2016 firstWordOfLine = 0;
2017
2018 int parNo;
2019 if (paragraphs->size() > 0 &&
2020 firstWordOfLine > paragraphs->getLastRef()->firstWord)
2021 // A special case: the paragraphs list has been partly built, but
2022 // not yet the paragraph containing the word in question. In
2023 // this case, only the rest of the paragraphs list must be
2024 // constructed. (Without this check, findParagraphOfWord would
2025 // return -1 in this case, so that all paragraphs would be
2026 // rebuilt.)
2027 parNo = paragraphs->size ();
2028 else
2029 // If there are no paragraphs yet, findParagraphOfWord will return
2030 // -1: use 0 then instead.
2031 parNo = max (0, findParagraphOfWord (firstWordOfLine));
2032
2033 paragraphs->setSize (parNo);
2034 DBG_OBJ_SET_NUM ("paragraphs.size", paragraphs->size ());
2035
2036 int firstWord;
2037 if (paragraphs->size () > 0)
2038 firstWord = paragraphs->getLastRef()->lastWord + 1;
2039 else
2040 firstWord = 0;
2041
2042 DBG_OBJ_MSGF ("resize", 1, "firstWord = %d, words->size() = %d [before]",
2043 firstWord, words->size ());
2044
2045 for (int i = firstWord; i < words->size (); i++)
2047
2048 DBG_OBJ_MSGF ("resize", 1, "words->size() = %d [after]", words->size ());
2049
2050 wrapRefParagraphs = -1;
2051 DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs);
2052 }
2053
2054 DBG_OBJ_LEAVE ();
2055}
2056
2058{
2059 DBG_OBJ_ENTER0 ("construct.line", 0, "initNewLine");
2060
2061 calcBorders (lines->size() > 0 ?
2062 lines->getLastRef()->lastOofRefPositionedBeforeThisLine : -1,
2063 1);
2064
2066
2067 DBG_OBJ_SET_NUM ("newLineAscent", newLineAscent);
2068 DBG_OBJ_SET_NUM ("newLineDescent", newLineDescent);
2069
2070 DBG_OBJ_LEAVE ();
2071}
2072
2073void Textblock::calcBorders (int lastOofRef, int height)
2074{
2075 DBG_OBJ_ENTER ("construct.line", 0, "calcBorders", "%d, %d",
2076 lastOofRef, height);
2077
2081
2082 bool oofmDefined = false;
2083 for (int i = 0; i < NUM_OOFM && !oofmDefined; i++)
2085 oofmDefined = true;
2086
2087 if (oofmDefined) {
2088 int firstWordOfLine =
2089 lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0;
2090 int effOofRef = max (lastOofRef, firstWordOfLine - 1);
2091 int yRel = yOffsetOfLineToBeCreated (), yRef;
2092
2093 for (int i = 0; i < NUM_OOFM; i++) {
2094 oof::OutOfFlowMgr *oofm;
2095 if ((oofm = searchOutOfFlowMgr (i)) &&
2096 findSizeRequestReference (i, NULL, &yRef)) {
2097 // Consider the example:
2098 //
2099 // <div>
2100 // Some text A ...
2101 // <p> Some text B ... <img style="float:right" ...> </p>
2102 // Some more text C ...
2103 // </div>
2104 //
2105 // If the image is large enough, it should float around the last
2106 // paragraph, "Some more text C ...":
2107 //
2108 // Some more text A ...
2109 //
2110 // Some more ,---------.
2111 // text B ... | |
2112 // | <img> |
2113 // Some more | | <---- Consider this line!
2114 // text C ... '---------'
2115 //
2116 // Since this float is generated in the <p> element, not in the-
2117 // <div> element, and since they are represented by different
2118 // instances of dw::Textblock, lastOofRefPositionedBeforeThisLine,
2119 // and so lastOofRef, is -1 for the line marked with an arrow;
2120 // this would result in ignoring the float, because -1 is
2121 // equivalent to the very beginning of the <div> element ("Some
2122 // more text A ..."), which is not affected by the float.
2123 //
2124 // On the other hand, the only relevant values of
2125 // Line::lastOofRefPositionedBeforeThisLine are those greater
2126 // than the first word of the new line, so a solution is to use
2127 // the maximum of both.
2128
2129 int y = yRef + yRel;
2130 bool thisHasLeft, thisHasRight;
2131
2132 thisHasLeft = oofm->hasFloatLeft (y, height, this, effOofRef);
2134 thisHasRight = oofm->hasFloatRight (y, height, this, effOofRef);
2136
2137 // TODO "max" is not really correct for the heights. (Does
2138 // not matter, since only one, the float manager, returns
2139 // meaningful values.)
2140 if (thisHasLeft) {
2143 oofm->getLeftBorder (y, height, this, effOofRef)
2144 - getGeneratorX (i));
2147 oofm->getLeftFloatHeight (y, height, this, effOofRef));
2148 }
2149
2150 if (thisHasRight) {
2153 oofm->getRightBorder (y, height, this, effOofRef)
2154 - getGeneratorRest (i));
2157 oofm->getRightFloatHeight (y, height, this, effOofRef));
2158 }
2159
2160 DBG_OBJ_MSGF ("construct.line", 1,
2161 "OOFM #%d: %d * %d (%s) / %d * %d (%s), at %d (%d), "
2162 "until %d = max (%d, %d - 1)",
2164 newLineHasFloatLeft ? "true" : "false",
2166 newLineHasFloatRight ? "true" : "false",
2167 y, height, effOofRef, lastOofRef, firstWordOfLine);
2168 }
2169 }
2170 }
2171
2172 DBG_OBJ_SET_BOOL ("newLineHasFloatLeft", newLineHasFloatLeft);
2173 DBG_OBJ_SET_BOOL ("newLineHasFloatRight", newLineHasFloatRight);
2174 DBG_OBJ_SET_NUM ("newLineLeftBorder", newLineLeftBorder);
2175 DBG_OBJ_SET_NUM ("newLineRightBorder", newLineRightBorder);
2176 DBG_OBJ_SET_NUM ("newLineLeftFloatHeight", newLineLeftFloatHeight);
2177 DBG_OBJ_SET_NUM ("newLineRightFloatHeight", newLineRightFloatHeight);
2178
2179 DBG_OBJ_LEAVE ();
2180}
2181
2183{
2184 DBG_OBJ_ENTER0 ("construct.line", 0, "showMissingLines");
2185
2186 // "Temporary word": when the last word is an OOF reference, it is
2187 // not processed, and not part of any line. For this reason, we
2188 // introduce a "temporary word", which is in flow, after this last
2189 // OOF reference, and later removed again.
2190 bool tempWord = words->size () > 0 &&
2191 words->getLastRef()->content.type == core::Content::WIDGET_OOF_REF;
2192 int firstWordToWrap =
2193 lines->size () > 0 ? lines->getLastRef()->lastWord + 1 : 0;
2194
2195 DBG_OBJ_MSGF ("construct.line", 1,
2196 "words->size() = %d, firstWordToWrap = %d, tempWord = %s",
2197 words->size (), firstWordToWrap, tempWord ? "true" : "false");
2198
2199 if (tempWord) {
2200 core::Requisition size = { 0, 0, 0 };
2201 addText0 ("", 0, Word::WORD_START | Word::WORD_END, getStyle (), &size);
2202 }
2203
2204 for (int i = firstWordToWrap; i < words->size (); i++)
2205 wordWrap (i, true);
2206
2207 // Remove temporary word again. The only reference should be the line.
2208 if (tempWord) {
2209 cleanupWord (words->size () - 1);
2210 words->setSize (words->size () - 1);
2211 if (lines->getLastRef()->lastWord > words->size () - 1)
2212 lines->getLastRef()->lastWord = words->size () - 1;
2213 }
2214
2215 // The following old code should not be necessary anymore, after
2216 // the introduction of the "virtual word". Instead, test the
2217 // condition.
2218 assert (lines->size () == 0 ||
2219 lines->getLastRef()->lastWord == words->size () - 1);
2220 /*
2221 // In some cases, there are some words of type WIDGET_OOF_REF left, which
2222 // are not added to line, since addLine() is only called within
2223 // wrapWordInFlow(), but not within wrapWordOofRef(). The missing line
2224 // is created here, so it is ensured that the last line ends with the last
2225 // word.
2226
2227 int firstWordNotInLine =
2228 lines->size () > 0 ? lines->getLastRef()->lastWord + 1: 0;
2229 DBG_OBJ_MSGF ("construct.line", 1, "firstWordNotInLine = %d (of %d)",
2230 firstWordNotInLine, words->size ());
2231 if (firstWordNotInLine < words->size ())
2232 addLine (firstWordNotInLine, words->size () - 1, -1, true);
2233 */
2234
2235 DBG_OBJ_LEAVE ();
2236}
2237
2238
2240{
2241 DBG_OBJ_ENTER0 ("construct.line", 0, "removeTemporaryLines");
2242
2243 if (nonTemporaryLines < lines->size ()) {
2244 lines->setSize (nonTemporaryLines);
2245 DBG_OBJ_SET_NUM ("lines.size", lines->size ());
2246
2247 // For words which will be added, the values calculated before in
2248 // accumulateWordData() are wrong, so it is called again. (Actually, the
2249 // words from the first temporary line are correct, but for simplicity,
2250 // we re-calculate all.)
2251 int firstWord =
2252 lines->size () > 0 ? lines->getLastRef()->lastWord + 1 : 0;
2253 for (int i = firstWord; i < words->size (); i++)
2255 }
2256
2257 DBG_OBJ_LEAVE ();
2258}
2259
2261{
2263 return word->origSpace / 3;
2264 else
2265 return 0;
2266}
2267
2269{
2271 return word->origSpace / 2;
2272 else
2273 return 0;
2274
2275 // Alternative: return word->origSpace / 2;
2276}
2277
2279{
2280 return 0;
2281}
2282
2284{
2285 DBG_OBJ_ENTER ("construct.word.accum", 0, "getLineStretchability", "%d",
2286 lastWordIndex);
2287 DBG_MSG_WORD ("construct.word.accum", 1, "<i>last word:</i> ",
2288 lastWordIndex, "");
2289
2290 Word *lastWord = words->getRef (lastWordIndex);
2291 int str;
2292
2294 str = 0;
2295 DBG_OBJ_MSG ("construct.word.accum", 1, "justified => 0");
2296 } else {
2297 str = stretchabilityFactor * (lastWord->maxAscent
2298 + lastWord->maxDescent) / 100;
2299 DBG_OBJ_MSGF ("construct.word.accum", 1,
2300 "not justified => %d * (%d + %d) / 100 = %d",
2302 lastWord->maxDescent, str);
2303 }
2304
2305 DBG_OBJ_LEAVE ();
2306 return str;
2307
2308 // Alternative: return 0;
2309}
2310
2311} // 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::@29 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 necessary 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:297
int textWidth(style::Font *font, const char *text, int len)
Definition layout.hh:370
int getWidthViewport()
Definition layout.hh:306
Platform * getPlatform()
Definition layout.hh:363
bool getUsesViewport()
Definition layout.hh:305
Layout * layout
Definition widget.hh:216
int boxDiffWidth()
Definition widget.hh:495
int parentRef
This value is defined by the parent widget, and used for incremential resizing.
Definition widget.hh:195
style::Style * getStyle()
Definition widget.hh:482
int boxRestWidth()
Definition widget.hh:493
float ratio
Preferred aspect ratio of the widget.
Definition widget.hh:214
virtual bool isBlockLevel()
Definition widget.cc:2192
TextAlignType textAlign
Definition style.hh:543
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:570
void appendInt(int n)
Definition misc.hh:594
void append(const char *str)
Append a NUL-terminated string to the buffer, with copying.
Definition misc.hh:593
const char * getChars()
Return a NUL-terminated strings containing all appended strings.
Definition misc.cc:87
const char * strndup(const char *str, size_t t)
Definition misc.hh:682
#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,...)
@ DISPLAY_INLINE_BLOCK
Definition style.hh:281
int multiplyWithPerLengthRounded(int x, Length l)
Like multiplyWithPerLength, but rounds to nearest integer instead of down.
Definition style.hh:484
bool isPerLength(Length l)
Returns true if l is a percentage.
Definition style.hh:446
int absLengthVal(Length l)
Returns the value of a length in pixels, as an integer.
Definition style.hh:452
Dw is in this namespace, or sub namespaces of this one.
Miscellaneous stuff, which does not fit anywhere else.
Definition misc.cc:32
T min(T a, T b)
Definition misc.hh:40
T max(T a, T b)
Definition misc.hh:41
void assertNotReached()
Definition misc.hh:56
const char * boolToStr(bool b)
Definition misc.hh:108
enum dw::Textblock::Line::@31 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
@ 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
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
@ 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(...)
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