Skip to content

Commit 20e68e9

Browse files
committed
Improve property values handling
DEVSIX-7636
1 parent a26f6df commit 20e68e9

File tree

6 files changed

+316
-23
lines changed

6 files changed

+316
-23
lines changed

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

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -272,26 +272,39 @@ private MulticolLayoutResult balanceContentAndLayoutColumns(LayoutContext prelay
272272
return result;
273273
}
274274

275-
//algorithm is based on pseudo algorithm from https://www.w3.org/TR/css-multicol-1/#propdef-column-span
275+
// Algorithm is based on pseudo algorithm from https://www.w3.org/TR/css-multicol-1/#propdef-column-span
276276
private void calculateColumnCountAndWidth(float initialWidth) {
277-
final Integer columnCount = (Integer)this.<Integer>getProperty(Property.COLUMN_COUNT);
278-
final Float columnWidth = (Float)this.<Float>getProperty(Property.COLUMN_WIDTH);
279-
final Float columnGap = (Float)this.<Float>getProperty(Property.COLUMN_GAP);
280-
this.columnGap = columnGap != null ? columnGap.floatValue() : 0;
281-
if ((columnCount == null && columnWidth == null)
282-
|| (columnCount != null && columnCount.intValue() < 0)
283-
|| (columnWidth != null && columnWidth.floatValue() < 0)) {
277+
final Integer columnCountTemp = (Integer)this.<Integer>getProperty(Property.COLUMN_COUNT);
278+
final Float columnWidthTemp = (Float)this.<Float>getProperty(Property.COLUMN_WIDTH);
279+
280+
final Float columnGapTemp = (Float)this.<Float>getProperty(Property.COLUMN_GAP);
281+
this.columnGap = columnGapTemp == null ? 0f : columnGapTemp.floatValue();
282+
if ((columnCountTemp == null && columnWidthTemp == null)
283+
|| (columnCountTemp != null && columnCountTemp.intValue() < 0)
284+
|| (columnWidthTemp != null && columnWidthTemp.floatValue() < 0)
285+
|| (this.columnGap < 0)) {
286+
284287
throw new IllegalStateException(LayoutExceptionMessageConstant.INVALID_COLUMN_PROPERTIES);
285288
}
286-
if (columnWidth == null) {
287-
this.columnCount = columnCount.intValue();
288-
} else if (columnCount == null) {
289-
this.columnCount = Math.max(1, (int) Math.floor((double)((initialWidth + this.columnGap)
290-
/ (columnWidth.floatValue() + this.columnGap))));
289+
290+
if (columnWidthTemp == null) {
291+
this.columnCount = columnCountTemp.intValue();
292+
} else if (columnCountTemp == null) {
293+
final float columnWidthPlusGap = columnWidthTemp.floatValue() + this.columnGap;
294+
if (columnWidthPlusGap > ZERO_DELTA) {
295+
this.columnCount = Math.max(1,
296+
(int) Math.floor((double) ((initialWidth + this.columnGap) / columnWidthPlusGap)));
297+
} else {
298+
this.columnCount = 1;
299+
}
291300
} else {
292-
this.columnCount = Math.min((int) columnCount,
293-
Math.max(1, (int) Math.floor((double) ((initialWidth + this.columnGap)
294-
/ (columnWidth.floatValue() + this.columnGap)))));
301+
final float columnWidthPlusGap = columnWidthTemp.floatValue() + this.columnGap;
302+
if (columnWidthPlusGap > ZERO_DELTA) {
303+
this.columnCount = Math.min((int) columnCountTemp,
304+
Math.max(1, (int) Math.floor((double) ((initialWidth + this.columnGap) / columnWidthPlusGap))));
305+
} else {
306+
this.columnCount = 1;
307+
}
295308
}
296309
this.columnWidth = Math.max(0.0f, ((initialWidth + this.columnGap)/this.columnCount - this.columnGap));
297310
}

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,9 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.styledxmlparser.css.util;
2424

25-
import com.itextpdf.kernel.colors.WebColors;
2625
import com.itextpdf.styledxmlparser.css.CommonCssConstants;
2726
import com.itextpdf.styledxmlparser.css.CssDeclaration;
2827
import com.itextpdf.styledxmlparser.css.validate.CssDeclarationValidationMaster;
29-
import com.itextpdf.styledxmlparser.css.validate.impl.datatype.CssColorValidator;
3028

3129
import java.util.regex.Pattern;
3230

@@ -181,6 +179,17 @@ public static boolean isNumber(final String value) {
181179
|| value.matches("^[-+]?\\.\\d\\d*$"));
182180
}
183181

182+
/**
183+
* Checks whether a string matches an integer numeric value (e.g. 123, 23). All these metric values are allowed in
184+
* HTML/CSS.
185+
*
186+
* @param value the string that needs to be checked
187+
* @return boolean true if value contains an allowed metric value
188+
*/
189+
public static boolean isIntegerNumber(final String value) {
190+
return value != null && value.matches("^[-+]?\\d\\d*$");
191+
}
192+
184193
/**
185194
* Checks whether a string contains a percentage value
186195
*

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ This file is part of the iText (R) project.
3030
import com.itextpdf.styledxmlparser.css.validate.impl.datatype.CssBlendModeValidator;
3131
import com.itextpdf.styledxmlparser.css.validate.impl.datatype.CssColorValidator;
3232
import com.itextpdf.styledxmlparser.css.validate.impl.datatype.CssEnumValidator;
33+
import com.itextpdf.styledxmlparser.css.validate.impl.datatype.CssIntegerNumberValueValidator;
3334
import com.itextpdf.styledxmlparser.css.validate.impl.datatype.CssLengthValueValidator;
3435
import com.itextpdf.styledxmlparser.css.validate.impl.datatype.CssNumberValueValidator;
3536
import com.itextpdf.styledxmlparser.css.validate.impl.datatype.CssPercentageValueValidator;
@@ -109,14 +110,12 @@ public CssDefaultValidator() {
109110
new CssPercentageValueValidator(false),
110111
normalValidator, inheritInitialUnsetValidator));
111112
defaultValidators.put(CommonCssConstants.COLUMN_GAP, new MultiTypeDeclarationValidator(
112-
new CssLengthValueValidator(false), new CssPercentageValueValidator(false), normalValidator,
113-
inheritInitialUnsetValidator));
113+
new CssLengthValueValidator(false), new CssPercentageValueValidator(false), normalValidator));
114114
defaultValidators.put(CommonCssConstants.COLUMN_WIDTH, new MultiTypeDeclarationValidator(
115115
new CssLengthValueValidator(false), new CssPercentageValueValidator(false),
116-
new CssEnumValidator(CommonCssConstants.AUTO), inheritInitialUnsetValidator));
116+
new CssEnumValidator(CommonCssConstants.AUTO)));
117117
defaultValidators.put(CommonCssConstants.COLUMN_COUNT, new MultiTypeDeclarationValidator(
118-
new CssNumberValueValidator(false), new CssEnumValidator(CommonCssConstants.AUTO),
119-
inheritInitialUnsetValidator));
118+
new CssIntegerNumberValueValidator(false, false), new CssEnumValidator(CommonCssConstants.AUTO)));
120119
defaultValidators.put(CommonCssConstants.ROW_GAP, new MultiTypeDeclarationValidator(
121120
new CssLengthValueValidator(false), new CssPercentageValueValidator(false), normalValidator,
122121
inheritInitialUnsetValidator));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2023 Apryse Group NV
4+
Authors: Apryse Software.
5+
6+
This program is offered under a commercial and under the AGPL license.
7+
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
8+
9+
AGPL licensing:
10+
This program is free software: you can redistribute it and/or modify
11+
it under the terms of the GNU Affero General Public License as published by
12+
the Free Software Foundation, either version 3 of the License, or
13+
(at your option) any later version.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU Affero General Public License for more details.
19+
20+
You should have received a copy of the GNU Affero General Public License
21+
along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
*/
23+
package com.itextpdf.styledxmlparser.css.validate.impl.datatype;
24+
25+
import com.itextpdf.styledxmlparser.css.CommonCssConstants;
26+
import com.itextpdf.styledxmlparser.css.util.CssTypesValidationUtils;
27+
import com.itextpdf.styledxmlparser.css.validate.ICssDataTypeValidator;
28+
29+
/**
30+
* {@link ICssDataTypeValidator} implementation for integer numeric elements.
31+
*/
32+
public class CssIntegerNumberValueValidator implements ICssDataTypeValidator {
33+
34+
private final boolean allowedNegative;
35+
36+
private final boolean allowedZero;
37+
38+
/**
39+
* Creates a new {@link CssIntegerNumberValueValidator} instance.
40+
*
41+
* @param allowedNegative is negative value allowed
42+
* @param allowedZero is zero value allowed
43+
*/
44+
public CssIntegerNumberValueValidator(boolean allowedNegative, boolean allowedZero) {
45+
this.allowedNegative = allowedNegative;
46+
this.allowedZero = allowedZero;
47+
}
48+
49+
/**
50+
* {@inheritDoc}
51+
*/
52+
@Override
53+
public boolean isValid(final String objectString) {
54+
if (objectString == null) {
55+
return false;
56+
}
57+
if (CommonCssConstants.INITIAL.equals(objectString) || CommonCssConstants.INHERIT.equals(objectString)
58+
|| CommonCssConstants.UNSET.equals(objectString)) {
59+
return true;
60+
}
61+
if (!CssTypesValidationUtils.isIntegerNumber(objectString)) {
62+
return false;
63+
}
64+
if (CssTypesValidationUtils.isNegativeValue(objectString) && !allowedNegative) {
65+
return false;
66+
}
67+
if (CssTypesValidationUtils.isZero(objectString) && !allowedZero) {
68+
return false;
69+
}
70+
return true;
71+
}
72+
}

styled-xml-parser/src/test/java/com/itextpdf/styledxmlparser/css/util/CssTypesValidationUtilsTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,19 @@ public void validateNumericValue() {
101101
Assert.assertFalse(CssTypesValidationUtils.isNumber(".12f"));
102102
}
103103

104+
@Test
105+
public void validateIntegerNumericValue() {
106+
Assert.assertTrue(CssTypesValidationUtils.isIntegerNumber("1"));
107+
Assert.assertTrue(CssTypesValidationUtils.isIntegerNumber("+12"));
108+
Assert.assertTrue(CssTypesValidationUtils.isIntegerNumber("-12"));
109+
Assert.assertFalse(CssTypesValidationUtils.isIntegerNumber(".12"));
110+
Assert.assertFalse(CssTypesValidationUtils.isIntegerNumber("1.2"));
111+
Assert.assertFalse(CssTypesValidationUtils.isIntegerNumber("1,2"));
112+
Assert.assertFalse(CssTypesValidationUtils.isIntegerNumber("12f"));
113+
Assert.assertFalse(CssTypesValidationUtils.isIntegerNumber("f1.2"));
114+
Assert.assertFalse(CssTypesValidationUtils.isIntegerNumber(".12f"));
115+
}
116+
104117
@Test
105118
public void testSpacesBeforeUnitTypes() {
106119
Assert.assertFalse(CssTypesValidationUtils.isAngleValue("10 deg"));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2023 Apryse Group NV
4+
Authors: Apryse Software.
5+
6+
This program is offered under a commercial and under the AGPL license.
7+
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
8+
9+
AGPL licensing:
10+
This program is free software: you can redistribute it and/or modify
11+
it under the terms of the GNU Affero General Public License as published by
12+
the Free Software Foundation, either version 3 of the License, or
13+
(at your option) any later version.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU Affero General Public License for more details.
19+
20+
You should have received a copy of the GNU Affero General Public License
21+
along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
*/
23+
package com.itextpdf.styledxmlparser.css.validate;
24+
25+
import com.itextpdf.styledxmlparser.css.validate.impl.datatype.CssIntegerNumberValueValidator;
26+
import com.itextpdf.test.ExtendedITextTest;
27+
import com.itextpdf.test.annotations.type.UnitTest;
28+
29+
import org.junit.Assert;
30+
import org.junit.Test;
31+
import org.junit.experimental.categories.Category;
32+
33+
@Category(UnitTest.class)
34+
public class CssIntegerNumberValueValidatorTest extends ExtendedITextTest {
35+
@Test
36+
public void zeroValueTest() {
37+
final ICssDataTypeValidator validator1 = new CssIntegerNumberValueValidator(false, true);
38+
Assert.assertTrue(validator1.isValid("0"));
39+
Assert.assertTrue(validator1.isValid("+0"));
40+
41+
final ICssDataTypeValidator validator2 = new CssIntegerNumberValueValidator(false, false);
42+
Assert.assertFalse(validator2.isValid("0"));
43+
44+
final ICssDataTypeValidator validator3 = new CssIntegerNumberValueValidator(true, true);
45+
Assert.assertTrue(validator3.isValid("0"));
46+
Assert.assertTrue(validator3.isValid("-0"));
47+
48+
final ICssDataTypeValidator validator4 = new CssIntegerNumberValueValidator(true, false);
49+
Assert.assertFalse(validator4.isValid("0"));
50+
}
51+
52+
@Test
53+
public void correctValueTest() {
54+
final ICssDataTypeValidator validator1 = new CssIntegerNumberValueValidator(false, true);
55+
Assert.assertTrue(validator1.isValid("123"));
56+
Assert.assertTrue(validator1.isValid("+123"));
57+
Assert.assertFalse(validator1.isValid("1.23"));
58+
59+
final ICssDataTypeValidator validator2 = new CssIntegerNumberValueValidator(false, false);
60+
Assert.assertFalse(validator2.isValid("-123"));
61+
Assert.assertFalse(validator2.isValid("-1.23"));
62+
63+
final ICssDataTypeValidator validator3 = new CssIntegerNumberValueValidator(true, true);
64+
Assert.assertTrue(validator3.isValid("-123"));
65+
Assert.assertTrue(validator3.isValid("-123"));
66+
Assert.assertFalse(validator3.isValid("-1.23"));
67+
68+
final ICssDataTypeValidator validator4 = new CssIntegerNumberValueValidator(true, false);
69+
Assert.assertFalse(validator4.isValid("0"));
70+
}
71+
72+
@Test
73+
public void nullValueTest() {
74+
final ICssDataTypeValidator validator1 = new CssIntegerNumberValueValidator(false, false);
75+
Assert.assertFalse(validator1.isValid(null));
76+
77+
final ICssDataTypeValidator validator2 = new CssIntegerNumberValueValidator(true, true);
78+
Assert.assertFalse(validator2.isValid(null));
79+
80+
final ICssDataTypeValidator validator3 = new CssIntegerNumberValueValidator(false, true);
81+
Assert.assertFalse(validator3.isValid(null));
82+
83+
final ICssDataTypeValidator validator4 = new CssIntegerNumberValueValidator(false, true);
84+
Assert.assertFalse(validator4.isValid(null));
85+
}
86+
87+
@Test
88+
public void initialInheritUnsetValuesTest() {
89+
final ICssDataTypeValidator validator1 = new CssIntegerNumberValueValidator(false, true);
90+
Assert.assertTrue(validator1.isValid("initial"));
91+
Assert.assertTrue(validator1.isValid("inherit"));
92+
Assert.assertTrue(validator1.isValid("unset"));
93+
94+
final ICssDataTypeValidator validator2 = new CssIntegerNumberValueValidator(true, false);
95+
Assert.assertTrue(validator2.isValid("initial"));
96+
Assert.assertTrue(validator2.isValid("inherit"));
97+
Assert.assertTrue(validator2.isValid("unset"));
98+
}
99+
100+
@Test
101+
public void normalValueTest() {
102+
final ICssDataTypeValidator validator1 = new CssIntegerNumberValueValidator(false, true);
103+
Assert.assertFalse(validator1.isValid("normal"));
104+
105+
final ICssDataTypeValidator validator2 = new CssIntegerNumberValueValidator(true, false);
106+
Assert.assertFalse(validator2.isValid("normal"));
107+
}
108+
109+
@Test
110+
public void invalidValuesTest() {
111+
final ICssDataTypeValidator validator1 = new CssIntegerNumberValueValidator(false, true);
112+
Assert.assertFalse(validator1.isValid(""));
113+
Assert.assertFalse(validator1.isValid("dja"));
114+
Assert.assertFalse(validator1.isValid("5pixels"));
115+
116+
final ICssDataTypeValidator validator2 = new CssIntegerNumberValueValidator(true, false);
117+
Assert.assertFalse(validator2.isValid(""));
118+
Assert.assertFalse(validator2.isValid("dja"));
119+
Assert.assertFalse(validator2.isValid("5pixels"));
120+
}
121+
122+
@Test
123+
public void absoluteValuesTest() {
124+
final ICssDataTypeValidator validator1 = new CssIntegerNumberValueValidator(false, true);
125+
Assert.assertTrue(validator1.isValid("12"));
126+
Assert.assertFalse(validator1.isValid("12pt"));
127+
Assert.assertFalse(validator1.isValid("-12pt"));
128+
Assert.assertFalse(validator1.isValid("12px"));
129+
Assert.assertFalse(validator1.isValid("12in"));
130+
Assert.assertFalse(validator1.isValid("12cm"));
131+
Assert.assertFalse(validator1.isValid("12mm"));
132+
Assert.assertFalse(validator1.isValid("12pc"));
133+
Assert.assertFalse(validator1.isValid("12q"));
134+
Assert.assertFalse(validator1.isValid("12 pt"));
135+
136+
final ICssDataTypeValidator validator2 = new CssIntegerNumberValueValidator(true, false);
137+
Assert.assertTrue(validator2.isValid("12"));
138+
Assert.assertFalse(validator2.isValid("12pt"));
139+
Assert.assertFalse(validator2.isValid("-12pt"));
140+
Assert.assertFalse(validator2.isValid("12px"));
141+
Assert.assertFalse(validator2.isValid("12in"));
142+
Assert.assertFalse(validator2.isValid("12cm"));
143+
Assert.assertFalse(validator2.isValid("12mm"));
144+
Assert.assertFalse(validator2.isValid("12pc"));
145+
Assert.assertFalse(validator2.isValid("12q"));
146+
Assert.assertFalse(validator2.isValid("12 pt"));
147+
148+
final ICssDataTypeValidator validator3 = new CssIntegerNumberValueValidator(true, true);
149+
Assert.assertTrue(validator3.isValid("12"));
150+
Assert.assertFalse(validator3.isValid("12pt"));
151+
Assert.assertFalse(validator3.isValid("-12pt"));
152+
Assert.assertFalse(validator3.isValid("12px"));
153+
Assert.assertFalse(validator3.isValid("12in"));
154+
Assert.assertFalse(validator3.isValid("12cm"));
155+
Assert.assertFalse(validator3.isValid("12mm"));
156+
Assert.assertFalse(validator3.isValid("12pc"));
157+
Assert.assertFalse(validator3.isValid("12q"));
158+
Assert.assertFalse(validator3.isValid("12 pt"));
159+
}
160+
161+
@Test
162+
public void relativeValuesTest() {
163+
final ICssDataTypeValidator validator1 = new CssIntegerNumberValueValidator(false, true);
164+
Assert.assertFalse(validator1.isValid("12em"));
165+
Assert.assertFalse(validator1.isValid("-12em"));
166+
Assert.assertFalse(validator1.isValid("12rem"));
167+
Assert.assertFalse(validator1.isValid("12ex"));
168+
Assert.assertFalse(validator1.isValid("12 em"));
169+
170+
final ICssDataTypeValidator validator2 = new CssIntegerNumberValueValidator(true, false);
171+
Assert.assertFalse(validator2.isValid("12em"));
172+
Assert.assertFalse(validator2.isValid("-12em"));
173+
Assert.assertFalse(validator2.isValid("12rem"));
174+
Assert.assertFalse(validator2.isValid("12ex"));
175+
Assert.assertFalse(validator2.isValid("12 em"));
176+
}
177+
178+
@Test
179+
public void percentValueTest() {
180+
final ICssDataTypeValidator validator1 = new CssIntegerNumberValueValidator(false, true);
181+
Assert.assertFalse(validator1.isValid("12%"));
182+
Assert.assertFalse(validator1.isValid("-12%"));
183+
final ICssDataTypeValidator validator2 = new CssIntegerNumberValueValidator(true, false);
184+
Assert.assertFalse(validator2.isValid("12%"));
185+
Assert.assertFalse(validator2.isValid("-12%"));
186+
}
187+
}

0 commit comments

Comments
 (0)