Add ENCODING=QUOTED-PRINTABLE support

git-svn-id: https://zxing.googlecode.com/svn/trunk@1317 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen 2010-04-24 09:28:10 +00:00
parent 52ba624795
commit 8a46200c72
2 changed files with 109 additions and 11 deletions

View file

@ -69,11 +69,14 @@ final class VCardResultParser extends ResultParser {
Vector matches = null;
int i = 0;
int max = rawText.length();
while (i < max) {
i = rawText.indexOf(prefix, i);
if (i < 0) {
break;
}
if (i > 0 && rawText.charAt(i - 1) != '\n') {
// then this didn't start a new token, we matched in the middle of something
i++;
@ -83,39 +86,81 @@ final class VCardResultParser extends ResultParser {
if (rawText.charAt(i) != ':' && rawText.charAt(i) != ';') {
continue;
}
int metadataStart = i;
while (rawText.charAt(i) != ':') { // Skip until a colon
i++;
}
i++; // skip colon
int start = i; // Found the start of a match here
while ((i = rawText.indexOf((int) '\n', i)) >= 0 && // Really, ends in \r\n
i < rawText.length() - 1 && // But if followed by tab or space,
(rawText.charAt(i+1) == ' ' || // this is only a continuation
rawText.charAt(i+1) == '\t')) {
i += 2;
boolean quotedPrintable = false;
if (i > metadataStart) {
// There was something after the tag, before colon
int j = metadataStart+1;
while (j <= i) {
if (rawText.charAt(j) == ';' || rawText.charAt(j) == ':') {
String metadata = rawText.substring(metadataStart+1, j);
int equals = metadata.indexOf('=');
if (equals >= 0) {
String key = metadata.substring(0, equals);
String value = metadata.substring(equals+1);
if (key.equalsIgnoreCase("ENCODING")) {
if (value.equalsIgnoreCase("QUOTED-PRINTABLE")) {
quotedPrintable = true;
}
}
}
metadataStart = j;
}
j++;
}
}
i++; // skip colon
int matchStart = i; // Found the start of a match here
while ((i = rawText.indexOf((int) '\n', i)) >= 0) { // Really, end in \r\n
if (i < rawText.length() - 1 && // But if followed by tab or space,
(rawText.charAt(i+1) == ' ' || // this is only a continuation
rawText.charAt(i+1) == '\t')) {
i += 2; // Skip \n and continutation whitespace
} else if (quotedPrintable && // If preceded by = in quoted printable
(rawText.charAt(i-1) == '=' || // this is a continuation
rawText.charAt(i-2) == '=')) {
i++; // Skip \n
} else {
break;
}
}
if (i < 0) {
// No terminating end character? uh, done. Set i such that loop terminates and break
i = max;
} else if (i > start) {
} else if (i > matchStart) {
// found a match
if (matches == null) {
matches = new Vector(3); // lazy init
matches = new Vector(1); // lazy init
}
if (rawText.charAt(i-1) == '\r') {
i--; // Back up over \r, which really should be there
}
String element = rawText.substring(start, i);
String element = rawText.substring(matchStart, i);
if (trim) {
element = element.trim();
}
element = stripContinuationCRLF(element);
if (quotedPrintable) {
element = decodeQuotedPrintable(element);
} else {
element = stripContinuationCRLF(element);
}
matches.addElement(element);
i++;
} else {
i++;
}
}
if (matches == null || matches.isEmpty()) {
return null;
}
@ -146,6 +191,50 @@ final class VCardResultParser extends ResultParser {
return result.toString();
}
private static String decodeQuotedPrintable(String value) {
int length = value.length();
StringBuffer result = new StringBuffer(length);
for (int i = 0; i < length; i++) {
char c = value.charAt(i);
switch (c) {
case '\r':
case '\n':
break;
case '=':
if (i < length - 2) {
char nextChar = value.charAt(i+1);
if (nextChar == '\r' || nextChar == '\n') {
// Ignore, it's just a continuation symbol
} else {
char nextNextChar = value.charAt(i+2);
try {
int encodedChar = 16 * toHexValue(nextChar) + toHexValue(nextNextChar);
result.append((char) encodedChar);
} catch (IllegalArgumentException iae) {
// continue, assume it was incorrectly encoded
}
i += 2;
}
}
break;
default:
result.append(c);
}
}
return result.toString();
}
private static int toHexValue(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
} else if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
} else if (c >= 'a' && c <= 'f') {
return c - 'a' + 10;
}
throw new IllegalArgumentException();
}
static String matchSingleVCardPrefixedField(String prefix, String rawText, boolean trim) {
String[] values = matchVCardPrefixedField(prefix, rawText, trim);
return values == null ? null : values[0];

View file

@ -60,6 +60,15 @@ public final class AddressBookParsedResultTestCase extends TestCase {
new String[] {"5555555555" }, "Company", null, null, "This is the memo.");
}
public void testQuotedPrintable() {
doTest("BEGIN:VCARD\r\nADR;HOME;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;;" +
"=35=38=20=4C=79=6E=62=72=6F=6F=6B=0D=0A=43=\r\n" +
"=4F=20=36=39=39=\r\n" +
"=32=36;;;\r\nEND:VCARD",
null, null, null, new String[] {"58 Lynbrook\r\nCO 69926"},
null, null, null, null, null, null);
}
private static void doTest(String contents,
String title,
String[] names,