context: 
  /* =======================================================================
   * vCard Library for .NET
   * Copyright (c) 2007-2009 David Pinch; http://wwww.thoughtproject.com
   * See LICENSE.TXT for licensing information.
   * ======================================================================= */
  
  using System;
  using System.IO;
  using System.Text;
  
  namespace Thought.vCards
  {
  
      /// <summary>
      ///     Implements the standard vCard 2.1 and 3.0 text formats.
      /// </summary>
      public class vCardStandardWriter : vCardWriter
      {
  
          private bool embedInternetImages;
          private bool embedLocalImages;
          private vCardStandardWriterOptions options;
          private string productId;
  
  
          /// <summary>
          ///     The characters that are escaped per the original
          ///     vCard specification.
          /// </summary>
          private readonly char[] standardEscapedCharacters =
              new char[] { ',', '\\', ';', '\r', '\n' };
  
  
          /// <summary>
          ///     The characters that are escaped by Microsoft Outlook.
          /// </summary>
          /// <remarks>
          ///     Microsoft Outlook does not property decode escaped
          ///     commas in values.
          /// </remarks>
          private readonly char[] outlookEscapedCharacters =
              new char[] { '\\', ';', '\r', '\n' };
  
  
          /// <summary>
          ///     Creates a new instance of the standard writer.
          /// </summary>
          /// <remarks>
          ///     The standard writer is configured to create vCard
          ///     files in the highest supported version.  This is
          ///     currently version 3.0.
          /// </remarks>
          public vCardStandardWriter()
          {
              this.embedLocalImages = true;
          }
  
  
          /// <summary>
          ///     Indicates whether images that reference Internet
          ///     URLs should be embedded in the output.  If not, 
          ///     then a URL is written instead.
          /// </summary>
          public bool EmbedInternetImages
          {
              get
              {
                  return this.embedInternetImages;
              }
              set
              {
                  this.embedInternetImages = value;
              }
          }
  
  
          /// <summary>
          ///     Indicates whether or not references to local
          ///     images should be embedded in the vCard.  If not,
          ///     then a local file reference is written instead.
          /// </summary>
          public bool EmbedLocalImages
          {
              get
              {
                  return this.embedLocalImages;
              }
              set
              {
                  this.embedLocalImages = value;
              }
          }
  
  
          /// <summary>
          ///     Extended options for the vCard writer.
          /// </summary>
          public vCardStandardWriterOptions Options
          {
              get
              {
                  return this.options;
              }
              set
              {
                  this.options = value;
              }
          }
  
  
          /// <summary>
          ///     The product ID to use when writing a vCard.
          /// </summary>
          public string ProductId
          {
              get
              {
                  return this.productId;
              }
              set
              {
                  this.productId = value;
              }
          }
  
  
          // The next set of functions generate raw vCard properties
          // from an object in the vCard object model.  Every method
          // has a collection (into which new properties should be
          // placed) and a vCard object (from which the properties
          // should be generated).
  
          #region [ BuildProperties ]
  
          /// <summary>
          ///     Builds a collection of standard properties based on
          ///     the specified vCard.
          /// </summary>
          /// <returns>
          ///     A <see cref="vCardPropertyCollection"/> that contains all
          ///     properties for the current vCard, including the header
          ///     and footer properties.
          /// </returns>
          /// <seealso cref="vCard"/>
          /// <seealso cref="vCardProperty"/>
          private vCardPropertyCollection BuildProperties(
              vCard card)
          {
  
              vCardPropertyCollection properties =
                  new vCardPropertyCollection();
  
              // The BEGIN:VCARD line marks the beginning of
              // the vCard contents.  Later it will end with END:VCARD.
              // See section 2.1.1 of RFC 2426.
  
              properties.Add(new vCardProperty("BEGIN", "VCARD"));
  
              BuildProperties_NAME(
                  properties,
                  card);
  
              BuildProperties_SOURCE(
                  properties,
                  card);
  
              BuildProperties_N(
                  properties,
                  card);
  
              BuildProperties_FN(
                  properties,
                  card);
  
              BuildProperties_ADR(
                  properties,
                  card);
  
              BuildProperties_BDAY(
                  properties,
                  card);
  
              BuildProperties_CATEGORIES(
                  properties,
                  card);
  
              BuildProperties_CLASS(
                  properties,
                  card);
  
              BuildProperties_EMAIL(
                  properties,
                  card);
  
              BuildProperties_GEO(
                  properties,
                  card);
  
              BuildProperties_KEY(
                  properties,
                  card);
  
              BuildProperties_LABEL(
                  properties,
                  card);
  
              BuildProperties_MAILER(
                  properties,
                  card);
  
              BuildProperties_NICKNAME(
                  properties,
                  card);
  
              BuildProperties_NOTE(
                  properties,
                  card);
  
              BuildProperties_ORG(
                  properties,
                  card);
  
              BuildProperties_PHOTO(
                  properties,
                  card);
  
              BuildProperties_PRODID(
                  properties,
                  card);
  
              BuildProperties_REV(
                  properties,
                  card);
  
              BuildProperties_ROLE(
                  properties,
                  card);
  
              BuildProperties_TEL(
                  properties,
                  card);
  
              BuildProperties_TITLE(
                  properties,
                  card);
  
              BuildProperties_TZ(
                  properties,
                  card);
  
              BuildProperties_UID(
                  properties,
                  card);
  
              BuildProperties_URL(
                  properties,
                  card);
  
              BuildProperties_X_WAB_GENDER(
                  properties,
                  card);
  
              // The end of the vCard is marked with an END:VCARD.
  
              properties.Add(new vCardProperty("END", "VCARD"));
              return properties;
  
          }
  
          #endregion
  
          #region [ BuildProperties_ADR ]
  
          /// <summary>
          ///     Builds ADR properties.
          /// </summary>
          private void BuildProperties_ADR(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              foreach (vCardDeliveryAddress address in card.DeliveryAddresses)
              {
  
                  // Do not generate a postal address (ADR) property
                  // if the entire address is blank.
  
                  if (
                      (!string.IsNullOrEmpty(address.City)) ||
                      (!string.IsNullOrEmpty(address.Country)) ||
                      (!string.IsNullOrEmpty(address.PostalCode)) ||
                      (!string.IsNullOrEmpty(address.Region)) ||
                      (!string.IsNullOrEmpty(address.Street)))
                  {
  
                      // The ADR property contains the following
                      // subvalues in order.  All are required:
                      //
                      //   - Post office box
                      //   - Extended address
                      //   - Street address
                      //   - Locality (e.g. city)
                      //   - Region (e.g. province or state)
                      //   - Postal code (e.g. ZIP code)
                      //   - Country name
  
                      vCardValueCollection values = new vCardValueCollection(';');
  
                      values.Add(string.Empty);
                      values.Add(string.Empty);
                      values.Add(address.Street);
                      values.Add(address.City);
                      values.Add(address.Region);
                      values.Add(address.PostalCode);
                      values.Add(address.Country);
  
                      vCardProperty property =
                          new vCardProperty("ADR", values);
  
                      if (address.IsDomestic)
                          property.Subproperties.Add("DOM");
  
                      if (address.IsInternational)
                          property.Subproperties.Add("INTL");
  
                      if (address.IsParcel)
                          property.Subproperties.Add("PARCEL");
  
                      if (address.IsPostal)
                          property.Subproperties.Add("POSTAL");
  
                      if (address.IsHome)
                          property.Subproperties.Add("HOME");
  
                      if (address.IsWork)
                          property.Subproperties.Add("WORK");
  
                      properties.Add(property);
  
                  }
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_BDAY ]
  
          /// <summary>
          ///     Builds the BDAY property.
          /// </summary>
          private void BuildProperties_BDAY(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              // The BDAY property indicates the birthdate
              // of the person.  The output format here is based on
              // Microsoft Outlook, which writes the date as YYYMMDD.
  
              if (card.BirthDate.HasValue)
              {
  
                  vCardProperty property =
                      new vCardProperty("BDAY", card.BirthDate.Value);
  
                  properties.Add(property);
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_CATEGORIES ]
  
          private void BuildProperties_CATEGORIES(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              if (card.Categories.Count > 0)
              {
  
                  vCardValueCollection values = new vCardValueCollection(',');
  
                  foreach (string category in card.Categories)
                  {
  
                      if (!string.IsNullOrEmpty(category))
                          values.Add(category);
                  }
  
                  properties.Add(
                      new vCardProperty("CATEGORIES", values));
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_CLASS ]
  
          private void BuildProperties_CLASS(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              vCardProperty property = new vCardProperty("CLASS");
  
              switch (card.AccessClassification)
              {
  
                  case vCardAccessClassification.Unknown:
                      // No value is written.
                      return;
  
                  case vCardAccessClassification.Confidential:
                      property.Value = "CONFIDENTIAL";
                      break;
  
                  case vCardAccessClassification.Private:
                      property.Value = "PRIVATE";
                      break;
  
                  case vCardAccessClassification.Public:
                      property.Value = "PUBLIC";
                      break;
  
                  default:
                      throw new NotSupportedException();
  
              }
  
              properties.Add(property);
  
          }
  
          #endregion
  
          #region [ BuildProperties_EMAIL ]
  
          /// <summary>
          ///     Builds EMAIL properties.
          /// </summary>
          private void BuildProperties_EMAIL(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              // The EMAIL property contains an electronic
              // mail address for the purpose.  A vCard may contain
              // as many email addresses as needed.  The format also
              // supports various vendors, such as CompuServe addresses
              // and Internet SMTP addresses.
              //
              // EMAIL;INTERNET:support@fairmetric.com
  
              foreach (vCardEmailAddress emailAddress in card.EmailAddresses)
              {
  
                  vCardProperty property = new vCardProperty();
                  property.Name = "EMAIL";
                  property.Value = emailAddress.Address;
  
                  if (emailAddress.IsPreferred)
                  {
                      property.Subproperties.Add("PREF");
                  }
  
                  switch (emailAddress.EmailType)
                  {
  
                      case vCardEmailAddressType.Internet:
                          property.Subproperties.Add("INTERNET");
                          break;
  
                      case vCardEmailAddressType.AOL:
                          property.Subproperties.Add("AOL");
                          break;
  
                      case vCardEmailAddressType.AppleLink:
                          property.Subproperties.Add("AppleLink");
                          break;
  
                      case vCardEmailAddressType.AttMail:
                          property.Subproperties.Add("ATTMail");
                          break;
  
                      case vCardEmailAddressType.CompuServe:
                          property.Subproperties.Add("CIS");
                          break;
  
                      case vCardEmailAddressType.eWorld:
                          property.Subproperties.Add("eWorld");
                          break;
  
                      case vCardEmailAddressType.IBMMail:
                          property.Subproperties.Add("IBMMail");
                          break;
  
                      case vCardEmailAddressType.MCIMail:
                          property.Subproperties.Add("MCIMail");
                          break;
  
                      case vCardEmailAddressType.PowerShare:
                          property.Subproperties.Add("POWERSHARE");
                          break;
  
                      case vCardEmailAddressType.Prodigy:
                          property.Subproperties.Add("PRODIGY");
                          break;
  
                      case vCardEmailAddressType.Telex:
                          property.Subproperties.Add("TLX");
                          break;
  
                      case vCardEmailAddressType.X400:
                          property.Subproperties.Add("X400");
                          break;
  
                      default:
                          property.Subproperties.Add("INTERNET");
                          break;
  
                  }
  
                  properties.Add(property);
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_FN ]
  
          private void BuildProperties_FN(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              // The FN property indicates the formatted 
              // name of the person.  This can be something
              // like "John Smith".
  
              if (!string.IsNullOrEmpty(card.FormattedName))
              {
  
                  vCardProperty property =
                      new vCardProperty("FN", card.FormattedName);
  
                  properties.Add(property);
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_GEO ]
  
          /// <summary>
          ///     Builds the GEO property.
          /// </summary>
          private void BuildProperties_GEO(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              // The GEO properties contains the latitude and
              // longitude of the person or company of the vCard.
  
              if (card.Latitude.HasValue && card.Longitude.HasValue)
              {
  
                  vCardProperty property = new vCardProperty();
  
                  property.Name = "GEO";
                  property.Value =
                      card.Latitude.ToString() + ";" + card.Longitude.ToString();
  
                  properties.Add(property);
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_KEY ]
  
          /// <summary>
          ///     Builds KEY properties.
          /// </summary>
          private void BuildProperties_KEY(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              // A KEY field contains an embedded security certificate.
  
              foreach (vCardCertificate certificate in card.Certificates)
              {
  
                  vCardProperty property = new vCardProperty();
  
                  property.Name = "KEY";
                  property.Value = certificate.Data;
                  property.Subproperties.Add(certificate.KeyType);
  
                  properties.Add(property);
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_LABEL ]
  
          private void BuildProperties_LABEL(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              foreach (vCardDeliveryLabel label in card.DeliveryLabels)
              {
  
                  if (label.Text.Length > 0)
                  {
  
                      vCardProperty property = new vCardProperty("LABEL", label.Text);
  
                      if (label.IsDomestic)
                          property.Subproperties.Add("DOM");
  
                      if (label.IsInternational)
                          property.Subproperties.Add("INTL");
  
                      if (label.IsParcel)
                          property.Subproperties.Add("PARCEL");
  
                      if (label.IsPostal)
                          property.Subproperties.Add("POSTAL");
  
                      if (label.IsHome)
                          property.Subproperties.Add("HOME");
  
                      if (label.IsWork)
                          property.Subproperties.Add("WORK");
  
                      // Give a hint to use QUOTED-PRINTABLE.
  
                      property.Subproperties.Add("ENCODING", "QUOTED-PRINTABLE");
                      properties.Add(property);
  
  
                  }
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_MAILER ]
  
          /// <summary>
          ///     Builds the MAILER property.
          /// </summary>
          private void BuildProperties_MAILER(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              // The MAILER property indicates the software that
              // generated the vCard.  See section 2.4.3 of the
              // vCard 2.1 specification.  Support is not widespread.
  
              if (!string.IsNullOrEmpty(card.Mailer))
              {
  
                  vCardProperty property =
                      new vCardProperty("MAILER", card.Mailer);
  
                  properties.Add(property);
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_N ]
  
          private void BuildProperties_N(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              // The property has the following components: Family Name,
              // Given Name, Additional Names, Name Prefix, and Name
              // Suffix.  Example:
              //
              //   N:Pinch;David
              //   N:Pinch;David;John
              //
              // The N property is required (see section 3.1.2 of RFC 2426).
  
              vCardValueCollection values = new vCardValueCollection(';');
              values.Add(card.FamilyName);
              values.Add(card.GivenName);
              values.Add(card.AdditionalNames);
              values.Add(card.NamePrefix);
              values.Add(card.NameSuffix);
  
              vCardProperty property = new vCardProperty("N", values);
  
              properties.Add(property);
  
          }
  
          #endregion
  
          #region [ BuildProperties_NAME ]
  
          private void BuildProperties_NAME(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              if (!string.IsNullOrEmpty(card.DisplayName))
              {
  
                  vCardProperty property =
                      new vCardProperty("NAME", card.DisplayName);
  
                  properties.Add(property);
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_NICKNAME ]
  
          /// <summary>
          ///     Builds the NICKNAME property.
          /// </summary>
          private void BuildProperties_NICKNAME(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              // The NICKNAME property specifies the familiar name
              // of the person, such as Jim.  This is defined in
              // section 3.1.3 of RFC 2426.  Multiple names can
              // be listed, separated by commas.
  
              if (card.Nicknames.Count > 0)
              {
  
                  // A NICKNAME property is a comma-separated
                  // list of values.  Create a value list and
                  // add the nicknames collection to it.
  
                  vCardValueCollection values = new vCardValueCollection(',');
                  values.Add(card.Nicknames);
  
                  // Create the new properties with each name separated
                  // by a comma.
  
                  vCardProperty property =
                      new vCardProperty("NICKNAME", values);
  
                  properties.Add(property);
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_NOTE ]
  
          /// <summary>
          ///     Builds the NOTE property.
          /// </summary>
          private void BuildProperties_NOTE(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              foreach (vCardNote note in card.Notes)
              {
  
                  if (!string.IsNullOrEmpty(note.Text))
                  {
  
                      vCardProperty property = new vCardProperty();
  
                      property.Name = "NOTE";
                      property.Value = note.Text;
  
                      if (!string.IsNullOrEmpty(note.Language))
                      {
                          property.Subproperties.Add("language", note.Language);
                      }
  
                      property.Subproperties.Add("ENCODING", "QUOTED-PRINTABLE");
                      properties.Add(property);
  
                  }
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_ORG ]
  
          /// <summary>
          ///     Builds the ORG property.
          /// </summary>
          private void BuildProperties_ORG(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              // The ORG property specifies the name of the
              // person's company or organization. Example:
              //
              // ORG:FairMetric LLC
  
              if (!string.IsNullOrEmpty(card.Organization))
              {
  
                  vCardProperty property =
                      new vCardProperty("ORG", card.Organization);
  
                  properties.Add(property);
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_PHOTO ]
  
          private void BuildProperties_PHOTO(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              foreach (vCardPhoto photo in card.Photos)
              {
  
                  if (photo.Url == null)
                  {
  
                      // This photo does not have a URL associated
                      // with it.  Therefore a property can be
                      // generated only if the image data is loaded.
                      // Otherwise there is not enough information.
  
                      if (photo.IsLoaded)
                      {
  
                          properties.Add(
                              new vCardProperty("PHOTO", photo.GetBytes()));
  
                      }
  
                  }
                  else
                  {
  
                      // This photo has a URL associated with it.  The
                      // PHOTO property can either be linked as an image
                      // or embedded, if desired.
  
                      bool doEmbedded =
                          photo.Url.IsFile ? this.embedLocalImages : this.embedInternetImages;
  
                      if (doEmbedded)
                      {
  
                          // According to the settings of the card writer,
                          // this linked image should be embedded into the
                          // vCard data.  Attempt to fetch the data.
  
                          try
                          {
                              photo.Fetch();
                          }
                          catch
                          {
  
                              // An error was encountered.  The image can
                              // still be written as a link, however.
  
                              doEmbedded = false;
                          }
  
                      }
  
                      // At this point, doEmbedded is true only if (a) the
                      // writer was configured to embed the image, and (b)
                      // the image was successfully downloaded.
  
                      if (doEmbedded)
                      {
                          properties.Add(
                              new vCardProperty("PHOTO", photo.GetBytes()));
                      }
                      else
                      {
  
                          vCardProperty uriPhotoProperty =
                              new vCardProperty("PHOTO");
  
                          // Set the VALUE property to indicate that
                          // the data for the photo is a URI.
  
                          uriPhotoProperty.Subproperties.Add("VALUE", "URI");
                          uriPhotoProperty.Value = photo.Url.ToString();
  
                          properties.Add(uriPhotoProperty);
                      }
  
                  }
              }
          }
  
          #endregion
  
          #region [ BuildProperties_PRODID ]
  
          /// <summary>
          ///     Builds PRODID properties.
          /// </summary>
          private void BuildProperties_PRODID(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              if (!string.IsNullOrEmpty(card.ProductId))
              {
                  vCardProperty property = new vCardProperty();
                  property.Name = "PRODID";
                  property.Value = card.ProductId;
                  properties.Add(property);
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_REV ]
  
          /// <summary>
          ///     Builds the REV property.
          /// </summary>
          private void BuildProperties_REV(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              if (card.RevisionDate.HasValue)
              {
  
                  vCardProperty property =
                      new vCardProperty("REV", card.RevisionDate.Value.ToString());
  
                  properties.Add(property);
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_ROLE ]
  
          /// <summary>
          ///     Builds the ROLE property.
          /// </summary>
          private void BuildProperties_ROLE(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              // The ROLE property identifies the role of
              // the person at his/her organization.
  
              if (!string.IsNullOrEmpty(card.Role))
              {
  
                  vCardProperty property =
                      new vCardProperty("ROLE", card.Role);
  
                  properties.Add(property);
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_SOURCE ]
  
          /// <summary>
          ///     Builds SOURCE properties.
          /// </summary>
          private void BuildProperties_SOURCE(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              foreach (vCardSource source in card.Sources)
              {
  
                  vCardProperty property = new vCardProperty();
  
                  property.Name = "SOURCE";
                  property.Value = source.Uri.ToString();
  
                  if (!string.IsNullOrEmpty(source.Context))
                      property.Subproperties.Add("CONTEXT", source.Context);
  
                  properties.Add(property);
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_TEL ]
  
          /// <summary>
          ///     Builds TEL properties.
          /// </summary>
          private void BuildProperties_TEL(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              // The TEL property indicates a telephone number of
              // the person (including non-voice numbers like fax
              // and BBS numbers).
              //
              // TEL;VOICE;WORK:1-800-929-5805
  
              foreach (vCardPhone phone in card.Phones)
              {
  
                  // A telephone entry has the property name TEL and
                  // can have zero or more subproperties like FAX
                  // or HOME.  Examples:
                  //
                  //   TEL;HOME:+1-612-555-1212
                  //   TEL;FAX;HOME:+1-612-555-1212
  
                  vCardProperty property = new vCardProperty();
  
                  property.Name = "TEL";
  
                  if (phone.IsBBS)
                      property.Subproperties.Add("BBS");
  
                  if (phone.IsCar)
                      property.Subproperties.Add("CAR");
  
                  if (phone.IsCellular)
                      property.Subproperties.Add("CELL");
  
                  if (phone.IsFax)
                      property.Subproperties.Add("FAX");
  
                  if (phone.IsHome)
                      property.Subproperties.Add("HOME");
  
                  if (phone.IsISDN)
                      property.Subproperties.Add("ISDN");
  
                  if (phone.IsMessagingService)
                      property.Subproperties.Add("MSG");
  
                  if (phone.IsModem)
                      property.Subproperties.Add("MODEM");
  
                  if (phone.IsPager)
                      property.Subproperties.Add("PAGER");
  
                  if (phone.IsPreferred)
                      property.Subproperties.Add("PREF");
  
                  if (phone.IsVideo)
                      property.Subproperties.Add("VIDEO");
  
                  if (phone.IsVoice)
                      property.Subproperties.Add("VOICE");
  
                  if (phone.IsWork)
                      property.Subproperties.Add("WORK");
  
                  property.Value = phone.FullNumber;
                  properties.Add(property);
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_TITLE ]
  
          private void BuildProperties_TITLE(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              // The TITLE property specifies the job title of 
              // the person.  Example:
              //
              // TITLE:Systems Analyst
              // TITLE:President
  
              if (!string.IsNullOrEmpty(card.Title))
              {
                  vCardProperty property =
                      new vCardProperty("TITLE", card.Title);
  
                  properties.Add(property);
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_TZ ]
  
          private void BuildProperties_TZ(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              if (!string.IsNullOrEmpty(card.TimeZone))
              {
                  properties.Add(new vCardProperty("TZ", card.TimeZone));
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_UID ]
  
          private void BuildProperties_UID(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              if (!string.IsNullOrEmpty(card.UniqueId))
              {
                  vCardProperty property = new vCardProperty();
                  property.Name = "UID";
                  property.Value = card.UniqueId;
                  properties.Add(property);
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_URL ]
  
          private void BuildProperties_URL(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              foreach (vCardWebsite webSite in card.Websites)
              {
  
                  if (!string.IsNullOrEmpty(webSite.Url))
                  {
                      vCardProperty property =
                          new vCardProperty("URL", webSite.Url.ToString());
  
                      if (webSite.IsWorkSite)
                          property.Subproperties.Add("WORK");
  
                      properties.Add(property);
                  }
  
              }
  
          }
  
          #endregion
  
          #region [ BuildProperties_X_WAB_GENDER ]
  
          private void BuildProperties_X_WAB_GENDER(
              vCardPropertyCollection properties,
              vCard card)
          {
  
              // The X-WAB-GENDER property is an extended (custom)
              // property supported by Microsoft Outlook.
  
              switch (card.Gender)
              {
                  case vCardGender.Female:
                      properties.Add(new vCardProperty("X-WAB-GENDER", "1"));
                      break;
  
                  case vCardGender.Male:
                      properties.Add(new vCardProperty("X-WAB-GENDER", "2"));
                      break;
  
              }
  
          }
  
          #endregion
  
          // The next set of functions translate raw values into
          // various string encodings.  A vCard file is a text file
          // with a defined format; values that break the format (such
          // as binary values or strings with ASCII control characters)
          // must be encoded.
  
          #region [ EncodeBase64(byte) ]
  
          /// <summary>
          ///     Converts a byte to a BASE64 string.
          /// </summary>
          public static string EncodeBase64(byte value)
          {
              return Convert.ToBase64String(new byte[] { value });
          }
  
          #endregion
  
          #region [ EncodeBase64(byte[]) ]
  
          /// <summary>
          ///     Converts a byte array to a BASE64 string.
          /// </summary>
          public static string EncodeBase64(byte[] value)
          {
              return Convert.ToBase64String(value);
          }
  
          #endregion
  
          #region [ EncodeBase64(int) ]
  
          /// <summary>
          ///     Converts an integer to a BASE64 string.
          /// </summary>
          public static string EncodeBase64(int value)
          {
  
              byte[] buffer = new byte[4];
  
              buffer[0] = (byte)(value);
              buffer[1] = (byte)(value >> 8);
              buffer[2] = (byte)(value >> 16);
              buffer[3] = (byte)(value >> 24);
  
              return Convert.ToBase64String(buffer);
          }
  
          #endregion
  
          #region [ EncodeEscaped(string) ]
  
          /// <summary>
          ///     Encodes a string using simple escape codes.
          /// </summary>
          public string EncodeEscaped(string value)
          {
  
              if (
                  (this.options & vCardStandardWriterOptions.IgnoreCommas) ==
                      vCardStandardWriterOptions.IgnoreCommas)
              {
                  return EncodeEscaped(value, outlookEscapedCharacters);
              }
              else
              {
                  return EncodeEscaped(value, standardEscapedCharacters);
              }
          }
  
          #endregion
  
          #region [ EncodeEscaped(string, char[]) ]
  
          /// <summary>
          ///     Encodes a character array using simple escape sequences.
          /// </summary>
          public static string EncodeEscaped(
              string value,
              char[] escaped)
          {
  
              if (escaped == null)
                  throw new ArgumentNullException("escaped");
  
              if (string.IsNullOrEmpty(value))
                  return value;
  
              StringBuilder buffer = new StringBuilder();
  
              int startIndex = 0;
  
              do
              {
  
                  // Get the index of the next character
                  // to be escaped (e.g. the next semicolon).
  
                  int nextIndex = value.IndexOfAny(escaped, startIndex);
  
                  if (nextIndex == -1)
                  {
                      // No more characters need to be escaped.
                      // Any characters between the start index
                      // and the end of the string can be copied
                      // to the buffer.
  
                      buffer.Append(
                          value,
                          startIndex,
                          value.Length - startIndex);
  
                      break;
  
                  }
                  else
                  {
  
                      char replacement;
                      switch (value[nextIndex])
                      {
                          case '\n':
                              replacement = 'n';
                              break;
  
                          case '\r':
                              replacement = 'r';
                              break;
  
                          default:
                              replacement = value[nextIndex];
                              break;
  
                      }
  
                      buffer.Append(
                          value,
                          startIndex,
                          nextIndex - startIndex);
  
                      buffer.Append('\\');
                      buffer.Append(replacement);
  
                      startIndex = nextIndex + 1;
  
                  }
  
              } while (startIndex < value.Length);
  
              return buffer.ToString();
  
              // The following must be encoded:
              //
              // Backslash (\\)
              // Colon (\:)
              // Semicolon (\;)
  
          }
  
          #endregion
  
          #region [ EncodeQuotedPrintable ]
  
          /// <summary>
          ///     Converts a string to quoted-printable format.
          /// </summary>
          /// <param name="value">
          ///     The value to encode in Quoted Printable format.
          /// </param>
          /// <returns>
          ///     The value encoded in Quoted Printable format.
          /// </returns>
          public static string EncodeQuotedPrintable(string value)
          {
  
              if (string.IsNullOrEmpty(value))
                  return value;
  
              StringBuilder builder = new StringBuilder();
  
              foreach (char c in value)
              {
  
                  int v = (int)c;
  
                  // The following are not required to be encoded:
                  //
                  // - Tab (ASCII 9)
                  // - Space (ASCII 32)
                  // - Characters 33 to 126, except for the equal sign (61).
  
                  if (
                      (v == 9) ||
                      ((v >= 32) && (v <= 60)) ||
                      ((v >= 62) && (v <= 126)))
                  {
                      builder.Append(c);
                  }
                  else
                  {
                      builder.Append('=');
                      builder.Append(v.ToString("X2"));
                  }
  
              }
  
              char lastChar = builder[builder.Length - 1];
              if (char.IsWhiteSpace(lastChar))
              {
                  builder.Remove(builder.Length - 1, 1);
                  builder.Append('=');
                  builder.Append(((int)lastChar).ToString("X2"));
              }
  
              return builder.ToString();
  
          }
  
          #endregion
  
          /// <summary>
          ///     Returns property encoded into a standard vCard NAME:VALUE format.
          /// </summary>
          public string EncodeProperty(vCardProperty property)
          {
  
              if (property == null)
                  throw new ArgumentNullException("property");
  
              if (string.IsNullOrEmpty(property.Name))
                  throw new ArgumentException();
  
              StringBuilder builder = new StringBuilder();
  
              builder.Append(property.Name);
  
              foreach (vCardSubproperty subproperty in property.Subproperties)
              {
                  builder.Append(';');
                  builder.Append(subproperty.Name);
  
                  if (!string.IsNullOrEmpty(subproperty.Value))
                  {
                      builder.Append('=');
                      builder.Append(subproperty.Value);
                  }
              }
  
              // The property name and all subproperties have been
              // written to the string builder (the colon separator
              // has not been written).  The next step is to write
              // the value.  Depending on the type of value and any
              // characters in the value, it may be necessary to
              // use an non-default encoding.  For example, byte arrays
              // are written encoded in BASE64.
  
              if (property.Value == null)
              {
                  builder.Append(':');
              }
              else
              {
  
                  Type valueType = property.Value.GetType();
  
                  if (valueType == typeof(byte[]))
                  {
  
                      // A byte array should be encoded in BASE64 format.
  
                      builder.Append(";ENCODING=BASE64:");
                      builder.Append(EncodeBase64((byte[])property.Value));
  
                  }
                  else if (valueType == typeof(vCardValueCollection))
                  {
  
                      vCardValueCollection values = (vCardValueCollection)property.Value;
  
                      builder.Append(':');
                      for (int index = 0; index < values.Count; index++)
                      {
  
                          builder.Append(EncodeEscaped(values[index]));
                          if (index < values.Count - 1)
                          {
                              builder.Append(values.Separator);
                          }
                      }
  
                  }
                  else
                  {
  
                      // The object will be converted to a string (if it is
                      // not a string already) and encoded if necessary.
                      // The first step is to get the string value.
  
                      string stringValue = null;
  
                      if (valueType == typeof(char[]))
                      {
                          stringValue = new string(((char[])property.Value));
                      }
                      else
                      {
                          stringValue = property.Value.ToString();
                      }
  
                      builder.Append(':');
  
                      switch (property.Subproperties.GetValue("ENCODING"))
                      {
  
                          case "QUOTED-PRINTABLE":
                              builder.Append(EncodeQuotedPrintable(stringValue));
                              break;
  
                          default:
                              builder.Append(EncodeEscaped(stringValue));
                              break;
  
                      }
  
                  }
  
              }
  
              return builder.ToString();
  
          }
  
  
          /// <summary>
          ///     Writes a vCard to an output text writer.
          /// </summary>
          public override void Write(vCard card, TextWriter output)
          {
  
              if (card == null)
  
gt:                 throw new ArgumentNullException("card");