Skip to content

Commit 75514e5

Browse files
author
dmitry.radchuk
committed
Add last line re-layout for correct leading check
DEVSIX-5359
1 parent ca32b23 commit 75514e5

File tree

5 files changed

+204
-3
lines changed

5 files changed

+204
-3
lines changed

layout/src/main/java/com/itextpdf/layout/renderer/ParagraphRenderer.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ protected LayoutResult directLayout(LayoutContext layoutContext) {
152152

153153
Float blockMaxHeight = retrieveMaxHeight();
154154
OverflowPropertyValue overflowY = (null == blockMaxHeight || blockMaxHeight > parentBBox.getHeight())
155-
&& !wasParentsHeightClipped
155+
&& !wasParentsHeightClipped
156156
? OverflowPropertyValue.FIT
157157
: this.<OverflowPropertyValue>getProperty(Property.OVERFLOW_Y);
158158

@@ -220,6 +220,7 @@ protected LayoutResult directLayout(LayoutContext layoutContext) {
220220
marginsCollapseHandler.startChildMarginsHandling(null, layoutBox);
221221
}
222222
boolean includeFloatsInOccupiedArea = BlockFormattingContextUtil.isRendererCreateBfc(this);
223+
223224
while (currentRenderer != null) {
224225
currentRenderer.setProperty(Property.TAB_DEFAULT, this.getPropertyAsFloat(Property.TAB_DEFAULT));
225226
currentRenderer.setProperty(Property.TAB_STOPS, this.<Object>getProperty(Property.TAB_STOPS));
@@ -234,8 +235,19 @@ protected LayoutResult directLayout(LayoutContext layoutContext) {
234235
.setTextIndent(lineIndent)
235236
.setFloatOverflowedToNextPageWithNothing(floatOverflowedToNextPageWithNothing);
236237
LineLayoutResult result = (LineLayoutResult)((LineRenderer) currentRenderer.setParent(this)).layout(lineLayoutContext);
238+
boolean isLastLineReLaidOut = false;
237239

238240
if (result.getStatus() == LayoutResult.NOTHING) {
241+
//Re-layout last line if it doesn't fit
242+
if (layoutContext.isClippedHeight()) {
243+
OverflowPropertyValue previousOverflowProperty = currentRenderer.<OverflowPropertyValue>getProperty(Property.OVERFLOW_Y);
244+
currentRenderer.setProperty(Property.OVERFLOW_Y, OverflowPropertyValue.VISIBLE);
245+
lineLayoutContext.setClippedHeight(true);
246+
result = (LineLayoutResult) ((LineRenderer) currentRenderer.setParent(this)).layout(lineLayoutContext);
247+
currentRenderer.setProperty(Property.OVERFLOW_Y, previousOverflowProperty);
248+
isLastLineReLaidOut = true;
249+
}
250+
239251
Float lineShiftUnderFloats = FloatingHelper.calculateLineShiftUnderFloats(floatRendererAreas, layoutBox);
240252
if (lineShiftUnderFloats != null) {
241253
layoutBox.decreaseHeight((float) lineShiftUnderFloats);
@@ -306,7 +318,12 @@ protected LayoutResult directLayout(LayoutContext layoutContext) {
306318
if (firstLineInBox) {
307319
deltaY = processedRenderer != null && leading != null ? -processedRenderer.getTopLeadingIndent(leading) : 0;
308320
}
309-
isFit = leading == null || processedRenderer.getOccupiedArea().getBBox().getY() + deltaY >= layoutBox.getY();
321+
322+
if (isLastLineReLaidOut) {
323+
isFit = leading == null || processedRenderer.getOccupiedArea().getBBox().getY() + deltaY - lastLineBottomLeadingIndent >= layoutBox.getY();
324+
} else {
325+
isFit = leading == null || processedRenderer.getOccupiedArea().getBBox().getY() + deltaY >= layoutBox.getY();
326+
}
310327
}
311328

312329
if (!isFit && (null == processedRenderer || isOverflowFit(overflowY))) {
@@ -712,7 +729,7 @@ private void alignStaticKids(LineRenderer renderer, float dxRight) {
712729
}
713730

714731
private void applyTextAlignment(TextAlignment textAlignment, LineLayoutResult result, LineRenderer processedRenderer,
715-
Rectangle layoutBox, List<Rectangle> floatRendererAreas, boolean onlyOverflowedFloatsLeft, float lineIndent) {
732+
Rectangle layoutBox, List<Rectangle> floatRendererAreas, boolean onlyOverflowedFloatsLeft, float lineIndent) {
716733
if (textAlignment == TextAlignment.JUSTIFIED && result.getStatus() == LayoutResult.PARTIAL && !result.isSplitForcedByNewline() && !onlyOverflowedFloatsLeft ||
717734
textAlignment == TextAlignment.JUSTIFIED_ALL) {
718735
if (processedRenderer != null) {
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package com.itextpdf.layout;
2+
3+
import com.itextpdf.io.logs.IoLogMessageConstant;
4+
import com.itextpdf.kernel.colors.ColorConstants;
5+
import com.itextpdf.kernel.geom.PageSize;
6+
import com.itextpdf.kernel.geom.Rectangle;
7+
import com.itextpdf.kernel.pdf.PdfDocument;
8+
import com.itextpdf.kernel.pdf.PdfWriter;
9+
import com.itextpdf.kernel.utils.CompareTool;
10+
import com.itextpdf.layout.borders.Border;
11+
import com.itextpdf.layout.element.Cell;
12+
import com.itextpdf.layout.element.Paragraph;
13+
import com.itextpdf.layout.element.Table;
14+
import com.itextpdf.layout.element.Text;
15+
import com.itextpdf.layout.layout.LayoutArea;
16+
import com.itextpdf.layout.layout.LayoutContext;
17+
import com.itextpdf.layout.layout.LayoutResult;
18+
import com.itextpdf.layout.properties.OverflowPropertyValue;
19+
import com.itextpdf.layout.properties.Property;
20+
import com.itextpdf.layout.properties.TextAlignment;
21+
import com.itextpdf.test.ExtendedITextTest;
22+
import com.itextpdf.test.annotations.LogMessage;
23+
import com.itextpdf.test.annotations.LogMessages;
24+
import com.itextpdf.test.annotations.type.IntegrationTest;
25+
26+
import java.io.IOException;
27+
import org.junit.Assert;
28+
import org.junit.BeforeClass;
29+
import org.junit.Test;
30+
import org.junit.experimental.categories.Category;
31+
32+
@Category(IntegrationTest.class)
33+
public class LeadingHeightTest extends ExtendedITextTest {
34+
35+
public static final String SOURCE_FOLDER = "./src/test/resources/com/itextpdf/layout/LeadingHeightTest/";
36+
public static final String DESTINATION_FOLDER = "./target/test/com/itextpdf/layout/LeadingHeightTest/";
37+
38+
@BeforeClass
39+
public static void beforeClass() {
40+
createOrClearDestinationFolder(DESTINATION_FOLDER);
41+
}
42+
43+
@LogMessages(messages = {@LogMessage(messageTemplate = IoLogMessageConstant.CLIP_ELEMENT, count = 2)})
44+
@Test
45+
public void clippedHeightParagraphTest() throws IOException, InterruptedException {
46+
String outPdf = DESTINATION_FOLDER + "leadingTestHeight.pdf";
47+
String cmpPdf = SOURCE_FOLDER + "cmp_leadingTestHeight.pdf";
48+
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outPdf));
49+
Document doc = new Document(pdfDoc, new PageSize(700, 700));
50+
// This is how table looks like if no height property is set
51+
addTable(doc, 504, "RETIREMENT PLANNING: BECAUSE YOU CAN’T BE A FINANCIAL PLANNER FOREVER.", -1);
52+
// Here we set value from pre layout as height. We expect that this table shall be equal to the previous one
53+
addTable(doc, 360, "RETIREMENT PLANNING: BECAUSE YOU CAN’T BE A FINANCIAL PLANNER FOREVER.", 0);
54+
// Here we set 100 as height. We expect that this will be enough and all text will be placed
55+
addTable(doc, 216, "RETIREMENT PLANNING: BECAUSE YOU CAN’T BE A FINANCIAL PLANNER FOREVER.", 100);
56+
// Here we set 100 as height. We expect that this will be enough to place 3 lines
57+
addTable(doc, 216, "RETIREMENT PLANNING: BECAUSE ***SOME TEST TEXT IS PLACED*** YOU CAN’T BE A FINANCIAL PLANNER FOREVER.", 100);
58+
// Here we set value from pre layout minus 0.5f as height. We expect that this table shall not be equal to the previous one
59+
addTable(doc, 50, "RETIREMENT PLANNING: BECAUSE YOU CAN’T BE A FINANCIAL PLANNER FOREVER.", -2);
60+
doc.close();
61+
62+
Assert.assertNull(new CompareTool().compareByContent(outPdf, cmpPdf, DESTINATION_FOLDER));
63+
}
64+
65+
@Test
66+
public void pageHeightParagraphTest() throws IOException, InterruptedException {
67+
String outPdf = DESTINATION_FOLDER + "pageHeightParagraphTest.pdf";
68+
String cmpPdf = SOURCE_FOLDER + "cmp_pageHeightParagraphTest.pdf";
69+
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outPdf));
70+
//176 = 104 + 36 + 36 (page margins)
71+
Document doc = new Document(pdfDoc, new PageSize(700, 176));
72+
Paragraph ph = new Paragraph();
73+
Text txt = new Text("RETIREMENT PLANNING: BECAUSE YOU CAN’T BE A FINANCIAL PLANNER FOREVER.");
74+
txt.setFontSize(32f);
75+
ph.add(txt);
76+
ph.setFixedLeading(32f);
77+
ph.setPaddingTop(0f);
78+
ph.setPaddingBottom(0f);
79+
ph.setWidth(585f);
80+
81+
doc.add(ph);
82+
doc.close();
83+
Assert.assertNull(new CompareTool().compareByContent(outPdf, cmpPdf, DESTINATION_FOLDER));
84+
}
85+
86+
@Test
87+
public void pageHeightParagraphWithWithWorkaroundTest() throws IOException, InterruptedException {
88+
String outPdf = DESTINATION_FOLDER + "pageHeightParagraphWorkAroundTest.pdf";
89+
String cmpPdf = SOURCE_FOLDER + "cmp_pageHeightParagraphWorkAroundTest.pdf";
90+
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outPdf));
91+
//176 = 104 + 36 + 36 (page margins)
92+
Document doc = new Document(pdfDoc, new PageSize(700, 176));
93+
Paragraph ph = new Paragraph();
94+
Text txt = new Text("RETIREMENT PLANNING: BECAUSE YOU CAN’T BE A FINANCIAL PLANNER FOREVER.");
95+
txt.setFontSize(32f);
96+
ph.add(txt);
97+
ph.setFixedLeading(32f);
98+
ph.setPaddingTop(0f);
99+
ph.setPaddingBottom(0f);
100+
ph.setWidth(585f);
101+
102+
103+
Paragraph ph2 = new Paragraph();
104+
ph2.setHeight(104);
105+
ph2.setMargin(0);
106+
ph2.setPadding(0);
107+
ph2.add(ph);
108+
ph2.setProperty(Property.OVERFLOW_Y, OverflowPropertyValue.VISIBLE);;
109+
doc.add(ph2);
110+
doc.close();
111+
Assert.assertNull(new CompareTool().compareByContent(outPdf, cmpPdf, DESTINATION_FOLDER));
112+
}
113+
114+
public void addTable(Document doc, int y, String text, int heightParam)
115+
{
116+
float width = 585f;
117+
float fontSize = 32f;
118+
119+
Table table = new Table(1);
120+
table.setWidth(width);
121+
table.setFixedLayout();
122+
123+
Paragraph ph = new Paragraph();
124+
Text txt = new Text(text);
125+
txt.setFontSize(fontSize);
126+
ph.add(txt);
127+
ph.setFixedLeading(fontSize);
128+
129+
Cell cell = new Cell();
130+
cell.setPaddingTop(0f);
131+
cell.setPaddingBottom(0f);
132+
cell.add(ph);
133+
cell.setBackgroundColor(ColorConstants.LIGHT_GRAY);
134+
cell.setBorder(null);
135+
136+
table.addCell(cell);
137+
138+
// find out how tall the cell is we just added
139+
LayoutResult result = table.createRendererSubTree()
140+
.setParent(doc.getRenderer())
141+
.layout(
142+
new LayoutContext(
143+
new LayoutArea(
144+
1,
145+
new Rectangle(0, 0, width, 10000.0F)
146+
)
147+
)
148+
);
149+
150+
String heightStr = "Natural";
151+
if (heightParam == -2) {
152+
float rowHeight = result.getOccupiedArea().getBBox().getHeight();
153+
cell.setHeight(rowHeight - 0.5f);
154+
heightStr = "Calculated " + (rowHeight - 0.5f);
155+
}
156+
if (heightParam == 0)
157+
{
158+
float rowHeight = result.getOccupiedArea().getBBox().getHeight();
159+
cell.setHeight(rowHeight + 1f);
160+
heightStr = "Calculated " + rowHeight;
161+
}
162+
else if (heightParam > 0)
163+
{
164+
cell.setHeight(heightParam);
165+
heightStr = "Explicit " + heightParam;
166+
}
167+
168+
table.setFixedPosition((float) 36, (float) y, width);
169+
170+
doc.add(table);
171+
172+
Table t2 = new Table(1);
173+
t2.setWidth(width);
174+
t2.setFixedLayout();
175+
Cell c2 = new Cell();
176+
c2.setTextAlignment(TextAlignment.CENTER);
177+
c2.setWidth(width);
178+
c2.setBorder(Border.NO_BORDER);
179+
c2.add(new Paragraph("Row Height: " + heightStr));
180+
t2.addCell(c2);
181+
t2.setFixedPosition((float) 36, (float) y-18, width);
182+
doc.add(t2);
183+
}
184+
}

0 commit comments

Comments
 (0)