PDF417HighLevelEncoder.java (zxing-zxing-3.4.1) | : | PDF417HighLevelEncoder.java (zxing-zxing-3.5.0) | ||
---|---|---|---|---|
skipping to change at line 25 | skipping to change at line 25 | |||
*/ | */ | |||
/* | /* | |||
* This file has been modified from its original form in Barcode4J. | * This file has been modified from its original form in Barcode4J. | |||
*/ | */ | |||
package com.google.zxing.pdf417.encoder; | package com.google.zxing.pdf417.encoder; | |||
import com.google.zxing.WriterException; | import com.google.zxing.WriterException; | |||
import com.google.zxing.common.CharacterSetECI; | import com.google.zxing.common.CharacterSetECI; | |||
import com.google.zxing.common.ECIInput; | ||||
import com.google.zxing.common.MinimalECIInput; | ||||
import java.math.BigInteger; | import java.math.BigInteger; | |||
import java.nio.charset.Charset; | import java.nio.charset.Charset; | |||
import java.nio.charset.CharsetEncoder; | import java.nio.charset.CharsetEncoder; | |||
import java.nio.charset.StandardCharsets; | import java.nio.charset.StandardCharsets; | |||
import java.util.Arrays; | import java.util.Arrays; | |||
/** | /** | |||
* PDF417 high-level encoder following the algorithm described in ISO/IEC 15438: 2001(E) in | * PDF417 high-level encoder following the algorithm described in ISO/IEC 15438: 2001(E) in | |||
* annex P. | * annex P. | |||
skipping to change at line 162 | skipping to change at line 164 | |||
/** | /** | |||
* Performs high-level encoding of a PDF417 message using the algorithm descri bed in annex P | * Performs high-level encoding of a PDF417 message using the algorithm descri bed in annex P | |||
* of ISO/IEC 15438:2001(E). If byte compaction has been selected, then only b yte compaction | * of ISO/IEC 15438:2001(E). If byte compaction has been selected, then only b yte compaction | |||
* is used. | * is used. | |||
* | * | |||
* @param msg the message | * @param msg the message | |||
* @param compaction compaction mode to use | * @param compaction compaction mode to use | |||
* @param encoding character encoding used to encode in default or byte compac tion | * @param encoding character encoding used to encode in default or byte compac tion | |||
* or {@code null} for default / not applicable | * or {@code null} for default / not applicable | |||
* @param autoECI encode input minimally using multiple ECIs if needed | ||||
* If autoECI encoding is specified and additionally {@code encoding} is spe | ||||
cified, then the encoder | ||||
* will use the specified {@link Charset} for any character that can be enco | ||||
ded by it, regardless | ||||
* if a different encoding would lead to a more compact encoding. When no {@ | ||||
code encoding} is specified | ||||
* then charsets will be chosen so that the byte representation is minimal. | ||||
* @return the encoded message (the char values range from 0 to 928) | * @return the encoded message (the char values range from 0 to 928) | |||
*/ | */ | |||
static String encodeHighLevel(String msg, Compaction compaction, Charset encod | static String encodeHighLevel(String msg, Compaction compaction, Charset encod | |||
ing) throws WriterException { | ing, boolean autoECI) | |||
throws WriterException { | ||||
if (encoding == null && !autoECI) { | ||||
for (int i = 0; i < msg.length(); i++) { | ||||
if (msg.charAt(i) > 255) { | ||||
throw new WriterException("Non-encodable character detected: " + msg.c | ||||
harAt(i) + " (Unicode: " + | ||||
(int) msg.charAt(i) + | ||||
"). Consider specifying EncodeHintType.PDF417_AUTO_ECI and/or Enco | ||||
deTypeHint.CHARACTER_SET."); | ||||
} | ||||
} | ||||
} | ||||
//the codewords 0..928 are encoded as Unicode characters | //the codewords 0..928 are encoded as Unicode characters | |||
StringBuilder sb = new StringBuilder(msg.length()); | StringBuilder sb = new StringBuilder(msg.length()); | |||
if (encoding == null) { | ECIInput input; | |||
encoding = DEFAULT_ENCODING; | if (autoECI) { | |||
} else if (!DEFAULT_ENCODING.equals(encoding)) { | input = new MinimalECIInput(msg, encoding, -1); | |||
CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding.na | } else { | |||
me()); | input = new NoECIInput(msg); | |||
if (eci != null) { | if (encoding == null) { | |||
encodingECI(eci.getValue(), sb); | encoding = DEFAULT_ENCODING; | |||
} else if (!DEFAULT_ENCODING.equals(encoding)) { | ||||
CharacterSetECI eci = CharacterSetECI.getCharacterSetECI(encoding); | ||||
if (eci != null) { | ||||
encodingECI(eci.getValue(), sb); | ||||
} | ||||
} | } | |||
} | } | |||
int len = msg.length(); | int len = input.length(); | |||
int p = 0; | int p = 0; | |||
int textSubMode = SUBMODE_ALPHA; | int textSubMode = SUBMODE_ALPHA; | |||
// User selected encoding mode | // User selected encoding mode | |||
switch (compaction) { | switch (compaction) { | |||
case TEXT: | case TEXT: | |||
encodeText(msg, p, len, sb, textSubMode); | encodeText(input, p, len, sb, textSubMode); | |||
break; | break; | |||
case BYTE: | case BYTE: | |||
byte[] msgBytes = msg.getBytes(encoding); | if (autoECI) { | |||
encodeBinary(msgBytes, p, msgBytes.length, BYTE_COMPACTION, sb); | encodeMultiECIBinary(input, 0, input.length(), TEXT_COMPACTION, sb); | |||
} else { | ||||
byte[] msgBytes = input.toString().getBytes(encoding); | ||||
encodeBinary(msgBytes, p, msgBytes.length, BYTE_COMPACTION, sb); | ||||
} | ||||
break; | break; | |||
case NUMERIC: | case NUMERIC: | |||
sb.append((char) LATCH_TO_NUMERIC); | sb.append((char) LATCH_TO_NUMERIC); | |||
encodeNumeric(msg, p, len, sb); | encodeNumeric(input, p, len, sb); | |||
break; | break; | |||
default: | default: | |||
int encodingMode = TEXT_COMPACTION; //Default mode, see 4.4.2.1 | int encodingMode = TEXT_COMPACTION; //Default mode, see 4.4.2.1 | |||
while (p < len) { | while (p < len) { | |||
int n = determineConsecutiveDigitCount(msg, p); | while (p < len && input.isECI(p)) { | |||
encodingECI(input.getECIValue(p), sb); | ||||
p++; | ||||
} | ||||
if (p >= len) { | ||||
break; | ||||
} | ||||
int n = determineConsecutiveDigitCount(input, p); | ||||
if (n >= 13) { | if (n >= 13) { | |||
sb.append((char) LATCH_TO_NUMERIC); | sb.append((char) LATCH_TO_NUMERIC); | |||
encodingMode = NUMERIC_COMPACTION; | encodingMode = NUMERIC_COMPACTION; | |||
textSubMode = SUBMODE_ALPHA; //Reset after latch | textSubMode = SUBMODE_ALPHA; //Reset after latch | |||
encodeNumeric(msg, p, n, sb); | encodeNumeric(input, p, n, sb); | |||
p += n; | p += n; | |||
} else { | } else { | |||
int t = determineConsecutiveTextCount(msg, p); | int t = determineConsecutiveTextCount(input, p); | |||
if (t >= 5 || n == len) { | if (t >= 5 || n == len) { | |||
if (encodingMode != TEXT_COMPACTION) { | if (encodingMode != TEXT_COMPACTION) { | |||
sb.append((char) LATCH_TO_TEXT); | sb.append((char) LATCH_TO_TEXT); | |||
encodingMode = TEXT_COMPACTION; | encodingMode = TEXT_COMPACTION; | |||
textSubMode = SUBMODE_ALPHA; //start with submode alpha after la tch | textSubMode = SUBMODE_ALPHA; //start with submode alpha after la tch | |||
} | } | |||
textSubMode = encodeText(msg, p, t, sb, textSubMode); | textSubMode = encodeText(input, p, t, sb, textSubMode); | |||
p += t; | p += t; | |||
} else { | } else { | |||
int b = determineConsecutiveBinaryCount(msg, p, encoding); | int b = determineConsecutiveBinaryCount(input, p, autoECI ? null : encoding); | |||
if (b == 0) { | if (b == 0) { | |||
b = 1; | b = 1; | |||
} | } | |||
byte[] bytes = msg.substring(p, p + b).getBytes(encoding); | byte[] bytes = autoECI ? null : input.subSequence(p, p + b).toStri | |||
if (bytes.length == 1 && encodingMode == TEXT_COMPACTION) { | ng().getBytes(encoding); | |||
if (((bytes == null && b == 1) || (bytes != null && bytes.length = | ||||
= 1)) | ||||
&& encodingMode == TEXT_COMPACTION) { | ||||
//Switch for one byte (instead of latch) | //Switch for one byte (instead of latch) | |||
encodeBinary(bytes, 0, 1, TEXT_COMPACTION, sb); | if (autoECI) { | |||
encodeMultiECIBinary(input, p, 1, TEXT_COMPACTION, sb); | ||||
} else { | ||||
encodeBinary(bytes, 0, 1, TEXT_COMPACTION, sb); | ||||
} | ||||
} else { | } else { | |||
//Mode latch performed by encodeBinary() | //Mode latch performed by encodeBinary() | |||
encodeBinary(bytes, 0, bytes.length, encodingMode, sb); | if (autoECI) { | |||
encodeMultiECIBinary(input, p, p + b, encodingMode, sb); | ||||
} else { | ||||
encodeBinary(bytes, 0, bytes.length, encodingMode, sb); | ||||
} | ||||
encodingMode = BYTE_COMPACTION; | encodingMode = BYTE_COMPACTION; | |||
textSubMode = SUBMODE_ALPHA; //Reset after latch | textSubMode = SUBMODE_ALPHA; //Reset after latch | |||
} | } | |||
p += b; | p += b; | |||
} | } | |||
} | } | |||
} | } | |||
break; | break; | |||
} | } | |||
return sb.toString(); | return sb.toString(); | |||
} | } | |||
/** | /** | |||
* Encode parts of the message using Text Compaction as described in ISO/IEC 1 5438:2001(E), | * Encode parts of the message using Text Compaction as described in ISO/IEC 1 5438:2001(E), | |||
* chapter 4.4.2. | * chapter 4.4.2. | |||
* | * | |||
* @param msg the message | * @param input the input | |||
* @param startpos the start position within the message | * @param startpos the start position within the message | |||
* @param count the number of characters to encode | * @param count the number of characters to encode | |||
* @param sb receives the encoded codewords | * @param sb receives the encoded codewords | |||
* @param initialSubmode should normally be SUBMODE_ALPHA | * @param initialSubmode should normally be SUBMODE_ALPHA | |||
* @return the text submode in which this method ends | * @return the text submode in which this method ends | |||
*/ | */ | |||
private static int encodeText(CharSequence msg, | private static int encodeText(ECIInput input, | |||
int startpos, | int startpos, | |||
int count, | int count, | |||
StringBuilder sb, | StringBuilder sb, | |||
int initialSubmode) { | int initialSubmode) throws WriterException { | |||
StringBuilder tmp = new StringBuilder(count); | StringBuilder tmp = new StringBuilder(count); | |||
int submode = initialSubmode; | int submode = initialSubmode; | |||
int idx = 0; | int idx = 0; | |||
while (true) { | while (true) { | |||
char ch = msg.charAt(startpos + idx); | if (input.isECI(startpos + idx)) { | |||
switch (submode) { | encodingECI(input.getECIValue(startpos + idx), sb); | |||
case SUBMODE_ALPHA: | idx++; | |||
if (isAlphaUpper(ch)) { | } else { | |||
if (ch == ' ') { | char ch = input.charAt(startpos + idx); | |||
tmp.append((char) 26); //space | switch (submode) { | |||
case SUBMODE_ALPHA: | ||||
if (isAlphaUpper(ch)) { | ||||
if (ch == ' ') { | ||||
tmp.append((char) 26); //space | ||||
} else { | ||||
tmp.append((char) (ch - 65)); | ||||
} | ||||
} else { | } else { | |||
tmp.append((char) (ch - 65)); | if (isAlphaLower(ch)) { | |||
submode = SUBMODE_LOWER; | ||||
tmp.append((char) 27); //ll | ||||
continue; | ||||
} else if (isMixed(ch)) { | ||||
submode = SUBMODE_MIXED; | ||||
tmp.append((char) 28); //ml | ||||
continue; | ||||
} else { | ||||
tmp.append((char) 29); //ps | ||||
tmp.append((char) PUNCTUATION[ch]); | ||||
break; | ||||
} | ||||
} | } | |||
} else { | break; | |||
case SUBMODE_LOWER: | ||||
if (isAlphaLower(ch)) { | if (isAlphaLower(ch)) { | |||
submode = SUBMODE_LOWER; | if (ch == ' ') { | |||
tmp.append((char) 27); //ll | tmp.append((char) 26); //space | |||
continue; | } else { | |||
} else if (isMixed(ch)) { | tmp.append((char) (ch - 97)); | |||
submode = SUBMODE_MIXED; | } | |||
tmp.append((char) 28); //ml | ||||
continue; | ||||
} else { | } else { | |||
tmp.append((char) 29); //ps | if (isAlphaUpper(ch)) { | |||
tmp.append((char) PUNCTUATION[ch]); | tmp.append((char) 27); //as | |||
break; | tmp.append((char) (ch - 65)); | |||
//space cannot happen here, it is also in "Lower" | ||||
break; | ||||
} else if (isMixed(ch)) { | ||||
submode = SUBMODE_MIXED; | ||||
tmp.append((char) 28); //ml | ||||
continue; | ||||
} else { | ||||
tmp.append((char) 29); //ps | ||||
tmp.append((char) PUNCTUATION[ch]); | ||||
break; | ||||
} | ||||
} | } | |||
} | break; | |||
break; | case SUBMODE_MIXED: | |||
case SUBMODE_LOWER: | if (isMixed(ch)) { | |||
if (isAlphaLower(ch)) { | tmp.append((char) MIXED[ch]); | |||
if (ch == ' ') { | ||||
tmp.append((char) 26); //space | ||||
} else { | } else { | |||
tmp.append((char) (ch - 97)); | if (isAlphaUpper(ch)) { | |||
submode = SUBMODE_ALPHA; | ||||
tmp.append((char) 28); //al | ||||
continue; | ||||
} else if (isAlphaLower(ch)) { | ||||
submode = SUBMODE_LOWER; | ||||
tmp.append((char) 27); //ll | ||||
continue; | ||||
} else { | ||||
if (startpos + idx + 1 < count) { | ||||
if (!input.isECI(startpos + idx + 1) && isPunctuation(input.ch | ||||
arAt(startpos + idx + 1))) { | ||||
submode = SUBMODE_PUNCTUATION; | ||||
tmp.append((char) 25); //pl | ||||
continue; | ||||
} | ||||
} | ||||
tmp.append((char) 29); //ps | ||||
tmp.append((char) PUNCTUATION[ch]); | ||||
} | ||||
} | } | |||
} else { | break; | |||
if (isAlphaUpper(ch)) { | default: //SUBMODE_PUNCTUATION | |||
tmp.append((char) 27); //as | if (isPunctuation(ch)) { | |||
tmp.append((char) (ch - 65)); | ||||
//space cannot happen here, it is also in "Lower" | ||||
break; | ||||
} else if (isMixed(ch)) { | ||||
submode = SUBMODE_MIXED; | ||||
tmp.append((char) 28); //ml | ||||
continue; | ||||
} else { | ||||
tmp.append((char) 29); //ps | ||||
tmp.append((char) PUNCTUATION[ch]); | tmp.append((char) PUNCTUATION[ch]); | |||
break; | } else { | |||
} | ||||
} | ||||
break; | ||||
case SUBMODE_MIXED: | ||||
if (isMixed(ch)) { | ||||
tmp.append((char) MIXED[ch]); | ||||
} else { | ||||
if (isAlphaUpper(ch)) { | ||||
submode = SUBMODE_ALPHA; | submode = SUBMODE_ALPHA; | |||
tmp.append((char) 28); //al | tmp.append((char) 29); //al | |||
continue; | ||||
} else if (isAlphaLower(ch)) { | ||||
submode = SUBMODE_LOWER; | ||||
tmp.append((char) 27); //ll | ||||
continue; | continue; | |||
} else { | ||||
if (startpos + idx + 1 < count) { | ||||
char next = msg.charAt(startpos + idx + 1); | ||||
if (isPunctuation(next)) { | ||||
submode = SUBMODE_PUNCTUATION; | ||||
tmp.append((char) 25); //pl | ||||
continue; | ||||
} | ||||
} | ||||
tmp.append((char) 29); //ps | ||||
tmp.append((char) PUNCTUATION[ch]); | ||||
} | } | |||
} | } | |||
idx++; | ||||
if (idx >= count) { | ||||
break; | break; | |||
default: //SUBMODE_PUNCTUATION | } | |||
if (isPunctuation(ch)) { | ||||
tmp.append((char) PUNCTUATION[ch]); | ||||
} else { | ||||
submode = SUBMODE_ALPHA; | ||||
tmp.append((char) 29); //al | ||||
continue; | ||||
} | ||||
} | ||||
idx++; | ||||
if (idx >= count) { | ||||
break; | ||||
} | } | |||
} | } | |||
char h = 0; | char h = 0; | |||
int len = tmp.length(); | int len = tmp.length(); | |||
for (int i = 0; i < len; i++) { | for (int i = 0; i < len; i++) { | |||
boolean odd = (i % 2) != 0; | boolean odd = (i % 2) != 0; | |||
if (odd) { | if (odd) { | |||
h = (char) ((h * 30) + tmp.charAt(i)); | h = (char) ((h * 30) + tmp.charAt(i)); | |||
sb.append(h); | sb.append(h); | |||
} else { | } else { | |||
h = tmp.charAt(i); | h = tmp.charAt(i); | |||
} | } | |||
} | } | |||
if ((len % 2) != 0) { | if ((len % 2) != 0) { | |||
sb.append((char) ((h * 30) + 29)); //ps | sb.append((char) ((h * 30) + 29)); //ps | |||
} | } | |||
return submode; | return submode; | |||
} | } | |||
/** | /** | |||
* Encode all of the message using Byte Compaction as described in ISO/IEC 154 | ||||
38:2001(E) | ||||
* | ||||
* @param input the input | ||||
* @param startpos the start position within the message | ||||
* @param count the number of bytes to encode | ||||
* @param startmode the mode from which this method starts | ||||
* @param sb receives the encoded codewords | ||||
*/ | ||||
private static void encodeMultiECIBinary(ECIInput input, | ||||
int startpos, | ||||
int count, | ||||
int startmode, | ||||
StringBuilder sb) throws WriterExcepti | ||||
on { | ||||
final int end = Math.min(startpos + count, input.length()); | ||||
int localStart = startpos; | ||||
while (true) { | ||||
//encode all leading ECIs and advance localStart | ||||
while (localStart < end && input.isECI(localStart)) { | ||||
encodingECI(input.getECIValue(localStart), sb); | ||||
localStart++; | ||||
} | ||||
int localEnd = localStart; | ||||
//advance end until before the next ECI | ||||
while (localEnd < end && !input.isECI(localEnd)) { | ||||
localEnd++; | ||||
} | ||||
final int localCount = localEnd - localStart; | ||||
if (localCount <= 0) { | ||||
//done | ||||
break; | ||||
} else { | ||||
//encode the segment | ||||
encodeBinary(subBytes(input, localStart, localEnd), | ||||
0, localCount, localStart == startpos ? startmode : BYTE_COMPACTION, | ||||
sb); | ||||
localStart = localEnd; | ||||
} | ||||
} | ||||
} | ||||
static byte[] subBytes(ECIInput input, int start, int end) { | ||||
final int count = end - start; | ||||
byte[] result = new byte[count]; | ||||
for (int i = start; i < end; i++) { | ||||
result[i - start] = (byte) (input.charAt(i) & 0xff); | ||||
} | ||||
return result; | ||||
} | ||||
/** | ||||
* Encode parts of the message using Byte Compaction as described in ISO/IEC 1 5438:2001(E), | * Encode parts of the message using Byte Compaction as described in ISO/IEC 1 5438:2001(E), | |||
* chapter 4.4.3. The Unicode characters will be converted to binary using the cp437 | * chapter 4.4.3. The Unicode characters will be converted to binary using the cp437 | |||
* codepage. | * codepage. | |||
* | * | |||
* @param bytes the message converted to a byte array | * @param bytes the message converted to a byte array | |||
* @param startpos the start position within the message | * @param startpos the start position within the message | |||
* @param count the number of bytes to encode | * @param count the number of bytes to encode | |||
* @param startmode the mode from which this method starts | * @param startmode the mode from which this method starts | |||
* @param sb receives the encoded codewords | * @param sb receives the encoded codewords | |||
*/ | */ | |||
skipping to change at line 419 | skipping to change at line 516 | |||
idx += 6; | idx += 6; | |||
} | } | |||
} | } | |||
//Encode rest (remaining n<5 bytes if any) | //Encode rest (remaining n<5 bytes if any) | |||
for (int i = idx; i < startpos + count; i++) { | for (int i = idx; i < startpos + count; i++) { | |||
int ch = bytes[i] & 0xff; | int ch = bytes[i] & 0xff; | |||
sb.append((char) ch); | sb.append((char) ch); | |||
} | } | |||
} | } | |||
private static void encodeNumeric(String msg, int startpos, int count, StringB uilder sb) { | private static void encodeNumeric(ECIInput input, int startpos, int count, Str ingBuilder sb) { | |||
int idx = 0; | int idx = 0; | |||
StringBuilder tmp = new StringBuilder(count / 3 + 1); | StringBuilder tmp = new StringBuilder(count / 3 + 1); | |||
BigInteger num900 = BigInteger.valueOf(900); | BigInteger num900 = BigInteger.valueOf(900); | |||
BigInteger num0 = BigInteger.valueOf(0); | BigInteger num0 = BigInteger.valueOf(0); | |||
while (idx < count) { | while (idx < count) { | |||
tmp.setLength(0); | tmp.setLength(0); | |||
int len = Math.min(44, count - idx); | int len = Math.min(44, count - idx); | |||
String part = '1' + msg.substring(startpos + idx, startpos + idx + len); | String part = "1" + input.subSequence(startpos + idx, startpos + idx + len ); | |||
BigInteger bigint = new BigInteger(part); | BigInteger bigint = new BigInteger(part); | |||
do { | do { | |||
tmp.append((char) bigint.mod(num900).intValue()); | tmp.append((char) bigint.mod(num900).intValue()); | |||
bigint = bigint.divide(num900); | bigint = bigint.divide(num900); | |||
} while (!bigint.equals(num0)); | } while (!bigint.equals(num0)); | |||
//Reverse temporary string | //Reverse temporary string | |||
for (int i = tmp.length() - 1; i >= 0; i--) { | for (int i = tmp.length() - 1; i >= 0; i--) { | |||
sb.append(tmp.charAt(i)); | sb.append(tmp.charAt(i)); | |||
} | } | |||
skipping to change at line 469 | skipping to change at line 566 | |||
return PUNCTUATION[ch] != -1; | return PUNCTUATION[ch] != -1; | |||
} | } | |||
private static boolean isText(char ch) { | private static boolean isText(char ch) { | |||
return ch == '\t' || ch == '\n' || ch == '\r' || (ch >= 32 && ch <= 126); | return ch == '\t' || ch == '\n' || ch == '\r' || (ch >= 32 && ch <= 126); | |||
} | } | |||
/** | /** | |||
* Determines the number of consecutive characters that are encodable using nu meric compaction. | * Determines the number of consecutive characters that are encodable using nu meric compaction. | |||
* | * | |||
* @param msg the message | * @param input the input | |||
* @param startpos the start position within the message | * @param startpos the start position within the input | |||
* @return the requested character count | * @return the requested character count | |||
*/ | */ | |||
private static int determineConsecutiveDigitCount(CharSequence msg, int startp os) { | private static int determineConsecutiveDigitCount(ECIInput input, int startpos ) { | |||
int count = 0; | int count = 0; | |||
int len = msg.length(); | final int len = input.length(); | |||
int idx = startpos; | int idx = startpos; | |||
if (idx < len) { | if (idx < len) { | |||
char ch = msg.charAt(idx); | while (idx < len && !input.isECI(idx) && isDigit(input.charAt(idx))) { | |||
while (isDigit(ch) && idx < len) { | ||||
count++; | count++; | |||
idx++; | idx++; | |||
if (idx < len) { | ||||
ch = msg.charAt(idx); | ||||
} | ||||
} | } | |||
} | } | |||
return count; | return count; | |||
} | } | |||
/** | /** | |||
* Determines the number of consecutive characters that are encodable using te xt compaction. | * Determines the number of consecutive characters that are encodable using te xt compaction. | |||
* | * | |||
* @param msg the message | * @param input the input | |||
* @param startpos the start position within the message | * @param startpos the start position within the input | |||
* @return the requested character count | * @return the requested character count | |||
*/ | */ | |||
private static int determineConsecutiveTextCount(CharSequence msg, int startpo | private static int determineConsecutiveTextCount(ECIInput input, int startpos) | |||
s) { | { | |||
int len = msg.length(); | final int len = input.length(); | |||
int idx = startpos; | int idx = startpos; | |||
while (idx < len) { | while (idx < len) { | |||
char ch = msg.charAt(idx); | ||||
int numericCount = 0; | int numericCount = 0; | |||
while (numericCount < 13 && isDigit(ch) && idx < len) { | while (numericCount < 13 && idx < len && !input.isECI(idx) && isDigit(inpu t.charAt(idx))) { | |||
numericCount++; | numericCount++; | |||
idx++; | idx++; | |||
if (idx < len) { | ||||
ch = msg.charAt(idx); | ||||
} | ||||
} | } | |||
if (numericCount >= 13) { | if (numericCount >= 13) { | |||
return idx - startpos - numericCount; | return idx - startpos - numericCount; | |||
} | } | |||
if (numericCount > 0) { | if (numericCount > 0) { | |||
//Heuristic: All text-encodable chars or digits are binary encodable | //Heuristic: All text-encodable chars or digits are binary encodable | |||
continue; | continue; | |||
} | } | |||
ch = msg.charAt(idx); | ||||
//Check if character is encodable | //Check if character is encodable | |||
if (!isText(ch)) { | if (input.isECI(idx) || !isText(input.charAt(idx))) { | |||
break; | break; | |||
} | } | |||
idx++; | idx++; | |||
} | } | |||
return idx - startpos; | return idx - startpos; | |||
} | } | |||
/** | /** | |||
* Determines the number of consecutive characters that are encodable using bi nary compaction. | * Determines the number of consecutive characters that are encodable using bi nary compaction. | |||
* | * | |||
* @param msg the message | * @param input the input | |||
* @param startpos the start position within the message | * @param startpos the start position within the message | |||
* @param encoding the charset used to convert the message to a byte array | * @param encoding the charset used to convert the message to a byte array | |||
* @return the requested character count | * @return the requested character count | |||
*/ | */ | |||
private static int determineConsecutiveBinaryCount(String msg, int startpos, C harset encoding) | private static int determineConsecutiveBinaryCount(ECIInput input, int startpo s, Charset encoding) | |||
throws WriterException { | throws WriterException { | |||
CharsetEncoder encoder = encoding.newEncoder(); | CharsetEncoder encoder = encoding == null ? null : encoding.newEncoder(); | |||
int len = msg.length(); | int len = input.length(); | |||
int idx = startpos; | int idx = startpos; | |||
while (idx < len) { | while (idx < len) { | |||
char ch = msg.charAt(idx); | ||||
int numericCount = 0; | int numericCount = 0; | |||
while (numericCount < 13 && isDigit(ch)) { | int i = idx; | |||
while (numericCount < 13 && !input.isECI(i) && isDigit(input.charAt(i))) { | ||||
numericCount++; | numericCount++; | |||
//textCount++; | //textCount++; | |||
int i = idx + numericCount; | i = idx + numericCount; | |||
if (i >= len) { | if (i >= len) { | |||
break; | break; | |||
} | } | |||
ch = msg.charAt(i); | ||||
} | } | |||
if (numericCount >= 13) { | if (numericCount >= 13) { | |||
return idx - startpos; | return idx - startpos; | |||
} | } | |||
ch = msg.charAt(idx); | ||||
if (!encoder.canEncode(ch)) { | if (encoder != null && !encoder.canEncode(input.charAt(idx))) { | |||
assert input instanceof NoECIInput; | ||||
char ch = input.charAt(idx); | ||||
throw new WriterException("Non-encodable character detected: " + ch + " (Unicode: " + (int) ch + ')'); | throw new WriterException("Non-encodable character detected: " + ch + " (Unicode: " + (int) ch + ')'); | |||
} | } | |||
idx++; | idx++; | |||
} | } | |||
return idx - startpos; | return idx - startpos; | |||
} | } | |||
private static void encodingECI(int eci, StringBuilder sb) throws WriterExcept ion { | private static void encodingECI(int eci, StringBuilder sb) throws WriterExcept ion { | |||
if (eci >= 0 && eci < 900) { | if (eci >= 0 && eci < 900) { | |||
sb.append((char) ECI_CHARSET); | sb.append((char) ECI_CHARSET); | |||
skipping to change at line 583 | skipping to change at line 671 | |||
sb.append((char) (eci / 900 - 1)); | sb.append((char) (eci / 900 - 1)); | |||
sb.append((char) (eci % 900)); | sb.append((char) (eci % 900)); | |||
} else if (eci < 811800) { | } else if (eci < 811800) { | |||
sb.append((char) ECI_USER_DEFINED); | sb.append((char) ECI_USER_DEFINED); | |||
sb.append((char) (810900 - eci)); | sb.append((char) (810900 - eci)); | |||
} else { | } else { | |||
throw new WriterException("ECI number not in valid range from 0..811799, b ut was " + eci); | throw new WriterException("ECI number not in valid range from 0..811799, b ut was " + eci); | |||
} | } | |||
} | } | |||
private static final class NoECIInput implements ECIInput { | ||||
String input; | ||||
private NoECIInput(String input) { | ||||
this.input = input; | ||||
} | ||||
public int length() { | ||||
return input.length(); | ||||
} | ||||
public char charAt(int index) { | ||||
return input.charAt(index); | ||||
} | ||||
public boolean isECI(int index) { | ||||
return false; | ||||
} | ||||
public int getECIValue(int index) { | ||||
return -1; | ||||
} | ||||
public boolean haveNCharacters(int index, int n) { | ||||
return index + n <= input.length(); | ||||
} | ||||
public CharSequence subSequence(int start, int end) { | ||||
return input.subSequence(start, end); | ||||
} | ||||
public String toString() { | ||||
return input; | ||||
} | ||||
} | ||||
} | } | |||
End of changes. 58 change blocks. | ||||
134 lines changed or deleted | 268 lines changed or added |