2024-02-26 18:47:32 +00:00

448 lines
10 KiB
C

/*****************************************************************************
* app - A library for creating Excel XLSX app files.
*
* Used in conjunction with the libxlsxwriter library.
*
* SPDX-License-Identifier: BSD-2-Clause
* Copyright 2014-2024, John McNamara, jmcnamara@cpan.org.
*
*/
#include "xlsxwriter/xmlwriter.h"
#include "xlsxwriter/app.h"
#include "xlsxwriter/utility.h"
/*
* Forward declarations.
*/
/*****************************************************************************
*
* Private functions.
*
****************************************************************************/
/*
* Create a new app object.
*/
lxw_app *
lxw_app_new(void)
{
lxw_app *app = calloc(1, sizeof(lxw_app));
GOTO_LABEL_ON_MEM_ERROR(app, mem_error);
app->heading_pairs = calloc(1, sizeof(struct lxw_heading_pairs));
GOTO_LABEL_ON_MEM_ERROR(app->heading_pairs, mem_error);
STAILQ_INIT(app->heading_pairs);
app->part_names = calloc(1, sizeof(struct lxw_part_names));
GOTO_LABEL_ON_MEM_ERROR(app->part_names, mem_error);
STAILQ_INIT(app->part_names);
return app;
mem_error:
lxw_app_free(app);
return NULL;
}
/*
* Free a app object.
*/
void
lxw_app_free(lxw_app *app)
{
lxw_heading_pair *heading_pair;
lxw_part_name *part_name;
if (!app)
return;
/* Free the lists in the App object. */
if (app->heading_pairs) {
while (!STAILQ_EMPTY(app->heading_pairs)) {
heading_pair = STAILQ_FIRST(app->heading_pairs);
STAILQ_REMOVE_HEAD(app->heading_pairs, list_pointers);
free(heading_pair->key);
free(heading_pair->value);
free(heading_pair);
}
free(app->heading_pairs);
}
if (app->part_names) {
while (!STAILQ_EMPTY(app->part_names)) {
part_name = STAILQ_FIRST(app->part_names);
STAILQ_REMOVE_HEAD(app->part_names, list_pointers);
free(part_name->name);
free(part_name);
}
free(app->part_names);
}
free(app);
}
/*****************************************************************************
*
* XML functions.
*
****************************************************************************/
/*
* Write the XML declaration.
*/
STATIC void
_app_xml_declaration(lxw_app *self)
{
lxw_xml_declaration(self->file);
}
/*
* Write the <Properties> element.
*/
STATIC void
_write_properties(lxw_app *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
char xmlns[] = LXW_SCHEMA_OFFICEDOC "/extended-properties";
char xmlns_vt[] = LXW_SCHEMA_OFFICEDOC "/docPropsVTypes";
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns);
LXW_PUSH_ATTRIBUTES_STR("xmlns:vt", xmlns_vt);
lxw_xml_start_tag(self->file, "Properties", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <Application> element.
*/
STATIC void
_write_application(lxw_app *self)
{
lxw_xml_data_element(self->file, "Application", "Microsoft Excel", NULL);
}
/*
* Write the <DocSecurity> element.
*/
STATIC void
_write_doc_security(lxw_app *self)
{
if (self->doc_security == 2)
lxw_xml_data_element(self->file, "DocSecurity", "2", NULL);
else
lxw_xml_data_element(self->file, "DocSecurity", "0", NULL);
}
/*
* Write the <ScaleCrop> element.
*/
STATIC void
_write_scale_crop(lxw_app *self)
{
lxw_xml_data_element(self->file, "ScaleCrop", "false", NULL);
}
/*
* Write the <vt:lpstr> element.
*/
STATIC void
_write_vt_lpstr(lxw_app *self, const char *str)
{
lxw_xml_data_element(self->file, "vt:lpstr", str, NULL);
}
/*
* Write the <vt:i4> element.
*/
STATIC void
_write_vt_i4(lxw_app *self, const char *value)
{
lxw_xml_data_element(self->file, "vt:i4", value, NULL);
}
/*
* Write the <vt:variant> element.
*/
STATIC void
_write_vt_variant(lxw_app *self, const char *key, const char *value)
{
/* Write the vt:lpstr element. */
lxw_xml_start_tag(self->file, "vt:variant", NULL);
_write_vt_lpstr(self, key);
lxw_xml_end_tag(self->file, "vt:variant");
/* Write the vt:i4 element. */
lxw_xml_start_tag(self->file, "vt:variant", NULL);
_write_vt_i4(self, value);
lxw_xml_end_tag(self->file, "vt:variant");
}
/*
* Write the <vt:vector> element for the heading pairs.
*/
STATIC void
_write_vt_vector_heading_pairs(lxw_app *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
lxw_heading_pair *heading_pair;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_INT("size", self->num_heading_pairs * 2);
LXW_PUSH_ATTRIBUTES_STR("baseType", "variant");
lxw_xml_start_tag(self->file, "vt:vector", &attributes);
STAILQ_FOREACH(heading_pair, self->heading_pairs, list_pointers) {
_write_vt_variant(self, heading_pair->key, heading_pair->value);
}
lxw_xml_end_tag(self->file, "vt:vector");
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <vt:vector> element for the named parts.
*/
STATIC void
_write_vt_vector_lpstr_named_parts(lxw_app *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
lxw_part_name *part_name;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_INT("size", self->num_part_names);
LXW_PUSH_ATTRIBUTES_STR("baseType", "lpstr");
lxw_xml_start_tag(self->file, "vt:vector", &attributes);
STAILQ_FOREACH(part_name, self->part_names, list_pointers) {
_write_vt_lpstr(self, part_name->name);
}
lxw_xml_end_tag(self->file, "vt:vector");
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <HeadingPairs> element.
*/
STATIC void
_write_heading_pairs(lxw_app *self)
{
lxw_xml_start_tag(self->file, "HeadingPairs", NULL);
/* Write the vt:vector element. */
_write_vt_vector_heading_pairs(self);
lxw_xml_end_tag(self->file, "HeadingPairs");
}
/*
* Write the <TitlesOfParts> element.
*/
STATIC void
_write_titles_of_parts(lxw_app *self)
{
lxw_xml_start_tag(self->file, "TitlesOfParts", NULL);
/* Write the vt:vector element. */
_write_vt_vector_lpstr_named_parts(self);
lxw_xml_end_tag(self->file, "TitlesOfParts");
}
/*
* Write the <Manager> element.
*/
STATIC void
_write_manager(lxw_app *self)
{
lxw_doc_properties *properties = self->properties;
if (!properties)
return;
if (properties->manager)
lxw_xml_data_element(self->file, "Manager", properties->manager,
NULL);
}
/*
* Write the <Company> element.
*/
STATIC void
_write_company(lxw_app *self)
{
lxw_doc_properties *properties = self->properties;
if (properties && properties->company)
lxw_xml_data_element(self->file, "Company", properties->company,
NULL);
else
lxw_xml_data_element(self->file, "Company", "", NULL);
}
/*
* Write the <LinksUpToDate> element.
*/
STATIC void
_write_links_up_to_date(lxw_app *self)
{
lxw_xml_data_element(self->file, "LinksUpToDate", "false", NULL);
}
/*
* Write the <SharedDoc> element.
*/
STATIC void
_write_shared_doc(lxw_app *self)
{
lxw_xml_data_element(self->file, "SharedDoc", "false", NULL);
}
/*
* Write the <HyperlinkBase> element.
*/
STATIC void
_write_hyperlink_base(lxw_app *self)
{
lxw_doc_properties *properties = self->properties;
if (!properties)
return;
if (properties->hyperlink_base)
lxw_xml_data_element(self->file, "HyperlinkBase",
properties->hyperlink_base, NULL);
}
/*
* Write the <HyperlinksChanged> element.
*/
STATIC void
_write_hyperlinks_changed(lxw_app *self)
{
lxw_xml_data_element(self->file, "HyperlinksChanged", "false", NULL);
}
/*
* Write the <AppVersion> element.
*/
STATIC void
_write_app_version(lxw_app *self)
{
lxw_xml_data_element(self->file, "AppVersion", "12.0000", NULL);
}
/*****************************************************************************
*
* XML file assembly functions.
*
****************************************************************************/
/*
* Assemble and write the XML file.
*/
void
lxw_app_assemble_xml_file(lxw_app *self)
{
/* Write the XML declaration. */
_app_xml_declaration(self);
_write_properties(self);
_write_application(self);
_write_doc_security(self);
_write_scale_crop(self);
_write_heading_pairs(self);
_write_titles_of_parts(self);
_write_manager(self);
_write_company(self);
_write_links_up_to_date(self);
_write_shared_doc(self);
_write_hyperlink_base(self);
_write_hyperlinks_changed(self);
_write_app_version(self);
lxw_xml_end_tag(self->file, "Properties");
}
/*****************************************************************************
*
* Public functions.
*
****************************************************************************/
/*
* Add the name of a workbook Part such as 'Sheet1' or 'Print_Titles'.
*/
void
lxw_app_add_part_name(lxw_app *self, const char *name)
{
lxw_part_name *part_name;
if (!name)
return;
part_name = calloc(1, sizeof(lxw_part_name));
GOTO_LABEL_ON_MEM_ERROR(part_name, mem_error);
part_name->name = lxw_strdup(name);
GOTO_LABEL_ON_MEM_ERROR(part_name->name, mem_error);
STAILQ_INSERT_TAIL(self->part_names, part_name, list_pointers);
self->num_part_names++;
return;
mem_error:
if (part_name) {
free(part_name->name);
free(part_name);
}
}
/*
* Add the name of a workbook Heading Pair such as 'Worksheets', 'Charts' or
* 'Named Ranges'.
*/
void
lxw_app_add_heading_pair(lxw_app *self, const char *key, const char *value)
{
lxw_heading_pair *heading_pair;
if (!key || !value)
return;
heading_pair = calloc(1, sizeof(lxw_heading_pair));
GOTO_LABEL_ON_MEM_ERROR(heading_pair, mem_error);
heading_pair->key = lxw_strdup(key);
GOTO_LABEL_ON_MEM_ERROR(heading_pair->key, mem_error);
heading_pair->value = lxw_strdup(value);
GOTO_LABEL_ON_MEM_ERROR(heading_pair->value, mem_error);
STAILQ_INSERT_TAIL(self->heading_pairs, heading_pair, list_pointers);
self->num_heading_pairs++;
return;
mem_error:
if (heading_pair) {
free(heading_pair->key);
free(heading_pair->value);
free(heading_pair);
}
}