Skip to content

Commit 43077c4

Browse files
committed
Support row\column span values for cell in the grid
DEVSIX-8363
1 parent b23b1c4 commit 43077c4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1294
-47
lines changed

src/main/java/com/itextpdf/html2pdf/css/apply/util/GridApplierUtil.java

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ This file is part of the iText (R) project.
4141
import java.util.List;
4242
import java.util.Map;
4343
import java.util.concurrent.ConcurrentHashMap;
44+
import java.util.regex.Matcher;
45+
import java.util.regex.Pattern;
4446
import org.slf4j.Logger;
4547
import org.slf4j.LoggerFactory;
4648

@@ -51,6 +53,8 @@ public final class GridApplierUtil {
5153

5254
private static final Logger LOGGER = LoggerFactory.getLogger(GridApplierUtil.class);
5355

56+
private static final Pattern SPAN_PLACEMENT = Pattern.compile("^span (\\d+)$");
57+
5458
private static final Map<String, NamedAreas> namedAreasCache = new ConcurrentHashMap<>();
5559
private static final int NAMED_AREAS_CACHE_CAPACITY = 10;
5660

@@ -96,54 +100,47 @@ public static void applyGridItemProperties(Map<String, String> cssProps, IStyles
96100
namedAreas = parseGridTemplateAreas(gridTemplateAreas);
97101
}
98102

99-
for (Map.Entry<String, String> entry : cssProps.entrySet()) {
100-
if (CssConstants.GRID_AREA.equals(entry.getKey())) {
101-
String gridArea = entry.getValue();
102-
String[] gridAreaParts = gridArea.split("/");
103-
for(int i = 0; i < gridAreaParts.length; ++i) {
104-
String part = gridAreaParts[i].trim();
105-
if (CommonCssConstants.AUTO.equals(part)) {
106-
// We override already set value if any
107-
element.deleteOwnProperty(propsMap.get(i).intValue());
108-
continue;
109-
}
110-
Integer partInt = CssDimensionParsingUtils.parseInteger(part);
111-
if (partInt != null) {
112-
element.setProperty(propsMap.get(i).intValue(), partInt);
113-
} else if (namedAreas != null && i == 0) {
114-
// We are interested in the 1st element in grid area for now
115-
// so let's even break immediately
116-
namedAreas.setPlaceToElement(part, element);
117-
break;
118-
}
119-
}
120-
}
121-
122-
if (CssConstants.GRID_COLUMN_START.equals(entry.getKey())) {
123-
Integer columnStart = CssDimensionParsingUtils.parseInteger(entry.getValue());
124-
if (columnStart != null) {
125-
element.setProperty(Property.GRID_COLUMN_START, columnStart);
126-
}
127-
}
128-
129-
if (CssConstants.GRID_COLUMN_END.equals(entry.getKey())) {
130-
Integer columnStart = CssDimensionParsingUtils.parseInteger(entry.getValue());
131-
if (columnStart != null) {
132-
element.setProperty(Property.GRID_COLUMN_END, columnStart);
103+
if (cssProps.get(CssConstants.GRID_AREA) != null) {
104+
String gridArea = cssProps.get(CssConstants.GRID_AREA);
105+
String[] gridAreaParts = gridArea.split("/");
106+
for(int i = 0; i < gridAreaParts.length; ++i) {
107+
String part = gridAreaParts[i].trim();
108+
if (CommonCssConstants.AUTO.equals(part)) {
109+
// We override already set value if any
110+
element.deleteOwnProperty(propsMap.get(i).intValue());
111+
continue;
133112
}
134-
}
135-
136-
if (CssConstants.GRID_ROW_START.equals(entry.getKey())) {
137-
Integer columnStart = CssDimensionParsingUtils.parseInteger(entry.getValue());
138-
if (columnStart != null) {
139-
element.setProperty(Property.GRID_ROW_START, columnStart);
113+
Integer partInt = CssDimensionParsingUtils.parseInteger(part);
114+
if (partInt != null) {
115+
element.setProperty(propsMap.get(i).intValue(), partInt);
116+
} else if (namedAreas != null && i == 0) {
117+
// We are interested in the 1st element in grid area for now
118+
// so let's even break immediately
119+
namedAreas.setPlaceToElement(part, element);
120+
break;
140121
}
141122
}
123+
}
142124

143-
if (CssConstants.GRID_ROW_END.equals(entry.getKey())) {
144-
Integer columnStart = CssDimensionParsingUtils.parseInteger(entry.getValue());
145-
if (columnStart != null) {
146-
element.setProperty(Property.GRID_ROW_END, columnStart);
125+
applyGridItemPlacement(cssProps.get(CssConstants.GRID_COLUMN_END), element, Property.GRID_COLUMN_END, Property.GRID_COLUMN_SPAN);
126+
applyGridItemPlacement(cssProps.get(CssConstants.GRID_COLUMN_START), element, Property.GRID_COLUMN_START, Property.GRID_COLUMN_SPAN);
127+
applyGridItemPlacement(cssProps.get(CssConstants.GRID_ROW_END), element, Property.GRID_ROW_END, Property.GRID_ROW_SPAN);
128+
applyGridItemPlacement(cssProps.get(CssConstants.GRID_ROW_START), element, Property.GRID_ROW_START, Property.GRID_ROW_SPAN);
129+
}
130+
131+
private static void applyGridItemPlacement(String value, IPropertyContainer element, int property, int spanProperty) {
132+
if (value == null) {
133+
return;
134+
}
135+
Integer intValue = CssDimensionParsingUtils.parseInteger(value);
136+
if (intValue != null) {
137+
element.setProperty(property, intValue);
138+
} else {
139+
Matcher matcher = SPAN_PLACEMENT.matcher(value.trim());
140+
if (matcher.matches()) {
141+
Integer spanValue = CssDimensionParsingUtils.parseInteger(matcher.group(1));
142+
if (spanValue != null) {
143+
element.setProperty(spanProperty, spanValue);
147144
}
148145
}
149146
}

src/test/java/com/itextpdf/html2pdf/css/apply/util/GridApplierUtilTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public void applyGridAreaOrder2Test() {
144144
IElement element = new Div();
145145
GridApplierUtil.applyGridItemProperties(cssProps, createStylesContainer(), element);
146146
Assertions.assertEquals(1, element.<Integer>getProperty(Property.GRID_ROW_START));
147-
Assertions.assertEquals(2, element.<Integer>getProperty(Property.GRID_COLUMN_START));
147+
Assertions.assertEquals(1, element.<Integer>getProperty(Property.GRID_COLUMN_START));
148148
Assertions.assertEquals(3, element.<Integer>getProperty(Property.GRID_ROW_END));
149149
Assertions.assertEquals(4, element.<Integer>getProperty(Property.GRID_COLUMN_END));
150150
}
@@ -169,7 +169,7 @@ public void applyGridAreaOrder4Test() {
169169
cssProps.put(CssConstants.GRID_AREA, "auto / 2 / 3 / 4");
170170
IElement element = new Div();
171171
GridApplierUtil.applyGridItemProperties(cssProps, createStylesContainer(), element);
172-
Assertions.assertNull(element.<Integer>getProperty(Property.GRID_ROW_START));
172+
Assertions.assertEquals(1, element.<Integer>getProperty(Property.GRID_ROW_START));
173173
Assertions.assertEquals(2, element.<Integer>getProperty(Property.GRID_COLUMN_START));
174174
Assertions.assertEquals(3, element.<Integer>getProperty(Property.GRID_ROW_END));
175175
Assertions.assertEquals(4, element.<Integer>getProperty(Property.GRID_COLUMN_END));
@@ -233,7 +233,7 @@ public void applyGridTemplateAreasOrder1Test() {
233233
GridApplierUtil.applyGridItemProperties(cssProps, stylesContainer, element);
234234
Assertions.assertEquals(1, element.<Integer>getProperty(Property.GRID_ROW_START));
235235
Assertions.assertEquals(1, element.<Integer>getProperty(Property.GRID_COLUMN_START));
236-
Assertions.assertEquals(4, element.<Integer>getProperty(Property.GRID_ROW_END));
236+
Assertions.assertEquals(3, element.<Integer>getProperty(Property.GRID_ROW_END));
237237
Assertions.assertEquals(2, element.<Integer>getProperty(Property.GRID_COLUMN_END));
238238
}
239239

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2024 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.html2pdf.css.grid;
24+
25+
import com.itextpdf.html2pdf.ConverterProperties;
26+
import com.itextpdf.html2pdf.ExtendedHtmlConversionITextTest;
27+
import com.itextpdf.layout.exceptions.LayoutExceptionMessageConstant;
28+
29+
import java.io.IOException;
30+
import org.junit.jupiter.api.Assertions;
31+
import org.junit.jupiter.api.BeforeAll;
32+
import org.junit.jupiter.api.Tag;
33+
import org.junit.jupiter.api.Test;
34+
35+
@Tag("IntegrationTest")
36+
public class GridItemPlacementTest extends ExtendedHtmlConversionITextTest {
37+
public static final String SOURCE_FOLDER = "./src/test/resources/com/itextpdf/html2pdf/css/grid/GridItemPlacementTest/";
38+
public static final String DESTINATION_FOLDER = "./target/test/com/itextpdf/html2pdf/css/grid/GridItemPlacementTest/";
39+
40+
@BeforeAll
41+
public static void beforeClass() {
42+
createOrClearDestinationFolder(DESTINATION_FOLDER);
43+
}
44+
45+
@Test
46+
public void colStartEnd1Test() throws IOException, InterruptedException {
47+
runTest("colStartEnd1");
48+
}
49+
50+
@Test
51+
public void colStartEnd2Test() throws IOException, InterruptedException {
52+
runTest("colStartEnd2");
53+
}
54+
55+
@Test
56+
public void colStartEnd3Test() throws IOException, InterruptedException {
57+
runTest("colStartEnd3");
58+
}
59+
60+
@Test
61+
public void colStartEnd4Test() throws IOException, InterruptedException {
62+
runTest("colStartEnd4");
63+
}
64+
65+
@Test
66+
public void colStartEnd5Test() throws IOException, InterruptedException {
67+
runTest("colStartEnd5");
68+
}
69+
70+
@Test
71+
public void fewCellsPlacement1Test() throws IOException, InterruptedException {
72+
runTest("fewCellsPlacement1");
73+
}
74+
75+
@Test
76+
public void fewCellsPlacement2Test() throws IOException, InterruptedException {
77+
runTest("fewCellsPlacement2");
78+
}
79+
80+
@Test
81+
public void fewCellsPlacement3Test() throws IOException, InterruptedException {
82+
runTest("fewCellsPlacement3");
83+
}
84+
85+
@Test
86+
public void fewCellsPlacement4Test() throws IOException, InterruptedException {
87+
runTest("fewCellsPlacement4");
88+
}
89+
90+
@Test
91+
public void fewCellsPlacement5Test() {
92+
Exception e = Assertions.assertThrows(IllegalArgumentException.class, () -> runTest("fewCellsPlacement5"));
93+
Assertions.assertEquals(LayoutExceptionMessageConstant.INVALID_CELL_INDEXES, e.getMessage());
94+
}
95+
96+
@Test
97+
public void fewCellsPlacement6Test() throws IOException, InterruptedException {
98+
runTest("fewCellsPlacement6");
99+
}
100+
101+
@Test
102+
public void rowStartEnd1Test() throws IOException, InterruptedException {
103+
runTest("rowStartEnd1");
104+
}
105+
106+
@Test
107+
public void rowStartEnd2Test() throws IOException, InterruptedException {
108+
runTest("rowStartEnd2");
109+
}
110+
111+
@Test
112+
public void rowStartEnd3Test() throws IOException, InterruptedException {
113+
runTest("rowStartEnd3");
114+
}
115+
116+
@Test
117+
// TODO
118+
public void rowStartEnd4Test() throws IOException, InterruptedException {
119+
runTest("rowStartEnd4");
120+
}
121+
122+
@Test
123+
public void twoColumnSpans1Test() throws IOException, InterruptedException {
124+
runTest("twoColumnSpans1");
125+
}
126+
127+
@Test
128+
public void twoColumnSpans2Test() throws IOException, InterruptedException {
129+
runTest("twoColumnSpans2");
130+
}
131+
132+
@Test
133+
public void twoColumnSpans3Test() throws IOException, InterruptedException {
134+
runTest("twoColumnSpans3");
135+
}
136+
137+
@Test
138+
public void twoRowSpans1Test() throws IOException, InterruptedException {
139+
runTest("twoRowSpans1");
140+
}
141+
142+
@Test
143+
public void twoRowSpans2Test() throws IOException, InterruptedException {
144+
runTest("twoRowSpans2");
145+
}
146+
147+
@Test
148+
public void twoRowSpans3Test() throws IOException, InterruptedException {
149+
runTest("twoRowSpans3");
150+
}
151+
152+
@Test
153+
public void invalid1Test() throws IOException, InterruptedException {
154+
runTest("invalid1");
155+
}
156+
157+
@Test
158+
public void invalid2Test() throws IOException, InterruptedException {
159+
runTest("invalid2");
160+
}
161+
162+
@Test
163+
public void invalid3Test() throws IOException, InterruptedException {
164+
runTest("invalid3");
165+
}
166+
167+
@Test
168+
public void invalid4Test() throws IOException, InterruptedException {
169+
runTest("invalid4");
170+
}
171+
172+
@Test
173+
public void invalid5Test() throws IOException, InterruptedException {
174+
runTest("invalid5");
175+
}
176+
177+
@Test
178+
public void noTemplate1Test() throws IOException, InterruptedException {
179+
runTest("noTemplate1");
180+
}
181+
182+
@Test
183+
public void noTemplate2Test() throws IOException, InterruptedException {
184+
runTest("noTemplate2");
185+
}
186+
187+
private void runTest(String testName) throws IOException, InterruptedException {
188+
convertToPdfAndCompare(testName, SOURCE_FOLDER, DESTINATION_FOLDER, false,
189+
new ConverterProperties().setBaseUri(SOURCE_FOLDER).setCssGridEnabled(true));
190+
}
191+
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)