xlsxio/lib/xlsxio_write.c
Brecht Sanders 1662674ec3
2023-08-03 11:37:36 +02:00

1085 lines
46 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "xlsxio_write.h"
#include "xlsxio_version.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <inttypes.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <fcntl.h>
#include <stdarg.h>
#if defined(USE_MINIZIP) || defined(USE_MINIZIP_NG)
# ifdef USE_MINIZIP_NG
# include <mz_compat.h>
# else
# include <minizip/zip.h>
# endif
# if !defined(Z_DEFLATED) && defined(MZ_COMPRESS_METHOD_DEFLATE) /* support minizip2 which defines MZ_COMPRESS_METHOD_DEFLATE instead of Z_DEFLATED */
# define Z_DEFLATED MZ_COMPRESS_METHOD_DEFLATE
# endif
# define ZIPFILETYPE zipFile
#else
# if (defined(STATIC) || defined(BUILD_XLSXIO_STATIC) || defined(BUILD_XLSXIO_STATIC_DLL) || (defined(BUILD_XLSXIO) && !defined(BUILD_XLSXIO_DLL) && !defined(BUILD_XLSXIO_SHARED))) && !defined(ZIP_STATIC)
# define ZIP_STATIC
# endif
# include <zip.h>
# ifndef ZIP_RDONLY
typedef struct zip zip_t;
typedef struct zip_source zip_source_t;
# endif
# define ZIPFILETYPE zip_t
# ifndef USE_LIBZIP
# define USE_LIBZIP
# endif
#endif
#if defined(_WIN32) && !defined(USE_PTHREADS)
# define USE_WINTHREADS
# include <windows.h>
#else
# define USE_PTHREADS
# include <pthread.h>
#endif
#if defined(_MSC_VER)
# undef DLL_EXPORT_XLSXIO
# define DLL_EXPORT_XLSXIO
# define va_copy(dst,src) ((dst) = (src))
#endif
#ifdef _WIN32
# define pipe(fds) _pipe(fds, 4096, _O_BINARY)
# define read _read
# define write _write
# define write _write
# define close _close
# define fdopen _fdopen
#else
# define _fdopen(f) f
#endif
//#undef WITHOUT_XLSX_STYLES
#define DEFAULT_BUFFERED_ROWS 5
#define FONT_CHAR_WIDTH 7
//#define CALCULATE_COLUMN_WIDTH(characters) ((double)characters + .75)
#define CALCULATE_COLUMN_WIDTH(characters) ((double)(long)(((long)characters * FONT_CHAR_WIDTH + 5) * 256 / FONT_CHAR_WIDTH) / 256.0)
#define CALCULATE_COLUMN_HEIGHT(characters) ((double)characters * 12.75)
DLL_EXPORT_XLSXIO void xlsxiowrite_get_version (int* pmajor, int* pminor, int* pmicro)
{
if (pmajor)
*pmajor = XLSXIO_VERSION_MAJOR;
if (pminor)
*pminor = XLSXIO_VERSION_MINOR;
if (pmicro)
*pmicro = XLSXIO_VERSION_MICRO;
}
DLL_EXPORT_XLSXIO const char* xlsxiowrite_get_version_string ()
{
return XLSXIO_VERSION_STRING;
}
////////////////////////////////////////////////////////////////////////
//#define WITHOUT_XLSX_SHAREDSTRINGS
#define WITHOUT_XLSX_THEMES
//#define WITHOUT_XLSX_FOLDERS
//#define OPTIONAL_LINE_BREAK "\r\n"
#define OPTIONAL_LINE_BREAK ""
#define XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n"
#define XML_FOLDER_RELS "_rels/"
#ifndef WITHOUT_XLSX_FOLDERS
#define XML_FOLDER_DOCPROPS "docProps/"
#define XML_FOLDER_XL "xl/"
#define XML_FOLDER_THEMES "theme/"
#define XML_FOLDER_WORKSHEETS "worksheets/"
#else
#define XML_FOLDER_DOCPROPS ""
#define XML_FOLDER_XL ""
#define XML_FOLDER_WORKSHEETS ""
#endif
#define XML_FILENAME_CONTENTTYPES "[Content_Types].xml"
#define XML_FILENAME_RELS ".rels"
#define XML_FILENAME_DOCPROPS_CORE "core.xml"
#define XML_FILENAME_DOCPROPS_APP "app.xml"
#define XML_FILENAME_XL_WORKBOOK_RELS "workbook.xml.rels"
#define XML_FILENAME_XL_WORKBOOK "workbook.xml"
#define XML_FILENAME_XL_STYLES "styles.xml"
#define XML_FILENAME_XL_THEME1 "theme1.xml"
#define XML_FILENAME_XL_SHAREDSTRINGS "sharedStrings.xml"
#define XML_FILENAME_XL_WORKSHEET1 "sheet1.xml"
#define XML_SHEETNAME_MAXLEN 31
#define XML_RELID_DOCPROPS_CORE "rId2"
#define XML_RELID_DOCPROPS_APP "rId3"
#define XML_RELID_XL_WORKBOOK "rId1"
#define XML_WORKBOOK_RELID_XL_STYLES "rId1"
#define XML_WORKBOOK_RELID_XL_WORKSHEET1 "rId2"
#define XML_WORKBOOK_RELID_XL_SHAREDSTRINGS "rId3"
#define XML_WORKBOOK_RELID_XL_THEME1 "rId4"
const char* content_types_xml =
XML_HEADER
"<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">" OPTIONAL_LINE_BREAK
"<Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>" OPTIONAL_LINE_BREAK
"<Default Extension=\"xml\" ContentType=\"application/xml\"/>" OPTIONAL_LINE_BREAK
"<Override PartName=\"/" XML_FOLDER_RELS XML_FILENAME_RELS "\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>" OPTIONAL_LINE_BREAK
"<Override PartName=\"/" XML_FOLDER_XL XML_FILENAME_XL_WORKBOOK "\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/>" OPTIONAL_LINE_BREAK
"<Override PartName=\"/" XML_FOLDER_DOCPROPS XML_FILENAME_DOCPROPS_CORE "\" ContentType=\"application/vnd.openxmlformats-package.core-properties+xml\"/>" OPTIONAL_LINE_BREAK
"<Override PartName=\"/" XML_FOLDER_DOCPROPS XML_FILENAME_DOCPROPS_APP "\" ContentType=\"application/vnd.openxmlformats-officedocument.extended-properties+xml\"/>" OPTIONAL_LINE_BREAK
#ifndef WITHOUT_XLSX_STYLES
"<Override PartName=\"/" XML_FOLDER_XL XML_FILENAME_XL_STYLES "\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml\"/>" OPTIONAL_LINE_BREAK
#endif
#ifndef WITHOUT_XLSX_THEMES
"<Override PartName=\"/" XML_FOLDER_XL XML_FOLDER_THEMES XML_FILENAME_XL_THEME1 "\" ContentType=\"application/vnd.openxmlformats-officedocument.theme+xml\"/>" OPTIONAL_LINE_BREAK
#endif
"<Override PartName=\"/" XML_FOLDER_XL XML_FOLDER_RELS XML_FILENAME_XL_WORKBOOK_RELS "\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>" OPTIONAL_LINE_BREAK
"<Override PartName=\"/" XML_FOLDER_XL XML_FOLDER_WORKSHEETS XML_FILENAME_XL_WORKSHEET1 "\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/>" OPTIONAL_LINE_BREAK
#ifndef WITHOUT_XLSX_SHAREDSTRINGS
"<Override PartName=\"/" XML_FOLDER_XL XML_FILENAME_XL_SHAREDSTRINGS "\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml\"/>" OPTIONAL_LINE_BREAK
#endif
"</Types>" OPTIONAL_LINE_BREAK;
const char* docprops_core_xml =
XML_HEADER
"<coreProperties xmlns=\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties\">" OPTIONAL_LINE_BREAK
//"<creator>" XLSXIOWRITE_FULLNAME "</creator>" OPTIONAL_LINE_BREAK
"<lastModifiedBy>" XLSXIOWRITE_FULLNAME "</lastModifiedBy>" OPTIONAL_LINE_BREAK
//"<modified>2016-04-24T17:50:35Z</modified>" OPTIONAL_LINE_BREAK
"</coreProperties>" OPTIONAL_LINE_BREAK;
const char* docprops_app_xml =
XML_HEADER
"<Properties xmlns=\"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties\" xmlns:vt=\"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes\">" OPTIONAL_LINE_BREAK
"<Application>" XLSXIOWRITE_NAME " " XLSXIO_VERSION_STRING "</Application>" OPTIONAL_LINE_BREAK
"</Properties>" OPTIONAL_LINE_BREAK;
const char* rels_xml =
XML_HEADER
"<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">" OPTIONAL_LINE_BREAK
"<Relationship Id=\"" XML_RELID_DOCPROPS_CORE "\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties\" Target=\"" XML_FOLDER_DOCPROPS XML_FILENAME_DOCPROPS_CORE "\"/>" OPTIONAL_LINE_BREAK
"<Relationship Id=\"" XML_RELID_DOCPROPS_APP "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties\" Target=\"" XML_FOLDER_DOCPROPS XML_FILENAME_DOCPROPS_APP "\"/>" OPTIONAL_LINE_BREAK
"<Relationship Id=\"" XML_RELID_XL_WORKBOOK "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"" XML_FOLDER_XL XML_FILENAME_XL_WORKBOOK "\"/>" OPTIONAL_LINE_BREAK
"</Relationships>" OPTIONAL_LINE_BREAK;
#ifndef WITHOUT_XLSX_STYLES
const char* styles_xml =
XML_HEADER
"<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">" OPTIONAL_LINE_BREAK
"<fonts count=\"2\">" OPTIONAL_LINE_BREAK
"<font>" OPTIONAL_LINE_BREAK
"<sz val=\"10\"/>" OPTIONAL_LINE_BREAK
//"<color theme=\"1\"/>" OPTIONAL_LINE_BREAK
"<name val=\"Consolas\"/>" OPTIONAL_LINE_BREAK
"<family val=\"2\"/>" OPTIONAL_LINE_BREAK
//"<scheme val=\"minor\"/>" OPTIONAL_LINE_BREAK
"</font>" OPTIONAL_LINE_BREAK
"<font>" OPTIONAL_LINE_BREAK
"<b/><u/>"
"<sz val=\"10\"/>" OPTIONAL_LINE_BREAK
//"<color theme=\"1\"/>" OPTIONAL_LINE_BREAK
"<name val=\"Consolas\"/>" OPTIONAL_LINE_BREAK
"<family val=\"2\"/>" OPTIONAL_LINE_BREAK
//"<scheme val=\"minor\"/>" OPTIONAL_LINE_BREAK
"</font>" OPTIONAL_LINE_BREAK
"</fonts>" OPTIONAL_LINE_BREAK
"<fills count=\"1\">" OPTIONAL_LINE_BREAK
"<fill/>" OPTIONAL_LINE_BREAK
//"<fill><patternFill patternType=\"none\"/></fill>" OPTIONAL_LINE_BREAK
"</fills>" OPTIONAL_LINE_BREAK
"<borders count=\"2\">" OPTIONAL_LINE_BREAK
"<border>" OPTIONAL_LINE_BREAK
//"<left/>" OPTIONAL_LINE_BREAK
//"<right/>" OPTIONAL_LINE_BREAK
//"<top/>" OPTIONAL_LINE_BREAK
//"<bottom/>" OPTIONAL_LINE_BREAK
//"<diagonal/>" OPTIONAL_LINE_BREAK
"</border>" OPTIONAL_LINE_BREAK
"<border><bottom style=\"thin\"><color indexed=\"64\"/></bottom></border>" OPTIONAL_LINE_BREAK
"</borders>" OPTIONAL_LINE_BREAK
"<cellStyleXfs count=\"1\">" OPTIONAL_LINE_BREAK
//"<xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/>" OPTIONAL_LINE_BREAK
"<xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\">" OPTIONAL_LINE_BREAK
"<alignment horizontal=\"general\" vertical=\"top\" wrapText=\"0\" shrinkToFit=\"0\" textRotation=\"0\" indent=\"0\"/>" OPTIONAL_LINE_BREAK
"</xf>" OPTIONAL_LINE_BREAK
"</cellStyleXfs>" OPTIONAL_LINE_BREAK
"<cellXfs count=\"6\">" OPTIONAL_LINE_BREAK
"<xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>" OPTIONAL_LINE_BREAK
#define STYLE_HEADER 1
"<xf numFmtId=\"0\" fontId=\"1\" fillId=\"0\" borderId=\"1\" xfId=\"0\" applyFont=\"1\" applyBorder=\"1\" applyAlignment=\"1\"><alignment vertical=\"top\"/></xf>" OPTIONAL_LINE_BREAK
#define STYLE_GENERAL 2
"<xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyAlignment=\"1\"><alignment vertical=\"top\"/></xf>" OPTIONAL_LINE_BREAK
#define STYLE_TEXT 3
"<xf numFmtId=\"49\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\" applyAlignment=\"1\"><alignment vertical=\"top\" wrapText=\"1\"/></xf>" OPTIONAL_LINE_BREAK
#define STYLE_INTEGER 4
"<xf numFmtId=\"1\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\" applyAlignment=\"1\"><alignment vertical=\"top\"/></xf>" OPTIONAL_LINE_BREAK
#define STYLE_DATETIME 5
"<xf numFmtId=\"22\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\" applyAlignment=\"1\"><alignment horizontal=\"center\" vertical=\"top\"/></xf>" OPTIONAL_LINE_BREAK
"</cellXfs>" OPTIONAL_LINE_BREAK
//"<cellStyles count=\"2\">" OPTIONAL_LINE_BREAK
//"<cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/>" OPTIONAL_LINE_BREAK
//"</cellStyles>" OPTIONAL_LINE_BREAK
"<dxfs count=\"0\"/>" OPTIONAL_LINE_BREAK
//"<tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium9\" defaultPivotStyle=\"PivotStyleLight16\"/>" OPTIONAL_LINE_BREAK
"</styleSheet>" OPTIONAL_LINE_BREAK;
#endif
#ifndef WITHOUT_XLSX_THEMES
const char* theme_xml =
XML_HEADER
"<theme xmlns=\"http://schemas.openxmlformats.org/drawingml/2006/main\" name=\"Office Theme\">" OPTIONAL_LINE_BREAK
"<themeElements><clrScheme name=\"Office\"><dk1><sysClr val=\"windowText\" lastClr=\"000000\"/></dk1><lt1><sysClr val=\"window\" lastClr=\"FFFFFF\"/></lt1><dk2><srgbClr val=\"1F497D\"/></dk2><lt2><srgbClr val=\"EEECE1\"/></lt2><accent1><srgbClr val=\"4F81BD\"/></accent1><accent2><srgbClr val=\"C0504D\"/></accent2><accent3><srgbClr val=\"9BBB59\"/></accent3><accent4><srgbClr val=\"8064A2\"/></accent4><accent5><srgbClr val=\"4BACC6\"/></accent5><accent6><srgbClr val=\"F79646\"/></accent6><hlink><srgbClr val=\"0000FF\"/></hlink><folHlink><srgbClr val=\"800080\"/></folHlink></clrScheme><fontScheme name=\"Office\"><majorFont><latin typeface=\"Cambria\"/><ea typeface=\"\"/><cs typeface=\"\"/><font script=\"Jpan\" typeface=\" Pゴシック\"/><font script=\"Hang\" typeface=\"맑은 고딕\"/><font script=\"Hans\" typeface=\"宋体\"/><font script=\"Hant\" typeface=\"新細明體\"/><font script=\"Arab\" typeface=\"Times New Roman\"/><font script=\"Hebr\" typeface=\"Times New Roman\"/><font script=\"Thai\" typeface=\"Tahoma\"/><font script=\"Ethi\" typeface=\"Nyala\"/><font script=\"Beng\" typeface=\"Vrinda\"/><font script=\"Gujr\" typeface=\"Shruti\"/><font script=\"Khmr\" typeface=\"MoolBoran\"/><font script=\"Knda\" typeface=\"Tunga\"/><font script=\"Guru\" typeface=\"Raavi\"/><font script=\"Cans\" typeface=\"Euphemia\"/><font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/><font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/><font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/><font script=\"Thaa\" typeface=\"MV Boli\"/><font script=\"Deva\" typeface=\"Mangal\"/><font script=\"Telu\" typeface=\"Gautami\"/><font script=\"Taml\" typeface=\"Latha\"/><font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/><font script=\"Orya\" typeface=\"Kalinga\"/><font script=\"Mlym\" typeface=\"Kartika\"/><font script=\"Laoo\" typeface=\"DokChampa\"/><font script=\"Sinh\" typeface=\"Iskoola Pota\"/><font script=\"Mong\" typeface=\"Mongolian Baiti\"/><font script=\"Viet\" typeface=\"Times New Roman\"/><font script=\"Uigh\" typeface=\"Microsoft Uighur\"/></majorFont><minorFont><latin typeface=\"Calibri\"/><ea typeface=\"\"/><cs typeface=\"\"/><font script=\"Jpan\" typeface=\" Pゴシック\"/><font script=\"Hang\" typeface=\"맑은 고딕\"/><font script=\"Hans\" typeface=\"宋体\"/><font script=\"Hant\" typeface=\"新細明體\"/><font script=\"Arab\" typeface=\"Arial\"/><font script=\"Hebr\" typeface=\"Arial\"/><font script=\"Thai\" typeface=\"Tahoma\"/><font script=\"Ethi\" typeface=\"Nyala\"/><font script=\"Beng\" typeface=\"Vrinda\"/><font script=\"Gujr\" typeface=\"Shruti\"/><font script=\"Khmr\" typeface=\"DaunPenh\"/><font script=\"Knda\" typeface=\"Tunga\"/><font script=\"Guru\" typeface=\"Raavi\"/><font script=\"Cans\" typeface=\"Euphemia\"/><font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/><font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/><font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/><font script=\"Thaa\" typeface=\"MV Boli\"/><font script=\"Deva\" typeface=\"Mangal\"/><font script=\"Telu\" typeface=\"Gautami\"/><font script=\"Taml\" typeface=\"Latha\"/><font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/><font script=\"Orya\" typeface=\"Kalinga\"/><font script=\"Mlym\" typeface=\"Kartika\"/><font script=\"Laoo\" typeface=\"DokChampa\"/><font script=\"Sinh\" typeface=\"Iskoola Pota\"/><font script=\"Mong\" typeface=\"Mongolian Baiti\"/><font script=\"Viet\" typeface=\"Arial\"/><font script=\"Uigh\" typeface=\"Microsoft Uighur\"/></minorFont></fontScheme><fmtScheme name=\"Office\"><fillStyleLst><solidFill><schemeClr val=\"phClr\"/></solidFill><gradFill rotWithShape=\"1\"><gsLst><gs pos=\"0\"><schemeClr val=\"phClr\"><tint val=\"50000\"/><satMod val=\"300000\"/></schemeClr></gs><gs pos=\"35000\"><schemeClr val=\"phClr\"><tint val=\"37000\"/><satMod val=\"300000\"/></schemeClr></gs><gs pos=\"100000\"><schemeClr val=\"phClr\"><tint val=\"15000\"/><satMod val=\"350000\"/></schemeClr></gs></gsLst><lin ang=\"16200000\" scaled=\"1\"/></gradFill><gradFill rotWithShape=\"1\"><gsLst><gs pos=\"0\"><schemeClr val=\"phClr\"><shade val=\"51000\"/><satMod val=\"130000\"/></schemeClr></gs><gs pos=\"80000\"><schemeClr val=\"phClr\"><shade val=\"93000\"/><satMod val=\"130000\"/></schemeClr></gs><gs pos=\"100000\"><schemeClr val=\"phClr\"><shade val=\"94000\"/><satMod val=\"135000\"/></schemeClr></gs></gsLst><lin ang=\"16200000\" scaled=\"0\"/></gradFill></fillStyleLst><lnStyleLst><ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><solidFill><schemeClr val=\"phClr\"><shade val=\"95000\"/><satMod val=\"105000\"/></schemeClr></solidFill><prstDash val=\"solid\"/></ln><ln w=\"25400\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><solidFill><schemeClr val=\"phClr\"/></solidFill><prstDash val=\"solid\"/></ln><ln w=\"38100\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><solidFill><schemeClr val=\"phClr\"/></solidFill><prstDash val=\"solid\"/></ln></lnStyleLst><effectStyleLst><effectStyle><effectLst><outerShdw blurRad=\"40000\" dist=\"20000\" dir=\"5400000\" rotWithShape=\"0\"><srgbClr val=\"000000\"><alpha val=\"38000\"/></srgbClr></outerShdw></effectLst></effectStyle><effectStyle><effectLst><outerShdw blurRad=\"40000\" dist=\"23000\" dir=\"5400000\" rotWithShape=\"0\"><srgbClr val=\"000000\"><alpha val=\"35000\"/></srgbClr></outerShdw></effectLst></effectStyle><effectStyle><effectLst><outerShdw blurRad=\"40000\" dist=\"23000\" dir=\"5400000\" rotWithShape=\"0\"><srgbClr val=\"000000\"><alpha val=\"35000\"/></srgbClr></outerShdw></effectLst><scene3d><camera prst=\"orthographicFront\"><rot lat=\"0\" lon=\"0\" rev=\"0\"/></camera><lightRig rig=\"threePt\" dir=\"t\"><rot lat=\"0\" lon=\"0\" rev=\"1200000\"/></lightRig></scene3d><sp3d><bevelT w=\"63500\" h=\"25400\"/></sp3d></effectStyle></effectStyleLst><bgFillStyleLst><solidFill><schemeClr val=\"phClr\"/></solidFill><gradFill rotWithShape=\"1\"><gsLst><gs pos=\"0\"><schemeClr val=\"phClr\"><tint val=\"40000\"/><satMod val=\"350000\"/></schemeClr></gs><gs pos=\"40000\"><schemeClr val=\"phClr\"><tint val=\"45000\"/><shade val=\"99000\"/><satMod val=\"350000\"/></schemeClr></gs><gs pos=\"100000\"><schemeClr val=\"phClr\"><shade val=\"20000\"/><satMod val=\"255000\"/></schemeClr></gs></gsLst><path path=\"circle\"><fillToRect l=\"50000\" t=\"-80000\" r=\"50000\" b=\"180000\"/></path></gradFill><gradFill rotWithShape=\"1\"><gsLst><gs pos=\"0\"><schemeClr val=\"phClr\"><tint val=\"80000\"/><satMod val=\"300000\"/></schemeClr></gs><gs pos=\"100000\"><schemeClr val=\"phClr\"><shade val=\"30000\"/><satMod val=\"200000\"/></schemeClr></gs></gsLst><path path=\"circle\"><fillToRect l=\"50000\" t=\"50000\" r=\"50000\" b=\"50000\"/></path></gradFill></bgFillStyleLst></fmtScheme></themeElements><objectDefaults/><extraClrSchemeLst/>" OPTIONAL_LINE_BREAK
"</theme>" OPTIONAL_LINE_BREAK;
#endif
#ifndef WITHOUT_XLSX_SHAREDSTRINGS
const char* sharedstrings_xml =
XML_HEADER
"<sst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"/>" OPTIONAL_LINE_BREAK;
//"<sst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" count=\"0\" uniqueCount=\"0\"/>" OPTIONAL_LINE_BREAK;
//"<sst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" count=\"1\" uniqueCount=\"1\">" OPTIONAL_LINE_BREAK
//"<si><t></t></si>" OPTIONAL_LINE_BREAK
//"</sst>" OPTIONAL_LINE_BREAK;
#endif
const char* workbook_rels_xml =
XML_HEADER
"<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">" OPTIONAL_LINE_BREAK
#ifndef WITHOUT_XLSX_STYLES
"<Relationship Id=\"" XML_WORKBOOK_RELID_XL_STYLES "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"" XML_FILENAME_XL_STYLES "\"/>" OPTIONAL_LINE_BREAK
#endif
"<Relationship Id=\"" XML_WORKBOOK_RELID_XL_WORKSHEET1 "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\" Target=\"" XML_FOLDER_WORKSHEETS XML_FILENAME_XL_WORKSHEET1 "\"/>" OPTIONAL_LINE_BREAK
#ifndef WITHOUT_XLSX_SHAREDSTRINGS
"<Relationship Id=\"" XML_WORKBOOK_RELID_XL_SHAREDSTRINGS "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings\" Target=\"" XML_FILENAME_XL_SHAREDSTRINGS "\"/>" OPTIONAL_LINE_BREAK
#endif
#ifndef WITHOUT_XLSX_THEMES
"<Relationship Id=\"" XML_WORKBOOK_RELID_XL_THEME1 "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme\" Target=\"" XML_FOLDER_THEMES XML_FILENAME_XL_THEME1 "\"/>" OPTIONAL_LINE_BREAK
#endif
"</Relationships>" OPTIONAL_LINE_BREAK;
const char* workbook_xml =
XML_HEADER
"<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">" OPTIONAL_LINE_BREAK
//"<workbookPr/>" OPTIONAL_LINE_BREAK
"<bookViews>" OPTIONAL_LINE_BREAK
//"<workbookView/>" OPTIONAL_LINE_BREAK
"<workbookView activeTab=\"0\"/>" OPTIONAL_LINE_BREAK
//"<workbookView activeTab=\"0\" xWindow=\"0\" yWindow=\"0\"/>" OPTIONAL_LINE_BREAK
"</bookViews>" OPTIONAL_LINE_BREAK
"<sheets>" OPTIONAL_LINE_BREAK
"<sheet name=\"%s\" sheetId=\"1\" r:id=\"" XML_WORKBOOK_RELID_XL_WORKSHEET1 "\"/>" OPTIONAL_LINE_BREAK
"</sheets>" OPTIONAL_LINE_BREAK
"</workbook>" OPTIONAL_LINE_BREAK;
const char* worksheet_xml_begin =
XML_HEADER
"<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">" OPTIONAL_LINE_BREAK;
//"<dimension ref=\"A1\"/> OPTIONAL_LINE_BREAK"
const char* worksheet_xml_freeze_top_row =
"<sheetViews>" OPTIONAL_LINE_BREAK
"<sheetView tabSelected=\"1\" workbookViewId=\"0\">" OPTIONAL_LINE_BREAK
"<pane ySplit=\"1\" topLeftCell=\"A2\" activePane=\"bottomLeft\" state=\"frozen\"/>" OPTIONAL_LINE_BREAK
"<selection pane=\"bottomLeft\"/>" OPTIONAL_LINE_BREAK
"</sheetView>" OPTIONAL_LINE_BREAK
"</sheetViews>" OPTIONAL_LINE_BREAK;
const char* worksheet_xml_start_data =
"<sheetData>" OPTIONAL_LINE_BREAK;
const char* worksheet_xml_end =
"</sheetData>" OPTIONAL_LINE_BREAK
//"<pageMargins left=\"0.75\" right=\"0.75\" top=\"1\" bottom=\"1\" header=\"0.5\" footer=\"0.5\"/>" OPTIONAL_LINE_BREAK
"</worksheet>" OPTIONAL_LINE_BREAK;
////////////////////////////////////////////////////////////////////////
#ifdef USE_LIBZIP
zip_int64_t zip_file_add_custom (ZIPFILETYPE* zip, const char* filename, zip_source_t* zipsrc)
{
zip_int64_t index;
if ((index = zip_file_add(zip, filename, zipsrc, ZIP_FL_OVERWRITE | ZIP_FL_ENC_UTF_8)) >= 0) {
zip_set_file_compression(zip, index, ZIP_CM_DEFLATE, 9);
//zip_set_file_compression(zip, index, ZIP_CM_DEFLATE, 5);
//zip_set_file_compression(zip, index, ZIP_CM_STORE, 0);
//zip_file_set_external_attributes(zip, index, 0, ZIP_OPSYS_DOS, 0);
zip_file_set_external_attributes(zip, index, 0, ZIP_OPSYS_VFAT, 0);
//zip_file_set_comment(zip, index, "Test", 4, ZIP_FL_ENC_UTF_8);
//zip_file_set_mtime(zip, index, time(NULL), 0);
//zip_file_extra_field_delete(zip, index, ZIP_EXTRA_FIELD_ALL, ZIP_FL_CENTRAL | ZIP_FL_LOCAL);
}
return index;
}
#endif
int zip_add_content_buffer (ZIPFILETYPE* zip, const char* filename, const char* buf, size_t buflen, int mustfree)
{
#ifdef USE_MINIZIP
zip_fileinfo zipinfo;
time_t now = time(NULL);
struct tm* newtm = localtime(&now);
zipinfo.tmz_date.tm_sec = newtm->tm_sec;
zipinfo.tmz_date.tm_min = newtm->tm_min;
zipinfo.tmz_date.tm_hour = newtm->tm_hour;
zipinfo.tmz_date.tm_mday = newtm->tm_mday;
zipinfo.tmz_date.tm_mon = newtm->tm_mon;
zipinfo.tmz_date.tm_year = newtm->tm_year;
zipinfo.dosDate = 0;
zipinfo.internal_fa = 0;
zipinfo.external_fa = 0;
if (zipOpenNewFileInZip(zip, filename, &zipinfo, NULL, 0, NULL, 0, NULL, Z_DEFLATED, 9) != ZIP_OK) {
fprintf(stderr, "Error creating file \"%s\" inside zip file\n", filename);/////
return 1;
}
if (zipWriteInFileInZip(zip, buf, buflen) != ZIP_OK) {
fprintf(stderr, "Error writing to file \"%s\" inside zip file\n", filename);/////
return 1;
}
zipCloseFileInZip(zip);
if (mustfree)
free((char*)buf);
#else
zip_source_t* zipsrc;
if ((zipsrc = zip_source_buffer(zip, buf, buflen, mustfree)) == NULL) {
fprintf(stderr, "Error creating file \"%s\" inside zip file\n", filename);/////
return 1;
}
if (zip_file_add_custom(zip, filename, zipsrc) < 0) {
fprintf(stderr, "Error in zip_file_add for file %s\n", filename);/////
zip_source_free(zipsrc);
return 2;
}
#endif
return 0;
}
int zip_add_static_content_string (ZIPFILETYPE* zip, const char* filename, const char* data)
{
return zip_add_content_buffer(zip, filename, data, strlen(data), 0);
}
int zip_add_dynamic_content_string (ZIPFILETYPE* zip, const char* filename, const char* data, ...)
{
int result;
char* buf;
int buflen;
va_list args;
va_start(args, data);
buflen = vsnprintf(NULL, 0, data, args);
if (buflen < 0 || (buf = (char*)malloc(buflen + 1)) == NULL) {
result = -1;
} else {
va_end(args);
va_start(args, data);
vsnprintf(buf, buflen + 1, data, args);
result = zip_add_content_buffer(zip, filename, buf, buflen, 1);
}
va_end(args);
return result;
}
////////////////////////////////////////////////////////////////////////
//replace part of a string
char* str_replace (char** s, size_t pos, size_t len, char* replacement)
{
if (!s || !*s)
return NULL;
size_t totallen = strlen(*s);
size_t replacementlen = strlen(replacement);
if (pos > totallen)
pos = totallen;
if (pos + len > totallen)
len = totallen - pos;
if (replacementlen > len)
if ((*s = (char*)realloc(*s, totallen - len + replacementlen + 1)) == NULL)
return NULL;
memmove(*s + pos + replacementlen, *s + pos + len, totallen - pos - len + 1);
memcpy(*s + pos, replacement, replacementlen);
return *s;
}
//fix string for use as XML data
char* fix_xml_special_chars (char** s)
{
int pos = 0;
while (*s && (*s)[pos]) {
switch ((*s)[pos]) {
case '&' :
str_replace(s, pos, 1, "&amp;");
pos += 5;
break;
case '\"' :
str_replace(s, pos, 1, "&quot;");
pos += 6;
break;
case '\'' :
str_replace(s, pos, 1, "&apos;");
pos += 6;
break;
case '<' :
str_replace(s, pos, 1, "&lt;");
pos += 4;
break;
case '>' :
str_replace(s, pos, 1, "&gt;");
pos += 4;
break;
case '\r' :
str_replace(s, pos, 1, "");
break;
default:
pos++;
break;
}
}
return *s;
}
/*
//add data to a null-terminated buffer and update the length counter
int vappend_data (char** pdata, size_t* pdatalen, const char* format, va_list args)
{
int len;
va_list args2;
va_copy(args2, args);
//va_start(args, format);
if ((len = vsnprintf(NULL, 0, format, args)) < 0)
return -1;
va_end(args);
if ((*pdata = (char*)realloc(*pdata, *pdatalen + len + 1)) == NULL)
return -1;
vsnprintf(*pdata + *pdatalen, len + 1, format, args2);
va_end(args2);
*pdatalen += len;
return len;
}
//add formatted data to a null-terminated buffer and update the length counter
int append_data (char** pdata, size_t* pdatalen, const char* format, ...)
{
int result;
va_list args;
va_start(args, format);
result = vappend_data(pdata, pdatalen, format, args);
va_end(args);
return result;
}
*/
//add formatted data to a null-terminated buffer and update the length counter
int append_data (char** pdata, size_t* pdatalen, const char* format, ...)
{
int len;
va_list args;
va_start(args, format);
len = vsnprintf(NULL, 0, format, args);
va_end(args);
if (len < 0)
return -1;
if ((*pdata = (char*)realloc(*pdata, *pdatalen + len + 1)) == NULL)
return -1;
va_start(args, format);
vsnprintf(*pdata + *pdatalen, len + 1, format, args);
va_end(args);
*pdatalen += len;
return len;
}
#ifndef NO_COLUMN_NUMBERS
/*
//insert formatted data into a null-terminated buffer at the specified position and update the length counter
int insert_data (char** pdata, size_t* pdatalen, size_t pos, const char* format, ...)
{
int len;
va_list args;
va_start(args, format);
len = vsnprintf(NULL, 0, format, args);
va_end(args);
if (len < 0)
return -1;
if ((*pdata = (char*)realloc(*pdata, *pdatalen + len + 1)) == NULL)
return -1;
if (pos > *pdatalen)
pos = *pdatalen;
if (pos < *pdatalen)
memmove(*pdata + pos + len, *pdata + pos, *pdatalen - pos + 1);
else
(*pdata)[pos + len] = 0;
va_start(args, format);
len = vsnprintf(*pdata + pos, len, format, args);
va_end(args);
*pdatalen += len;
return len;
}
char* get_A1col (uint64_t col)
{
char* result = NULL;
size_t resultlen = 0;
if (col > 0) {
do {
col--;
insert_data(&result, &resultlen, 0, "%c", 'A' + (col % 26));
col = col / 26;
} while (col > 0);
}
return result;
}
*/
char* get_A1col (uint64_t col)
{
char* result = NULL;
size_t resultlen = 0;
//allocate 19 bytes as the maximum value for 64-bit divided by 26 has 18 digits
if (col > 0 && (result = (char*)malloc(19)) != NULL) {
result[0] = 0;
do {
col--;
memmove(result + 1, result, ++resultlen);
result[0] = 'A' + (col % 26);
col = col / 26;
} while (col > 0);
}
return result;
}
#endif
#define need_space_preserve_attr(value) 1
/*
int need_space_preserve_attr (const char* value)
{
/////TO DO: return non-zero only if space at beginning or end, or contains multiple consecutive spaces
return 1;
}
*/
////////////////////////////////////////////////////////////////////////
struct column_info_struct {
int width;
int maxwidth;
struct column_info_struct* next;
};
struct xlsxio_write_struct {
char* filename;
char* sheetname;
ZIPFILETYPE* zip;
#ifdef USE_WINTHREADS
HANDLE thread;
#else
pthread_t thread;
#endif
FILE* pipe_read;
FILE* pipe_write;
struct column_info_struct* columninfo;
struct column_info_struct** pcurrentcolumn;
char* buf;
size_t buflen;
size_t rowstobuffer;
size_t rowheight;
int freezetop;
int sheetopen;
int rowopen;
#ifndef NO_ROW_NUMBERS
uint64_t rownr;
#ifndef NO_COLUMN_NUMBERS
uint64_t colnr;
#endif
#endif
};
#ifndef NO_ROW_NUMBERS
#define ROWNRTAG " r=\"%" PRIu64 "\""
#define ROWNRPARAM(handle) , handle->rownr
#ifndef NO_COLUMN_NUMBERS
#define COLNRTAG " r=\"%s%" PRIu64 "\""
#endif
#endif
//thread function used for creating .xlsx file from pipe
#ifdef USE_WINTHREADS
DWORD WINAPI thread_proc (LPVOID arg)
#else
void* thread_proc (void* arg)
#endif
{
xlsxiowriter handle = (xlsxiowriter)arg;
//generate required files
zip_add_static_content_string(handle->zip, XML_FILENAME_CONTENTTYPES, content_types_xml);
zip_add_static_content_string(handle->zip, XML_FOLDER_DOCPROPS XML_FILENAME_DOCPROPS_CORE, docprops_core_xml);
zip_add_static_content_string(handle->zip, XML_FOLDER_DOCPROPS XML_FILENAME_DOCPROPS_APP, docprops_app_xml);
zip_add_static_content_string(handle->zip, XML_FOLDER_RELS XML_FILENAME_RELS, rels_xml);
#ifndef WITHOUT_XLSX_STYLES
zip_add_static_content_string(handle->zip, XML_FOLDER_XL XML_FILENAME_XL_STYLES, styles_xml);
#endif
#ifndef WITHOUT_XLSX_THEMES
zip_add_static_content_string(handle->zip, XML_FOLDER_XL XML_FOLDER_THEMES XML_FILENAME_XL_THEME1, theme_xml);
#endif
zip_add_static_content_string(handle->zip, XML_FOLDER_XL XML_FOLDER_RELS XML_FILENAME_XL_WORKBOOK_RELS, workbook_rels_xml);
{ //TO DO: this crashes on Linux
char* sheetname = NULL;
if (handle->sheetname) {
sheetname = strdup(handle->sheetname);
if (sheetname) {
if (strlen(sheetname) > XML_SHEETNAME_MAXLEN)
sheetname[XML_SHEETNAME_MAXLEN] = 0;
fix_xml_special_chars(&sheetname);
}
}
zip_add_dynamic_content_string(handle->zip, XML_FOLDER_XL XML_FILENAME_XL_WORKBOOK, workbook_xml, (sheetname ? sheetname : "Sheet1"));
free(sheetname);
}
#ifndef WITHOUT_XLSX_SHAREDSTRINGS
zip_add_static_content_string(handle->zip, XML_FOLDER_XL XML_FILENAME_XL_SHAREDSTRINGS, sharedstrings_xml);
#endif
//add sheet content file with pipe data
#ifdef USE_MINIZIP
#define MINIZIP_PIPE_BUFFER_SIZE 1024
//#error TO DO:
if (zipOpenNewFileInZip(handle->zip, XML_FOLDER_XL XML_FOLDER_WORKSHEETS XML_FILENAME_XL_WORKSHEET1, NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, 9) != ZIP_OK) {
fprintf(stderr, "Error adding file");
} else {
char* buf;
size_t buflen;
if ((buf = (char*)malloc(MINIZIP_PIPE_BUFFER_SIZE)) == NULL) {
fprintf(stderr, "Memory allocation error");/////
} else {
while ((buflen = fread(buf, 1, MINIZIP_PIPE_BUFFER_SIZE, handle->pipe_read)) > 0) {
if (zipWriteInFileInZip(handle->zip, buf, buflen) != ZIP_OK) {
fprintf(stderr, "Error writing file inside archive");/////
break;
}
}
free(buf);
}
fclose(handle->pipe_read);
zipCloseFileInZip(handle->zip);
zipClose(handle->zip, NULL);
}
#else
zip_source_t* zipsrc = zip_source_filep(handle->zip, handle->pipe_read, 0, -1);
if (zip_file_add_custom(handle->zip, XML_FOLDER_XL XML_FOLDER_WORKSHEETS XML_FILENAME_XL_WORKSHEET1, zipsrc) < 0) {
zip_source_free(zipsrc);
fprintf(stderr, "Error adding file");
}
#ifdef ZIP_RDONLY
zip_file_set_mtime(handle->zip, zip_get_num_entries(handle->zip, 0) - 1, time(NULL), 0);
#endif
//close zip file (processes all data, will block until pipe is closed)
if (zip_close(handle->zip) != 0) {
int ze, se;
#ifdef ZIP_RDONLY
zip_error_t* error = zip_get_error(handle->zip);
ze = zip_error_code_zip(error);
se = zip_error_code_system(error);
#else
zip_error_get(handle->zip, &ze, &se);
#endif
fprintf(stderr, "zip_close failed (%i,%i)\n", ze, se);/////
fprintf(stderr, "can't close zip archive : %s\n", zip_strerror(handle->zip));
}
#endif
handle->zip = NULL;
handle->pipe_read = NULL;
#ifdef USE_WINTHREADS
return 0;
#else
return NULL;
#endif
}
////////////////////////////////////////////////////////////////////////
DLL_EXPORT_XLSXIO xlsxiowriter xlsxiowrite_open (const char* filename, const char* sheetname)
{
xlsxiowriter handle;
if (!filename)
return NULL;
if ((handle = (xlsxiowriter)malloc(sizeof(struct xlsxio_write_struct))) != NULL) {
int pipefd[2];
//initialize
handle->filename = strdup(filename);
handle->sheetname = (sheetname ? strdup(sheetname) : NULL);
handle->zip = NULL;
//handle->pipe_read = NULL;
//handle->pipe_write = NULL;
handle->columninfo = NULL;
handle->pcurrentcolumn = &handle->columninfo;
handle->buf = NULL;
handle->buflen = 0;
handle->rowstobuffer = DEFAULT_BUFFERED_ROWS;
handle->rowheight = 0;
handle->freezetop = 0;
handle->sheetopen = 0;
handle->rowopen = 0;
#ifndef NO_ROW_NUMBERS
handle->rownr = 0;
#ifndef NO_COLUMN_NUMBERS
handle->colnr = 0;
#endif
#endif
//remove filename first if it already exists
unlink(filename);
//initialize zip file object
#ifdef USE_MINIZIP
if ((handle->zip = zipOpen(handle->filename, 0)) == NULL) {
#else
if ((handle->zip = zip_open(handle->filename, ZIP_CREATE, NULL)) == NULL) {
#endif
fprintf(stderr, "Error writing to file %s\n", filename);/////
//unlink(filename);
free(handle->filename);
free(handle);
return NULL;
}
//create pipe
if (pipe(pipefd) != 0) {
fprintf(stderr, "Error creating pipe\n");/////
free(handle);
return NULL;
}
handle->pipe_read = fdopen(pipefd[0], "rb");
handle->pipe_write = fdopen(pipefd[1], "wb");
//create and start thread that will receive data via pipe
#ifdef USE_WINTHREADS
if ((handle->thread = CreateThread(NULL, 0, thread_proc, handle, 0, NULL)) == NULL) {
#else
if (pthread_create(&handle->thread, NULL, thread_proc, handle) != 0) {
#endif
fprintf(stderr, "Error creating thread\n");/////
#ifdef USE_MINIZIP
zipClose(handle->zip, NULL);
#else
zip_close(handle->zip);
#endif
//unlink(filename);
free(handle->filename);
fclose(handle->pipe_read);
fclose(handle->pipe_write);
free(handle);
return NULL;
}
//write initial worksheet data
fprintf(handle->pipe_write, "%s", worksheet_xml_begin);
}
return handle;
}
void flush_buffer (xlsxiowriter handle);
DLL_EXPORT_XLSXIO int xlsxiowrite_close (xlsxiowriter handle)
{
struct column_info_struct* colinfo;
struct column_info_struct* colinfonext;
if (!handle)
return -1;
//finalize data
if (handle->pipe_write) {
//check if buffer should be flushed
if (!handle->sheetopen)
flush_buffer(handle);
//close row if needed
if (handle->rowopen)
fprintf(handle->pipe_write, "</row>" OPTIONAL_LINE_BREAK);
//write worksheet data
fprintf(handle->pipe_write, "%s", worksheet_xml_end);
//close pipe
fclose(handle->pipe_write);
}
//wait for thread to finish
#ifdef USE_WINTHREADS
WaitForSingleObject(handle->thread, INFINITE);
#else
pthread_join(handle->thread, NULL);
#endif
//clean up
colinfo = handle->columninfo;
while (colinfo) {
colinfonext = colinfo->next;
free(colinfo);
colinfo = colinfonext;
}
free(handle->filename);
free(handle->sheetname);
if (handle->zip)
#ifdef USE_MINIZIP
zipClose(handle->zip, NULL);
#else
zip_close(handle->zip);
#endif
if (handle->pipe_read)
fclose(handle->pipe_read);
free(handle);
return 0;
}
#ifndef WITHOUT_XLSX_STYLES
#define STYLE_ATTR_HELPER(x) #x
#define STYLE_ATTR(style) " s=\"" STYLE_ATTR_HELPER(style) "\""
#else
#define STYLE_ATTR(style) ""
#endif
//output start of row
void write_row_start (xlsxiowriter handle, const char* rowattr)
{
#ifndef NO_ROW_NUMBERS
handle->rownr++;
#ifndef NO_COLUMN_NUMBERS
handle->colnr = 0;
#endif
#endif
if (handle->sheetopen) {
if (!handle->rowheight)
fprintf(handle->pipe_write, "<row%s" ROWNRTAG ">", (rowattr ? rowattr : "") ROWNRPARAM(handle));
else
fprintf(handle->pipe_write, "<row ht=\"%.6G\" customHeight=\"1\"%s" ROWNRTAG ">", CALCULATE_COLUMN_HEIGHT(handle->rowheight), (rowattr ? rowattr : "") ROWNRPARAM(handle));
} else {
if (!handle->rowheight)
append_data(&handle->buf, &handle->buflen, "<row%s" ROWNRTAG ">", (rowattr ? rowattr : "") ROWNRPARAM(handle));
else
append_data(&handle->buf, &handle->buflen, "<row ht=\"%.6G\" customHeight=\"1\"%s" ROWNRTAG ">", CALCULATE_COLUMN_HEIGHT(handle->rowheight), (rowattr ? rowattr : "") ROWNRPARAM(handle));
}
handle->rowopen = 1;
}
//output cell data
void write_cell_data (xlsxiowriter handle, const char* rowattr, const char* prefix, const char* suffix, const char* format, ...)
{
va_list args;
#if !defined(NO_ROW_NUMBERS) && !defined(NO_COLUMN_NUMBERS)
char* cellcoord;
#endif
if (!handle)
return;
//start new row if needed
if (!handle->rowopen)
write_row_start(handle, rowattr);
//get formatted data
int datalen;
char* data;
va_start(args, format);
if (format && (datalen = vsnprintf(NULL, 0, format, args)) >= 0 && (data = (char*)malloc(datalen + 1)) != NULL) {
va_end(args);
va_start(args, format);
vsnprintf(data, datalen + 1, format, args);
//prepare data for XML output
fix_xml_special_chars(&data);
} else {
data = NULL;
datalen = 0;
}
va_end(args);
//determine cell coordinate
#if !defined(NO_ROW_NUMBERS) && !defined(NO_COLUMN_NUMBERS)
cellcoord = get_A1col(++handle->colnr);
#define COLNRPARAM(handle) , cellcoord, handle->rownr
#else
#define COLNRPARAM(handle)
#endif
//add cell data
if (handle->sheetopen) {
//write cell data
if (prefix)
fprintf(handle->pipe_write, prefix COLNRPARAM(handle));
if (data)
fprintf(handle->pipe_write, "%s", data);
if (suffix)
fprintf(handle->pipe_write, "%s", suffix);
} else {
//add cell data to buffer
if (prefix)
append_data(&handle->buf, &handle->buflen, prefix COLNRPARAM(handle));
if (data)
append_data(&handle->buf, &handle->buflen, "%s", data);
if (suffix)
append_data(&handle->buf, &handle->buflen, suffix);
//collect cell information
if (!handle->sheetopen) {
if (!*handle->pcurrentcolumn) {
//create new column information structure
struct column_info_struct* colinfo;
if ((colinfo = (struct column_info_struct*)malloc(sizeof(struct column_info_struct))) != NULL) {
colinfo->width = 0;
colinfo->maxwidth = 0;
colinfo->next = NULL;
*handle->pcurrentcolumn = colinfo;
}
}
//keep track of biggest column width
if (data) {
//only count first line in multiline data
char* p = strchr(data, '\n');
if (p)
datalen = p - data;
//remember this length if it is the longest one so far
if (datalen > 0 && datalen > (*handle->pcurrentcolumn)->maxwidth)
(*handle->pcurrentcolumn)->maxwidth = datalen;
}
//prepare for the next column
handle->pcurrentcolumn = &(*handle->pcurrentcolumn)->next;
}
}
#if !defined(NO_ROW_NUMBERS) && !defined(NO_COLUMN_NUMBERS)
free(cellcoord);
#endif
free(data);
}
//output buffered data and stop buffering
void flush_buffer (xlsxiowriter handle)
{
//write section to freeze top row
if (handle->freezetop > 0)
fprintf(handle->pipe_write, "%s", worksheet_xml_freeze_top_row);
//default to row height of 1 line
//fprintf(handle->pipe_write, "<sheetFormatPr defaultRowHeight=\"%.6G\" customHeight=\"1\"/> OPTIONAL_LINE_BREAK", (double)12.75);
//write column information
if (handle->columninfo) {
int col = 0;
int len;
struct column_info_struct* colinfo = handle->columninfo;
fprintf(handle->pipe_write, "<cols>");
while (colinfo) {
++col;
//determine column width
len = colinfo->width;
if (len == 0) {
//use detected maximum length if column width specified was zero
if (colinfo->maxwidth > 0)
len = colinfo->maxwidth;
} else if (len < 0) {
//use detected maximum length if column width specified was negative and the detected maximum length is larger than the absolute value of the specified width
len = -len;
if (colinfo->maxwidth > len)
len = colinfo->maxwidth;
}
if (len)
fprintf(handle->pipe_write, "<col min=\"%i\" max=\"%i\" width=\"%.6G\" customWidth=\"1\"/>" OPTIONAL_LINE_BREAK, col, col, CALCULATE_COLUMN_WIDTH(len));
else
fprintf(handle->pipe_write, "<col min=\"%i\" max=\"%i\"/>" OPTIONAL_LINE_BREAK, col, col);
colinfo = colinfo->next;
}
fprintf(handle->pipe_write, "</cols>" OPTIONAL_LINE_BREAK);
}
//write initial data
fprintf(handle->pipe_write, "%s", worksheet_xml_start_data);
//write buffer and clear it
if (handle->buf) {
if (handle->buflen > 0)
fwrite(handle->buf, 1, handle->buflen, handle->pipe_write);
free(handle->buf);
handle->buf = NULL;
}
handle->buflen = 0;
handle->sheetopen = 1;
}
DLL_EXPORT_XLSXIO void xlsxiowrite_set_detection_rows (xlsxiowriter handle, size_t rows)
{
//abort if currently not buffering
if (!handle->rowstobuffer || handle->sheetopen)
return;
//set number of rows to buffer
handle->rowstobuffer = rows;
//flush when zero was specified
if (!rows)
flush_buffer(handle);
}
DLL_EXPORT_XLSXIO void xlsxiowrite_set_row_height (xlsxiowriter handle, size_t height)
{
handle->rowheight = height;
}
DLL_EXPORT_XLSXIO void xlsxiowrite_add_column (xlsxiowriter handle, const char* value, int width)
{
struct column_info_struct** pcolinfo = handle->pcurrentcolumn;
if (value) {
if (need_space_preserve_attr(value))
write_cell_data(handle, STYLE_ATTR(STYLE_HEADER), "<c t=\"inlineStr\"" STYLE_ATTR(STYLE_HEADER) COLNRTAG "><is xml:space=\"preserve\"><t>", "</t></is></c>", "%s", value);
else
write_cell_data(handle, STYLE_ATTR(STYLE_HEADER), "<c t=\"inlineStr\"" STYLE_ATTR(STYLE_HEADER) COLNRTAG "><is><t>", "</t></is></c>", "%s", value);
} else {
write_cell_data(handle, STYLE_ATTR(STYLE_HEADER), "<c" STYLE_ATTR(STYLE_HEADER) COLNRTAG "/>", NULL, NULL);
}
if (*pcolinfo)
(*pcolinfo)->width = width;
if (handle->freezetop == 0)
handle->freezetop = 1;
}
DLL_EXPORT_XLSXIO void xlsxiowrite_add_cell_string (xlsxiowriter handle, const char* value)
{
if (value) {
if (need_space_preserve_attr(value))
write_cell_data(handle, NULL, "<c t=\"inlineStr\"" STYLE_ATTR(STYLE_TEXT) COLNRTAG "><is xml:space=\"preserve\"><t>", "</t></is></c>", "%s", value);
else
write_cell_data(handle, NULL, "<c t=\"inlineStr\"" STYLE_ATTR(STYLE_TEXT) COLNRTAG "><is><t>", "</t></is></c>", "%s", value);
} else {
write_cell_data(handle, NULL, "<c" STYLE_ATTR(STYLE_TEXT) COLNRTAG "/>", NULL, NULL);
}
}
DLL_EXPORT_XLSXIO void xlsxiowrite_add_cell_int (xlsxiowriter handle, int64_t value)
{
write_cell_data(handle, NULL, "<c" STYLE_ATTR(STYLE_INTEGER) COLNRTAG "><v>", "</v></c>", "%" PRIi64, value);
}
DLL_EXPORT_XLSXIO void xlsxiowrite_add_cell_float (xlsxiowriter handle, double value)
{
write_cell_data(handle, NULL, "<c" STYLE_ATTR(STYLE_GENERAL) COLNRTAG "><v>", "</v></c>", "%.32G", value);
}
DLL_EXPORT_XLSXIO void xlsxiowrite_add_cell_datetime (xlsxiowriter handle, time_t value)
{
double timestamp = ((double)(value) + .499) / 86400 + 25569; //conversion from Unix to Excel timestamp
write_cell_data(handle, NULL, "<c" STYLE_ATTR(STYLE_DATETIME) COLNRTAG "><v>", "</v></c>", "%.16G", timestamp);
}
/*
Windows (And Mac Office 2011+):
Unix Timestamp = (Excel Timestamp - 25569) * 86400
Excel Timestamp = (Unix Timestamp / 86400) + 25569
MAC OS X (pre Office 2011):
Unix Timestamp = (Excel Timestamp - 24107) * 86400
Excel Timestamp = (Unix Timestamp / 86400) + 24107
*/
DLL_EXPORT_XLSXIO void xlsxiowrite_next_row (xlsxiowriter handle)
{
if (!handle)
return;
//check if buffer should be flushed
if (!handle->sheetopen) {
if (handle->rowstobuffer > 0) {
if (--handle->rowstobuffer == 0) {
flush_buffer(handle);
} else {
}
}
}
//start new row if needed
if (!handle->rowopen)
write_row_start(handle, NULL);
//end row
if (handle->rowstobuffer == 0)
fprintf(handle->pipe_write, "</row>" OPTIONAL_LINE_BREAK);
else
append_data(&handle->buf, &handle->buflen, "</row>" OPTIONAL_LINE_BREAK);
handle->rowopen = 0;
handle->pcurrentcolumn = &handle->columninfo;
}