diff --git a/src/main/java/ezvcard/io/json/JCardWriter.java b/src/main/java/ezvcard/io/json/JCardWriter.java index fb1a500b1..4b2325254 100644 --- a/src/main/java/ezvcard/io/json/JCardWriter.java +++ b/src/main/java/ezvcard/io/json/JCardWriter.java @@ -172,6 +172,7 @@ protected void _write(VCard vcard, List properties) throws IOExce String group = property.getGroup(); String name = scribe.getPropertyName().toLowerCase(); VCardParameters parameters = scribe.prepareParameters(property, targetVersion, vcard); + removeUnsupportedParameters(parameters); VCardDataType dataType = scribe.dataType(property, targetVersion); writer.writeProperty(group, name, parameters, dataType, value); @@ -182,7 +183,7 @@ protected void _write(VCard vcard, List properties) throws IOExce setCurrentValue(previousValue); } -/** + /** * If this object has a {@link JsonGenerator), and the generator has an * output context, gets the current value of the output context. * @@ -193,7 +194,7 @@ private Object getCurrentValue() { return (generator == null) ? null : generator.getCurrentValue(); } -/** + /** * If this object has a {@link JsonGenerator), and the generator has an * output context, sets the current value of the output context. * @@ -241,6 +242,16 @@ public void setPrettyPrinter(PrettyPrinter prettyPrinter) { writer.setPrettyPrinter(prettyPrinter); } + /** + * Removes parameters that are not supported by jCard. + * @param parameters the property parameters + */ + private void removeUnsupportedParameters(VCardParameters parameters) { + parameters.setCharset(null); + parameters.setEncoding(null); + parameters.setValue(null); + } + /** * Flushes the jCard data stream. * @throws IOException if there's a problem flushing the stream diff --git a/src/main/java/ezvcard/io/text/VCardWriter.java b/src/main/java/ezvcard/io/text/VCardWriter.java index b27bcb9b6..95d344734 100644 --- a/src/main/java/ezvcard/io/text/VCardWriter.java +++ b/src/main/java/ezvcard/io/text/VCardWriter.java @@ -24,6 +24,7 @@ import ezvcard.io.SkipMeException; import ezvcard.io.StreamWriter; import ezvcard.io.scribe.VCardPropertyScribe; +import ezvcard.parameter.Encoding; import ezvcard.parameter.VCardParameters; import ezvcard.property.Address; import ezvcard.property.BinaryProperty; @@ -328,6 +329,7 @@ protected void _write(VCard vcard, List propertiesToAdd) throws I handleValueParameter(property, scribe, parameters); handleLabelParameter(property, parameters); + handleQuotedPrintableEncodingParameter(property, parameters); writer.writeProperty(property.getGroup(), scribe.getPropertyName(), new VObjectParameters(parameters.getMap()), value); @@ -431,6 +433,23 @@ private void handleLabelParameter(VCardProperty property, VCardParameters parame parameters.setLabel(label); } + /** + * Disables quoted-printable encoding on the given property if the target + * vCard version does not support this encoding scheme. + * @param property the property + * @param parameters the property parameters + */ + private void handleQuotedPrintableEncodingParameter(VCardProperty property, VCardParameters parameters) { + if (targetVersion == VCardVersion.V2_1) { + return; + } + + if (parameters.getEncoding() == Encoding.QUOTED_PRINTABLE) { + parameters.setEncoding(null); + parameters.setCharset(null); + } + } + /** * @see TargetApplication#OUTLOOK */ diff --git a/src/main/java/ezvcard/io/xml/XCardDocument.java b/src/main/java/ezvcard/io/xml/XCardDocument.java index 96b720847..072219af9 100644 --- a/src/main/java/ezvcard/io/xml/XCardDocument.java +++ b/src/main/java/ezvcard/io/xml/XCardDocument.java @@ -723,6 +723,7 @@ private Element marshalProperty(VCardProperty property, VCard vcard) { //marshal the parameters VCardParameters parameters = scribe.prepareParameters(property, targetVersion, vcard); + removeUnsupportedParameters(parameters); if (!parameters.isEmpty()) { Element parametersElement = marshalParameters(parameters); Node firstChild = propertyElement.getFirstChild(); diff --git a/src/main/java/ezvcard/io/xml/XCardWriter.java b/src/main/java/ezvcard/io/xml/XCardWriter.java index 498ad9554..e8ee34b88 100644 --- a/src/main/java/ezvcard/io/xml/XCardWriter.java +++ b/src/main/java/ezvcard/io/xml/XCardWriter.java @@ -359,6 +359,8 @@ private void write(VCardProperty property, VCard vcard) throws SAXException { VCardPropertyScribe scribe = index.getPropertyScribe(property); VCardParameters parameters = scribe.prepareParameters(property, targetVersion, vcard); + removeUnsupportedParameters(parameters); + //get the property element to write Element propertyElement; if (property instanceof Xml) { diff --git a/src/main/java/ezvcard/io/xml/XCardWriterBase.java b/src/main/java/ezvcard/io/xml/XCardWriterBase.java index cbfa14853..4d1a7ee37 100644 --- a/src/main/java/ezvcard/io/xml/XCardWriterBase.java +++ b/src/main/java/ezvcard/io/xml/XCardWriterBase.java @@ -73,6 +73,16 @@ protected VCardVersion getTargetVersion() { return targetVersion; } + /** + * Removes parameters that are not supported by xCard. + * @param parameters the property parameters + */ + protected void removeUnsupportedParameters(VCardParameters parameters) { + parameters.setCharset(null); + parameters.setEncoding(null); + parameters.setValue(null); + } + /** * Registers the data type of an experimental parameter. Experimental * parameters use the "unknown" data type by default. diff --git a/src/test/java/ezvcard/io/json/JCardWriterTest.java b/src/test/java/ezvcard/io/json/JCardWriterTest.java index fb5f327de..bffb33da4 100644 --- a/src/test/java/ezvcard/io/json/JCardWriterTest.java +++ b/src/test/java/ezvcard/io/json/JCardWriterTest.java @@ -8,6 +8,8 @@ import java.io.IOException; import java.io.StringWriter; +import ezvcard.parameter.Encoding; +import ezvcard.property.FormattedName; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -274,6 +276,32 @@ public void write_extended_property() throws Throwable { assertEquals(expected, sw.toString()); } + @Test + public void write_quoted_printable_encoding_not_supported() throws Exception { + StringWriter sw = new StringWriter(); + JCardWriter writer = new JCardWriter(sw); + writer.setAddProdId(false); + + VCard vcard = new VCard(); + FormattedName fn = vcard.setFormattedName("Ömür Öde"); + fn.getParameters().setEncoding(Encoding.QUOTED_PRINTABLE); + fn.getParameters().setCharset("UTF-8"); + writer.write(vcard); + + writer.close(); + + //@formatter:off + String expected = + "[\"vcard\"," + + "[" + + "[\"version\",{},\"text\",\"4.0\"]," + + "[\"fn\",{},\"text\",\"Ömür Öde\"]" + + "]" + + "]"; + //@formatter:on + assertEquals(expected, sw.toString()); + } + @Test public void skipMeException() throws Throwable { StringWriter sw = new StringWriter(); diff --git a/src/test/java/ezvcard/io/text/VCardWriterTest.java b/src/test/java/ezvcard/io/text/VCardWriterTest.java index a767087ba..0bf3501e2 100644 --- a/src/test/java/ezvcard/io/text/VCardWriterTest.java +++ b/src/test/java/ezvcard/io/text/VCardWriterTest.java @@ -7,6 +7,8 @@ import java.io.StringWriter; import java.nio.charset.Charset; +import ezvcard.parameter.Encoding; +import ezvcard.property.FormattedName; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -622,6 +624,68 @@ public void setTargetApplication_outlook() throws Throwable { } } + @Test + public void quoted_printable_encoding() throws Throwable { + VCard vcard = new VCard(); + FormattedName fn = vcard.setFormattedName("Ömür Öde"); + fn.getParameters().setEncoding(Encoding.QUOTED_PRINTABLE); + fn.getParameters().setCharset("UTF-8"); + + VCardVersion version = VCardVersion.V2_1; + { + StringWriter sw = new StringWriter(); + VCardWriter writer = new VCardWriter(sw, version); + writer.setAddProdId(false); + writer.write(vcard); + writer.close(); + String actual = sw.toString(); + + String expected = //@formatter:off + "BEGIN:VCARD\r\n" + + "VERSION:2.1\r\n" + + "FN;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:=C3=96m=C3=BCr =C3=96de\r\n" + + "END:VCARD\r\n"; //@formatter:on + + assertEquals(expected, actual); + } + + version = VCardVersion.V3_0; + { + StringWriter sw = new StringWriter(); + VCardWriter writer = new VCardWriter(sw, version); + writer.setAddProdId(false); + writer.write(vcard); + writer.close(); + String actual = sw.toString(); + + String expected = //@formatter:off + "BEGIN:VCARD\r\n" + + "VERSION:3.0\r\n" + + "FN:Ömür Öde\r\n" + + "END:VCARD\r\n"; //@formatter:on + + assertEquals(expected, actual); + } + + version = VCardVersion.V4_0; + { + StringWriter sw = new StringWriter(); + VCardWriter writer = new VCardWriter(sw, version); + writer.setAddProdId(false); + writer.write(vcard); + writer.close(); + String actual = sw.toString(); + + String expected = //@formatter:off + "BEGIN:VCARD\r\n" + + "VERSION:4.0\r\n" + + "FN:Ömür Öde\r\n" + + "END:VCARD\r\n"; //@formatter:on + + assertEquals(expected, actual); + } + } + @Test public void rfc6350_example() throws Throwable { VCard vcard = new VCard(); diff --git a/src/test/java/ezvcard/io/xml/XCardDocumentTest.java b/src/test/java/ezvcard/io/xml/XCardDocumentTest.java index a027d11fc..cd55b66c0 100644 --- a/src/test/java/ezvcard/io/xml/XCardDocumentTest.java +++ b/src/test/java/ezvcard/io/xml/XCardDocumentTest.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.io.InputStreamReader; +import ezvcard.parameter.Encoding; import org.custommonkey.xmlunit.XMLUnit; import org.junit.BeforeClass; import org.junit.Rule; @@ -679,6 +680,31 @@ public void write_utf8() throws Throwable { assertTrue(xml.matches(".*?\u019dote.*")); } + @Test + public void add_quoted_printable_encoding_not_supported() throws Throwable { + VCard vcard = new VCard(); + FormattedName fn = vcard.setFormattedName("Ömür Öde"); + fn.getParameters().setEncoding(Encoding.QUOTED_PRINTABLE); + fn.getParameters().setCharset("UTF-8"); + + XCardDocument xcard = new XCardDocument(); + XCardDocumentStreamWriter writer = xcard.writer(); + writer.setAddProdId(false); + writer.write(vcard); + + Document actual = xcard.getDocument(); + + String xml = //@formatter:off + "" + + "" + + "Ömür Öde" + + "" + + ""; //@formatter:on + Document expected = XmlUtils.toDocument(xml); + + assertXMLEqual(expected, actual); + } + @Test public void add_embedded_vcards_not_supported() throws Throwable { VCard vcard = new VCard(); diff --git a/src/test/java/ezvcard/io/xml/XCardWriterTest.java b/src/test/java/ezvcard/io/xml/XCardWriterTest.java index 5c2a0e150..d7bdc4781 100644 --- a/src/test/java/ezvcard/io/xml/XCardWriterTest.java +++ b/src/test/java/ezvcard/io/xml/XCardWriterTest.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.io.StringWriter; +import ezvcard.parameter.Encoding; import org.custommonkey.xmlunit.XMLUnit; import org.junit.Before; import org.junit.BeforeClass; @@ -693,6 +694,26 @@ public void write_embedded_vcards_not_supported() throws Exception { assertOutput(expected); } + @Test + public void write_quoted_printable_encoding_not_supported() throws Exception { + VCard vcard = new VCard(); + FormattedName fn = vcard.setFormattedName("Ömür Öde"); + fn.getParameters().setEncoding(Encoding.QUOTED_PRINTABLE); + fn.getParameters().setCharset("UTF-8"); + + writer.write(vcard); + writer.close(); + + String expected = //@formatter:off + "" + + "" + + "Ömür Öde" + + "" + + ""; //@formatter:on + + assertOutput(expected); + } + @Test public void write_rfc6351_example() throws Exception { VCard vcard = new VCard(); diff --git a/src/test/java/ezvcard/issue/Issue112.java b/src/test/java/ezvcard/issue/Issue112.java new file mode 100644 index 000000000..a45a4dbcd --- /dev/null +++ b/src/test/java/ezvcard/issue/Issue112.java @@ -0,0 +1,65 @@ +package ezvcard.issue; + +import ezvcard.Ezvcard; +import ezvcard.VCard; +import ezvcard.VCardVersion; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Michael Angstadt + * @see "https://github.com/mangstadt/ez-vcard/issues/112" + */ +public class Issue112 { + @Test + public void test() { + String vcardStr = //@formatter:off + "BEGIN:VCARD\r\n" + + "VERSION:2.1\r\n" + + "N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=C3=96=64=65;=C3=96=6D=C3=BC=72;;;\r\n" + + "FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=C3=96=6D=C3=BC=72=20=C3=96=64=65\r\n" + + "END:VCARD\r\n"; //@formatter:on + + VCard vcard = Ezvcard.parse(vcardStr).first(); + + VCardVersion version = VCardVersion.V2_1; + { + String actual = Ezvcard.write(vcard).version(version).prodId(false).go(); + String expected = //@formatter:off + "BEGIN:VCARD\r\n" + + "VERSION:2.1\r\n" + + "N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=C3=96de;=C3=96m=C3=BCr\r\n" + + "FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=C3=96m=C3=BCr =C3=96de\r\n" + + "END:VCARD\r\n"; //@formatter:on + + assertEquals(expected, actual); + } + + version = VCardVersion.V3_0; + { + String actual = Ezvcard.write(vcard).version(version).prodId(false).go(); + String expected = //@formatter:off + "BEGIN:VCARD\r\n" + + "VERSION:3.0\r\n" + + "N:Öde;Ömür\r\n" + + "FN:Ömür Öde\r\n" + + "END:VCARD\r\n"; //@formatter:on + + assertEquals(expected, actual); + } + + version = VCardVersion.V4_0; + { + String actual = Ezvcard.write(vcard).version(version).prodId(false).go(); + String expected = //@formatter:off + "BEGIN:VCARD\r\n" + + "VERSION:4.0\r\n" + + "N:Öde;Ömür;;;\r\n" + + "FN:Ömür Öde\r\n" + + "END:VCARD\r\n"; //@formatter:on + + assertEquals(expected, actual); + } + } +}