packager.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961
  1. /*****************************************************************************
  2. * packager - A library for creating Excel XLSX packager files.
  3. *
  4. * Used in conjunction with the libxlsxwriter library.
  5. *
  6. * Copyright 2014-2018, John McNamara, [email protected]. See LICENSE.txt.
  7. *
  8. */
  9. #include "xlsxwriter/xmlwriter.h"
  10. #include "xlsxwriter/packager.h"
  11. #include "xlsxwriter/hash_table.h"
  12. #include "xlsxwriter/utility.h"
  13. STATIC lxw_error _add_file_to_zip(lxw_packager *self, FILE * file,
  14. const char *filename);
  15. /*
  16. * Forward declarations.
  17. */
  18. /*****************************************************************************
  19. *
  20. * Private functions.
  21. *
  22. ****************************************************************************/
  23. /* Avoid non MSVC definition of _WIN32 in MinGW. */
  24. #ifdef __MINGW32__
  25. #undef _WIN32
  26. #endif
  27. #ifdef _WIN32
  28. /* Silence Windows warning with duplicate symbol for SLIST_ENTRY in local
  29. * queue.h and widows.h. */
  30. #undef SLIST_ENTRY
  31. #include <windows.h>
  32. #ifdef USE_SYSTEM_MINIZIP
  33. #include "minizip/iowin32.h"
  34. #else
  35. #include "iowin32.h"
  36. #endif
  37. zipFile
  38. _open_zipfile_win32(const char *filename)
  39. {
  40. int n;
  41. zlib_filefunc64_def filefunc;
  42. wchar_t wide_filename[_MAX_PATH + 1] = L"";
  43. /* Build a UTF-16 filename for Win32. */
  44. n = MultiByteToWideChar(CP_UTF8, 0, filename, (int) strlen(filename),
  45. wide_filename, _MAX_PATH);
  46. if (n == 0) {
  47. LXW_ERROR("MultiByteToWideChar error");
  48. return NULL;
  49. }
  50. /* Use the native Win32 file handling functions with minizip. */
  51. fill_win32_filefunc64W(&filefunc);
  52. return zipOpen2_64(wide_filename, 0, NULL, &filefunc);
  53. }
  54. #endif
  55. /*
  56. * Create a new packager object.
  57. */
  58. lxw_packager *
  59. lxw_packager_new(const char *filename, char *tmpdir)
  60. {
  61. lxw_packager *packager = calloc(1, sizeof(lxw_packager));
  62. GOTO_LABEL_ON_MEM_ERROR(packager, mem_error);
  63. packager->buffer = calloc(1, LXW_ZIP_BUFFER_SIZE);
  64. GOTO_LABEL_ON_MEM_ERROR(packager->buffer, mem_error);
  65. packager->filename = lxw_strdup(filename);
  66. packager->tmpdir = tmpdir;
  67. GOTO_LABEL_ON_MEM_ERROR(packager->filename, mem_error);
  68. packager->buffer_size = LXW_ZIP_BUFFER_SIZE;
  69. /* Initialize the zip_fileinfo struct to Jan 1 1980 like Excel. */
  70. packager->zipfile_info.tmz_date.tm_sec = 0;
  71. packager->zipfile_info.tmz_date.tm_min = 0;
  72. packager->zipfile_info.tmz_date.tm_hour = 0;
  73. packager->zipfile_info.tmz_date.tm_mday = 1;
  74. packager->zipfile_info.tmz_date.tm_mon = 0;
  75. packager->zipfile_info.tmz_date.tm_year = 1980;
  76. packager->zipfile_info.dosDate = 0;
  77. packager->zipfile_info.internal_fa = 0;
  78. packager->zipfile_info.external_fa = 0;
  79. /* Create a zip container for the xlsx file. */
  80. #ifdef _WIN32
  81. packager->zipfile = _open_zipfile_win32(packager->filename);
  82. #else
  83. packager->zipfile = zipOpen(packager->filename, 0);
  84. #endif
  85. if (packager->zipfile == NULL)
  86. goto mem_error;
  87. return packager;
  88. mem_error:
  89. lxw_packager_free(packager);
  90. return NULL;
  91. }
  92. /*
  93. * Free a packager object.
  94. */
  95. void
  96. lxw_packager_free(lxw_packager *packager)
  97. {
  98. if (!packager)
  99. return;
  100. free(packager->buffer);
  101. free(packager->filename);
  102. free(packager);
  103. }
  104. /*****************************************************************************
  105. *
  106. * File assembly functions.
  107. *
  108. ****************************************************************************/
  109. /*
  110. * Write the workbook.xml file.
  111. */
  112. STATIC lxw_error
  113. _write_workbook_file(lxw_packager *self)
  114. {
  115. lxw_workbook *workbook = self->workbook;
  116. lxw_error err;
  117. workbook->file = lxw_tmpfile(self->tmpdir);
  118. if (!workbook->file)
  119. return LXW_ERROR_CREATING_TMPFILE;
  120. lxw_workbook_assemble_xml_file(workbook);
  121. err = _add_file_to_zip(self, workbook->file, "xl/workbook.xml");
  122. RETURN_ON_ERROR(err);
  123. fclose(workbook->file);
  124. return LXW_NO_ERROR;
  125. }
  126. /*
  127. * Write the worksheet files.
  128. */
  129. STATIC lxw_error
  130. _write_worksheet_files(lxw_packager *self)
  131. {
  132. lxw_workbook *workbook = self->workbook;
  133. lxw_worksheet *worksheet;
  134. char sheetname[LXW_FILENAME_LENGTH] = { 0 };
  135. uint16_t index = 1;
  136. lxw_error err;
  137. STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
  138. lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
  139. "xl/worksheets/sheet%d.xml", index++);
  140. if (worksheet->optimize_row)
  141. lxw_worksheet_write_single_row(worksheet);
  142. worksheet->file = lxw_tmpfile(self->tmpdir);
  143. if (!worksheet->file)
  144. return LXW_ERROR_CREATING_TMPFILE;
  145. lxw_worksheet_assemble_xml_file(worksheet);
  146. err = _add_file_to_zip(self, worksheet->file, sheetname);
  147. RETURN_ON_ERROR(err);
  148. fclose(worksheet->file);
  149. }
  150. return LXW_NO_ERROR;
  151. }
  152. /*
  153. * Write the /xl/media/image?.xml files.
  154. */
  155. STATIC lxw_error
  156. _write_image_files(lxw_packager *self)
  157. {
  158. lxw_workbook *workbook = self->workbook;
  159. lxw_worksheet *worksheet;
  160. lxw_image_options *image;
  161. lxw_error err;
  162. FILE *image_stream;
  163. char filename[LXW_FILENAME_LENGTH] = { 0 };
  164. uint16_t index = 1;
  165. STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
  166. if (STAILQ_EMPTY(worksheet->image_data))
  167. continue;
  168. STAILQ_FOREACH(image, worksheet->image_data, list_pointers) {
  169. lxw_snprintf(filename, LXW_FILENAME_LENGTH,
  170. "xl/media/image%d.%s", index++, image->extension);
  171. /* Check that the image file exists and can be opened. */
  172. image_stream = fopen(image->filename, "rb");
  173. if (!image_stream) {
  174. LXW_WARN_FORMAT1("Error adding image to xlsx file: file "
  175. "doesn't exist or can't be opened: %s.",
  176. image->filename);
  177. return LXW_ERROR_CREATING_TMPFILE;
  178. }
  179. err = _add_file_to_zip(self, image_stream, filename);
  180. fclose(image_stream);
  181. RETURN_ON_ERROR(err);
  182. }
  183. }
  184. return LXW_NO_ERROR;
  185. }
  186. /*
  187. * Write the chart files.
  188. */
  189. STATIC lxw_error
  190. _write_chart_files(lxw_packager *self)
  191. {
  192. lxw_workbook *workbook = self->workbook;
  193. lxw_chart *chart;
  194. char sheetname[LXW_FILENAME_LENGTH] = { 0 };
  195. uint16_t index = 1;
  196. lxw_error err;
  197. STAILQ_FOREACH(chart, workbook->ordered_charts, ordered_list_pointers) {
  198. lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
  199. "xl/charts/chart%d.xml", index++);
  200. chart->file = lxw_tmpfile(self->tmpdir);
  201. if (!chart->file)
  202. return LXW_ERROR_CREATING_TMPFILE;
  203. lxw_chart_assemble_xml_file(chart);
  204. err = _add_file_to_zip(self, chart->file, sheetname);
  205. RETURN_ON_ERROR(err);
  206. self->chart_count++;
  207. fclose(chart->file);
  208. }
  209. return LXW_NO_ERROR;
  210. }
  211. /*
  212. * Write the drawing files.
  213. */
  214. STATIC lxw_error
  215. _write_drawing_files(lxw_packager *self)
  216. {
  217. lxw_workbook *workbook = self->workbook;
  218. lxw_worksheet *worksheet;
  219. lxw_drawing *drawing;
  220. char filename[LXW_FILENAME_LENGTH] = { 0 };
  221. uint16_t index = 1;
  222. lxw_error err;
  223. STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
  224. drawing = worksheet->drawing;
  225. if (drawing) {
  226. lxw_snprintf(filename, LXW_FILENAME_LENGTH,
  227. "xl/drawings/drawing%d.xml", index++);
  228. drawing->file = lxw_tmpfile(self->tmpdir);
  229. if (!drawing->file)
  230. return LXW_ERROR_CREATING_TMPFILE;
  231. lxw_drawing_assemble_xml_file(drawing);
  232. err = _add_file_to_zip(self, drawing->file, filename);
  233. RETURN_ON_ERROR(err);
  234. fclose(drawing->file);
  235. self->drawing_count++;
  236. }
  237. }
  238. return LXW_NO_ERROR;
  239. }
  240. /*
  241. * Write the sharedStrings.xml file.
  242. */
  243. STATIC lxw_error
  244. _write_shared_strings_file(lxw_packager *self)
  245. {
  246. lxw_sst *sst = self->workbook->sst;
  247. lxw_error err;
  248. /* Skip the sharedStrings file if there are no shared strings. */
  249. if (!sst->string_count)
  250. return LXW_NO_ERROR;
  251. sst->file = lxw_tmpfile(self->tmpdir);
  252. if (!sst->file)
  253. return LXW_ERROR_CREATING_TMPFILE;
  254. lxw_sst_assemble_xml_file(sst);
  255. err = _add_file_to_zip(self, sst->file, "xl/sharedStrings.xml");
  256. RETURN_ON_ERROR(err);
  257. fclose(sst->file);
  258. return LXW_NO_ERROR;
  259. }
  260. /*
  261. * Write the app.xml file.
  262. */
  263. STATIC lxw_error
  264. _write_app_file(lxw_packager *self)
  265. {
  266. lxw_workbook *workbook = self->workbook;
  267. lxw_worksheet *worksheet;
  268. lxw_defined_name *defined_name;
  269. lxw_app *app;
  270. uint16_t named_range_count = 0;
  271. char *autofilter;
  272. char *has_range;
  273. char number[LXW_ATTR_32] = { 0 };
  274. lxw_error err = LXW_NO_ERROR;
  275. app = lxw_app_new();
  276. if (!app) {
  277. err = LXW_ERROR_MEMORY_MALLOC_FAILED;
  278. goto mem_error;
  279. }
  280. app->file = lxw_tmpfile(self->tmpdir);
  281. if (!app->file) {
  282. err = LXW_ERROR_CREATING_TMPFILE;
  283. goto mem_error;
  284. }
  285. lxw_snprintf(number, LXW_ATTR_32, "%d", self->workbook->num_sheets);
  286. lxw_app_add_heading_pair(app, "Worksheets", number);
  287. STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
  288. lxw_app_add_part_name(app, worksheet->name);
  289. }
  290. /* Add the Named Ranges parts. */
  291. TAILQ_FOREACH(defined_name, workbook->defined_names, list_pointers) {
  292. has_range = strchr(defined_name->formula, '!');
  293. autofilter = strstr(defined_name->app_name, "_FilterDatabase");
  294. /* Only store defined names with ranges (except for autofilters). */
  295. if (has_range && !autofilter) {
  296. lxw_app_add_part_name(app, defined_name->app_name);
  297. named_range_count++;
  298. }
  299. }
  300. /* Add the Named Range heading pairs. */
  301. if (named_range_count) {
  302. lxw_snprintf(number, LXW_ATTR_32, "%d", named_range_count);
  303. lxw_app_add_heading_pair(app, "Named Ranges", number);
  304. }
  305. /* Set the app/doc properties. */
  306. app->properties = workbook->properties;
  307. lxw_app_assemble_xml_file(app);
  308. err = _add_file_to_zip(self, app->file, "docProps/app.xml");
  309. fclose(app->file);
  310. mem_error:
  311. lxw_app_free(app);
  312. return err;
  313. }
  314. /*
  315. * Write the core.xml file.
  316. */
  317. STATIC lxw_error
  318. _write_core_file(lxw_packager *self)
  319. {
  320. lxw_error err = LXW_NO_ERROR;
  321. lxw_core *core = lxw_core_new();
  322. if (!core) {
  323. err = LXW_ERROR_MEMORY_MALLOC_FAILED;
  324. goto mem_error;
  325. }
  326. core->file = lxw_tmpfile(self->tmpdir);
  327. if (!core->file) {
  328. err = LXW_ERROR_CREATING_TMPFILE;
  329. goto mem_error;
  330. }
  331. core->properties = self->workbook->properties;
  332. lxw_core_assemble_xml_file(core);
  333. err = _add_file_to_zip(self, core->file, "docProps/core.xml");
  334. fclose(core->file);
  335. mem_error:
  336. lxw_core_free(core);
  337. return err;
  338. }
  339. /*
  340. * Write the custom.xml file.
  341. */
  342. STATIC lxw_error
  343. _write_custom_file(lxw_packager *self)
  344. {
  345. lxw_custom *custom;
  346. lxw_error err = LXW_NO_ERROR;
  347. if (STAILQ_EMPTY(self->workbook->custom_properties))
  348. return LXW_NO_ERROR;
  349. custom = lxw_custom_new();
  350. if (!custom) {
  351. err = LXW_ERROR_MEMORY_MALLOC_FAILED;
  352. goto mem_error;
  353. }
  354. custom->file = lxw_tmpfile(self->tmpdir);
  355. if (!custom->file) {
  356. err = LXW_ERROR_CREATING_TMPFILE;
  357. goto mem_error;
  358. }
  359. custom->custom_properties = self->workbook->custom_properties;
  360. lxw_custom_assemble_xml_file(custom);
  361. err = _add_file_to_zip(self, custom->file, "docProps/custom.xml");
  362. fclose(custom->file);
  363. mem_error:
  364. lxw_custom_free(custom);
  365. return err;
  366. }
  367. /*
  368. * Write the theme.xml file.
  369. */
  370. STATIC lxw_error
  371. _write_theme_file(lxw_packager *self)
  372. {
  373. lxw_error err = LXW_NO_ERROR;
  374. lxw_theme *theme = lxw_theme_new();
  375. if (!theme) {
  376. err = LXW_ERROR_MEMORY_MALLOC_FAILED;
  377. goto mem_error;
  378. }
  379. theme->file = lxw_tmpfile(self->tmpdir);
  380. if (!theme->file) {
  381. err = LXW_ERROR_CREATING_TMPFILE;
  382. goto mem_error;
  383. }
  384. lxw_theme_assemble_xml_file(theme);
  385. err = _add_file_to_zip(self, theme->file, "xl/theme/theme1.xml");
  386. fclose(theme->file);
  387. mem_error:
  388. lxw_theme_free(theme);
  389. return err;
  390. }
  391. /*
  392. * Write the styles.xml file.
  393. */
  394. STATIC lxw_error
  395. _write_styles_file(lxw_packager *self)
  396. {
  397. lxw_styles *styles = lxw_styles_new();
  398. lxw_hash_element *hash_element;
  399. lxw_error err = LXW_NO_ERROR;
  400. if (!styles) {
  401. err = LXW_ERROR_MEMORY_MALLOC_FAILED;
  402. goto mem_error;
  403. }
  404. /* Copy the unique and in-use formats from the workbook to the styles
  405. * xf_format list. */
  406. LXW_FOREACH_ORDERED(hash_element, self->workbook->used_xf_formats) {
  407. lxw_format *workbook_format = (lxw_format *) hash_element->value;
  408. lxw_format *style_format = lxw_format_new();
  409. if (!style_format) {
  410. err = LXW_ERROR_MEMORY_MALLOC_FAILED;
  411. goto mem_error;
  412. }
  413. memcpy(style_format, workbook_format, sizeof(lxw_format));
  414. STAILQ_INSERT_TAIL(styles->xf_formats, style_format, list_pointers);
  415. }
  416. styles->font_count = self->workbook->font_count;
  417. styles->border_count = self->workbook->border_count;
  418. styles->fill_count = self->workbook->fill_count;
  419. styles->num_format_count = self->workbook->num_format_count;
  420. styles->xf_count = self->workbook->used_xf_formats->unique_count;
  421. styles->file = lxw_tmpfile(self->tmpdir);
  422. if (!styles->file) {
  423. err = LXW_ERROR_CREATING_TMPFILE;
  424. goto mem_error;
  425. }
  426. lxw_styles_assemble_xml_file(styles);
  427. err = _add_file_to_zip(self, styles->file, "xl/styles.xml");
  428. fclose(styles->file);
  429. mem_error:
  430. lxw_styles_free(styles);
  431. return err;
  432. }
  433. /*
  434. * Write the ContentTypes.xml file.
  435. */
  436. STATIC lxw_error
  437. _write_content_types_file(lxw_packager *self)
  438. {
  439. lxw_content_types *content_types = lxw_content_types_new();
  440. lxw_workbook *workbook = self->workbook;
  441. lxw_worksheet *worksheet;
  442. char filename[LXW_MAX_ATTRIBUTE_LENGTH] = { 0 };
  443. uint16_t index = 1;
  444. lxw_error err = LXW_NO_ERROR;
  445. if (!content_types) {
  446. err = LXW_ERROR_MEMORY_MALLOC_FAILED;
  447. goto mem_error;
  448. }
  449. content_types->file = lxw_tmpfile(self->tmpdir);
  450. if (!content_types->file) {
  451. err = LXW_ERROR_CREATING_TMPFILE;
  452. goto mem_error;
  453. }
  454. if (workbook->has_png)
  455. lxw_ct_add_default(content_types, "png", "image/png");
  456. if (workbook->has_jpeg)
  457. lxw_ct_add_default(content_types, "jpeg", "image/jpeg");
  458. if (workbook->has_bmp)
  459. lxw_ct_add_default(content_types, "bmp", "image/bmp");
  460. STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
  461. lxw_snprintf(filename, LXW_FILENAME_LENGTH,
  462. "/xl/worksheets/sheet%d.xml", index++);
  463. lxw_ct_add_worksheet_name(content_types, filename);
  464. }
  465. for (index = 1; index <= self->chart_count; index++) {
  466. lxw_snprintf(filename, LXW_FILENAME_LENGTH, "/xl/charts/chart%d.xml",
  467. index);
  468. lxw_ct_add_chart_name(content_types, filename);
  469. }
  470. for (index = 1; index <= self->drawing_count; index++) {
  471. lxw_snprintf(filename, LXW_FILENAME_LENGTH,
  472. "/xl/drawings/drawing%d.xml", index);
  473. lxw_ct_add_drawing_name(content_types, filename);
  474. }
  475. if (workbook->sst->string_count)
  476. lxw_ct_add_shared_strings(content_types);
  477. if (!STAILQ_EMPTY(self->workbook->custom_properties))
  478. lxw_ct_add_custom_properties(content_types);
  479. lxw_content_types_assemble_xml_file(content_types);
  480. err = _add_file_to_zip(self, content_types->file, "[Content_Types].xml");
  481. fclose(content_types->file);
  482. mem_error:
  483. lxw_content_types_free(content_types);
  484. return err;
  485. }
  486. /*
  487. * Write the workbook .rels xml file.
  488. */
  489. STATIC lxw_error
  490. _write_workbook_rels_file(lxw_packager *self)
  491. {
  492. lxw_relationships *rels = lxw_relationships_new();
  493. lxw_workbook *workbook = self->workbook;
  494. lxw_worksheet *worksheet;
  495. char sheetname[LXW_FILENAME_LENGTH] = { 0 };
  496. uint16_t index = 1;
  497. lxw_error err = LXW_NO_ERROR;
  498. if (!rels) {
  499. err = LXW_ERROR_MEMORY_MALLOC_FAILED;
  500. goto mem_error;
  501. }
  502. rels->file = lxw_tmpfile(self->tmpdir);
  503. if (!rels->file) {
  504. err = LXW_ERROR_CREATING_TMPFILE;
  505. goto mem_error;
  506. }
  507. STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
  508. lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "worksheets/sheet%d.xml",
  509. index++);
  510. lxw_add_document_relationship(rels, "/worksheet", sheetname);
  511. }
  512. lxw_add_document_relationship(rels, "/theme", "theme/theme1.xml");
  513. lxw_add_document_relationship(rels, "/styles", "styles.xml");
  514. if (workbook->sst->string_count)
  515. lxw_add_document_relationship(rels, "/sharedStrings",
  516. "sharedStrings.xml");
  517. lxw_relationships_assemble_xml_file(rels);
  518. err = _add_file_to_zip(self, rels->file, "xl/_rels/workbook.xml.rels");
  519. fclose(rels->file);
  520. mem_error:
  521. lxw_free_relationships(rels);
  522. return err;
  523. }
  524. /*
  525. * Write the worksheet .rels files for worksheets that contain links to
  526. * external data such as hyperlinks or drawings.
  527. */
  528. STATIC lxw_error
  529. _write_worksheet_rels_file(lxw_packager *self)
  530. {
  531. lxw_relationships *rels;
  532. lxw_rel_tuple *rel;
  533. lxw_workbook *workbook = self->workbook;
  534. lxw_worksheet *worksheet;
  535. char sheetname[LXW_FILENAME_LENGTH] = { 0 };
  536. uint16_t index = 0;
  537. lxw_error err;
  538. STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
  539. index++;
  540. if (STAILQ_EMPTY(worksheet->external_hyperlinks) &&
  541. STAILQ_EMPTY(worksheet->external_drawing_links))
  542. continue;
  543. rels = lxw_relationships_new();
  544. rels->file = lxw_tmpfile(self->tmpdir);
  545. if (!rels->file) {
  546. lxw_free_relationships(rels);
  547. return LXW_ERROR_CREATING_TMPFILE;
  548. }
  549. STAILQ_FOREACH(rel, worksheet->external_hyperlinks, list_pointers) {
  550. lxw_add_worksheet_relationship(rels, rel->type, rel->target,
  551. rel->target_mode);
  552. }
  553. STAILQ_FOREACH(rel, worksheet->external_drawing_links, list_pointers) {
  554. lxw_add_worksheet_relationship(rels, rel->type, rel->target,
  555. rel->target_mode);
  556. }
  557. lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
  558. "xl/worksheets/_rels/sheet%d.xml.rels", index);
  559. lxw_relationships_assemble_xml_file(rels);
  560. err = _add_file_to_zip(self, rels->file, sheetname);
  561. fclose(rels->file);
  562. lxw_free_relationships(rels);
  563. RETURN_ON_ERROR(err);
  564. }
  565. return LXW_NO_ERROR;
  566. }
  567. /*
  568. * Write the drawing .rels files for worksheets that contain charts or
  569. * drawings.
  570. */
  571. STATIC lxw_error
  572. _write_drawing_rels_file(lxw_packager *self)
  573. {
  574. lxw_relationships *rels;
  575. lxw_rel_tuple *rel;
  576. lxw_workbook *workbook = self->workbook;
  577. lxw_worksheet *worksheet;
  578. char sheetname[LXW_FILENAME_LENGTH] = { 0 };
  579. uint16_t index = 1;
  580. lxw_error err;
  581. STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
  582. if (STAILQ_EMPTY(worksheet->drawing_links))
  583. continue;
  584. rels = lxw_relationships_new();
  585. rels->file = lxw_tmpfile(self->tmpdir);
  586. if (!rels->file) {
  587. lxw_free_relationships(rels);
  588. return LXW_ERROR_CREATING_TMPFILE;
  589. }
  590. STAILQ_FOREACH(rel, worksheet->drawing_links, list_pointers) {
  591. lxw_add_worksheet_relationship(rels, rel->type, rel->target,
  592. rel->target_mode);
  593. }
  594. lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
  595. "xl/drawings/_rels/drawing%d.xml.rels", index++);
  596. lxw_relationships_assemble_xml_file(rels);
  597. err = _add_file_to_zip(self, rels->file, sheetname);
  598. fclose(rels->file);
  599. lxw_free_relationships(rels);
  600. RETURN_ON_ERROR(err);
  601. }
  602. return LXW_NO_ERROR;
  603. }
  604. /*
  605. * Write the _rels/.rels xml file.
  606. */
  607. STATIC lxw_error
  608. _write_root_rels_file(lxw_packager *self)
  609. {
  610. lxw_relationships *rels = lxw_relationships_new();
  611. lxw_error err = LXW_NO_ERROR;
  612. if (!rels) {
  613. err = LXW_ERROR_MEMORY_MALLOC_FAILED;
  614. goto mem_error;
  615. }
  616. rels->file = lxw_tmpfile(self->tmpdir);
  617. if (!rels->file) {
  618. err = LXW_ERROR_CREATING_TMPFILE;
  619. goto mem_error;
  620. }
  621. lxw_add_document_relationship(rels, "/officeDocument", "xl/workbook.xml");
  622. lxw_add_package_relationship(rels,
  623. "/metadata/core-properties",
  624. "docProps/core.xml");
  625. lxw_add_document_relationship(rels,
  626. "/extended-properties", "docProps/app.xml");
  627. if (!STAILQ_EMPTY(self->workbook->custom_properties))
  628. lxw_add_document_relationship(rels,
  629. "/custom-properties",
  630. "docProps/custom.xml");
  631. lxw_relationships_assemble_xml_file(rels);
  632. err = _add_file_to_zip(self, rels->file, "_rels/.rels");
  633. fclose(rels->file);
  634. mem_error:
  635. lxw_free_relationships(rels);
  636. return err;
  637. }
  638. /*****************************************************************************
  639. *
  640. * Public functions.
  641. *
  642. ****************************************************************************/
  643. STATIC lxw_error
  644. _add_file_to_zip(lxw_packager *self, FILE * file, const char *filename)
  645. {
  646. int16_t error = ZIP_OK;
  647. size_t size_read;
  648. error = zipOpenNewFileInZip4_64(self->zipfile,
  649. filename,
  650. &self->zipfile_info,
  651. NULL, 0, NULL, 0, NULL,
  652. Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0,
  653. -MAX_WBITS, DEF_MEM_LEVEL,
  654. Z_DEFAULT_STRATEGY, NULL, 0, 0, 0, 0);
  655. if (error != ZIP_OK) {
  656. LXW_ERROR("Error adding member to zipfile");
  657. RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
  658. }
  659. fflush(file);
  660. rewind(file);
  661. size_read = fread(self->buffer, 1, self->buffer_size, file);
  662. while (size_read) {
  663. if (size_read < self->buffer_size) {
  664. if (feof(file) == 0) {
  665. LXW_ERROR("Error reading member file data");
  666. RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
  667. }
  668. }
  669. error = zipWriteInFileInZip(self->zipfile,
  670. self->buffer, (unsigned int) size_read);
  671. if (error < 0) {
  672. LXW_ERROR("Error in writing member in the zipfile");
  673. RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
  674. }
  675. size_read = fread(self->buffer, 1, self->buffer_size, file);
  676. }
  677. if (error < 0) {
  678. RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
  679. }
  680. else {
  681. error = zipCloseFileInZip(self->zipfile);
  682. if (error != ZIP_OK) {
  683. LXW_ERROR("Error in closing member in the zipfile");
  684. RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
  685. }
  686. }
  687. return LXW_NO_ERROR;
  688. }
  689. /*
  690. * Write the xml files that make up the XLXS OPC package.
  691. */
  692. lxw_error
  693. lxw_create_package(lxw_packager *self)
  694. {
  695. lxw_error error;
  696. int8_t zip_error;
  697. error = _write_worksheet_files(self);
  698. RETURN_ON_ERROR(error);
  699. error = _write_workbook_file(self);
  700. RETURN_ON_ERROR(error);
  701. error = _write_chart_files(self);
  702. RETURN_ON_ERROR(error);
  703. error = _write_drawing_files(self);
  704. RETURN_ON_ERROR(error);
  705. error = _write_shared_strings_file(self);
  706. RETURN_ON_ERROR(error);
  707. error = _write_app_file(self);
  708. RETURN_ON_ERROR(error);
  709. error = _write_core_file(self);
  710. RETURN_ON_ERROR(error);
  711. error = _write_custom_file(self);
  712. RETURN_ON_ERROR(error);
  713. error = _write_theme_file(self);
  714. RETURN_ON_ERROR(error);
  715. error = _write_styles_file(self);
  716. RETURN_ON_ERROR(error);
  717. error = _write_content_types_file(self);
  718. RETURN_ON_ERROR(error);
  719. error = _write_workbook_rels_file(self);
  720. RETURN_ON_ERROR(error);
  721. error = _write_worksheet_rels_file(self);
  722. RETURN_ON_ERROR(error);
  723. error = _write_drawing_rels_file(self);
  724. RETURN_ON_ERROR(error);
  725. error = _write_image_files(self);
  726. RETURN_ON_ERROR(error);
  727. error = _write_root_rels_file(self);
  728. RETURN_ON_ERROR(error);
  729. zip_error = zipClose(self->zipfile, NULL);
  730. if (zip_error) {
  731. RETURN_ON_ZIP_ERROR(zip_error, LXW_ERROR_ZIP_CLOSE);
  732. }
  733. return LXW_NO_ERROR;
  734. }