Skip to content

Commit

Permalink
Added outlook compatibility flag (issue 33).
Browse files Browse the repository at this point in the history
  • Loading branch information
mangstadt committed Oct 24, 2015
1 parent 394746d commit c8da9a4
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 21 deletions.
26 changes: 26 additions & 0 deletions src/main/java/ezvcard/io/chain/ChainingTextWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
public class ChainingTextWriter extends ChainingWriter<ChainingTextWriter> {
private VCardVersion version;
private boolean caretEncoding = false;
private boolean outlook = false;

/**
* @param vcards the vCards to write
Expand Down Expand Up @@ -88,6 +89,30 @@ public ChainingTextWriter caretEncoding(boolean enable) {
return this;
}

/**
* <p>
* Sets whether the vCards should be fully compatible with Microsoft Outlook
* mail clients. This setting is disabled by default.
* </p>
* <p>
* Enabling this setting may make the vCards incompatible with other vCard
* consumers.
* </p>
* <p>
* Enabling this setting adds an empty line after all base64-encoded
* property values for vCards with versions 2.1 and 3.0. This setting has no
* effect on 4.0 vCards, or on vCards that do not have any properties with
* base64-encoded values.
* </p>
* @param enable true to enable, false to disable (defaults to false).
* @return this
* @see VCardWriter#setOutlookCompatibility(boolean)
*/
public ChainingTextWriter outlook(boolean enable) {
this.outlook = enable;
return this;
}

@Override
public ChainingTextWriter prodId(boolean include) {
return super.prodId(include);
Expand Down Expand Up @@ -165,6 +190,7 @@ private void go(VCardWriter writer) throws IOException {
writer.setAddProdId(prodId);
writer.setCaretEncodingEnabled(caretEncoding);
writer.setVersionStrict(versionStrict);
writer.setOutlookCompatibility(outlook);
if (index != null) {
writer.setScribeIndex(index);
}
Expand Down
81 changes: 70 additions & 11 deletions src/main/java/ezvcard/io/text/VCardWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
public class VCardWriter extends StreamWriter implements Flushable {
private final VCardRawWriter writer;
private final LinkedList<Boolean> prodIdStack = new LinkedList<Boolean>();
private boolean outlookCompatibility = false;

/**
* @param out the output stream to write to
Expand Down Expand Up @@ -208,6 +209,42 @@ public void setCaretEncodingEnabled(boolean enable) {
writer.setCaretEncodingEnabled(enable);
}

/**
* <p>
* Gets whether vCards generated by this writer will be compatible with
* Microsoft Outlook mail clients. This setting is disabled by default.
* </p>
* <p>
* Enabling this setting adds an empty line after all base64-encoded
* property values for vCard versions 2.1 and 3.0.
* </p>
* @return true if enabled, false if disabled (defaults to false).
*/
public boolean isOutlookCompatibility() {
return outlookCompatibility;
}

/**
* <p>
* Sets whether vCards generated by this writer should be fully compatible
* with Microsoft Outlook mail clients. This setting is disabled by default.
* </p>
* <p>
* Enabling this setting may make the vCards incompatible with other vCard
* consumers.
* </p>
* <p>
* Enabling this setting adds an empty line after all base64-encoded
* property values for vCards with versions 2.1 and 3.0. This setting has no
* effect on 4.0 vCards, or on vCards that do not have any properties with
* base64-encoded values.
* </p>
* @param enable true to enable, false to disable (defaults to false).
*/
public void setOutlookCompatibility(boolean enable) {
outlookCompatibility = enable;
}

@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void _write(VCard vcard, List<VCardProperty> propertiesToAdd) throws IOException {
Expand Down Expand Up @@ -283,22 +320,44 @@ protected void _write(VCard vcard, List<VCardProperty> propertiesToAdd) throws I
//write the property
writer.writeProperty(property.getGroup(), scribe.getPropertyName(), parameters, value);

/*
* Outlook 2010 requires an empty line after base64 values (at
* least, some of the time).
* See: https://code.google.com/p/ez-vcard/issues/detail?id=21
*/
if (targetVersion != VCardVersion.V4_0 && property instanceof BinaryProperty) {
BinaryProperty binaryProperty = (BinaryProperty) property;
if (binaryProperty.getData() != null) {
writer.getFoldedLineWriter().writeln("");
}
}
insertEmptyLineForOutlook(targetVersion, property);
}

writer.writeEndComponent("VCARD");
}

/**
* Outlook 2010 requires an empty line after base64 values (at
* least, some of the time).
* @param targetVersion the vCard version
* @param property the property being written
* @see https://github.com/mangstadt/ez-vcard/issues/21
*/
private void insertEmptyLineForOutlook(VCardVersion targetVersion, VCardProperty property) throws IOException {
if (!outlookCompatibility) {
//setting not enabled
return;
}

if (targetVersion == VCardVersion.V4_0) {
//only do this for 2.1 and 3.0 vCards
return;
}

if (!(property instanceof BinaryProperty)) {
//property does not have binary data
return;
}

BinaryProperty<?> binaryProperty = (BinaryProperty<?>) property;
if (binaryProperty.getData() == null) {
//property value is not base64-encoded
return;
}

writer.getFoldedLineWriter().writeln("");
}

/**
* Determines if the given default data type is "date-and-or-time" and the
* given data type is time-based. Properties that meet this criteria should
Expand Down
55 changes: 55 additions & 0 deletions src/test/java/ezvcard/EzvcardTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
import ezvcard.io.LuckyNumType;
import ezvcard.io.LuckyNumType.LuckyNumScribe;
import ezvcard.io.xml.XCardNamespaceContext;
import ezvcard.parameter.ImageType;
import ezvcard.property.FormattedName;
import ezvcard.property.Photo;
import ezvcard.util.XCardBuilder;
import ezvcard.util.XmlUtils;

Expand Down Expand Up @@ -456,6 +458,59 @@ public void write_versionStrict() throws Exception {
assertTrue(actual.contains("\r\nMAILER:"));
}

@Test
public void write_outlook() throws Exception {
byte data[] = "data".getBytes();
VCard vcard = new VCard();
vcard.addPhoto(new Photo(data, ImageType.JPEG));

//default
{
String actual = Ezvcard.write(vcard).prodId(false).version(VCardVersion.V2_1).go();

//@formatter:off
String expected =
"BEGIN:VCARD\r\n" +
"VERSION:2.1\r\n" +
"PHOTO;ENCODING=base64;JPEG:ZGF0YQ==\r\n" +
"END:VCARD\r\n";
//@formatter:on

assertEquals(expected, actual);
}

//true
{
String actual = Ezvcard.write(vcard).prodId(false).version(VCardVersion.V2_1).outlook(true).go();

//@formatter:off
String expected =
"BEGIN:VCARD\r\n" +
"VERSION:2.1\r\n" +
"PHOTO;ENCODING=base64;JPEG:ZGF0YQ==\r\n" +
"\r\n" +
"END:VCARD\r\n";
//@formatter:on

assertEquals(expected, actual);
}

//false
{
String actual = Ezvcard.write(vcard).prodId(false).version(VCardVersion.V2_1).outlook(false).go();

//@formatter:off
String expected =
"BEGIN:VCARD\r\n" +
"VERSION:2.1\r\n" +
"PHOTO;ENCODING=base64;JPEG:ZGF0YQ==\r\n" +
"END:VCARD\r\n";
//@formatter:on

assertEquals(expected, actual);
}
}

@Test
public void writeXml_go() throws Exception {
VCard vcard = new VCard();
Expand Down
51 changes: 41 additions & 10 deletions src/test/java/ezvcard/io/text/VCardWriterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -412,30 +412,41 @@ class DateAndOrTimeProperty extends VCardProperty {
}

@Test
public void newline_after_base64() throws Throwable {
public void outlookCompatibility() throws Throwable {
VCard vcard = new VCard();
byte data[] = "foobar".getBytes();
vcard.addKey(new Key(data, KeyType.X509));
vcard.addPhoto(new Photo(data, ImageType.JPEG));
vcard.addLogo(new Logo("http://www.company.com/logo.png", ImageType.PNG));
vcard.addNote("note");

{
StringWriter sw = new StringWriter();
VCardWriter vcw = new VCardWriter(sw, VCardVersion.V2_1);
vcw.setAddProdId(false);
vcw.write(vcard);
VCardWriter writer = new VCardWriter(sw, VCardVersion.V2_1);
writer.setAddProdId(false);
writer.write(vcard);
writer.setOutlookCompatibility(true);
writer.write(vcard);

String actual = sw.toString();

//@formatter:off
String expected =
"BEGIN:VCARD\r\n" +
"VERSION:2.1\r\n" +
"KEY;ENCODING=base64;X509:Zm9vYmFy\r\n" +
"PHOTO;ENCODING=base64;JPEG:Zm9vYmFy\r\n" +
"LOGO;PNG;VALUE=url:http://www.company.com/logo.png\r\n" +
"NOTE:note\r\n" +
"END:VCARD\r\n" +
"BEGIN:VCARD\r\n" +
"VERSION:2.1\r\n" +
"KEY;ENCODING=base64;X509:Zm9vYmFy\r\n" +
"\r\n" +
"PHOTO;ENCODING=base64;JPEG:Zm9vYmFy\r\n" +
"\r\n" +
"LOGO;PNG;VALUE=url:http://www.company.com/logo.png\r\n" +
"NOTE:note\r\n" +
"END:VCARD\r\n";
//@formatter:on

Expand All @@ -444,21 +455,31 @@ public void newline_after_base64() throws Throwable {

{
StringWriter sw = new StringWriter();
VCardWriter vcw = new VCardWriter(sw, VCardVersion.V3_0);
vcw.setAddProdId(false);
vcw.write(vcard);
VCardWriter writer = new VCardWriter(sw, VCardVersion.V3_0);
writer.setAddProdId(false);
writer.write(vcard);
writer.setOutlookCompatibility(true);
writer.write(vcard);

String actual = sw.toString();

//@formatter:off
String expected =
"BEGIN:VCARD\r\n" +
"VERSION:3.0\r\n" +
"KEY;ENCODING=b;TYPE=x509:Zm9vYmFy\r\n" +
"PHOTO;ENCODING=b;TYPE=jpeg:Zm9vYmFy\r\n" +
"LOGO;TYPE=png;VALUE=uri:http://www.company.com/logo.png\r\n" +
"NOTE:note\r\n" +
"END:VCARD\r\n" +
"BEGIN:VCARD\r\n" +
"VERSION:3.0\r\n" +
"KEY;ENCODING=b;TYPE=x509:Zm9vYmFy\r\n" +
"\r\n" +
"PHOTO;ENCODING=b;TYPE=jpeg:Zm9vYmFy\r\n" +
"\r\n" +
"LOGO;TYPE=png;VALUE=uri:http://www.company.com/logo.png\r\n" +
"NOTE:note\r\n" +
"END:VCARD\r\n";
//@formatter:on

Expand All @@ -467,9 +488,11 @@ public void newline_after_base64() throws Throwable {

{
StringWriter sw = new StringWriter();
VCardWriter vcw = new VCardWriter(sw, VCardVersion.V4_0);
vcw.setAddProdId(false);
vcw.write(vcard);
VCardWriter writer = new VCardWriter(sw, VCardVersion.V4_0);
writer.setAddProdId(false);
writer.write(vcard);
writer.setOutlookCompatibility(true);
writer.write(vcard);

String actual = sw.toString();

Expand All @@ -480,6 +503,14 @@ public void newline_after_base64() throws Throwable {
"KEY:data:application/x509;base64,Zm9vYmFy\r\n" +
"PHOTO:data:image/jpeg;base64,Zm9vYmFy\r\n" +
"LOGO;MEDIATYPE=image/png:http://www.company.com/logo.png\r\n" +
"NOTE:note\r\n" +
"END:VCARD\r\n" +
"BEGIN:VCARD\r\n" +
"VERSION:4.0\r\n" +
"KEY:data:application/x509;base64,Zm9vYmFy\r\n" +
"PHOTO:data:image/jpeg;base64,Zm9vYmFy\r\n" +
"LOGO;MEDIATYPE=image/png:http://www.company.com/logo.png\r\n" +
"NOTE:note\r\n" +
"END:VCARD\r\n";
//@formatter:on

Expand Down

0 comments on commit c8da9a4

Please sign in to comment.