Skip to content

Commit 5c50c64

Browse files
committed
Preserve case for url() argument in css
DEVSIX-1452
1 parent 3013027 commit 5c50c64

File tree

3 files changed

+80
-39
lines changed

3 files changed

+80
-39
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ public final class LogMessageConstant {
151151
/** The Constant UNKNOWN_MARGIN_BOX_CHILD. */
152152
public static final String UNKNOWN_MARGIN_BOX_CHILD = "Unknown margin box child";
153153

154+
public static final String URL_IS_NOT_CLOSED_IN_CSS_EXPRESSION = "url function is not properly closed in expression:{0}";
155+
156+
public static final String URL_IS_EMPTY_IN_CSS_EXPRESSION = "url function is empty in expression:{0}";
157+
154158
/** The Constant WAS_NOT_ABLE_TO_DEFINE_BACKGROUND_CSS_SHORTHAND_PROPERTIES. */
155159
public static final String WAS_NOT_ABLE_TO_DEFINE_BACKGROUND_CSS_SHORTHAND_PROPERTIES = "Was not able to define one of the background CSS shorthand properties: {0}";
156160

src/main/java/com/itextpdf/html2pdf/css/util/CssPropertyNormalizer.java

Lines changed: 67 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -59,68 +59,96 @@ class CssPropertyNormalizer {
5959
* @return the normalized property
6060
*/
6161
public static String normalize(String str) {
62-
StringBuilder buffer = new StringBuilder();
63-
int segmentStart = 0;
64-
for (int i = 0; i < str.length(); ++i) {
62+
StringBuilder sb = new StringBuilder();
63+
boolean isWhitespace = false;
64+
int i = 0;
65+
while (i < str.length()) {
6566
if (str.charAt(i) == '\\') {
67+
sb.append(str.charAt(i));
6668
++i;
67-
} else if (str.charAt(i) == '\'' || str.charAt(i) == '"') {
68-
appendAndFormatSegment(buffer, str, segmentStart, i + 1);
69-
segmentStart = i = appendQuoteContent(buffer, str, i + 1, str.charAt(i));
70-
}
71-
}
72-
if (segmentStart < str.length()) {
73-
appendAndFormatSegment(buffer, str, segmentStart, str.length());
74-
}
75-
return buffer.toString();
76-
}
77-
78-
/**
79-
* Appends and formats a segment.
80-
*
81-
* @param buffer the current buffer
82-
* @param source a source
83-
* @param start where to start in the source
84-
* @param end where to end in the source
85-
*/
86-
private static void appendAndFormatSegment(StringBuilder buffer, String source, int start, int end) {
87-
String[] parts = source.substring(start, end).split("\\s");
88-
StringBuilder sb = new StringBuilder();
89-
for (String part : parts) {
90-
if (part.length() > 0) {
91-
if (sb.length() > 0 && !trimSpaceAfter(sb.charAt(sb.length() - 1)) && !trimSpaceBefore(part.charAt(0))) {
92-
sb.append(" ");
69+
if (i < str.length()) {
70+
sb.append(str.charAt(i));
71+
++i;
72+
}
73+
} else if (Character.isWhitespace(str.charAt(i))) {
74+
isWhitespace = true;
75+
++i;
76+
} else {
77+
if (isWhitespace) {
78+
if (sb.length() > 0 && !trimSpaceAfter(sb.charAt(sb.length() - 1)) && !trimSpaceBefore(str.charAt(i))) {
79+
sb.append(" ");
80+
}
81+
isWhitespace = false;
9382
}
94-
// Do not make base64 data lowercase, function name only
95-
if (part.matches("^[uU][rR][lL]\\(.+\\)") && CssUtils.isBase64Data(part.substring(4, part.length() - 1))) {
96-
sb.append(part.substring(0, 3).toLowerCase()).append(part.substring(3));
83+
if (str.charAt(i) == '\'' || str.charAt(i) == '"') {
84+
i = appendQuotedString(sb, str, i);
85+
} else if ((str.charAt(i) == 'u' || str.charAt(i) == 'U') && str.substring(i).matches("^[uU][rR][lL]\\(.*?")) {
86+
sb.append(str.substring(i, i + 4).toLowerCase());
87+
i = appendUrlContent(sb, str, i + 4);
9788
} else {
98-
sb.append(part.toLowerCase());
89+
sb.append(Character.toLowerCase(str.charAt(i)));
90+
++i;
9991
}
10092
}
10193
}
102-
buffer.append(sb);
94+
return sb.toString();
10395
}
10496

10597
/**
106-
* Appends quoted content.
98+
* Appends quoted string.
10799
*
108100
* @param buffer the current buffer
109101
* @param source a source
110-
* @param start where to start in the source
111-
* @param endQuoteSymbol the end quote symbol
102+
* @param start where to start in the source. Should point at quote symbol.
112103
* @return the new position in the source
113104
*/
114-
private static int appendQuoteContent(StringBuilder buffer, String source, int start, char endQuoteSymbol) {
115-
int end = CssUtils.findNextUnescapedChar(source, endQuoteSymbol, start);
105+
private static int appendQuotedString(StringBuilder buffer, String source, int start) {
106+
char endQuoteSymbol = source.charAt(start);
107+
int end = CssUtils.findNextUnescapedChar(source, endQuoteSymbol, start + 1);
116108
if (end == -1) {
117109
end = source.length();
118110
LoggerFactory.getLogger(CssPropertyNormalizer.class).warn(MessageFormatUtil.format(LogMessageConstant.QUOTE_IS_NOT_CLOSED_IN_CSS_EXPRESSION, source));
111+
} else {
112+
++end;
119113
}
120114
buffer.append(source, start, end);
121115
return end;
122116
}
123117

118+
/**
119+
* Appends url content and end parenthesis if url is correct.
120+
*
121+
* @param buffer the current buffer
122+
* @param source a source
123+
* @param start where to start in the source. Should point at first symbol after "url(".
124+
* @return the new position in the source
125+
*/
126+
private static int appendUrlContent(StringBuilder buffer, String source, int start) {
127+
while (Character.isWhitespace(source.charAt(start)) && start < source.length()) {
128+
++start;
129+
}
130+
if (start < source.length()) {
131+
int curr = start;
132+
if (source.charAt(curr) == '"' || source.charAt(curr) == '\'') {
133+
curr = appendQuotedString(buffer, source, curr);
134+
return curr;
135+
} else {
136+
curr = CssUtils.findNextUnescapedChar(source, ')', curr);
137+
if (curr == -1) {
138+
LoggerFactory.getLogger(CssPropertyNormalizer.class).warn(MessageFormatUtil.format(LogMessageConstant.URL_IS_NOT_CLOSED_IN_CSS_EXPRESSION, source));
139+
return source.length();
140+
} else {
141+
buffer.append(source.substring(start, curr).trim());
142+
buffer.append(')');
143+
return curr + 1;
144+
}
145+
}
146+
} else {
147+
LoggerFactory.getLogger(CssPropertyNormalizer.class).warn(MessageFormatUtil.format(LogMessageConstant.URL_IS_EMPTY_IN_CSS_EXPRESSION, source));
148+
return source.length();
149+
}
150+
}
151+
124152
/**
125153
* Checks if spaces can be trimmed after a specific character.
126154
*

src/test/java/com/itextpdf/html2pdf/css/util/CssUtilTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,13 @@ public void normalizeProperty() {
9797
assertEquals("\" ( \"attr(href) \" ) \"", CssUtils.normalizeCssProperty("\" ( \"aTTr( hREf ) \" ) \""));
9898
assertEquals("rgba(255,255,255,0.2)", CssUtils.normalizeCssProperty("rgba( 255, 255 , 255 ,0.2 )"));
9999
}
100+
101+
@Test
102+
public void normalizeUrlTest() {
103+
assertEquals("url(data:application/font-woff;base64,2CBPCRXmgywtV1t4oWwjBju0kqkvfhPs0cYdMgFtDSY5uL7MIGT5wiGs078HrvBHekp0Yf=)",
104+
CssUtils.normalizeCssProperty("url(data:application/font-woff;base64,2CBPCRXmgywtV1t4oWwjBju0kqkvfhPs0cYdMgFtDSY5uL7MIGT5wiGs078HrvBHekp0Yf=)"));
105+
assertEquals("url(\"quoted Url\")", CssUtils.normalizeCssProperty(" url( \"quoted Url\")"));
106+
assertEquals("url('quoted Url')", CssUtils.normalizeCssProperty(" url( 'quoted Url')"));
107+
assertEquals("url(haveEscapedEndBracket\\))", CssUtils.normalizeCssProperty("url( haveEscapedEndBracket\\) )"));
108+
}
100109
}

0 commit comments

Comments
 (0)