Dillo v3.1.1-98-g318d1f14
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 = 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 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 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 // Addiditional 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 neccessarily 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
1807 switch (firstWord->style->textAlign) {
1809 DBG_OBJ_MSG ("construct.line", 1,
1810 "first word has 'text-align: left'");
1811 line->alignment = Line::LEFT;
1812 break;
1813 case core::style::TEXT_ALIGN_STRING: /* handled elsewhere (in the
1814 * future)? */
1815 DBG_OBJ_MSG ("construct.line", 1,
1816 "first word has 'text-align: string'");
1817 line->alignment = Line::LEFT;
1818 break;
1819 case core::style::TEXT_ALIGN_JUSTIFY: /* see some lines above */
1820 DBG_OBJ_MSG ("construct.line", 1,
1821 "first word has 'text-align: justify'");
1822 line->alignment = Line::LEFT;
1823 // Do not justify the last line of a paragraph (which ends on a
1824 // BREAK or with the last word of the page).
1825 if(!(lastWord->content.type == core::Content::BREAK ||
1826 line->lastWord == words->size () - 1) ||
1827 // In some cases, however, an unjustified line would be too wide:
1828 // when the line would be shrunken otherwise. (This solution is
1829 // far from perfect, but a better solution would make changes in
1830 // the line breaking algorithm necessary.)
1831 lineBreakWidth < lastWord->totalWidth)
1832 justifyLine (line, lineBreakWidth - lastWord->totalWidth);
1833 break;
1835 DBG_OBJ_MSG ("construct.line", 1,
1836 "first word has 'text-align: right'");
1837 line->alignment = Line::RIGHT;
1838 break;
1840 DBG_OBJ_MSG ("construct.line", 1,
1841 "first word has 'text-align: center'");
1842 line->alignment = Line::CENTER;
1843 break;
1844 default:
1845 // compiler happiness
1846 line->alignment = Line::LEFT;
1847 }
1848
1849 } else
1850 // empty line (only line break);
1851 line->alignment = Line::LEFT;
1852 } else
1853 // empty line (or only OOF references).
1854 line->alignment = Line::LEFT;
1855
1856 DBG_OBJ_LEAVE ();
1857}
1858
1859void Textblock::calcTextOffset (int lineIndex, int totalWidth)
1860{
1861 DBG_OBJ_ENTER ("construct.line", 0, "calcTextOffset", "%d, %d",
1862 lineIndex, totalWidth);
1863
1864 Line *line = lines->getRef (lineIndex);
1865 int lineWidth = line->firstWord <= line->lastWord ?
1866 words->getRef(line->lastWord)->totalWidth : 0;
1867
1868 switch (line->alignment) {
1869 case Line::LEFT:
1870 line->textOffset = line->leftOffset;
1871 DBG_OBJ_MSGF ("construct.line", 1, "left: textOffset = %d",
1872 line->textOffset);
1873 break;
1874
1875 case Line::RIGHT:
1876 line->textOffset = totalWidth - line->rightOffset - lineWidth;
1877 DBG_OBJ_MSGF ("construct.line", 1,
1878 "right: textOffset = %d - %d - %d = %d",
1879 totalWidth, line->rightOffset, lineWidth, line->textOffset);
1880 break;
1881
1882 case Line::CENTER:
1883 line->textOffset =
1884 (line->leftOffset + totalWidth - line->rightOffset - lineWidth) / 2;
1885 DBG_OBJ_MSGF ("construct.line", 1,
1886 "center: textOffset = (%d + %d - %d - %d) /2 = %d",
1887 line->leftOffset, totalWidth, line->rightOffset, lineWidth,
1888 line->textOffset);
1889 break;
1890
1891 default:
1893 break;
1894 }
1895
1896 // For large lines (images etc), which do not fit into the viewport:
1897 if (line->textOffset < line->leftOffset)
1898 line->textOffset = line->leftOffset;
1899
1900 DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "textOffset", line->textOffset);
1901 DBG_OBJ_LEAVE ();
1902}
1903
1911{
1912 DBG_OBJ_ENTER0 ("construct.line", 0, "rewrap");
1913
1914 if (wrapRefLines == -1)
1915 DBG_OBJ_MSG ("construct.line", 0, "does not have to be rewrapped");
1916 else {
1917 // All lines up from wrapRef will be rebuild from the word list,
1918 // the line list up from this position is rebuild.
1919 lines->setSize (wrapRefLines);
1920 DBG_OBJ_SET_NUM ("lines.size", lines->size ());
1922
1923 initNewLine ();
1924
1925 int firstWord;
1926 if (lines->size () > 0) {
1927 Line *lastLine = lines->getLastRef();
1928 firstWord = lastLine->lastWord + 1;
1929 } else
1930 firstWord = 0;
1931
1932 DBG_OBJ_MSGF ("construct.line", 0, "starting with word %d", firstWord);
1933
1934 lastWordDrawn = min (lastWordDrawn, firstWord - 1);
1935 DBG_OBJ_SET_NUM ("lastWordDrawn", lastWordDrawn);
1936
1937 for (int i = firstWord; i < words->size (); i++) {
1938 Word *word = words->getRef (i);
1939
1940 switch (word->content.type) {
1942 calcSizeOfWidgetInFlow (i, word->content.widget, &word->size);
1944 break;
1945
1947 {
1948 int oofmIndex =
1950 oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr (oofmIndex);
1952 &(word->size));
1954 }
1955 break;
1956
1957 default:
1958 break;
1959 }
1960
1961 wordWrap (i, false);
1962
1963 // Somewhat historical, but still important, note:
1964 //
1965 // For the case that something else is done with this word, it
1966 // is important that wordWrap() may insert some new words; since
1967 // NotSoSimpleVector is used for the words list, the internal
1968 // structure may have changed, so getRef() must be called again.
1969 //
1970 // So this is necessary: word = words->getRef (i);
1971 }
1972
1973 // Next time, the page will not have to be rewrapped.
1974 wrapRefLines = -1;
1975 DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines);
1976 }
1977
1978 DBG_OBJ_LEAVE ();
1979}
1980
1985{
1986 DBG_OBJ_ENTER0 ("resize", 0, "fillParagraphs");
1987
1988 DBG_OBJ_MSGF ("resize", 1, "wrapRefParagraphs = %d", wrapRefParagraphs);
1989
1990 if (wrapRefParagraphs != -1) {
1991 // Notice that wrapRefParagraphs refers to the lines, not to the
1992 // paragraphs.
1993 int firstWordOfLine;
1994 if (lines->size () > 0 && wrapRefParagraphs > 0) {
1995 // Sometimes, wrapRefParagraphs is larger than lines->size(), due to
1996 // floats? (Has to be clarified.)
1997 int lineNo = min (wrapRefParagraphs, lines->size ()) - 1;
1998 firstWordOfLine = lines->getRef(lineNo)->lastWord + 1;
1999 } else
2000 firstWordOfLine = 0;
2001
2002 int parNo;
2003 if (paragraphs->size() > 0 &&
2004 firstWordOfLine > paragraphs->getLastRef()->firstWord)
2005 // A special case: the paragraphs list has been partly built, but
2006 // not yet the paragraph containing the word in question. In
2007 // this case, only the rest of the paragraphs list must be
2008 // constructed. (Without this check, findParagraphOfWord would
2009 // return -1 in this case, so that all paragraphs would be
2010 // rebuilt.)
2011 parNo = paragraphs->size ();
2012 else
2013 // If there are no paragraphs yet, findParagraphOfWord will return
2014 // -1: use 0 then instead.
2015 parNo = max (0, findParagraphOfWord (firstWordOfLine));
2016
2017 paragraphs->setSize (parNo);
2018 DBG_OBJ_SET_NUM ("paragraphs.size", paragraphs->size ());
2019
2020 int firstWord;
2021 if (paragraphs->size () > 0)
2022 firstWord = paragraphs->getLastRef()->lastWord + 1;
2023 else
2024 firstWord = 0;
2025
2026 DBG_OBJ_MSGF ("resize", 1, "firstWord = %d, words->size() = %d [before]",
2027 firstWord, words->size ());
2028
2029 for (int i = firstWord; i < words->size (); i++)
2031
2032 DBG_OBJ_MSGF ("resize", 1, "words->size() = %d [after]", words->size ());
2033
2034 wrapRefParagraphs = -1;
2035 DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs);
2036 }
2037
2038 DBG_OBJ_LEAVE ();
2039}
2040
2042{
2043 DBG_OBJ_ENTER0 ("construct.line", 0, "initNewLine");
2044
2045 calcBorders (lines->size() > 0 ?
2046 lines->getLastRef()->lastOofRefPositionedBeforeThisLine : -1,
2047 1);
2048
2050
2051 DBG_OBJ_SET_NUM ("newLineAscent", newLineAscent);
2052 DBG_OBJ_SET_NUM ("newLineDescent", newLineDescent);
2053
2054 DBG_OBJ_LEAVE ();
2055}
2056
2057void Textblock::calcBorders (int lastOofRef, int height)
2058{
2059 DBG_OBJ_ENTER ("construct.line", 0, "calcBorders", "%d, %d",
2060 lastOofRef, height);
2061
2065
2066 bool oofmDefined = false;
2067 for (int i = 0; i < NUM_OOFM && !oofmDefined; i++)
2069 oofmDefined = true;
2070
2071 if (oofmDefined) {
2072 int firstWordOfLine =
2073 lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0;
2074 int effOofRef = max (lastOofRef, firstWordOfLine - 1);
2075 int yRel = yOffsetOfLineToBeCreated (), yRef;
2076
2077 for (int i = 0; i < NUM_OOFM; i++) {
2078 oof::OutOfFlowMgr *oofm;
2079 if ((oofm = searchOutOfFlowMgr (i)) &&
2080 findSizeRequestReference (i, NULL, &yRef)) {
2081 // Consider the example:
2082 //
2083 // <div>
2084 // Some text A ...
2085 // <p> Some text B ... <img style="float:right" ...> </p>
2086 // Some more text C ...
2087 // </div>
2088 //
2089 // If the image is large enough, it should float around the last
2090 // paragraph, "Some more text C ...":
2091 //
2092 // Some more text A ...
2093 //
2094 // Some more ,---------.
2095 // text B ... | |
2096 // | <img> |
2097 // Some more | | <---- Consider this line!
2098 // text C ... '---------'
2099 //
2100 // Since this float is generated in the <p> element, not in the-
2101 // <div> element, and since they are represented by different
2102 // instances of dw::Textblock, lastOofRefPositionedBeforeThisLine,
2103 // and so lastOofRef, is -1 for the line marked with an arrow;
2104 // this would result in ignoring the float, because -1 is
2105 // equivalent to the very beginning of the <div> element ("Some
2106 // more text A ..."), which is not affected by the float.
2107 //
2108 // On the other hand, the only relevant values of
2109 // Line::lastOofRefPositionedBeforeThisLine are those greater
2110 // than the first word of the new line, so a solution is to use
2111 // the maximum of both.
2112
2113 int y = yRef + yRel;
2114 bool thisHasLeft, thisHasRight;
2115
2116 thisHasLeft = oofm->hasFloatLeft (y, height, this, effOofRef);
2118 thisHasRight = oofm->hasFloatRight (y, height, this, effOofRef);
2120
2121 // TODO "max" is not really correct for the heights. (Does
2122 // not matter, since only one, the float manager, returns
2123 // meaningful values.)
2124 if (thisHasLeft) {
2127 oofm->getLeftBorder (y, height, this, effOofRef)
2128 - getGeneratorX (i));
2131 oofm->getLeftFloatHeight (y, height, this, effOofRef));
2132 }
2133
2134 if (thisHasRight) {
2137 oofm->getRightBorder (y, height, this, effOofRef)
2138 - getGeneratorRest (i));
2141 oofm->getRightFloatHeight (y, height, this, effOofRef));
2142 }
2143
2144 DBG_OBJ_MSGF ("construct.line", 1,
2145 "OOFM #%d: %d * %d (%s) / %d * %d (%s), at %d (%d), "
2146 "until %d = max (%d, %d - 1)",
2148 newLineHasFloatLeft ? "true" : "false",
2150 newLineHasFloatRight ? "true" : "false",
2151 y, height, effOofRef, lastOofRef, firstWordOfLine);
2152 }
2153 }
2154 }
2155
2156 DBG_OBJ_SET_BOOL ("newLineHasFloatLeft", newLineHasFloatLeft);
2157 DBG_OBJ_SET_BOOL ("newLineHasFloatRight", newLineHasFloatRight);
2158 DBG_OBJ_SET_NUM ("newLineLeftBorder", newLineLeftBorder);
2159 DBG_OBJ_SET_NUM ("newLineRightBorder", newLineRightBorder);
2160 DBG_OBJ_SET_NUM ("newLineLeftFloatHeight", newLineLeftFloatHeight);
2161 DBG_OBJ_SET_NUM ("newLineRightFloatHeight", newLineRightFloatHeight);
2162
2163 DBG_OBJ_LEAVE ();
2164}
2165
2167{
2168 DBG_OBJ_ENTER0 ("construct.line", 0, "showMissingLines");
2169
2170 // "Temporary word": when the last word is an OOF reference, it is
2171 // not processed, and not part of any line. For this reason, we
2172 // introduce a "temporary word", which is in flow, after this last
2173 // OOF reference, and later removed again.
2174 bool tempWord = words->size () > 0 &&
2175 words->getLastRef()->content.type == core::Content::WIDGET_OOF_REF;
2176 int firstWordToWrap =
2177 lines->size () > 0 ? lines->getLastRef()->lastWord + 1 : 0;
2178
2179 DBG_OBJ_MSGF ("construct.line", 1,
2180 "words->size() = %d, firstWordToWrap = %d, tempWord = %s",
2181 words->size (), firstWordToWrap, tempWord ? "true" : "false");
2182
2183 if (tempWord) {
2184 core::Requisition size = { 0, 0, 0 };
2185 addText0 ("", 0, Word::WORD_START | Word::WORD_END, getStyle (), &size);
2186 }
2187
2188 for (int i = firstWordToWrap; i < words->size (); i++)
2189 wordWrap (i, true);
2190
2191 // Remove temporary word again. The only reference should be the line.
2192 if (tempWord) {
2193 cleanupWord (words->size () - 1);
2194 words->setSize (words->size () - 1);
2195 if (lines->getLastRef()->lastWord > words->size () - 1)
2196 lines->getLastRef()->lastWord = words->size () - 1;
2197 }
2198
2199 // The following old code should not be necessary anymore, after
2200 // the introduction of the "virtual word". Instead, test the
2201 // condition.
2202 assert (lines->size () == 0 ||
2203 lines->getLastRef()->lastWord == words->size () - 1);
2204 /*
2205 // In some cases, there are some words of type WIDGET_OOF_REF left, which
2206 // are not added to line, since addLine() is only called within
2207 // wrapWordInFlow(), but not within wrapWordOofRef(). The missing line
2208 // is created here, so it is ensured that the last line ends with the last
2209 // word.
2210
2211 int firstWordNotInLine =
2212 lines->size () > 0 ? lines->getLastRef()->lastWord + 1: 0;
2213 DBG_OBJ_MSGF ("construct.line", 1, "firstWordNotInLine = %d (of %d)",
2214 firstWordNotInLine, words->size ());
2215 if (firstWordNotInLine < words->size ())
2216 addLine (firstWordNotInLine, words->size () - 1, -1, true);
2217 */
2218
2219 DBG_OBJ_LEAVE ();
2220}
2221
2222
2224{
2225 DBG_OBJ_ENTER0 ("construct.line", 0, "removeTemporaryLines");
2226
2227 if (nonTemporaryLines < lines->size ()) {
2228 lines->setSize (nonTemporaryLines);
2229 DBG_OBJ_SET_NUM ("lines.size", lines->size ());
2230
2231 // For words which will be added, the values calculated before in
2232 // accumulateWordData() are wrong, so it is called again. (Actually, the
2233 // words from the first temporary line are correct, but for simplicity,
2234 // we re-calculate all.)
2235 int firstWord =
2236 lines->size () > 0 ? lines->getLastRef()->lastWord + 1 : 0;
2237 for (int i = firstWord; i < words->size (); i++)
2239 }
2240
2241 DBG_OBJ_LEAVE ();
2242}
2243
2245{
2247 return word->origSpace / 3;
2248 else
2249 return 0;
2250}
2251
2253{
2255 return word->origSpace / 2;
2256 else
2257 return 0;
2258
2259 // Alternative: return word->origSpace / 2;
2260}
2261
2263{
2264 return 0;
2265}
2266
2268{
2269 DBG_OBJ_ENTER ("construct.word.accum", 0, "getLineStretchability", "%d",
2270 lastWordIndex);
2271 DBG_MSG_WORD ("construct.word.accum", 1, "<i>last word:</i> ",
2272 lastWordIndex, "");
2273
2274 Word *lastWord = words->getRef (lastWordIndex);
2275 int str;
2276
2278 str = 0;
2279 DBG_OBJ_MSG ("construct.word.accum", 1, "justified => 0");
2280 } else {
2281 str = stretchabilityFactor * (lastWord->maxAscent
2282 + lastWord->maxDescent) / 100;
2283 DBG_OBJ_MSGF ("construct.word.accum", 1,
2284 "not justified => %d * (%d + %d) / 100 = %d",
2286 lastWord->maxDescent, str);
2287 }
2288
2289 DBG_OBJ_LEAVE ();
2290 return str;
2291
2292 // Alternative: return 0;
2293}
2294
2295} // 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 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:277
int textWidth(style::Font *font, const char *text, int len)
Definition layout.hh:350
int getWidthViewport()
Definition layout.hh:286
Platform * getPlatform()
Definition layout.hh:343
bool getUsesViewport()
Definition layout.hh:285
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:86
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,...)
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:31
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