@@ -65,6 +65,7 @@ This file is part of the iText (R) project.
65
65
import com .itextpdf .layout .property .FloatPropertyValue ;
66
66
import com .itextpdf .layout .property .Leading ;
67
67
import com .itextpdf .layout .property .OverflowPropertyValue ;
68
+ import com .itextpdf .layout .property .OverflowWrapPropertyValue ;
68
69
import com .itextpdf .layout .property .Property ;
69
70
import com .itextpdf .layout .property .RenderingMode ;
70
71
import com .itextpdf .layout .property .TabAlignment ;
@@ -100,6 +101,9 @@ public class LineRenderer extends AbstractRenderer {
100
101
101
102
@ Override
102
103
public LayoutResult layout (LayoutContext layoutContext ) {
104
+ boolean moveForwardsSpecialScriptsOverflowX = false ;
105
+ int firstChildToRelayout = -1 ;
106
+
103
107
Rectangle layoutBox = layoutContext .getArea ().getBBox ().clone ();
104
108
boolean wasParentsHeightClipped = layoutContext .isClippedHeight ();
105
109
List <Rectangle > floatRendererAreas = layoutContext .getFloatRendererAreas ();
@@ -353,20 +357,50 @@ && hasChildRendererInHtmlMode()) {
353
357
}
354
358
}
355
359
360
+
361
+ boolean shouldBreakLayouting = false ;
362
+
356
363
if (childResult == null ) {
357
- if (!wasXOverflowChanged && childPos > 0 ) {
364
+ if (TypographyUtils .isPdfCalligraphAvailable ()
365
+ && isTextRendererAndRequiresSpecialScriptPreLayoutProcessing (childRenderer )) {
366
+ specialScriptPreLayoutProcessing (childPos );
367
+ }
368
+
369
+ boolean setOverflowFitCausedBySpecialScripts = childRenderer instanceof TextRenderer
370
+ && ((TextRenderer ) childRenderer ).textContainsSpecialScriptGlyphs (true );
371
+
372
+ if (!wasXOverflowChanged
373
+ && (childPos > 0 || setOverflowFitCausedBySpecialScripts )
374
+ && !moveForwardsSpecialScriptsOverflowX ) {
358
375
oldXOverflow = this .<OverflowPropertyValue >getProperty (Property .OVERFLOW_X );
359
376
wasXOverflowChanged = true ;
360
377
setProperty (Property .OVERFLOW_X , OverflowPropertyValue .FIT );
361
378
}
362
379
363
- if (TypographyUtils .isPdfCalligraphAvailable ()
364
- && isTextRendererAndRequiresSpecialScriptPreLayoutProcessing (childRenderer )) {
365
- specialScriptPreLayoutProcessing (childPos );
380
+ if (moveForwardsSpecialScriptsOverflowX ) {
381
+ int firstPossibleBreakWithinTheRenderer =
382
+ ((TextRenderer ) childRenderer ).getSpecialScriptsWordBreakPoints ().get (0 );
383
+ if (firstPossibleBreakWithinTheRenderer != -1 ) {
384
+ ((TextRenderer ) childRenderer )
385
+ .setSpecialScriptFirstNotFittingIndex (firstPossibleBreakWithinTheRenderer );
386
+ }
387
+ if (wasXOverflowChanged ) {
388
+ setProperty (Property .OVERFLOW_X , oldXOverflow );
389
+ }
366
390
}
367
391
368
392
childResult = childRenderer .layout (new LayoutContext (new LayoutArea (layoutContext .getArea ().getPageNumber (), bbox ), wasParentsHeightClipped ));
369
393
394
+ if (moveForwardsSpecialScriptsOverflowX ) {
395
+ if (((TextRenderer ) childRenderer ).getSpecialScriptFirstNotFittingIndex () > 0 ) {
396
+ shouldBreakLayouting = true ;
397
+ }
398
+ ((TextRenderer ) childRenderer ).setSpecialScriptFirstNotFittingIndex (-1 );
399
+ if (wasXOverflowChanged ) {
400
+ setProperty (Property .OVERFLOW_X , OverflowPropertyValue .FIT );
401
+ }
402
+ }
403
+
370
404
updateSpecialScriptLayoutResults (specialScriptLayoutResults , childRenderer , childPos , childResult );
371
405
372
406
// it means that we've already increased layout area by MIN_MAX_WIDTH_CORRECTION_EPS
@@ -425,7 +459,9 @@ && isTextRendererAndRequiresSpecialScriptPreLayoutProcessing(childRenderer)) {
425
459
}
426
460
427
461
boolean newLineOccurred = (childResult instanceof TextLayoutResult && ((TextLayoutResult ) childResult ).isSplitForcedByNewline ());
428
- boolean shouldBreakLayouting = childResult .getStatus () != LayoutResult .FULL || newLineOccurred ;
462
+ if (!shouldBreakLayouting ) {
463
+ shouldBreakLayouting = childResult .getStatus () != LayoutResult .FULL || newLineOccurred ;
464
+ }
429
465
430
466
boolean forceOverflowForTextRendererPartialResult = false ;
431
467
@@ -452,69 +488,80 @@ && isTextRendererAndRequiresSpecialScriptPreLayoutProcessing(childRenderer)) {
452
488
}
453
489
}
454
490
} else if (shouldBreakLayouting && !newLineOccurred && childRenderers .get (childPos ) instanceof TextRenderer
455
- && ((TextRenderer ) childRenderers .get (childPos )).textContainsSpecialScriptGlyphs (true )) {
491
+ && ((TextRenderer ) childRenderers .get (childPos )).textContainsSpecialScriptGlyphs (true )
492
+ && !moveForwardsSpecialScriptsOverflowX ) {
493
+ boolean isOverflowFit = wasXOverflowChanged
494
+ ? (oldXOverflow == OverflowPropertyValue .FIT )
495
+ : isOverflowFit (this .<OverflowPropertyValue >getProperty (Property .OVERFLOW_X ));
456
496
LastFittingChildRendererData lastFittingChildRendererData =
457
- getIndexAndLayoutResultOfTheLastRendererToRemainOnTheLine
458
- (childPos , specialScriptLayoutResults , wasParentsHeightClipped , floatsOverflowedToNextLine );
459
-
460
- curWidth -= getCurWidthSpecialScriptsDecrement (childPos , lastFittingChildRendererData .childIndex ,
461
- specialScriptLayoutResults );
497
+ getIndexAndLayoutResultOfTheLastRendererToRemainOnTheLine (childPos , specialScriptLayoutResults ,
498
+ wasParentsHeightClipped , floatsOverflowedToNextLine , isOverflowFit );
462
499
463
- childPos = lastFittingChildRendererData .childIndex ;
464
- childResult = lastFittingChildRendererData .childLayoutResult ;
465
- }
466
-
467
- if (!forceOverflowForTextRendererPartialResult ) {
468
- maxAscent = Math .max (maxAscent , childAscent );
469
- if (childRenderer instanceof TextRenderer ) {
470
- maxTextAscent = Math .max (maxTextAscent , childAscent );
471
- } else if (!isChildFloating ) {
472
- maxBlockAscent = Math .max (maxBlockAscent , childAscent );
473
- }
474
- maxDescent = Math .min (maxDescent , childDescent );
475
- if (childRenderer instanceof TextRenderer ) {
476
- maxTextDescent = Math .min (maxTextDescent , childDescent );
477
- } else if (!isChildFloating ) {
478
- maxBlockDescent = Math .min (maxBlockDescent , childDescent );
500
+ if (lastFittingChildRendererData == null ) {
501
+ moveForwardsSpecialScriptsOverflowX = true ;
502
+ shouldBreakLayouting = false ;
503
+ firstChildToRelayout = childPos ;
504
+ } else {
505
+ curWidth -= getCurWidthSpecialScriptsDecrement (childPos , lastFittingChildRendererData .childIndex ,
506
+ specialScriptLayoutResults );
507
+ childPos = lastFittingChildRendererData .childIndex ;
508
+ childResult = lastFittingChildRendererData .childLayoutResult ;
479
509
}
480
510
}
481
- float maxHeight = maxAscent - maxDescent ;
482
-
483
- float currChildTextIndent = anythingPlaced ? 0 : lineLayoutContext .getTextIndent ();
484
- if (hangingTabStop != null
485
- && (TabAlignment .LEFT == hangingTabStop .getTabAlignment () || shouldBreakLayouting || childRenderers .size () - 1 == childPos || childRenderers .get (childPos + 1 ) instanceof TabRenderer )) {
486
- IRenderer tabRenderer = childRenderers .get (lastTabIndex );
487
- List <IRenderer > affectedRenderers = new ArrayList <>();
488
- affectedRenderers .addAll (childRenderers .subList (lastTabIndex + 1 , childPos + 1 ));
489
- float tabWidth = calculateTab (layoutBox , curWidth , hangingTabStop , affectedRenderers , tabRenderer );
490
-
491
- tabRenderer .layout (new LayoutContext (new LayoutArea (layoutContext .getArea ().getPageNumber (), bbox ), wasParentsHeightClipped ));
492
- float sumOfAffectedRendererWidths = 0 ;
493
- for (IRenderer renderer : affectedRenderers ) {
494
- renderer .move (tabWidth + sumOfAffectedRendererWidths , 0 );
495
- sumOfAffectedRendererWidths += renderer .getOccupiedArea ().getBBox ().getWidth ();
496
- }
497
- if (childResult .getSplitRenderer () != null ) {
498
- childResult .getSplitRenderer ().move (tabWidth + sumOfAffectedRendererWidths - childResult .getSplitRenderer ().getOccupiedArea ().getBBox ().getWidth (), 0 );
511
+
512
+ if (childPos != firstChildToRelayout ) {
513
+ if (!forceOverflowForTextRendererPartialResult ) {
514
+ maxAscent = Math .max (maxAscent , childAscent );
515
+ if (childRenderer instanceof TextRenderer ) {
516
+ maxTextAscent = Math .max (maxTextAscent , childAscent );
517
+ } else if (!isChildFloating ) {
518
+ maxBlockAscent = Math .max (maxBlockAscent , childAscent );
519
+ }
520
+ maxDescent = Math .min (maxDescent , childDescent );
521
+ if (childRenderer instanceof TextRenderer ) {
522
+ maxTextDescent = Math .min (maxTextDescent , childDescent );
523
+ } else if (!isChildFloating ) {
524
+ maxBlockDescent = Math .min (maxBlockDescent , childDescent );
525
+ }
499
526
}
500
- float tabAndNextElemWidth = tabWidth + childResult .getOccupiedArea ().getBBox ().getWidth ();
501
- if (hangingTabStop .getTabAlignment () == TabAlignment .RIGHT && curWidth + tabAndNextElemWidth < hangingTabStop .getTabPosition ()) {
502
- curWidth = hangingTabStop .getTabPosition ();
503
- } else {
504
- curWidth += tabAndNextElemWidth ;
527
+ float maxHeight = maxAscent - maxDescent ;
528
+
529
+ float currChildTextIndent = anythingPlaced ? 0 : lineLayoutContext .getTextIndent ();
530
+ if (hangingTabStop != null
531
+ && (TabAlignment .LEFT == hangingTabStop .getTabAlignment () || shouldBreakLayouting || childRenderers .size () - 1 == childPos || childRenderers .get (childPos + 1 ) instanceof TabRenderer )) {
532
+ IRenderer tabRenderer = childRenderers .get (lastTabIndex );
533
+ List <IRenderer > affectedRenderers = new ArrayList <>();
534
+ affectedRenderers .addAll (childRenderers .subList (lastTabIndex + 1 , childPos + 1 ));
535
+ float tabWidth = calculateTab (layoutBox , curWidth , hangingTabStop , affectedRenderers , tabRenderer );
536
+
537
+ tabRenderer .layout (new LayoutContext (new LayoutArea (layoutContext .getArea ().getPageNumber (), bbox ), wasParentsHeightClipped ));
538
+ float sumOfAffectedRendererWidths = 0 ;
539
+ for (IRenderer renderer : affectedRenderers ) {
540
+ renderer .move (tabWidth + sumOfAffectedRendererWidths , 0 );
541
+ sumOfAffectedRendererWidths += renderer .getOccupiedArea ().getBBox ().getWidth ();
542
+ }
543
+ if (childResult .getSplitRenderer () != null ) {
544
+ childResult .getSplitRenderer ().move (tabWidth + sumOfAffectedRendererWidths - childResult .getSplitRenderer ().getOccupiedArea ().getBBox ().getWidth (), 0 );
545
+ }
546
+ float tabAndNextElemWidth = tabWidth + childResult .getOccupiedArea ().getBBox ().getWidth ();
547
+ if (hangingTabStop .getTabAlignment () == TabAlignment .RIGHT && curWidth + tabAndNextElemWidth < hangingTabStop .getTabPosition ()) {
548
+ curWidth = hangingTabStop .getTabPosition ();
549
+ } else {
550
+ curWidth += tabAndNextElemWidth ;
551
+ }
552
+ widthHandler .updateMinChildWidth (minChildWidth + currChildTextIndent );
553
+ widthHandler .updateMaxChildWidth (tabWidth + maxChildWidth + currChildTextIndent );
554
+ hangingTabStop = null ;
555
+ } else if (null == hangingTabStop ) {
556
+ if (childResult .getOccupiedArea () != null && childResult .getOccupiedArea ().getBBox () != null ) {
557
+ curWidth += childResult .getOccupiedArea ().getBBox ().getWidth ();
558
+ }
559
+ widthHandler .updateMinChildWidth (minChildWidth + currChildTextIndent );
560
+ widthHandler .updateMaxChildWidth (maxChildWidth + currChildTextIndent );
505
561
}
506
- widthHandler .updateMinChildWidth (minChildWidth + currChildTextIndent );
507
- widthHandler .updateMaxChildWidth (tabWidth + maxChildWidth + currChildTextIndent );
508
- hangingTabStop = null ;
509
- } else if (null == hangingTabStop ) {
510
- if (childResult .getOccupiedArea () != null && childResult .getOccupiedArea ().getBBox () != null ) {
511
- curWidth += childResult .getOccupiedArea ().getBBox ().getWidth ();
562
+ if (!forceOverflowForTextRendererPartialResult ) {
563
+ occupiedArea .setBBox (new Rectangle (layoutBox .getX (), layoutBox .getY () + layoutBox .getHeight () - maxHeight , curWidth , maxHeight ));
512
564
}
513
- widthHandler .updateMinChildWidth (minChildWidth + currChildTextIndent );
514
- widthHandler .updateMaxChildWidth (maxChildWidth + currChildTextIndent );
515
- }
516
- if (!forceOverflowForTextRendererPartialResult ) {
517
- occupiedArea .setBBox (new Rectangle (layoutBox .getX (), layoutBox .getY () + layoutBox .getHeight () - maxHeight , curWidth , maxHeight ));
518
565
}
519
566
520
567
if (shouldBreakLayouting ) {
@@ -575,8 +622,12 @@ && isTextRendererAndRequiresSpecialScriptPreLayoutProcessing(childRenderer)) {
575
622
576
623
break ;
577
624
} else {
578
- anythingPlaced = true ;
579
- childPos ++;
625
+ if (childPos == firstChildToRelayout ) {
626
+ firstChildToRelayout = -1 ;
627
+ } else {
628
+ anythingPlaced = true ;
629
+ childPos ++;
630
+ }
580
631
}
581
632
}
582
633
@@ -1378,7 +1429,7 @@ void distributePossibleBreakPointsOverSequentialTextRenderers(
1378
1429
1379
1430
LastFittingChildRendererData getIndexAndLayoutResultOfTheLastRendererToRemainOnTheLine
1380
1431
(int childPos , Map <Integer , LayoutResult > specialScriptLayoutResults , boolean wasParentsHeightClipped ,
1381
- List <IRenderer > floatsOverflowedToNextLine ) {
1432
+ List <IRenderer > floatsOverflowedToNextLine , boolean isOverflowFit ) {
1382
1433
int indexOfRendererContainingLastFullyFittingWord = childPos ;
1383
1434
int splitPosition = 0 ;
1384
1435
boolean needToSplitRendererContainingLastFullyFittingWord = false ;
@@ -1440,14 +1491,25 @@ && isChildFloating(childRenderers.get(analyzedTextRendererIndex - 1))) {
1440
1491
SpecialScriptsContainingSequenceStatus status =
1441
1492
getSpecialScriptsContainingSequenceStatus (analyzedTextRendererIndex );
1442
1493
1443
- // possible breaks haven't been found, can't move back
1494
+ // possible breaks haven't been found, can't move back:
1444
1495
// forced split on the latter renderer having either Full or Partial result
1496
+ // if either OVERFLOW_X is FIT or OVERFLOW_WRAP is either ANYWHERE or BREAK_WORD,
1497
+ // otherwise return null as a flag to move forward across this.childRenderers
1498
+ // till the end of the unbreakable word
1445
1499
if (status == SpecialScriptsContainingSequenceStatus .FORCED_SPLIT ) {
1446
- if (childPosLayoutResult .getStatus () != LayoutResult .NOTHING ) {
1447
- returnLayoutResult = childPosLayoutResult ;
1500
+ OverflowWrapPropertyValue overflowWrapPropertyValue =
1501
+ childRenderers .get (childPos ).<OverflowWrapPropertyValue >getProperty (Property .OVERFLOW_WRAP );
1502
+ if (overflowWrapPropertyValue == OverflowWrapPropertyValue .ANYWHERE
1503
+ || overflowWrapPropertyValue == OverflowWrapPropertyValue .BREAK_WORD
1504
+ || isOverflowFit ) {
1505
+ if (childPosLayoutResult .getStatus () != LayoutResult .NOTHING ) {
1506
+ returnLayoutResult = childPosLayoutResult ;
1507
+ }
1508
+ indexOfRendererContainingLastFullyFittingWord = childPos ;
1509
+ break ;
1510
+ } else {
1511
+ return null ;
1448
1512
}
1449
- indexOfRendererContainingLastFullyFittingWord = childPos ;
1450
- break ;
1451
1513
}
1452
1514
1453
1515
// possible breaks haven't been found, can't move back
@@ -1497,8 +1559,8 @@ void updateFloatsOverflowedToNextLine(List<IRenderer> floatsOverflowedToNextLine
1497
1559
* This method defines how to proceed with a {@link TextRenderer} within which possible breaks haven't been found.
1498
1560
* Possible scenarios are:
1499
1561
* - Preceding renderer is also an instance of {@link TextRenderer} and does contain special scripts:
1500
- * {@link LineRenderer#getIndexAndLayoutResultOfTheLastRendererToRemainOnTheLine(int, Map, boolean, List)} will
1501
- * proceed to analyze the preceding {@link TextRenderer} on the subject of possible breaks;
1562
+ * {@link LineRenderer#getIndexAndLayoutResultOfTheLastRendererToRemainOnTheLine(int, Map, boolean, List, boolean)}
1563
+ * will proceed to analyze the preceding {@link TextRenderer} on the subject of possible breaks;
1502
1564
* - Preceding renderer is either an instance of {@link TextRenderer} which does not contain special scripts,
1503
1565
* or an instance of {@link ImageRenderer} or is an inlineBlock child: in this case the entire subsequence of
1504
1566
* {@link TextRenderer}-s containing special scripts is to be moved to the next line;
0 commit comments