Skip to content

Commit a54cb19

Browse files
Egor MartsynkovskyiText-CI
authored andcommitted
Add support of attr() with type for url and string
Add support of attr() with fallback for url and string Add support of attr() in target-counter Add tests DEVSIX-2994
1 parent a9bca9c commit a54cb19

File tree

7 files changed

+156
-13
lines changed

7 files changed

+156
-13
lines changed

src/main/java/com/itextpdf/html2pdf/css/resolve/CssContentPropertyResolver.java

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ This file is part of the iText (R) project.
5252
import com.itextpdf.html2pdf.html.TagConstants;
5353
import com.itextpdf.io.util.MessageFormatUtil;
5454
import com.itextpdf.html2pdf.html.AttributeConstants;
55+
import com.itextpdf.styledxmlparser.css.CommonCssConstants;
5556
import com.itextpdf.styledxmlparser.css.page.PageMarginBoxContextNode;
5657
import com.itextpdf.html2pdf.css.page.PageMarginRunningElementNode;
5758
import com.itextpdf.styledxmlparser.css.parse.CssDeclarationValueTokenizer;
59+
import com.itextpdf.styledxmlparser.css.parse.CssDeclarationValueTokenizer.Token;
5860
import com.itextpdf.styledxmlparser.css.pseudo.CssPseudoElementNode;
5961
import com.itextpdf.styledxmlparser.css.resolve.CssQuotes;
6062
import com.itextpdf.styledxmlparser.css.util.CssGradientUtil;
@@ -164,7 +166,12 @@ static List<INode> resolveContent(Map<String, String> styles, INode contentConta
164166
if (params.size() < TARGET_COUNTER_MIN_PARAMS_SIZE) {
165167
return errorFallback(contentStr);
166168
}
167-
final String target = CssUtils.extractUrl(params.get(0));
169+
final String target = params.get(0).startsWith(CommonCssConstants.ATTRIBUTE + "(") ? CssUtils
170+
.extractAttributeValue(params.get(0), (IElementNode) contentContainer.parentNode())
171+
: CssUtils.extractUrl(params.get(0));
172+
if (target == null) {
173+
return errorFallback(contentStr);
174+
}
168175
final String counterName = params.get(1).trim();
169176
final CounterDigitsGlyphStyle listStyleType = HtmlUtils.convertStringCounterGlyphStyleToEnum(
170177
params.size() > TARGET_COUNTER_MIN_PARAMS_SIZE ?
@@ -188,7 +195,12 @@ static List<INode> resolveContent(Map<String, String> styles, INode contentConta
188195
if (params.size() < TARGET_COUNTERS_MIN_PARAMS_SIZE) {
189196
return errorFallback(contentStr);
190197
}
191-
final String target = CssUtils.extractUrl(params.get(0));
198+
final String target = params.get(0).startsWith(CommonCssConstants.ATTRIBUTE + "(") ? CssUtils
199+
.extractAttributeValue(params.get(0), (IElementNode) contentContainer.parentNode())
200+
: CssUtils.extractUrl(params.get(0));
201+
if (target == null) {
202+
return errorFallback(contentStr);
203+
}
192204
final String counterName = params.get(1).trim();
193205
String counterSeparator = params.get(2).trim();
194206
counterSeparator = counterSeparator.substring(1, counterSeparator.length() - 1);
@@ -220,18 +232,14 @@ static List<INode> resolveContent(Map<String, String> styles, INode contentConta
220232
+ CssConstants.HEIGHT + ":" + CssConstants.INHERIT + ";"
221233
+ CssConstants.WIDTH + ":" + CssConstants.INHERIT + ";");
222234
result.add(new CssContentElementNode(contentContainer, TagConstants.DIV, attributes));
223-
} else if (token.getValue().startsWith("attr(") && contentContainer instanceof CssPseudoElementNode) {
224-
int endBracket = token.getValue().indexOf(')');
225-
if (endBracket > 5) {
226-
String attrName = token.getValue().substring(5, endBracket);
227-
if (attrName.contains("(") || attrName.contains(" ")
228-
|| attrName.contains("'") || attrName.contains("\"")) {
229-
return errorFallback(contentStr);
230-
}
231-
IElementNode element = (IElementNode) contentContainer.parentNode();
232-
String value = element.getAttribute(attrName);
233-
result.add(new ContentTextNode(contentContainer, value == null ? "" : value));
235+
} else if (token.getValue().startsWith(CommonCssConstants.ATTRIBUTE + "(")
236+
&& contentContainer instanceof CssPseudoElementNode) {
237+
String value = CssUtils
238+
.extractAttributeValue(token.getValue(), (IElementNode) contentContainer.parentNode());
239+
if (value == null) {
240+
return errorFallback(contentStr);
234241
}
242+
result.add(new ContentTextNode(contentContainer, value));
235243
} else if (token.getValue().endsWith("quote") && contentContainer instanceof IStylesContainer) {
236244
if (quotes == null) {
237245
quotes = CssQuotes.createQuotes(styles.get(CssConstants.QUOTES), true);
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2021 iText Group NV
4+
Authors: iText Software.
5+
6+
This program is free software; you can redistribute it and/or modify
7+
it under the terms of the GNU Affero General Public License version 3
8+
as published by the Free Software Foundation with the addition of the
9+
following permission added to Section 15 as permitted in Section 7(a):
10+
FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
11+
ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
12+
OF THIRD PARTY RIGHTS
13+
14+
This program is distributed in the hope that it will be useful, but
15+
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16+
or FITNESS FOR A PARTICULAR PURPOSE.
17+
See the GNU Affero General Public License for more details.
18+
You should have received a copy of the GNU Affero General Public License
19+
along with this program; if not, see http://www.gnu.org/licenses or write to
20+
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21+
Boston, MA, 02110-1301 USA, or download the license from the following URL:
22+
http://itextpdf.com/terms-of-use/
23+
24+
The interactive user interfaces in modified source and object code versions
25+
of this program must display Appropriate Legal Notices, as required under
26+
Section 5 of the GNU Affero General Public License.
27+
28+
In accordance with Section 7(b) of the GNU Affero General Public License,
29+
a covered work must retain the producer line in every PDF that is created
30+
or manipulated using iText.
31+
32+
You can be released from the requirements of the license by purchasing
33+
a commercial license. Buying such a license is mandatory as soon as you
34+
develop commercial activities involving the iText software without
35+
disclosing the source code of your own applications.
36+
These activities include: offering paid services to customers as an ASP,
37+
serving PDFs on the fly in a web application, shipping iText with a closed
38+
source product.
39+
40+
For more information, please contact iText Software Corp. at this
41+
address: sales@itextpdf.com
42+
*/
43+
package com.itextpdf.html2pdf.css.resolve;
44+
45+
import com.itextpdf.html2pdf.ExtendedHtmlConversionITextTest;
46+
import com.itextpdf.test.annotations.type.IntegrationTest;
47+
48+
import java.io.IOException;
49+
import org.junit.BeforeClass;
50+
import org.junit.Test;
51+
import org.junit.experimental.categories.Category;
52+
53+
@Category(IntegrationTest.class)
54+
public class CssContentPropertyResolverIntegrationTest extends ExtendedHtmlConversionITextTest {
55+
56+
public static final String SOURCE_FOLDER = "./src/test/resources/com/itextpdf/html2pdf/css"
57+
+ "/CssContentPropertyResolverIntegrationTest/";
58+
public static final String DESTINATION_FOLDER = "./target/test/com/itextpdf/html2pdf/css"
59+
+ "/CssContentPropertyResolverIntegrationTest/";
60+
61+
@BeforeClass
62+
public static void beforeClass() {
63+
createOrClearDestinationFolder(DESTINATION_FOLDER);
64+
}
65+
66+
@Test
67+
public void checkSimpleAttrFunctionTest() throws IOException, InterruptedException {
68+
convertToPdfAndCompare("checkSimpleAttrFunctionTest", SOURCE_FOLDER, DESTINATION_FOLDER);
69+
}
70+
71+
@Test
72+
public void checkAttrFunctionWithTypeTest() throws IOException, InterruptedException {
73+
convertToPdfAndCompare("checkAttrFunctionWithTypeTest", SOURCE_FOLDER, DESTINATION_FOLDER);
74+
}
75+
}

src/test/java/com/itextpdf/html2pdf/css/resolve/CssContentPropertyResolverTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ This file is part of the iText (R) project.
2626
import com.itextpdf.html2pdf.css.CssConstants;
2727
import com.itextpdf.html2pdf.css.resolve.func.counter.PageCountElementNode;
2828
import com.itextpdf.html2pdf.css.resolve.func.counter.PageTargetCountElementNode;
29+
import com.itextpdf.styledxmlparser.css.pseudo.CssPseudoElementNode;
30+
import com.itextpdf.styledxmlparser.node.IElementNode;
2931
import com.itextpdf.styledxmlparser.node.INode;
3032
import com.itextpdf.styledxmlparser.node.ITextNode;
3133
import com.itextpdf.test.ExtendedITextTest;
@@ -117,4 +119,28 @@ public void resolveContentCounterNotPageTest() {
117119
Assert.assertEquals(1, result.size());
118120
Assert.assertTrue(result.get(0) instanceof ITextNode);
119121
}
122+
123+
@Test
124+
@LogMessages(messages = @LogMessage(messageTemplate = LogMessageConstant.CONTENT_PROPERTY_INVALID, count = 1))
125+
public void resolveContentWrongTargetCounterTest(){
126+
Map<String,String> styles = new HashMap<>();
127+
styles.put(CssConstants.CONTENT,"target-counter(attr(), pages)");
128+
CssContext context = new CssContext();
129+
IElementNode iNode = new CssPseudoElementNode(null, "test");
130+
131+
List<INode> result = CssContentPropertyResolver.resolveContent(styles,iNode,context);
132+
Assert.assertNull(result);
133+
}
134+
135+
@Test
136+
@LogMessages(messages = @LogMessage(messageTemplate = LogMessageConstant.CONTENT_PROPERTY_INVALID, count = 1))
137+
public void resolveContentWrongTargetCountersTest(){
138+
Map<String,String> styles = new HashMap<>();
139+
styles.put(CssConstants.CONTENT,"target-counters(attr(), pages)");
140+
CssContext context = new CssContext();
141+
IElementNode iNode = new CssPseudoElementNode(null, "test");
142+
143+
List<INode> result = CssContentPropertyResolver.resolveContent(styles,iNode,context);
144+
Assert.assertNull(result);
145+
}
120146
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<html>
2+
3+
<head>
4+
<style>
5+
.toc-row a::after {
6+
content: " " target-counter(attr(href url), page);
7+
}
8+
</style>
9+
</head>
10+
<body>
11+
12+
<div class="toc-container">
13+
<div class="toc-row"><a href="#one">Link To Page One</a></div>
14+
<div class="toc-row"><a href="#two">Link To Page Two</a></div>
15+
</div>
16+
17+
<div class="content-container">
18+
<div class="page" id="one">Page One Content Here</div>
19+
<div class="page" id="two" style="page-break-before: always">Page Two Content Here</div>
20+
</div>
21+
22+
</body>
23+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<style>
4+
[data-foo]::before {
5+
content: attr(data-foo) " ";
6+
}
7+
</style>
8+
<body>
9+
<p data-foo="hello">world</p>
10+
</body>
11+
</html>

0 commit comments

Comments
 (0)