From 1cf9eaaeba610d8e820d64d4196fb480c3eb858c Mon Sep 17 00:00:00 2001 From: Blake Date: Wed, 17 Jan 2024 07:49:19 -0500 Subject: [PATCH] Support for platforms where SJIS is unavailable (#1747) * Add support for embedded JREs where SJIS isn't available (cherry picked from commit 67896f97042f5a9772f0ebcfdbb91f275d86e087) * Add support for embedded JREs where EUC_JP isn't available (cherry picked from commit 979d3b648982b151b7a1e779e58d445086911b31) --- .../com/google/zxing/common/StringUtils.java | 30 +++++++++++++++--- .../decoder/DecodedBitStreamParser.java | 4 +++ .../google/zxing/qrcode/encoder/Encoder.java | 31 +++++++++++++------ 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/google/zxing/common/StringUtils.java b/core/src/main/java/com/google/zxing/common/StringUtils.java index b749d838b..0a773715f 100644 --- a/core/src/main/java/com/google/zxing/common/StringUtils.java +++ b/core/src/main/java/com/google/zxing/common/StringUtils.java @@ -32,7 +32,17 @@ import com.google.zxing.DecodeHintType; public final class StringUtils { private static final Charset PLATFORM_DEFAULT_ENCODING = Charset.defaultCharset(); - public static final Charset SHIFT_JIS_CHARSET = Charset.forName("SJIS"); + public static final Charset SHIFT_JIS_CHARSET; + static { + Charset sjisCharset; + try { + sjisCharset = Charset.forName("SJIS"); + } catch (UnsupportedCharsetException ucee) { + // Can happen on JREs without lib/charsets.jar + sjisCharset = null; + } + SHIFT_JIS_CHARSET = sjisCharset; + } public static final Charset GB2312_CHARSET; static { Charset gb2312Charset; @@ -44,10 +54,20 @@ public final class StringUtils { } GB2312_CHARSET = gb2312Charset; } - private static final Charset EUC_JP = Charset.forName("EUC_JP"); + private static final Charset EUC_JP; + static { + Charset eucJpCharset; + try { + eucJpCharset = Charset.forName("EUC_JP"); + } catch (UnsupportedCharsetException ucee) { + // Can happen on JREs without lib/charsets.jar + eucJpCharset = null; + } + EUC_JP = eucJpCharset; + } private static final boolean ASSUME_SHIFT_JIS = - SHIFT_JIS_CHARSET.equals(PLATFORM_DEFAULT_ENCODING) || - EUC_JP.equals(PLATFORM_DEFAULT_ENCODING); + (SHIFT_JIS_CHARSET != null && SHIFT_JIS_CHARSET.equals(PLATFORM_DEFAULT_ENCODING)) || + (EUC_JP != null && EUC_JP.equals(PLATFORM_DEFAULT_ENCODING)); // Retained for ABI compatibility with earlier versions public static final String SHIFT_JIS = "SJIS"; @@ -101,7 +121,7 @@ public final class StringUtils { // which should be by far the most common encodings. int length = bytes.length; boolean canBeISO88591 = true; - boolean canBeShiftJIS = true; + boolean canBeShiftJIS = SHIFT_JIS_CHARSET != null; boolean canBeUTF8 = true; int utf8BytesLeft = 0; int utf2BytesChars = 0; diff --git a/core/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java b/core/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java index 6aa955333..e23ca545b 100644 --- a/core/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java +++ b/core/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java @@ -211,6 +211,10 @@ final class DecodedBitStreamParser { private static void decodeKanjiSegment(BitSource bits, StringBuilder result, int count) throws FormatException { + if (StringUtils.SHIFT_JIS_CHARSET == null) { + // Not supported without charset support + throw FormatException.getFormatInstance(); + } // Don't crash trying to read more bits than we have available. if (count * 13 > bits.available()) { throw FormatException.getFormatInstance(); diff --git a/core/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java b/core/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java index cc093205e..97f316ddd 100644 --- a/core/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java +++ b/core/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java @@ -29,6 +29,7 @@ import com.google.zxing.qrcode.decoder.Version; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; import java.util.Collection; import java.util.Map; @@ -91,7 +92,11 @@ public final class Encoder { Charset encoding = DEFAULT_BYTE_MODE_ENCODING; boolean hasEncodingHint = hints != null && hints.containsKey(EncodeHintType.CHARACTER_SET); if (hasEncodingHint) { - encoding = Charset.forName(hints.get(EncodeHintType.CHARACTER_SET).toString()); + try { + encoding = Charset.forName(hints.get(EncodeHintType.CHARACTER_SET).toString()); + } catch (UnsupportedCharsetException ignore) { + //ignore the encodingHint and use the DEFAULT_BYTE_MODE_ENCODING + } } if (hasCompactionHint) { @@ -105,15 +110,15 @@ public final class Encoder { version = rn.getVersion(); } else { - + // Pick an encoding mode appropriate for the content. Note that this will not attempt to use // multiple modes / segments even if that were more efficient. mode = chooseMode(content, encoding); - + // This will store the header information, like mode and // length, as well as "header" segments like an ECI segment. BitArray headerBits = new BitArray(); - + // Append ECI segment if applicable if (mode == Mode.BYTE && hasEncodingHint) { CharacterSetECI eci = CharacterSetECI.getCharacterSetECI(encoding); @@ -121,21 +126,21 @@ public final class Encoder { appendECI(eci, headerBits); } } - + // Append the FNC1 mode header for GS1 formatted data if applicable if (hasGS1FormatHint) { // GS1 formatted codes are prefixed with a FNC1 in first position mode header appendModeInfo(Mode.FNC1_FIRST_POSITION, headerBits); } - + // (With ECI in place,) Write the mode marker appendModeInfo(mode, headerBits); - + // Collect data within the main segment, separately, to count its size if needed. Don't add it to // main payload yet. BitArray dataBits = new BitArray(); appendBytes(content, mode, dataBits, encoding); - + if (hints != null && hints.containsKey(EncodeHintType.QR_VERSION)) { int versionNumber = Integer.parseInt(hints.get(EncodeHintType.QR_VERSION).toString()); version = Version.getVersionForNumber(versionNumber); @@ -146,7 +151,7 @@ public final class Encoder { } else { version = recommendVersion(ecLevel, mode, headerBits, dataBits); } - + headerAndDataBits = new BitArray(); headerAndDataBits.appendBitArray(headerBits); // Find "length" of main segment and write it @@ -244,7 +249,9 @@ public final class Encoder { * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}. */ private static Mode chooseMode(String content, Charset encoding) { - if (StringUtils.SHIFT_JIS_CHARSET.equals(encoding) && isOnlyDoubleByteKanji(content)) { + if (StringUtils.SHIFT_JIS_CHARSET != null && + StringUtils.SHIFT_JIS_CHARSET.equals(encoding) && + isOnlyDoubleByteKanji(content)) { // Choose Kanji mode if all input are double-byte characters return Mode.KANJI; } @@ -605,6 +612,10 @@ public final class Encoder { } static void appendKanjiBytes(String content, BitArray bits) throws WriterException { + if (StringUtils.SHIFT_JIS_CHARSET == null) { + // Not supported without charset support + throw new WriterException("SJIS Charset not supported on this platform"); + } byte[] bytes = content.getBytes(StringUtils.SHIFT_JIS_CHARSET); if (bytes.length % 2 != 0) { throw new WriterException("Kanji byte size not even");