Add memory buffer support

* Avoid using temporary files when possible
* Add support for writing to a buffer

PR #382
This commit is contained in:
Mohamed Akram 2022-11-10 13:15:28 +04:00 committed by John McNamara
parent 07c67b504c
commit 5097c0e41f
31 changed files with 339 additions and 72 deletions

View File

@ -12,7 +12,7 @@ jobs:
cmake_flags: ["",
"-DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON",
"-DUSE_DTOA_LIBRARY=ON -DBUILD_TESTS=ON",
"-DUSE_FMEMOPEN=ON -DBUILD_TESTS=ON",
"-DUSE_MEM_FILE=ON -DBUILD_TESTS=ON",
"-DUSE_NO_MD5=ON -DBUILD_TESTS=ON",
"-DUSE_OPENSSL_MD5=ON -DBUILD_TESTS=ON",
"-DUSE_STANDARD_TMPFILE=ON -DBUILD_TESTS=ON",

View File

@ -15,7 +15,7 @@ jobs:
"USE_DTOA_LIBRARY=1",
"USE_NO_MD5=1",
"USE_OPENSSL_MD5=1",
"USE_FMEMOPEN=1"]
"USE_MEM_FILE=1"]
runs-on: ubuntu-latest
env:
CC: ${{ matrix.cc }}

View File

@ -131,7 +131,7 @@ option(USE_SYSTEM_MINIZIP "Use system minizip installation" OFF)
option(USE_STANDARD_TMPFILE "Use the C standard library's tmpfile()" OFF)
option(USE_NO_MD5 "Build libxlsxwriter without third party MD5 lib" OFF)
option(USE_OPENSSL_MD5 "Build libxlsxwriter with the OpenSSL MD5 lib" OFF)
option(USE_FMEMOPEN "Use fmemopen() in place of some temporary files" OFF)
option(USE_MEM_FILE "Use fmemopen()/open_memstream() in place of temporary files" OFF)
option(IOAPI_NO_64 "Disable 64-bit filesystem support" OFF)
option(USE_DTOA_LIBRARY "Use the locale independent third party Milo Yip DTOA library" OFF)
@ -165,7 +165,7 @@ if(USE_OPENSSL_MD5)
list(APPEND LXW_PRIVATE_COMPILE_DEFINITIONS USE_OPENSSL_MD5)
endif()
if(USE_FMEMOPEN)
if(USE_MEM_FILE OR USE_FMEMOPEN)
list(APPEND LXW_PRIVATE_COMPILE_DEFINITIONS USE_FMEMOPEN)
endif()

View File

@ -537,11 +537,11 @@ with larger CMake builds. In particular it enables building on Windows.
The following are various compilation targets and options for both build systems:
| Make | CMake | Description |
| :----------------------- | :----------------------------------------- | :---------------------------------- |
| :----------------------- | :----------------------------------------- | :---------------------------------------------------- |
| `examples` | `-DBUILD_EXAMPLES=ON` | Build the example |
| `test` | `-DBUILD_TESTS=ON` | Build the tests |
| `USE_DTOA_LIBRARY=1` | `-DUSE_DTOA_LIBRARY=ON` | Use alternative double in sprintf |
| `USE_FMEMOPEN=1` | `-DUSE_FMEMOPEN=ON` | Use fmemopen() for temp image files |
| `USE_MEM_FILE=1` | `-DUSE_MEM_FILE=ON` | Use fmemopen()/open_memstream() instead of temp files |
| `USE_OPENSSL_MD5=1` | `-DUSE_OPENSSL_MD5=ON` | Use OpenSSL for MD5 digest |
| `USE_NO_MD5=1` | `-DUSE_NO_MD5=ON` | Don't use a MD5 digest |
| `USE_SYSTEM_MINIZIP=1` | `-DUSE_SYSTEM_MINIZIP=ON` | Use system minzip library |
@ -573,11 +573,8 @@ Each of the options are explained below:
- `USE_DTOA_LIBRARY`: See @ref gsg_dtoa "using a double formatting library".
- `USE_FMEMOPEN`: When inserting an image from a buffer using
`worksheet_insert_image_buffer()` libxlsxwriter writes the buffer to a
temporary file before processing it. In order to avoid this small overhead
you can use this option to invoke `fmemopen()` instead. This option isn't on
by default since it isn't supported on Windows.
- `USE_MEM_FILE`: Use fmemopen()/open_memstream() instead of temporary files.
This option isn't on by default since it isn't supported on Windows.
- `USE_OPENSSL_MD5`: Uses OpenSSL to provide a MD5 digest of image files in
order to avoid storing duplicates. See @ref gsg_md5.

View File

@ -18,7 +18,9 @@ int main() {
/* Set the worksheet options. */
lxw_workbook_options options = {.constant_memory = LXW_TRUE,
.tmpdir = NULL,
.use_zip64 = LXW_FALSE};
.use_zip64 = LXW_FALSE,
.output_buffer = NULL,
.output_buffer_size = NULL};
/* Create a new workbook with options. */
lxw_workbook *workbook = workbook_new_opt("constant_memory.xlsx", &options);

42
examples/output_buffer.c Normal file
View File

@ -0,0 +1,42 @@
/*
* Example of using libxlsxwriter for writing a workbook file to a buffer.
*
* Copyright 2014-2021, John McNamara, jmcnamara@cpan.org
*
*/
#include <stdio.h>
#include "xlsxwriter.h"
int main() {
char *output_buffer;
size_t output_buffer_size;
/* Set the worksheet options. */
lxw_workbook_options options = {.output_buffer = &output_buffer,
.output_buffer_size = &output_buffer_size,
.constant_memory = LXW_FALSE,
.tmpdir = NULL,
.use_zip64 = LXW_FALSE};
/* Create a new workbook with options. */
lxw_workbook *workbook = workbook_new_opt(NULL, &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
worksheet_write_string(worksheet, 0, 0, "Hello", NULL);
worksheet_write_number(worksheet, 1, 0, 123, NULL);
lxw_error error = workbook_close(workbook);
if (error)
return error;
/* Do something with the XLSX data in the output buffer. */
FILE *file = fopen("output_buffer.xlsx", "wb");
fwrite(output_buffer, output_buffer_size, 1, file);
fclose(file);
free(output_buffer);
return ferror(stdout);
}

View File

@ -62,10 +62,12 @@ typedef struct lxw_packager {
lxw_workbook *workbook;
size_t buffer_size;
size_t output_buffer_size;
zipFile zipfile;
zip_fileinfo zipfile_info;
char *filename;
char *buffer;
char *output_buffer;
char *tmpdir;
uint8_t use_zip64;

View File

@ -233,6 +233,7 @@ void lxw_str_tolower(char *str);
#endif
FILE *lxw_tmpfile(char *tmpdir);
FILE *lxw_get_filehandle(char **buf, size_t *size, char *tmpdir);
FILE *lxw_fopen(const char *filename, const char *mode);
/* Use the third party dtoa function to avoid locale issues with sprintf

View File

@ -243,6 +243,13 @@ typedef struct lxw_doc_properties {
* for more information. This option is off by default.
*
* [zip64_wiki]: https://en.wikipedia.org/wiki/Zip_(file_format)#ZIP64
* - `output_buffer`: Output to a buffer instead of a file. The buffer must be
* freed manually by calling free(). This option can only be used if filename
* is NULL.
*
* - `output_buffer_size`: Used with output_buffer to get the size of the
* created buffer. This option can only be used if filename is NULL.
*
* @note In `constant_memory` mode each row of in-memory data is written to
* disk and then freed when a new row is started via one of the
@ -268,6 +275,11 @@ typedef struct lxw_workbook_options {
/** Allow ZIP64 extensions when creating the xlsx file zip container. */
uint8_t use_zip64;
/** Output buffer to use instead of writing to a file */
char **output_buffer;
/** Used with output_buffer to get the size of the created buffer */
size_t *output_buffer_size;
} lxw_workbook_options;
/**
@ -376,7 +388,9 @@ lxw_workbook *workbook_new(const char *filename);
* @code
* lxw_workbook_options options = {.constant_memory = LXW_TRUE,
* .tmpdir = "C:\\Temp",
* .use_zip64 = LXW_FALSE};
* .use_zip64 = LXW_FALSE,
* .output_buffer = NULL,
* .output_buffer_size = NULL};
*
* lxw_workbook *workbook = workbook_new_opt("filename.xlsx", &options);
* @endcode

View File

@ -2108,6 +2108,7 @@ typedef struct lxw_worksheet {
FILE *file;
FILE *optimize_tmpfile;
char *optimize_buffer;
struct lxw_table_rows *table;
struct lxw_table_rows *hyperlinks;
struct lxw_table_rows *comments;

View File

@ -72,7 +72,11 @@ DTOA_LIB_OBJ = $(DTOA_LIB_DIR)/emyg_dtoa.o
DTOA_LIB_SO = $(DTOA_LIB_DIR)/emyg_dtoa.so
endif
# Use fmemopen() to avoid creating certain temporary files
# Use fmemopen()/open_memstream() to avoid creating temporary files
ifdef USE_MEM_FILE
USE_FMEMOPEN = 1
endif
ifdef USE_FMEMOPEN
CFLAGS += -DUSE_FMEMOPEN
endif

View File

@ -113,21 +113,75 @@ _open_zipfile_win32(const char *filename)
#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(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)
{
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;
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;
@ -143,12 +197,24 @@ lxw_packager_new(const char *filename, char *tmpdir, uint8_t use_zip64)
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);
#else
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;
@ -188,7 +254,8 @@ _write_workbook_file(lxw_packager *self)
lxw_workbook *workbook = self->workbook;
lxw_error err;
workbook->file = lxw_tmpfile(self->tmpdir);
char *buffer = NULL;
workbook->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!workbook->file)
return LXW_ERROR_CREATING_TMPFILE;
@ -196,6 +263,7 @@ _write_workbook_file(lxw_packager *self)
err = _add_file_to_zip(self, workbook->file, "xl/workbook.xml");
fclose(workbook->file);
free(buffer);
RETURN_ON_ERROR(err);
return LXW_NO_ERROR;
@ -211,6 +279,7 @@ _write_worksheet_files(lxw_packager *self)
lxw_sheet *sheet;
lxw_worksheet *worksheet;
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
char *buffer = NULL;
uint32_t index = 1;
lxw_error err;
@ -226,7 +295,7 @@ _write_worksheet_files(lxw_packager *self)
if (worksheet->optimize_row)
lxw_worksheet_write_single_row(worksheet);
worksheet->file = lxw_tmpfile(self->tmpdir);
worksheet->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!worksheet->file)
return LXW_ERROR_CREATING_TMPFILE;
@ -234,6 +303,7 @@ _write_worksheet_files(lxw_packager *self)
err = _add_file_to_zip(self, worksheet->file, sheetname);
fclose(worksheet->file);
free(buffer);
RETURN_ON_ERROR(err);
}
@ -250,6 +320,7 @@ _write_chartsheet_files(lxw_packager *self)
lxw_sheet *sheet;
lxw_chartsheet *chartsheet;
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
char *buffer = NULL;
uint32_t index = 1;
lxw_error err;
@ -262,7 +333,7 @@ _write_chartsheet_files(lxw_packager *self)
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
"xl/chartsheets/sheet%d.xml", index++);
chartsheet->file = lxw_tmpfile(self->tmpdir);
chartsheet->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!chartsheet->file)
return LXW_ERROR_CREATING_TMPFILE;
@ -270,6 +341,7 @@ _write_chartsheet_files(lxw_packager *self)
err = _add_file_to_zip(self, chartsheet->file, sheetname);
fclose(chartsheet->file);
free(buffer);
RETURN_ON_ERROR(err);
}
@ -375,6 +447,7 @@ _write_chart_files(lxw_packager *self)
lxw_workbook *workbook = self->workbook;
lxw_chart *chart;
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
char *buffer = NULL;
uint32_t index = 1;
lxw_error err;
@ -383,7 +456,7 @@ _write_chart_files(lxw_packager *self)
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
"xl/charts/chart%d.xml", index++);
chart->file = lxw_tmpfile(self->tmpdir);
chart->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!chart->file)
return LXW_ERROR_CREATING_TMPFILE;
@ -391,6 +464,7 @@ _write_chart_files(lxw_packager *self)
err = _add_file_to_zip(self, chart->file, sheetname);
fclose(chart->file);
free(buffer);
RETURN_ON_ERROR(err);
}
@ -425,6 +499,7 @@ _write_drawing_files(lxw_packager *self)
lxw_worksheet *worksheet;
lxw_drawing *drawing;
char filename[LXW_FILENAME_LENGTH] = { 0 };
char *buffer = NULL;
uint32_t index = 1;
lxw_error err;
@ -440,7 +515,7 @@ _write_drawing_files(lxw_packager *self)
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
"xl/drawings/drawing%d.xml", index++);
drawing->file = lxw_tmpfile(self->tmpdir);
drawing->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!drawing->file)
return LXW_ERROR_CREATING_TMPFILE;
@ -448,6 +523,7 @@ _write_drawing_files(lxw_packager *self)
err = _add_file_to_zip(self, drawing->file, filename);
fclose(drawing->file);
free(buffer);
RETURN_ON_ERROR(err);
}
}
@ -496,6 +572,7 @@ _write_table_files(lxw_packager *self)
lxw_error err;
char filename[LXW_FILENAME_LENGTH] = { 0 };
char *buffer = NULL;
uint32_t index = 1;
STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) {
@ -518,7 +595,7 @@ _write_table_files(lxw_packager *self)
RETURN_ON_ERROR(err);
}
table->file = lxw_tmpfile(self->tmpdir);
table->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!table->file) {
lxw_table_free(table);
return LXW_ERROR_CREATING_TMPFILE;
@ -530,6 +607,7 @@ _write_table_files(lxw_packager *self)
err = _add_file_to_zip(self, table->file, filename);
fclose(table->file);
free(buffer);
lxw_table_free(table);
RETURN_ON_ERROR(err);
}
@ -572,6 +650,7 @@ _write_vml_files(lxw_packager *self)
lxw_worksheet *worksheet;
lxw_vml *vml;
char filename[LXW_FILENAME_LENGTH] = { 0 };
char *buffer = NULL;
uint32_t index = 1;
lxw_error err;
@ -593,7 +672,7 @@ _write_vml_files(lxw_packager *self)
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
"xl/drawings/vmlDrawing%d.vml", index++);
vml->file = lxw_tmpfile(self->tmpdir);
vml->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!vml->file) {
lxw_vml_free(vml);
return LXW_ERROR_CREATING_TMPFILE;
@ -609,6 +688,7 @@ _write_vml_files(lxw_packager *self)
}
else {
fclose(vml->file);
free(buffer);
lxw_vml_free(vml);
return LXW_ERROR_MEMORY_MALLOC_FAILED;
}
@ -618,6 +698,7 @@ _write_vml_files(lxw_packager *self)
err = _add_file_to_zip(self, vml->file, filename);
fclose(vml->file);
free(buffer);
lxw_vml_free(vml);
RETURN_ON_ERROR(err);
@ -635,7 +716,7 @@ _write_vml_files(lxw_packager *self)
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
"xl/drawings/vmlDrawing%d.vml", index++);
vml->file = lxw_tmpfile(self->tmpdir);
vml->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!vml->file) {
lxw_vml_free(vml);
return LXW_ERROR_CREATING_TMPFILE;
@ -649,6 +730,7 @@ _write_vml_files(lxw_packager *self)
}
else {
fclose(vml->file);
free(buffer);
lxw_vml_free(vml);
return LXW_ERROR_MEMORY_MALLOC_FAILED;
}
@ -658,6 +740,7 @@ _write_vml_files(lxw_packager *self)
err = _add_file_to_zip(self, vml->file, filename);
fclose(vml->file);
free(buffer);
lxw_vml_free(vml);
RETURN_ON_ERROR(err);
@ -678,6 +761,7 @@ _write_comment_files(lxw_packager *self)
lxw_worksheet *worksheet;
lxw_comment *comment;
char filename[LXW_FILENAME_LENGTH] = { 0 };
char *buffer = NULL;
uint32_t index = 1;
lxw_error err;
@ -697,7 +781,7 @@ _write_comment_files(lxw_packager *self)
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
"xl/comments%d.xml", index++);
comment->file = lxw_tmpfile(self->tmpdir);
comment->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!comment->file) {
lxw_comment_free(comment);
return LXW_ERROR_CREATING_TMPFILE;
@ -711,6 +795,7 @@ _write_comment_files(lxw_packager *self)
err = _add_file_to_zip(self, comment->file, filename);
fclose(comment->file);
free(buffer);
lxw_comment_free(comment);
RETURN_ON_ERROR(err);
@ -726,13 +811,14 @@ STATIC lxw_error
_write_shared_strings_file(lxw_packager *self)
{
lxw_sst *sst = self->workbook->sst;
char *buffer = NULL;
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, NULL, self->tmpdir);
if (!sst->file)
return LXW_ERROR_CREATING_TMPFILE;
@ -740,6 +826,7 @@ _write_shared_strings_file(lxw_packager *self)
err = _add_file_to_zip(self, sst->file, "xl/sharedStrings.xml");
fclose(sst->file);
free(buffer);
RETURN_ON_ERROR(err);
return LXW_NO_ERROR;
@ -757,6 +844,7 @@ _write_app_file(lxw_packager *self)
lxw_chartsheet *chartsheet;
lxw_defined_name *defined_name;
lxw_app *app;
char *buffer = NULL;
uint32_t named_range_count = 0;
char *autofilter;
char *has_range;
@ -769,7 +857,7 @@ _write_app_file(lxw_packager *self)
goto mem_error;
}
app->file = lxw_tmpfile(self->tmpdir);
app->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!app->file) {
err = LXW_ERROR_CREATING_TMPFILE;
goto mem_error;
@ -830,6 +918,7 @@ _write_app_file(lxw_packager *self)
err = _add_file_to_zip(self, app->file, "docProps/app.xml");
fclose(app->file);
free(buffer);
mem_error:
lxw_app_free(app);
@ -845,13 +934,14 @@ _write_core_file(lxw_packager *self)
{
lxw_error err = LXW_NO_ERROR;
lxw_core *core = lxw_core_new();
char *buffer = NULL;
if (!core) {
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
goto mem_error;
}
core->file = lxw_tmpfile(self->tmpdir);
core->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!core->file) {
err = LXW_ERROR_CREATING_TMPFILE;
goto mem_error;
@ -864,6 +954,7 @@ _write_core_file(lxw_packager *self)
err = _add_file_to_zip(self, core->file, "docProps/core.xml");
fclose(core->file);
free(buffer);
mem_error:
lxw_core_free(core);
@ -879,6 +970,7 @@ _write_metadata_file(lxw_packager *self)
{
lxw_error err = LXW_NO_ERROR;
lxw_metadata *metadata;
char *buffer = NULL;
if (!self->workbook->has_metadata)
return LXW_NO_ERROR;
@ -890,7 +982,7 @@ _write_metadata_file(lxw_packager *self)
goto mem_error;
}
metadata->file = lxw_tmpfile(self->tmpdir);
metadata->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!metadata->file) {
err = LXW_ERROR_CREATING_TMPFILE;
goto mem_error;
@ -901,6 +993,7 @@ _write_metadata_file(lxw_packager *self)
err = _add_file_to_zip(self, metadata->file, "xl/metadata.xml");
fclose(metadata->file);
free(buffer);
mem_error:
lxw_metadata_free(metadata);
@ -915,6 +1008,7 @@ STATIC lxw_error
_write_custom_file(lxw_packager *self)
{
lxw_custom *custom;
char *buffer = NULL;
lxw_error err = LXW_NO_ERROR;
if (STAILQ_EMPTY(self->workbook->custom_properties))
@ -926,7 +1020,7 @@ _write_custom_file(lxw_packager *self)
goto mem_error;
}
custom->file = lxw_tmpfile(self->tmpdir);
custom->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!custom->file) {
err = LXW_ERROR_CREATING_TMPFILE;
goto mem_error;
@ -939,6 +1033,7 @@ _write_custom_file(lxw_packager *self)
err = _add_file_to_zip(self, custom->file, "docProps/custom.xml");
fclose(custom->file);
free(buffer);
mem_error:
lxw_custom_free(custom);
@ -953,13 +1048,14 @@ _write_theme_file(lxw_packager *self)
{
lxw_error err = LXW_NO_ERROR;
lxw_theme *theme = lxw_theme_new();
char *buffer = NULL;
if (!theme) {
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
goto mem_error;
}
theme->file = lxw_tmpfile(self->tmpdir);
theme->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!theme->file) {
err = LXW_ERROR_CREATING_TMPFILE;
goto mem_error;
@ -970,6 +1066,7 @@ _write_theme_file(lxw_packager *self)
err = _add_file_to_zip(self, theme->file, "xl/theme/theme1.xml");
fclose(theme->file);
free(buffer);
mem_error:
lxw_theme_free(theme);
@ -984,6 +1081,7 @@ STATIC lxw_error
_write_styles_file(lxw_packager *self)
{
lxw_styles *styles = lxw_styles_new();
char *buffer = NULL;
lxw_hash_element *hash_element;
lxw_error err = LXW_NO_ERROR;
@ -1030,7 +1128,7 @@ _write_styles_file(lxw_packager *self)
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, NULL, self->tmpdir);
if (!styles->file) {
err = LXW_ERROR_CREATING_TMPFILE;
goto mem_error;
@ -1041,6 +1139,7 @@ _write_styles_file(lxw_packager *self)
err = _add_file_to_zip(self, styles->file, "xl/styles.xml");
fclose(styles->file);
free(buffer);
mem_error:
lxw_styles_free(styles);
@ -1055,6 +1154,7 @@ STATIC lxw_error
_write_content_types_file(lxw_packager *self)
{
lxw_content_types *content_types = lxw_content_types_new();
char *buffer = NULL;
lxw_workbook *workbook = self->workbook;
lxw_sheet *sheet;
char filename[LXW_MAX_ATTRIBUTE_LENGTH] = { 0 };
@ -1071,7 +1171,7 @@ _write_content_types_file(lxw_packager *self)
goto mem_error;
}
content_types->file = lxw_tmpfile(self->tmpdir);
content_types->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!content_types->file) {
err = LXW_ERROR_CREATING_TMPFILE;
goto mem_error;
@ -1154,6 +1254,7 @@ _write_content_types_file(lxw_packager *self)
err = _add_file_to_zip(self, content_types->file, "[Content_Types].xml");
fclose(content_types->file);
free(buffer);
mem_error:
lxw_content_types_free(content_types);
@ -1168,6 +1269,7 @@ STATIC lxw_error
_write_workbook_rels_file(lxw_packager *self)
{
lxw_relationships *rels = lxw_relationships_new();
char *buffer = NULL;
lxw_workbook *workbook = self->workbook;
lxw_sheet *sheet;
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
@ -1180,7 +1282,7 @@ _write_workbook_rels_file(lxw_packager *self)
goto mem_error;
}
rels->file = lxw_tmpfile(self->tmpdir);
rels->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!rels->file) {
err = LXW_ERROR_CREATING_TMPFILE;
goto mem_error;
@ -1220,6 +1322,7 @@ _write_workbook_rels_file(lxw_packager *self)
err = _add_file_to_zip(self, rels->file, "xl/_rels/workbook.xml.rels");
fclose(rels->file);
free(buffer);
mem_error:
lxw_free_relationships(rels);
@ -1235,6 +1338,7 @@ STATIC lxw_error
_write_worksheet_rels_file(lxw_packager *self)
{
lxw_relationships *rels;
char *buffer = NULL;
lxw_rel_tuple *rel;
lxw_workbook *workbook = self->workbook;
lxw_sheet *sheet;
@ -1262,7 +1366,7 @@ _write_worksheet_rels_file(lxw_packager *self)
rels = lxw_relationships_new();
rels->file = lxw_tmpfile(self->tmpdir);
rels->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!rels->file) {
lxw_free_relationships(rels);
return LXW_ERROR_CREATING_TMPFILE;
@ -1311,6 +1415,7 @@ _write_worksheet_rels_file(lxw_packager *self)
err = _add_file_to_zip(self, rels->file, sheetname);
fclose(rels->file);
free(buffer);
lxw_free_relationships(rels);
RETURN_ON_ERROR(err);
@ -1327,6 +1432,7 @@ STATIC lxw_error
_write_chartsheet_rels_file(lxw_packager *self)
{
lxw_relationships *rels;
char *buffer = NULL;
lxw_rel_tuple *rel;
lxw_workbook *workbook = self->workbook;
lxw_sheet *sheet;
@ -1348,7 +1454,7 @@ _write_chartsheet_rels_file(lxw_packager *self)
rels = lxw_relationships_new();
rels->file = lxw_tmpfile(self->tmpdir);
rels->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!rels->file) {
lxw_free_relationships(rels);
return LXW_ERROR_CREATING_TMPFILE;
@ -1372,6 +1478,7 @@ _write_chartsheet_rels_file(lxw_packager *self)
err = _add_file_to_zip(self, rels->file, sheetname);
fclose(rels->file);
free(buffer);
lxw_free_relationships(rels);
RETURN_ON_ERROR(err);
@ -1388,6 +1495,7 @@ STATIC lxw_error
_write_drawing_rels_file(lxw_packager *self)
{
lxw_relationships *rels;
char *buffer = NULL;
lxw_rel_tuple *rel;
lxw_workbook *workbook = self->workbook;
lxw_sheet *sheet;
@ -1407,7 +1515,7 @@ _write_drawing_rels_file(lxw_packager *self)
rels = lxw_relationships_new();
rels->file = lxw_tmpfile(self->tmpdir);
rels->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!rels->file) {
lxw_free_relationships(rels);
return LXW_ERROR_CREATING_TMPFILE;
@ -1427,6 +1535,7 @@ _write_drawing_rels_file(lxw_packager *self)
err = _add_file_to_zip(self, rels->file, sheetname);
fclose(rels->file);
free(buffer);
lxw_free_relationships(rels);
RETURN_ON_ERROR(err);
@ -1444,13 +1553,14 @@ _write_vml_drawing_rels_file(lxw_packager *self, lxw_worksheet *worksheet,
uint32_t index)
{
lxw_relationships *rels;
char *buffer = NULL;
lxw_rel_tuple *rel;
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
lxw_error err = LXW_NO_ERROR;
rels = lxw_relationships_new();
rels->file = lxw_tmpfile(self->tmpdir);
rels->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!rels->file) {
lxw_free_relationships(rels);
return LXW_ERROR_CREATING_TMPFILE;
@ -1470,6 +1580,7 @@ _write_vml_drawing_rels_file(lxw_packager *self, lxw_worksheet *worksheet,
err = _add_file_to_zip(self, rels->file, sheetname);
fclose(rels->file);
free(buffer);
lxw_free_relationships(rels);
return err;
@ -1482,6 +1593,7 @@ STATIC lxw_error
_write_root_rels_file(lxw_packager *self)
{
lxw_relationships *rels = lxw_relationships_new();
char *buffer = NULL;
lxw_error err = LXW_NO_ERROR;
if (!rels) {
@ -1489,7 +1601,7 @@ _write_root_rels_file(lxw_packager *self)
goto mem_error;
}
rels->file = lxw_tmpfile(self->tmpdir);
rels->file = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!rels->file) {
err = LXW_ERROR_CREATING_TMPFILE;
goto mem_error;
@ -1514,6 +1626,7 @@ _write_root_rels_file(lxw_packager *self)
err = _add_file_to_zip(self, rels->file, "_rels/.rels");
fclose(rels->file);
free(buffer);
mem_error:
lxw_free_relationships(rels);
@ -1555,7 +1668,7 @@ _add_file_to_zip(lxw_packager *self, FILE * file, const char *filename)
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);
}

View File

@ -7,6 +7,10 @@
*
*/
#ifdef USE_FMEMOPEN
#define _POSIX_C_SOURCE 200809L
#endif
#include <ctype.h>
#include <stdio.h>
#include <string.h>
@ -578,6 +582,25 @@ lxw_tmpfile(char *tmpdir)
#endif
}
/**
* Return a memory-backed file if supported, otherwise a temporary one
*/
FILE *
lxw_get_filehandle(char **buf, size_t *size, char *tmpdir)
{
static size_t s;
if (!size)
size = &s;
*buf = NULL;
*size = 0;
#ifdef USE_FMEMOPEN
(void) tmpdir;
return open_memstream(buf, size);
#else
return lxw_tmpfile(tmpdir);
#endif
}
/*
* Use third party function to handle sprintf of doubles for locale portable
* code.

View File

@ -1880,6 +1880,8 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
workbook->options.constant_memory = options->constant_memory;
workbook->options.tmpdir = lxw_strdup(options->tmpdir);
workbook->options.use_zip64 = options->use_zip64;
workbook->options.output_buffer = options->output_buffer;
workbook->options.output_buffer_size = options->output_buffer_size;
}
workbook->max_url_length = 2079;
@ -2192,6 +2194,11 @@ workbook_close(lxw_workbook *self)
/* Assemble all the sub-files in the xlsx package. */
error = lxw_create_package(packager);
if (!self->filename) {
*self->options.output_buffer = packager->output_buffer;
*self->options.output_buffer_size = packager->output_buffer_size;
}
/* Error and non-error conditions fall through to the cleanup code. */
if (error == LXW_ERROR_CREATING_TMPFILE) {
LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): "

View File

@ -195,7 +195,9 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
if (init_data && init_data->optimize) {
FILE *tmpfile;
tmpfile = lxw_tmpfile(init_data->tmpdir);
worksheet->optimize_buffer = NULL;
tmpfile = lxw_get_filehandle(&worksheet->optimize_buffer,
NULL, init_data->tmpdir);
if (!tmpfile) {
LXW_ERROR("Error creating tmpfile() for worksheet in "
"'constant_memory' mode.");
@ -2536,6 +2538,7 @@ _worksheet_write_optimized_sheet_data(lxw_worksheet *self)
}
fclose(self->optimize_tmpfile);
free(self->optimize_buffer);
lxw_xml_end_tag(self->file, "sheetData");
}
@ -8415,6 +8418,7 @@ worksheet_write_rich_string(lxw_worksheet *self,
lxw_format *default_format = NULL;
lxw_rich_string_tuple *rich_string_tuple = NULL;
FILE *tmpfile;
char *buffer = NULL;
err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
if (err)
@ -8439,7 +8443,7 @@ worksheet_write_rich_string(lxw_worksheet *self,
return err;
/* Create a tmp file for the styles object. */
tmpfile = lxw_tmpfile(self->tmpdir);
tmpfile = lxw_get_filehandle(&buffer, NULL, self->tmpdir);
if (!tmpfile)
return LXW_ERROR_CREATING_TMPFILE;
@ -8487,12 +8491,14 @@ worksheet_write_rich_string(lxw_worksheet *self,
rewind(tmpfile);
if (fread(rich_string, file_size, 1, tmpfile) < 1) {
fclose(tmpfile);
free(buffer);
free(rich_string);
return LXW_ERROR_READING_TMPFILE;
}
/* Close the temp file. */
fclose(tmpfile);
free(buffer);
if (lxw_utf8_strlen(rich_string) > LXW_STR_MAX) {
free(rich_string);
@ -8532,6 +8538,7 @@ mem_error:
lxw_styles_free(styles);
lxw_format_free(default_format);
fclose(tmpfile);
free(buffer);
return LXW_ERROR_MEMORY_MALLOC_FAILED;
}

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE};
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE, NULL, NULL};
lxw_workbook *workbook = workbook_new_opt("test_optimize01.xlsx", &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE};
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE, NULL, NULL};
lxw_workbook *workbook = workbook_new_opt("test_optimize02.xlsx", &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE};
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE, NULL, NULL};
lxw_workbook *workbook = workbook_new_opt("test_optimize04.xlsx", &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE};
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE, NULL, NULL};
lxw_workbook *workbook = workbook_new_opt("test_optimize05.xlsx", &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE};
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE, NULL, NULL};
lxw_workbook *workbook = workbook_new_opt("test_optimize06.xlsx", &options);

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE};
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE, NULL, NULL};
lxw_workbook *workbook = workbook_new_opt("test_optimize08.xlsx", &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE};
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE, NULL, NULL};
lxw_workbook *workbook = workbook_new_opt("test_optimize21.xlsx", &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE};
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE, NULL, NULL};
lxw_workbook *workbook = workbook_new_opt("test_optimize22.xlsx", &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE};
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE, NULL, NULL};
lxw_workbook *workbook = workbook_new_opt("test_optimize23.xlsx", &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE};
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE, NULL, NULL};
lxw_workbook *workbook = workbook_new_opt("test_optimize24.xlsx", &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE};
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE, NULL, NULL};
lxw_workbook *workbook = workbook_new_opt("test_optimize25.xlsx", &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE};
lxw_workbook_options options = {LXW_TRUE, NULL, LXW_FALSE, NULL, NULL};
/* Use deprecated constructor for testing. */
lxw_workbook *workbook = workbook_new_opt("test_optimize26.xlsx", &options);

View File

@ -0,0 +1,37 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Simple test case to test data writing.
*
* Copyright 2014-2022, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
char *output_buffer;
size_t output_buffer_size;
lxw_workbook_options options = {LXW_FALSE,
".",
LXW_FALSE,
&output_buffer,
&output_buffer_size};
lxw_workbook *workbook = workbook_new_opt(NULL, &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
worksheet_write_string(worksheet, 0, 0, "Hello", NULL);
worksheet_write_number(worksheet, 1, 0, 123, NULL);
int error = workbook_close(workbook);
if (error)
return error;
FILE *file = fopen("test_output_buffer01.xlsx", "wb");
fwrite(output_buffer, output_buffer_size, 1, file);
fclose(file);
free(output_buffer);
return 0;
}

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_FALSE, ".", LXW_FALSE};
lxw_workbook_options options = {LXW_FALSE, ".", LXW_FALSE, NULL, NULL};
lxw_workbook *workbook = workbook_new_opt("test_tmpdir01.xlsx", &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

View File

@ -11,7 +11,7 @@
int main() {
lxw_workbook_options options = {LXW_TRUE, ".", LXW_FALSE};
lxw_workbook_options options = {LXW_TRUE, ".", LXW_FALSE, NULL, NULL};
lxw_workbook *workbook = workbook_new_opt("test_tmpdir02.xlsx", &options);
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

View File

@ -0,0 +1,17 @@
###############################################################################
#
# Tests for libxlsxwriter.
#
# Copyright 2014-2022, John McNamara, jmcnamara@cpan.org
#
import base_test_class
class TestCompareXLSXFiles(base_test_class.XLSXBaseTest):
"""
Test file created with libxlsxwriter against a file created by Excel.
"""
def test_output_buffer01(self):
self.run_exe_test('test_output_buffer01', 'simple01.xlsx')