Skip to content

Commit a52e878

Browse files
committed
Support column-rule-width column-rule-style and column-rule-color
DEVSIX-7562
1 parent 20e68e9 commit a52e878

File tree

10 files changed

+196
-23
lines changed

10 files changed

+196
-23
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public final class Property {
7070
public static final int COLUMN_COUNT = 138;
7171
public static final int COLUMN_WIDTH = 142;
7272
public static final int COLUMN_GAP = 143;
73+
public static final int COLUMN_GAP_BORDER = 144;
7374
public static final int DESTINATION = 17;
7475
public static final int FILL_AVAILABLE_AREA = 86;
7576
public static final int FILL_AVAILABLE_AREA_ON_SPLIT = 87;
@@ -218,7 +219,7 @@ public final class Property {
218219
* related to textual operations. Indicates whether or not this type of property is inheritable.
219220
*/
220221
private static final boolean[] INHERITED_PROPERTIES;
221-
private static final int MAX_INHERITED_PROPERTY_ID = 143;
222+
private static final int MAX_INHERITED_PROPERTY_ID = 144;
222223

223224
static {
224225
INHERITED_PROPERTIES = new boolean[MAX_INHERITED_PROPERTY_ID + 1];

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

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ This file is part of the iText (R) project.
2323
package com.itextpdf.layout.renderer;
2424

2525
import com.itextpdf.kernel.geom.Rectangle;
26+
import com.itextpdf.kernel.pdf.canvas.CanvasArtifact;
27+
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
2628
import com.itextpdf.layout.borders.Border;
29+
import com.itextpdf.layout.borders.Border.Side;
2730
import com.itextpdf.layout.element.MulticolContainer;
2831
import com.itextpdf.layout.exceptions.LayoutExceptionMessageConstant;
2932
import com.itextpdf.layout.layout.LayoutArea;
@@ -36,6 +39,7 @@ This file is part of the iText (R) project.
3639

3740
import java.util.ArrayList;
3841
import java.util.List;
42+
import java.util.function.Consumer;
3943

4044
/**
4145
* Represents a renderer for columns.
@@ -120,6 +124,43 @@ public IRenderer getNextRenderer() {
120124
return new MulticolRenderer((MulticolContainer) modelElement);
121125
}
122126

127+
128+
/**
129+
* Performs the drawing operation for the border of this renderer, if
130+
* defined by any of the {@link Property#BORDER} values in either the layout
131+
* element or this {@link IRenderer} itself.
132+
*
133+
* @param drawContext the context (canvas, document, etc) of this drawing operation.
134+
*/
135+
@Override
136+
public void drawBorder(DrawContext drawContext) {
137+
super.drawBorder(drawContext);
138+
139+
Rectangle borderRect = applyMargins(occupiedArea.getBBox().clone(), getMargins(), false);
140+
boolean isAreaClipped = clipBorderArea(drawContext, borderRect);
141+
Border gap = this.<Border>getProperty(Property.COLUMN_GAP_BORDER);
142+
if (getChildRenderers().isEmpty() || gap == null || gap.getWidth() <= ZERO_DELTA) {
143+
return;
144+
}
145+
146+
drawTaggedWhenNeeded(drawContext, canvas -> {
147+
for (int i = 0; i < getChildRenderers().size() - 1; ++i) {
148+
Rectangle columnBBox = getChildRenderers().get(i).getOccupiedArea().getBBox();
149+
Rectangle columnSpaceBBox = new Rectangle(columnBBox.getX() + columnBBox.getWidth(), columnBBox.getY(),
150+
columnGap, columnBBox.getHeight());
151+
float x1 = columnSpaceBBox.getX() + columnSpaceBBox.getWidth() / 2 + gap.getWidth() / 2;
152+
float y1 = columnSpaceBBox.getY();
153+
float y2 = columnSpaceBBox.getY() + columnSpaceBBox.getHeight();
154+
gap.draw(canvas, x1, y1, x1, y2, Side.RIGHT, 0, 0);
155+
}
156+
if (isAreaClipped) {
157+
drawContext.getCanvas().restoreState();
158+
}
159+
});
160+
161+
162+
}
163+
123164
protected MulticolLayoutResult layoutInColumns(LayoutContext layoutContext, Rectangle actualBBox) {
124165
LayoutResult inifiniteHeighOneColumnLayoutResult = elementRenderer.layout(
125166
new LayoutContext(new LayoutArea(1, new Rectangle(columnWidth, INF))));
@@ -180,6 +221,16 @@ private void setOverflowForAllChildren(IRenderer renderer) {
180221
setOverflowForAllChildren(child);
181222
}
182223
}
224+
private void drawTaggedWhenNeeded(DrawContext drawContext, Consumer<PdfCanvas> action) {
225+
PdfCanvas canvas = drawContext.getCanvas();
226+
if (drawContext.isTaggingEnabled()) {
227+
canvas.openTag(new CanvasArtifact());
228+
}
229+
action.accept(canvas);
230+
if (drawContext.isTaggingEnabled()) {
231+
canvas.closeTag();
232+
}
233+
}
183234

184235
private void applyWidth(Rectangle parentBbox, float originalWidth) {
185236
final Float blockWidth = retrieveWidth(originalWidth);
@@ -306,7 +357,7 @@ private void calculateColumnCountAndWidth(float initialWidth) {
306357
this.columnCount = 1;
307358
}
308359
}
309-
this.columnWidth = Math.max(0.0f, ((initialWidth + this.columnGap)/this.columnCount - this.columnGap));
360+
this.columnWidth = Math.max(0.0f, ((initialWidth + this.columnGap) / this.columnCount - this.columnGap));
310361
}
311362

312363
private void clearOverFlowRendererIfNeeded(MulticolLayoutResult result) {

layout/src/test/java/com/itextpdf/layout/element/MulticolContainerTest.java

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,93 @@ public void paragraphWithColumnCountAndGapTest() throws IOException, Interrupted
10891089
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, DESTINATION_FOLDER, "diff"));
10901090
}
10911091

1092+
@Test
1093+
public void paragraphWithSimpleSolidColumnGapTest() throws IOException, InterruptedException {
1094+
String outFileName = DESTINATION_FOLDER + "paragraphWithSimpleStyledColumnGapTest.pdf";
1095+
String cmpFileName = SOURCE_FOLDER + "cmp_paragraphWithSimpleStyledColumnGapTest.pdf";
1096+
1097+
try (Document document = new Document(new PdfDocument(new PdfWriter(outFileName)))) {
1098+
Div columnContainer = new MulticolContainer();
1099+
columnContainer.setProperty(Property.COLUMN_COUNT, 5);
1100+
columnContainer.setProperty(Property.COLUMN_GAP, 50.0f);
1101+
columnContainer.setProperty(Property.COLUMN_GAP_BORDER, new SolidBorder(50));
1102+
Paragraph paragraph = createDummyParagraph();
1103+
columnContainer.add(paragraph);
1104+
document.add(columnContainer);
1105+
}
1106+
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, DESTINATION_FOLDER, "diff"));
1107+
}
1108+
1109+
1110+
@Test
1111+
public void divWithSimpleSolidColumnGapTest() throws IOException, InterruptedException {
1112+
String outFileName = DESTINATION_FOLDER + "divWithSimpleStyledColumnGapTest.pdf";
1113+
String cmpFileName = SOURCE_FOLDER + "cmp_divWithSimpleStyledColumnGapTest.pdf";
1114+
1115+
try (Document document = new Document(new PdfDocument(new PdfWriter(outFileName)))) {
1116+
Div columnContainer = new MulticolContainer();
1117+
columnContainer.setProperty(Property.COLUMN_COUNT, 5);
1118+
columnContainer.setProperty(Property.COLUMN_GAP, 50.0f);
1119+
columnContainer.setProperty(Property.COLUMN_GAP_BORDER, new SolidBorder(50));
1120+
Div div = new Div();
1121+
for (int i = 0; i < 20; i++) {
1122+
Paragraph paragraph = new Paragraph("Hello world! " + i);
1123+
div.add(paragraph);
1124+
}
1125+
columnContainer.add(div);
1126+
document.add(columnContainer);
1127+
}
1128+
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, DESTINATION_FOLDER, "diff"));
1129+
}
1130+
1131+
@Test
1132+
public void paragraphWithNegativeValueSolidColumnGapTest() throws IOException, InterruptedException {
1133+
String outFileName = DESTINATION_FOLDER + "paragraphWithNegativeValueSolidColumnGapTest.pdf";
1134+
String cmpFileName = SOURCE_FOLDER + "cmp_paragraphWithNegativeValueSolidColumnGapTest.pdf";
10921135

1136+
try (Document document = new Document(new PdfDocument(new PdfWriter(outFileName)))) {
1137+
Div columnContainer = new MulticolContainer();
1138+
columnContainer.setProperty(Property.COLUMN_COUNT, 5);
1139+
columnContainer.setProperty(Property.COLUMN_GAP_BORDER, new SolidBorder(0));
1140+
Paragraph paragraph = createDummyParagraph();
1141+
columnContainer.add(paragraph);
1142+
document.add(columnContainer);
1143+
}
1144+
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, DESTINATION_FOLDER, "diff"));
1145+
}
1146+
1147+
@Test
1148+
public void paragraphWithBiggerValueSolidColumnGapTest() throws IOException, InterruptedException {
1149+
String outFileName = DESTINATION_FOLDER + "paragraphWithBiggerValueSolidColumnGapTest.pdf";
1150+
String cmpFileName = SOURCE_FOLDER + "cmp_paragraphWithBiggerValueSolidColumnGapTest.pdf";
1151+
1152+
try (Document document = new Document(new PdfDocument(new PdfWriter(outFileName)))) {
1153+
Div columnContainer = new MulticolContainer();
1154+
columnContainer.setProperty(Property.COLUMN_COUNT, 5);
1155+
columnContainer.setProperty(Property.COLUMN_GAP_BORDER, new SolidBorder(600));
1156+
Paragraph paragraph = createDummyParagraph();
1157+
columnContainer.add(paragraph);
1158+
document.add(columnContainer);
1159+
}
1160+
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, DESTINATION_FOLDER, "diff"));
1161+
}
1162+
1163+
1164+
@Test
1165+
public void paragraphWithNullValueSolidColumnGapTest() throws IOException, InterruptedException {
1166+
String outFileName = DESTINATION_FOLDER + "paragraphWithNullValueSolidColumnGapTest.pdf";
1167+
String cmpFileName = SOURCE_FOLDER + "cmp_paragraphWithNullValueSolidColumnGapTest.pdf";
1168+
1169+
try (Document document = new Document(new PdfDocument(new PdfWriter(outFileName)))) {
1170+
Div columnContainer = new MulticolContainer();
1171+
columnContainer.setProperty(Property.COLUMN_COUNT, 5);
1172+
columnContainer.setProperty(Property.COLUMN_GAP_BORDER, null);
1173+
Paragraph paragraph = createDummyParagraph();
1174+
columnContainer.add(paragraph);
1175+
document.add(columnContainer);
1176+
}
1177+
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, DESTINATION_FOLDER, "diff"));
1178+
}
10931179
private void executeTest(String testName, Consumer<MulticolContainer> executor, boolean wrapByP)
10941180
throws IOException, InterruptedException {
10951181
String filename = DESTINATION_FOLDER + testName + ".pdf";

styled-xml-parser/src/main/java/com/itextpdf/styledxmlparser/css/CommonCssConstants.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,20 @@ public class CommonCssConstants {
304304
*/
305305
public static final String COLUMN_GAP = "column-gap";
306306

307+
/**
308+
* The Constant COLUMN_RULE_WIDTH.
309+
*/
310+
public static final String COLUMN_RULE_WIDTH = "column-rule-width";
311+
312+
/**
313+
* The Constant COLUMN_RULE_STYLE.
314+
*/
315+
public static final String COLUMN_RULE_STYLE = "column-rule-style";
316+
317+
/**
318+
* The Constant COLUMN_RULE_COLOR.
319+
*/
320+
public static final String COLUMN_RULE_COLOR = "column-rule-color";
307321
/**
308322
* The Constant COLUMNS
309323
*/

styled-xml-parser/src/main/java/com/itextpdf/styledxmlparser/css/validate/impl/CssDefaultValidator.java

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ This file is part of the iText (R) project.
4242
import java.util.Arrays;
4343
import java.util.HashMap;
4444
import java.util.Map;
45+
import static com.itextpdf.styledxmlparser.css.CommonCssConstants.BORDER_WIDTH_VALUES;
4546

4647
/**
4748
* Class that bundles all the CSS declaration validators.
@@ -53,11 +54,12 @@ public class CssDefaultValidator implements ICssDeclarationValidator {
5354
*/
5455
protected final Map<String, ICssDeclarationValidator> defaultValidators;
5556

57+
private static final ICssDeclarationValidator colorCommonValidator = new MultiTypeDeclarationValidator(
58+
new CssEnumValidator(CommonCssConstants.TRANSPARENT, CommonCssConstants.INITIAL,
59+
CommonCssConstants.INHERIT, CommonCssConstants.CURRENTCOLOR),
60+
new CssColorValidator());
61+
5662
public CssDefaultValidator() {
57-
ICssDeclarationValidator colorCommonValidator = new MultiTypeDeclarationValidator(
58-
new CssEnumValidator(CommonCssConstants.TRANSPARENT, CommonCssConstants.INITIAL,
59-
CommonCssConstants.INHERIT, CommonCssConstants.CURRENTCOLOR),
60-
new CssColorValidator());
6163
final CssEnumValidator normalValidator = new CssEnumValidator(CommonCssConstants.NORMAL);
6264
final CssEnumValidator relativeSizeValidator =
6365
new CssEnumValidator(CommonCssConstants.LARGER, CommonCssConstants.SMALLER);
@@ -97,23 +99,26 @@ public CssDefaultValidator() {
9799
defaultValidators.put(CommonCssConstants.FONT_SIZE, new MultiTypeDeclarationValidator(
98100
new CssLengthValueValidator(false), new CssPercentageValueValidator(false), relativeSizeValidator,
99101
absoluteSizeValidator));
100-
defaultValidators.put(CommonCssConstants.WORD_SPACING, new MultiTypeDeclarationValidator(
101-
new CssLengthValueValidator(true), normalValidator));
102-
defaultValidators.put(CommonCssConstants.LETTER_SPACING, new MultiTypeDeclarationValidator(
103-
new CssLengthValueValidator(true), normalValidator));
104-
defaultValidators.put(CommonCssConstants.TEXT_INDENT, new MultiTypeDeclarationValidator(
105-
new CssLengthValueValidator(true), new CssPercentageValueValidator(true),
106-
new CssEnumValidator(CommonCssConstants.EACH_LINE, CommonCssConstants.HANGING,
107-
CommonCssConstants.HANGING + " " + CommonCssConstants.EACH_LINE)));
108-
defaultValidators.put(CommonCssConstants.LINE_HEIGHT, new MultiTypeDeclarationValidator(
109-
new CssNumberValueValidator(false), new CssLengthValueValidator(false),
110-
new CssPercentageValueValidator(false),
111-
normalValidator, inheritInitialUnsetValidator));
112-
defaultValidators.put(CommonCssConstants.COLUMN_GAP, new MultiTypeDeclarationValidator(
113-
new CssLengthValueValidator(false), new CssPercentageValueValidator(false), normalValidator));
114-
defaultValidators.put(CommonCssConstants.COLUMN_WIDTH, new MultiTypeDeclarationValidator(
115-
new CssLengthValueValidator(false), new CssPercentageValueValidator(false),
116-
new CssEnumValidator(CommonCssConstants.AUTO)));
102+
defaultValidators.put(CommonCssConstants.WORD_SPACING,
103+
new MultiTypeDeclarationValidator(new CssLengthValueValidator(true), normalValidator));
104+
defaultValidators.put(CommonCssConstants.LETTER_SPACING,
105+
new MultiTypeDeclarationValidator(new CssLengthValueValidator(true), normalValidator));
106+
defaultValidators.put(CommonCssConstants.TEXT_INDENT,
107+
new MultiTypeDeclarationValidator(new CssLengthValueValidator(true),
108+
new CssPercentageValueValidator(true),
109+
new CssEnumValidator(CommonCssConstants.EACH_LINE, CommonCssConstants.HANGING,
110+
CommonCssConstants.HANGING + " " + CommonCssConstants.EACH_LINE)));
111+
addColumnRuleValidation(defaultValidators);
112+
defaultValidators.put(CommonCssConstants.LINE_HEIGHT,
113+
new MultiTypeDeclarationValidator(new CssNumberValueValidator(false),
114+
new CssLengthValueValidator(false), new CssPercentageValueValidator(false), normalValidator,
115+
inheritInitialUnsetValidator));
116+
defaultValidators.put(CommonCssConstants.COLUMN_GAP,
117+
new MultiTypeDeclarationValidator(new CssLengthValueValidator(false),
118+
new CssPercentageValueValidator(false), normalValidator));
119+
defaultValidators.put(CommonCssConstants.COLUMN_WIDTH,
120+
new MultiTypeDeclarationValidator(new CssLengthValueValidator(false),
121+
new CssPercentageValueValidator(false), new CssEnumValidator(CommonCssConstants.AUTO)));
117122
defaultValidators.put(CommonCssConstants.COLUMN_COUNT, new MultiTypeDeclarationValidator(
118123
new CssIntegerNumberValueValidator(false, false), new CssEnumValidator(CommonCssConstants.AUTO)));
119124
defaultValidators.put(CommonCssConstants.ROW_GAP, new MultiTypeDeclarationValidator(
@@ -203,11 +208,27 @@ public CssDefaultValidator() {
203208
* Validates a CSS declaration.
204209
*
205210
* @param declaration the CSS declaration
211+
*
206212
* @return true, if the validation was successful
207213
*/
208214
@Override
209215
public boolean isValid(CssDeclaration declaration) {
210216
ICssDeclarationValidator validator = defaultValidators.get(declaration.getProperty());
211217
return validator == null || validator.isValid(declaration);
212218
}
219+
220+
private static void addColumnRuleValidation(Map<String, ICssDeclarationValidator> container) {
221+
container.put(CommonCssConstants.COLUMN_RULE_COLOR, colorCommonValidator);
222+
container.put(CommonCssConstants.COLUMN_RULE_WIDTH,
223+
new MultiTypeDeclarationValidator(
224+
new CssNumberValueValidator(false),
225+
new CssLengthValueValidator(false),
226+
new CssEnumValidator(BORDER_WIDTH_VALUES),
227+
new CssEnumValidator(CommonCssConstants.AUTO)));
228+
container.put(CommonCssConstants.COLUMN_RULE_STYLE,
229+
new MultiTypeDeclarationValidator(
230+
new CssEnumValidator(CommonCssConstants.BORDER_STYLE_VALUES)
231+
));
232+
233+
}
213234
}

0 commit comments

Comments
 (0)