Skip to content

Commit 3c699ba

Browse files
committed
Parse invalid unicode escapes as Windows-1252 instead [Fixes #1034]
1 parent 0f7e0cc commit 3c699ba

2 files changed

Lines changed: 43 additions & 0 deletions

File tree

src/main/java/org/jsoup/parser/Tokeniser.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@ final class Tokeniser {
1313
static final char replacementChar = '\uFFFD'; // replaces null character
1414
private static final char[] notCharRefCharsSorted = new char[]{'\t', '\n', '\r', '\f', ' ', '<', '&'};
1515

16+
// Some illegal character escapes are parsed by browsers as windows-1252 instead. See issue #1034
17+
static final int win1252ExtensionsStart = 0x80;
18+
static final int[] win1252Extensions = new int[] {
19+
// we could build this manually, but Windows-1252 is not a standard java charset so that could break on
20+
// some platforms - this table is verified with a test
21+
0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
22+
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F,
23+
0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
24+
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178,
25+
};
26+
1627
static {
1728
Arrays.sort(notCharRefCharsSorted);
1829
}
@@ -148,6 +159,12 @@ int[] consumeCharacterReference(Character additionalAllowedCharacter, boolean in
148159
codeRef[0] = replacementChar;
149160
return codeRef;
150161
} else {
162+
// fix illegal unicode characters to match browser behavior
163+
if (charval >= win1252ExtensionsStart && charval < win1252ExtensionsStart + win1252Extensions.length) {
164+
characterReferenceError("character is not a valid unicode code point");
165+
charval = win1252Extensions[charval - win1252ExtensionsStart];
166+
}
167+
151168
// todo: implement number replacement table
152169
// todo: check for extra illegal unicode points as parse errors
153170
codeRef[0] = charval;

src/test/java/org/jsoup/parser/TokeniserTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.jsoup.parser;
22

3+
import java.io.UnsupportedEncodingException;
34
import org.jsoup.Jsoup;
45
import org.jsoup.nodes.Attribute;
56
import org.jsoup.nodes.Comment;
@@ -154,4 +155,29 @@ public void bufferUpInAttributeVal() {
154155
assertEquals(title, child.getWholeText());
155156
assertEquals(title, doc.title());
156157
}
158+
159+
@Test public void cp1252Entities() {
160+
assertEquals("\u20ac", Jsoup.parse("&#0128;").text());
161+
assertEquals("\u201a", Jsoup.parse("&#0130;").text());
162+
assertEquals("\u20ac", Jsoup.parse("&#x80;").text());
163+
}
164+
165+
@Test public void cp1252EntitiesProduceError() {
166+
Parser parser = new Parser(new HtmlTreeBuilder());
167+
parser.setTrackErrors(10);
168+
assertEquals("\u20ac", parser.parseInput("<html><body>&#0128;</body></html>", "").text());
169+
assertEquals(1, parser.getErrors().size());
170+
}
171+
172+
@Test public void cp1252SubstitutionTable() throws UnsupportedEncodingException {
173+
for (int i = 0; i < Tokeniser.win1252Extensions.length; i++) {
174+
String s = new String(new byte[]{ (byte) (i + Tokeniser.win1252ExtensionsStart) }, "Windows-1252");
175+
assertEquals(1, s.length());
176+
177+
// some of these characters are illegal
178+
if (s.charAt(0) == '\ufffd') { continue; }
179+
180+
assertEquals("At: " + i, s.charAt(0), Tokeniser.win1252Extensions[i]);
181+
}
182+
}
157183
}

0 commit comments

Comments
 (0)