2014-06-08 17:40:59 +01:00
|
|
|
/*****************************************************************************
|
2022-06-25 10:51:16 +01:00
|
|
|
* packager - A library for assembling xml files into an Excel XLSX file.
|
2014-06-08 17:40:59 +01:00
|
|
|
*
|
2022-06-25 10:51:16 +01:00
|
|
|
* A class for writing the Excel XLSX Packager file.
|
|
|
|
*
|
|
|
|
* 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
|
2023-09-20 20:37:38 +02:00
|
|
|
* | |____ workbook.xml.rels
|
2022-06-25 10:51:16 +01:00
|
|
|
* |
|
|
|
|
* |_____rels
|
|
|
|
* |____ .rels
|
|
|
|
*
|
|
|
|
* The Packager class coordinates the classes that represent the
|
|
|
|
* elements of the package and writes them into the XLSX file.
|
2014-06-08 17:40:59 +01:00
|
|
|
*
|
2024-02-26 18:47:32 +00:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2025-02-11 00:03:36 +00:00
|
|
|
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org.
|
2014-06-08 17:40:59 +01:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2021-03-30 12:25:21 +01:00
|
|
|
#include <zlib.h>
|
2014-06-08 17:40:59 +01:00
|
|
|
#include "xlsxwriter/xmlwriter.h"
|
|
|
|
#include "xlsxwriter/packager.h"
|
|
|
|
#include "xlsxwriter/hash_table.h"
|
|
|
|
#include "xlsxwriter/utility.h"
|
|
|
|
|
2025-02-12 19:46:42 +00:00
|
|
|
STATIC lxw_error _add_file_to_zip(lxw_packager *self, FILE *file,
|
2017-01-09 20:26:32 +00:00
|
|
|
const char *filename);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2023-09-24 20:40:07 +01:00
|
|
|
STATIC lxw_error _add_buffer_to_zip(lxw_packager *self, const char *buffer,
|
2018-08-29 00:35:54 +01:00
|
|
|
size_t buffer_size, const char *filename);
|
|
|
|
|
2025-02-12 19:46:42 +00:00
|
|
|
STATIC lxw_error _add_to_zip(lxw_packager *self, FILE *file,
|
2022-11-12 20:42:46 +04:00
|
|
|
char **buffer, size_t *buffer_size,
|
|
|
|
const char *filename);
|
|
|
|
|
2020-08-16 16:03:56 +01:00
|
|
|
STATIC lxw_error _write_vml_drawing_rels_file(lxw_packager *self,
|
|
|
|
lxw_worksheet *worksheet,
|
|
|
|
uint32_t index);
|
|
|
|
|
2014-06-08 17:40:59 +01:00
|
|
|
/*
|
|
|
|
* Forward declarations.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Private functions.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
2016-06-23 21:35:23 +01:00
|
|
|
/* Avoid non MSVC definition of _WIN32 in MinGW. */
|
2016-07-03 14:05:38 +01:00
|
|
|
|
2016-06-23 21:35:23 +01:00
|
|
|
#ifdef __MINGW32__
|
|
|
|
#undef _WIN32
|
|
|
|
#endif
|
|
|
|
|
2015-11-08 20:46:58 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
|
2016-07-03 14:05:38 +01:00
|
|
|
/* Silence Windows warning with duplicate symbol for SLIST_ENTRY in local
|
2023-09-20 20:37:38 +02:00
|
|
|
* queue.h and windows.h. */
|
2016-07-03 14:05:38 +01:00
|
|
|
#undef SLIST_ENTRY
|
|
|
|
|
2015-11-08 20:46:58 +00:00
|
|
|
#include <windows.h>
|
2017-08-20 11:01:41 +01:00
|
|
|
|
|
|
|
#ifdef USE_SYSTEM_MINIZIP
|
|
|
|
#include "minizip/iowin32.h"
|
|
|
|
#else
|
2017-01-07 00:54:13 +00:00
|
|
|
#include "../third_party/minizip/iowin32.h"
|
2017-08-20 11:01:41 +01:00
|
|
|
#endif
|
2015-11-08 20:46:58 +00:00
|
|
|
|
|
|
|
zipFile
|
|
|
|
_open_zipfile_win32(const char *filename)
|
|
|
|
{
|
|
|
|
int n;
|
2016-07-04 21:51:32 +01:00
|
|
|
zlib_filefunc64_def filefunc;
|
|
|
|
|
2015-11-08 20:46:58 +00:00
|
|
|
wchar_t wide_filename[_MAX_PATH + 1] = L"";
|
|
|
|
|
|
|
|
/* Build a UTF-16 filename for Win32. */
|
2016-05-24 20:24:09 +01:00
|
|
|
n = MultiByteToWideChar(CP_UTF8, 0, filename, (int) strlen(filename),
|
2015-12-05 00:25:09 +00:00
|
|
|
wide_filename, _MAX_PATH);
|
2015-11-08 20:46:58 +00:00
|
|
|
|
|
|
|
if (n == 0) {
|
|
|
|
LXW_ERROR("MultiByteToWideChar error");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Use the native Win32 file handling functions with minizip. */
|
2018-04-19 10:17:00 -07:00
|
|
|
fill_win32_filefunc64W(&filefunc);
|
2015-11-08 20:46:58 +00:00
|
|
|
|
|
|
|
return zipOpen2_64(wide_filename, 0, NULL, &filefunc);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-10 13:15:28 +04:00
|
|
|
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);
|
2023-09-24 20:40:07 +01:00
|
|
|
if (fread((void *) packager->output_buffer, size, 1, file) < 1)
|
2022-11-10 13:15:28 +04:00
|
|
|
goto mem_error;
|
|
|
|
|
|
|
|
packager->output_buffer_size = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fclose(file);
|
|
|
|
|
|
|
|
mem_error:
|
|
|
|
fclose(file);
|
|
|
|
return EOF;
|
|
|
|
}
|
|
|
|
|
2014-06-08 17:40:59 +01:00
|
|
|
/*
|
|
|
|
* Create a new packager object.
|
|
|
|
*/
|
|
|
|
lxw_packager *
|
2023-09-24 20:40:07 +01:00
|
|
|
lxw_packager_new(const char *filename, const char *tmpdir, uint8_t use_zip64)
|
2014-06-08 17:40:59 +01:00
|
|
|
{
|
2022-11-10 13:15:28 +04:00
|
|
|
zlib_filefunc_def filefunc;
|
2014-06-08 17:40:59 +01:00
|
|
|
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);
|
|
|
|
|
2022-11-10 13:15:28 +04:00
|
|
|
packager->filename = NULL;
|
2016-07-10 23:12:01 +01:00
|
|
|
packager->tmpdir = tmpdir;
|
2022-11-10 13:15:28 +04:00
|
|
|
|
|
|
|
if (filename) {
|
|
|
|
packager->filename = lxw_strdup(filename);
|
|
|
|
GOTO_LABEL_ON_MEM_ERROR(packager->filename, mem_error);
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
|
|
|
packager->buffer_size = LXW_ZIP_BUFFER_SIZE;
|
2019-06-08 00:24:40 +01:00
|
|
|
packager->use_zip64 = use_zip64;
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2016-07-11 23:34:24 +01:00
|
|
|
/* Initialize the zip_fileinfo struct to Jan 1 1980 like Excel. */
|
|
|
|
packager->zipfile_info.tmz_date.tm_sec = 0;
|
|
|
|
packager->zipfile_info.tmz_date.tm_min = 0;
|
|
|
|
packager->zipfile_info.tmz_date.tm_hour = 0;
|
|
|
|
packager->zipfile_info.tmz_date.tm_mday = 1;
|
|
|
|
packager->zipfile_info.tmz_date.tm_mon = 0;
|
|
|
|
packager->zipfile_info.tmz_date.tm_year = 1980;
|
2014-06-08 17:40:59 +01:00
|
|
|
packager->zipfile_info.dosDate = 0;
|
|
|
|
packager->zipfile_info.internal_fa = 0;
|
|
|
|
packager->zipfile_info.external_fa = 0;
|
|
|
|
|
2022-11-10 13:15:28 +04:00
|
|
|
packager->output_buffer = NULL;
|
|
|
|
packager->output_buffer_size = 0;
|
|
|
|
|
2014-06-08 17:40:59 +01:00
|
|
|
/* Create a zip container for the xlsx file. */
|
2022-11-10 13:15:28 +04:00
|
|
|
if (packager->filename) {
|
2015-11-08 20:46:58 +00:00
|
|
|
#ifdef _WIN32
|
2022-11-10 13:15:28 +04:00
|
|
|
packager->zipfile = _open_zipfile_win32(packager->filename);
|
2015-11-08 20:46:58 +00:00
|
|
|
#else
|
2022-11-10 13:15:28 +04:00
|
|
|
packager->zipfile = zipOpen(packager->filename, 0);
|
2015-11-08 20:46:58 +00:00
|
|
|
#endif
|
2022-11-10 13:15:28 +04:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
2015-11-08 20:46:58 +00:00
|
|
|
|
2015-12-05 00:25:09 +00:00
|
|
|
if (packager->zipfile == NULL)
|
2014-06-08 17:40:59 +01:00
|
|
|
goto mem_error;
|
|
|
|
|
|
|
|
return packager;
|
|
|
|
|
|
|
|
mem_error:
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_packager_free(packager);
|
2014-06-08 17:40:59 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free a packager object.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_packager_free(lxw_packager *packager)
|
2014-06-08 17:40:59 +01:00
|
|
|
{
|
|
|
|
if (!packager)
|
|
|
|
return;
|
|
|
|
|
2023-09-24 20:40:07 +01:00
|
|
|
free((void *) packager->buffer);
|
|
|
|
free((void *) packager->filename);
|
2014-06-08 17:40:59 +01:00
|
|
|
free(packager);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* File assembly functions.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
/*
|
|
|
|
* Write the workbook.xml file.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2014-06-08 17:40:59 +01:00
|
|
|
_write_workbook_file(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_workbook *workbook = self->workbook;
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err;
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
|
|
|
workbook->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2016-05-30 00:30:08 +01:00
|
|
|
if (!workbook->file)
|
2016-05-30 21:16:25 +01:00
|
|
|
return LXW_ERROR_CREATING_TMPFILE;
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2016-01-04 20:05:22 +00:00
|
|
|
lxw_workbook_assemble_xml_file(workbook);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, workbook->file, &buffer, &buffer_size,
|
|
|
|
"xl/workbook.xml");
|
2014-06-08 17:40:59 +01:00
|
|
|
fclose(workbook->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_ON_ERROR(err);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-09 20:26:32 +00:00
|
|
|
return LXW_NO_ERROR;
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the worksheet files.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2014-06-08 17:40:59 +01:00
|
|
|
_write_worksheet_files(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_workbook *workbook = self->workbook;
|
2018-09-07 11:51:03 +01:00
|
|
|
lxw_sheet *sheet;
|
2014-06-08 17:40:59 +01:00
|
|
|
lxw_worksheet *worksheet;
|
2016-05-05 23:40:06 +01:00
|
|
|
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t index = 1;
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err;
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2018-09-07 11:51:03 +01:00
|
|
|
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
|
|
|
|
if (sheet->is_chartsheet)
|
|
|
|
continue;
|
|
|
|
else
|
|
|
|
worksheet = sheet->u.worksheet;
|
|
|
|
|
2016-05-05 23:40:06 +01:00
|
|
|
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
|
2015-11-08 00:19:34 +00:00
|
|
|
"xl/worksheets/sheet%d.xml", index++);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2014-06-25 00:59:04 +01:00
|
|
|
if (worksheet->optimize_row)
|
2016-01-04 20:10:38 +00:00
|
|
|
lxw_worksheet_write_single_row(worksheet);
|
2014-06-25 00:59:04 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
worksheet->file = lxw_get_filehandle(&buffer, &buffer_size,
|
|
|
|
self->tmpdir);
|
2016-05-30 00:30:08 +01:00
|
|
|
if (!worksheet->file)
|
2016-05-30 21:16:25 +01:00
|
|
|
return LXW_ERROR_CREATING_TMPFILE;
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2016-01-04 20:10:38 +00:00
|
|
|
lxw_worksheet_assemble_xml_file(worksheet);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, worksheet->file, &buffer, &buffer_size,
|
|
|
|
sheetname);
|
2014-06-08 17:40:59 +01:00
|
|
|
fclose(worksheet->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_ON_ERROR(err);
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
2017-01-09 20:26:32 +00:00
|
|
|
return LXW_NO_ERROR;
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
2018-09-08 23:36:09 +01:00
|
|
|
/*
|
|
|
|
* Write the chartsheet files.
|
|
|
|
*/
|
|
|
|
STATIC lxw_error
|
|
|
|
_write_chartsheet_files(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_workbook *workbook = self->workbook;
|
|
|
|
lxw_sheet *sheet;
|
|
|
|
lxw_chartsheet *chartsheet;
|
|
|
|
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t index = 1;
|
2018-09-08 23:36:09 +01:00
|
|
|
lxw_error err;
|
|
|
|
|
|
|
|
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
|
|
|
|
if (sheet->is_chartsheet)
|
|
|
|
chartsheet = sheet->u.chartsheet;
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
|
|
|
|
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
|
|
|
|
"xl/chartsheets/sheet%d.xml", index++);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
chartsheet->file = lxw_get_filehandle(&buffer, &buffer_size,
|
|
|
|
self->tmpdir);
|
2018-09-08 23:36:09 +01:00
|
|
|
if (!chartsheet->file)
|
|
|
|
return LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
|
|
|
|
lxw_chartsheet_assemble_xml_file(chartsheet);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, chartsheet->file, &buffer, &buffer_size,
|
|
|
|
sheetname);
|
2018-09-08 23:36:09 +01:00
|
|
|
fclose(chartsheet->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_ON_ERROR(err);
|
2018-09-08 23:36:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return LXW_NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2015-12-30 00:35:03 +00:00
|
|
|
/*
|
|
|
|
* Write the /xl/media/image?.xml files.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2015-12-30 00:35:03 +00:00
|
|
|
_write_image_files(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_workbook *workbook = self->workbook;
|
2018-09-07 11:51:03 +01:00
|
|
|
lxw_sheet *sheet;
|
2015-12-30 00:35:03 +00:00
|
|
|
lxw_worksheet *worksheet;
|
2019-11-11 20:53:15 +00:00
|
|
|
lxw_object_properties *object_props;
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err;
|
2017-06-26 23:51:54 +01:00
|
|
|
FILE *image_stream;
|
2015-12-30 00:35:03 +00:00
|
|
|
|
2016-05-05 23:40:06 +01:00
|
|
|
char filename[LXW_FILENAME_LENGTH] = { 0 };
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t index = 1;
|
2015-12-30 00:35:03 +00:00
|
|
|
|
2018-09-07 11:51:03 +01:00
|
|
|
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
|
|
|
|
if (sheet->is_chartsheet)
|
|
|
|
continue;
|
|
|
|
else
|
|
|
|
worksheet = sheet->u.worksheet;
|
2015-12-30 00:35:03 +00:00
|
|
|
|
2024-07-30 00:33:14 +01:00
|
|
|
if (STAILQ_EMPTY(worksheet->image_props)
|
|
|
|
&& STAILQ_EMPTY(worksheet->embedded_image_props))
|
2015-12-30 00:35:03 +00:00
|
|
|
continue;
|
|
|
|
|
2024-07-30 00:33:14 +01:00
|
|
|
STAILQ_FOREACH(object_props, worksheet->embedded_image_props,
|
|
|
|
list_pointers) {
|
|
|
|
|
|
|
|
if (object_props->is_duplicate)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
|
|
|
|
"xl/media/image%d.%s", index++,
|
|
|
|
object_props->extension);
|
|
|
|
|
|
|
|
if (!object_props->is_image_buffer) {
|
|
|
|
/* Check that the image file exists and can be opened. */
|
|
|
|
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.",
|
|
|
|
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,
|
|
|
|
object_props->image_buffer,
|
|
|
|
object_props->image_buffer_size,
|
|
|
|
filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
RETURN_ON_ERROR(err);
|
|
|
|
}
|
|
|
|
|
2019-11-11 20:53:15 +00:00
|
|
|
STAILQ_FOREACH(object_props, worksheet->image_props, list_pointers) {
|
2015-12-30 00:35:03 +00:00
|
|
|
|
2019-12-24 19:16:13 +00:00
|
|
|
if (object_props->is_duplicate)
|
|
|
|
continue;
|
|
|
|
|
2016-05-05 23:40:06 +01:00
|
|
|
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
|
2019-11-11 20:53:15 +00:00
|
|
|
"xl/media/image%d.%s", index++,
|
|
|
|
object_props->extension);
|
2015-12-30 00:35:03 +00:00
|
|
|
|
2019-11-11 20:53:15 +00:00
|
|
|
if (!object_props->is_image_buffer) {
|
2018-08-29 00:35:54 +01:00
|
|
|
/* Check that the image file exists and can be opened. */
|
2019-11-11 20:53:15 +00:00
|
|
|
image_stream = lxw_fopen(object_props->filename, "rb");
|
2018-08-29 00:35:54 +01:00
|
|
|
if (!image_stream) {
|
|
|
|
LXW_WARN_FORMAT1("Error adding image to xlsx file: file "
|
|
|
|
"doesn't exist or can't be opened: %s.",
|
2019-11-11 20:53:15 +00:00
|
|
|
object_props->filename);
|
2018-08-29 00:35:54 +01:00
|
|
|
return LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = _add_file_to_zip(self, image_stream, filename);
|
|
|
|
fclose(image_stream);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
err = _add_buffer_to_zip(self,
|
2019-11-11 20:53:15 +00:00
|
|
|
object_props->image_buffer,
|
|
|
|
object_props->image_buffer_size,
|
|
|
|
filename);
|
2017-06-26 23:51:54 +01:00
|
|
|
}
|
2017-10-04 08:16:09 +01:00
|
|
|
|
|
|
|
RETURN_ON_ERROR(err);
|
2015-12-30 00:35:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-09 20:26:32 +00:00
|
|
|
return LXW_NO_ERROR;
|
2015-12-30 00:35:03 +00:00
|
|
|
}
|
|
|
|
|
2019-06-16 15:46:09 +01:00
|
|
|
/*
|
|
|
|
* Write the xl/vbaProject.bin file.
|
|
|
|
*/
|
|
|
|
STATIC lxw_error
|
|
|
|
_add_vba_project(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_workbook *workbook = self->workbook;
|
|
|
|
lxw_error err;
|
|
|
|
FILE *image_stream;
|
|
|
|
|
|
|
|
if (!workbook->vba_project)
|
|
|
|
return LXW_NO_ERROR;
|
|
|
|
|
|
|
|
/* Check that the image file exists and can be opened. */
|
2019-10-06 19:57:28 +01:00
|
|
|
image_stream = lxw_fopen(workbook->vba_project, "rb");
|
2019-06-16 15:46:09 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = _add_file_to_zip(self, image_stream, "xl/vbaProject.bin");
|
|
|
|
fclose(image_stream);
|
|
|
|
RETURN_ON_ERROR(err);
|
|
|
|
|
|
|
|
return LXW_NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2023-09-20 20:37:38 +02:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2016-05-02 21:43:37 +01:00
|
|
|
/*
|
|
|
|
* Write the chart files.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2016-05-02 21:43:37 +01:00
|
|
|
_write_chart_files(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_workbook *workbook = self->workbook;
|
|
|
|
lxw_chart *chart;
|
2016-05-05 23:40:06 +01:00
|
|
|
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t index = 1;
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err;
|
2016-05-02 21:43:37 +01:00
|
|
|
|
2016-05-20 00:14:07 +01:00
|
|
|
STAILQ_FOREACH(chart, workbook->ordered_charts, ordered_list_pointers) {
|
2016-05-06 23:45:03 +01:00
|
|
|
|
2016-05-05 23:40:06 +01:00
|
|
|
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
|
2016-05-02 21:43:37 +01:00
|
|
|
"xl/charts/chart%d.xml", index++);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
chart->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2016-05-30 00:30:08 +01:00
|
|
|
if (!chart->file)
|
2016-05-30 21:16:25 +01:00
|
|
|
return LXW_ERROR_CREATING_TMPFILE;
|
2016-05-02 21:43:37 +01:00
|
|
|
|
|
|
|
lxw_chart_assemble_xml_file(chart);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, chart->file, &buffer, &buffer_size,
|
|
|
|
sheetname);
|
2016-05-02 21:43:37 +01:00
|
|
|
fclose(chart->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_ON_ERROR(err);
|
2016-05-02 21:43:37 +01:00
|
|
|
}
|
|
|
|
|
2017-01-09 20:26:32 +00:00
|
|
|
return LXW_NO_ERROR;
|
2016-05-02 21:43:37 +01:00
|
|
|
}
|
|
|
|
|
2019-03-29 18:10:20 +00:00
|
|
|
/*
|
|
|
|
* Count the chart files.
|
|
|
|
*/
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t
|
2019-03-29 18:10:20 +00:00
|
|
|
_get_chart_count(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_workbook *workbook = self->workbook;
|
|
|
|
lxw_chart *chart;
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t chart_count = 0;
|
2019-03-29 18:10:20 +00:00
|
|
|
|
|
|
|
STAILQ_FOREACH(chart, workbook->ordered_charts, ordered_list_pointers) {
|
|
|
|
chart_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return chart_count;
|
|
|
|
}
|
|
|
|
|
2015-12-30 00:35:03 +00:00
|
|
|
/*
|
|
|
|
* Write the drawing files.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2015-12-30 00:35:03 +00:00
|
|
|
_write_drawing_files(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_workbook *workbook = self->workbook;
|
2018-09-07 11:51:03 +01:00
|
|
|
lxw_sheet *sheet;
|
2015-12-30 00:35:03 +00:00
|
|
|
lxw_worksheet *worksheet;
|
|
|
|
lxw_drawing *drawing;
|
2016-05-05 23:40:06 +01:00
|
|
|
char filename[LXW_FILENAME_LENGTH] = { 0 };
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t index = 1;
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err;
|
2015-12-30 00:35:03 +00:00
|
|
|
|
2018-09-07 11:51:03 +01:00
|
|
|
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
|
|
|
|
if (sheet->is_chartsheet)
|
2018-09-08 23:36:09 +01:00
|
|
|
worksheet = sheet->u.chartsheet->worksheet;
|
2018-09-07 11:51:03 +01:00
|
|
|
else
|
|
|
|
worksheet = sheet->u.worksheet;
|
|
|
|
|
2015-12-30 00:35:03 +00:00
|
|
|
drawing = worksheet->drawing;
|
|
|
|
|
|
|
|
if (drawing) {
|
2016-05-05 23:40:06 +01:00
|
|
|
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
|
2015-12-30 00:35:03 +00:00
|
|
|
"xl/drawings/drawing%d.xml", index++);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
drawing->file = lxw_get_filehandle(&buffer, &buffer_size,
|
|
|
|
self->tmpdir);
|
2016-05-30 00:30:08 +01:00
|
|
|
if (!drawing->file)
|
2016-05-30 21:16:25 +01:00
|
|
|
return LXW_ERROR_CREATING_TMPFILE;
|
2016-05-30 00:30:08 +01:00
|
|
|
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_drawing_assemble_xml_file(drawing);
|
2016-05-02 21:43:37 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, drawing->file, &buffer, &buffer_size,
|
|
|
|
filename);
|
2015-12-30 00:35:03 +00:00
|
|
|
fclose(drawing->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_ON_ERROR(err);
|
2015-12-30 00:35:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-09 20:26:32 +00:00
|
|
|
return LXW_NO_ERROR;
|
2015-12-30 00:35:03 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 18:10:20 +00:00
|
|
|
/*
|
|
|
|
* Count the drawing files.
|
|
|
|
*/
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t
|
2019-03-29 18:10:20 +00:00
|
|
|
_get_drawing_count(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_workbook *workbook = self->workbook;
|
|
|
|
lxw_sheet *sheet;
|
|
|
|
lxw_worksheet *worksheet;
|
|
|
|
lxw_drawing *drawing;
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t drawing_count = 0;
|
2019-03-29 18:10:20 +00:00
|
|
|
|
|
|
|
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
|
|
|
|
if (sheet->is_chartsheet)
|
|
|
|
worksheet = sheet->u.chartsheet->worksheet;
|
|
|
|
else
|
|
|
|
worksheet = sheet->u.worksheet;
|
|
|
|
|
|
|
|
drawing = worksheet->drawing;
|
|
|
|
|
|
|
|
if (drawing)
|
|
|
|
drawing_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return drawing_count;
|
|
|
|
}
|
|
|
|
|
2021-08-16 21:29:27 +01:00
|
|
|
/*
|
|
|
|
* 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 };
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2021-08-16 21:29:27 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
table->file = lxw_get_filehandle(&buffer, &buffer_size,
|
|
|
|
self->tmpdir);
|
2021-08-16 21:29:27 +01:00
|
|
|
if (!table->file) {
|
|
|
|
lxw_table_free(table);
|
|
|
|
return LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
}
|
|
|
|
|
|
|
|
table->table_obj = table_obj;
|
|
|
|
|
|
|
|
lxw_table_assemble_xml_file(table);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, table->file, &buffer, &buffer_size,
|
|
|
|
filename);
|
2021-08-16 21:29:27 +01:00
|
|
|
fclose(table->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2021-08-16 21:29:27 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-12-27 20:10:11 +00:00
|
|
|
/*
|
|
|
|
* 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 };
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2019-12-27 20:10:11 +00:00
|
|
|
uint32_t index = 1;
|
|
|
|
lxw_error err;
|
|
|
|
|
|
|
|
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
|
|
|
|
if (sheet->is_chartsheet)
|
|
|
|
continue;
|
|
|
|
else
|
|
|
|
worksheet = sheet->u.worksheet;
|
|
|
|
|
2020-08-16 16:03:56 +01:00
|
|
|
if (!worksheet->has_vml && !worksheet->has_header_vml)
|
2019-12-27 20:10:11 +00:00
|
|
|
continue;
|
|
|
|
|
2020-08-16 16:03:56 +01:00
|
|
|
if (worksheet->has_vml) {
|
2019-12-27 20:10:11 +00:00
|
|
|
|
2020-08-16 16:03:56 +01:00
|
|
|
vml = lxw_vml_new();
|
|
|
|
if (!vml)
|
|
|
|
return LXW_ERROR_MEMORY_MALLOC_FAILED;
|
2019-12-27 20:10:11 +00:00
|
|
|
|
2020-08-16 16:03:56 +01:00
|
|
|
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
|
|
|
|
"xl/drawings/vmlDrawing%d.vml", index++);
|
2019-12-27 20:10:11 +00:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
vml->file = lxw_get_filehandle(&buffer, &buffer_size,
|
|
|
|
self->tmpdir);
|
2020-08-16 16:03:56 +01:00
|
|
|
if (!vml->file) {
|
|
|
|
lxw_vml_free(vml);
|
|
|
|
return LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
}
|
|
|
|
|
|
|
|
vml->comment_objs = worksheet->comment_objs;
|
2021-08-26 21:29:31 +01:00
|
|
|
vml->button_objs = worksheet->button_objs;
|
2020-08-16 16:03:56 +01:00
|
|
|
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);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2020-08-16 16:03:56 +01:00
|
|
|
lxw_vml_free(vml);
|
|
|
|
return LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
lxw_vml_assemble_xml_file(vml);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, vml->file, &buffer, &buffer_size,
|
|
|
|
filename);
|
2019-12-27 20:10:11 +00:00
|
|
|
|
|
|
|
fclose(vml->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2019-12-27 20:10:11 +00:00
|
|
|
lxw_vml_free(vml);
|
2020-08-16 16:03:56 +01:00
|
|
|
|
|
|
|
RETURN_ON_ERROR(err);
|
2019-12-27 20:10:11 +00:00
|
|
|
}
|
|
|
|
|
2020-08-16 16:03:56 +01:00
|
|
|
if (worksheet->has_header_vml) {
|
2019-12-27 20:10:11 +00:00
|
|
|
|
2020-08-16 16:03:56 +01:00
|
|
|
err = _write_vml_drawing_rels_file(self, worksheet, index);
|
|
|
|
RETURN_ON_ERROR(err);
|
2019-12-27 20:10:11 +00:00
|
|
|
|
2020-08-16 16:03:56 +01:00
|
|
|
vml = lxw_vml_new();
|
|
|
|
if (!vml)
|
|
|
|
return LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
|
|
|
|
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
|
|
|
|
"xl/drawings/vmlDrawing%d.vml", index++);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
vml->file = lxw_get_filehandle(&buffer, &buffer_size,
|
|
|
|
self->tmpdir);
|
2020-08-16 16:03:56 +01:00
|
|
|
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);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2020-08-16 16:03:56 +01:00
|
|
|
lxw_vml_free(vml);
|
|
|
|
return LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
lxw_vml_assemble_xml_file(vml);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, vml->file, &buffer, &buffer_size,
|
|
|
|
filename);
|
2020-08-16 16:03:56 +01:00
|
|
|
|
|
|
|
fclose(vml->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2020-08-16 16:03:56 +01:00
|
|
|
lxw_vml_free(vml);
|
|
|
|
|
|
|
|
RETURN_ON_ERROR(err);
|
|
|
|
}
|
2019-12-27 20:10:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 };
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2019-12-27 20:10:11 +00:00
|
|
|
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++);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
comment->file = lxw_get_filehandle(&buffer, &buffer_size,
|
|
|
|
self->tmpdir);
|
2019-12-27 20:10:11 +00:00
|
|
|
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);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, comment->file, &buffer, &buffer_size,
|
|
|
|
filename);
|
2019-12-27 20:10:11 +00:00
|
|
|
|
|
|
|
fclose(comment->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2019-12-27 20:10:11 +00:00
|
|
|
lxw_comment_free(comment);
|
|
|
|
|
|
|
|
RETURN_ON_ERROR(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
return LXW_NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2014-06-08 17:40:59 +01:00
|
|
|
/*
|
|
|
|
* Write the sharedStrings.xml file.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2014-06-08 17:40:59 +01:00
|
|
|
_write_shared_strings_file(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_sst *sst = self->workbook->sst;
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err;
|
2014-06-08 17:40:59 +01:00
|
|
|
|
|
|
|
/* Skip the sharedStrings file if there are no shared strings. */
|
|
|
|
if (!sst->string_count)
|
2017-01-09 20:26:32 +00:00
|
|
|
return LXW_NO_ERROR;
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
sst->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2016-05-30 00:30:08 +01:00
|
|
|
if (!sst->file)
|
2016-05-30 21:16:25 +01:00
|
|
|
return LXW_ERROR_CREATING_TMPFILE;
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_sst_assemble_xml_file(sst);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, sst->file, &buffer, &buffer_size,
|
|
|
|
"xl/sharedStrings.xml");
|
2014-06-08 17:40:59 +01:00
|
|
|
fclose(sst->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_ON_ERROR(err);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-09 20:26:32 +00:00
|
|
|
return LXW_NO_ERROR;
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the app.xml file.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2014-06-08 17:40:59 +01:00
|
|
|
_write_app_file(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_workbook *workbook = self->workbook;
|
2018-09-07 11:51:03 +01:00
|
|
|
lxw_sheet *sheet;
|
2014-06-08 17:40:59 +01:00
|
|
|
lxw_worksheet *worksheet;
|
2018-09-08 23:36:09 +01:00
|
|
|
lxw_chartsheet *chartsheet;
|
2015-04-06 23:46:39 +01:00
|
|
|
lxw_defined_name *defined_name;
|
2017-01-07 00:54:13 +00:00
|
|
|
lxw_app *app;
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t named_range_count = 0;
|
2015-04-18 01:25:38 +01:00
|
|
|
char *autofilter;
|
|
|
|
char *has_range;
|
2016-05-05 23:40:06 +01:00
|
|
|
char number[LXW_ATTR_32] = { 0 };
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err = LXW_NO_ERROR;
|
2017-01-07 00:54:13 +00:00
|
|
|
|
|
|
|
app = lxw_app_new();
|
|
|
|
if (!app) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
app->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2017-01-07 00:54:13 +00:00
|
|
|
if (!app->file) {
|
|
|
|
err = LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2018-09-08 23:36:09 +01:00
|
|
|
if (self->workbook->num_worksheets) {
|
|
|
|
lxw_snprintf(number, LXW_ATTR_32, "%d",
|
|
|
|
self->workbook->num_worksheets);
|
|
|
|
lxw_app_add_heading_pair(app, "Worksheets", number);
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2018-09-08 23:36:09 +01:00
|
|
|
if (self->workbook->num_chartsheets) {
|
|
|
|
lxw_snprintf(number, LXW_ATTR_32, "%d",
|
|
|
|
self->workbook->num_chartsheets);
|
|
|
|
lxw_app_add_heading_pair(app, "Charts", number);
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2018-09-12 22:35:27 +01:00
|
|
|
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
|
|
|
|
if (!sheet->is_chartsheet) {
|
|
|
|
worksheet = sheet->u.worksheet;
|
|
|
|
lxw_app_add_part_name(app, worksheet->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-07 11:51:03 +01:00
|
|
|
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
|
2018-09-08 23:36:09 +01:00
|
|
|
if (sheet->is_chartsheet) {
|
|
|
|
chartsheet = sheet->u.chartsheet;
|
|
|
|
lxw_app_add_part_name(app, chartsheet->name);
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
2015-04-06 23:46:39 +01:00
|
|
|
/* Add the Named Ranges parts. */
|
|
|
|
TAILQ_FOREACH(defined_name, workbook->defined_names, list_pointers) {
|
2015-04-17 23:54:05 +01:00
|
|
|
|
2015-04-18 14:06:14 +01:00
|
|
|
has_range = strchr(defined_name->formula, '!');
|
2015-04-18 01:25:38 +01:00
|
|
|
autofilter = strstr(defined_name->app_name, "_FilterDatabase");
|
2015-04-06 23:46:39 +01:00
|
|
|
|
2015-04-18 01:25:38 +01:00
|
|
|
/* Only store defined names with ranges (except for autofilters). */
|
|
|
|
if (has_range && !autofilter) {
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_app_add_part_name(app, defined_name->app_name);
|
2015-04-06 23:46:39 +01:00
|
|
|
named_range_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add the Named Range heading pairs. */
|
|
|
|
if (named_range_count) {
|
2016-05-05 23:40:06 +01:00
|
|
|
lxw_snprintf(number, LXW_ATTR_32, "%d", named_range_count);
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_app_add_heading_pair(app, "Named Ranges", number);
|
2015-04-06 23:46:39 +01:00
|
|
|
}
|
|
|
|
|
2015-12-13 22:49:13 +00:00
|
|
|
/* Set the app/doc properties. */
|
|
|
|
app->properties = workbook->properties;
|
|
|
|
|
2021-03-27 20:36:13 +00:00
|
|
|
app->doc_security = workbook->read_only;
|
|
|
|
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_app_assemble_xml_file(app);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, app->file, &buffer, &buffer_size,
|
|
|
|
"docProps/app.xml");
|
2014-06-08 17:40:59 +01:00
|
|
|
|
|
|
|
fclose(app->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
mem_error:
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_app_free(app);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
return err;
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the core.xml file.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2014-06-08 17:40:59 +01:00
|
|
|
_write_core_file(lxw_packager *self)
|
|
|
|
{
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err = LXW_NO_ERROR;
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_core *core = lxw_core_new();
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2017-01-07 00:54:13 +00:00
|
|
|
|
|
|
|
if (!core) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
core->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2017-01-07 00:54:13 +00:00
|
|
|
if (!core->file) {
|
|
|
|
err = LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
|
|
|
core->properties = self->workbook->properties;
|
|
|
|
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_core_assemble_xml_file(core);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, core->file, &buffer, &buffer_size,
|
|
|
|
"docProps/core.xml");
|
2014-06-08 17:40:59 +01:00
|
|
|
|
|
|
|
fclose(core->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
mem_error:
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_core_free(core);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
return err;
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
2021-04-23 07:25:50 +01:00
|
|
|
/*
|
|
|
|
* Write the metadata.xml file.
|
|
|
|
*/
|
|
|
|
STATIC lxw_error
|
|
|
|
_write_metadata_file(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_error err = LXW_NO_ERROR;
|
|
|
|
lxw_metadata *metadata;
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2021-04-23 07:25:50 +01:00
|
|
|
|
|
|
|
if (!self->workbook->has_metadata)
|
|
|
|
return LXW_NO_ERROR;
|
|
|
|
|
|
|
|
metadata = lxw_metadata_new();
|
|
|
|
|
|
|
|
if (!metadata) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
metadata->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2021-04-23 07:25:50 +01:00
|
|
|
if (!metadata->file) {
|
|
|
|
err = LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
|
|
|
|
2024-07-30 00:33:14 +01:00
|
|
|
metadata->has_embedded_images = self->workbook->has_embedded_images;
|
|
|
|
metadata->num_embedded_images = self->workbook->num_embedded_images;
|
|
|
|
metadata->has_dynamic_functions = self->workbook->has_dynamic_functions;
|
|
|
|
|
2021-04-23 07:25:50 +01:00
|
|
|
lxw_metadata_assemble_xml_file(metadata);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, metadata->file, &buffer, &buffer_size,
|
|
|
|
"xl/metadata.xml");
|
2021-04-23 07:25:50 +01:00
|
|
|
|
|
|
|
fclose(metadata->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2021-04-23 07:25:50 +01:00
|
|
|
|
|
|
|
mem_error:
|
|
|
|
lxw_metadata_free(metadata);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2024-07-30 00:33:14 +01:00
|
|
|
/*
|
|
|
|
* Write the rdrichvalue.xml file.
|
|
|
|
*/
|
|
|
|
STATIC lxw_error
|
|
|
|
_write_rich_value_file(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_error err = LXW_NO_ERROR;
|
|
|
|
lxw_rich_value *rich_value;
|
|
|
|
char *buffer = NULL;
|
|
|
|
size_t buffer_size = 0;
|
|
|
|
|
|
|
|
if (!self->workbook->has_embedded_images)
|
|
|
|
return LXW_NO_ERROR;
|
|
|
|
|
|
|
|
rich_value = lxw_rich_value_new();
|
|
|
|
if (!rich_value) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
|
|
|
|
2024-07-30 23:30:33 +01:00
|
|
|
rich_value->workbook = self->workbook;
|
|
|
|
|
2024-07-30 00:33:14 +01:00
|
|
|
rich_value->file =
|
|
|
|
lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
|
|
|
if (!rich_value->file) {
|
|
|
|
err = LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
lxw_rich_value_assemble_xml_file(rich_value);
|
|
|
|
|
|
|
|
err = _add_to_zip(self, rich_value->file, &buffer, &buffer_size,
|
|
|
|
"xl/richData/rdrichvalue.xml");
|
|
|
|
|
|
|
|
fclose(rich_value->file);
|
|
|
|
free(buffer);
|
|
|
|
|
|
|
|
mem_error:
|
|
|
|
lxw_rich_value_free(rich_value);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the richValueRel.xml file.
|
|
|
|
*/
|
|
|
|
STATIC lxw_error
|
|
|
|
_write_rich_value_rel_file(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_error err = LXW_NO_ERROR;
|
|
|
|
lxw_rich_value_rel *rich_value_rel;
|
|
|
|
char *buffer = NULL;
|
|
|
|
size_t buffer_size = 0;
|
|
|
|
|
|
|
|
if (!self->workbook->has_embedded_images)
|
|
|
|
return LXW_NO_ERROR;
|
|
|
|
|
|
|
|
rich_value_rel = lxw_rich_value_rel_new();
|
|
|
|
if (!rich_value_rel) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
|
|
|
|
2024-07-30 23:30:33 +01:00
|
|
|
rich_value_rel->num_embedded_images = self->workbook->num_embedded_images;
|
|
|
|
|
2024-07-30 00:33:14 +01:00
|
|
|
rich_value_rel->file =
|
|
|
|
lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
|
|
|
if (!rich_value_rel->file) {
|
|
|
|
err = LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
lxw_rich_value_rel_assemble_xml_file(rich_value_rel);
|
|
|
|
|
|
|
|
err = _add_to_zip(self, rich_value_rel->file, &buffer, &buffer_size,
|
|
|
|
"xl/richData/richValueRel.xml");
|
|
|
|
|
|
|
|
fclose(rich_value_rel->file);
|
|
|
|
free(buffer);
|
|
|
|
|
|
|
|
mem_error:
|
|
|
|
lxw_rich_value_rel_free(rich_value_rel);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the rdRichValueTypes.xml file.
|
|
|
|
*/
|
|
|
|
STATIC lxw_error
|
|
|
|
_write_rich_value_types_file(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_error err = LXW_NO_ERROR;
|
|
|
|
lxw_rich_value_types *rich_value_types;
|
|
|
|
char *buffer = NULL;
|
|
|
|
size_t buffer_size = 0;
|
|
|
|
|
|
|
|
if (!self->workbook->has_embedded_images)
|
|
|
|
return LXW_NO_ERROR;
|
|
|
|
|
|
|
|
rich_value_types = lxw_rich_value_types_new();
|
|
|
|
if (!rich_value_types) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
rich_value_types->file =
|
|
|
|
lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
|
|
|
if (!rich_value_types->file) {
|
|
|
|
err = LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
lxw_rich_value_types_assemble_xml_file(rich_value_types);
|
|
|
|
|
|
|
|
err = _add_to_zip(self, rich_value_types->file, &buffer, &buffer_size,
|
|
|
|
"xl/richData/rdRichValueTypes.xml");
|
|
|
|
|
|
|
|
fclose(rich_value_types->file);
|
|
|
|
free(buffer);
|
|
|
|
|
|
|
|
mem_error:
|
|
|
|
lxw_rich_value_types_free(rich_value_types);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the rdrichvaluestructure.xml file.
|
|
|
|
*/
|
|
|
|
STATIC lxw_error
|
|
|
|
_write_rich_value_structure_file(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_error err = LXW_NO_ERROR;
|
|
|
|
lxw_rich_value_structure *rich_value_structure;
|
|
|
|
char *buffer = NULL;
|
|
|
|
size_t buffer_size = 0;
|
|
|
|
|
|
|
|
if (!self->workbook->has_embedded_images)
|
|
|
|
return LXW_NO_ERROR;
|
|
|
|
|
|
|
|
rich_value_structure = lxw_rich_value_structure_new();
|
|
|
|
if (!rich_value_structure) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
|
|
|
|
2024-07-30 23:30:33 +01:00
|
|
|
rich_value_structure->has_embedded_image_descriptions =
|
|
|
|
self->workbook->has_embedded_image_descriptions;
|
|
|
|
|
2024-07-30 00:33:14 +01:00
|
|
|
rich_value_structure->file =
|
|
|
|
lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
|
|
|
if (!rich_value_structure->file) {
|
|
|
|
err = LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
lxw_rich_value_structure_assemble_xml_file(rich_value_structure);
|
|
|
|
|
|
|
|
err = _add_to_zip(self, rich_value_structure->file, &buffer, &buffer_size,
|
|
|
|
"xl/richData/rdrichvaluestructure.xml");
|
|
|
|
|
|
|
|
fclose(rich_value_structure->file);
|
|
|
|
free(buffer);
|
|
|
|
|
|
|
|
mem_error:
|
|
|
|
lxw_rich_value_structure_free(rich_value_structure);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-06-10 00:03:38 +01:00
|
|
|
/*
|
|
|
|
* Write the custom.xml file.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2016-06-10 00:03:38 +01:00
|
|
|
_write_custom_file(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_custom *custom;
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err = LXW_NO_ERROR;
|
2016-06-10 00:03:38 +01:00
|
|
|
|
|
|
|
if (STAILQ_EMPTY(self->workbook->custom_properties))
|
2017-01-09 20:26:32 +00:00
|
|
|
return LXW_NO_ERROR;
|
2016-06-10 00:03:38 +01:00
|
|
|
|
|
|
|
custom = lxw_custom_new();
|
2017-01-07 00:54:13 +00:00
|
|
|
if (!custom) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2016-06-10 00:03:38 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
custom->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2017-01-07 00:54:13 +00:00
|
|
|
if (!custom->file) {
|
|
|
|
err = LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2016-06-10 00:03:38 +01:00
|
|
|
|
|
|
|
custom->custom_properties = self->workbook->custom_properties;
|
|
|
|
|
|
|
|
lxw_custom_assemble_xml_file(custom);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, custom->file, &buffer, &buffer_size,
|
|
|
|
"docProps/custom.xml");
|
2016-06-10 00:03:38 +01:00
|
|
|
|
|
|
|
fclose(custom->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2016-06-10 00:03:38 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
mem_error:
|
2016-06-10 00:03:38 +01:00
|
|
|
lxw_custom_free(custom);
|
2017-01-07 00:54:13 +00:00
|
|
|
return err;
|
2016-06-10 00:03:38 +01:00
|
|
|
}
|
|
|
|
|
2014-06-08 17:40:59 +01:00
|
|
|
/*
|
|
|
|
* Write the theme.xml file.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2014-06-08 17:40:59 +01:00
|
|
|
_write_theme_file(lxw_packager *self)
|
|
|
|
{
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err = LXW_NO_ERROR;
|
2016-01-04 20:26:23 +00:00
|
|
|
lxw_theme *theme = lxw_theme_new();
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2017-01-07 00:54:13 +00:00
|
|
|
|
|
|
|
if (!theme) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
theme->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2017-01-07 00:54:13 +00:00
|
|
|
if (!theme->file) {
|
|
|
|
err = LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2016-01-04 20:26:23 +00:00
|
|
|
lxw_theme_assemble_xml_file(theme);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, theme->file, &buffer, &buffer_size,
|
|
|
|
"xl/theme/theme1.xml");
|
2014-06-08 17:40:59 +01:00
|
|
|
|
|
|
|
fclose(theme->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
mem_error:
|
2016-01-04 20:26:23 +00:00
|
|
|
lxw_theme_free(theme);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
return err;
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the styles.xml file.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2014-06-08 17:40:59 +01:00
|
|
|
_write_styles_file(lxw_packager *self)
|
|
|
|
{
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_styles *styles = lxw_styles_new();
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2014-06-08 17:40:59 +01:00
|
|
|
lxw_hash_element *hash_element;
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err = LXW_NO_ERROR;
|
2017-01-07 00:54:13 +00:00
|
|
|
|
|
|
|
if (!styles) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2015-03-01 12:36:59 +00:00
|
|
|
/* Copy the unique and in-use formats from the workbook to the styles
|
|
|
|
* xf_format list. */
|
|
|
|
LXW_FOREACH_ORDERED(hash_element, self->workbook->used_xf_formats) {
|
2014-06-08 17:40:59 +01:00
|
|
|
lxw_format *workbook_format = (lxw_format *) hash_element->value;
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_format *style_format = lxw_format_new();
|
2017-01-07 00:54:13 +00:00
|
|
|
|
|
|
|
if (!style_format) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
|
|
|
|
2014-06-08 17:40:59 +01:00
|
|
|
memcpy(style_format, workbook_format, sizeof(lxw_format));
|
|
|
|
STAILQ_INSERT_TAIL(styles->xf_formats, style_format, list_pointers);
|
|
|
|
}
|
|
|
|
|
2020-08-21 00:59:57 +01:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2014-06-08 17:40:59 +01:00
|
|
|
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;
|
2015-03-01 12:36:59 +00:00
|
|
|
styles->xf_count = self->workbook->used_xf_formats->unique_count;
|
2020-08-21 00:59:57 +01:00
|
|
|
styles->dxf_count = self->workbook->used_dxf_formats->unique_count;
|
2020-01-08 23:16:56 +00:00
|
|
|
styles->has_comments = self->workbook->has_comments;
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
styles->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2017-01-07 00:54:13 +00:00
|
|
|
if (!styles->file) {
|
|
|
|
err = LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_styles_assemble_xml_file(styles);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, styles->file, &buffer, &buffer_size,
|
|
|
|
"xl/styles.xml");
|
2014-06-08 17:40:59 +01:00
|
|
|
|
|
|
|
fclose(styles->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
mem_error:
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_styles_free(styles);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
return err;
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the ContentTypes.xml file.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2014-06-08 17:40:59 +01:00
|
|
|
_write_content_types_file(lxw_packager *self)
|
|
|
|
{
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_content_types *content_types = lxw_content_types_new();
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2014-06-08 17:40:59 +01:00
|
|
|
lxw_workbook *workbook = self->workbook;
|
2018-09-07 11:51:03 +01:00
|
|
|
lxw_sheet *sheet;
|
2016-05-05 23:40:06 +01:00
|
|
|
char filename[LXW_MAX_ATTRIBUTE_LENGTH] = { 0 };
|
2019-06-11 20:50:21 +01:00
|
|
|
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);
|
2021-08-16 21:29:27 +01:00
|
|
|
uint32_t table_count = _get_table_count(self);
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err = LXW_NO_ERROR;
|
2017-01-07 00:54:13 +00:00
|
|
|
|
|
|
|
if (!content_types) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
content_types->file = lxw_get_filehandle(&buffer, &buffer_size,
|
|
|
|
self->tmpdir);
|
2017-01-07 00:54:13 +00:00
|
|
|
if (!content_types->file) {
|
|
|
|
err = LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2015-12-30 00:35:03 +00:00
|
|
|
if (workbook->has_png)
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_ct_add_default(content_types, "png", "image/png");
|
2015-12-30 00:35:03 +00:00
|
|
|
|
2015-12-31 08:28:23 +00:00
|
|
|
if (workbook->has_jpeg)
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_ct_add_default(content_types, "jpeg", "image/jpeg");
|
2015-12-31 08:28:23 +00:00
|
|
|
|
|
|
|
if (workbook->has_bmp)
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_ct_add_default(content_types, "bmp", "image/bmp");
|
2015-12-31 08:28:23 +00:00
|
|
|
|
2021-05-08 00:25:14 +01:00
|
|
|
if (workbook->has_gif)
|
|
|
|
lxw_ct_add_default(content_types, "gif", "image/gif");
|
|
|
|
|
2019-06-16 15:46:09 +01:00
|
|
|
if (workbook->vba_project)
|
|
|
|
lxw_ct_add_default(content_types, "bin",
|
|
|
|
"application/vnd.ms-office.vbaProject");
|
|
|
|
|
|
|
|
if (workbook->vba_project)
|
|
|
|
lxw_ct_add_override(content_types, "/xl/workbook.xml",
|
|
|
|
LXW_APP_MSEXCEL "sheet.macroEnabled.main+xml");
|
|
|
|
else
|
|
|
|
lxw_ct_add_override(content_types, "/xl/workbook.xml",
|
|
|
|
LXW_APP_DOCUMENT "spreadsheetml.sheet.main+xml");
|
|
|
|
|
2023-09-20 20:37:38 +02:00
|
|
|
if (workbook->vba_project_signature)
|
|
|
|
lxw_ct_add_override(content_types, "/xl/vbaProjectSignature.bin",
|
|
|
|
"application/vnd.ms-office.vbaProjectSignature");
|
|
|
|
|
2018-09-07 11:51:03 +01:00
|
|
|
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
|
2018-09-08 23:36:09 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
|
|
|
|
"/xl/worksheets/sheet%d.xml", worksheet_index++);
|
|
|
|
lxw_ct_add_worksheet_name(content_types, filename);
|
|
|
|
}
|
2015-12-30 00:35:03 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 18:10:20 +00:00
|
|
|
for (index = 1; index <= chart_count; index++) {
|
2016-05-05 23:40:06 +01:00
|
|
|
lxw_snprintf(filename, LXW_FILENAME_LENGTH, "/xl/charts/chart%d.xml",
|
|
|
|
index);
|
2016-05-02 21:43:37 +01:00
|
|
|
lxw_ct_add_chart_name(content_types, filename);
|
|
|
|
}
|
|
|
|
|
2019-03-29 18:10:20 +00:00
|
|
|
for (index = 1; index <= drawing_count; index++) {
|
2016-05-05 23:40:06 +01:00
|
|
|
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
|
2015-12-31 08:28:23 +00:00
|
|
|
"/xl/drawings/drawing%d.xml", index);
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_ct_add_drawing_name(content_types, filename);
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
2021-08-16 21:29:27 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-12-27 20:10:11 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-06-08 17:40:59 +01:00
|
|
|
if (workbook->sst->string_count)
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_ct_add_shared_strings(content_types);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2016-06-10 00:03:38 +01:00
|
|
|
if (!STAILQ_EMPTY(self->workbook->custom_properties))
|
|
|
|
lxw_ct_add_custom_properties(content_types);
|
|
|
|
|
2021-04-23 07:25:50 +01:00
|
|
|
if (workbook->has_metadata)
|
|
|
|
lxw_ct_add_metadata(content_types);
|
|
|
|
|
2024-07-30 00:33:14 +01:00
|
|
|
if (workbook->has_embedded_images)
|
|
|
|
lxw_ct_add_rich_value(content_types);
|
|
|
|
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_content_types_assemble_xml_file(content_types);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, content_types->file, &buffer, &buffer_size,
|
|
|
|
"[Content_Types].xml");
|
2014-06-08 17:40:59 +01:00
|
|
|
|
|
|
|
fclose(content_types->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
mem_error:
|
2016-01-04 23:26:52 +00:00
|
|
|
lxw_content_types_free(content_types);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
return err;
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the workbook .rels xml file.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2014-06-08 17:40:59 +01:00
|
|
|
_write_workbook_rels_file(lxw_packager *self)
|
|
|
|
{
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_relationships *rels = lxw_relationships_new();
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2014-06-08 17:40:59 +01:00
|
|
|
lxw_workbook *workbook = self->workbook;
|
2018-09-07 11:51:03 +01:00
|
|
|
lxw_sheet *sheet;
|
2016-05-05 23:40:06 +01:00
|
|
|
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t worksheet_index = 1;
|
|
|
|
uint32_t chartsheet_index = 1;
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err = LXW_NO_ERROR;
|
2017-01-07 00:54:13 +00:00
|
|
|
|
|
|
|
if (!rels) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2017-01-07 00:54:13 +00:00
|
|
|
if (!rels->file) {
|
|
|
|
err = LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2018-09-07 11:51:03 +01:00
|
|
|
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
|
2018-09-08 23:36:09 +01:00
|
|
|
if (sheet->is_chartsheet) {
|
|
|
|
lxw_snprintf(sheetname,
|
|
|
|
LXW_FILENAME_LENGTH,
|
|
|
|
"chartsheets/sheet%d.xml", chartsheet_index++);
|
|
|
|
lxw_add_document_relationship(rels, "/chartsheet", sheetname);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
lxw_snprintf(sheetname,
|
|
|
|
LXW_FILENAME_LENGTH,
|
|
|
|
"worksheets/sheet%d.xml", worksheet_index++);
|
|
|
|
lxw_add_document_relationship(rels, "/worksheet", sheetname);
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_add_document_relationship(rels, "/theme", "theme/theme1.xml");
|
|
|
|
lxw_add_document_relationship(rels, "/styles", "styles.xml");
|
2014-06-08 17:40:59 +01:00
|
|
|
|
|
|
|
if (workbook->sst->string_count)
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_add_document_relationship(rels, "/sharedStrings",
|
|
|
|
"sharedStrings.xml");
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2019-06-16 15:46:09 +01:00
|
|
|
if (workbook->vba_project)
|
|
|
|
lxw_add_ms_package_relationship(rels, "/vbaProject",
|
|
|
|
"vbaProject.bin");
|
|
|
|
|
2021-04-23 07:25:50 +01:00
|
|
|
if (workbook->has_metadata)
|
|
|
|
lxw_add_document_relationship(rels, "/sheetMetadata", "metadata.xml");
|
|
|
|
|
2024-07-30 00:33:14 +01:00
|
|
|
if (workbook->has_embedded_images)
|
|
|
|
lxw_add_rich_value_relationship(rels);
|
|
|
|
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_relationships_assemble_xml_file(rels);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, rels->file, &buffer, &buffer_size,
|
|
|
|
"xl/_rels/workbook.xml.rels");
|
2014-06-08 17:40:59 +01:00
|
|
|
|
|
|
|
fclose(rels->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2017-01-07 00:54:13 +00:00
|
|
|
|
|
|
|
mem_error:
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_free_relationships(rels);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
return err;
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
2015-04-26 23:55:17 +01:00
|
|
|
/*
|
|
|
|
* Write the worksheet .rels files for worksheets that contain links to
|
|
|
|
* external data such as hyperlinks or drawings.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2015-04-26 23:55:17 +01:00
|
|
|
_write_worksheet_rels_file(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_relationships *rels;
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2015-12-30 00:35:03 +00:00
|
|
|
lxw_rel_tuple *rel;
|
2015-04-26 23:55:17 +01:00
|
|
|
lxw_workbook *workbook = self->workbook;
|
2018-09-07 11:51:03 +01:00
|
|
|
lxw_sheet *sheet;
|
2015-04-26 23:55:17 +01:00
|
|
|
lxw_worksheet *worksheet;
|
2016-05-05 23:40:06 +01:00
|
|
|
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t index = 0;
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err;
|
2015-04-26 23:55:17 +01:00
|
|
|
|
2018-09-07 11:51:03 +01:00
|
|
|
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
|
|
|
|
if (sheet->is_chartsheet)
|
|
|
|
continue;
|
|
|
|
else
|
|
|
|
worksheet = sheet->u.worksheet;
|
2015-04-26 23:55:17 +01:00
|
|
|
|
2016-05-04 20:23:41 +01:00
|
|
|
index++;
|
|
|
|
|
2015-12-30 00:35:03 +00:00
|
|
|
if (STAILQ_EMPTY(worksheet->external_hyperlinks) &&
|
2019-12-27 20:10:11 +00:00
|
|
|
STAILQ_EMPTY(worksheet->external_drawing_links) &&
|
2021-08-16 21:29:27 +01:00
|
|
|
STAILQ_EMPTY(worksheet->external_table_links) &&
|
2020-08-16 16:03:56 +01:00
|
|
|
!worksheet->external_vml_header_link &&
|
2019-12-27 20:10:11 +00:00
|
|
|
!worksheet->external_vml_comment_link &&
|
2021-05-13 22:59:01 +01:00
|
|
|
!worksheet->external_background_link &&
|
2019-12-27 20:10:11 +00:00
|
|
|
!worksheet->external_comment_link)
|
2015-04-26 23:55:17 +01:00
|
|
|
continue;
|
|
|
|
|
2016-01-04 20:49:54 +00:00
|
|
|
rels = lxw_relationships_new();
|
2017-01-07 00:54:13 +00:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2017-01-07 00:54:13 +00:00
|
|
|
if (!rels->file) {
|
|
|
|
lxw_free_relationships(rels);
|
2016-05-30 21:16:25 +01:00
|
|
|
return LXW_ERROR_CREATING_TMPFILE;
|
2017-01-07 00:54:13 +00:00
|
|
|
}
|
2015-04-26 23:55:17 +01:00
|
|
|
|
2015-12-30 00:35:03 +00:00
|
|
|
STAILQ_FOREACH(rel, worksheet->external_hyperlinks, list_pointers) {
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_add_worksheet_relationship(rels, rel->type, rel->target,
|
|
|
|
rel->target_mode);
|
2015-12-30 00:35:03 +00:00
|
|
|
}
|
2015-04-26 23:55:17 +01:00
|
|
|
|
2015-12-30 00:35:03 +00:00
|
|
|
STAILQ_FOREACH(rel, worksheet->external_drawing_links, list_pointers) {
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_add_worksheet_relationship(rels, rel->type, rel->target,
|
|
|
|
rel->target_mode);
|
2015-04-26 23:55:17 +01:00
|
|
|
}
|
|
|
|
|
2019-12-27 20:10:11 +00:00
|
|
|
rel = worksheet->external_vml_comment_link;
|
|
|
|
if (rel)
|
|
|
|
lxw_add_worksheet_relationship(rels, rel->type, rel->target,
|
|
|
|
rel->target_mode);
|
|
|
|
|
2020-08-16 16:03:56 +01:00
|
|
|
rel = worksheet->external_vml_header_link;
|
|
|
|
if (rel)
|
|
|
|
lxw_add_worksheet_relationship(rels, rel->type, rel->target,
|
|
|
|
rel->target_mode);
|
|
|
|
|
2023-01-31 23:32:05 +00:00
|
|
|
rel = worksheet->external_background_link;
|
|
|
|
if (rel)
|
2021-08-16 21:29:27 +01:00
|
|
|
lxw_add_worksheet_relationship(rels, rel->type, rel->target,
|
|
|
|
rel->target_mode);
|
|
|
|
|
2023-01-31 23:32:05 +00:00
|
|
|
STAILQ_FOREACH(rel, worksheet->external_table_links, list_pointers) {
|
2021-05-13 22:59:01 +01:00
|
|
|
lxw_add_worksheet_relationship(rels, rel->type, rel->target,
|
|
|
|
rel->target_mode);
|
2023-01-31 23:32:05 +00:00
|
|
|
}
|
2021-05-13 22:59:01 +01:00
|
|
|
|
2019-12-27 20:10:11 +00:00
|
|
|
rel = worksheet->external_comment_link;
|
|
|
|
if (rel)
|
|
|
|
lxw_add_worksheet_relationship(rels, rel->type, rel->target,
|
|
|
|
rel->target_mode);
|
|
|
|
|
2016-05-05 23:40:06 +01:00
|
|
|
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
|
2016-05-04 20:23:41 +01:00
|
|
|
"xl/worksheets/_rels/sheet%d.xml.rels", index);
|
2015-04-26 23:55:17 +01:00
|
|
|
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_relationships_assemble_xml_file(rels);
|
2015-04-26 23:55:17 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname);
|
2015-04-26 23:55:17 +01:00
|
|
|
|
|
|
|
fclose(rels->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_free_relationships(rels);
|
2017-01-07 00:54:13 +00:00
|
|
|
|
|
|
|
RETURN_ON_ERROR(err);
|
2015-04-26 23:55:17 +01:00
|
|
|
}
|
|
|
|
|
2017-01-09 20:26:32 +00:00
|
|
|
return LXW_NO_ERROR;
|
2015-04-26 23:55:17 +01:00
|
|
|
}
|
|
|
|
|
2018-09-08 23:36:09 +01:00
|
|
|
/*
|
|
|
|
* Write the chartsheet .rels files for chartsheets that contain links to
|
|
|
|
* external data such as drawings.
|
|
|
|
*/
|
|
|
|
STATIC lxw_error
|
|
|
|
_write_chartsheet_rels_file(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_relationships *rels;
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2018-09-08 23:36:09 +01:00
|
|
|
lxw_rel_tuple *rel;
|
|
|
|
lxw_workbook *workbook = self->workbook;
|
|
|
|
lxw_sheet *sheet;
|
|
|
|
lxw_worksheet *worksheet;
|
|
|
|
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t index = 0;
|
2018-09-08 23:36:09 +01:00
|
|
|
lxw_error err;
|
|
|
|
|
|
|
|
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
|
|
|
|
if (sheet->is_chartsheet)
|
|
|
|
worksheet = sheet->u.chartsheet->worksheet;
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
|
|
|
|
index++;
|
|
|
|
|
|
|
|
if (STAILQ_EMPTY(worksheet->external_drawing_links))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
rels = lxw_relationships_new();
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2018-09-08 23:36:09 +01:00
|
|
|
if (!rels->file) {
|
|
|
|
lxw_free_relationships(rels);
|
|
|
|
return LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
}
|
|
|
|
|
|
|
|
STAILQ_FOREACH(rel, worksheet->external_hyperlinks, list_pointers) {
|
|
|
|
lxw_add_worksheet_relationship(rels, rel->type, rel->target,
|
|
|
|
rel->target_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
STAILQ_FOREACH(rel, worksheet->external_drawing_links, list_pointers) {
|
|
|
|
lxw_add_worksheet_relationship(rels, rel->type, rel->target,
|
|
|
|
rel->target_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
|
|
|
|
"xl/chartsheets/_rels/sheet%d.xml.rels", index);
|
|
|
|
|
|
|
|
lxw_relationships_assemble_xml_file(rels);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname);
|
2018-09-08 23:36:09 +01:00
|
|
|
|
|
|
|
fclose(rels->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2018-09-08 23:36:09 +01:00
|
|
|
lxw_free_relationships(rels);
|
|
|
|
|
|
|
|
RETURN_ON_ERROR(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
return LXW_NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2015-12-30 00:35:03 +00:00
|
|
|
/*
|
|
|
|
* Write the drawing .rels files for worksheets that contain charts or
|
|
|
|
* drawings.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2015-12-30 00:35:03 +00:00
|
|
|
_write_drawing_rels_file(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_relationships *rels;
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2015-12-30 00:35:03 +00:00
|
|
|
lxw_rel_tuple *rel;
|
|
|
|
lxw_workbook *workbook = self->workbook;
|
2018-09-07 11:51:03 +01:00
|
|
|
lxw_sheet *sheet;
|
2015-12-30 00:35:03 +00:00
|
|
|
lxw_worksheet *worksheet;
|
2016-05-05 23:40:06 +01:00
|
|
|
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
2019-06-11 20:50:21 +01:00
|
|
|
uint32_t index = 1;
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err;
|
2015-12-30 00:35:03 +00:00
|
|
|
|
2018-09-07 11:51:03 +01:00
|
|
|
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
|
|
|
|
if (sheet->is_chartsheet)
|
2018-09-08 23:36:09 +01:00
|
|
|
worksheet = sheet->u.chartsheet->worksheet;
|
2018-09-07 11:51:03 +01:00
|
|
|
else
|
|
|
|
worksheet = sheet->u.worksheet;
|
2015-12-30 00:35:03 +00:00
|
|
|
|
|
|
|
if (STAILQ_EMPTY(worksheet->drawing_links))
|
|
|
|
continue;
|
|
|
|
|
2016-01-04 20:49:54 +00:00
|
|
|
rels = lxw_relationships_new();
|
2017-01-07 00:54:13 +00:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2017-01-07 00:54:13 +00:00
|
|
|
if (!rels->file) {
|
|
|
|
lxw_free_relationships(rels);
|
2016-05-30 21:16:25 +01:00
|
|
|
return LXW_ERROR_CREATING_TMPFILE;
|
2017-01-07 00:54:13 +00:00
|
|
|
}
|
2015-12-30 00:35:03 +00:00
|
|
|
|
|
|
|
STAILQ_FOREACH(rel, worksheet->drawing_links, list_pointers) {
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_add_worksheet_relationship(rels, rel->type, rel->target,
|
|
|
|
rel->target_mode);
|
2015-12-30 00:35:03 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-05-05 23:40:06 +01:00
|
|
|
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
|
2015-12-30 00:35:03 +00:00
|
|
|
"xl/drawings/_rels/drawing%d.xml.rels", index++);
|
|
|
|
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_relationships_assemble_xml_file(rels);
|
2015-12-30 00:35:03 +00:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname);
|
2015-12-30 00:35:03 +00:00
|
|
|
|
|
|
|
fclose(rels->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_free_relationships(rels);
|
2017-01-07 00:54:13 +00:00
|
|
|
|
|
|
|
RETURN_ON_ERROR(err);
|
2015-12-30 00:35:03 +00:00
|
|
|
}
|
|
|
|
|
2017-01-09 20:26:32 +00:00
|
|
|
return LXW_NO_ERROR;
|
2015-12-30 00:35:03 +00:00
|
|
|
}
|
|
|
|
|
2020-08-16 16:03:56 +01:00
|
|
|
/*
|
|
|
|
* 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;
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2020-08-16 16:03:56 +01:00
|
|
|
lxw_rel_tuple *rel;
|
|
|
|
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
|
|
|
lxw_error err = LXW_NO_ERROR;
|
|
|
|
|
|
|
|
rels = lxw_relationships_new();
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2020-08-16 16:03:56 +01:00
|
|
|
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);
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname);
|
2020-08-16 16:03:56 +01:00
|
|
|
|
|
|
|
fclose(rels->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2020-08-16 16:03:56 +01:00
|
|
|
lxw_free_relationships(rels);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2023-09-20 20:37:38 +02:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2024-07-30 00:33:14 +01:00
|
|
|
/*
|
|
|
|
* Write the richValueRel.xml.rels files for embedded images.
|
|
|
|
*/
|
|
|
|
STATIC lxw_error
|
|
|
|
_write_rich_value_rels_file(lxw_packager *self)
|
|
|
|
{
|
|
|
|
lxw_workbook *workbook = self->workbook;
|
|
|
|
lxw_sheet *sheet;
|
|
|
|
lxw_worksheet *worksheet;
|
|
|
|
lxw_object_properties *object_props;
|
|
|
|
|
|
|
|
lxw_relationships *rels;
|
|
|
|
char *buffer = NULL;
|
|
|
|
size_t buffer_size = 0;
|
|
|
|
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
|
|
|
char target[LXW_FILENAME_LENGTH] = { 0 };
|
|
|
|
lxw_error err = LXW_NO_ERROR;
|
|
|
|
uint32_t index = 1;
|
|
|
|
|
|
|
|
if (!workbook->has_embedded_images)
|
|
|
|
return 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(sheet, workbook->sheets, list_pointers) {
|
|
|
|
if (sheet->is_chartsheet)
|
|
|
|
continue;
|
|
|
|
else
|
|
|
|
worksheet = sheet->u.worksheet;
|
|
|
|
|
|
|
|
if (STAILQ_EMPTY(worksheet->embedded_image_props))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
STAILQ_FOREACH(object_props, worksheet->embedded_image_props,
|
|
|
|
list_pointers) {
|
|
|
|
|
|
|
|
if (object_props->is_duplicate)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
lxw_snprintf(target, LXW_FILENAME_LENGTH,
|
|
|
|
"../media/image%d.%s", index++,
|
|
|
|
object_props->extension);
|
|
|
|
|
|
|
|
lxw_add_document_relationship(rels, "/image", target);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
|
|
|
|
"xl/richData/_rels/richValueRel.xml.rels");
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-06-08 17:40:59 +01:00
|
|
|
/*
|
|
|
|
* Write the _rels/.rels xml file.
|
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2014-06-08 17:40:59 +01:00
|
|
|
_write_root_rels_file(lxw_packager *self)
|
|
|
|
{
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_relationships *rels = lxw_relationships_new();
|
2022-11-10 13:15:28 +04:00
|
|
|
char *buffer = NULL;
|
2022-11-12 20:42:46 +04:00
|
|
|
size_t buffer_size = 0;
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error err = LXW_NO_ERROR;
|
2017-01-07 00:54:13 +00:00
|
|
|
|
|
|
|
if (!rels) {
|
|
|
|
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
|
2017-01-07 00:54:13 +00:00
|
|
|
if (!rels->file) {
|
|
|
|
err = LXW_ERROR_CREATING_TMPFILE;
|
|
|
|
goto mem_error;
|
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_add_document_relationship(rels, "/officeDocument", "xl/workbook.xml");
|
2016-06-10 00:03:38 +01:00
|
|
|
|
|
|
|
lxw_add_package_relationship(rels,
|
|
|
|
"/metadata/core-properties",
|
2016-01-04 20:49:54 +00:00
|
|
|
"docProps/core.xml");
|
2016-06-10 00:03:38 +01:00
|
|
|
|
|
|
|
lxw_add_document_relationship(rels,
|
|
|
|
"/extended-properties", "docProps/app.xml");
|
|
|
|
|
|
|
|
if (!STAILQ_EMPTY(self->workbook->custom_properties))
|
|
|
|
lxw_add_document_relationship(rels,
|
|
|
|
"/custom-properties",
|
|
|
|
"docProps/custom.xml");
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_relationships_assemble_xml_file(rels);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
err = _add_to_zip(self, rels->file, &buffer, &buffer_size, "_rels/.rels");
|
2014-06-08 17:40:59 +01:00
|
|
|
|
|
|
|
fclose(rels->file);
|
2022-11-10 13:15:28 +04:00
|
|
|
free(buffer);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
mem_error:
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_free_relationships(rels);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-07 00:54:13 +00:00
|
|
|
return err;
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Public functions.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2017-01-09 20:26:32 +00:00
|
|
|
STATIC lxw_error
|
2025-02-12 19:46:42 +00:00
|
|
|
_add_file_to_zip(lxw_packager *self, FILE *file, const char *filename)
|
2014-06-08 17:40:59 +01:00
|
|
|
{
|
|
|
|
int16_t error = ZIP_OK;
|
|
|
|
size_t size_read;
|
|
|
|
|
|
|
|
error = zipOpenNewFileInZip4_64(self->zipfile,
|
|
|
|
filename,
|
|
|
|
&self->zipfile_info,
|
|
|
|
NULL, 0, NULL, 0, NULL,
|
|
|
|
Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0,
|
|
|
|
-MAX_WBITS, DEF_MEM_LEVEL,
|
2019-06-08 00:24:40 +01:00
|
|
|
Z_DEFAULT_STRATEGY, NULL, 0, 0, 0,
|
|
|
|
self->use_zip64);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
|
|
|
if (error != ZIP_OK) {
|
2015-11-07 23:59:13 +00:00
|
|
|
LXW_ERROR("Error adding member to zipfile");
|
2016-05-30 21:16:25 +01:00
|
|
|
RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fflush(file);
|
|
|
|
rewind(file);
|
|
|
|
|
2023-09-24 20:40:07 +01:00
|
|
|
size_read = fread((void *) self->buffer, 1, self->buffer_size, file);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
|
|
|
while (size_read) {
|
|
|
|
|
|
|
|
if (size_read < self->buffer_size) {
|
2022-11-10 13:15:28 +04:00
|
|
|
if (ferror(file)) {
|
2015-11-07 23:59:13 +00:00
|
|
|
LXW_ERROR("Error reading member file data");
|
2016-05-30 21:16:25 +01:00
|
|
|
RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-27 21:01:50 +01:00
|
|
|
error = zipWriteInFileInZip(self->zipfile,
|
|
|
|
self->buffer, (unsigned int) size_read);
|
|
|
|
|
2014-06-08 17:40:59 +01:00
|
|
|
if (error < 0) {
|
2015-11-07 23:59:13 +00:00
|
|
|
LXW_ERROR("Error in writing member in the zipfile");
|
2016-05-30 21:16:25 +01:00
|
|
|
RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
2023-09-24 20:40:07 +01:00
|
|
|
size_read =
|
|
|
|
fread((void *) (void *) self->buffer, 1, self->buffer_size, file);
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
2019-03-23 14:35:56 +00:00
|
|
|
error = zipCloseFileInZip(self->zipfile);
|
|
|
|
if (error != ZIP_OK) {
|
|
|
|
LXW_ERROR("Error in closing member in the zipfile");
|
2016-05-30 21:16:25 +01:00
|
|
|
RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
2017-01-09 20:26:32 +00:00
|
|
|
return LXW_NO_ERROR;
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|
|
|
|
|
2018-08-29 00:35:54 +01:00
|
|
|
STATIC lxw_error
|
2023-09-24 20:40:07 +01:00
|
|
|
_add_buffer_to_zip(lxw_packager *self, const char *buffer, size_t buffer_size,
|
2022-11-12 20:42:46 +04:00
|
|
|
const char *filename)
|
2018-08-29 00:35:54 +01:00
|
|
|
{
|
|
|
|
int16_t error = ZIP_OK;
|
|
|
|
|
|
|
|
error = zipOpenNewFileInZip4_64(self->zipfile,
|
|
|
|
filename,
|
|
|
|
&self->zipfile_info,
|
|
|
|
NULL, 0, NULL, 0, NULL,
|
|
|
|
Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0,
|
|
|
|
-MAX_WBITS, DEF_MEM_LEVEL,
|
2019-06-08 00:24:40 +01:00
|
|
|
Z_DEFAULT_STRATEGY, NULL, 0, 0, 0,
|
|
|
|
self->use_zip64);
|
2018-08-29 00:35:54 +01:00
|
|
|
|
|
|
|
if (error != ZIP_OK) {
|
|
|
|
LXW_ERROR("Error adding member to zipfile");
|
|
|
|
RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
|
|
|
|
}
|
|
|
|
|
|
|
|
error = zipWriteInFileInZip(self->zipfile,
|
|
|
|
buffer, (unsigned int) buffer_size);
|
|
|
|
|
|
|
|
if (error < 0) {
|
|
|
|
LXW_ERROR("Error in writing member in the zipfile");
|
|
|
|
RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
|
|
|
|
}
|
|
|
|
|
2019-03-23 14:35:56 +00:00
|
|
|
error = zipCloseFileInZip(self->zipfile);
|
|
|
|
if (error != ZIP_OK) {
|
|
|
|
LXW_ERROR("Error in closing member in the zipfile");
|
2018-08-29 00:35:54 +01:00
|
|
|
RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
|
|
|
|
}
|
|
|
|
|
|
|
|
return LXW_NO_ERROR;
|
|
|
|
}
|
|
|
|
|
2022-11-12 20:42:46 +04:00
|
|
|
STATIC lxw_error
|
2025-02-12 19:46:42 +00:00
|
|
|
_add_to_zip(lxw_packager *self, FILE *file, char **buffer,
|
2022-11-12 20:42:46 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-06-08 17:40:59 +01:00
|
|
|
/*
|
2023-09-24 20:40:07 +01:00
|
|
|
* Write the xml files that make up the XLSX OPC package.
|
2014-06-08 17:40:59 +01:00
|
|
|
*/
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error
|
2016-01-04 20:49:54 +00:00
|
|
|
lxw_create_package(lxw_packager *self)
|
2014-06-08 17:40:59 +01:00
|
|
|
{
|
2017-01-09 20:26:32 +00:00
|
|
|
lxw_error error;
|
|
|
|
int8_t zip_error;
|
2016-05-30 00:30:08 +01:00
|
|
|
|
2019-03-29 18:10:20 +00:00
|
|
|
error = _write_content_types_file(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2019-03-29 18:10:20 +00:00
|
|
|
|
|
|
|
error = _write_root_rels_file(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2019-03-29 18:10:20 +00:00
|
|
|
|
|
|
|
error = _write_workbook_rels_file(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2019-03-29 18:10:20 +00:00
|
|
|
|
2016-05-30 21:16:25 +01:00
|
|
|
error = _write_worksheet_files(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2016-05-30 00:30:08 +01:00
|
|
|
|
2018-09-08 23:36:09 +01:00
|
|
|
error = _write_chartsheet_files(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2018-09-08 23:36:09 +01:00
|
|
|
|
2016-05-30 21:16:25 +01:00
|
|
|
error = _write_workbook_file(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2016-05-30 00:30:08 +01:00
|
|
|
|
2016-05-30 21:16:25 +01:00
|
|
|
error = _write_chart_files(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2016-05-30 00:30:08 +01:00
|
|
|
|
2016-05-30 21:16:25 +01:00
|
|
|
error = _write_drawing_files(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2016-05-30 00:30:08 +01:00
|
|
|
|
2019-12-27 20:10:11 +00:00
|
|
|
error = _write_vml_files(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2019-12-27 20:10:11 +00:00
|
|
|
|
|
|
|
error = _write_comment_files(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2019-12-27 20:10:11 +00:00
|
|
|
|
2021-08-16 21:29:27 +01:00
|
|
|
error = _write_table_files(self);
|
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
|
|
|
|
2016-05-30 21:16:25 +01:00
|
|
|
error = _write_shared_strings_file(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2016-05-30 00:30:08 +01:00
|
|
|
|
2016-06-10 00:03:38 +01:00
|
|
|
error = _write_custom_file(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2016-06-10 00:03:38 +01:00
|
|
|
|
2016-05-30 21:16:25 +01:00
|
|
|
error = _write_theme_file(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2016-05-30 00:30:08 +01:00
|
|
|
|
2016-05-30 21:16:25 +01:00
|
|
|
error = _write_styles_file(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2016-05-30 00:30:08 +01:00
|
|
|
|
2016-05-30 21:16:25 +01:00
|
|
|
error = _write_worksheet_rels_file(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2016-05-30 00:30:08 +01:00
|
|
|
|
2018-09-08 23:36:09 +01:00
|
|
|
error = _write_chartsheet_rels_file(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2018-09-08 23:36:09 +01:00
|
|
|
|
2016-05-30 21:16:25 +01:00
|
|
|
error = _write_drawing_rels_file(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2016-05-30 00:30:08 +01:00
|
|
|
|
2016-05-30 21:16:25 +01:00
|
|
|
error = _write_image_files(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2016-05-30 00:30:08 +01:00
|
|
|
|
2019-06-16 15:46:09 +01:00
|
|
|
error = _add_vba_project(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2019-06-16 15:46:09 +01:00
|
|
|
|
2023-09-20 20:37:38 +02:00
|
|
|
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);
|
|
|
|
|
2019-03-29 18:10:20 +00:00
|
|
|
error = _write_core_file(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2019-03-29 18:10:20 +00:00
|
|
|
|
2021-04-23 07:25:50 +01:00
|
|
|
error = _write_metadata_file(self);
|
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
|
|
|
|
2024-07-30 00:33:14 +01:00
|
|
|
error = _write_rich_value_file(self);
|
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
|
|
|
|
|
|
|
error = _write_rich_value_rel_file(self);
|
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
|
|
|
|
|
|
|
error = _write_rich_value_types_file(self);
|
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
|
|
|
|
|
|
|
error = _write_rich_value_structure_file(self);
|
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
|
|
|
|
|
|
|
error = _write_rich_value_rels_file(self);
|
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
|
|
|
|
2019-03-29 18:10:20 +00:00
|
|
|
error = _write_app_file(self);
|
2020-09-24 14:32:04 +01:00
|
|
|
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-09 20:26:32 +00:00
|
|
|
zip_error = zipClose(self->zipfile, NULL);
|
|
|
|
if (zip_error) {
|
|
|
|
RETURN_ON_ZIP_ERROR(zip_error, LXW_ERROR_ZIP_CLOSE);
|
2016-05-30 21:16:25 +01:00
|
|
|
}
|
2014-06-08 17:40:59 +01:00
|
|
|
|
2017-01-09 20:26:32 +00:00
|
|
|
return LXW_NO_ERROR;
|
2014-06-08 17:40:59 +01:00
|
|
|
}
|