This is an automated email from the ASF dual-hosted git repository.

tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/main by this push:
     new 88f5737122 Decode legacy single-byte charsets as their windows 
supersets (#3593)
88f5737122 is described below

commit 88f5737122222e7c8def2e5808f6fcbbabaa3b39
Author: Tim Allison <[email protected]>
AuthorDate: Mon Nov 9 15:38:55 2026 -0500

    Decode legacy single-byte charsets as their windows supersets (#3593)
---
 CHANGES.txt                                        |  5 ++++
 .../org/apache/tika/detect/CharsetSupersets.java   |  9 +++++++
 .../apache/tika/detect/CharsetSupersetsTest.java   | 29 ++++++++++++++++++++++
 3 files changed, 43 insertions(+)

diff --git a/CHANGES.txt b/CHANGES.txt
index b92cbac3a0..b8f5f3a343 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -104,6 +104,11 @@ Release 4.3.0 - ???
 
   EXTRACTION AND FORMATS
 
+   * ISO-2022-JP is decoded as x-windows-50221, so NEC/IBM extension 
characters (circled
+     numbers, Roman numerals) no longer become U+FFFD. ISO-8859-1, ISO-8859-9, 
TIS-620 and
+     ISO-8859-11 decode as windows-1252, windows-1254 and windows-874, as 
browsers do, when a
+     detector other than the default chain reports them (TIKA-5045).
+     
    * Binary files are no longer detected as text because of their name or a 
text header.
      When content has no magic and at least 5% control bytes, and does not 
decode as
      UTF-16 or UTF-32, a text type from the file name or Content-Type no 
longer wins: Terminal Reality
diff --git 
a/tika-core/src/main/java/org/apache/tika/detect/CharsetSupersets.java 
b/tika-core/src/main/java/org/apache/tika/detect/CharsetSupersets.java
index c9935feb3a..f862e37595 100644
--- a/tika-core/src/main/java/org/apache/tika/detect/CharsetSupersets.java
+++ b/tika-core/src/main/java/org/apache/tika/detect/CharsetSupersets.java
@@ -43,6 +43,10 @@ import java.util.Map;
  *   <li>GBK → GB18030 (GB18030 is a strict superset; enables 4-byte extension 
sequences)</li>
  *   <li>Shift_JIS → windows-31j (MS932 is a strict superset with NEC/IBM 
extensions)</li>
  *   <li>EUC-JP → x-eucJP-Open (EUC packing of the NEC/IBM vendor 
extensions)</li>
+ *   <li>ISO-2022-JP → x-windows-50221 (CP50221 adds the NEC/IBM 
extensions)</li>
+ *   <li>ISO-8859-1 → windows-1252, ISO-8859-9 → windows-1254, TIS-620 and 
ISO-8859-11 →
+ *       windows-874 (what browsers do: 0x80-0x9F are punctuation, not C1 
controls; the few
+ *       bytes the windows code page leaves undefined decode as U+FFFD)</li>
  * </ul>
  */
 public final class CharsetSupersets {
@@ -61,6 +65,11 @@ public final class CharsetSupersets {
         m.put("GBK",       "GB18030");
         m.put("Shift_JIS", "windows-31j");
         m.put("EUC-JP",    "x-eucJP-Open");
+        m.put("ISO-2022-JP", "x-windows-50221");
+        m.put("ISO-8859-1", "windows-1252");
+        m.put("ISO-8859-9", "windows-1254");
+        m.put("TIS-620",    "x-windows-874");
+        m.put("x-iso-8859-11", "x-windows-874");
         SUPERSET_MAP = Collections.unmodifiableMap(m);
     }
 
diff --git 
a/tika-core/src/test/java/org/apache/tika/detect/CharsetSupersetsTest.java 
b/tika-core/src/test/java/org/apache/tika/detect/CharsetSupersetsTest.java
index 738fad6196..d8cfd6d9e5 100644
--- a/tika-core/src/test/java/org/apache/tika/detect/CharsetSupersetsTest.java
+++ b/tika-core/src/test/java/org/apache/tika/detect/CharsetSupersetsTest.java
@@ -39,6 +39,7 @@ public class CharsetSupersetsTest {
         assertEquals("GB18030", name("GBK"));
         assertEquals("windows-31j", name("Shift_JIS"));
         assertEquals("x-eucJP-Open", name("EUC-JP"));
+        assertEquals("x-windows-50221", name("ISO-2022-JP"));
     }
 
     @Test
@@ -63,5 +64,33 @@ public class CharsetSupersetsTest {
         byte[] eucjp = {(byte) 0xAD, (byte) 0xA1};
         assertEquals('\uFFFD', new String(eucjp, 
Charset.forName("EUC-JP")).charAt(0));
         assertEquals("\u2460", new String(eucjp, 
Charset.forName(name("EUC-JP"))));
+
+        byte[] jis = 
"\u65e5\u2460".getBytes(Charset.forName("x-windows-50221"));
+        assertEquals('\uFFFD', new String(jis, 
Charset.forName("ISO-2022-JP")).charAt(1));
+        assertEquals("\u65e5\u2460", new String(jis, 
Charset.forName(name("ISO-2022-JP"))));
+    }
+
+    @Test
+    public void singleByteDecodeAsBrowserCodePage() {
+        assertEquals("windows-1252", name("ISO-8859-1"));
+        assertEquals("windows-1254", name("ISO-8859-9"));
+        assertEquals("x-windows-874", name("TIS-620"));
+        assertEquals("x-windows-874", name("ISO-8859-11"));
+        // 0x92 is a C1 control under ISO-8859-1, a right single quote on real 
pages
+        assertEquals("\u2019", new String(new byte[]{(byte) 0x92}, 
Charset.forName(name("ISO-8859-1"))));
+        for (String narrow : new String[]{"ISO-8859-1", "ISO-8859-9", 
"TIS-620", "ISO-8859-11"}) {
+            Charset n = Charset.forName(narrow);
+            Charset w = Charset.forName(name(narrow));
+            for (int b = 0; b < 256; b++) {
+                if (b >= 0x80 && b < 0xA0) {
+                    continue;
+                }
+                byte[] one = {(byte) b};
+                String expected = new String(one, n);
+                if (expected.charAt(0) != '\uFFFD') {
+                    assertEquals(expected, new String(one, w), narrow + " byte 
" + b);
+                }
+            }
+        }
     }
 }

Reply via email to