text: /*****************************************************************************
   * vml - A library for creating Excel XLSX vml files.
   *
   * Used in conjunction with the libxlsxwriter library.
   *
   * Copyright 2014-2020, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
   *
   */
  
  #include "xlsxwriter/xmlwriter.h"
  #include "xlsxwriter/vml.h"
  #include "xlsxwriter/utility.h"
  
  /*
   * Forward declarations.
   */
  
  /*****************************************************************************
   *
   * Private functions.
   *
   ****************************************************************************/
  
  /*
   * Create a new vml object.
   */
  lxw_vml *
  lxw_vml_new(void)
  {
      lxw_vml *vml = calloc(1, sizeof(lxw_vml));
      GOTO_LABEL_ON_MEM_ERROR(vml, mem_error);
  
      return vml;
  
  mem_error:
      lxw_vml_free(vml);
      return NULL;
  }
  
  /*
   * Free a vml object.
   */
  void
  lxw_vml_free(lxw_vml *vml)
  {
      if (!vml)
          return;
  
      free(vml);
  }
  
  /*****************************************************************************
   *
   * XML functions.
   *
   ****************************************************************************/
  /*
   * Write the <x:Visible> element.
   */
  STATIC void
  _vml_write_visible(lxw_vml *self)
  {
      lxw_xml_empty_tag(self->file, "x:Visible", NULL);
  }
  
  /*
   * Write the <v:f> element.
   */
  STATIC void
  _vml_write_formula(lxw_vml *self, char *equation)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("eqn", equation);
  
      lxw_xml_empty_tag(self->file, "v:f", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:formulas> element.
   */
  STATIC void
  _vml_write_formulas(lxw_vml *self)
  {
      lxw_xml_start_tag(self->file, "v:formulas", NULL);
  
      _vml_write_formula(self, "if lineDrawn pixelLineWidth 0");
      _vml_write_formula(self, "sum @0 1 0");
      _vml_write_formula(self, "sum 0 0 @1");
      _vml_write_formula(self, "prod @2 1 2");
      _vml_write_formula(self, "prod @3 21600 pixelWidth");
      _vml_write_formula(self, "prod @3 21600 pixelHeight");
      _vml_write_formula(self, "sum @0 0 1");
      _vml_write_formula(self, "prod @6 1 2");
      _vml_write_formula(self, "prod @7 21600 pixelWidth");
      _vml_write_formula(self, "sum @8 21600 0");
      _vml_write_formula(self, "prod @7 21600 pixelHeight");
      _vml_write_formula(self, "sum @10 21600 0");
  
      lxw_xml_end_tag(self->file, "v:formulas");
  }
  
  /*
   * Write the <x:TextHAlign> element.
   */
  STATIC void
  _vml_write_text_halign(lxw_vml *self)
  {
  
      lxw_xml_data_element(self->file, "x:TextHAlign", "Center", NULL);
  }
  
  /*
   * Write the <x:TextVAlign> element.
   */
  STATIC void
  _vml_write_text_valign(lxw_vml *self)
  {
      lxw_xml_data_element(self->file, "x:TextVAlign", "Center", NULL);
  }
  
  /*
   * Write the <x:FmlaMacro> element.
   */
  STATIC void
  _vml_write_fmla_macro(lxw_vml *self)
  {
      lxw_xml_data_element(self->file, "x:FmlaMacro", "[0]!Button1_Click",
                           NULL);
  }
  
  /*
   * Write the <x:PrintObject> element.
   */
  STATIC void
  _vml_write_print_object(lxw_vml *self)
  {
      lxw_xml_data_element(self->file, "x:PrintObject", "False", NULL);
  }
  
  /*
   * Write the <o:lock> element.
   */
  STATIC void
  _vml_write_aspect_ratio_lock(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("v:ext", "edit");
      LXW_PUSH_ATTRIBUTES_STR("aspectratio", "t");
  
      lxw_xml_empty_tag(self->file, "o:lock", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <o:lock> element.
   */
  STATIC void
  _vml_write_rotation_lock(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("v:ext", "edit");
      LXW_PUSH_ATTRIBUTES_STR("rotation", "t");
  
      lxw_xml_empty_tag(self->file, "o:lock", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <x:Column> element.
   */
  STATIC void
  _vml_write_column(lxw_vml *self, lxw_vml_obj *comment_obj)
  {
      char data[LXW_ATTR_32];
  
      lxw_snprintf(data, LXW_ATTR_32, "%d", comment_obj->col);
  
      lxw_xml_data_element(self->file, "x:Column", data, NULL);
  }
  
  /*
   * Write the <x:Row> element.
   */
  STATIC void
  _vml_write_row(lxw_vml *self, lxw_vml_obj *comment_obj)
  {
      char data[LXW_ATTR_32];
  
      lxw_snprintf(data, LXW_ATTR_32, "%d", comment_obj->row);
  
      lxw_xml_data_element(self->file, "x:Row", data, NULL);
  }
  
  /*
   * Write the <x:AutoFill> element.
   */
  STATIC void
  _vml_write_auto_fill(lxw_vml *self)
  {
      lxw_xml_data_element(self->file, "x:AutoFill", "False", NULL);
  }
  
  /*
   * Write the <x:Anchor> element.
   */
  STATIC void
  _vml_write_anchor(lxw_vml *self, lxw_vml_obj *comment_obj)
  {
      char anchor_data[LXW_MAX_ATTRIBUTE_LENGTH];
  
      lxw_snprintf(anchor_data,
                   LXW_MAX_ATTRIBUTE_LENGTH,
                   "%d, %d, %d, %d, %d, %d, %d, %d",
                   comment_obj->from.col,
                   (uint32_t) comment_obj->from.col_offset,
                   comment_obj->from.row,
                   (uint32_t) comment_obj->from.row_offset,
                   comment_obj->to.col,
                   (uint32_t) comment_obj->to.col_offset,
                   comment_obj->to.row, (uint32_t) comment_obj->to.row_offset);
  
      lxw_xml_data_element(self->file, "x:Anchor", anchor_data, NULL);
  }
  
  /*
   * Write the <x:SizeWithCells> element.
   */
  STATIC void
  _vml_write_size_with_cells(lxw_vml *self)
  {
      lxw_xml_empty_tag(self->file, "x:SizeWithCells", NULL);
  }
  
  /*
   * Write the <x:MoveWithCells> element.
   */
  STATIC void
  _vml_write_move_with_cells(lxw_vml *self)
  {
      lxw_xml_empty_tag(self->file, "x:MoveWithCells", NULL);
  }
  
  /*
   * Write the <v:shadow> element.
   */
  STATIC void
  _vml_write_shadow(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("on", "t");
      LXW_PUSH_ATTRIBUTES_STR("color", "black");
      LXW_PUSH_ATTRIBUTES_STR("obscured", "t");
  
      lxw_xml_empty_tag(self->file, "v:shadow", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:stroke> element.
   */
  STATIC void
  _vml_write_stroke(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("joinstyle", "miter");
  
      lxw_xml_empty_tag(self->file, "v:stroke", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <o:lock> element.
   */
  STATIC void
  _vml_write_shapetype_lock(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("v:ext", "edit");
      LXW_PUSH_ATTRIBUTES_STR("shapetype", "t");
  
      lxw_xml_empty_tag(self->file, "o:lock", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <font> element.
   */
  STATIC void
  _vml_write_font(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("face", "Calibri");
      LXW_PUSH_ATTRIBUTES_STR("size", "220");
      LXW_PUSH_ATTRIBUTES_STR("color", "#000000");
  
      lxw_xml_data_element(self->file, "font", "Button 1", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:imagedata> element.
   */
  STATIC void
  _vml_write_imagedata(lxw_vml *self, uint32_t rel_index, char *name)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
      char rel_id[LXW_ATTR_32];
  
      lxw_snprintf(rel_id, LXW_ATTR_32, "rId%d", rel_index);
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("o:relid", rel_id);
      LXW_PUSH_ATTRIBUTES_STR("o:title", name);
  
      lxw_xml_empty_tag(self->file, "v:imagedata", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:path> element.
   */
  STATIC void
  _vml_write_image_path(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("o:extrusionok", "f");
      LXW_PUSH_ATTRIBUTES_STR("gradientshapeok", "t");
      LXW_PUSH_ATTRIBUTES_STR("o:connecttype", "rect");
  
      lxw_xml_empty_tag(self->file, "v:path", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:shape> element.
   */
  STATIC void
  _vml_write_image_shape(lxw_vml *self, uint32_t vml_shape_id, uint32_t z_index,
                         lxw_vml_obj *image_obj)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
      char width_str[LXW_ATTR_32];
      char height_str[LXW_ATTR_32];
      char style[LXW_MAX_ATTRIBUTE_LENGTH];
      char o_spid[LXW_ATTR_32];
      char type[] = "#_x0000_t75";
      double width;
      double height;
  
      /* Scale the height/width by the resolution, relative to 72dpi. */
      width = image_obj->width * (72.0 / image_obj->x_dpi);
      height = image_obj->height * (72.0 / image_obj->y_dpi);
  
      /* Excel uses a rounding based around 72 and 96 dpi. */
      width = 72.0 / 96.0 * (uint32_t) (width * 96.0 / 72 + 0.25);
      height = 72.0 / 96.0 * (uint32_t) (height * 96.0 / 72 + 0.25);
  
      lxw_sprintf_dbl(width_str, width);
      lxw_sprintf_dbl(height_str, height);
  
      lxw_snprintf(o_spid, LXW_ATTR_32, "_x0000_s%d", vml_shape_id);
  
      lxw_snprintf(style,
                   LXW_MAX_ATTRIBUTE_LENGTH,
                   "position:absolute;"
                   "margin-left:0;"
                   "margin-top:0;"
                   "width:%spt;"
                   "height:%spt;" "z-index:%d", width_str, height_str, z_index);
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("id", image_obj->image_position);
      LXW_PUSH_ATTRIBUTES_STR("o:spid", o_spid);
      LXW_PUSH_ATTRIBUTES_STR("type", type);
      LXW_PUSH_ATTRIBUTES_STR("style", style);
  
      lxw_xml_start_tag(self->file, "v:shape", &attributes);
  
      /* Write the v:imagedata element. */
      _vml_write_imagedata(self, image_obj->rel_index, image_obj->name);
  
      /* Write the o:lock element. */
      _vml_write_rotation_lock(self);
  
      lxw_xml_end_tag(self->file, "v:shape");
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:shapetype> element for images.
   */
  STATIC void
  _vml_write_image_shapetype(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
      char id[] = "_x0000_t75";
      char coordsize[] = "21600,21600";
      char o_spt[] = "75";
      char o_preferrelative[] = "t";
      char path[] = "m@4@5l@4@11@9@11@9@5xe";
      char filled[] = "f";
      char stroked[] = "f";
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("id", id);
      LXW_PUSH_ATTRIBUTES_STR("coordsize", coordsize);
      LXW_PUSH_ATTRIBUTES_STR("o:spt", o_spt);
      LXW_PUSH_ATTRIBUTES_STR("o:preferrelative", o_preferrelative);
      LXW_PUSH_ATTRIBUTES_STR("path", path);
      LXW_PUSH_ATTRIBUTES_STR("filled", filled);
      LXW_PUSH_ATTRIBUTES_STR("stroked", stroked);
  
      lxw_xml_start_tag(self->file, "v:shapetype", &attributes);
  
      /* Write the v:stroke element. */
      _vml_write_stroke(self);
  
      /* Write the v:formulas element. */
      _vml_write_formulas(self);
  
      /* Write the v:path element. */
      _vml_write_image_path(self);
  
      /* Write the o:lock element. */
      _vml_write_aspect_ratio_lock(self);
  
      lxw_xml_end_tag(self->file, "v:shapetype");
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <x:ClientData> element.
   */
  STATIC void
  _vml_write_button_client_data(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("ObjectType", "Button");
  
      lxw_xml_start_tag(self->file, "x:ClientData", &attributes);
  
      /* Write the <x:Anchor> element. */
      _vml_write_anchor(self, NULL);
  
      /* Write the x:PrintObject element. */
      _vml_write_print_object(self);
  
      /* Write the x:AutoFill element. */
      _vml_write_auto_fill(self);
  
      /* Write the x:FmlaMacro element. */
      _vml_write_fmla_macro(self);
  
      /* Write the x:TextHAlign element. */
      _vml_write_text_halign(self);
  
      /* Write the x:TextVAlign element. */
      _vml_write_text_valign(self);
  
      lxw_xml_end_tag(self->file, "x:ClientData");
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <div> element.
   */
  STATIC void
  _vml_write_button_div(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("style", "text-align:center");
  
      lxw_xml_start_tag(self->file, "div", &attributes);
  
      /* Write the font element. */
      _vml_write_font(self);
  
      lxw_xml_end_tag(self->file, "div");
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:textbox> element.
   */
  STATIC void
  _vml_write_button_textbox(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("style", "mso-direction-alt:auto");
      LXW_PUSH_ATTRIBUTES_STR("o:singleclick", "f");
  
      lxw_xml_start_tag(self->file, "v:textbox", &attributes);
  
      /* Write the div element. */
      _vml_write_button_div(self);
  
      lxw_xml_end_tag(self->file, "v:textbox");
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:fill> element.
   */
  STATIC void
  _vml_write_button_fill(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("color2", "buttonFace [67]");
      LXW_PUSH_ATTRIBUTES_STR("o:detectmouseclick", "t");
  
      lxw_xml_empty_tag(self->file, "v:fill", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:path> element for buttons.
   */
  STATIC void
  _vml_write_button_path(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("shadowok", "f");
      LXW_PUSH_ATTRIBUTES_STR("o:extrusionok", "f");
      LXW_PUSH_ATTRIBUTES_STR("strokeok", "f");
      LXW_PUSH_ATTRIBUTES_STR("fillok", "f");
      LXW_PUSH_ATTRIBUTES_STR("o:connecttype", "rect");
  
      lxw_xml_empty_tag(self->file, "v:path", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:shape> element for buttons.
   */
  STATIC void
  _vml_write_button_shape(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
      char id[] = "_x0000_s1025";
      char type[] = "#_x0000_t201";
      char style[] = "position:absolute;margin-left:96pt;margin-top:15pt;"
          "width:48pt;height:15pt;z-index:1;mso-wrap-style:tight";
      char o_button[] = "t";
      char fillcolor[] = "buttonFace [67]";
      char strokecolor[] = "windowText [64]";
      char o_insetmode[] = "auto";
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("id", id);
      LXW_PUSH_ATTRIBUTES_STR("type", type);
      LXW_PUSH_ATTRIBUTES_STR("style", style);
      LXW_PUSH_ATTRIBUTES_STR("o:button", o_button);
      LXW_PUSH_ATTRIBUTES_STR("fillcolor", fillcolor);
      LXW_PUSH_ATTRIBUTES_STR("strokecolor", strokecolor);
      LXW_PUSH_ATTRIBUTES_STR("o:insetmode", o_insetmode);
  
      lxw_xml_start_tag(self->file, "v:shape", &attributes);
  
      /* Write the v:fill element. */
      _vml_write_button_fill(self);
  
      /* Write the o:lock element. */
      _vml_write_rotation_lock(self);
  
      /* Write the v:textbox element. */
      _vml_write_button_textbox(self);
  
      /* Write the x:ClientData element. */
      _vml_write_button_client_data(self);
  
      lxw_xml_end_tag(self->file, "v:shape");
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:shapetype> element for buttons.
   */
  STATIC void
  _vml_write_button_shapetype(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
      char id[] = "_x0000_t201";
      char coordsize[] = "21600,21600";
      char o_spt[] = "201";
      char path[] = "m,l,21600r21600,l21600,xe";
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("id", id);
      LXW_PUSH_ATTRIBUTES_STR("coordsize", coordsize);
      LXW_PUSH_ATTRIBUTES_STR("o:spt", o_spt);
      LXW_PUSH_ATTRIBUTES_STR("path", path);
  
      lxw_xml_start_tag(self->file, "v:shapetype", &attributes);
  
      /* Write the v:stroke element. */
      _vml_write_stroke(self);
  
      /* Write the v:path element. */
      _vml_write_button_path(self);
  
      /* Write the o:lock element. */
      _vml_write_shapetype_lock(self);
  
      lxw_xml_end_tag(self->file, "v:shapetype");
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <x:ClientData> element.
   */
  STATIC void
  _vml_write_comment_client_data(lxw_vml *self, lxw_vml_obj *comment_obj)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("ObjectType", "Note");
  
      lxw_xml_start_tag(self->file, "x:ClientData", &attributes);
  
      /* Write the <x:MoveWithCells> element. */
      _vml_write_move_with_cells(self);
  
      /* Write the <x:SizeWithCells> element. */
      _vml_write_size_with_cells(self);
  
      /* Write the <x:Anchor> element. */
      _vml_write_anchor(self, comment_obj);
  
      /* Write the <x:AutoFill> element. */
      _vml_write_auto_fill(self);
  
      /* Write the <x:Row> element. */
      _vml_write_row(self, comment_obj);
  
      /* Write the <x:Column> element. */
      _vml_write_column(self, comment_obj);
  
      /* Write the x:Visible element. */
      if (comment_obj->visible == LXW_COMMENT_DISPLAY_VISIBLE)
          _vml_write_visible(self);
  
      lxw_xml_end_tag(self->file, "x:ClientData");
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <div> element.
   */
  STATIC void
  _vml_write_comment_div(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("style", "text-align:left");
  
      lxw_xml_start_tag(self->file, "div", &attributes);
  
      lxw_xml_end_tag(self->file, "div");
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:textbox> element.
   */
  STATIC void
  _vml_write_comment_textbox(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("style", "mso-direction-alt:auto");
  
      lxw_xml_start_tag(self->file, "v:textbox", &attributes);
  
      /* Write the div element. */
      _vml_write_comment_div(self);
  
      lxw_xml_end_tag(self->file, "v:textbox");
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:fill> element.
   */
  STATIC void
  _vml_write_comment_fill(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("color2", "#ffffe1");
  
      lxw_xml_empty_tag(self->file, "v:fill", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:path> element.
   */
  STATIC void
  _vml_write_comment_path(lxw_vml *self, uint8_t has_gradient, char *type)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
  
      if (has_gradient)
          LXW_PUSH_ATTRIBUTES_STR("gradientshapeok", "t");
  
      LXW_PUSH_ATTRIBUTES_STR("o:connecttype", type);
  
      lxw_xml_empty_tag(self->file, "v:path", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:shape> element for comments.
   */
  STATIC void
  _vml_write_comment_shape(lxw_vml *self, uint32_t vml_shape_id,
                           uint32_t z_index, lxw_vml_obj *comment_obj)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
      char id[LXW_ATTR_32];
      char margin_left[LXW_ATTR_32];
      char margin_top[LXW_ATTR_32];
      char width[LXW_ATTR_32];
      char height[LXW_ATTR_32];
      char visible[LXW_ATTR_32];
      char fillcolor[LXW_ATTR_32];
      char style[LXW_MAX_ATTRIBUTE_LENGTH];
      char type[] = "#_x0000_t202";
      char o_insetmode[] = "auto";
  
      lxw_sprintf_dbl(margin_left, comment_obj->col_absolute * 0.75);
      lxw_sprintf_dbl(margin_top, comment_obj->row_absolute * 0.75);
      lxw_sprintf_dbl(width, comment_obj->width * 0.75);
      lxw_sprintf_dbl(height, comment_obj->height * 0.75);
  
      lxw_snprintf(id, LXW_ATTR_32, "_x0000_s%d", vml_shape_id);
  
      if (comment_obj->visible == LXW_COMMENT_DISPLAY_DEFAULT)
          comment_obj->visible = self->comment_display_default;
  
      if (comment_obj->visible == LXW_COMMENT_DISPLAY_VISIBLE)
          lxw_snprintf(visible, LXW_ATTR_32, "visible");
      else
          lxw_snprintf(visible, LXW_ATTR_32, "hidden");
  
      if (comment_obj->color)
          lxw_snprintf(fillcolor, LXW_ATTR_32, "#%06x",
                       comment_obj->color & LXW_COLOR_MASK);
      else
          lxw_snprintf(fillcolor, LXW_ATTR_32, "#%06x", 0xffffe1);
  
      lxw_snprintf(style,
                   LXW_MAX_ATTRIBUTE_LENGTH,
                   "position:absolute;"
                   "margin-left:%spt;"
                   "margin-top:%spt;"
                   "width:%spt;"
                   "height:%spt;"
                   "z-index:%d;"
                   "visibility:%s",
                   margin_left, margin_top, width, height, z_index, visible);
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("id", id);
      LXW_PUSH_ATTRIBUTES_STR("type", type);
      LXW_PUSH_ATTRIBUTES_STR("style", style);
      LXW_PUSH_ATTRIBUTES_STR("fillcolor", fillcolor);
      LXW_PUSH_ATTRIBUTES_STR("o:insetmode", o_insetmode);
  
      lxw_xml_start_tag(self->file, "v:shape", &attributes);
  
      /* Write the v:fill element. */
      _vml_write_comment_fill(self);
  
      /* Write the v:shadow element. */
      _vml_write_shadow(self);
  
      /* Write the v:path element. */
      _vml_write_comment_path(self, LXW_FALSE, "none");
  
      /* Write the v:textbox element. */
      _vml_write_comment_textbox(self);
  
      /* Write the x:ClientData element. */
      _vml_write_comment_client_data(self, comment_obj);
  
      lxw_xml_end_tag(self->file, "v:shape");
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <v:shapetype> element for comments.
   */
  STATIC void
  _vml_write_comment_shapetype(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
      char id[] = "_x0000_t202";
      char coordsize[] = "21600,21600";
      char o_spt[] = "202";
      char path[] = "m,l,21600r21600,l21600,xe";
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("id", id);
      LXW_PUSH_ATTRIBUTES_STR("coordsize", coordsize);
      LXW_PUSH_ATTRIBUTES_STR("o:spt", o_spt);
      LXW_PUSH_ATTRIBUTES_STR("path", path);
  
      lxw_xml_start_tag(self->file, "v:shapetype", &attributes);
  
      /* Write the v:stroke element. */
      _vml_write_stroke(self);
  
      /* Write the v:path element. */
      _vml_write_comment_path(self, LXW_TRUE, "rect");
  
      lxw_xml_end_tag(self->file, "v:shapetype");
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <o:idmap> element.
   */
  STATIC void
  _vml_write_idmap(lxw_vml *self)
  {
      /* Since the vml_data_id_str may exceed the LXW_MAX_ATTRIBUTE_LENGTH we
       * write it directly without the xml helper functions. */
      fprintf(self->file, "<o:idmap v:ext=\"edit\" data=\"%s\"/>",
              self->vml_data_id_str);
  }
  
  /*
   * Write the <o:shapelayout> element.
   */
  STATIC void
  _vml_write_shapelayout(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("v:ext", "edit");
  
      lxw_xml_start_tag(self->file, "o:shapelayout", &attributes);
  
      /* Write the o:idmap element. */
      _vml_write_idmap(self);
  
      lxw_xml_end_tag(self->file, "o:shapelayout");
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*
   * Write the <xml> element.
   */
  STATIC void
  _vml_write_xml_namespace(lxw_vml *self)
  {
      struct xml_attribute_list attributes;
      struct xml_attribute *attribute;
      char xmlns_v[] = "urn:schemas-microsoft-com:vml";
      char xmlns_o[] = "urn:schemas-microsoft-com:office:office";
      char xmlns_x[] = "urn:schemas-microsoft-com:office:excel";
  
      LXW_INIT_ATTRIBUTES();
      LXW_PUSH_ATTRIBUTES_STR("xmlns:v", xmlns_v);
      LXW_PUSH_ATTRIBUTES_STR("xmlns:o", xmlns_o);
      LXW_PUSH_ATTRIBUTES_STR("xmlns:x", xmlns_x);
  
      lxw_xml_start_tag(self->file, "xml", &attributes);
  
      LXW_FREE_ATTRIBUTES();
  }
  
  /*****************************************************************************
   *
   * XML file assembly functions.
   *
   ****************************************************************************/
  
  /*
   * Assemble and write the XML file.
   */
  void
  lxw_vml_assemble_xml_file(lxw_vml *self)
  {
      lxw_vml_obj *comment_obj;
      lxw_vml_obj *button_obj;
      lxw_vml_obj *image_obj;
      uint32_t z_index = 1;
  
      /* Write the xml namespace element. Note, the VML files have no
       * XML declaration.*/
      _vml_write_xml_namespace(self);
  
      /* Write the o:shapelayout element. */
      _vml_write_shapelayout(self);
  
      if (self->comment_objs) {
          /* Write the <v:shapetype> element. */
          _vml_write_comment_shapetype(self);
  
          STAILQ_FOREACH(comment_obj, self->comment_objs, list_pointers) {
              self->vml_shape_id++;
  
              /* Write the <v:shape> element. */
              _vml_write_comment_shape(self, self->vml_shape_id, z_index,
                                       comment_obj);
  
              z_index++;
          }
      }
  
      if (self->button_objs) {
          /* Write the <v:shapetype> element. */
          _vml_write_button_shapetype(self);
  
          STAILQ_FOREACH(button_obj, self->button_objs, list_pointers) {
              /* Write the <v:shape> element. */
              _vml_write_button_shape(self);
  
          }
      }
  
      if (self->image_objs) {
          /* Write the <v:shapetype> element. */
          _vml_write_image_shapetype(self);
  
          STAILQ_FOREACH(image_obj, self->image_objs, list_pointers) {
              self->vml_shape_id++;
  
              /* Write the <v:shape> element. */
              _vml_write_image_shape(self, self->vml_shape_id, z_index,
                                     image_obj);
  
              z_index++;
          }
      }
  
      lxw_xml_end_tag(self->file, "xml");
  }
  
  /*****************************************************************************
   *
   * Public functions.
   *
   ****************************************************************************/
  