libxlsxwriter/src/packager.c in fast_excel-0.4.1 vs libxlsxwriter/src/packager.c in fast_excel-0.5.0

- old
+ new

@@ -1,25 +1,70 @@ /***************************************************************************** - * packager - A library for creating Excel XLSX packager files. + * packager - A library for assembling xml files into an Excel XLSX file. * - * Used in conjunction with the libxlsxwriter library. + * A class for writing the Excel XLSX Packager file. * - * Copyright 2014-2019, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * This module is used in conjunction with libxlsxwriter to create an + * Excel XLSX container file. * + * From Wikipedia: The Open Packaging Conventions (OPC) is a + * container-file technology initially created by Microsoft to store + * a combination of XML and non-XML files that together form a single + * entity such as an Open XML Paper Specification (OpenXPS) + * document. http://en.wikipedia.org/wiki/Open_Packaging_Conventions. + * + * At its simplest an Excel XLSX file contains the following elements:: + * + * ____ [Content_Types].xml + * | + * |____ docProps + * | |____ app.xml + * | |____ core.xml + * | + * |____ xl + * | |____ workbook.xml + * | |____ worksheets + * | | |____ sheet1.xml + * | | + * | |____ styles.xml + * | | + * | |____ theme + * | | |____ theme1.xml + * | | + * | |_____rels + * | |____ workbook.xml.rels + * | + * |_____rels + * |____ .rels + * + * The Packager class coordinates the classes that represent the + * elements of the package and writes them into the XLSX file. + * + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * */ +#include <zlib.h> #include "xlsxwriter/xmlwriter.h" #include "xlsxwriter/packager.h" #include "xlsxwriter/hash_table.h" #include "xlsxwriter/utility.h" STATIC lxw_error _add_file_to_zip(lxw_packager *self, FILE * file, const char *filename); -STATIC lxw_error _add_buffer_to_zip(lxw_packager *self, unsigned char *buffer, +STATIC lxw_error _add_buffer_to_zip(lxw_packager *self, const char *buffer, size_t buffer_size, const char *filename); +STATIC lxw_error _add_to_zip(lxw_packager *self, FILE * file, + char **buffer, size_t *buffer_size, + const char *filename); + +STATIC lxw_error _write_vml_drawing_rels_file(lxw_packager *self, + lxw_worksheet *worksheet, + uint32_t index); + /* * Forward declarations. */ /***************************************************************************** @@ -34,11 +79,11 @@ #endif #ifdef _WIN32 /* Silence Windows warning with duplicate symbol for SLIST_ENTRY in local - * queue.h and widows.h. */ + * queue.h and windows.h. */ #undef SLIST_ENTRY #include <windows.h> #ifdef USE_SYSTEM_MINIZIP @@ -70,26 +115,80 @@ return zipOpen2_64(wide_filename, 0, NULL, &filefunc); } #endif +STATIC voidpf ZCALLBACK +_fopen_memstream(voidpf opaque, const char *filename, int mode) +{ + lxw_packager *packager = (lxw_packager *) opaque; + (void) filename; + (void) mode; + return lxw_get_filehandle(&packager->output_buffer, + &packager->output_buffer_size, + packager->tmpdir); +} + +STATIC int ZCALLBACK +_fclose_memstream(voidpf opaque, voidpf stream) +{ + lxw_packager *packager = (lxw_packager *) opaque; + FILE *file = (FILE *) stream; + long size; + + /* Ensure memstream buffer is updated */ + if (fflush(file)) + goto mem_error; + + /* If the memstream is backed by a temporary file, no buffer is created, + so create it manually. */ + if (!packager->output_buffer) { + if (fseek(file, 0L, SEEK_END)) + goto mem_error; + + size = ftell(file); + if (size == -1) + goto mem_error; + + packager->output_buffer = malloc(size); + GOTO_LABEL_ON_MEM_ERROR(packager->output_buffer, mem_error); + + rewind(file); + if (fread((void *) packager->output_buffer, size, 1, file) < 1) + goto mem_error; + + packager->output_buffer_size = size; + } + + return fclose(file); + +mem_error: + fclose(file); + return EOF; +} + /* * Create a new packager object. */ lxw_packager * -lxw_packager_new(const char *filename, char *tmpdir, uint8_t use_zip64) +lxw_packager_new(const char *filename, const char *tmpdir, uint8_t use_zip64) { + zlib_filefunc_def filefunc; lxw_packager *packager = calloc(1, sizeof(lxw_packager)); GOTO_LABEL_ON_MEM_ERROR(packager, mem_error); packager->buffer = calloc(1, LXW_ZIP_BUFFER_SIZE); GOTO_LABEL_ON_MEM_ERROR(packager->buffer, mem_error); - packager->filename = lxw_strdup(filename); + packager->filename = NULL; packager->tmpdir = tmpdir; - GOTO_LABEL_ON_MEM_ERROR(packager->filename, mem_error); + if (filename) { + packager->filename = lxw_strdup(filename); + GOTO_LABEL_ON_MEM_ERROR(packager->filename, mem_error); + } + packager->buffer_size = LXW_ZIP_BUFFER_SIZE; packager->use_zip64 = use_zip64; /* Initialize the zip_fileinfo struct to Jan 1 1980 like Excel. */ packager->zipfile_info.tmz_date.tm_sec = 0; @@ -100,16 +199,28 @@ packager->zipfile_info.tmz_date.tm_year = 1980; packager->zipfile_info.dosDate = 0; packager->zipfile_info.internal_fa = 0; packager->zipfile_info.external_fa = 0; + packager->output_buffer = NULL; + packager->output_buffer_size = 0; + /* Create a zip container for the xlsx file. */ + if (packager->filename) { #ifdef _WIN32 - packager->zipfile = _open_zipfile_win32(packager->filename); + packager->zipfile = _open_zipfile_win32(packager->filename); #else - packager->zipfile = zipOpen(packager->filename, 0); + packager->zipfile = zipOpen(packager->filename, 0); #endif + } + else { + fill_fopen_filefunc(&filefunc); + filefunc.opaque = packager; + filefunc.zopen_file = _fopen_memstream; + filefunc.zclose_file = _fclose_memstream; + packager->zipfile = zipOpen2(packager->filename, 0, NULL, &filefunc); + } if (packager->zipfile == NULL) goto mem_error; return packager; @@ -126,12 +237,12 @@ lxw_packager_free(lxw_packager *packager) { if (!packager) return; - free(packager->buffer); - free(packager->filename); + free((void *) packager->buffer); + free((void *) packager->filename); free(packager); } /***************************************************************************** * @@ -145,21 +256,24 @@ _write_workbook_file(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_error err; - workbook->file = lxw_tmpfile(self->tmpdir); + char *buffer = NULL; + size_t buffer_size = 0; + workbook->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!workbook->file) return LXW_ERROR_CREATING_TMPFILE; lxw_workbook_assemble_xml_file(workbook); - err = _add_file_to_zip(self, workbook->file, "xl/workbook.xml"); + err = _add_to_zip(self, workbook->file, &buffer, &buffer_size, + "xl/workbook.xml"); + fclose(workbook->file); + free(buffer); RETURN_ON_ERROR(err); - fclose(workbook->file); - return LXW_NO_ERROR; } /* * Write the worksheet files. @@ -169,10 +283,12 @@ { lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; uint32_t index = 1; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) @@ -184,20 +300,22 @@ "xl/worksheets/sheet%d.xml", index++); if (worksheet->optimize_row) lxw_worksheet_write_single_row(worksheet); - worksheet->file = lxw_tmpfile(self->tmpdir); + worksheet->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); if (!worksheet->file) return LXW_ERROR_CREATING_TMPFILE; lxw_worksheet_assemble_xml_file(worksheet); - err = _add_file_to_zip(self, worksheet->file, sheetname); - RETURN_ON_ERROR(err); - + err = _add_to_zip(self, worksheet->file, &buffer, &buffer_size, + sheetname); fclose(worksheet->file); + free(buffer); + RETURN_ON_ERROR(err); } return LXW_NO_ERROR; } @@ -209,10 +327,12 @@ { lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_chartsheet *chartsheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; uint32_t index = 1; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) @@ -221,20 +341,22 @@ continue; lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "xl/chartsheets/sheet%d.xml", index++); - chartsheet->file = lxw_tmpfile(self->tmpdir); + chartsheet->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); if (!chartsheet->file) return LXW_ERROR_CREATING_TMPFILE; lxw_chartsheet_assemble_xml_file(chartsheet); - err = _add_file_to_zip(self, chartsheet->file, sheetname); - RETURN_ON_ERROR(err); - + err = _add_to_zip(self, chartsheet->file, &buffer, &buffer_size, + sheetname); fclose(chartsheet->file); + free(buffer); + RETURN_ON_ERROR(err); } return LXW_NO_ERROR; } @@ -245,11 +367,11 @@ _write_image_files(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; - lxw_image_options *image; + lxw_object_properties *object_props; lxw_error err; FILE *image_stream; char filename[LXW_FILENAME_LENGTH] = { 0 }; uint32_t index = 1; @@ -258,35 +380,40 @@ if (sheet->is_chartsheet) continue; else worksheet = sheet->u.worksheet; - if (STAILQ_EMPTY(worksheet->image_data)) + if (STAILQ_EMPTY(worksheet->image_props)) continue; - STAILQ_FOREACH(image, worksheet->image_data, list_pointers) { + STAILQ_FOREACH(object_props, worksheet->image_props, list_pointers) { + if (object_props->is_duplicate) + continue; + lxw_snprintf(filename, LXW_FILENAME_LENGTH, - "xl/media/image%d.%s", index++, image->extension); + "xl/media/image%d.%s", index++, + object_props->extension); - if (!image->is_image_buffer) { + if (!object_props->is_image_buffer) { /* Check that the image file exists and can be opened. */ - image_stream = fopen(image->filename, "rb"); + image_stream = lxw_fopen(object_props->filename, "rb"); if (!image_stream) { LXW_WARN_FORMAT1("Error adding image to xlsx file: file " "doesn't exist or can't be opened: %s.", - image->filename); + object_props->filename); return LXW_ERROR_CREATING_TMPFILE; } err = _add_file_to_zip(self, image_stream, filename); fclose(image_stream); } else { err = _add_buffer_to_zip(self, - image->image_buffer, - image->image_buffer_size, filename); + object_props->image_buffer, + object_props->image_buffer_size, + filename); } RETURN_ON_ERROR(err); } } @@ -306,11 +433,11 @@ if (!workbook->vba_project) return LXW_NO_ERROR; /* Check that the image file exists and can be opened. */ - image_stream = fopen(workbook->vba_project, "rb"); + image_stream = lxw_fopen(workbook->vba_project, "rb"); if (!image_stream) { LXW_WARN_FORMAT1("Error adding vbaProject.bin to xlsx file: " "file doesn't exist or can't be opened: %s.", workbook->vba_project); return LXW_ERROR_CREATING_TMPFILE; @@ -322,36 +449,68 @@ return LXW_NO_ERROR; } /* + * Write the xl/vbaProjectSignature.bin file. + */ +STATIC lxw_error +_add_vba_project_signature(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_error err; + FILE *image_stream; + + if (!workbook->vba_project_signature) + return LXW_NO_ERROR; + + /* Check that the image file exists and can be opened. */ + image_stream = lxw_fopen(workbook->vba_project_signature, "rb"); + if (!image_stream) { + LXW_WARN_FORMAT1("Error adding vbaProjectSignature.bin to xlsx file: " + "file doesn't exist or can't be opened: %s.", + workbook->vba_project_signature); + return LXW_ERROR_CREATING_TMPFILE; + } + + err = _add_file_to_zip(self, image_stream, "xl/vbaProjectSignature.bin"); + fclose(image_stream); + RETURN_ON_ERROR(err); + + return LXW_NO_ERROR; +} + +/* * Write the chart files. */ STATIC lxw_error _write_chart_files(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_chart *chart; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; uint32_t index = 1; lxw_error err; STAILQ_FOREACH(chart, workbook->ordered_charts, ordered_list_pointers) { lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "xl/charts/chart%d.xml", index++); - chart->file = lxw_tmpfile(self->tmpdir); + chart->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!chart->file) return LXW_ERROR_CREATING_TMPFILE; lxw_chart_assemble_xml_file(chart); - err = _add_file_to_zip(self, chart->file, sheetname); - RETURN_ON_ERROR(err); - + err = _add_to_zip(self, chart->file, &buffer, &buffer_size, + sheetname); fclose(chart->file); + free(buffer); + RETURN_ON_ERROR(err); } return LXW_NO_ERROR; } @@ -381,10 +540,12 @@ lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; lxw_drawing *drawing; char filename[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; uint32_t index = 1; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) @@ -396,19 +557,22 @@ if (drawing) { lxw_snprintf(filename, LXW_FILENAME_LENGTH, "xl/drawings/drawing%d.xml", index++); - drawing->file = lxw_tmpfile(self->tmpdir); + drawing->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); if (!drawing->file) return LXW_ERROR_CREATING_TMPFILE; lxw_drawing_assemble_xml_file(drawing); - err = _add_file_to_zip(self, drawing->file, filename); - RETURN_ON_ERROR(err); + err = _add_to_zip(self, drawing->file, &buffer, &buffer_size, + filename); fclose(drawing->file); + free(buffer); + RETURN_ON_ERROR(err); } } return LXW_NO_ERROR; } @@ -439,33 +603,293 @@ return drawing_count; } /* + * Write the worksheet table files. + */ +STATIC lxw_error +_write_table_files(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_sheet *sheet; + lxw_worksheet *worksheet; + lxw_table *table; + lxw_table_obj *table_obj; + lxw_error err; + + char filename[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; + uint32_t index = 1; + + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { + if (sheet->is_chartsheet) + continue; + else + worksheet = sheet->u.worksheet; + + if (STAILQ_EMPTY(worksheet->table_objs)) + continue; + + STAILQ_FOREACH(table_obj, worksheet->table_objs, list_pointers) { + + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "xl/tables/table%d.xml", index++); + + table = lxw_table_new(); + if (!table) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + RETURN_ON_ERROR(err); + } + + table->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); + if (!table->file) { + lxw_table_free(table); + return LXW_ERROR_CREATING_TMPFILE; + } + + table->table_obj = table_obj; + + lxw_table_assemble_xml_file(table); + + err = _add_to_zip(self, table->file, &buffer, &buffer_size, + filename); + fclose(table->file); + free(buffer); + lxw_table_free(table); + RETURN_ON_ERROR(err); + } + } + + return LXW_NO_ERROR; +} + +/* + * Count the table files. + */ +uint32_t +_get_table_count(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_sheet *sheet; + lxw_worksheet *worksheet; + uint32_t table_count = 0; + + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { + if (sheet->is_chartsheet) + worksheet = sheet->u.chartsheet->worksheet; + else + worksheet = sheet->u.worksheet; + + table_count += worksheet->table_count; + } + + return table_count; +} + +/* + * Write the comment/header VML files. + */ +STATIC lxw_error +_write_vml_files(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_sheet *sheet; + lxw_worksheet *worksheet; + lxw_vml *vml; + char filename[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; + uint32_t index = 1; + lxw_error err; + + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { + if (sheet->is_chartsheet) + continue; + else + worksheet = sheet->u.worksheet; + + if (!worksheet->has_vml && !worksheet->has_header_vml) + continue; + + if (worksheet->has_vml) { + + vml = lxw_vml_new(); + if (!vml) + return LXW_ERROR_MEMORY_MALLOC_FAILED; + + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "xl/drawings/vmlDrawing%d.vml", index++); + + vml->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); + if (!vml->file) { + lxw_vml_free(vml); + return LXW_ERROR_CREATING_TMPFILE; + } + + vml->comment_objs = worksheet->comment_objs; + vml->button_objs = worksheet->button_objs; + vml->vml_shape_id = worksheet->vml_shape_id; + vml->comment_display_default = worksheet->comment_display_default; + + if (worksheet->vml_data_id_str) { + vml->vml_data_id_str = worksheet->vml_data_id_str; + } + else { + fclose(vml->file); + free(buffer); + lxw_vml_free(vml); + return LXW_ERROR_MEMORY_MALLOC_FAILED; + } + + lxw_vml_assemble_xml_file(vml); + + err = _add_to_zip(self, vml->file, &buffer, &buffer_size, + filename); + + fclose(vml->file); + free(buffer); + lxw_vml_free(vml); + + RETURN_ON_ERROR(err); + } + + if (worksheet->has_header_vml) { + + err = _write_vml_drawing_rels_file(self, worksheet, index); + RETURN_ON_ERROR(err); + + vml = lxw_vml_new(); + if (!vml) + return LXW_ERROR_MEMORY_MALLOC_FAILED; + + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "xl/drawings/vmlDrawing%d.vml", index++); + + vml->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); + if (!vml->file) { + lxw_vml_free(vml); + return LXW_ERROR_CREATING_TMPFILE; + } + + vml->image_objs = worksheet->header_image_objs; + vml->vml_shape_id = worksheet->vml_header_id * 1024; + + if (worksheet->vml_header_id_str) { + vml->vml_data_id_str = worksheet->vml_header_id_str; + } + else { + fclose(vml->file); + free(buffer); + lxw_vml_free(vml); + return LXW_ERROR_MEMORY_MALLOC_FAILED; + } + + lxw_vml_assemble_xml_file(vml); + + err = _add_to_zip(self, vml->file, &buffer, &buffer_size, + filename); + + fclose(vml->file); + free(buffer); + lxw_vml_free(vml); + + RETURN_ON_ERROR(err); + } + } + + return LXW_NO_ERROR; +} + +/* + * Write the comment files. + */ +STATIC lxw_error +_write_comment_files(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_sheet *sheet; + lxw_worksheet *worksheet; + lxw_comment *comment; + char filename[LXW_FILENAME_LENGTH] = { 0 }; + char *buffer = NULL; + size_t buffer_size = 0; + uint32_t index = 1; + lxw_error err; + + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { + if (sheet->is_chartsheet) + continue; + else + worksheet = sheet->u.worksheet; + + if (!worksheet->has_comments) + continue; + + comment = lxw_comment_new(); + if (!comment) + return LXW_ERROR_MEMORY_MALLOC_FAILED; + + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "xl/comments%d.xml", index++); + + comment->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); + if (!comment->file) { + lxw_comment_free(comment); + return LXW_ERROR_CREATING_TMPFILE; + } + + comment->comment_objs = worksheet->comment_objs; + comment->comment_author = worksheet->comment_author; + + lxw_comment_assemble_xml_file(comment); + + err = _add_to_zip(self, comment->file, &buffer, &buffer_size, + filename); + + fclose(comment->file); + free(buffer); + lxw_comment_free(comment); + + RETURN_ON_ERROR(err); + } + + return LXW_NO_ERROR; +} + +/* * Write the sharedStrings.xml file. */ STATIC lxw_error _write_shared_strings_file(lxw_packager *self) { lxw_sst *sst = self->workbook->sst; + char *buffer = NULL; + size_t buffer_size = 0; lxw_error err; /* Skip the sharedStrings file if there are no shared strings. */ if (!sst->string_count) return LXW_NO_ERROR; - sst->file = lxw_tmpfile(self->tmpdir); + sst->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!sst->file) return LXW_ERROR_CREATING_TMPFILE; lxw_sst_assemble_xml_file(sst); - err = _add_file_to_zip(self, sst->file, "xl/sharedStrings.xml"); + err = _add_to_zip(self, sst->file, &buffer, &buffer_size, + "xl/sharedStrings.xml"); + fclose(sst->file); + free(buffer); RETURN_ON_ERROR(err); - fclose(sst->file); - return LXW_NO_ERROR; } /* * Write the app.xml file. @@ -477,10 +901,12 @@ lxw_sheet *sheet; lxw_worksheet *worksheet; lxw_chartsheet *chartsheet; lxw_defined_name *defined_name; lxw_app *app; + char *buffer = NULL; + size_t buffer_size = 0; uint32_t named_range_count = 0; char *autofilter; char *has_range; char number[LXW_ATTR_32] = { 0 }; lxw_error err = LXW_NO_ERROR; @@ -489,11 +915,11 @@ if (!app) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } - app->file = lxw_tmpfile(self->tmpdir); + app->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!app->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } @@ -543,15 +969,19 @@ } /* Set the app/doc properties. */ app->properties = workbook->properties; + app->doc_security = workbook->read_only; + lxw_app_assemble_xml_file(app); - err = _add_file_to_zip(self, app->file, "docProps/app.xml"); + err = _add_to_zip(self, app->file, &buffer, &buffer_size, + "docProps/app.xml"); fclose(app->file); + free(buffer); mem_error: lxw_app_free(app); return err; @@ -563,43 +993,90 @@ STATIC lxw_error _write_core_file(lxw_packager *self) { lxw_error err = LXW_NO_ERROR; lxw_core *core = lxw_core_new(); + char *buffer = NULL; + size_t buffer_size = 0; if (!core) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } - core->file = lxw_tmpfile(self->tmpdir); + core->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!core->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } core->properties = self->workbook->properties; lxw_core_assemble_xml_file(core); - err = _add_file_to_zip(self, core->file, "docProps/core.xml"); + err = _add_to_zip(self, core->file, &buffer, &buffer_size, + "docProps/core.xml"); fclose(core->file); + free(buffer); mem_error: lxw_core_free(core); return err; } /* + * Write the metadata.xml file. + */ +STATIC lxw_error +_write_metadata_file(lxw_packager *self) +{ + lxw_error err = LXW_NO_ERROR; + lxw_metadata *metadata; + char *buffer = NULL; + size_t buffer_size = 0; + + if (!self->workbook->has_metadata) + return LXW_NO_ERROR; + + metadata = lxw_metadata_new(); + + if (!metadata) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + metadata->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); + if (!metadata->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + lxw_metadata_assemble_xml_file(metadata); + + err = _add_to_zip(self, metadata->file, &buffer, &buffer_size, + "xl/metadata.xml"); + + fclose(metadata->file); + free(buffer); + +mem_error: + lxw_metadata_free(metadata); + + return err; +} + +/* * Write the custom.xml file. */ STATIC lxw_error _write_custom_file(lxw_packager *self) { lxw_custom *custom; + char *buffer = NULL; + size_t buffer_size = 0; lxw_error err = LXW_NO_ERROR; if (STAILQ_EMPTY(self->workbook->custom_properties)) return LXW_NO_ERROR; @@ -607,23 +1084,25 @@ if (!custom) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } - custom->file = lxw_tmpfile(self->tmpdir); + custom->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!custom->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } custom->custom_properties = self->workbook->custom_properties; lxw_custom_assemble_xml_file(custom); - err = _add_file_to_zip(self, custom->file, "docProps/custom.xml"); + err = _add_to_zip(self, custom->file, &buffer, &buffer_size, + "docProps/custom.xml"); fclose(custom->file); + free(buffer); mem_error: lxw_custom_free(custom); return err; } @@ -634,27 +1113,31 @@ STATIC lxw_error _write_theme_file(lxw_packager *self) { lxw_error err = LXW_NO_ERROR; lxw_theme *theme = lxw_theme_new(); + char *buffer = NULL; + size_t buffer_size = 0; if (!theme) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } - theme->file = lxw_tmpfile(self->tmpdir); + theme->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!theme->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } lxw_theme_assemble_xml_file(theme); - err = _add_file_to_zip(self, theme->file, "xl/theme/theme1.xml"); + err = _add_to_zip(self, theme->file, &buffer, &buffer_size, + "xl/theme/theme1.xml"); fclose(theme->file); + free(buffer); mem_error: lxw_theme_free(theme); return err; @@ -665,10 +1148,12 @@ */ STATIC lxw_error _write_styles_file(lxw_packager *self) { lxw_styles *styles = lxw_styles_new(); + char *buffer = NULL; + size_t buffer_size = 0; lxw_hash_element *hash_element; lxw_error err = LXW_NO_ERROR; if (!styles) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; @@ -688,27 +1173,46 @@ memcpy(style_format, workbook_format, sizeof(lxw_format)); STAILQ_INSERT_TAIL(styles->xf_formats, style_format, list_pointers); } + /* Copy the unique and in-use dxf formats from the workbook to the styles + * dxf_format list. */ + LXW_FOREACH_ORDERED(hash_element, self->workbook->used_dxf_formats) { + lxw_format *workbook_format = (lxw_format *) hash_element->value; + lxw_format *style_format = lxw_format_new(); + + if (!style_format) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + memcpy(style_format, workbook_format, sizeof(lxw_format)); + STAILQ_INSERT_TAIL(styles->dxf_formats, style_format, list_pointers); + } + styles->font_count = self->workbook->font_count; styles->border_count = self->workbook->border_count; styles->fill_count = self->workbook->fill_count; styles->num_format_count = self->workbook->num_format_count; styles->xf_count = self->workbook->used_xf_formats->unique_count; + styles->dxf_count = self->workbook->used_dxf_formats->unique_count; + styles->has_comments = self->workbook->has_comments; - styles->file = lxw_tmpfile(self->tmpdir); + styles->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!styles->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } lxw_styles_assemble_xml_file(styles); - err = _add_file_to_zip(self, styles->file, "xl/styles.xml"); + err = _add_to_zip(self, styles->file, &buffer, &buffer_size, + "xl/styles.xml"); fclose(styles->file); + free(buffer); mem_error: lxw_styles_free(styles); return err; @@ -719,26 +1223,30 @@ */ STATIC lxw_error _write_content_types_file(lxw_packager *self) { lxw_content_types *content_types = lxw_content_types_new(); + char *buffer = NULL; + size_t buffer_size = 0; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; char filename[LXW_MAX_ATTRIBUTE_LENGTH] = { 0 }; uint32_t index = 1; uint32_t worksheet_index = 1; uint32_t chartsheet_index = 1; uint32_t drawing_count = _get_drawing_count(self); uint32_t chart_count = _get_chart_count(self); + uint32_t table_count = _get_table_count(self); lxw_error err = LXW_NO_ERROR; if (!content_types) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } - content_types->file = lxw_tmpfile(self->tmpdir); + content_types->file = lxw_get_filehandle(&buffer, &buffer_size, + self->tmpdir); if (!content_types->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } @@ -749,10 +1257,13 @@ lxw_ct_add_default(content_types, "jpeg", "image/jpeg"); if (workbook->has_bmp) lxw_ct_add_default(content_types, "bmp", "image/bmp"); + if (workbook->has_gif) + lxw_ct_add_default(content_types, "gif", "image/gif"); + if (workbook->vba_project) lxw_ct_add_default(content_types, "bin", "application/vnd.ms-office.vbaProject"); if (workbook->vba_project) @@ -760,10 +1271,14 @@ LXW_APP_MSEXCEL "sheet.macroEnabled.main+xml"); else lxw_ct_add_override(content_types, "/xl/workbook.xml", LXW_APP_DOCUMENT "spreadsheetml.sheet.main+xml"); + if (workbook->vba_project_signature) + lxw_ct_add_override(content_types, "/xl/vbaProjectSignature.bin", + "application/vnd.ms-office.vbaProjectSignature"); + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) { lxw_snprintf(filename, LXW_FILENAME_LENGTH, "/xl/chartsheets/sheet%d.xml", chartsheet_index++); lxw_ct_add_chartsheet_name(content_types, filename); @@ -785,21 +1300,41 @@ lxw_snprintf(filename, LXW_FILENAME_LENGTH, "/xl/drawings/drawing%d.xml", index); lxw_ct_add_drawing_name(content_types, filename); } + for (index = 1; index <= table_count; index++) { + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "/xl/tables/table%d.xml", index); + lxw_ct_add_table_name(content_types, filename); + } + + if (workbook->has_vml) + lxw_ct_add_vml_name(content_types); + + for (index = 1; index <= workbook->comment_count; index++) { + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "/xl/comments%d.xml", index); + lxw_ct_add_comment_name(content_types, filename); + } + if (workbook->sst->string_count) lxw_ct_add_shared_strings(content_types); if (!STAILQ_EMPTY(self->workbook->custom_properties)) lxw_ct_add_custom_properties(content_types); + if (workbook->has_metadata) + lxw_ct_add_metadata(content_types); + lxw_content_types_assemble_xml_file(content_types); - err = _add_file_to_zip(self, content_types->file, "[Content_Types].xml"); + err = _add_to_zip(self, content_types->file, &buffer, &buffer_size, + "[Content_Types].xml"); fclose(content_types->file); + free(buffer); mem_error: lxw_content_types_free(content_types); return err; @@ -810,10 +1345,12 @@ */ STATIC lxw_error _write_workbook_rels_file(lxw_packager *self) { lxw_relationships *rels = lxw_relationships_new(); + char *buffer = NULL; + size_t buffer_size = 0; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; uint32_t worksheet_index = 1; uint32_t chartsheet_index = 1; @@ -822,11 +1359,11 @@ if (!rels) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } - rels->file = lxw_tmpfile(self->tmpdir); + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } @@ -854,15 +1391,20 @@ if (workbook->vba_project) lxw_add_ms_package_relationship(rels, "/vbaProject", "vbaProject.bin"); + if (workbook->has_metadata) + lxw_add_document_relationship(rels, "/sheetMetadata", "metadata.xml"); + lxw_relationships_assemble_xml_file(rels); - err = _add_file_to_zip(self, rels->file, "xl/_rels/workbook.xml.rels"); + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, + "xl/_rels/workbook.xml.rels"); fclose(rels->file); + free(buffer); mem_error: lxw_free_relationships(rels); return err; @@ -874,10 +1416,12 @@ */ STATIC lxw_error _write_worksheet_rels_file(lxw_packager *self) { lxw_relationships *rels; + char *buffer = NULL; + size_t buffer_size = 0; lxw_rel_tuple *rel; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; @@ -891,16 +1435,21 @@ worksheet = sheet->u.worksheet; index++; if (STAILQ_EMPTY(worksheet->external_hyperlinks) && - STAILQ_EMPTY(worksheet->external_drawing_links)) + STAILQ_EMPTY(worksheet->external_drawing_links) && + STAILQ_EMPTY(worksheet->external_table_links) && + !worksheet->external_vml_header_link && + !worksheet->external_vml_comment_link && + !worksheet->external_background_link && + !worksheet->external_comment_link) continue; rels = lxw_relationships_new(); - rels->file = lxw_tmpfile(self->tmpdir); + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { lxw_free_relationships(rels); return LXW_ERROR_CREATING_TMPFILE; } @@ -912,18 +1461,44 @@ STAILQ_FOREACH(rel, worksheet->external_drawing_links, list_pointers) { lxw_add_worksheet_relationship(rels, rel->type, rel->target, rel->target_mode); } + rel = worksheet->external_vml_comment_link; + if (rel) + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + + rel = worksheet->external_vml_header_link; + if (rel) + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + + rel = worksheet->external_background_link; + if (rel) + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + + STAILQ_FOREACH(rel, worksheet->external_table_links, list_pointers) { + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + } + + rel = worksheet->external_comment_link; + if (rel) + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "xl/worksheets/_rels/sheet%d.xml.rels", index); lxw_relationships_assemble_xml_file(rels); - err = _add_file_to_zip(self, rels->file, sheetname); + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname); fclose(rels->file); + free(buffer); lxw_free_relationships(rels); RETURN_ON_ERROR(err); } @@ -936,10 +1511,12 @@ */ STATIC lxw_error _write_chartsheet_rels_file(lxw_packager *self) { lxw_relationships *rels; + char *buffer = NULL; + size_t buffer_size = 0; lxw_rel_tuple *rel; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; @@ -952,17 +1529,16 @@ else continue; index++; - /* TODO. This should never be empty. Put check higher up. */ if (STAILQ_EMPTY(worksheet->external_drawing_links)) continue; rels = lxw_relationships_new(); - rels->file = lxw_tmpfile(self->tmpdir); + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { lxw_free_relationships(rels); return LXW_ERROR_CREATING_TMPFILE; } @@ -979,13 +1555,14 @@ lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "xl/chartsheets/_rels/sheet%d.xml.rels", index); lxw_relationships_assemble_xml_file(rels); - err = _add_file_to_zip(self, rels->file, sheetname); + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname); fclose(rels->file); + free(buffer); lxw_free_relationships(rels); RETURN_ON_ERROR(err); } @@ -998,10 +1575,12 @@ */ STATIC lxw_error _write_drawing_rels_file(lxw_packager *self) { lxw_relationships *rels; + char *buffer = NULL; + size_t buffer_size = 0; lxw_rel_tuple *rel; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; @@ -1017,11 +1596,11 @@ if (STAILQ_EMPTY(worksheet->drawing_links)) continue; rels = lxw_relationships_new(); - rels->file = lxw_tmpfile(self->tmpdir); + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { lxw_free_relationships(rels); return LXW_ERROR_CREATING_TMPFILE; } @@ -1034,36 +1613,126 @@ lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "xl/drawings/_rels/drawing%d.xml.rels", index++); lxw_relationships_assemble_xml_file(rels); - err = _add_file_to_zip(self, rels->file, sheetname); + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname); fclose(rels->file); + free(buffer); lxw_free_relationships(rels); RETURN_ON_ERROR(err); } return LXW_NO_ERROR; } /* + * Write the vmlDrawing .rels files for worksheets that contain images in + * headers or footers. + */ +STATIC lxw_error +_write_vml_drawing_rels_file(lxw_packager *self, lxw_worksheet *worksheet, + uint32_t index) +{ + lxw_relationships *rels; + char *buffer = NULL; + size_t buffer_size = 0; + lxw_rel_tuple *rel; + char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + lxw_error err = LXW_NO_ERROR; + + rels = lxw_relationships_new(); + + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); + if (!rels->file) { + lxw_free_relationships(rels); + return LXW_ERROR_CREATING_TMPFILE; + } + + STAILQ_FOREACH(rel, worksheet->vml_drawing_links, list_pointers) { + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + + } + + lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, + "xl/drawings/_rels/vmlDrawing%d.vml.rels", index); + + lxw_relationships_assemble_xml_file(rels); + + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname); + + fclose(rels->file); + free(buffer); + lxw_free_relationships(rels); + + return err; +} + +/* + * Write the vbaProject .rels xml file. + */ +STATIC lxw_error +_write_vba_project_rels_file(lxw_packager *self) +{ + lxw_relationships *rels; + lxw_workbook *workbook = self->workbook; + lxw_error err = LXW_NO_ERROR; + char *buffer = NULL; + size_t buffer_size = 0; + + if (!workbook->vba_project_signature) + return LXW_NO_ERROR; + + rels = lxw_relationships_new(); + if (!rels) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); + if (!rels->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + lxw_add_ms_package_relationship(rels, "/vbaProjectSignature", + "vbaProjectSignature.bin"); + + lxw_relationships_assemble_xml_file(rels); + + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, + "xl/_rels/vbaProject.bin.rels"); + + fclose(rels->file); + free(buffer); + +mem_error: + lxw_free_relationships(rels); + + return err; +} + +/* * Write the _rels/.rels xml file. */ STATIC lxw_error _write_root_rels_file(lxw_packager *self) { lxw_relationships *rels = lxw_relationships_new(); + char *buffer = NULL; + size_t buffer_size = 0; lxw_error err = LXW_NO_ERROR; if (!rels) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } - rels->file = lxw_tmpfile(self->tmpdir); + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } @@ -1081,13 +1750,14 @@ "/custom-properties", "docProps/custom.xml"); lxw_relationships_assemble_xml_file(rels); - err = _add_file_to_zip(self, rels->file, "_rels/.rels"); + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, "_rels/.rels"); fclose(rels->file); + free(buffer); mem_error: lxw_free_relationships(rels); return err; @@ -1120,16 +1790,16 @@ } fflush(file); rewind(file); - size_read = fread(self->buffer, 1, self->buffer_size, file); + size_read = fread((void *) self->buffer, 1, self->buffer_size, file); while (size_read) { if (size_read < self->buffer_size) { - if (feof(file) == 0) { + if (ferror(file)) { LXW_ERROR("Error reading member file data"); RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD); } } @@ -1139,11 +1809,12 @@ if (error < 0) { LXW_ERROR("Error in writing member in the zipfile"); RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD); } - size_read = fread(self->buffer, 1, self->buffer_size, file); + size_read = + fread((void *) (void *) self->buffer, 1, self->buffer_size, file); } error = zipCloseFileInZip(self->zipfile); if (error != ZIP_OK) { LXW_ERROR("Error in closing member in the zipfile"); @@ -1152,12 +1823,12 @@ return LXW_NO_ERROR; } STATIC lxw_error -_add_buffer_to_zip(lxw_packager *self, unsigned char *buffer, - size_t buffer_size, const char *filename) +_add_buffer_to_zip(lxw_packager *self, const char *buffer, size_t buffer_size, + const char *filename) { int16_t error = ZIP_OK; error = zipOpenNewFileInZip4_64(self->zipfile, filename, @@ -1188,74 +1859,103 @@ } return LXW_NO_ERROR; } +STATIC lxw_error +_add_to_zip(lxw_packager *self, FILE * file, char **buffer, + size_t *buffer_size, const char *filename) +{ + /* Flush to ensure buffer is updated when using a memory-backed file. */ + fflush(file); + return *buffer ? + _add_buffer_to_zip(self, *buffer, *buffer_size, filename) : + _add_file_to_zip(self, file, filename); +} + /* - * Write the xml files that make up the XLXS OPC package. + * Write the xml files that make up the XLSX OPC package. */ lxw_error lxw_create_package(lxw_packager *self) { lxw_error error; int8_t zip_error; error = _write_content_types_file(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_root_rels_file(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_workbook_rels_file(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_worksheet_files(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_chartsheet_files(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_workbook_file(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_chart_files(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_drawing_files(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_vml_files(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + + error = _write_comment_files(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + + error = _write_table_files(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_shared_strings_file(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_custom_file(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_theme_file(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_styles_file(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_worksheet_rels_file(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_chartsheet_rels_file(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_drawing_rels_file(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_image_files(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _add_vba_project(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _add_vba_project_signature(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + + error = _write_vba_project_rels_file(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_core_file(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_metadata_file(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_app_file(self); - RETURN_ON_ERROR(error); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); zip_error = zipClose(self->zipfile, NULL); if (zip_error) { RETURN_ON_ZIP_ERROR(zip_error, LXW_ERROR_ZIP_CLOSE); }