Skip to content

Commit 23f7573

Browse files
yulian-gaponenkoLodrKumquat
authored andcommitted
Support orphans/widows processing
DEVSIX-1858
1 parent 13c0680 commit 23f7573

21 files changed

+688
-37
lines changed

io/src/main/java/com/itextpdf/io/LogMessageConstant.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ public final class LogMessageConstant {
150150
public static final String ONE_OF_GROUPED_SOURCES_CLOSING_FAILED = "Closing of one of the grouped sources failed.";
151151
public static final String ONLY_ONE_OF_ARTBOX_OR_TRIMBOX_CAN_EXIST_IN_THE_PAGE = "Only one of artbox or trimbox can exist on the page. The trimbox will be deleted";
152152
public static final String OPENTYPE_GDEF_TABLE_ERROR = "OpenType GDEF table error: {0}";
153+
public static final String ORPHANS_CONSTRAINT_VIOLATED = "Orphans constraint violated for paragraph split at page {0}. Min number of orphans: {1}; actual: {2}. \nComment: {3}";
153154
public static final String PAGE_TREE_IS_BROKEN_FAILED_TO_RETRIEVE_PAGE = "Page tree is broken. Failed to retrieve page number {0}. Null will be returned.";
154155
public static final String PASSED_PAGE_SHALL_BE_ON_WHICH_CANVAS_WILL_BE_RENDERED = "The page passed to Canvas#enableAutoTagging(PdfPage) method shall be the one on which this canvas will be rendered. However the actual passed PdfPage instance sets not such page. This might lead to creation of malformed PDF document.";
155156
public static final String PATH_KEY_IS_PRESENT_VERTICES_WILL_BE_IGNORED = "Path key is present. Vertices will be ignored";
@@ -159,6 +160,7 @@ public final class LogMessageConstant {
159160
public static final String PDF_WRITER_CLOSING_FAILED = "PdfWriter closing failed due to the error occurred!";
160161
public static final String PNG_IMAGE_HAS_ICC_PROFILE_WITH_INCOMPATIBLE_NUMBER_OF_COLOR_COMPONENTS = "Png image has color profile with incompatible number of color components.";
161162
public static final String POPUP_ENTRY_IS_NOT_POPUP_ANNOTATION = "Popup entry in the markup annotations refers not to the annotation with Popup subtype.";
163+
public static final String PREMATURE_CALL_OF_HANDLE_VIOLATION_METHOD = "Premature call of handleViolation method.";
162164
public static final String PROPERTY_IN_PERCENTS_NOT_SUPPORTED = "Property {0} in percents is not supported";
163165
public static final String RECTANGLE_HAS_NEGATIVE_OR_ZERO_SIZES = "The {0} rectangle has negative or zero sizes. It will not be displayed.";
164166
public static final String RECTANGLE_HAS_NEGATIVE_SIZE = "The {0} rectangle has negative size. It will not be displayed.";
@@ -197,6 +199,7 @@ public final class LogMessageConstant {
197199
public static final String UNKNOWN_ERROR_WHILE_PROCESSING_CMAP = "Unknown error while processing CMap.";
198200
public static final String UNSUPPORTED_COLOR_IN_DA = "Unsupported color in FormField's DA";
199201
public static final String VERSION_INCOMPATIBILITY_FOR_DICTIONARY_ENTRY = "\"{0}\" entry in the \"{1}\" dictionary is a {2} and higher version feature. It is meaningless for the current {3} version.";
202+
public static final String WIDOWS_CONSTRAINT_VIOLATED = "Widows constraint violated for paragraph split at page {0}. Min number of widows: {1}; actual: {2}.\nComment: {3}";
200203
public static final String WRITER_ENCRYPTION_IS_IGNORED_APPEND = "Writer encryption will be ignored, because append mode is used. Document will preserve the original encryption (or will stay unencrypted)";
201204
public static final String WRITER_ENCRYPTION_IS_IGNORED_PRESERVE = "Writer encryption will be ignored, because preservation of encryption is enabled. Document will preserve the original encryption (or will stay unencrypted)";
202205
public static final String WRONG_MEDIABOX_SIZE_TOO_MANY_ARGUMENTS = "Wrong media box size: {0}. The arguments beyond the 4th will be ignored";

layout/src/main/java/com/itextpdf/layout/element/Paragraph.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ This file is part of the iText (R) project.
4747
import com.itextpdf.kernel.pdf.tagutils.DefaultAccessibilityProperties;
4848
import com.itextpdf.kernel.pdf.tagutils.AccessibilityProperties;
4949
import com.itextpdf.layout.property.Leading;
50+
import com.itextpdf.layout.property.ParagraphOrphansControl;
51+
import com.itextpdf.layout.property.ParagraphWidowsControl;
5052
import com.itextpdf.layout.property.Property;
5153
import com.itextpdf.layout.property.UnitValue;
5254
import com.itextpdf.layout.renderer.IRenderer;
@@ -198,6 +200,28 @@ public Paragraph setFirstLineIndent(float indent) {
198200
return this;
199201
}
200202

203+
/**
204+
* Sets orphans restriction on a {@link Paragraph}.
205+
*
206+
* @param orphansControl an instance of {@link ParagraphOrphansControl}.
207+
* @return this {@link Paragraph} instance.
208+
*/
209+
public Paragraph setOrphansControl(ParagraphOrphansControl orphansControl) {
210+
setProperty(Property.ORPHANS_CONTROL, orphansControl);
211+
return this;
212+
}
213+
214+
/**
215+
* Sets widows restriction on a {@link Paragraph}.
216+
*
217+
* @param widowsControl an instance of {@link ParagraphWidowsControl}.
218+
* @return this {@link Paragraph} instance.
219+
*/
220+
public Paragraph setWidowsControl(ParagraphWidowsControl widowsControl) {
221+
setProperty(Property.WIDOWS_CONTROL, widowsControl);
222+
return this;
223+
}
224+
201225
/**
202226
* Sets the leading value, using the {@link Leading#FIXED} strategy.
203227
*
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.itextpdf.layout.property;
2+
3+
import com.itextpdf.io.LogMessageConstant;
4+
import com.itextpdf.io.util.MessageFormatUtil;
5+
import com.itextpdf.layout.renderer.ParagraphRenderer;
6+
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
10+
/**
11+
* A specialized class holding configurable parameters related to {@link com.itextpdf.layout.element.Paragraph}'s
12+
* orphans restrictions. This class is meant to be used as the value for the {@link Property#ORPHANS_CONTROL} key.
13+
*/
14+
public class ParagraphOrphansControl {
15+
private int minOrphans;
16+
17+
/**
18+
* Creates a {@link ParagraphOrphansControl} instance with a specified orphans limitation.
19+
*
20+
* @param minOrphans minimal number of paragraph's lines to remain on an area before an area break.
21+
*/
22+
public ParagraphOrphansControl(int minOrphans) {
23+
this.minOrphans = minOrphans;
24+
}
25+
26+
/**
27+
* Sets parameter that defines orphans restrictions.
28+
*
29+
* @param minOrphans minimal number of paragraph's lines to remain on an area before an area break.
30+
* @return this {@link ParagraphOrphansControl} instance
31+
*/
32+
public ParagraphOrphansControl setMinAllowedOrphans(int minOrphans) {
33+
this.minOrphans = minOrphans;
34+
return this;
35+
}
36+
37+
/**
38+
* Gets minimal number of paragraph's lines to remain on an area before a split.
39+
*
40+
* @return minimal number of paragraph's lines to remain on an area before an area break.
41+
*/
42+
public int getMinOrphans() {
43+
return minOrphans;
44+
}
45+
46+
/**
47+
* Writes a log message reporting that orphans constraint is violated.
48+
*
49+
* This method is to be overridden if violation scenarios need to be handled in some other way.
50+
*
51+
* @param renderer a renderer processing orphans
52+
* @param message {@link String} explaining the reason for violation
53+
*/
54+
public void handleViolatedOrphans(ParagraphRenderer renderer, String message) {
55+
Logger logger = LoggerFactory.getLogger(ParagraphOrphansControl.class);
56+
if (renderer.getOccupiedArea() != null && renderer.getLines() != null) {
57+
int pageNumber = renderer.getOccupiedArea().getPageNumber();
58+
String warnText = MessageFormatUtil.format(LogMessageConstant.ORPHANS_CONSTRAINT_VIOLATED, pageNumber,
59+
minOrphans, renderer.getLines().size(), message);
60+
logger.warn(warnText);
61+
} else {
62+
logger.warn(LogMessageConstant.PREMATURE_CALL_OF_HANDLE_VIOLATION_METHOD);
63+
}
64+
}
65+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package com.itextpdf.layout.property;
2+
3+
import com.itextpdf.io.LogMessageConstant;
4+
import com.itextpdf.io.util.MessageFormatUtil;
5+
import com.itextpdf.layout.renderer.ParagraphRenderer;
6+
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
10+
/**
11+
* A specialized class holding configurable parameters related to {@link com.itextpdf.layout.element.Paragraph}'s
12+
* widows restrictions. This class is meant to be used as the value for the {@link Property#WIDOWS_CONTROL} key.
13+
*/
14+
public class ParagraphWidowsControl {
15+
private int minWidows;
16+
private int maxLinesToMove;
17+
private boolean overflowOnWidowsViolation;
18+
19+
/**
20+
* Creates a {@link ParagraphWidowsControl} instance with specified widows restrictions.
21+
*
22+
* @param minWidows minimal number of paragraph's lines to be overflowed to the next area.
23+
* @param maxLinesToMove a number of lines that are allowed to be moved to the next area
24+
* in order to fix widows constraint violation.
25+
* @param overflowParagraphOnViolation defines whether the entire paragraph should be pushed to the next area
26+
* if widows constraint is violated and cannot be automatically fixed.
27+
*/
28+
public ParagraphWidowsControl(int minWidows, int maxLinesToMove, boolean overflowParagraphOnViolation) {
29+
this.minWidows = minWidows;
30+
this.maxLinesToMove = maxLinesToMove;
31+
overflowOnWidowsViolation = overflowParagraphOnViolation;
32+
}
33+
34+
/**
35+
* Sets parameters that define widows restrictions and conditions of handling cases of widows constraint violation.
36+
*
37+
* @param minWidows minimal number of paragraph's lines to be overflowed to the next area.
38+
* @param maxLinesToMove a number of lines that are allowed to be moved to the next area
39+
* in order to fix widows constraint violation.
40+
* @param overflowParagraphOnViolation defines whether paragraph should be completely pushed to the next area
41+
* if widows constraint is violated and cannot be automatically fixed.
42+
* @return this {@link ParagraphWidowsControl} instance.
43+
*/
44+
public ParagraphWidowsControl setMinAllowedWidows(int minWidows, int maxLinesToMove,
45+
boolean overflowParagraphOnViolation) {
46+
this.minWidows = minWidows;
47+
this.maxLinesToMove = maxLinesToMove;
48+
overflowOnWidowsViolation = overflowParagraphOnViolation;
49+
return this;
50+
}
51+
52+
/**
53+
* Gets minimal number of paragraph's lines to be overflowed to the next area.
54+
*
55+
* @return minimal number of paragraph's lines to be overflowed to the next area
56+
*/
57+
public int getMinWidows() {
58+
return minWidows;
59+
}
60+
61+
/**
62+
* Gets a number of lines that are allowed to be moved to the next area in order to fix
63+
* widows constraint violation.
64+
*
65+
* @return a number of lines that are allowed to be moved to the next are
66+
*/
67+
public int getMaxLinesToMove() {
68+
return maxLinesToMove;
69+
}
70+
71+
/**
72+
* Indicates whether paragraph should be completely pushed to the next area if widows constraint is violated and
73+
* cannot be automatically fixed.
74+
*
75+
* @return true if paragraph should be completely pushed to the next area if widows constraint is violated and
76+
* cannot be automatically fixed, otherwise - false
77+
*/
78+
public boolean isOverflowOnWidowsViolation() {
79+
return overflowOnWidowsViolation;
80+
}
81+
82+
/**
83+
* Writes a log message reporting that widows constraint is violated and cannot be automatically fixed.
84+
*
85+
* This method is to be overridden if violation scenarios need to be handled in some other way.
86+
*
87+
* @param widowsRenderer a renderer processing widows
88+
* @param message {@link String} explaining the reason for violation
89+
*/
90+
public void handleViolatedWidows(ParagraphRenderer widowsRenderer, String message) {
91+
Logger logger = LoggerFactory.getLogger(ParagraphWidowsControl.class);
92+
if (widowsRenderer.getOccupiedArea() != null && widowsRenderer.getLines() != null) {
93+
int pageNumber = widowsRenderer.getOccupiedArea().getPageNumber();
94+
String warnText = MessageFormatUtil.format(LogMessageConstant.WIDOWS_CONSTRAINT_VIOLATED,
95+
pageNumber, minWidows, widowsRenderer.getLines().size(), message);
96+
logger.warn(warnText);
97+
} else {
98+
logger.warn(LogMessageConstant.PREMATURE_CALL_OF_HANDLE_VIOLATION_METHOD);
99+
}
100+
}
101+
}

layout/src/main/java/com/itextpdf/layout/property/Property.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ private Property() {
157157
public static final int NO_SOFT_WRAP_INLINE = 118;
158158

159159
public static final int OPACITY = 92;
160+
public static final int ORPHANS_CONTROL = 121;
160161
public static final int OUTLINE = 106;
161162
public static final int OUTLINE_OFFSET = 107;
162163
/**
@@ -208,6 +209,7 @@ private Property() {
208209
* Value of 1 is equivalent to no scaling
209210
**/
210211
public static final int VERTICAL_SCALING = 76;
212+
public static final int WIDOWS_CONTROL = 122;
211213
public static final int WIDTH = 77;
212214
public static final int WORD_SPACING = 78;
213215

@@ -217,7 +219,7 @@ private Property() {
217219
* related to textual operations. Indicates whether or not this type of property is inheritable.
218220
*/
219221
private static final boolean[] INHERITED_PROPERTIES;
220-
private static final int MAX_INHERITED_PROPERTY_ID = 119;
222+
private static final int MAX_INHERITED_PROPERTY_ID = 122;
221223

222224
static {
223225
INHERITED_PROPERTIES = new boolean[MAX_INHERITED_PROPERTY_ID + 1];
@@ -244,6 +246,7 @@ private Property() {
244246
INHERITED_PROPERTIES[Property.KEEP_TOGETHER] = true;
245247
INHERITED_PROPERTIES[Property.LEADING] = true;
246248
INHERITED_PROPERTIES[Property.NO_SOFT_WRAP_INLINE] = true;
249+
INHERITED_PROPERTIES[Property.ORPHANS_CONTROL] = true;
247250
INHERITED_PROPERTIES[Property.SPACING_RATIO] = true;
248251
INHERITED_PROPERTIES[Property.SPLIT_CHARACTERS] = true;
249252
INHERITED_PROPERTIES[Property.STROKE_COLOR] = true;
@@ -252,6 +255,7 @@ private Property() {
252255
INHERITED_PROPERTIES[Property.TEXT_RENDERING_MODE] = true;
253256
INHERITED_PROPERTIES[Property.TEXT_RISE] = true;
254257
INHERITED_PROPERTIES[Property.UNDERLINE] = true;
258+
INHERITED_PROPERTIES[Property.WIDOWS_CONTROL] = true;
255259
INHERITED_PROPERTIES[Property.WORD_SPACING] = true;
256260
INHERITED_PROPERTIES[Property.TAGGING_HELPER] = true;
257261
INHERITED_PROPERTIES[Property.TYPOGRAPHY_CONFIG] = true;

0 commit comments

Comments
 (0)