@@ -43,6 +43,7 @@ This file is part of the iText (R) project.
43
43
*/
44
44
package com .itextpdf .layout .renderer ;
45
45
46
+ import com .itextpdf .io .LogMessageConstant ;
46
47
import com .itextpdf .io .font .otf .Glyph ;
47
48
import com .itextpdf .io .font .otf .GlyphLine ;
48
49
import com .itextpdf .io .util .ArrayUtil ;
@@ -62,6 +63,7 @@ This file is part of the iText (R) project.
62
63
import com .itextpdf .layout .property .Property ;
63
64
import com .itextpdf .layout .property .TabAlignment ;
64
65
import com .itextpdf .layout .property .UnitValue ;
66
+ import org .slf4j .LoggerFactory ;
65
67
66
68
import java .util .ArrayList ;
67
69
import java .util .Arrays ;
@@ -74,6 +76,10 @@ public class LineRenderer extends AbstractRenderer {
74
76
75
77
protected float maxAscent ;
76
78
protected float maxDescent ;
79
+ private float maxTextAscent ;
80
+ private float maxTextDescent ;
81
+ private float maxBlockAscent ;
82
+ private float maxBlockDescent ;
77
83
78
84
// bidi levels
79
85
protected byte [] levels ;
@@ -92,6 +98,10 @@ public LineLayoutResult layout(LayoutContext layoutContext) {
92
98
float curWidth = 0 ;
93
99
maxAscent = 0 ;
94
100
maxDescent = 0 ;
101
+ maxTextAscent = 0 ;
102
+ maxTextDescent = 0 ;
103
+ maxBlockAscent = -1e20f ;
104
+ maxBlockDescent = 1e20f ;
95
105
int childPos = 0 ;
96
106
97
107
MinMaxWidth minMaxWidth = new MinMaxWidth (0 , layoutBox .getWidth ());
@@ -117,7 +127,7 @@ public LineLayoutResult layout(LayoutContext layoutContext) {
117
127
118
128
while (childPos < childRenderers .size ()) {
119
129
IRenderer childRenderer = childRenderers .get (childPos );
120
- LayoutResult childResult ;
130
+ LayoutResult childResult = null ;
121
131
Rectangle bbox = new Rectangle (layoutBox .getX () + curWidth , layoutBox .getY (), layoutBox .getWidth () - curWidth , layoutBox .getHeight ());
122
132
123
133
if (childRenderer instanceof TextRenderer ) {
@@ -241,7 +251,33 @@ public LineLayoutResult layout(LayoutContext layoutContext) {
241
251
continue ;
242
252
}
243
253
244
- childResult = childRenderer .layout (new LayoutContext (new LayoutArea (layoutContext .getArea ().getPageNumber (), bbox )));
254
+ MinMaxWidth childBlockMinMaxWidth = null ;
255
+ boolean isInlineBlockChild = isInlineBlockChild (childRenderer );
256
+ if (!childWidthWasReplaced ) {
257
+ if (isInlineBlockChild && childRenderer instanceof AbstractRenderer ) {
258
+ childBlockMinMaxWidth = ((AbstractRenderer )childRenderer ).getMinMaxWidth (layoutContext .getArea ().getBBox ().getWidth ());
259
+ // TODO fix eps?
260
+ float eps = 0.001f ;
261
+ float childMaxWidth = childBlockMinMaxWidth .getMaxWidth () + eps ;
262
+ // Decrease the calculated width by margins, paddings and borders so that even for 100% width the content definitely fits
263
+ // TODO DEVSIX-1174 fix depending on box-sizing
264
+ if (childBlockMinMaxWidth != null ) {
265
+ if (childMaxWidth > bbox .getWidth () && bbox .getWidth () != layoutContext .getArea ().getBBox ().getWidth ()) {
266
+ childResult = new LineLayoutResult (LayoutResult .NOTHING , null , null , childRenderer , childRenderer );
267
+ } else {
268
+ if (bbox .getWidth () == layoutContext .getArea ().getBBox ().getWidth () && childBlockMinMaxWidth .getMinWidth () > layoutContext .getArea ().getBBox ().getWidth ()) {
269
+ LoggerFactory .getLogger (LineRenderer .class ).warn (LogMessageConstant .INLINE_BLOCK_ELEMENT_WILL_BE_CLIPPED );
270
+ childRenderer .setProperty (Property .FORCED_PLACEMENT , true );
271
+ }
272
+ bbox .setWidth (childMaxWidth );
273
+ }
274
+ }
275
+ }
276
+ }
277
+
278
+ if (childResult == null ) {
279
+ childResult = childRenderer .layout (new LayoutContext (new LayoutArea (layoutContext .getArea ().getPageNumber (), bbox )));
280
+ }
245
281
246
282
// Get back child width so that it's not lost
247
283
if (childWidthWasReplaced ) {
@@ -259,17 +295,44 @@ public LineLayoutResult layout(LayoutContext layoutContext) {
259
295
minChildWidth = ((MinMaxWidthLayoutResult ) childResult ).getNotNullMinMaxWidth (bbox .getWidth ()).getMinWidth ();
260
296
}
261
297
maxChildWidth = ((MinMaxWidthLayoutResult ) childResult ).getNotNullMinMaxWidth (bbox .getWidth ()).getMaxWidth ();
298
+ } else if (childBlockMinMaxWidth != null ) {
299
+ minChildWidth = childBlockMinMaxWidth .getMinWidth ();
300
+ maxChildWidth = childBlockMinMaxWidth .getMaxWidth ();
262
301
}
263
302
264
303
float childAscent = 0 ;
265
304
float childDescent = 0 ;
266
- if (childRenderer instanceof ILeafElementRenderer ) {
305
+ if (childRenderer instanceof ILeafElementRenderer && childResult . getStatus () != LayoutResult . NOTHING ) {
267
306
childAscent = ((ILeafElementRenderer ) childRenderer ).getAscent ();
268
307
childDescent = ((ILeafElementRenderer ) childRenderer ).getDescent ();
308
+ } else if (isInlineBlockChild && childResult .getStatus () != LayoutResult .NOTHING ) {
309
+ if (childRenderer instanceof AbstractRenderer ) {
310
+ Float yLine = ((AbstractRenderer )childRenderer ).getLastYLineRecursively ();
311
+ if (yLine == null ) {
312
+ childAscent = childRenderer .getOccupiedArea ().getBBox ().getHeight ();
313
+ } else {
314
+ childAscent = childRenderer .getOccupiedArea ().getBBox ().getTop () - (float )yLine ;
315
+ childDescent = -((float )yLine - childRenderer .getOccupiedArea ().getBBox ().getBottom ());
316
+ }
317
+ } else {
318
+ childAscent = childRenderer .getOccupiedArea ().getBBox ().getHeight ();
319
+ }
269
320
}
270
321
271
322
maxAscent = Math .max (maxAscent , childAscent );
323
+ // TODO treat images as blocks
324
+ if (childRenderer instanceof TextRenderer || childRenderer instanceof ImageRenderer ) {
325
+ maxTextAscent = Math .max (maxTextAscent , childAscent );
326
+ } else if (!isChildFloating ) {
327
+ maxBlockAscent = Math .max (maxBlockAscent , childAscent );
328
+ }
272
329
maxDescent = Math .min (maxDescent , childDescent );
330
+ // TODO treat images as blocks
331
+ if (childRenderer instanceof TextRenderer || childRenderer instanceof ImageRenderer ) {
332
+ maxTextDescent = Math .min (maxTextDescent , childDescent );
333
+ } else if (!isChildFloating ) {
334
+ maxBlockDescent = Math .min (maxBlockDescent , childDescent );
335
+ }
273
336
float maxHeight = maxAscent - maxDescent ;
274
337
275
338
boolean newLineOccurred = (childResult instanceof TextLayoutResult && ((TextLayoutResult ) childResult ).isSplitForcedByNewline ());
@@ -302,7 +365,9 @@ public LineLayoutResult layout(LayoutContext layoutContext) {
302
365
widthHandler .updateMaxChildWidth (tabWidth + maxChildWidth );
303
366
hangingTabStop = null ;
304
367
} else if (null == hangingTabStop ) {
305
- curWidth += childResult .getOccupiedArea ().getBBox ().getWidth ();
368
+ if (childResult .getOccupiedArea () != null && childResult .getOccupiedArea ().getBBox () != null ) {
369
+ curWidth += childResult .getOccupiedArea ().getBBox ().getWidth ();
370
+ }
306
371
widthHandler .updateMinChildWidth (minChildWidth );
307
372
widthHandler .updateMaxChildWidth (maxChildWidth );
308
373
}
@@ -325,13 +390,23 @@ public LineLayoutResult layout(LayoutContext layoutContext) {
325
390
split [1 ].childRenderers .add (childRenderer );
326
391
split [1 ].childRenderers .addAll (childRenderers .subList (childPos + 1 , childRenderers .size ()));
327
392
} else {
328
- if (childResult .getStatus () == LayoutResult .PARTIAL || childResult .getStatus () == LayoutResult .FULL ) {
393
+ boolean forcePlacement = Boolean .TRUE .equals (getPropertyAsBoolean (Property .FORCED_PLACEMENT ));
394
+ boolean isInlineBlockAndFirstOnRootArea = isInlineBlockChild && isFirstOnRootArea ();
395
+ if (childResult .getStatus () == LayoutResult .PARTIAL && (!isInlineBlockChild || forcePlacement || isInlineBlockAndFirstOnRootArea ) || childResult .getStatus () == LayoutResult .FULL ) {
329
396
split [0 ].addChild (childResult .getSplitRenderer ());
330
397
anythingPlaced = true ;
331
398
}
332
399
333
400
if (null != childResult .getOverflowRenderer ()) {
334
- split [1 ].childRenderers .add (childResult .getOverflowRenderer ());
401
+ if (isInlineBlockChild && !forcePlacement && !isInlineBlockAndFirstOnRootArea ) {
402
+ split [1 ].childRenderers .add (childRenderer );
403
+ } else {
404
+ if (isInlineBlockChild && childResult .getOverflowRenderer ().getChildRenderers ().size () == 0 ) {
405
+ LoggerFactory .getLogger (LineRenderer .class ).warn (LogMessageConstant .INLINE_BLOCK_ELEMENT_WILL_BE_CLIPPED );
406
+ } else {
407
+ split [1 ].childRenderers .add (childResult .getOverflowRenderer ());
408
+ }
409
+ }
335
410
}
336
411
split [1 ].childRenderers .addAll (childRenderers .subList (childPos + 1 , childRenderers .size ()));
337
412
}
@@ -540,9 +615,9 @@ public float getYLine() {
540
615
public float getLeadingValue (Leading leading ) {
541
616
switch (leading .getType ()) {
542
617
case Leading .FIXED :
543
- return leading .getValue ();
618
+ return Math . max ( leading .getValue (), maxBlockAscent - maxBlockDescent );
544
619
case Leading .MULTIPLIED :
545
- return occupiedArea . getBBox (). getHeight () * leading . getValue ( );
620
+ return getTopLeadingIndent ( leading ) + getBottomLeadingIndent ( leading );
546
621
default :
547
622
throw new IllegalStateException ();
548
623
}
@@ -558,6 +633,11 @@ protected Float getFirstYLineRecursively() {
558
633
return getYLine ();
559
634
}
560
635
636
+ @ Override
637
+ protected Float getLastYLineRecursively () {
638
+ return getYLine ();
639
+ }
640
+
561
641
public void justify (float width ) {
562
642
float ratio = (float ) this .getPropertyAsFloat (Property .SPACING_RATIO );
563
643
float freeWidth = occupiedArea .getBBox ().getX () + width -
@@ -649,6 +729,10 @@ protected LineRenderer[] split() {
649
729
splitRenderer .parent = parent ;
650
730
splitRenderer .maxAscent = maxAscent ;
651
731
splitRenderer .maxDescent = maxDescent ;
732
+ splitRenderer .maxTextAscent = maxTextAscent ;
733
+ splitRenderer .maxTextDescent = maxTextDescent ;
734
+ splitRenderer .maxBlockAscent = maxBlockAscent ;
735
+ splitRenderer .maxBlockDescent = maxBlockDescent ;
652
736
splitRenderer .levels = levels ;
653
737
splitRenderer .addAllProperties (getOwnProperties ());
654
738
@@ -669,7 +753,8 @@ protected LineRenderer adjustChildrenYLine() {
669
753
float descent = ((ILeafElementRenderer ) renderer ).getDescent ();
670
754
renderer .move (0 , actualYLine - renderer .getOccupiedArea ().getBBox ().getBottom () + descent );
671
755
} else {
672
- renderer .move (0 , occupiedArea .getBBox ().getY () - renderer .getOccupiedArea ().getBBox ().getBottom ());
756
+ Float yLine = isInlineBlockChild (renderer ) && renderer instanceof AbstractRenderer ? ((AbstractRenderer ) renderer ).getLastYLineRecursively () : null ;
757
+ renderer .move (0 , actualYLine - (yLine == null ? renderer .getOccupiedArea ().getBBox ().getBottom () : (float )yLine ));
673
758
}
674
759
}
675
760
return this ;
@@ -717,11 +802,33 @@ protected MinMaxWidth getMinMaxWidth(float availableWidth) {
717
802
}
718
803
719
804
float getTopLeadingIndent (Leading leading ) {
720
- return (getLeadingValue (leading ) - occupiedArea .getBBox ().getHeight ()) / 2 ;
805
+ switch (leading .getType ()) {
806
+ case Leading .FIXED :
807
+ return (Math .max (leading .getValue (), maxBlockAscent - maxBlockDescent ) - occupiedArea .getBBox ().getHeight ()) / 2 ;
808
+ case Leading .MULTIPLIED :
809
+ float fontSize = (float )this .getPropertyAsFloat (Property .FONT_SIZE , 0f );
810
+ // TODO contains image to be removed
811
+ float textAscent = maxTextAscent == 0 && maxTextDescent == 0 && Math .abs (maxAscent ) + Math .abs (maxDescent ) != 0 && !containsImage () ? fontSize * 0.8f : maxTextAscent ;
812
+ float textDescent = maxTextAscent == 0 && maxTextDescent == 0 && Math .abs (maxAscent ) + Math .abs (maxDescent ) != 0 && !containsImage () ? -fontSize * 0.2f : maxTextDescent ;
813
+ return Math .max (textAscent + ((textAscent - textDescent ) * (leading .getValue () - 1 )) / 2 , maxBlockAscent ) - maxAscent ;
814
+ default :
815
+ throw new IllegalStateException ();
816
+ }
721
817
}
722
818
723
819
float getBottomLeadingIndent (Leading leading ) {
724
- return (getLeadingValue (leading ) - occupiedArea .getBBox ().getHeight ()) / 2 ;
820
+ switch (leading .getType ()) {
821
+ case Leading .FIXED :
822
+ return (Math .max (leading .getValue (), maxBlockAscent - maxBlockDescent ) - occupiedArea .getBBox ().getHeight ()) / 2 ;
823
+ case Leading .MULTIPLIED :
824
+ float fontSize = (float )this .getPropertyAsFloat (Property .FONT_SIZE , 0f );
825
+ // TODO contains image to be removed
826
+ float textAscent = maxTextAscent == 0 && maxTextDescent == 0 && !containsImage () ? fontSize * 0.8f : maxTextAscent ;
827
+ float textDescent = maxTextAscent == 0 && maxTextDescent == 0 && !containsImage () ? -fontSize * 0.2f : maxTextDescent ;
828
+ return Math .max (-textDescent + ((textAscent - textDescent ) * (leading .getValue () - 1 )) / 2 , -maxBlockDescent ) + maxDescent ;
829
+ default :
830
+ throw new IllegalStateException ();
831
+ }
725
832
}
726
833
727
834
private LineRenderer [] splitNotFittingFloat (int childPos , LayoutResult childResult ) {
@@ -966,6 +1073,9 @@ private void resolveChildrenFonts() {
966
1073
}
967
1074
}
968
1075
1076
+ private boolean isInlineBlockChild (IRenderer child ) {
1077
+ return child instanceof BlockRenderer || child instanceof TableRenderer ;
1078
+ }
969
1079
970
1080
static class RendererGlyph {
971
1081
public RendererGlyph (Glyph glyph , TextRenderer textRenderer ) {
0 commit comments