From 0ca88e430aec488c35a5d1348faae60bbf4ad15f Mon Sep 17 00:00:00 2001 From: Jeroen Date: Mon, 21 Aug 2017 19:22:15 +0200 Subject: [PATCH] first commit --- .Rbuildignore | 4 + .gitignore | 6 + DESCRIPTION | 15 + LICENSE | 2 + NAMESPACE | 6 + R/version.R | 11 + R/write_xlsx.R | 30 + man/write_xlsx.Rd | 23 + man/writexl.Rd | 11 + src/Makevars | 24 + src/include/xlsxwriter.h | 23 + src/include/xlsxwriter/app.h | 79 + src/include/xlsxwriter/chart.h | 3477 +++++++++ src/include/xlsxwriter/common.h | 372 + src/include/xlsxwriter/content_types.h | 74 + src/include/xlsxwriter/core.h | 51 + src/include/xlsxwriter/custom.h | 52 + src/include/xlsxwriter/drawing.h | 111 + src/include/xlsxwriter/format.h | 1214 ++++ src/include/xlsxwriter/hash_table.h | 76 + src/include/xlsxwriter/packager.h | 85 + src/include/xlsxwriter/relationships.h | 77 + src/include/xlsxwriter/shared_strings.h | 83 + src/include/xlsxwriter/styles.h | 77 + src/include/xlsxwriter/theme.h | 47 + src/include/xlsxwriter/third_party/ioapi.h | 214 + src/include/xlsxwriter/third_party/queue.h | 694 ++ .../xlsxwriter/third_party/tmpfileplus.h | 53 + src/include/xlsxwriter/third_party/tree.h | 801 +++ src/include/xlsxwriter/third_party/zip.h | 375 + src/include/xlsxwriter/utility.h | 166 + src/include/xlsxwriter/workbook.h | 751 ++ src/include/xlsxwriter/worksheet.h | 2641 +++++++ src/include/xlsxwriter/xmlwriter.h | 178 + src/libxlsxwriter/app.c | 443 ++ src/libxlsxwriter/chart.c | 6347 +++++++++++++++++ src/libxlsxwriter/content_types.c | 345 + src/libxlsxwriter/core.c | 293 + src/libxlsxwriter/custom.c | 224 + src/libxlsxwriter/drawing.c | 746 ++ src/libxlsxwriter/format.c | 728 ++ src/libxlsxwriter/hash_table.c | 223 + src/libxlsxwriter/packager.c | 962 +++ src/libxlsxwriter/relationships.c | 245 + src/libxlsxwriter/shared_strings.c | 266 + src/libxlsxwriter/styles.c | 1088 +++ src/libxlsxwriter/theme.c | 348 + src/libxlsxwriter/utility.c | 515 ++ src/libxlsxwriter/workbook.c | 1911 +++++ src/libxlsxwriter/worksheet.c | 5024 +++++++++++++ src/libxlsxwriter/xmlwriter.c | 355 + src/minizip/crypt.h | 131 + src/minizip/ioapi.c | 247 + src/minizip/ioapi.h | 208 + src/minizip/iowin32.c | 456 ++ src/minizip/iowin32.h | 28 + src/minizip/miniunz.c | 660 ++ src/minizip/minizip.c | 520 ++ src/minizip/mztools.c | 291 + src/minizip/mztools.h | 37 + src/minizip/unzip.c | 2125 ++++++ src/minizip/unzip.h | 437 ++ src/minizip/zip.c | 2007 ++++++ src/minizip/zip.h | 367 + src/tmpfileplus/tmpfileplus.c | 342 + src/tmpfileplus/tmpfileplus.h | 53 + src/write_xlsx.c | 100 + writexl.Rproj | 20 + 68 files changed, 39995 insertions(+) create mode 100644 .Rbuildignore create mode 100644 .gitignore create mode 100644 DESCRIPTION create mode 100644 LICENSE create mode 100644 NAMESPACE create mode 100644 R/version.R create mode 100644 R/write_xlsx.R create mode 100644 man/write_xlsx.Rd create mode 100644 man/writexl.Rd create mode 100644 src/Makevars create mode 100644 src/include/xlsxwriter.h create mode 100644 src/include/xlsxwriter/app.h create mode 100644 src/include/xlsxwriter/chart.h create mode 100644 src/include/xlsxwriter/common.h create mode 100644 src/include/xlsxwriter/content_types.h create mode 100644 src/include/xlsxwriter/core.h create mode 100644 src/include/xlsxwriter/custom.h create mode 100644 src/include/xlsxwriter/drawing.h create mode 100644 src/include/xlsxwriter/format.h create mode 100644 src/include/xlsxwriter/hash_table.h create mode 100644 src/include/xlsxwriter/packager.h create mode 100644 src/include/xlsxwriter/relationships.h create mode 100644 src/include/xlsxwriter/shared_strings.h create mode 100644 src/include/xlsxwriter/styles.h create mode 100644 src/include/xlsxwriter/theme.h create mode 100644 src/include/xlsxwriter/third_party/ioapi.h create mode 100644 src/include/xlsxwriter/third_party/queue.h create mode 100644 src/include/xlsxwriter/third_party/tmpfileplus.h create mode 100644 src/include/xlsxwriter/third_party/tree.h create mode 100644 src/include/xlsxwriter/third_party/zip.h create mode 100644 src/include/xlsxwriter/utility.h create mode 100644 src/include/xlsxwriter/workbook.h create mode 100644 src/include/xlsxwriter/worksheet.h create mode 100644 src/include/xlsxwriter/xmlwriter.h create mode 100644 src/libxlsxwriter/app.c create mode 100644 src/libxlsxwriter/chart.c create mode 100644 src/libxlsxwriter/content_types.c create mode 100644 src/libxlsxwriter/core.c create mode 100644 src/libxlsxwriter/custom.c create mode 100644 src/libxlsxwriter/drawing.c create mode 100644 src/libxlsxwriter/format.c create mode 100644 src/libxlsxwriter/hash_table.c create mode 100644 src/libxlsxwriter/packager.c create mode 100644 src/libxlsxwriter/relationships.c create mode 100644 src/libxlsxwriter/shared_strings.c create mode 100644 src/libxlsxwriter/styles.c create mode 100644 src/libxlsxwriter/theme.c create mode 100644 src/libxlsxwriter/utility.c create mode 100644 src/libxlsxwriter/workbook.c create mode 100644 src/libxlsxwriter/worksheet.c create mode 100644 src/libxlsxwriter/xmlwriter.c create mode 100644 src/minizip/crypt.h create mode 100644 src/minizip/ioapi.c create mode 100644 src/minizip/ioapi.h create mode 100644 src/minizip/iowin32.c create mode 100644 src/minizip/iowin32.h create mode 100644 src/minizip/miniunz.c create mode 100644 src/minizip/minizip.c create mode 100644 src/minizip/mztools.c create mode 100644 src/minizip/mztools.h create mode 100644 src/minizip/unzip.c create mode 100644 src/minizip/unzip.h create mode 100644 src/minizip/zip.c create mode 100644 src/minizip/zip.h create mode 100644 src/tmpfileplus/tmpfileplus.c create mode 100644 src/tmpfileplus/tmpfileplus.h create mode 100644 src/write_xlsx.c create mode 100644 writexl.Rproj diff --git a/.Rbuildignore b/.Rbuildignore new file mode 100644 index 0000000..518bf1a --- /dev/null +++ b/.Rbuildignore @@ -0,0 +1,4 @@ +^.*\.Rproj$ +^\.Rproj\.user$ +^src/.*\.a$ +^src/.*\.o$ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e09eceb --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.Rproj.user +.Rhistory +.RData +*.so +*.o +*.a diff --git a/DESCRIPTION b/DESCRIPTION new file mode 100644 index 0000000..a3f59a9 --- /dev/null +++ b/DESCRIPTION @@ -0,0 +1,15 @@ +Package: writexl +Type: Package +Title: Export R Data Frames to xlsx Format +Version: 0.1 +Authors@R: c( + person("Jeroen", "Ooms", ,"jeroen@berkeley.edu", role = c("aut", "cre")), + person("John McNamara", role = "cph", comment = "Author of libxlsxwriter")) +Description: Light weight data frame to xlsx exporter based on libxlsxwriter. + No Java or Excel required. +License: MIT + file LICENSE +Encoding: UTF-8 +LazyData: true +URL: https://libxlsxwriter.github.io (upstream) +RoxygenNote: 6.0.1 +Suggests: readxl diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7bade8c --- /dev/null +++ b/LICENSE @@ -0,0 +1,2 @@ +YEAR: 2017 +COPYRIGHT HOLDER: Jeroen Ooms diff --git a/NAMESPACE b/NAMESPACE new file mode 100644 index 0000000..ae09aa2 --- /dev/null +++ b/NAMESPACE @@ -0,0 +1,6 @@ +# Generated by roxygen2: do not edit by hand + +export(lxw_version) +export(write_xlsx) +useDynLib(writexl,C_lxw_version) +useDynLib(writexl,C_write_data_frame) diff --git a/R/version.R b/R/version.R new file mode 100644 index 0000000..d26e27f --- /dev/null +++ b/R/version.R @@ -0,0 +1,11 @@ +#' Version +#' +#' Shows version of bundled libxlsxwriter. +#' +#' @export +#' @rdname writexl +#' @useDynLib writexl C_lxw_version +lxw_version <- function(){ + version <- .Call(C_lxw_version) + as.numeric_version(version) +} diff --git a/R/write_xlsx.R b/R/write_xlsx.R new file mode 100644 index 0000000..9b16d64 --- /dev/null +++ b/R/write_xlsx.R @@ -0,0 +1,30 @@ +#' Export to xlsx +#' +#' Writes a data frame to an xlsx file. +#' +#' @export +#' @aliases writexl +#' @useDynLib writexl C_write_data_frame +#' @param x data frame to write to disk +#' @param path a file name to write to +#' @param col_names write column names at the top of the file? +#' @examples tmp <- write_xlsx(iris) +#' readxl::read_xlsx(tmp) +write_xlsx <- function(x, path = tempfile(fileext = ".xlsx"), col_names = TRUE){ + stopifnot(is.data.frame(x)) + stopifnot(is.character(path)) + df <- normalize_df(x) + headers <- if(isTRUE(col_names)) + colnames(x) + .Call(C_write_data_frame, df, path, headers) +} + +normalize_df <- function(df){ + for(i in which(vapply(df, is.factor, logical(1)))){ + df[[i]] <- as.character(df[[i]]) + } + for(i in which(vapply(df, inherits, logical(1), "POSIXlt"))){ + df[[i]] <- as.POSIXct(df[[i]]) + } + df +} diff --git a/man/write_xlsx.Rd b/man/write_xlsx.Rd new file mode 100644 index 0000000..7ebd8bf --- /dev/null +++ b/man/write_xlsx.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/write_xlsx.R +\name{write_xlsx} +\alias{write_xlsx} +\alias{writexl} +\title{Export to xlsx} +\usage{ +write_xlsx(x, path = tempfile(fileext = ".xlsx"), col_names = TRUE) +} +\arguments{ +\item{x}{data frame to write to disk} + +\item{path}{a file name to write to} + +\item{col_names}{write column names at the top of the file?} +} +\description{ +Writes a data frame to an xlsx file. +} +\examples{ +tmp <- write_xlsx(iris) +readxl::read_xlsx(tmp) +} diff --git a/man/writexl.Rd b/man/writexl.Rd new file mode 100644 index 0000000..8812e6e --- /dev/null +++ b/man/writexl.Rd @@ -0,0 +1,11 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/version.R +\name{lxw_version} +\alias{lxw_version} +\title{Version} +\usage{ +lxw_version() +} +\description{ +Shows version of bundled libxlsxwriter. +} diff --git a/src/Makevars b/src/Makevars new file mode 100644 index 0000000..d63eaf8 --- /dev/null +++ b/src/Makevars @@ -0,0 +1,24 @@ +LIBXLSXWRITER = \ + libxlsxwriter/app.o libxlsxwriter/format.o libxlsxwriter/theme.o \ + libxlsxwriter/chart.o libxlsxwriter/hash_table.o libxlsxwriter/utility.o \ + libxlsxwriter/content_types.o libxlsxwriter/packager.o libxlsxwriter/workbook.o \ + libxlsxwriter/core.o libxlsxwriter/relationships.o libxlsxwriter/worksheet.o \ + libxlsxwriter/custom.o libxlsxwriter/shared_strings.o libxlsxwriter/xmlwriter.o \ + libxlsxwriter/drawing.o libxlsxwriter/styles.o tmpfileplus/tmpfileplus.o \ + minizip/ioapi.o minizip/mztools.o minizip/zip.o minizip/unzip.o + +STATICLIB=libxlsxwriter/libstatxlsxwriter.a + +PKG_CFLAGS=$(C_VISIBILITY) +PKG_CPPFLAGS=-Iinclude +PKG_LIBS=-Llibxlsxwriter -lstatxlsxwriter -lz + +all: clean + +$(SHLIB): $(STATICLIB) + +$(STATICLIB): $(LIBXLSXWRITER) + $(AR) rcs $(STATICLIB) $(LIBXLSXWRITER) + +clean: + rm -f $(SHLIB) $(STATICLIB) $(OBJECTS) $(LIBXLSXWRITER) diff --git a/src/include/xlsxwriter.h b/src/include/xlsxwriter.h new file mode 100644 index 0000000..2ece327 --- /dev/null +++ b/src/include/xlsxwriter.h @@ -0,0 +1,23 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + */ + +/** + * @file xlsxwriter.h + * + * xlsxwriter - A library for creating Excel XLSX files. + * + */ +#ifndef __LXW_XLSXWRITER_H__ +#define __LXW_XLSXWRITER_H__ + +#include "xlsxwriter/workbook.h" +#include "xlsxwriter/worksheet.h" +#include "xlsxwriter/format.h" +#include "xlsxwriter/utility.h" + +#define LXW_VERSION "0.7.4" + +#endif /* __LXW_XLSXWRITER_H__ */ diff --git a/src/include/xlsxwriter/app.h b/src/include/xlsxwriter/app.h new file mode 100644 index 0000000..27cb4ff --- /dev/null +++ b/src/include/xlsxwriter/app.h @@ -0,0 +1,79 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * app - A libxlsxwriter library for creating Excel XLSX app files. + * + */ +#ifndef __LXW_APP_H__ +#define __LXW_APP_H__ + +#include +#include +#include "workbook.h" +#include "common.h" + +/* Define the queue.h TAILQ structs for the App structs. */ +STAILQ_HEAD(lxw_heading_pairs, lxw_heading_pair); +STAILQ_HEAD(lxw_part_names, lxw_part_name); + +typedef struct lxw_heading_pair { + + char *key; + char *value; + + STAILQ_ENTRY (lxw_heading_pair) list_pointers; + +} lxw_heading_pair; + +typedef struct lxw_part_name { + + char *name; + + STAILQ_ENTRY (lxw_part_name) list_pointers; + +} lxw_part_name; + +/* Struct to represent an App object. */ +typedef struct lxw_app { + + FILE *file; + + struct lxw_heading_pairs *heading_pairs; + struct lxw_part_names *part_names; + lxw_doc_properties *properties; + + uint32_t num_heading_pairs; + uint32_t num_part_names; + +} lxw_app; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_app *lxw_app_new(); +void lxw_app_free(lxw_app *app); +void lxw_app_assemble_xml_file(lxw_app *self); +void lxw_app_add_part_name(lxw_app *self, const char *name); +void lxw_app_add_heading_pair(lxw_app *self, const char *key, + const char *value); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _app_xml_declaration(lxw_app *self); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_APP_H__ */ diff --git a/src/include/xlsxwriter/chart.h b/src/include/xlsxwriter/chart.h new file mode 100644 index 0000000..e3f16c3 --- /dev/null +++ b/src/include/xlsxwriter/chart.h @@ -0,0 +1,3477 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * chart - A libxlsxwriter library for creating Excel XLSX chart files. + * + */ + +/** + * @page chart_page The Chart object + * + * The Chart object represents an Excel chart. It provides functions for + * adding data series to the chart and for configuring the chart. + * + * See @ref chart.h for full details of the functionality. + * + * @file chart.h + * + * @brief Functions related to adding data to and configuring a chart. + * + * The Chart object represents an Excel chart. It provides functions for + * adding data series to the chart and for configuring the chart. + * + * A Chart object isn't created directly. Instead a chart is created by + * calling the `workbook_add_chart()` function from a Workbook object. For + * example: + * + * @code + * + * #include "xlsxwriter.h" + * + * int main() { + * + * lxw_workbook *workbook = new_workbook("chart.xlsx"); + * lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + * + * // User function to add data to worksheet, not shown here. + * write_worksheet_data(worksheet); + * + * // Create a chart object. + * lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_COLUMN); + * + * // In the simplest case we just add some value data series. + * // The NULL categories will default to 1 to 5 like in Excel. + * chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5"); + * chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5"); + * chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5"); + * + * // Insert the chart into the worksheet + * worksheet_insert_chart(worksheet, CELL("B7"), chart); + * + * return workbook_close(workbook); + * } + * + * @endcode + * + * The chart in the worksheet will look like this: + * @image html chart_simple.png + * + * The basic procedure for adding a chart to a worksheet is: + * + * 1. Create the chart with `workbook_add_chart()`. + * 2. Add one or more data series to the chart which refers to data in the + * workbook using `chart_add_series()`. + * 3. Configure the chart with the other available functions shown below. + * 4. Insert the chart into a worksheet using `worksheet_insert_chart()`. + * + */ + +#ifndef __LXW_CHART_H__ +#define __LXW_CHART_H__ + +#include +#include + +#include "common.h" +#include "format.h" + +STAILQ_HEAD(lxw_chart_series_list, lxw_chart_series); +STAILQ_HEAD(lxw_series_data_points, lxw_series_data_point); + +#define LXW_CHART_NUM_FORMAT_LEN 128 +#define LXW_CHART_DEFAULT_GAP 501 + +/** + * @brief Available chart types. + */ +typedef enum lxw_chart_type { + + /** None. */ + LXW_CHART_NONE = 0, + + /** Area chart. */ + LXW_CHART_AREA, + + /** Area chart - stacked. */ + LXW_CHART_AREA_STACKED, + + /** Area chart - percentage stacked. */ + LXW_CHART_AREA_STACKED_PERCENT, + + /** Bar chart. */ + LXW_CHART_BAR, + + /** Bar chart - stacked. */ + LXW_CHART_BAR_STACKED, + + /** Bar chart - percentage stacked. */ + LXW_CHART_BAR_STACKED_PERCENT, + + /** Column chart. */ + LXW_CHART_COLUMN, + + /** Column chart - stacked. */ + LXW_CHART_COLUMN_STACKED, + + /** Column chart - percentage stacked. */ + LXW_CHART_COLUMN_STACKED_PERCENT, + + /** Doughnut chart. */ + LXW_CHART_DOUGHNUT, + + /** Line chart. */ + LXW_CHART_LINE, + + /** Pie chart. */ + LXW_CHART_PIE, + + /** Scatter chart. */ + LXW_CHART_SCATTER, + + /** Scatter chart - straight. */ + LXW_CHART_SCATTER_STRAIGHT, + + /** Scatter chart - straight with markers. */ + LXW_CHART_SCATTER_STRAIGHT_WITH_MARKERS, + + /** Scatter chart - smooth. */ + LXW_CHART_SCATTER_SMOOTH, + + /** Scatter chart - smooth with markers. */ + LXW_CHART_SCATTER_SMOOTH_WITH_MARKERS, + + /** Radar chart. */ + LXW_CHART_RADAR, + + /** Radar chart - with markers. */ + LXW_CHART_RADAR_WITH_MARKERS, + + /** Radar chart - filled. */ + LXW_CHART_RADAR_FILLED +} lxw_chart_type; + +/** + * @brief Chart legend positions. + */ +typedef enum lxw_chart_legend_position { + + /** No chart legend. */ + LXW_CHART_LEGEND_NONE = 0, + + /** Chart legend positioned at right side. */ + LXW_CHART_LEGEND_RIGHT, + + /** Chart legend positioned at left side. */ + LXW_CHART_LEGEND_LEFT, + + /** Chart legend positioned at top. */ + LXW_CHART_LEGEND_TOP, + + /** Chart legend positioned at bottom. */ + LXW_CHART_LEGEND_BOTTOM, + + /** Chart legend overlaid at right side. */ + LXW_CHART_LEGEND_OVERLAY_RIGHT, + + /** Chart legend overlaid at left side. */ + LXW_CHART_LEGEND_OVERLAY_LEFT +} lxw_chart_legend_position; + +/** + * @brief Chart line dash types. + * + * The dash types are shown in the order that they appear in the Excel dialog. + * See @ref chart_lines. + */ +typedef enum lxw_chart_line_dash_type { + + /** Solid. */ + LXW_CHART_LINE_DASH_SOLID = 0, + + /** Round Dot. */ + LXW_CHART_LINE_DASH_ROUND_DOT, + + /** Square Dot. */ + LXW_CHART_LINE_DASH_SQUARE_DOT, + + /** Dash. */ + LXW_CHART_LINE_DASH_DASH, + + /** Dash Dot. */ + LXW_CHART_LINE_DASH_DASH_DOT, + + /** Long Dash. */ + LXW_CHART_LINE_DASH_LONG_DASH, + + /** Long Dash Dot. */ + LXW_CHART_LINE_DASH_LONG_DASH_DOT, + + /** Long Dash Dot Dot. */ + LXW_CHART_LINE_DASH_LONG_DASH_DOT_DOT, + + /* These aren't available in the dialog but are used by Excel. */ + LXW_CHART_LINE_DASH_DOT, + LXW_CHART_LINE_DASH_SYSTEM_DASH_DOT, + LXW_CHART_LINE_DASH_SYSTEM_DASH_DOT_DOT +} lxw_chart_line_dash_type; + +/** + * @brief Chart marker types. + */ +typedef enum lxw_chart_marker_type { + + /** Automatic, series default, marker type. */ + LXW_CHART_MARKER_AUTOMATIC, + + /** No marker type. */ + LXW_CHART_MARKER_NONE, + + /** Square marker type. */ + LXW_CHART_MARKER_SQUARE, + + /** Diamond marker type. */ + LXW_CHART_MARKER_DIAMOND, + + /** Triangle marker type. */ + LXW_CHART_MARKER_TRIANGLE, + + /** X shape marker type. */ + LXW_CHART_MARKER_X, + + /** Star marker type. */ + LXW_CHART_MARKER_STAR, + + /** Short dash marker type. */ + LXW_CHART_MARKER_SHORT_DASH, + + /** Long dash marker type. */ + LXW_CHART_MARKER_LONG_DASH, + + /** Circle marker type. */ + LXW_CHART_MARKER_CIRCLE, + + /** Plus (+) marker type. */ + LXW_CHART_MARKER_PLUS +} lxw_chart_marker_type; + +/** + * @brief Chart pattern types. + */ +typedef enum lxw_chart_pattern_type { + + /** None pattern. */ + LXW_CHART_PATTERN_NONE, + + /** 5 Percent pattern. */ + LXW_CHART_PATTERN_PERCENT_5, + + /** 10 Percent pattern. */ + LXW_CHART_PATTERN_PERCENT_10, + + /** 20 Percent pattern. */ + LXW_CHART_PATTERN_PERCENT_20, + + /** 25 Percent pattern. */ + LXW_CHART_PATTERN_PERCENT_25, + + /** 30 Percent pattern. */ + LXW_CHART_PATTERN_PERCENT_30, + + /** 40 Percent pattern. */ + LXW_CHART_PATTERN_PERCENT_40, + + /** 50 Percent pattern. */ + LXW_CHART_PATTERN_PERCENT_50, + + /** 60 Percent pattern. */ + LXW_CHART_PATTERN_PERCENT_60, + + /** 70 Percent pattern. */ + LXW_CHART_PATTERN_PERCENT_70, + + /** 75 Percent pattern. */ + LXW_CHART_PATTERN_PERCENT_75, + + /** 80 Percent pattern. */ + LXW_CHART_PATTERN_PERCENT_80, + + /** 90 Percent pattern. */ + LXW_CHART_PATTERN_PERCENT_90, + + /** Light downward diagonal pattern. */ + LXW_CHART_PATTERN_LIGHT_DOWNWARD_DIAGONAL, + + /** Light upward diagonal pattern. */ + LXW_CHART_PATTERN_LIGHT_UPWARD_DIAGONAL, + + /** Dark downward diagonal pattern. */ + LXW_CHART_PATTERN_DARK_DOWNWARD_DIAGONAL, + + /** Dark upward diagonal pattern. */ + LXW_CHART_PATTERN_DARK_UPWARD_DIAGONAL, + + /** Wide downward diagonal pattern. */ + LXW_CHART_PATTERN_WIDE_DOWNWARD_DIAGONAL, + + /** Wide upward diagonal pattern. */ + LXW_CHART_PATTERN_WIDE_UPWARD_DIAGONAL, + + /** Light vertical pattern. */ + LXW_CHART_PATTERN_LIGHT_VERTICAL, + + /** Light horizontal pattern. */ + LXW_CHART_PATTERN_LIGHT_HORIZONTAL, + + /** Narrow vertical pattern. */ + LXW_CHART_PATTERN_NARROW_VERTICAL, + + /** Narrow horizontal pattern. */ + LXW_CHART_PATTERN_NARROW_HORIZONTAL, + + /** Dark vertical pattern. */ + LXW_CHART_PATTERN_DARK_VERTICAL, + + /** Dark horizontal pattern. */ + LXW_CHART_PATTERN_DARK_HORIZONTAL, + + /** Dashed downward diagonal pattern. */ + LXW_CHART_PATTERN_DASHED_DOWNWARD_DIAGONAL, + + /** Dashed upward diagonal pattern. */ + LXW_CHART_PATTERN_DASHED_UPWARD_DIAGONAL, + + /** Dashed horizontal pattern. */ + LXW_CHART_PATTERN_DASHED_HORIZONTAL, + + /** Dashed vertical pattern. */ + LXW_CHART_PATTERN_DASHED_VERTICAL, + + /** Small confetti pattern. */ + LXW_CHART_PATTERN_SMALL_CONFETTI, + + /** Large confetti pattern. */ + LXW_CHART_PATTERN_LARGE_CONFETTI, + + /** Zigzag pattern. */ + LXW_CHART_PATTERN_ZIGZAG, + + /** Wave pattern. */ + LXW_CHART_PATTERN_WAVE, + + /** Diagonal brick pattern. */ + LXW_CHART_PATTERN_DIAGONAL_BRICK, + + /** Horizontal brick pattern. */ + LXW_CHART_PATTERN_HORIZONTAL_BRICK, + + /** Weave pattern. */ + LXW_CHART_PATTERN_WEAVE, + + /** Plaid pattern. */ + LXW_CHART_PATTERN_PLAID, + + /** Divot pattern. */ + LXW_CHART_PATTERN_DIVOT, + + /** Dotted grid pattern. */ + LXW_CHART_PATTERN_DOTTED_GRID, + + /** Dotted diamond pattern. */ + LXW_CHART_PATTERN_DOTTED_DIAMOND, + + /** Shingle pattern. */ + LXW_CHART_PATTERN_SHINGLE, + + /** Trellis pattern. */ + LXW_CHART_PATTERN_TRELLIS, + + /** Sphere pattern. */ + LXW_CHART_PATTERN_SPHERE, + + /** Small grid pattern. */ + LXW_CHART_PATTERN_SMALL_GRID, + + /** Large grid pattern. */ + LXW_CHART_PATTERN_LARGE_GRID, + + /** Small check pattern. */ + LXW_CHART_PATTERN_SMALL_CHECK, + + /** Large check pattern. */ + LXW_CHART_PATTERN_LARGE_CHECK, + + /** Outlined diamond pattern. */ + LXW_CHART_PATTERN_OUTLINED_DIAMOND, + + /** Solid diamond pattern. */ + LXW_CHART_PATTERN_SOLID_DIAMOND +} lxw_chart_pattern_type; + +/** + * @brief Chart data label positions. + */ +typedef enum lxw_chart_label_position { + /** Series data label position: default position. */ + LXW_CHART_LABEL_POSITION_DEFAULT, + + /** Series data label position: center. */ + LXW_CHART_LABEL_POSITION_CENTER, + + /** Series data label position: right. */ + LXW_CHART_LABEL_POSITION_RIGHT, + + /** Series data label position: left. */ + LXW_CHART_LABEL_POSITION_LEFT, + + /** Series data label position: above. */ + LXW_CHART_LABEL_POSITION_ABOVE, + + /** Series data label position: below. */ + LXW_CHART_LABEL_POSITION_BELOW, + + /** Series data label position: inside base. */ + LXW_CHART_LABEL_POSITION_INSIDE_BASE, + + /** Series data label position: inside end. */ + LXW_CHART_LABEL_POSITION_INSIDE_END, + + /** Series data label position: outside end. */ + LXW_CHART_LABEL_POSITION_OUTSIDE_END, + + /** Series data label position: best fit. */ + LXW_CHART_LABEL_POSITION_BEST_FIT +} lxw_chart_label_position; + +/** + * @brief Chart data label separator. + */ +typedef enum lxw_chart_label_separator { + /** Series data label separator: comma (the default). */ + LXW_CHART_LABEL_SEPARATOR_COMMA, + + /** Series data label separator: semicolon. */ + LXW_CHART_LABEL_SEPARATOR_SEMICOLON, + + /** Series data label separator: period. */ + LXW_CHART_LABEL_SEPARATOR_PERIOD, + + /** Series data label separator: newline. */ + LXW_CHART_LABEL_SEPARATOR_NEWLINE, + + /** Series data label separator: space. */ + LXW_CHART_LABEL_SEPARATOR_SPACE +} lxw_chart_label_separator; + +enum lxw_chart_subtype { + + LXW_CHART_SUBTYPE_NONE = 0, + LXW_CHART_SUBTYPE_STACKED, + LXW_CHART_SUBTYPE_STACKED_PERCENT +}; + +enum lxw_chart_grouping { + LXW_GROUPING_CLUSTERED, + LXW_GROUPING_STANDARD, + LXW_GROUPING_PERCENTSTACKED, + LXW_GROUPING_STACKED +}; + +/** + * @brief Axis positions for category axes. + */ +typedef enum lxw_chart_axis_tick_position { + + LXW_CHART_AXIS_POSITION_DEFAULT, + + /** Position category axis on tick marks. */ + LXW_CHART_AXIS_POSITION_ON_TICK, + + /** Position category axis between tick marks. */ + LXW_CHART_AXIS_POSITION_BETWEEN +} lxw_chart_axis_tick_position; + +/** + * @brief Axis label positions. + */ +typedef enum lxw_chart_axis_label_position { + + /** Position the axis labels next to the axis. The default. */ + LXW_CHART_AXIS_LABEL_POSITION_NEXT_TO, + + /** Position the axis labels at the top of the chart, for horizontal + * axes, or to the right for vertical axes.*/ + LXW_CHART_AXIS_LABEL_POSITION_HIGH, + + /** Position the axis labels at the bottom of the chart, for horizontal + * axes, or to the left for vertical axes.*/ + LXW_CHART_AXIS_LABEL_POSITION_LOW, + + /** Turn off the the axis labels. */ + LXW_CHART_AXIS_LABEL_POSITION_NONE +} lxw_chart_axis_label_position; + +/** + * @brief Display units for chart value axis. + */ +typedef enum lxw_chart_axis_display_unit { + + /** Axis display units: None. The default. */ + LXW_CHART_AXIS_UNITS_NONE, + + /** Axis display units: Hundreds. */ + LXW_CHART_AXIS_UNITS_HUNDREDS, + + /** Axis display units: Thousands. */ + LXW_CHART_AXIS_UNITS_THOUSANDS, + + /** Axis display units: Ten thousands. */ + LXW_CHART_AXIS_UNITS_TEN_THOUSANDS, + + /** Axis display units: Hundred thousands. */ + LXW_CHART_AXIS_UNITS_HUNDRED_THOUSANDS, + + /** Axis display units: Millions. */ + LXW_CHART_AXIS_UNITS_MILLIONS, + + /** Axis display units: Ten millions. */ + LXW_CHART_AXIS_UNITS_TEN_MILLIONS, + + /** Axis display units: Hundred millions. */ + LXW_CHART_AXIS_UNITS_HUNDRED_MILLIONS, + + /** Axis display units: Billions. */ + LXW_CHART_AXIS_UNITS_BILLIONS, + + /** Axis display units: Trillions. */ + LXW_CHART_AXIS_UNITS_TRILLIONS +} lxw_chart_axis_display_unit; + +/** + * @brief Tick mark types for an axis. + */ +typedef enum lxw_chart_axis_tick_mark { + + /** Default tick mark for the chart axis. Usually outside. */ + LXW_CHART_AXIS_TICK_MARK_DEFAULT, + + /** No tick mark for the axis. */ + LXW_CHART_AXIS_TICK_MARK_NONE, + + /** Tick mark inside the axis only. */ + LXW_CHART_AXIS_TICK_MARK_INSIDE, + + /** Tick mark outside the axis only. */ + LXW_CHART_AXIS_TICK_MARK_OUTSIDE, + + /** Tick mark inside and outside the axis. */ + LXW_CHART_AXIS_TICK_MARK_CROSSING +} lxw_chart_tick_mark; + +typedef struct lxw_series_range { + char *formula; + char *sheetname; + lxw_row_t first_row; + lxw_row_t last_row; + lxw_col_t first_col; + lxw_col_t last_col; + uint8_t ignore_cache; + + uint8_t has_string_cache; + uint16_t num_data_points; + struct lxw_series_data_points *data_cache; + +} lxw_series_range; + +typedef struct lxw_series_data_point { + uint8_t is_string; + double number; + char *string; + uint8_t no_data; + + STAILQ_ENTRY (lxw_series_data_point) list_pointers; + +} lxw_series_data_point; + +/** + * @brief Struct to represent a chart line. + * + * See @ref chart_lines. + */ +typedef struct lxw_chart_line { + + /** The chart font color. See @ref working_with_colors. */ + lxw_color_t color; + + /** Turn off/hide line. Set to 0 or 1.*/ + uint8_t none; + + /** Width of the line in increments of 0.25. Default is 2.25. */ + float width; + + /** The line dash type. See #lxw_chart_line_dash_type. */ + uint8_t dash_type; + + /* Transparency for lines isn't generally useful. Undocumented for now. */ + uint8_t transparency; + + /* Members for internal use only. */ + uint8_t has_color; + +} lxw_chart_line; + +/** + * @brief Struct to represent a chart fill. + * + * See @ref chart_fills. + */ +typedef struct lxw_chart_fill { + + /** The chart font color. See @ref working_with_colors. */ + lxw_color_t color; + + /** Turn off/hide line. Set to 0 or 1.*/ + uint8_t none; + + /** Set the transparency of the fill. 0 - 100. Default 0. */ + uint8_t transparency; + + /* Members for internal use only. */ + uint8_t has_color; + +} lxw_chart_fill; + +/** + * @brief Struct to represent a chart pattern. + * + * See @ref chart_patterns. + */ +typedef struct lxw_chart_pattern { + + /** The pattern foreground color. See @ref working_with_colors. */ + lxw_color_t fg_color; + + /** The pattern background color. See @ref working_with_colors. */ + lxw_color_t bg_color; + + /** The pattern type. See #lxw_chart_pattern_type. */ + uint8_t type; + + /* Members for internal use only. */ + uint8_t has_fg_color; + uint8_t has_bg_color; + +} lxw_chart_pattern; + +/** + * @brief Struct to represent a chart font. + * + * See @ref chart_fonts. + */ +typedef struct lxw_chart_font { + + /** The chart font name, such as "Arial" or "Calibri". */ + char *name; + + /** The chart font size. The default is 11. */ + double size; + + /** The chart font bold property. Set to 0 or 1. */ + uint8_t bold; + + /** The chart font italic property. Set to 0 or 1. */ + uint8_t italic; + + /** The chart font underline property. Set to 0 or 1. */ + uint8_t underline; + + /** The chart font rotation property. Range: -90 to 90. */ + int32_t rotation; + + /** The chart font color. See @ref working_with_colors. */ + lxw_color_t color; + + /* Members for internal use only. */ + uint8_t pitch_family; + uint8_t charset; + int8_t baseline; + uint8_t has_color; + +} lxw_chart_font; + +typedef struct lxw_chart_marker { + + uint8_t type; + uint8_t size; + lxw_chart_line *line; + lxw_chart_fill *fill; + lxw_chart_pattern *pattern; + +} lxw_chart_marker; + +typedef struct lxw_chart_legend { + + lxw_chart_font *font; + uint8_t position; + +} lxw_chart_legend; + +typedef struct lxw_chart_title { + + char *name; + lxw_row_t row; + lxw_col_t col; + lxw_chart_font *font; + uint8_t off; + uint8_t is_horizontal; + uint8_t ignore_cache; + + /* We use a range to hold the title formula properties even though it + * will only have 1 point in order to re-use similar functions.*/ + lxw_series_range *range; + + struct lxw_series_data_point data_point; + +} lxw_chart_title; + +/** + * @brief Struct to represent an Excel chart data point. + * + * The lxw_chart_point used to set the line, fill and pattern of one or more + * points in a chart data series. See @ref chart_points. + */ +typedef struct lxw_chart_point { + + /** The line/border for the chart point. See @ref chart_lines. */ + lxw_chart_line *line; + + /** The fill for the chart point. See @ref chart_fills. */ + lxw_chart_fill *fill; + + /** The pattern for the chart point. See @ref chart_patterns.*/ + lxw_chart_pattern *pattern; + +} lxw_chart_point; + +/** + * @brief Define how blank values are displayed in a chart. + */ +typedef enum lxw_chart_blank { + + /** Show empty chart cells as gaps in the data. The default. */ + LXW_CHART_BLANKS_AS_GAP, + + /** Show empty chart cells as zeros. */ + LXW_CHART_BLANKS_AS_ZERO, + + /** Show empty chart cells as connected. Only for charts with lines. */ + LXW_CHART_BLANKS_AS_CONNECTED +} lxw_chart_blank; + +enum lxw_chart_position { + LXW_CHART_AXIS_RIGHT, + LXW_CHART_AXIS_LEFT, + LXW_CHART_AXIS_TOP, + LXW_CHART_AXIS_BOTTOM +}; + +/** + * @brief Type/amount of data series error bar. + */ +typedef enum lxw_chart_error_bar_type { + /** Error bar type: Standard error. */ + LXW_CHART_ERROR_BAR_TYPE_STD_ERROR, + + /** Error bar type: Fixed value. */ + LXW_CHART_ERROR_BAR_TYPE_FIXED, + + /** Error bar type: Percentage. */ + LXW_CHART_ERROR_BAR_TYPE_PERCENTAGE, + + /** Error bar type: Standard deviation(s). */ + LXW_CHART_ERROR_BAR_TYPE_STD_DEV +} lxw_chart_error_bar_type; + +/** + * @brief Direction for a data series error bar. + */ +typedef enum lxw_chart_error_bar_direction { + + /** Error bar extends in both directions. The default. */ + LXW_CHART_ERROR_BAR_DIR_BOTH, + + /** Error bar extends in positive direction. */ + LXW_CHART_ERROR_BAR_DIR_PLUS, + + /** Error bar extends in negative direction. */ + LXW_CHART_ERROR_BAR_DIR_MINUS +} lxw_chart_error_bar_direction; + +/** + * @brief End cap styles for a data series error bar. + */ +typedef enum lxw_chart_error_bar_cap { + /** Flat end cap. The default. */ + LXW_CHART_ERROR_BAR_END_CAP, + + /** No end cap. */ + LXW_CHART_ERROR_BAR_NO_CAP +} lxw_chart_error_bar_cap; + +typedef struct lxw_series_error_bars { + uint8_t type; + uint8_t direction; + uint8_t endcap; + uint8_t has_value; + uint8_t is_set; + uint8_t is_x; + uint8_t chart_group; + double value; + lxw_chart_line *line; + +} lxw_series_error_bars; + +/** + * @brief Series trendline/regression types. + */ +typedef enum lxw_chart_trendline_type { + /** Trendline type: Linear. */ + LXW_CHART_TRENDLINE_TYPE_LINEAR, + + /** Trendline type: Logarithm. */ + LXW_CHART_TRENDLINE_TYPE_LOG, + + /** Trendline type: Polynomial. */ + LXW_CHART_TRENDLINE_TYPE_POLY, + + /** Trendline type: Power. */ + LXW_CHART_TRENDLINE_TYPE_POWER, + + /** Trendline type: Exponential. */ + LXW_CHART_TRENDLINE_TYPE_EXP, + + /** Trendline type: Moving Average. */ + LXW_CHART_TRENDLINE_TYPE_AVERAGE +} lxw_chart_trendline_type; + +/** + * @brief Struct to represent an Excel chart data series. + * + * The lxw_chart_series is created using the chart_add_series function. It is + * used in functions that modify a chart series but the members of the struct + * aren't modified directly. + */ +typedef struct lxw_chart_series { + + lxw_series_range *categories; + lxw_series_range *values; + lxw_chart_title title; + lxw_chart_line *line; + lxw_chart_fill *fill; + lxw_chart_pattern *pattern; + lxw_chart_marker *marker; + lxw_chart_point *points; + uint16_t point_count; + + uint8_t smooth; + uint8_t invert_if_negative; + + /* Data label parameters. */ + uint8_t has_labels; + uint8_t show_labels_value; + uint8_t show_labels_category; + uint8_t show_labels_name; + uint8_t show_labels_leader; + uint8_t show_labels_legend; + uint8_t show_labels_percent; + uint8_t label_position; + uint8_t label_separator; + uint8_t default_label_position; + char *label_num_format; + lxw_chart_font *label_font; + + lxw_series_error_bars *x_error_bars; + lxw_series_error_bars *y_error_bars; + + uint8_t has_trendline; + uint8_t has_trendline_forecast; + uint8_t has_trendline_equation; + uint8_t has_trendline_r_squared; + uint8_t has_trendline_intercept; + uint8_t trendline_type; + uint8_t trendline_value; + double trendline_forward; + double trendline_backward; + uint8_t trendline_value_type; + char *trendline_name; + lxw_chart_line *trendline_line; + double trendline_intercept; + + STAILQ_ENTRY (lxw_chart_series) list_pointers; + +} lxw_chart_series; + +/* Struct for major/minor axis gridlines. */ +typedef struct lxw_chart_gridline { + + uint8_t visible; + lxw_chart_line *line; + +} lxw_chart_gridline; + +/** + * @brief Struct to represent an Excel chart axis. + * + * The lxw_chart_axis struct is used in functions that modify a chart axis + * but the members of the struct aren't modified directly. + */ +typedef struct lxw_chart_axis { + + lxw_chart_title title; + + char *num_format; + char *default_num_format; + uint8_t source_linked; + + uint8_t major_tick_mark; + uint8_t minor_tick_mark; + uint8_t is_horizontal; + + lxw_chart_gridline major_gridlines; + lxw_chart_gridline minor_gridlines; + + lxw_chart_font *num_font; + lxw_chart_line *line; + lxw_chart_fill *fill; + lxw_chart_pattern *pattern; + + uint8_t is_category; + uint8_t is_date; + uint8_t is_value; + uint8_t axis_position; + uint8_t position_axis; + uint8_t label_position; + uint8_t hidden; + uint8_t reverse; + + uint8_t has_min; + double min; + uint8_t has_max; + double max; + + uint8_t has_major_unit; + double major_unit; + uint8_t has_minor_unit; + double minor_unit; + + uint16_t interval_unit; + uint16_t interval_tick; + + uint16_t log_base; + + uint8_t display_units; + uint8_t display_units_visible; + + uint8_t has_crossing; + uint8_t crossing_max; + double crossing; + +} lxw_chart_axis; + +/** + * @brief Struct to represent an Excel chart. + * + * The members of the lxw_chart struct aren't modified directly. Instead + * the chart properties are set by calling the functions shown in chart.h. + */ +typedef struct lxw_chart { + + FILE *file; + + uint8_t type; + uint8_t subtype; + uint16_t series_index; + + void (*write_chart_type) (struct lxw_chart *); + void (*write_plot_area) (struct lxw_chart *); + + /** + * A pointer to the chart x_axis object which can be used in functions + * that configures the X axis. + */ + lxw_chart_axis *x_axis; + + /** + * A pointer to the chart y_axis object which can be used in functions + * that configures the Y axis. + */ + lxw_chart_axis *y_axis; + + lxw_chart_title title; + + uint32_t id; + uint32_t axis_id_1; + uint32_t axis_id_2; + uint32_t axis_id_3; + uint32_t axis_id_4; + + uint8_t in_use; + uint8_t chart_group; + uint8_t cat_has_num_fmt; + + uint8_t has_horiz_cat_axis; + uint8_t has_horiz_val_axis; + + uint8_t style_id; + uint16_t rotation; + uint16_t hole_size; + + uint8_t no_title; + uint8_t has_overlap; + int8_t overlap_y1; + int8_t overlap_y2; + uint16_t gap_y1; + uint16_t gap_y2; + + uint8_t grouping; + uint8_t default_cross_between; + + lxw_chart_legend legend; + int16_t *delete_series; + uint16_t delete_series_count; + lxw_chart_marker *default_marker; + + lxw_chart_line *chartarea_line; + lxw_chart_fill *chartarea_fill; + lxw_chart_pattern *chartarea_pattern; + lxw_chart_line *plotarea_line; + lxw_chart_fill *plotarea_fill; + lxw_chart_pattern *plotarea_pattern; + + uint8_t has_drop_lines; + lxw_chart_line *drop_lines_line; + + uint8_t has_high_low_lines; + lxw_chart_line *high_low_lines_line; + + struct lxw_chart_series_list *series_list; + + uint8_t has_table; + uint8_t has_table_vertical; + uint8_t has_table_horizontal; + uint8_t has_table_outline; + uint8_t has_table_legend_keys; + lxw_chart_font *table_font; + + uint8_t show_blanks_as; + uint8_t show_hidden_data; + + uint8_t has_up_down_bars; + lxw_chart_line *up_bar_line; + lxw_chart_line *down_bar_line; + lxw_chart_fill *up_bar_fill; + lxw_chart_fill *down_bar_fill; + + uint8_t default_label_position; + + STAILQ_ENTRY (lxw_chart) ordered_list_pointers; + STAILQ_ENTRY (lxw_chart) list_pointers; + +} lxw_chart; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_chart *lxw_chart_new(uint8_t type); +void lxw_chart_free(lxw_chart *chart); +void lxw_chart_assemble_xml_file(lxw_chart *chart); + +/** + * @brief Add a data series to a chart. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param categories The range of categories in the data series. + * @param values The range of values in the data series. + * + * @return A lxw_chart_series object pointer. + * + * In Excel a chart **series** is a collection of information that defines + * which data is plotted such as the categories and values. It is also used to + * define the formatting for the data. + * + * For an libxlsxwriter chart object the `%chart_add_series()` function is + * used to set the categories and values of the series: + * + * @code + * chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7"); + * @endcode + * + * + * The series parameters are: + * + * - `categories`: This sets the chart category labels. The category is more + * or less the same as the X axis. In most Excel chart types the + * `categories` property is optional and the chart will just assume a + * sequential series from `1..n`: + * + * @code + * // The NULL category will default to 1 to 5 like in Excel. + * chart_add_series(chart, NULL, "Sheet1!$A$1:$A$5"); + * @endcode + * + * - `values`: This is the most important property of a series and is the + * only mandatory option for every chart object. This parameter links the + * chart with the worksheet data that it displays. + * + * The `categories` and `values` should be a string formula like + * `"=Sheet1!$A$2:$A$7"` in the same way it is represented in Excel. This is + * convenient when recreating a chart from an example in Excel but it is + * trickier to generate programmatically. For these cases you can set the + * `categories` and `values` to `NULL` and use the + * `chart_series_set_categories()` and `chart_series_set_values()` functions: + * + * @code + * lxw_chart_series *series = chart_add_series(chart, NULL, NULL); + * + * // Configure the series using a syntax that is easier to define programmatically. + * chart_series_set_categories(series, "Sheet1", 1, 0, 6, 0); // "=Sheet1!$A$2:$A$7" + * chart_series_set_values( series, "Sheet1", 1, 2, 6, 2); // "=Sheet1!$C$2:$C$7" + * @endcode + * + * As shown in the previous example the return value from + * `%chart_add_series()` is a lxw_chart_series pointer. This can be used in + * other functions that configure a series. + * + * + * More than one series can be added to a chart. The series numbering and + * order in the Excel chart will be the same as the order in which they are + * added in libxlsxwriter: + * + * @code + * chart_add_series(chart, NULL, "Sheet1!$A$1:$A$5"); + * chart_add_series(chart, NULL, "Sheet1!$B$1:$B$5"); + * chart_add_series(chart, NULL, "Sheet1!$C$1:$C$5"); + * @endcode + * + * It is also possible to specify non-contiguous ranges: + * + * @code + * chart_add_series( + * chart, + * "=(Sheet1!$A$1:$A$9,Sheet1!$A$14:$A$25)", + * "=(Sheet1!$B$1:$B$9,Sheet1!$B$14:$B$25)" + * ); + * @endcode + * + */ +lxw_chart_series *chart_add_series(lxw_chart *chart, + const char *categories, + const char *values); + +/** + * @brief Set a series "categories" range using row and column values. + * + * @param series A series object created via `chart_add_series()`. + * @param sheetname The name of the worksheet that contains the data range. + * @param first_row The first row of the range. (All zero indexed.) + * @param first_col The first column of the range. + * @param last_row The last row of the range. + * @param last_col The last col of the range. + * + * The `categories` and `values` of a chart data series are generally set + * using the `chart_add_series()` function and Excel range formulas like + * `"=Sheet1!$A$2:$A$7"`. + * + * The `%chart_series_set_categories()` function is an alternative method that + * is easier to generate programmatically. It requires that you set the + * `categories` and `values` parameters in `chart_add_series()`to `NULL` and + * then set them using row and column values in + * `chart_series_set_categories()` and `chart_series_set_values()`: + * + * @code + * lxw_chart_series *series = chart_add_series(chart, NULL, NULL); + * + * // Configure the series ranges programmatically. + * chart_series_set_categories(series, "Sheet1", 1, 0, 6, 0); // "=Sheet1!$A$2:$A$7" + * chart_series_set_values( series, "Sheet1", 1, 2, 6, 2); // "=Sheet1!$C$2:$C$7" + * @endcode + * + */ +void chart_series_set_categories(lxw_chart_series *series, + const char *sheetname, lxw_row_t first_row, + lxw_col_t first_col, lxw_row_t last_row, + lxw_col_t last_col); + +/** + * @brief Set a series "values" range using row and column values. + * + * @param series A series object created via `chart_add_series()`. + * @param sheetname The name of the worksheet that contains the data range. + * @param first_row The first row of the range. (All zero indexed.) + * @param first_col The first column of the range. + * @param last_row The last row of the range. + * @param last_col The last col of the range. + * + * The `categories` and `values` of a chart data series are generally set + * using the `chart_add_series()` function and Excel range formulas like + * `"=Sheet1!$A$2:$A$7"`. + * + * The `%chart_series_set_values()` function is an alternative method that is + * easier to generate programmatically. See the documentation for + * `chart_series_set_categories()` above. + */ +void chart_series_set_values(lxw_chart_series *series, const char *sheetname, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t last_row, lxw_col_t last_col); + +/** + * @brief Set the name of a chart series range. + * + * @param series A series object created via `chart_add_series()`. + * @param name The series name. + * + * The `%chart_series_set_name` function is used to set the name for a chart + * data series. The series name in Excel is displayed in the chart legend and + * in the formula bar. The name property is optional and if it isn't supplied + * it will default to `Series 1..n`. + * + * The function applies to a #lxw_chart_series object created using + * `chart_add_series()`: + * + * @code + * lxw_chart_series *series = chart_add_series(chart, NULL, "=Sheet1!$B$2:$B$7"); + * + * chart_series_set_name(series, "Quarterly budget data"); + * @endcode + * + * The name parameter can also be a formula such as `=Sheet1!$A$1` to point to + * a cell in the workbook that contains the name: + * + * @code + * lxw_chart_series *series = chart_add_series(chart, NULL, "=Sheet1!$B$2:$B$7"); + * + * chart_series_set_name(series, "=Sheet1!$B1$1"); + * @endcode + * + * See also the `chart_series_set_name_range()` function to see how to set the + * name formula programmatically. + */ +void chart_series_set_name(lxw_chart_series *series, const char *name); + +/** + * @brief Set a series name formula using row and column values. + * + * @param series A series object created via `chart_add_series()`. + * @param sheetname The name of the worksheet that contains the cell range. + * @param row The zero indexed row number of the range. + * @param col The zero indexed column number of the range. + * + * The `%chart_series_set_name_range()` function can be used to set a series + * name range and is an alternative to using `chart_series_set_name()` and a + * string formula: + * + * @code + * lxw_chart_series *series = chart_add_series(chart, NULL, "=Sheet1!$B$2:$B$7"); + * + * chart_series_set_name_range(series, "Sheet1", 0, 2); // "=Sheet1!$C$1" + * @endcode + */ +void chart_series_set_name_range(lxw_chart_series *series, + const char *sheetname, lxw_row_t row, + lxw_col_t col); +/** + * @brief Set the line properties for a chart series. + * + * @param series A series object created via `chart_add_series()`. + * @param line A #lxw_chart_line struct. + * + * Set the line/border properties of a chart series: + * + * @code + * lxw_chart_line line = {.color = LXW_COLOR_RED}; + * + * chart_series_set_line(series1, &line); + * chart_series_set_line(series2, &line); + * chart_series_set_line(series3, &line); + * @endcode + * + * @image html chart_series_set_line.png + * + * For more information see @ref chart_lines. + */ +void chart_series_set_line(lxw_chart_series *series, lxw_chart_line *line); + +/** + * @brief Set the fill properties for a chart series. + * + * @param series A series object created via `chart_add_series()`. + * @param fill A #lxw_chart_fill struct. + * + * Set the fill properties of a chart series: + * + * @code + * lxw_chart_fill fill1 = {.color = LXW_COLOR_RED}; + * lxw_chart_fill fill2 = {.color = LXW_COLOR_YELLOW}; + * lxw_chart_fill fill3 = {.color = LXW_COLOR_GREEN}; + * + * chart_series_set_fill(series1, &fill1); + * chart_series_set_fill(series2, &fill2); + * chart_series_set_fill(series3, &fill3); + * @endcode + * + * @image html chart_series_set_fill.png + * + * For more information see @ref chart_fills. + */ +void chart_series_set_fill(lxw_chart_series *series, lxw_chart_fill *fill); + +/** + * @brief Invert the fill color for negative series values. + * + * @param series A series object created via `chart_add_series()`. + * + * Invert the fill color for negative values. Usually only applicable to + * column and bar charts. + * + * @code + * chart_series_set_invert_if_negative(series); + * @endcode + * + */ +void chart_series_set_invert_if_negative(lxw_chart_series *series); + +/** + * @brief Set the pattern properties for a chart series. + * + * @param series A series object created via `chart_add_series()`. + * @param pattern A #lxw_chart_pattern struct. + * + * Set the pattern properties of a chart series: + * + * @code + * lxw_chart_pattern pattern1 = {.type = LXW_CHART_PATTERN_SHINGLE, + * .fg_color = 0x804000, + * .bg_color = 0XC68C53}; + * + * lxw_chart_pattern pattern2 = {.type = LXW_CHART_PATTERN_HORIZONTAL_BRICK, + * .fg_color = 0XB30000, + * .bg_color = 0XFF6666}; + * + * chart_series_set_pattern(series1, &pattern1); + * chart_series_set_pattern(series2, &pattern2); + * + * @endcode + * + * @image html chart_pattern.png + * + * For more information see #lxw_chart_pattern_type and @ref chart_patterns. + */ +void chart_series_set_pattern(lxw_chart_series *series, + lxw_chart_pattern *pattern); + +/** + * @brief Set the data marker type for a series. + * + * @param series A series object created via `chart_add_series()`. + * @param type The marker type, see #lxw_chart_marker_type. + * + * In Excel a chart marker is used to distinguish data points in a plotted + * series. In general only Line and Scatter and Radar chart types use + * markers. The libxlsxwriter chart types that can have markers are: + * + * - #LXW_CHART_LINE + * - #LXW_CHART_SCATTER + * - #LXW_CHART_SCATTER_STRAIGHT + * - #LXW_CHART_SCATTER_STRAIGHT_WITH_MARKERS + * - #LXW_CHART_SCATTER_SMOOTH + * - #LXW_CHART_SCATTER_SMOOTH_WITH_MARKERS + * - #LXW_CHART_RADAR + * - #LXW_CHART_RADAR_WITH_MARKERS + * + * The chart types with `MARKERS` in the name have markers with default colors + * and shapes turned on by default but it is possible using the various + * `chart_series_set_marker_xxx()` functions below to change these defaults. It + * is also possible to turn on an off markers. + * + * The `%chart_series_set_marker_type()` function is used to specify the + * type of the series marker: + * + * @code + * chart_series_set_marker_type(series, LXW_CHART_MARKER_DIAMOND); + * @endcode + * + * @image html chart_marker1.png + * + * The available marker types defined by #lxw_chart_marker_type are: + * + * - #LXW_CHART_MARKER_AUTOMATIC + * - #LXW_CHART_MARKER_NONE + * - #LXW_CHART_MARKER_SQUARE + * - #LXW_CHART_MARKER_DIAMOND + * - #LXW_CHART_MARKER_TRIANGLE + * - #LXW_CHART_MARKER_X + * - #LXW_CHART_MARKER_STAR + * - #LXW_CHART_MARKER_SHORT_DASH + * - #LXW_CHART_MARKER_LONG_DASH + * - #LXW_CHART_MARKER_CIRCLE + * - #LXW_CHART_MARKER_PLUS + * + * The `#LXW_CHART_MARKER_NONE` type can be used to turn off default markers: + * + * @code + * chart_series_set_marker_type(series, LXW_CHART_MARKER_NONE); + * @endcode + * + * @image html chart_series_set_marker_none.png + * + * The `#LXW_CHART_MARKER_AUTOMATIC` type is a special case which turns on a + * marker using the default marker style for the particular series. If + * automatic is on then other marker properties such as size, line or fill + * cannot be set. + */ +void chart_series_set_marker_type(lxw_chart_series *series, uint8_t type); + +/** + * @brief Set the size of a data marker for a series. + * + * @param series A series object created via `chart_add_series()`. + * @param size The size of the marker. + * + * The `%chart_series_set_marker_size()` function is used to specify the + * size of the series marker: + * + * @code + * chart_series_set_marker_type(series, LXW_CHART_MARKER_CIRCLE); + * chart_series_set_marker_size(series, 10); + * @endcode + * + * @image html chart_series_set_marker_size.png + * + */ +void chart_series_set_marker_size(lxw_chart_series *series, uint8_t size); + +/** + * @brief Set the line properties for a chart series marker. + * + * @param series A series object created via `chart_add_series()`. + * @param line A #lxw_chart_line struct. + * + * Set the line/border properties of a chart marker: + * + * @code + * lxw_chart_line line = {.color = LXW_COLOR_BLACK}; + * lxw_chart_fill fill = {.color = LXW_COLOR_RED}; + * + * chart_series_set_marker_type(series, LXW_CHART_MARKER_SQUARE); + * chart_series_set_marker_size(series, 8); + * + * chart_series_set_marker_line(series, &line); + * chart_series_set_marker_fill(series, &fill); + * @endcode + * + * @image html chart_marker2.png + * + * For more information see @ref chart_lines. + */ +void chart_series_set_marker_line(lxw_chart_series *series, + lxw_chart_line *line); + +/** + * @brief Set the fill properties for a chart series marker. + * + * @param series A series object created via `chart_add_series()`. + * @param fill A #lxw_chart_fill struct. + * + * Set the fill properties of a chart marker: + * + * @code + * chart_series_set_marker_fill(series, &fill); + * @endcode + * + * See the example and image above and also see @ref chart_fills. + */ +void chart_series_set_marker_fill(lxw_chart_series *series, + lxw_chart_fill *fill); + +/** + * @brief Set the pattern properties for a chart series marker. + * + * @param series A series object created via `chart_add_series()`. + * @param pattern A #lxw_chart_pattern struct. + * + * Set the pattern properties of a chart marker: + * + * @code + * chart_series_set_marker_pattern(series, &pattern); + * @endcode + * + * For more information see #lxw_chart_pattern_type and @ref chart_patterns. + */ +void chart_series_set_marker_pattern(lxw_chart_series *series, + lxw_chart_pattern *pattern); + +/** + * @brief Set the formatting for points in the series. + * + * @param series A series object created via `chart_add_series()`. + * @param points An NULL terminated array of #lxw_chart_point pointers. + * + * @return A #lxw_error. + * + * In general formatting is applied to an entire series in a chart. However, + * it is occasionally required to format individual points in a series. In + * particular this is required for Pie/Doughnut charts where each segment is + * represented by a point. + * + * @dontinclude chart_pie_colors.c + * @skip Add the data series + * @until chart_series_set_points + * + * @image html chart_points1.png + * + * @note The array of #lxw_chart_point pointers should be NULL terminated + * as shown in the example. + * + * For more details see @ref chart_points + */ +lxw_error chart_series_set_points(lxw_chart_series *series, + lxw_chart_point *points[]); + +/** + * @brief Smooth a line or scatter chart series. + * + * @param series A series object created via `chart_add_series()`. + * @param smooth Turn off/on the line smoothing. (0/1) + * + * The `chart_series_set_smooth()` function is used to set the smooth property + * of a line series. It is only applicable to the line and scatter chart + * types: + * + * @code + * chart_series_set_smooth(series2, LXW_TRUE); + * @endcode + * + * @image html chart_smooth.png + * + * + */ +void chart_series_set_smooth(lxw_chart_series *series, uint8_t smooth); + +/** + * @brief Add data labels to a chart series. + * + * @param series A series object created via `chart_add_series()`. + * + * The `%chart_series_set_labels()` function is used to turn on data labels + * for a chart series. Data labels indicate the values of the plotted data + * points. + * + * @code + * chart_series_set_labels(series); + * @endcode + * + * @image html chart_labels1.png + * + * By default data labels are displayed in Excel with only the values shown: + * + * @image html chart_labels2.png + * + * However, it is possible to configure other display options, as shown + * in the functions below. + * + * For more information see @ref chart_labels. + */ +void chart_series_set_labels(lxw_chart_series *series); + +/** + * @brief Set the display options for the labels of a data series. + * + * @param series A series object created via `chart_add_series()`. + * @param show_name Turn on/off the series name in the label caption. + * @param show_category Turn on/off the category name in the label caption. + * @param show_value Turn on/off the value in the label caption. + * + * The `%chart_series_set_labels_options()` function is used to set the + * parameters that are displayed in the series data label: + * + * @code + * chart_series_set_labels(series); + * chart_series_set_labels_options(series, LXW_TRUE, LXW_TRUE, LXW_TRUE); + * @endcode + * + * @image html chart_labels3.png + * + * For more information see @ref chart_labels. + */ +void chart_series_set_labels_options(lxw_chart_series *series, + uint8_t show_name, uint8_t show_category, + uint8_t show_value); + +/** + * @brief Set the separator for the data label captions. + * + * @param series A series object created via `chart_add_series()`. + * @param separator The separator for the data label options: + * #lxw_chart_label_separator. + * + * The `%chart_series_set_labels_separator()` function is used to change the + * separator between multiple data label items. The default options is a comma + * separator as shown in the previous example. + * + * The available options are: + * + * - #LXW_CHART_LABEL_SEPARATOR_SEMICOLON: semicolon separator. + * - #LXW_CHART_LABEL_SEPARATOR_PERIOD: a period (dot) separator. + * - #LXW_CHART_LABEL_SEPARATOR_NEWLINE: a newline separator. + * - #LXW_CHART_LABEL_SEPARATOR_SPACE: a space separator. + * + * For example: + * + * @code + * chart_series_set_labels(series); + * chart_series_set_labels_options(series, LXW_TRUE, LXW_TRUE, LXW_TRUE); + * chart_series_set_labels_separator(series, LXW_CHART_LABEL_SEPARATOR_NEWLINE); + * @endcode + * + * @image html chart_labels4.png + * + * For more information see @ref chart_labels. + */ +void chart_series_set_labels_separator(lxw_chart_series *series, + uint8_t separator); + +/** + * @brief Set the data label position for a series. + * + * @param series A series object created via `chart_add_series()`. + * @param position The data label position: #lxw_chart_label_position. + * + * The `%chart_series_set_labels_position()` function sets the position of + * the labels in the data series: + * + * @code + * chart_series_set_labels(series); + * chart_series_set_labels_position(series, LXW_CHART_LABEL_POSITION_ABOVE); + * @endcode + * + * @image html chart_labels5.png + * + * In Excel the allowable data label positions vary for different chart + * types. The allowable, and default, positions are: + * + * | Position | Line, Scatter | Bar, Column | Pie, Doughnut | Area, Radar | + * | :------------------------------------ | :------------ | :------------ | :------------ | :------------ | + * | #LXW_CHART_LABEL_POSITION_CENTER | Yes | Yes | Yes | Yes (default) | + * | #LXW_CHART_LABEL_POSITION_RIGHT | Yes (default) | | | | + * | #LXW_CHART_LABEL_POSITION_LEFT | Yes | | | | + * | #LXW_CHART_LABEL_POSITION_ABOVE | Yes | | | | + * | #LXW_CHART_LABEL_POSITION_BELOW | Yes | | | | + * | #LXW_CHART_LABEL_POSITION_INSIDE_BASE | | Yes | | | + * | #LXW_CHART_LABEL_POSITION_INSIDE_END | | Yes | Yes | | + * | #LXW_CHART_LABEL_POSITION_OUTSIDE_END | | Yes (default) | Yes | | + * | #LXW_CHART_LABEL_POSITION_BEST_FIT | | | Yes (default) | | + * + * + * For more information see @ref chart_labels. + */ +void chart_series_set_labels_position(lxw_chart_series *series, + uint8_t position); + +/** + * @brief Set leader lines for Pie and Doughnut charts. + * + * @param series A series object created via `chart_add_series()`. + * + * The `%chart_series_set_labels_leader_line()` function is used to turn on + * leader lines for the data label of a series. It is mainly used for pie + * or doughnut charts: + * + * @code + * chart_series_set_labels(series); + * chart_series_set_labels_leader_line(series); + * @endcode + * + * @note Even when leader lines are turned on they aren't automatically + * visible in Excel or XlsxWriter. Due to an Excel limitation + * (or design) leader lines only appear if the data label is moved + * manually or if the data labels are very close and need to be + * adjusted automatically. + * + * For more information see @ref chart_labels. + */ +void chart_series_set_labels_leader_line(lxw_chart_series *series); + +/** + * @brief Set the legend key for a data label in a chart series. + * + * @param series A series object created via `chart_add_series()`. + * + * The `%chart_series_set_labels_legend()` function is used to set the + * legend key for a data series: + * + * @code + * chart_series_set_labels(series); + * chart_series_set_labels_legend(series); + * @endcode + * + * @image html chart_labels6.png + * + * For more information see @ref chart_labels. + */ +void chart_series_set_labels_legend(lxw_chart_series *series); + +/** + * @brief Set the percentage for a Pie/Doughnut data point. + * + * @param series A series object created via `chart_add_series()`. + * + * The `%chart_series_set_labels_percentage()` function is used to turn on + * the display of data labels as a percentage for a series. It is mainly + * used for pie charts: + * + * @code + * chart_series_set_labels(series); + * chart_series_set_labels_options(series, LXW_FALSE, LXW_FALSE, LXW_FALSE); + * chart_series_set_labels_percentage(series); + * @endcode + * + * @image html chart_labels7.png + * + * For more information see @ref chart_labels. + */ +void chart_series_set_labels_percentage(lxw_chart_series *series); + +/** + * @brief Set the number format for chart data labels in a series. + * + * @param series A series object created via `chart_add_series()`. + * @param num_format The number format string. + * + * The `%chart_series_set_labels_num_format()` function is used to set the + * number format for data labels: + * + * @code + * chart_series_set_labels(series); + * chart_series_set_labels_num_format(series, "$0.00"); + * @endcode + * + * @image html chart_labels8.png + * + * The number format is similar to the Worksheet Cell Format num_format, + * see `format_set_num_format()`. + * + * For more information see @ref chart_labels. + */ +void chart_series_set_labels_num_format(lxw_chart_series *series, + const char *num_format); + +/** + * @brief Set the font properties for chart data labels in a series + * + * @param series A series object created via `chart_add_series()`. + * @param font A pointer to a chart #lxw_chart_font font struct. + * + * + * The `%chart_series_set_labels_font()` function is used to set the font + * for data labels: + * + * @code + * lxw_chart_font font = {.name = "Consolas", .color = LXW_COLOR_RED}; + * + * chart_series_set_labels(series); + * chart_series_set_labels_font(series, &font); + * @endcode + * + * @image html chart_labels9.png + * + * For more information see @ref chart_fonts and @ref chart_labels. + * + */ +void chart_series_set_labels_font(lxw_chart_series *series, + lxw_chart_font *font); + +/** + * @brief Turn on a trendline for a chart data series. + * + * @param series A series object created via `chart_add_series()`. + * @param type The type of trendline: #lxw_chart_trendline_type. + * @param value The order/period value for polynomial and moving average + * trendlines. + * + * A trendline can be added to a chart series to indicate trends in the data + * such as a moving average or a polynomial fit. The trendlines types are + * shown in the following Excel dialog: + * + * @image html chart_trendline0.png + * + * The `%chart_series_set_trendline()` function turns on these trendlines for + * a data series: + * + * @code + * chart = workbook_add_chart(workbook, LXW_CHART_LINE); + * series = chart_add_series(chart, NULL, "Sheet1!$A$1:$A$6"); + * + * chart_series_set_trendline(series, LXW_CHART_TRENDLINE_TYPE_LINEAR, 0); + * @endcode + * + * @image html chart_trendline2.png + * + * The `value` parameter corresponds to *order* for a polynomial trendline + * and *period* for a Moving Average trendline. It both cases it must be >= 2. + * The `value` parameter is ignored for all other trendlines: + * + * @code + * chart_series_set_trendline(series, LXW_CHART_TRENDLINE_TYPE_AVERAGE, 2); + * @endcode + * + * @image html chart_trendline3.png + * + * The allowable values for the the trendline `type` are: + * + * - #LXW_CHART_TRENDLINE_TYPE_LINEAR: Linear trendline. + * - #LXW_CHART_TRENDLINE_TYPE_LOG: Logarithm trendline. + * - #LXW_CHART_TRENDLINE_TYPE_POLY: Polynomial trendline. The `value` + * parameter corresponds to *order*. + * - #LXW_CHART_TRENDLINE_TYPE_POWER: Power trendline. + * - #LXW_CHART_TRENDLINE_TYPE_EXP: Exponential trendline. + * - #LXW_CHART_TRENDLINE_TYPE_AVERAGE: Moving Average trendline. The `value` + * parameter corresponds to *period*. + * + * Other trendline options, such as those shown in the following Excel + * dialog, can be set using the functions below. + * + * @image html chart_trendline1.png + * + * For more information see @ref chart_trendlines. + */ +void chart_series_set_trendline(lxw_chart_series *series, uint8_t type, + uint8_t value); + +/** + * @brief Set the trendline forecast for a chart data series. + * + * @param series A series object created via `chart_add_series()`. + * @param forward The forward period. + * @param backward The backwards period. + * + * The `%chart_series_set_trendline_forecast()` function sets the forward + * and backward forecast periods for the trendline: + * + * @code + * chart_series_set_trendline(series, LXW_CHART_TRENDLINE_TYPE_LINEAR, 0); + * chart_series_set_trendline_forecast(series, 0.5, 0.5); + * @endcode + * + * @image html chart_trendline4.png + * + * @note This feature isn't available for Moving Average in Excel. + * + * For more information see @ref chart_trendlines. + */ +void chart_series_set_trendline_forecast(lxw_chart_series *series, + double forward, double backward); + +/** + * @brief Display the equation of a trendline for a chart data series. + * + * @param series A series object created via `chart_add_series()`. + * + * The `%chart_series_set_trendline_equation()` function displays the + * equation of the trendline on the chart: + * + * @code + * chart_series_set_trendline(series, LXW_CHART_TRENDLINE_TYPE_LINEAR, 0); + * chart_series_set_trendline_equation(series); + * @endcode + * + * @image html chart_trendline5.png + * + * @note This feature isn't available for Moving Average in Excel. + * + * For more information see @ref chart_trendlines. + */ +void chart_series_set_trendline_equation(lxw_chart_series *series); + +/** + * @brief Display the R squared value of a trendline for a chart data series. + * + * @param series A series object created via `chart_add_series()`. + * + * The `%chart_series_set_trendline_r_squared()` function displays the + * R-squared value for the trendline on the chart: + * + * @code + * chart_series_set_trendline(series, LXW_CHART_TRENDLINE_TYPE_LINEAR, 0); + * chart_series_set_trendline_r_squared(series); + * @endcode + * + * @image html chart_trendline6.png + * + * @note This feature isn't available for Moving Average in Excel. + * + * For more information see @ref chart_trendlines. + */ +void chart_series_set_trendline_r_squared(lxw_chart_series *series); + +/** + * @brief Set the trendline Y-axis intercept for a chart data series. + * + * @param series A series object created via `chart_add_series()`. + * @param intercept Y-axis intercept value. + * + * The `%chart_series_set_trendline_intercept()` function sets the Y-axis + * intercept for the trendline: + * + * @code + * chart_series_set_trendline(series, LXW_CHART_TRENDLINE_TYPE_LINEAR, 0); + * chart_series_set_trendline_equation(series); + * chart_series_set_trendline_intercept(series, 0.8); + * @endcode + * + * @image html chart_trendline7.png + * + * As can be seen from the equation on the chart the intercept point + * (when X=0) is the same as the value set in the equation. + * + * @note The intercept feature is only available in Excel for Exponential, + * Linear and Polynomial trendline types. + * + * For more information see @ref chart_trendlines. + */ +void chart_series_set_trendline_intercept(lxw_chart_series *series, + double intercept); + +/** + * @brief Set the trendline name for a chart data series. + * + * @param series A series object created via `chart_add_series()`. + * @param name The name of the trendline to display in the legend. + * + * The `%chart_series_set_trendline_name()` function sets the name of the + * trendline that is displayed in the chart legend. In the examples above + * the trendlines are displayed with default names like "Linear (Series 1)" + * and "2 per Mov. Avg. (Series 1)". If these names are too verbose or not + * descriptive enough you can set your own trendline name: + * + * @code + * chart_series_set_trendline(series, LXW_CHART_TRENDLINE_TYPE_LINEAR, 0); + * chart_series_set_trendline_name(series, "My trendline"); + * @endcode + * + * @image html chart_trendline8.png + * + * It is often preferable to turn off the trendline caption in the legend. + * This is down in Excel by deleting the trendline name from the legend. + * In libxlsxwriter this is done using the `chart_legend_delete_series()` + * function to delete the zero based series numbers: + * + * @code + * chart_series_set_trendline(series, LXW_CHART_TRENDLINE_TYPE_LINEAR, 0); + * + * // Delete the series name for the second series (=1 in zero base). + * // The -1 value indicates the end of the array of values. + * int16_t names[] = {1, -1}; + * chart_legend_delete_series(chart, names); + * @endcode + * + * @image html chart_trendline9.png + * + * For more information see @ref chart_trendlines. + */ +void chart_series_set_trendline_name(lxw_chart_series *series, + const char *name); + +/** + * @brief Set the trendline line properties for a chart data series. + * + * @param series A series object created via `chart_add_series()`. + * @param line A #lxw_chart_line struct. + * + * The `%chart_series_set_trendline_line()` function is used to set the line + * properties of a trendline: + * + * @code + * lxw_chart_line line = {.color = LXW_COLOR_RED, + * .dash_type = LXW_CHART_LINE_DASH_LONG_DASH}; + * + * chart_series_set_trendline(series, LXW_CHART_TRENDLINE_TYPE_LINEAR, 0); + * chart_series_set_trendline_line(series, &line); + * @endcode + * + * @image html chart_trendline10.png + * + * For more information see @ref chart_trendlines and @ref chart_lines. + */ +void chart_series_set_trendline_line(lxw_chart_series *series, + lxw_chart_line *line); + +/** + * Set the X or Y error bars for a chart series. + * + * @param error_bars A pointer to the series X or Y error bars. + * @param type The type of error bar: #lxw_chart_error_bar_type. + * @param value The error value. + * + * Error bars can be added to a chart series to indicate error bounds in the + * data. The error bars can be vertical `y_error_bars` (the most common type) + * or horizontal `x_error_bars` (for Bar and Scatter charts only). + * + * @image html chart_error_bars0.png + * + * The `%chart_series_set_error_bars()` function sets the error bar type + * and value associated with the type: + * + * @code + * lxw_chart_series *series = chart_add_series(chart, + * "=Sheet1!$A$1:$A$5", + * "=Sheet1!$B$1:$B$5"); + * + * chart_series_set_error_bars(series->y_error_bars, + * LXW_CHART_ERROR_BAR_TYPE_STD_ERROR, 0); + * @endcode + * + * @image html chart_error_bars1.png + * + * The error bar types that be used are: + * + * - #LXW_CHART_ERROR_BAR_TYPE_STD_ERROR: Standard error. + * - #LXW_CHART_ERROR_BAR_TYPE_FIXED: Fixed value. + * - #LXW_CHART_ERROR_BAR_TYPE_PERCENTAGE: Percentage. + * - #LXW_CHART_ERROR_BAR_TYPE_STD_DEV: Standard deviation(s). + * + * @note Custom error bars are not currently supported. + * + * All error bar types, apart from Standard error, should have a valid + * value to set the error range: + * + * @code + * chart_series_set_error_bars(series1->y_error_bars, + * LXW_CHART_ERROR_BAR_TYPE_FIXED, 2); + * + * chart_series_set_error_bars(series2->y_error_bars, + * LXW_CHART_ERROR_BAR_TYPE_PERCENTAGE, 5); + * + * chart_series_set_error_bars(series3->y_error_bars, + * LXW_CHART_ERROR_BAR_TYPE_STD_DEV, 1); + * @endcode + * + * For the Standard error type the value is ignored. + * + * For more information see @ref chart_error_bars. + */ +void chart_series_set_error_bars(lxw_series_error_bars *error_bars, + uint8_t type, double value); + +/** + * @brief Set the direction (up, down or both) of the error bars for a chart + * series. + * + * @param error_bars A pointer to the series X or Y error bars. + * @param direction The bar direction: #lxw_chart_error_bar_direction . + * + * The `%chart_series_set_error_bars_direction()` function sets the + * direction of the error bars: + * + * @code + * chart_series_set_error_bars(series->y_error_bars, + * LXW_CHART_ERROR_BAR_TYPE_STD_ERROR, 0); + * + * chart_series_set_error_bars_direction(series->y_error_bars, + * LXW_CHART_ERROR_BAR_DIR_PLUS); + * @endcode + * + * @image html chart_error_bars2.png + * + * The valid directions are: + * + * - #LXW_CHART_ERROR_BAR_DIR_BOTH: Error bar extends in both directions. + * The default. + * - #LXW_CHART_ERROR_BAR_DIR_PLUS: Error bar extends in positive direction. + * - #LXW_CHART_ERROR_BAR_DIR_MINUS: Error bar extends in negative direction. + * + * For more information see @ref chart_error_bars. + */ +void chart_series_set_error_bars_direction(lxw_series_error_bars *error_bars, + uint8_t direction); + +/** + * @brief Set the end cap type for the error bars of a chart series. + * + * @param error_bars A pointer to the series X or Y error bars. + * @param endcap The error bar end cap type: #lxw_chart_error_bar_cap . + * + * The `%chart_series_set_error_bars_endcap()` function sets the end cap + * type for the error bars: + * + * @code + * chart_series_set_error_bars(series->y_error_bars, + * LXW_CHART_ERROR_BAR_TYPE_STD_ERROR, 0); + * + * chart_series_set_error_bars_endcap(series->y_error_bars, + LXW_CHART_ERROR_BAR_NO_CAP); + * @endcode + * + * @image html chart_error_bars3.png + * + * The valid values are: + * + * - #LXW_CHART_ERROR_BAR_END_CAP: Flat end cap. The default. + * - #LXW_CHART_ERROR_BAR_NO_CAP: No end cap. + * + * For more information see @ref chart_error_bars. + */ +void chart_series_set_error_bars_endcap(lxw_series_error_bars *error_bars, + uint8_t endcap); + +/** + * @brief Set the line properties for a chart series error bars. + * + * @param error_bars A pointer to the series X or Y error bars. + * @param line A #lxw_chart_line struct. + * + * The `%chart_series_set_error_bars_line()` function sets the line + * properties for the error bars: + * + * @code + * lxw_chart_line line = {.color = LXW_COLOR_RED, + * .dash_type = LXW_CHART_LINE_DASH_ROUND_DOT}; + * + * chart_series_set_error_bars(series->y_error_bars, + * LXW_CHART_ERROR_BAR_TYPE_STD_ERROR, 0); + * + * chart_series_set_error_bars_line(series->y_error_bars, &line); + * @endcode + * + * @image html chart_error_bars4.png + * + * For more information see @ref chart_lines and @ref chart_error_bars. + */ +void chart_series_set_error_bars_line(lxw_series_error_bars *error_bars, + lxw_chart_line *line); + +/** + * @brief Set the name caption of the an axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param name The name caption of the axis. + * + * The `%chart_axis_set_name()` function sets the name (also known as title or + * caption) for an axis. It can be used for the X or Y axes. The name is + * displayed below an X axis and to the side of a Y axis. + * + * @code + * chart_axis_set_name(chart->x_axis, "Earnings per Quarter"); + * chart_axis_set_name(chart->y_axis, "US Dollars (Millions)"); + * @endcode + * + * @image html chart_axis_set_name.png + * + * The name parameter can also be a formula such as `=Sheet1!$A$1` to point to + * a cell in the workbook that contains the name: + * + * @code + * chart_axis_set_name(chart->x_axis, "=Sheet1!$B1$1"); + * @endcode + * + * See also the `chart_axis_set_name_range()` function to see how to set the + * name formula programmatically. + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_name(lxw_chart_axis *axis, const char *name); + +/** + * @brief Set a chart axis name formula using row and column values. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param sheetname The name of the worksheet that contains the cell range. + * @param row The zero indexed row number of the range. + * @param col The zero indexed column number of the range. + * + * The `%chart_axis_set_name_range()` function can be used to set an axis name + * range and is an alternative to using `chart_axis_set_name()` and a string + * formula: + * + * @code + * chart_axis_set_name_range(chart->x_axis, "Sheet1", 1, 0); + * chart_axis_set_name_range(chart->y_axis, "Sheet1", 2, 0); + * @endcode + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_name_range(lxw_chart_axis *axis, const char *sheetname, + lxw_row_t row, lxw_col_t col); + +/** + * @brief Set the font properties for a chart axis name. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param font A pointer to a chart #lxw_chart_font font struct. + * + * The `%chart_axis_set_name_font()` function is used to set the font of an + * axis name: + * + * @code + * lxw_chart_font font = {.bold = LXW_TRUE, .color = LXW_COLOR_BLUE}; + * + * chart_axis_set_name(chart->x_axis, "Yearly data"); + * chart_axis_set_name_font(chart->x_axis, &font); + * @endcode + * + * @image html chart_axis_set_name_font.png + * + * For more information see @ref chart_fonts. + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_name_font(lxw_chart_axis *axis, lxw_chart_font *font); + +/** + * @brief Set the font properties for the numbers of a chart axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param font A pointer to a chart #lxw_chart_font font struct. + * + * The `%chart_axis_set_num_font()` function is used to set the font of the + * numbers on an axis: + * + * @code + * lxw_chart_font font = {.bold = LXW_TRUE, .color = LXW_COLOR_BLUE}; + * + * chart_axis_set_num_font(chart->x_axis, &font1); + * @endcode + * + * @image html chart_axis_set_num_font.png + * + * For more information see @ref chart_fonts. + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_num_font(lxw_chart_axis *axis, lxw_chart_font *font); + +/** + * @brief Set the number format for a chart axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param num_format The number format string. + * + * The `%chart_axis_set_num_format()` function is used to set the format of + * the numbers on an axis: + * + * @code + * chart_axis_set_num_format(chart->x_axis, "0.00%"); + * chart_axis_set_num_format(chart->y_axis, "$#,##0.00"); + * @endcode + * + * The number format is similar to the Worksheet Cell Format num_format, + * see `format_set_num_format()`. + * + * @image html chart_axis_num_format.png + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_num_format(lxw_chart_axis *axis, const char *num_format); + +/** + * @brief Set the line properties for a chart axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param line A #lxw_chart_line struct. + * + * Set the line properties of a chart axis: + * + * @code + * // Hide the Y axis. + * lxw_chart_line line = {.none = LXW_TRUE}; + * + * chart_axis_set_line(chart->y_axis, &line); + * @endcode + * + * @image html chart_axis_set_line.png + * + * For more information see @ref chart_lines. + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_line(lxw_chart_axis *axis, lxw_chart_line *line); + +/** + * @brief Set the fill properties for a chart axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param fill A #lxw_chart_fill struct. + * + * Set the fill properties of a chart axis: + * + * @code + * lxw_chart_fill fill = {.color = LXW_COLOR_YELLOW}; + * + * chart_axis_set_fill(chart->y_axis, &fill); + * @endcode + * + * @image html chart_axis_set_fill.png + * + * For more information see @ref chart_fills. + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_fill(lxw_chart_axis *axis, lxw_chart_fill *fill); + +/** + * @brief Set the pattern properties for a chart axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param pattern A #lxw_chart_pattern struct. + * + * Set the pattern properties of a chart axis: + * + * @code + * chart_axis_set_pattern(chart->y_axis, &pattern); + * @endcode + * + * For more information see #lxw_chart_pattern_type and @ref chart_patterns. + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_pattern(lxw_chart_axis *axis, lxw_chart_pattern *pattern); + +/** + * @brief Reverse the order of the axis categories or values. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * + * Reverse the order of the axis categories or values: + * + * @code + * chart_axis_set_reverse(chart->x_axis); + * @endcode + * + * @image html chart_reverse.png + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_reverse(lxw_chart_axis *axis); + +/** + * @brief Set the position that the axis will cross the opposite axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param value The category or value that the axis crosses at. + * + * Set the position that the axis will cross the opposite axis: + * + * @code + * chart_axis_set_crossing(chart->x_axis, 3); + * chart_axis_set_crossing(chart->y_axis, 8); + * @endcode + * + * @image html chart_crossing1.png + * + * If crossing is omitted (the default) the crossing will be set automatically + * by Excel based on the chart data. + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_crossing(lxw_chart_axis *axis, double value); + +/** + * @brief Set the opposite axis crossing position as the axis maximum. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * + * Set the position that the opposite axis will cross as the axis maximum. + * The default axis crossing position is generally the axis minimum so this + * function can be used to reverse the location of the axes without reversing + * the number sequence: + * + * @code + * chart_axis_set_crossing_max(chart->x_axis); + * chart_axis_set_crossing_max(chart->y_axis); + * @endcode + * + * @image html chart_crossing2.png + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_crossing_max(lxw_chart_axis *axis); + +/** + * @brief Turn off/hide an axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * + * Turn off, hide, a chart axis: + * + * @code + * chart_axis_off(chart->x_axis); + * @endcode + * + * @image html chart_axis_off.png + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_off(lxw_chart_axis *axis); + +/** + * @brief Position a category axis on or between the axis tick marks. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param position A #lxw_chart_axis_tick_position value. + * + * Position a category axis horizontally on, or between, the axis tick marks. + * + * There are two allowable values: + * + * - #LXW_CHART_AXIS_POSITION_ON_TICK + * - #LXW_CHART_AXIS_POSITION_BETWEEN + * + * @code + * chart_axis_set_position(chart->x_axis, LXW_CHART_AXIS_POSITION_BETWEEN); + * @endcode + * + * @image html chart_axis_set_position.png + * + * **Axis types**: This function is applicable to category axes only. + * See @ref ww_charts_axes. + */ +void chart_axis_set_position(lxw_chart_axis *axis, uint8_t position); + +/** + * @brief Position the axis labels. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param position A #lxw_chart_axis_label_position value. + * + * Position the axis labels for the chart. The labels are the numbers, or + * strings or dates, on the axis that indicate the categories or values of + * the axis. + * + * For example: + * + * @code + * chart_axis_set_label_position(chart->x_axis, LXW_CHART_AXIS_LABEL_POSITION_HIGH); + chart_axis_set_label_position(chart->y_axis, LXW_CHART_AXIS_LABEL_POSITION_HIGH); + * @endcode + * + * @image html chart_label_position2.png + * + * The allowable values: + * + * - #LXW_CHART_AXIS_LABEL_POSITION_NEXT_TO - The default. + * - #LXW_CHART_AXIS_LABEL_POSITION_HIGH - Also right for vertical axes. + * - #LXW_CHART_AXIS_LABEL_POSITION_LOW - Also left for vertical axes. + * - #LXW_CHART_AXIS_LABEL_POSITION_NONE + * + * @image html chart_label_position1.png + * + * The #LXW_CHART_AXIS_LABEL_POSITION_NONE turns off the axis labels. This + * is slightly different from `chart_axis_off()` which also turns off the + * labels but also turns off tick marks. + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_label_position(lxw_chart_axis *axis, uint8_t position); + +/** + * @brief Set the minimum value for a chart axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param min Minimum value for chart axis. Value axes only. + * + * Set the minimum value for the axis range. + * + * @code + * chart_axis_set_min(chart->y_axis, -4); + * chart_axis_set_max(chart->y_axis, 21); + * @endcode + * + * @image html chart_max_min.png + * + * **Axis types**: This function is applicable to value and date axes only. + * See @ref ww_charts_axes. + */ +void chart_axis_set_min(lxw_chart_axis *axis, double min); + +/** + * @brief Set the maximum value for a chart axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param max Maximum value for chart axis. Value axes only. + * + * Set the maximum value for the axis range. + * + * @code + * chart_axis_set_min(chart->y_axis, -4); + * chart_axis_set_max(chart->y_axis, 21); + * @endcode + * + * See the above image. + * + * **Axis types**: This function is applicable to value and date axes only. + * See @ref ww_charts_axes. + */ +void chart_axis_set_max(lxw_chart_axis *axis, double max); + +/** + * @brief Set the log base of the axis range. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param log_base The log base for value axis. Value axes only. + * + * Set the log base for the axis: + * + * @code + * chart_axis_set_log_base(chart->y_axis, 10); + * @endcode + * + * @image html chart_log_base.png + * + * The allowable range of values for the log base in Excel is between 2 and + * 1000. + * + * **Axis types**: This function is applicable to value axes only. + * See @ref ww_charts_axes. + */ +void chart_axis_set_log_base(lxw_chart_axis *axis, uint16_t log_base); + +/** + * @brief Set the major axis tick mark type. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param type The tick mark type, defined by #lxw_chart_tick_mark. + * + * Set the type of the major axis tick mark: + * + * @code + * chart_axis_set_major_tick_mark(chart->x_axis, LXW_CHART_AXIS_TICK_MARK_CROSSING); + * chart_axis_set_minor_tick_mark(chart->x_axis, LXW_CHART_AXIS_TICK_MARK_INSIDE); + * + * chart_axis_set_major_tick_mark(chart->x_axis, LXW_CHART_AXIS_TICK_MARK_OUTSIDE); + * chart_axis_set_minor_tick_mark(chart->y_axis, LXW_CHART_AXIS_TICK_MARK_INSIDE); + * + * // Hide the default gridlines so the tick marks are visible. + * chart_axis_major_gridlines_set_visible(chart->y_axis, LXW_FALSE); + * @endcode + * + * @image html chart_tick_marks.png + * + * The tick mark types are: + * + * - #LXW_CHART_AXIS_TICK_MARK_NONE + * - #LXW_CHART_AXIS_TICK_MARK_INSIDE + * - #LXW_CHART_AXIS_TICK_MARK_OUTSIDE + * - #LXW_CHART_AXIS_TICK_MARK_CROSSING + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_major_tick_mark(lxw_chart_axis *axis, uint8_t type); + +/** + * @brief Set the minor axis tick mark type. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param type The tick mark type, defined by #lxw_chart_tick_mark. + * + * Set the type of the minor axis tick mark: + * + * @code + * chart_axis_set_minor_tick_mark(chart->x_axis, LXW_CHART_AXIS_TICK_MARK_INSIDE); + * @endcode + * + * See the image and example above. + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_set_minor_tick_mark(lxw_chart_axis *axis, uint8_t type); + +/** + * @brief Set the interval between category values. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param unit The interval between the categories. + * + * Set the interval between the category values. The default interval is 1 + * which gives the intervals shown in the charts above: + * + * 1, 2, 3, 4, 5, etc. + * + * Setting it to 2 gives: + * + * 1, 3, 5, 7, etc. + * + * For example: + * + * @code + * chart_axis_set_interval_unit(chart->x_axis, 2); + * @endcode + * + * @image html chart_set_interval1.png + * + * **Axis types**: This function is applicable to category and date axes only. + * See @ref ww_charts_axes. + */ +void chart_axis_set_interval_unit(lxw_chart_axis *axis, uint16_t unit); + +/** + * @brief Set the interval between category tick marks. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param unit The interval between the category ticks. + * + * Set the interval between the category tick marks. The default interval is 1 + * between each category but it can be set to other integer values: + * + * @code + * chart_axis_set_interval_tick(chart->x_axis, 2); + * @endcode + * + * @image html chart_set_interval2.png + * + * **Axis types**: This function is applicable to category and date axes only. + * See @ref ww_charts_axes. + */ +void chart_axis_set_interval_tick(lxw_chart_axis *axis, uint16_t unit); + +/** + * @brief Set the increment of the major units in the axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param unit The increment of the major units. + * + * Set the increment of the major units in the axis range. + * + * @code + * // Turn on the minor gridline (it is off by default). + * chart_axis_minor_gridlines_set_visible(chart->y_axis, LXW_TRUE); + * + * chart_axis_set_major_unit(chart->y_axis, 4); + * chart_axis_set_minor_unit(chart->y_axis, 2); + * @endcode + * + * @image html chart_set_major_units.png + * + * **Axis types**: This function is applicable to value and date axes only. + * See @ref ww_charts_axes. + */ +void chart_axis_set_major_unit(lxw_chart_axis *axis, double unit); + +/** + * @brief Set the increment of the minor units in the axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param unit The increment of the minor units. + * + * Set the increment of the minor units in the axis range. + * + * @code + * chart_axis_set_minor_unit(chart->y_axis, 2); + * @endcode + * + * See the image above + * + * **Axis types**: This function is applicable to value and date axes only. + * See @ref ww_charts_axes. + */ +void chart_axis_set_minor_unit(lxw_chart_axis *axis, double unit); + +/** + * @brief Set the display units for a value axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param units The display units: #lxw_chart_axis_display_unit. + * + * Set the display units for the axis. This can be useful if the axis numbers + * are very large but you don't want to represent them in scientific notation: + * + * @code + * chart_axis_set_display_units(chart->x_axis, LXW_CHART_AXIS_UNITS_THOUSANDS); + * chart_axis_set_display_units(chart->y_axis, LXW_CHART_AXIS_UNITS_MILLIONS); + * @endcode + * + * @image html chart_display_units.png + * + * **Axis types**: This function is applicable to value axes only. + * See @ref ww_charts_axes. + */ +void chart_axis_set_display_units(lxw_chart_axis *axis, uint8_t units); + +/** + * @brief Turn on/off the display units for a value axis. + + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param visible Turn off/on the display units. (0/1) + * + * Turn on or off the display units for the axis. This option is set on + * automatically by `chart_axis_set_display_units()`. + * + * @code + * chart_axis_set_display_units_visible(chart->y_axis, LXW_TRUE); + * @endcode + * + * **Axis types**: This function is applicable to value axes only. + * See @ref ww_charts_axes. + */ +void chart_axis_set_display_units_visible(lxw_chart_axis *axis, + uint8_t visible); + +/** + * @brief Turn on/off the major gridlines for an axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param visible Turn off/on the major gridline. (0/1) + * + * Turn on or off the major gridlines for an X or Y axis. In most Excel charts + * the Y axis major gridlines are on by default and the X axis major + * gridlines are off by default. + * + * Example: + * + * @code + * // Reverse the normal visible/hidden gridlines for a column chart. + * chart_axis_major_gridlines_set_visible(chart->x_axis, LXW_TRUE); + * chart_axis_major_gridlines_set_visible(chart->y_axis, LXW_FALSE); + * @endcode + * + * @image html chart_gridline1.png + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_major_gridlines_set_visible(lxw_chart_axis *axis, + uint8_t visible); + +/** + * @brief Turn on/off the minor gridlines for an axis. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param visible Turn off/on the minor gridline. (0/1) + * + * Turn on or off the minor gridlines for an X or Y axis. In most Excel charts + * the X and Y axis minor gridlines are off by default. + * + * Example, turn on all major and minor gridlines: + * + * @code + * chart_axis_major_gridlines_set_visible(chart->x_axis, LXW_TRUE); + * chart_axis_minor_gridlines_set_visible(chart->x_axis, LXW_TRUE); + * chart_axis_major_gridlines_set_visible(chart->y_axis, LXW_TRUE); + * chart_axis_minor_gridlines_set_visible(chart->y_axis, LXW_TRUE); + * @endcode + * + * @image html chart_gridline2.png + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_minor_gridlines_set_visible(lxw_chart_axis *axis, + uint8_t visible); + +/** + * @brief Set the line properties for the chart axis major gridlines. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param line A #lxw_chart_line struct. + * + * Format the line properties of the major gridlines of a chart: + * + * @code + * lxw_chart_line line1 = {.color = LXW_COLOR_RED, + * .width = 0.5, + * .dash_type = LXW_CHART_LINE_DASH_SQUARE_DOT}; + * + * lxw_chart_line line2 = {.color = LXW_COLOR_YELLOW}; + * + * lxw_chart_line line3 = {.width = 1.25, + * .dash_type = LXW_CHART_LINE_DASH_DASH}; + * + * lxw_chart_line line4 = {.color = 0x00B050}; + * + * chart_axis_major_gridlines_set_line(chart->x_axis, &line1); + * chart_axis_minor_gridlines_set_line(chart->x_axis, &line2); + * chart_axis_major_gridlines_set_line(chart->y_axis, &line3); + * chart_axis_minor_gridlines_set_line(chart->y_axis, &line4); + * @endcode + * + * @image html chart_gridline3.png + * + * For more information see @ref chart_lines. + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_major_gridlines_set_line(lxw_chart_axis *axis, + lxw_chart_line *line); + +/** + * @brief Set the line properties for the chart axis minor gridlines. + * + * @param axis A pointer to a chart #lxw_chart_axis object. + * @param line A #lxw_chart_line struct. + * + * Format the line properties of the minor gridlines of a chart, see the + * example above. + * + * For more information see @ref chart_lines. + * + * **Axis types**: This function is applicable to to all axes types. + * See @ref ww_charts_axes. + */ +void chart_axis_minor_gridlines_set_line(lxw_chart_axis *axis, + lxw_chart_line *line); + +/** + * @brief Set the title of the chart. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param name The chart title name. + * + * The `%chart_title_set_name()` function sets the name (title) for the + * chart. The name is displayed above the chart. + * + * @code + * chart_title_set_name(chart, "Year End Results"); + * @endcode + * + * @image html chart_title_set_name.png + * + * The name parameter can also be a formula such as `=Sheet1!$A$1` to point to + * a cell in the workbook that contains the name: + * + * @code + * chart_title_set_name(chart, "=Sheet1!$B1$1"); + * @endcode + * + * See also the `chart_title_set_name_range()` function to see how to set the + * name formula programmatically. + * + * The Excel default is to have no chart title. + */ +void chart_title_set_name(lxw_chart *chart, const char *name); + +/** + * @brief Set a chart title formula using row and column values. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param sheetname The name of the worksheet that contains the cell range. + * @param row The zero indexed row number of the range. + * @param col The zero indexed column number of the range. + * + * The `%chart_title_set_name_range()` function can be used to set a chart + * title range and is an alternative to using `chart_title_set_name()` and a + * string formula: + * + * @code + * chart_title_set_name_range(chart, "Sheet1", 1, 0); + * @endcode + */ +void chart_title_set_name_range(lxw_chart *chart, const char *sheetname, + lxw_row_t row, lxw_col_t col); + +/** + * @brief Set the font properties for a chart title. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param font A pointer to a chart #lxw_chart_font font struct. + * + * The `%chart_title_set_name_font()` function is used to set the font of a + * chart title: + * + * @code + * lxw_chart_font font = {.bold = LXW_TRUE, .color = LXW_COLOR_BLUE}; + * + * chart_title_set_name(chart, "Year End Results"); + * chart_title_set_name_font(chart, &font); + * @endcode + * + * @image html chart_title_set_name_font.png + * + * For more information see @ref chart_fonts. + */ +void chart_title_set_name_font(lxw_chart *chart, lxw_chart_font *font); + +/** + * @brief Turn off an automatic chart title. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * + * In general in Excel a chart title isn't displayed unless the user + * explicitly adds one. However, Excel adds an automatic chart title to charts + * with a single series and a user defined series name. The + * `chart_title_off()` function allows you to turn off this automatic chart + * title: + * + * @code + * chart_title_off(chart); + * @endcode + */ +void chart_title_off(lxw_chart *chart); + +/** + * @brief Set the position of the chart legend. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param position The #lxw_chart_legend_position value for the legend. + * + * The `%chart_legend_set_position()` function is used to set the chart + * legend to one of the #lxw_chart_legend_position values: + * + * LXW_CHART_LEGEND_NONE + * LXW_CHART_LEGEND_RIGHT + * LXW_CHART_LEGEND_LEFT + * LXW_CHART_LEGEND_TOP + * LXW_CHART_LEGEND_BOTTOM + * LXW_CHART_LEGEND_OVERLAY_RIGHT + * LXW_CHART_LEGEND_OVERLAY_LEFT + * + * For example: + * + * @code + * chart_legend_set_position(chart, LXW_CHART_LEGEND_BOTTOM); + * @endcode + * + * @image html chart_legend_bottom.png + * + * This function can also be used to turn off a chart legend: + * + * @code + * chart_legend_set_position(chart, LXW_CHART_LEGEND_NONE); + * @endcode + * + * @image html chart_legend_none.png + * + */ +void chart_legend_set_position(lxw_chart *chart, uint8_t position); + +/** + * @brief Set the font properties for a chart legend. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param font A pointer to a chart #lxw_chart_font font struct. + * + * The `%chart_legend_set_font()` function is used to set the font of a + * chart legend: + * + * @code + * lxw_chart_font font = {.bold = LXW_TRUE, .color = LXW_COLOR_BLUE}; + * + * chart_legend_set_font(chart, &font); + * @endcode + * + * @image html chart_legend_set_font.png + * + * For more information see @ref chart_fonts. + */ +void chart_legend_set_font(lxw_chart *chart, lxw_chart_font *font); + +/** + * @brief Remove one or more series from the the legend. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param delete_series An array of zero-indexed values to delete from series. + * + * @return A #lxw_error. + * + * The `%chart_legend_delete_series()` function allows you to remove/hide one + * or more series in a chart legend (the series will still display on the chart). + * + * This function takes an array of one or more zero indexed series + * numbers. The array should be terminated with -1. + * + * For example to remove the first and third zero-indexed series from the + * legend of a chart with 3 series: + * + * @code + * int16_t series[] = {0, 2, -1}; + * + * chart_legend_delete_series(chart, series); + * @endcode + * + * @image html chart_legend_delete.png + */ +lxw_error chart_legend_delete_series(lxw_chart *chart, + int16_t delete_series[]); + +/** + * @brief Set the line properties for a chartarea. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param line A #lxw_chart_line struct. + * + * Set the line/border properties of a chartarea. In Excel the chartarea + * is the background area behind the chart: + * + * @code + * lxw_chart_line line = {.none = LXW_TRUE}; + * lxw_chart_fill fill = {.color = LXW_COLOR_RED}; + * + * chart_chartarea_set_line(chart, &line); + * chart_chartarea_set_fill(chart, &fill); + * @endcode + * + * @image html chart_chartarea.png + * + * For more information see @ref chart_lines. + */ +void chart_chartarea_set_line(lxw_chart *chart, lxw_chart_line *line); + +/** + * @brief Set the fill properties for a chartarea. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param fill A #lxw_chart_fill struct. + * + * Set the fill properties of a chartarea: + * + * @code + * chart_chartarea_set_fill(chart, &fill); + * @endcode + * + * See the example and image above. + * + * For more information see @ref chart_fills. + */ +void chart_chartarea_set_fill(lxw_chart *chart, lxw_chart_fill *fill); + +/** + * @brief Set the pattern properties for a chartarea. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param pattern A #lxw_chart_pattern struct. + * + * Set the pattern properties of a chartarea: + * + * @code + * chart_chartarea_set_pattern(series1, &pattern); + * @endcode + * + * For more information see #lxw_chart_pattern_type and @ref chart_patterns. + */ +void chart_chartarea_set_pattern(lxw_chart *chart, + lxw_chart_pattern *pattern); + +/** + * @brief Set the line properties for a plotarea. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param line A #lxw_chart_line struct. + * + * Set the line/border properties of a plotarea. In Excel the plotarea is + * the area between the axes on which the chart series are plotted: + * + * @code + * lxw_chart_line line = {.color = LXW_COLOR_RED, + * .width = 2, + * .dash_type = LXW_CHART_LINE_DASH_DASH}; + * lxw_chart_fill fill = {.color = 0xFFFFC2}; + * + * chart_plotarea_set_line(chart, &line); + * chart_plotarea_set_fill(chart, &fill); + * + * @endcode + * + * @image html chart_plotarea.png + * + * For more information see @ref chart_lines. + */ +void chart_plotarea_set_line(lxw_chart *chart, lxw_chart_line *line); + +/** + * @brief Set the fill properties for a plotarea. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param fill A #lxw_chart_fill struct. + * + * Set the fill properties of a plotarea: + * + * @code + * chart_plotarea_set_fill(chart, &fill); + * @endcode + * + * See the example and image above. + * + * For more information see @ref chart_fills. + */ +void chart_plotarea_set_fill(lxw_chart *chart, lxw_chart_fill *fill); + +/** + * @brief Set the pattern properties for a plotarea. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param pattern A #lxw_chart_pattern struct. + * + * Set the pattern properties of a plotarea: + * + * @code + * chart_plotarea_set_pattern(series1, &pattern); + * @endcode + * + * For more information see #lxw_chart_pattern_type and @ref chart_patterns. + */ +void chart_plotarea_set_pattern(lxw_chart *chart, lxw_chart_pattern *pattern); + +/** + * @brief Set the chart style type. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param style_id An index representing the chart style, 1 - 48. + * + * The `%chart_set_style()` function is used to set the style of the chart to + * one of the 48 built-in styles available on the "Design" tab in Excel 2007: + * + * @code + * chart_set_style(chart, 37) + * @endcode + * + * @image html chart_style.png + * + * The style index number is counted from 1 on the top left in the Excel + * dialog. The default style is 2. + * + * **Note:** + * + * In Excel 2013 the Styles section of the "Design" tab in Excel shows what + * were referred to as "Layouts" in previous versions of Excel. These layouts + * are not defined in the file format. They are a collection of modifications + * to the base chart type. They can not be defined by the `chart_set_style()`` + * function. + * + */ +void chart_set_style(lxw_chart *chart, uint8_t style_id); + +/** + * @brief Turn on a data table below the horizontal axis. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * + * The `%chart_set_table()` function adds a data table below the horizontal + * axis with the data used to plot the chart: + * + * @code + * // Turn on the data table with default options. + * chart_set_table(chart); + * @endcode + * + * @image html chart_data_table1.png + * + * The data table can only be shown with Bar, Column, Line and Area charts. + * + */ +void chart_set_table(lxw_chart *chart); + +/** + * @brief Turn on/off grid options for a chart data table. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param horizontal Turn on/off the horizontal grid lines in the table. + * @param vertical Turn on/off the vertical grid lines in the table. + * @param outline Turn on/off the outline lines in the table. + * @param legend_keys Turn on/off the legend keys in the table. + * + * The `%chart_set_table_grid()` function turns on/off grid options for a + * chart data table. The data table grid options in Excel are shown in the + * dialog below: + * + * @image html chart_data_table3.png + * + * These options can be passed to the `%chart_set_table_grid()` function. + * The values for a default chart are: + * + * - `horizontal`: On. + * - `vertical`: On. + * - `outline`: On. + * - `legend_keys`: Off. + * + * Example: + * + * @code + * // Turn on the data table with default options. + * chart_set_table(chart); + * + * // Turn on all grid lines and the grid legend. + * chart_set_table_grid(chart, LXW_TRUE, LXW_TRUE, LXW_TRUE, LXW_TRUE); + * + * // Turn off the legend since it is show in the table. + * chart_legend_set_position(chart, LXW_CHART_LEGEND_NONE); + * + * @endcode + * + * @image html chart_data_table2.png + * + * The data table can only be shown with Bar, Column, Line and Area charts. + * + */ +void chart_set_table_grid(lxw_chart *chart, uint8_t horizontal, + uint8_t vertical, uint8_t outline, + uint8_t legend_keys); + +void chart_set_table_font(lxw_chart *chart, lxw_chart_font *font); + +/** + * @brief Turn on up-down bars for the chart. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * + * The `%chart_set_up_down_bars()` function adds Up-Down bars to Line charts + * to indicate the difference between the first and last data series: + * + * @code + * chart_set_up_down_bars(chart); + * @endcode + * + * @image html chart_data_tools4.png + * + * Up-Down bars are only available in Line charts. By default Up-Down bars are + * black and white like in the above example. To format the border or fill + * of the bars see the `chart_set_up_down_bars_format()` function below. + */ +void chart_set_up_down_bars(lxw_chart *chart); + +/** + * @brief Turn on up-down bars for the chart, with formatting. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param up_bar_line A #lxw_chart_line struct for the up-bar border. + * @param up_bar_fill A #lxw_chart_fill struct for the up-bar fill. + * @param down_bar_line A #lxw_chart_line struct for the down-bar border. + * @param down_bar_fill A #lxw_chart_fill struct for the down-bar fill. + * + * The `%chart_set_up_down_bars_format()` function adds Up-Down bars to Line + * charts to indicate the difference between the first and last data series. + * It also allows the up and down bars to be formatted: + * + * @code + * lxw_chart_line line = {.color = LXW_COLOR_BLACK}; + * lxw_chart_fill up_fill = {.color = 0x00B050}; + * lxw_chart_fill down_fill = {.color = LXW_COLOR_RED}; + * + * chart_set_up_down_bars_format(chart, &line, &up_fill, &line, &down_fill); + * @endcode + * + * @image html chart_up_down_bars.png + * + * Up-Down bars are only available in Line charts. + * For more format information see @ref chart_lines and @ref chart_fills. + */ +void chart_set_up_down_bars_format(lxw_chart *chart, + lxw_chart_line *up_bar_line, + lxw_chart_fill *up_bar_fill, + lxw_chart_line *down_bar_line, + lxw_chart_fill *down_bar_fill); + +/** + * @brief Turn on and format Drop Lines for a chart. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param line A #lxw_chart_line struct. + * + * The `%chart_set_drop_lines()` function adds Drop Lines to charts to + * show the Category value of points in the data: + * + * @code + * chart_set_drop_lines(chart, NULL); + * @endcode + * + * @image html chart_data_tools6.png + * + * It is possible to format the Drop Line line properties if required: + * + * @code + * lxw_chart_line line = {.color = LXW_COLOR_RED, + * .dash_type = LXW_CHART_LINE_DASH_SQUARE_DOT}; + * + * chart_set_drop_lines(chart, &line); + * @endcode + * + * Drop Lines are only available in Line and Area charts. + * For more format information see @ref chart_lines. + */ +void chart_set_drop_lines(lxw_chart *chart, lxw_chart_line *line); + +/** + * @brief Turn on and format high-low Lines for a chart. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param line A #lxw_chart_line struct. + * + * The `%chart_set_high_low_lines()` function adds High-Low Lines to charts + * to show the Category value of points in the data: + * + * @code + * chart_set_high_low_lines(chart, NULL); + * @endcode + * + * @image html chart_data_tools5.png + * + * It is possible to format the High-Low Line line properties if required: + * + * @code + * lxw_chart_line line = {.color = LXW_COLOR_RED, + * .dash_type = LXW_CHART_LINE_DASH_SQUARE_DOT}; + * + * chart_set_high_low_lines(chart, &line); + * @endcode + * + * High-Low Lines are only available in Line charts. + * For more format information see @ref chart_lines. + */ +void chart_set_high_low_lines(lxw_chart *chart, lxw_chart_line *line); + +/** + * @brief Set the overlap between series in a Bar/Column chart. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param overlap The overlap between the series. -100 to 100. + * + * The `%chart_set_series_overlap()` function sets the overlap between series + * in Bar and Column charts. + * + * @code + * chart_set_series_overlap(chart, -50); + * @endcode + * + * @image html chart_overlap.png + * + * The overlap value must be in the range `0 <= overlap <= 500`. + * The default value is 0. + * + * This option is only available for Bar/Column charts. + */ +void chart_set_series_overlap(lxw_chart *chart, int8_t overlap); + +/** + * @brief Set the gap between series in a Bar/Column chart. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param gap The gap between the series. 0 to 500. + * + * The `%chart_set_series_gap()` function sets the gap between series in + * Bar and Column charts. + * + * @code + * chart_set_series_gap(chart, 400); + * @endcode + * + * @image html chart_gap.png + * + * The gap value must be in the range `0 <= gap <= 500`. The default value + * is 150. + * + * This option is only available for Bar/Column charts. + */ +void chart_set_series_gap(lxw_chart *chart, uint16_t gap); + +/** + * @brief Set the option for displaying blank data in a chart. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param option The display option. A #lxw_chart_blank option. + * + * The `%chart_show_blanks_as()` function controls how blank data is displayed + * in a chart: + * + * @code + * chart_show_blanks_as(chart, LXW_CHART_BLANKS_AS_CONNECTED); + * @endcode + * + * The `option` parameter can have one of the following values: + * + * - #LXW_CHART_BLANKS_AS_GAP: Show empty chart cells as gaps in the data. + * This is the default option for Excel charts. + * - #LXW_CHART_BLANKS_AS_ZERO: Show empty chart cells as zeros. + * - #LXW_CHART_BLANKS_AS_CONNECTED: Show empty chart cells as connected. + * Only for charts with lines. + */ +void chart_show_blanks_as(lxw_chart *chart, uint8_t option); + +/** + * @brief Display data on charts from hidden rows or columns. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * + * Display data that is in hidden rows or columns on the chart: + * + * @code + * chart_show_hidden_data(chart); + * @endcode + */ +void chart_show_hidden_data(lxw_chart *chart); + +/** + * @brief Set the Pie/Doughnut chart rotation. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param rotation The angle of rotation. + * + * The `chart_set_rotation()` function is used to set the rotation of the + * first segment of a Pie/Doughnut chart. This has the effect of rotating + * the entire chart: + * + * @code + * chart_set_rotation(chart, 28); + * @endcode + * + * The angle of rotation must be in the range `0 <= rotation <= 360`. + * + * This option is only available for Pie/Doughnut charts. + * + */ +void chart_set_rotation(lxw_chart *chart, uint16_t rotation); + +/** + * @brief Set the Doughnut chart hole size. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param size The hole size as a percentage. + * + * The `chart_set_hole_size()` function is used to set the hole size of a + * Doughnut chart: + * + * @code + * chart_set_hole_size(chart, 33); + * @endcode + * + * The hole size must be in the range `10 <= size <= 90`. + * + * This option is only available for Doughnut charts. + * + */ +void chart_set_hole_size(lxw_chart *chart, uint8_t size); + +lxw_error lxw_chart_add_data_cache(lxw_series_range *range, uint8_t *data, + uint16_t rows, uint8_t cols, uint8_t col); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _chart_xml_declaration(lxw_chart *chart); +STATIC void _chart_write_legend(lxw_chart *chart); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_CHART_H__ */ diff --git a/src/include/xlsxwriter/common.h b/src/include/xlsxwriter/common.h new file mode 100644 index 0000000..fb4b29b --- /dev/null +++ b/src/include/xlsxwriter/common.h @@ -0,0 +1,372 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + */ + +/** + * @file common.h + * + * @brief Common functions and defines for the libxlsxwriter library. + * + * + * + */ +#ifndef __LXW_COMMON_H__ +#define __LXW_COMMON_H__ + +#include +#include "third_party/queue.h" +#include "third_party/tree.h" + +#ifndef TESTING +#define STATIC static +#else +#define STATIC +#endif + +/** Integer data type to represent a row value. Equivalent to `uint32_t`. + * + * The maximum row in Excel is 1,048,576. + */ +typedef uint32_t lxw_row_t; + +/** Integer data type to represent a column value. Equivalent to `uint16_t`. + * + * The maximum column in Excel is 16,384. + */ +typedef uint16_t lxw_col_t; + +/** Boolean values used in libxlsxwriter. */ +enum lxw_boolean { + /** False value. */ + LXW_FALSE, + /** True value. */ + LXW_TRUE +}; + +/** + * @brief Error codes from libxlsxwriter functions. + * + * See the `lxw_strerror()` function for an example of how to convert the + * enum number to a descriptive error message string. + */ +typedef enum lxw_error { + + /** No error. */ + LXW_NO_ERROR = 0, + + /** Memory error, failed to malloc() required memory. */ + LXW_ERROR_MEMORY_MALLOC_FAILED, + + /** Error creating output xlsx file. Usually a permissions error. */ + LXW_ERROR_CREATING_XLSX_FILE, + + /** Error encountered when creating a tmpfile during file assembly. */ + LXW_ERROR_CREATING_TMPFILE, + + /** Zlib error with a file operation while creating xlsx file. */ + LXW_ERROR_ZIP_FILE_OPERATION, + + /** Zlib error when adding sub file to xlsx file. */ + LXW_ERROR_ZIP_FILE_ADD, + + /** Zlib error when closing xlsx file. */ + LXW_ERROR_ZIP_CLOSE, + + /** NULL function parameter ignored. */ + LXW_ERROR_NULL_PARAMETER_IGNORED, + + /** Function parameter validation error. */ + LXW_ERROR_PARAMETER_VALIDATION, + + /** Worksheet name exceeds Excel's limit of 31 characters. */ + LXW_ERROR_SHEETNAME_LENGTH_EXCEEDED, + + /** Worksheet name contains invalid Excel character: '[]:*?/\\' */ + LXW_ERROR_INVALID_SHEETNAME_CHARACTER, + + /** Worksheet name is already in use. */ + LXW_ERROR_SHEETNAME_ALREADY_USED, + + /** Parameter exceeds Excel's limit of 128 characters. */ + LXW_ERROR_128_STRING_LENGTH_EXCEEDED, + + /** Parameter exceeds Excel's limit of 255 characters. */ + LXW_ERROR_255_STRING_LENGTH_EXCEEDED, + + /** String exceeds Excel's limit of 32,767 characters. */ + LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED, + + /** Error finding internal string index. */ + LXW_ERROR_SHARED_STRING_INDEX_NOT_FOUND, + + /** Worksheet row or column index out of range. */ + LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE, + + /** Maximum number of worksheet URLs (65530) exceeded. */ + LXW_ERROR_WORKSHEET_MAX_NUMBER_URLS_EXCEEDED, + + /** Couldn't read image dimensions or DPI. */ + LXW_ERROR_IMAGE_DIMENSIONS, + + LXW_MAX_ERRNO +} lxw_error; + +/** @brief Struct to represent a date and time in Excel. + * + * Struct to represent a date and time in Excel. See @ref working_with_dates. + */ +typedef struct lxw_datetime { + + /** Year : 1900 - 9999 */ + int year; + /** Month : 1 - 12 */ + int month; + /** Day : 1 - 31 */ + int day; + /** Hour : 0 - 23 */ + int hour; + /** Minute : 0 - 59 */ + int min; + /** Seconds : 0 - 59.999 */ + double sec; + +} lxw_datetime; + +enum lxw_custom_property_types { + LXW_CUSTOM_NONE, + LXW_CUSTOM_STRING, + LXW_CUSTOM_DOUBLE, + LXW_CUSTOM_INTEGER, + LXW_CUSTOM_BOOLEAN, + LXW_CUSTOM_DATETIME +}; + +/* Excel sheetname max of 31 chars. */ +#define LXW_SHEETNAME_MAX 31 + +/* Max with all worksheet chars 4xUTF-8 bytes + start and end quotes + \0. */ +#define LXW_MAX_SHEETNAME_LENGTH ((LXW_SHEETNAME_MAX * 4) + 2 + 1) + +/* Max col string length. */ +#define LXW_MAX_COL_NAME_LENGTH sizeof("$XFD") + +/* Max row string length. */ +#define LXW_MAX_ROW_NAME_LENGTH sizeof("$1048576") + +/* Max cell string length. */ +#define LXW_MAX_CELL_NAME_LENGTH sizeof("$XFWD$1048576") + +/* Max range: $XFWD$1048576:$XFWD$1048576\0 */ +#define LXW_MAX_CELL_RANGE_LENGTH (LXW_MAX_CELL_NAME_LENGTH * 2) + +/* Max range formula Sheet1!$A$1:$C$5$ style. */ +#define LXW_MAX_FORMULA_RANGE_LENGTH (LXW_MAX_SHEETNAME_LENGTH + LXW_MAX_CELL_RANGE_LENGTH) + +/* Datetime string length. */ +#define LXW_DATETIME_LENGTH sizeof("2016-12-12T23:00:00Z") + +#define LXW_EPOCH_1900 0 +#define LXW_EPOCH_1904 1 + +#define LXW_UINT32_T_LENGTH sizeof("4294967296") +#define LXW_FILENAME_LENGTH 128 +#define LXW_IGNORE 1 + +#define LXW_SCHEMA_MS "http://schemas.microsoft.com/office/2006/relationships" +#define LXW_SCHEMA_ROOT "http://schemas.openxmlformats.org" +#define LXW_SCHEMA_DRAWING LXW_SCHEMA_ROOT "/drawingml/2006" +#define LXW_SCHEMA_OFFICEDOC LXW_SCHEMA_ROOT "/officeDocument/2006" +#define LXW_SCHEMA_PACKAGE LXW_SCHEMA_ROOT "/package/2006/relationships" +#define LXW_SCHEMA_DOCUMENT LXW_SCHEMA_ROOT "/officeDocument/2006/relationships" +#define LXW_SCHEMA_CONTENT LXW_SCHEMA_ROOT "/package/2006/content-types" + +#define LXW_ERROR(message) \ + fprintf(stderr, "[ERROR][%s:%d]: " message "\n", __FILE__, __LINE__) + +#define LXW_MEM_ERROR() \ + LXW_ERROR("Memory allocation failed.") + +#define GOTO_LABEL_ON_MEM_ERROR(pointer, label) \ + if (!pointer) { \ + LXW_MEM_ERROR(); \ + goto label; \ + } + +#define RETURN_ON_MEM_ERROR(pointer, error) \ + if (!pointer) { \ + LXW_MEM_ERROR(); \ + return error; \ + } + +#define RETURN_VOID_ON_MEM_ERROR(pointer) \ + if (!pointer) { \ + LXW_MEM_ERROR(); \ + return; \ + } + +#define RETURN_ON_ERROR(error) \ + if (error) \ + return error; + +#define LXW_WARN(message) \ + fprintf(stderr, "[WARNING]: " message "\n") + +/* We can't use variadic macros here since we support ANSI C. */ +#define LXW_WARN_FORMAT(message) \ + fprintf(stderr, "[WARNING]: " message "\n") + +#define LXW_WARN_FORMAT1(message, var) \ + fprintf(stderr, "[WARNING]: " message "\n", var) + +#define LXW_WARN_FORMAT2(message, var1, var2) \ + fprintf(stderr, "[WARNING]: " message "\n", var1, var2) + +/* Chart axis type checks. */ +#define LXW_WARN_CAT_AXIS_ONLY(function) \ + if (!axis->is_category) { \ + fprintf(stderr, "[WARNING]: " \ + function "() is only valid for category axes\n"); \ + return; \ + } + +#define LXW_WARN_VALUE_AXIS_ONLY(function) \ + if (!axis->is_value) { \ + fprintf(stderr, "[WARNING]: " \ + function "() is only valid for value axes\n"); \ + return; \ + } + +#define LXW_WARN_DATE_AXIS_ONLY(function) \ + if (!axis->is_date) { \ + fprintf(stderr, "[WARNING]: " \ + function "() is only valid for date axes\n"); \ + return; \ + } + +#define LXW_WARN_CAT_AND_DATE_AXIS_ONLY(function) \ + if (!axis->is_category && !axis->is_date) { \ + fprintf(stderr, "[WARNING]: " \ + function "() is only valid for category and date axes\n"); \ + return; \ + } + +#define LXW_WARN_VALUE_AND_DATE_AXIS_ONLY(function) \ + if (!axis->is_value && !axis->is_date) { \ + fprintf(stderr, "[WARNING]: " \ + function "() is only valid for value and date axes\n"); \ + return; \ + } + +#ifndef LXW_BIG_ENDIAN +#define LXW_UINT32_NETWORK(n) ((((n) & 0xFF) << 24) | \ + (((n) & 0xFF00) << 8) | \ + (((n) & 0xFF0000) >> 8) | \ + (((n) & 0xFF000000) >> 24)) +#define LXW_UINT16_NETWORK(n) ((((n) & 0x00FF) << 8) | (((n) & 0xFF00) >> 8)) +#else +#define LXW_UINT32_NETWORK(n) (n) +#define LXW_UINT16_NETWORK(n) (n) +#endif + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +/* Compilers that have a native snprintf() can use it directly. */ +#ifdef _MSC_VER +#define LXW_HAS_SNPRINTF +#endif + +#ifdef LXW_HAS_SNPRINTF +#define lxw_snprintf snprintf +#else +#define lxw_snprintf __builtin_snprintf +#endif + +/* Define a snprintf for MSVC 2010. */ +#if defined(_MSC_VER) && _MSC_VER < 1900 + +#include +#define snprintf msvc2010_snprintf +#define vsnprintf msvc2010_vsnprintf + +__inline int +msvc2010_vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + int count = -1; + + if (size != 0) + count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + + return count; +} + +__inline int +msvc2010_snprintf(char *str, size_t size, const char *format, ...) +{ + int count; + va_list ap; + + va_start(ap, format); + count = msvc2010_vsnprintf(str, size, format, ap); + va_end(ap); + + return count; +} + +#endif + +/* Safer strcpy for fixed width char arrays. */ +#define lxw_strcpy(dest, src) \ + lxw_snprintf(dest, sizeof(dest), "%s", src) + +/* Define the queue.h structs for the formats list. */ +STAILQ_HEAD(lxw_formats, lxw_format); + +/* Define the queue.h structs for the generic data structs. */ +STAILQ_HEAD(lxw_tuples, lxw_tuple); +STAILQ_HEAD(lxw_custom_properties, lxw_custom_property); + +typedef struct lxw_tuple { + char *key; + char *value; + + STAILQ_ENTRY (lxw_tuple) list_pointers; +} lxw_tuple; + +/* Define custom property used in workbook.c and custom.c. */ +typedef struct lxw_custom_property { + + enum lxw_custom_property_types type; + char *name; + + union { + char *string; + double number; + int32_t integer; + uint8_t boolean; + lxw_datetime datetime; + } u; + + STAILQ_ENTRY (lxw_custom_property) list_pointers; + +} lxw_custom_property; + +/* Declarations required for unit testing. */ +#ifdef TESTING + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_COMMON_H__ */ diff --git a/src/include/xlsxwriter/content_types.h b/src/include/xlsxwriter/content_types.h new file mode 100644 index 0000000..b91f2a8 --- /dev/null +++ b/src/include/xlsxwriter/content_types.h @@ -0,0 +1,74 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * content_types - A libxlsxwriter library for creating Excel XLSX + * content_types files. + * + */ +#ifndef __LXW_CONTENT_TYPES_H__ +#define __LXW_CONTENT_TYPES_H__ + +#include +#include + +#include "common.h" + +#define LXW_APP_PACKAGE "application/vnd.openxmlformats-package." +#define LXW_APP_DOCUMENT "application/vnd.openxmlformats-officedocument." + +/* + * Struct to represent a content_types. + */ +typedef struct lxw_content_types { + + FILE *file; + + struct lxw_tuples *default_types; + struct lxw_tuples *overrides; + +} lxw_content_types; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_content_types *lxw_content_types_new(); +void lxw_content_types_free(lxw_content_types *content_types); +void lxw_content_types_assemble_xml_file(lxw_content_types *content_types); +void lxw_ct_add_default(lxw_content_types *content_types, const char *key, + const char *value); +void lxw_ct_add_override(lxw_content_types *content_types, const char *key, + const char *value); +void lxw_ct_add_worksheet_name(lxw_content_types *content_types, + const char *name); +void lxw_ct_add_chart_name(lxw_content_types *content_types, + const char *name); +void lxw_ct_add_drawing_name(lxw_content_types *content_types, + const char *name); +void lxw_ct_add_shared_strings(lxw_content_types *content_types); +void lxw_ct_add_calc_chain(lxw_content_types *content_types); +void lxw_ct_add_custom_properties(lxw_content_types *content_types); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _content_types_xml_declaration(lxw_content_types *self); +STATIC void _write_default(lxw_content_types *self, const char *ext, + const char *type); +STATIC void _write_override(lxw_content_types *self, const char *part_name, + const char *type); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_CONTENT_TYPES_H__ */ diff --git a/src/include/xlsxwriter/core.h b/src/include/xlsxwriter/core.h new file mode 100644 index 0000000..0d11452 --- /dev/null +++ b/src/include/xlsxwriter/core.h @@ -0,0 +1,51 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * core - A libxlsxwriter library for creating Excel XLSX core files. + * + */ +#ifndef __LXW_CORE_H__ +#define __LXW_CORE_H__ + +#include + +#include "workbook.h" +#include "common.h" + +/* + * Struct to represent a core. + */ +typedef struct lxw_core { + + FILE *file; + lxw_doc_properties *properties; + +} lxw_core; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_core *lxw_core_new(); +void lxw_core_free(lxw_core *core); +void lxw_core_assemble_xml_file(lxw_core *self); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _core_xml_declaration(lxw_core *self); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_CORE_H__ */ diff --git a/src/include/xlsxwriter/custom.h b/src/include/xlsxwriter/custom.h new file mode 100644 index 0000000..a5411a7 --- /dev/null +++ b/src/include/xlsxwriter/custom.h @@ -0,0 +1,52 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * custom - A libxlsxwriter library for creating Excel custom property files. + * + */ +#ifndef __LXW_CUSTOM_H__ +#define __LXW_CUSTOM_H__ + +#include + +#include "common.h" + +/* + * Struct to represent a custom property file object. + */ +typedef struct lxw_custom { + + FILE *file; + + struct lxw_custom_properties *custom_properties; + uint32_t pid; + +} lxw_custom; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_custom *lxw_custom_new(); +void lxw_custom_free(lxw_custom *custom); +void lxw_custom_assemble_xml_file(lxw_custom *self); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _custom_xml_declaration(lxw_custom *self); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_CUSTOM_H__ */ diff --git a/src/include/xlsxwriter/drawing.h b/src/include/xlsxwriter/drawing.h new file mode 100644 index 0000000..c6c5e11 --- /dev/null +++ b/src/include/xlsxwriter/drawing.h @@ -0,0 +1,111 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * drawing - A libxlsxwriter library for creating Excel XLSX drawing files. + * + */ +#ifndef __LXW_DRAWING_H__ +#define __LXW_DRAWING_H__ + +#include + +#include "common.h" + +STAILQ_HEAD(lxw_drawing_objects, lxw_drawing_object); + +enum lxw_drawing_types { + LXW_DRAWING_NONE = 0, + LXW_DRAWING_IMAGE, + LXW_DRAWING_CHART, + LXW_DRAWING_SHAPE +}; + +enum lxw_anchor_types { + LXW_ANCHOR_TYPE_NONE = 0, + LXW_ANCHOR_TYPE_IMAGE, + LXW_ANCHOR_TYPE_CHART +}; + +enum lxw_anchor_edit_types { + LXW_ANCHOR_EDIT_AS_NONE = 0, + LXW_ANCHOR_EDIT_AS_RELATIVE, + LXW_ANCHOR_EDIT_AS_ONE_CELL, + LXW_ANCHOR_EDIT_AS_ABSOLUTE +}; + +enum image_types { + LXW_IMAGE_UNKNOWN = 0, + LXW_IMAGE_PNG, + LXW_IMAGE_JPEG, + LXW_IMAGE_BMP +}; + +/* Coordinates used in a drawing object. */ +typedef struct lxw_drawing_coords { + uint32_t col; + uint32_t row; + double col_offset; + double row_offset; +} lxw_drawing_coords; + +/* Object to represent the properties of a drawing. */ +typedef struct lxw_drawing_object { + uint8_t anchor_type; + uint8_t edit_as; + struct lxw_drawing_coords from; + struct lxw_drawing_coords to; + uint32_t col_absolute; + uint32_t row_absolute; + uint32_t width; + uint32_t height; + uint8_t shape; + char *description; + char *url; + char *tip; + + STAILQ_ENTRY (lxw_drawing_object) list_pointers; + +} lxw_drawing_object; + +/* + * Struct to represent a collection of drawings. + */ +typedef struct lxw_drawing { + + FILE *file; + + uint8_t embedded; + + struct lxw_drawing_objects *drawing_objects; + +} lxw_drawing; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_drawing *lxw_drawing_new(); +void lxw_drawing_free(lxw_drawing *drawing); +void lxw_drawing_assemble_xml_file(lxw_drawing *self); +void lxw_free_drawing_object(struct lxw_drawing_object *drawing_object); +void lxw_add_drawing_object(lxw_drawing *drawing, + lxw_drawing_object *drawing_object); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _drawing_xml_declaration(lxw_drawing *self); +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_DRAWING_H__ */ diff --git a/src/include/xlsxwriter/format.h b/src/include/xlsxwriter/format.h new file mode 100644 index 0000000..dcee3e4 --- /dev/null +++ b/src/include/xlsxwriter/format.h @@ -0,0 +1,1214 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + */ + +/** + * @page format_page The Format object + * + * The Format object represents an the formatting properties that can be + * applied to a cell including: fonts, colors, patterns, + * borders, alignment and number formatting. + * + * See @ref format.h for full details of the functionality. + * + * @file format.h + * + * @brief Functions and properties for adding formatting to cells in Excel. + * + * This section describes the functions and properties that are available for + * formatting cells in Excel. + * + * The properties of a cell that can be formatted include: fonts, colors, + * patterns, borders, alignment and number formatting. + * + * @image html formats_intro.png + * + * Formats in `libxlsxwriter` are accessed via the lxw_format + * struct. Throughout this document these will be referred to simply as + * *Formats*. + * + * Formats are created by calling the workbook_add_format() method as + * follows: + * + * @code + * lxw_format *format = workbook_add_format(workbook); + * @endcode + * + * The members of the lxw_format struct aren't modified directly. Instead the + * format properties are set by calling the functions shown in this section. + * For example: + * + * @code + * // Create the Format. + * lxw_format *format = workbook_add_format(workbook); + * + * // Set some of the format properties. + * format_set_bold(format); + * format_set_font_color(format, LXW_COLOR_RED); + * + * // Use the format to change the text format in a cell. + * worksheet_write_string(worksheet, 0, 0, "Hello", format); + * + * @endcode + * + * The full range of formatting options that can be applied using + * `libxlsxwriter` are shown below. + * + */ +#ifndef __LXW_FORMAT_H__ +#define __LXW_FORMAT_H__ + +#include +#include +#include "hash_table.h" + +#include "common.h" + +/** + * @brief The type for RGB colors in libxlsxwriter. + * + * The type for RGB colors in libxlsxwriter. The valid range is `0x000000` + * (black) to `0xFFFFFF` (white). See @ref working_with_colors. + */ +typedef int32_t lxw_color_t; + +#define LXW_FORMAT_FIELD_LEN 128 +#define LXW_DEFAULT_FONT_NAME "Calibri" +#define LXW_DEFAULT_FONT_FAMILY 2 +#define LXW_DEFAULT_FONT_THEME 1 +#define LXW_PROPERTY_UNSET -1 +#define LXW_COLOR_UNSET -1 +#define LXW_COLOR_MASK 0xFFFFFF +#define LXW_MIN_FONT_SIZE 1.0 +#define LXW_MAX_FONT_SIZE 409.0 + +#define LXW_FORMAT_FIELD_COPY(dst, src) \ + do{ \ + strncpy(dst, src, LXW_FORMAT_FIELD_LEN -1); \ + dst[LXW_FORMAT_FIELD_LEN - 1] = '\0'; \ + } while (0) + +/** Format underline values for format_set_underline(). */ +enum lxw_format_underlines { + /** Single underline */ + LXW_UNDERLINE_SINGLE = 1, + + /** Double underline */ + LXW_UNDERLINE_DOUBLE, + + /** Single accounting underline */ + LXW_UNDERLINE_SINGLE_ACCOUNTING, + + /** Double accounting underline */ + LXW_UNDERLINE_DOUBLE_ACCOUNTING +}; + +/** Superscript and subscript values for format_set_font_script(). */ +enum lxw_format_scripts { + + /** Superscript font */ + LXW_FONT_SUPERSCRIPT = 1, + + /** Subscript font */ + LXW_FONT_SUBSCRIPT +}; + +/** Alignment values for format_set_align(). */ +enum lxw_format_alignments { + /** No alignment. Cell will use Excel's default for the data type */ + LXW_ALIGN_NONE = 0, + + /** Left horizontal alignment */ + LXW_ALIGN_LEFT, + + /** Center horizontal alignment */ + LXW_ALIGN_CENTER, + + /** Right horizontal alignment */ + LXW_ALIGN_RIGHT, + + /** Cell fill horizontal alignment */ + LXW_ALIGN_FILL, + + /** Justify horizontal alignment */ + LXW_ALIGN_JUSTIFY, + + /** Center Across horizontal alignment */ + LXW_ALIGN_CENTER_ACROSS, + + /** Left horizontal alignment */ + LXW_ALIGN_DISTRIBUTED, + + /** Top vertical alignment */ + LXW_ALIGN_VERTICAL_TOP, + + /** Bottom vertical alignment */ + LXW_ALIGN_VERTICAL_BOTTOM, + + /** Center vertical alignment */ + LXW_ALIGN_VERTICAL_CENTER, + + /** Justify vertical alignment */ + LXW_ALIGN_VERTICAL_JUSTIFY, + + /** Distributed vertical alignment */ + LXW_ALIGN_VERTICAL_DISTRIBUTED +}; + +enum lxw_format_diagonal_types { + LXW_DIAGONAL_BORDER_UP = 1, + LXW_DIAGONAL_BORDER_DOWN, + LXW_DIAGONAL_BORDER_UP_DOWN +}; + +/** Predefined values for common colors. */ +enum lxw_defined_colors { + /** Black */ + LXW_COLOR_BLACK = 0x1000000, + + /** Blue */ + LXW_COLOR_BLUE = 0x0000FF, + + /** Brown */ + LXW_COLOR_BROWN = 0x800000, + + /** Cyan */ + LXW_COLOR_CYAN = 0x00FFFF, + + /** Gray */ + LXW_COLOR_GRAY = 0x808080, + + /** Green */ + LXW_COLOR_GREEN = 0x008000, + + /** Lime */ + LXW_COLOR_LIME = 0x00FF00, + + /** Magenta */ + LXW_COLOR_MAGENTA = 0xFF00FF, + + /** Navy */ + LXW_COLOR_NAVY = 0x000080, + + /** Orange */ + LXW_COLOR_ORANGE = 0xFF6600, + + /** Pink */ + LXW_COLOR_PINK = 0xFF00FF, + + /** Purple */ + LXW_COLOR_PURPLE = 0x800080, + + /** Red */ + LXW_COLOR_RED = 0xFF0000, + + /** Silver */ + LXW_COLOR_SILVER = 0xC0C0C0, + + /** White */ + LXW_COLOR_WHITE = 0xFFFFFF, + + /** Yellow */ + LXW_COLOR_YELLOW = 0xFFFF00 +}; + +/** Pattern value for use with format_set_pattern(). */ +enum lxw_format_patterns { + /** Empty pattern */ + LXW_PATTERN_NONE = 0, + + /** Solid pattern */ + LXW_PATTERN_SOLID, + + /** Medium gray pattern */ + LXW_PATTERN_MEDIUM_GRAY, + + /** Dark gray pattern */ + LXW_PATTERN_DARK_GRAY, + + /** Light gray pattern */ + LXW_PATTERN_LIGHT_GRAY, + + /** Dark horizontal line pattern */ + LXW_PATTERN_DARK_HORIZONTAL, + + /** Dark vertical line pattern */ + LXW_PATTERN_DARK_VERTICAL, + + /** Dark diagonal stripe pattern */ + LXW_PATTERN_DARK_DOWN, + + /** Reverse dark diagonal stripe pattern */ + LXW_PATTERN_DARK_UP, + + /** Dark grid pattern */ + LXW_PATTERN_DARK_GRID, + + /** Dark trellis pattern */ + LXW_PATTERN_DARK_TRELLIS, + + /** Light horizontal Line pattern */ + LXW_PATTERN_LIGHT_HORIZONTAL, + + /** Light vertical line pattern */ + LXW_PATTERN_LIGHT_VERTICAL, + + /** Light diagonal stripe pattern */ + LXW_PATTERN_LIGHT_DOWN, + + /** Reverse light diagonal stripe pattern */ + LXW_PATTERN_LIGHT_UP, + + /** Light grid pattern */ + LXW_PATTERN_LIGHT_GRID, + + /** Light trellis pattern */ + LXW_PATTERN_LIGHT_TRELLIS, + + /** 12.5% gray pattern */ + LXW_PATTERN_GRAY_125, + + /** 6.25% gray pattern */ + LXW_PATTERN_GRAY_0625 +}; + +/** Cell border styles for use with format_set_border(). */ +enum lxw_format_borders { + /** No border */ + LXW_BORDER_NONE, + + /** Thin border style */ + LXW_BORDER_THIN, + + /** Medium border style */ + LXW_BORDER_MEDIUM, + + /** Dashed border style */ + LXW_BORDER_DASHED, + + /** Dotted border style */ + LXW_BORDER_DOTTED, + + /** Thick border style */ + LXW_BORDER_THICK, + + /** Double border style */ + LXW_BORDER_DOUBLE, + + /** Hair border style */ + LXW_BORDER_HAIR, + + /** Medium dashed border style */ + LXW_BORDER_MEDIUM_DASHED, + + /** Dash-dot border style */ + LXW_BORDER_DASH_DOT, + + /** Medium dash-dot border style */ + LXW_BORDER_MEDIUM_DASH_DOT, + + /** Dash-dot-dot border style */ + LXW_BORDER_DASH_DOT_DOT, + + /** Medium dash-dot-dot border style */ + LXW_BORDER_MEDIUM_DASH_DOT_DOT, + + /** Slant dash-dot border style */ + LXW_BORDER_SLANT_DASH_DOT +}; + +/** + * @brief Struct to represent the formatting properties of an Excel format. + * + * Formats in `libxlsxwriter` are accessed via this struct. + * + * The members of the lxw_format struct aren't modified directly. Instead the + * format properties are set by calling the functions shown in format.h. + * + * For example: + * + * @code + * // Create the Format. + * lxw_format *format = workbook_add_format(workbook); + * + * // Set some of the format properties. + * format_set_bold(format); + * format_set_font_color(format, LXW_COLOR_RED); + * + * // Use the format to change the text format in a cell. + * worksheet_write_string(worksheet, 0, 0, "Hello", format); + * + * @endcode + * + */ +typedef struct lxw_format { + + FILE *file; + + lxw_hash_table *xf_format_indices; + uint16_t *num_xf_formats; + + int32_t xf_index; + int32_t dxf_index; + + char num_format[LXW_FORMAT_FIELD_LEN]; + char font_name[LXW_FORMAT_FIELD_LEN]; + char font_scheme[LXW_FORMAT_FIELD_LEN]; + uint16_t num_format_index; + uint16_t font_index; + uint8_t has_font; + uint8_t has_dxf_font; + double font_size; + uint8_t bold; + uint8_t italic; + lxw_color_t font_color; + uint8_t underline; + uint8_t font_strikeout; + uint8_t font_outline; + uint8_t font_shadow; + uint8_t font_script; + uint8_t font_family; + uint8_t font_charset; + uint8_t font_condense; + uint8_t font_extend; + uint8_t theme; + uint8_t hyperlink; + + uint8_t hidden; + uint8_t locked; + + uint8_t text_h_align; + uint8_t text_wrap; + uint8_t text_v_align; + uint8_t text_justlast; + int16_t rotation; + + lxw_color_t fg_color; + lxw_color_t bg_color; + uint8_t pattern; + uint8_t has_fill; + uint8_t has_dxf_fill; + int32_t fill_index; + int32_t fill_count; + + int32_t border_index; + uint8_t has_border; + uint8_t has_dxf_border; + int32_t border_count; + + uint8_t bottom; + uint8_t diag_border; + uint8_t diag_type; + uint8_t left; + uint8_t right; + uint8_t top; + lxw_color_t bottom_color; + lxw_color_t diag_color; + lxw_color_t left_color; + lxw_color_t right_color; + lxw_color_t top_color; + + uint8_t indent; + uint8_t shrink; + uint8_t merge_range; + uint8_t reading_order; + uint8_t just_distrib; + uint8_t color_indexed; + uint8_t font_only; + + STAILQ_ENTRY (lxw_format) list_pointers; +} lxw_format; + +/* + * Struct to represent the font component of a format. + */ +typedef struct lxw_font { + + char font_name[LXW_FORMAT_FIELD_LEN]; + double font_size; + uint8_t bold; + uint8_t italic; + uint8_t underline; + uint8_t font_strikeout; + uint8_t font_outline; + uint8_t font_shadow; + uint8_t font_script; + uint8_t font_family; + uint8_t font_charset; + uint8_t font_condense; + uint8_t font_extend; + lxw_color_t font_color; +} lxw_font; + +/* + * Struct to represent the border component of a format. + */ +typedef struct lxw_border { + + uint8_t bottom; + uint8_t diag_border; + uint8_t diag_type; + uint8_t left; + uint8_t right; + uint8_t top; + + lxw_color_t bottom_color; + lxw_color_t diag_color; + lxw_color_t left_color; + lxw_color_t right_color; + lxw_color_t top_color; + +} lxw_border; + +/* + * Struct to represent the fill component of a format. + */ +typedef struct lxw_fill { + + lxw_color_t fg_color; + lxw_color_t bg_color; + uint8_t pattern; + +} lxw_fill; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_format *lxw_format_new(); +void lxw_format_free(lxw_format *format); +int32_t lxw_format_get_xf_index(lxw_format *format); +lxw_font *lxw_format_get_font_key(lxw_format *format); +lxw_border *lxw_format_get_border_key(lxw_format *format); +lxw_fill *lxw_format_get_fill_key(lxw_format *format); + +lxw_color_t lxw_format_check_color(lxw_color_t color); + +/** + * @brief Set the font used in the cell. + * + * @param format Pointer to a Format instance. + * @param font_name Cell font name. + * + * Specify the font used used in the cell format: + * + * @code + * format_set_font_name(format, "Avenir Black Oblique"); + * @endcode + * + * @image html format_set_font_name.png + * + * Excel can only display fonts that are installed on the system that it is + * running on. Therefore it is generally best to use the fonts that come as + * standard with Excel such as Calibri, Times New Roman and Courier New. + * + * The default font in Excel 2007, and later, is Calibri. + */ +void format_set_font_name(lxw_format *format, const char *font_name); + +/** + * @brief Set the size of the font used in the cell. + * + * @param format Pointer to a Format instance. + * @param size The cell font size. + * + * Set the font size of the cell format: + * + * @code + * format_set_font_size(format, 30); + * @endcode + * + * @image html format_font_size.png + * + * Excel adjusts the height of a row to accommodate the largest font + * size in the row. You can also explicitly specify the height of a + * row using the worksheet_set_row() function. + */ +void format_set_font_size(lxw_format *format, double size); + +/** + * @brief Set the color of the font used in the cell. + * + * @param format Pointer to a Format instance. + * @param color The cell font color. + * + * + * Set the font color: + * + * @code + * format = workbook_add_format(workbook); + * format_set_font_color(format, LXW_COLOR_RED); + * + * worksheet_write_string(worksheet, 0, 0, "Wheelbarrow", format); + * @endcode + * + * @image html format_font_color.png + * + * The color should be an RGB integer value, see @ref working_with_colors. + * + * @note + * The format_set_font_color() method is used to set the font color in a + * cell. To set the color of a cell background use the format_set_bg_color() + * and format_set_pattern() methods. + */ +void format_set_font_color(lxw_format *format, lxw_color_t color); + +/** + * @brief Turn on bold for the format font. + * + * @param format Pointer to a Format instance. + * + * Set the bold property of the font: + * + * @code + * format = workbook_add_format(workbook); + * format_set_bold(format); + * + * worksheet_write_string(worksheet, 0, 0, "Bold Text", format); + * @endcode + * + * @image html format_font_bold.png + */ +void format_set_bold(lxw_format *format); + +/** + * @brief Turn on italic for the format font. + * + * @param format Pointer to a Format instance. + * + * Set the italic property of the font: + * + * @code + * format = workbook_add_format(workbook); + * format_set_italic(format); + * + * worksheet_write_string(worksheet, 0, 0, "Italic Text", format); + * @endcode + * + * @image html format_font_italic.png + */ +void format_set_italic(lxw_format *format); + +/** + * @brief Turn on underline for the format: + * + * @param format Pointer to a Format instance. + * @param style Underline style. + * + * Set the underline property of the format: + * + * @code + * format_set_underline(format, LXW_UNDERLINE_SINGLE); + * @endcode + * + * @image html format_font_underlined.png + * + * The available underline styles are: + * + * - #LXW_UNDERLINE_SINGLE + * - #LXW_UNDERLINE_DOUBLE + * - #LXW_UNDERLINE_SINGLE_ACCOUNTING + * - #LXW_UNDERLINE_DOUBLE_ACCOUNTING + * + */ +void format_set_underline(lxw_format *format, uint8_t style); + +/** + * @brief Set the strikeout property of the font. + * + * @param format Pointer to a Format instance. + * + * @image html format_font_strikeout.png + * + */ +void format_set_font_strikeout(lxw_format *format); + +/** + * @brief Set the superscript/subscript property of the font. + * + * @param format Pointer to a Format instance. + * @param style Superscript or subscript style. + * + * Set the superscript o subscript property of the font. + * + * @image html format_font_script.png + * + * The available script styles are: + * + * - #LXW_FONT_SUPERSCRIPT + * - #LXW_FONT_SUBSCRIPT + */ +void format_set_font_script(lxw_format *format, uint8_t style); + +/** + * @brief Set the number format for a cell. + * + * @param format Pointer to a Format instance. + * @param num_format The cell number format string. + * + * This method is used to define the numerical format of a number in + * Excel. It controls whether a number is displayed as an integer, a + * floating point number, a date, a currency value or some other user + * defined format. + * + * The numerical format of a cell can be specified by using a format + * string: + * + * @code + * format = workbook_add_format(workbook); + * format_set_num_format(format, "d mmm yyyy"); + * @endcode + * + * Format strings can control any aspect of number formatting allowed by Excel: + * + * @dontinclude format_num_format.c + * @skipline set_num_format + * @until 1209 + * + * @image html format_set_num_format.png + * + * The number system used for dates is described in @ref working_with_dates. + * + * For more information on number formats in Excel refer to the + * [Microsoft documentation on cell formats](http://office.microsoft.com/en-gb/assistance/HP051995001033.aspx). + */ +void format_set_num_format(lxw_format *format, const char *num_format); + +/** + * @brief Set the Excel built-in number format for a cell. + * + * @param format Pointer to a Format instance. + * @param index The built-in number format index for the cell. + * + * This function is similar to format_set_num_format() except that it takes an + * index to a limited number of Excel's built-in number formats instead of a + * user defined format string: + * + * @code + * format = workbook_add_format(workbook); + * format_set_num_format(format, 0x0F); // d-mmm-yy + * @endcode + * + * @note + * Unless you need to specifically access one of Excel's built-in number + * formats the format_set_num_format() function above is a better + * solution. The format_set_num_format_index() function is mainly included for + * backward compatibility and completeness. + * + * The Excel built-in number formats as shown in the table below: + * + * | Index | Index | Format String | + * | ----- | ----- | ---------------------------------------------------- | + * | 0 | 0x00 | `General` | + * | 1 | 0x01 | `0` | + * | 2 | 0x02 | `0.00` | + * | 3 | 0x03 | `#,##0` | + * | 4 | 0x04 | `#,##0.00` | + * | 5 | 0x05 | `($#,##0_);($#,##0)` | + * | 6 | 0x06 | `($#,##0_);[Red]($#,##0)` | + * | 7 | 0x07 | `($#,##0.00_);($#,##0.00)` | + * | 8 | 0x08 | `($#,##0.00_);[Red]($#,##0.00)` | + * | 9 | 0x09 | `0%` | + * | 10 | 0x0a | `0.00%` | + * | 11 | 0x0b | `0.00E+00` | + * | 12 | 0x0c | `# ?/?` | + * | 13 | 0x0d | `# ??/??` | + * | 14 | 0x0e | `m/d/yy` | + * | 15 | 0x0f | `d-mmm-yy` | + * | 16 | 0x10 | `d-mmm` | + * | 17 | 0x11 | `mmm-yy` | + * | 18 | 0x12 | `h:mm AM/PM` | + * | 19 | 0x13 | `h:mm:ss AM/PM` | + * | 20 | 0x14 | `h:mm` | + * | 21 | 0x15 | `h:mm:ss` | + * | 22 | 0x16 | `m/d/yy h:mm` | + * | ... | ... | ... | + * | 37 | 0x25 | `(#,##0_);(#,##0)` | + * | 38 | 0x26 | `(#,##0_);[Red](#,##0)` | + * | 39 | 0x27 | `(#,##0.00_);(#,##0.00)` | + * | 40 | 0x28 | `(#,##0.00_);[Red](#,##0.00)` | + * | 41 | 0x29 | `_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)` | + * | 42 | 0x2a | `_($* #,##0_);_($* (#,##0);_($* "-"_);_(@_)` | + * | 43 | 0x2b | `_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)` | + * | 44 | 0x2c | `_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)` | + * | 45 | 0x2d | `mm:ss` | + * | 46 | 0x2e | `[h]:mm:ss` | + * | 47 | 0x2f | `mm:ss.0` | + * | 48 | 0x30 | `##0.0E+0` | + * | 49 | 0x31 | `@` | + * + * @note + * - Numeric formats 23 to 36 are not documented by Microsoft and may differ + * in international versions. The listed date and currency formats may also + * vary depending on system settings. + * - The dollar sign in the above format appears as the defined local currency + * symbol. + * - These formats can also be set via format_set_num_format(). + */ +void format_set_num_format_index(lxw_format *format, uint8_t index); + +/** + * @brief Set the cell unlocked state. + * + * @param format Pointer to a Format instance. + * + * This property can be used to allow modification of a cell in a protected + * worksheet. In Excel, cell locking is turned on by default for all + * cells. However, it only has an effect if the worksheet has been protected + * using the worksheet worksheet_protect() function: + * + * @code + * format = workbook_add_format(workbook); + * format_set_unlocked(format); + * + * // Enable worksheet protection, without password or options. + * worksheet_protect(worksheet, NULL, NULL); + * + * // This cell cannot be edited. + * worksheet_write_formula(worksheet, 0, 0, "=1+2", NULL); + * + * // This cell can be edited. + * worksheet_write_formula(worksheet, 1, 0, "=1+2", format); + * @endcode + */ +void format_set_unlocked(lxw_format *format); + +/** + * @brief Hide formulas in a cell. + * + * @param format Pointer to a Format instance. + * + * This property is used to hide a formula while still displaying its + * result. This is generally used to hide complex calculations from end users + * who are only interested in the result. It only has an effect if the + * worksheet has been protected using the worksheet worksheet_protect() + * function: + * + * @code + * format = workbook_add_format(workbook); + * format_set_hidden(format); + * + * // Enable worksheet protection, without password or options. + * worksheet_protect(worksheet, NULL, NULL); + * + * // The formula in this cell isn't visible. + * worksheet_write_formula(worksheet, 0, 0, "=1+2", format); + * @endcode + */ +void format_set_hidden(lxw_format *format); + +/** + * @brief Set the alignment for data in the cell. + * + * @param format Pointer to a Format instance. + * @param alignment The horizontal and or vertical alignment direction. + * + * This method is used to set the horizontal and vertical text alignment within a + * cell. The following are the available horizontal alignments: + * + * - #LXW_ALIGN_LEFT + * - #LXW_ALIGN_CENTER + * - #LXW_ALIGN_RIGHT + * - #LXW_ALIGN_FILL + * - #LXW_ALIGN_JUSTIFY + * - #LXW_ALIGN_CENTER_ACROSS + * - #LXW_ALIGN_DISTRIBUTED + * + * The following are the available vertical alignments: + * + * - #LXW_ALIGN_VERTICAL_TOP + * - #LXW_ALIGN_VERTICAL_BOTTOM + * - #LXW_ALIGN_VERTICAL_CENTER + * - #LXW_ALIGN_VERTICAL_JUSTIFY + * - #LXW_ALIGN_VERTICAL_DISTRIBUTED + * + * As in Excel, vertical and horizontal alignments can be combined: + * + * @code + * format = workbook_add_format(workbook); + * + * format_set_align(format, LXW_ALIGN_CENTER); + * format_set_align(format, LXW_ALIGN_VERTICAL_CENTER); + * + * worksheet_set_row(0, 30); + * worksheet_write_string(worksheet, 0, 0, "Some Text", format); + * @endcode + * + * @image html format_font_align.png + * + * Text can be aligned across two or more adjacent cells using the + * center_across property. However, for genuine merged cells it is better to + * use the worksheet_merge_range() worksheet method. + * + * The vertical justify option can be used to provide automatic text wrapping + * in a cell. The height of the cell will be adjusted to accommodate the + * wrapped text. To specify where the text wraps use the + * format_set_text_wrap() method. + */ +void format_set_align(lxw_format *format, uint8_t alignment); + +/** + * @brief Wrap text in a cell. + * + * Turn text wrapping on for text in a cell. + * + * @code + * format = workbook_add_format(workbook); + * format_set_text_wrap(format); + * + * worksheet_write_string(worksheet, 0, 0, "Some long text to wrap in a cell", format); + * @endcode + * + * If you wish to control where the text is wrapped you can add newline characters + * to the string: + * + * @code + * format = workbook_add_format(workbook); + * format_set_text_wrap(format); + * + * worksheet_write_string(worksheet, 0, 0, "It's\na bum\nwrap", format); + * @endcode + * + * @image html format_font_text_wrap.png + * + * Excel will adjust the height of the row to accommodate the wrapped text. A + * similar effect can be obtained without newlines using the + * format_set_align() function with #LXW_ALIGN_VERTICAL_JUSTIFY. + */ +void format_set_text_wrap(lxw_format *format); + +/** + * @brief Set the rotation of the text in a cell. + * + * @param format Pointer to a Format instance. + * @param angle Rotation angle in the range -90 to 90 and 270. + * + * Set the rotation of the text in a cell. The rotation can be any angle in the + * range -90 to 90 degrees: + * + * @code + * format = workbook_add_format(workbook); + * format_set_rotation(format, 30); + * + * worksheet_write_string(worksheet, 0, 0, "This text is rotated", format); + * @endcode + * + * @image html format_font_text_rotated.png + * + * The angle 270 is also supported. This indicates text where the letters run from + * top to bottom. + */ +void format_set_rotation(lxw_format *format, int16_t angle); + +/** + * @brief Set the cell text indentation level. + * + * @param format Pointer to a Format instance. + * @param level Indentation level. + * + * This method can be used to indent text in a cell. The argument, which should be + * an integer, is taken as the level of indentation: + * + * @code + * format1 = workbook_add_format(workbook); + * format2 = workbook_add_format(workbook); + * + * format_set_indent(format1, 1); + * format_set_indent(format2, 2); + * + * worksheet_write_string(worksheet, 0, 0, "This text is indented 1 level", format1); + * worksheet_write_string(worksheet, 1, 0, "This text is indented 2 levels", format2); + * @endcode + * + * @image html text_indent.png + * + * @note + * Indentation is a horizontal alignment property. It will override any other + * horizontal properties but it can be used in conjunction with vertical + * properties. + */ +void format_set_indent(lxw_format *format, uint8_t level); + +/** + * @brief Turn on the text "shrink to fit" for a cell. + * + * @param format Pointer to a Format instance. + * + * This method can be used to shrink text so that it fits in a cell: + * + * @code + * format = workbook_add_format(workbook); + * format_set_shrink(format); + * + * worksheet_write_string(worksheet, 0, 0, "Honey, I shrunk the text!", format); + * @endcode + */ +void format_set_shrink(lxw_format *format); + +/** + * @brief Set the background fill pattern for a cell + * + * @param format Pointer to a Format instance. + * @param index Pattern index. + * + * Set the background pattern for a cell. + * + * The most common pattern is a solid fill of the background color: + * + * @code + * format = workbook_add_format(workbook); + * + * format_set_pattern (format, LXW_PATTERN_SOLID); + * format_set_bg_color(format, LXW_COLOR_YELLOW); + * @endcode + * + * The available fill patterns are: + * + * Fill Type | Define + * ----------------------------- | ----------------------------- + * Solid | #LXW_PATTERN_SOLID + * Medium gray | #LXW_PATTERN_MEDIUM_GRAY + * Dark gray | #LXW_PATTERN_DARK_GRAY + * Light gray | #LXW_PATTERN_LIGHT_GRAY + * Dark horizontal line | #LXW_PATTERN_DARK_HORIZONTAL + * Dark vertical line | #LXW_PATTERN_DARK_VERTICAL + * Dark diagonal stripe | #LXW_PATTERN_DARK_DOWN + * Reverse dark diagonal stripe | #LXW_PATTERN_DARK_UP + * Dark grid | #LXW_PATTERN_DARK_GRID + * Dark trellis | #LXW_PATTERN_DARK_TRELLIS + * Light horizontal line | #LXW_PATTERN_LIGHT_HORIZONTAL + * Light vertical line | #LXW_PATTERN_LIGHT_VERTICAL + * Light diagonal stripe | #LXW_PATTERN_LIGHT_DOWN + * Reverse light diagonal stripe | #LXW_PATTERN_LIGHT_UP + * Light grid | #LXW_PATTERN_LIGHT_GRID + * Light trellis | #LXW_PATTERN_LIGHT_TRELLIS + * 12.5% gray | #LXW_PATTERN_GRAY_125 + * 6.25% gray | #LXW_PATTERN_GRAY_0625 + * + */ +void format_set_pattern(lxw_format *format, uint8_t index); + +/** + * @brief Set the pattern background color for a cell. + * + * @param format Pointer to a Format instance. + * @param color The cell pattern background color. + * + * The format_set_bg_color() method can be used to set the background color of + * a pattern. Patterns are defined via the format_set_pattern() method. If a + * pattern hasn't been defined then a solid fill pattern is used as the + * default. + * + * Here is an example of how to set up a solid fill in a cell: + * + * @code + * format = workbook_add_format(workbook); + * + * format_set_pattern (format, LXW_PATTERN_SOLID); + * format_set_bg_color(format, LXW_COLOR_GREEN); + * + * worksheet_write_string(worksheet, 0, 0, "Ray", format); + * @endcode + * + * @image html formats_set_bg_color.png + * + * The color should be an RGB integer value, see @ref working_with_colors. + * + */ +void format_set_bg_color(lxw_format *format, lxw_color_t color); + +/** + * @brief Set the pattern foreground color for a cell. + * + * @param format Pointer to a Format instance. + * @param color The cell pattern foreground color. + * + * The format_set_fg_color() method can be used to set the foreground color of + * a pattern. + * + * The color should be an RGB integer value, see @ref working_with_colors. + * + */ +void format_set_fg_color(lxw_format *format, lxw_color_t color); + +/** + * @brief Set the cell border style. + * + * @param format Pointer to a Format instance. + * @param style Border style index. + * + * Set the cell border style: + * + * @code + * format_set_border(format, LXW_BORDER_THIN); + * @endcode + * + * Individual border elements can be configured using the following functions with + * the same parameters: + * + * - format_set_bottom() + * - format_set_top() + * - format_set_left() + * - format_set_right() + * + * A cell border is comprised of a border on the bottom, top, left and right. + * These can be set to the same value using format_set_border() or + * individually using the relevant method calls shown above. + * + * The following border styles are available: + * + * - #LXW_BORDER_THIN + * - #LXW_BORDER_MEDIUM + * - #LXW_BORDER_DASHED + * - #LXW_BORDER_DOTTED + * - #LXW_BORDER_THICK + * - #LXW_BORDER_DOUBLE + * - #LXW_BORDER_HAIR + * - #LXW_BORDER_MEDIUM_DASHED + * - #LXW_BORDER_DASH_DOT + * - #LXW_BORDER_MEDIUM_DASH_DOT + * - #LXW_BORDER_DASH_DOT_DOT + * - #LXW_BORDER_MEDIUM_DASH_DOT_DOT + * - #LXW_BORDER_SLANT_DASH_DOT + * + * The most commonly used style is the `thin` style. + */ +void format_set_border(lxw_format *format, uint8_t style); + +/** + * @brief Set the cell bottom border style. + * + * @param format Pointer to a Format instance. + * @param style Border style index. + * + * Set the cell bottom border style. See format_set_border() for details on the + * border styles. + */ +void format_set_bottom(lxw_format *format, uint8_t style); + +/** + * @brief Set the cell top border style. + * + * @param format Pointer to a Format instance. + * @param style Border style index. + * + * Set the cell top border style. See format_set_border() for details on the border + * styles. + */ +void format_set_top(lxw_format *format, uint8_t style); + +/** + * @brief Set the cell left border style. + * + * @param format Pointer to a Format instance. + * @param style Border style index. + * + * Set the cell left border style. See format_set_border() for details on the + * border styles. + */ +void format_set_left(lxw_format *format, uint8_t style); + +/** + * @brief Set the cell right border style. + * + * @param format Pointer to a Format instance. + * @param style Border style index. + * + * Set the cell right border style. See format_set_border() for details on the + * border styles. + */ +void format_set_right(lxw_format *format, uint8_t style); + +/** + * @brief Set the color of the cell border. + * + * @param format Pointer to a Format instance. + * @param color The cell border color. + * + * Individual border elements can be configured using the following methods with + * the same parameters: + * + * - format_set_bottom_color() + * - format_set_top_color() + * - format_set_left_color() + * - format_set_right_color() + * + * Set the color of the cell borders. A cell border is comprised of a border + * on the bottom, top, left and right. These can be set to the same color + * using format_set_border_color() or individually using the relevant method + * calls shown above. + * + * The color should be an RGB integer value, see @ref working_with_colors. + */ +void format_set_border_color(lxw_format *format, lxw_color_t color); + +/** + * @brief Set the color of the bottom cell border. + * + * @param format Pointer to a Format instance. + * @param color The cell border color. + * + * See format_set_border_color() for details on the border colors. + */ +void format_set_bottom_color(lxw_format *format, lxw_color_t color); + +/** + * @brief Set the color of the top cell border. + * + * @param format Pointer to a Format instance. + * @param color The cell border color. + * + * See format_set_border_color() for details on the border colors. + */ +void format_set_top_color(lxw_format *format, lxw_color_t color); + +/** + * @brief Set the color of the left cell border. + * + * @param format Pointer to a Format instance. + * @param color The cell border color. + * + * See format_set_border_color() for details on the border colors. + */ +void format_set_left_color(lxw_format *format, lxw_color_t color); + +/** + * @brief Set the color of the right cell border. + * + * @param format Pointer to a Format instance. + * @param color The cell border color. + * + * See format_set_border_color() for details on the border colors. + */ +void format_set_right_color(lxw_format *format, lxw_color_t color); + +void format_set_diag_type(lxw_format *format, uint8_t value); +void format_set_diag_color(lxw_format *format, lxw_color_t color); +void format_set_diag_border(lxw_format *format, uint8_t value); +void format_set_font_outline(lxw_format *format); +void format_set_font_shadow(lxw_format *format); +void format_set_font_family(lxw_format *format, uint8_t value); +void format_set_font_charset(lxw_format *format, uint8_t value); +void format_set_font_scheme(lxw_format *format, const char *font_scheme); +void format_set_font_condense(lxw_format *format); +void format_set_font_extend(lxw_format *format); +void format_set_reading_order(lxw_format *format, uint8_t value); +void format_set_theme(lxw_format *format, uint8_t value); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_FORMAT_H__ */ diff --git a/src/include/xlsxwriter/hash_table.h b/src/include/xlsxwriter/hash_table.h new file mode 100644 index 0000000..fcba0ba --- /dev/null +++ b/src/include/xlsxwriter/hash_table.h @@ -0,0 +1,76 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * hash_table - Hash table functions for libxlsxwriter. + * + */ + +#ifndef __LXW_HASH_TABLE_H__ +#define __LXW_HASH_TABLE_H__ + +#include "common.h" + +/* Macro to loop over hash table elements in insertion order. */ +#define LXW_FOREACH_ORDERED(elem, hash_table) \ + STAILQ_FOREACH((elem), (hash_table)->order_list, lxw_hash_order_pointers) + +/* List declarations. */ +STAILQ_HEAD(lxw_hash_order_list, lxw_hash_element); +SLIST_HEAD(lxw_hash_bucket_list, lxw_hash_element); + +/* LXW_HASH hash table struct. */ +typedef struct lxw_hash_table { + uint32_t num_buckets; + uint32_t used_buckets; + uint32_t unique_count; + uint8_t free_key; + uint8_t free_value; + + struct lxw_hash_order_list *order_list; + struct lxw_hash_bucket_list **buckets; +} lxw_hash_table; + +/* + * LXW_HASH table element struct. + * + * The hash elements contain pointers to allow them to be stored in + * lists in the the hash table buckets and also pointers to track the + * insertion order in a separate list. + */ +typedef struct lxw_hash_element { + void *key; + void *value; + + STAILQ_ENTRY (lxw_hash_element) lxw_hash_order_pointers; + SLIST_ENTRY (lxw_hash_element) lxw_hash_list_pointers; +} lxw_hash_element; + + + /* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_hash_element *lxw_hash_key_exists(lxw_hash_table *lxw_hash, void *key, + size_t key_len); +lxw_hash_element *lxw_insert_hash_element(lxw_hash_table *lxw_hash, void *key, + void *value, size_t key_len); +lxw_hash_table *lxw_hash_new(uint32_t num_buckets, uint8_t free_key, + uint8_t free_value); +void lxw_hash_free(lxw_hash_table *lxw_hash); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +#endif + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_HASH_TABLE_H__ */ diff --git a/src/include/xlsxwriter/packager.h b/src/include/xlsxwriter/packager.h new file mode 100644 index 0000000..f332ba0 --- /dev/null +++ b/src/include/xlsxwriter/packager.h @@ -0,0 +1,85 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * packager - A libxlsxwriter library for creating Excel XLSX packager files. + * + */ +#ifndef __LXW_PACKAGER_H__ +#define __LXW_PACKAGER_H__ + +#include + +#ifdef USE_SYSTEM_MINIZIP +#include "minizip/zip.h" +#else +#include "third_party/zip.h" +#endif + +#include "common.h" +#include "workbook.h" +#include "worksheet.h" +#include "shared_strings.h" +#include "app.h" +#include "core.h" +#include "custom.h" +#include "theme.h" +#include "styles.h" +#include "format.h" +#include "content_types.h" +#include "relationships.h" + +#define LXW_ZIP_BUFFER_SIZE (16384) + +/* If zlib returns Z_ERRNO then errno is set and we can trap that. Otherwise + * return a default libxlsxwriter error. */ +#define RETURN_ON_ZIP_ERROR(err, default_err) \ + if (err == Z_ERRNO) \ + return LXW_ERROR_ZIP_FILE_OPERATION; \ + else \ + return default_err; + +/* + * Struct to represent a packager. + */ +typedef struct lxw_packager { + + FILE *file; + lxw_workbook *workbook; + + size_t buffer_size; + zipFile zipfile; + zip_fileinfo zipfile_info; + char *filename; + char *buffer; + char *tmpdir; + + uint16_t chart_count; + uint16_t drawing_count; + +} lxw_packager; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_packager *lxw_packager_new(const char *filename, char *tmpdir); +void lxw_packager_free(lxw_packager *packager); +lxw_error lxw_create_package(lxw_packager *self); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_PACKAGER_H__ */ diff --git a/src/include/xlsxwriter/relationships.h b/src/include/xlsxwriter/relationships.h new file mode 100644 index 0000000..c1431e9 --- /dev/null +++ b/src/include/xlsxwriter/relationships.h @@ -0,0 +1,77 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * relationships - A libxlsxwriter library for creating Excel XLSX + * relationships files. + * + */ +#ifndef __LXW_RELATIONSHIPS_H__ +#define __LXW_RELATIONSHIPS_H__ + +#include + +#include "common.h" + +/* Define the queue.h STAILQ structs for the generic data structs. */ +STAILQ_HEAD(lxw_rel_tuples, lxw_rel_tuple); + +typedef struct lxw_rel_tuple { + + char *type; + char *target; + char *target_mode; + + STAILQ_ENTRY (lxw_rel_tuple) list_pointers; + +} lxw_rel_tuple; + +/* + * Struct to represent a relationships. + */ +typedef struct lxw_relationships { + + FILE *file; + + uint32_t rel_id; + struct lxw_rel_tuples *relationships; + +} lxw_relationships; + + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_relationships *lxw_relationships_new(); +void lxw_free_relationships(lxw_relationships *relationships); +void lxw_relationships_assemble_xml_file(lxw_relationships *self); + +void lxw_add_document_relationship(lxw_relationships *self, const char *type, + const char *target); +void lxw_add_package_relationship(lxw_relationships *self, const char *type, + const char *target); +void lxw_add_ms_package_relationship(lxw_relationships *self, + const char *type, const char *target); +void lxw_add_worksheet_relationship(lxw_relationships *self, const char *type, + const char *target, + const char *target_mode); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _relationships_xml_declaration(lxw_relationships *self); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_RELATIONSHIPS_H__ */ diff --git a/src/include/xlsxwriter/shared_strings.h b/src/include/xlsxwriter/shared_strings.h new file mode 100644 index 0000000..97b857e --- /dev/null +++ b/src/include/xlsxwriter/shared_strings.h @@ -0,0 +1,83 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * shared_strings - A libxlsxwriter library for creating Excel XLSX + * sst files. + * + */ +#ifndef __LXW_SST_H__ +#define __LXW_SST_H__ + +#include +#include + +#include "common.h" + +/* Define a tree.h RB structure for storing shared strings. */ +RB_HEAD(sst_rb_tree, sst_element); + +/* Define a queue.h structure for storing shared strings in insertion order. */ +STAILQ_HEAD(sst_order_list, sst_element); + +/* Wrapper around RB_GENERATE_STATIC from tree.h to avoid unused function + * warnings and to avoid portability issues with the _unused attribute. */ +#define LXW_RB_GENERATE_ELEMENT(name, type, field, cmp) \ + RB_GENERATE_INSERT_COLOR(name, type, field, static) \ + RB_GENERATE_INSERT(name, type, field, cmp, static) \ + /* Add unused struct to allow adding a semicolon */ \ + struct lxw_rb_generate_element{int unused;} + +/* + * Elements of the SST table. They contain pointers to allow them to + * be stored in a RB tree and also pointers to track the insertion order + * in a separate list. + */ +struct sst_element { + uint32_t index; + char *string; + + STAILQ_ENTRY (sst_element) sst_order_pointers; + RB_ENTRY (sst_element) sst_tree_pointers; +}; + +/* + * Struct to represent a sst. + */ +typedef struct lxw_sst { + FILE *file; + + uint32_t string_count; + uint32_t unique_count; + + struct sst_order_list *order_list; + struct sst_rb_tree *rb_tree; + +} lxw_sst; + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_sst *lxw_sst_new(); +void lxw_sst_free(lxw_sst *sst); +struct sst_element *lxw_get_sst_index(lxw_sst *sst, const char *string); +void lxw_sst_assemble_xml_file(lxw_sst *self); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _sst_xml_declaration(lxw_sst *self); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_SST_H__ */ diff --git a/src/include/xlsxwriter/styles.h b/src/include/xlsxwriter/styles.h new file mode 100644 index 0000000..52b819c --- /dev/null +++ b/src/include/xlsxwriter/styles.h @@ -0,0 +1,77 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * styles - A libxlsxwriter library for creating Excel XLSX styles files. + * + */ +#ifndef __LXW_STYLES_H__ +#define __LXW_STYLES_H__ + +#include + +#include "format.h" + +/* + * Struct to represent a styles. + */ +typedef struct lxw_styles { + + FILE *file; + uint32_t font_count; + uint32_t xf_count; + uint32_t dxf_count; + uint32_t num_format_count; + uint32_t border_count; + uint32_t fill_count; + struct lxw_formats *xf_formats; + struct lxw_formats *dxf_formats; + +} lxw_styles; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_styles *lxw_styles_new(); +void lxw_styles_free(lxw_styles *styles); +void lxw_styles_assemble_xml_file(lxw_styles *self); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _styles_xml_declaration(lxw_styles *self); +STATIC void _write_style_sheet(lxw_styles *self); +STATIC void _write_font_size(lxw_styles *self, double font_size); +STATIC void _write_font_color_theme(lxw_styles *self, uint8_t theme); +STATIC void _write_font_name(lxw_styles *self, const char *font_name); +STATIC void _write_font_family(lxw_styles *self, uint8_t font_family); +STATIC void _write_font_scheme(lxw_styles *self, const char *font_scheme); +STATIC void _write_font(lxw_styles *self, lxw_format *format); +STATIC void _write_fonts(lxw_styles *self); +STATIC void _write_default_fill(lxw_styles *self, const char *pattern); +STATIC void _write_fills(lxw_styles *self); +STATIC void _write_border(lxw_styles *self, lxw_format *format); +STATIC void _write_borders(lxw_styles *self); +STATIC void _write_style_xf(lxw_styles *self); +STATIC void _write_cell_style_xfs(lxw_styles *self); +STATIC void _write_xf(lxw_styles *self, lxw_format *format); +STATIC void _write_cell_xfs(lxw_styles *self); +STATIC void _write_cell_style(lxw_styles *self); +STATIC void _write_cell_styles(lxw_styles *self); +STATIC void _write_dxfs(lxw_styles *self); +STATIC void _write_table_styles(lxw_styles *self); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_STYLES_H__ */ diff --git a/src/include/xlsxwriter/theme.h b/src/include/xlsxwriter/theme.h new file mode 100644 index 0000000..79791e4 --- /dev/null +++ b/src/include/xlsxwriter/theme.h @@ -0,0 +1,47 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * theme - A libxlsxwriter library for creating Excel XLSX theme files. + * + */ +#ifndef __LXW_THEME_H__ +#define __LXW_THEME_H__ + +#include + +#include "common.h" + +/* + * Struct to represent a theme. + */ +typedef struct lxw_theme { + + FILE *file; +} lxw_theme; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_theme *lxw_theme_new(); +void lxw_theme_free(lxw_theme *theme); +void lxw_theme_xml_declaration(lxw_theme *self); +void lxw_theme_assemble_xml_file(lxw_theme *self); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_THEME_H__ */ diff --git a/src/include/xlsxwriter/third_party/ioapi.h b/src/include/xlsxwriter/third_party/ioapi.h new file mode 100644 index 0000000..ba71ff7 --- /dev/null +++ b/src/include/xlsxwriter/third_party/ioapi.h @@ -0,0 +1,214 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + + Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) + Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. + More if/def section may be needed to support other platforms + Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. + (but you should use iowin32.c for windows instead) + +*/ + + +/* Pragma added by libxlsxwriter to avoid warnings with -pedantic -ansi. */ +#ifndef _WIN32 +#pragma GCC system_header +#endif + +#ifndef _ZLIBIOAPI64_H +#define _ZLIBIOAPI64_H + +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) + + // Linux needs this to support file operation on files larger then 4+GB + // But might need better if/def to select just the platforms that needs them. + + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif + +#endif + +#include +#include +#include "zlib.h" + +#if defined(USE_FILE32API) +#define fopen64 fopen +#define ftello64 ftell +#define fseeko64 fseek +#else +#ifdef __FreeBSD__ +#define fopen64 fopen +#define ftello64 ftello +#define fseeko64 fseeko +#endif +#ifdef _MSC_VER + #define fopen64 fopen + #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) + #define ftello64 _ftelli64 + #define fseeko64 _fseeki64 + #else // old MSC + #define ftello64 ftell + #define fseeko64 fseek + #endif +#endif +#endif + +/* +#ifndef ZPOS64_T + #ifdef _WIN32 + #define ZPOS64_T fpos_t + #else + #include + #define ZPOS64_T uint64_t + #endif +#endif +*/ + +#ifdef HAVE_MINIZIP64_CONF_H +#include "mz64conf.h" +#endif + +/* a type choosen by DEFINE */ +#ifdef HAVE_64BIT_INT_CUSTOM +typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; +#else +#ifdef HAS_STDINT_H +#include "stdint.h" +typedef uint64_t ZPOS64_T; +#else + +/* Maximum unsigned 32-bit value used as placeholder for zip64 */ +#define MAXU32 0xffffffff + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 ZPOS64_T; +#else +typedef unsigned long long int ZPOS64_T; +#endif +#endif +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) + #define ZCALLBACK CALLBACK + #else + #define ZCALLBACK + #endif +#endif + + + + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); + + +/* here is the "old" 32 bits structure structure */ +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + +typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); + +typedef struct zlib_filefunc64_def_s +{ + open64_file_func zopen64_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell64_file_func ztell64_file; + seek64_file_func zseek64_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc64_def; + +void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +/* now internal definition, only for zip.c and unzip.h */ +typedef struct zlib_filefunc64_32_def_s +{ + zlib_filefunc64_def zfile_func64; + open_file_func zopen32_file; + tell_file_func ztell32_file; + seek_file_func zseek32_file; +} zlib_filefunc64_32_def; + + +#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +/* #define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) */ +/* #define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) */ +#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) +#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) + +voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); +long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); +ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); + +#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) +#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) +#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/xlsxwriter/third_party/queue.h b/src/include/xlsxwriter/third_party/queue.h new file mode 100644 index 0000000..64e5c1d --- /dev/null +++ b/src/include/xlsxwriter/third_party/queue.h @@ -0,0 +1,694 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD$ + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* #include */ + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may be traversed in either direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - + - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_FROM + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_FROM_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_FROM - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _FOREACH_REVERSE_FROM_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_AFTER + - + - + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * _SWAP + + + + + * + */ +#ifdef QUEUE_MACRO_DEBUG +/* Store the last 2 places the queue element or head was altered */ +struct qm_trace { + unsigned long lastline; + unsigned long prevline; + const char *lastfile; + const char *prevfile; +}; + +#define TRACEBUF struct qm_trace trace; +#define TRACEBUF_INITIALIZER { __FILE__, __LINE__, NULL, 0 } , +#define TRASHIT(x) do {(x) = (void *)-1;} while (0) +#define QMD_SAVELINK(name, link) void **name = (void *)&(link) + +#define QMD_TRACE_HEAD(head) do { \ + (head)->trace.prevline = (head)->trace.lastline; \ + (head)->trace.prevfile = (head)->trace.lastfile; \ + (head)->trace.lastline = __LINE__; \ + (head)->trace.lastfile = __FILE__; \ +} while (0) + +#define QMD_TRACE_ELEM(elem) do { \ + (elem)->trace.prevline = (elem)->trace.lastline; \ + (elem)->trace.prevfile = (elem)->trace.lastfile; \ + (elem)->trace.lastline = __LINE__; \ + (elem)->trace.lastfile = __FILE__; \ +} while (0) + +#else +#define QMD_TRACE_ELEM(elem) +#define QMD_TRACE_HEAD(head) +#define QMD_SAVELINK(name, link) +#define TRACEBUF +#define TRACEBUF_INITIALIZER +#define TRASHIT(x) +#endif /* QUEUE_MACRO_DEBUG */ + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.sle_next); \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +#define SLIST_SWAP(head1, head2, type) do { \ + struct type *swap_first = SLIST_FIRST(head1); \ + SLIST_FIRST(head1) = SLIST_FIRST(head2); \ + SLIST_FIRST(head2) = swap_first; \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? NULL : \ + __containerof((head)->stqh_last, struct type, field.stqe_next)) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + struct type *swap_first = STAILQ_FIRST(head1); \ + struct type **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ +} while (0) + + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_LIST_CHECK_HEAD(head, field) do { \ + if (LIST_FIRST((head)) != NULL && \ + LIST_FIRST((head))->field.le_prev != \ + &LIST_FIRST((head))) \ + panic("Bad list head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_LIST_CHECK_NEXT(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL && \ + LIST_NEXT((elm), field)->field.le_prev != \ + &((elm)->field.le_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_LIST_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.le_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_LIST_CHECK_HEAD(head, field) +#define QMD_LIST_CHECK_NEXT(elm, field) +#define QMD_LIST_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QMD_LIST_CHECK_NEXT(listelm, field); \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_LIST_CHECK_PREV(listelm, field); \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QMD_LIST_CHECK_HEAD((head), field); \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_PREV(elm, head, type, field) \ + ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \ + __containerof((elm)->field.le_prev, struct type, field.le_next)) + +#define LIST_REMOVE(elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.le_next); \ + QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ + QMD_LIST_CHECK_NEXT(elm, field); \ + QMD_LIST_CHECK_PREV(elm, field); \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ +} while (0) + +#define LIST_SWAP(head1, head2, type, field) do { \ + struct type *swap_tmp = LIST_FIRST((head1)); \ + LIST_FIRST((head1)) = LIST_FIRST((head2)); \ + LIST_FIRST((head2)) = swap_tmp; \ + if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ + if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ +} while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +/* + * Tail queue functions. + */ +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_TAILQ_CHECK_HEAD(head, field) do { \ + if (!TAILQ_EMPTY(head) && \ + TAILQ_FIRST((head))->field.tqe_prev != \ + &TAILQ_FIRST((head))) \ + panic("Bad tailq head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_TAIL(head, field) do { \ + if (*(head)->tqh_last != NULL) \ + panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ + if (TAILQ_NEXT((elm), field) != NULL && \ + TAILQ_NEXT((elm), field)->field.tqe_prev != \ + &((elm)->field.tqe_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_TAILQ_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_TAILQ_CHECK_HEAD(head, field) +#define QMD_TAILQ_CHECK_TAIL(head, headname) +#define QMD_TAILQ_CHECK_NEXT(elm, field) +#define QMD_TAILQ_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + QMD_TRACE_HEAD(head1); \ + QMD_TRACE_HEAD(head2); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QMD_TAILQ_CHECK_NEXT(listelm, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_TAILQ_CHECK_PREV(listelm, field); \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QMD_TAILQ_CHECK_HEAD(head, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QMD_TAILQ_CHECK_TAIL(head, field); \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ + QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ + QMD_TAILQ_CHECK_NEXT(elm, field); \ + QMD_TAILQ_CHECK_PREV(elm, field); \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + QMD_TRACE_HEAD(head); \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_SWAP(head1, head2, type, field) do { \ + struct type *swap_first = (head1)->tqh_first; \ + struct type **swap_last = (head1)->tqh_last; \ + (head1)->tqh_first = (head2)->tqh_first; \ + (head1)->tqh_last = (head2)->tqh_last; \ + (head2)->tqh_first = swap_first; \ + (head2)->tqh_last = swap_last; \ + if ((swap_first = (head1)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head1)->tqh_first; \ + else \ + (head1)->tqh_last = &(head1)->tqh_first; \ + if ((swap_first = (head2)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head2)->tqh_first; \ + else \ + (head2)->tqh_last = &(head2)->tqh_first; \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/src/include/xlsxwriter/third_party/tmpfileplus.h b/src/include/xlsxwriter/third_party/tmpfileplus.h new file mode 100644 index 0000000..6c6ac38 --- /dev/null +++ b/src/include/xlsxwriter/third_party/tmpfileplus.h @@ -0,0 +1,53 @@ +/* $Id: tmpfileplus.h $ */ +/* + * $Date: 2016-06-01 03:31Z $ + * $Revision: 2.0.0 $ + * $Author: dai $ + */ + +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) 2012-16 David Ireland, DI Management Services Pty Ltd + * . + */ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#ifndef TMPFILEPLUS_H_ +#define TMPFILEPLUS_H_ + +#include + +/** Create a unique temporary file. +@param dir (optional) directory to create file. If NULL use default TMP directory. +@param prefix (optional) prefix for file name. If NULL use "tmp.". +@param pathname (optional) pointer to a buffer to receive the temp filename. + Allocated using `malloc()`; user to free. Ignored if NULL. +@param keep If `keep` is nonzero and `pathname` is not NULL, then keep the file after closing. + Otherwise file is automatically deleted when closed. +@return Pointer to stream opened in binary read/write (w+b) mode, or a null pointer on error. +@exception ENOMEM Not enough memory to allocate filename. +*/ +FILE *tmpfileplus(const char *dir, const char *prefix, char **pathname, int keep); + + +/** Create a unique temporary file with filename stored in a fixed-length buffer. +@param dir (optional) directory to create file. If NULL use default directory. +@param prefix (optional) prefix for file name. If NULL use "tmp.". +@param pathnamebuf (optional) buffer to receive full pathname of temporary file. Ignored if NULL. +@param pathsize Size of buffer to receive filename and its terminating null character. +@param keep If `keep` is nonzero and `pathname` is not NULL, then keep the file after closing. + Otherwise file is automatically deleted when closed. +@return Pointer to stream opened in binary read/write (w+b) mode, or a null pointer on error. +@exception E2BIG Resulting filename is too big for the buffer `pathnamebuf`. +*/ +FILE *tmpfileplus_f(const char *dir, const char *prefix, char *pathnamebuf, size_t pathsize, int keep); + +#define TMPFILE_KEEP 1 + +#endif /* end TMPFILEPLUS_H_ */ diff --git a/src/include/xlsxwriter/third_party/tree.h b/src/include/xlsxwriter/third_party/tree.h new file mode 100644 index 0000000..6944502 --- /dev/null +++ b/src/include/xlsxwriter/third_party/tree.h @@ -0,0 +1,801 @@ +/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ +/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +/* #include */ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ + RB_PROTOTYPE_INSERT_COLOR(name, type, attr); \ + RB_PROTOTYPE_REMOVE_COLOR(name, type, attr); \ + RB_PROTOTYPE_INSERT(name, type, attr); \ + RB_PROTOTYPE_REMOVE(name, type, attr); \ + RB_PROTOTYPE_FIND(name, type, attr); \ + RB_PROTOTYPE_NFIND(name, type, attr); \ + RB_PROTOTYPE_NEXT(name, type, attr); \ + RB_PROTOTYPE_PREV(name, type, attr); \ + RB_PROTOTYPE_MINMAX(name, type, attr); +#define RB_PROTOTYPE_INSERT_COLOR(name, type, attr) \ + attr void name##_RB_INSERT_COLOR(struct name *, struct type *) +#define RB_PROTOTYPE_REMOVE_COLOR(name, type, attr) \ + attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *) +#define RB_PROTOTYPE_REMOVE(name, type, attr) \ + attr struct type *name##_RB_REMOVE(struct name *, struct type *) +#define RB_PROTOTYPE_INSERT(name, type, attr) \ + attr struct type *name##_RB_INSERT(struct name *, struct type *) +#define RB_PROTOTYPE_FIND(name, type, attr) \ + attr struct type *name##_RB_FIND(struct name *, struct type *) +#define RB_PROTOTYPE_NFIND(name, type, attr) \ + attr struct type *name##_RB_NFIND(struct name *, struct type *) +#define RB_PROTOTYPE_NEXT(name, type, attr) \ + attr struct type *name##_RB_NEXT(struct type *) +#define RB_PROTOTYPE_PREV(name, type, attr) \ + attr struct type *name##_RB_PREV(struct type *) +#define RB_PROTOTYPE_MINMAX(name, type, attr) \ + attr struct type *name##_RB_MINMAX(struct name *, int) + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ + RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ + RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \ + RB_GENERATE_INSERT(name, type, field, cmp, attr) \ + RB_GENERATE_REMOVE(name, type, field, attr) \ + RB_GENERATE_FIND(name, type, field, cmp, attr) \ + RB_GENERATE_NFIND(name, type, field, cmp, attr) \ + RB_GENERATE_NEXT(name, type, field, attr) \ + RB_GENERATE_PREV(name, type, field, attr) \ + RB_GENERATE_MINMAX(name, type, field, attr) + +#define RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) != NULL && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} + +#define RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)) \ + != NULL) \ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)) \ + != NULL) \ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} + +#define RB_GENERATE_REMOVE(name, type, field, attr) \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field)) != NULL) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field)) != NULL); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + +#define RB_GENERATE_INSERT(name, type, field, cmp, attr) \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} + +#define RB_GENERATE_FIND(name, type, field, cmp, attr) \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} + +#define RB_GENERATE_NFIND(name, type, field, cmp, attr) \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} + +#define RB_GENERATE_NEXT(name, type, field, attr) \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} + +#define RB_GENERATE_PREV(name, type, field, attr) \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} + +#define RB_GENERATE_MINMAX(name, type, field, attr) \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#endif /* _SYS_TREE_H_ */ diff --git a/src/include/xlsxwriter/third_party/zip.h b/src/include/xlsxwriter/third_party/zip.h new file mode 100644 index 0000000..c177744 --- /dev/null +++ b/src/include/xlsxwriter/third_party/zip.h @@ -0,0 +1,375 @@ +/* zip.h -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------- + + Changes + + See header of zip.h + +*/ + +/* Pragma added by libxlsxwriter to avoid warnings with -pedantic -ansi. */ +#ifndef _WIN32 +#pragma GCC system_header +#endif + +#ifndef _zip12_H +#define _zip12_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* #define HAVE_BZIP2 */ + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +/* Encryption not required by libxlsxwriter. */ +#ifndef NOCRYPT +#define NOCRYPT +#endif +#ifndef NOUNCRYPT +#define NOUNCRYPT +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); +extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); + +extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64)); + +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) + zip64 is set to 1 if a zip64 extended information block should be added to the local file header. + this MUST be '1' if the uncompressed size is >= 0xffffffff. + +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + + +extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64)); +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting)); + +extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64 + )); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCrypting : crc of file to compress (needed for crypting) + */ + +extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase + )); + + +extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64 + )); +/* + Same than zipOpenNewFileInZip4, except + versionMadeBy : value for Version made by field + flag : value for flag field (compression level info will be added) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); + +extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32)); + +/* + Close the current file in the zipfile, for file opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + + +extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); +/* + zipRemoveExtraInfoBlock - Added by Mathias Svensson + + Remove extra information block from a extra information data for the local file header or central directory header + + It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. + + 0x0001 is the signature header for the ZIP64 extra information blocks + + usage. + Remove ZIP64 Extra information from a central director extra field data + zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); + + Remove ZIP64 Extra information from a Local File Header extra field data + zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip64_H */ diff --git a/src/include/xlsxwriter/utility.h b/src/include/xlsxwriter/utility.h new file mode 100644 index 0000000..023f6ef --- /dev/null +++ b/src/include/xlsxwriter/utility.h @@ -0,0 +1,166 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + */ + +/** + * @file utility.h + * + * @brief Utility functions for libxlsxwriter. + * + * + * + */ + +#ifndef __LXW_UTILITY_H__ +#define __LXW_UTILITY_H__ + +#include +#include "common.h" + +/** + * @brief Convert an Excel `A1` cell string into a `(row, col)` pair. + * + * Convert an Excel `A1` cell string into a `(row, col)` pair. + * + * This is a little syntactic shortcut to help with worksheet layout: + * + * @code + * worksheet_write_string(worksheet, CELL("A1"), "Foo", NULL); + * + * //Same as: + * worksheet_write_string(worksheet, 0, 0, "Foo", NULL); + * @endcode + * + * @note + * + * This macro shouldn't be used in performance critical situations since it + * expands to two function calls. + */ +#define CELL(cell) \ + lxw_name_to_row(cell), lxw_name_to_col(cell) + +/** + * @brief Convert an Excel `A:B` column range into a `(col1, col2)` pair. + * + * Convert an Excel `A:B` column range into a `(col1, col2)` pair. + * + * This is a little syntactic shortcut to help with worksheet layout: + * + * @code + * worksheet_set_column(worksheet, COLS("B:D"), 20, NULL, NULL); + * + * // Same as: + * worksheet_set_column(worksheet, 1, 3, 20, NULL, NULL); + * @endcode + * + */ +#define COLS(cols) \ + lxw_name_to_col(cols), lxw_name_to_col_2(cols) + +/** + * @brief Convert an Excel `A1:B2` range into a `(first_row, first_col, + * last_row, last_col)` sequence. + * + * Convert an Excel `A1:B2` range into a `(first_row, first_col, last_row, + * last_col)` sequence. + * + * This is a little syntactic shortcut to help with worksheet layout. + * + * @code + * worksheet_print_area(worksheet, 0, 0, 41, 10); // A1:K42. + * + * // Same as: + * worksheet_print_area(worksheet, RANGE("A1:K42")); + * @endcode + */ +#define RANGE(range) \ + lxw_name_to_row(range), lxw_name_to_col(range), \ + lxw_name_to_row_2(range), lxw_name_to_col_2(range) + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +/** + * @brief Converts a libxlsxwriter error number to a string. + * + * The `%lxw_strerror` function converts a libxlsxwriter error number defined + * by #lxw_error to a pointer to a string description of the error. + * Similar to the standard library strerror(3) function. + * + * For example: + * + * @code + * lxw_error error = workbook_close(workbook); + * + * if (error) + * printf("Error in workbook_close().\n" + * "Error %d = %s\n", error, lxw_strerror(error)); + * @endcode + * + * This would produce output like the following if the target file wasn't + * writable: + * + * Error in workbook_close(). + * Error 2 = Error creating output xlsx file. Usually a permissions error. + * + * @param error_num The error number returned by a libxlsxwriter function. + * + * @return A pointer to a statically allocated string. Do not free. + */ +char *lxw_strerror(lxw_error error_num); + +/* Create a quoted version of the worksheet name */ +char *lxw_quote_sheetname(const char *str); + +void lxw_col_to_name(char *col_name, lxw_col_t col_num, uint8_t absolute); + +void lxw_rowcol_to_cell(char *cell_name, lxw_row_t row, lxw_col_t col); + +void lxw_rowcol_to_cell_abs(char *cell_name, + lxw_row_t row, + lxw_col_t col, uint8_t abs_row, uint8_t abs_col); + +void lxw_rowcol_to_range(char *range, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t last_row, lxw_col_t last_col); + +void lxw_rowcol_to_range_abs(char *range, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t last_row, lxw_col_t last_col); + +void lxw_rowcol_to_formula_abs(char *formula, const char *sheetname, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t last_row, lxw_col_t last_col); + +uint32_t lxw_name_to_row(const char *row_str); +uint16_t lxw_name_to_col(const char *col_str); +uint32_t lxw_name_to_row_2(const char *row_str); +uint16_t lxw_name_to_col_2(const char *col_str); + +double lxw_datetime_to_excel_date(lxw_datetime *datetime, uint8_t date_1904); + +char *lxw_strdup(const char *str); + +size_t lxw_utf8_strlen(const char *str); + +void lxw_str_tolower(char *str); + +FILE *lxw_tmpfile(char *tmpdir); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +#endif + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_UTILITY_H__ */ diff --git a/src/include/xlsxwriter/workbook.h b/src/include/xlsxwriter/workbook.h new file mode 100644 index 0000000..46cf8c5 --- /dev/null +++ b/src/include/xlsxwriter/workbook.h @@ -0,0 +1,751 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + */ + +/** + * @page workbook_page The Workbook object + * + * The Workbook is the main object exposed by the libxlsxwriter library. It + * represents the entire spreadsheet as you see it in Excel and internally it + * represents the Excel file as it is written on disk. + * + * See @ref workbook.h for full details of the functionality. + * + * @file workbook.h + * + * @brief Functions related to creating an Excel xlsx workbook. + * + * The Workbook is the main object exposed by the libxlsxwriter library. It + * represents the entire spreadsheet as you see it in Excel and internally it + * represents the Excel file as it is written on disk. + * + * @code + * #include "xlsxwriter.h" + * + * int main() { + * + * lxw_workbook *workbook = workbook_new("filename.xlsx"); + * lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + * + * worksheet_write_string(worksheet, 0, 0, "Hello Excel", NULL); + * + * return workbook_close(workbook); + * } + * @endcode + * + * @image html workbook01.png + * + */ +#ifndef __LXW_WORKBOOK_H__ +#define __LXW_WORKBOOK_H__ + +#include +#include +#include + +#include "worksheet.h" +#include "chart.h" +#include "shared_strings.h" +#include "hash_table.h" +#include "common.h" + +#define LXW_DEFINED_NAME_LENGTH 128 + +/* Define the tree.h RB structs for the red-black head types. */ +RB_HEAD(lxw_worksheet_names, lxw_worksheet_name); + +/* Define the queue.h structs for the workbook lists. */ +STAILQ_HEAD(lxw_worksheets, lxw_worksheet); +STAILQ_HEAD(lxw_charts, lxw_chart); +TAILQ_HEAD(lxw_defined_names, lxw_defined_name); + +/* Struct to represent a worksheet name/pointer pair. */ +typedef struct lxw_worksheet_name { + const char *name; + lxw_worksheet *worksheet; + + RB_ENTRY (lxw_worksheet_name) tree_pointers; +} lxw_worksheet_name; + +/* Wrapper around RB_GENERATE_STATIC from tree.h to avoid unused function + * warnings and to avoid portability issues with the _unused attribute. */ +#define LXW_RB_GENERATE_NAMES(name, type, field, cmp) \ + RB_GENERATE_INSERT_COLOR(name, type, field, static) \ + RB_GENERATE_REMOVE_COLOR(name, type, field, static) \ + RB_GENERATE_INSERT(name, type, field, cmp, static) \ + RB_GENERATE_REMOVE(name, type, field, static) \ + RB_GENERATE_FIND(name, type, field, cmp, static) \ + RB_GENERATE_NEXT(name, type, field, static) \ + RB_GENERATE_MINMAX(name, type, field, static) \ + /* Add unused struct to allow adding a semicolon */ \ + struct lxw_rb_generate_names{int unused;} + +/** + * @brief Macro to loop over all the worksheets in a workbook. + * + * This macro allows you to loop over all the worksheets that have been + * added to a workbook. You must provide a lxw_worksheet pointer and + * a pointer to the lxw_workbook: + * + * @code + * lxw_workbook *workbook = workbook_new("test.xlsx"); + * + * lxw_worksheet *worksheet; // Generic worksheet pointer. + * + * // Worksheet objects used in the program. + * lxw_worksheet *worksheet1 = workbook_add_worksheet(workbook, NULL); + * lxw_worksheet *worksheet2 = workbook_add_worksheet(workbook, NULL); + * lxw_worksheet *worksheet3 = workbook_add_worksheet(workbook, NULL); + * + * // Iterate over the 3 worksheets and perform the same operation on each. + * LXW_FOREACH_WORKSHEET(worksheet, workbook) { + * worksheet_write_string(worksheet, 0, 0, "Hello", NULL); + * } + * @endcode + */ +#define LXW_FOREACH_WORKSHEET(worksheet, workbook) \ + STAILQ_FOREACH((worksheet), (workbook)->worksheets, list_pointers) + +/* Struct to represent a defined name. */ +typedef struct lxw_defined_name { + int16_t index; + uint8_t hidden; + char name[LXW_DEFINED_NAME_LENGTH]; + char app_name[LXW_DEFINED_NAME_LENGTH]; + char formula[LXW_DEFINED_NAME_LENGTH]; + char normalised_name[LXW_DEFINED_NAME_LENGTH]; + char normalised_sheetname[LXW_DEFINED_NAME_LENGTH]; + + /* List pointers for queue.h. */ + TAILQ_ENTRY (lxw_defined_name) list_pointers; +} lxw_defined_name; + +/** + * Workbook document properties. + */ +typedef struct lxw_doc_properties { + /** The title of the Excel Document. */ + char *title; + + /** The subject of the Excel Document. */ + char *subject; + + /** The author of the Excel Document. */ + char *author; + + /** The manager field of the Excel Document. */ + char *manager; + + /** The company field of the Excel Document. */ + char *company; + + /** The category of the Excel Document. */ + char *category; + + /** The keywords of the Excel Document. */ + char *keywords; + + /** The comment field of the Excel Document. */ + char *comments; + + /** The status of the Excel Document. */ + char *status; + + /** The hyperlink base url of the Excel Document. */ + char *hyperlink_base; + + time_t created; + +} lxw_doc_properties; + +/** + * @brief Workbook options. + * + * Optional parameters when creating a new Workbook object via + * workbook_new_opt(). + * + * The following properties are supported: + * + * - `constant_memory`: Reduces the amount of data stored in memory so that + * large files can be written efficiently. + * + * @note In this mode a row of data is written and then discarded when a + * cell in a new row is added via one of the `worksheet_write_*()` + * functions. Therefore, once this option is active, data should be written in + * sequential row order. For this reason the `worksheet_merge_range()` + * doesn't work in this mode. See also @ref ww_mem_constant. + * + * - `tmpdir`: libxlsxwriter stores workbook data in temporary files prior + * to assembling the final XLSX file. The temporary files are created in the + * system's temp directory. If the default temporary directory isn't + * accessible to your application, or doesn't contain enough space, you can + * specify an alternative location using the `tempdir` option. + */ +typedef struct lxw_workbook_options { + /** Optimize the workbook to use constant memory for worksheets */ + uint8_t constant_memory; + + /** Directory to use for the temporary files created by libxlsxwriter. */ + char *tmpdir; +} lxw_workbook_options; + +/** + * @brief Struct to represent an Excel workbook. + * + * The members of the lxw_workbook struct aren't modified directly. Instead + * the workbook properties are set by calling the functions shown in + * workbook.h. + */ +typedef struct lxw_workbook { + + FILE *file; + struct lxw_worksheets *worksheets; + struct lxw_worksheet_names *worksheet_names; + struct lxw_charts *charts; + struct lxw_charts *ordered_charts; + struct lxw_formats *formats; + struct lxw_defined_names *defined_names; + lxw_sst *sst; + lxw_doc_properties *properties; + struct lxw_custom_properties *custom_properties; + + char *filename; + lxw_workbook_options options; + + uint16_t num_sheets; + uint16_t first_sheet; + uint16_t active_sheet; + uint16_t num_xf_formats; + uint16_t num_format_count; + uint16_t drawing_count; + + uint16_t font_count; + uint16_t border_count; + uint16_t fill_count; + uint8_t optimize; + + uint8_t has_png; + uint8_t has_jpeg; + uint8_t has_bmp; + + lxw_hash_table *used_xf_formats; + +} lxw_workbook; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +/** + * @brief Create a new workbook object. + * + * @param filename The name of the new Excel file to create. + * + * @return A lxw_workbook instance. + * + * The `%workbook_new()` constructor is used to create a new Excel workbook + * with a given filename: + * + * @code + * lxw_workbook *workbook = workbook_new("filename.xlsx"); + * @endcode + * + * When specifying a filename it is recommended that you use an `.xlsx` + * extension or Excel will generate a warning when opening the file. + * + */ +lxw_workbook *workbook_new(const char *filename); + +/** + * @brief Create a new workbook object, and set the workbook options. + * + * @param filename The name of the new Excel file to create. + * @param options Workbook options. + * + * @return A lxw_workbook instance. + * + * This function is the same as the `workbook_new()` constructor but allows + * additional options to be set. + * + * @code + * lxw_workbook_options options = {.constant_memory = 1, + * .tmpdir = "C:\\Temp"}; + * + * lxw_workbook *workbook = workbook_new_opt("filename.xlsx", &options); + * @endcode + * + * The options that can be set via #lxw_workbook_options are: + * + * - `constant_memory`: Reduces the amount of data stored in memory so that + * large files can be written efficiently. + * + * @note In this mode a row of data is written and then discarded when a + * cell in a new row is added via one of the `worksheet_write_*()` + * functions. Therefore, once this option is active, data should be written in + * sequential row order. For this reason the `worksheet_merge_range()` + * doesn't work in this mode. See also @ref ww_mem_constant. + * + * - `tmpdir`: libxlsxwriter stores workbook data in temporary files prior + * to assembling the final XLSX file. The temporary files are created in the + * system's temp directory. If the default temporary directory isn't + * accessible to your application, or doesn't contain enough space, you can + * specify an alternative location using the `tempdir` option.* + * + * See @ref working_with_memory for more details. + * + */ +lxw_workbook *workbook_new_opt(const char *filename, + lxw_workbook_options *options); + +/* Deprecated function name for backwards compatibility. */ +lxw_workbook *new_workbook(const char *filename); + +/* Deprecated function name for backwards compatibility. */ +lxw_workbook *new_workbook_opt(const char *filename, + lxw_workbook_options *options); + +/** + * @brief Add a new worksheet to a workbook. + * + * @param workbook Pointer to a lxw_workbook instance. + * @param sheetname Optional worksheet name, defaults to Sheet1, etc. + * + * @return A lxw_worksheet object. + * + * The `%workbook_add_worksheet()` function adds a new worksheet to a workbook: + * + * At least one worksheet should be added to a new workbook: The @ref + * worksheet.h "Worksheet" object is used to write data and configure a + * worksheet in the workbook. + * + * The `sheetname` parameter is optional. If it is `NULL` the default + * Excel convention will be followed, i.e. Sheet1, Sheet2, etc.: + * + * @code + * worksheet = workbook_add_worksheet(workbook, NULL ); // Sheet1 + * worksheet = workbook_add_worksheet(workbook, "Foglio2"); // Foglio2 + * worksheet = workbook_add_worksheet(workbook, "Data"); // Data + * worksheet = workbook_add_worksheet(workbook, NULL ); // Sheet4 + * + * @endcode + * + * @image html workbook02.png + * + * The worksheet name must be a valid Excel worksheet name, i.e. it must be + * less than 32 character and it cannot contain any of the characters: + * + * / \ [ ] : * ? + * + * In addition, you cannot use the same, case insensitive, `sheetname` for more + * than one worksheet. + * + */ +lxw_worksheet *workbook_add_worksheet(lxw_workbook *workbook, + const char *sheetname); + +/** + * @brief Create a new @ref format.h "Format" object to formats cells in + * worksheets. + * + * @param workbook Pointer to a lxw_workbook instance. + * + * @return A lxw_format instance. + * + * The `workbook_add_format()` function can be used to create new @ref + * format.h "Format" objects which are used to apply formatting to a cell. + * + * @code + * // Create the Format. + * lxw_format *format = workbook_add_format(workbook); + * + * // Set some of the format properties. + * format_set_bold(format); + * format_set_font_color(format, LXW_COLOR_RED); + * + * // Use the format to change the text format in a cell. + * worksheet_write_string(worksheet, 0, 0, "Hello", format); + * @endcode + * + * See @ref format.h "the Format object" and @ref working_with_formats + * sections for more details about Format properties and how to set them. + * + */ +lxw_format *workbook_add_format(lxw_workbook *workbook); + +/** + * @brief Create a new chart to be added to a worksheet: + * + * @param workbook Pointer to a lxw_workbook instance. + * @param chart_type The type of chart to be created. See #lxw_chart_type. + * + * @return A lxw_chart object. + * + * The `%workbook_add_chart()` function creates a new chart object that can + * be added to a worksheet: + * + * @code + * // Create a chart object. + * lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_COLUMN); + * + * // Add data series to the chart. + * chart_add_series(chart, NULL, "Sheet1!$A$1:$A$5"); + * chart_add_series(chart, NULL, "Sheet1!$B$1:$B$5"); + * chart_add_series(chart, NULL, "Sheet1!$C$1:$C$5"); + * + * // Insert the chart into the worksheet + * worksheet_insert_chart(worksheet, CELL("B7"), chart); + * @endcode + * + * The available chart types are defined in #lxw_chart_type. The types of + * charts that are supported are: + * + * | Chart type | Description | + * | :--------------------------------------- | :------------------------------------ | + * | #LXW_CHART_AREA | Area chart. | + * | #LXW_CHART_AREA_STACKED | Area chart - stacked. | + * | #LXW_CHART_AREA_STACKED_PERCENT | Area chart - percentage stacked. | + * | #LXW_CHART_BAR | Bar chart. | + * | #LXW_CHART_BAR_STACKED | Bar chart - stacked. | + * | #LXW_CHART_BAR_STACKED_PERCENT | Bar chart - percentage stacked. | + * | #LXW_CHART_COLUMN | Column chart. | + * | #LXW_CHART_COLUMN_STACKED | Column chart - stacked. | + * | #LXW_CHART_COLUMN_STACKED_PERCENT | Column chart - percentage stacked. | + * | #LXW_CHART_DOUGHNUT | Doughnut chart. | + * | #LXW_CHART_LINE | Line chart. | + * | #LXW_CHART_PIE | Pie chart. | + * | #LXW_CHART_SCATTER | Scatter chart. | + * | #LXW_CHART_SCATTER_STRAIGHT | Scatter chart - straight. | + * | #LXW_CHART_SCATTER_STRAIGHT_WITH_MARKERS | Scatter chart - straight with markers. | + * | #LXW_CHART_SCATTER_SMOOTH | Scatter chart - smooth. | + * | #LXW_CHART_SCATTER_SMOOTH_WITH_MARKERS | Scatter chart - smooth with markers. | + * | #LXW_CHART_RADAR | Radar chart. | + * | #LXW_CHART_RADAR_WITH_MARKERS | Radar chart - with markers. | + * | #LXW_CHART_RADAR_FILLED | Radar chart - filled. | + * + * + * + * See @ref chart.h for details. + */ +lxw_chart *workbook_add_chart(lxw_workbook *workbook, uint8_t chart_type); + +/** + * @brief Close the Workbook object and write the XLSX file. + * + * @param workbook Pointer to a lxw_workbook instance. + * + * @return A #lxw_error. + * + * The `%workbook_close()` function closes a Workbook object, writes the Excel + * file to disk, frees any memory allocated internally to the Workbook and + * frees the object itself. + * + * @code + * workbook_close(workbook); + * @endcode + * + * The `%workbook_close()` function returns any #lxw_error error codes + * encountered when creating the Excel file. The error code can be returned + * from the program main or the calling function: + * + * @code + * return workbook_close(workbook); + * @endcode + * + */ +lxw_error workbook_close(lxw_workbook *workbook); + +/** + * @brief Set the document properties such as Title, Author etc. + * + * @param workbook Pointer to a lxw_workbook instance. + * @param properties Document properties to set. + * + * @return A #lxw_error. + * + * The `%workbook_set_properties` function can be used to set the document + * properties of the Excel file created by `libxlsxwriter`. These properties + * are visible when you use the `Office Button -> Prepare -> Properties` + * option in Excel and are also available to external applications that read + * or index windows files. + * + * The properties that can be set are: + * + * - `title` + * - `subject` + * - `author` + * - `manager` + * - `company` + * - `category` + * - `keywords` + * - `comments` + * - `hyperlink_base` + * + * The properties are specified via a `lxw_doc_properties` struct. All the + * members are `char *` and they are all optional. An example of how to create + * and pass the properties is: + * + * @code + * // Create a properties structure and set some of the fields. + * lxw_doc_properties properties = { + * .title = "This is an example spreadsheet", + * .subject = "With document properties", + * .author = "John McNamara", + * .manager = "Dr. Heinz Doofenshmirtz", + * .company = "of Wolves", + * .category = "Example spreadsheets", + * .keywords = "Sample, Example, Properties", + * .comments = "Created with libxlsxwriter", + * .status = "Quo", + * }; + * + * // Set the properties in the workbook. + * workbook_set_properties(workbook, &properties); + * @endcode + * + * @image html doc_properties.png + * + */ +lxw_error workbook_set_properties(lxw_workbook *workbook, + lxw_doc_properties *properties); + +/** + * @brief Set a custom document text property. + * + * @param workbook Pointer to a lxw_workbook instance. + * @param name The name of the custom property. + * @param value The value of the custom property. + * + * @return A #lxw_error. + * + * The `%workbook_set_custom_property_string()` function can be used to set one + * or more custom document text properties not covered by the standard + * properties in the `workbook_set_properties()` function above. + * + * For example: + * + * @code + * workbook_set_custom_property_string(workbook, "Checked by", "Eve"); + * @endcode + * + * @image html custom_properties.png + * + * There are 4 `workbook_set_custom_property_string_*()` functions for each + * of the custom property types supported by Excel: + * + * - text/string: `workbook_set_custom_property_string()` + * - number: `workbook_set_custom_property_number()` + * - datetime: `workbook_set_custom_property_datetime()` + * - boolean: `workbook_set_custom_property_boolean()` + * + * **Note**: the name and value parameters are limited to 255 characters + * by Excel. + * + */ +lxw_error workbook_set_custom_property_string(lxw_workbook *workbook, + const char *name, + const char *value); +/** + * @brief Set a custom document number property. + * + * @param workbook Pointer to a lxw_workbook instance. + * @param name The name of the custom property. + * @param value The value of the custom property. + * + * @return A #lxw_error. + * + * Set a custom document number property. + * See `workbook_set_custom_property_string()` above for details. + * + * @code + * workbook_set_custom_property_number(workbook, "Document number", 12345); + * @endcode + */ +lxw_error workbook_set_custom_property_number(lxw_workbook *workbook, + const char *name, double value); + +/* Undocumented since the user can use workbook_set_custom_property_number(). + * Only implemented for file format completeness and testing. + */ +lxw_error workbook_set_custom_property_integer(lxw_workbook *workbook, + const char *name, + int32_t value); + +/** + * @brief Set a custom document boolean property. + * + * @param workbook Pointer to a lxw_workbook instance. + * @param name The name of the custom property. + * @param value The value of the custom property. + * + * @return A #lxw_error. + * + * Set a custom document boolean property. + * See `workbook_set_custom_property_string()` above for details. + * + * @code + * workbook_set_custom_property_boolean(workbook, "Has Review", 1); + * @endcode + */ +lxw_error workbook_set_custom_property_boolean(lxw_workbook *workbook, + const char *name, + uint8_t value); +/** + * @brief Set a custom document date or time property. + * + * @param workbook Pointer to a lxw_workbook instance. + * @param name The name of the custom property. + * @param datetime The value of the custom property. + * + * @return A #lxw_error. + * + * Set a custom date or time number property. + * See `workbook_set_custom_property_string()` above for details. + * + * @code + * lxw_datetime datetime = {2016, 12, 1, 11, 55, 0.0}; + * + * workbook_set_custom_property_datetime(workbook, "Date completed", &datetime); + * @endcode + */ +lxw_error workbook_set_custom_property_datetime(lxw_workbook *workbook, + const char *name, + lxw_datetime *datetime); + +/** + * @brief Create a defined name in the workbook to use as a variable. + * + * @param workbook Pointer to a lxw_workbook instance. + * @param name The defined name. + * @param formula The cell or range that the defined name refers to. + * + * @return A #lxw_error. + * + * This function is used to defined a name that can be used to represent a + * value, a single cell or a range of cells in a workbook: These defined names + * can then be used in formulas: + * + * @code + * workbook_define_name(workbook, "Exchange_rate", "=0.96"); + * worksheet_write_formula(worksheet, 2, 1, "=Exchange_rate", NULL); + * + * @endcode + * + * @image html defined_name.png + * + * As in Excel a name defined like this is "global" to the workbook and can be + * referred to from any worksheet: + * + * @code + * // Global workbook name. + * workbook_define_name(workbook, "Sales", "=Sheet1!$G$1:$H$10"); + * @endcode + * + * It is also possible to define a local/worksheet name by prefixing it with + * the sheet name using the syntax `'sheetname!definedname'`: + * + * @code + * // Local worksheet name. + * workbook_define_name(workbook, "Sheet2!Sales", "=Sheet2!$G$1:$G$10"); + * @endcode + * + * If the sheet name contains spaces or special characters you must follow the + * Excel convention and enclose it in single quotes: + * + * @code + * workbook_define_name(workbook, "'New Data'!Sales", "=Sheet2!$G$1:$G$10"); + * @endcode + * + * The rules for names in Excel are explained in the + * [Microsoft Office +documentation](http://office.microsoft.com/en-001/excel-help/define-and-use-names-in-formulas-HA010147120.aspx). + * + */ +lxw_error workbook_define_name(lxw_workbook *workbook, const char *name, + const char *formula); + +/** + * @brief Get a worksheet object from its name. + * + * @param workbook Pointer to a lxw_workbook instance. + * @param name Worksheet name. + * + * @return A lxw_worksheet object. + * + * This function returns a lxw_worksheet object reference based on its name: + * + * @code + * worksheet = workbook_get_worksheet_by_name(workbook, "Sheet1"); + * @endcode + * + */ +lxw_worksheet *workbook_get_worksheet_by_name(lxw_workbook *workbook, + const char *name); + +/** + * @brief Validate a worksheet name. + * + * @param workbook Pointer to a lxw_workbook instance. + * @param sheetname Worksheet name to validate. + * + * @return A #lxw_error. + * + * This function is used to validate a worksheet name according to the rules + * used by Excel: + * + * - The name is less than or equal to 31 UTF-8 characters. + * - The name doesn't contain any of the characters: ` [ ] : * ? / \ ` + * - The name isn't already in use. + * + * @code + * lxw_error err = workbook_validate_worksheet_name(workbook, "Foglio"); + * @endcode + * + * This function is called by `workbook_add_worksheet()` but it can be + * explicitly called by the user beforehand to ensure that the worksheet + * name is valid. + * + */ +lxw_error workbook_validate_worksheet_name(lxw_workbook *workbook, + const char *sheetname); + +void lxw_workbook_free(lxw_workbook *workbook); +void lxw_workbook_assemble_xml_file(lxw_workbook *workbook); +void lxw_workbook_set_default_xf_indices(lxw_workbook *workbook); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _workbook_xml_declaration(lxw_workbook *self); +STATIC void _write_workbook(lxw_workbook *self); +STATIC void _write_file_version(lxw_workbook *self); +STATIC void _write_workbook_pr(lxw_workbook *self); +STATIC void _write_book_views(lxw_workbook *self); +STATIC void _write_workbook_view(lxw_workbook *self); +STATIC void _write_sheet(lxw_workbook *self, + const char *name, uint32_t sheet_id, uint8_t hidden); +STATIC void _write_sheets(lxw_workbook *self); +STATIC void _write_calc_pr(lxw_workbook *self); + +STATIC void _write_defined_name(lxw_workbook *self, + lxw_defined_name *define_name); +STATIC void _write_defined_names(lxw_workbook *self); + +STATIC lxw_error _store_defined_name(lxw_workbook *self, const char *name, + const char *app_name, + const char *formula, int16_t index, + uint8_t hidden); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_WORKBOOK_H__ */ diff --git a/src/include/xlsxwriter/worksheet.h b/src/include/xlsxwriter/worksheet.h new file mode 100644 index 0000000..8dc4a6e --- /dev/null +++ b/src/include/xlsxwriter/worksheet.h @@ -0,0 +1,2641 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + */ + +/** + * @page worksheet_page The Worksheet object + * + * The Worksheet object represents an Excel worksheet. It handles + * operations such as writing data to cells or formatting worksheet + * layout. + * + * See @ref worksheet.h for full details of the functionality. + * + * @file worksheet.h + * + * @brief Functions related to adding data and formatting to a worksheet. + * + * The Worksheet object represents an Excel worksheet. It handles + * operations such as writing data to cells or formatting worksheet + * layout. + * + * A Worksheet object isn't created directly. Instead a worksheet is + * created by calling the workbook_add_worksheet() function from a + * Workbook object: + * + * @code + * #include "xlsxwriter.h" + * + * int main() { + * + * lxw_workbook *workbook = workbook_new("filename.xlsx"); + * lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + * + * worksheet_write_string(worksheet, 0, 0, "Hello Excel", NULL); + * + * return workbook_close(workbook); + * } + * @endcode + * + */ +#ifndef __LXW_WORKSHEET_H__ +#define __LXW_WORKSHEET_H__ + +#include +#include +#include +#include + +#include "shared_strings.h" +#include "chart.h" +#include "drawing.h" +#include "common.h" +#include "format.h" +#include "utility.h" + +#define LXW_ROW_MAX 1048576 +#define LXW_COL_MAX 16384 +#define LXW_COL_META_MAX 128 +#define LXW_HEADER_FOOTER_MAX 255 +#define LXW_MAX_NUMBER_URLS 65530 +#define LXW_PANE_NAME_LENGTH 12 /* bottomRight + 1 */ + +/* The Excel 2007 specification says that the maximum number of page + * breaks is 1026. However, in practice it is actually 1023. */ +#define LXW_BREAKS_MAX 1023 + +/** Default column width in Excel */ +#define LXW_DEF_COL_WIDTH (double)8.43 + +/** Default row height in Excel */ +#define LXW_DEF_ROW_HEIGHT (double)15.0 + +/** Gridline options using in `worksheet_gridlines()`. */ +enum lxw_gridlines { + /** Hide screen and print gridlines. */ + LXW_HIDE_ALL_GRIDLINES = 0, + /** Show screen gridlines. */ + LXW_SHOW_SCREEN_GRIDLINES, + /** Show print gridlines. */ + LXW_SHOW_PRINT_GRIDLINES, + /** Show screen and print gridlines. */ + LXW_SHOW_ALL_GRIDLINES +}; + +enum cell_types { + NUMBER_CELL = 1, + STRING_CELL, + INLINE_STRING_CELL, + FORMULA_CELL, + ARRAY_FORMULA_CELL, + BLANK_CELL, + BOOLEAN_CELL, + HYPERLINK_URL, + HYPERLINK_INTERNAL, + HYPERLINK_EXTERNAL +}; + +enum pane_types { + NO_PANES = 0, + FREEZE_PANES, + SPLIT_PANES, + FREEZE_SPLIT_PANES +}; + +/* Define the tree.h RB structs for the red-black head types. */ +RB_HEAD(lxw_table_cells, lxw_cell); + +/* Define a RB_TREE struct manually to add extra members. */ +struct lxw_table_rows { + struct lxw_row *rbh_root; + struct lxw_row *cached_row; + lxw_row_t cached_row_num; +}; + +/* Wrapper around RB_GENERATE_STATIC from tree.h to avoid unused function + * warnings and to avoid portability issues with the _unused attribute. */ +#define LXW_RB_GENERATE_ROW(name, type, field, cmp) \ + RB_GENERATE_INSERT_COLOR(name, type, field, static) \ + RB_GENERATE_REMOVE_COLOR(name, type, field, static) \ + RB_GENERATE_INSERT(name, type, field, cmp, static) \ + RB_GENERATE_REMOVE(name, type, field, static) \ + RB_GENERATE_FIND(name, type, field, cmp, static) \ + RB_GENERATE_NEXT(name, type, field, static) \ + RB_GENERATE_MINMAX(name, type, field, static) \ + /* Add unused struct to allow adding a semicolon */ \ + struct lxw_rb_generate_row{int unused;} + +#define LXW_RB_GENERATE_CELL(name, type, field, cmp) \ + RB_GENERATE_INSERT_COLOR(name, type, field, static) \ + RB_GENERATE_REMOVE_COLOR(name, type, field, static) \ + RB_GENERATE_INSERT(name, type, field, cmp, static) \ + RB_GENERATE_REMOVE(name, type, field, static) \ + RB_GENERATE_FIND(name, type, field, cmp, static) \ + RB_GENERATE_NEXT(name, type, field, static) \ + RB_GENERATE_MINMAX(name, type, field, static) \ + /* Add unused struct to allow adding a semicolon */ \ + struct lxw_rb_generate_cell{int unused;} + +STAILQ_HEAD(lxw_merged_ranges, lxw_merged_range); +STAILQ_HEAD(lxw_selections, lxw_selection); +STAILQ_HEAD(lxw_image_data, lxw_image_options); +STAILQ_HEAD(lxw_chart_data, lxw_image_options); + +/** + * @brief Options for rows and columns. + * + * Options struct for the worksheet_set_column() and worksheet_set_row() + * functions. + * + * It has the following members but currently only the `hidden` property is + * supported: + * + * * `hidden` + * * `level` + * * `collapsed` + */ +typedef struct lxw_row_col_options { + /** Hide the row/column */ + uint8_t hidden; + uint8_t level; + uint8_t collapsed; +} lxw_row_col_options; + +typedef struct lxw_col_options { + lxw_col_t firstcol; + lxw_col_t lastcol; + double width; + lxw_format *format; + uint8_t hidden; + uint8_t level; + uint8_t collapsed; +} lxw_col_options; + +typedef struct lxw_merged_range { + lxw_row_t first_row; + lxw_row_t last_row; + lxw_col_t first_col; + lxw_col_t last_col; + + STAILQ_ENTRY (lxw_merged_range) list_pointers; +} lxw_merged_range; + +typedef struct lxw_repeat_rows { + uint8_t in_use; + lxw_row_t first_row; + lxw_row_t last_row; +} lxw_repeat_rows; + +typedef struct lxw_repeat_cols { + uint8_t in_use; + lxw_col_t first_col; + lxw_col_t last_col; +} lxw_repeat_cols; + +typedef struct lxw_print_area { + uint8_t in_use; + lxw_row_t first_row; + lxw_row_t last_row; + lxw_col_t first_col; + lxw_col_t last_col; +} lxw_print_area; + +typedef struct lxw_autofilter { + uint8_t in_use; + lxw_row_t first_row; + lxw_row_t last_row; + lxw_col_t first_col; + lxw_col_t last_col; +} lxw_autofilter; + +typedef struct lxw_panes { + uint8_t type; + lxw_row_t first_row; + lxw_col_t first_col; + lxw_row_t top_row; + lxw_col_t left_col; + double x_split; + double y_split; +} lxw_panes; + +typedef struct lxw_selection { + char pane[LXW_PANE_NAME_LENGTH]; + char active_cell[LXW_MAX_CELL_RANGE_LENGTH]; + char sqref[LXW_MAX_CELL_RANGE_LENGTH]; + + STAILQ_ENTRY (lxw_selection) list_pointers; + +} lxw_selection; + +/** + * @brief Options for inserted images + * + * Options for modifying images inserted via `worksheet_insert_image_opt()`. + * + */ +typedef struct lxw_image_options { + + /** Offset from the left of the cell in pixels. */ + int32_t x_offset; + + /** Offset from the top of the cell in pixels. */ + int32_t y_offset; + + /** X scale of the image as a decimal. */ + double x_scale; + + /** Y scale of the image as a decimal. */ + double y_scale; + + lxw_row_t row; + lxw_col_t col; + char *filename; + char *url; + char *tip; + uint8_t anchor; + + /* Internal metadata. */ + FILE *stream; + uint8_t image_type; + double width; + double height; + char *short_name; + char *extension; + double x_dpi; + double y_dpi; + lxw_chart *chart; + + STAILQ_ENTRY (lxw_image_options) list_pointers; + +} lxw_image_options; + +/** + * @brief Header and footer options. + * + * Optional parameters used in the worksheet_set_header_opt() and + * worksheet_set_footer_opt() functions. + * + */ +typedef struct lxw_header_footer_options { + /** Header or footer margin in inches. Excel default is 0.3. */ + double margin; +} lxw_header_footer_options; + +/** + * @brief Worksheet protection options. + */ +typedef struct lxw_protection { + /** Turn off selection of locked cells. This in on in Excel by default.*/ + uint8_t no_select_locked_cells; + + /** Turn off selection of unlocked cells. This in on in Excel by default.*/ + uint8_t no_select_unlocked_cells; + + /** Prevent formatting of cells. */ + uint8_t format_cells; + + /** Prevent formatting of columns. */ + uint8_t format_columns; + + /** Prevent formatting of rows. */ + uint8_t format_rows; + + /** Prevent insertion of columns. */ + uint8_t insert_columns; + + /** Prevent insertion of rows. */ + uint8_t insert_rows; + + /** Prevent insertion of hyperlinks. */ + uint8_t insert_hyperlinks; + + /** Prevent deletion of columns. */ + uint8_t delete_columns; + + /** Prevent deletion of rows. */ + uint8_t delete_rows; + + /** Prevent sorting data. */ + uint8_t sort; + + /** Prevent filtering data. */ + uint8_t autofilter; + + /** Prevent insertion of pivot tables. */ + uint8_t pivot_tables; + + /** Protect scenarios. */ + uint8_t scenarios; + + /** Protect drawing objects. */ + uint8_t objects; + + uint8_t no_sheet; + uint8_t content; + uint8_t is_configured; + char hash[5]; +} lxw_protection; + +/** + * @brief Struct to represent an Excel worksheet. + * + * The members of the lxw_worksheet struct aren't modified directly. Instead + * the worksheet properties are set by calling the functions shown in + * worksheet.h. + */ +typedef struct lxw_worksheet { + + FILE *file; + FILE *optimize_tmpfile; + struct lxw_table_rows *table; + struct lxw_table_rows *hyperlinks; + struct lxw_cell **array; + struct lxw_merged_ranges *merged_ranges; + struct lxw_selections *selections; + struct lxw_image_data *image_data; + struct lxw_chart_data *chart_data; + + lxw_row_t dim_rowmin; + lxw_row_t dim_rowmax; + lxw_col_t dim_colmin; + lxw_col_t dim_colmax; + + lxw_sst *sst; + char *name; + char *quoted_name; + char *tmpdir; + + uint32_t index; + uint8_t active; + uint8_t selected; + uint8_t hidden; + uint16_t *active_sheet; + uint16_t *first_sheet; + + lxw_col_options **col_options; + uint16_t col_options_max; + + double *col_sizes; + uint16_t col_sizes_max; + + lxw_format **col_formats; + uint16_t col_formats_max; + + uint8_t col_size_changed; + uint8_t row_size_changed; + uint8_t optimize; + struct lxw_row *optimize_row; + + uint16_t fit_height; + uint16_t fit_width; + uint16_t horizontal_dpi; + uint16_t hlink_count; + uint16_t page_start; + uint16_t print_scale; + uint16_t rel_count; + uint16_t vertical_dpi; + uint16_t zoom; + uint8_t filter_on; + uint8_t fit_page; + uint8_t hcenter; + uint8_t orientation; + uint8_t outline_changed; + uint8_t outline_on; + uint8_t page_order; + uint8_t page_setup_changed; + uint8_t page_view; + uint8_t paper_size; + uint8_t print_gridlines; + uint8_t print_headers; + uint8_t print_options_changed; + uint8_t right_to_left; + uint8_t screen_gridlines; + uint8_t show_zeros; + uint8_t vba_codename; + uint8_t vcenter; + uint8_t zoom_scale_normal; + + lxw_color_t tab_color; + + double margin_left; + double margin_right; + double margin_top; + double margin_bottom; + double margin_header; + double margin_footer; + + double default_row_height; + uint32_t default_row_pixels; + uint32_t default_col_pixels; + uint8_t default_row_zeroed; + uint8_t default_row_set; + + uint8_t header_footer_changed; + char header[LXW_HEADER_FOOTER_MAX]; + char footer[LXW_HEADER_FOOTER_MAX]; + + struct lxw_repeat_rows repeat_rows; + struct lxw_repeat_cols repeat_cols; + struct lxw_print_area print_area; + struct lxw_autofilter autofilter; + + uint16_t merged_range_count; + + lxw_row_t *hbreaks; + lxw_col_t *vbreaks; + uint16_t hbreaks_count; + uint16_t vbreaks_count; + + struct lxw_rel_tuples *external_hyperlinks; + struct lxw_rel_tuples *external_drawing_links; + struct lxw_rel_tuples *drawing_links; + + struct lxw_panes panes; + + struct lxw_protection protection; + + lxw_drawing *drawing; + + STAILQ_ENTRY (lxw_worksheet) list_pointers; + +} lxw_worksheet; + +/* + * Worksheet initialization data. + */ +typedef struct lxw_worksheet_init_data { + uint32_t index; + uint8_t hidden; + uint8_t optimize; + uint16_t *active_sheet; + uint16_t *first_sheet; + lxw_sst *sst; + char *name; + char *quoted_name; + char *tmpdir; + +} lxw_worksheet_init_data; + +/* Struct to represent a worksheet row. */ +typedef struct lxw_row { + lxw_row_t row_num; + double height; + lxw_format *format; + uint8_t hidden; + uint8_t level; + uint8_t collapsed; + uint8_t row_changed; + uint8_t data_changed; + uint8_t height_changed; + struct lxw_table_cells *cells; + + /* tree management pointers for tree.h. */ + RB_ENTRY (lxw_row) tree_pointers; +} lxw_row; + +/* Struct to represent a worksheet cell. */ +typedef struct lxw_cell { + lxw_row_t row_num; + lxw_col_t col_num; + enum cell_types type; + lxw_format *format; + + union { + double number; + int32_t string_id; + char *string; + } u; + + double formula_result; + char *user_data1; + char *user_data2; + char *sst_string; + + /* List pointers for tree.h. */ + RB_ENTRY (lxw_cell) tree_pointers; +} lxw_cell; + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +/** + * @brief Write a number to a worksheet cell. + * + * @param worksheet pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param number The number to write to the cell. + * @param format A pointer to a Format instance or NULL. + * + * @return A #lxw_error code. + * + * The `worksheet_write_number()` function writes numeric types to the cell + * specified by `row` and `column`: + * + * @code + * worksheet_write_number(worksheet, 0, 0, 123456, NULL); + * worksheet_write_number(worksheet, 1, 0, 2.3451, NULL); + * @endcode + * + * @image html write_number01.png + * + * The native data type for all numbers in Excel is a IEEE-754 64-bit + * double-precision floating point, which is also the default type used by + * `%worksheet_write_number`. + * + * The `format` parameter is used to apply formatting to the cell. This + * parameter can be `NULL` to indicate no formatting or it can be a + * @ref format.h "Format" object. + * + * @code + * lxw_format *format = workbook_add_format(workbook); + * format_set_num_format(format, "$#,##0.00"); + * + * worksheet_write_number(worksheet, 0, 0, 1234.567, format); + * @endcode + * + * @image html write_number02.png + * + */ +lxw_error worksheet_write_number(lxw_worksheet *worksheet, + lxw_row_t row, + lxw_col_t col, double number, + lxw_format *format); +/** + * @brief Write a string to a worksheet cell. + * + * @param worksheet pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param string String to write to cell. + * @param format A pointer to a Format instance or NULL. + * + * @return A #lxw_error code. + * + * The `%worksheet_write_string()` function writes a string to the cell + * specified by `row` and `column`: + * + * @code + * worksheet_write_string(worksheet, 0, 0, "This phrase is English!", NULL); + * @endcode + * + * @image html write_string01.png + * + * The `format` parameter is used to apply formatting to the cell. This + * parameter can be `NULL` to indicate no formatting or it can be a + * @ref format.h "Format" object: + * + * @code + * lxw_format *format = workbook_add_format(workbook); + * format_set_bold(format); + * + * worksheet_write_string(worksheet, 0, 0, "This phrase is Bold!", format); + * @endcode + * + * @image html write_string02.png + * + * Unicode strings are supported in UTF-8 encoding. This generally requires + * that your source file is UTF-8 encoded or that the data has been read from + * a UTF-8 source: + * + * @code + * worksheet_write_string(worksheet, 0, 0, "Это фраза на русском!", NULL); + * @endcode + * + * @image html write_string03.png + * + */ +lxw_error worksheet_write_string(lxw_worksheet *worksheet, + lxw_row_t row, + lxw_col_t col, const char *string, + lxw_format *format); +/** + * @brief Write a formula to a worksheet cell. + * + * @param worksheet pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param formula Formula string to write to cell. + * @param format A pointer to a Format instance or NULL. + * + * @return A #lxw_error code. + * + * The `%worksheet_write_formula()` function writes a formula or function to + * the cell specified by `row` and `column`: + * + * @code + * worksheet_write_formula(worksheet, 0, 0, "=B3 + 6", NULL); + * worksheet_write_formula(worksheet, 1, 0, "=SIN(PI()/4)", NULL); + * worksheet_write_formula(worksheet, 2, 0, "=SUM(A1:A2)", NULL); + * worksheet_write_formula(worksheet, 3, 0, "=IF(A3>1,\"Yes\", \"No\")", NULL); + * worksheet_write_formula(worksheet, 4, 0, "=AVERAGE(1, 2, 3, 4)", NULL); + * worksheet_write_formula(worksheet, 5, 0, "=DATEVALUE(\"1-Jan-2013\")", NULL); + * @endcode + * + * @image html write_formula01.png + * + * The `format` parameter is used to apply formatting to the cell. This + * parameter can be `NULL` to indicate no formatting or it can be a + * @ref format.h "Format" object. + * + * Libxlsxwriter doesn't calculate the value of a formula and instead stores a + * default value of `0`. The correct formula result is displayed in Excel, as + * shown in the example above, since it recalculates the formulas when it loads + * the file. For cases where this is an issue see the + * `worksheet_write_formula_num()` function and the discussion in that section. + * + * Formulas must be written with the US style separator/range operator which + * is a comma (not semi-colon). Therefore a formula with multiple values + * should be written as follows: + * + * @code + * // OK. + * worksheet_write_formula(worksheet, 0, 0, "=SUM(1, 2, 3)", NULL); + * + * // NO. Error on load. + * worksheet_write_formula(worksheet, 1, 0, "=SUM(1; 2; 3)", NULL); + * @endcode + * + */ +lxw_error worksheet_write_formula(lxw_worksheet *worksheet, + lxw_row_t row, + lxw_col_t col, const char *formula, + lxw_format *format); +/** + * @brief Write an array formula to a worksheet cell. + * + * @param worksheet pointer to a lxw_worksheet instance to be updated. + * @param first_row The first row of the range. (All zero indexed.) + * @param first_col The first column of the range. + * @param last_row The last row of the range. + * @param last_col The last col of the range. + * @param formula Array formula to write to cell. + * @param format A pointer to a Format instance or NULL. + * + * @return A #lxw_error code. + * + * The `%worksheet_write_array_formula()` function writes an array formula to + * a cell range. In Excel an array formula is a formula that performs a + * calculation on a set of values. + * + * In Excel an array formula is indicated by a pair of braces around the + * formula: `{=SUM(A1:B1*A2:B2)}`. + * + * Array formulas can return a single value or a range or values. For array + * formulas that return a range of values you must specify the range that the + * return values will be written to. This is why this function has `first_` + * and `last_` row/column parameters. The RANGE() macro can also be used to + * specify the range: + * + * @code + * worksheet_write_array_formula(worksheet, 4, 0, 6, 0, "{=TREND(C5:C7,B5:B7)}", NULL); + * + * // Same as above using the RANGE() macro. + * worksheet_write_array_formula(worksheet, RANGE("A5:A7"), "{=TREND(C5:C7,B5:B7)}", NULL); + * @endcode + * + * If the array formula returns a single value then the `first_` and `last_` + * parameters should be the same: + * + * @code + * worksheet_write_array_formula(worksheet, 1, 0, 1, 0, "{=SUM(B1:C1*B2:C2)}", NULL); + * worksheet_write_array_formula(worksheet, RANGE("A2:A2"), "{=SUM(B1:C1*B2:C2)}", NULL); + * @endcode + * + */ +lxw_error worksheet_write_array_formula(lxw_worksheet *worksheet, + lxw_row_t first_row, + lxw_col_t first_col, + lxw_row_t last_row, + lxw_col_t last_col, + const char *formula, + lxw_format *format); + +lxw_error worksheet_write_array_formula_num(lxw_worksheet *worksheet, + lxw_row_t first_row, + lxw_col_t first_col, + lxw_row_t last_row, + lxw_col_t last_col, + const char *formula, + lxw_format *format, + double result); + +/** + * @brief Write a date or time to a worksheet cell. + * + * @param worksheet pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param datetime The datetime to write to the cell. + * @param format A pointer to a Format instance or NULL. + * + * @return A #lxw_error code. + * + * The `worksheet_write_datetime()` function can be used to write a date or + * time to the cell specified by `row` and `column`: + * + * @dontinclude dates_and_times02.c + * @skip include + * @until num_format + * @skip Feb + * @until } + * + * The `format` parameter should be used to apply formatting to the cell using + * a @ref format.h "Format" object as shown above. Without a date format the + * datetime will appear as a number only. + * + * See @ref working_with_dates for more information about handling dates and + * times in libxlsxwriter. + */ +lxw_error worksheet_write_datetime(lxw_worksheet *worksheet, + lxw_row_t row, + lxw_col_t col, lxw_datetime *datetime, + lxw_format *format); + +lxw_error worksheet_write_url_opt(lxw_worksheet *worksheet, + lxw_row_t row_num, + lxw_col_t col_num, const char *url, + lxw_format *format, const char *string, + const char *tooltip); +/** + * + * @param worksheet pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param url The url to write to the cell. + * @param format A pointer to a Format instance or NULL. + * + * @return A #lxw_error code. + * + * + * The `%worksheet_write_url()` function is used to write a URL/hyperlink to a + * worksheet cell specified by `row` and `column`. + * + * @code + * worksheet_write_url(worksheet, 0, 0, "http://libxlsxwriter.github.io", url_format); + * @endcode + * + * @image html hyperlinks_short.png + * + * The `format` parameter is used to apply formatting to the cell. This + * parameter can be `NULL` to indicate no formatting or it can be a @ref + * format.h "Format" object. The typical worksheet format for a hyperlink is a + * blue underline: + * + * @code + * lxw_format *url_format = workbook_add_format(workbook); + * + * format_set_underline (url_format, LXW_UNDERLINE_SINGLE); + * format_set_font_color(url_format, LXW_COLOR_BLUE); + * + * @endcode + * + * The usual web style URI's are supported: `%http://`, `%https://`, `%ftp://` + * and `mailto:` : + * + * @code + * worksheet_write_url(worksheet, 0, 0, "ftp://www.python.org/", url_format); + * worksheet_write_url(worksheet, 1, 0, "http://www.python.org/", url_format); + * worksheet_write_url(worksheet, 2, 0, "https://www.python.org/", url_format); + * worksheet_write_url(worksheet, 3, 0, "mailto:jmcnamara@cpan.org", url_format); + * + * @endcode + * + * An Excel hyperlink is comprised of two elements: the displayed string and + * the non-displayed link. By default the displayed string is the same as the + * link. However, it is possible to overwrite it with any other + * `libxlsxwriter` type using the appropriate `worksheet_write_*()` + * function. The most common case is to overwrite the displayed link text with + * another string: + * + * @code + * // Write a hyperlink but overwrite the displayed string. + * worksheet_write_url (worksheet, 2, 0, "http://libxlsxwriter.github.io", url_format); + * worksheet_write_string(worksheet, 2, 0, "Read the documentation.", url_format); + * + * @endcode + * + * @image html hyperlinks_short2.png + * + * Two local URIs are supported: `internal:` and `external:`. These are used + * for hyperlinks to internal worksheet references or external workbook and + * worksheet references: + * + * @code + * worksheet_write_url(worksheet, 0, 0, "internal:Sheet2!A1", url_format); + * worksheet_write_url(worksheet, 1, 0, "internal:Sheet2!B2", url_format); + * worksheet_write_url(worksheet, 2, 0, "internal:Sheet2!A1:B2", url_format); + * worksheet_write_url(worksheet, 3, 0, "internal:'Sales Data'!A1", url_format); + * worksheet_write_url(worksheet, 4, 0, "external:c:\\temp\\foo.xlsx", url_format); + * worksheet_write_url(worksheet, 5, 0, "external:c:\\foo.xlsx#Sheet2!A1", url_format); + * worksheet_write_url(worksheet, 6, 0, "external:..\\foo.xlsx", url_format); + * worksheet_write_url(worksheet, 7, 0, "external:..\\foo.xlsx#Sheet2!A1", url_format); + * worksheet_write_url(worksheet, 8, 0, "external:\\\\NET\\share\\foo.xlsx", url_format); + * + * @endcode + * + * Worksheet references are typically of the form `Sheet1!A1`. You can also + * link to a worksheet range using the standard Excel notation: + * `Sheet1!A1:B2`. + * + * In external links the workbook and worksheet name must be separated by the + * `#` character: + * + * @code + * worksheet_write_url(worksheet, 0, 0, "external:c:\\foo.xlsx#Sheet2!A1", url_format); + * @endcode + * + * You can also link to a named range in the target worksheet: For example say + * you have a named range called `my_name` in the workbook `c:\temp\foo.xlsx` + * you could link to it as follows: + * + * @code + * worksheet_write_url(worksheet, 0, 0, "external:c:\\temp\\foo.xlsx#my_name", url_format); + * + * @endcode + * + * Excel requires that worksheet names containing spaces or non alphanumeric + * characters are single quoted as follows: + * + * @code + * worksheet_write_url(worksheet, 0, 0, "internal:'Sales Data'!A1", url_format); + * @endcode + * + * Links to network files are also supported. Network files normally begin + * with two back slashes as follows `\\NETWORK\etc`. In order to represent + * this in a C string literal the backslashes should be escaped: + * @code + * worksheet_write_url(worksheet, 0, 0, "external:\\\\NET\\share\\foo.xlsx", url_format); + * @endcode + * + * + * Alternatively, you can use Windows style forward slashes. These are + * translated internally to backslashes: + * + * @code + * worksheet_write_url(worksheet, 0, 0, "external:c:/temp/foo.xlsx", url_format); + * worksheet_write_url(worksheet, 1, 0, "external://NET/share/foo.xlsx", url_format); + * + * @endcode + * + * + * **Note:** + * + * libxlsxwriter will escape the following characters in URLs as required + * by Excel: `\s " < > \ [ ] ^ { }` unless the URL already contains `%%xx` + * style escapes. In which case it is assumed that the URL was escaped + * correctly by the user and will by passed directly to Excel. + * + */ +lxw_error worksheet_write_url(lxw_worksheet *worksheet, + lxw_row_t row, + lxw_col_t col, const char *url, + lxw_format *format); + +/** + * @brief Write a formatted boolean worksheet cell. + * + * @param worksheet pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param value The boolean value to write to the cell. + * @param format A pointer to a Format instance or NULL. + * + * @return A #lxw_error code. + * + * Write an Excel boolean to the cell specified by `row` and `column`: + * + * @code + * worksheet_write_boolean(worksheet, 2, 2, 0, my_format); + * @endcode + * + */ +lxw_error worksheet_write_boolean(lxw_worksheet *worksheet, + lxw_row_t row, lxw_col_t col, + int value, lxw_format *format); + +/** + * @brief Write a formatted blank worksheet cell. + * + * @param worksheet pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param format A pointer to a Format instance or NULL. + * + * @return A #lxw_error code. + * + * Write a blank cell specified by `row` and `column`: + * + * @code + * worksheet_write_blank(worksheet, 1, 1, border_format); + * @endcode + * + * This function is used to add formatting to a cell which doesn't contain a + * string or number value. + * + * Excel differentiates between an "Empty" cell and a "Blank" cell. An Empty + * cell is a cell which doesn't contain data or formatting whilst a Blank cell + * doesn't contain data but does contain formatting. Excel stores Blank cells + * but ignores Empty cells. + * + * As such, if you write an empty cell without formatting it is ignored. + * + */ +lxw_error worksheet_write_blank(lxw_worksheet *worksheet, + lxw_row_t row, lxw_col_t col, + lxw_format *format); + +/** + * @brief Write a formula to a worksheet cell with a user defined result. + * + * @param worksheet pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param formula Formula string to write to cell. + * @param format A pointer to a Format instance or NULL. + * @param result A user defined result for a formula. + * + * @return A #lxw_error code. + * + * The `%worksheet_write_formula_num()` function writes a formula or Excel + * function to the cell specified by `row` and `column` with a user defined + * result: + * + * @code + * // Required as a workaround only. + * worksheet_write_formula_num(worksheet, 0, 0, "=1 + 2", NULL, 3); + * @endcode + * + * Libxlsxwriter doesn't calculate the value of a formula and instead stores + * the value `0` as the formula result. It then sets a global flag in the XLSX + * file to say that all formulas and functions should be recalculated when the + * file is opened. + * + * This is the method recommended in the Excel documentation and in general it + * works fine with spreadsheet applications. + * + * However, applications that don't have a facility to calculate formulas, + * such as Excel Viewer, or some mobile applications will only display the `0` + * results. + * + * If required, the `%worksheet_write_formula_num()` function can be used to + * specify a formula and its result. + * + * This function is rarely required and is only provided for compatibility + * with some third party applications. For most applications the + * worksheet_write_formula() function is the recommended way of writing + * formulas. + * + */ +lxw_error worksheet_write_formula_num(lxw_worksheet *worksheet, + lxw_row_t row, + lxw_col_t col, + const char *formula, + lxw_format *format, double result); + +/** + * @brief Set the properties for a row of cells. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param height The row height. + * @param format A pointer to a Format instance or NULL. + * + * The `%worksheet_set_row()` function is used to change the default + * properties of a row. The most common use for this function is to change the + * height of a row: + * + * @code + * // Set the height of Row 1 to 20. + * worksheet_set_row(worksheet, 0, 20, NULL); + * @endcode + * + * The other common use for `%worksheet_set_row()` is to set the a @ref + * format.h "Format" for all cells in the row: + * + * @code + * lxw_format *bold = workbook_add_format(workbook); + * format_set_bold(bold); + * + * // Set the header row to bold. + * worksheet_set_row(worksheet, 0, 15, bold); + * @endcode + * + * If you wish to set the format of a row without changing the height you can + * pass the default row height of #LXW_DEF_ROW_HEIGHT = 15: + * + * @code + * worksheet_set_row(worksheet, 0, LXW_DEF_ROW_HEIGHT, format); + * worksheet_set_row(worksheet, 0, 15, format); // Same as above. + * @endcode + * + * The `format` parameter will be applied to any cells in the row that don't + * have a format. As with Excel the row format is overridden by an explicit + * cell format. For example: + * + * @code + * // Row 1 has format1. + * worksheet_set_row(worksheet, 0, 15, format1); + * + * // Cell A1 in Row 1 defaults to format1. + * worksheet_write_string(worksheet, 0, 0, "Hello", NULL); + * + * // Cell B1 in Row 1 keeps format2. + * worksheet_write_string(worksheet, 0, 1, "Hello", format2); + * @endcode + * + */ +lxw_error worksheet_set_row(lxw_worksheet *worksheet, + lxw_row_t row, double height, lxw_format *format); + +/** + * @brief Set the properties for a row of cells. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param height The row height. + * @param format A pointer to a Format instance or NULL. + * @param options Optional row parameters: hidden, level, collapsed. + * + * The `%worksheet_set_row_opt()` function is the same as + * `worksheet_set_row()` with an additional `options` parameter. + * + * The `options` parameter is a #lxw_row_col_options struct. It has the + * following members but currently only the `hidden` property is supported: + * + * - `hidden` + * - `level` + * - `collapsed` + * + * The `"hidden"` option is used to hide a row. This can be used, for + * example, to hide intermediary steps in a complicated calculation: + * + * @code + * lxw_row_col_options options = {.hidden = 1, .level = 0, .collapsed = 0}; + * + * // Hide the fourth row. + * worksheet_set_row(worksheet, 3, 20, NULL, &options); + * @endcode + * + */ +lxw_error worksheet_set_row_opt(lxw_worksheet *worksheet, + lxw_row_t row, + double height, + lxw_format *format, + lxw_row_col_options *options); + +/** + * @brief Set the properties for one or more columns of cells. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param first_col The zero indexed first column. + * @param last_col The zero indexed last column. + * @param width The width of the column(s). + * @param format A pointer to a Format instance or NULL. + * + * The `%worksheet_set_column()` function can be used to change the default + * properties of a single column or a range of columns: + * + * @code + * // Width of columns B:D set to 30. + * worksheet_set_column(worksheet, 1, 3, 30, NULL); + * + * @endcode + * + * If `%worksheet_set_column()` is applied to a single column the value of + * `first_col` and `last_col` should be the same: + * + * @code + * // Width of column B set to 30. + * worksheet_set_column(worksheet, 1, 1, 30, NULL); + * + * @endcode + * + * It is also possible, and generally clearer, to specify a column range using + * the form of `COLS()` macro: + * + * @code + * worksheet_set_column(worksheet, 4, 4, 20, NULL); + * worksheet_set_column(worksheet, 5, 8, 30, NULL); + * + * // Same as the examples above but clearer. + * worksheet_set_column(worksheet, COLS("E:E"), 20, NULL); + * worksheet_set_column(worksheet, COLS("F:H"), 30, NULL); + * + * @endcode + * + * The `width` parameter sets the column width in the same units used by Excel + * which is: the number of characters in the default font. The default width + * is 8.43 in the default font of Calibri 11. The actual relationship between + * a string width and a column width in Excel is complex. See the + * [following explanation of column widths](https://support.microsoft.com/en-us/kb/214123) + * from the Microsoft support documentation for more details. + * + * There is no way to specify "AutoFit" for a column in the Excel file + * format. This feature is only available at runtime from within Excel. It is + * possible to simulate "AutoFit" in your application by tracking the maximum + * width of the data in the column as your write it and then adjusting the + * column width at the end. + * + * As usual the @ref format.h `format` parameter is optional. If you wish to + * set the format without changing the width you can pass a default column + * width of #LXW_DEF_COL_WIDTH = 8.43: + * + * @code + * lxw_format *bold = workbook_add_format(workbook); + * format_set_bold(bold); + * + * // Set the first column to bold. + * worksheet_set_column(worksheet, 0, 0, LXW_DEF_COL_HEIGHT, bold); + * @endcode + * + * The `format` parameter will be applied to any cells in the column that + * don't have a format. For example: + * + * @code + * // Column 1 has format1. + * worksheet_set_column(worksheet, COLS("A:A"), 8.43, format1); + * + * // Cell A1 in column 1 defaults to format1. + * worksheet_write_string(worksheet, 0, 0, "Hello", NULL); + * + * // Cell A2 in column 1 keeps format2. + * worksheet_write_string(worksheet, 1, 0, "Hello", format2); + * @endcode + * + * As in Excel a row format takes precedence over a default column format: + * + * @code + * // Row 1 has format1. + * worksheet_set_row(worksheet, 0, 15, format1); + * + * // Col 1 has format2. + * worksheet_set_column(worksheet, COLS("A:A"), 8.43, format2); + * + * // Cell A1 defaults to format1, the row format. + * worksheet_write_string(worksheet, 0, 0, "Hello", NULL); + * + * // Cell A2 keeps format2, the column format. + * worksheet_write_string(worksheet, 1, 0, "Hello", NULL); + * @endcode + */ +lxw_error worksheet_set_column(lxw_worksheet *worksheet, + lxw_col_t first_col, + lxw_col_t last_col, + double width, lxw_format *format); + + /** + * @brief Set the properties for one or more columns of cells with options. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param first_col The zero indexed first column. + * @param last_col The zero indexed last column. + * @param width The width of the column(s). + * @param format A pointer to a Format instance or NULL. + * @param options Optional row parameters: hidden, level, collapsed. + * + * The `%worksheet_set_column_opt()` function is the same as + * `worksheet_set_column()` with an additional `options` parameter. + * + * The `options` parameter is a #lxw_row_col_options struct. It has the + * following members but currently only the `hidden` property is supported: + * + * - `hidden` + * - `level` + * - `collapsed` + * + * The `"hidden"` option is used to hide a column. This can be used, for + * example, to hide intermediary steps in a complicated calculation: + * + * @code + * lxw_row_col_options options = {.hidden = 1, .level = 0, .collapsed = 0}; + * + * worksheet_set_column_opt(worksheet, COLS("A:A"), 8.43, NULL, &options); + * @endcode + * + */ +lxw_error worksheet_set_column_opt(lxw_worksheet *worksheet, + lxw_col_t first_col, + lxw_col_t last_col, + double width, + lxw_format *format, + lxw_row_col_options *options); + +/** + * @brief Insert an image in a worksheet cell. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param filename The image filename, with path if required. + * + * @return A #lxw_error code. + * + * This function can be used to insert a image into a worksheet. The image can + * be in PNG, JPEG or BMP format: + * + * @code + * worksheet_insert_image(worksheet, 2, 1, "logo.png"); + * @endcode + * + * @image html insert_image.png + * + * The `worksheet_insert_image_opt()` function takes additional optional + * parameters to position and scale the image, see below. + * + * **Note**: + * The scaling of a image may be affected if is crosses a row that has its + * default height changed due to a font that is larger than the default font + * size or that has text wrapping turned on. To avoid this you should + * explicitly set the height of the row using `worksheet_set_row()` if it + * crosses an inserted image. + * + * BMP images are only supported for backward compatibility. In general it is + * best to avoid BMP images since they aren't compressed. If used, BMP images + * must be 24 bit, true color, bitmaps. + */ +lxw_error worksheet_insert_image(lxw_worksheet *worksheet, + lxw_row_t row, lxw_col_t col, + const char *filename); + +/** + * @brief Insert an image in a worksheet cell, with options. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param filename The image filename, with path if required. + * @param options Optional image parameters. + * + * @return A #lxw_error code. + * + * The `%worksheet_insert_image_opt()` function is like + * `worksheet_insert_image()` function except that it takes an optional + * #lxw_image_options struct to scale and position the image: + * + * @code + * lxw_image_options options = {.x_offset = 30, .y_offset = 10, + * .x_scale = 0.5, .y_scale = 0.5}; + * + * worksheet_insert_image_opt(worksheet, 2, 1, "logo.png", &options); + * + * @endcode + * + * @image html insert_image_opt.png + * + * @note See the notes about row scaling and BMP images in + * `worksheet_insert_image()` above. + */ +lxw_error worksheet_insert_image_opt(lxw_worksheet *worksheet, + lxw_row_t row, lxw_col_t col, + const char *filename, + lxw_image_options *options); +/** + * @brief Insert a chart object into a worksheet. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param chart A #lxw_chart object created via workbook_add_chart(). + * + * @return A #lxw_error code. + * + * The `%worksheet_insert_chart()` can be used to insert a chart into a + * worksheet. The chart object must be created first using the + * `workbook_add_chart()` function and configured using the @ref chart.h + * functions. + * + * @code + * // Create a chart object. + * lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE); + * + * // Add a data series to the chart. + * chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$6"); + * + * // Insert the chart into the worksheet + * worksheet_insert_chart(worksheet, 0, 2, chart); + * @endcode + * + * @image html chart_working.png + * + * + * **Note:** + * + * A chart may only be inserted into a worksheet once. If several similar + * charts are required then each one must be created separately with + * `%worksheet_insert_chart()`. + * + */ +lxw_error worksheet_insert_chart(lxw_worksheet *worksheet, + lxw_row_t row, lxw_col_t col, + lxw_chart *chart); + +/** + * @brief Insert a chart object into a worksheet, with options. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param row The zero indexed row number. + * @param col The zero indexed column number. + * @param chart A #lxw_chart object created via workbook_add_chart(). + * @param user_options Optional chart parameters. + * + * @return A #lxw_error code. + * + * The `%worksheet_insert_chart_opt()` function is like + * `worksheet_insert_chart()` function except that it takes an optional + * #lxw_image_options struct to scale and position the image of the chart: + * + * @code + * lxw_image_options options = {.x_offset = 30, .y_offset = 10, + * .x_scale = 0.5, .y_scale = 0.75}; + * + * worksheet_insert_chart_opt(worksheet, 0, 2, chart, &options); + * + * @endcode + * + * @image html chart_line_opt.png + * + * The #lxw_image_options struct is the same struct used in + * `worksheet_insert_image_opt()` to position and scale images. + * + */ +lxw_error worksheet_insert_chart_opt(lxw_worksheet *worksheet, + lxw_row_t row, lxw_col_t col, + lxw_chart *chart, + lxw_image_options *user_options); + +/** + * @brief Merge a range of cells. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param first_row The first row of the range. (All zero indexed.) + * @param first_col The first column of the range. + * @param last_row The last row of the range. + * @param last_col The last col of the range. + * @param string String to write to the merged range. + * @param format A pointer to a Format instance or NULL. + * + * @return A #lxw_error code. + * + * The `%worksheet_merge_range()` function allows cells to be merged together + * so that they act as a single area. + * + * Excel generally merges and centers cells at same time. To get similar + * behavior with libxlsxwriter you need to apply a @ref format.h "Format" + * object with the appropriate alignment: + * + * @code + * lxw_format *merge_format = workbook_add_format(workbook); + * format_set_align(merge_format, LXW_ALIGN_CENTER); + * + * worksheet_merge_range(worksheet, 1, 1, 1, 3, "Merged Range", merge_format); + * + * @endcode + * + * It is possible to apply other formatting to the merged cells as well: + * + * @code + * format_set_align (merge_format, LXW_ALIGN_CENTER); + * format_set_align (merge_format, LXW_ALIGN_VERTICAL_CENTER); + * format_set_border (merge_format, LXW_BORDER_DOUBLE); + * format_set_bold (merge_format); + * format_set_bg_color(merge_format, 0xD7E4BC); + * + * worksheet_merge_range(worksheet, 2, 1, 3, 3, "Merged Range", merge_format); + * + * @endcode + * + * @image html merge.png + * + * The `%worksheet_merge_range()` function writes a `char*` string using + * `worksheet_write_string()`. In order to write other data types, such as a + * number or a formula, you can overwrite the first cell with a call to one of + * the other write functions. The same Format should be used as was used in + * the merged range. + * + * @code + * // First write a range with a blank string. + * worksheet_merge_range (worksheet, 1, 1, 1, 3, "", format); + * + * // Then overwrite the first cell with a number. + * worksheet_write_number(worksheet, 1, 1, 123, format); + * @endcode + * + * @note Merged ranges generally don’t work in libxlsxwriter when the Workbook + * #lxw_workbook_options `constant_memory` mode is enabled. + */ +lxw_error worksheet_merge_range(lxw_worksheet *worksheet, lxw_row_t first_row, + lxw_col_t first_col, lxw_row_t last_row, + lxw_col_t last_col, const char *string, + lxw_format *format); + +/** + * @brief Set the autofilter area in the worksheet. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param first_row The first row of the range. (All zero indexed.) + * @param first_col The first column of the range. + * @param last_row The last row of the range. + * @param last_col The last col of the range. + * + * @return A #lxw_error code. + * + * The `%worksheet_autofilter()` function allows an autofilter to be added to + * a worksheet. + * + * An autofilter is a way of adding drop down lists to the headers of a 2D + * range of worksheet data. This allows users to filter the data based on + * simple criteria so that some data is shown and some is hidden. + * + * @image html autofilter.png + * + * To add an autofilter to a worksheet: + * + * @code + * worksheet_autofilter(worksheet, 0, 0, 50, 3); + * + * // Same as above using the RANGE() macro. + * worksheet_autofilter(worksheet, RANGE("A1:D51")); + * @endcode + * + * Note: it isn't currently possible to apply filter conditions to the + * autofilter. + */ +lxw_error worksheet_autofilter(lxw_worksheet *worksheet, lxw_row_t first_row, + lxw_col_t first_col, lxw_row_t last_row, + lxw_col_t last_col); + + /** + * @brief Make a worksheet the active, i.e., visible worksheet. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * The `%worksheet_activate()` function is used to specify which worksheet is + * initially visible in a multi-sheet workbook: + * + * @code + * lxw_worksheet *worksheet1 = workbook_add_worksheet(workbook, NULL); + * lxw_worksheet *worksheet2 = workbook_add_worksheet(workbook, NULL); + * lxw_worksheet *worksheet3 = workbook_add_worksheet(workbook, NULL); + * + * worksheet_activate(worksheet3); + * @endcode + * + * @image html worksheet_activate.png + * + * More than one worksheet can be selected via the `worksheet_select()` + * function, see below, however only one worksheet can be active. + * + * The default active worksheet is the first worksheet. + * + */ +void worksheet_activate(lxw_worksheet *worksheet); + + /** + * @brief Set a worksheet tab as selected. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * The `%worksheet_select()` function is used to indicate that a worksheet is + * selected in a multi-sheet workbook: + * + * @code + * worksheet_activate(worksheet1); + * worksheet_select(worksheet2); + * worksheet_select(worksheet3); + * + * @endcode + * + * A selected worksheet has its tab highlighted. Selecting worksheets is a + * way of grouping them together so that, for example, several worksheets + * could be printed in one go. A worksheet that has been activated via the + * `worksheet_activate()` function will also appear as selected. + * + */ +void worksheet_select(lxw_worksheet *worksheet); + +/** + * @brief Hide the current worksheet. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * The `%worksheet_hide()` function is used to hide a worksheet: + * + * @code + * worksheet_hide(worksheet2); + * @endcode + * + * You may wish to hide a worksheet in order to avoid confusing a user with + * intermediate data or calculations. + * + * @image html hide_sheet.png + * + * A hidden worksheet can not be activated or selected so this function is + * mutually exclusive with the `worksheet_activate()` and `worksheet_select()` + * functions. In addition, since the first worksheet will default to being the + * active worksheet, you cannot hide the first worksheet without activating + * another sheet: + * + * @code + * worksheet_activate(worksheet2); + * worksheet_hide(worksheet1); + * @endcode + */ +void worksheet_hide(lxw_worksheet *worksheet); + +/** + * @brief Set current worksheet as the first visible sheet tab. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * The `worksheet_activate()` function determines which worksheet is initially + * selected. However, if there are a large number of worksheets the selected + * worksheet may not appear on the screen. To avoid this you can select the + * leftmost visible worksheet tab using `%worksheet_set_first_sheet()`: + * + * @code + * worksheet_set_first_sheet(worksheet19); // First visible worksheet tab. + * worksheet_activate(worksheet20); // First visible worksheet. + * @endcode + * + * This function is not required very often. The default value is the first + * worksheet. + */ +void worksheet_set_first_sheet(lxw_worksheet *worksheet); + +/** + * @brief Split and freeze a worksheet into panes. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param row The cell row (zero indexed). + * @param col The cell column (zero indexed). + * + * The `%worksheet_freeze_panes()` function can be used to divide a worksheet + * into horizontal or vertical regions known as panes and to "freeze" these + * panes so that the splitter bars are not visible. + * + * The parameters `row` and `col` are used to specify the location of the + * split. It should be noted that the split is specified at the top or left of + * a cell and that the function uses zero based indexing. Therefore to freeze + * the first row of a worksheet it is necessary to specify the split at row 2 + * (which is 1 as the zero-based index). + * + * You can set one of the `row` and `col` parameters as zero if you do not + * want either a vertical or horizontal split. + * + * Examples: + * + * @code + * worksheet_freeze_panes(worksheet1, 1, 0); // Freeze the first row. + * worksheet_freeze_panes(worksheet2, 0, 1); // Freeze the first column. + * worksheet_freeze_panes(worksheet3, 1, 1); // Freeze first row/column. + * + * @endcode + * + */ +void worksheet_freeze_panes(lxw_worksheet *worksheet, + lxw_row_t row, lxw_col_t col); +/** + * @brief Split a worksheet into panes. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param vertical The position for the vertical split. + * @param horizontal The position for the horizontal split. + * + * The `%worksheet_split_panes()` function can be used to divide a worksheet + * into horizontal or vertical regions known as panes. This function is + * different from the `worksheet_freeze_panes()` function in that the splits + * between the panes will be visible to the user and each pane will have its + * own scroll bars. + * + * The parameters `vertical` and `horizontal` are used to specify the vertical + * and horizontal position of the split. The units for `vertical` and + * `horizontal` are the same as those used by Excel to specify row height and + * column width. However, the vertical and horizontal units are different from + * each other. Therefore you must specify the `vertical` and `horizontal` + * parameters in terms of the row heights and column widths that you have set + * or the default values which are 15 for a row and 8.43 for a column. + * + * Examples: + * + * @code + * worksheet_split_panes(worksheet1, 15, 0); // First row. + * worksheet_split_panes(worksheet2, 0, 8.43); // First column. + * worksheet_split_panes(worksheet3, 15, 8.43); // First row and column. + * + * @endcode + * + */ +void worksheet_split_panes(lxw_worksheet *worksheet, + double vertical, double horizontal); + +/* worksheet_freeze_panes() with infrequent options. Undocumented for now. */ +void worksheet_freeze_panes_opt(lxw_worksheet *worksheet, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t top_row, lxw_col_t left_col, + uint8_t type); + +/* worksheet_split_panes() with infrequent options. Undocumented for now. */ +void worksheet_split_panes_opt(lxw_worksheet *worksheet, + double vertical, double horizontal, + lxw_row_t top_row, lxw_col_t left_col); +/** + * @brief Set the selected cell or cells in a worksheet: + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param first_row The first row of the range. (All zero indexed.) + * @param first_col The first column of the range. + * @param last_row The last row of the range. + * @param last_col The last col of the range. + * + * + * The `%worksheet_set_selection()` function can be used to specify which cell + * or range of cells is selected in a worksheet: The most common requirement + * is to select a single cell, in which case the `first_` and `last_` + * parameters should be the same. + * + * The active cell within a selected range is determined by the order in which + * `first_` and `last_` are specified. + * + * Examples: + * + * @code + * worksheet_set_selection(worksheet1, 3, 3, 3, 3); // Cell D4. + * worksheet_set_selection(worksheet2, 3, 3, 6, 6); // Cells D4 to G7. + * worksheet_set_selection(worksheet3, 6, 6, 3, 3); // Cells G7 to D4. + * worksheet_set_selection(worksheet5, RANGE("D4:G7")); // Using the RANGE macro. + * + * @endcode + * + */ +void worksheet_set_selection(lxw_worksheet *worksheet, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t last_row, lxw_col_t last_col); + +/** + * @brief Set the page orientation as landscape. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * This function is used to set the orientation of a worksheet's printed page + * to landscape: + * + * @code + * worksheet_set_landscape(worksheet); + * @endcode + */ +void worksheet_set_landscape(lxw_worksheet *worksheet); + +/** + * @brief Set the page orientation as portrait. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * This function is used to set the orientation of a worksheet's printed page + * to portrait. The default worksheet orientation is portrait, so this + * function isn't generally required: + * + * @code + * worksheet_set_portrait(worksheet); + * @endcode + */ +void worksheet_set_portrait(lxw_worksheet *worksheet); + +/** + * @brief Set the page layout to page view mode. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * This function is used to display the worksheet in "Page View/Layout" mode: + * + * @code + * worksheet_set_page_view(worksheet); + * @endcode + */ +void worksheet_set_page_view(lxw_worksheet *worksheet); + +/** + * @brief Set the paper type for printing. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param paper_type The Excel paper format type. + * + * This function is used to set the paper format for the printed output of a + * worksheet. The following paper styles are available: + * + * + * Index | Paper format | Paper size + * :------- | :---------------------- | :------------------- + * 0 | Printer default | Printer default + * 1 | Letter | 8 1/2 x 11 in + * 2 | Letter Small | 8 1/2 x 11 in + * 3 | Tabloid | 11 x 17 in + * 4 | Ledger | 17 x 11 in + * 5 | Legal | 8 1/2 x 14 in + * 6 | Statement | 5 1/2 x 8 1/2 in + * 7 | Executive | 7 1/4 x 10 1/2 in + * 8 | A3 | 297 x 420 mm + * 9 | A4 | 210 x 297 mm + * 10 | A4 Small | 210 x 297 mm + * 11 | A5 | 148 x 210 mm + * 12 | B4 | 250 x 354 mm + * 13 | B5 | 182 x 257 mm + * 14 | Folio | 8 1/2 x 13 in + * 15 | Quarto | 215 x 275 mm + * 16 | --- | 10x14 in + * 17 | --- | 11x17 in + * 18 | Note | 8 1/2 x 11 in + * 19 | Envelope 9 | 3 7/8 x 8 7/8 + * 20 | Envelope 10 | 4 1/8 x 9 1/2 + * 21 | Envelope 11 | 4 1/2 x 10 3/8 + * 22 | Envelope 12 | 4 3/4 x 11 + * 23 | Envelope 14 | 5 x 11 1/2 + * 24 | C size sheet | --- + * 25 | D size sheet | --- + * 26 | E size sheet | --- + * 27 | Envelope DL | 110 x 220 mm + * 28 | Envelope C3 | 324 x 458 mm + * 29 | Envelope C4 | 229 x 324 mm + * 30 | Envelope C5 | 162 x 229 mm + * 31 | Envelope C6 | 114 x 162 mm + * 32 | Envelope C65 | 114 x 229 mm + * 33 | Envelope B4 | 250 x 353 mm + * 34 | Envelope B5 | 176 x 250 mm + * 35 | Envelope B6 | 176 x 125 mm + * 36 | Envelope | 110 x 230 mm + * 37 | Monarch | 3.875 x 7.5 in + * 38 | Envelope | 3 5/8 x 6 1/2 in + * 39 | Fanfold | 14 7/8 x 11 in + * 40 | German Std Fanfold | 8 1/2 x 12 in + * 41 | German Legal Fanfold | 8 1/2 x 13 in + * + * Note, it is likely that not all of these paper types will be available to + * the end user since it will depend on the paper formats that the user's + * printer supports. Therefore, it is best to stick to standard paper types: + * + * @code + * worksheet_set_paper(worksheet1, 1); // US Letter + * worksheet_set_paper(worksheet2, 9); // A4 + * @endcode + * + * If you do not specify a paper type the worksheet will print using the + * printer's default paper style. + */ +void worksheet_set_paper(lxw_worksheet *worksheet, uint8_t paper_type); + +/** + * @brief Set the worksheet margins for the printed page. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param left Left margin in inches. Excel default is 0.7. + * @param right Right margin in inches. Excel default is 0.7. + * @param top Top margin in inches. Excel default is 0.75. + * @param bottom Bottom margin in inches. Excel default is 0.75. + * + * The `%worksheet_set_margins()` function is used to set the margins of the + * worksheet when it is printed. The units are in inches. Specifying `-1` for + * any parameter will give the default Excel value as shown above. + * + * @code + * worksheet_set_margins(worksheet, 1.3, 1.2, -1, -1); + * @endcode + * + */ +void worksheet_set_margins(lxw_worksheet *worksheet, double left, + double right, double top, double bottom); + +/** + * @brief Set the printed page header caption. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param string The header string. + * + * @return A #lxw_error code. + * + * Headers and footers are generated using a string which is a combination of + * plain text and control characters. + * + * The available control character are: + * + * + * | Control | Category | Description | + * | --------------- | ------------- | --------------------- | + * | `&L` | Justification | Left | + * | `&C` | | Center | + * | `&R` | | Right | + * | `&P` | Information | Page number | + * | `&N` | | Total number of pages | + * | `&D` | | Date | + * | `&T` | | Time | + * | `&F` | | File name | + * | `&A` | | Worksheet name | + * | `&Z` | | Workbook path | + * | `&fontsize` | Font | Font size | + * | `&"font,style"` | | Font name and style | + * | `&U` | | Single underline | + * | `&E` | | Double underline | + * | `&S` | | Strikethrough | + * | `&X` | | Superscript | + * | `&Y` | | Subscript | + * + * + * Text in headers and footers can be justified (aligned) to the left, center + * and right by prefixing the text with the control characters `&L`, `&C` and + * `&R`. + * + * For example (with ASCII art representation of the results): + * + * @code + * worksheet_set_header(worksheet, "&LHello"); + * + * --------------------------------------------------------------- + * | | + * | Hello | + * | | + * + * + * worksheet_set_header(worksheet, "&CHello"); + * + * --------------------------------------------------------------- + * | | + * | Hello | + * | | + * + * + * worksheet_set_header(worksheet, "&RHello"); + * + * --------------------------------------------------------------- + * | | + * | Hello | + * | | + * + * + * @endcode + * + * For simple text, if you do not specify any justification the text will be + * centered. However, you must prefix the text with `&C` if you specify a font + * name or any other formatting: + * + * @code + * worksheet_set_header(worksheet, "Hello"); + * + * --------------------------------------------------------------- + * | | + * | Hello | + * | | + * + * @endcode + * + * You can have text in each of the justification regions: + * + * @code + * worksheet_set_header(worksheet, "&LCiao&CBello&RCielo"); + * + * --------------------------------------------------------------- + * | | + * | Ciao Bello Cielo | + * | | + * + * @endcode + * + * The information control characters act as variables that Excel will update + * as the workbook or worksheet changes. Times and dates are in the users + * default format: + * + * @code + * worksheet_set_header(worksheet, "&CPage &P of &N"); + * + * --------------------------------------------------------------- + * | | + * | Page 1 of 6 | + * | | + * + * worksheet_set_header(worksheet, "&CUpdated at &T"); + * + * --------------------------------------------------------------- + * | | + * | Updated at 12:30 PM | + * | | + * + * @endcode + * + * You can specify the font size of a section of the text by prefixing it with + * the control character `&n` where `n` is the font size: + * + * @code + * worksheet_set_header(worksheet1, "&C&30Hello Big"); + * worksheet_set_header(worksheet2, "&C&10Hello Small"); + * + * @endcode + * + * You can specify the font of a section of the text by prefixing it with the + * control sequence `&"font,style"` where `fontname` is a font name such as + * Windows font descriptions: "Regular", "Italic", "Bold" or "Bold Italic": + * "Courier New" or "Times New Roman" and `style` is one of the standard + * + * @code + * worksheet_set_header(worksheet1, "&C&\"Courier New,Italic\"Hello"); + * worksheet_set_header(worksheet2, "&C&\"Courier New,Bold Italic\"Hello"); + * worksheet_set_header(worksheet3, "&C&\"Times New Roman,Regular\"Hello"); + * + * @endcode + * + * It is possible to combine all of these features together to create + * sophisticated headers and footers. As an aid to setting up complicated + * headers and footers you can record a page set-up as a macro in Excel and + * look at the format strings that VBA produces. Remember however that VBA + * uses two double quotes `""` to indicate a single double quote. For the last + * example above the equivalent VBA code looks like this: + * + * @code + * .LeftHeader = "" + * .CenterHeader = "&""Times New Roman,Regular""Hello" + * .RightHeader = "" + * + * @endcode + * + * Alternatively you can inspect the header and footer strings in an Excel + * file by unzipping it and grepping the XML sub-files. The following shows + * how to do that using libxml's xmllint to format the XML for clarity: + * + * @code + * + * $ unzip myfile.xlsm -d myfile + * $ xmllint --format `find myfile -name "*.xml" | xargs` | egrep "Header|Footer" + * + * + * &L&P + * + * + * @endcode + * + * Note that in this case you need to unescape the Html. In the above example + * the header string would be `&L&P`. + * + * To include a single literal ampersand `&` in a header or footer you should + * use a double ampersand `&&`: + * + * @code + * worksheet_set_header(worksheet, "&CCuriouser && Curiouser - Attorneys at Law"); + * @endcode + * + * Note, the header or footer string must be less than 255 characters. Strings + * longer than this will not be written. + * + */ +lxw_error worksheet_set_header(lxw_worksheet *worksheet, const char *string); + +/** + * @brief Set the printed page footer caption. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param string The footer string. + * + * @return A #lxw_error code. + * + * The syntax of this function is the same as worksheet_set_header(). + * + */ +lxw_error worksheet_set_footer(lxw_worksheet *worksheet, const char *string); + +/** + * @brief Set the printed page header caption with additional options. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param string The header string. + * @param options Header options. + * + * @return A #lxw_error code. + * + * The syntax of this function is the same as worksheet_set_header() with an + * additional parameter to specify options for the header. + * + * Currently, the only available option is the header margin: + * + * @code + * + * lxw_header_footer_options header_options = { 0.2 }; + * + * worksheet_set_header_opt(worksheet, "Some text", &header_options); + * + * @endcode + * + */ +lxw_error worksheet_set_header_opt(lxw_worksheet *worksheet, + const char *string, + lxw_header_footer_options *options); + +/** + * @brief Set the printed page footer caption with additional options. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param string The footer string. + * @param options Footer options. + * + * @return A #lxw_error code. + * + * The syntax of this function is the same as worksheet_set_header_opt(). + * + */ +lxw_error worksheet_set_footer_opt(lxw_worksheet *worksheet, + const char *string, + lxw_header_footer_options *options); + +/** + * @brief Set the horizontal page breaks on a worksheet. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param breaks Array of page breaks. + * + * @return A #lxw_error code. + * + * The `%worksheet_set_h_pagebreaks()` function adds horizontal page breaks to + * a worksheet. A page break causes all the data that follows it to be printed + * on the next page. Horizontal page breaks act between rows. + * + * The function takes an array of one or more page breaks. The type of the + * array data is @ref lxw_row_t and the last element of the array must be 0: + * + * @code + * lxw_row_t breaks1[] = {20, 0}; // 1 page break. Zero indicates the end. + * lxw_row_t breaks2[] = {20, 40, 60, 80, 0}; + * + * worksheet_set_h_pagebreaks(worksheet1, breaks1); + * worksheet_set_h_pagebreaks(worksheet2, breaks2); + * @endcode + * + * To create a page break between rows 20 and 21 you must specify the break at + * row 21. However in zero index notation this is actually row 20: + * + * @code + * // Break between row 20 and 21. + * lxw_row_t breaks[] = {20, 0}; + * + * worksheet_set_h_pagebreaks(worksheet, breaks); + * @endcode + * + * There is an Excel limitation of 1023 horizontal page breaks per worksheet. + * + * Note: If you specify the "fit to page" option via the + * `worksheet_fit_to_pages()` function it will override all manual page + * breaks. + * + */ +lxw_error worksheet_set_h_pagebreaks(lxw_worksheet *worksheet, + lxw_row_t breaks[]); + +/** + * @brief Set the vertical page breaks on a worksheet. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param breaks Array of page breaks. + * + * @return A #lxw_error code. + * + * The `%worksheet_set_v_pagebreaks()` function adds vertical page breaks to a + * worksheet. A page break causes all the data that follows it to be printed + * on the next page. Vertical page breaks act between columns. + * + * The function takes an array of one or more page breaks. The type of the + * array data is @ref lxw_col_t and the last element of the array must be 0: + * + * @code + * lxw_col_t breaks1[] = {20, 0}; // 1 page break. Zero indicates the end. + * lxw_col_t breaks2[] = {20, 40, 60, 80, 0}; + * + * worksheet_set_v_pagebreaks(worksheet1, breaks1); + * worksheet_set_v_pagebreaks(worksheet2, breaks2); + * @endcode + * + * To create a page break between columns 20 and 21 you must specify the break + * at column 21. However in zero index notation this is actually column 20: + * + * @code + * // Break between column 20 and 21. + * lxw_col_t breaks[] = {20, 0}; + * + * worksheet_set_v_pagebreaks(worksheet, breaks); + * @endcode + * + * There is an Excel limitation of 1023 vertical page breaks per worksheet. + * + * Note: If you specify the "fit to page" option via the + * `worksheet_fit_to_pages()` function it will override all manual page + * breaks. + * + */ +lxw_error worksheet_set_v_pagebreaks(lxw_worksheet *worksheet, + lxw_col_t breaks[]); + +/** + * @brief Set the order in which pages are printed. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * The `%worksheet_print_across()` function is used to change the default + * print direction. This is referred to by Excel as the sheet "page order": + * + * @code + * worksheet_print_across(worksheet); + * @endcode + * + * The default page order is shown below for a worksheet that extends over 4 + * pages. The order is called "down then across": + * + * [1] [3] + * [2] [4] + * + * However, by using the `print_across` function the print order will be + * changed to "across then down": + * + * [1] [2] + * [3] [4] + * + */ +void worksheet_print_across(lxw_worksheet *worksheet); + +/** + * @brief Set the worksheet zoom factor. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param scale Worksheet zoom factor. + * + * Set the worksheet zoom factor in the range `10 <= zoom <= 400`: + * + * @code + * worksheet_set_zoom(worksheet1, 50); + * worksheet_set_zoom(worksheet2, 75); + * worksheet_set_zoom(worksheet3, 300); + * worksheet_set_zoom(worksheet4, 400); + * @endcode + * + * The default zoom factor is 100. It isn't possible to set the zoom to + * "Selection" because it is calculated by Excel at run-time. + * + * Note, `%worksheet_zoom()` does not affect the scale of the printed + * page. For that you should use `worksheet_set_print_scale()`. + */ +void worksheet_set_zoom(lxw_worksheet *worksheet, uint16_t scale); + +/** + * @brief Set the option to display or hide gridlines on the screen and + * the printed page. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param option Gridline option. + * + * Display or hide screen and print gridlines using one of the values of + * @ref lxw_gridlines. + * + * @code + * worksheet_gridlines(worksheet1, LXW_HIDE_ALL_GRIDLINES); + * + * worksheet_gridlines(worksheet2, LXW_SHOW_PRINT_GRIDLINES); + * @endcode + * + * The Excel default is that the screen gridlines are on and the printed + * worksheet is off. + * + */ +void worksheet_gridlines(lxw_worksheet *worksheet, uint8_t option); + +/** + * @brief Center the printed page horizontally. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * Center the worksheet data horizontally between the margins on the printed + * page: + * + * @code + * worksheet_center_horizontally(worksheet); + * @endcode + * + */ +void worksheet_center_horizontally(lxw_worksheet *worksheet); + +/** + * @brief Center the printed page vertically. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * Center the worksheet data vertically between the margins on the printed + * page: + * + * @code + * worksheet_center_vertically(worksheet); + * @endcode + * + */ +void worksheet_center_vertically(lxw_worksheet *worksheet); + +/** + * @brief Set the option to print the row and column headers on the printed + * page. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * When printing a worksheet from Excel the row and column headers (the row + * numbers on the left and the column letters at the top) aren't printed by + * default. + * + * This function sets the printer option to print these headers: + * + * @code + * worksheet_print_row_col_headers(worksheet); + * @endcode + * + */ +void worksheet_print_row_col_headers(lxw_worksheet *worksheet); + +/** + * @brief Set the number of rows to repeat at the top of each printed page. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param first_row First row of repeat range. + * @param last_row Last row of repeat range. + * + * @return A #lxw_error code. + * + * For large Excel documents it is often desirable to have the first row or + * rows of the worksheet print out at the top of each page. + * + * This can be achieved by using this function. The parameters `first_row` + * and `last_row` are zero based: + * + * @code + * worksheet_repeat_rows(worksheet, 0, 0); // Repeat the first row. + * worksheet_repeat_rows(worksheet, 0, 1); // Repeat the first two rows. + * @endcode + */ +lxw_error worksheet_repeat_rows(lxw_worksheet *worksheet, lxw_row_t first_row, + lxw_row_t last_row); + +/** + * @brief Set the number of columns to repeat at the top of each printed page. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param first_col First column of repeat range. + * @param last_col Last column of repeat range. + * + * @return A #lxw_error code. + * + * For large Excel documents it is often desirable to have the first column or + * columns of the worksheet print out at the left of each page. + * + * This can be achieved by using this function. The parameters `first_col` + * and `last_col` are zero based: + * + * @code + * worksheet_repeat_columns(worksheet, 0, 0); // Repeat the first col. + * worksheet_repeat_columns(worksheet, 0, 1); // Repeat the first two cols. + * @endcode + */ +lxw_error worksheet_repeat_columns(lxw_worksheet *worksheet, + lxw_col_t first_col, lxw_col_t last_col); + +/** + * @brief Set the print area for a worksheet. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param first_row The first row of the range. (All zero indexed.) + * @param first_col The first column of the range. + * @param last_row The last row of the range. + * @param last_col The last col of the range. + * + * @return A #lxw_error code. + * + * This function is used to specify the area of the worksheet that will be + * printed. The RANGE() macro is often convenient for this. + * + * @code + * worksheet_print_area(worksheet, 0, 0, 41, 10); // A1:K42. + * + * // Same as: + * worksheet_print_area(worksheet, RANGE("A1:K42")); + * @endcode + * + * In order to set a row or column range you must specify the entire range: + * + * @code + * worksheet_print_area(worksheet, RANGE("A1:H1048576")); // Same as A:H. + * @endcode + */ +lxw_error worksheet_print_area(lxw_worksheet *worksheet, lxw_row_t first_row, + lxw_col_t first_col, lxw_row_t last_row, + lxw_col_t last_col); +/** + * @brief Fit the printed area to a specific number of pages both vertically + * and horizontally. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param width Number of pages horizontally. + * @param height Number of pages vertically. + * + * The `%worksheet_fit_to_pages()` function is used to fit the printed area to + * a specific number of pages both vertically and horizontally. If the printed + * area exceeds the specified number of pages it will be scaled down to + * fit. This ensures that the printed area will always appear on the specified + * number of pages even if the page size or margins change: + * + * @code + * worksheet_fit_to_pages(worksheet1, 1, 1); // Fit to 1x1 pages. + * worksheet_fit_to_pages(worksheet2, 2, 1); // Fit to 2x1 pages. + * worksheet_fit_to_pages(worksheet3, 1, 2); // Fit to 1x2 pages. + * @endcode + * + * The print area can be defined using the `worksheet_print_area()` function + * as described above. + * + * A common requirement is to fit the printed output to `n` pages wide but + * have the height be as long as necessary. To achieve this set the `height` + * to zero: + * + * @code + * // 1 page wide and as long as necessary. + * worksheet_fit_to_pages(worksheet, 1, 0); + * @endcode + * + * **Note**: + * + * - Although it is valid to use both `%worksheet_fit_to_pages()` and + * `worksheet_set_print_scale()` on the same worksheet Excel only allows one + * of these options to be active at a time. The last function call made will + * set the active option. + * + * - The `%worksheet_fit_to_pages()` function will override any manual page + * breaks that are defined in the worksheet. + * + * - When using `%worksheet_fit_to_pages()` it may also be required to set the + * printer paper size using `worksheet_set_paper()` or else Excel will + * default to "US Letter". + * + */ +void worksheet_fit_to_pages(lxw_worksheet *worksheet, uint16_t width, + uint16_t height); + +/** + * @brief Set the start page number when printing. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param start_page Starting page number. + * + * The `%worksheet_set_start_page()` function is used to set the number of + * the starting page when the worksheet is printed out: + * + * @code + * // Start print from page 2. + * worksheet_set_start_page(worksheet, 2); + * @endcode + */ +void worksheet_set_start_page(lxw_worksheet *worksheet, uint16_t start_page); + +/** + * @brief Set the scale factor for the printed page. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param scale Print scale of worksheet to be printed. + * + * This function sets the scale factor of the printed page. The Scale factor + * must be in the range `10 <= scale <= 400`: + * + * @code + * worksheet_set_print_scale(worksheet1, 75); + * worksheet_set_print_scale(worksheet2, 400); + * @endcode + * + * The default scale factor is 100. Note, `%worksheet_set_print_scale()` does + * not affect the scale of the visible page in Excel. For that you should use + * `worksheet_set_zoom()`. + * + * Note that although it is valid to use both `worksheet_fit_to_pages()` and + * `%worksheet_set_print_scale()` on the same worksheet Excel only allows one + * of these options to be active at a time. The last function call made will + * set the active option. + * + */ +void worksheet_set_print_scale(lxw_worksheet *worksheet, uint16_t scale); + +/** + * @brief Display the worksheet cells from right to left for some versions of + * Excel. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * The `%worksheet_right_to_left()` function is used to change the default + * direction of the worksheet from left-to-right, with the `A1` cell in the + * top left, to right-to-left, with the `A1` cell in the top right. + * + * @code + * worksheet_right_to_left(worksheet1); + * @endcode + * + * This is useful when creating Arabic, Hebrew or other near or far eastern + * worksheets that use right-to-left as the default direction. + */ +void worksheet_right_to_left(lxw_worksheet *worksheet); + +/** + * @brief Hide zero values in worksheet cells. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * + * The `%worksheet_hide_zero()` function is used to hide any zero values that + * appear in cells: + * + * @code + * worksheet_hide_zero(worksheet1); + * @endcode + */ +void worksheet_hide_zero(lxw_worksheet *worksheet); + +/** + * @brief Set the color of the worksheet tab. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param color The tab color. + * + * The `%worksheet_set_tab_color()` function is used to change the color of the worksheet + * tab: + * + * @code + * worksheet_set_tab_color(worksheet1, LXW_COLOR_RED); + * worksheet_set_tab_color(worksheet2, LXW_COLOR_GREEN); + * worksheet_set_tab_color(worksheet3, 0xFF9900); // Orange. + * @endcode + * + * The color should be an RGB integer value, see @ref working_with_colors. + */ +void worksheet_set_tab_color(lxw_worksheet *worksheet, lxw_color_t color); + +/** + * @brief Protect elements of a worksheet from modification. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param password A worksheet password. + * @param options Worksheet elements to protect. + * + * The `%worksheet_protect()` function protects worksheet elements from modification: + * + * @code + * worksheet_protect(worksheet, "Some Password", options); + * @endcode + * + * The `password` and lxw_protection pointer are both optional: + * + * @code + * worksheet_protect(worksheet1, NULL, NULL); + * worksheet_protect(worksheet2, NULL, my_options); + * worksheet_protect(worksheet3, "password", NULL); + * worksheet_protect(worksheet4, "password", my_options); + * @endcode + * + * Passing a `NULL` password is the same as turning on protection without a + * password. Passing a `NULL` password and `NULL` options, or any other + * combination has the effect of enabling a cell's `locked` and `hidden` + * properties if they have been set. + * + * A *locked* cell cannot be edited and this property is on by default for all + * cells. A *hidden* cell will display the results of a formula but not the + * formula itself. These properties can be set using the format_set_unlocked() + * and format_set_hidden() format functions. + * + * You can specify which worksheet elements you wish to protect by passing a + * lxw_protection pointer in the `options` argument with any or all of the + * following members set: + * + * no_select_locked_cells + * no_select_unlocked_cells + * format_cells + * format_columns + * format_rows + * insert_columns + * insert_rows + * insert_hyperlinks + * delete_columns + * delete_rows + * sort + * autofilter + * pivot_tables + * scenarios + * objects + * + * All parameters are off by default. Individual elements can be protected as + * follows: + * + * @code + * lxw_protection options = { + * .format_cells = 1, + * .insert_hyperlinks = 1, + * .insert_rows = 1, + * .delete_rows = 1, + * .insert_columns = 1, + * .delete_columns = 1, + * }; + * + * worksheet_protect(worksheet, NULL, &options); + * + * @endcode + * + * See also the format_set_unlocked() and format_set_hidden() format functions. + * + * **Note:** Worksheet level passwords in Excel offer **very** weak + * protection. They don't encrypt your data and are very easy to + * deactivate. Full workbook encryption is not supported by `libxlsxwriter` + * since it requires a completely different file format and would take several + * man months to implement. + */ +void worksheet_protect(lxw_worksheet *worksheet, const char *password, + lxw_protection *options); + +/** + * @brief Set the default row properties. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param height Default row height. + * @param hide_unused_rows Hide unused cells. + * + * The `%worksheet_set_default_row()` function is used to set Excel default + * row properties such as the default height and the option to hide unused + * rows. These parameters are an optimization used by Excel to set row + * properties without generating a very large file with an entry for each row. + * + * To set the default row height: + * + * @code + * worksheet_set_default_row(worksheet, 24, LXW_FALSE); + * + * @endcode + * + * To hide unused rows: + * + * @code + * worksheet_set_default_row(worksheet, 15, LXW_TRUE); + * @endcode + * + * Note, in the previous case we use the default height #LXW_DEF_ROW_HEIGHT = + * 15 so the the height remains unchanged. + */ +void worksheet_set_default_row(lxw_worksheet *worksheet, double height, + uint8_t hide_unused_rows); + +lxw_worksheet *lxw_worksheet_new(lxw_worksheet_init_data *init_data); +void lxw_worksheet_free(lxw_worksheet *worksheet); +void lxw_worksheet_assemble_xml_file(lxw_worksheet *worksheet); +void lxw_worksheet_write_single_row(lxw_worksheet *worksheet); + +void lxw_worksheet_prepare_image(lxw_worksheet *worksheet, + uint16_t image_ref_id, uint16_t drawing_id, + lxw_image_options *image_data); + +void lxw_worksheet_prepare_chart(lxw_worksheet *worksheet, + uint16_t chart_ref_id, uint16_t drawing_id, + lxw_image_options *image_data); + +lxw_row *lxw_worksheet_find_row(lxw_worksheet *worksheet, lxw_row_t row_num); +lxw_cell *lxw_worksheet_find_cell(lxw_row *row, lxw_col_t col_num); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _worksheet_xml_declaration(lxw_worksheet *worksheet); +STATIC void _worksheet_write_worksheet(lxw_worksheet *worksheet); +STATIC void _worksheet_write_dimension(lxw_worksheet *worksheet); +STATIC void _worksheet_write_sheet_view(lxw_worksheet *worksheet); +STATIC void _worksheet_write_sheet_views(lxw_worksheet *worksheet); +STATIC void _worksheet_write_sheet_format_pr(lxw_worksheet *worksheet); +STATIC void _worksheet_write_sheet_data(lxw_worksheet *worksheet); +STATIC void _worksheet_write_page_margins(lxw_worksheet *worksheet); +STATIC void _worksheet_write_page_setup(lxw_worksheet *worksheet); +STATIC void _worksheet_write_col_info(lxw_worksheet *worksheet, + lxw_col_options *options); +STATIC void _write_row(lxw_worksheet *worksheet, lxw_row *row, char *spans); +STATIC lxw_row *_get_row_list(struct lxw_table_rows *table, + lxw_row_t row_num); + +STATIC void _worksheet_write_merge_cell(lxw_worksheet *worksheet, + lxw_merged_range *merged_range); +STATIC void _worksheet_write_merge_cells(lxw_worksheet *worksheet); + +STATIC void _worksheet_write_odd_header(lxw_worksheet *worksheet); +STATIC void _worksheet_write_odd_footer(lxw_worksheet *worksheet); +STATIC void _worksheet_write_header_footer(lxw_worksheet *worksheet); + +STATIC void _worksheet_write_print_options(lxw_worksheet *worksheet); +STATIC void _worksheet_write_sheet_pr(lxw_worksheet *worksheet); +STATIC void _worksheet_write_tab_color(lxw_worksheet *worksheet); +STATIC void _worksheet_write_sheet_protection(lxw_worksheet *worksheet); +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_WORKSHEET_H__ */ diff --git a/src/include/xlsxwriter/xmlwriter.h b/src/include/xlsxwriter/xmlwriter.h new file mode 100644 index 0000000..b6dad62 --- /dev/null +++ b/src/include/xlsxwriter/xmlwriter.h @@ -0,0 +1,178 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * xmlwriter - A libxlsxwriter library for creating Excel XLSX + * XML files. + * + * The xmlwriter library is used to create the XML sub-components files + * in the Excel XLSX file format. + * + * This library is used in preference to a more generic XML library to allow + * for customization and optimization for the XLSX file format. + * + * The xmlwriter functions are only used internally and do not need to be + * called directly by the end user. + * + */ +#ifndef __XMLWRITER_H__ +#define __XMLWRITER_H__ + +#include +#include +#include +#include "common.h" + +#define LXW_MAX_ATTRIBUTE_LENGTH 256 +#define LXW_ATTR_32 32 + +#define LXW_ATTRIBUTE_COPY(dst, src) \ + do{ \ + strncpy(dst, src, LXW_MAX_ATTRIBUTE_LENGTH -1); \ + dst[LXW_MAX_ATTRIBUTE_LENGTH - 1] = '\0'; \ + } while (0) + + + /* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +/* Attribute used in XML elements. */ +struct xml_attribute { + char key[LXW_MAX_ATTRIBUTE_LENGTH]; + char value[LXW_MAX_ATTRIBUTE_LENGTH]; + + /* Make the struct a queue.h list element. */ + STAILQ_ENTRY (xml_attribute) list_entries; +}; + +/* Use queue.h macros to define the xml_attribute_list type. */ +STAILQ_HEAD(xml_attribute_list, xml_attribute); + +/* Create a new attribute struct to add to a xml_attribute_list. */ +struct xml_attribute *lxw_new_attribute_str(const char *key, + const char *value); +struct xml_attribute *lxw_new_attribute_int(const char *key, uint32_t value); +struct xml_attribute *lxw_new_attribute_dbl(const char *key, double value); + +/* Macro to initialize the xml_attribute_list pointers. */ +#define LXW_INIT_ATTRIBUTES() \ + STAILQ_INIT(&attributes) + +/* Macro to add attribute string elements to xml_attribute_list. */ +#define LXW_PUSH_ATTRIBUTES_STR(key, value) \ + do { \ + attribute = lxw_new_attribute_str((key), (value)); \ + STAILQ_INSERT_TAIL(&attributes, attribute, list_entries); \ + } while (0) + +/* Macro to add attribute int values to xml_attribute_list. */ +#define LXW_PUSH_ATTRIBUTES_INT(key, value) \ + do { \ + attribute = lxw_new_attribute_int((key), (value)); \ + STAILQ_INSERT_TAIL(&attributes, attribute, list_entries); \ + } while (0) + +/* Macro to add attribute double values to xml_attribute_list. */ +#define LXW_PUSH_ATTRIBUTES_DBL(key, value) \ + do { \ + attribute = lxw_new_attribute_dbl((key), (value)); \ + STAILQ_INSERT_TAIL(&attributes, attribute, list_entries); \ + } while (0) + +/* Macro to free xml_attribute_list and attribute. */ +#define LXW_FREE_ATTRIBUTES() \ + while (!STAILQ_EMPTY(&attributes)) { \ + attribute = STAILQ_FIRST(&attributes); \ + STAILQ_REMOVE_HEAD(&attributes, list_entries); \ + free(attribute); \ + } + +/** + * Create the XML declaration in an XML file. + * + * @param xmlfile A FILE pointer to the output XML file. + */ +void lxw_xml_declaration(FILE * xmlfile); + +/** + * Write an XML start tag with optional attributes. + * + * @param xmlfile A FILE pointer to the output XML file. + * @param tag The XML tag to write. + * @param attributes An optional list of attributes to add to the tag. + */ +void lxw_xml_start_tag(FILE * xmlfile, + const char *tag, + struct xml_attribute_list *attributes); + +/** + * Write an XML start tag with optional un-encoded attributes. + * This is a minor optimization for attributes that don't need encoding. + * + * @param xmlfile A FILE pointer to the output XML file. + * @param tag The XML tag to write. + * @param attributes An optional list of attributes to add to the tag. + */ +void lxw_xml_start_tag_unencoded(FILE * xmlfile, + const char *tag, + struct xml_attribute_list *attributes); + +/** + * Write an XML end tag. + * + * @param xmlfile A FILE pointer to the output XML file. + * @param tag The XML tag to write. + */ +void lxw_xml_end_tag(FILE * xmlfile, const char *tag); + +/** + * Write an XML empty tag with optional attributes. + * + * @param xmlfile A FILE pointer to the output XML file. + * @param tag The XML tag to write. + * @param attributes An optional list of attributes to add to the tag. + */ +void lxw_xml_empty_tag(FILE * xmlfile, + const char *tag, + struct xml_attribute_list *attributes); + +/** + * Write an XML empty tag with optional un-encoded attributes. + * This is a minor optimization for attributes that don't need encoding. + * + * @param xmlfile A FILE pointer to the output XML file. + * @param tag The XML tag to write. + * @param attributes An optional list of attributes to add to the tag. + */ +void lxw_xml_empty_tag_unencoded(FILE * xmlfile, + const char *tag, + struct xml_attribute_list *attributes); + +/** + * Write an XML element containing data and optional attributes. + * + * @param xmlfile A FILE pointer to the output XML file. + * @param tag The XML tag to write. + * @param data The data section of the XML element. + * @param attributes An optional list of attributes to add to the tag. + */ +void lxw_xml_data_element(FILE * xmlfile, + const char *tag, + const char *data, + struct xml_attribute_list *attributes); + +char *lxw_escape_control_characters(const char *string); + +char *lxw_escape_data(const char *data); + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __XMLWRITER_H__ */ diff --git a/src/libxlsxwriter/app.c b/src/libxlsxwriter/app.c new file mode 100644 index 0000000..2270a51 --- /dev/null +++ b/src/libxlsxwriter/app.c @@ -0,0 +1,443 @@ +/***************************************************************************** + * app - A library for creating Excel XLSX app files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/app.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new app object. + */ +lxw_app * +lxw_app_new() +{ + lxw_app *app = calloc(1, sizeof(lxw_app)); + GOTO_LABEL_ON_MEM_ERROR(app, mem_error); + + app->heading_pairs = calloc(1, sizeof(struct lxw_heading_pairs)); + GOTO_LABEL_ON_MEM_ERROR(app->heading_pairs, mem_error); + STAILQ_INIT(app->heading_pairs); + + app->part_names = calloc(1, sizeof(struct lxw_part_names)); + GOTO_LABEL_ON_MEM_ERROR(app->part_names, mem_error); + STAILQ_INIT(app->part_names); + + return app; + +mem_error: + lxw_app_free(app); + return NULL; +} + +/* + * Free a app object. + */ +void +lxw_app_free(lxw_app *app) +{ + lxw_heading_pair *heading_pair; + lxw_part_name *part_name; + + if (!app) + return; + + /* Free the lists in the App object. */ + if (app->heading_pairs) { + while (!STAILQ_EMPTY(app->heading_pairs)) { + heading_pair = STAILQ_FIRST(app->heading_pairs); + STAILQ_REMOVE_HEAD(app->heading_pairs, list_pointers); + free(heading_pair->key); + free(heading_pair->value); + free(heading_pair); + } + free(app->heading_pairs); + } + + if (app->part_names) { + while (!STAILQ_EMPTY(app->part_names)) { + part_name = STAILQ_FIRST(app->part_names); + STAILQ_REMOVE_HEAD(app->part_names, list_pointers); + free(part_name->name); + free(part_name); + } + free(app->part_names); + } + + free(app); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_app_xml_declaration(lxw_app *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_write_properties(lxw_app *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns[] = LXW_SCHEMA_OFFICEDOC "/extended-properties"; + char xmlns_vt[] = LXW_SCHEMA_OFFICEDOC "/docPropsVTypes"; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); + LXW_PUSH_ATTRIBUTES_STR("xmlns:vt", xmlns_vt); + + lxw_xml_start_tag(self->file, "Properties", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_application(lxw_app *self) +{ + lxw_xml_data_element(self->file, "Application", "Microsoft Excel", NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_doc_security(lxw_app *self) +{ + lxw_xml_data_element(self->file, "DocSecurity", "0", NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_scale_crop(lxw_app *self) +{ + lxw_xml_data_element(self->file, "ScaleCrop", "false", NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_vt_lpstr(lxw_app *self, const char *str) +{ + lxw_xml_data_element(self->file, "vt:lpstr", str, NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_vt_i4(lxw_app *self, const char *value) +{ + lxw_xml_data_element(self->file, "vt:i4", value, NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_vt_variant(lxw_app *self, const char *key, const char *value) +{ + /* Write the vt:lpstr element. */ + lxw_xml_start_tag(self->file, "vt:variant", NULL); + _write_vt_lpstr(self, key); + lxw_xml_end_tag(self->file, "vt:variant"); + + /* Write the vt:i4 element. */ + lxw_xml_start_tag(self->file, "vt:variant", NULL); + _write_vt_i4(self, value); + lxw_xml_end_tag(self->file, "vt:variant"); +} + +/* + * Write the element for the heading pairs. + */ +STATIC void +_write_vt_vector_heading_pairs(lxw_app *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + lxw_heading_pair *heading_pair; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("size", self->num_heading_pairs * 2); + LXW_PUSH_ATTRIBUTES_STR("baseType", "variant"); + + lxw_xml_start_tag(self->file, "vt:vector", &attributes); + + STAILQ_FOREACH(heading_pair, self->heading_pairs, list_pointers) { + _write_vt_variant(self, heading_pair->key, heading_pair->value); + } + + lxw_xml_end_tag(self->file, "vt:vector"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element for the named parts. + */ +STATIC void +_write_vt_vector_lpstr_named_parts(lxw_app *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + lxw_part_name *part_name; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("size", self->num_part_names); + LXW_PUSH_ATTRIBUTES_STR("baseType", "lpstr"); + + lxw_xml_start_tag(self->file, "vt:vector", &attributes); + + STAILQ_FOREACH(part_name, self->part_names, list_pointers) { + _write_vt_lpstr(self, part_name->name); + } + + lxw_xml_end_tag(self->file, "vt:vector"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_heading_pairs(lxw_app *self) +{ + lxw_xml_start_tag(self->file, "HeadingPairs", NULL); + + /* Write the vt:vector element. */ + _write_vt_vector_heading_pairs(self); + + lxw_xml_end_tag(self->file, "HeadingPairs"); +} + +/* + * Write the element. + */ +STATIC void +_write_titles_of_parts(lxw_app *self) +{ + lxw_xml_start_tag(self->file, "TitlesOfParts", NULL); + + /* Write the vt:vector element. */ + _write_vt_vector_lpstr_named_parts(self); + + lxw_xml_end_tag(self->file, "TitlesOfParts"); +} + +/* + * Write the element. + */ +STATIC void +_write_manager(lxw_app *self) +{ + lxw_doc_properties *properties = self->properties; + + if (!properties) + return; + + if (properties->manager) + lxw_xml_data_element(self->file, "Manager", properties->manager, + NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_company(lxw_app *self) +{ + lxw_doc_properties *properties = self->properties; + + if (properties && properties->company) + lxw_xml_data_element(self->file, "Company", properties->company, + NULL); + else + lxw_xml_data_element(self->file, "Company", "", NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_links_up_to_date(lxw_app *self) +{ + lxw_xml_data_element(self->file, "LinksUpToDate", "false", NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_shared_doc(lxw_app *self) +{ + lxw_xml_data_element(self->file, "SharedDoc", "false", NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_hyperlink_base(lxw_app *self) +{ + lxw_doc_properties *properties = self->properties; + + if (!properties) + return; + + if (properties->hyperlink_base) + lxw_xml_data_element(self->file, "HyperlinkBase", + properties->hyperlink_base, NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_hyperlinks_changed(lxw_app *self) +{ + lxw_xml_data_element(self->file, "HyperlinksChanged", "false", NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_app_version(lxw_app *self) +{ + lxw_xml_data_element(self->file, "AppVersion", "12.0000", NULL); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Assemble and write the XML file. + */ +void +lxw_app_assemble_xml_file(lxw_app *self) +{ + + /* Write the XML declaration. */ + _app_xml_declaration(self); + + _write_properties(self); + _write_application(self); + _write_doc_security(self); + _write_scale_crop(self); + _write_heading_pairs(self); + _write_titles_of_parts(self); + _write_manager(self); + _write_company(self); + _write_links_up_to_date(self); + _write_shared_doc(self); + _write_hyperlink_base(self); + _write_hyperlinks_changed(self); + _write_app_version(self); + + lxw_xml_end_tag(self->file, "Properties"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ + +/* + * Add the name of a workbook Part such as 'Sheet1' or 'Print_Titles'. + */ +void +lxw_app_add_part_name(lxw_app *self, const char *name) +{ + lxw_part_name *part_name; + + if (!name) + return; + + part_name = calloc(1, sizeof(lxw_part_name)); + GOTO_LABEL_ON_MEM_ERROR(part_name, mem_error); + + part_name->name = lxw_strdup(name); + GOTO_LABEL_ON_MEM_ERROR(part_name->name, mem_error); + + STAILQ_INSERT_TAIL(self->part_names, part_name, list_pointers); + self->num_part_names++; + + return; + +mem_error: + if (part_name) { + free(part_name->name); + free(part_name); + } +} + +/* + * Add the name of a workbook Heading Pair such as 'Worksheets', 'Charts' or + * 'Named Ranges'. + */ +void +lxw_app_add_heading_pair(lxw_app *self, const char *key, const char *value) +{ + lxw_heading_pair *heading_pair; + + if (!key || !value) + return; + + heading_pair = calloc(1, sizeof(lxw_heading_pair)); + GOTO_LABEL_ON_MEM_ERROR(heading_pair, mem_error); + + heading_pair->key = lxw_strdup(key); + GOTO_LABEL_ON_MEM_ERROR(heading_pair->key, mem_error); + + heading_pair->value = lxw_strdup(value); + GOTO_LABEL_ON_MEM_ERROR(heading_pair->value, mem_error); + + STAILQ_INSERT_TAIL(self->heading_pairs, heading_pair, list_pointers); + self->num_heading_pairs++; + + return; + +mem_error: + if (heading_pair) { + free(heading_pair->key); + free(heading_pair->value); + free(heading_pair); + } +} diff --git a/src/libxlsxwriter/chart.c b/src/libxlsxwriter/chart.c new file mode 100644 index 0000000..d1bbe2d --- /dev/null +++ b/src/libxlsxwriter/chart.c @@ -0,0 +1,6347 @@ +/***************************************************************************** + * chart - A library for creating Excel XLSX chart files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/chart.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +STATIC void _chart_initialize(lxw_chart *self, uint8_t type); +STATIC void _chart_axis_set_default_num_format(lxw_chart_axis *axis, + char *num_format); + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Free a series range object. + */ +STATIC void +_chart_free_range(lxw_series_range *range) +{ + struct lxw_series_data_point *data_point; + + if (!range) + return; + + if (range->data_cache) { + while (!STAILQ_EMPTY(range->data_cache)) { + data_point = STAILQ_FIRST(range->data_cache); + free(data_point->string); + STAILQ_REMOVE_HEAD(range->data_cache, list_pointers); + + free(data_point); + } + free(range->data_cache); + } + + free(range->formula); + free(range->sheetname); + free(range); +} + +STATIC void +_chart_free_points(lxw_chart_series *series) +{ + uint16_t index; + + for (index = 0; index < series->point_count; index++) { + lxw_chart_point *point = &series->points[index]; + + free(point->line); + free(point->fill); + free(point->pattern); + } + + series->point_count = 0; + free(series->points); +} + +/* + * Free a chart font object. + */ +STATIC void +_chart_free_font(lxw_chart_font *font) +{ + if (!font) + return; + + free(font->name); + free(font); +} + +/* + * Free a series object. + */ +STATIC void +_chart_series_free(lxw_chart_series *series) +{ + if (!series) + return; + + free(series->title.name); + free(series->line); + free(series->fill); + free(series->pattern); + free(series->label_num_format); + _chart_free_font(series->label_font); + + if (series->marker) { + free(series->marker->line); + free(series->marker->fill); + free(series->marker->pattern); + free(series->marker); + } + + _chart_free_range(series->categories); + _chart_free_range(series->values); + _chart_free_range(series->title.range); + _chart_free_points(series); + + if (series->x_error_bars) { + free(series->x_error_bars->line); + free(series->x_error_bars); + } + + if (series->y_error_bars) { + free(series->y_error_bars->line); + free(series->y_error_bars); + } + + free(series->trendline_line); + free(series->trendline_name); + + free(series); +} + +/* + * Initialize the data cache in a range object. + */ +STATIC lxw_error +_chart_init_data_cache(lxw_series_range *range) +{ + /* Initialize the series range data cache. */ + range->data_cache = calloc(1, sizeof(struct lxw_series_data_points)); + RETURN_ON_MEM_ERROR(range->data_cache, LXW_ERROR_MEMORY_MALLOC_FAILED); + STAILQ_INIT(range->data_cache); + + return LXW_NO_ERROR; +} + +/* + * Free a chart object. + */ +void +lxw_chart_free(lxw_chart *chart) +{ + lxw_chart_series *series; + + if (!chart) + return; + + /* Chart series. */ + if (chart->series_list) { + while (!STAILQ_EMPTY(chart->series_list)) { + series = STAILQ_FIRST(chart->series_list); + STAILQ_REMOVE_HEAD(chart->series_list, list_pointers); + + _chart_series_free(series); + } + + free(chart->series_list); + } + + /* X Axis. */ + if (chart->x_axis) { + _chart_free_font(chart->x_axis->title.font); + _chart_free_font(chart->x_axis->num_font); + _chart_free_range(chart->x_axis->title.range); + free(chart->x_axis->title.name); + free(chart->x_axis->line); + free(chart->x_axis->fill); + free(chart->x_axis->pattern); + free(chart->x_axis->major_gridlines.line); + free(chart->x_axis->minor_gridlines.line); + free(chart->x_axis->num_format); + free(chart->x_axis->default_num_format); + free(chart->x_axis); + } + + /* Y Axis. */ + if (chart->y_axis) { + _chart_free_font(chart->y_axis->title.font); + _chart_free_font(chart->y_axis->num_font); + _chart_free_range(chart->y_axis->title.range); + free(chart->y_axis->title.name); + free(chart->y_axis->line); + free(chart->y_axis->fill); + free(chart->y_axis->pattern); + free(chart->y_axis->major_gridlines.line); + free(chart->y_axis->minor_gridlines.line); + free(chart->y_axis->num_format); + free(chart->y_axis->default_num_format); + free(chart->y_axis); + } + + /* Chart title. */ + _chart_free_font(chart->title.font); + _chart_free_range(chart->title.range); + free(chart->title.name); + + /* Chart legend. */ + _chart_free_font(chart->legend.font); + free(chart->delete_series); + + free(chart->default_marker); + + free(chart->chartarea_line); + free(chart->chartarea_fill); + free(chart->chartarea_pattern); + free(chart->plotarea_line); + free(chart->plotarea_fill); + free(chart->plotarea_pattern); + + free(chart->drop_lines_line); + free(chart->high_low_lines_line); + + free(chart->up_bar_line); + free(chart->up_bar_fill); + free(chart->down_bar_line); + free(chart->down_bar_fill); + + _chart_free_font(chart->table_font); + + free(chart); +} + +/* + * Create a new chart object. + */ +lxw_chart * +lxw_chart_new(uint8_t type) +{ + lxw_chart *chart = calloc(1, sizeof(lxw_chart)); + GOTO_LABEL_ON_MEM_ERROR(chart, mem_error); + + chart->series_list = calloc(1, sizeof(struct lxw_chart_series_list)); + GOTO_LABEL_ON_MEM_ERROR(chart->series_list, mem_error); + STAILQ_INIT(chart->series_list); + + chart->x_axis = calloc(1, sizeof(struct lxw_chart_axis)); + GOTO_LABEL_ON_MEM_ERROR(chart->x_axis, mem_error); + + chart->y_axis = calloc(1, sizeof(struct lxw_chart_axis)); + GOTO_LABEL_ON_MEM_ERROR(chart->y_axis, mem_error); + + chart->title.range = calloc(1, sizeof(lxw_series_range)); + GOTO_LABEL_ON_MEM_ERROR(chart->title.range, mem_error); + + chart->x_axis->title.range = calloc(1, sizeof(lxw_series_range)); + GOTO_LABEL_ON_MEM_ERROR(chart->x_axis->title.range, mem_error); + + chart->y_axis->title.range = calloc(1, sizeof(lxw_series_range)); + GOTO_LABEL_ON_MEM_ERROR(chart->y_axis->title.range, mem_error); + + /* Initialize the ranges in the chart titles. */ + if (_chart_init_data_cache(chart->title.range) != LXW_NO_ERROR) + goto mem_error; + + if (_chart_init_data_cache(chart->x_axis->title.range) != LXW_NO_ERROR) + goto mem_error; + + if (_chart_init_data_cache(chart->y_axis->title.range) != LXW_NO_ERROR) + goto mem_error; + + chart->type = type; + chart->style_id = 2; + chart->hole_size = 50; + + /* Set the default axis positions. */ + chart->x_axis->axis_position = LXW_CHART_AXIS_BOTTOM; + chart->y_axis->axis_position = LXW_CHART_AXIS_LEFT; + + /* Set the default axis number formats. */ + _chart_axis_set_default_num_format(chart->x_axis, "General"); + _chart_axis_set_default_num_format(chart->y_axis, "General"); + + chart->x_axis->major_gridlines.visible = LXW_FALSE; + chart->y_axis->major_gridlines.visible = LXW_TRUE; + + chart->has_horiz_cat_axis = LXW_FALSE; + chart->has_horiz_val_axis = LXW_TRUE; + + chart->legend.position = LXW_CHART_LEGEND_RIGHT; + + chart->gap_y1 = LXW_CHART_DEFAULT_GAP; + chart->gap_y2 = LXW_CHART_DEFAULT_GAP; + + /* Initialize the chart specific properties. */ + _chart_initialize(chart, chart->type); + + return chart; + +mem_error: + lxw_chart_free(chart); + return NULL; +} + +/* + * Create a copy of a user supplied font. + */ +STATIC lxw_chart_font * +_chart_convert_font_args(lxw_chart_font *user_font) +{ + lxw_chart_font *font; + + if (!user_font) + return NULL; + + font = calloc(1, sizeof(struct lxw_chart_font)); + RETURN_ON_MEM_ERROR(font, NULL); + + memcpy(font, user_font, sizeof(lxw_chart_font)); + + font->name = lxw_strdup(user_font->name); + + /* Convert font size units. */ + if (font->size > 0.0) + font->size = font->size * 100.0; + + /* Convert rotation into 60,000ths of a degree. */ + if (font->rotation) + font->rotation = font->rotation * 60000; + + if (font->color) { + font->color = lxw_format_check_color(font->color); + font->has_color = LXW_TRUE; + } + + return font; +} + +/* + * Create a copy of a user supplied line. + */ +STATIC lxw_chart_line * +_chart_convert_line_args(lxw_chart_line *user_line) +{ + lxw_chart_line *line; + + if (!user_line) + return NULL; + + line = calloc(1, sizeof(struct lxw_chart_line)); + RETURN_ON_MEM_ERROR(line, NULL); + + memcpy(line, user_line, sizeof(lxw_chart_line)); + + if (line->color) { + line->color = lxw_format_check_color(line->color); + line->has_color = LXW_TRUE; + } + + if (line->transparency > 100) + line->transparency = 0; + + return line; +} + +/* + * Create a copy of a user supplied fill. + */ +STATIC lxw_chart_fill * +_chart_convert_fill_args(lxw_chart_fill *user_fill) +{ + lxw_chart_fill *fill; + + if (!user_fill) + return NULL; + + fill = calloc(1, sizeof(struct lxw_chart_fill)); + RETURN_ON_MEM_ERROR(fill, NULL); + + memcpy(fill, user_fill, sizeof(lxw_chart_fill)); + + if (fill->color) { + fill->color = lxw_format_check_color(fill->color); + fill->has_color = LXW_TRUE; + } + + if (fill->transparency > 100) + fill->transparency = 0; + + return fill; +} + +/* + * Create a copy of a user supplied pattern. + */ +STATIC lxw_chart_pattern * +_chart_convert_pattern_args(lxw_chart_pattern *user_pattern) +{ + lxw_chart_pattern *pattern; + + if (!user_pattern) + return NULL; + + if (!user_pattern->type) { + LXW_WARN("chart_xxx_set_pattern: 'type' must be specified"); + return NULL; + } + + if (!user_pattern->fg_color) { + LXW_WARN("chart_xxx_set_pattern: 'fg_color' must be specified"); + return NULL; + } + + pattern = calloc(1, sizeof(struct lxw_chart_pattern)); + RETURN_ON_MEM_ERROR(pattern, NULL); + + memcpy(pattern, user_pattern, sizeof(lxw_chart_pattern)); + + pattern->fg_color = lxw_format_check_color(pattern->fg_color); + pattern->has_fg_color = LXW_TRUE; + + if (pattern->bg_color) { + pattern->bg_color = lxw_format_check_color(pattern->bg_color); + pattern->has_bg_color = LXW_TRUE; + } + else { + /* Default background color in Excel is white, when unspecified. */ + pattern->bg_color = LXW_COLOR_WHITE; + pattern->has_bg_color = LXW_TRUE; + } + + pattern->type = user_pattern->type; + + return pattern; +} + +/* + * Set a marker type for a series. + */ +STATIC void +_chart_set_default_marker_type(lxw_chart *self, uint8_t type) +{ + if (!self->default_marker) { + lxw_chart_marker *marker = calloc(1, sizeof(struct lxw_chart_marker)); + RETURN_VOID_ON_MEM_ERROR(marker); + self->default_marker = marker; + } + + self->default_marker->type = type; +} + +/* + * Set an axis number format. + */ +void +_chart_axis_set_default_num_format(lxw_chart_axis *axis, char *num_format) +{ + if (!num_format) + return; + + /* Free any previously allocated resource. */ + free(axis->default_num_format); + + axis->default_num_format = lxw_strdup(num_format); +} + +/* + * Verify that a X/Y error bar property is support for the chart type. + * All chart types, except Bar have Y error bars. Only Bar and Scatter + * support X error bars. + */ +lxw_error +_chart_check_error_bars(lxw_series_error_bars *error_bars, char *property) +{ + /* Check that the error bar type has been set for all error bar + * functions except the one that is used to set the type. */ + if (strlen(property) && !error_bars->is_set) { + LXW_WARN_FORMAT1("chart_series_set_error_bars%s(): " + "error bar type must be set first using " + "chart_series_set_error_bars()", property); + + return LXW_ERROR_PARAMETER_VALIDATION; + } + + if (error_bars->is_x) { + if (error_bars->chart_group != LXW_CHART_SCATTER + && error_bars->chart_group != LXW_CHART_BAR) { + + LXW_WARN_FORMAT1("chart_series_set_error_bars%s(): " + "'X error bar' properties only available for" + " Scatter and Bar charts in Excel", property); + + return LXW_ERROR_PARAMETER_VALIDATION; + } + } + else { + if (error_bars->chart_group == LXW_CHART_BAR) { + LXW_WARN_FORMAT1("chart_series_set_error_bars%s(): " + "'Y error bar' properties not available for " + "Bar charts in Excel", property); + + return LXW_ERROR_PARAMETER_VALIDATION; + } + } + + return LXW_NO_ERROR; +} + +/* + * Add unique ids for primary or secondary axes. + */ +STATIC void +_chart_add_axis_ids(lxw_chart *self) +{ + uint32_t chart_id = 50010000 + self->id; + uint32_t axis_count = 1; + + self->axis_id_1 = chart_id + axis_count; + self->axis_id_2 = self->axis_id_1 + 1; +} + +/* + * Utility function to set a chart range. + */ +STATIC void +_chart_set_range(lxw_series_range *range, const char *sheetname, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t last_row, lxw_col_t last_col) +{ + char formula[LXW_MAX_FORMULA_RANGE_LENGTH] = { 0 }; + + /* Set the range properties. */ + range->sheetname = lxw_strdup(sheetname); + range->first_row = first_row; + range->first_col = first_col; + range->last_row = last_row; + range->last_col = last_col; + + /* Free any existing range. */ + free(range->formula); + + /* Convert the range properties to a formula like: Sheet1!$A$1:$A$5. */ + lxw_rowcol_to_formula_abs(formula, sheetname, + first_row, first_col, last_row, last_col); + + range->formula = lxw_strdup(formula); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_chart_xml_declaration(lxw_chart *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_chart_space(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns_c[] = LXW_SCHEMA_DRAWING "/chart"; + char xmlns_a[] = LXW_SCHEMA_DRAWING "/main"; + char xmlns_r[] = LXW_SCHEMA_OFFICEDOC "/relationships"; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns:c", xmlns_c); + LXW_PUSH_ATTRIBUTES_STR("xmlns:a", xmlns_a); + LXW_PUSH_ATTRIBUTES_STR("xmlns:r", xmlns_r); + + lxw_xml_start_tag(self->file, "c:chartSpace", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_lang(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "en-US"); + + lxw_xml_empty_tag(self->file, "c:lang", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_style(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + /* Don"t write an element for the default style, 2. */ + if (self->style_id == 2) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", self->style_id); + + lxw_xml_empty_tag(self->file, "c:style", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_layout(lxw_chart *self) +{ + lxw_xml_empty_tag(self->file, "c:layout", NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_grouping(lxw_chart *self, uint8_t grouping) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (grouping == LXW_GROUPING_STANDARD) + LXW_PUSH_ATTRIBUTES_STR("val", "standard"); + else if (grouping == LXW_GROUPING_PERCENTSTACKED) + LXW_PUSH_ATTRIBUTES_STR("val", "percentStacked"); + else if (grouping == LXW_GROUPING_STACKED) + LXW_PUSH_ATTRIBUTES_STR("val", "stacked"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "clustered"); + + lxw_xml_empty_tag(self->file, "c:grouping", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_radar_style(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (self->type == LXW_CHART_RADAR_FILLED) + LXW_PUSH_ATTRIBUTES_STR("val", "filled"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "marker"); + + lxw_xml_empty_tag(self->file, "c:radarStyle", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_vary_colors(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:varyColors", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_first_slice_ang(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", self->rotation); + + lxw_xml_empty_tag(self->file, "c:firstSliceAng", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_hole_size(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", self->hole_size); + + lxw_xml_empty_tag(self->file, "c:holeSize", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_alpha(lxw_chart *self, uint8_t transparency) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + uint32_t val; + + LXW_INIT_ATTRIBUTES(); + + val = (100 - transparency) * 1000; + + LXW_PUSH_ATTRIBUTES_INT("val", val); + + lxw_xml_empty_tag(self->file, "a:alpha", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_srgb_clr(lxw_chart *self, lxw_color_t color, + uint8_t transparency) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char rgb_str[LXW_ATTR_32]; + + LXW_INIT_ATTRIBUTES(); + + lxw_snprintf(rgb_str, LXW_ATTR_32, "%06X", color & LXW_COLOR_MASK); + LXW_PUSH_ATTRIBUTES_STR("val", rgb_str); + + if (transparency) { + lxw_xml_start_tag(self->file, "a:srgbClr", &attributes); + + /* Write the a:alpha element. */ + _chart_write_a_alpha(self, transparency); + + lxw_xml_end_tag(self->file, "a:srgbClr"); + } + else { + lxw_xml_empty_tag(self->file, "a:srgbClr", &attributes); + } + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_solid_fill(lxw_chart *self, lxw_color_t color, + uint8_t transparency) +{ + + lxw_xml_start_tag(self->file, "a:solidFill", NULL); + + /* Write the a:srgbClr element. */ + _chart_write_a_srgb_clr(self, color, transparency); + + lxw_xml_end_tag(self->file, "a:solidFill"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_t(lxw_chart *self, char *name) +{ + lxw_xml_data_element(self->file, "a:t", name, NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_end_para_rpr(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("lang", "en-US"); + + lxw_xml_empty_tag(self->file, "a:endParaRPr", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_def_rpr(lxw_chart *self, lxw_chart_font *font) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + uint8_t has_color = LXW_FALSE; + uint8_t has_latin = LXW_FALSE; + uint8_t use_font_default = LXW_FALSE; + + LXW_INIT_ATTRIBUTES(); + + if (font) { + has_color = font->color || font->has_color; + has_latin = font->name || font->pitch_family || font->charset; + use_font_default = !(has_color || has_latin || font->baseline == -1); + + /* Set the font attributes. */ + if (font->size > 0.0) + LXW_PUSH_ATTRIBUTES_DBL("sz", font->size); + + if (use_font_default || font->bold) + LXW_PUSH_ATTRIBUTES_INT("b", font->bold & 0x1); + + if (use_font_default || font->italic) + LXW_PUSH_ATTRIBUTES_INT("i", font->italic & 0x1); + + if (font->underline) + LXW_PUSH_ATTRIBUTES_STR("u", "sng"); + + if (font->baseline != -1) + LXW_PUSH_ATTRIBUTES_INT("baseline", font->baseline); + } + + /* There are sub-elements if the font name or color have changed. */ + if (has_latin || has_color) { + + lxw_xml_start_tag(self->file, "a:defRPr", &attributes); + + if (has_color) { + _chart_write_a_solid_fill(self, font->color, LXW_FALSE); + } + + if (has_latin) { + /* Free and reuse the attribute list for the latin attributes. */ + LXW_FREE_ATTRIBUTES(); + + if (font->name) + LXW_PUSH_ATTRIBUTES_STR("typeface", font->name); + + if (font->pitch_family) + LXW_PUSH_ATTRIBUTES_INT("pitchFamily", font->pitch_family); + + if (font->pitch_family || font->charset) + LXW_PUSH_ATTRIBUTES_INT("charset", font->charset); + + /* Write the element. */ + lxw_xml_empty_tag(self->file, "a:latin", &attributes); + } + + lxw_xml_end_tag(self->file, "a:defRPr"); + } + else { + lxw_xml_empty_tag(self->file, "a:defRPr", &attributes); + } + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_r_pr(lxw_chart *self, lxw_chart_font *font) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + uint8_t has_color = LXW_FALSE; + uint8_t has_latin = LXW_FALSE; + uint8_t use_font_default = LXW_FALSE; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("lang", "en-US"); + + if (font) { + has_color = font->color || font->has_color; + has_latin = font->name || font->pitch_family || font->charset; + use_font_default = !(has_color || has_latin || font->baseline == -1); + + /* Set the font attributes. */ + if (font->size > 0.0) + LXW_PUSH_ATTRIBUTES_DBL("sz", font->size); + + if (use_font_default || font->bold) + LXW_PUSH_ATTRIBUTES_INT("b", font->bold & 0x1); + + if (use_font_default || font->italic) + LXW_PUSH_ATTRIBUTES_INT("i", font->italic & 0x1); + + if (font->underline) + LXW_PUSH_ATTRIBUTES_STR("u", "sng"); + + if (font->baseline != -1) + LXW_PUSH_ATTRIBUTES_INT("baseline", font->baseline); + } + + /* There are sub-elements if the font name or color have changed. */ + if (has_latin || has_color) { + + lxw_xml_start_tag(self->file, "a:rPr", &attributes); + + if (has_color) { + _chart_write_a_solid_fill(self, font->color, LXW_FALSE); + } + + if (has_latin) { + /* Free and reuse the attribute list for the latin attributes. */ + LXW_FREE_ATTRIBUTES(); + + if (font->name) + LXW_PUSH_ATTRIBUTES_STR("typeface", font->name); + + if (font->pitch_family) + LXW_PUSH_ATTRIBUTES_INT("pitchFamily", font->pitch_family); + + if (font->pitch_family || font->charset) + LXW_PUSH_ATTRIBUTES_INT("charset", font->charset); + + /* Write the element. */ + lxw_xml_empty_tag(self->file, "a:latin", &attributes); + } + + lxw_xml_end_tag(self->file, "a:rPr"); + } + else { + lxw_xml_empty_tag(self->file, "a:rPr", &attributes); + } + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_r(lxw_chart *self, char *name, lxw_chart_font *font) +{ + lxw_xml_start_tag(self->file, "a:r", NULL); + + /* Write the a:rPr element. */ + _chart_write_a_r_pr(self, font); + + /* Write the a:t element. */ + _chart_write_a_t(self, name); + + lxw_xml_end_tag(self->file, "a:r"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_p_pr_formula(lxw_chart *self, lxw_chart_font *font) +{ + lxw_xml_start_tag(self->file, "a:pPr", NULL); + + /* Write the a:defRPr element. */ + _chart_write_a_def_rpr(self, font); + + lxw_xml_end_tag(self->file, "a:pPr"); +} + +/* + * Write the element for pie chart legends. + */ +STATIC void +_chart_write_a_p_pr_pie(lxw_chart *self, lxw_chart_font *font) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("rtl", "0"); + + lxw_xml_start_tag(self->file, "a:pPr", &attributes); + + /* Write the a:defRPr element. */ + _chart_write_a_def_rpr(self, font); + + lxw_xml_end_tag(self->file, "a:pPr"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_p_pr_rich(lxw_chart *self, lxw_chart_font *font) +{ + lxw_xml_start_tag(self->file, "a:pPr", NULL); + + /* Write the a:defRPr element. */ + _chart_write_a_def_rpr(self, font); + + lxw_xml_end_tag(self->file, "a:pPr"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_p_formula(lxw_chart *self, lxw_chart_font *font) +{ + lxw_xml_start_tag(self->file, "a:p", NULL); + + /* Write the a:pPr element. */ + _chart_write_a_p_pr_formula(self, font); + + /* Write the a:endParaRPr element. */ + _chart_write_a_end_para_rpr(self); + + lxw_xml_end_tag(self->file, "a:p"); +} + +/* + * Write the element for pie chart legends. + */ +STATIC void +_chart_write_a_p_pie(lxw_chart *self, lxw_chart_font *font) +{ + lxw_xml_start_tag(self->file, "a:p", NULL); + + /* Write the a:pPr element. */ + _chart_write_a_p_pr_pie(self, font); + + /* Write the a:endParaRPr element. */ + _chart_write_a_end_para_rpr(self); + + lxw_xml_end_tag(self->file, "a:p"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_p_rich(lxw_chart *self, char *name, lxw_chart_font *font) +{ + lxw_xml_start_tag(self->file, "a:p", NULL); + + /* Write the a:pPr element. */ + _chart_write_a_p_pr_rich(self, font); + + /* Write the a:r element. */ + _chart_write_a_r(self, name, font); + + lxw_xml_end_tag(self->file, "a:p"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_lst_style(lxw_chart *self) +{ + lxw_xml_empty_tag(self->file, "a:lstStyle", NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_body_pr(lxw_chart *self, int32_t rotation, + uint8_t is_horizontal) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (rotation == 0 && is_horizontal) + rotation = -5400000; + + if (rotation) + LXW_PUSH_ATTRIBUTES_INT("rot", rotation); + + if (is_horizontal) + LXW_PUSH_ATTRIBUTES_STR("vert", "horz"); + + lxw_xml_empty_tag(self->file, "a:bodyPr", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_pt_count(lxw_chart *self, uint16_t num_data_points) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", num_data_points); + + lxw_xml_empty_tag(self->file, "c:ptCount", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_v_num(lxw_chart *self, double number) +{ + char data[LXW_ATTR_32]; + + lxw_snprintf(data, LXW_ATTR_32, "%.16g", number); + + lxw_xml_data_element(self->file, "c:v", data, NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_v_str(lxw_chart *self, char *str) +{ + lxw_xml_data_element(self->file, "c:v", str, NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_f(lxw_chart *self, char *formula) +{ + lxw_xml_data_element(self->file, "c:f", formula, NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_pt(lxw_chart *self, uint16_t index, + lxw_series_data_point *data_point) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + /* Ignore chart points that have no data. */ + if (data_point->no_data) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("idx", index); + + lxw_xml_start_tag(self->file, "c:pt", &attributes); + + if (data_point->is_string && data_point->string) + _chart_write_v_str(self, data_point->string); + else + _chart_write_v_num(self, data_point->number); + + lxw_xml_end_tag(self->file, "c:pt"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_num_pt(lxw_chart *self, uint16_t index, + lxw_series_data_point *data_point) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + /* Ignore chart points that have no data. */ + if (data_point->no_data) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("idx", index); + + lxw_xml_start_tag(self->file, "c:pt", &attributes); + + _chart_write_v_num(self, data_point->number); + + lxw_xml_end_tag(self->file, "c:pt"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_format_code(lxw_chart *self) +{ + lxw_xml_data_element(self->file, "c:formatCode", "General", NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_num_cache(lxw_chart *self, lxw_series_range *range) +{ + lxw_series_data_point *data_point; + uint16_t index = 0; + + lxw_xml_start_tag(self->file, "c:numCache", NULL); + + /* Write the c:formatCode element. */ + _chart_write_format_code(self); + + /* Write the c:ptCount element. */ + _chart_write_pt_count(self, range->num_data_points); + + STAILQ_FOREACH(data_point, range->data_cache, list_pointers) { + /* Write the c:pt element. */ + _chart_write_num_pt(self, index, data_point); + index++; + } + + lxw_xml_end_tag(self->file, "c:numCache"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_str_cache(lxw_chart *self, lxw_series_range *range) +{ + lxw_series_data_point *data_point; + uint16_t index = 0; + + lxw_xml_start_tag(self->file, "c:strCache", NULL); + + /* Write the c:ptCount element. */ + _chart_write_pt_count(self, range->num_data_points); + + STAILQ_FOREACH(data_point, range->data_cache, list_pointers) { + /* Write the c:pt element. */ + _chart_write_pt(self, index, data_point); + index++; + } + + lxw_xml_end_tag(self->file, "c:strCache"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_num_ref(lxw_chart *self, lxw_series_range *range) +{ + lxw_xml_start_tag(self->file, "c:numRef", NULL); + + /* Write the c:f element. */ + _chart_write_f(self, range->formula); + + if (!STAILQ_EMPTY(range->data_cache)) { + /* Write the c:numCache element. */ + _chart_write_num_cache(self, range); + } + + lxw_xml_end_tag(self->file, "c:numRef"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_str_ref(lxw_chart *self, lxw_series_range *range) +{ + lxw_xml_start_tag(self->file, "c:strRef", NULL); + + /* Write the c:f element. */ + _chart_write_f(self, range->formula); + + if (!STAILQ_EMPTY(range->data_cache)) { + /* Write the c:strCache element. */ + _chart_write_str_cache(self, range); + } + + lxw_xml_end_tag(self->file, "c:strRef"); +} + +/* + * Write the cached data elements. + */ +STATIC void +_chart_write_data_cache(lxw_chart *self, lxw_series_range *range, + uint8_t has_string_cache) +{ + if (has_string_cache) { + /* Write the c:strRef element. */ + _chart_write_str_ref(self, range); + } + else { + /* Write the c:numRef element. */ + _chart_write_num_ref(self, range); + } +} + +/* + * Write the element with a simple value such as for series names. + */ +STATIC void +_chart_write_tx_value(lxw_chart *self, char *name) +{ + lxw_xml_start_tag(self->file, "c:tx", NULL); + + /* Write the c:v element. */ + _chart_write_v_str(self, name); + + lxw_xml_end_tag(self->file, "c:tx"); +} + +/* + * Write the element with a simple value such as for series names. + */ +STATIC void +_chart_write_tx_formula(lxw_chart *self, lxw_chart_title *title) +{ + lxw_xml_start_tag(self->file, "c:tx", NULL); + + _chart_write_str_ref(self, title->range); + + lxw_xml_end_tag(self->file, "c:tx"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_tx_pr(lxw_chart *self, uint8_t is_horizontal, + lxw_chart_font *font) +{ + int32_t rotation = 0; + + if (font) + rotation = font->rotation; + + lxw_xml_start_tag(self->file, "c:txPr", NULL); + + /* Write the a:bodyPr element. */ + _chart_write_a_body_pr(self, rotation, is_horizontal); + + /* Write the a:lstStyle element. */ + _chart_write_a_lst_style(self); + + /* Write the a:p element. */ + _chart_write_a_p_formula(self, font); + + lxw_xml_end_tag(self->file, "c:txPr"); +} + +/* + * Write the element for pie chart legends. + */ +STATIC void +_chart_write_tx_pr_pie(lxw_chart *self, uint8_t is_horizontal, + lxw_chart_font *font) +{ + int32_t rotation = 0; + + if (font) + rotation = font->rotation; + + lxw_xml_start_tag(self->file, "c:txPr", NULL); + + /* Write the a:bodyPr element. */ + _chart_write_a_body_pr(self, rotation, is_horizontal); + + /* Write the a:lstStyle element. */ + _chart_write_a_lst_style(self); + + /* Write the a:p element. */ + _chart_write_a_p_pie(self, font); + + lxw_xml_end_tag(self->file, "c:txPr"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_axis_font(lxw_chart *self, lxw_chart_font *font) +{ + if (!font) + return; + + lxw_xml_start_tag(self->file, "c:txPr", NULL); + + /* Write the a:bodyPr element. */ + _chart_write_a_body_pr(self, font->rotation, LXW_FALSE); + + /* Write the a:lstStyle element. */ + _chart_write_a_lst_style(self); + + lxw_xml_start_tag(self->file, "a:p", NULL); + + /* Write the a:pPr element. */ + _chart_write_a_p_pr_rich(self, font); + + /* Write the a:endParaRPr element. */ + _chart_write_a_end_para_rpr(self); + + lxw_xml_end_tag(self->file, "a:p"); + lxw_xml_end_tag(self->file, "c:txPr"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_rich(lxw_chart *self, char *name, uint8_t is_horizontal, + lxw_chart_font *font) +{ + int32_t rotation = 0; + + if (font) + rotation = font->rotation; + + lxw_xml_start_tag(self->file, "c:rich", NULL); + + /* Write the a:bodyPr element. */ + _chart_write_a_body_pr(self, rotation, is_horizontal); + + /* Write the a:lstStyle element. */ + _chart_write_a_lst_style(self); + + /* Write the a:p element. */ + _chart_write_a_p_rich(self, name, font); + + lxw_xml_end_tag(self->file, "c:rich"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_tx_rich(lxw_chart *self, char *name, uint8_t is_horizontal, + lxw_chart_font *font) +{ + + lxw_xml_start_tag(self->file, "c:tx", NULL); + + /* Write the c:rich element. */ + _chart_write_rich(self, name, is_horizontal, font); + + lxw_xml_end_tag(self->file, "c:tx"); +} + +/* + * Write the element for rich strings. + */ +STATIC void +_chart_write_title_rich(lxw_chart *self, lxw_chart_title *title) +{ + lxw_xml_start_tag(self->file, "c:title", NULL); + + /* Write the c:tx element. */ + _chart_write_tx_rich(self, title->name, title->is_horizontal, + title->font); + + /* Write the c:layout element. */ + _chart_write_layout(self); + + lxw_xml_end_tag(self->file, "c:title"); +} + +/* + * Write the element for a formula style title + */ +STATIC void +_chart_write_title_formula(lxw_chart *self, lxw_chart_title *title) +{ + lxw_xml_start_tag(self->file, "c:title", NULL); + + /* Write the c:tx element. */ + _chart_write_tx_formula(self, title); + + /* Write the c:layout element. */ + _chart_write_layout(self); + + /* Write the c:txPr element. */ + _chart_write_tx_pr(self, title->is_horizontal, title->font); + + lxw_xml_end_tag(self->file, "c:title"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_delete(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:delete", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_auto_title_deleted(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:autoTitleDeleted", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_idx(lxw_chart *self, uint16_t index) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", index); + + lxw_xml_empty_tag(self->file, "c:idx", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_prst_dash(lxw_chart *self, uint8_t dash_type) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (dash_type == LXW_CHART_LINE_DASH_ROUND_DOT) + LXW_PUSH_ATTRIBUTES_STR("val", "sysDot"); + else if (dash_type == LXW_CHART_LINE_DASH_SQUARE_DOT) + LXW_PUSH_ATTRIBUTES_STR("val", "sysDash"); + else if (dash_type == LXW_CHART_LINE_DASH_DASH_DOT) + LXW_PUSH_ATTRIBUTES_STR("val", "dashDot"); + else if (dash_type == LXW_CHART_LINE_DASH_LONG_DASH) + LXW_PUSH_ATTRIBUTES_STR("val", "lgDash"); + else if (dash_type == LXW_CHART_LINE_DASH_LONG_DASH_DOT) + LXW_PUSH_ATTRIBUTES_STR("val", "lgDashDot"); + else if (dash_type == LXW_CHART_LINE_DASH_LONG_DASH_DOT_DOT) + LXW_PUSH_ATTRIBUTES_STR("val", "lgDashDotDot"); + else if (dash_type == LXW_CHART_LINE_DASH_DOT) + LXW_PUSH_ATTRIBUTES_STR("val", "dot"); + else if (dash_type == LXW_CHART_LINE_DASH_SYSTEM_DASH_DOT) + LXW_PUSH_ATTRIBUTES_STR("val", "sysDashDot"); + else if (dash_type == LXW_CHART_LINE_DASH_SYSTEM_DASH_DOT_DOT) + LXW_PUSH_ATTRIBUTES_STR("val", "sysDashDotDot"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "dash"); + + lxw_xml_empty_tag(self->file, "a:prstDash", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_no_fill(lxw_chart *self) +{ + lxw_xml_empty_tag(self->file, "a:noFill", NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_ln(lxw_chart *self, lxw_chart_line *line) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + float width_flt; + uint32_t width_int; + + LXW_INIT_ATTRIBUTES(); + + /* Round width to nearest 0.25, like Excel. */ + width_flt = (float) (uint32_t) ((line->width + 0.125) * 4.0F) / 4.0F; + + /* Convert to internal units. */ + width_int = (uint32_t) (0.5 + (12700.0 * width_flt)); + + if (width_int) + LXW_PUSH_ATTRIBUTES_INT("w", width_int); + + lxw_xml_start_tag(self->file, "a:ln", &attributes); + + /* Write the line fill. */ + if (line->none) { + /* Write the a:noFill element. */ + _chart_write_a_no_fill(self); + } + else if (line->has_color) { + /* Write the a:solidFill element. */ + _chart_write_a_solid_fill(self, line->color, line->transparency); + } + + /* Write the line/dash type. */ + if (line->dash_type) { + /* Write the a:prstDash element. */ + _chart_write_a_prst_dash(self, line->dash_type); + } + + lxw_xml_end_tag(self->file, "a:ln"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_fg_clr(lxw_chart *self, lxw_color_t color) +{ + lxw_xml_start_tag(self->file, "a:fgClr", NULL); + + _chart_write_a_srgb_clr(self, color, LXW_FALSE); + + lxw_xml_end_tag(self->file, "a:fgClr"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_bg_clr(lxw_chart *self, lxw_color_t color) +{ + lxw_xml_start_tag(self->file, "a:bgClr", NULL); + + _chart_write_a_srgb_clr(self, color, LXW_FALSE); + + lxw_xml_end_tag(self->file, "a:bgClr"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_a_patt_fill(lxw_chart *self, lxw_chart_pattern *pattern) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (pattern->type == LXW_CHART_PATTERN_NONE) + LXW_PUSH_ATTRIBUTES_STR("prst", "none"); + else if (pattern->type == LXW_CHART_PATTERN_PERCENT_5) + LXW_PUSH_ATTRIBUTES_STR("prst", "pct5"); + else if (pattern->type == LXW_CHART_PATTERN_PERCENT_10) + LXW_PUSH_ATTRIBUTES_STR("prst", "pct10"); + else if (pattern->type == LXW_CHART_PATTERN_PERCENT_20) + LXW_PUSH_ATTRIBUTES_STR("prst", "pct20"); + else if (pattern->type == LXW_CHART_PATTERN_PERCENT_25) + LXW_PUSH_ATTRIBUTES_STR("prst", "pct25"); + else if (pattern->type == LXW_CHART_PATTERN_PERCENT_30) + LXW_PUSH_ATTRIBUTES_STR("prst", "pct30"); + else if (pattern->type == LXW_CHART_PATTERN_PERCENT_40) + LXW_PUSH_ATTRIBUTES_STR("prst", "pct40"); + else if (pattern->type == LXW_CHART_PATTERN_PERCENT_50) + LXW_PUSH_ATTRIBUTES_STR("prst", "pct50"); + else if (pattern->type == LXW_CHART_PATTERN_PERCENT_60) + LXW_PUSH_ATTRIBUTES_STR("prst", "pct60"); + else if (pattern->type == LXW_CHART_PATTERN_PERCENT_70) + LXW_PUSH_ATTRIBUTES_STR("prst", "pct70"); + else if (pattern->type == LXW_CHART_PATTERN_PERCENT_75) + LXW_PUSH_ATTRIBUTES_STR("prst", "pct75"); + else if (pattern->type == LXW_CHART_PATTERN_PERCENT_80) + LXW_PUSH_ATTRIBUTES_STR("prst", "pct80"); + else if (pattern->type == LXW_CHART_PATTERN_PERCENT_90) + LXW_PUSH_ATTRIBUTES_STR("prst", "pct90"); + else if (pattern->type == LXW_CHART_PATTERN_LIGHT_DOWNWARD_DIAGONAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "ltDnDiag"); + else if (pattern->type == LXW_CHART_PATTERN_LIGHT_UPWARD_DIAGONAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "ltUpDiag"); + else if (pattern->type == LXW_CHART_PATTERN_DARK_DOWNWARD_DIAGONAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "dkDnDiag"); + else if (pattern->type == LXW_CHART_PATTERN_DARK_UPWARD_DIAGONAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "dkUpDiag"); + else if (pattern->type == LXW_CHART_PATTERN_WIDE_DOWNWARD_DIAGONAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "wdDnDiag"); + else if (pattern->type == LXW_CHART_PATTERN_WIDE_UPWARD_DIAGONAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "wdUpDiag"); + else if (pattern->type == LXW_CHART_PATTERN_LIGHT_VERTICAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "ltVert"); + else if (pattern->type == LXW_CHART_PATTERN_LIGHT_HORIZONTAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "ltHorz"); + else if (pattern->type == LXW_CHART_PATTERN_NARROW_VERTICAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "narVert"); + else if (pattern->type == LXW_CHART_PATTERN_NARROW_HORIZONTAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "narHorz"); + else if (pattern->type == LXW_CHART_PATTERN_DARK_VERTICAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "dkVert"); + else if (pattern->type == LXW_CHART_PATTERN_DARK_HORIZONTAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "dkHorz"); + else if (pattern->type == LXW_CHART_PATTERN_DASHED_DOWNWARD_DIAGONAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "dashDnDiag"); + else if (pattern->type == LXW_CHART_PATTERN_DASHED_UPWARD_DIAGONAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "dashUpDiag"); + else if (pattern->type == LXW_CHART_PATTERN_DASHED_HORIZONTAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "dashHorz"); + else if (pattern->type == LXW_CHART_PATTERN_DASHED_VERTICAL) + LXW_PUSH_ATTRIBUTES_STR("prst", "dashVert"); + else if (pattern->type == LXW_CHART_PATTERN_SMALL_CONFETTI) + LXW_PUSH_ATTRIBUTES_STR("prst", "smConfetti"); + else if (pattern->type == LXW_CHART_PATTERN_LARGE_CONFETTI) + LXW_PUSH_ATTRIBUTES_STR("prst", "lgConfetti"); + else if (pattern->type == LXW_CHART_PATTERN_ZIGZAG) + LXW_PUSH_ATTRIBUTES_STR("prst", "zigZag"); + else if (pattern->type == LXW_CHART_PATTERN_WAVE) + LXW_PUSH_ATTRIBUTES_STR("prst", "wave"); + else if (pattern->type == LXW_CHART_PATTERN_DIAGONAL_BRICK) + LXW_PUSH_ATTRIBUTES_STR("prst", "diagBrick"); + else if (pattern->type == LXW_CHART_PATTERN_HORIZONTAL_BRICK) + LXW_PUSH_ATTRIBUTES_STR("prst", "horzBrick"); + else if (pattern->type == LXW_CHART_PATTERN_WEAVE) + LXW_PUSH_ATTRIBUTES_STR("prst", "weave"); + else if (pattern->type == LXW_CHART_PATTERN_PLAID) + LXW_PUSH_ATTRIBUTES_STR("prst", "plaid"); + else if (pattern->type == LXW_CHART_PATTERN_DIVOT) + LXW_PUSH_ATTRIBUTES_STR("prst", "divot"); + else if (pattern->type == LXW_CHART_PATTERN_DOTTED_GRID) + LXW_PUSH_ATTRIBUTES_STR("prst", "dotGrid"); + else if (pattern->type == LXW_CHART_PATTERN_DOTTED_DIAMOND) + LXW_PUSH_ATTRIBUTES_STR("prst", "dotDmnd"); + else if (pattern->type == LXW_CHART_PATTERN_SHINGLE) + LXW_PUSH_ATTRIBUTES_STR("prst", "shingle"); + else if (pattern->type == LXW_CHART_PATTERN_TRELLIS) + LXW_PUSH_ATTRIBUTES_STR("prst", "trellis"); + else if (pattern->type == LXW_CHART_PATTERN_SPHERE) + LXW_PUSH_ATTRIBUTES_STR("prst", "sphere"); + else if (pattern->type == LXW_CHART_PATTERN_SMALL_GRID) + LXW_PUSH_ATTRIBUTES_STR("prst", "smGrid"); + else if (pattern->type == LXW_CHART_PATTERN_LARGE_GRID) + LXW_PUSH_ATTRIBUTES_STR("prst", "lgGrid"); + else if (pattern->type == LXW_CHART_PATTERN_SMALL_CHECK) + LXW_PUSH_ATTRIBUTES_STR("prst", "smCheck"); + else if (pattern->type == LXW_CHART_PATTERN_LARGE_CHECK) + LXW_PUSH_ATTRIBUTES_STR("prst", "lgCheck"); + else if (pattern->type == LXW_CHART_PATTERN_OUTLINED_DIAMOND) + LXW_PUSH_ATTRIBUTES_STR("prst", "openDmnd"); + else if (pattern->type == LXW_CHART_PATTERN_SOLID_DIAMOND) + LXW_PUSH_ATTRIBUTES_STR("prst", "solidDmnd"); + else + LXW_PUSH_ATTRIBUTES_STR("prst", "percent_50"); + + lxw_xml_start_tag(self->file, "a:pattFill", &attributes); + + if (pattern->has_fg_color) + _chart_write_a_fg_clr(self, pattern->fg_color); + + if (pattern->has_bg_color) + _chart_write_a_bg_clr(self, pattern->bg_color); + + lxw_xml_end_tag(self->file, "a:pattFill"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_sp_pr(lxw_chart *self, lxw_chart_line *line, + lxw_chart_fill *fill, lxw_chart_pattern *pattern) +{ + if (!line && !fill && !pattern) + return; + + lxw_xml_start_tag(self->file, "c:spPr", NULL); + + /* Write the series fill. Note: a pattern fill overrides a solid fill. */ + if (fill && !pattern) { + if (fill->none) { + /* Write the a:noFill element. */ + _chart_write_a_no_fill(self); + } + else { + /* Write the a:solidFill element. */ + _chart_write_a_solid_fill(self, fill->color, fill->transparency); + } + } + + if (pattern) { + /* Write the a:pattFill element. */ + _chart_write_a_patt_fill(self, pattern); + } + + if (line) { + /* Write the a:ln element. */ + _chart_write_a_ln(self, line); + } + + lxw_xml_end_tag(self->file, "c:spPr"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_order(lxw_chart *self, uint16_t index) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", index); + + lxw_xml_empty_tag(self->file, "c:order", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_axis_id(lxw_chart *self, uint32_t axis_id) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", axis_id); + + lxw_xml_empty_tag(self->file, "c:axId", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_axis_ids(lxw_chart *self) +{ + if (!self->axis_id_1) + _chart_add_axis_ids(self); + + _chart_write_axis_id(self, self->axis_id_1); + _chart_write_axis_id(self, self->axis_id_2); +} + +/* + * Write the series name. + */ +STATIC void +_chart_write_series_name(lxw_chart *self, lxw_chart_series *series) +{ + if (series->title.name) { + /* Write the c:tx element. */ + _chart_write_tx_value(self, series->title.name); + } + else if (series->title.range->formula) { + /* Write the c:tx element. */ + _chart_write_tx_formula(self, &series->title); + + } +} + +/* + * Write the element. + */ +STATIC void +_chart_write_major_tick_mark(lxw_chart *self, lxw_chart_axis *axis) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!axis->major_tick_mark) + return; + + LXW_INIT_ATTRIBUTES(); + + if (axis->major_tick_mark == LXW_CHART_AXIS_TICK_MARK_NONE) + LXW_PUSH_ATTRIBUTES_STR("val", "none"); + else if (axis->major_tick_mark == LXW_CHART_AXIS_TICK_MARK_INSIDE) + LXW_PUSH_ATTRIBUTES_STR("val", "in"); + else if (axis->major_tick_mark == LXW_CHART_AXIS_TICK_MARK_CROSSING) + LXW_PUSH_ATTRIBUTES_STR("val", "cross"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "out"); + + lxw_xml_empty_tag(self->file, "c:majorTickMark", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_minor_tick_mark(lxw_chart *self, lxw_chart_axis *axis) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!axis->minor_tick_mark) + return; + + LXW_INIT_ATTRIBUTES(); + + if (axis->minor_tick_mark == LXW_CHART_AXIS_TICK_MARK_NONE) + LXW_PUSH_ATTRIBUTES_STR("val", "none"); + else if (axis->minor_tick_mark == LXW_CHART_AXIS_TICK_MARK_INSIDE) + LXW_PUSH_ATTRIBUTES_STR("val", "in"); + else if (axis->minor_tick_mark == LXW_CHART_AXIS_TICK_MARK_CROSSING) + LXW_PUSH_ATTRIBUTES_STR("val", "cross"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "out"); + + lxw_xml_empty_tag(self->file, "c:minorTickMark", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_symbol(lxw_chart *self, uint8_t type) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (type == LXW_CHART_MARKER_SQUARE) + LXW_PUSH_ATTRIBUTES_STR("val", "square"); + else if (type == LXW_CHART_MARKER_DIAMOND) + LXW_PUSH_ATTRIBUTES_STR("val", "diamond"); + else if (type == LXW_CHART_MARKER_TRIANGLE) + LXW_PUSH_ATTRIBUTES_STR("val", "triangle"); + else if (type == LXW_CHART_MARKER_X) + LXW_PUSH_ATTRIBUTES_STR("val", "x"); + else if (type == LXW_CHART_MARKER_STAR) + LXW_PUSH_ATTRIBUTES_STR("val", "star"); + else if (type == LXW_CHART_MARKER_SHORT_DASH) + LXW_PUSH_ATTRIBUTES_STR("val", "short_dash"); + else if (type == LXW_CHART_MARKER_LONG_DASH) + LXW_PUSH_ATTRIBUTES_STR("val", "long_dash"); + else if (type == LXW_CHART_MARKER_CIRCLE) + LXW_PUSH_ATTRIBUTES_STR("val", "circle"); + else if (type == LXW_CHART_MARKER_PLUS) + LXW_PUSH_ATTRIBUTES_STR("val", "plus"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "none"); + + lxw_xml_empty_tag(self->file, "c:symbol", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_d_pt(lxw_chart *self, lxw_chart_point *point, uint16_t index) +{ + lxw_xml_start_tag(self->file, "c:dPt", NULL); + + /* Write the c:idx element. */ + _chart_write_idx(self, index); + + /* Scatter/Line charts have an additional marker for the point. */ + if (self->chart_group == LXW_CHART_SCATTER + || self->chart_group == LXW_CHART_LINE) + lxw_xml_start_tag(self->file, "c:marker", NULL); + + /* Write the c:spPr element. */ + _chart_write_sp_pr(self, point->line, point->fill, point->pattern); + + if (self->chart_group == LXW_CHART_SCATTER + || self->chart_group == LXW_CHART_LINE) + lxw_xml_end_tag(self->file, "c:marker"); + + lxw_xml_end_tag(self->file, "c:dPt"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_points(lxw_chart *self, lxw_chart_series *series) +{ + uint16_t index; + + for (index = 0; index < series->point_count; index++) { + lxw_chart_point *point = &series->points[index]; + + /* Ignore empty points. */ + if (!point->line && !point->fill && !point->pattern) + continue; + + /* Write the c:dPt element. */ + _chart_write_d_pt(self, &series->points[index], index); + } +} + +/* + * Write the element. + */ +STATIC void +_chart_write_invert_if_negative(lxw_chart *self, lxw_chart_series *series) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!series->invert_if_negative) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:invertIfNegative", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_show_val(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:showVal", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_show_cat_name(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:showCatName", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_show_ser_name(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:showSerName", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_show_leader_lines(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:showLeaderLines", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_d_lbl_pos(lxw_chart *self, uint8_t position) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (position == LXW_CHART_LABEL_POSITION_RIGHT) + LXW_PUSH_ATTRIBUTES_STR("val", "r"); + else if (position == LXW_CHART_LABEL_POSITION_LEFT) + LXW_PUSH_ATTRIBUTES_STR("val", "l"); + else if (position == LXW_CHART_LABEL_POSITION_ABOVE) + LXW_PUSH_ATTRIBUTES_STR("val", "t"); + else if (position == LXW_CHART_LABEL_POSITION_BELOW) + LXW_PUSH_ATTRIBUTES_STR("val", "b"); + else if (position == LXW_CHART_LABEL_POSITION_INSIDE_BASE) + LXW_PUSH_ATTRIBUTES_STR("val", "inBase"); + else if (position == LXW_CHART_LABEL_POSITION_INSIDE_END) + LXW_PUSH_ATTRIBUTES_STR("val", "inEnd"); + else if (position == LXW_CHART_LABEL_POSITION_OUTSIDE_END) + LXW_PUSH_ATTRIBUTES_STR("val", "outEnd"); + else if (position == LXW_CHART_LABEL_POSITION_BEST_FIT) + LXW_PUSH_ATTRIBUTES_STR("val", "bestFit"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "ctr"); + + lxw_xml_empty_tag(self->file, "c:dLblPos", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_separator(lxw_chart *self, uint8_t separator) +{ + if (separator == LXW_CHART_LABEL_SEPARATOR_SEMICOLON) + lxw_xml_data_element(self->file, "c:separator", "; ", NULL); + else if (separator == LXW_CHART_LABEL_SEPARATOR_PERIOD) + lxw_xml_data_element(self->file, "c:separator", ". ", NULL); + else if (separator == LXW_CHART_LABEL_SEPARATOR_NEWLINE) + lxw_xml_data_element(self->file, "c:separator", "\n", NULL); + else if (separator == LXW_CHART_LABEL_SEPARATOR_SPACE) + lxw_xml_data_element(self->file, "c:separator", " ", NULL); + else + lxw_xml_data_element(self->file, "c:separator", ", ", NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_show_legend_key(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:showLegendKey", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_show_percent(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:showPercent", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_label_num_fmt(lxw_chart *self, char *format) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("formatCode", format); + LXW_PUSH_ATTRIBUTES_STR("sourceLinked", "0"); + + lxw_xml_empty_tag(self->file, "c:numFmt", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_d_lbls(lxw_chart *self, lxw_chart_series *series) +{ + if (!series->has_labels) + return; + + lxw_xml_start_tag(self->file, "c:dLbls", NULL); + + /* Write the c:numFmt element. */ + if (series->label_num_format) + _chart_write_label_num_fmt(self, series->label_num_format); + + if (series->label_font) + _chart_write_tx_pr(self, LXW_FALSE, series->label_font); + + /* Write the c:dLblPos element. */ + if (series->label_position) + _chart_write_d_lbl_pos(self, series->label_position); + + /* Write the c:showLegendKey element. */ + if (series->show_labels_legend) + _chart_write_show_legend_key(self); + + /* Write the c:showVal element. */ + if (series->show_labels_value) + _chart_write_show_val(self); + + /* Write the c:showCatName element. */ + if (series->show_labels_category) + _chart_write_show_cat_name(self); + + /* Write the c:showSerName element. */ + if (series->show_labels_name) + _chart_write_show_ser_name(self); + + /* Write the c:showPercent element. */ + if (series->show_labels_percent) + _chart_write_show_percent(self); + + /* Write the c:separator element. */ + if (series->label_separator) + _chart_write_separator(self, series->label_separator); + + /* Write the c:showLeaderLines element. */ + if (series->show_labels_leader) + _chart_write_show_leader_lines(self); + + lxw_xml_end_tag(self->file, "c:dLbls"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_intercept(lxw_chart *self, double value) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_DBL("val", value); + + lxw_xml_empty_tag(self->file, "c:intercept", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_disp_rsqr(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:dispRSqr", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_trendline_lbl(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + lxw_xml_start_tag(self->file, "c:trendlineLbl", NULL); + + lxw_xml_empty_tag(self->file, "c:layout", NULL); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("formatCode", "General"); + LXW_PUSH_ATTRIBUTES_INT("sourceLinked", 0); + + lxw_xml_empty_tag(self->file, "c:numFmt", &attributes); + + lxw_xml_end_tag(self->file, "c:trendlineLbl"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_disp_eq(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:dispEq", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_period(lxw_chart *self, uint8_t value) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", value); + + lxw_xml_empty_tag(self->file, "c:period", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_forward(lxw_chart *self, double value) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_DBL("val", value); + + lxw_xml_empty_tag(self->file, "c:forward", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_backward(lxw_chart *self, double value) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_DBL("val", value); + + lxw_xml_empty_tag(self->file, "c:backward", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_name(lxw_chart *self, char *name) +{ + lxw_xml_data_element(self->file, "c:name", name, NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_trendline_type(lxw_chart *self, uint8_t type) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (type == LXW_CHART_TRENDLINE_TYPE_LOG) + LXW_PUSH_ATTRIBUTES_STR("val", "log"); + else if (type == LXW_CHART_TRENDLINE_TYPE_POLY) + LXW_PUSH_ATTRIBUTES_STR("val", "poly"); + else if (type == LXW_CHART_TRENDLINE_TYPE_POWER) + LXW_PUSH_ATTRIBUTES_STR("val", "power"); + else if (type == LXW_CHART_TRENDLINE_TYPE_EXP) + LXW_PUSH_ATTRIBUTES_STR("val", "exp"); + else if (type == LXW_CHART_TRENDLINE_TYPE_AVERAGE) + LXW_PUSH_ATTRIBUTES_STR("val", "movingAvg"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "linear"); + + lxw_xml_empty_tag(self->file, "c:trendlineType", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_trendline(lxw_chart *self, lxw_chart_series *series) +{ + if (!series->has_trendline) + return; + + lxw_xml_start_tag(self->file, "c:trendline", NULL); + + /* Write the c:name element. */ + if (series->trendline_name) + _chart_write_name(self, series->trendline_name); + + /* Write the c:spPr element. */ + _chart_write_sp_pr(self, series->trendline_line, NULL, NULL); + + /* Write the c:trendlineType element. */ + _chart_write_trendline_type(self, series->trendline_type); + + /* Write the c:order element. */ + if (series->trendline_type == LXW_CHART_TRENDLINE_TYPE_POLY + && series->trendline_value >= 2) { + + _chart_write_order(self, series->trendline_value); + } + + /* Write the c:period element. */ + if (series->trendline_type == LXW_CHART_TRENDLINE_TYPE_AVERAGE + && series->trendline_value >= 2) { + + _chart_write_period(self, series->trendline_value); + } + + if (series->has_trendline_forecast) { + /* Write the c:forward element. */ + _chart_write_forward(self, series->trendline_forward); + + /* Write the c:backward element. */ + _chart_write_backward(self, series->trendline_backward); + } + + /* Write the c:intercept element. */ + if (series->has_trendline_intercept) + _chart_write_intercept(self, series->trendline_intercept); + + /* Write the c:dispRSqr element. */ + if (series->has_trendline_r_squared) + _chart_write_disp_rsqr(self); + + if (series->has_trendline_equation) { + /* Write the c:dispEq element. */ + _chart_write_disp_eq(self); + + /* Write the c:trendlineLbl element. */ + _chart_write_trendline_lbl(self); + + } + + lxw_xml_end_tag(self->file, "c:trendline"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_error_val(lxw_chart *self, double value) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_DBL("val", value); + + lxw_xml_empty_tag(self->file, "c:val", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_no_end_cap(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:noEndCap", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_err_val_type(lxw_chart *self, uint8_t type) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (type == LXW_CHART_ERROR_BAR_TYPE_FIXED) + LXW_PUSH_ATTRIBUTES_STR("val", "fixedVal"); + else if (type == LXW_CHART_ERROR_BAR_TYPE_PERCENTAGE) + LXW_PUSH_ATTRIBUTES_STR("val", "percentage"); + else if (type == LXW_CHART_ERROR_BAR_TYPE_STD_DEV) + LXW_PUSH_ATTRIBUTES_STR("val", "stdDev"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "stdErr"); + + lxw_xml_empty_tag(self->file, "c:errValType", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_err_bar_type(lxw_chart *self, uint8_t direction) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (direction == LXW_CHART_ERROR_BAR_DIR_PLUS) + LXW_PUSH_ATTRIBUTES_STR("val", "plus"); + else if (direction == LXW_CHART_ERROR_BAR_DIR_MINUS) + LXW_PUSH_ATTRIBUTES_STR("val", "minus"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "both"); + + lxw_xml_empty_tag(self->file, "c:errBarType", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_err_dir(lxw_chart *self, uint8_t is_x) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (is_x) + LXW_PUSH_ATTRIBUTES_STR("val", "x"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "y"); + + lxw_xml_empty_tag(self->file, "c:errDir", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_err_bars(lxw_chart *self, lxw_series_error_bars *error_bars) +{ + if (!error_bars->is_set) + return; + + lxw_xml_start_tag(self->file, "c:errBars", NULL); + + /* Write the c:errDir element, except for Column/Bar charts. */ + if (error_bars->chart_group != LXW_CHART_BAR + && error_bars->chart_group != LXW_CHART_COLUMN) { + + _chart_write_err_dir(self, error_bars->is_x); + } + + /* Write the c:errBarType element. */ + _chart_write_err_bar_type(self, error_bars->direction); + + /* Write the c:errValType element. */ + _chart_write_err_val_type(self, error_bars->type); + + /* Write the c:noEndCap element. */ + if (error_bars->endcap == LXW_CHART_ERROR_BAR_NO_CAP) + _chart_write_no_end_cap(self); + + /* Write the c:val element. */ + if (error_bars->has_value) + _chart_write_error_val(self, error_bars->value); + + /* Write the c:spPr element. */ + _chart_write_sp_pr(self, error_bars->line, NULL, NULL); + + lxw_xml_end_tag(self->file, "c:errBars"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_error_bars(lxw_chart *self, lxw_chart_series *series) +{ + _chart_write_err_bars(self, series->x_error_bars); + _chart_write_err_bars(self, series->y_error_bars); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_marker_size(lxw_chart *self, uint8_t size) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", size); + + lxw_xml_empty_tag(self->file, "c:size", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_marker(lxw_chart *self, lxw_chart_marker *marker) +{ + /* If there isn't a user defined marker use the default, if this chart + * type one. The default usually turns the marker off. */ + if (!marker) + marker = self->default_marker; + + if (!marker) + return; + + if (marker->type == LXW_CHART_MARKER_AUTOMATIC) + return; + + lxw_xml_start_tag(self->file, "c:marker", NULL); + + /* Write the c:symbol element. */ + _chart_write_symbol(self, marker->type); + + /* Write the c:size element. */ + if (marker->size) + _chart_write_marker_size(self, marker->size); + + /* Write the c:spPr element. */ + _chart_write_sp_pr(self, marker->line, marker->fill, marker->pattern); + + lxw_xml_end_tag(self->file, "c:marker"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_marker_value(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:marker", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_smooth(lxw_chart *self, uint8_t smooth) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!smooth) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:smooth", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_scatter_style(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (self->type == LXW_CHART_SCATTER_SMOOTH + || self->type == LXW_CHART_SCATTER_SMOOTH_WITH_MARKERS) + LXW_PUSH_ATTRIBUTES_STR("val", "smoothMarker"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "lineMarker"); + + lxw_xml_empty_tag(self->file, "c:scatterStyle", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_cat(lxw_chart *self, lxw_chart_series *series) +{ + uint8_t has_string_cache = series->categories->has_string_cache; + + /* Ignore elements for charts without category values. */ + if (!series->categories->formula) + return; + + self->cat_has_num_fmt = !has_string_cache; + + lxw_xml_start_tag(self->file, "c:cat", NULL); + + /* Write the c:numRef element. */ + _chart_write_data_cache(self, series->categories, has_string_cache); + + lxw_xml_end_tag(self->file, "c:cat"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_x_val(lxw_chart *self, lxw_chart_series *series) +{ + uint8_t has_string_cache = series->categories->has_string_cache; + + lxw_xml_start_tag(self->file, "c:xVal", NULL); + + /* Write the data cache elements. */ + _chart_write_data_cache(self, series->categories, has_string_cache); + + lxw_xml_end_tag(self->file, "c:xVal"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_val(lxw_chart *self, lxw_chart_series *series) +{ + lxw_xml_start_tag(self->file, "c:val", NULL); + + /* Write the data cache elements. The string_cache is set to false since + * this should always be a number series. */ + _chart_write_data_cache(self, series->values, LXW_FALSE); + + lxw_xml_end_tag(self->file, "c:val"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_y_val(lxw_chart *self, lxw_chart_series *series) +{ + lxw_xml_start_tag(self->file, "c:yVal", NULL); + + /* Write the data cache elements. The string_cache is set to false since + * this should always be a number series. */ + _chart_write_data_cache(self, series->values, LXW_FALSE); + + lxw_xml_end_tag(self->file, "c:yVal"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_ser(lxw_chart *self, lxw_chart_series *series) +{ + uint16_t index = self->series_index++; + + lxw_xml_start_tag(self->file, "c:ser", NULL); + + /* Write the c:idx element. */ + _chart_write_idx(self, index); + + /* Write the c:order element. */ + _chart_write_order(self, index); + + /* Write the series name. */ + _chart_write_series_name(self, series); + + /* Write the c:spPr element. */ + _chart_write_sp_pr(self, series->line, series->fill, series->pattern); + + /* Write the c:marker element. */ + _chart_write_marker(self, series->marker); + + /* Write the c:invertIfNegative element. */ + _chart_write_invert_if_negative(self, series); + + /* Write the char points. */ + _chart_write_points(self, series); + + /* Write the c:dLbls element. */ + _chart_write_d_lbls(self, series); + + /* Write the c:trendline element. */ + _chart_write_trendline(self, series); + + /* Write the c:errBars element. */ + _chart_write_error_bars(self, series); + + /* Write the c:cat element. */ + _chart_write_cat(self, series); + + /* Write the c:val element. */ + _chart_write_val(self, series); + + /* Write the c:smooth element. */ + if (self->chart_group == LXW_CHART_SCATTER + || self->chart_group == LXW_CHART_LINE) + _chart_write_smooth(self, series->smooth); + + lxw_xml_end_tag(self->file, "c:ser"); +} + +/* + * Write the element but with c:xVal/c:yVal instead of c:cat/c:val + * elements. + */ +STATIC void +_chart_write_xval_ser(lxw_chart *self, lxw_chart_series *series) +{ + uint16_t index = self->series_index++; + + lxw_xml_start_tag(self->file, "c:ser", NULL); + + /* Write the c:idx element. */ + _chart_write_idx(self, index); + + /* Write the c:order element. */ + _chart_write_order(self, index); + + /* Write the series name. */ + _chart_write_series_name(self, series); + + /* Write the c:spPr element. */ + _chart_write_sp_pr(self, series->line, series->fill, series->pattern); + + /* Write the c:marker element. */ + _chart_write_marker(self, series->marker); + + /* Write the char points. */ + _chart_write_points(self, series); + + /* Write the c:dLbls element. */ + _chart_write_d_lbls(self, series); + + /* Write the c:trendline element. */ + _chart_write_trendline(self, series); + + /* Write the c:errBars element. */ + _chart_write_error_bars(self, series); + + /* Write the c:xVal element. */ + _chart_write_x_val(self, series); + + /* Write the yVal element. */ + _chart_write_y_val(self, series); + + /* Write the c:smooth element. */ + _chart_write_smooth(self, series->smooth); + + lxw_xml_end_tag(self->file, "c:ser"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_orientation(lxw_chart *self, uint8_t reverse) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + if (reverse) + LXW_PUSH_ATTRIBUTES_STR("val", "maxMin"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "minMax"); + + lxw_xml_empty_tag(self->file, "c:orientation", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_max(lxw_chart *self, double max) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_DBL("val", max); + + lxw_xml_empty_tag(self->file, "c:max", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_min(lxw_chart *self, double min) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_DBL("val", min); + + lxw_xml_empty_tag(self->file, "c:min", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_log_base(lxw_chart *self, uint16_t log_base) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!log_base) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", log_base); + + lxw_xml_empty_tag(self->file, "c:logBase", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_scaling(lxw_chart *self, uint8_t reverse, + uint8_t has_min, double min, + uint8_t has_max, double max, uint16_t log_base) +{ + lxw_xml_start_tag(self->file, "c:scaling", NULL); + + /* Write the c:logBase element. */ + _chart_write_log_base(self, log_base); + + /* Write the c:orientation element. */ + _chart_write_orientation(self, reverse); + + if (has_max) { + /* Write the c:max element. */ + _chart_write_max(self, max); + } + + if (has_min) { + /* Write the c:min element. */ + _chart_write_min(self, min); + } + + lxw_xml_end_tag(self->file, "c:scaling"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_axis_pos(lxw_chart *self, uint8_t position, uint8_t reverse) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + /* Reverse the axis direction if required. */ + position ^= reverse; + + if (position == LXW_CHART_AXIS_RIGHT) + LXW_PUSH_ATTRIBUTES_STR("val", "r"); + else if (position == LXW_CHART_AXIS_LEFT) + LXW_PUSH_ATTRIBUTES_STR("val", "l"); + else if (position == LXW_CHART_AXIS_TOP) + LXW_PUSH_ATTRIBUTES_STR("val", "t"); + else if (position == LXW_CHART_AXIS_BOTTOM) + LXW_PUSH_ATTRIBUTES_STR("val", "b"); + + lxw_xml_empty_tag(self->file, "c:axPos", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_tick_label_pos(lxw_chart *self, lxw_chart_axis *axis) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (axis->label_position == LXW_CHART_AXIS_LABEL_POSITION_HIGH) + LXW_PUSH_ATTRIBUTES_STR("val", "high"); + else if (axis->label_position == LXW_CHART_AXIS_LABEL_POSITION_LOW) + LXW_PUSH_ATTRIBUTES_STR("val", "low"); + else if (axis->label_position == LXW_CHART_AXIS_LABEL_POSITION_NONE) + LXW_PUSH_ATTRIBUTES_STR("val", "none"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "nextTo"); + + lxw_xml_empty_tag(self->file, "c:tickLblPos", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_cross_axis(lxw_chart *self, uint32_t axis_id) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", axis_id); + + lxw_xml_empty_tag(self->file, "c:crossAx", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_crosses(lxw_chart *self, lxw_chart_axis *axis) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (axis->crossing_max) + LXW_PUSH_ATTRIBUTES_STR("val", "max"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "autoZero"); + + lxw_xml_empty_tag(self->file, "c:crosses", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_crosses_at(lxw_chart *self, lxw_chart_axis *axis) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_DBL("val", axis->crossing); + + lxw_xml_empty_tag(self->file, "c:crossesAt", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_auto(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:auto", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_label_align(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "ctr"); + + lxw_xml_empty_tag(self->file, "c:lblAlgn", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_tick_label_skip(lxw_chart *self, lxw_chart_axis *axis) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!axis->interval_unit) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", axis->interval_unit); + + lxw_xml_empty_tag(self->file, "c:tickLblSkip", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_tick_mark_skip(lxw_chart *self, lxw_chart_axis *axis) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!axis->interval_tick) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", axis->interval_tick); + + lxw_xml_empty_tag(self->file, "c:tickMarkSkip", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_major_unit(lxw_chart *self, lxw_chart_axis *axis) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!axis->has_major_unit) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_DBL("val", axis->major_unit); + + lxw_xml_empty_tag(self->file, "c:majorUnit", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_minor_unit(lxw_chart *self, lxw_chart_axis *axis) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!axis->has_minor_unit) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_DBL("val", axis->minor_unit); + + lxw_xml_empty_tag(self->file, "c:minorUnit", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_disp_units(lxw_chart *self, lxw_chart_axis *axis) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!axis->display_units) + return; + + LXW_INIT_ATTRIBUTES(); + + lxw_xml_start_tag(self->file, "c:dispUnits", NULL); + + if (axis->display_units == LXW_CHART_AXIS_UNITS_HUNDREDS) + LXW_PUSH_ATTRIBUTES_STR("val", "hundreds"); + else if (axis->display_units == LXW_CHART_AXIS_UNITS_THOUSANDS) + LXW_PUSH_ATTRIBUTES_STR("val", "thousands"); + else if (axis->display_units == LXW_CHART_AXIS_UNITS_TEN_THOUSANDS) + LXW_PUSH_ATTRIBUTES_STR("val", "tenThousands"); + else if (axis->display_units == LXW_CHART_AXIS_UNITS_HUNDRED_THOUSANDS) + LXW_PUSH_ATTRIBUTES_STR("val", "hundredThousands"); + else if (axis->display_units == LXW_CHART_AXIS_UNITS_MILLIONS) + LXW_PUSH_ATTRIBUTES_STR("val", "millions"); + else if (axis->display_units == LXW_CHART_AXIS_UNITS_TEN_MILLIONS) + LXW_PUSH_ATTRIBUTES_STR("val", "tenMillions"); + else if (axis->display_units == LXW_CHART_AXIS_UNITS_HUNDRED_MILLIONS) + LXW_PUSH_ATTRIBUTES_STR("val", "hundredMillions"); + else if (axis->display_units == LXW_CHART_AXIS_UNITS_BILLIONS) + LXW_PUSH_ATTRIBUTES_STR("val", "billions"); + else if (axis->display_units == LXW_CHART_AXIS_UNITS_TRILLIONS) + LXW_PUSH_ATTRIBUTES_STR("val", "trillions"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "hundreds"); + + lxw_xml_empty_tag(self->file, "c:builtInUnit", &attributes); + + if (axis->display_units_visible) { + lxw_xml_start_tag(self->file, "c:dispUnitsLbl", NULL); + lxw_xml_empty_tag(self->file, "c:layout", NULL); + lxw_xml_end_tag(self->file, "c:dispUnitsLbl"); + } + + lxw_xml_end_tag(self->file, "c:dispUnits"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_label_offset(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "100"); + + lxw_xml_empty_tag(self->file, "c:lblOffset", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_major_gridlines(lxw_chart *self, lxw_chart_axis *axis) +{ + if (!axis->major_gridlines.visible) + return; + + if (axis->major_gridlines.line) { + lxw_xml_start_tag(self->file, "c:majorGridlines", NULL); + + /* Write the c:spPr element for the axis line. */ + _chart_write_sp_pr(self, axis->major_gridlines.line, NULL, NULL); + + lxw_xml_end_tag(self->file, "c:majorGridlines"); + } + else { + lxw_xml_empty_tag(self->file, "c:majorGridlines", NULL); + } +} + +/* + * Write the element. + */ +STATIC void +_chart_write_minor_gridlines(lxw_chart *self, lxw_chart_axis *axis) +{ + if (!axis->minor_gridlines.visible) + return; + + if (axis->minor_gridlines.line) { + lxw_xml_start_tag(self->file, "c:minorGridlines", NULL); + + /* Write the c:spPr element for the axis line. */ + _chart_write_sp_pr(self, axis->minor_gridlines.line, NULL, NULL); + + lxw_xml_end_tag(self->file, "c:minorGridlines"); + + } + else { + lxw_xml_empty_tag(self->file, "c:minorGridlines", NULL); + } +} + +/* + * Write the element. Note: It is assumed that if a user + * defined number format is supplied (i.e., non-default) then the sourceLinked + * attribute is 0. The user can override this if required. + */ +STATIC void +_chart_write_number_format(lxw_chart *self, lxw_chart_axis *axis) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char *num_format; + uint8_t source_linked = 1; + + /* Set the number format to the axis default if not set. */ + if (axis->num_format) + num_format = axis->num_format; + else + num_format = axis->default_num_format; + + /* Check if a user defined number format has been set. */ + if (strcmp(num_format, axis->default_num_format)) + source_linked = 0; + + /* Allow override of sourceLinked. */ + if (axis->source_linked) + source_linked = 1; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("formatCode", num_format); + LXW_PUSH_ATTRIBUTES_INT("sourceLinked", source_linked); + + lxw_xml_empty_tag(self->file, "c:numFmt", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. Special case handler for category axes which + * don't always have a number format. + */ +STATIC void +_chart_write_cat_number_format(lxw_chart *self, lxw_chart_axis *axis) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char *num_format; + uint8_t source_linked = 1; + uint8_t default_format = LXW_TRUE; + + /* Set the number format to the axis default if not set. */ + if (axis->num_format) + num_format = axis->num_format; + else + num_format = axis->default_num_format; + + /* Check if a user defined number format has been set. */ + if (strcmp(num_format, axis->default_num_format)) { + source_linked = 0; + default_format = LXW_FALSE; + } + + /* Allow override of sourceLinked. */ + if (axis->source_linked) + source_linked = 1; + + /* Skip if cat doesn't have a num format (unless it is non-default). */ + if (!self->cat_has_num_fmt && default_format) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("formatCode", num_format); + LXW_PUSH_ATTRIBUTES_INT("sourceLinked", source_linked); + + lxw_xml_empty_tag(self->file, "c:numFmt", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_cross_between(lxw_chart *self, uint8_t position) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!position) + position = self->default_cross_between; + + LXW_INIT_ATTRIBUTES(); + + if (position == LXW_CHART_AXIS_POSITION_ON_TICK) + LXW_PUSH_ATTRIBUTES_STR("val", "midCat"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "between"); + + lxw_xml_empty_tag(self->file, "c:crossBetween", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_overlay(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:overlay", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_legend_pos(lxw_chart *self, char *position) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_STR("val", position); + + lxw_xml_empty_tag(self->file, "c:legendPos", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_legend_entry(lxw_chart *self, uint16_t index) +{ + lxw_xml_start_tag(self->file, "c:legendEntry", NULL); + + /* Write the c:idx element. */ + _chart_write_idx(self, self->delete_series[index]); + + /* Write the c:delete element. */ + _chart_write_delete(self); + + lxw_xml_end_tag(self->file, "c:legendEntry"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_legend(lxw_chart *self) +{ + uint8_t has_overlay = LXW_FALSE; + uint16_t index; + + if (self->legend.position == LXW_CHART_LEGEND_NONE) + return; + + lxw_xml_start_tag(self->file, "c:legend", NULL); + + /* Write the c:legendPos element. */ + switch (self->legend.position) { + case LXW_CHART_LEGEND_LEFT: + _chart_write_legend_pos(self, "l"); + break; + case LXW_CHART_LEGEND_TOP: + _chart_write_legend_pos(self, "t"); + break; + case LXW_CHART_LEGEND_BOTTOM: + _chart_write_legend_pos(self, "b"); + break; + case LXW_CHART_LEGEND_OVERLAY_RIGHT: + _chart_write_legend_pos(self, "r"); + has_overlay = LXW_TRUE; + break; + case LXW_CHART_LEGEND_OVERLAY_LEFT: + _chart_write_legend_pos(self, "l"); + has_overlay = LXW_TRUE; + break; + default: + _chart_write_legend_pos(self, "r"); + } + + /* Remove series labels from the legend. */ + for (index = 0; index < self->delete_series_count; index++) { + /* Write the c:legendEntry element. */ + _chart_write_legend_entry(self, index); + } + + /* Write the c:layout element. */ + _chart_write_layout(self); + + if (self->chart_group == LXW_CHART_PIE + || self->chart_group == LXW_CHART_DOUGHNUT) { + /* Write the c:overlay element. */ + if (has_overlay) + _chart_write_overlay(self); + + /* Write the c:txPr element for Pie/Doughnut charts. */ + _chart_write_tx_pr_pie(self, LXW_FALSE, self->legend.font); + } + else { + /* Write the c:txPr element for all other charts. */ + if (self->legend.font) + _chart_write_tx_pr(self, LXW_FALSE, self->legend.font); + + /* Write the c:overlay element. */ + if (has_overlay) + _chart_write_overlay(self); + } + + lxw_xml_end_tag(self->file, "c:legend"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_plot_vis_only(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (self->show_hidden_data) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:plotVisOnly", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_header_footer(lxw_chart *self) +{ + lxw_xml_empty_tag(self->file, "c:headerFooter", NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_page_margins(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_STR("b", "0.75"); + LXW_PUSH_ATTRIBUTES_STR("l", "0.7"); + LXW_PUSH_ATTRIBUTES_STR("r", "0.7"); + LXW_PUSH_ATTRIBUTES_STR("t", "0.75"); + LXW_PUSH_ATTRIBUTES_STR("header", "0.3"); + LXW_PUSH_ATTRIBUTES_STR("footer", "0.3"); + + lxw_xml_empty_tag(self->file, "c:pageMargins", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_page_setup(lxw_chart *self) +{ + lxw_xml_empty_tag(self->file, "c:pageSetup", NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_print_settings(lxw_chart *self) +{ + lxw_xml_start_tag(self->file, "c:printSettings", NULL); + + /* Write the c:headerFooter element. */ + _chart_write_header_footer(self); + + /* Write the c:pageMargins element. */ + _chart_write_page_margins(self); + + /* Write the c:pageSetup element. */ + _chart_write_page_setup(self); + + lxw_xml_end_tag(self->file, "c:printSettings"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_overlap(lxw_chart *self, int8_t overlap) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!overlap) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", overlap); + + lxw_xml_empty_tag(self->file, "c:overlap", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_gap_width(lxw_chart *self, uint16_t gap) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (gap == LXW_CHART_DEFAULT_GAP) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", gap); + + lxw_xml_empty_tag(self->file, "c:gapWidth", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_disp_blanks_as(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (self->show_blanks_as != LXW_CHART_BLANKS_AS_ZERO + && self->show_blanks_as != LXW_CHART_BLANKS_AS_CONNECTED) + return; + + LXW_INIT_ATTRIBUTES(); + + if (self->show_blanks_as == LXW_CHART_BLANKS_AS_ZERO) + LXW_PUSH_ATTRIBUTES_STR("val", "zero"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "span"); + + lxw_xml_empty_tag(self->file, "c:dispBlanksAs", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_show_horz_border(lxw_chart *self, uint8_t value) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!value) + return; + + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:showHorzBorder", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_show_vert_border(lxw_chart *self, uint8_t value) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!value) + return; + + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:showVertBorder", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_show_outline(lxw_chart *self, uint8_t value) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!value) + return; + + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:showOutline", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_show_keys(lxw_chart *self, uint8_t value) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!value) + return; + + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:showKeys", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_d_table(lxw_chart *self) +{ + if (!self->has_table) + return; + + lxw_xml_start_tag(self->file, "c:dTable", NULL); + + /* Write the c:showHorzBorder element. */ + _chart_write_show_horz_border(self, self->has_table_horizontal); + + /* Write the c:showVertBorder element. */ + _chart_write_show_vert_border(self, self->has_table_vertical); + + /* Write the c:showOutline element. */ + _chart_write_show_outline(self, self->has_table_outline); + + /* Write the c:showKeys element. */ + _chart_write_show_keys(self, self->has_table_legend_keys); + + /* Write the c:txPr element. */ + if (self->table_font) + _chart_write_tx_pr(self, LXW_FALSE, self->table_font); + + lxw_xml_end_tag(self->file, "c:dTable"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_up_bars(lxw_chart *self, lxw_chart_line *line, + lxw_chart_fill *fill) +{ + if (line || fill) { + lxw_xml_start_tag(self->file, "c:upBars", NULL); + + /* Write the c:spPr element. */ + _chart_write_sp_pr(self, line, fill, NULL); + + lxw_xml_end_tag(self->file, "c:upBars"); + } + else { + lxw_xml_empty_tag(self->file, "c:upBars", NULL); + } +} + +/* + * Write the element. + */ +STATIC void +_chart_write_down_bars(lxw_chart *self, lxw_chart_line *line, + lxw_chart_fill *fill) +{ + if (line || fill) { + lxw_xml_start_tag(self->file, "c:downBars", NULL); + + /* Write the c:spPr element. */ + _chart_write_sp_pr(self, line, fill, NULL); + + lxw_xml_end_tag(self->file, "c:downBars"); + } + else { + lxw_xml_empty_tag(self->file, "c:downBars", NULL); + } +} + +/* + * Write the element. + */ +STATIC void +_chart_write_up_down_bars(lxw_chart *self) +{ + if (!self->has_up_down_bars) + return; + + lxw_xml_start_tag(self->file, "c:upDownBars", NULL); + + /* Write the c:gapWidth element. */ + _chart_write_gap_width(self, 150); + + /* Write the c:upBars element. */ + _chart_write_up_bars(self, self->up_bar_line, self->up_bar_fill); + + /* Write the c:downBars element. */ + _chart_write_down_bars(self, self->down_bar_line, self->down_bar_fill); + + lxw_xml_end_tag(self->file, "c:upDownBars"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_drop_lines(lxw_chart *self) +{ + if (!self->has_drop_lines) + return; + + if (self->drop_lines_line) { + lxw_xml_start_tag(self->file, "c:dropLines", NULL); + + _chart_write_sp_pr(self, self->drop_lines_line, NULL, NULL); + + lxw_xml_end_tag(self->file, "c:dropLines"); + } + else { + lxw_xml_empty_tag(self->file, "c:dropLines", NULL); + } +} + +/* + * Write the element. + */ +STATIC void +_chart_write_hi_low_lines(lxw_chart *self) +{ + if (!self->has_high_low_lines) + return; + + if (self->high_low_lines_line) { + lxw_xml_start_tag(self->file, "c:hiLowLines", NULL); + + _chart_write_sp_pr(self, self->high_low_lines_line, NULL, NULL); + + lxw_xml_end_tag(self->file, "c:hiLowLines"); + } + else { + lxw_xml_empty_tag(self->file, "c:hiLowLines", NULL); + } +} + +/* + * Write the element. + */ +STATIC void +_chart_write_title(lxw_chart *self, lxw_chart_title *title) +{ + if (title->name) { + /* Write the c:title element. */ + _chart_write_title_rich(self, title); + } + else if (title->range->formula) { + /* Write the c:title element. */ + _chart_write_title_formula(self, title); + } +} + +/* + * Write the element. + */ +STATIC void +_chart_write_chart_title(lxw_chart *self) +{ + if (self->title.off) { + /* Write the c:autoTitleDeleted element. */ + _chart_write_auto_title_deleted(self); + } + else { + /* Write the c:title element. */ + _chart_write_title(self, &self->title); + } +} + +/* + * Write the element. Usually the X axis. + */ +STATIC void +_chart_write_cat_axis(lxw_chart *self) +{ + lxw_xml_start_tag(self->file, "c:catAx", NULL); + + _chart_write_axis_id(self, self->axis_id_1); + + /* Write the c:scaling element. Note we can't set max, min, or log base + * for a Category axis in Excel.*/ + _chart_write_scaling(self, + self->x_axis->reverse, + LXW_FALSE, 0.0, LXW_FALSE, 0.0, 0); + + /* Write the c:delete element to hide axis. */ + if (self->x_axis->hidden) + _chart_write_delete(self); + + /* Write the c:axPos element. */ + _chart_write_axis_pos(self, self->x_axis->axis_position, + self->y_axis->reverse); + + /* Write the c:majorGridlines element. */ + _chart_write_major_gridlines(self, self->x_axis); + + /* Write the c:minorGridlines element. */ + _chart_write_minor_gridlines(self, self->x_axis); + + /* Write the axis title elements. */ + self->x_axis->title.is_horizontal = self->has_horiz_cat_axis; + _chart_write_title(self, &self->x_axis->title); + + /* Write the c:numFmt element. */ + _chart_write_cat_number_format(self, self->x_axis); + + /* Write the c:majorTickMark element. */ + _chart_write_major_tick_mark(self, self->x_axis); + + /* Write the c:minorTickMark element. */ + _chart_write_minor_tick_mark(self, self->x_axis); + + /* Write the c:tickLblPos element. */ + _chart_write_tick_label_pos(self, self->x_axis); + + /* Write the c:spPr element for the axis line. */ + _chart_write_sp_pr(self, self->x_axis->line, self->x_axis->fill, + self->x_axis->pattern); + + /* Write the axis font elements. */ + _chart_write_axis_font(self, self->x_axis->num_font); + + /* Write the c:crossAx element. */ + _chart_write_cross_axis(self, self->axis_id_2); + + /* Write the c:crosses element. */ + if (!self->y_axis->has_crossing || self->y_axis->crossing_max) + _chart_write_crosses(self, self->y_axis); + else + _chart_write_crosses_at(self, self->y_axis); + + /* Write the c:auto element. */ + _chart_write_auto(self); + + /* Write the c:lblAlgn element. */ + _chart_write_label_align(self); + + /* Write the c:lblOffset element. */ + _chart_write_label_offset(self); + + /* Write the c:tickLblSkip element. */ + _chart_write_tick_label_skip(self, self->x_axis); + + /* Write the c:tickMarkSkip element. */ + _chart_write_tick_mark_skip(self, self->x_axis); + + lxw_xml_end_tag(self->file, "c:catAx"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_val_axis(lxw_chart *self) +{ + lxw_xml_start_tag(self->file, "c:valAx", NULL); + + _chart_write_axis_id(self, self->axis_id_2); + + /* Write the c:scaling element. */ + _chart_write_scaling(self, + self->y_axis->reverse, + self->y_axis->has_min, self->y_axis->min, + self->y_axis->has_max, self->y_axis->max, + self->y_axis->log_base); + + /* Write the c:delete element to hide axis. */ + if (self->y_axis->hidden) + _chart_write_delete(self); + + /* Write the c:axPos element. */ + _chart_write_axis_pos(self, self->y_axis->axis_position, + self->x_axis->reverse); + + /* Write the c:majorGridlines element. */ + _chart_write_major_gridlines(self, self->y_axis); + + /* Write the c:minorGridlines element. */ + _chart_write_minor_gridlines(self, self->y_axis); + + /* Write the axis title elements. */ + self->y_axis->title.is_horizontal = self->has_horiz_val_axis; + _chart_write_title(self, &self->y_axis->title); + + /* Write the c:numFmt element. */ + _chart_write_number_format(self, self->y_axis); + + /* Write the c:majorTickMark element. */ + _chart_write_major_tick_mark(self, self->y_axis); + + /* Write the c:minorTickMark element. */ + _chart_write_minor_tick_mark(self, self->y_axis); + + /* Write the c:tickLblPos element. */ + _chart_write_tick_label_pos(self, self->y_axis); + + /* Write the c:spPr element for the axis line. */ + _chart_write_sp_pr(self, self->y_axis->line, self->y_axis->fill, + self->y_axis->pattern); + + /* Write the axis font elements. */ + _chart_write_axis_font(self, self->y_axis->num_font); + + /* Write the c:crossAx element. */ + _chart_write_cross_axis(self, self->axis_id_1); + + /* Write the c:crosses element. */ + if (!self->x_axis->has_crossing || self->x_axis->crossing_max) + _chart_write_crosses(self, self->x_axis); + else + _chart_write_crosses_at(self, self->x_axis); + + /* Write the c:crossBetween element. */ + _chart_write_cross_between(self, self->x_axis->position_axis); + + /* Write the c:majorUnit element. */ + _chart_write_major_unit(self, self->y_axis); + + /* Write the c:minorUnit element. */ + _chart_write_minor_unit(self, self->y_axis); + + /* Write the c:dispUnits element. */ + _chart_write_disp_units(self, self->y_axis); + + lxw_xml_end_tag(self->file, "c:valAx"); +} + +/* + * Write the element. This is for the second valAx in scatter plots. + */ +STATIC void +_chart_write_cat_val_axis(lxw_chart *self) +{ + lxw_xml_start_tag(self->file, "c:valAx", NULL); + + _chart_write_axis_id(self, self->axis_id_1); + + /* Write the c:scaling element. */ + _chart_write_scaling(self, + self->x_axis->reverse, + self->x_axis->has_min, self->x_axis->min, + self->x_axis->has_max, self->x_axis->max, + self->x_axis->log_base); + + /* Write the c:delete element to hide axis. */ + if (self->x_axis->hidden) + _chart_write_delete(self); + + /* Write the c:axPos element. */ + _chart_write_axis_pos(self, self->x_axis->axis_position, + self->y_axis->reverse); + + /* Write the c:majorGridlines element. */ + _chart_write_major_gridlines(self, self->x_axis); + + /* Write the c:minorGridlines element. */ + _chart_write_minor_gridlines(self, self->x_axis); + + /* Write the axis title elements. */ + self->x_axis->title.is_horizontal = self->has_horiz_val_axis; + _chart_write_title(self, &self->x_axis->title); + + /* Write the c:numFmt element. */ + _chart_write_number_format(self, self->x_axis); + + /* Write the c:majorTickMark element. */ + _chart_write_major_tick_mark(self, self->x_axis); + + /* Write the c:minorTickMark element. */ + _chart_write_minor_tick_mark(self, self->x_axis); + + /* Write the c:tickLblPos element. */ + _chart_write_tick_label_pos(self, self->x_axis); + + /* Write the c:spPr element for the axis line. */ + _chart_write_sp_pr(self, self->x_axis->line, self->x_axis->fill, + self->x_axis->pattern); + + /* Write the axis font elements. */ + _chart_write_axis_font(self, self->x_axis->num_font); + + /* Write the c:crossAx element. */ + _chart_write_cross_axis(self, self->axis_id_2); + + /* Write the c:crosses element. */ + if (!self->y_axis->has_crossing || self->y_axis->crossing_max) + _chart_write_crosses(self, self->y_axis); + else + _chart_write_crosses_at(self, self->y_axis); + + /* Write the c:crossBetween element. */ + _chart_write_cross_between(self, self->y_axis->position_axis); + + /* Write the c:majorUnit element. */ + _chart_write_major_unit(self, self->x_axis); + + /* Write the c:minorUnit element. */ + _chart_write_minor_unit(self, self->x_axis); + + /* Write the c:dispUnits element. */ + _chart_write_disp_units(self, self->x_axis); + + lxw_xml_end_tag(self->file, "c:valAx"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_bar_dir(lxw_chart *self, char *type) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", type); + + lxw_xml_empty_tag(self->file, "c:barDir", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write a area chart. + */ +STATIC void +_chart_write_area_chart(lxw_chart *self) +{ + lxw_chart_series *series; + + lxw_xml_start_tag(self->file, "c:areaChart", NULL); + + /* Write the c:grouping element. */ + _chart_write_grouping(self, self->grouping); + + STAILQ_FOREACH(series, self->series_list, list_pointers) { + /* Write the c:ser element. */ + _chart_write_ser(self, series); + } + + /* Write the c:dropLines element. */ + _chart_write_drop_lines(self); + + /* Write the c:axId elements. */ + _chart_write_axis_ids(self); + + lxw_xml_end_tag(self->file, "c:areaChart"); +} + +/* + * Write a bar chart. + */ +STATIC void +_chart_write_bar_chart(lxw_chart *self) +{ + lxw_chart_series *series; + + lxw_xml_start_tag(self->file, "c:barChart", NULL); + + /* Write the c:barDir element. */ + _chart_write_bar_dir(self, "bar"); + + /* Write the c:grouping element. */ + _chart_write_grouping(self, self->grouping); + + STAILQ_FOREACH(series, self->series_list, list_pointers) { + /* Write the c:ser element. */ + _chart_write_ser(self, series); + } + + /* Write the c:gapWidth element. */ + _chart_write_gap_width(self, self->gap_y1); + + /* Write the c:overlap element. */ + _chart_write_overlap(self, self->overlap_y1); + + /* Write the c:axId elements. */ + _chart_write_axis_ids(self); + + lxw_xml_end_tag(self->file, "c:barChart"); +} + +/* + * Write a column chart. + */ +STATIC void +_chart_write_column_chart(lxw_chart *self) +{ + lxw_chart_series *series; + + lxw_xml_start_tag(self->file, "c:barChart", NULL); + + /* Write the c:barDir element. */ + _chart_write_bar_dir(self, "col"); + + /* Write the c:grouping element. */ + _chart_write_grouping(self, self->grouping); + + STAILQ_FOREACH(series, self->series_list, list_pointers) { + /* Write the c:ser element. */ + _chart_write_ser(self, series); + } + + /* Write the c:gapWidth element. */ + _chart_write_gap_width(self, self->gap_y1); + + /* Write the c:overlap element. */ + _chart_write_overlap(self, self->overlap_y1); + + /* Write the c:axId elements. */ + _chart_write_axis_ids(self); + + lxw_xml_end_tag(self->file, "c:barChart"); +} + +/* + * Write a doughnut chart. + */ +STATIC void +_chart_write_doughnut_chart(lxw_chart *self) +{ + lxw_chart_series *series; + + lxw_xml_start_tag(self->file, "c:doughnutChart", NULL); + + /* Write the c:varyColors element. */ + _chart_write_vary_colors(self); + + STAILQ_FOREACH(series, self->series_list, list_pointers) { + /* Write the c:ser element. */ + _chart_write_ser(self, series); + } + + /* Write the c:firstSliceAng element. */ + _chart_write_first_slice_ang(self); + + /* Write the c:holeSize element. */ + _chart_write_hole_size(self); + + lxw_xml_end_tag(self->file, "c:doughnutChart"); +} + +/* + * Write a line chart. + */ +STATIC void +_chart_write_line_chart(lxw_chart *self) +{ + lxw_chart_series *series; + + lxw_xml_start_tag(self->file, "c:lineChart", NULL); + + /* Write the c:grouping element. */ + _chart_write_grouping(self, self->grouping); + + STAILQ_FOREACH(series, self->series_list, list_pointers) { + /* Write the c:ser element. */ + _chart_write_ser(self, series); + } + + /* Write the c:dropLines element. */ + _chart_write_drop_lines(self); + + /* Write the c:hiLowLines element. */ + _chart_write_hi_low_lines(self); + + /* Write the c:upDownBars element. */ + _chart_write_up_down_bars(self); + + /* Write the c:marker element. */ + _chart_write_marker_value(self); + + /* Write the c:axId elements. */ + _chart_write_axis_ids(self); + + lxw_xml_end_tag(self->file, "c:lineChart"); +} + +/* + * Write a pie chart. + */ +STATIC void +_chart_write_pie_chart(lxw_chart *self) +{ + lxw_chart_series *series; + + lxw_xml_start_tag(self->file, "c:pieChart", NULL); + + /* Write the c:varyColors element. */ + _chart_write_vary_colors(self); + + STAILQ_FOREACH(series, self->series_list, list_pointers) { + /* Write the c:ser element. */ + _chart_write_ser(self, series); + } + + /* Write the c:firstSliceAng element. */ + _chart_write_first_slice_ang(self); + + lxw_xml_end_tag(self->file, "c:pieChart"); +} + +/* + * Write a scatter chart. + */ +STATIC void +_chart_write_scatter_chart(lxw_chart *self) +{ + lxw_chart_series *series; + + lxw_xml_start_tag(self->file, "c:scatterChart", NULL); + + /* Write the c:scatterStyle element. */ + _chart_write_scatter_style(self); + + STAILQ_FOREACH(series, self->series_list, list_pointers) { + + /* Add default scatter chart formatting to the series data unless + * it has already been specified by the user.*/ + if (self->type == LXW_CHART_SCATTER) { + if (!series->line) { + lxw_chart_line line = { + 0x000000, + LXW_TRUE, + 2.25, + LXW_CHART_LINE_DASH_SOLID, + 0, + LXW_FALSE + }; + series->line = _chart_convert_line_args(&line); + } + } + + /* Write the c:ser element. */ + _chart_write_xval_ser(self, series); + } + + /* Write the c:axId elements. */ + _chart_write_axis_ids(self); + + lxw_xml_end_tag(self->file, "c:scatterChart"); +} + +/* + * Write a radar chart. + */ +STATIC void +_chart_write_radar_chart(lxw_chart *self) +{ + lxw_chart_series *series; + + lxw_xml_start_tag(self->file, "c:radarChart", NULL); + + /* Write the c:radarStyle element. */ + _chart_write_radar_style(self); + + STAILQ_FOREACH(series, self->series_list, list_pointers) { + /* Write the c:ser element. */ + _chart_write_ser(self, series); + } + + /* Write the c:axId elements. */ + _chart_write_axis_ids(self); + + lxw_xml_end_tag(self->file, "c:radarChart"); +} + +/* + * Reverse the opposite axis position if crossing position is "max". + */ +STATIC void +_chart_adjust_max_crossing(lxw_chart *self) +{ + if (self->x_axis->crossing_max) + self->y_axis->axis_position ^= 1; + + if (self->y_axis->crossing_max) + self->x_axis->axis_position ^= 1; +} + +/* + * Write the element. + */ +STATIC void +_chart_write_scatter_plot_area(lxw_chart *self) +{ + lxw_xml_start_tag(self->file, "c:plotArea", NULL); + + /* Write the c:layout element. */ + _chart_write_layout(self); + + /* Write subclass chart type elements for primary and secondary axes. */ + self->write_chart_type(self); + + /* Reverse the opposite axis position if crossing position is "max". */ + _chart_adjust_max_crossing(self); + + /* Write the c:catAx element. */ + _chart_write_cat_val_axis(self); + + self->has_horiz_val_axis = LXW_TRUE; + + /* Write the c:valAx element. */ + _chart_write_val_axis(self); + + /* Write the c:spPr element for the plotarea formatting. */ + _chart_write_sp_pr(self, self->plotarea_line, self->plotarea_fill, + self->plotarea_pattern); + + lxw_xml_end_tag(self->file, "c:plotArea"); +} + +/* + * Write the element. Special handling for pie/doughnut. + */ +STATIC void +_chart_write_pie_plot_area(lxw_chart *self) +{ + lxw_xml_start_tag(self->file, "c:plotArea", NULL); + + /* Write the c:layout element. */ + _chart_write_layout(self); + + /* Write subclass chart type elements for primary and secondary axes. */ + self->write_chart_type(self); + + lxw_xml_end_tag(self->file, "c:plotArea"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_plot_area(lxw_chart *self) +{ + lxw_xml_start_tag(self->file, "c:plotArea", NULL); + + /* Write the c:layout element. */ + _chart_write_layout(self); + + /* Write subclass chart type elements for primary and secondary axes. */ + self->write_chart_type(self); + + /* Reverse the opposite axis position if crossing position is "max". */ + _chart_adjust_max_crossing(self); + + /* Write the c:catAx element. */ + _chart_write_cat_axis(self); + + /* Write the c:valAx element. */ + _chart_write_val_axis(self); + + /* Write the c:dTable element. */ + _chart_write_d_table(self); + + /* Write the c:spPr element for the plotarea formatting. */ + _chart_write_sp_pr(self, self->plotarea_line, self->plotarea_fill, + self->plotarea_pattern); + + lxw_xml_end_tag(self->file, "c:plotArea"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_chart(lxw_chart *self) +{ + lxw_xml_start_tag(self->file, "c:chart", NULL); + + /* Write the c:title element. */ + _chart_write_chart_title(self); + + /* Write the c:plotArea element. */ + self->write_plot_area(self); + + /* Write the c:legend element. */ + _chart_write_legend(self); + + /* Write the c:plotVisOnly element. */ + _chart_write_plot_vis_only(self); + + /* Write the c:dispBlanksAs element. */ + _chart_write_disp_blanks_as(self); + + lxw_xml_end_tag(self->file, "c:chart"); +} + +/* + * Initialize a area chart. + */ +STATIC void +_chart_initialize_area_chart(lxw_chart *self, uint8_t type) +{ + self->chart_group = LXW_CHART_AREA; + self->grouping = LXW_GROUPING_STANDARD; + self->default_cross_between = LXW_CHART_AXIS_POSITION_ON_TICK; + self->x_axis->is_category = LXW_TRUE; + self->default_label_position = LXW_CHART_LABEL_POSITION_CENTER; + + if (type == LXW_CHART_AREA_STACKED) { + self->grouping = LXW_GROUPING_STACKED; + self->subtype = LXW_CHART_SUBTYPE_STACKED; + } + + if (type == LXW_CHART_AREA_STACKED_PERCENT) { + self->grouping = LXW_GROUPING_PERCENTSTACKED; + _chart_axis_set_default_num_format(self->y_axis, "0%"); + self->subtype = LXW_CHART_SUBTYPE_STACKED; + } + + /* Initialize the function pointers for this chart type. */ + self->write_chart_type = _chart_write_area_chart; + self->write_plot_area = _chart_write_plot_area; +} + +/* + * Swap/reverse the bar chart axes prior to writing. It is the only chart + * with the category axis in the vertical direction. + */ +STATIC void +_chart_swap_bar_axes(lxw_chart *self) +{ + lxw_chart_axis *tmp = self->x_axis; + self->x_axis = self->y_axis; + self->y_axis = tmp; +} + +/* + * Initialize a bar chart. + */ +STATIC void +_chart_initialize_bar_chart(lxw_chart *self, uint8_t type) +{ + /* Note: Bar chart category/value axis are reversed in comparison to + * other charts. Some of the defaults reflect this. */ + self->chart_group = LXW_CHART_BAR; + self->x_axis->major_gridlines.visible = LXW_TRUE; + self->y_axis->major_gridlines.visible = LXW_FALSE; + self->y_axis->is_category = LXW_TRUE; + self->x_axis->is_value = LXW_TRUE; + self->has_horiz_cat_axis = LXW_TRUE; + self->has_horiz_val_axis = LXW_FALSE; + self->default_label_position = LXW_CHART_LABEL_POSITION_OUTSIDE_END; + + if (type == LXW_CHART_BAR_STACKED) { + self->grouping = LXW_GROUPING_STACKED; + self->has_overlap = LXW_TRUE; + self->subtype = LXW_CHART_SUBTYPE_STACKED; + self->overlap_y1 = 100; + + } + + if (type == LXW_CHART_BAR_STACKED_PERCENT) { + self->grouping = LXW_GROUPING_PERCENTSTACKED; + _chart_axis_set_default_num_format(self->x_axis, "0%"); + self->has_overlap = LXW_TRUE; + self->subtype = LXW_CHART_SUBTYPE_STACKED; + self->overlap_y1 = 100; + } + + /* Initialize the function pointers for this chart type. */ + self->write_chart_type = _chart_write_bar_chart; + self->write_plot_area = _chart_write_plot_area; +} + +/* + * Initialize a column chart. + */ +STATIC void +_chart_initialize_column_chart(lxw_chart *self, uint8_t type) +{ + self->chart_group = LXW_CHART_COLUMN; + self->has_horiz_val_axis = LXW_FALSE; + self->x_axis->is_category = LXW_TRUE; + self->y_axis->is_value = LXW_TRUE; + self->default_label_position = LXW_CHART_LABEL_POSITION_OUTSIDE_END; + + if (type == LXW_CHART_COLUMN_STACKED) { + self->grouping = LXW_GROUPING_STACKED; + self->has_overlap = LXW_TRUE; + self->subtype = LXW_CHART_SUBTYPE_STACKED; + self->overlap_y1 = 100; + } + + if (type == LXW_CHART_COLUMN_STACKED_PERCENT) { + self->grouping = LXW_GROUPING_PERCENTSTACKED; + _chart_axis_set_default_num_format(self->y_axis, "0%"); + self->has_overlap = LXW_TRUE; + self->subtype = LXW_CHART_SUBTYPE_STACKED; + self->overlap_y1 = 100; + } + + /* Initialize the function pointers for this chart type. */ + self->write_chart_type = _chart_write_column_chart; + self->write_plot_area = _chart_write_plot_area; +} + +/* + * Initialize a doughnut chart. + */ +STATIC void +_chart_initialize_doughnut_chart(lxw_chart *self) +{ + /* Initialize the function pointers for this chart type. */ + self->chart_group = LXW_CHART_DOUGHNUT; + self->write_chart_type = _chart_write_doughnut_chart; + self->write_plot_area = _chart_write_pie_plot_area; + self->default_label_position = LXW_CHART_LABEL_POSITION_BEST_FIT; +} + +/* + * Initialize a line chart. + */ +STATIC void +_chart_initialize_line_chart(lxw_chart *self) +{ + self->chart_group = LXW_CHART_LINE; + _chart_set_default_marker_type(self, LXW_CHART_MARKER_NONE); + self->grouping = LXW_GROUPING_STANDARD; + self->x_axis->is_category = LXW_TRUE; + self->y_axis->is_value = LXW_TRUE; + self->default_label_position = LXW_CHART_LABEL_POSITION_RIGHT; + + /* Initialize the function pointers for this chart type. */ + self->write_chart_type = _chart_write_line_chart; + self->write_plot_area = _chart_write_plot_area; +} + +/* + * Initialize a pie chart. + */ +STATIC void +_chart_initialize_pie_chart(lxw_chart *self) +{ + /* Initialize the function pointers for this chart type. */ + self->chart_group = LXW_CHART_PIE; + self->write_chart_type = _chart_write_pie_chart; + self->write_plot_area = _chart_write_pie_plot_area; + self->default_label_position = LXW_CHART_LABEL_POSITION_BEST_FIT; +} + +/* + * Initialize a scatter chart. + */ +STATIC void +_chart_initialize_scatter_chart(lxw_chart *self) +{ + self->chart_group = LXW_CHART_SCATTER; + self->has_horiz_val_axis = LXW_FALSE; + self->default_cross_between = LXW_CHART_AXIS_POSITION_ON_TICK; + self->x_axis->is_value = LXW_TRUE; + self->y_axis->is_value = LXW_TRUE; + self->default_label_position = LXW_CHART_LABEL_POSITION_RIGHT; + + if (self->type == LXW_CHART_SCATTER_STRAIGHT + || self->type == LXW_CHART_SCATTER_SMOOTH) { + + _chart_set_default_marker_type(self, LXW_CHART_MARKER_NONE); + } + + /* Initialize the function pointers for this chart type. */ + self->write_chart_type = _chart_write_scatter_chart; + self->write_plot_area = _chart_write_scatter_plot_area; +} + +/* + * Initialize a radar chart. + */ +STATIC void +_chart_initialize_radar_chart(lxw_chart *self, uint8_t type) +{ + if (type == LXW_CHART_RADAR) + _chart_set_default_marker_type(self, LXW_CHART_MARKER_NONE); + + self->chart_group = LXW_CHART_RADAR; + self->x_axis->major_gridlines.visible = LXW_TRUE; + self->x_axis->is_category = LXW_TRUE; + self->y_axis->is_value = LXW_TRUE; + self->y_axis->major_tick_mark = LXW_CHART_AXIS_TICK_MARK_CROSSING; + self->default_label_position = LXW_CHART_LABEL_POSITION_CENTER; + + /* Initialize the function pointers for this chart type. */ + self->write_chart_type = _chart_write_radar_chart; + self->write_plot_area = _chart_write_plot_area; +} + +/* + * Initialize the chart specific properties. + */ +STATIC void +_chart_initialize(lxw_chart *self, uint8_t type) +{ + switch (type) { + + case LXW_CHART_AREA: + case LXW_CHART_AREA_STACKED: + case LXW_CHART_AREA_STACKED_PERCENT: + _chart_initialize_area_chart(self, type); + break; + + case LXW_CHART_BAR: + case LXW_CHART_BAR_STACKED: + case LXW_CHART_BAR_STACKED_PERCENT: + _chart_initialize_bar_chart(self, type); + break; + + case LXW_CHART_COLUMN: + case LXW_CHART_COLUMN_STACKED: + case LXW_CHART_COLUMN_STACKED_PERCENT: + _chart_initialize_column_chart(self, type); + break; + + case LXW_CHART_DOUGHNUT: + _chart_initialize_doughnut_chart(self); + break; + + case LXW_CHART_LINE: + _chart_initialize_line_chart(self); + break; + + case LXW_CHART_PIE: + _chart_initialize_pie_chart(self); + break; + + case LXW_CHART_SCATTER: + case LXW_CHART_SCATTER_STRAIGHT: + case LXW_CHART_SCATTER_STRAIGHT_WITH_MARKERS: + case LXW_CHART_SCATTER_SMOOTH: + case LXW_CHART_SCATTER_SMOOTH_WITH_MARKERS: + _chart_initialize_scatter_chart(self); + break; + + case LXW_CHART_RADAR: + case LXW_CHART_RADAR_WITH_MARKERS: + case LXW_CHART_RADAR_FILLED: + _chart_initialize_radar_chart(self, type); + break; + + default: + LXW_WARN_FORMAT1("workbook_add_chart(): " + "unhandled chart type '%d'", type); + } +} + +/* + * Assemble and write the XML file. + */ +void +lxw_chart_assemble_xml_file(lxw_chart *self) +{ + /* Reverse the X and Y axes for Bar charts. */ + if (self->type == LXW_CHART_BAR || self->type == LXW_CHART_BAR_STACKED + || self->type == LXW_CHART_BAR_STACKED_PERCENT) + _chart_swap_bar_axes(self); + + /* Write the XML declaration. */ + _chart_xml_declaration(self); + + /* Write the c:chartSpace element. */ + _chart_write_chart_space(self); + + /* Write the c:lang element. */ + _chart_write_lang(self); + + /* Write the c:style element. */ + _chart_write_style(self); + + /* Write the c:chart element. */ + _chart_write_chart(self); + + /* Write the c:spPr element for the chartarea formatting. */ + _chart_write_sp_pr(self, self->chartarea_line, self->chartarea_fill, + self->chartarea_pattern); + + /* Write the c:printSettings element. */ + _chart_write_print_settings(self); + + lxw_xml_end_tag(self->file, "c:chartSpace"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ + +/* + * Add data to a data cache in a range object, for testing only. + */ +lxw_error +lxw_chart_add_data_cache(lxw_series_range *range, uint8_t *data, + uint16_t rows, uint8_t cols, uint8_t col) +{ + struct lxw_series_data_point *data_point; + uint16_t i; + + range->ignore_cache = LXW_TRUE; + range->num_data_points = rows; + + /* Initialize the series range data cache. */ + for (i = 0; i < rows; i++) { + data_point = calloc(1, sizeof(struct lxw_series_data_point)); + RETURN_ON_MEM_ERROR(data_point, LXW_ERROR_MEMORY_MALLOC_FAILED); + STAILQ_INSERT_TAIL(range->data_cache, data_point, list_pointers); + data_point->number = data[i * cols + col]; + } + + return LXW_NO_ERROR; +} + +/* + * Insert an image into the worksheet. + */ +lxw_chart_series * +chart_add_series(lxw_chart *self, const char *categories, const char *values) +{ + lxw_chart_series *series; + + /* Scatter charts require categories and values. */ + if (self->chart_group == LXW_CHART_SCATTER && values && !categories) { + LXW_WARN("chart_add_series(): scatter charts must have " + "'categories' and 'values'"); + + return NULL; + } + + /* Create a new object to hold the series. */ + series = calloc(1, sizeof(lxw_chart_series)); + GOTO_LABEL_ON_MEM_ERROR(series, mem_error); + + series->categories = calloc(1, sizeof(lxw_series_range)); + GOTO_LABEL_ON_MEM_ERROR(series->categories, mem_error); + + series->values = calloc(1, sizeof(lxw_series_range)); + GOTO_LABEL_ON_MEM_ERROR(series->values, mem_error); + + series->title.range = calloc(1, sizeof(lxw_series_range)); + GOTO_LABEL_ON_MEM_ERROR(series->title.range, mem_error); + + series->x_error_bars = calloc(1, sizeof(lxw_series_error_bars)); + GOTO_LABEL_ON_MEM_ERROR(series->x_error_bars, mem_error); + + series->y_error_bars = calloc(1, sizeof(lxw_series_error_bars)); + GOTO_LABEL_ON_MEM_ERROR(series->y_error_bars, mem_error); + + if (categories) { + if (categories[0] == '=') + series->categories->formula = lxw_strdup(categories + 1); + else + series->categories->formula = lxw_strdup(categories); + } + + if (values) { + if (values[0] == '=') + series->values->formula = lxw_strdup(values + 1); + else + series->values->formula = lxw_strdup(values); + } + + if (_chart_init_data_cache(series->categories) != LXW_NO_ERROR) + goto mem_error; + + if (_chart_init_data_cache(series->values) != LXW_NO_ERROR) + goto mem_error; + + if (_chart_init_data_cache(series->title.range) != LXW_NO_ERROR) + goto mem_error; + + if (self->type == LXW_CHART_SCATTER_SMOOTH) + series->smooth = LXW_TRUE; + + if (self->type == LXW_CHART_SCATTER_SMOOTH_WITH_MARKERS) + series->smooth = LXW_TRUE; + + series->y_error_bars->chart_group = self->chart_group; + series->x_error_bars->chart_group = self->chart_group; + series->x_error_bars->is_x = LXW_TRUE; + + series->default_label_position = self->default_label_position; + + STAILQ_INSERT_TAIL(self->series_list, series, list_pointers); + + return series; + +mem_error: + _chart_series_free(series); + return NULL; +} + +/* + * Set on of the 48 built-in Excel chart styles. + */ +void +chart_set_style(lxw_chart *self, uint8_t style_id) +{ + /* The default style is 2. The range is 1 - 48 */ + if (style_id < 1 || style_id > 48) + style_id = 2; + + self->style_id = style_id; +} + +/* + * Set a user defined name for a series. + */ +void +chart_series_set_name(lxw_chart_series *series, const char *name) +{ + if (!name) + return; + + if (name[0] == '=') + series->title.range->formula = lxw_strdup(name + 1); + else + series->title.name = lxw_strdup(name); +} + +/* + * Set an axis caption, with a range instead or a formula.. + */ +void +chart_series_set_name_range(lxw_chart_series *series, const char *sheetname, + lxw_row_t row, lxw_col_t col) +{ + if (!sheetname) { + LXW_WARN("chart_series_set_name_range(): " + "sheetname must be specified"); + return; + } + + /* Start and end row, col are the same for single cell range. */ + _chart_set_range(series->title.range, sheetname, row, col, row, col); +} + +/* + * Set the categories range for a series. + */ +void +chart_series_set_categories(lxw_chart_series *series, const char *sheetname, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t last_row, lxw_col_t last_col) +{ + if (!sheetname) { + LXW_WARN("chart_series_set_categories(): " + "sheetname must be specified"); + return; + } + + _chart_set_range(series->categories, sheetname, + first_row, first_col, last_row, last_col); +} + +/* + * Set the values range for a series. + */ +void +chart_series_set_values(lxw_chart_series *series, const char *sheetname, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t last_row, lxw_col_t last_col) +{ + if (!sheetname) { + LXW_WARN("chart_series_set_values(): sheetname must be specified"); + return; + } + + _chart_set_range(series->values, sheetname, + first_row, first_col, last_row, last_col); +} + +/* + * Set a line type for a series. + */ +void +chart_series_set_line(lxw_chart_series *series, lxw_chart_line *line) +{ + if (!line) + return; + + /* Free any previously allocated resource. */ + free(series->line); + + series->line = _chart_convert_line_args(line); +} + +/* + * Set a fill type for a series. + */ +void +chart_series_set_fill(lxw_chart_series *series, lxw_chart_fill *fill) +{ + if (!fill) + return; + + /* Free any previously allocated resource. */ + free(series->fill); + + series->fill = _chart_convert_fill_args(fill); +} + +/* + * Invert the colors of a fill for a series. + */ +void +chart_series_set_invert_if_negative(lxw_chart_series *series) +{ + series->invert_if_negative = LXW_TRUE; +} + +/* + * Set a pattern type for a series. + */ +void +chart_series_set_pattern(lxw_chart_series *series, lxw_chart_pattern *pattern) +{ + if (!pattern) + return; + + /* Free any previously allocated resource. */ + free(series->pattern); + + series->pattern = _chart_convert_pattern_args(pattern); +} + +/* + * Set a marker type for a series. + */ +void +chart_series_set_marker_type(lxw_chart_series *series, uint8_t type) +{ + if (!series->marker) { + lxw_chart_marker *marker = calloc(1, sizeof(struct lxw_chart_marker)); + RETURN_VOID_ON_MEM_ERROR(marker); + series->marker = marker; + } + + series->marker->type = type; +} + +/* + * Set a marker size for a series. + */ +void +chart_series_set_marker_size(lxw_chart_series *series, uint8_t size) +{ + if (!series->marker) { + lxw_chart_marker *marker = calloc(1, sizeof(struct lxw_chart_marker)); + RETURN_VOID_ON_MEM_ERROR(marker); + series->marker = marker; + } + + series->marker->size = size; +} + +/* + * Set a line type for a series marker. + */ +void +chart_series_set_marker_line(lxw_chart_series *series, lxw_chart_line *line) +{ + if (!line) + return; + + if (!series->marker) { + lxw_chart_marker *marker = calloc(1, sizeof(struct lxw_chart_marker)); + RETURN_VOID_ON_MEM_ERROR(marker); + series->marker = marker; + } + + /* Free any previously allocated resource. */ + free(series->marker->line); + + series->marker->line = _chart_convert_line_args(line); +} + +/* + * Set a fill type for a series marker. + */ +void +chart_series_set_marker_fill(lxw_chart_series *series, lxw_chart_fill *fill) +{ + if (!fill) + return; + + if (!series->marker) { + lxw_chart_marker *marker = calloc(1, sizeof(struct lxw_chart_marker)); + RETURN_VOID_ON_MEM_ERROR(marker); + series->marker = marker; + } + + /* Free any previously allocated resource. */ + free(series->marker->fill); + + series->marker->fill = _chart_convert_fill_args(fill); +} + +/* + * Set a pattern type for a series. + */ +void +chart_series_set_marker_pattern(lxw_chart_series *series, + lxw_chart_pattern *pattern) +{ + if (!pattern) + return; + + if (!series->marker) { + lxw_chart_marker *marker = calloc(1, sizeof(struct lxw_chart_marker)); + RETURN_VOID_ON_MEM_ERROR(marker); + series->marker = marker; + } + + /* Free any previously allocated resource. */ + free(series->marker->pattern); + + series->marker->pattern = _chart_convert_pattern_args(pattern); +} + +/* + * Store the horizontal page breaks on a worksheet. + */ +lxw_error +chart_series_set_points(lxw_chart_series *series, lxw_chart_point *points[]) +{ + uint16_t i = 0; + uint16_t point_count = 0; + + if (points == NULL) + return LXW_ERROR_NULL_PARAMETER_IGNORED; + + while (points[point_count]) + point_count++; + + if (point_count == 0) + return LXW_ERROR_NULL_PARAMETER_IGNORED; + + /* Free any existing resource. */ + _chart_free_points(series); + + series->points = calloc(point_count, sizeof(lxw_chart_point)); + RETURN_ON_MEM_ERROR(series->points, LXW_ERROR_MEMORY_MALLOC_FAILED); + + for (i = 0; i < point_count; i++) { + lxw_chart_point *src_point = points[i]; + lxw_chart_point *dst_point = &series->points[i]; + + dst_point->line = _chart_convert_line_args(src_point->line); + dst_point->fill = _chart_convert_fill_args(src_point->fill); + dst_point->pattern = _chart_convert_pattern_args(src_point->pattern); + } + + series->point_count = point_count; + + return LXW_NO_ERROR; +} + +/* + * Set the smooth property for a line or scatter series. + */ +void +chart_series_set_smooth(lxw_chart_series *series, uint8_t smooth) +{ + series->smooth = smooth; +} + +/* + * Turn on default data labels for a series. + */ +void +chart_series_set_labels(lxw_chart_series *series) +{ + series->has_labels = LXW_TRUE; + series->show_labels_value = LXW_TRUE; +} + +/* + * Set the data labels options for a series. + */ +void +chart_series_set_labels_options(lxw_chart_series *series, uint8_t show_name, + uint8_t show_category, uint8_t show_value) +{ + series->has_labels = LXW_TRUE; + series->show_labels_name = show_name; + series->show_labels_category = show_category; + series->show_labels_value = show_value; +} + +/* + * Set the data labels separator for a series. + */ +void +chart_series_set_labels_separator(lxw_chart_series *series, uint8_t separator) +{ + series->has_labels = LXW_TRUE; + series->label_separator = separator; +} + +/* + * Set the data labels position for a series. + */ +void +chart_series_set_labels_position(lxw_chart_series *series, uint8_t position) +{ + series->has_labels = LXW_TRUE; + series->show_labels_value = LXW_TRUE; + + if (position != series->default_label_position) + series->label_position = position; +} + +/* + * Set the data labels position for a series. + */ +void +chart_series_set_labels_leader_line(lxw_chart_series *series) +{ + series->has_labels = LXW_TRUE; + series->show_labels_leader = LXW_TRUE; +} + +/* + * Turn on the data labels legend for a series. + */ +void +chart_series_set_labels_legend(lxw_chart_series *series) +{ + series->has_labels = LXW_TRUE; + series->show_labels_legend = LXW_TRUE; +} + +/* + * Turn on the data labels percentage for a series. + */ +void +chart_series_set_labels_percentage(lxw_chart_series *series) +{ + series->has_labels = LXW_TRUE; + series->show_labels_percent = LXW_TRUE; +} + +/* + * Set an data labels number format. + */ +void +chart_series_set_labels_num_format(lxw_chart_series *series, + const char *num_format) +{ + if (!num_format) + return; + + /* Free any previously allocated resource. */ + free(series->label_num_format); + + series->label_num_format = lxw_strdup(num_format); +} + +/* + * Set an data labels font. + */ +void +chart_series_set_labels_font(lxw_chart_series *series, lxw_chart_font *font) +{ + if (!font) + return; + + /* Free any previously allocated resource. */ + _chart_free_font(series->label_font); + + series->label_font = _chart_convert_font_args(font); +} + +/* + * Set the trendline for a chart series. + */ +void +chart_series_set_trendline(lxw_chart_series *series, uint8_t type, + uint8_t value) +{ + if (type == LXW_CHART_TRENDLINE_TYPE_POLY + || type == LXW_CHART_TRENDLINE_TYPE_AVERAGE) { + + if (value < 2) { + LXW_WARN("chart_series_set_trendline(): order/period value must " + "be >= 2 for Polynomial and Moving Average types"); + return; + } + + series->trendline_value_type = type; + } + + series->has_trendline = LXW_TRUE; + series->trendline_type = type; + series->trendline_value = value; +} + +/* + * Set the trendline forecast for a chart series. + */ +void +chart_series_set_trendline_forecast(lxw_chart_series *series, double forward, + double backward) +{ + if (!series->has_trendline) { + LXW_WARN("chart_series_set_trendline_forecast(): trendline type " + "must be set first using chart_series_set_trendline()"); + return; + } + + if (series->trendline_type == LXW_CHART_TRENDLINE_TYPE_AVERAGE) { + LXW_WARN("chart_series_set_trendline(): forecast isn't available " + "in Excel for a Moving Average trendline"); + return; + } + + series->has_trendline_forecast = LXW_TRUE; + series->trendline_forward = forward; + series->trendline_backward = backward; +} + +/* + * Display the equation for a series trendline. + */ +void +chart_series_set_trendline_equation(lxw_chart_series *series) +{ + if (!series->has_trendline) { + LXW_WARN("chart_series_set_trendline_equation(): trendline type " + "must be set first using chart_series_set_trendline()"); + return; + } + + if (series->trendline_type == LXW_CHART_TRENDLINE_TYPE_AVERAGE) { + LXW_WARN("chart_series_set_trendline_equation(): equation isn't " + "available in Excel for a Moving Average trendline"); + return; + } + + series->has_trendline_equation = LXW_TRUE; +} + +/* + * Display the R squared value for a series trendline. + */ +void +chart_series_set_trendline_r_squared(lxw_chart_series *series) +{ + if (!series->has_trendline) { + LXW_WARN("chart_series_set_trendline_r_squared(): trendline type " + "must be set first using chart_series_set_trendline()"); + return; + } + + if (series->trendline_type == LXW_CHART_TRENDLINE_TYPE_AVERAGE) { + LXW_WARN("chart_series_set_trendline_r_squared(): R squared isn't " + "available in Excel for a Moving Average trendline"); + return; + } + + series->has_trendline_r_squared = LXW_TRUE; +} + +/* + * Set the trendline intercept for a chart series. + */ +void +chart_series_set_trendline_intercept(lxw_chart_series *series, + double intercept) +{ + if (!series->has_trendline) { + LXW_WARN("chart_series_set_trendline_intercept(): trendline type " + "must be set first using chart_series_set_trendline()"); + return; + } + + if (series->trendline_type != LXW_CHART_TRENDLINE_TYPE_EXP + && series->trendline_type != LXW_CHART_TRENDLINE_TYPE_LINEAR + && series->trendline_type != LXW_CHART_TRENDLINE_TYPE_POLY) { + + LXW_WARN("chart_series_set_trendline_intercept(): intercept is only " + "available in Excel for Exponential, Linear and Polynomial " + "trendline types"); + return; + } + + series->has_trendline_intercept = LXW_TRUE; + series->trendline_intercept = intercept; +} + +/* + * Set a line type for a series trendline. + */ +void +chart_series_set_trendline_name(lxw_chart_series *series, const char *name) +{ + if (!name) + return; + + /* Free any previously allocated resource. */ + free(series->trendline_name); + + series->trendline_name = lxw_strdup(name); +} + +/* + * Set a line type for a series trendline. + */ +void +chart_series_set_trendline_line(lxw_chart_series *series, + lxw_chart_line *line) +{ + if (!line) + return; + + /* Free any previously allocated resource. */ + free(series->trendline_line); + + series->trendline_line = _chart_convert_line_args(line); +} + +/* + * Set the error bars and type for a chart series. + */ +void +chart_series_set_error_bars(lxw_series_error_bars *error_bars, + uint8_t type, double value) +{ + if (_chart_check_error_bars(error_bars, "")) + return; + + error_bars->type = type; + error_bars->value = value; + error_bars->has_value = LXW_TRUE; + error_bars->is_set = LXW_TRUE; + + if (type == LXW_CHART_ERROR_BAR_TYPE_STD_ERROR) + error_bars->has_value = LXW_FALSE; +} + +/* + * Set the error bars direction for a chart series. + */ +void +chart_series_set_error_bars_direction(lxw_series_error_bars *error_bars, + uint8_t direction) +{ + if (_chart_check_error_bars(error_bars, "_direction")) + return; + + error_bars->direction = direction; +} + +/* + * Set the error bars end cap type for a chart series. + */ +void +chart_series_set_error_bars_endcap(lxw_series_error_bars *error_bars, + uint8_t endcap) +{ + if (_chart_check_error_bars(error_bars, "_endcap")) + return; + + error_bars->endcap = endcap; +} + +/* + * Set a line type for a series error bars. + */ +void +chart_series_set_error_bars_line(lxw_series_error_bars *error_bars, + lxw_chart_line *line) +{ + if (_chart_check_error_bars(error_bars, "_line")) + return; + + if (!line) + return; + + /* Free any previously allocated resource. */ + free(error_bars->line); + + error_bars->line = _chart_convert_line_args(line); +} + +/* + * Set an axis caption. + */ +void +chart_axis_set_name(lxw_chart_axis *axis, const char *name) +{ + if (!name) + return; + + if (name[0] == '=') + axis->title.range->formula = lxw_strdup(name + 1); + else + axis->title.name = lxw_strdup(name); +} + +/* + * Set an axis caption, with a range instead or a formula.. + */ +void +chart_axis_set_name_range(lxw_chart_axis *axis, const char *sheetname, + lxw_row_t row, lxw_col_t col) +{ + if (!sheetname) { + LXW_WARN("chart_axis_set_name_range(): sheetname must be specified"); + return; + } + + /* Start and end row, col are the same for single cell range. */ + _chart_set_range(axis->title.range, sheetname, row, col, row, col); +} + +/* + * Set an axis title/name font. + */ +void +chart_axis_set_name_font(lxw_chart_axis *axis, lxw_chart_font *font) +{ + if (!font) + return; + + /* Free any previously allocated resource. */ + _chart_free_font(axis->title.font); + + axis->title.font = _chart_convert_font_args(font); +} + +/* + * Set an axis number font. + */ +void +chart_axis_set_num_font(lxw_chart_axis *axis, lxw_chart_font *font) +{ + if (!font) + return; + + /* Free any previously allocated resource. */ + _chart_free_font(axis->num_font); + + axis->num_font = _chart_convert_font_args(font); +} + +/* + * Set an axis number format. + */ +void +chart_axis_set_num_format(lxw_chart_axis *axis, const char *num_format) +{ + if (!num_format) + return; + + /* Free any previously allocated resource. */ + free(axis->num_format); + + axis->num_format = lxw_strdup(num_format); +} + +/* + * Set a line type for an axis. + */ +void +chart_axis_set_line(lxw_chart_axis *axis, lxw_chart_line *line) +{ + if (!line) + return; + + /* Free any previously allocated resource. */ + free(axis->line); + + axis->line = _chart_convert_line_args(line); +} + +/* + * Set a fill type for an axis. + */ +void +chart_axis_set_fill(lxw_chart_axis *axis, lxw_chart_fill *fill) +{ + if (!fill) + return; + + /* Free any previously allocated resource. */ + free(axis->fill); + + axis->fill = _chart_convert_fill_args(fill); +} + +/* + * Set a pattern type for an axis. + */ +void +chart_axis_set_pattern(lxw_chart_axis *axis, lxw_chart_pattern *pattern) +{ + if (!pattern) + return; + + /* Free any previously allocated resource. */ + free(axis->pattern); + + axis->pattern = _chart_convert_pattern_args(pattern); +} + +/* + * Reverse the direction of an axis. + */ +void +chart_axis_set_reverse(lxw_chart_axis *axis) +{ + axis->reverse = LXW_TRUE; +} + +/* + * Set the axis crossing position. + */ +void +chart_axis_set_crossing(lxw_chart_axis *axis, double value) +{ + axis->has_crossing = LXW_TRUE; + axis->crossing = value; +} + +/* + * Set the axis crossing position as the max possible value. + */ +void +chart_axis_set_crossing_max(lxw_chart_axis *axis) +{ + axis->has_crossing = LXW_TRUE; + axis->crossing_max = LXW_TRUE; +} + +/* + * Turn off/hide the axis. + */ +void +chart_axis_off(lxw_chart_axis *axis) +{ + axis->hidden = LXW_TRUE; +} + +/* + * Set the category axis position. + */ +void +chart_axis_set_position(lxw_chart_axis *axis, uint8_t position) +{ + LXW_WARN_CAT_AND_DATE_AXIS_ONLY("chart_axis_set_position"); + + axis->position_axis = position; +} + +/* + * Set the axis label position. + */ +void +chart_axis_set_label_position(lxw_chart_axis *axis, uint8_t position) +{ + axis->label_position = position; +} + +/* + * Set the minimum value for an axis. + */ +void +chart_axis_set_min(lxw_chart_axis *axis, double min) +{ + LXW_WARN_VALUE_AND_DATE_AXIS_ONLY("chart_axis_set_min"); + + axis->min = min; + axis->has_min = LXW_TRUE; +} + +/* + * Set the maximum value for an axis. + */ +void +chart_axis_set_max(lxw_chart_axis *axis, double max) +{ + LXW_WARN_VALUE_AND_DATE_AXIS_ONLY("chart_axis_set_max"); + + axis->max = max; + axis->has_max = LXW_TRUE; +} + +/* + * Set the log base for an axis. + */ +void +chart_axis_set_log_base(lxw_chart_axis *axis, uint16_t log_base) +{ + LXW_WARN_VALUE_AXIS_ONLY("chart_axis_set_log_base"); + + /* Excel log range is 2-1000. */ + if (log_base >= 2 && log_base <= 1000) + axis->log_base = log_base; +} + +/* + * Set the major mark for an axis. + */ +void +chart_axis_set_major_tick_mark(lxw_chart_axis *axis, uint8_t type) +{ + axis->major_tick_mark = type; +} + +/* + * Set the minor mark for an axis. + */ +void +chart_axis_set_minor_tick_mark(lxw_chart_axis *axis, uint8_t type) +{ + axis->minor_tick_mark = type; +} + +/* + * Set interval unit for a category axis. + */ +void +chart_axis_set_interval_unit(lxw_chart_axis *axis, uint16_t unit) +{ + LXW_WARN_CAT_AND_DATE_AXIS_ONLY("chart_axis_set_major_unit"); + + axis->interval_unit = unit; +} + +/* + * Set tick interval for a category axis. + */ +void +chart_axis_set_interval_tick(lxw_chart_axis *axis, uint16_t unit) +{ + LXW_WARN_CAT_AND_DATE_AXIS_ONLY("chart_axis_set_major_tick"); + + axis->interval_tick = unit; +} + +/* + * Set major unit for a value axis. + */ +void +chart_axis_set_major_unit(lxw_chart_axis *axis, double unit) +{ + LXW_WARN_VALUE_AND_DATE_AXIS_ONLY("chart_axis_set_major_unit"); + + axis->has_major_unit = LXW_TRUE; + axis->major_unit = unit; +} + +/* + * Set minor unit for a value axis. + */ +void +chart_axis_set_minor_unit(lxw_chart_axis *axis, double unit) +{ + LXW_WARN_VALUE_AND_DATE_AXIS_ONLY("chart_axis_set_minor_unit"); + + axis->has_minor_unit = LXW_TRUE; + axis->minor_unit = unit; +} + +/* + * Set the display units for a value axis. + */ +void +chart_axis_set_display_units(lxw_chart_axis *axis, uint8_t units) +{ + LXW_WARN_VALUE_AXIS_ONLY("chart_axis_set_display_units"); + + axis->display_units = units; + axis->display_units_visible = LXW_TRUE; +} + +/* + * Turn on/off the display units for a value axis. + */ +void +chart_axis_set_display_units_visible(lxw_chart_axis *axis, uint8_t visible) +{ + LXW_WARN_VALUE_AXIS_ONLY("chart_axis_set_display_units"); + + axis->display_units_visible = visible; +} + +/* + * Set the axis major gridlines on/off. + */ +void +chart_axis_major_gridlines_set_visible(lxw_chart_axis *axis, uint8_t visible) +{ + axis->major_gridlines.visible = visible; +} + +/* + * Set a line type for the major gridlines. + */ +void +chart_axis_major_gridlines_set_line(lxw_chart_axis *axis, + lxw_chart_line *line) +{ + if (!line) + return; + + /* Free any previously allocated resource. */ + free(axis->major_gridlines.line); + + axis->major_gridlines.line = _chart_convert_line_args(line); + + /* If the gridline has a format it should also be visible. */ + if (axis->major_gridlines.line) + axis->major_gridlines.visible = LXW_TRUE; +} + +/* + * Set the axis minor gridlines on/off. + */ +void +chart_axis_minor_gridlines_set_visible(lxw_chart_axis *axis, uint8_t visible) +{ + axis->minor_gridlines.visible = visible; +} + +/* + * Set a line type for the minor gridlines. + */ +void +chart_axis_minor_gridlines_set_line(lxw_chart_axis *axis, + lxw_chart_line *line) +{ + if (!line) + return; + + /* Free any previously allocated resource. */ + free(axis->minor_gridlines.line); + + axis->minor_gridlines.line = _chart_convert_line_args(line); + + /* If the gridline has a format it should also be visible. */ + if (axis->minor_gridlines.line) + axis->minor_gridlines.visible = LXW_TRUE; +} + +/* + * Set the chart title. + */ +void +chart_title_set_name(lxw_chart *self, const char *name) +{ + if (!name) + return; + + if (name[0] == '=') + self->title.range->formula = lxw_strdup(name + 1); + else + self->title.name = lxw_strdup(name); +} + +/* + * Set the chart title, with a range instead or a formula. + */ +void +chart_title_set_name_range(lxw_chart *self, const char *sheetname, + lxw_row_t row, lxw_col_t col) +{ + if (!sheetname) { + LXW_WARN("chart_title_set_name_range(): sheetname must be specified"); + return; + } + + /* Start and end row, col are the same for single cell range. */ + _chart_set_range(self->title.range, sheetname, row, col, row, col); +} + +/* + * Set the chart title font. + */ +void +chart_title_set_name_font(lxw_chart *self, lxw_chart_font *font) +{ + /* Free any previously allocated resource. */ + _chart_free_font(self->title.font); + + self->title.font = _chart_convert_font_args(font); +} + +/* + * Turn off the chart title. + */ +void +chart_title_off(lxw_chart *self) +{ + self->title.off = LXW_TRUE; +} + +/* + * Set the chart legend position. + */ +void +chart_legend_set_position(lxw_chart *self, uint8_t position) +{ + self->legend.position = position; +} + +/* + * Set the legend font. + */ +void +chart_legend_set_font(lxw_chart *self, lxw_chart_font *font) +{ + /* Free any previously allocated resource. */ + _chart_free_font(self->legend.font); + + self->legend.font = _chart_convert_font_args(font); +} + +/* + * Remove one or more series from the the legend. + */ +lxw_error +chart_legend_delete_series(lxw_chart *self, int16_t delete_series[]) +{ + uint16_t count = 0; + + if (delete_series == NULL) + return LXW_ERROR_NULL_PARAMETER_IGNORED; + + while (delete_series[count] >= 0) + count++; + + if (count == 0) + return LXW_ERROR_NULL_PARAMETER_IGNORED; + + /* The maximum number of series in a chart is 255. */ + if (count > 255) + count = 255; + + self->delete_series = calloc(count, sizeof(int16_t)); + RETURN_ON_MEM_ERROR(self->delete_series, LXW_ERROR_MEMORY_MALLOC_FAILED); + memcpy(self->delete_series, delete_series, count * sizeof(int16_t)); + self->delete_series_count = count; + + return LXW_NO_ERROR; +} + +/* + * Set a line type for the chartarea. + */ +void +chart_chartarea_set_line(lxw_chart *self, lxw_chart_line *line) +{ + if (!line) + return; + + /* Free any previously allocated resource. */ + free(self->chartarea_line); + + self->chartarea_line = _chart_convert_line_args(line); +} + +/* + * Set a fill type for the chartarea. + */ +void +chart_chartarea_set_fill(lxw_chart *self, lxw_chart_fill *fill) +{ + if (!fill) + return; + + /* Free any previously allocated resource. */ + free(self->chartarea_fill); + + self->chartarea_fill = _chart_convert_fill_args(fill); +} + +/* + * Set a pattern type for the chartarea. + */ +void +chart_chartarea_set_pattern(lxw_chart *self, lxw_chart_pattern *pattern) +{ + if (!pattern) + return; + + /* Free any previously allocated resource. */ + free(self->chartarea_pattern); + + self->chartarea_pattern = _chart_convert_pattern_args(pattern); +} + +/* + * Set a line type for the plotarea. + */ +void +chart_plotarea_set_line(lxw_chart *self, lxw_chart_line *line) +{ + if (!line) + return; + + /* Free any previously allocated resource. */ + free(self->plotarea_line); + + self->plotarea_line = _chart_convert_line_args(line); +} + +/* + * Set a fill type for the plotarea. + */ +void +chart_plotarea_set_fill(lxw_chart *self, lxw_chart_fill *fill) +{ + if (!fill) + return; + + /* Free any previously allocated resource. */ + free(self->plotarea_fill); + + self->plotarea_fill = _chart_convert_fill_args(fill); +} + +/* + * Set a pattern type for the plotarea. + */ +void +chart_plotarea_set_pattern(lxw_chart *self, lxw_chart_pattern *pattern) +{ + if (!pattern) + return; + + /* Free any previously allocated resource. */ + free(self->plotarea_pattern); + + self->plotarea_pattern = _chart_convert_pattern_args(pattern); +} + +/* + * Turn on the chart data table. + */ +void +chart_set_table(lxw_chart *self) +{ + self->has_table = LXW_TRUE; + self->has_table_horizontal = LXW_TRUE; + self->has_table_vertical = LXW_TRUE; + self->has_table_outline = LXW_TRUE; + self->has_table_legend_keys = LXW_FALSE; +} + +/* + * Set the options for the chart data table grid. + */ +void +chart_set_table_grid(lxw_chart *self, uint8_t horizontal, uint8_t vertical, + uint8_t outline, uint8_t legend_keys) +{ + self->has_table = LXW_TRUE; + self->has_table_horizontal = horizontal; + self->has_table_vertical = vertical; + self->has_table_outline = outline; + self->has_table_legend_keys = legend_keys; +} + +/* + * Set the font for the chart data table grid. + */ +void +chart_set_table_font(lxw_chart *self, lxw_chart_font *font) +{ + self->has_table = LXW_TRUE; + + /* Free any previously allocated resource. */ + _chart_free_font(self->table_font); + + self->table_font = _chart_convert_font_args(font); +} + +/* + * Turn on up-down bars for the chart. + */ +void +chart_set_up_down_bars(lxw_chart *self) +{ + self->has_up_down_bars = LXW_TRUE; +} + +/* + * Turn on up-down bars for the chart, with formatting. + */ +void +chart_set_up_down_bars_format(lxw_chart *self, lxw_chart_line *up_bar_line, + lxw_chart_fill *up_bar_fill, + lxw_chart_line *down_bar_line, + lxw_chart_fill *down_bar_fill) +{ + self->has_up_down_bars = LXW_TRUE; + + /* Free any previously allocated resource. */ + free(self->up_bar_line); + free(self->up_bar_fill); + free(self->down_bar_line); + free(self->down_bar_fill); + + self->up_bar_line = _chart_convert_line_args(up_bar_line); + self->up_bar_fill = _chart_convert_fill_args(up_bar_fill); + self->down_bar_line = _chart_convert_line_args(down_bar_line); + self->down_bar_fill = _chart_convert_fill_args(down_bar_fill); +} + +/* + * Turn on drop lines for the chart. + */ +void +chart_set_drop_lines(lxw_chart *self, lxw_chart_line *line) +{ + /* Free any previously allocated resource. */ + free(self->drop_lines_line); + + self->has_drop_lines = LXW_TRUE; + self->drop_lines_line = _chart_convert_line_args(line); +} + +/* + * Turn on high_low lines for the chart. + */ +void +chart_set_high_low_lines(lxw_chart *self, lxw_chart_line *line) +{ + /* Free any previously allocated resource. */ + free(self->high_low_lines_line); + + self->has_high_low_lines = LXW_TRUE; + self->high_low_lines_line = _chart_convert_line_args(line); +} + +/* + * Set the Bar/Column overlap for all data series. + */ +void +chart_set_series_overlap(lxw_chart *self, int8_t overlap) +{ + if (overlap >= -100 && overlap <= 100) + self->overlap_y1 = overlap; + else + LXW_WARN_FORMAT1("chart_set_series_overlap(): Chart series overlap " + "'%d' outside Excel range: -100 <= overlap <= 100", + overlap); +} + +/* + * Set the option for displaying blank data in a chart. + */ +void +chart_show_blanks_as(lxw_chart *self, uint8_t option) +{ + self->show_blanks_as = option; +} + +/* + * Display data on charts from hidden rows or columns. + */ +void +chart_show_hidden_data(lxw_chart *self) +{ + self->show_hidden_data = LXW_TRUE; +} + +/* + * Set the Bar/Column gap for all data series. + */ +void +chart_set_series_gap(lxw_chart *self, uint16_t gap) +{ + if (gap <= 500) + self->gap_y1 = gap; + else + LXW_WARN_FORMAT1("chart_set_series_gap(): Chart series gap '%d' " + "outside Excel range: 0 <= gap <= 500", gap); +} + +/* + * Set the Pie/Doughnut chart rotation: the angle of the first slice. + */ +void +chart_set_rotation(lxw_chart *self, uint16_t rotation) +{ + if (rotation <= 360) + self->rotation = rotation; + else + LXW_WARN_FORMAT1("chart_set_rotation(): Chart rotation '%d' outside " + "Excel range: 0 <= rotation <= 360", rotation); +} + +/* + * Set the Doughnut chart hole size. + */ +void +chart_set_hole_size(lxw_chart *self, uint8_t size) +{ + if (size >= 10 && size <= 90) + self->hole_size = size; + else + LXW_WARN_FORMAT1("chart_set_hole_size(): Hole size '%d' outside " + "Excel range: 10 <= size <= 90", size); +} diff --git a/src/libxlsxwriter/content_types.c b/src/libxlsxwriter/content_types.c new file mode 100644 index 0000000..2040c35 --- /dev/null +++ b/src/libxlsxwriter/content_types.c @@ -0,0 +1,345 @@ +/***************************************************************************** + * content_types - A library for creating Excel XLSX content_types files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/content_types.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new content_types object. + */ +lxw_content_types * +lxw_content_types_new() +{ + lxw_content_types *content_types = calloc(1, sizeof(lxw_content_types)); + GOTO_LABEL_ON_MEM_ERROR(content_types, mem_error); + + content_types->default_types = calloc(1, sizeof(struct lxw_tuples)); + GOTO_LABEL_ON_MEM_ERROR(content_types->default_types, mem_error); + STAILQ_INIT(content_types->default_types); + + content_types->overrides = calloc(1, sizeof(struct lxw_tuples)); + GOTO_LABEL_ON_MEM_ERROR(content_types->overrides, mem_error); + STAILQ_INIT(content_types->overrides); + + lxw_ct_add_default(content_types, "rels", + LXW_APP_PACKAGE "relationships+xml"); + lxw_ct_add_default(content_types, "xml", "application/xml"); + + lxw_ct_add_override(content_types, "/docProps/app.xml", + LXW_APP_DOCUMENT "extended-properties+xml"); + lxw_ct_add_override(content_types, "/docProps/core.xml", + LXW_APP_PACKAGE "core-properties+xml"); + lxw_ct_add_override(content_types, "/xl/styles.xml", + LXW_APP_DOCUMENT "spreadsheetml.styles+xml"); + lxw_ct_add_override(content_types, "/xl/theme/theme1.xml", + LXW_APP_DOCUMENT "theme+xml"); + lxw_ct_add_override(content_types, "/xl/workbook.xml", + LXW_APP_DOCUMENT "spreadsheetml.sheet.main+xml"); + + return content_types; + +mem_error: + lxw_content_types_free(content_types); + return NULL; +} + +/* + * Free a content_types object. + */ +void +lxw_content_types_free(lxw_content_types *content_types) +{ + lxw_tuple *default_type; + lxw_tuple *override; + + if (!content_types) + return; + + if (content_types->default_types) { + while (!STAILQ_EMPTY(content_types->default_types)) { + default_type = STAILQ_FIRST(content_types->default_types); + STAILQ_REMOVE_HEAD(content_types->default_types, list_pointers); + free(default_type->key); + free(default_type->value); + free(default_type); + } + free(content_types->default_types); + } + + if (content_types->overrides) { + while (!STAILQ_EMPTY(content_types->overrides)) { + override = STAILQ_FIRST(content_types->overrides); + STAILQ_REMOVE_HEAD(content_types->overrides, list_pointers); + free(override->key); + free(override->value); + free(override); + } + free(content_types->overrides); + } + + free(content_types); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_content_types_xml_declaration(lxw_content_types *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_write_types(lxw_content_types *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns", LXW_SCHEMA_CONTENT); + + lxw_xml_start_tag(self->file, "Types", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_default(lxw_content_types *self, const char *ext, const char *type) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("Extension", ext); + LXW_PUSH_ATTRIBUTES_STR("ContentType", type); + + lxw_xml_empty_tag(self->file, "Default", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_override(lxw_content_types *self, const char *part_name, + const char *type) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("PartName", part_name); + LXW_PUSH_ATTRIBUTES_STR("ContentType", type); + + lxw_xml_empty_tag(self->file, "Override", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Write out all of the types. + */ +STATIC void +_write_defaults(lxw_content_types *self) +{ + lxw_tuple *tuple; + + STAILQ_FOREACH(tuple, self->default_types, list_pointers) { + _write_default(self, tuple->key, tuple->value); + } +} + +/* + * Write out all of the types. + */ +STATIC void +_write_overrides(lxw_content_types *self) +{ + lxw_tuple *tuple; + + STAILQ_FOREACH(tuple, self->overrides, list_pointers) { + _write_override(self, tuple->key, tuple->value); + } +} + +/* + * Assemble and write the XML file. + */ +void +lxw_content_types_assemble_xml_file(lxw_content_types *self) +{ + /* Write the XML declaration. */ + _content_types_xml_declaration(self); + + _write_types(self); + _write_defaults(self); + _write_overrides(self); + + /* Close the content_types tag. */ + lxw_xml_end_tag(self->file, "Types"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ +/* + * Add elements to the ContentTypes defaults. + */ +void +lxw_ct_add_default(lxw_content_types *self, const char *key, + const char *value) +{ + lxw_tuple *tuple; + + if (!key || !value) + return; + + tuple = calloc(1, sizeof(lxw_tuple)); + GOTO_LABEL_ON_MEM_ERROR(tuple, mem_error); + + tuple->key = lxw_strdup(key); + GOTO_LABEL_ON_MEM_ERROR(tuple->key, mem_error); + + tuple->value = lxw_strdup(value); + GOTO_LABEL_ON_MEM_ERROR(tuple->value, mem_error); + + STAILQ_INSERT_TAIL(self->default_types, tuple, list_pointers); + + return; + +mem_error: + if (tuple) { + free(tuple->key); + free(tuple->value); + free(tuple); + } +} + +/* + * Add elements to the ContentTypes overrides. + */ +void +lxw_ct_add_override(lxw_content_types *self, const char *key, + const char *value) +{ + lxw_tuple *tuple; + + if (!key || !value) + return; + + tuple = calloc(1, sizeof(lxw_tuple)); + GOTO_LABEL_ON_MEM_ERROR(tuple, mem_error); + + tuple->key = lxw_strdup(key); + GOTO_LABEL_ON_MEM_ERROR(tuple->key, mem_error); + + tuple->value = lxw_strdup(value); + GOTO_LABEL_ON_MEM_ERROR(tuple->value, mem_error); + + STAILQ_INSERT_TAIL(self->overrides, tuple, list_pointers); + + return; + +mem_error: + if (tuple) { + free(tuple->key); + free(tuple->value); + free(tuple); + } +} + +/* + * Add the name of a worksheet to the ContentTypes overrides. + */ +void +lxw_ct_add_worksheet_name(lxw_content_types *self, const char *name) +{ + lxw_ct_add_override(self, name, + LXW_APP_DOCUMENT "spreadsheetml.worksheet+xml"); +} + +/* + * Add the name of a chart to the ContentTypes overrides. + */ +void +lxw_ct_add_chart_name(lxw_content_types *self, const char *name) +{ + lxw_ct_add_override(self, name, LXW_APP_DOCUMENT "drawingml.chart+xml"); +} + +/* + * Add the name of a drawing to the ContentTypes overrides. + */ +void +lxw_ct_add_drawing_name(lxw_content_types *self, const char *name) +{ + lxw_ct_add_override(self, name, LXW_APP_DOCUMENT "drawing+xml"); +} + +/* + * Add the sharedStrings link to the ContentTypes overrides. + */ +void +lxw_ct_add_shared_strings(lxw_content_types *self) +{ + lxw_ct_add_override(self, "/xl/sharedStrings.xml", + LXW_APP_DOCUMENT "spreadsheetml.sharedStrings+xml"); +} + +/* + * Add the calcChain link to the ContentTypes overrides. + */ +void +lxw_ct_add_calc_chain(lxw_content_types *self) +{ + lxw_ct_add_override(self, "/xl/calcChain.xml", + LXW_APP_DOCUMENT "spreadsheetml.calcChain+xml"); +} + +/* + * Add the custom properties to the ContentTypes overrides. + */ +void +lxw_ct_add_custom_properties(lxw_content_types *self) +{ + lxw_ct_add_override(self, "/docProps/custom.xml", + LXW_APP_DOCUMENT "custom-properties+xml"); +} diff --git a/src/libxlsxwriter/core.c b/src/libxlsxwriter/core.c new file mode 100644 index 0000000..cda1bee --- /dev/null +++ b/src/libxlsxwriter/core.c @@ -0,0 +1,293 @@ +/***************************************************************************** + * core - A library for creating Excel XLSX core files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/core.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new core object. + */ +lxw_core * +lxw_core_new() +{ + lxw_core *core = calloc(1, sizeof(lxw_core)); + GOTO_LABEL_ON_MEM_ERROR(core, mem_error); + + return core; + +mem_error: + lxw_core_free(core); + return NULL; +} + +/* + * Free a core object. + */ +void +lxw_core_free(lxw_core *core) +{ + if (!core) + return; + + free(core); +} + +/* + * Convert a time_t struct to a ISO 8601 style "2010-01-01T00:00:00Z" date. + */ +static void +_datetime_to_iso8601_date(time_t *timer, char *str, size_t size) +{ + struct tm *tmp_datetime; + time_t current_time = time(NULL); + + if (*timer) + tmp_datetime = gmtime(timer); + else + tmp_datetime = gmtime(¤t_time); + + strftime(str, size - 1, "%Y-%m-%dT%H:%M:%SZ", tmp_datetime); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_core_xml_declaration(lxw_core *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_write_cp_core_properties(lxw_core *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns:cp", + "http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); + LXW_PUSH_ATTRIBUTES_STR("xmlns:dc", "http://purl.org/dc/elements/1.1/"); + LXW_PUSH_ATTRIBUTES_STR("xmlns:dcterms", "http://purl.org/dc/terms/"); + LXW_PUSH_ATTRIBUTES_STR("xmlns:dcmitype", "http://purl.org/dc/dcmitype/"); + LXW_PUSH_ATTRIBUTES_STR("xmlns:xsi", + "http://www.w3.org/2001/XMLSchema-instance"); + + lxw_xml_start_tag(self->file, "cp:coreProperties", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_dc_creator(lxw_core *self) +{ + if (self->properties->author) { + lxw_xml_data_element(self->file, "dc:creator", + self->properties->author, NULL); + } + else { + lxw_xml_data_element(self->file, "dc:creator", "", NULL); + } +} + +/* + * Write the element. + */ +STATIC void +_write_cp_last_modified_by(lxw_core *self) +{ + if (self->properties->author) { + lxw_xml_data_element(self->file, "cp:lastModifiedBy", + self->properties->author, NULL); + } + else { + lxw_xml_data_element(self->file, "cp:lastModifiedBy", "", NULL); + } +} + +/* + * Write the element. + */ +STATIC void +_write_dcterms_created(lxw_core *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char datetime[LXW_ATTR_32]; + + _datetime_to_iso8601_date(&self->properties->created, datetime, + LXW_ATTR_32); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xsi:type", "dcterms:W3CDTF"); + + lxw_xml_data_element(self->file, "dcterms:created", datetime, + &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_dcterms_modified(lxw_core *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char datetime[LXW_ATTR_32]; + + _datetime_to_iso8601_date(&self->properties->created, datetime, + LXW_ATTR_32); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xsi:type", "dcterms:W3CDTF"); + + lxw_xml_data_element(self->file, "dcterms:modified", datetime, + &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_dc_title(lxw_core *self) +{ + if (!self->properties->title) + return; + + lxw_xml_data_element(self->file, "dc:title", self->properties->title, + NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_dc_subject(lxw_core *self) +{ + if (!self->properties->subject) + return; + + lxw_xml_data_element(self->file, "dc:subject", self->properties->subject, + NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_cp_keywords(lxw_core *self) +{ + if (!self->properties->keywords) + return; + + lxw_xml_data_element(self->file, "cp:keywords", + self->properties->keywords, NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_dc_description(lxw_core *self) +{ + if (!self->properties->comments) + return; + + lxw_xml_data_element(self->file, "dc:description", + self->properties->comments, NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_cp_category(lxw_core *self) +{ + if (!self->properties->category) + return; + + lxw_xml_data_element(self->file, "cp:category", + self->properties->category, NULL); +} + +/* + * Write the element. + */ +STATIC void +_write_cp_content_status(lxw_core *self) +{ + if (!self->properties->status) + return; + + lxw_xml_data_element(self->file, "cp:contentStatus", + self->properties->status, NULL); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Assemble and write the XML file. + */ +void +lxw_core_assemble_xml_file(lxw_core *self) +{ + /* Write the XML declaration. */ + _core_xml_declaration(self); + + _write_cp_core_properties(self); + _write_dc_title(self); + _write_dc_subject(self); + _write_dc_creator(self); + _write_cp_keywords(self); + _write_dc_description(self); + _write_cp_last_modified_by(self); + _write_dcterms_created(self); + _write_dcterms_modified(self); + _write_cp_category(self); + _write_cp_content_status(self); + + lxw_xml_end_tag(self->file, "cp:coreProperties"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ diff --git a/src/libxlsxwriter/custom.c b/src/libxlsxwriter/custom.c new file mode 100644 index 0000000..7dd70af --- /dev/null +++ b/src/libxlsxwriter/custom.c @@ -0,0 +1,224 @@ +/***************************************************************************** + * custom - A library for creating Excel custom property files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/custom.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new custom object. + */ +lxw_custom * +lxw_custom_new() +{ + lxw_custom *custom = calloc(1, sizeof(lxw_custom)); + GOTO_LABEL_ON_MEM_ERROR(custom, mem_error); + + return custom; + +mem_error: + lxw_custom_free(custom); + return NULL; +} + +/* + * Free a custom object. + */ +void +lxw_custom_free(lxw_custom *custom) +{ + if (!custom) + return; + + free(custom); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_custom_xml_declaration(lxw_custom *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_vt_lpwstr(lxw_custom *self, char *value) +{ + lxw_xml_data_element(self->file, "vt:lpwstr", value, NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_vt_r_8(lxw_custom *self, double value) +{ + char data[LXW_ATTR_32]; + + lxw_snprintf(data, LXW_ATTR_32, "%.16g", value); + + lxw_xml_data_element(self->file, "vt:r8", data, NULL); +} + +/* + * Write the element. + */ +STATIC void +_custom_write_vt_i_4(lxw_custom *self, int32_t value) +{ + char data[LXW_ATTR_32]; + + lxw_snprintf(data, LXW_ATTR_32, "%d", value); + + lxw_xml_data_element(self->file, "vt:i4", data, NULL); +} + +/* + * Write the element. + */ +STATIC void +_custom_write_vt_bool(lxw_custom *self, uint8_t value) +{ + if (value) + lxw_xml_data_element(self->file, "vt:bool", "true", NULL); + else + lxw_xml_data_element(self->file, "vt:bool", "false", NULL); +} + +/* + * Write the element. + */ +STATIC void +_custom_write_vt_filetime(lxw_custom *self, lxw_datetime *datetime) +{ + char data[LXW_DATETIME_LENGTH]; + + lxw_snprintf(data, LXW_DATETIME_LENGTH, "%4d-%02d-%02dT%02d:%02d:%02dZ", + datetime->year, datetime->month, datetime->day, + datetime->hour, datetime->min, (int) datetime->sec); + + lxw_xml_data_element(self->file, "vt:filetime", data, NULL); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_custom_property(lxw_custom *self, + lxw_custom_property *custom_property) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char fmtid[] = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"; + + self->pid++; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("fmtid", fmtid); + LXW_PUSH_ATTRIBUTES_INT("pid", self->pid + 1); + LXW_PUSH_ATTRIBUTES_STR("name", custom_property->name); + + lxw_xml_start_tag(self->file, "property", &attributes); + + if (custom_property->type == LXW_CUSTOM_STRING) { + /* Write the vt:lpwstr element. */ + _chart_write_vt_lpwstr(self, custom_property->u.string); + } + else if (custom_property->type == LXW_CUSTOM_DOUBLE) { + /* Write the vt:r8 element. */ + _chart_write_vt_r_8(self, custom_property->u.number); + } + else if (custom_property->type == LXW_CUSTOM_INTEGER) { + /* Write the vt:i4 element. */ + _custom_write_vt_i_4(self, custom_property->u.integer); + } + else if (custom_property->type == LXW_CUSTOM_BOOLEAN) { + /* Write the vt:bool element. */ + _custom_write_vt_bool(self, custom_property->u.boolean); + } + else if (custom_property->type == LXW_CUSTOM_DATETIME) { + /* Write the vt:filetime element. */ + _custom_write_vt_filetime(self, &custom_property->u.datetime); + } + + lxw_xml_end_tag(self->file, "property"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_custom_properties(lxw_custom *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns[] = LXW_SCHEMA_OFFICEDOC "/custom-properties"; + char xmlns_vt[] = LXW_SCHEMA_OFFICEDOC "/docPropsVTypes"; + lxw_custom_property *custom_property; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); + LXW_PUSH_ATTRIBUTES_STR("xmlns:vt", xmlns_vt); + + lxw_xml_start_tag(self->file, "Properties", &attributes); + + STAILQ_FOREACH(custom_property, self->custom_properties, list_pointers) { + _chart_write_custom_property(self, custom_property); + } + + LXW_FREE_ATTRIBUTES(); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Assemble and write the XML file. + */ +void +lxw_custom_assemble_xml_file(lxw_custom *self) +{ + /* Write the XML declaration. */ + _custom_xml_declaration(self); + + _write_custom_properties(self); + + lxw_xml_end_tag(self->file, "Properties"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ diff --git a/src/libxlsxwriter/drawing.c b/src/libxlsxwriter/drawing.c new file mode 100644 index 0000000..27ed28a --- /dev/null +++ b/src/libxlsxwriter/drawing.c @@ -0,0 +1,746 @@ +/***************************************************************************** + * drawing - A library for creating Excel XLSX drawing files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/common.h" +#include "xlsxwriter/drawing.h" +#include "xlsxwriter/utility.h" + +#define LXW_OBJ_NAME_LENGTH 14 /* "Picture 65536", or "Chart 65536" */ +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new drawing collection. + */ +lxw_drawing * +lxw_drawing_new() +{ + lxw_drawing *drawing = calloc(1, sizeof(lxw_drawing)); + GOTO_LABEL_ON_MEM_ERROR(drawing, mem_error); + + drawing->drawing_objects = calloc(1, sizeof(struct lxw_drawing_objects)); + GOTO_LABEL_ON_MEM_ERROR(drawing->drawing_objects, mem_error); + + STAILQ_INIT(drawing->drawing_objects); + + return drawing; + +mem_error: + lxw_drawing_free(drawing); + return NULL; +} + +/* + * Free a drawing object. + */ +void +lxw_free_drawing_object(lxw_drawing_object *drawing_object) +{ + if (!drawing_object) + return; + + free(drawing_object->description); + free(drawing_object->url); + free(drawing_object->tip); + + free(drawing_object); +} + +/* + * Free a drawing collection. + */ +void +lxw_drawing_free(lxw_drawing *drawing) +{ + lxw_drawing_object *drawing_object; + + if (!drawing) + return; + + if (drawing->drawing_objects) { + while (!STAILQ_EMPTY(drawing->drawing_objects)) { + drawing_object = STAILQ_FIRST(drawing->drawing_objects); + STAILQ_REMOVE_HEAD(drawing->drawing_objects, list_pointers); + lxw_free_drawing_object(drawing_object); + } + + free(drawing->drawing_objects); + } + + free(drawing); +} + +/* + * Add a drawing object to the drawing collection. + */ +void +lxw_add_drawing_object(lxw_drawing *drawing, + lxw_drawing_object *drawing_object) +{ + STAILQ_INSERT_TAIL(drawing->drawing_objects, drawing_object, + list_pointers); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_drawing_xml_declaration(lxw_drawing *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_write_drawing_workspace(lxw_drawing *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns_xdr[] = LXW_SCHEMA_DRAWING "/spreadsheetDrawing"; + char xmlns_a[] = LXW_SCHEMA_DRAWING "/main"; + + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_STR("xmlns:xdr", xmlns_xdr); + LXW_PUSH_ATTRIBUTES_STR("xmlns:a", xmlns_a); + + lxw_xml_start_tag(self->file, "xdr:wsDr", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_col(lxw_drawing *self, char *data) +{ + lxw_xml_data_element(self->file, "xdr:col", data, NULL); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_col_off(lxw_drawing *self, char *data) +{ + lxw_xml_data_element(self->file, "xdr:colOff", data, NULL); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_row(lxw_drawing *self, char *data) +{ + lxw_xml_data_element(self->file, "xdr:row", data, NULL); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_row_off(lxw_drawing *self, char *data) +{ + lxw_xml_data_element(self->file, "xdr:rowOff", data, NULL); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_from(lxw_drawing *self, lxw_drawing_coords *coords) +{ + char data[LXW_UINT32_T_LENGTH]; + + lxw_xml_start_tag(self->file, "xdr:from", NULL); + + lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", coords->col); + _drawing_write_col(self, data); + + lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", + (uint32_t) coords->col_offset); + _drawing_write_col_off(self, data); + + lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", coords->row); + _drawing_write_row(self, data); + + lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", + (uint32_t) coords->row_offset); + _drawing_write_row_off(self, data); + + lxw_xml_end_tag(self->file, "xdr:from"); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_to(lxw_drawing *self, lxw_drawing_coords *coords) +{ + char data[LXW_UINT32_T_LENGTH]; + + lxw_xml_start_tag(self->file, "xdr:to", NULL); + + lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", coords->col); + _drawing_write_col(self, data); + + lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", + (uint32_t) coords->col_offset); + _drawing_write_col_off(self, data); + + lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", coords->row); + _drawing_write_row(self, data); + + lxw_snprintf(data, LXW_UINT32_T_LENGTH, "%u", + (uint32_t) coords->row_offset); + _drawing_write_row_off(self, data); + + lxw_xml_end_tag(self->file, "xdr:to"); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_c_nv_pr(lxw_drawing *self, char *object_name, uint16_t index, + lxw_drawing_object *drawing_object) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + char name[LXW_OBJ_NAME_LENGTH]; + lxw_snprintf(name, LXW_OBJ_NAME_LENGTH, "%s %d", object_name, index); + + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_INT("id", index + 1); + LXW_PUSH_ATTRIBUTES_STR("name", name); + + if (drawing_object) + LXW_PUSH_ATTRIBUTES_STR("descr", drawing_object->description); + + lxw_xml_empty_tag(self->file, "xdr:cNvPr", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_a_pic_locks(lxw_drawing *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("noChangeAspect", "1"); + + lxw_xml_empty_tag(self->file, "a:picLocks", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_c_nv_pic_pr(lxw_drawing *self) +{ + lxw_xml_start_tag(self->file, "xdr:cNvPicPr", NULL); + + /* Write the a:picLocks element. */ + _drawing_write_a_pic_locks(self); + + lxw_xml_end_tag(self->file, "xdr:cNvPicPr"); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_nv_pic_pr(lxw_drawing *self, uint16_t index, + lxw_drawing_object *drawing_object) +{ + lxw_xml_start_tag(self->file, "xdr:nvPicPr", NULL); + + /* Write the xdr:cNvPr element. */ + _drawing_write_c_nv_pr(self, "Picture", index, drawing_object); + + /* Write the xdr:cNvPicPr element. */ + _drawing_write_c_nv_pic_pr(self); + + lxw_xml_end_tag(self->file, "xdr:nvPicPr"); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_a_blip(lxw_drawing *self, uint16_t index) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns_r[] = LXW_SCHEMA_OFFICEDOC "/relationships"; + char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; + + lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", index); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns:r", xmlns_r); + LXW_PUSH_ATTRIBUTES_STR("r:embed", r_id); + + lxw_xml_empty_tag(self->file, "a:blip", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_a_fill_rect(lxw_drawing *self) +{ + lxw_xml_empty_tag(self->file, "a:fillRect", NULL); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_a_stretch(lxw_drawing *self) +{ + lxw_xml_start_tag(self->file, "a:stretch", NULL); + + /* Write the a:fillRect element. */ + _drawing_write_a_fill_rect(self); + + lxw_xml_end_tag(self->file, "a:stretch"); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_blip_fill(lxw_drawing *self, uint16_t index) +{ + lxw_xml_start_tag(self->file, "xdr:blipFill", NULL); + + /* Write the a:blip element. */ + _drawing_write_a_blip(self, index); + + /* Write the a:stretch element. */ + _drawing_write_a_stretch(self); + + lxw_xml_end_tag(self->file, "xdr:blipFill"); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_a_ext(lxw_drawing *self, lxw_drawing_object *drawing_object) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("cx", drawing_object->width); + LXW_PUSH_ATTRIBUTES_INT("cy", drawing_object->height); + + lxw_xml_empty_tag(self->file, "a:ext", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_a_off(lxw_drawing *self, lxw_drawing_object *drawing_object) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("x", drawing_object->col_absolute); + LXW_PUSH_ATTRIBUTES_INT("y", drawing_object->row_absolute); + + lxw_xml_empty_tag(self->file, "a:off", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_a_xfrm(lxw_drawing *self, lxw_drawing_object *drawing_object) +{ + lxw_xml_start_tag(self->file, "a:xfrm", NULL); + + /* Write the a:off element. */ + _drawing_write_a_off(self, drawing_object); + + /* Write the a:ext element. */ + _drawing_write_a_ext(self, drawing_object); + + lxw_xml_end_tag(self->file, "a:xfrm"); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_a_av_lst(lxw_drawing *self) +{ + lxw_xml_empty_tag(self->file, "a:avLst", NULL); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_a_prst_geom(lxw_drawing *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("prst", "rect"); + + lxw_xml_start_tag(self->file, "a:prstGeom", &attributes); + + /* Write the a:avLst element. */ + _drawing_write_a_av_lst(self); + + lxw_xml_end_tag(self->file, "a:prstGeom"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_sp_pr(lxw_drawing *self, lxw_drawing_object *drawing_object) +{ + lxw_xml_start_tag(self->file, "xdr:spPr", NULL); + + /* Write the a:xfrm element. */ + _drawing_write_a_xfrm(self, drawing_object); + + /* Write the a:prstGeom element. */ + _drawing_write_a_prst_geom(self); + + lxw_xml_end_tag(self->file, "xdr:spPr"); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_pic(lxw_drawing *self, uint16_t index, + lxw_drawing_object *drawing_object) +{ + lxw_xml_start_tag(self->file, "xdr:pic", NULL); + + /* Write the xdr:nvPicPr element. */ + _drawing_write_nv_pic_pr(self, index, drawing_object); + + /* Write the xdr:blipFill element. */ + _drawing_write_blip_fill(self, index); + + /* Write the xdr:spPr element. */ + _drawing_write_sp_pr(self, drawing_object); + + lxw_xml_end_tag(self->file, "xdr:pic"); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_client_data(lxw_drawing *self) +{ + lxw_xml_empty_tag(self->file, "xdr:clientData", NULL); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_c_nv_graphic_frame_pr(lxw_drawing *self) +{ + lxw_xml_empty_tag(self->file, "xdr:cNvGraphicFramePr", NULL); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_nv_graphic_frame_pr(lxw_drawing *self, uint16_t index) +{ + lxw_xml_start_tag(self->file, "xdr:nvGraphicFramePr", NULL); + + /* Write the xdr:cNvPr element. */ + _drawing_write_c_nv_pr(self, "Chart", index, NULL); + + /* Write the xdr:cNvGraphicFramePr element. */ + _drawing_write_c_nv_graphic_frame_pr(self); + + lxw_xml_end_tag(self->file, "xdr:nvGraphicFramePr"); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_xfrm_offset(lxw_drawing *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("x", "0"); + LXW_PUSH_ATTRIBUTES_STR("y", "0"); + + lxw_xml_empty_tag(self->file, "a:off", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_xfrm_extension(lxw_drawing *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("cx", "0"); + LXW_PUSH_ATTRIBUTES_STR("cy", "0"); + + lxw_xml_empty_tag(self->file, "a:ext", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_xfrm(lxw_drawing *self) +{ + lxw_xml_start_tag(self->file, "xdr:xfrm", NULL); + + /* Write the a:off element. */ + _drawing_write_xfrm_offset(self); + + /* Write the a:ext element. */ + _drawing_write_xfrm_extension(self); + + lxw_xml_end_tag(self->file, "xdr:xfrm"); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_chart(lxw_drawing *self, uint16_t index) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns_c[] = LXW_SCHEMA_DRAWING "/chart"; + char xmlns_r[] = LXW_SCHEMA_OFFICEDOC "/relationships"; + char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; + + lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", index); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns:c", xmlns_c); + LXW_PUSH_ATTRIBUTES_STR("xmlns:r", xmlns_r); + LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); + + lxw_xml_empty_tag(self->file, "c:chart", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_a_graphic_data(lxw_drawing *self, uint16_t index) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char uri[] = LXW_SCHEMA_DRAWING "/chart"; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("uri", uri); + + lxw_xml_start_tag(self->file, "a:graphicData", &attributes); + + /* Write the c:chart element. */ + _drawing_write_chart(self, index); + + lxw_xml_end_tag(self->file, "a:graphicData"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_a_graphic(lxw_drawing *self, uint16_t index) +{ + + lxw_xml_start_tag(self->file, "a:graphic", NULL); + + /* Write the a:graphicData element. */ + _drawing_write_a_graphic_data(self, index); + + lxw_xml_end_tag(self->file, "a:graphic"); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_graphic_frame(lxw_drawing *self, uint16_t index) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("macro", ""); + + lxw_xml_start_tag(self->file, "xdr:graphicFrame", &attributes); + + /* Write the xdr:nvGraphicFramePr element. */ + _drawing_write_nv_graphic_frame_pr(self, index); + + /* Write the xdr:xfrm element. */ + _drawing_write_xfrm(self); + + /* Write the a:graphic element. */ + _drawing_write_a_graphic(self, index); + + lxw_xml_end_tag(self->file, "xdr:graphicFrame"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_two_cell_anchor(lxw_drawing *self, uint16_t index, + lxw_drawing_object *drawing_object) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (drawing_object->anchor_type == LXW_ANCHOR_TYPE_IMAGE) { + + if (drawing_object->edit_as == LXW_ANCHOR_EDIT_AS_ABSOLUTE) + LXW_PUSH_ATTRIBUTES_STR("editAs", "absolute"); + else if (drawing_object->edit_as != LXW_ANCHOR_EDIT_AS_RELATIVE) + LXW_PUSH_ATTRIBUTES_STR("editAs", "oneCell"); + } + + lxw_xml_start_tag(self->file, "xdr:twoCellAnchor", &attributes); + + _drawing_write_from(self, &drawing_object->from); + _drawing_write_to(self, &drawing_object->to); + + if (drawing_object->anchor_type == LXW_ANCHOR_TYPE_CHART) { + /* Write the xdr:graphicFrame element for charts. */ + _drawing_write_graphic_frame(self, index); + } + else if (drawing_object->anchor_type == LXW_ANCHOR_TYPE_IMAGE) { + /* Write the xdr:pic element. */ + _drawing_write_pic(self, index, drawing_object); + } + else { + /* Write the xdr:sp element for shapes. */ + /* _drawing_write_sp(self, index, col_absolute, row_absolute, width, + height, shape); */ + } + + /* Write the xdr:clientData element. */ + _drawing_write_client_data(self); + + lxw_xml_end_tag(self->file, "xdr:twoCellAnchor"); + + LXW_FREE_ATTRIBUTES(); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Assemble and write the XML file. + */ +void +lxw_drawing_assemble_xml_file(lxw_drawing *self) +{ + uint16_t index; + lxw_drawing_object *drawing_object; + + /* Write the XML declaration. */ + _drawing_xml_declaration(self); + + /* Write the xdr:wsDr element. */ + _write_drawing_workspace(self); + + if (self->embedded) { + index = 1; + + STAILQ_FOREACH(drawing_object, self->drawing_objects, list_pointers) { + _drawing_write_two_cell_anchor(self, index, drawing_object); + index++; + } + + } + + lxw_xml_end_tag(self->file, "xdr:wsDr"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ diff --git a/src/libxlsxwriter/format.c b/src/libxlsxwriter/format.c new file mode 100644 index 0000000..9e6b2b1 --- /dev/null +++ b/src/libxlsxwriter/format.c @@ -0,0 +1,728 @@ +/***************************************************************************** + * format - A library for creating Excel XLSX format files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/format.h" +#include "xlsxwriter/utility.h" + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new format object. + */ +lxw_format * +lxw_format_new() +{ + lxw_format *format = calloc(1, sizeof(lxw_format)); + GOTO_LABEL_ON_MEM_ERROR(format, mem_error); + + format->xf_format_indices = NULL; + + format->xf_index = LXW_PROPERTY_UNSET; + format->dxf_index = LXW_PROPERTY_UNSET; + + format->font_name[0] = '\0'; + format->font_scheme[0] = '\0'; + format->num_format[0] = '\0'; + format->num_format_index = 0; + format->font_index = 0; + format->has_font = LXW_FALSE; + format->has_dxf_font = LXW_FALSE; + format->font_size = 11.0; + format->bold = LXW_FALSE; + format->italic = LXW_FALSE; + format->font_color = LXW_COLOR_UNSET; + format->underline = LXW_FALSE; + format->font_strikeout = LXW_FALSE; + format->font_outline = LXW_FALSE; + format->font_shadow = LXW_FALSE; + format->font_script = LXW_FALSE; + format->font_family = LXW_DEFAULT_FONT_FAMILY; + format->font_charset = LXW_FALSE; + format->font_condense = LXW_FALSE; + format->font_extend = LXW_FALSE; + format->theme = LXW_FALSE; + format->hyperlink = LXW_FALSE; + + format->hidden = LXW_FALSE; + format->locked = LXW_TRUE; + + format->text_h_align = LXW_ALIGN_NONE; + format->text_wrap = LXW_FALSE; + format->text_v_align = LXW_ALIGN_NONE; + format->text_justlast = LXW_FALSE; + format->rotation = 0; + + format->fg_color = LXW_COLOR_UNSET; + format->bg_color = LXW_COLOR_UNSET; + format->pattern = LXW_PATTERN_NONE; + format->has_fill = LXW_FALSE; + format->has_dxf_fill = LXW_FALSE; + format->fill_index = 0; + format->fill_count = 0; + + format->border_index = 0; + format->has_border = LXW_FALSE; + format->has_dxf_border = LXW_FALSE; + format->border_count = 0; + + format->bottom = LXW_BORDER_NONE; + format->left = LXW_BORDER_NONE; + format->right = LXW_BORDER_NONE; + format->top = LXW_BORDER_NONE; + format->diag_border = LXW_BORDER_NONE; + format->diag_type = LXW_BORDER_NONE; + format->bottom_color = LXW_COLOR_UNSET; + format->left_color = LXW_COLOR_UNSET; + format->right_color = LXW_COLOR_UNSET; + format->top_color = LXW_COLOR_UNSET; + format->diag_color = LXW_COLOR_UNSET; + + format->indent = 0; + format->shrink = LXW_FALSE; + format->merge_range = LXW_FALSE; + format->reading_order = 0; + format->just_distrib = LXW_FALSE; + format->color_indexed = LXW_FALSE; + format->font_only = LXW_FALSE; + + return format; + +mem_error: + lxw_format_free(format); + return NULL; +} + +/* + * Free a format object. + */ +void +lxw_format_free(lxw_format *format) +{ + if (!format) + return; + + free(format); + format = NULL; +} + +/* + * Check a user input color. + */ +lxw_color_t +lxw_format_check_color(lxw_color_t color) +{ + if (color == LXW_COLOR_UNSET) + return color; + else + return color & LXW_COLOR_MASK; +} + +/* + * Check a user input border. + */ +STATIC uint8_t +_check_border(uint8_t border) +{ + if (border >= LXW_BORDER_THIN && border <= LXW_BORDER_SLANT_DASH_DOT) + return border; + else + return LXW_BORDER_NONE; +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ + +/* + * Returns a format struct suitable for hashing as a lookup key. This is + * mainly a memcpy with any pointer members set to NULL. + */ +STATIC lxw_format * +_get_format_key(lxw_format *self) +{ + lxw_format *key = calloc(1, sizeof(lxw_format)); + GOTO_LABEL_ON_MEM_ERROR(key, mem_error); + + memcpy(key, self, sizeof(lxw_format)); + + /* Set pointer members to NULL since they aren't part of the comparison. */ + key->xf_format_indices = NULL; + key->num_xf_formats = NULL; + key->list_pointers.stqe_next = NULL; + + return key; + +mem_error: + return NULL; +} + +/* + * Returns a font struct suitable for hashing as a lookup key. + */ +lxw_font * +lxw_format_get_font_key(lxw_format *self) +{ + lxw_font *key = calloc(1, sizeof(lxw_font)); + GOTO_LABEL_ON_MEM_ERROR(key, mem_error); + + LXW_FORMAT_FIELD_COPY(key->font_name, self->font_name); + key->font_size = self->font_size; + key->bold = self->bold; + key->italic = self->italic; + key->font_color = self->font_color; + key->underline = self->underline; + key->font_strikeout = self->font_strikeout; + key->font_outline = self->font_outline; + key->font_shadow = self->font_shadow; + key->font_script = self->font_script; + key->font_family = self->font_family; + key->font_charset = self->font_charset; + key->font_condense = self->font_condense; + key->font_extend = self->font_extend; + + return key; + +mem_error: + return NULL; +} + +/* + * Returns a border struct suitable for hashing as a lookup key. + */ +lxw_border * +lxw_format_get_border_key(lxw_format *self) +{ + lxw_border *key = calloc(1, sizeof(lxw_border)); + GOTO_LABEL_ON_MEM_ERROR(key, mem_error); + + key->bottom = self->bottom; + key->left = self->left; + key->right = self->right; + key->top = self->top; + key->diag_border = self->diag_border; + key->diag_type = self->diag_type; + key->bottom_color = self->bottom_color; + key->left_color = self->left_color; + key->right_color = self->right_color; + key->top_color = self->top_color; + key->diag_color = self->diag_color; + + return key; + +mem_error: + return NULL; +} + +/* + * Returns a pattern fill struct suitable for hashing as a lookup key. + */ +lxw_fill * +lxw_format_get_fill_key(lxw_format *self) +{ + lxw_fill *key = calloc(1, sizeof(lxw_fill)); + GOTO_LABEL_ON_MEM_ERROR(key, mem_error); + + key->fg_color = self->fg_color; + key->bg_color = self->bg_color; + key->pattern = self->pattern; + + return key; + +mem_error: + return NULL; +} + +/* + * Returns the XF index number used by Excel to identify a format. + */ +int32_t +lxw_format_get_xf_index(lxw_format *self) +{ + lxw_format *format_key; + lxw_format *existing_format; + lxw_hash_element *hash_element; + lxw_hash_table *formats_hash_table = self->xf_format_indices; + int32_t index; + + /* Note: The formats_hash_table/xf_format_indices contains the unique and + * more importantly the *used* formats in the workbook. + */ + + /* Format already has an index number so return it. */ + if (self->xf_index != LXW_PROPERTY_UNSET) { + return self->xf_index; + } + + /* Otherwise, the format doesn't have an index number so we assign one. + * First generate a unique key to identify the format in the hash table. + */ + format_key = _get_format_key(self); + + /* Return the default format index if the key generation failed. */ + if (!format_key) + return 0; + + /* Look up the format in the hash table. */ + hash_element = + lxw_hash_key_exists(formats_hash_table, format_key, + sizeof(lxw_format)); + + if (hash_element) { + /* Format matches existing format with an index. */ + free(format_key); + existing_format = hash_element->value; + return existing_format->xf_index; + } + else { + /* New format requiring an index. */ + index = formats_hash_table->unique_count; + self->xf_index = index; + lxw_insert_hash_element(formats_hash_table, format_key, self, + sizeof(lxw_format)); + return index; + } +} + +/* + * Set the font_name property. + */ +void +format_set_font_name(lxw_format *self, const char *font_name) +{ + LXW_FORMAT_FIELD_COPY(self->font_name, font_name); +} + +/* + * Set the font_size property. + */ +void +format_set_font_size(lxw_format *self, double size) +{ + + if (size >= LXW_MIN_FONT_SIZE && size <= LXW_MAX_FONT_SIZE) + self->font_size = size; +} + +/* + * Set the font_color property. + */ +void +format_set_font_color(lxw_format *self, lxw_color_t color) +{ + self->font_color = lxw_format_check_color(color); +} + +/* + * Set the bold property. + */ +void +format_set_bold(lxw_format *self) +{ + self->bold = LXW_TRUE; +} + +/* + * Set the italic property. + */ + +void +format_set_italic(lxw_format *self) +{ + self->italic = LXW_TRUE; +} + +/* + * Set the underline property. + */ +void +format_set_underline(lxw_format *self, uint8_t style) +{ + if (style >= LXW_UNDERLINE_SINGLE + && style <= LXW_UNDERLINE_DOUBLE_ACCOUNTING) + self->underline = style; +} + +/* + * Set the font_strikeout property. + */ +void +format_set_font_strikeout(lxw_format *self) +{ + self->font_strikeout = LXW_TRUE; +} + +/* + * Set the font_script property. + */ +void +format_set_font_script(lxw_format *self, uint8_t style) +{ + if (style >= LXW_FONT_SUPERSCRIPT && style <= LXW_FONT_SUBSCRIPT) + self->font_script = style; +} + +/* + * Set the font_outline property. + */ +void +format_set_font_outline(lxw_format *self) +{ + self->font_outline = LXW_TRUE; +} + +/* + * Set the font_shadow property. + */ +void +format_set_font_shadow(lxw_format *self) +{ + self->font_shadow = LXW_TRUE; +} + +/* + * Set the num_format property. + */ +void +format_set_num_format(lxw_format *self, const char *num_format) +{ + LXW_FORMAT_FIELD_COPY(self->num_format, num_format); +} + +/* + * Set the unlocked property. + */ +void +format_set_unlocked(lxw_format *self) +{ + self->locked = LXW_FALSE; +} + +/* + * Set the hidden property. + */ +void +format_set_hidden(lxw_format *self) +{ + self->hidden = LXW_TRUE; +} + +/* + * Set the align property. + */ +void +format_set_align(lxw_format *self, uint8_t value) +{ + if (value >= LXW_ALIGN_LEFT && value <= LXW_ALIGN_DISTRIBUTED) { + self->text_h_align = value; + } + + if (value >= LXW_ALIGN_VERTICAL_TOP + && value <= LXW_ALIGN_VERTICAL_DISTRIBUTED) { + self->text_v_align = value; + } +} + +/* + * Set the text_wrap property. + */ +void +format_set_text_wrap(lxw_format *self) +{ + self->text_wrap = LXW_TRUE; +} + +/* + * Set the rotation property. + */ +void +format_set_rotation(lxw_format *self, int16_t angle) +{ + /* Convert user angle to Excel angle. */ + if (angle == 270) { + self->rotation = 255; + } + else if (angle >= -90 || angle <= 90) { + if (angle < 0) + angle = -angle + 90; + + self->rotation = angle; + } + else { + LXW_WARN("Rotation rotation outside range: -90 <= angle <= 90."); + self->rotation = 0; + } +} + +/* + * Set the indent property. + */ +void +format_set_indent(lxw_format *self, uint8_t value) +{ + self->indent = value; +} + +/* + * Set the shrink property. + */ +void +format_set_shrink(lxw_format *self) +{ + self->shrink = LXW_TRUE; +} + +/* + * Set the text_justlast property. + */ +void +format_set_text_justlast(lxw_format *self) +{ + self->text_justlast = LXW_TRUE; +} + +/* + * Set the pattern property. + */ +void +format_set_pattern(lxw_format *self, uint8_t value) +{ + self->pattern = value; +} + +/* + * Set the bg_color property. + */ +void +format_set_bg_color(lxw_format *self, lxw_color_t color) +{ + self->bg_color = lxw_format_check_color(color); +} + +/* + * Set the fg_color property. + */ +void +format_set_fg_color(lxw_format *self, lxw_color_t color) +{ + self->fg_color = lxw_format_check_color(color); +} + +/* + * Set the border property. + */ +void +format_set_border(lxw_format *self, uint8_t style) +{ + style = _check_border(style); + self->bottom = style; + self->top = style; + self->left = style; + self->right = style; +} + +/* + * Set the border_color property. + */ +void +format_set_border_color(lxw_format *self, lxw_color_t color) +{ + color = lxw_format_check_color(color); + self->bottom_color = color; + self->top_color = color; + self->left_color = color; + self->right_color = color; +} + +/* + * Set the bottom property. + */ +void +format_set_bottom(lxw_format *self, uint8_t style) +{ + self->bottom = _check_border(style); +} + +/* + * Set the bottom_color property. + */ +void +format_set_bottom_color(lxw_format *self, lxw_color_t color) +{ + self->bottom_color = lxw_format_check_color(color); +} + +/* + * Set the left property. + */ +void +format_set_left(lxw_format *self, uint8_t style) +{ + self->left = _check_border(style); +} + +/* + * Set the left_color property. + */ +void +format_set_left_color(lxw_format *self, lxw_color_t color) +{ + self->left_color = lxw_format_check_color(color); +} + +/* + * Set the right property. + */ +void +format_set_right(lxw_format *self, uint8_t style) +{ + self->right = _check_border(style); +} + +/* + * Set the right_color property. + */ +void +format_set_right_color(lxw_format *self, lxw_color_t color) +{ + self->right_color = lxw_format_check_color(color); +} + +/* + * Set the top property. + */ +void +format_set_top(lxw_format *self, uint8_t style) +{ + self->top = _check_border(style); +} + +/* + * Set the top_color property. + */ +void +format_set_top_color(lxw_format *self, lxw_color_t color) +{ + self->top_color = lxw_format_check_color(color); +} + +/* + * Set the diag_type property. + */ +void +format_set_diag_type(lxw_format *self, uint8_t type) +{ + if (type >= LXW_DIAGONAL_BORDER_UP && type <= LXW_DIAGONAL_BORDER_UP_DOWN) + self->diag_type = type; +} + +/* + * Set the diag_color property. + */ +void +format_set_diag_color(lxw_format *self, lxw_color_t color) +{ + self->diag_color = lxw_format_check_color(color); +} + +/* + * Set the diag_border property. + */ +void +format_set_diag_border(lxw_format *self, uint8_t style) +{ + self->diag_border = style; +} + +/* + * Set the num_format_index property. + */ +void +format_set_num_format_index(lxw_format *self, uint8_t value) +{ + self->num_format_index = value; +} + +/* + * Set the valign property. + */ +void +format_set_valign(lxw_format *self, uint8_t value) +{ + self->text_v_align = value; +} + +/* + * Set the reading_order property. + */ +void +format_set_reading_order(lxw_format *self, uint8_t value) +{ + self->reading_order = value; +} + +/* + * Set the font_family property. + */ +void +format_set_font_family(lxw_format *self, uint8_t value) +{ + self->font_family = value; +} + +/* + * Set the font_charset property. + */ +void +format_set_font_charset(lxw_format *self, uint8_t value) +{ + self->font_charset = value; +} + +/* + * Set the font_scheme property. + */ +void +format_set_font_scheme(lxw_format *self, const char *font_scheme) +{ + LXW_FORMAT_FIELD_COPY(self->font_scheme, font_scheme); +} + +/* + * Set the font_condense property. + */ +void +format_set_font_condense(lxw_format *self) +{ + self->font_condense = LXW_TRUE; +} + +/* + * Set the font_extend property. + */ +void +format_set_font_extend(lxw_format *self) +{ + self->font_extend = LXW_TRUE; +} + +/* + * Set the theme property. + */ +void +format_set_theme(lxw_format *self, uint8_t value) +{ + self->theme = value; +} diff --git a/src/libxlsxwriter/hash_table.c b/src/libxlsxwriter/hash_table.c new file mode 100644 index 0000000..6aca28d --- /dev/null +++ b/src/libxlsxwriter/hash_table.c @@ -0,0 +1,223 @@ +/***************************************************************************** + * hash_table - Hash table functions for libxlsxwriter. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include +#include +#include +#include +#include "xlsxwriter/hash_table.h" + +/* + * Calculate the hash key using the FNV function. See: + * http://en.wikipedia.org/wiki/Fowler-Noll-Vo_hash_function + */ +STATIC size_t +_generate_hash_key(void *data, size_t data_len, size_t num_buckets) +{ + unsigned char *p = data; + size_t hash = 2166136261U; + size_t i; + + for (i = 0; i < data_len; i++) + hash = (hash * 16777619) ^ p[i]; + + return hash % num_buckets; +} + +/* + * Check if an element exists in the hash table and return a pointer + * to it if it does. + */ +lxw_hash_element * +lxw_hash_key_exists(lxw_hash_table *lxw_hash, void *key, size_t key_len) +{ + size_t hash_key = _generate_hash_key(key, key_len, lxw_hash->num_buckets); + struct lxw_hash_bucket_list *list; + lxw_hash_element *element; + + if (!lxw_hash->buckets[hash_key]) { + /* The key isn't in the LXW_HASH hash table. */ + return NULL; + } + else { + /* The key is already in the table or there is a hash collision. */ + list = lxw_hash->buckets[hash_key]; + + /* Iterate over the keys in the bucket's linked list. */ + SLIST_FOREACH(element, list, lxw_hash_list_pointers) { + if (memcmp(element->key, key, key_len) == 0) { + /* The key already exists in the table. */ + return element; + } + } + + /* Key doesn't exist in the list so this is a hash collision. */ + return NULL; + } +} + +/* + * Insert or update a value in the LXW_HASH table based on a key + * and return a pointer to the new or updated element. + */ +lxw_hash_element * +lxw_insert_hash_element(lxw_hash_table *lxw_hash, void *key, void *value, + size_t key_len) +{ + size_t hash_key = _generate_hash_key(key, key_len, lxw_hash->num_buckets); + struct lxw_hash_bucket_list *list = NULL; + lxw_hash_element *element = NULL; + + if (!lxw_hash->buckets[hash_key]) { + /* The key isn't in the LXW_HASH hash table. */ + + /* Create a linked list in the bucket to hold the lxw_hash keys. */ + list = calloc(1, sizeof(struct lxw_hash_bucket_list)); + GOTO_LABEL_ON_MEM_ERROR(list, mem_error1); + + /* Initialize the bucket linked list. */ + SLIST_INIT(list); + + /* Create an lxw_hash element to add to the linked list. */ + element = calloc(1, sizeof(lxw_hash_element)); + GOTO_LABEL_ON_MEM_ERROR(element, mem_error1); + + /* Store the key and value. */ + element->key = key; + element->value = value; + + /* Add the lxw_hash element to the bucket's linked list. */ + SLIST_INSERT_HEAD(list, element, lxw_hash_list_pointers); + + /* Also add it to the insertion order linked list. */ + STAILQ_INSERT_TAIL(lxw_hash->order_list, element, + lxw_hash_order_pointers); + + /* Store the bucket list at the hash index. */ + lxw_hash->buckets[hash_key] = list; + + lxw_hash->used_buckets++; + lxw_hash->unique_count++; + + return element; + } + else { + /* The key is already in the table or there is a hash collision. */ + list = lxw_hash->buckets[hash_key]; + + /* Iterate over the keys in the bucket's linked list. */ + SLIST_FOREACH(element, list, lxw_hash_list_pointers) { + if (memcmp(element->key, key, key_len) == 0) { + /* The key already exists in the table. Update the value. */ + if (lxw_hash->free_value) + free(element->value); + + element->value = value; + return element; + } + } + + /* Key doesn't exist in the list so this is a hash collision. + * Create an lxw_hash element to add to the linked list. */ + element = calloc(1, sizeof(lxw_hash_element)); + GOTO_LABEL_ON_MEM_ERROR(element, mem_error2); + + /* Store the key and value. */ + element->key = key; + element->value = value; + + /* Add the lxw_hash element to the bucket linked list. */ + SLIST_INSERT_HEAD(list, element, lxw_hash_list_pointers); + + /* Also add it to the insertion order linked list. */ + STAILQ_INSERT_TAIL(lxw_hash->order_list, element, + lxw_hash_order_pointers); + + lxw_hash->unique_count++; + + return element; + } + +mem_error1: + free(list); + +mem_error2: + free(element); + return NULL; +} + +/* + * Create a new LXW_HASH hash table object. + */ +lxw_hash_table * +lxw_hash_new(uint32_t num_buckets, uint8_t free_key, uint8_t free_value) +{ + /* Create the new hash table. */ + lxw_hash_table *lxw_hash = calloc(1, sizeof(lxw_hash_table)); + RETURN_ON_MEM_ERROR(lxw_hash, NULL); + + lxw_hash->free_key = free_key; + lxw_hash->free_value = free_value; + + /* Add the lxw_hash element buckets. */ + lxw_hash->buckets = + calloc(num_buckets, sizeof(struct lxw_hash_bucket_list *)); + GOTO_LABEL_ON_MEM_ERROR(lxw_hash->buckets, mem_error); + + /* Add a list for tracking the insertion order. */ + lxw_hash->order_list = calloc(1, sizeof(struct lxw_hash_order_list)); + GOTO_LABEL_ON_MEM_ERROR(lxw_hash->order_list, mem_error); + + /* Initialize the order list. */ + STAILQ_INIT(lxw_hash->order_list); + + /* Store the number of buckets to calculate the load factor. */ + lxw_hash->num_buckets = num_buckets; + + return lxw_hash; + +mem_error: + lxw_hash_free(lxw_hash); + return NULL; +} + +/* + * Free the LXW_HASH hash table object. + */ +void +lxw_hash_free(lxw_hash_table *lxw_hash) +{ + size_t i; + lxw_hash_element *element; + lxw_hash_element *element_temp; + + if (!lxw_hash) + return; + + /* Free the lxw_hash_elements and data using the ordered linked list. */ + if (lxw_hash->order_list) { + STAILQ_FOREACH_SAFE(element, lxw_hash->order_list, + lxw_hash_order_pointers, element_temp) { + if (lxw_hash->free_key) + free(element->key); + if (lxw_hash->free_value) + free(element->value); + free(element); + } + } + + /* Free the buckets from the hash table. */ + for (i = 0; i < lxw_hash->num_buckets; i++) { + free(lxw_hash->buckets[i]); + } + + free(lxw_hash->order_list); + free(lxw_hash->buckets); + free(lxw_hash); +} diff --git a/src/libxlsxwriter/packager.c b/src/libxlsxwriter/packager.c new file mode 100644 index 0000000..71bea86 --- /dev/null +++ b/src/libxlsxwriter/packager.c @@ -0,0 +1,962 @@ +/***************************************************************************** + * packager - A library for creating Excel XLSX packager files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/packager.h" +#include "xlsxwriter/hash_table.h" +#include "xlsxwriter/utility.h" + +STATIC lxw_error _add_file_to_zip(lxw_packager *self, FILE * file, + const char *filename); + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ +/* Avoid non MSVC definition of _WIN32 in MinGW. */ + +#ifdef __MINGW32__ +#undef _WIN32 +#endif + +#ifdef _WIN32 + +/* Silence Windows warning with duplicate symbol for SLIST_ENTRY in local + * queue.h and widows.h. */ +#undef SLIST_ENTRY + +#include + + +#ifdef USE_SYSTEM_MINIZIP +#include "minizip/iowin32.h" +#else +#include "../third_party/minizip/iowin32.h" +#endif + +zipFile +_open_zipfile_win32(const char *filename) +{ + int n; + zlib_filefunc64_def filefunc; + + wchar_t wide_filename[_MAX_PATH + 1] = L""; + + /* Build a UTF-16 filename for Win32. */ + n = MultiByteToWideChar(CP_UTF8, 0, filename, (int) strlen(filename), + wide_filename, _MAX_PATH); + + if (n == 0) { + LXW_ERROR("MultiByteToWideChar error"); + return NULL; + } + + /* Use the native Win32 file handling functions with minizip. */ + fill_win32_filefunc64(&filefunc); + + return zipOpen2_64(wide_filename, 0, NULL, &filefunc); +} + +#endif + +/* + * Create a new packager object. + */ +lxw_packager * +lxw_packager_new(const char *filename, char *tmpdir) +{ + 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->tmpdir = tmpdir; + GOTO_LABEL_ON_MEM_ERROR(packager->filename, mem_error); + + packager->buffer_size = LXW_ZIP_BUFFER_SIZE; + + /* Initialize the zip_fileinfo struct to Jan 1 1980 like Excel. */ + packager->zipfile_info.tmz_date.tm_sec = 0; + packager->zipfile_info.tmz_date.tm_min = 0; + packager->zipfile_info.tmz_date.tm_hour = 0; + packager->zipfile_info.tmz_date.tm_mday = 1; + packager->zipfile_info.tmz_date.tm_mon = 0; + packager->zipfile_info.tmz_date.tm_year = 1980; + packager->zipfile_info.dosDate = 0; + packager->zipfile_info.internal_fa = 0; + packager->zipfile_info.external_fa = 0; + + /* Create a zip container for the xlsx file. */ +#ifdef _WIN32 + packager->zipfile = _open_zipfile_win32(packager->filename); +#else + packager->zipfile = zipOpen(packager->filename, 0); +#endif + + if (packager->zipfile == NULL) + goto mem_error; + + return packager; + +mem_error: + lxw_packager_free(packager); + return NULL; +} + +/* + * Free a packager object. + */ +void +lxw_packager_free(lxw_packager *packager) +{ + if (!packager) + return; + + free(packager->buffer); + free(packager->filename); + free(packager); +} + +/***************************************************************************** + * + * File assembly functions. + * + ****************************************************************************/ +/* + * Write the workbook.xml file. + */ +STATIC lxw_error +_write_workbook_file(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_error err; + + workbook->file = lxw_tmpfile(self->tmpdir); + if (!workbook->file) + return LXW_ERROR_CREATING_TMPFILE; + + lxw_workbook_assemble_xml_file(workbook); + + err = _add_file_to_zip(self, workbook->file, "xl/workbook.xml"); + RETURN_ON_ERROR(err); + + fclose(workbook->file); + + return LXW_NO_ERROR; +} + +/* + * Write the worksheet files. + */ +STATIC lxw_error +_write_worksheet_files(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_worksheet *worksheet; + char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + uint16_t index = 1; + lxw_error err; + + STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) { + lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, + "xl/worksheets/sheet%d.xml", index++); + + if (worksheet->optimize_row) + lxw_worksheet_write_single_row(worksheet); + + worksheet->file = lxw_tmpfile(self->tmpdir); + if (!worksheet->file) + return LXW_ERROR_CREATING_TMPFILE; + + lxw_worksheet_assemble_xml_file(worksheet); + + err = _add_file_to_zip(self, worksheet->file, sheetname); + RETURN_ON_ERROR(err); + + fclose(worksheet->file); + } + + return LXW_NO_ERROR; +} + +/* + * Write the /xl/media/image?.xml files. + */ +STATIC lxw_error +_write_image_files(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_worksheet *worksheet; + lxw_image_options *image; + lxw_error err; + FILE *image_stream; + + char filename[LXW_FILENAME_LENGTH] = { 0 }; + uint16_t index = 1; + + STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) { + + if (STAILQ_EMPTY(worksheet->image_data)) + continue; + + STAILQ_FOREACH(image, worksheet->image_data, list_pointers) { + + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "xl/media/image%d.%s", index++, image->extension); + + /* Check that the image file exists and can be opened. */ + image_stream = fopen(image->filename, "rb"); + if (!image_stream) { + LXW_WARN_FORMAT1("Error adding image to xlsx file: file " + "doesn't exist or can't be opened: %s.", + image->filename); + return LXW_ERROR_CREATING_TMPFILE; + } + + err = _add_file_to_zip(self, image_stream, filename); + RETURN_ON_ERROR(err); + + fclose(image_stream); + } + } + + return LXW_NO_ERROR; +} + +/* + * Write the chart files. + */ +STATIC lxw_error +_write_chart_files(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_chart *chart; + char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + uint16_t index = 1; + lxw_error err; + + STAILQ_FOREACH(chart, workbook->ordered_charts, ordered_list_pointers) { + + lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, + "xl/charts/chart%d.xml", index++); + + chart->file = lxw_tmpfile(self->tmpdir); + if (!chart->file) + return LXW_ERROR_CREATING_TMPFILE; + + lxw_chart_assemble_xml_file(chart); + + err = _add_file_to_zip(self, chart->file, sheetname); + RETURN_ON_ERROR(err); + + self->chart_count++; + + fclose(chart->file); + } + + return LXW_NO_ERROR; +} + +/* + * Write the drawing files. + */ +STATIC lxw_error +_write_drawing_files(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_worksheet *worksheet; + lxw_drawing *drawing; + char filename[LXW_FILENAME_LENGTH] = { 0 }; + uint16_t index = 1; + lxw_error err; + + STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) { + drawing = worksheet->drawing; + + if (drawing) { + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "xl/drawings/drawing%d.xml", index++); + + drawing->file = lxw_tmpfile(self->tmpdir); + if (!drawing->file) + return LXW_ERROR_CREATING_TMPFILE; + + lxw_drawing_assemble_xml_file(drawing); + err = _add_file_to_zip(self, drawing->file, filename); + RETURN_ON_ERROR(err); + + fclose(drawing->file); + + self->drawing_count++; + } + } + + return LXW_NO_ERROR; +} + +/* + * Write the sharedStrings.xml file. + */ +STATIC lxw_error +_write_shared_strings_file(lxw_packager *self) +{ + lxw_sst *sst = self->workbook->sst; + 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); + if (!sst->file) + return LXW_ERROR_CREATING_TMPFILE; + + lxw_sst_assemble_xml_file(sst); + + err = _add_file_to_zip(self, sst->file, "xl/sharedStrings.xml"); + RETURN_ON_ERROR(err); + + fclose(sst->file); + + return LXW_NO_ERROR; +} + +/* + * Write the app.xml file. + */ +STATIC lxw_error +_write_app_file(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_worksheet *worksheet; + lxw_defined_name *defined_name; + lxw_app *app; + uint16_t named_range_count = 0; + char *autofilter; + char *has_range; + char number[LXW_ATTR_32] = { 0 }; + lxw_error err = LXW_NO_ERROR; + + app = lxw_app_new(); + if (!app) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + app->file = lxw_tmpfile(self->tmpdir); + if (!app->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + lxw_snprintf(number, LXW_ATTR_32, "%d", self->workbook->num_sheets); + + lxw_app_add_heading_pair(app, "Worksheets", number); + + STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) { + lxw_app_add_part_name(app, worksheet->name); + } + + /* Add the Named Ranges parts. */ + TAILQ_FOREACH(defined_name, workbook->defined_names, list_pointers) { + + has_range = strchr(defined_name->formula, '!'); + autofilter = strstr(defined_name->app_name, "_FilterDatabase"); + + /* Only store defined names with ranges (except for autofilters). */ + if (has_range && !autofilter) { + lxw_app_add_part_name(app, defined_name->app_name); + named_range_count++; + } + } + + /* Add the Named Range heading pairs. */ + if (named_range_count) { + lxw_snprintf(number, LXW_ATTR_32, "%d", named_range_count); + lxw_app_add_heading_pair(app, "Named Ranges", number); + } + + /* Set the app/doc properties. */ + app->properties = workbook->properties; + + lxw_app_assemble_xml_file(app); + + err = _add_file_to_zip(self, app->file, "docProps/app.xml"); + + fclose(app->file); + +mem_error: + lxw_app_free(app); + + return err; +} + +/* + * Write the core.xml file. + */ +STATIC lxw_error +_write_core_file(lxw_packager *self) +{ + lxw_error err = LXW_NO_ERROR; + lxw_core *core = lxw_core_new(); + + if (!core) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + core->file = lxw_tmpfile(self->tmpdir); + if (!core->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + core->properties = self->workbook->properties; + + lxw_core_assemble_xml_file(core); + + err = _add_file_to_zip(self, core->file, "docProps/core.xml"); + + fclose(core->file); + +mem_error: + lxw_core_free(core); + + return err; +} + +/* + * Write the custom.xml file. + */ +STATIC lxw_error +_write_custom_file(lxw_packager *self) +{ + lxw_custom *custom; + lxw_error err = LXW_NO_ERROR; + + if (STAILQ_EMPTY(self->workbook->custom_properties)) + return LXW_NO_ERROR; + + custom = lxw_custom_new(); + if (!custom) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + custom->file = lxw_tmpfile(self->tmpdir); + if (!custom->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + custom->custom_properties = self->workbook->custom_properties; + + lxw_custom_assemble_xml_file(custom); + + err = _add_file_to_zip(self, custom->file, "docProps/custom.xml"); + + fclose(custom->file); + +mem_error: + lxw_custom_free(custom); + return err; +} + +/* + * Write the theme.xml file. + */ +STATIC lxw_error +_write_theme_file(lxw_packager *self) +{ + lxw_error err = LXW_NO_ERROR; + lxw_theme *theme = lxw_theme_new(); + + if (!theme) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + theme->file = lxw_tmpfile(self->tmpdir); + if (!theme->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + lxw_theme_assemble_xml_file(theme); + + err = _add_file_to_zip(self, theme->file, "xl/theme/theme1.xml"); + + fclose(theme->file); + +mem_error: + lxw_theme_free(theme); + + return err; +} + +/* + * Write the styles.xml file. + */ +STATIC lxw_error +_write_styles_file(lxw_packager *self) +{ + lxw_styles *styles = lxw_styles_new(); + lxw_hash_element *hash_element; + lxw_error err = LXW_NO_ERROR; + + if (!styles) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + /* Copy the unique and in-use formats from the workbook to the styles + * xf_format list. */ + LXW_FOREACH_ORDERED(hash_element, self->workbook->used_xf_formats) { + lxw_format *workbook_format = (lxw_format *) hash_element->value; + lxw_format *style_format = lxw_format_new(); + + if (!style_format) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + memcpy(style_format, workbook_format, sizeof(lxw_format)); + STAILQ_INSERT_TAIL(styles->xf_formats, style_format, list_pointers); + } + + styles->font_count = self->workbook->font_count; + styles->border_count = self->workbook->border_count; + styles->fill_count = self->workbook->fill_count; + styles->num_format_count = self->workbook->num_format_count; + styles->xf_count = self->workbook->used_xf_formats->unique_count; + + styles->file = lxw_tmpfile(self->tmpdir); + if (!styles->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + lxw_styles_assemble_xml_file(styles); + + err = _add_file_to_zip(self, styles->file, "xl/styles.xml"); + + fclose(styles->file); + +mem_error: + lxw_styles_free(styles); + + return err; +} + +/* + * Write the ContentTypes.xml file. + */ +STATIC lxw_error +_write_content_types_file(lxw_packager *self) +{ + lxw_content_types *content_types = lxw_content_types_new(); + lxw_workbook *workbook = self->workbook; + lxw_worksheet *worksheet; + char filename[LXW_MAX_ATTRIBUTE_LENGTH] = { 0 }; + uint16_t index = 1; + lxw_error err = LXW_NO_ERROR; + + if (!content_types) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + content_types->file = lxw_tmpfile(self->tmpdir); + if (!content_types->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + if (workbook->has_png) + lxw_ct_add_default(content_types, "png", "image/png"); + + if (workbook->has_jpeg) + lxw_ct_add_default(content_types, "jpeg", "image/jpeg"); + + if (workbook->has_bmp) + lxw_ct_add_default(content_types, "bmp", "image/bmp"); + + STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) { + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "/xl/worksheets/sheet%d.xml", index++); + lxw_ct_add_worksheet_name(content_types, filename); + } + + for (index = 1; index <= self->chart_count; index++) { + lxw_snprintf(filename, LXW_FILENAME_LENGTH, "/xl/charts/chart%d.xml", + index); + lxw_ct_add_chart_name(content_types, filename); + } + + for (index = 1; index <= self->drawing_count; index++) { + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "/xl/drawings/drawing%d.xml", index); + lxw_ct_add_drawing_name(content_types, filename); + } + + if (workbook->sst->string_count) + lxw_ct_add_shared_strings(content_types); + + if (!STAILQ_EMPTY(self->workbook->custom_properties)) + lxw_ct_add_custom_properties(content_types); + + lxw_content_types_assemble_xml_file(content_types); + + err = _add_file_to_zip(self, content_types->file, "[Content_Types].xml"); + + fclose(content_types->file); + +mem_error: + lxw_content_types_free(content_types); + + return err; +} + +/* + * Write the workbook .rels xml file. + */ +STATIC lxw_error +_write_workbook_rels_file(lxw_packager *self) +{ + lxw_relationships *rels = lxw_relationships_new(); + lxw_workbook *workbook = self->workbook; + lxw_worksheet *worksheet; + char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + uint16_t index = 1; + lxw_error err = LXW_NO_ERROR; + + if (!rels) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + rels->file = lxw_tmpfile(self->tmpdir); + if (!rels->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) { + lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "worksheets/sheet%d.xml", + index++); + lxw_add_document_relationship(rels, "/worksheet", sheetname); + } + + lxw_add_document_relationship(rels, "/theme", "theme/theme1.xml"); + lxw_add_document_relationship(rels, "/styles", "styles.xml"); + + if (workbook->sst->string_count) + lxw_add_document_relationship(rels, "/sharedStrings", + "sharedStrings.xml"); + + lxw_relationships_assemble_xml_file(rels); + + err = _add_file_to_zip(self, rels->file, "xl/_rels/workbook.xml.rels"); + + fclose(rels->file); + +mem_error: + lxw_free_relationships(rels); + + return err; +} + +/* + * Write the worksheet .rels files for worksheets that contain links to + * external data such as hyperlinks or drawings. + */ +STATIC lxw_error +_write_worksheet_rels_file(lxw_packager *self) +{ + lxw_relationships *rels; + lxw_rel_tuple *rel; + lxw_workbook *workbook = self->workbook; + lxw_worksheet *worksheet; + char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + uint16_t index = 0; + lxw_error err; + + STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) { + + index++; + + if (STAILQ_EMPTY(worksheet->external_hyperlinks) && + STAILQ_EMPTY(worksheet->external_drawing_links)) + continue; + + rels = lxw_relationships_new(); + + rels->file = lxw_tmpfile(self->tmpdir); + if (!rels->file) { + lxw_free_relationships(rels); + return LXW_ERROR_CREATING_TMPFILE; + } + + STAILQ_FOREACH(rel, worksheet->external_hyperlinks, list_pointers) { + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + } + + STAILQ_FOREACH(rel, worksheet->external_drawing_links, list_pointers) { + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + } + + lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, + "xl/worksheets/_rels/sheet%d.xml.rels", index); + + lxw_relationships_assemble_xml_file(rels); + + err = _add_file_to_zip(self, rels->file, sheetname); + + fclose(rels->file); + lxw_free_relationships(rels); + + RETURN_ON_ERROR(err); + } + + return LXW_NO_ERROR; +} + +/* + * Write the drawing .rels files for worksheets that contain charts or + * drawings. + */ +STATIC lxw_error +_write_drawing_rels_file(lxw_packager *self) +{ + lxw_relationships *rels; + lxw_rel_tuple *rel; + lxw_workbook *workbook = self->workbook; + lxw_worksheet *worksheet; + char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + uint16_t index = 1; + lxw_error err; + + STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) { + + if (STAILQ_EMPTY(worksheet->drawing_links)) + continue; + + rels = lxw_relationships_new(); + + rels->file = lxw_tmpfile(self->tmpdir); + if (!rels->file) { + lxw_free_relationships(rels); + return LXW_ERROR_CREATING_TMPFILE; + } + + STAILQ_FOREACH(rel, worksheet->drawing_links, list_pointers) { + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + + } + + lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, + "xl/drawings/_rels/drawing%d.xml.rels", index++); + + lxw_relationships_assemble_xml_file(rels); + + err = _add_file_to_zip(self, rels->file, sheetname); + + fclose(rels->file); + lxw_free_relationships(rels); + + RETURN_ON_ERROR(err); + } + + return LXW_NO_ERROR; +} + +/* + * Write the _rels/.rels xml file. + */ +STATIC lxw_error +_write_root_rels_file(lxw_packager *self) +{ + lxw_relationships *rels = lxw_relationships_new(); + lxw_error err = LXW_NO_ERROR; + + if (!rels) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto mem_error; + } + + rels->file = lxw_tmpfile(self->tmpdir); + if (!rels->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + lxw_add_document_relationship(rels, "/officeDocument", "xl/workbook.xml"); + + lxw_add_package_relationship(rels, + "/metadata/core-properties", + "docProps/core.xml"); + + lxw_add_document_relationship(rels, + "/extended-properties", "docProps/app.xml"); + + if (!STAILQ_EMPTY(self->workbook->custom_properties)) + lxw_add_document_relationship(rels, + "/custom-properties", + "docProps/custom.xml"); + + lxw_relationships_assemble_xml_file(rels); + + err = _add_file_to_zip(self, rels->file, "_rels/.rels"); + + fclose(rels->file); + +mem_error: + lxw_free_relationships(rels); + + return err; +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ + +STATIC lxw_error +_add_file_to_zip(lxw_packager *self, FILE * file, const char *filename) +{ + int16_t error = ZIP_OK; + size_t size_read; + + error = zipOpenNewFileInZip4_64(self->zipfile, + filename, + &self->zipfile_info, + NULL, 0, NULL, 0, NULL, + Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0, + -MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, NULL, 0, 0, 0, 0); + + if (error != ZIP_OK) { + LXW_ERROR("Error adding member to zipfile"); + RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD); + } + + fflush(file); + rewind(file); + + size_read = fread(self->buffer, 1, self->buffer_size, file); + + while (size_read) { + + if (size_read < self->buffer_size) { + if (feof(file) == 0) { + LXW_ERROR("Error reading member file data"); + RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD); + } + } + + error = zipWriteInFileInZip(self->zipfile, + self->buffer, (unsigned int) size_read); + + if (error < 0) { + LXW_ERROR("Error in writing member in the zipfile"); + RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD); + } + + size_read = fread(self->buffer, 1, self->buffer_size, file); + } + + if (error < 0) { + RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD); + } + else { + error = zipCloseFileInZip(self->zipfile); + if (error != ZIP_OK) { + LXW_ERROR("Error in closing member in the zipfile"); + RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD); + } + } + + return LXW_NO_ERROR; +} + +/* + * Write the xml files that make up the XLXS OPC package. + */ +lxw_error +lxw_create_package(lxw_packager *self) +{ + lxw_error error; + int8_t zip_error; + + error = _write_worksheet_files(self); + RETURN_ON_ERROR(error); + + error = _write_workbook_file(self); + RETURN_ON_ERROR(error); + + error = _write_chart_files(self); + RETURN_ON_ERROR(error); + + error = _write_drawing_files(self); + RETURN_ON_ERROR(error); + + error = _write_shared_strings_file(self); + RETURN_ON_ERROR(error); + + error = _write_app_file(self); + RETURN_ON_ERROR(error); + + error = _write_core_file(self); + RETURN_ON_ERROR(error); + + error = _write_custom_file(self); + RETURN_ON_ERROR(error); + + error = _write_theme_file(self); + RETURN_ON_ERROR(error); + + error = _write_styles_file(self); + RETURN_ON_ERROR(error); + + error = _write_content_types_file(self); + RETURN_ON_ERROR(error); + + error = _write_workbook_rels_file(self); + RETURN_ON_ERROR(error); + + error = _write_worksheet_rels_file(self); + RETURN_ON_ERROR(error); + + error = _write_drawing_rels_file(self); + RETURN_ON_ERROR(error); + + error = _write_image_files(self); + RETURN_ON_ERROR(error); + + error = _write_root_rels_file(self); + RETURN_ON_ERROR(error); + + zip_error = zipClose(self->zipfile, NULL); + if (zip_error) { + RETURN_ON_ZIP_ERROR(zip_error, LXW_ERROR_ZIP_CLOSE); + } + + return LXW_NO_ERROR; +} diff --git a/src/libxlsxwriter/relationships.c b/src/libxlsxwriter/relationships.c new file mode 100644 index 0000000..b88fae7 --- /dev/null +++ b/src/libxlsxwriter/relationships.c @@ -0,0 +1,245 @@ +/***************************************************************************** + * relationships - A library for creating Excel XLSX relationships files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/relationships.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new relationships object. + */ +lxw_relationships * +lxw_relationships_new() +{ + lxw_relationships *rels = calloc(1, sizeof(lxw_relationships)); + GOTO_LABEL_ON_MEM_ERROR(rels, mem_error); + + rels->relationships = calloc(1, sizeof(struct lxw_rel_tuples)); + GOTO_LABEL_ON_MEM_ERROR(rels->relationships, mem_error); + STAILQ_INIT(rels->relationships); + + return rels; + +mem_error: + lxw_free_relationships(rels); + return NULL; +} + +/* + * Free a relationships object. + */ +void +lxw_free_relationships(lxw_relationships *rels) +{ + lxw_rel_tuple *relationship; + + if (!rels) + return; + + if (rels->relationships) { + while (!STAILQ_EMPTY(rels->relationships)) { + relationship = STAILQ_FIRST(rels->relationships); + STAILQ_REMOVE_HEAD(rels->relationships, list_pointers); + free(relationship->type); + free(relationship->target); + free(relationship->target_mode); + free(relationship); + } + + free(rels->relationships); + } + + free(rels); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_relationships_xml_declaration(lxw_relationships *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_write_relationship(lxw_relationships *self, const char *type, + const char *target, const char *target_mode) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char r_id[LXW_MAX_ATTRIBUTE_LENGTH] = { 0 }; + + self->rel_id++; + lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", self->rel_id); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("Id", r_id); + LXW_PUSH_ATTRIBUTES_STR("Type", type); + LXW_PUSH_ATTRIBUTES_STR("Target", target); + + if (target_mode) + LXW_PUSH_ATTRIBUTES_STR("TargetMode", target_mode); + + lxw_xml_empty_tag(self->file, "Relationship", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_relationships(lxw_relationships *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + lxw_rel_tuple *rel; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns", LXW_SCHEMA_PACKAGE); + + lxw_xml_start_tag(self->file, "Relationships", &attributes); + + STAILQ_FOREACH(rel, self->relationships, list_pointers) { + _write_relationship(self, rel->type, rel->target, rel->target_mode); + } + + LXW_FREE_ATTRIBUTES(); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Assemble and write the XML file. + */ +void +lxw_relationships_assemble_xml_file(lxw_relationships *self) +{ + /* Write the XML declaration. */ + _relationships_xml_declaration(self); + + _write_relationships(self); + + /* Close the relationships tag. */ + lxw_xml_end_tag(self->file, "Relationships"); +} + +/* + * Add a generic container relationship to XLSX .rels xml files. + */ +STATIC void +_add_relationship(lxw_relationships *self, const char *schema, + const char *type, const char *target, + const char *target_mode) +{ + lxw_rel_tuple *relationship; + + if (!schema || !type || !target) + return; + + relationship = calloc(1, sizeof(lxw_rel_tuple)); + GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); + + relationship->type = calloc(1, LXW_MAX_ATTRIBUTE_LENGTH); + GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); + + /* Add the schema to the relationship type. */ + lxw_snprintf(relationship->type, LXW_MAX_ATTRIBUTE_LENGTH, "%s%s", + schema, type); + + relationship->target = lxw_strdup(target); + GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); + + if (target_mode) { + relationship->target_mode = lxw_strdup(target_mode); + GOTO_LABEL_ON_MEM_ERROR(relationship->target_mode, mem_error); + } + + STAILQ_INSERT_TAIL(self->relationships, relationship, list_pointers); + + return; + +mem_error: + if (relationship) { + free(relationship->type); + free(relationship->target); + free(relationship->target_mode); + free(relationship); + } +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ + +/* + * Add a document relationship to XLSX .rels xml files. + */ +void +lxw_add_document_relationship(lxw_relationships *self, const char *type, + const char *target) +{ + _add_relationship(self, LXW_SCHEMA_DOCUMENT, type, target, NULL); +} + +/* + * Add a package relationship to XLSX .rels xml files. + */ +void +lxw_add_package_relationship(lxw_relationships *self, const char *type, + const char *target) +{ + _add_relationship(self, LXW_SCHEMA_PACKAGE, type, target, NULL); +} + +/* + * Add a MS schema package relationship to XLSX .rels xml files. + */ +void +lxw_add_ms_package_relationship(lxw_relationships *self, const char *type, + const char *target) +{ + _add_relationship(self, LXW_SCHEMA_MS, type, target, NULL); +} + +/* + * Add a worksheet relationship to sheet .rels xml files. + */ +void +lxw_add_worksheet_relationship(lxw_relationships *self, const char *type, + const char *target, const char *target_mode) +{ + _add_relationship(self, LXW_SCHEMA_DOCUMENT, type, target, target_mode); +} diff --git a/src/libxlsxwriter/shared_strings.c b/src/libxlsxwriter/shared_strings.c new file mode 100644 index 0000000..c4b16a8 --- /dev/null +++ b/src/libxlsxwriter/shared_strings.c @@ -0,0 +1,266 @@ +/***************************************************************************** + * shared_strings - A library for creating Excel XLSX sst files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/shared_strings.h" +#include "xlsxwriter/utility.h" +#include + +/* + * Forward declarations. + */ + +STATIC int _element_cmp(struct sst_element *element1, + struct sst_element *element2); + +#ifndef __clang_analyzer__ +LXW_RB_GENERATE_ELEMENT(sst_rb_tree, sst_element, sst_tree_pointers, + _element_cmp); +#endif + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new SST SharedString object. + */ +lxw_sst * +lxw_sst_new() +{ + /* Create the new shared string table. */ + lxw_sst *sst = calloc(1, sizeof(lxw_sst)); + RETURN_ON_MEM_ERROR(sst, NULL); + + /* Add the sst RB tree. */ + sst->rb_tree = calloc(1, sizeof(struct sst_rb_tree)); + GOTO_LABEL_ON_MEM_ERROR(sst->rb_tree, mem_error); + + /* Add a list for tracking the insertion order. */ + sst->order_list = calloc(1, sizeof(struct sst_order_list)); + GOTO_LABEL_ON_MEM_ERROR(sst->order_list, mem_error); + + /* Initialize the order list. */ + STAILQ_INIT(sst->order_list); + + /* Initialize the RB tree. */ + RB_INIT(sst->rb_tree); + + return sst; + +mem_error: + lxw_sst_free(sst); + return NULL; +} + +/* + * Free a SST SharedString table object. + */ +void +lxw_sst_free(lxw_sst *sst) +{ + struct sst_element *sst_element; + struct sst_element *sst_element_temp; + + if (!sst) + return; + + /* Free the sst_elements and their data using the ordered linked list. */ + if (sst->order_list) { + STAILQ_FOREACH_SAFE(sst_element, sst->order_list, sst_order_pointers, + sst_element_temp) { + + if (sst_element && sst_element->string) + free(sst_element->string); + if (sst_element) + free(sst_element); + } + } + + free(sst->order_list); + free(sst->rb_tree); + free(sst); +} + +/* + * Comparator for the element structure + */ +STATIC int +_element_cmp(struct sst_element *element1, struct sst_element *element2) +{ + return strcmp(element1->string, element2->string); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ +/* + * Write the XML declaration. + */ +STATIC void +_sst_xml_declaration(lxw_sst *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_write_t(lxw_sst *self, char *string) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + /* Add attribute to preserve leading or trailing whitespace. */ + if (isspace((unsigned char) string[0]) + || isspace((unsigned char) string[strlen(string) - 1])) + LXW_PUSH_ATTRIBUTES_STR("xml:space", "preserve"); + + lxw_xml_data_element(self->file, "t", string, &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_si(lxw_sst *self, char *string) +{ + uint8_t escaped_string = LXW_FALSE; + + lxw_xml_start_tag(self->file, "si", NULL); + + /* Look for and escape control chars in the string. */ + if (strpbrk(string, "\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C" + "\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16" + "\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F")) { + string = lxw_escape_control_characters(string); + escaped_string = LXW_TRUE; + } + + /* Write the t element. */ + _write_t(self, string); + + lxw_xml_end_tag(self->file, "si"); + + if (escaped_string) + free(string); +} + +/* + * Write the element. + */ +STATIC void +_write_sst(lxw_sst *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns[] = + "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); + LXW_PUSH_ATTRIBUTES_INT("count", self->string_count); + LXW_PUSH_ATTRIBUTES_INT("uniqueCount", self->unique_count); + + lxw_xml_start_tag(self->file, "sst", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Assemble and write the XML file. + */ +STATIC void +_write_sst_strings(lxw_sst *self) +{ + struct sst_element *sst_element; + + STAILQ_FOREACH(sst_element, self->order_list, sst_order_pointers) { + /* Write the si element. */ + _write_si(self, sst_element->string); + } +} + +/* + * Assemble and write the XML file. + */ +void +lxw_sst_assemble_xml_file(lxw_sst *self) +{ + /* Write the XML declaration. */ + _sst_xml_declaration(self); + + /* Write the sst element. */ + _write_sst(self); + + /* Write the sst strings. */ + _write_sst_strings(self); + + /* Close the sst tag. */ + lxw_xml_end_tag(self->file, "sst"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ +/* + * Add to or find a string in the SST SharedString table and return it's index. + */ +struct sst_element * +lxw_get_sst_index(lxw_sst *sst, const char *string) +{ + struct sst_element *element; + struct sst_element *existing_element; + + /* Create an sst element to potentially add to the table. */ + element = calloc(1, sizeof(struct sst_element)); + if (!element) + return NULL; + + /* Create potential new element with the string and its index. */ + element->index = sst->unique_count; + element->string = lxw_strdup(string); + + /* Try to insert it and see whether we already have that string. */ + existing_element = RB_INSERT(sst_rb_tree, sst->rb_tree, element); + + /* If existing_element is not NULL, then it already existed. */ + /* Free new created element. */ + if (existing_element) { + free(element->string); + free(element); + sst->string_count++; + return existing_element; + } + + /* If it didn't exist, also add it to the insertion order linked list. */ + STAILQ_INSERT_TAIL(sst->order_list, element, sst_order_pointers); + + /* Update SST string counts. */ + sst->string_count++; + sst->unique_count++; + return element; +} diff --git a/src/libxlsxwriter/styles.c b/src/libxlsxwriter/styles.c new file mode 100644 index 0000000..234523f --- /dev/null +++ b/src/libxlsxwriter/styles.c @@ -0,0 +1,1088 @@ +/***************************************************************************** + * styles - A library for creating Excel XLSX styles files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/styles.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new styles object. + */ +lxw_styles * +lxw_styles_new() +{ + lxw_styles *styles = calloc(1, sizeof(lxw_styles)); + GOTO_LABEL_ON_MEM_ERROR(styles, mem_error); + + styles->xf_formats = calloc(1, sizeof(struct lxw_formats)); + GOTO_LABEL_ON_MEM_ERROR(styles->xf_formats, mem_error); + + STAILQ_INIT(styles->xf_formats); + + return styles; + +mem_error: + lxw_styles_free(styles); + return NULL; +} + +/* + * Free a styles object. + */ +void +lxw_styles_free(lxw_styles *styles) +{ + lxw_format *format; + + if (!styles) + return; + + /* Free the formats in the styles. */ + if (styles->xf_formats) { + while (!STAILQ_EMPTY(styles->xf_formats)) { + format = STAILQ_FIRST(styles->xf_formats); + STAILQ_REMOVE_HEAD(styles->xf_formats, list_pointers); + free(format); + } + free(styles->xf_formats); + } + + free(styles); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_styles_xml_declaration(lxw_styles *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_write_style_sheet(lxw_styles *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns", + "http://schemas.openxmlformats.org/spreadsheetml/2006/main"); + + lxw_xml_start_tag(self->file, "styleSheet", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_num_fmt(lxw_styles *self, uint16_t num_fmt_id, char *format_code) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("numFmtId", num_fmt_id); + LXW_PUSH_ATTRIBUTES_STR("formatCode", format_code); + + lxw_xml_empty_tag(self->file, "numFmt", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_num_fmts(lxw_styles *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + lxw_format *format; + + if (!self->num_format_count) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("count", self->num_format_count); + + lxw_xml_start_tag(self->file, "numFmts", &attributes); + + /* Write the numFmts elements. */ + STAILQ_FOREACH(format, self->xf_formats, list_pointers) { + + /* Ignore built-in number formats, i.e., < 164. */ + if (format->num_format_index < 164) + continue; + + _write_num_fmt(self, format->num_format_index, format->num_format); + } + + lxw_xml_end_tag(self->file, "numFmts"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_font_size(lxw_styles *self, double font_size) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_DBL("val", font_size); + + lxw_xml_empty_tag(self->file, "sz", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element for themes. + */ +STATIC void +_write_font_color_theme(lxw_styles *self, uint8_t theme) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("theme", theme); + + lxw_xml_empty_tag(self->file, "color", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element for RGB colors. + */ +STATIC void +_write_font_color_rgb(lxw_styles *self, int32_t rgb) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char rgb_str[LXW_ATTR_32]; + + lxw_snprintf(rgb_str, LXW_ATTR_32, "FF%06X", rgb & LXW_COLOR_MASK); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("rgb", rgb_str); + + lxw_xml_empty_tag(self->file, "color", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_font_name(lxw_styles *self, const char *font_name) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (*font_name) + LXW_PUSH_ATTRIBUTES_STR("val", font_name); + else + LXW_PUSH_ATTRIBUTES_STR("val", LXW_DEFAULT_FONT_NAME); + + lxw_xml_empty_tag(self->file, "name", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_font_family(lxw_styles *self, uint8_t font_family) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("val", font_family); + + lxw_xml_empty_tag(self->file, "family", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_font_scheme(lxw_styles *self, const char *font_scheme) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (*font_scheme) + LXW_PUSH_ATTRIBUTES_STR("val", font_scheme); + else + LXW_PUSH_ATTRIBUTES_STR("val", "minor"); + + lxw_xml_empty_tag(self->file, "scheme", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the underline font element. + */ +STATIC void +_write_font_underline(lxw_styles *self, uint8_t underline) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + /* Handle the underline variants. */ + if (underline == LXW_UNDERLINE_DOUBLE) + LXW_PUSH_ATTRIBUTES_STR("val", "double"); + else if (underline == LXW_UNDERLINE_SINGLE_ACCOUNTING) + LXW_PUSH_ATTRIBUTES_STR("val", "singleAccounting"); + else if (underline == LXW_UNDERLINE_DOUBLE_ACCOUNTING) + LXW_PUSH_ATTRIBUTES_STR("val", "doubleAccounting"); + /* Default to single underline. */ + + lxw_xml_empty_tag(self->file, "u", &attributes); + + LXW_FREE_ATTRIBUTES(); + +} + +/* + * Write the font sub-element. + */ +STATIC void +_write_vert_align(lxw_styles *self, const char *align) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", align); + + lxw_xml_empty_tag(self->file, "vertAlign", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_font(lxw_styles *self, lxw_format *format) +{ + lxw_xml_start_tag(self->file, "font", NULL); + + if (format->bold) + lxw_xml_empty_tag(self->file, "b", NULL); + + if (format->italic) + lxw_xml_empty_tag(self->file, "i", NULL); + + if (format->font_strikeout) + lxw_xml_empty_tag(self->file, "strike", NULL); + + if (format->font_outline) + lxw_xml_empty_tag(self->file, "outline", NULL); + + if (format->font_shadow) + lxw_xml_empty_tag(self->file, "shadow", NULL); + + if (format->underline) + _write_font_underline(self, format->underline); + + if (format->font_script == LXW_FONT_SUPERSCRIPT) + _write_vert_align(self, "superscript"); + + if (format->font_script == LXW_FONT_SUBSCRIPT) + _write_vert_align(self, "subscript"); + + if (format->font_size > 0.0) + _write_font_size(self, format->font_size); + + if (format->theme) + _write_font_color_theme(self, format->theme); + else if (format->font_color != LXW_COLOR_UNSET) + _write_font_color_rgb(self, format->font_color); + else + _write_font_color_theme(self, LXW_DEFAULT_FONT_THEME); + + _write_font_name(self, format->font_name); + _write_font_family(self, format->font_family); + + /* Only write the scheme element for the default font type if it + * is a hyperlink. */ + if ((!*format->font_name + || strcmp(LXW_DEFAULT_FONT_NAME, format->font_name) == 0) + && !format->hyperlink) { + _write_font_scheme(self, format->font_scheme); + } + + lxw_xml_end_tag(self->file, "font"); +} + +/* + * Write the element. + */ +STATIC void +_write_fonts(lxw_styles *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + lxw_format *format; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("count", self->font_count); + + lxw_xml_start_tag(self->file, "fonts", &attributes); + + STAILQ_FOREACH(format, self->xf_formats, list_pointers) { + if (format->has_font) + _write_font(self, format); + } + + lxw_xml_end_tag(self->file, "fonts"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the default element. + */ +STATIC void +_write_default_fill(lxw_styles *self, const char *pattern) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("patternType", pattern); + + lxw_xml_start_tag(self->file, "fill", NULL); + lxw_xml_empty_tag(self->file, "patternFill", &attributes); + lxw_xml_end_tag(self->file, "fill"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_fg_color(lxw_styles *self, lxw_color_t color) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char rgb_str[LXW_ATTR_32]; + + LXW_INIT_ATTRIBUTES(); + + lxw_snprintf(rgb_str, LXW_ATTR_32, "FF%06X", color & LXW_COLOR_MASK); + LXW_PUSH_ATTRIBUTES_STR("rgb", rgb_str); + + lxw_xml_empty_tag(self->file, "fgColor", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_bg_color(lxw_styles *self, lxw_color_t color) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char rgb_str[LXW_ATTR_32]; + + LXW_INIT_ATTRIBUTES(); + + if (color == LXW_COLOR_UNSET) { + LXW_PUSH_ATTRIBUTES_STR("indexed", "64"); + } + else { + lxw_snprintf(rgb_str, LXW_ATTR_32, "FF%06X", color & LXW_COLOR_MASK); + LXW_PUSH_ATTRIBUTES_STR("rgb", rgb_str); + } + + lxw_xml_empty_tag(self->file, "bgColor", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_fill(lxw_styles *self, lxw_format *format) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + uint8_t pattern = format->pattern; + lxw_color_t bg_color = format->bg_color; + lxw_color_t fg_color = format->fg_color; + + char *patterns[] = { + "none", + "solid", + "mediumGray", + "darkGray", + "lightGray", + "darkHorizontal", + "darkVertical", + "darkDown", + "darkUp", + "darkGrid", + "darkTrellis", + "lightHorizontal", + "lightVertical", + "lightDown", + "lightUp", + "lightGrid", + "lightTrellis", + "gray125", + "gray0625", + }; + + LXW_INIT_ATTRIBUTES(); + + lxw_xml_start_tag(self->file, "fill", NULL); + + if (pattern) + LXW_PUSH_ATTRIBUTES_STR("patternType", patterns[pattern]); + + lxw_xml_start_tag(self->file, "patternFill", &attributes); + + if (fg_color != LXW_COLOR_UNSET) + _write_fg_color(self, fg_color); + + _write_bg_color(self, bg_color); + + lxw_xml_end_tag(self->file, "patternFill"); + lxw_xml_end_tag(self->file, "fill"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_fills(lxw_styles *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + lxw_format *format; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("count", self->fill_count); + + lxw_xml_start_tag(self->file, "fills", &attributes); + + /* Write the default fills. */ + _write_default_fill(self, "none"); + _write_default_fill(self, "gray125"); + + STAILQ_FOREACH(format, self->xf_formats, list_pointers) { + if (format->has_fill) + _write_fill(self, format); + } + + lxw_xml_end_tag(self->file, "fills"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the border element. + */ +STATIC void +_write_border_color(lxw_styles *self, lxw_color_t color) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char rgb_str[LXW_ATTR_32]; + + LXW_INIT_ATTRIBUTES(); + + if (color != LXW_COLOR_UNSET) { + lxw_snprintf(rgb_str, LXW_ATTR_32, "FF%06X", color & LXW_COLOR_MASK); + LXW_PUSH_ATTRIBUTES_STR("rgb", rgb_str); + } + else { + LXW_PUSH_ATTRIBUTES_STR("auto", "1"); + } + + lxw_xml_empty_tag(self->file, "color", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the sub elements such as , , etc. + */ +STATIC void +_write_sub_border(lxw_styles *self, const char *type, uint8_t style, + lxw_color_t color) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + char *border_styles[] = { + "none", + "thin", + "medium", + "dashed", + "dotted", + "thick", + "double", + "hair", + "mediumDashed", + "dashDot", + "mediumDashDot", + "dashDotDot", + "mediumDashDotDot", + "slantDashDot", + }; + + if (!style) { + lxw_xml_empty_tag(self->file, type, NULL); + return; + } + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("style", border_styles[style]); + + lxw_xml_start_tag(self->file, type, &attributes); + + _write_border_color(self, color); + + lxw_xml_end_tag(self->file, type); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_border(lxw_styles *self, lxw_format *format) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + /* Add attributes for diagonal borders. */ + if (format->diag_type == LXW_DIAGONAL_BORDER_UP) { + LXW_PUSH_ATTRIBUTES_STR("diagonalUp", "1"); + } + else if (format->diag_type == LXW_DIAGONAL_BORDER_DOWN) { + LXW_PUSH_ATTRIBUTES_STR("diagonalDown", "1"); + } + else if (format->diag_type == LXW_DIAGONAL_BORDER_UP_DOWN) { + LXW_PUSH_ATTRIBUTES_STR("diagonalUp", "1"); + LXW_PUSH_ATTRIBUTES_STR("diagonalDown", "1"); + } + + /* Ensure that a default diag border is set if the diag type is set. */ + if (format->diag_type && !format->diag_border) { + format->diag_border = 1; + } + + /* Write the start border tag. */ + lxw_xml_start_tag(self->file, "border", &attributes); + + /* Write the sub elements. */ + _write_sub_border(self, "left", format->left, format->left_color); + _write_sub_border(self, "right", format->right, format->right_color); + _write_sub_border(self, "top", format->top, format->top_color); + _write_sub_border(self, "bottom", format->bottom, format->bottom_color); + _write_sub_border(self, + "diagonal", format->diag_border, format->diag_color); + + lxw_xml_end_tag(self->file, "border"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_borders(lxw_styles *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + lxw_format *format; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("count", self->border_count); + + lxw_xml_start_tag(self->file, "borders", &attributes); + + STAILQ_FOREACH(format, self->xf_formats, list_pointers) { + if (format->has_border) + _write_border(self, format); + } + + lxw_xml_end_tag(self->file, "borders"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element for styles. + */ +STATIC void +_write_style_xf(lxw_styles *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("numFmtId", "0"); + LXW_PUSH_ATTRIBUTES_STR("fontId", "0"); + LXW_PUSH_ATTRIBUTES_STR("fillId", "0"); + LXW_PUSH_ATTRIBUTES_STR("borderId", "0"); + + lxw_xml_empty_tag(self->file, "xf", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_cell_style_xfs(lxw_styles *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("count", "1"); + + lxw_xml_start_tag(self->file, "cellStyleXfs", &attributes); + _write_style_xf(self); + lxw_xml_end_tag(self->file, "cellStyleXfs"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Check if a format struct has alignment properties set and the + * "applyAlignment" attribute should be set. + */ +STATIC uint8_t +_apply_alignment(lxw_format *format) +{ + return format->text_h_align != LXW_ALIGN_NONE + || format->text_v_align != LXW_ALIGN_NONE + || format->indent != 0 + || format->rotation != 0 + || format->text_wrap != 0 + || format->shrink != 0 || format->reading_order != 0; +} + +/* + * Check if a format struct has alignment properties set apart from the + * LXW_ALIGN_VERTICAL_BOTTOM which Excel treats as a default. + */ +STATIC uint8_t +_has_alignment(lxw_format *format) +{ + return format->text_h_align != LXW_ALIGN_NONE + || !(format->text_v_align == LXW_ALIGN_NONE || + format->text_v_align == LXW_ALIGN_VERTICAL_BOTTOM) + || format->indent != 0 + || format->rotation != 0 + || format->text_wrap != 0 + || format->shrink != 0 || format->reading_order != 0; +} + +/* + * Write the element. + */ +STATIC void +_write_alignment(lxw_styles *self, lxw_format *format) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + int16_t rotation = format->rotation; + + LXW_INIT_ATTRIBUTES(); + + /* Indent is only allowed for horizontal left, right and distributed. */ + /* If it is defined for any other alignment or no alignment has been */ + /* set then default to left alignment. */ + if (format->indent + && format->text_h_align != LXW_ALIGN_LEFT + && format->text_h_align != LXW_ALIGN_RIGHT + && format->text_h_align != LXW_ALIGN_DISTRIBUTED) { + format->text_h_align = LXW_ALIGN_LEFT; + } + + /* Check for properties that are mutually exclusive. */ + if (format->text_wrap) + format->shrink = 0; + + if (format->text_h_align == LXW_ALIGN_FILL) + format->shrink = 0; + + if (format->text_h_align == LXW_ALIGN_JUSTIFY) + format->shrink = 0; + + if (format->text_h_align == LXW_ALIGN_DISTRIBUTED) + format->shrink = 0; + + if (format->text_h_align != LXW_ALIGN_DISTRIBUTED) + format->just_distrib = 0; + + if (format->indent) + format->just_distrib = 0; + + if (format->text_h_align == LXW_ALIGN_LEFT) + LXW_PUSH_ATTRIBUTES_STR("horizontal", "left"); + + if (format->text_h_align == LXW_ALIGN_CENTER) + LXW_PUSH_ATTRIBUTES_STR("horizontal", "center"); + + if (format->text_h_align == LXW_ALIGN_RIGHT) + LXW_PUSH_ATTRIBUTES_STR("horizontal", "right"); + + if (format->text_h_align == LXW_ALIGN_FILL) + LXW_PUSH_ATTRIBUTES_STR("horizontal", "fill"); + + if (format->text_h_align == LXW_ALIGN_JUSTIFY) + LXW_PUSH_ATTRIBUTES_STR("horizontal", "justify"); + + if (format->text_h_align == LXW_ALIGN_CENTER_ACROSS) + LXW_PUSH_ATTRIBUTES_STR("horizontal", "centerContinuous"); + + if (format->text_h_align == LXW_ALIGN_DISTRIBUTED) + LXW_PUSH_ATTRIBUTES_STR("horizontal", "distributed"); + + if (format->just_distrib) + LXW_PUSH_ATTRIBUTES_STR("justifyLastLine", "1"); + + if (format->text_v_align == LXW_ALIGN_VERTICAL_TOP) + LXW_PUSH_ATTRIBUTES_STR("vertical", "top"); + + if (format->text_v_align == LXW_ALIGN_VERTICAL_CENTER) + LXW_PUSH_ATTRIBUTES_STR("vertical", "center"); + + if (format->text_v_align == LXW_ALIGN_VERTICAL_JUSTIFY) + LXW_PUSH_ATTRIBUTES_STR("vertical", "justify"); + + if (format->text_v_align == LXW_ALIGN_VERTICAL_DISTRIBUTED) + LXW_PUSH_ATTRIBUTES_STR("vertical", "distributed"); + + if (format->indent) + LXW_PUSH_ATTRIBUTES_INT("indent", format->indent); + + /* Map rotation to Excel values. */ + if (rotation) { + if (rotation == 270) + rotation = 255; + else if (rotation < 0) + rotation = -rotation + 90; + + LXW_PUSH_ATTRIBUTES_INT("textRotation", rotation); + } + + if (format->text_wrap) + LXW_PUSH_ATTRIBUTES_STR("wrapText", "1"); + + if (format->shrink) + LXW_PUSH_ATTRIBUTES_STR("shrinkToFit", "1"); + + if (format->reading_order == 1) + LXW_PUSH_ATTRIBUTES_STR("readingOrder", "1"); + + if (format->reading_order == 2) + LXW_PUSH_ATTRIBUTES_STR("readingOrder", "2"); + + if (!STAILQ_EMPTY(&attributes)) + lxw_xml_empty_tag(self->file, "alignment", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_protection(lxw_styles *self, lxw_format *format) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (!format->locked) + LXW_PUSH_ATTRIBUTES_STR("locked", "0"); + + if (format->hidden) + LXW_PUSH_ATTRIBUTES_STR("hidden", "1"); + + lxw_xml_empty_tag(self->file, "protection", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_xf(lxw_styles *self, lxw_format *format) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + uint8_t has_protection = (!format->locked) | format->hidden; + uint8_t has_alignment = _has_alignment(format); + uint8_t apply_alignment = _apply_alignment(format); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("numFmtId", format->num_format_index); + LXW_PUSH_ATTRIBUTES_INT("fontId", format->font_index); + LXW_PUSH_ATTRIBUTES_INT("fillId", format->fill_index); + LXW_PUSH_ATTRIBUTES_INT("borderId", format->border_index); + LXW_PUSH_ATTRIBUTES_STR("xfId", "0"); + + if (format->num_format_index > 0) + LXW_PUSH_ATTRIBUTES_STR("applyNumberFormat", "1"); + + /* Add applyFont attribute if XF format uses a font element. */ + if (format->font_index > 0) + LXW_PUSH_ATTRIBUTES_STR("applyFont", "1"); + + /* Add applyFill attribute if XF format uses a fill element. */ + if (format->fill_index > 0) + LXW_PUSH_ATTRIBUTES_STR("applyFill", "1"); + + /* Add applyBorder attribute if XF format uses a border element. */ + if (format->border_index > 0) + LXW_PUSH_ATTRIBUTES_STR("applyBorder", "1"); + + /* We can also have applyAlignment without a sub-element. */ + if (apply_alignment) + LXW_PUSH_ATTRIBUTES_STR("applyAlignment", "1"); + + if (has_protection) + LXW_PUSH_ATTRIBUTES_STR("applyProtection", "1"); + + /* Write XF with sub-elements if required. */ + if (has_alignment || has_protection) { + lxw_xml_start_tag(self->file, "xf", &attributes); + + if (has_alignment) + _write_alignment(self, format); + + if (has_protection) + _write_protection(self, format); + + lxw_xml_end_tag(self->file, "xf"); + } + else { + lxw_xml_empty_tag(self->file, "xf", &attributes); + } + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_cell_xfs(lxw_styles *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + lxw_format *format; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("count", self->xf_count); + + lxw_xml_start_tag(self->file, "cellXfs", &attributes); + + STAILQ_FOREACH(format, self->xf_formats, list_pointers) { + _write_xf(self, format); + } + + lxw_xml_end_tag(self->file, "cellXfs"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_cell_style(lxw_styles *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("name", "Normal"); + LXW_PUSH_ATTRIBUTES_STR("xfId", "0"); + LXW_PUSH_ATTRIBUTES_STR("builtinId", "0"); + + lxw_xml_empty_tag(self->file, "cellStyle", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_cell_styles(lxw_styles *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("count", "1"); + + lxw_xml_start_tag(self->file, "cellStyles", &attributes); + _write_cell_style(self); + lxw_xml_end_tag(self->file, "cellStyles"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_dxfs(lxw_styles *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("count", "0"); + + lxw_xml_empty_tag(self->file, "dxfs", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_table_styles(lxw_styles *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("count", "0"); + LXW_PUSH_ATTRIBUTES_STR("defaultTableStyle", "TableStyleMedium9"); + LXW_PUSH_ATTRIBUTES_STR("defaultPivotStyle", "PivotStyleLight16"); + + lxw_xml_empty_tag(self->file, "tableStyles", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Assemble and write the XML file. + */ +void +lxw_styles_assemble_xml_file(lxw_styles *self) +{ + /* Write the XML declaration. */ + _styles_xml_declaration(self); + + /* Add the style sheet. */ + _write_style_sheet(self); + + /* Write the number formats. */ + _write_num_fmts(self); + + /* Write the fonts. */ + _write_fonts(self); + + /* Write the fills. */ + _write_fills(self); + + /* Write the borders element. */ + _write_borders(self); + + /* Write the cellStyleXfs element. */ + _write_cell_style_xfs(self); + + /* Write the cellXfs element. */ + _write_cell_xfs(self); + + /* Write the cellStyles element. */ + _write_cell_styles(self); + + /* Write the dxfs element. */ + _write_dxfs(self); + + /* Write the tableStyles element. */ + _write_table_styles(self); + + /* Write the colors element. */ + /* _write_colors(self); */ + + /* Close the style sheet tag. */ + lxw_xml_end_tag(self->file, "styleSheet"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ diff --git a/src/libxlsxwriter/theme.c b/src/libxlsxwriter/theme.c new file mode 100644 index 0000000..7a67fe3 --- /dev/null +++ b/src/libxlsxwriter/theme.c @@ -0,0 +1,348 @@ +/***************************************************************************** + * theme - A library for creating Excel XLSX theme files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/theme.h" +#include "xlsxwriter/utility.h" + +const char *theme_strs[] = { + "\n", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "\n", + "" +}; + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new theme object. + */ +lxw_theme * +lxw_theme_new() +{ + lxw_theme *theme = calloc(1, sizeof(lxw_theme)); + GOTO_LABEL_ON_MEM_ERROR(theme, mem_error); + + return theme; + +mem_error: + lxw_theme_free(theme); + return NULL; +} + +/* + * Free a theme object. + */ +void +lxw_theme_free(lxw_theme *theme) +{ + if (!theme) + return; + + free(theme); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* This library isn't a xmlwriter. */ + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Assemble and write the XML file. + */ +void +lxw_theme_assemble_xml_file(lxw_theme *self) +{ + int i = 0; + + while (strlen(theme_strs[i])) { + fprintf(self->file, "%s", theme_strs[i]); + i++; + } +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ diff --git a/src/libxlsxwriter/utility.c b/src/libxlsxwriter/utility.c new file mode 100644 index 0000000..10ba017 --- /dev/null +++ b/src/libxlsxwriter/utility.c @@ -0,0 +1,515 @@ +/***************************************************************************** + * utility - Utility functions for libxlsxwriter. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include +#include +#include +#include +#include +#include "xlsxwriter/utility.h" +#include "xlsxwriter/third_party/tmpfileplus.h" + +char *error_strings[LXW_MAX_ERRNO + 1] = { + "No error.", + "Memory error, failed to malloc() required memory.", + "Error creating output xlsx file. Usually a permissions error.", + "Error encountered when creating a tmpfile during file assembly.", + "Zlib error with a file operation while creating xlsx file.", + "Zlib error when adding sub file to xlsx file.", + "Zlib error when closing xlsx file.", + "NULL function parameter ignored.", + "Function parameter validation error.", + "Worksheet name exceeds Excel's limit of 31 characters.", + "Worksheet name contains invalid Excel character: '[]:*?/\\'", + "Worksheet name is already in use.", + "Parameter exceeds Excel's limit of 128 characters.", + "Parameter exceeds Excel's limit of 255 characters.", + "String exceeds Excel's limit of 32,767 characters.", + "Error finding internal string index.", + "Worksheet row or column index out of range.", + "Maximum number of worksheet URLs (65530) exceeded.", + "Couldn't read image dimensions or DPI.", + "Unknown error number." +}; + +char * +lxw_strerror(lxw_error error_num) +{ + if (error_num > LXW_MAX_ERRNO) + error_num = LXW_MAX_ERRNO; + + return error_strings[error_num]; +} + +/* + * Convert Excel A-XFD style column name to zero based number. + */ +void +lxw_col_to_name(char *col_name, lxw_col_t col_num, uint8_t absolute) +{ + uint8_t pos = 0; + size_t len; + uint8_t i; + + /* Change from 0 index to 1 index. */ + col_num++; + + /* Convert the column number to a string in reverse order. */ + while (col_num) { + + /* Get the remainder in base 26. */ + int remainder = col_num % 26; + + if (remainder == 0) + remainder = 26; + + /* Convert the remainder value to a character. */ + col_name[pos++] = 'A' + remainder - 1; + col_name[pos] = '\0'; + + /* Get the next order of magnitude. */ + col_num = (col_num - 1) / 26; + } + + if (absolute) { + col_name[pos] = '$'; + col_name[pos + 1] = '\0'; + } + + /* Reverse the column name string. */ + len = strlen(col_name); + for (i = 0; i < (len / 2); i++) { + char tmp = col_name[i]; + col_name[i] = col_name[len - i - 1]; + col_name[len - i - 1] = tmp; + } +} + +/* + * Convert zero indexed row and column to an Excel style A1 cell reference. + */ +void +lxw_rowcol_to_cell(char *cell_name, lxw_row_t row, lxw_col_t col) +{ + size_t pos; + + /* Add the column to the cell. */ + lxw_col_to_name(cell_name, col, 0); + + /* Get the end of the cell. */ + pos = strlen(cell_name); + + /* Add the row to the cell. */ + lxw_snprintf(&cell_name[pos], LXW_MAX_ROW_NAME_LENGTH, "%d", ++row); +} + +/* + * Convert zero indexed row and column to an Excel style $A$1 cell with + * an absolute reference. + */ +void +lxw_rowcol_to_cell_abs(char *cell_name, lxw_row_t row, lxw_col_t col, + uint8_t abs_row, uint8_t abs_col) +{ + size_t pos; + + /* Add the column to the cell. */ + lxw_col_to_name(cell_name, col, abs_col); + + /* Get the end of the cell. */ + pos = strlen(cell_name); + + if (abs_row) + cell_name[pos++] = '$'; + + /* Add the row to the cell. */ + lxw_snprintf(&cell_name[pos], LXW_MAX_ROW_NAME_LENGTH, "%d", ++row); +} + +/* + * Convert zero indexed row and column pair to an Excel style A1:C5 + * range reference. + */ +void +lxw_rowcol_to_range(char *range, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t last_row, lxw_col_t last_col) +{ + size_t pos; + + /* Add the first cell to the range. */ + lxw_rowcol_to_cell(range, first_row, first_col); + + /* If the start and end cells are the same just return a single cell. */ + if (first_row == last_row && first_col == last_col) + return; + + /* Get the end of the cell. */ + pos = strlen(range); + + /* Add the range separator. */ + range[pos++] = ':'; + + /* Add the first cell to the range. */ + lxw_rowcol_to_cell(&range[pos], last_row, last_col); +} + +/* + * Convert zero indexed row and column pairs to an Excel style $A$1:$C$5 + * range reference with absolute values. + */ +void +lxw_rowcol_to_range_abs(char *range, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t last_row, lxw_col_t last_col) +{ + size_t pos; + + /* Add the first cell to the range. */ + lxw_rowcol_to_cell_abs(range, first_row, first_col, 1, 1); + + /* If the start and end cells are the same just return a single cell. */ + if (first_row == last_row && first_col == last_col) + return; + + /* Get the end of the cell. */ + pos = strlen(range); + + /* Add the range separator. */ + range[pos++] = ':'; + + /* Add the first cell to the range. */ + lxw_rowcol_to_cell_abs(&range[pos], last_row, last_col, 1, 1); +} + +/* + * Convert sheetname and zero indexed row and column pairs to an Excel style + * Sheet1!$A$1:$C$5 formula reference with absolute values. + */ +void +lxw_rowcol_to_formula_abs(char *formula, const char *sheetname, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t last_row, lxw_col_t last_col) +{ + size_t pos; + char *quoted_name = lxw_quote_sheetname(sheetname); + + strncpy(formula, quoted_name, LXW_MAX_FORMULA_RANGE_LENGTH - 1); + free(quoted_name); + + /* Get the end of the sheetname. */ + pos = strlen(formula); + + /* Add the range separator. */ + formula[pos++] = '!'; + + /* Add the first cell to the range. */ + lxw_rowcol_to_cell_abs(&formula[pos], first_row, first_col, 1, 1); + + /* If the start and end cells are the same just return a single cell. */ + if (first_row == last_row && first_col == last_col) + return; + + /* Get the end of the cell. */ + pos = strlen(formula); + + /* Add the range separator. */ + formula[pos++] = ':'; + + /* Add the first cell to the range. */ + lxw_rowcol_to_cell_abs(&formula[pos], last_row, last_col, 1, 1); +} + +/* + * Convert an Excel style A1 cell reference to a zero indexed row number. + */ +lxw_row_t +lxw_name_to_row(const char *row_str) +{ + lxw_row_t row_num = 0; + const char *p = row_str; + + /* Skip the column letters and absolute symbol of the A1 cell. */ + while (p && !isdigit((unsigned char) *p)) + p++; + + /* Convert the row part of the A1 cell to a number. */ + if (p) + row_num = atoi(p); + + if (row_num) + return row_num - 1; + else + return 0; +} + +/* + * Convert an Excel style A1 cell reference to a zero indexed column number. + */ +lxw_col_t +lxw_name_to_col(const char *col_str) +{ + lxw_col_t col_num = 0; + const char *p = col_str; + + /* Convert leading column letters of A1 cell. Ignore absolute $ marker. */ + while (p && (isupper((unsigned char) *p) || *p == '$')) { + if (*p != '$') + col_num = (col_num * 26) + (*p - 'A' + 1); + p++; + } + + return col_num - 1; +} + +/* + * Convert the second row of an Excel range ref to a zero indexed number. + */ +uint32_t +lxw_name_to_row_2(const char *row_str) +{ + const char *p = row_str; + + /* Find the : separator in the range. */ + while (p && *p != ':') + p++; + + if (p) + return lxw_name_to_row(++p); + else + return -1; +} + +/* + * Convert the second column of an Excel range ref to a zero indexed number. + */ +uint16_t +lxw_name_to_col_2(const char *col_str) +{ + const char *p = col_str; + + /* Find the : separator in the range. */ + while (p && *p != ':') + p++; + + if (p) + return lxw_name_to_col(++p); + else + return -1; +} + +/* + * Convert a lxw_datetime struct to an Excel serial date. + */ +double +lxw_datetime_to_excel_date(lxw_datetime *datetime, uint8_t date_1904) +{ + int year = datetime->year; + int month = datetime->month; + int day = datetime->day; + int hour = datetime->hour; + int min = datetime->min; + double sec = datetime->sec; + double seconds; + int epoch = date_1904 ? 1904 : 1900; + int offset = date_1904 ? 4 : 0; + int norm = 300; + int range; + /* Set month days and check for leap year. */ + int mdays[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int leap = 0; + int days = 0; + int i; + + /* For times without dates set the default date for the epoch. */ + if (!year) { + if (!date_1904) { + year = 1899; + month = 12; + day = 31; + } + else { + year = 1904; + month = 1; + day = 1; + } + } + + /* Convert the Excel seconds to a fraction of the seconds in 24 hours. */ + seconds = (hour * 60 * 60 + min * 60 + sec) / (24 * 60 * 60.0); + + /* Special cases for Excel dates in the 1900 epoch. */ + if (!date_1904) { + /* Excel 1900 epoch. */ + if (year == 1899 && month == 12 && day == 31) + return seconds; + + /* Excel 1900 epoch. */ + if (year == 1900 && month == 1 && day == 0) + return seconds; + + /* Excel false leapday */ + if (year == 1900 && month == 2 && day == 29) + return 60 + seconds; + } + + /* We calculate the date by calculating the number of days since the */ + /* epoch and adjust for the number of leap days. We calculate the */ + /* number of leap days by normalizing the year in relation to the */ + /* epoch. Thus the year 2000 becomes 100 for 4-year and 100-year */ + /* leapdays and 400 for 400-year leapdays. */ + range = year - epoch; + + if (year % 4 == 0 && (year % 100 > 0 || year % 400 == 0)) { + leap = 1; + mdays[2] = 29; + } + + /* + * Calculate the serial date by accumulating the number of days + * since the epoch. + */ + + /* Add days for previous months. */ + for (i = 0; i < month; i++) { + days += mdays[i]; + } + /* Add days for current month. */ + days += day; + /* Add days for all previous years. */ + days += range * 365; + /* Add 4 year leapdays. */ + days += (range) / 4; + /* Remove 100 year leapdays. */ + days -= (range + offset) / 100; + /* Add 400 year leapdays. */ + days += (range + offset + norm) / 400; + /* Remove leap days already counted. */ + days -= leap; + + /* Adjust for Excel erroneously treating 1900 as a leap year. */ + if (!date_1904 && days > 59) + days++; + + return days + seconds; +} + +/* Simple strdup() implementation since it isn't ANSI C. */ +char * +lxw_strdup(const char *str) +{ + size_t len; + char *copy; + + if (!str) + return NULL; + + len = strlen(str) + 1; + copy = malloc(len); + + if (copy) + memcpy(copy, str, len); + + return copy; +} + +/* Simple strlen that counts UTF-8 characters. Assumes well formed UTF-8. */ +size_t +lxw_utf8_strlen(const char *str) +{ + size_t byte_count = 0; + size_t char_count = 0; + + while (str[byte_count]) { + if ((str[byte_count] & 0xc0) != 0x80) + char_count++; + + byte_count++; + } + + return char_count; +} + +/* Simple tolower() for strings. */ +void +lxw_str_tolower(char *str) +{ + int i; + + for (i = 0; str[i]; i++) + str[i] = tolower(str[i]); +} + +/* Create a quoted version of the worksheet name, or return an unmodified + * copy if it doesn't required quoting. */ +char * +lxw_quote_sheetname(const char *str) +{ + + uint8_t needs_quoting = 0; + size_t number_of_quotes = 2; + size_t i, j; + size_t len = strlen(str); + + /* Don't quote the sheetname if it is already quoted. */ + if (str[0] == '\'') + return lxw_strdup(str); + + /* Check if the sheetname contains any characters that require it + * to be quoted. Also check for single quotes within the string. */ + for (i = 0; i < len; i++) { + if (!isalnum((unsigned char) str[i]) && str[i] != '_' + && str[i] != '.') + needs_quoting = 1; + + if (str[i] == '\'') { + needs_quoting = 1; + number_of_quotes++; + } + } + + if (!needs_quoting) { + return lxw_strdup(str); + } + else { + /* Add single quotes to the start and end of the string. */ + char *quoted_name = calloc(1, len + number_of_quotes + 1); + RETURN_ON_MEM_ERROR(quoted_name, NULL); + + quoted_name[0] = '\''; + + for (i = 0, j = 1; i < len; i++, j++) { + quoted_name[j] = str[i]; + + /* Double quote inline single quotes. */ + if (str[i] == '\'') { + quoted_name[++j] = '\''; + } + } + quoted_name[j++] = '\''; + quoted_name[j++] = '\0'; + + return quoted_name; + } +} + +/* + * Thin wrapper for tmpfile() so it can be over-ridden with a user defined + * version if required for safety or portability. + */ +FILE * +lxw_tmpfile(char *tmpdir) +{ +#ifndef USE_STANDARD_TMPFILE + return tmpfileplus(tmpdir, NULL, NULL, 0); +#else + (void) tmpdir; + return tmpfile(); +#endif +} diff --git a/src/libxlsxwriter/workbook.c b/src/libxlsxwriter/workbook.c new file mode 100644 index 0000000..25c2591 --- /dev/null +++ b/src/libxlsxwriter/workbook.c @@ -0,0 +1,1911 @@ +/***************************************************************************** + * workbook - A library for creating Excel XLSX workbook files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/workbook.h" +#include "xlsxwriter/utility.h" +#include "xlsxwriter/packager.h" +#include "xlsxwriter/hash_table.h" + +STATIC int _name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2); +#ifndef __clang_analyzer__ +LXW_RB_GENERATE_NAMES(lxw_worksheet_names, lxw_worksheet_name, tree_pointers, + _name_cmp); +#endif + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Comparator for the worksheet names structure red/black tree. + */ +STATIC int +_name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2) +{ + return strcmp(name1->name, name2->name); +} + +/* + * Free workbook properties. + */ +STATIC void +_free_doc_properties(lxw_doc_properties *properties) +{ + if (properties) { + free(properties->title); + free(properties->subject); + free(properties->author); + free(properties->manager); + free(properties->company); + free(properties->category); + free(properties->keywords); + free(properties->comments); + free(properties->status); + free(properties->hyperlink_base); + } + + free(properties); +} + +/* + * Free workbook custom property. + */ +STATIC void +_free_custom_doc_property(lxw_custom_property *custom_property) +{ + if (custom_property) { + free(custom_property->name); + if (custom_property->type == LXW_CUSTOM_STRING) + free(custom_property->u.string); + } + + free(custom_property); +} + +/* + * Free a workbook object. + */ +void +lxw_workbook_free(lxw_workbook *workbook) +{ + lxw_worksheet *worksheet; + struct lxw_worksheet_name *worksheet_name; + struct lxw_worksheet_name *next_name; + lxw_chart *chart; + lxw_format *format; + lxw_defined_name *defined_name; + lxw_defined_name *defined_name_tmp; + lxw_custom_property *custom_property; + + if (!workbook) + return; + + _free_doc_properties(workbook->properties); + + free(workbook->filename); + + /* Free the worksheets in the workbook. */ + if (workbook->worksheets) { + while (!STAILQ_EMPTY(workbook->worksheets)) { + worksheet = STAILQ_FIRST(workbook->worksheets); + STAILQ_REMOVE_HEAD(workbook->worksheets, list_pointers); + lxw_worksheet_free(worksheet); + } + free(workbook->worksheets); + + } + + /* Free the charts in the workbook. */ + if (workbook->charts) { + while (!STAILQ_EMPTY(workbook->charts)) { + chart = STAILQ_FIRST(workbook->charts); + STAILQ_REMOVE_HEAD(workbook->charts, list_pointers); + lxw_chart_free(chart); + } + free(workbook->charts); + } + + /* Free the formats in the workbook. */ + if (workbook->formats) { + while (!STAILQ_EMPTY(workbook->formats)) { + format = STAILQ_FIRST(workbook->formats); + STAILQ_REMOVE_HEAD(workbook->formats, list_pointers); + lxw_format_free(format); + } + free(workbook->formats); + } + + /* Free the defined_names in the workbook. */ + if (workbook->defined_names) { + defined_name = TAILQ_FIRST(workbook->defined_names); + while (defined_name) { + + defined_name_tmp = TAILQ_NEXT(defined_name, list_pointers); + free(defined_name); + defined_name = defined_name_tmp; + } + free(workbook->defined_names); + } + + /* Free the custom_properties in the workbook. */ + if (workbook->custom_properties) { + while (!STAILQ_EMPTY(workbook->custom_properties)) { + custom_property = STAILQ_FIRST(workbook->custom_properties); + STAILQ_REMOVE_HEAD(workbook->custom_properties, list_pointers); + _free_custom_doc_property(custom_property); + } + free(workbook->custom_properties); + } + + if (workbook->worksheet_names) { + for (worksheet_name = + RB_MIN(lxw_worksheet_names, workbook->worksheet_names); + worksheet_name; worksheet_name = next_name) { + + next_name = RB_NEXT(lxw_worksheet_names, + workbook->worksheet_name, worksheet_name); + RB_REMOVE(lxw_worksheet_names, + workbook->worksheet_names, worksheet_name); + free(worksheet_name); + } + + free(workbook->worksheet_names); + } + + lxw_hash_free(workbook->used_xf_formats); + lxw_sst_free(workbook->sst); + free(workbook->options.tmpdir); + free(workbook->ordered_charts); + free(workbook); +} + +/* + * Set the default index for each format. This is only used for testing. + */ +void +lxw_workbook_set_default_xf_indices(lxw_workbook *self) +{ + lxw_format *format; + + STAILQ_FOREACH(format, self->formats, list_pointers) { + lxw_format_get_xf_index(format); + } +} + +/* + * Iterate through the XF Format objects and give them an index to non-default + * font elements. + */ +STATIC void +_prepare_fonts(lxw_workbook *self) +{ + + lxw_hash_table *fonts = lxw_hash_new(128, 1, 1); + lxw_hash_element *hash_element; + lxw_hash_element *used_format_element; + uint16_t index = 0; + + LXW_FOREACH_ORDERED(used_format_element, self->used_xf_formats) { + lxw_format *format = (lxw_format *) used_format_element->value; + lxw_font *key = lxw_format_get_font_key(format); + + if (key) { + /* Look up the format in the hash table. */ + hash_element = lxw_hash_key_exists(fonts, key, sizeof(lxw_font)); + + if (hash_element) { + /* Font has already been used. */ + format->font_index = *(uint16_t *) hash_element->value; + format->has_font = LXW_FALSE; + free(key); + } + else { + /* This is a new font. */ + uint16_t *font_index = calloc(1, sizeof(uint16_t)); + *font_index = index; + format->font_index = index; + format->has_font = 1; + lxw_insert_hash_element(fonts, key, font_index, + sizeof(lxw_font)); + index++; + } + } + } + + lxw_hash_free(fonts); + + self->font_count = index; +} + +/* + * Iterate through the XF Format objects and give them an index to non-default + * border elements. + */ +STATIC void +_prepare_borders(lxw_workbook *self) +{ + + lxw_hash_table *borders = lxw_hash_new(128, 1, 1); + lxw_hash_element *hash_element; + lxw_hash_element *used_format_element; + uint16_t index = 0; + + LXW_FOREACH_ORDERED(used_format_element, self->used_xf_formats) { + lxw_format *format = (lxw_format *) used_format_element->value; + lxw_border *key = lxw_format_get_border_key(format); + + if (key) { + /* Look up the format in the hash table. */ + hash_element = + lxw_hash_key_exists(borders, key, sizeof(lxw_border)); + + if (hash_element) { + /* Border has already been used. */ + format->border_index = *(uint16_t *) hash_element->value; + format->has_border = LXW_FALSE; + free(key); + } + else { + /* This is a new border. */ + uint16_t *border_index = calloc(1, sizeof(uint16_t)); + *border_index = index; + format->border_index = index; + format->has_border = 1; + lxw_insert_hash_element(borders, key, border_index, + sizeof(lxw_border)); + index++; + } + } + } + + lxw_hash_free(borders); + + self->border_count = index; +} + +/* + * Iterate through the XF Format objects and give them an index to non-default + * fill elements. + */ +STATIC void +_prepare_fills(lxw_workbook *self) +{ + + lxw_hash_table *fills = lxw_hash_new(128, 1, 1); + lxw_hash_element *hash_element; + lxw_hash_element *used_format_element; + uint16_t index = 2; + lxw_fill *default_fill_1 = NULL; + lxw_fill *default_fill_2 = NULL; + uint16_t *fill_index1 = NULL; + uint16_t *fill_index2 = NULL; + + default_fill_1 = calloc(1, sizeof(lxw_fill)); + GOTO_LABEL_ON_MEM_ERROR(default_fill_1, mem_error); + + default_fill_2 = calloc(1, sizeof(lxw_fill)); + GOTO_LABEL_ON_MEM_ERROR(default_fill_2, mem_error); + + fill_index1 = calloc(1, sizeof(uint16_t)); + GOTO_LABEL_ON_MEM_ERROR(fill_index1, mem_error); + + fill_index2 = calloc(1, sizeof(uint16_t)); + GOTO_LABEL_ON_MEM_ERROR(fill_index2, mem_error); + + /* Add the default fills. */ + default_fill_1->pattern = LXW_PATTERN_NONE; + default_fill_1->fg_color = LXW_COLOR_UNSET; + default_fill_1->bg_color = LXW_COLOR_UNSET; + *fill_index1 = 0; + lxw_insert_hash_element(fills, default_fill_1, fill_index1, + sizeof(lxw_fill)); + + default_fill_2->pattern = LXW_PATTERN_GRAY_125; + default_fill_2->fg_color = LXW_COLOR_UNSET; + default_fill_2->bg_color = LXW_COLOR_UNSET; + *fill_index2 = 1; + lxw_insert_hash_element(fills, default_fill_2, fill_index2, + sizeof(lxw_fill)); + + LXW_FOREACH_ORDERED(used_format_element, self->used_xf_formats) { + lxw_format *format = (lxw_format *) used_format_element->value; + lxw_fill *key = lxw_format_get_fill_key(format); + + /* The following logical statements jointly take care of special */ + /* cases in relation to cell colors and patterns: */ + /* 1. For a solid fill (pattern == 1) Excel reverses the role of */ + /* foreground and background colors, and */ + /* 2. If the user specifies a foreground or background color */ + /* without a pattern they probably wanted a solid fill, so */ + /* we fill in the defaults. */ + if (format->pattern == LXW_PATTERN_SOLID + && format->bg_color != LXW_COLOR_UNSET + && format->fg_color != LXW_COLOR_UNSET) { + lxw_color_t tmp = format->fg_color; + format->fg_color = format->bg_color; + format->bg_color = tmp; + } + + if (format->pattern <= LXW_PATTERN_SOLID + && format->bg_color != LXW_COLOR_UNSET + && format->fg_color == LXW_COLOR_UNSET) { + format->fg_color = format->bg_color; + format->bg_color = LXW_COLOR_UNSET; + format->pattern = LXW_PATTERN_SOLID; + } + + if (format->pattern <= LXW_PATTERN_SOLID + && format->bg_color == LXW_COLOR_UNSET + && format->fg_color != LXW_COLOR_UNSET) { + format->bg_color = LXW_COLOR_UNSET; + format->pattern = LXW_PATTERN_SOLID; + } + + if (key) { + /* Look up the format in the hash table. */ + hash_element = lxw_hash_key_exists(fills, key, sizeof(lxw_fill)); + + if (hash_element) { + /* Fill has already been used. */ + format->fill_index = *(uint16_t *) hash_element->value; + format->has_fill = LXW_FALSE; + free(key); + } + else { + /* This is a new fill. */ + uint16_t *fill_index = calloc(1, sizeof(uint16_t)); + *fill_index = index; + format->fill_index = index; + format->has_fill = 1; + lxw_insert_hash_element(fills, key, fill_index, + sizeof(lxw_fill)); + index++; + } + } + } + + lxw_hash_free(fills); + + self->fill_count = index; + + return; + +mem_error: + free(fill_index2); + free(fill_index1); + free(default_fill_2); + free(default_fill_1); + lxw_hash_free(fills); +} + +/* + * Iterate through the XF Format objects and give them an index to non-default + * number format elements. Note, user defined records start from index 0xA4. + */ +STATIC void +_prepare_num_formats(lxw_workbook *self) +{ + + lxw_hash_table *num_formats = lxw_hash_new(128, 0, 1); + lxw_hash_element *hash_element; + lxw_hash_element *used_format_element; + uint16_t index = 0xA4; + uint16_t num_format_count = 0; + char *num_format; + uint16_t *num_format_index; + + LXW_FOREACH_ORDERED(used_format_element, self->used_xf_formats) { + lxw_format *format = (lxw_format *) used_format_element->value; + + /* Format already has a number format index. */ + if (format->num_format_index) + continue; + + /* Check if there is a user defined number format string. */ + num_format = format->num_format; + + if (*num_format) { + /* Look up the num_format in the hash table. */ + hash_element = lxw_hash_key_exists(num_formats, num_format, + strlen(num_format)); + + if (hash_element) { + /* Num_Format has already been used. */ + format->num_format_index = *(uint16_t *) hash_element->value; + } + else { + /* This is a new num_format. */ + num_format_index = calloc(1, sizeof(uint16_t)); + *num_format_index = index; + format->num_format_index = index; + lxw_insert_hash_element(num_formats, num_format, + num_format_index, strlen(num_format)); + index++; + num_format_count++; + } + } + } + + lxw_hash_free(num_formats); + + self->num_format_count = num_format_count; +} + +/* + * Prepare workbook and sub-objects for writing. + */ +STATIC void +_prepare_workbook(lxw_workbook *self) +{ + /* Set the font index for the format objects. */ + _prepare_fonts(self); + + /* Set the number format index for the format objects. */ + _prepare_num_formats(self); + + /* Set the border index for the format objects. */ + _prepare_borders(self); + + /* Set the fill index for the format objects. */ + _prepare_fills(self); + +} + +/* + * Compare two defined_name structures. + */ +static int +_compare_defined_names(lxw_defined_name *a, lxw_defined_name *b) +{ + int res = strcmp(a->normalised_name, b->normalised_name); + + /* Primary comparison based on defined name. */ + if (res) + return res; + + /* Secondary comparison based on worksheet name. */ + res = strcmp(a->normalised_sheetname, b->normalised_sheetname); + + return res; +} + +/* + * Process and store the defined names. The defined names are stored with + * the Workbook.xml but also with the App.xml if they refer to a sheet + * range like "Sheet1!:A1". The defined names are store in sorted + * order for consistency with Excel. The names need to be normalized before + * sorting. + */ +STATIC lxw_error +_store_defined_name(lxw_workbook *self, const char *name, + const char *app_name, const char *formula, int16_t index, + uint8_t hidden) +{ + lxw_worksheet *worksheet; + lxw_defined_name *defined_name; + lxw_defined_name *list_defined_name; + char name_copy[LXW_DEFINED_NAME_LENGTH]; + char *tmp_str; + char *worksheet_name; + + /* Do some checks on the input data */ + if (!name || !formula) + return LXW_ERROR_NULL_PARAMETER_IGNORED; + + if (lxw_utf8_strlen(name) > LXW_DEFINED_NAME_LENGTH || + lxw_utf8_strlen(formula) > LXW_DEFINED_NAME_LENGTH) { + return LXW_ERROR_128_STRING_LENGTH_EXCEEDED; + } + + /* Allocate a new defined_name to be added to the linked list of names. */ + defined_name = calloc(1, sizeof(struct lxw_defined_name)); + RETURN_ON_MEM_ERROR(defined_name, LXW_ERROR_MEMORY_MALLOC_FAILED); + + /* Copy the user input string. */ + lxw_strcpy(name_copy, name); + + /* Set the worksheet index or -1 for a global defined name. */ + defined_name->index = index; + defined_name->hidden = hidden; + + /* Check for local defined names like like "Sheet1!name". */ + tmp_str = strchr(name_copy, '!'); + + if (tmp_str == NULL) { + /* The name is global. We just store the defined name string. */ + lxw_strcpy(defined_name->name, name_copy); + } + else { + /* The name is worksheet local. We need to extract the sheet name + * and map it to a sheet index. */ + + /* Split the into the worksheet name and defined name. */ + *tmp_str = '\0'; + tmp_str++; + worksheet_name = name_copy; + + /* Remove any worksheet quoting. */ + if (worksheet_name[0] == '\'') + worksheet_name++; + if (worksheet_name[strlen(worksheet_name) - 1] == '\'') + worksheet_name[strlen(worksheet_name) - 1] = '\0'; + + /* Search for worksheet name to get the equivalent worksheet index. */ + STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) { + if (strcmp(worksheet_name, worksheet->name) == 0) { + defined_name->index = worksheet->index; + lxw_strcpy(defined_name->normalised_sheetname, + worksheet_name); + } + } + + /* If we didn't find the worksheet name we exit. */ + if (defined_name->index == -1) + goto mem_error; + + lxw_strcpy(defined_name->name, tmp_str); + } + + /* Print titles and repeat title pass in the name used for App.xml. */ + if (app_name) { + lxw_strcpy(defined_name->app_name, app_name); + lxw_strcpy(defined_name->normalised_sheetname, app_name); + } + else { + lxw_strcpy(defined_name->app_name, name); + } + + /* We need to normalize the defined names for sorting. This involves + * removing any _xlnm namespace and converting it to lowercase. */ + tmp_str = strstr(name_copy, "_xlnm."); + + if (tmp_str) + lxw_strcpy(defined_name->normalised_name, defined_name->name + 6); + else + lxw_strcpy(defined_name->normalised_name, defined_name->name); + + lxw_str_tolower(defined_name->normalised_name); + lxw_str_tolower(defined_name->normalised_sheetname); + + /* Strip leading "=" from the formula. */ + if (formula[0] == '=') + lxw_strcpy(defined_name->formula, formula + 1); + else + lxw_strcpy(defined_name->formula, formula); + + /* We add the defined name to the list in sorted order. */ + list_defined_name = TAILQ_FIRST(self->defined_names); + + if (list_defined_name == NULL || + _compare_defined_names(defined_name, list_defined_name) < 1) { + /* List is empty or defined name goes to the head. */ + TAILQ_INSERT_HEAD(self->defined_names, defined_name, list_pointers); + return LXW_NO_ERROR; + } + + TAILQ_FOREACH(list_defined_name, self->defined_names, list_pointers) { + int res = _compare_defined_names(defined_name, list_defined_name); + + /* The entry already exists. We exit and don't overwrite. */ + if (res == 0) + goto mem_error; + + /* New defined name is inserted in sorted order before other entries. */ + if (res < 0) { + TAILQ_INSERT_BEFORE(list_defined_name, defined_name, + list_pointers); + return LXW_NO_ERROR; + } + } + + /* If the entry wasn't less than any of the entries in the list we add it + * to the end. */ + TAILQ_INSERT_TAIL(self->defined_names, defined_name, list_pointers); + return LXW_NO_ERROR; + +mem_error: + free(defined_name); + return LXW_ERROR_MEMORY_MALLOC_FAILED; +} + +/* + * Populate the data cache of a chart data series by reading the data from the + * relevant worksheet and adding it to the cached in the range object as a + * list of points. + * + * Note, the data cache isn't strictly required by Excel but it helps if the + * chart is embedded in another application such as PowerPoint and it also + * helps with comparison testing. + */ +STATIC void +_populate_range_data_cache(lxw_workbook *self, lxw_series_range *range) +{ + lxw_worksheet *worksheet; + lxw_row_t row_num; + lxw_col_t col_num; + lxw_row *row_obj; + lxw_cell *cell_obj; + struct lxw_series_data_point *data_point; + uint16_t num_data_points = 0; + + /* If ignore_cache is set then don't try to populate the cache. This flag + * may be set manually, for testing, or due to a case where the cache + * can't be calculated. + */ + if (range->ignore_cache) + return; + + /* Currently we only handle 2D ranges so ensure either the rows or cols + * are the same. + */ + if (range->first_row != range->last_row + && range->first_col != range->last_col) { + range->ignore_cache = LXW_TRUE; + return; + } + + /* Check that the sheetname exists. */ + worksheet = workbook_get_worksheet_by_name(self, range->sheetname); + if (!worksheet) { + LXW_WARN_FORMAT2("workbook_add_chart(): worksheet name '%s' " + "in chart formula '%s' doesn't exist.", + range->sheetname, range->formula); + range->ignore_cache = LXW_TRUE; + return; + } + + /* We can't read the data when worksheet optimization is on. */ + if (worksheet->optimize) { + range->ignore_cache = LXW_TRUE; + return; + } + + /* Iterate through the worksheet data and populate the range cache. */ + for (row_num = range->first_row; row_num <= range->last_row; row_num++) { + row_obj = lxw_worksheet_find_row(worksheet, row_num); + + for (col_num = range->first_col; col_num <= range->last_col; + col_num++) { + + data_point = calloc(1, sizeof(struct lxw_series_data_point)); + if (!data_point) { + range->ignore_cache = LXW_TRUE; + return; + } + + cell_obj = lxw_worksheet_find_cell(row_obj, col_num); + + if (cell_obj) { + if (cell_obj->type == NUMBER_CELL) { + data_point->number = cell_obj->u.number; + } + + if (cell_obj->type == STRING_CELL) { + data_point->string = lxw_strdup(cell_obj->sst_string); + data_point->is_string = LXW_TRUE; + range->has_string_cache = LXW_TRUE; + } + } + else { + data_point->no_data = LXW_TRUE; + } + + STAILQ_INSERT_TAIL(range->data_cache, data_point, list_pointers); + num_data_points++; + } + } + + range->num_data_points = num_data_points; + +} + +/* Convert a chart range such as Sheet1!$A$1:$A$5 to a sheet name and row-col + * dimensions, or vice-versa. This gives us the dimensions to read data back + * from the worksheet. + */ +STATIC void +_populate_range_dimensions(lxw_workbook *self, lxw_series_range *range) +{ + + char formula[LXW_MAX_FORMULA_RANGE_LENGTH] = { 0 }; + char *tmp_str; + char *sheetname; + + /* If neither the range formula or sheetname is defined then this probably + * isn't a valid range. + */ + if (!range->formula && !range->sheetname) { + range->ignore_cache = LXW_TRUE; + return; + } + + /* If the sheetname is already defined it was already set via + * chart_series_set_categories() or chart_series_set_values(). + */ + if (range->sheetname) + return; + + /* Ignore non-contiguous range like (Sheet1!$A$1:$A$2,Sheet1!$A$4:$A$5) */ + if (range->formula[0] == '(') { + range->ignore_cache = LXW_TRUE; + return; + } + + /* Create a copy of the formula to modify and parse into parts. */ + lxw_snprintf(formula, LXW_MAX_FORMULA_RANGE_LENGTH, "%s", range->formula); + + /* Check for valid formula. TODO. This needs stronger validation. */ + tmp_str = strchr(formula, '!'); + + if (tmp_str == NULL) { + range->ignore_cache = LXW_TRUE; + return; + } + else { + /* Split the formulas into sheetname and row-col data. */ + *tmp_str = '\0'; + tmp_str++; + sheetname = formula; + + /* Remove any worksheet quoting. */ + if (sheetname[0] == '\'') + sheetname++; + if (sheetname[strlen(sheetname) - 1] == '\'') + sheetname[strlen(sheetname) - 1] = '\0'; + + /* Check that the sheetname exists. */ + if (!workbook_get_worksheet_by_name(self, sheetname)) { + LXW_WARN_FORMAT2("workbook_add_chart(): worksheet name '%s' " + "in chart formula '%s' doesn't exist.", + sheetname, range->formula); + range->ignore_cache = LXW_TRUE; + return; + } + + range->sheetname = lxw_strdup(sheetname); + range->first_row = lxw_name_to_row(tmp_str); + range->first_col = lxw_name_to_col(tmp_str); + + if (strchr(tmp_str, ':')) { + /* 2D range. */ + range->last_row = lxw_name_to_row_2(tmp_str); + range->last_col = lxw_name_to_col_2(tmp_str); + } + else { + /* 1D range. */ + range->last_row = range->first_row; + range->last_col = range->first_col; + } + + } +} + +/* Set the range dimensions and set the data cache. + */ +STATIC void +_populate_range(lxw_workbook *self, lxw_series_range *range) +{ + _populate_range_dimensions(self, range); + _populate_range_data_cache(self, range); +} + +/* + * Add "cached" data to charts to provide the numCache and strCache data for + * series and title/axis ranges. + */ +STATIC void +_add_chart_cache_data(lxw_workbook *self) +{ + lxw_chart *chart; + lxw_chart_series *series; + + STAILQ_FOREACH(chart, self->ordered_charts, ordered_list_pointers) { + + _populate_range(self, chart->title.range); + _populate_range(self, chart->x_axis->title.range); + _populate_range(self, chart->y_axis->title.range); + + if (STAILQ_EMPTY(chart->series_list)) + continue; + + STAILQ_FOREACH(series, chart->series_list, list_pointers) { + _populate_range(self, series->categories); + _populate_range(self, series->values); + _populate_range(self, series->title.range); + } + } +} + +/* + * Iterate through the worksheets and set up any chart or image drawings. + */ +STATIC void +_prepare_drawings(lxw_workbook *self) +{ + lxw_worksheet *worksheet; + lxw_image_options *image_options; + uint16_t chart_ref_id = 0; + uint16_t image_ref_id = 0; + uint16_t drawing_id = 0; + + STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) { + + if (STAILQ_EMPTY(worksheet->image_data) + && STAILQ_EMPTY(worksheet->chart_data)) + continue; + + drawing_id++; + + STAILQ_FOREACH(image_options, worksheet->chart_data, list_pointers) { + chart_ref_id++; + lxw_worksheet_prepare_chart(worksheet, chart_ref_id, drawing_id, + image_options); + if (image_options->chart) + STAILQ_INSERT_TAIL(self->ordered_charts, image_options->chart, + ordered_list_pointers); + } + + STAILQ_FOREACH(image_options, worksheet->image_data, list_pointers) { + + if (image_options->image_type == LXW_IMAGE_PNG) + self->has_png = LXW_TRUE; + + if (image_options->image_type == LXW_IMAGE_JPEG) + self->has_jpeg = LXW_TRUE; + + if (image_options->image_type == LXW_IMAGE_BMP) + self->has_bmp = LXW_TRUE; + + image_ref_id++; + + lxw_worksheet_prepare_image(worksheet, image_ref_id, drawing_id, + image_options); + } + } + + self->drawing_count = drawing_id; +} + +/* + * Iterate through the worksheets and store any defined names used for print + * ranges or repeat rows/columns. + */ +STATIC void +_prepare_defined_names(lxw_workbook *self) +{ + lxw_worksheet *worksheet; + char app_name[LXW_DEFINED_NAME_LENGTH]; + char range[LXW_DEFINED_NAME_LENGTH]; + char area[LXW_MAX_CELL_RANGE_LENGTH]; + char first_col[8]; + char last_col[8]; + + STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) { + + /* + * Check for autofilter settings and store them. + */ + if (worksheet->autofilter.in_use) { + + lxw_snprintf(app_name, LXW_DEFINED_NAME_LENGTH, + "%s!_FilterDatabase", worksheet->quoted_name); + + lxw_rowcol_to_range_abs(area, + worksheet->autofilter.first_row, + worksheet->autofilter.first_col, + worksheet->autofilter.last_row, + worksheet->autofilter.last_col); + + lxw_snprintf(range, LXW_DEFINED_NAME_LENGTH, "%s!%s", + worksheet->quoted_name, area); + + /* Autofilters are the only defined name to set the hidden flag. */ + _store_defined_name(self, "_xlnm._FilterDatabase", app_name, + range, worksheet->index, LXW_TRUE); + } + + /* + * Check for Print Area settings and store them. + */ + if (worksheet->print_area.in_use) { + + lxw_snprintf(app_name, LXW_DEFINED_NAME_LENGTH, + "%s!Print_Area", worksheet->quoted_name); + + /* Check for print area that is the max row range. */ + if (worksheet->print_area.first_row == 0 + && worksheet->print_area.last_row == LXW_ROW_MAX - 1) { + + lxw_col_to_name(first_col, + worksheet->print_area.first_col, LXW_FALSE); + + lxw_col_to_name(last_col, + worksheet->print_area.last_col, LXW_FALSE); + + lxw_snprintf(area, LXW_MAX_CELL_RANGE_LENGTH - 1, "$%s:$%s", + first_col, last_col); + + } + /* Check for print area that is the max column range. */ + else if (worksheet->print_area.first_col == 0 + && worksheet->print_area.last_col == LXW_COL_MAX - 1) { + + lxw_snprintf(area, LXW_MAX_CELL_RANGE_LENGTH - 1, "$%d:$%d", + worksheet->print_area.first_row + 1, + worksheet->print_area.last_row + 1); + + } + else { + lxw_rowcol_to_range_abs(area, + worksheet->print_area.first_row, + worksheet->print_area.first_col, + worksheet->print_area.last_row, + worksheet->print_area.last_col); + } + + lxw_snprintf(range, LXW_DEFINED_NAME_LENGTH, "%s!%s", + worksheet->quoted_name, area); + + _store_defined_name(self, "_xlnm.Print_Area", app_name, + range, worksheet->index, LXW_FALSE); + } + + /* + * Check for repeat rows/cols. aka, Print Titles and store them. + */ + if (worksheet->repeat_rows.in_use || worksheet->repeat_cols.in_use) { + if (worksheet->repeat_rows.in_use + && worksheet->repeat_cols.in_use) { + lxw_snprintf(app_name, LXW_DEFINED_NAME_LENGTH, + "%s!Print_Titles", worksheet->quoted_name); + + lxw_col_to_name(first_col, + worksheet->repeat_cols.first_col, LXW_FALSE); + + lxw_col_to_name(last_col, + worksheet->repeat_cols.last_col, LXW_FALSE); + + lxw_snprintf(range, LXW_DEFINED_NAME_LENGTH, + "%s!$%s:$%s,%s!$%d:$%d", + worksheet->quoted_name, first_col, + last_col, worksheet->quoted_name, + worksheet->repeat_rows.first_row + 1, + worksheet->repeat_rows.last_row + 1); + + _store_defined_name(self, "_xlnm.Print_Titles", app_name, + range, worksheet->index, LXW_FALSE); + } + else if (worksheet->repeat_rows.in_use) { + + lxw_snprintf(app_name, LXW_DEFINED_NAME_LENGTH, + "%s!Print_Titles", worksheet->quoted_name); + + lxw_snprintf(range, LXW_DEFINED_NAME_LENGTH, + "%s!$%d:$%d", worksheet->quoted_name, + worksheet->repeat_rows.first_row + 1, + worksheet->repeat_rows.last_row + 1); + + _store_defined_name(self, "_xlnm.Print_Titles", app_name, + range, worksheet->index, LXW_FALSE); + } + else if (worksheet->repeat_cols.in_use) { + lxw_snprintf(app_name, LXW_DEFINED_NAME_LENGTH, + "%s!Print_Titles", worksheet->quoted_name); + + lxw_col_to_name(first_col, + worksheet->repeat_cols.first_col, LXW_FALSE); + + lxw_col_to_name(last_col, + worksheet->repeat_cols.last_col, LXW_FALSE); + + lxw_snprintf(range, LXW_DEFINED_NAME_LENGTH, + "%s!$%s:$%s", worksheet->quoted_name, + first_col, last_col); + + _store_defined_name(self, "_xlnm.Print_Titles", app_name, + range, worksheet->index, LXW_FALSE); + } + } + } +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_workbook_xml_declaration(lxw_workbook *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_write_workbook(lxw_workbook *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns[] = "http://schemas.openxmlformats.org" + "/spreadsheetml/2006/main"; + char xmlns_r[] = "http://schemas.openxmlformats.org" + "/officeDocument/2006/relationships"; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); + LXW_PUSH_ATTRIBUTES_STR("xmlns:r", xmlns_r); + + lxw_xml_start_tag(self->file, "workbook", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_file_version(lxw_workbook *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("appName", "xl"); + LXW_PUSH_ATTRIBUTES_STR("lastEdited", "4"); + LXW_PUSH_ATTRIBUTES_STR("lowestEdited", "4"); + LXW_PUSH_ATTRIBUTES_STR("rupBuild", "4505"); + + lxw_xml_empty_tag(self->file, "fileVersion", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_workbook_pr(lxw_workbook *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("defaultThemeVersion", "124226"); + + lxw_xml_empty_tag(self->file, "workbookPr", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_workbook_view(lxw_workbook *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xWindow", "240"); + LXW_PUSH_ATTRIBUTES_STR("yWindow", "15"); + LXW_PUSH_ATTRIBUTES_STR("windowWidth", "16095"); + LXW_PUSH_ATTRIBUTES_STR("windowHeight", "9660"); + + if (self->first_sheet) + LXW_PUSH_ATTRIBUTES_INT("firstSheet", self->first_sheet); + + if (self->active_sheet) + LXW_PUSH_ATTRIBUTES_INT("activeTab", self->active_sheet); + + lxw_xml_empty_tag(self->file, "workbookView", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_book_views(lxw_workbook *self) +{ + lxw_xml_start_tag(self->file, "bookViews", NULL); + + _write_workbook_view(self); + + lxw_xml_end_tag(self->file, "bookViews"); +} + +/* + * Write the element. + */ +STATIC void +_write_sheet(lxw_workbook *self, const char *name, uint32_t sheet_id, + uint8_t hidden) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char r_id[LXW_MAX_ATTRIBUTE_LENGTH] = "rId1"; + + lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", sheet_id); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("name", name); + LXW_PUSH_ATTRIBUTES_INT("sheetId", sheet_id); + + if (hidden) + LXW_PUSH_ATTRIBUTES_STR("state", "hidden"); + + LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); + + lxw_xml_empty_tag(self->file, "sheet", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_sheets(lxw_workbook *self) +{ + lxw_worksheet *worksheet; + + lxw_xml_start_tag(self->file, "sheets", NULL); + + STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) { + _write_sheet(self, worksheet->name, worksheet->index + 1, + worksheet->hidden); + } + + lxw_xml_end_tag(self->file, "sheets"); +} + +/* + * Write the element. + */ +STATIC void +_write_calc_pr(lxw_workbook *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("calcId", "124519"); + LXW_PUSH_ATTRIBUTES_STR("fullCalcOnLoad", "1"); + + lxw_xml_empty_tag(self->file, "calcPr", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_defined_name(lxw_workbook *self, lxw_defined_name *defined_name) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("name", defined_name->name); + + if (defined_name->index != -1) + LXW_PUSH_ATTRIBUTES_INT("localSheetId", defined_name->index); + + if (defined_name->hidden) + LXW_PUSH_ATTRIBUTES_INT("hidden", 1); + + lxw_xml_data_element(self->file, "definedName", defined_name->formula, + &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_defined_names(lxw_workbook *self) +{ + lxw_defined_name *defined_name; + + if (TAILQ_EMPTY(self->defined_names)) + return; + + lxw_xml_start_tag(self->file, "definedNames", NULL); + + TAILQ_FOREACH(defined_name, self->defined_names, list_pointers) { + _write_defined_name(self, defined_name); + } + + lxw_xml_end_tag(self->file, "definedNames"); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Assemble and write the XML file. + */ +void +lxw_workbook_assemble_xml_file(lxw_workbook *self) +{ + /* Prepare workbook and sub-objects for writing. */ + _prepare_workbook(self); + + /* Write the XML declaration. */ + _workbook_xml_declaration(self); + + /* Write the root workbook element. */ + _write_workbook(self); + + /* Write the XLSX file version. */ + _write_file_version(self); + + /* Write the workbook properties. */ + _write_workbook_pr(self); + + /* Write the workbook view properties. */ + _write_book_views(self); + + /* Write the worksheet names and ids. */ + _write_sheets(self); + + /* Write the workbook defined names. */ + _write_defined_names(self); + + /* Write the workbook calculation properties. */ + _write_calc_pr(self); + + /* Close the workbook tag. */ + lxw_xml_end_tag(self->file, "workbook"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ + +/* + * Create a new workbook object. + */ +lxw_workbook * +workbook_new(const char *filename) +{ + return workbook_new_opt(filename, NULL); +} + +/* Deprecated function name for backwards compatibility. */ +lxw_workbook * +new_workbook(const char *filename) +{ + return workbook_new_opt(filename, NULL); +} + +/* Deprecated function name for backwards compatibility. */ +lxw_workbook * +new_workbook_opt(const char *filename, lxw_workbook_options *options) +{ + return workbook_new_opt(filename, options); +} + +/* + * Create a new workbook object with options. + */ +lxw_workbook * +workbook_new_opt(const char *filename, lxw_workbook_options *options) +{ + lxw_format *format; + lxw_workbook *workbook; + + /* Create the workbook object. */ + workbook = calloc(1, sizeof(lxw_workbook)); + GOTO_LABEL_ON_MEM_ERROR(workbook, mem_error); + workbook->filename = lxw_strdup(filename); + + /* Add the worksheets list. */ + workbook->worksheets = calloc(1, sizeof(struct lxw_worksheets)); + GOTO_LABEL_ON_MEM_ERROR(workbook->worksheets, mem_error); + STAILQ_INIT(workbook->worksheets); + + /* Add the worksheet names tree. */ + workbook->worksheet_names = calloc(1, sizeof(struct lxw_worksheet_names)); + GOTO_LABEL_ON_MEM_ERROR(workbook->worksheet_names, mem_error); + RB_INIT(workbook->worksheet_names); + + /* Add the charts list. */ + workbook->charts = calloc(1, sizeof(struct lxw_charts)); + GOTO_LABEL_ON_MEM_ERROR(workbook->charts, mem_error); + STAILQ_INIT(workbook->charts); + + /* Add the ordered charts list to track chart insertion order. */ + workbook->ordered_charts = calloc(1, sizeof(struct lxw_charts)); + GOTO_LABEL_ON_MEM_ERROR(workbook->ordered_charts, mem_error); + STAILQ_INIT(workbook->ordered_charts); + + /* Add the formats list. */ + workbook->formats = calloc(1, sizeof(struct lxw_formats)); + GOTO_LABEL_ON_MEM_ERROR(workbook->formats, mem_error); + STAILQ_INIT(workbook->formats); + + /* Add the defined_names list. */ + workbook->defined_names = calloc(1, sizeof(struct lxw_defined_names)); + GOTO_LABEL_ON_MEM_ERROR(workbook->defined_names, mem_error); + TAILQ_INIT(workbook->defined_names); + + /* Add the shared strings table. */ + workbook->sst = lxw_sst_new(); + GOTO_LABEL_ON_MEM_ERROR(workbook->sst, mem_error); + + /* Add the default workbook properties. */ + workbook->properties = calloc(1, sizeof(lxw_doc_properties)); + GOTO_LABEL_ON_MEM_ERROR(workbook->properties, mem_error); + + /* Add a hash table to track format indices. */ + workbook->used_xf_formats = lxw_hash_new(128, 1, 0); + GOTO_LABEL_ON_MEM_ERROR(workbook->used_xf_formats, mem_error); + + /* Add the worksheets list. */ + workbook->custom_properties = + calloc(1, sizeof(struct lxw_custom_properties)); + GOTO_LABEL_ON_MEM_ERROR(workbook->custom_properties, mem_error); + STAILQ_INIT(workbook->custom_properties); + + /* Add the default cell format. */ + format = workbook_add_format(workbook); + GOTO_LABEL_ON_MEM_ERROR(format, mem_error); + + /* Initialize its index. */ + lxw_format_get_xf_index(format); + + if (options) { + workbook->options.constant_memory = options->constant_memory; + workbook->options.tmpdir = lxw_strdup(options->tmpdir); + } + + return workbook; + +mem_error: + lxw_workbook_free(workbook); + workbook = NULL; + return NULL; +} + +/* + * Add a new worksheet to the Excel workbook. + */ +lxw_worksheet * +workbook_add_worksheet(lxw_workbook *self, const char *sheetname) +{ + lxw_worksheet *worksheet; + lxw_worksheet_name *worksheet_name = NULL; + lxw_error error; + lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + char *new_name = NULL; + + if (sheetname) { + /* Use the user supplied name. */ + init_data.name = lxw_strdup(sheetname); + init_data.quoted_name = lxw_quote_sheetname((char *) sheetname); + } + else { + /* Use the default SheetN name. */ + new_name = malloc(LXW_MAX_SHEETNAME_LENGTH); + GOTO_LABEL_ON_MEM_ERROR(new_name, mem_error); + + lxw_snprintf(new_name, LXW_MAX_SHEETNAME_LENGTH, "Sheet%d", + self->num_sheets + 1); + init_data.name = new_name; + init_data.quoted_name = lxw_strdup(new_name); + } + + /* Check that the worksheet name is valid. */ + error = workbook_validate_worksheet_name(self, init_data.name); + if (error) { + LXW_WARN_FORMAT2("workbook_add_worksheet(): worksheet name '%s' has " + "error: %s", init_data.name, lxw_strerror(error)); + goto mem_error; + } + + /* Create a struct to find/store the worksheet name/pointer. */ + worksheet_name = calloc(1, sizeof(struct lxw_worksheet_name)); + GOTO_LABEL_ON_MEM_ERROR(worksheet_name, mem_error); + + /* Initialize the metadata to pass to the worksheet. */ + init_data.hidden = 0; + init_data.index = self->num_sheets; + init_data.sst = self->sst; + init_data.optimize = self->options.constant_memory; + init_data.active_sheet = &self->active_sheet; + init_data.first_sheet = &self->first_sheet; + init_data.tmpdir = self->options.tmpdir; + + /* Create a new worksheet object. */ + worksheet = lxw_worksheet_new(&init_data); + GOTO_LABEL_ON_MEM_ERROR(worksheet, mem_error); + + self->num_sheets++; + STAILQ_INSERT_TAIL(self->worksheets, worksheet, list_pointers); + + /* Store the worksheet so we can look it up by name. */ + worksheet_name->name = init_data.name; + worksheet_name->worksheet = worksheet; + RB_INSERT(lxw_worksheet_names, self->worksheet_names, worksheet_name); + + return worksheet; + +mem_error: + free(init_data.name); + free(init_data.quoted_name); + free(worksheet_name); + return NULL; +} + +/* + * Add a new chart to the Excel workbook. + */ +lxw_chart * +workbook_add_chart(lxw_workbook *self, uint8_t type) +{ + lxw_chart *chart; + + /* Create a new chart object. */ + chart = lxw_chart_new(type); + + if (chart) + STAILQ_INSERT_TAIL(self->charts, chart, list_pointers); + + return chart; +} + +/* + * Add a new format to the Excel workbook. + */ +lxw_format * +workbook_add_format(lxw_workbook *self) +{ + /* Create a new format object. */ + lxw_format *format = lxw_format_new(); + RETURN_ON_MEM_ERROR(format, NULL); + + format->xf_format_indices = self->used_xf_formats; + format->num_xf_formats = &self->num_xf_formats; + + STAILQ_INSERT_TAIL(self->formats, format, list_pointers); + + return format; +} + +/* + * Call finalization code and close file. + */ +lxw_error +workbook_close(lxw_workbook *self) +{ + lxw_worksheet *worksheet = NULL; + lxw_packager *packager = NULL; + lxw_error error = LXW_NO_ERROR; + + /* Add a default worksheet if non have been added. */ + if (!self->num_sheets) + workbook_add_worksheet(self, NULL); + + /* Ensure that at least one worksheet has been selected. */ + if (self->active_sheet == 0) { + worksheet = STAILQ_FIRST(self->worksheets); + worksheet->selected = 1; + worksheet->hidden = 0; + } + + /* Set the active sheet. */ + STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) { + if (worksheet->index == self->active_sheet) + worksheet->active = 1; + } + + /* Set the defined names for the worksheets such as Print Titles. */ + _prepare_defined_names(self); + + /* Prepare the drawings, charts and images. */ + _prepare_drawings(self); + + /* Add cached data to charts. */ + _add_chart_cache_data(self); + + /* Create a packager object to assemble sub-elements into a zip file. */ + packager = lxw_packager_new(self->filename, self->options.tmpdir); + + /* If the packager fails it is generally due to a zip permission error. */ + if (packager == NULL) { + fprintf(stderr, "[ERROR] workbook_close(): " + "Error creating '%s'. " + "Error = %s\n", self->filename, strerror(errno)); + + error = LXW_ERROR_CREATING_XLSX_FILE; + goto mem_error; + } + + /* Set the workbook object in the packager. */ + packager->workbook = self; + + /* Assemble all the sub-files in the xlsx package. */ + error = lxw_create_package(packager); + + /* Error and non-error conditions fall through to the cleanup code. */ + if (error == LXW_ERROR_CREATING_TMPFILE) { + fprintf(stderr, "[ERROR] workbook_close(): " + "Error creating tmpfile(s) to assemble '%s'. " + "Error = %s\n", self->filename, strerror(errno)); + } + + /* If LXW_ERROR_ZIP_FILE_OPERATION then errno is set by zlib. */ + if (error == LXW_ERROR_ZIP_FILE_OPERATION) { + fprintf(stderr, "[ERROR] workbook_close(): " + "Zlib error while creating xlsx file '%s'. " + "Error = %s\n", self->filename, strerror(errno)); + } + + /* The next 2 error conditions don't set errno. */ + if (error == LXW_ERROR_ZIP_FILE_ADD) { + fprintf(stderr, "[ERROR] workbook_close(): " + "Zlib error adding file to xlsx file '%s'.\n", + self->filename); + } + + if (error == LXW_ERROR_ZIP_CLOSE) { + fprintf(stderr, "[ERROR] workbook_close(): " + "Zlib error closing xlsx file '%s'.\n", self->filename); + } + +mem_error: + lxw_packager_free(packager); + lxw_workbook_free(self); + return error; +} + +/* + * Create a defined name in Excel. We handle global/workbook level names and + * local/worksheet names. + */ +lxw_error +workbook_define_name(lxw_workbook *self, const char *name, + const char *formula) +{ + return _store_defined_name(self, name, NULL, formula, -1, LXW_FALSE); +} + +/* + * Set the document properties such as Title, Author etc. + */ +lxw_error +workbook_set_properties(lxw_workbook *self, lxw_doc_properties *user_props) +{ + lxw_doc_properties *doc_props; + + /* Free any existing properties. */ + _free_doc_properties(self->properties); + + doc_props = calloc(1, sizeof(lxw_doc_properties)); + GOTO_LABEL_ON_MEM_ERROR(doc_props, mem_error); + + /* Copy the user properties to an internal structure. */ + if (user_props->title) { + doc_props->title = lxw_strdup(user_props->title); + GOTO_LABEL_ON_MEM_ERROR(doc_props->title, mem_error); + } + + if (user_props->subject) { + doc_props->subject = lxw_strdup(user_props->subject); + GOTO_LABEL_ON_MEM_ERROR(doc_props->subject, mem_error); + } + + if (user_props->author) { + doc_props->author = lxw_strdup(user_props->author); + GOTO_LABEL_ON_MEM_ERROR(doc_props->author, mem_error); + } + + if (user_props->manager) { + doc_props->manager = lxw_strdup(user_props->manager); + GOTO_LABEL_ON_MEM_ERROR(doc_props->manager, mem_error); + } + + if (user_props->company) { + doc_props->company = lxw_strdup(user_props->company); + GOTO_LABEL_ON_MEM_ERROR(doc_props->company, mem_error); + } + + if (user_props->category) { + doc_props->category = lxw_strdup(user_props->category); + GOTO_LABEL_ON_MEM_ERROR(doc_props->category, mem_error); + } + + if (user_props->keywords) { + doc_props->keywords = lxw_strdup(user_props->keywords); + GOTO_LABEL_ON_MEM_ERROR(doc_props->keywords, mem_error); + } + + if (user_props->comments) { + doc_props->comments = lxw_strdup(user_props->comments); + GOTO_LABEL_ON_MEM_ERROR(doc_props->comments, mem_error); + } + + if (user_props->status) { + doc_props->status = lxw_strdup(user_props->status); + GOTO_LABEL_ON_MEM_ERROR(doc_props->status, mem_error); + } + + if (user_props->hyperlink_base) { + doc_props->hyperlink_base = lxw_strdup(user_props->hyperlink_base); + GOTO_LABEL_ON_MEM_ERROR(doc_props->hyperlink_base, mem_error); + } + + self->properties = doc_props; + + return LXW_NO_ERROR; + +mem_error: + _free_doc_properties(doc_props); + return LXW_ERROR_MEMORY_MALLOC_FAILED; +} + +/* + * Set a string custom document property. + */ +lxw_error +workbook_set_custom_property_string(lxw_workbook *self, const char *name, + const char *value) +{ + lxw_custom_property *custom_property; + + if (!name) { + LXW_WARN_FORMAT("workbook_set_custom_property_string(): " + "parameter 'name' cannot be NULL."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + if (!value) { + LXW_WARN_FORMAT("workbook_set_custom_property_string(): " + "parameter 'value' cannot be NULL."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + if (lxw_utf8_strlen(name) > 255) { + LXW_WARN_FORMAT("workbook_set_custom_property_string(): parameter " + "'name' exceeds Excel length limit of 255."); + return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + } + + if (lxw_utf8_strlen(value) > 255) { + LXW_WARN_FORMAT("workbook_set_custom_property_string(): parameter " + "'value' exceeds Excel length limit of 255."); + return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + } + + /* Create a struct to hold the custom property. */ + custom_property = calloc(1, sizeof(struct lxw_custom_property)); + RETURN_ON_MEM_ERROR(custom_property, LXW_ERROR_MEMORY_MALLOC_FAILED); + + custom_property->name = lxw_strdup(name); + custom_property->u.string = lxw_strdup(value); + custom_property->type = LXW_CUSTOM_STRING; + + STAILQ_INSERT_TAIL(self->custom_properties, custom_property, + list_pointers); + + return LXW_NO_ERROR; +} + +/* + * Set a double number custom document property. + */ +lxw_error +workbook_set_custom_property_number(lxw_workbook *self, const char *name, + double value) +{ + lxw_custom_property *custom_property; + + if (!name) { + LXW_WARN_FORMAT("workbook_set_custom_property_number(): parameter " + "'name' cannot be NULL."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + if (lxw_utf8_strlen(name) > 255) { + LXW_WARN_FORMAT("workbook_set_custom_property_number(): parameter " + "'name' exceeds Excel length limit of 255."); + return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + } + + /* Create a struct to hold the custom property. */ + custom_property = calloc(1, sizeof(struct lxw_custom_property)); + RETURN_ON_MEM_ERROR(custom_property, LXW_ERROR_MEMORY_MALLOC_FAILED); + + custom_property->name = lxw_strdup(name); + custom_property->u.number = value; + custom_property->type = LXW_CUSTOM_DOUBLE; + + STAILQ_INSERT_TAIL(self->custom_properties, custom_property, + list_pointers); + + return LXW_NO_ERROR; +} + +/* + * Set a integer number custom document property. + */ +lxw_error +workbook_set_custom_property_integer(lxw_workbook *self, const char *name, + int32_t value) +{ + lxw_custom_property *custom_property; + + if (!name) { + LXW_WARN_FORMAT("workbook_set_custom_property_integer(): parameter " + "'name' cannot be NULL."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + if (strlen(name) > 255) { + LXW_WARN_FORMAT("workbook_set_custom_property_integer(): parameter " + "'name' exceeds Excel length limit of 255."); + return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + } + + /* Create a struct to hold the custom property. */ + custom_property = calloc(1, sizeof(struct lxw_custom_property)); + RETURN_ON_MEM_ERROR(custom_property, LXW_ERROR_MEMORY_MALLOC_FAILED); + + custom_property->name = lxw_strdup(name); + custom_property->u.integer = value; + custom_property->type = LXW_CUSTOM_INTEGER; + + STAILQ_INSERT_TAIL(self->custom_properties, custom_property, + list_pointers); + + return LXW_NO_ERROR; +} + +/* + * Set a boolean custom document property. + */ +lxw_error +workbook_set_custom_property_boolean(lxw_workbook *self, const char *name, + uint8_t value) +{ + lxw_custom_property *custom_property; + + if (!name) { + LXW_WARN_FORMAT("workbook_set_custom_property_boolean(): parameter " + "'name' cannot be NULL."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + if (lxw_utf8_strlen(name) > 255) { + LXW_WARN_FORMAT("workbook_set_custom_property_boolean(): parameter " + "'name' exceeds Excel length limit of 255."); + return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + } + + /* Create a struct to hold the custom property. */ + custom_property = calloc(1, sizeof(struct lxw_custom_property)); + RETURN_ON_MEM_ERROR(custom_property, LXW_ERROR_MEMORY_MALLOC_FAILED); + + custom_property->name = lxw_strdup(name); + custom_property->u.boolean = value; + custom_property->type = LXW_CUSTOM_BOOLEAN; + + STAILQ_INSERT_TAIL(self->custom_properties, custom_property, + list_pointers); + + return LXW_NO_ERROR; +} + +/* + * Set a datetime custom document property. + */ +lxw_error +workbook_set_custom_property_datetime(lxw_workbook *self, const char *name, + lxw_datetime *datetime) +{ + lxw_custom_property *custom_property; + + if (!name) { + LXW_WARN_FORMAT("workbook_set_custom_property_datetime(): parameter " + "'name' cannot be NULL."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + if (lxw_utf8_strlen(name) > 255) { + LXW_WARN_FORMAT("workbook_set_custom_property_datetime(): parameter " + "'name' exceeds Excel length limit of 255."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + if (!datetime) { + LXW_WARN_FORMAT("workbook_set_custom_property_datetime(): parameter " + "'datetime' cannot be NULL."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + /* Create a struct to hold the custom property. */ + custom_property = calloc(1, sizeof(struct lxw_custom_property)); + RETURN_ON_MEM_ERROR(custom_property, LXW_ERROR_MEMORY_MALLOC_FAILED); + + custom_property->name = lxw_strdup(name); + + memcpy(&custom_property->u.datetime, datetime, sizeof(lxw_datetime)); + custom_property->type = LXW_CUSTOM_DATETIME; + + STAILQ_INSERT_TAIL(self->custom_properties, custom_property, + list_pointers); + + return LXW_NO_ERROR; +} + +/* + * Get a worksheet object from its name. + */ +lxw_worksheet * +workbook_get_worksheet_by_name(lxw_workbook *self, const char *name) +{ + lxw_worksheet_name worksheet_name; + lxw_worksheet_name *found; + + if (!name) + return NULL; + + worksheet_name.name = name; + found = RB_FIND(lxw_worksheet_names, + self->worksheet_names, &worksheet_name); + + if (found) + return found->worksheet; + else + return NULL; +} + +/* + * Validate the worksheet name based on Excel's rules. + */ +lxw_error +workbook_validate_worksheet_name(lxw_workbook *self, const char *sheetname) +{ + /* Check the UTF-8 length of the worksheet name. */ + if (lxw_utf8_strlen(sheetname) > LXW_SHEETNAME_MAX) + return LXW_ERROR_SHEETNAME_LENGTH_EXCEEDED; + + /* Check that the worksheet name doesn't contain invalid characters. */ + if (strpbrk(sheetname, "[]:*?/\\")) + return LXW_ERROR_INVALID_SHEETNAME_CHARACTER; + + /* Check if the worksheet name is already in use. */ + if (workbook_get_worksheet_by_name(self, sheetname)) + return LXW_ERROR_SHEETNAME_ALREADY_USED; + + return LXW_NO_ERROR; +} diff --git a/src/libxlsxwriter/worksheet.c b/src/libxlsxwriter/worksheet.c new file mode 100644 index 0000000..6f8fded --- /dev/null +++ b/src/libxlsxwriter/worksheet.c @@ -0,0 +1,5024 @@ +/***************************************************************************** + * worksheet - A library for creating Excel XLSX worksheet files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/worksheet.h" +#include "xlsxwriter/format.h" +#include "xlsxwriter/utility.h" +#include "xlsxwriter/relationships.h" + +#define LXW_STR_MAX 32767 +#define LXW_BUFFER_SIZE 4096 +#define LXW_PORTRAIT 1 +#define LXW_LANDSCAPE 0 +#define LXW_PRINT_ACROSS 1 + +/* + * Forward declarations. + */ +STATIC void _worksheet_write_rows(lxw_worksheet *self); +STATIC int _row_cmp(lxw_row *row1, lxw_row *row2); +STATIC int _cell_cmp(lxw_cell *cell1, lxw_cell *cell2); + +#ifndef __clang_analyzer__ +LXW_RB_GENERATE_ROW(lxw_table_rows, lxw_row, tree_pointers, _row_cmp); +LXW_RB_GENERATE_CELL(lxw_table_cells, lxw_cell, tree_pointers, _cell_cmp); +#endif + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Find but don't create a row object for a given row number. + */ +lxw_row * +lxw_worksheet_find_row(lxw_worksheet *self, lxw_row_t row_num) +{ + lxw_row row; + + row.row_num = row_num; + + return RB_FIND(lxw_table_rows, self->table, &row); +} + +/* + * Find but don't create a cell object for a given row object and col number. + */ +lxw_cell * +lxw_worksheet_find_cell(lxw_row *row, lxw_col_t col_num) +{ + lxw_cell cell; + + if (!row) + return NULL; + + cell.col_num = col_num; + + return RB_FIND(lxw_table_cells, row->cells, &cell); +} + +/* + * Create a new worksheet object. + */ +lxw_worksheet * +lxw_worksheet_new(lxw_worksheet_init_data *init_data) +{ + lxw_worksheet *worksheet = calloc(1, sizeof(lxw_worksheet)); + GOTO_LABEL_ON_MEM_ERROR(worksheet, mem_error); + + worksheet->table = calloc(1, sizeof(struct lxw_table_rows)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->table, mem_error); + RB_INIT(worksheet->table); + + worksheet->hyperlinks = calloc(1, sizeof(struct lxw_table_rows)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->hyperlinks, mem_error); + RB_INIT(worksheet->hyperlinks); + + /* Initialize the cached rows. */ + worksheet->table->cached_row_num = LXW_ROW_MAX + 1; + worksheet->hyperlinks->cached_row_num = LXW_ROW_MAX + 1; + + if (init_data && init_data->optimize) { + worksheet->array = calloc(LXW_COL_MAX, sizeof(struct lxw_cell *)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->array, mem_error); + } + + worksheet->col_options = + calloc(LXW_COL_META_MAX, sizeof(lxw_col_options *)); + worksheet->col_options_max = LXW_COL_META_MAX; + GOTO_LABEL_ON_MEM_ERROR(worksheet->col_options, mem_error); + + worksheet->col_formats = calloc(LXW_COL_META_MAX, sizeof(lxw_format *)); + worksheet->col_formats_max = LXW_COL_META_MAX; + GOTO_LABEL_ON_MEM_ERROR(worksheet->col_formats, mem_error); + + worksheet->optimize_row = calloc(1, sizeof(struct lxw_row)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->optimize_row, mem_error); + worksheet->optimize_row->height = LXW_DEF_ROW_HEIGHT; + + worksheet->merged_ranges = calloc(1, sizeof(struct lxw_merged_ranges)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->merged_ranges, mem_error); + STAILQ_INIT(worksheet->merged_ranges); + + worksheet->image_data = calloc(1, sizeof(struct lxw_image_data)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->image_data, mem_error); + STAILQ_INIT(worksheet->image_data); + + worksheet->chart_data = calloc(1, sizeof(struct lxw_chart_data)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->chart_data, mem_error); + STAILQ_INIT(worksheet->chart_data); + + worksheet->selections = calloc(1, sizeof(struct lxw_selections)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->selections, mem_error); + STAILQ_INIT(worksheet->selections); + + worksheet->external_hyperlinks = calloc(1, sizeof(struct lxw_rel_tuples)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->external_hyperlinks, mem_error); + STAILQ_INIT(worksheet->external_hyperlinks); + + worksheet->external_drawing_links = + calloc(1, sizeof(struct lxw_rel_tuples)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->external_drawing_links, mem_error); + STAILQ_INIT(worksheet->external_drawing_links); + + worksheet->drawing_links = calloc(1, sizeof(struct lxw_rel_tuples)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->drawing_links, mem_error); + STAILQ_INIT(worksheet->drawing_links); + + if (init_data && init_data->optimize) { + FILE *tmpfile; + + tmpfile = lxw_tmpfile(init_data->tmpdir); + if (!tmpfile) { + LXW_ERROR("Error creating tmpfile() for worksheet in " + "'constant_memory' mode."); + goto mem_error; + } + + worksheet->optimize_tmpfile = tmpfile; + GOTO_LABEL_ON_MEM_ERROR(worksheet->optimize_tmpfile, mem_error); + worksheet->file = worksheet->optimize_tmpfile; + } + + /* Initialize the worksheet dimensions. */ + worksheet->dim_rowmax = 0; + worksheet->dim_colmax = 0; + worksheet->dim_rowmin = LXW_ROW_MAX; + worksheet->dim_colmin = LXW_COL_MAX; + + worksheet->default_row_height = LXW_DEF_ROW_HEIGHT; + worksheet->default_row_pixels = 20; + worksheet->default_col_pixels = 64; + + /* Initialize the page setup properties. */ + worksheet->fit_height = 0; + worksheet->fit_width = 0; + worksheet->page_start = 0; + worksheet->print_scale = 100; + worksheet->fit_page = 0; + worksheet->orientation = LXW_TRUE; + worksheet->page_order = 0; + worksheet->page_setup_changed = LXW_FALSE; + worksheet->page_view = LXW_FALSE; + worksheet->paper_size = 0; + worksheet->vertical_dpi = 0; + worksheet->horizontal_dpi = 0; + worksheet->margin_left = 0.7; + worksheet->margin_right = 0.7; + worksheet->margin_top = 0.75; + worksheet->margin_bottom = 0.75; + worksheet->margin_header = 0.3; + worksheet->margin_footer = 0.3; + worksheet->print_gridlines = 0; + worksheet->screen_gridlines = 1; + worksheet->print_options_changed = 0; + worksheet->zoom = 100; + worksheet->zoom_scale_normal = LXW_TRUE; + worksheet->show_zeros = LXW_TRUE; + worksheet->outline_on = LXW_TRUE; + worksheet->tab_color = LXW_COLOR_UNSET; + + if (init_data) { + worksheet->name = init_data->name; + worksheet->quoted_name = init_data->quoted_name; + worksheet->tmpdir = init_data->tmpdir; + worksheet->index = init_data->index; + worksheet->hidden = init_data->hidden; + worksheet->sst = init_data->sst; + worksheet->optimize = init_data->optimize; + worksheet->active_sheet = init_data->active_sheet; + worksheet->first_sheet = init_data->first_sheet; + } + + return worksheet; + +mem_error: + lxw_worksheet_free(worksheet); + return NULL; +} + +/* + * Free a worksheet cell. + */ +STATIC void +_free_cell(lxw_cell *cell) +{ + if (!cell) + return; + + if (cell->type != NUMBER_CELL && cell->type != STRING_CELL + && cell->type != BLANK_CELL && cell->type != BOOLEAN_CELL) { + + free(cell->u.string); + } + + free(cell->user_data1); + free(cell->user_data2); + + free(cell); +} + +/* + * Free a worksheet row. + */ +STATIC void +_free_row(lxw_row *row) +{ + lxw_cell *cell; + lxw_cell *next_cell; + + if (!row) + return; + + for (cell = RB_MIN(lxw_table_cells, row->cells); cell; cell = next_cell) { + next_cell = RB_NEXT(lxw_table_cells, row->cells, cell); + RB_REMOVE(lxw_table_cells, row->cells, cell); + _free_cell(cell); + } + + free(row->cells); + free(row); +} + +/* + * Free a worksheet image_options. + */ +STATIC void +_free_image_options(lxw_image_options *image) +{ + if (!image) + return; + + free(image->filename); + free(image->short_name); + free(image->extension); + free(image->url); + free(image->tip); + free(image); +} + +/* + * Free a worksheet object. + */ +void +lxw_worksheet_free(lxw_worksheet *worksheet) +{ + lxw_row *row; + lxw_row *next_row; + lxw_col_t col; + lxw_merged_range *merged_range; + lxw_image_options *image_options; + lxw_selection *selection; + lxw_rel_tuple *relationship; + + if (!worksheet) + return; + + if (worksheet->col_options) { + for (col = 0; col < worksheet->col_options_max; col++) { + if (worksheet->col_options[col]) + free(worksheet->col_options[col]); + } + } + + free(worksheet->col_options); + free(worksheet->col_sizes); + free(worksheet->col_formats); + + if (worksheet->table) { + for (row = RB_MIN(lxw_table_rows, worksheet->table); row; + row = next_row) { + + next_row = RB_NEXT(lxw_table_rows, worksheet->table, row); + RB_REMOVE(lxw_table_rows, worksheet->table, row); + _free_row(row); + } + + free(worksheet->table); + } + + if (worksheet->hyperlinks) { + + for (row = RB_MIN(lxw_table_rows, worksheet->hyperlinks); row; + row = next_row) { + + next_row = RB_NEXT(lxw_table_rows, worksheet->hyperlinks, row); + RB_REMOVE(lxw_table_rows, worksheet->hyperlinks, row); + _free_row(row); + } + + free(worksheet->hyperlinks); + } + + if (worksheet->merged_ranges) { + while (!STAILQ_EMPTY(worksheet->merged_ranges)) { + merged_range = STAILQ_FIRST(worksheet->merged_ranges); + STAILQ_REMOVE_HEAD(worksheet->merged_ranges, list_pointers); + free(merged_range); + } + + free(worksheet->merged_ranges); + } + + if (worksheet->image_data) { + while (!STAILQ_EMPTY(worksheet->image_data)) { + image_options = STAILQ_FIRST(worksheet->image_data); + STAILQ_REMOVE_HEAD(worksheet->image_data, list_pointers); + _free_image_options(image_options); + } + + free(worksheet->image_data); + } + + if (worksheet->chart_data) { + while (!STAILQ_EMPTY(worksheet->chart_data)) { + image_options = STAILQ_FIRST(worksheet->chart_data); + STAILQ_REMOVE_HEAD(worksheet->chart_data, list_pointers); + _free_image_options(image_options); + } + + free(worksheet->chart_data); + } + + if (worksheet->selections) { + while (!STAILQ_EMPTY(worksheet->selections)) { + selection = STAILQ_FIRST(worksheet->selections); + STAILQ_REMOVE_HEAD(worksheet->selections, list_pointers); + free(selection); + } + + free(worksheet->selections); + } + + /* TODO. Add function for freeing the relationship lists. */ + while (!STAILQ_EMPTY(worksheet->external_hyperlinks)) { + relationship = STAILQ_FIRST(worksheet->external_hyperlinks); + STAILQ_REMOVE_HEAD(worksheet->external_hyperlinks, list_pointers); + free(relationship->type); + free(relationship->target); + free(relationship->target_mode); + free(relationship); + } + free(worksheet->external_hyperlinks); + + while (!STAILQ_EMPTY(worksheet->external_drawing_links)) { + relationship = STAILQ_FIRST(worksheet->external_drawing_links); + STAILQ_REMOVE_HEAD(worksheet->external_drawing_links, list_pointers); + free(relationship->type); + free(relationship->target); + free(relationship->target_mode); + free(relationship); + } + free(worksheet->external_drawing_links); + + while (!STAILQ_EMPTY(worksheet->drawing_links)) { + relationship = STAILQ_FIRST(worksheet->drawing_links); + STAILQ_REMOVE_HEAD(worksheet->drawing_links, list_pointers); + free(relationship->type); + free(relationship->target); + free(relationship->target_mode); + free(relationship); + } + free(worksheet->drawing_links); + + if (worksheet->array) { + for (col = 0; col < LXW_COL_MAX; col++) { + _free_cell(worksheet->array[col]); + } + free(worksheet->array); + } + + if (worksheet->optimize_row) + free(worksheet->optimize_row); + + if (worksheet->drawing) + lxw_drawing_free(worksheet->drawing); + + free(worksheet->hbreaks); + free(worksheet->vbreaks); + free(worksheet->name); + free(worksheet->quoted_name); + + free(worksheet); + worksheet = NULL; +} + +/* + * Create a new worksheet row object. + */ +STATIC lxw_row * +_new_row(lxw_row_t row_num) +{ + lxw_row *row = calloc(1, sizeof(lxw_row)); + + if (row) { + row->row_num = row_num; + row->cells = calloc(1, sizeof(struct lxw_table_cells)); + row->height = LXW_DEF_ROW_HEIGHT; + + if (row->cells) + RB_INIT(row->cells); + else + LXW_MEM_ERROR(); + } + else { + LXW_MEM_ERROR(); + } + + return row; +} + +/* + * Create a new worksheet number cell object. + */ +STATIC lxw_cell * +_new_number_cell(lxw_row_t row_num, + lxw_col_t col_num, double value, lxw_format *format) +{ + lxw_cell *cell = calloc(1, sizeof(lxw_cell)); + RETURN_ON_MEM_ERROR(cell, cell); + + cell->row_num = row_num; + cell->col_num = col_num; + cell->type = NUMBER_CELL; + cell->format = format; + cell->u.number = value; + + return cell; +} + +/* + * Create a new worksheet string cell object. + */ +STATIC lxw_cell * +_new_string_cell(lxw_row_t row_num, + lxw_col_t col_num, int32_t string_id, char *sst_string, + lxw_format *format) +{ + lxw_cell *cell = calloc(1, sizeof(lxw_cell)); + RETURN_ON_MEM_ERROR(cell, cell); + + cell->row_num = row_num; + cell->col_num = col_num; + cell->type = STRING_CELL; + cell->format = format; + cell->u.string_id = string_id; + cell->sst_string = sst_string; + + return cell; +} + +/* + * Create a new worksheet inline_string cell object. + */ +STATIC lxw_cell * +_new_inline_string_cell(lxw_row_t row_num, + lxw_col_t col_num, char *string, lxw_format *format) +{ + lxw_cell *cell = calloc(1, sizeof(lxw_cell)); + RETURN_ON_MEM_ERROR(cell, cell); + + cell->row_num = row_num; + cell->col_num = col_num; + cell->type = INLINE_STRING_CELL; + cell->format = format; + cell->u.string = string; + + return cell; +} + +/* + * Create a new worksheet formula cell object. + */ +STATIC lxw_cell * +_new_formula_cell(lxw_row_t row_num, + lxw_col_t col_num, char *formula, lxw_format *format) +{ + lxw_cell *cell = calloc(1, sizeof(lxw_cell)); + RETURN_ON_MEM_ERROR(cell, cell); + + cell->row_num = row_num; + cell->col_num = col_num; + cell->type = FORMULA_CELL; + cell->format = format; + cell->u.string = formula; + + return cell; +} + +/* + * Create a new worksheet array formula cell object. + */ +STATIC lxw_cell * +_new_array_formula_cell(lxw_row_t row_num, lxw_col_t col_num, char *formula, + char *range, lxw_format *format) +{ + lxw_cell *cell = calloc(1, sizeof(lxw_cell)); + RETURN_ON_MEM_ERROR(cell, cell); + + cell->row_num = row_num; + cell->col_num = col_num; + cell->type = ARRAY_FORMULA_CELL; + cell->format = format; + cell->u.string = formula; + cell->user_data1 = range; + + return cell; +} + +/* + * Create a new worksheet blank cell object. + */ +STATIC lxw_cell * +_new_blank_cell(lxw_row_t row_num, lxw_col_t col_num, lxw_format *format) +{ + lxw_cell *cell = calloc(1, sizeof(lxw_cell)); + RETURN_ON_MEM_ERROR(cell, cell); + + cell->row_num = row_num; + cell->col_num = col_num; + cell->type = BLANK_CELL; + cell->format = format; + + return cell; +} + +/* + * Create a new worksheet boolean cell object. + */ +STATIC lxw_cell * +_new_boolean_cell(lxw_row_t row_num, lxw_col_t col_num, int value, + lxw_format *format) +{ + lxw_cell *cell = calloc(1, sizeof(lxw_cell)); + RETURN_ON_MEM_ERROR(cell, cell); + + cell->row_num = row_num; + cell->col_num = col_num; + cell->type = BOOLEAN_CELL; + cell->format = format; + cell->u.number = value; + + return cell; +} + +/* + * Create a new worksheet hyperlink cell object. + */ +STATIC lxw_cell * +_new_hyperlink_cell(lxw_row_t row_num, lxw_col_t col_num, + enum cell_types link_type, char *url, char *string, + char *tooltip) +{ + lxw_cell *cell = calloc(1, sizeof(lxw_cell)); + RETURN_ON_MEM_ERROR(cell, cell); + + cell->row_num = row_num; + cell->col_num = col_num; + cell->type = link_type; + cell->u.string = url; + cell->user_data1 = string; + cell->user_data2 = tooltip; + + return cell; +} + +/* + * Get or create the row object for a given row number. + */ +STATIC lxw_row * +_get_row_list(struct lxw_table_rows *table, lxw_row_t row_num) +{ + lxw_row *row; + lxw_row *existing_row; + + if (table->cached_row_num == row_num) + return table->cached_row; + + /* Create a new row and try and insert it. */ + row = _new_row(row_num); + existing_row = RB_INSERT(lxw_table_rows, table, row); + + /* If existing_row is not NULL, then it already existed. Free new row */ + /* and return existing_row. */ + if (existing_row) { + _free_row(row); + row = existing_row; + } + + table->cached_row = row; + table->cached_row_num = row_num; + + return row; +} + +/* + * Get or create the row object for a given row number. + */ +STATIC lxw_row * +_get_row(lxw_worksheet *self, lxw_row_t row_num) +{ + lxw_row *row; + + if (!self->optimize) { + row = _get_row_list(self->table, row_num); + return row; + } + else { + if (row_num < self->optimize_row->row_num) { + return NULL; + } + else if (row_num == self->optimize_row->row_num) { + return self->optimize_row; + } + else { + /* Flush row. */ + lxw_worksheet_write_single_row(self); + row = self->optimize_row; + row->row_num = row_num; + return row; + } + } +} + +/* + * Insert a cell object in the cell list of a row object. + */ +STATIC void +_insert_cell_list(struct lxw_table_cells *cell_list, + lxw_cell *cell, lxw_col_t col_num) +{ + lxw_cell *existing_cell; + + cell->col_num = col_num; + + existing_cell = RB_INSERT(lxw_table_cells, cell_list, cell); + + /* If existing_cell is not NULL, then that cell already existed. */ + /* Remove existing_cell and add new one in again. */ + if (existing_cell) { + RB_REMOVE(lxw_table_cells, cell_list, existing_cell); + + /* Add it in again. */ + RB_INSERT(lxw_table_cells, cell_list, cell); + _free_cell(existing_cell); + } + + return; +} + +/* + * Insert a cell object into the cell list or array. + */ +STATIC void +_insert_cell(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, + lxw_cell *cell) +{ + lxw_row *row = _get_row(self, row_num); + + if (!self->optimize) { + row->data_changed = LXW_TRUE; + _insert_cell_list(row->cells, cell, col_num); + } + else { + if (row) { + row->data_changed = LXW_TRUE; + + /* Overwrite an existing cell if necessary. */ + if (self->array[col_num]) + _free_cell(self->array[col_num]); + + self->array[col_num] = cell; + } + } +} + +/* + * Insert a hyperlink object into the hyperlink list. + */ +STATIC void +_insert_hyperlink(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, + lxw_cell *link) +{ + lxw_row *row = _get_row_list(self->hyperlinks, row_num); + + _insert_cell_list(row->cells, link, col_num); +} + +/* + * Next power of two for column reallocs. Taken from bithacks in the public + * domain. + */ +STATIC lxw_col_t +_next_power_of_two(uint16_t col) +{ + col--; + col |= col >> 1; + col |= col >> 2; + col |= col >> 4; + col |= col >> 8; + col++; + + return col; +} + +/* + * Check that row and col are within the allowed Excel range and store max + * and min values for use in other methods/elements. + * + * The ignore_row/ignore_col flags are used to indicate that we wish to + * perform the dimension check without storing the value. + */ +STATIC lxw_error +_check_dimensions(lxw_worksheet *self, + lxw_row_t row_num, + lxw_col_t col_num, int8_t ignore_row, int8_t ignore_col) +{ + if (row_num >= LXW_ROW_MAX) + return LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE; + + if (col_num >= LXW_COL_MAX) + return LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE; + + /* In optimization mode we don't change dimensions for rows that are */ + /* already written. */ + if (!ignore_row && !ignore_col && self->optimize) { + if (row_num < self->optimize_row->row_num) + return LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE; + } + + if (!ignore_row) { + if (row_num < self->dim_rowmin) + self->dim_rowmin = row_num; + if (row_num > self->dim_rowmax) + self->dim_rowmax = row_num; + } + + if (!ignore_col) { + if (col_num < self->dim_colmin) + self->dim_colmin = col_num; + if (col_num > self->dim_colmax) + self->dim_colmax = col_num; + } + + return LXW_NO_ERROR; +} + +/* + * Comparator for the row structure red/black tree. + */ +STATIC int +_row_cmp(lxw_row *row1, lxw_row *row2) +{ + if (row1->row_num > row2->row_num) + return 1; + if (row1->row_num < row2->row_num) + return -1; + return 0; +} + +/* + * Comparator for the cell structure red/black tree. + */ +STATIC int +_cell_cmp(lxw_cell *cell1, lxw_cell *cell2) +{ + if (cell1->col_num > cell2->col_num) + return 1; + if (cell1->col_num < cell2->col_num) + return -1; + return 0; +} + +/* + * Hash a worksheet password. Based on the algorithm provided by Daniel Rentz + * of OpenOffice. + */ +STATIC uint16_t +_hash_password(const char *password) +{ + size_t count; + uint8_t i; + uint16_t hash = 0x0000; + + count = strlen(password); + + for (i = 0; i < count; i++) { + uint32_t low_15; + uint32_t high_15; + uint32_t letter = password[i] << (i + 1); + + low_15 = letter & 0x7fff; + high_15 = letter & (0x7fff << 15); + high_15 = high_15 >> 15; + letter = low_15 | high_15; + + hash ^= letter; + } + + hash ^= count; + hash ^= 0xCE4B; + + return hash; +} + +/* + * Simple replacement for libgen.h basename() for compatibility with MSVC. It + * handles forward and back slashes. It doesn't copy exactly the return + * format of basename(). + */ +char * +lxw_basename(const char *path) +{ + + char *forward_slash; + char *back_slash; + + if (!path) + return NULL; + + forward_slash = strrchr(path, '/'); + back_slash = strrchr(path, '\\'); + + if (!forward_slash && !back_slash) + return (char *) path; + + if (forward_slash > back_slash) + return forward_slash + 1; + else + return back_slash + 1; +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ +/* + * Write the XML declaration. + */ +STATIC void +_worksheet_xml_declaration(lxw_worksheet *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_worksheet(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns[] = "http://schemas.openxmlformats.org/" + "spreadsheetml/2006/main"; + char xmlns_r[] = "http://schemas.openxmlformats.org/" + "officeDocument/2006/relationships"; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); + LXW_PUSH_ATTRIBUTES_STR("xmlns:r", xmlns_r); + + lxw_xml_start_tag(self->file, "worksheet", &attributes); + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_dimension(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char ref[LXW_MAX_CELL_RANGE_LENGTH]; + lxw_row_t dim_rowmin = self->dim_rowmin; + lxw_row_t dim_rowmax = self->dim_rowmax; + lxw_col_t dim_colmin = self->dim_colmin; + lxw_col_t dim_colmax = self->dim_colmax; + + if (dim_rowmin == LXW_ROW_MAX && dim_colmin == LXW_COL_MAX) { + /* If the rows and cols are still the defaults then no dimensions have + * been set and we use the default range "A1". */ + lxw_rowcol_to_range(ref, 0, 0, 0, 0); + } + else if (dim_rowmin == LXW_ROW_MAX && dim_colmin != LXW_COL_MAX) { + /* If the rows aren't set but the columns are then the dimensions have + * been changed via set_column(). */ + lxw_rowcol_to_range(ref, 0, dim_colmin, 0, dim_colmax); + } + else { + lxw_rowcol_to_range(ref, dim_rowmin, dim_colmin, dim_rowmax, + dim_colmax); + } + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("ref", ref); + + lxw_xml_empty_tag(self->file, "dimension", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element for freeze panes. + */ +STATIC void +_worksheet_write_freeze_panes(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + lxw_selection *selection; + lxw_selection *user_selection; + lxw_row_t row = self->panes.first_row; + lxw_col_t col = self->panes.first_col; + lxw_row_t top_row = self->panes.top_row; + lxw_col_t left_col = self->panes.left_col; + + char row_cell[LXW_MAX_CELL_NAME_LENGTH]; + char col_cell[LXW_MAX_CELL_NAME_LENGTH]; + char top_left_cell[LXW_MAX_CELL_NAME_LENGTH]; + char active_pane[LXW_PANE_NAME_LENGTH]; + + /* If there is a user selection we remove it from the list and use it. */ + if (!STAILQ_EMPTY(self->selections)) { + user_selection = STAILQ_FIRST(self->selections); + STAILQ_REMOVE_HEAD(self->selections, list_pointers); + } + else { + /* or else create a new blank selection. */ + user_selection = calloc(1, sizeof(lxw_selection)); + RETURN_VOID_ON_MEM_ERROR(user_selection); + } + + LXW_INIT_ATTRIBUTES(); + + lxw_rowcol_to_cell(top_left_cell, top_row, left_col); + + /* Set the active pane. */ + if (row && col) { + lxw_strcpy(active_pane, "bottomRight"); + + lxw_rowcol_to_cell(row_cell, row, 0); + lxw_rowcol_to_cell(col_cell, 0, col); + + selection = calloc(1, sizeof(lxw_selection)); + if (selection) { + lxw_strcpy(selection->pane, "topRight"); + lxw_strcpy(selection->active_cell, col_cell); + lxw_strcpy(selection->sqref, col_cell); + + STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); + } + + selection = calloc(1, sizeof(lxw_selection)); + if (selection) { + lxw_strcpy(selection->pane, "bottomLeft"); + lxw_strcpy(selection->active_cell, row_cell); + lxw_strcpy(selection->sqref, row_cell); + + STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); + } + + selection = calloc(1, sizeof(lxw_selection)); + if (selection) { + lxw_strcpy(selection->pane, "bottomRight"); + lxw_strcpy(selection->active_cell, user_selection->active_cell); + lxw_strcpy(selection->sqref, user_selection->sqref); + + STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); + } + } + else if (col) { + lxw_strcpy(active_pane, "topRight"); + + selection = calloc(1, sizeof(lxw_selection)); + if (selection) { + lxw_strcpy(selection->pane, "topRight"); + lxw_strcpy(selection->active_cell, user_selection->active_cell); + lxw_strcpy(selection->sqref, user_selection->sqref); + + STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); + } + } + else { + lxw_strcpy(active_pane, "bottomLeft"); + + selection = calloc(1, sizeof(lxw_selection)); + if (selection) { + lxw_strcpy(selection->pane, "bottomLeft"); + lxw_strcpy(selection->active_cell, user_selection->active_cell); + lxw_strcpy(selection->sqref, user_selection->sqref); + + STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); + } + } + + if (col) + LXW_PUSH_ATTRIBUTES_INT("xSplit", col); + + if (row) + LXW_PUSH_ATTRIBUTES_INT("ySplit", row); + + LXW_PUSH_ATTRIBUTES_STR("topLeftCell", top_left_cell); + LXW_PUSH_ATTRIBUTES_STR("activePane", active_pane); + + if (self->panes.type == FREEZE_PANES) + LXW_PUSH_ATTRIBUTES_STR("state", "frozen"); + else if (self->panes.type == FREEZE_SPLIT_PANES) + LXW_PUSH_ATTRIBUTES_STR("state", "frozenSplit"); + + lxw_xml_empty_tag(self->file, "pane", &attributes); + + free(user_selection); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Convert column width from user units to pane split width. + */ +STATIC uint32_t +_worksheet_calculate_x_split_width(double x_split) +{ + uint32_t width; + uint32_t pixels; + uint32_t points; + uint32_t twips; + double max_digit_width = 7.0; /* For Calabri 11. */ + double padding = 5.0; + + /* Convert to pixels. */ + if (x_split < 1.0) { + pixels = (uint32_t) (x_split * (max_digit_width + padding) + 0.5); + } + else { + pixels = (uint32_t) (x_split * max_digit_width + 0.5) + 5; + } + + /* Convert to points. */ + points = (pixels * 3) / 4; + + /* Convert to twips (twentieths of a point). */ + twips = points * 20; + + /* Add offset/padding. */ + width = twips + 390; + + return width; +} + +/* + * Write the element for split panes. + */ +STATIC void +_worksheet_write_split_panes(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + lxw_selection *selection; + lxw_selection *user_selection; + lxw_row_t row = self->panes.first_row; + lxw_col_t col = self->panes.first_col; + lxw_row_t top_row = self->panes.top_row; + lxw_col_t left_col = self->panes.left_col; + double x_split = self->panes.x_split; + double y_split = self->panes.y_split; + uint8_t has_selection = LXW_FALSE; + + char row_cell[LXW_MAX_CELL_NAME_LENGTH]; + char col_cell[LXW_MAX_CELL_NAME_LENGTH]; + char top_left_cell[LXW_MAX_CELL_NAME_LENGTH]; + char active_pane[LXW_PANE_NAME_LENGTH]; + + /* If there is a user selection we remove it from the list and use it. */ + if (!STAILQ_EMPTY(self->selections)) { + user_selection = STAILQ_FIRST(self->selections); + STAILQ_REMOVE_HEAD(self->selections, list_pointers); + has_selection = LXW_TRUE; + } + else { + /* or else create a new blank selection. */ + user_selection = calloc(1, sizeof(lxw_selection)); + RETURN_VOID_ON_MEM_ERROR(user_selection); + } + + LXW_INIT_ATTRIBUTES(); + + /* Convert the row and col to 1/20 twip units with padding. */ + if (y_split > 0.0) + y_split = (uint32_t) (20 * y_split + 300); + + if (x_split > 0.0) + x_split = _worksheet_calculate_x_split_width(x_split); + + /* For non-explicit topLeft definitions, estimate the cell offset based on + * the pixels dimensions. This is only a workaround and doesn't take + * adjusted cell dimensions into account. + */ + if (top_row == row && left_col == col) { + top_row = (lxw_row_t) (0.5 + (y_split - 300.0) / 20.0 / 15.0); + left_col = (lxw_col_t) (0.5 + (x_split - 390.0) / 20.0 / 3.0 / 16.0); + } + + lxw_rowcol_to_cell(top_left_cell, top_row, left_col); + + /* If there is no selection set the active cell to the top left cell. */ + if (!has_selection) { + lxw_strcpy(user_selection->active_cell, top_left_cell); + lxw_strcpy(user_selection->sqref, top_left_cell); + } + + /* Set the active pane. */ + if (y_split > 0.0 && x_split > 0.0) { + lxw_strcpy(active_pane, "bottomRight"); + + lxw_rowcol_to_cell(row_cell, top_row, 0); + lxw_rowcol_to_cell(col_cell, 0, left_col); + + selection = calloc(1, sizeof(lxw_selection)); + if (selection) { + lxw_strcpy(selection->pane, "topRight"); + lxw_strcpy(selection->active_cell, col_cell); + lxw_strcpy(selection->sqref, col_cell); + + STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); + } + + selection = calloc(1, sizeof(lxw_selection)); + if (selection) { + lxw_strcpy(selection->pane, "bottomLeft"); + lxw_strcpy(selection->active_cell, row_cell); + lxw_strcpy(selection->sqref, row_cell); + + STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); + } + + selection = calloc(1, sizeof(lxw_selection)); + if (selection) { + lxw_strcpy(selection->pane, "bottomRight"); + lxw_strcpy(selection->active_cell, user_selection->active_cell); + lxw_strcpy(selection->sqref, user_selection->sqref); + + STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); + } + } + else if (x_split > 0.0) { + lxw_strcpy(active_pane, "topRight"); + + selection = calloc(1, sizeof(lxw_selection)); + if (selection) { + lxw_strcpy(selection->pane, "topRight"); + lxw_strcpy(selection->active_cell, user_selection->active_cell); + lxw_strcpy(selection->sqref, user_selection->sqref); + + STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); + } + } + else { + lxw_strcpy(active_pane, "bottomLeft"); + + selection = calloc(1, sizeof(lxw_selection)); + if (selection) { + lxw_strcpy(selection->pane, "bottomLeft"); + lxw_strcpy(selection->active_cell, user_selection->active_cell); + lxw_strcpy(selection->sqref, user_selection->sqref); + + STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); + } + } + + if (x_split > 0.0) + LXW_PUSH_ATTRIBUTES_DBL("xSplit", x_split); + + if (y_split > 0.0) + LXW_PUSH_ATTRIBUTES_DBL("ySplit", y_split); + + LXW_PUSH_ATTRIBUTES_STR("topLeftCell", top_left_cell); + + if (has_selection) + LXW_PUSH_ATTRIBUTES_STR("activePane", active_pane); + + lxw_xml_empty_tag(self->file, "pane", &attributes); + + free(user_selection); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_selection(lxw_worksheet *self, lxw_selection *selection) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (*selection->pane) + LXW_PUSH_ATTRIBUTES_STR("pane", selection->pane); + + if (*selection->active_cell) + LXW_PUSH_ATTRIBUTES_STR("activeCell", selection->active_cell); + + if (*selection->sqref) + LXW_PUSH_ATTRIBUTES_STR("sqref", selection->sqref); + + lxw_xml_empty_tag(self->file, "selection", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the elements. + */ +STATIC void +_worksheet_write_selections(lxw_worksheet *self) +{ + lxw_selection *selection; + + STAILQ_FOREACH(selection, self->selections, list_pointers) { + _worksheet_write_selection(self, selection); + } +} + +/* + * Write the frozen or split elements. + */ +STATIC void +_worksheet_write_panes(lxw_worksheet *self) +{ + if (self->panes.type == NO_PANES) + return; + + else if (self->panes.type == FREEZE_PANES) + _worksheet_write_freeze_panes(self); + + else if (self->panes.type == FREEZE_SPLIT_PANES) + _worksheet_write_freeze_panes(self); + + else if (self->panes.type == SPLIT_PANES) + _worksheet_write_split_panes(self); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_sheet_view(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + /* Hide screen gridlines if required */ + if (!self->screen_gridlines) + LXW_PUSH_ATTRIBUTES_STR("showGridLines", "0"); + + /* Hide zeroes in cells. */ + if (!self->show_zeros) { + LXW_PUSH_ATTRIBUTES_STR("showZeros", "0"); + } + + /* Display worksheet right to left for Hebrew, Arabic and others. */ + if (self->right_to_left) { + LXW_PUSH_ATTRIBUTES_STR("rightToLeft", "1"); + } + + /* Show that the sheet tab is selected. */ + if (self->selected) + LXW_PUSH_ATTRIBUTES_STR("tabSelected", "1"); + + /* Turn outlines off. Also required in the outlinePr element. */ + if (!self->outline_on) { + LXW_PUSH_ATTRIBUTES_STR("showOutlineSymbols", "0"); + } + + /* Set the page view/layout mode if required. */ + if (self->page_view) + LXW_PUSH_ATTRIBUTES_STR("view", "pageLayout"); + + /* Set the zoom level. */ + if (self->zoom != 100) { + if (!self->page_view) { + LXW_PUSH_ATTRIBUTES_INT("zoomScale", self->zoom); + + if (self->zoom_scale_normal) + LXW_PUSH_ATTRIBUTES_INT("zoomScaleNormal", self->zoom); + } + } + + LXW_PUSH_ATTRIBUTES_STR("workbookViewId", "0"); + + if (self->panes.type != NO_PANES || !STAILQ_EMPTY(self->selections)) { + lxw_xml_start_tag(self->file, "sheetView", &attributes); + _worksheet_write_panes(self); + _worksheet_write_selections(self); + lxw_xml_end_tag(self->file, "sheetView"); + } + else { + lxw_xml_empty_tag(self->file, "sheetView", &attributes); + } + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_sheet_views(lxw_worksheet *self) +{ + lxw_xml_start_tag(self->file, "sheetViews", NULL); + + /* Write the sheetView element. */ + _worksheet_write_sheet_view(self); + + lxw_xml_end_tag(self->file, "sheetViews"); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_sheet_format_pr(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_DBL("defaultRowHeight", self->default_row_height); + + if (self->default_row_height != LXW_DEF_ROW_HEIGHT) + LXW_PUSH_ATTRIBUTES_STR("customHeight", "1"); + + if (self->default_row_zeroed) + LXW_PUSH_ATTRIBUTES_STR("zeroHeight", "1"); + + lxw_xml_empty_tag(self->file, "sheetFormatPr", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_sheet_data(lxw_worksheet *self) +{ + if (RB_EMPTY(self->table)) { + lxw_xml_empty_tag(self->file, "sheetData", NULL); + } + else { + lxw_xml_start_tag(self->file, "sheetData", NULL); + _worksheet_write_rows(self); + lxw_xml_end_tag(self->file, "sheetData"); + } +} + +/* + * Write the element when the memory optimization is on. In which + * case we read the data stored in the temp file and rewrite it to the XML + * sheet file. + */ +STATIC void +_worksheet_write_optimized_sheet_data(lxw_worksheet *self) +{ + size_t read_size = 1; + char buffer[LXW_BUFFER_SIZE]; + + if (self->dim_rowmin == LXW_ROW_MAX) { + /* If the dimensions aren't defined then there is no data to write. */ + lxw_xml_empty_tag(self->file, "sheetData", NULL); + } + else { + + lxw_xml_start_tag(self->file, "sheetData", NULL); + + /* Flush and rewind the temp file. */ + fflush(self->optimize_tmpfile); + rewind(self->optimize_tmpfile); + + while (read_size) { + read_size = + fread(buffer, 1, LXW_BUFFER_SIZE, self->optimize_tmpfile); + fwrite(buffer, 1, read_size, self->file); + } + + fclose(self->optimize_tmpfile); + + lxw_xml_end_tag(self->file, "sheetData"); + } +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_page_margins(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + double left = self->margin_left; + double right = self->margin_right; + double top = self->margin_top; + double bottom = self->margin_bottom; + double header = self->margin_header; + double footer = self->margin_footer; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_DBL("left", left); + LXW_PUSH_ATTRIBUTES_DBL("right", right); + LXW_PUSH_ATTRIBUTES_DBL("top", top); + LXW_PUSH_ATTRIBUTES_DBL("bottom", bottom); + LXW_PUSH_ATTRIBUTES_DBL("header", header); + LXW_PUSH_ATTRIBUTES_DBL("footer", footer); + + lxw_xml_empty_tag(self->file, "pageMargins", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + * The following is an example taken from Excel. + * + */ +STATIC void +_worksheet_write_page_setup(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (!self->page_setup_changed) + return; + + /* Set paper size. */ + if (self->paper_size) + LXW_PUSH_ATTRIBUTES_INT("paperSize", self->paper_size); + + /* Set the print_scale. */ + if (self->print_scale != 100) + LXW_PUSH_ATTRIBUTES_INT("scale", self->print_scale); + + /* Set the "Fit to page" properties. */ + if (self->fit_page && self->fit_width != 1) + LXW_PUSH_ATTRIBUTES_INT("fitToWidth", self->fit_width); + + if (self->fit_page && self->fit_height != 1) + LXW_PUSH_ATTRIBUTES_INT("fitToHeight", self->fit_height); + + /* Set the page print direction. */ + if (self->page_order) + LXW_PUSH_ATTRIBUTES_STR("pageOrder", "overThenDown"); + + /* Set start page. */ + if (self->page_start > 1) + LXW_PUSH_ATTRIBUTES_INT("firstPageNumber", self->page_start); + + /* Set page orientation. */ + if (self->orientation) + LXW_PUSH_ATTRIBUTES_STR("orientation", "portrait"); + else + LXW_PUSH_ATTRIBUTES_STR("orientation", "landscape"); + + /* Set start page active flag. */ + if (self->page_start) + LXW_PUSH_ATTRIBUTES_INT("useFirstPageNumber", 1); + + /* Set the DPI. Mainly only for testing. */ + if (self->horizontal_dpi) + LXW_PUSH_ATTRIBUTES_INT("horizontalDpi", self->horizontal_dpi); + + if (self->vertical_dpi) + LXW_PUSH_ATTRIBUTES_INT("verticalDpi", self->vertical_dpi); + + lxw_xml_empty_tag(self->file, "pageSetup", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_print_options(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + if (!self->print_options_changed) + return; + + LXW_INIT_ATTRIBUTES(); + + /* Set horizontal centering. */ + if (self->hcenter) { + LXW_PUSH_ATTRIBUTES_STR("horizontalCentered", "1"); + } + + /* Set vertical centering. */ + if (self->vcenter) { + LXW_PUSH_ATTRIBUTES_STR("verticalCentered", "1"); + } + + /* Enable row and column headers. */ + if (self->print_headers) { + LXW_PUSH_ATTRIBUTES_STR("headings", "1"); + } + + /* Set printed gridlines. */ + if (self->print_gridlines) { + LXW_PUSH_ATTRIBUTES_STR("gridLines", "1"); + } + + lxw_xml_empty_tag(self->file, "printOptions", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_row(lxw_worksheet *self, lxw_row *row, char *spans) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + int32_t xf_index = 0; + double height; + + if (row->format) { + xf_index = lxw_format_get_xf_index(row->format); + } + + if (row->height_changed) + height = row->height; + else + height = self->default_row_height; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("r", row->row_num + 1); + + if (spans) + LXW_PUSH_ATTRIBUTES_STR("spans", spans); + + if (xf_index) + LXW_PUSH_ATTRIBUTES_INT("s", xf_index); + + if (row->format) + LXW_PUSH_ATTRIBUTES_STR("customFormat", "1"); + + if (height != LXW_DEF_ROW_HEIGHT) + LXW_PUSH_ATTRIBUTES_DBL("ht", height); + + if (row->hidden) + LXW_PUSH_ATTRIBUTES_STR("hidden", "1"); + + if (height != LXW_DEF_ROW_HEIGHT) + LXW_PUSH_ATTRIBUTES_STR("customHeight", "1"); + + if (row->collapsed) + LXW_PUSH_ATTRIBUTES_STR("collapsed", "1"); + + if (!row->data_changed) + lxw_xml_empty_tag(self->file, "row", &attributes); + else + lxw_xml_start_tag(self->file, "row", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Convert the width of a cell from user's units to pixels. Excel rounds the + * column width to the nearest pixel. If the width hasn't been set by the user + * we use the default value. If the column is hidden it has a value of zero. + */ +STATIC int32_t +_worksheet_size_col(lxw_worksheet *self, lxw_col_t col_num) +{ + lxw_col_options *col_opt = NULL; + uint32_t pixels; + double width; + double max_digit_width = 7.0; /* For Calabri 11. */ + double padding = 5.0; + lxw_col_t col_index; + + /* Search for the col number in the array of col_options. Each col_option + * entry contains the start and end column for a range. + */ + for (col_index = 0; col_index < self->col_options_max; col_index++) { + col_opt = self->col_options[col_index]; + + if (col_opt) { + if (col_num >= col_opt->firstcol && col_num <= col_opt->lastcol) + break; + else + col_opt = NULL; + } + } + + if (col_opt) { + width = col_opt->width; + + /* Convert to pixels. */ + if (width == 0) { + pixels = 0; + } + else if (width < 1.0) { + pixels = (uint32_t) (width * (max_digit_width + padding) + 0.5); + } + else { + pixels = (uint32_t) (width * max_digit_width + 0.5) + 5; + } + } + else { + pixels = self->default_col_pixels; + } + + return pixels; +} + +/* + * Convert the height of a cell from user's units to pixels. If the height + * hasn't been set by the user we use the default value. If the row is hidden + * it has a value of zero. + */ +STATIC int32_t +_worksheet_size_row(lxw_worksheet *self, lxw_row_t row_num) +{ + lxw_row *row; + uint32_t pixels; + double height; + + row = lxw_worksheet_find_row(self, row_num); + + if (row) { + height = row->height; + + if (height == 0) + pixels = 0; + else + pixels = (uint32_t) (4.0 / 3.0 * height); + } + else { + pixels = (uint32_t) (4.0 / 3.0 * self->default_row_height); + } + + return pixels; +} + +/* + * Calculate the vertices that define the position of a graphical object + * within the worksheet in pixels. + * +------------+------------+ + * | A | B | + * +-----+------------+------------+ + * | |(x1,y1) | | + * | 1 |(A1)._______|______ | + * | | | | | + * | | | | | + * +-----+----| BITMAP |-----+ + * | | | | | + * | 2 | |______________. | + * | | | (B2)| + * | | | (x2,y2)| + * +---- +------------+------------+ + * + * Example of an object that covers some of the area from cell A1 to cell B2. + * Based on the width and height of the object we need to calculate 8 vars: + * + * col_start, row_start, col_end, row_end, x1, y1, x2, y2. + * + * We also calculate the absolute x and y position of the top left vertex of + * the object. This is required for images: + * + * x_abs, y_abs + * + * The width and height of the cells that the object occupies can be variable + * and have to be taken into account. + * + * The values of col_start and row_start are passed in from the calling + * function. The values of col_end and row_end are calculated by subtracting + * the width and height of the object from the width and height of the + * underlying cells. + */ +STATIC void +_worksheet_position_object_pixels(lxw_worksheet *self, + lxw_image_options *image, + lxw_drawing_object *drawing_object) +{ + lxw_col_t col_start; /* Column containing upper left corner. */ + int32_t x1; /* Distance to left side of object. */ + + lxw_row_t row_start; /* Row containing top left corner. */ + int32_t y1; /* Distance to top of object. */ + + lxw_col_t col_end; /* Column containing lower right corner. */ + double x2; /* Distance to right side of object. */ + + lxw_row_t row_end; /* Row containing bottom right corner. */ + double y2; /* Distance to bottom of object. */ + + double width; /* Width of object frame. */ + double height; /* Height of object frame. */ + + uint32_t x_abs = 0; /* Abs. distance to left side of object. */ + uint32_t y_abs = 0; /* Abs. distance to top side of object. */ + + uint32_t i; + + col_start = image->col; + row_start = image->row; + x1 = image->x_offset; + y1 = image->y_offset; + width = image->width; + height = image->height; + + /* Adjust start column for negative offsets. */ + while (x1 < 0 && col_start > 0) { + x1 += _worksheet_size_col(self, col_start - 1); + col_start--; + } + + /* Adjust start row for negative offsets. */ + while (y1 < 0 && row_start > 0) { + y1 += _worksheet_size_row(self, row_start - 1); + row_start--; + } + + /* Ensure that the image isn't shifted off the page at top left. */ + if (x1 < 0) + x1 = 0; + + if (y1 < 0) + y1 = 0; + + /* Calculate the absolute x offset of the top-left vertex. */ + if (self->col_size_changed) { + for (i = 0; i < col_start; i++) + x_abs += _worksheet_size_col(self, i); + } + else { + /* Optimization for when the column widths haven't changed. */ + x_abs += self->default_col_pixels * col_start; + } + + x_abs += x1; + + /* Calculate the absolute y offset of the top-left vertex. */ + /* Store the column change to allow optimizations. */ + if (self->row_size_changed) { + for (i = 0; i < row_start; i++) + y_abs += _worksheet_size_row(self, i); + } + else { + /* Optimization for when the row heights haven"t changed. */ + y_abs += self->default_row_pixels * row_start; + } + + y_abs += y1; + + /* Adjust start col for offsets that are greater than the col width. */ + while (x1 >= _worksheet_size_col(self, col_start)) { + x1 -= _worksheet_size_col(self, col_start); + col_start++; + } + + /* Adjust start row for offsets that are greater than the row height. */ + while (y1 >= _worksheet_size_row(self, row_start)) { + y1 -= _worksheet_size_row(self, row_start); + row_start++; + } + + /* Initialize end cell to the same as the start cell. */ + col_end = col_start; + row_end = row_start; + + width = width + x1; + height = height + y1; + + /* Subtract the underlying cell widths to find the end cell. */ + while (width >= _worksheet_size_col(self, col_end)) { + width -= _worksheet_size_col(self, col_end); + col_end++; + } + + /* Subtract the underlying cell heights to find the end cell. */ + while (height >= _worksheet_size_row(self, row_end)) { + height -= _worksheet_size_row(self, row_end); + row_end++; + } + + /* The end vertices are whatever is left from the width and height. */ + x2 = width; + y2 = height; + + /* Add the dimensions to the drawing object. */ + drawing_object->from.col = col_start; + drawing_object->from.row = row_start; + drawing_object->from.col_offset = x1; + drawing_object->from.row_offset = y1; + drawing_object->to.col = col_end; + drawing_object->to.row = row_end; + drawing_object->to.col_offset = x2; + drawing_object->to.row_offset = y2; + drawing_object->col_absolute = x_abs; + drawing_object->row_absolute = y_abs; + +} + +/* + * Calculate the vertices that define the position of a graphical object + * within the worksheet in EMUs. The vertices are expressed as English + * Metric Units (EMUs). There are 12,700 EMUs per point. + * Therefore, 12,700 * 3 /4 = 9,525 EMUs per pixel. + */ +STATIC void +_worksheet_position_object_emus(lxw_worksheet *self, + lxw_image_options *image, + lxw_drawing_object *drawing_object) +{ + + _worksheet_position_object_pixels(self, image, drawing_object); + + /* Convert the pixel values to EMUs. See above. */ + drawing_object->from.col_offset *= 9525; + drawing_object->from.row_offset *= 9525; + drawing_object->to.col_offset *= 9525; + drawing_object->to.row_offset *= 9525; + drawing_object->to.col_offset += 0.5; + drawing_object->to.row_offset += 0.5; + drawing_object->col_absolute *= 9525; + drawing_object->row_absolute *= 9525; +} + +/* + * Set up image/drawings. + */ +void +lxw_worksheet_prepare_image(lxw_worksheet *self, + uint16_t image_ref_id, uint16_t drawing_id, + lxw_image_options *image_data) +{ + lxw_drawing_object *drawing_object; + lxw_rel_tuple *relationship; + double width; + double height; + char filename[LXW_FILENAME_LENGTH]; + + if (!self->drawing) { + self->drawing = lxw_drawing_new(); + self->drawing->embedded = LXW_TRUE; + RETURN_VOID_ON_MEM_ERROR(self->drawing); + + relationship = calloc(1, sizeof(lxw_rel_tuple)); + GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); + + relationship->type = lxw_strdup("/drawing"); + GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); + + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "../drawings/drawing%d.xml", drawing_id); + + relationship->target = lxw_strdup(filename); + GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); + + STAILQ_INSERT_TAIL(self->external_drawing_links, relationship, + list_pointers); + } + + drawing_object = calloc(1, sizeof(lxw_drawing_object)); + RETURN_VOID_ON_MEM_ERROR(drawing_object); + + drawing_object->anchor_type = LXW_ANCHOR_TYPE_IMAGE; + drawing_object->edit_as = LXW_ANCHOR_EDIT_AS_ONE_CELL; + drawing_object->description = lxw_strdup(image_data->short_name); + + /* Scale to user scale. */ + width = image_data->width * image_data->x_scale; + height = image_data->height * image_data->y_scale; + + /* Scale by non 96dpi resolutions. */ + width *= 96.0 / image_data->x_dpi; + height *= 96.0 / image_data->y_dpi; + + /* Convert to the nearest pixel. */ + image_data->width = width; + image_data->height = height; + + _worksheet_position_object_emus(self, image_data, drawing_object); + + /* Convert from pixels to emus. */ + drawing_object->width = (uint32_t) (0.5 + width * 9525); + drawing_object->height = (uint32_t) (0.5 + height * 9525); + + lxw_add_drawing_object(self->drawing, drawing_object); + + relationship = calloc(1, sizeof(lxw_rel_tuple)); + GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); + + relationship->type = lxw_strdup("/image"); + GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); + + lxw_snprintf(filename, 32, "../media/image%d.%s", image_ref_id, + image_data->extension); + + relationship->target = lxw_strdup(filename); + GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); + + STAILQ_INSERT_TAIL(self->drawing_links, relationship, list_pointers); + + return; + +mem_error: + if (relationship) { + free(relationship->type); + free(relationship->target); + free(relationship->target_mode); + free(relationship); + } +} + +/* + * Set up chart/drawings. + */ +void +lxw_worksheet_prepare_chart(lxw_worksheet *self, + uint16_t chart_ref_id, uint16_t drawing_id, + lxw_image_options *image_data) +{ + lxw_drawing_object *drawing_object; + lxw_rel_tuple *relationship; + double width; + double height; + char filename[LXW_FILENAME_LENGTH]; + + if (!self->drawing) { + self->drawing = lxw_drawing_new(); + self->drawing->embedded = LXW_TRUE; + RETURN_VOID_ON_MEM_ERROR(self->drawing); + + relationship = calloc(1, sizeof(lxw_rel_tuple)); + GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); + + relationship->type = lxw_strdup("/drawing"); + GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); + + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "../drawings/drawing%d.xml", drawing_id); + + relationship->target = lxw_strdup(filename); + GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); + + STAILQ_INSERT_TAIL(self->external_drawing_links, relationship, + list_pointers); + } + + drawing_object = calloc(1, sizeof(lxw_drawing_object)); + RETURN_VOID_ON_MEM_ERROR(drawing_object); + + drawing_object->anchor_type = LXW_ANCHOR_TYPE_CHART; + drawing_object->edit_as = LXW_ANCHOR_EDIT_AS_ONE_CELL; + drawing_object->description = lxw_strdup("TODO_DESC"); + + /* Scale to user scale. */ + width = image_data->width * image_data->x_scale; + height = image_data->height * image_data->y_scale; + + /* Convert to the nearest pixel. */ + image_data->width = width; + image_data->height = height; + + _worksheet_position_object_emus(self, image_data, drawing_object); + + /* Convert from pixels to emus. */ + drawing_object->width = (uint32_t) (0.5 + width * 9525); + drawing_object->height = (uint32_t) (0.5 + height * 9525); + + lxw_add_drawing_object(self->drawing, drawing_object); + + relationship = calloc(1, sizeof(lxw_rel_tuple)); + GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); + + relationship->type = lxw_strdup("/chart"); + GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); + + lxw_snprintf(filename, 32, "../charts/chart%d.xml", chart_ref_id); + + relationship->target = lxw_strdup(filename); + GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); + + STAILQ_INSERT_TAIL(self->drawing_links, relationship, list_pointers); + + return; + +mem_error: + if (relationship) { + free(relationship->type); + free(relationship->target); + free(relationship->target_mode); + free(relationship); + } +} + +/* + * Extract width and height information from a PNG file. + */ +STATIC lxw_error +_process_png(lxw_image_options *image_options) +{ + uint32_t length; + uint32_t offset; + char type[4]; + uint32_t width = 0; + uint32_t height = 0; + double x_dpi = 96; + double y_dpi = 96; + int fseek_err; + + FILE *stream = image_options->stream; + + /* Skip another 4 bytes to the end of the PNG header. */ + fseek_err = fseek(stream, 4, SEEK_CUR); + if (fseek_err) + goto file_error; + + while (!feof(stream)) { + + /* Read the PNG length and type fields for the sub-section. */ + if (fread(&length, sizeof(length), 1, stream) < 1) + break; + + if (fread(&type, 1, 4, stream) < 4) + break; + + /* Convert the length to network order. */ + length = LXW_UINT32_NETWORK(length); + + /* The offset for next fseek() is the field length + type length. */ + offset = length + 4; + + if (memcmp(type, "IHDR", 4) == 0) { + if (fread(&width, sizeof(width), 1, stream) < 1) + break; + + if (fread(&height, sizeof(height), 1, stream) < 1) + break; + + width = LXW_UINT32_NETWORK(width); + height = LXW_UINT32_NETWORK(height); + + /* Reduce the offset by the length of previous freads(). */ + offset -= 8; + } + + if (memcmp(type, "pHYs", 4) == 0) { + uint32_t x_ppu = 0; + uint32_t y_ppu = 0; + uint8_t units = 1; + + if (fread(&x_ppu, sizeof(x_ppu), 1, stream) < 1) + break; + + if (fread(&y_ppu, sizeof(y_ppu), 1, stream) < 1) + break; + + if (fread(&units, sizeof(units), 1, stream) < 1) + break; + + if (units == 1) { + x_ppu = LXW_UINT32_NETWORK(x_ppu); + y_ppu = LXW_UINT32_NETWORK(y_ppu); + + x_dpi = (double) x_ppu *0.0254; + y_dpi = (double) y_ppu *0.0254; + } + + /* Reduce the offset by the length of previous freads(). */ + offset -= 9; + } + + if (memcmp(type, "IEND", 4) == 0) + break; + + if (!feof(stream)) { + fseek_err = fseek(stream, offset, SEEK_CUR); + if (fseek_err) + goto file_error; + } + } + + /* Ensure that we read some valid data from the file. */ + if (width == 0) + goto file_error; + + /* Set the image metadata. */ + image_options->image_type = LXW_IMAGE_PNG; + image_options->width = width; + image_options->height = height; + image_options->x_dpi = x_dpi ? x_dpi : 96; + image_options->y_dpi = y_dpi ? x_dpi : 96; + image_options->extension = lxw_strdup("png"); + + return LXW_NO_ERROR; + +file_error: + LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " + "no size data found in file: %s.", + image_options->filename); + + return LXW_ERROR_IMAGE_DIMENSIONS; +} + +/* + * Extract width and height information from a JPEG file. + */ +STATIC lxw_error +_process_jpeg(lxw_image_options *image_options) +{ + uint16_t length; + uint16_t marker; + uint32_t offset; + uint16_t width = 0; + uint16_t height = 0; + double x_dpi = 96; + double y_dpi = 96; + int fseek_err; + + FILE *stream = image_options->stream; + + /* Read back 2 bytes to the end of the initial 0xFFD8 marker. */ + fseek_err = fseek(stream, -2, SEEK_CUR); + if (fseek_err) + goto file_error; + + /* Search through the image data to read the height and width in the */ + /* 0xFFC0/C2 element. Also read the DPI in the 0xFFE0 element. */ + while (!feof(stream)) { + + /* Read the JPEG marker and length fields for the sub-section. */ + if (fread(&marker, sizeof(marker), 1, stream) < 1) + break; + + if (fread(&length, sizeof(length), 1, stream) < 1) + break; + + /* Convert the marker and length to network order. */ + marker = LXW_UINT16_NETWORK(marker); + length = LXW_UINT16_NETWORK(length); + + /* The offset for next fseek() is the field length + type length. */ + offset = length - 2; + + if (marker == 0xFFC0 || marker == 0xFFC2) { + /* Skip 1 byte to height and width. */ + fseek_err = fseek(stream, 1, SEEK_CUR); + if (fseek_err) + goto file_error; + + if (fread(&height, sizeof(height), 1, stream) < 1) + break; + + if (fread(&width, sizeof(width), 1, stream) < 1) + break; + + height = LXW_UINT16_NETWORK(height); + width = LXW_UINT16_NETWORK(width); + + offset -= 9; + } + + if (marker == 0xFFE0) { + uint16_t x_density = 0; + uint16_t y_density = 0; + uint8_t units = 1; + + fseek_err = fseek(stream, 7, SEEK_CUR); + if (fseek_err) + goto file_error; + + if (fread(&units, sizeof(units), 1, stream) < 1) + break; + + if (fread(&x_density, sizeof(x_density), 1, stream) < 1) + break; + + if (fread(&y_density, sizeof(y_density), 1, stream) < 1) + break; + + x_density = LXW_UINT16_NETWORK(x_density); + y_density = LXW_UINT16_NETWORK(y_density); + + if (units == 1) { + x_dpi = x_density; + y_dpi = y_density; + } + + if (units == 2) { + x_dpi = x_density * 2.54; + y_dpi = y_density * 2.54; + } + + offset -= 12; + } + + if (marker == 0xFFDA) + break; + + if (!feof(stream)) { + fseek_err = fseek(stream, offset, SEEK_CUR); + if (fseek_err) + goto file_error; + } + } + + /* Ensure that we read some valid data from the file. */ + if (width == 0) + goto file_error; + + /* Set the image metadata. */ + image_options->image_type = LXW_IMAGE_JPEG; + image_options->width = width; + image_options->height = height; + image_options->x_dpi = x_dpi ? x_dpi : 96; + image_options->y_dpi = y_dpi ? x_dpi : 96; + image_options->extension = lxw_strdup("jpeg"); + + return LXW_NO_ERROR; + +file_error: + LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " + "no size data found in file: %s.", + image_options->filename); + + return LXW_ERROR_IMAGE_DIMENSIONS; +} + +/* + * Extract width and height information from a BMP file. + */ +STATIC lxw_error +_process_bmp(lxw_image_options *image_options) +{ + uint32_t width = 0; + uint32_t height = 0; + double x_dpi = 96; + double y_dpi = 96; + int fseek_err; + + FILE *stream = image_options->stream; + + /* Skip another 14 bytes to the start of the BMP height/width. */ + fseek_err = fseek(stream, 14, SEEK_CUR); + if (fseek_err) + goto file_error; + + if (fread(&width, sizeof(width), 1, stream) < 1) + width = 0; + + if (fread(&height, sizeof(height), 1, stream) < 1) + height = 0; + + /* Ensure that we read some valid data from the file. */ + if (width == 0) + goto file_error; + + /* Set the image metadata. */ + image_options->image_type = LXW_IMAGE_BMP; + image_options->width = width; + image_options->height = height; + image_options->x_dpi = x_dpi; + image_options->y_dpi = y_dpi; + image_options->extension = lxw_strdup("bmp"); + + return LXW_NO_ERROR; + +file_error: + LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " + "no size data found in file: %s.", + image_options->filename); + + return LXW_ERROR_IMAGE_DIMENSIONS; +} + +/* + * Extract information from the image file such as dimension, type, filename, + * and extension. + */ +STATIC lxw_error +_get_image_properties(lxw_image_options *image_options) +{ + unsigned char signature[4]; + + /* Read 4 bytes to look for the file header/signature. */ + if (fread(signature, 1, 4, image_options->stream) < 4) { + LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " + "couldn't read file type for file: %s.", + image_options->filename); + return LXW_ERROR_IMAGE_DIMENSIONS; + } + + if (memcmp(&signature[1], "PNG", 3) == 0) { + if (_process_png(image_options) != LXW_NO_ERROR) + return LXW_ERROR_IMAGE_DIMENSIONS; + } + else if (signature[0] == 0xFF && signature[1] == 0xD8) { + if (_process_jpeg(image_options) != LXW_NO_ERROR) + return LXW_ERROR_IMAGE_DIMENSIONS; + } + else if (memcmp(signature, "BM", 2) == 0) { + if (_process_bmp(image_options) != LXW_NO_ERROR) + return LXW_ERROR_IMAGE_DIMENSIONS; + } + else { + LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " + "unsupported image format for file: %s.", + image_options->filename); + return LXW_ERROR_IMAGE_DIMENSIONS; + } + + return LXW_NO_ERROR; +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Write out a number worksheet cell. Doesn't use the xml functions as an + * optimization in the inner cell writing loop. + */ +STATIC void +_write_number_cell(lxw_worksheet *self, char *range, + int32_t style_index, lxw_cell *cell) +{ + if (style_index) + fprintf(self->file, + "%.16g", + range, style_index, cell->u.number); + else + fprintf(self->file, + "%.16g", range, cell->u.number); +} + +/* + * Write out a string worksheet cell. Doesn't use the xml functions as an + * optimization in the inner cell writing loop. + */ +STATIC void +_write_string_cell(lxw_worksheet *self, char *range, + int32_t style_index, lxw_cell *cell) +{ + if (style_index) + fprintf(self->file, + "%d", + range, style_index, cell->u.string_id); + else + fprintf(self->file, + "%d", + range, cell->u.string_id); +} + +/* + * Write out an inline string. Doesn't use the xml functions as an + * optimization in the inner cell writing loop. + */ +STATIC void +_write_inline_string_cell(lxw_worksheet *self, char *range, + int32_t style_index, lxw_cell *cell) +{ + char *string = lxw_escape_data(cell->u.string); + + /* Add attribute to preserve leading or trailing whitespace. */ + if (isspace((unsigned char) string[0]) + || isspace((unsigned char) string[strlen(string) - 1])) { + + if (style_index) + fprintf(self->file, + "" + "%s", + range, style_index, string); + else + fprintf(self->file, + "" + "%s", + range, string); + } + else { + if (style_index) + fprintf(self->file, + "" + "%s", range, style_index, string); + else + fprintf(self->file, + "" + "%s", range, string); + } + + free(string); +} + +/* + * Write out a formula worksheet cell with a numeric result. + */ +STATIC void +_write_formula_num_cell(lxw_worksheet *self, lxw_cell *cell) +{ + char data[LXW_ATTR_32]; + + lxw_snprintf(data, LXW_ATTR_32, "%.16g", cell->formula_result); + + lxw_xml_data_element(self->file, "f", cell->u.string, NULL); + lxw_xml_data_element(self->file, "v", data, NULL); +} + +/* + * Write out an array formula worksheet cell with a numeric result. + */ +STATIC void +_write_array_formula_num_cell(lxw_worksheet *self, lxw_cell *cell) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char data[LXW_ATTR_32]; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("t", "array"); + LXW_PUSH_ATTRIBUTES_STR("ref", cell->user_data1); + + lxw_snprintf(data, LXW_ATTR_32, "%.16g", cell->formula_result); + + lxw_xml_data_element(self->file, "f", cell->u.string, &attributes); + lxw_xml_data_element(self->file, "v", data, NULL); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write out a boolean worksheet cell. + */ +STATIC void +_write_boolean_cell(lxw_worksheet *self, lxw_cell *cell) +{ + char data[LXW_ATTR_32]; + + if (cell->u.number) + data[0] = '1'; + else + data[0] = '0'; + + data[1] = '\0'; + + lxw_xml_data_element(self->file, "v", data, NULL); +} + +/* + * Calculate the "spans" attribute of the tag. This is an XLSX + * optimization and isn't strictly required. However, it makes comparing + * files easier. + * + * The span is the same for each block of 16 rows. + */ +STATIC void +_calculate_spans(struct lxw_row *row, char *span, int32_t *block_num) +{ + lxw_col_t span_col_min = RB_MIN(lxw_table_cells, row->cells)->col_num; + lxw_col_t span_col_max = RB_MAX(lxw_table_cells, row->cells)->col_num; + lxw_col_t col_min; + lxw_col_t col_max; + *block_num = row->row_num / 16; + + row = RB_NEXT(lxw_table_rows, root, row); + + while (row && (int32_t) (row->row_num / 16) == *block_num) { + + if (!RB_EMPTY(row->cells)) { + col_min = RB_MIN(lxw_table_cells, row->cells)->col_num; + col_max = RB_MAX(lxw_table_cells, row->cells)->col_num; + + if (col_min < span_col_min) + span_col_min = col_min; + + if (col_max > span_col_max) + span_col_max = col_max; + } + + row = RB_NEXT(lxw_table_rows, root, row); + } + + lxw_snprintf(span, LXW_MAX_CELL_RANGE_LENGTH, + "%d:%d", span_col_min + 1, span_col_max + 1); +} + +/* + * Write out a generic worksheet cell. + */ +STATIC void +_write_cell(lxw_worksheet *self, lxw_cell *cell, lxw_format *row_format) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char range[LXW_MAX_CELL_NAME_LENGTH] = { 0 }; + lxw_row_t row_num = cell->row_num; + lxw_col_t col_num = cell->col_num; + int32_t style_index = 0; + + lxw_rowcol_to_cell(range, row_num, col_num); + + if (cell->format) { + style_index = lxw_format_get_xf_index(cell->format); + } + else if (row_format) { + style_index = lxw_format_get_xf_index(row_format); + } + else if (col_num < self->col_formats_max && self->col_formats[col_num]) { + style_index = lxw_format_get_xf_index(self->col_formats[col_num]); + } + + /* Unrolled optimization for most commonly written cell types. */ + if (cell->type == NUMBER_CELL) { + _write_number_cell(self, range, style_index, cell); + return; + } + + if (cell->type == STRING_CELL) { + _write_string_cell(self, range, style_index, cell); + return; + } + + if (cell->type == INLINE_STRING_CELL) { + _write_inline_string_cell(self, range, style_index, cell); + return; + } + + /* For other cell types use the general functions. */ + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("r", range); + + if (style_index) + LXW_PUSH_ATTRIBUTES_INT("s", style_index); + + if (cell->type == FORMULA_CELL) { + lxw_xml_start_tag(self->file, "c", &attributes); + _write_formula_num_cell(self, cell); + lxw_xml_end_tag(self->file, "c"); + } + else if (cell->type == BLANK_CELL) { + lxw_xml_empty_tag(self->file, "c", &attributes); + } + else if (cell->type == BOOLEAN_CELL) { + LXW_PUSH_ATTRIBUTES_STR("t", "b"); + lxw_xml_start_tag(self->file, "c", &attributes); + _write_boolean_cell(self, cell); + lxw_xml_end_tag(self->file, "c"); + } + else if (cell->type == ARRAY_FORMULA_CELL) { + lxw_xml_start_tag(self->file, "c", &attributes); + _write_array_formula_num_cell(self, cell); + lxw_xml_end_tag(self->file, "c"); + } + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write out the worksheet data as a series of rows and cells. + */ +STATIC void +_worksheet_write_rows(lxw_worksheet *self) +{ + lxw_row *row; + lxw_cell *cell; + int32_t block_num = -1; + char spans[LXW_MAX_CELL_RANGE_LENGTH] = { 0 }; + + RB_FOREACH(row, lxw_table_rows, self->table) { + + if (RB_EMPTY(row->cells)) { + /* Row contains no cells but has height, format or other data. */ + + /* Write a default span for default rows. */ + if (self->default_row_set) + _write_row(self, row, "1:1"); + else + _write_row(self, row, NULL); + } + else { + /* Row and cell data. */ + if ((int32_t) row->row_num / 16 > block_num) + _calculate_spans(row, spans, &block_num); + + _write_row(self, row, spans); + + RB_FOREACH(cell, lxw_table_cells, row->cells) { + _write_cell(self, cell, row->format); + } + lxw_xml_end_tag(self->file, "row"); + } + } +} + +/* + * Write out the worksheet data as a single row with cells. This method is + * used when memory optimization is on. A single row is written and the data + * array is reset. That way only one row of data is kept in memory at any one + * time. We don't write span data in the optimized case since it is optional. + */ +void +lxw_worksheet_write_single_row(lxw_worksheet *self) +{ + lxw_row *row = self->optimize_row; + lxw_col_t col; + + /* skip row if it doesn't contain row formatting, cell data or a comment. */ + if (!(row->row_changed || row->data_changed)) + return; + + /* Write the cells if the row contains data. */ + if (!row->data_changed) { + /* Row data only. No cells. */ + _write_row(self, row, NULL); + } + else { + /* Row and cell data. */ + _write_row(self, row, NULL); + + for (col = self->dim_colmin; col <= self->dim_colmax; col++) { + if (self->array[col]) { + _write_cell(self, self->array[col], row->format); + _free_cell(self->array[col]); + self->array[col] = NULL; + } + } + + lxw_xml_end_tag(self->file, "row"); + } + + /* Reset the row. */ + row->height = LXW_DEF_ROW_HEIGHT; + row->format = NULL; + row->hidden = LXW_FALSE; + row->level = 0; + row->collapsed = LXW_FALSE; + row->data_changed = LXW_FALSE; + row->row_changed = LXW_FALSE; +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_col_info(lxw_worksheet *self, lxw_col_options *options) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + double width = options->width; + uint8_t has_custom_width = LXW_TRUE; + int32_t xf_index = 0; + double max_digit_width = 7.0; /* For Calabri 11. */ + double padding = 5.0; + + /* Get the format index. */ + if (options->format) { + xf_index = lxw_format_get_xf_index(options->format); + } + + /* Check if width is the Excel default. */ + if (width == LXW_DEF_COL_WIDTH) { + + /* The default col width changes to 0 for hidden columns. */ + if (options->hidden) + width = 0; + else + has_custom_width = LXW_FALSE; + + } + + /* Convert column width from user units to character width. */ + if (width > 0) { + if (width < 1) { + width = (uint16_t) (((uint16_t) + (width * (max_digit_width + padding) + 0.5)) + / max_digit_width * 256.0) / 256.0; + } + else { + width = (uint16_t) (((uint16_t) + (width * max_digit_width + 0.5) + padding) + / max_digit_width * 256.0) / 256.0; + } + } + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("min", 1 + options->firstcol); + LXW_PUSH_ATTRIBUTES_INT("max", 1 + options->lastcol); + LXW_PUSH_ATTRIBUTES_DBL("width", width); + + if (xf_index) + LXW_PUSH_ATTRIBUTES_INT("style", xf_index); + + if (options->hidden) + LXW_PUSH_ATTRIBUTES_STR("hidden", "1"); + + if (has_custom_width) + LXW_PUSH_ATTRIBUTES_STR("customWidth", "1"); + + if (options->level) + LXW_PUSH_ATTRIBUTES_INT("outlineLevel", options->level); + + if (options->collapsed) + LXW_PUSH_ATTRIBUTES_STR("collapsed", "1"); + + lxw_xml_empty_tag(self->file, "col", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element and sub elements. + */ +STATIC void +_worksheet_write_cols(lxw_worksheet *self) +{ + lxw_col_t col; + + if (!self->col_size_changed) + return; + + lxw_xml_start_tag(self->file, "cols", NULL); + + for (col = 0; col < self->col_options_max; col++) { + if (self->col_options[col]) + _worksheet_write_col_info(self, self->col_options[col]); + } + + lxw_xml_end_tag(self->file, "cols"); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_merge_cell(lxw_worksheet *self, + lxw_merged_range *merged_range) +{ + + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char ref[LXW_MAX_CELL_RANGE_LENGTH]; + + LXW_INIT_ATTRIBUTES(); + + /* Convert the merge dimensions to a cell range. */ + lxw_rowcol_to_range(ref, merged_range->first_row, merged_range->first_col, + merged_range->last_row, merged_range->last_col); + + LXW_PUSH_ATTRIBUTES_STR("ref", ref); + + lxw_xml_empty_tag(self->file, "mergeCell", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_merge_cells(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + lxw_merged_range *merged_range; + + if (self->merged_range_count) { + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_INT("count", self->merged_range_count); + + lxw_xml_start_tag(self->file, "mergeCells", &attributes); + + STAILQ_FOREACH(merged_range, self->merged_ranges, list_pointers) { + _worksheet_write_merge_cell(self, merged_range); + } + lxw_xml_end_tag(self->file, "mergeCells"); + + LXW_FREE_ATTRIBUTES(); + } +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_odd_header(lxw_worksheet *self) +{ + lxw_xml_data_element(self->file, "oddHeader", self->header, NULL); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_odd_footer(lxw_worksheet *self) +{ + lxw_xml_data_element(self->file, "oddFooter", self->footer, NULL); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_header_footer(lxw_worksheet *self) +{ + if (!self->header_footer_changed) + return; + + lxw_xml_start_tag(self->file, "headerFooter", NULL); + + if (self->header[0] != '\0') + _worksheet_write_odd_header(self); + + if (self->footer[0] != '\0') + _worksheet_write_odd_footer(self); + + lxw_xml_end_tag(self->file, "headerFooter"); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_page_set_up_pr(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!self->fit_page) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("fitToPage", "1"); + + lxw_xml_empty_tag(self->file, "pageSetUpPr", &attributes); + + LXW_FREE_ATTRIBUTES(); + +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_tab_color(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char rgb_str[LXW_ATTR_32]; + + if (self->tab_color == LXW_COLOR_UNSET) + return; + + lxw_snprintf(rgb_str, LXW_ATTR_32, "FF%06X", + self->tab_color & LXW_COLOR_MASK); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("rgb", rgb_str); + + lxw_xml_empty_tag(self->file, "tabColor", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element for Sheet level properties. + */ +STATIC void +_worksheet_write_sheet_pr(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (!self->fit_page + && !self->filter_on + && self->tab_color == LXW_COLOR_UNSET + && !self->outline_changed && !self->vba_codename) { + return; + } + + LXW_INIT_ATTRIBUTES(); + + if (self->vba_codename) + LXW_PUSH_ATTRIBUTES_INT("codeName", self->vba_codename); + + if (self->filter_on) + LXW_PUSH_ATTRIBUTES_STR("filterMode", "1"); + + if (self->fit_page || self->tab_color != LXW_COLOR_UNSET + || self->outline_changed) { + lxw_xml_start_tag(self->file, "sheetPr", &attributes); + _worksheet_write_tab_color(self); + /* _worksheet_write_outline_pr(self); */ + _worksheet_write_page_set_up_pr(self); + lxw_xml_end_tag(self->file, "sheetPr"); + } + else { + lxw_xml_empty_tag(self->file, "sheetPr", &attributes); + } + + LXW_FREE_ATTRIBUTES(); + +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_brk(lxw_worksheet *self, uint32_t id, uint32_t max) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("id", id); + LXW_PUSH_ATTRIBUTES_INT("max", max); + LXW_PUSH_ATTRIBUTES_STR("man", "1"); + + lxw_xml_empty_tag(self->file, "brk", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_row_breaks(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + uint16_t count = self->hbreaks_count; + uint16_t i; + + if (!count) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("count", count); + LXW_PUSH_ATTRIBUTES_INT("manualBreakCount", count); + + lxw_xml_start_tag(self->file, "rowBreaks", &attributes); + + for (i = 0; i < count; i++) + _worksheet_write_brk(self, self->hbreaks[i], LXW_COL_MAX - 1); + + lxw_xml_end_tag(self->file, "rowBreaks"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_col_breaks(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + uint16_t count = self->vbreaks_count; + uint16_t i; + + if (!count) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("count", count); + LXW_PUSH_ATTRIBUTES_INT("manualBreakCount", count); + + lxw_xml_start_tag(self->file, "colBreaks", &attributes); + + for (i = 0; i < count; i++) + _worksheet_write_brk(self, self->vbreaks[i], LXW_ROW_MAX - 1); + + lxw_xml_end_tag(self->file, "colBreaks"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_auto_filter(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char range[LXW_MAX_CELL_RANGE_LENGTH]; + + if (!self->autofilter.in_use) + return; + + lxw_rowcol_to_range(range, + self->autofilter.first_row, + self->autofilter.first_col, + self->autofilter.last_row, self->autofilter.last_col); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("ref", range); + + lxw_xml_empty_tag(self->file, "autoFilter", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element for external links. + */ +STATIC void +_worksheet_write_hyperlink_external(lxw_worksheet *self, lxw_row_t row_num, + lxw_col_t col_num, const char *location, + const char *tooltip, uint16_t id) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char ref[LXW_MAX_CELL_NAME_LENGTH]; + char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; + + lxw_rowcol_to_cell(ref, row_num, col_num); + + lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", id); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("ref", ref); + LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); + + if (location) + LXW_PUSH_ATTRIBUTES_STR("location", location); + + if (tooltip) + LXW_PUSH_ATTRIBUTES_STR("tooltip", tooltip); + + lxw_xml_empty_tag(self->file, "hyperlink", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element for internal links. + */ +STATIC void +_worksheet_write_hyperlink_internal(lxw_worksheet *self, lxw_row_t row_num, + lxw_col_t col_num, const char *location, + const char *display, const char *tooltip) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char ref[LXW_MAX_CELL_NAME_LENGTH]; + + lxw_rowcol_to_cell(ref, row_num, col_num); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("ref", ref); + + if (location) + LXW_PUSH_ATTRIBUTES_STR("location", location); + + if (tooltip) + LXW_PUSH_ATTRIBUTES_STR("tooltip", tooltip); + + if (display) + LXW_PUSH_ATTRIBUTES_STR("display", display); + + lxw_xml_empty_tag(self->file, "hyperlink", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Process any stored hyperlinks in row/col order and write the + * element. The attributes are different for internal and external links. + */ +STATIC void +_worksheet_write_hyperlinks(lxw_worksheet *self) +{ + + lxw_row *row; + lxw_cell *link; + lxw_rel_tuple *relationship; + + if (RB_EMPTY(self->hyperlinks)) + return; + + /* Write the hyperlink elements. */ + lxw_xml_start_tag(self->file, "hyperlinks", NULL); + + RB_FOREACH(row, lxw_table_rows, self->hyperlinks) { + + RB_FOREACH(link, lxw_table_cells, row->cells) { + + if (link->type == HYPERLINK_URL + || link->type == HYPERLINK_EXTERNAL) { + + self->rel_count++; + + relationship = calloc(1, sizeof(lxw_rel_tuple)); + GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); + + relationship->type = lxw_strdup("/hyperlink"); + GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); + + relationship->target = lxw_strdup(link->u.string); + GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); + + relationship->target_mode = lxw_strdup("External"); + GOTO_LABEL_ON_MEM_ERROR(relationship->target_mode, mem_error); + + STAILQ_INSERT_TAIL(self->external_hyperlinks, relationship, + list_pointers); + + _worksheet_write_hyperlink_external(self, link->row_num, + link->col_num, + link->user_data1, + link->user_data2, + self->rel_count); + } + + if (link->type == HYPERLINK_INTERNAL) { + + _worksheet_write_hyperlink_internal(self, link->row_num, + link->col_num, + link->u.string, + link->user_data1, + link->user_data2); + } + + } + + } + + lxw_xml_end_tag(self->file, "hyperlinks"); + return; + +mem_error: + if (relationship) { + free(relationship->type); + free(relationship->target); + free(relationship->target_mode); + free(relationship); + } + lxw_xml_end_tag(self->file, "hyperlinks"); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_sheet_protection(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + struct lxw_protection *protect = &self->protection; + + if (!protect->is_configured) + return; + + LXW_INIT_ATTRIBUTES(); + + if (*protect->hash) + LXW_PUSH_ATTRIBUTES_STR("password", protect->hash); + + if (!protect->no_sheet) + LXW_PUSH_ATTRIBUTES_INT("sheet", 1); + + if (protect->content) + LXW_PUSH_ATTRIBUTES_INT("content", 1); + + if (!protect->objects) + LXW_PUSH_ATTRIBUTES_INT("objects", 1); + + if (!protect->scenarios) + LXW_PUSH_ATTRIBUTES_INT("scenarios", 1); + + if (protect->format_cells) + LXW_PUSH_ATTRIBUTES_INT("formatCells", 0); + + if (protect->format_columns) + LXW_PUSH_ATTRIBUTES_INT("formatColumns", 0); + + if (protect->format_rows) + LXW_PUSH_ATTRIBUTES_INT("formatRows", 0); + + if (protect->insert_columns) + LXW_PUSH_ATTRIBUTES_INT("insertColumns", 0); + + if (protect->insert_rows) + LXW_PUSH_ATTRIBUTES_INT("insertRows", 0); + + if (protect->insert_hyperlinks) + LXW_PUSH_ATTRIBUTES_INT("insertHyperlinks", 0); + + if (protect->delete_columns) + LXW_PUSH_ATTRIBUTES_INT("deleteColumns", 0); + + if (protect->delete_rows) + LXW_PUSH_ATTRIBUTES_INT("deleteRows", 0); + + if (protect->no_select_locked_cells) + LXW_PUSH_ATTRIBUTES_INT("selectLockedCells", 1); + + if (protect->sort) + LXW_PUSH_ATTRIBUTES_INT("sort", 0); + + if (protect->autofilter) + LXW_PUSH_ATTRIBUTES_INT("autoFilter", 0); + + if (protect->pivot_tables) + LXW_PUSH_ATTRIBUTES_INT("pivotTables", 0); + + if (protect->no_select_unlocked_cells) + LXW_PUSH_ATTRIBUTES_INT("selectUnlockedCells", 1); + + lxw_xml_empty_tag(self->file, "sheetProtection", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_write_drawing(lxw_worksheet *self, uint16_t id) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; + + lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", id); + + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); + + lxw_xml_empty_tag(self->file, "drawing", &attributes); + + LXW_FREE_ATTRIBUTES(); + +} + +/* + * Write the elements. + */ +STATIC void +_write_drawings(lxw_worksheet *self) +{ + if (!self->drawing) + return; + + self->rel_count++; + + _write_drawing(self, self->rel_count); +} + +/* + * Assemble and write the XML file. + */ +void +lxw_worksheet_assemble_xml_file(lxw_worksheet *self) +{ + /* Write the XML declaration. */ + _worksheet_xml_declaration(self); + + /* Write the worksheet element. */ + _worksheet_write_worksheet(self); + + /* Write the worksheet properties. */ + _worksheet_write_sheet_pr(self); + + /* Write the worksheet dimensions. */ + _worksheet_write_dimension(self); + + /* Write the sheet view properties. */ + _worksheet_write_sheet_views(self); + + /* Write the sheet format properties. */ + _worksheet_write_sheet_format_pr(self); + + /* Write the sheet column info. */ + _worksheet_write_cols(self); + + /* Write the sheetData element. */ + if (!self->optimize) + _worksheet_write_sheet_data(self); + else + _worksheet_write_optimized_sheet_data(self); + + /* Write the sheetProtection element. */ + _worksheet_write_sheet_protection(self); + + /* Write the autoFilter element. */ + _worksheet_write_auto_filter(self); + + /* Write the mergeCells element. */ + _worksheet_write_merge_cells(self); + + /* Write the hyperlink element. */ + _worksheet_write_hyperlinks(self); + + /* Write the printOptions element. */ + _worksheet_write_print_options(self); + + /* Write the worksheet page_margins. */ + _worksheet_write_page_margins(self); + + /* Write the worksheet page setup. */ + _worksheet_write_page_setup(self); + + /* Write the headerFooter element. */ + _worksheet_write_header_footer(self); + + /* Write the rowBreaks element. */ + _worksheet_write_row_breaks(self); + + /* Write the colBreaks element. */ + _worksheet_write_col_breaks(self); + + /* Write the drawing element. */ + _write_drawings(self); + + /* Close the worksheet tag. */ + lxw_xml_end_tag(self->file, "worksheet"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ + +/* + * Write a number to a cell in Excel. + */ +lxw_error +worksheet_write_number(lxw_worksheet *self, + lxw_row_t row_num, + lxw_col_t col_num, double value, lxw_format *format) +{ + lxw_cell *cell; + lxw_error err; + + err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); + if (err) + return err; + + cell = _new_number_cell(row_num, col_num, value, format); + + _insert_cell(self, row_num, col_num, cell); + + return LXW_NO_ERROR; +} + +/* + * Write a string to an Excel file. + */ +lxw_error +worksheet_write_string(lxw_worksheet *self, + lxw_row_t row_num, + lxw_col_t col_num, const char *string, + lxw_format *format) +{ + lxw_cell *cell; + int32_t string_id; + char *string_copy; + struct sst_element *sst_element; + lxw_error err; + + if (!string || !*string) { + /* Treat a NULL or empty string with formatting as a blank cell. */ + /* Null strings without formats should be ignored. */ + if (format) + return worksheet_write_blank(self, row_num, col_num, format); + else + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); + if (err) + return err; + + if (lxw_utf8_strlen(string) > LXW_STR_MAX) + return LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED; + + if (!self->optimize) { + /* Get the SST element and string id. */ + sst_element = lxw_get_sst_index(self->sst, string); + + if (!sst_element) + return LXW_ERROR_SHARED_STRING_INDEX_NOT_FOUND; + + string_id = sst_element->index; + cell = _new_string_cell(row_num, col_num, string_id, + sst_element->string, format); + } + else { + /* Look for and escape control chars in the string. */ + if (strpbrk(string, "\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C" + "\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16" + "\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F")) { + string_copy = lxw_escape_control_characters(string); + } + else { + string_copy = lxw_strdup(string); + } + cell = _new_inline_string_cell(row_num, col_num, string_copy, format); + } + + _insert_cell(self, row_num, col_num, cell); + + return LXW_NO_ERROR; +} + +/* + * Write a formula with a numerical result to a cell in Excel. + */ +lxw_error +worksheet_write_formula_num(lxw_worksheet *self, + lxw_row_t row_num, + lxw_col_t col_num, + const char *formula, + lxw_format *format, double result) +{ + lxw_cell *cell; + char *formula_copy; + lxw_error err; + + if (!formula) + return LXW_ERROR_NULL_PARAMETER_IGNORED; + + err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); + if (err) + return err; + + /* Strip leading "=" from formula. */ + if (formula[0] == '=') + formula_copy = lxw_strdup(formula + 1); + else + formula_copy = lxw_strdup(formula); + + cell = _new_formula_cell(row_num, col_num, formula_copy, format); + cell->formula_result = result; + + _insert_cell(self, row_num, col_num, cell); + + return LXW_NO_ERROR; +} + +/* + * Write a formula with a default result to a cell in Excel . + */ +lxw_error +worksheet_write_formula(lxw_worksheet *self, + lxw_row_t row_num, + lxw_col_t col_num, const char *formula, + lxw_format *format) +{ + return worksheet_write_formula_num(self, row_num, col_num, formula, + format, 0); +} + +/* + * Write a formula with a numerical result to a cell in Excel. + */ +lxw_error +worksheet_write_array_formula_num(lxw_worksheet *self, + lxw_row_t first_row, + lxw_col_t first_col, + lxw_row_t last_row, + lxw_col_t last_col, + const char *formula, + lxw_format *format, double result) +{ + lxw_cell *cell; + lxw_row_t tmp_row; + lxw_col_t tmp_col; + char *formula_copy; + char *range; + lxw_error err; + + /* Swap last row/col with first row/col as necessary */ + if (first_row > last_row) { + tmp_row = last_row; + last_row = first_row; + first_row = tmp_row; + } + if (first_col > last_col) { + tmp_col = last_col; + last_col = first_col; + first_col = tmp_col; + } + + if (!formula) + return LXW_ERROR_NULL_PARAMETER_IGNORED; + + /* Check that column number is valid and store the max value */ + err = _check_dimensions(self, last_row, last_col, LXW_FALSE, LXW_FALSE); + if (err) + return err; + + /* Define the array range. */ + range = calloc(1, LXW_MAX_CELL_RANGE_LENGTH); + RETURN_ON_MEM_ERROR(range, LXW_ERROR_MEMORY_MALLOC_FAILED); + + if (first_row == last_row && first_col == last_col) + lxw_rowcol_to_cell(range, first_row, last_col); + else + lxw_rowcol_to_range(range, first_row, first_col, last_row, last_col); + + /* Copy and trip leading "{=" from formula. */ + if (formula[0] == '{') + if (formula[1] == '=') + formula_copy = lxw_strdup(formula + 2); + else + formula_copy = lxw_strdup(formula + 1); + else + formula_copy = lxw_strdup(formula); + + /* Strip trailing "}" from formula. */ + if (formula_copy[strlen(formula_copy) - 1] == '}') + formula_copy[strlen(formula_copy) - 1] = '\0'; + + /* Create a new array formula cell object. */ + cell = _new_array_formula_cell(first_row, first_col, + formula_copy, range, format); + + cell->formula_result = result; + + _insert_cell(self, first_row, first_col, cell); + + /* Pad out the rest of the area with formatted zeroes. */ + if (!self->optimize) { + for (tmp_row = first_row; tmp_row <= last_row; tmp_row++) { + for (tmp_col = first_col; tmp_col <= last_col; tmp_col++) { + if (tmp_row == first_row && tmp_col == first_col) + continue; + + worksheet_write_number(self, tmp_row, tmp_col, 0, format); + } + } + } + + return LXW_NO_ERROR; +} + +/* + * Write an array formula with a default result to a cell in Excel . + */ +lxw_error +worksheet_write_array_formula(lxw_worksheet *self, + lxw_row_t first_row, + lxw_col_t first_col, + lxw_row_t last_row, + lxw_col_t last_col, + const char *formula, lxw_format *format) +{ + return worksheet_write_array_formula_num(self, first_row, first_col, + last_row, last_col, formula, + format, 0); +} + +/* + * Write a blank cell with a format to a cell in Excel. + */ +lxw_error +worksheet_write_blank(lxw_worksheet *self, + lxw_row_t row_num, lxw_col_t col_num, + lxw_format *format) +{ + lxw_cell *cell; + lxw_error err; + + /* Blank cells without formatting are ignored by Excel. */ + if (!format) + return LXW_NO_ERROR; + + err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); + if (err) + return err; + + cell = _new_blank_cell(row_num, col_num, format); + + _insert_cell(self, row_num, col_num, cell); + + return LXW_NO_ERROR; +} + +/* + * Write a boolean cell with a format to a cell in Excel. + */ +lxw_error +worksheet_write_boolean(lxw_worksheet *self, + lxw_row_t row_num, lxw_col_t col_num, + int value, lxw_format *format) +{ + lxw_cell *cell; + lxw_error err; + + err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); + + if (err) + return err; + + cell = _new_boolean_cell(row_num, col_num, value, format); + + _insert_cell(self, row_num, col_num, cell); + + return LXW_NO_ERROR; +} + +/* + * Write a date and or time to a cell in Excel. + */ +lxw_error +worksheet_write_datetime(lxw_worksheet *self, + lxw_row_t row_num, + lxw_col_t col_num, lxw_datetime *datetime, + lxw_format *format) +{ + lxw_cell *cell; + double excel_date; + lxw_error err; + + err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); + if (err) + return err; + + excel_date = lxw_datetime_to_excel_date(datetime, LXW_EPOCH_1900); + + cell = _new_number_cell(row_num, col_num, excel_date, format); + + _insert_cell(self, row_num, col_num, cell); + + return LXW_NO_ERROR; +} + +/* + * Write a hyperlink/url to an Excel file. + */ +lxw_error +worksheet_write_url_opt(lxw_worksheet *self, + lxw_row_t row_num, + lxw_col_t col_num, const char *url, + lxw_format *format, const char *string, + const char *tooltip) +{ + lxw_cell *link; + char *string_copy = NULL; + char *url_copy = NULL; + char *url_external = NULL; + char *url_string = NULL; + char *tooltip_copy = NULL; + char *found_string; + lxw_error err; + size_t string_size; + size_t i; + enum cell_types link_type = HYPERLINK_URL; + + if (!url || !*url) + return LXW_ERROR_NULL_PARAMETER_IGNORED; + + /* Check the Excel limit of URLS per worksheet. */ + if (self->hlink_count > LXW_MAX_NUMBER_URLS) + return LXW_ERROR_WORKSHEET_MAX_NUMBER_URLS_EXCEEDED; + + err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); + if (err) + return err; + + /* Set the URI scheme from internal links. */ + found_string = strstr(url, "internal:"); + if (found_string) + link_type = HYPERLINK_INTERNAL; + + /* Set the URI scheme from external links. */ + found_string = strstr(url, "external:"); + if (found_string) + link_type = HYPERLINK_EXTERNAL; + + if (string) { + string_copy = lxw_strdup(string); + GOTO_LABEL_ON_MEM_ERROR(string_copy, mem_error); + } + else { + if (link_type == HYPERLINK_URL) { + /* Strip the mailto header. */ + found_string = strstr(url, "mailto:"); + if (found_string) + string_copy = lxw_strdup(url + sizeof("mailto")); + else + string_copy = lxw_strdup(url); + } + else { + string_copy = lxw_strdup(url + sizeof("__ternal")); + } + GOTO_LABEL_ON_MEM_ERROR(string_copy, mem_error); + } + + if (url) { + if (link_type == HYPERLINK_URL) + url_copy = lxw_strdup(url); + else + url_copy = lxw_strdup(url + sizeof("__ternal")); + + GOTO_LABEL_ON_MEM_ERROR(url_copy, mem_error); + } + + if (tooltip) { + tooltip_copy = lxw_strdup(tooltip); + GOTO_LABEL_ON_MEM_ERROR(tooltip_copy, mem_error); + } + + if (link_type == HYPERLINK_INTERNAL) { + url_string = lxw_strdup(string_copy); + GOTO_LABEL_ON_MEM_ERROR(url_string, mem_error); + } + + /* Escape the URL. */ + if (link_type == HYPERLINK_URL && strlen(url_copy) >= 3) { + uint8_t not_escaped = 1; + + /* First check if the URL is already escaped by the user. */ + for (i = 0; i <= strlen(url_copy) - 3; i++) { + if (url_copy[i] == '%' && isxdigit(url_copy[i + 1]) + && isxdigit(url_copy[i + 2])) { + + not_escaped = 0; + break; + } + } + + if (not_escaped) { + url_external = calloc(1, strlen(url_copy) * 3 + 1); + GOTO_LABEL_ON_MEM_ERROR(url_external, mem_error); + + for (i = 0; i <= strlen(url_copy); i++) { + switch (url_copy[i]) { + case (' '): + case ('"'): + case ('%'): + case ('<'): + case ('>'): + case ('['): + case (']'): + case ('`'): + case ('^'): + case ('{'): + case ('}'): + lxw_snprintf(url_external + strlen(url_external), + sizeof("%xx"), "%%%2x", url_copy[i]); + break; + default: + url_external[strlen(url_external)] = url_copy[i]; + } + + } + + free(url_copy); + url_copy = lxw_strdup(url_external); + GOTO_LABEL_ON_MEM_ERROR(url_copy, mem_error); + + free(url_external); + url_external = NULL; + } + } + + if (link_type == HYPERLINK_EXTERNAL) { + /* External Workbook links need to be modified into the right format. + * The URL will look something like "c:\temp\file.xlsx#Sheet!A1". + * We need the part to the left of the # as the URL and the part to + * the right as the "location" string (if it exists). + */ + + /* For external links change the dir separator from Unix to DOS. */ + for (i = 0; i <= strlen(url_copy); i++) + if (url_copy[i] == '/') + url_copy[i] = '\\'; + + for (i = 0; i <= strlen(string_copy); i++) + if (string_copy[i] == '/') + string_copy[i] = '\\'; + + found_string = strchr(url_copy, '#'); + + if (found_string) { + url_string = lxw_strdup(found_string + 1); + GOTO_LABEL_ON_MEM_ERROR(url_string, mem_error); + + *found_string = '\0'; + } + + /* Look for Windows style "C:/" link or Windows share "\\" link. */ + found_string = strchr(url_copy, ':'); + if (!found_string) + found_string = strstr(url_copy, "\\\\"); + + if (found_string) { + /* Add the file:/// URI to the url if non-local. */ + string_size = sizeof("file:///") + strlen(url_copy); + url_external = calloc(1, string_size); + GOTO_LABEL_ON_MEM_ERROR(url_external, mem_error); + + lxw_snprintf(url_external, string_size, "file:///%s", url_copy); + + } + + /* Convert a ./dir/file.xlsx link to dir/file.xlsx. */ + found_string = strstr(url_copy, ".\\"); + if (found_string == url_copy) + memmove(url_copy, url_copy + 2, strlen(url_copy) - 1); + + if (url_external) { + free(url_copy); + url_copy = lxw_strdup(url_external); + GOTO_LABEL_ON_MEM_ERROR(url_copy, mem_error); + + free(url_external); + url_external = NULL; + } + + } + + /* Excel limits escaped URL to 255 characters. */ + if (lxw_utf8_strlen(url_copy) > 255) + goto mem_error; + + err = worksheet_write_string(self, row_num, col_num, string_copy, format); + if (err) + goto mem_error; + + link = _new_hyperlink_cell(row_num, col_num, link_type, url_copy, + url_string, tooltip_copy); + GOTO_LABEL_ON_MEM_ERROR(link, mem_error); + + _insert_hyperlink(self, row_num, col_num, link); + + free(string_copy); + self->hlink_count++; + return LXW_NO_ERROR; + +mem_error: + free(string_copy); + free(url_copy); + free(url_external); + free(url_string); + free(tooltip_copy); + return LXW_ERROR_MEMORY_MALLOC_FAILED; +} + +/* + * Write a hyperlink/url to an Excel file. + */ +lxw_error +worksheet_write_url(lxw_worksheet *self, + lxw_row_t row_num, + lxw_col_t col_num, const char *url, lxw_format *format) +{ + return worksheet_write_url_opt(self, row_num, col_num, url, format, NULL, + NULL); +} + +/* + * Set the properties of a single column or a range of columns with options. + */ +lxw_error +worksheet_set_column_opt(lxw_worksheet *self, + lxw_col_t firstcol, + lxw_col_t lastcol, + double width, + lxw_format *format, + lxw_row_col_options *user_options) +{ + lxw_col_options *copied_options; + uint8_t ignore_row = LXW_TRUE; + uint8_t ignore_col = LXW_TRUE; + uint8_t hidden = LXW_FALSE; + uint8_t level = 0; + uint8_t collapsed = LXW_FALSE; + lxw_col_t col; + lxw_error err; + + if (user_options) { + hidden = user_options->hidden; + level = user_options->level; + collapsed = user_options->collapsed; + } + + /* Ensure second col is larger than first. */ + if (firstcol > lastcol) { + lxw_col_t tmp = firstcol; + firstcol = lastcol; + lastcol = tmp; + } + + /* Ensure that the cols are valid and store max and min values. + * NOTE: The check shouldn't modify the row dimensions and should only + * modify the column dimensions in certain cases. */ + if (format != NULL || (width != LXW_DEF_COL_WIDTH && hidden)) + ignore_col = LXW_FALSE; + + err = _check_dimensions(self, 0, firstcol, ignore_row, ignore_col); + + if (!err) + err = _check_dimensions(self, 0, lastcol, ignore_row, ignore_col); + + if (err) + return err; + + /* Resize the col_options array if required. */ + if (firstcol >= self->col_options_max) { + lxw_col_t col; + lxw_col_t old_size = self->col_options_max; + lxw_col_t new_size = _next_power_of_two(firstcol + 1); + lxw_col_options **new_ptr = realloc(self->col_options, + new_size * + sizeof(lxw_col_options *)); + + if (new_ptr) { + for (col = old_size; col < new_size; col++) + new_ptr[col] = NULL; + + self->col_options = new_ptr; + self->col_options_max = new_size; + } + else { + return LXW_ERROR_MEMORY_MALLOC_FAILED; + } + } + + /* Resize the col_formats array if required. */ + if (lastcol >= self->col_formats_max) { + lxw_col_t col; + lxw_col_t old_size = self->col_formats_max; + lxw_col_t new_size = _next_power_of_two(lastcol + 1); + lxw_format **new_ptr = realloc(self->col_formats, + new_size * sizeof(lxw_format *)); + + if (new_ptr) { + for (col = old_size; col < new_size; col++) + new_ptr[col] = NULL; + + self->col_formats = new_ptr; + self->col_formats_max = new_size; + } + else { + return LXW_ERROR_MEMORY_MALLOC_FAILED; + } + } + + /* Store the column options. */ + copied_options = calloc(1, sizeof(lxw_col_options)); + RETURN_ON_MEM_ERROR(copied_options, LXW_ERROR_MEMORY_MALLOC_FAILED); + + copied_options->firstcol = firstcol; + copied_options->lastcol = lastcol; + copied_options->width = width; + copied_options->format = format; + copied_options->hidden = hidden; + copied_options->level = level; + copied_options->collapsed = collapsed; + + self->col_options[firstcol] = copied_options; + + /* Store the column formats for use when writing cell data. */ + for (col = firstcol; col <= lastcol; col++) { + self->col_formats[col] = format; + } + + /* Store the column change to allow optimizations. */ + self->col_size_changed = LXW_TRUE; + + return LXW_NO_ERROR; +} + +/* + * Set the properties of a single column or a range of columns. + */ +lxw_error +worksheet_set_column(lxw_worksheet *self, + lxw_col_t firstcol, + lxw_col_t lastcol, double width, lxw_format *format) +{ + return worksheet_set_column_opt(self, firstcol, lastcol, width, format, + NULL); +} + +/* + * Set the properties of a row with options. + */ +lxw_error +worksheet_set_row_opt(lxw_worksheet *self, + lxw_row_t row_num, + double height, + lxw_format *format, lxw_row_col_options *user_options) +{ + + lxw_col_t min_col; + uint8_t hidden = LXW_FALSE; + uint8_t level = 0; + uint8_t collapsed = LXW_FALSE; + lxw_row *row; + lxw_error err; + + if (user_options) { + hidden = user_options->hidden; + level = user_options->level; + collapsed = user_options->collapsed; + } + + /* Use minimum col in _check_dimensions(). */ + if (self->dim_colmin != LXW_COL_MAX) + min_col = self->dim_colmin; + else + min_col = 0; + + err = _check_dimensions(self, row_num, min_col, LXW_FALSE, LXW_FALSE); + if (err) + return err; + + /* If the height is 0 the row is hidden and the height is the default. */ + if (height == 0) { + hidden = LXW_TRUE; + height = self->default_row_height; + } + + row = _get_row(self, row_num); + + row->height = height; + row->format = format; + row->hidden = hidden; + row->level = level; + row->collapsed = collapsed; + row->row_changed = LXW_TRUE; + + if (height != self->default_row_height) + row->height_changed = LXW_TRUE; + + return LXW_NO_ERROR; +} + +/* + * Set the properties of a row. + */ +lxw_error +worksheet_set_row(lxw_worksheet *self, + lxw_row_t row_num, double height, lxw_format *format) +{ + return worksheet_set_row_opt(self, row_num, height, format, NULL); +} + +/* + * Merge a range of cells. The first cell should contain the data and the others + * should be blank. All cells should contain the same format. + */ +lxw_error +worksheet_merge_range(lxw_worksheet *self, lxw_row_t first_row, + lxw_col_t first_col, lxw_row_t last_row, + lxw_col_t last_col, const char *string, + lxw_format *format) +{ + lxw_merged_range *merged_range; + lxw_row_t tmp_row; + lxw_col_t tmp_col; + lxw_error err; + + /* Excel doesn't allow a single cell to be merged */ + if (first_row == last_row && first_col == last_col) + return LXW_ERROR_PARAMETER_VALIDATION; + + /* Swap last row/col with first row/col as necessary */ + if (first_row > last_row) { + tmp_row = last_row; + last_row = first_row; + first_row = tmp_row; + } + if (first_col > last_col) { + tmp_col = last_col; + last_col = first_col; + first_col = tmp_col; + } + + /* Check that column number is valid and store the max value */ + err = _check_dimensions(self, last_row, last_col, LXW_FALSE, LXW_FALSE); + if (err) + return err; + + /* Store the merge range. */ + merged_range = calloc(1, sizeof(lxw_merged_range)); + RETURN_ON_MEM_ERROR(merged_range, LXW_ERROR_MEMORY_MALLOC_FAILED); + + merged_range->first_row = first_row; + merged_range->first_col = first_col; + merged_range->last_row = last_row; + merged_range->last_col = last_col; + + STAILQ_INSERT_TAIL(self->merged_ranges, merged_range, list_pointers); + self->merged_range_count++; + + /* Write the first cell */ + worksheet_write_string(self, first_row, first_col, string, format); + + /* Pad out the rest of the area with formatted blank cells. */ + for (tmp_row = first_row; tmp_row <= last_row; tmp_row++) { + for (tmp_col = first_col; tmp_col <= last_col; tmp_col++) { + if (tmp_row == first_row && tmp_col == first_col) + continue; + + worksheet_write_blank(self, tmp_row, tmp_col, format); + } + } + + return LXW_NO_ERROR; +} + +/* + * Set the autofilter area in the worksheet. + */ +lxw_error +worksheet_autofilter(lxw_worksheet *self, lxw_row_t first_row, + lxw_col_t first_col, lxw_row_t last_row, + lxw_col_t last_col) +{ + lxw_row_t tmp_row; + lxw_col_t tmp_col; + lxw_error err; + + /* Excel doesn't allow a single cell to be merged */ + if (first_row == last_row && first_col == last_col) + return LXW_ERROR_PARAMETER_VALIDATION; + + /* Swap last row/col with first row/col as necessary */ + if (first_row > last_row) { + tmp_row = last_row; + last_row = first_row; + first_row = tmp_row; + } + if (first_col > last_col) { + tmp_col = last_col; + last_col = first_col; + first_col = tmp_col; + } + + /* Check that column number is valid and store the max value */ + err = _check_dimensions(self, last_row, last_col, LXW_FALSE, LXW_FALSE); + if (err) + return err; + + self->autofilter.in_use = LXW_TRUE; + self->autofilter.first_row = first_row; + self->autofilter.first_col = first_col; + self->autofilter.last_row = last_row; + self->autofilter.last_col = last_col; + + return LXW_NO_ERROR; +} + +/* + * Set this worksheet as a selected worksheet, i.e. the worksheet has its tab + * highlighted. + */ +void +worksheet_select(lxw_worksheet *self) +{ + self->selected = LXW_TRUE; + + /* Selected worksheet can't be hidden. */ + self->hidden = LXW_FALSE; +} + +/* + * Set this worksheet as the active worksheet, i.e. the worksheet that is + * displayed when the workbook is opened. Also set it as selected. + */ +void +worksheet_activate(lxw_worksheet *self) +{ + self->selected = LXW_TRUE; + self->active = LXW_TRUE; + + /* Active worksheet can't be hidden. */ + self->hidden = LXW_FALSE; + + *self->active_sheet = self->index; +} + +/* + * Set this worksheet as the first visible sheet. This is necessary + * when there are a large number of worksheets and the activated + * worksheet is not visible on the screen. + */ +void +worksheet_set_first_sheet(lxw_worksheet *self) +{ + /* Active worksheet can't be hidden. */ + self->hidden = LXW_FALSE; + + *self->first_sheet = self->index; +} + +/* + * Hide this worksheet. + */ +void +worksheet_hide(lxw_worksheet *self) +{ + self->hidden = LXW_TRUE; + + /* A hidden worksheet shouldn't be active or selected. */ + self->selected = LXW_FALSE; + + /* If this is active_sheet or first_sheet reset the workbook value. */ + if (*self->first_sheet == self->index) + *self->first_sheet = 0; + + if (*self->active_sheet == self->index) + *self->active_sheet = 0; +} + +/* + * Set which cell or cells are selected in a worksheet. + */ +void +worksheet_set_selection(lxw_worksheet *self, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t last_row, lxw_col_t last_col) +{ + lxw_selection *selection; + lxw_row_t tmp_row; + lxw_col_t tmp_col; + char active_cell[LXW_MAX_CELL_RANGE_LENGTH]; + char sqref[LXW_MAX_CELL_RANGE_LENGTH]; + + /* Only allow selection to be set once to avoid freeing/re-creating it. */ + if (!STAILQ_EMPTY(self->selections)) + return; + + /* Excel doesn't set a selection for cell A1 since it is the default. */ + if (first_row == 0 && first_col == 0 && last_row == 0 && last_col == 0) + return; + + selection = calloc(1, sizeof(lxw_selection)); + RETURN_VOID_ON_MEM_ERROR(selection); + + /* Set the cell range selection. Do this before swapping max/min to */ + /* allow the selection direction to be reversed. */ + lxw_rowcol_to_cell(active_cell, first_row, first_col); + + /* Swap last row/col for first row/col if necessary. */ + if (first_row > last_row) { + tmp_row = first_row; + first_row = last_row; + last_row = tmp_row; + } + + if (first_col > last_col) { + tmp_col = first_col; + first_col = last_col; + last_col = tmp_col; + } + + /* If the first and last cell are the same write a single cell. */ + if ((first_row == last_row) && (first_col == last_col)) + lxw_rowcol_to_cell(sqref, first_row, first_col); + else + lxw_rowcol_to_range(sqref, first_row, first_col, last_row, last_col); + + lxw_strcpy(selection->pane, ""); + lxw_strcpy(selection->active_cell, active_cell); + lxw_strcpy(selection->sqref, sqref); + + STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); +} + +/* + * Set panes and mark them as frozen. With extra options. + */ +void +worksheet_freeze_panes_opt(lxw_worksheet *self, + lxw_row_t first_row, lxw_col_t first_col, + lxw_row_t top_row, lxw_col_t left_col, + uint8_t type) +{ + self->panes.first_row = first_row; + self->panes.first_col = first_col; + self->panes.top_row = top_row; + self->panes.left_col = left_col; + self->panes.x_split = 0.0; + self->panes.y_split = 0.0; + + if (type) + self->panes.type = FREEZE_SPLIT_PANES; + else + self->panes.type = FREEZE_PANES; +} + +/* + * Set panes and mark them as frozen. + */ +void +worksheet_freeze_panes(lxw_worksheet *self, + lxw_row_t first_row, lxw_col_t first_col) +{ + worksheet_freeze_panes_opt(self, first_row, first_col, + first_row, first_col, 0); +} + +/* + * Set panes and mark them as split.With extra options. + */ +void +worksheet_split_panes_opt(lxw_worksheet *self, + double y_split, double x_split, + lxw_row_t top_row, lxw_col_t left_col) +{ + self->panes.first_row = 0; + self->panes.first_col = 0; + self->panes.top_row = top_row; + self->panes.left_col = left_col; + self->panes.x_split = x_split; + self->panes.y_split = y_split; + self->panes.type = SPLIT_PANES; +} + +/* + * Set panes and mark them as split. + */ +void +worksheet_split_panes(lxw_worksheet *self, double y_split, double x_split) +{ + worksheet_split_panes_opt(self, y_split, x_split, 0, 0); +} + +/* + * Set the page orientation as portrait. + */ +void +worksheet_set_portrait(lxw_worksheet *self) +{ + self->orientation = LXW_PORTRAIT; + self->page_setup_changed = LXW_TRUE; +} + +/* + * Set the page orientation as landscape. + */ +void +worksheet_set_landscape(lxw_worksheet *self) +{ + self->orientation = LXW_LANDSCAPE; + self->page_setup_changed = LXW_TRUE; +} + +/* + * Set the page view mode for Mac Excel. + */ +void +worksheet_set_page_view(lxw_worksheet *self) +{ + self->page_view = LXW_TRUE; +} + +/* + * Set the paper type. Example. 1 = US Letter, 9 = A4 + */ +void +worksheet_set_paper(lxw_worksheet *self, uint8_t paper_size) +{ + self->paper_size = paper_size; + self->page_setup_changed = LXW_TRUE; +} + +/* + * Set the order in which pages are printed. + */ +void +worksheet_print_across(lxw_worksheet *self) +{ + self->page_order = LXW_PRINT_ACROSS; + self->page_setup_changed = LXW_TRUE; +} + +/* + * Set all the page margins in inches. + */ +void +worksheet_set_margins(lxw_worksheet *self, double left, double right, + double top, double bottom) +{ + + if (left >= 0) + self->margin_left = left; + + if (right >= 0) + self->margin_right = right; + + if (top >= 0) + self->margin_top = top; + + if (bottom >= 0) + self->margin_bottom = bottom; +} + +/* + * Set the page header caption and options. + */ +lxw_error +worksheet_set_header_opt(lxw_worksheet *self, const char *string, + lxw_header_footer_options *options) +{ + if (options) { + if (options->margin > 0) + self->margin_header = options->margin; + } + + if (!string) + return LXW_ERROR_NULL_PARAMETER_IGNORED; + + if (lxw_utf8_strlen(string) >= LXW_HEADER_FOOTER_MAX) + return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + + lxw_strcpy(self->header, string); + self->header_footer_changed = 1; + + return LXW_NO_ERROR; +} + +/* + * Set the page footer caption and options. + */ +lxw_error +worksheet_set_footer_opt(lxw_worksheet *self, const char *string, + lxw_header_footer_options *options) +{ + if (options) { + if (options->margin > 0) + self->margin_footer = options->margin; + } + + if (!string) + return LXW_ERROR_NULL_PARAMETER_IGNORED; + + if (lxw_utf8_strlen(string) >= LXW_HEADER_FOOTER_MAX) + return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + + lxw_strcpy(self->footer, string); + self->header_footer_changed = 1; + + return LXW_NO_ERROR; +} + +/* + * Set the page header caption. + */ +lxw_error +worksheet_set_header(lxw_worksheet *self, const char *string) +{ + return worksheet_set_header_opt(self, string, NULL); +} + +/* + * Set the page footer caption. + */ +lxw_error +worksheet_set_footer(lxw_worksheet *self, const char *string) +{ + return worksheet_set_footer_opt(self, string, NULL); +} + +/* + * Set the option to show/hide gridlines on the screen and the printed page. + */ +void +worksheet_gridlines(lxw_worksheet *self, uint8_t option) +{ + if (option == LXW_HIDE_ALL_GRIDLINES) { + self->print_gridlines = 0; + self->screen_gridlines = 0; + } + + if (option & LXW_SHOW_SCREEN_GRIDLINES) { + self->screen_gridlines = 1; + } + + if (option & LXW_SHOW_PRINT_GRIDLINES) { + self->print_gridlines = 1; + self->print_options_changed = 1; + } +} + +/* + * Center the page horizontally. + */ +void +worksheet_center_horizontally(lxw_worksheet *self) +{ + self->print_options_changed = 1; + self->hcenter = 1; +} + +/* + * Center the page horizontally. + */ +void +worksheet_center_vertically(lxw_worksheet *self) +{ + self->print_options_changed = 1; + self->vcenter = 1; +} + +/* + * Set the option to print the row and column headers on the printed page. + */ +void +worksheet_print_row_col_headers(lxw_worksheet *self) +{ + self->print_headers = 1; + self->print_options_changed = 1; +} + +/* + * Set the rows to repeat at the top of each printed page. + */ +lxw_error +worksheet_repeat_rows(lxw_worksheet *self, lxw_row_t first_row, + lxw_row_t last_row) +{ + lxw_row_t tmp_row; + lxw_error err; + + if (first_row > last_row) { + tmp_row = last_row; + last_row = first_row; + first_row = tmp_row; + } + + err = _check_dimensions(self, last_row, 0, LXW_IGNORE, LXW_IGNORE); + if (err) + return err; + + self->repeat_rows.in_use = LXW_TRUE; + self->repeat_rows.first_row = first_row; + self->repeat_rows.last_row = last_row; + + return LXW_NO_ERROR; +} + +/* + * Set the columns to repeat at the left hand side of each printed page. + */ +lxw_error +worksheet_repeat_columns(lxw_worksheet *self, lxw_col_t first_col, + lxw_col_t last_col) +{ + lxw_col_t tmp_col; + lxw_error err; + + if (first_col > last_col) { + tmp_col = last_col; + last_col = first_col; + first_col = tmp_col; + } + + err = _check_dimensions(self, last_col, 0, LXW_IGNORE, LXW_IGNORE); + if (err) + return err; + + self->repeat_cols.in_use = LXW_TRUE; + self->repeat_cols.first_col = first_col; + self->repeat_cols.last_col = last_col; + + return LXW_NO_ERROR; +} + +/* + * Set the print area in the current worksheet. + */ +lxw_error +worksheet_print_area(lxw_worksheet *self, lxw_row_t first_row, + lxw_col_t first_col, lxw_row_t last_row, + lxw_col_t last_col) +{ + lxw_row_t tmp_row; + lxw_col_t tmp_col; + lxw_error err; + + if (first_row > last_row) { + tmp_row = last_row; + last_row = first_row; + first_row = tmp_row; + } + + if (first_col > last_col) { + tmp_col = last_col; + last_col = first_col; + first_col = tmp_col; + } + + err = _check_dimensions(self, last_row, last_col, LXW_IGNORE, LXW_IGNORE); + if (err) + return err; + + /* Ignore max area since it is the same as no print area in Excel. */ + if (first_row == 0 && first_col == 0 && last_row == LXW_ROW_MAX - 1 + && last_col == LXW_COL_MAX - 1) { + return LXW_NO_ERROR; + } + + self->print_area.in_use = LXW_TRUE; + self->print_area.first_row = first_row; + self->print_area.last_row = last_row; + self->print_area.first_col = first_col; + self->print_area.last_col = last_col; + + return LXW_NO_ERROR; +} + +/* Store the vertical and horizontal number of pages that will define the + * maximum area printed. + */ +void +worksheet_fit_to_pages(lxw_worksheet *self, uint16_t width, uint16_t height) +{ + self->fit_page = 1; + self->fit_width = width; + self->fit_height = height; + self->page_setup_changed = 1; +} + +/* + * Set the start page number. + */ +void +worksheet_set_start_page(lxw_worksheet *self, uint16_t start_page) +{ + self->page_start = start_page; +} + +/* + * Set the scale factor for the printed page. + */ +void +worksheet_set_print_scale(lxw_worksheet *self, uint16_t scale) +{ + /* Confine the scale to Excel"s range */ + if (scale < 10 || scale > 400) + return; + + /* Turn off "fit to page" option. */ + self->fit_page = LXW_FALSE; + + self->print_scale = scale; + self->page_setup_changed = LXW_TRUE; +} + +/* + * Store the horizontal page breaks on a worksheet. + */ +lxw_error +worksheet_set_h_pagebreaks(lxw_worksheet *self, lxw_row_t hbreaks[]) +{ + uint16_t count = 0; + + if (hbreaks == NULL) + return LXW_ERROR_NULL_PARAMETER_IGNORED; + + while (hbreaks[count]) + count++; + + /* The Excel 2007 specification says that the maximum number of page + * breaks is 1026. However, in practice it is actually 1023. */ + if (count > LXW_BREAKS_MAX) + count = LXW_BREAKS_MAX; + + self->hbreaks = calloc(count, sizeof(lxw_row_t)); + RETURN_ON_MEM_ERROR(self->hbreaks, LXW_ERROR_MEMORY_MALLOC_FAILED); + memcpy(self->hbreaks, hbreaks, count * sizeof(lxw_row_t)); + self->hbreaks_count = count; + + return LXW_NO_ERROR; +} + +/* + * Store the vertical page breaks on a worksheet. + */ +lxw_error +worksheet_set_v_pagebreaks(lxw_worksheet *self, lxw_col_t vbreaks[]) +{ + uint16_t count = 0; + + if (vbreaks == NULL) + return LXW_ERROR_NULL_PARAMETER_IGNORED; + + while (vbreaks[count]) + count++; + + /* The Excel 2007 specification says that the maximum number of page + * breaks is 1026. However, in practice it is actually 1023. */ + if (count > LXW_BREAKS_MAX) + count = LXW_BREAKS_MAX; + + self->vbreaks = calloc(count, sizeof(lxw_col_t)); + RETURN_ON_MEM_ERROR(self->vbreaks, LXW_ERROR_MEMORY_MALLOC_FAILED); + memcpy(self->vbreaks, vbreaks, count * sizeof(lxw_col_t)); + self->vbreaks_count = count; + + return LXW_NO_ERROR; +} + +/* + * Set the worksheet zoom factor. + */ +void +worksheet_set_zoom(lxw_worksheet *self, uint16_t scale) +{ + /* Confine the scale to Excel"s range */ + if (scale < 10 || scale > 400) { + LXW_WARN("worksheet_set_zoom(): " + "Zoom factor scale outside range: 10 <= zoom <= 400."); + return; + } + + self->zoom = scale; +} + +/* + * Hide cell zero values. + */ +void +worksheet_hide_zero(lxw_worksheet *self) +{ + self->show_zeros = LXW_FALSE; +} + +/* + * Display the worksheet right to left for some eastern versions of Excel. + */ +void +worksheet_right_to_left(lxw_worksheet *self) +{ + self->right_to_left = LXW_TRUE; +} + +/* + * Set the color of the worksheet tab. + */ +void +worksheet_set_tab_color(lxw_worksheet *self, lxw_color_t color) +{ + self->tab_color = color; +} + +/* + * Set the worksheet protection flags to prevent modification of worksheet + * objects. + */ +void +worksheet_protect(lxw_worksheet *self, const char *password, + lxw_protection *options) +{ + struct lxw_protection *protect = &self->protection; + + /* Copy any user parameters to the internal structure. */ + if (options) + memcpy(protect, options, sizeof(lxw_protection)); + + /* Zero the hash storage in case of copied initialization data. */ + protect->hash[0] = '\0'; + + if (password) { + uint16_t hash = _hash_password(password); + lxw_snprintf(protect->hash, 5, "%X", hash); + } + + protect->is_configured = LXW_TRUE; +} + +/* + * Set the default row properties + */ +void +worksheet_set_default_row(lxw_worksheet *self, double height, + uint8_t hide_unused_rows) +{ + if (height < 0) + height = self->default_row_height; + + if (height != self->default_row_height) { + self->default_row_height = height; + self->row_size_changed = LXW_TRUE; + } + + if (hide_unused_rows) + self->default_row_zeroed = LXW_TRUE; + + self->default_row_set = LXW_TRUE; +} + +/* + * Insert an image into the worksheet. + */ +lxw_error +worksheet_insert_image_opt(lxw_worksheet *self, + lxw_row_t row_num, lxw_col_t col_num, + const char *filename, + lxw_image_options *user_options) +{ + FILE *image_stream; + char *short_name; + lxw_image_options *options; + + if (!filename) { + LXW_WARN("worksheet_insert_image()/_opt(): " + "filename must be specified."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + /* Check that the image file exists and can be opened. */ + image_stream = fopen(filename, "rb"); + if (!image_stream) { + LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " + "file doesn't exist or can't be opened: %s.", + filename); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + /* Get the filename from the full path to add to the Drawing object. */ + short_name = lxw_basename(filename); + if (!short_name) { + LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " + "couldn't get basename for file: %s.", filename); + fclose(image_stream); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + /* Create a new object to hold the image options. */ + options = calloc(1, sizeof(lxw_image_options)); + if (!options) { + fclose(image_stream); + return LXW_ERROR_MEMORY_MALLOC_FAILED; + } + + if (user_options) { + memcpy(options, user_options, sizeof(lxw_image_options)); + options->url = lxw_strdup(user_options->url); + options->tip = lxw_strdup(user_options->tip); + } + + /* Copy other options or set defaults. */ + options->filename = lxw_strdup(filename); + options->short_name = lxw_strdup(short_name); + options->stream = image_stream; + options->row = row_num; + options->col = col_num; + + if (!options->x_scale) + options->x_scale = 1; + + if (!options->y_scale) + options->y_scale = 1; + + if (_get_image_properties(options) == LXW_NO_ERROR) { + STAILQ_INSERT_TAIL(self->image_data, options, list_pointers); + fclose(image_stream); + return LXW_NO_ERROR; + } + else { + free(options); + fclose(image_stream); + return LXW_ERROR_IMAGE_DIMENSIONS; + } +} + +/* + * Insert an image into the worksheet. + */ +lxw_error +worksheet_insert_image(lxw_worksheet *self, + lxw_row_t row_num, lxw_col_t col_num, + const char *filename) +{ + return worksheet_insert_image_opt(self, row_num, col_num, filename, NULL); +} + +/* + * Insert an chart into the worksheet. + */ +lxw_error +worksheet_insert_chart_opt(lxw_worksheet *self, + lxw_row_t row_num, lxw_col_t col_num, + lxw_chart *chart, lxw_image_options *user_options) +{ + lxw_image_options *options; + lxw_chart_series *series; + + if (!chart) { + LXW_WARN("worksheet_insert_chart()/_opt(): chart must be non-NULL."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + /* Check that the chart isn't being used more than once. */ + if (chart->in_use) { + LXW_WARN("worksheet_insert_chart()/_opt(): the same chart object " + "cannot be inserted in a worksheet more than once."); + + return LXW_ERROR_PARAMETER_VALIDATION; + } + + /* Check that the chart has a data series. */ + if (STAILQ_EMPTY(chart->series_list)) { + LXW_WARN + ("worksheet_insert_chart()/_opt(): chart must have a series."); + + return LXW_ERROR_PARAMETER_VALIDATION; + } + + /* Check that the chart has a 'values' series. */ + STAILQ_FOREACH(series, chart->series_list, list_pointers) { + if (!series->values->formula && !series->values->sheetname) { + LXW_WARN("worksheet_insert_chart()/_opt(): chart must have a " + "'values' series."); + + return LXW_ERROR_PARAMETER_VALIDATION; + } + } + + /* Create a new object to hold the chart image options. */ + options = calloc(1, sizeof(lxw_image_options)); + RETURN_ON_MEM_ERROR(options, LXW_ERROR_MEMORY_MALLOC_FAILED); + + if (user_options) + memcpy(options, user_options, sizeof(lxw_image_options)); + + /* Copy other options or set defaults. */ + options->row = row_num; + options->col = col_num; + + /* TODO. Read defaults from chart. */ + options->width = 480; + options->height = 288; + + if (!options->x_scale) + options->x_scale = 1; + + if (!options->y_scale) + options->y_scale = 1; + + /* Store chart references so they can be ordered in the workbook. */ + options->chart = chart; + + STAILQ_INSERT_TAIL(self->chart_data, options, list_pointers); + + chart->in_use = LXW_TRUE; + + return LXW_NO_ERROR; +} + +/* + * Insert an image into the worksheet. + */ +lxw_error +worksheet_insert_chart(lxw_worksheet *self, + lxw_row_t row_num, lxw_col_t col_num, lxw_chart *chart) +{ + return worksheet_insert_chart_opt(self, row_num, col_num, chart, NULL); +} diff --git a/src/libxlsxwriter/xmlwriter.c b/src/libxlsxwriter/xmlwriter.c new file mode 100644 index 0000000..c30d322 --- /dev/null +++ b/src/libxlsxwriter/xmlwriter.c @@ -0,0 +1,355 @@ +/***************************************************************************** + * xmlwriter - A base library for libxlsxwriter libraries. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include +#include +#include +#include "xlsxwriter/xmlwriter.h" + +#define LXW_AMP "&" +#define LXW_LT "<" +#define LXW_GT ">" +#define LXW_QUOT """ + +/* Defines. */ +#define LXW_MAX_ENCODED_ATTRIBUTE_LENGTH (LXW_MAX_ATTRIBUTE_LENGTH*6) + +/* Forward declarations. */ +STATIC char *_escape_attributes(struct xml_attribute *attribute); + +char *lxw_escape_data(const char *data); + +STATIC void _fprint_escaped_attributes(FILE * xmlfile, + struct xml_attribute_list *attributes); + +STATIC void _fprint_escaped_data(FILE * xmlfile, const char *data); + +/* + * Write the XML declaration. + */ +void +lxw_xml_declaration(FILE * xmlfile) +{ + fprintf(xmlfile, "\n"); +} + +/* + * Write an XML start tag with optional attributes. + */ +void +lxw_xml_start_tag(FILE * xmlfile, + const char *tag, struct xml_attribute_list *attributes) +{ + fprintf(xmlfile, "<%s", tag); + + _fprint_escaped_attributes(xmlfile, attributes); + + fprintf(xmlfile, ">"); +} + +/* + * Write an XML start tag with optional, unencoded, attributes. + * This is a minor speed optimization for elements that don't need encoding. + */ +void +lxw_xml_start_tag_unencoded(FILE * xmlfile, + const char *tag, + struct xml_attribute_list *attributes) +{ + struct xml_attribute *attribute; + + fprintf(xmlfile, "<%s", tag); + + if (attributes) { + STAILQ_FOREACH(attribute, attributes, list_entries) { + fprintf(xmlfile, " %s=\"%s\"", attribute->key, attribute->value); + } + } + + fprintf(xmlfile, ">"); +} + +/* + * Write an XML end tag. + */ +void +lxw_xml_end_tag(FILE * xmlfile, const char *tag) +{ + fprintf(xmlfile, "", tag); +} + +/* + * Write an empty XML tag with optional attributes. + */ +void +lxw_xml_empty_tag(FILE * xmlfile, + const char *tag, struct xml_attribute_list *attributes) +{ + fprintf(xmlfile, "<%s", tag); + + _fprint_escaped_attributes(xmlfile, attributes); + + fprintf(xmlfile, "/>"); +} + +/* + * Write an XML start tag with optional, unencoded, attributes. + * This is a minor speed optimization for elements that don't need encoding. + */ +void +lxw_xml_empty_tag_unencoded(FILE * xmlfile, + const char *tag, + struct xml_attribute_list *attributes) +{ + struct xml_attribute *attribute; + + fprintf(xmlfile, "<%s", tag); + + if (attributes) { + STAILQ_FOREACH(attribute, attributes, list_entries) { + fprintf(xmlfile, " %s=\"%s\"", attribute->key, attribute->value); + } + } + + fprintf(xmlfile, "/>"); +} + +/* + * Write an XML element containing data with optional attributes. + */ +void +lxw_xml_data_element(FILE * xmlfile, + const char *tag, + const char *data, struct xml_attribute_list *attributes) +{ + fprintf(xmlfile, "<%s", tag); + + _fprint_escaped_attributes(xmlfile, attributes); + + fprintf(xmlfile, ">"); + + _fprint_escaped_data(xmlfile, data); + + fprintf(xmlfile, "", tag); +} + +/* + * Escape XML characters in attributes. + */ +STATIC char * +_escape_attributes(struct xml_attribute *attribute) +{ + char *encoded = (char *) calloc(LXW_MAX_ENCODED_ATTRIBUTE_LENGTH, 1); + char *p_encoded = encoded; + char *p_attr = attribute->value; + + while (*p_attr) { + switch (*p_attr) { + case '&': + strncat(p_encoded, LXW_AMP, sizeof(LXW_AMP) - 1); + p_encoded += sizeof(LXW_AMP) - 1; + break; + case '<': + strncat(p_encoded, LXW_LT, sizeof(LXW_LT) - 1); + p_encoded += sizeof(LXW_LT) - 1; + break; + case '>': + strncat(p_encoded, LXW_GT, sizeof(LXW_GT) - 1); + p_encoded += sizeof(LXW_GT) - 1; + break; + case '"': + strncat(p_encoded, LXW_QUOT, sizeof(LXW_QUOT) - 1); + p_encoded += sizeof(LXW_QUOT) - 1; + break; + default: + *p_encoded = *p_attr; + p_encoded++; + break; + } + p_attr++; + } + + return encoded; +} + +/* + * Escape XML characters in data sections of tags. + * Note, this is different from _escape_attributes() + * in that double quotes are not escaped by Excel. + */ +char * +lxw_escape_data(const char *data) +{ + size_t encoded_len = (strlen(data) * 5 + 1); + + char *encoded = (char *) calloc(encoded_len, 1); + char *p_encoded = encoded; + + while (*data) { + switch (*data) { + case '&': + strncat(p_encoded, LXW_AMP, sizeof(LXW_AMP) - 1); + p_encoded += sizeof(LXW_AMP) - 1; + break; + case '<': + strncat(p_encoded, LXW_LT, sizeof(LXW_LT) - 1); + p_encoded += sizeof(LXW_LT) - 1; + break; + case '>': + strncat(p_encoded, LXW_GT, sizeof(LXW_GT) - 1); + p_encoded += sizeof(LXW_GT) - 1; + break; + default: + *p_encoded = *data; + p_encoded++; + break; + } + data++; + } + + return encoded; +} + +/* + * Escape control characters in strings with with _xHHHH_. + */ +char * +lxw_escape_control_characters(const char *string) +{ + size_t escape_len = sizeof("_xHHHH_") - 1; + size_t encoded_len = (strlen(string) * escape_len + 1); + + char *encoded = (char *) calloc(encoded_len, 1); + char *p_encoded = encoded; + + while (*string) { + switch (*string) { + case '\x01': + case '\x02': + case '\x03': + case '\x04': + case '\x05': + case '\x06': + case '\x07': + case '\x08': + case '\x0B': + case '\x0C': + case '\x0D': + case '\x0E': + case '\x0F': + case '\x10': + case '\x11': + case '\x12': + case '\x13': + case '\x14': + case '\x15': + case '\x16': + case '\x17': + case '\x18': + case '\x19': + case '\x1A': + case '\x1B': + case '\x1C': + case '\x1D': + case '\x1E': + case '\x1F': + lxw_snprintf(p_encoded, escape_len + 1, "_x%04X_", *string); + p_encoded += escape_len; + break; + default: + *p_encoded = *string; + p_encoded++; + break; + } + string++; + } + + return encoded; +} + +/* Write out escaped attributes. */ +STATIC void +_fprint_escaped_attributes(FILE * xmlfile, + struct xml_attribute_list *attributes) +{ + struct xml_attribute *attribute; + + if (attributes) { + STAILQ_FOREACH(attribute, attributes, list_entries) { + fprintf(xmlfile, " %s=", attribute->key); + + if (!strpbrk(attribute->value, "&<>\"")) { + fprintf(xmlfile, "\"%s\"", attribute->value); + } + else { + char *encoded = _escape_attributes(attribute); + + if (encoded) { + fprintf(xmlfile, "\"%s\"", encoded); + + free(encoded); + } + } + } + } +} + +/* Write out escaped XML data. */ +STATIC void +_fprint_escaped_data(FILE * xmlfile, const char *data) +{ + /* Escape the data section of the XML element. */ + if (!strpbrk(data, "&<>")) { + fprintf(xmlfile, "%s", data); + } + else { + char *encoded = lxw_escape_data(data); + if (encoded) { + fprintf(xmlfile, "%s", encoded); + free(encoded); + } + } +} + +/* Create a new string XML attribute. */ +struct xml_attribute * +lxw_new_attribute_str(const char *key, const char *value) +{ + struct xml_attribute *attribute = malloc(sizeof(struct xml_attribute)); + + LXW_ATTRIBUTE_COPY(attribute->key, key); + LXW_ATTRIBUTE_COPY(attribute->value, value); + + return attribute; +} + +/* Create a new integer XML attribute. */ +struct xml_attribute * +lxw_new_attribute_int(const char *key, uint32_t value) +{ + struct xml_attribute *attribute = malloc(sizeof(struct xml_attribute)); + + LXW_ATTRIBUTE_COPY(attribute->key, key); + lxw_snprintf(attribute->value, LXW_MAX_ATTRIBUTE_LENGTH, "%d", value); + + return attribute; +} + +/* Create a new double XML attribute. */ +struct xml_attribute * +lxw_new_attribute_dbl(const char *key, double value) +{ + struct xml_attribute *attribute = malloc(sizeof(struct xml_attribute)); + + LXW_ATTRIBUTE_COPY(attribute->key, key); + lxw_snprintf(attribute->value, LXW_MAX_ATTRIBUTE_LENGTH, "%.16g", value); + + return attribute; +} diff --git a/src/minizip/crypt.h b/src/minizip/crypt.h new file mode 100644 index 0000000..1e9e820 --- /dev/null +++ b/src/minizip/crypt.h @@ -0,0 +1,131 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const z_crc_t* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const z_crc_t* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const z_crc_t* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(const char* passwd, /* password string */ + unsigned char* buf, /* where to write header */ + int bufSize, + unsigned long* pkeys, + const z_crc_t* pcrc_32_tab, + unsigned long crcForCrypting) +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/src/minizip/ioapi.c b/src/minizip/ioapi.c new file mode 100644 index 0000000..7f5c191 --- /dev/null +++ b/src/minizip/ioapi.c @@ -0,0 +1,247 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#if defined(_WIN32) && (!(defined(_CRT_SECURE_NO_WARNINGS))) + #define _CRT_SECURE_NO_WARNINGS +#endif + +#if defined(__APPLE__) || defined(IOAPI_NO_64) +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions +#define FOPEN_FUNC(filename, mode) fopen(filename, mode) +#define FTELLO_FUNC(stream) ftello(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) +#else +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode) +#define FTELLO_FUNC(stream) ftello64(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) +#endif + + +#include "ioapi.h" + +voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) +{ + if (pfilefunc->zfile_func64.zopen64_file != NULL) + return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); + else + { + return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); + } +} + +long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); + else + { + uLong offsetTruncated = (uLong)offset; + if (offsetTruncated != offset) + return -1; + else + return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); + } +} + +ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); + else + { + uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); + if ((tell_uLong) == MAXU32) + return (ZPOS64_T)-1; + else + return tell_uLong; + } +} + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) +{ + p_filefunc64_32->zfile_func64.zopen64_file = NULL; + p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; + p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; + p_filefunc64_32->zfile_func64.ztell64_file = NULL; + p_filefunc64_32->zfile_func64.zseek64_file = NULL; + p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; + p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; + p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; +} + + + +static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); +static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); +static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); +static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); +static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); + +static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + +static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = FOPEN_FUNC((const char*)filename, mode_fopen); + return file; +} + + +static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + + +static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret; + ret = FTELLO_FUNC((FILE *)stream); + return ret; +} + +static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + if (fseek((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + return ret; +} + +static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + + if(FSEEKO_FUNC((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + + return ret; +} + + +static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = fopen64_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell64_file = ftell64_file_func; + pzlib_filefunc_def->zseek64_file = fseek64_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/src/minizip/ioapi.h b/src/minizip/ioapi.h new file mode 100644 index 0000000..43aaf6f --- /dev/null +++ b/src/minizip/ioapi.h @@ -0,0 +1,208 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + + Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) + Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. + More if/def section may be needed to support other platforms + Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. + (but you should use iowin32.c for windows instead) + +*/ + +#ifndef _ZLIBIOAPI64_H +#define _ZLIBIOAPI64_H + +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) + + // Linux needs this to support file operation on files larger then 4+GB + // But might need better if/def to select just the platforms that needs them. + + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif + +#endif + +#include +#include +#include "zlib.h" + +#if defined(USE_FILE32API) +#define fopen64 fopen +#define ftello64 ftell +#define fseeko64 fseek +#else +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#define fopen64 fopen +#define ftello64 ftello +#define fseeko64 fseeko +#endif +#ifdef _MSC_VER + #define fopen64 fopen + #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) + #define ftello64 _ftelli64 + #define fseeko64 _fseeki64 + #else // old MSC + #define ftello64 ftell + #define fseeko64 fseek + #endif +#endif +#endif + +/* +#ifndef ZPOS64_T + #ifdef _WIN32 + #define ZPOS64_T fpos_t + #else + #include + #define ZPOS64_T uint64_t + #endif +#endif +*/ + +#ifdef HAVE_MINIZIP64_CONF_H +#include "mz64conf.h" +#endif + +/* a type choosen by DEFINE */ +#ifdef HAVE_64BIT_INT_CUSTOM +typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; +#else +#ifdef HAS_STDINT_H +#include "stdint.h" +typedef uint64_t ZPOS64_T; +#else + +/* Maximum unsigned 32-bit value used as placeholder for zip64 */ +#define MAXU32 0xffffffff + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 ZPOS64_T; +#else +typedef unsigned long long int ZPOS64_T; +#endif +#endif +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) + #define ZCALLBACK CALLBACK + #else + #define ZCALLBACK + #endif +#endif + + + + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); + + +/* here is the "old" 32 bits structure structure */ +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + +typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); + +typedef struct zlib_filefunc64_def_s +{ + open64_file_func zopen64_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell64_file_func ztell64_file; + seek64_file_func zseek64_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc64_def; + +void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +/* now internal definition, only for zip.c and unzip.h */ +typedef struct zlib_filefunc64_32_def_s +{ + zlib_filefunc64_def zfile_func64; + open_file_func zopen32_file; + tell_file_func ztell32_file; + seek_file_func zseek32_file; +} zlib_filefunc64_32_def; + + +#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +/* #define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) */ +/* #define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) */ +#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) +#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) + +voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); +long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); +ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); + +#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) +#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) +#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/minizip/iowin32.c b/src/minizip/iowin32.c new file mode 100644 index 0000000..93d3ada --- /dev/null +++ b/src/minizip/iowin32.c @@ -0,0 +1,456 @@ +/* iowin32.c -- IO base function header for compress/uncompress .zip + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#include + +#include "zlib.h" +#include "ioapi.h" +#include "iowin32.h" + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE (0xFFFFFFFF) +#endif + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + + + +voidpf ZCALLBACK win32_open_file_func OF((voidpf opaque, const char* filename, int mode)); +uLong ZCALLBACK win32_read_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +uLong ZCALLBACK win32_write_file_func OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +ZPOS64_T ZCALLBACK win32_tell64_file_func OF((voidpf opaque, voidpf stream)); +long ZCALLBACK win32_seek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +int ZCALLBACK win32_close_file_func OF((voidpf opaque, voidpf stream)); +int ZCALLBACK win32_error_file_func OF((voidpf opaque, voidpf stream)); + +typedef struct +{ + HANDLE hf; + int error; +} WIN32FILE_IOWIN; + + +static void win32_translate_open_mode(int mode, + DWORD* lpdwDesiredAccess, + DWORD* lpdwCreationDisposition, + DWORD* lpdwShareMode, + DWORD* lpdwFlagsAndAttributes) +{ + *lpdwDesiredAccess = *lpdwShareMode = *lpdwFlagsAndAttributes = *lpdwCreationDisposition = 0; + + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + { + *lpdwDesiredAccess = GENERIC_READ; + *lpdwCreationDisposition = OPEN_EXISTING; + *lpdwShareMode = FILE_SHARE_READ; + } + else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + { + *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + *lpdwCreationDisposition = OPEN_EXISTING; + } + else if (mode & ZLIB_FILEFUNC_MODE_CREATE) + { + *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + *lpdwCreationDisposition = CREATE_ALWAYS; + } +} + +static voidpf win32_build_iowin(HANDLE hFile) +{ + voidpf ret=NULL; + + if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + WIN32FILE_IOWIN w32fiow; + w32fiow.hf = hFile; + w32fiow.error = 0; + ret = malloc(sizeof(WIN32FILE_IOWIN)); + + if (ret==NULL) + CloseHandle(hFile); + else + *((WIN32FILE_IOWIN*)ret) = w32fiow; + } + return ret; +} + +voidpf ZCALLBACK win32_open64_file_func (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + +#ifdef IOWIN32_USING_WINRT_API +#ifdef UNICODE + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile2((LPCTSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + { + WCHAR filenameW[FILENAME_MAX + 0x200 + 1]; + MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200); + hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); + } +#endif +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); +#endif + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open64_file_funcA (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + +#ifdef IOWIN32_USING_WINRT_API + if ((filename!=NULL) && (dwDesiredAccess != 0)) + { + WCHAR filenameW[FILENAME_MAX + 0x200 + 1]; + MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200); + hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); + } +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFileA((LPCSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); +#endif + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open64_file_funcW (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + +#ifdef IOWIN32_USING_WINRT_API + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile2((LPCWSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition,NULL); +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFileW((LPCWSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); +#endif + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open_file_func (voidpf opaque,const char* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + +#ifdef IOWIN32_USING_WINRT_API +#ifdef UNICODE + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile2((LPCTSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + { + WCHAR filenameW[FILENAME_MAX + 0x200 + 1]; + MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200); + hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); + } +#endif +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); +#endif + + return win32_build_iowin(hFile); +} + + +uLong ZCALLBACK win32_read_file_func (voidpf opaque, voidpf stream, void* buf,uLong size) +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile != NULL) + { + if (!ReadFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + } + + return ret; +} + + +uLong ZCALLBACK win32_write_file_func (voidpf opaque,voidpf stream,const void* buf,uLong size) +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile != NULL) + { + if (!WriteFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + } + + return ret; +} + +static BOOL MySetFilePointerEx(HANDLE hFile, LARGE_INTEGER pos, LARGE_INTEGER *newPos, DWORD dwMoveMethod) +{ +#ifdef IOWIN32_USING_WINRT_API + return SetFilePointerEx(hFile, pos, newPos, dwMoveMethod); +#else + LONG lHigh = pos.HighPart; + DWORD dwNewPos = SetFilePointer(hFile, pos.LowPart, &lHigh, dwMoveMethod); + BOOL fOk = TRUE; + if (dwNewPos == 0xFFFFFFFF) + if (GetLastError() != NO_ERROR) + fOk = FALSE; + if ((newPos != NULL) && (fOk)) + { + newPos->LowPart = dwNewPos; + newPos->HighPart = lHigh; + } + return fOk; +#endif +} + +long ZCALLBACK win32_tell_file_func (voidpf opaque,voidpf stream) +{ + long ret=-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + LARGE_INTEGER pos; + pos.QuadPart = 0; + + if (!MySetFilePointerEx(hFile, pos, &pos, FILE_CURRENT)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=(long)pos.LowPart; + } + return ret; +} + +ZPOS64_T ZCALLBACK win32_tell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret= (ZPOS64_T)-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream)->hf; + + if (hFile) + { + LARGE_INTEGER pos; + pos.QuadPart = 0; + + if (!MySetFilePointerEx(hFile, pos, &pos, FILE_CURRENT)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = (ZPOS64_T)-1; + } + else + ret=pos.QuadPart; + } + return ret; +} + + +long ZCALLBACK win32_seek_file_func (voidpf opaque,voidpf stream,uLong offset,int origin) +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + + long ret=-1; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile != NULL) + { + LARGE_INTEGER pos; + pos.QuadPart = offset; + if (!MySetFilePointerEx(hFile, pos, NULL, dwMoveMethod)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +long ZCALLBACK win32_seek64_file_func (voidpf opaque, voidpf stream,ZPOS64_T offset,int origin) +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + long ret=-1; + + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream)->hf; + + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile) + { + LARGE_INTEGER pos; + pos.QuadPart = offset; + if (!MySetFilePointerEx(hFile, pos, NULL, dwMoveMethod)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +int ZCALLBACK win32_close_file_func (voidpf opaque, voidpf stream) +{ + int ret=-1; + + if (stream!=NULL) + { + HANDLE hFile; + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + CloseHandle(hFile); + ret=0; + } + free(stream); + } + return ret; +} + +int ZCALLBACK win32_error_file_func (voidpf opaque,voidpf stream) +{ + int ret=-1; + if (stream!=NULL) + { + ret = ((WIN32FILE_IOWIN*)stream) -> error; + } + return ret; +} + +void fill_win32_filefunc (zlib_filefunc_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen_file = win32_open_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell_file = win32_tell_file_func; + pzlib_filefunc_def->zseek_file = win32_seek_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_win32_filefunc64(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + + +void fill_win32_filefunc64A(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_funcA; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + + +void fill_win32_filefunc64W(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_funcW; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/src/minizip/iowin32.h b/src/minizip/iowin32.h new file mode 100644 index 0000000..0ca0969 --- /dev/null +++ b/src/minizip/iowin32.h @@ -0,0 +1,28 @@ +/* iowin32.h -- IO base function header for compress/uncompress .zip + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); +void fill_win32_filefunc64 OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_win32_filefunc64A OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_win32_filefunc64W OF((zlib_filefunc64_def* pzlib_filefunc_def)); + +#ifdef __cplusplus +} +#endif diff --git a/src/minizip/miniunz.c b/src/minizip/miniunz.c new file mode 100644 index 0000000..3d65401 --- /dev/null +++ b/src/minizip/miniunz.c @@ -0,0 +1,660 @@ +/* + miniunz.c + Version 1.1, February 14h, 2010 + sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) +*/ + +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif +#endif + +#ifdef __APPLE__ +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions +#define FOPEN_FUNC(filename, mode) fopen(filename, mode) +#define FTELLO_FUNC(stream) ftello(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) +#else +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode) +#define FTELLO_FUNC(stream) ftello64(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) +#endif + + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include +#else +# include +# include +#endif + + +#include "unzip.h" + +#define CASESENSITIVITY (0) +#define WRITEBUFFERSIZE (8192) +#define MAXFILENAME (256) + +#ifdef _WIN32 +#define USEWIN32IOAPI +#include "iowin32.h" +#endif +/* + mini unzip, demo of unzip package + + usage : + Usage : miniunz [-exvlo] file.zip [file_to_extract] [-d extractdir] + + list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT + if it exists +*/ + + +/* change_file_date : change the date/time of a file + filename : the filename of the file where date/time must be modified + dosdate : the new date at the MSDos format (4 bytes) + tmu_date : the SAME new date at the tm_unz format */ +void change_file_date(filename,dosdate,tmu_date) + const char *filename; + uLong dosdate; + tm_unz tmu_date; +{ +#ifdef _WIN32 + HANDLE hFile; + FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite; + + hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE, + 0,NULL,OPEN_EXISTING,0,NULL); + GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite); + DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal); + LocalFileTimeToFileTime(&ftLocal,&ftm); + SetFileTime(hFile,&ftm,&ftLastAcc,&ftm); + CloseHandle(hFile); +#else +#ifdef unix || __APPLE__ + struct utimbuf ut; + struct tm newdate; + newdate.tm_sec = tmu_date.tm_sec; + newdate.tm_min=tmu_date.tm_min; + newdate.tm_hour=tmu_date.tm_hour; + newdate.tm_mday=tmu_date.tm_mday; + newdate.tm_mon=tmu_date.tm_mon; + if (tmu_date.tm_year > 1900) + newdate.tm_year=tmu_date.tm_year - 1900; + else + newdate.tm_year=tmu_date.tm_year ; + newdate.tm_isdst=-1; + + ut.actime=ut.modtime=mktime(&newdate); + utime(filename,&ut); +#endif +#endif +} + + +/* mymkdir and change_file_date are not 100 % portable + As I don't know well Unix, I wait feedback for the unix portion */ + +int mymkdir(dirname) + const char* dirname; +{ + int ret=0; +#ifdef _WIN32 + ret = _mkdir(dirname); +#elif unix + ret = mkdir (dirname,0775); +#elif __APPLE__ + ret = mkdir (dirname,0775); +#endif + return ret; +} + +int makedir (newdir) + char *newdir; +{ + char *buffer ; + char *p; + int len = (int)strlen(newdir); + + if (len <= 0) + return 0; + + buffer = (char*)malloc(len+1); + if (buffer==NULL) + { + printf("Error allocating memory\n"); + return UNZ_INTERNALERROR; + } + strcpy(buffer,newdir); + + if (buffer[len-1] == '/') { + buffer[len-1] = '\0'; + } + if (mymkdir(buffer) == 0) + { + free(buffer); + return 1; + } + + p = buffer+1; + while (1) + { + char hold; + + while(*p && *p != '\\' && *p != '/') + p++; + hold = *p; + *p = 0; + if ((mymkdir(buffer) == -1) && (errno == ENOENT)) + { + printf("couldn't create directory %s\n",buffer); + free(buffer); + return 0; + } + if (hold == 0) + break; + *p++ = hold; + } + free(buffer); + return 1; +} + +void do_banner() +{ + printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant\n"); + printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n"); +} + +void do_help() +{ + printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \ + " -e Extract without pathname (junk paths)\n" \ + " -x Extract with pathname\n" \ + " -v list files\n" \ + " -l list files\n" \ + " -d directory to extract into\n" \ + " -o overwrite files without prompting\n" \ + " -p extract crypted file using password\n\n"); +} + +void Display64BitsSize(ZPOS64_T n, int size_char) +{ + /* to avoid compatibility problem , we do here the conversion */ + char number[21]; + int offset=19; + int pos_string = 19; + number[20]=0; + for (;;) { + number[offset]=(char)((n%10)+'0'); + if (number[offset] != '0') + pos_string=offset; + n/=10; + if (offset==0) + break; + offset--; + } + { + int size_display_string = 19-pos_string; + while (size_char > size_display_string) + { + size_char--; + printf(" "); + } + } + + printf("%s",&number[pos_string]); +} + +int do_list(uf) + unzFile uf; +{ + uLong i; + unz_global_info64 gi; + int err; + + err = unzGetGlobalInfo64(uf,&gi); + if (err!=UNZ_OK) + printf("error %d with zipfile in unzGetGlobalInfo \n",err); + printf(" Length Method Size Ratio Date Time CRC-32 Name\n"); + printf(" ------ ------ ---- ----- ---- ---- ------ ----\n"); + for (i=0;i0) + ratio = (uLong)((file_info.compressed_size*100)/file_info.uncompressed_size); + + /* display a '*' if the file is crypted */ + if ((file_info.flag & 1) != 0) + charCrypt='*'; + + if (file_info.compression_method==0) + string_method="Stored"; + else + if (file_info.compression_method==Z_DEFLATED) + { + uInt iLevel=(uInt)((file_info.flag & 0x6)/2); + if (iLevel==0) + string_method="Defl:N"; + else if (iLevel==1) + string_method="Defl:X"; + else if ((iLevel==2) || (iLevel==3)) + string_method="Defl:F"; /* 2:fast , 3 : extra fast*/ + } + else + if (file_info.compression_method==Z_BZIP2ED) + { + string_method="BZip2 "; + } + else + string_method="Unkn. "; + + Display64BitsSize(file_info.uncompressed_size,7); + printf(" %6s%c",string_method,charCrypt); + Display64BitsSize(file_info.compressed_size,7); + printf(" %3lu%% %2.2lu-%2.2lu-%2.2lu %2.2lu:%2.2lu %8.8lx %s\n", + ratio, + (uLong)file_info.tmu_date.tm_mon + 1, + (uLong)file_info.tmu_date.tm_mday, + (uLong)file_info.tmu_date.tm_year % 100, + (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min, + (uLong)file_info.crc,filename_inzip); + if ((i+1)='a') && (rep<='z')) + rep -= 0x20; + } + while ((rep!='Y') && (rep!='N') && (rep!='A')); + } + + if (rep == 'N') + skip = 1; + + if (rep == 'A') + *popt_overwrite=1; + } + + if ((skip==0) && (err==UNZ_OK)) + { + fout=FOPEN_FUNC(write_filename,"wb"); + /* some zipfile don't contain directory alone before file */ + if ((fout==NULL) && ((*popt_extract_without_path)==0) && + (filename_withoutpath!=(char*)filename_inzip)) + { + char c=*(filename_withoutpath-1); + *(filename_withoutpath-1)='\0'; + makedir(write_filename); + *(filename_withoutpath-1)=c; + fout=FOPEN_FUNC(write_filename,"wb"); + } + + if (fout==NULL) + { + printf("error opening %s\n",write_filename); + } + } + + if (fout!=NULL) + { + printf(" extracting: %s\n",write_filename); + + do + { + err = unzReadCurrentFile(uf,buf,size_buf); + if (err<0) + { + printf("error %d with zipfile in unzReadCurrentFile\n",err); + break; + } + if (err>0) + if (fwrite(buf,err,1,fout)!=1) + { + printf("error in writing extracted file\n"); + err=UNZ_ERRNO; + break; + } + } + while (err>0); + if (fout) + fclose(fout); + + if (err==0) + change_file_date(write_filename,file_info.dosDate, + file_info.tmu_date); + } + + if (err==UNZ_OK) + { + err = unzCloseCurrentFile (uf); + if (err!=UNZ_OK) + { + printf("error %d with zipfile in unzCloseCurrentFile\n",err); + } + } + else + unzCloseCurrentFile(uf); /* don't lose the error */ + } + + free(buf); + return err; +} + + +int do_extract(uf,opt_extract_without_path,opt_overwrite,password) + unzFile uf; + int opt_extract_without_path; + int opt_overwrite; + const char* password; +{ + uLong i; + unz_global_info64 gi; + int err; + FILE* fout=NULL; + + err = unzGetGlobalInfo64(uf,&gi); + if (err!=UNZ_OK) + printf("error %d with zipfile in unzGetGlobalInfo \n",err); + + for (i=0;i +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include +#else +# include +# include +# include +# include +#endif + +#include "zip.h" + +#ifdef _WIN32 + #define USEWIN32IOAPI + #include "iowin32.h" +#endif + + + +#define WRITEBUFFERSIZE (16384) +#define MAXFILENAME (256) + +#ifdef _WIN32 +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + int ret = 0; + { + FILETIME ftLocal; + HANDLE hFind; + WIN32_FIND_DATAA ff32; + + hFind = FindFirstFileA(f,&ff32); + if (hFind != INVALID_HANDLE_VALUE) + { + FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal); + FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0); + FindClose(hFind); + ret = 1; + } + } + return ret; +} +#else +#ifdef unix || __APPLE__ +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + int ret=0; + struct stat s; /* results of stat() */ + struct tm* filedate; + time_t tm_t=0; + + if (strcmp(f,"-")!=0) + { + char name[MAXFILENAME+1]; + int len = strlen(f); + if (len > MAXFILENAME) + len = MAXFILENAME; + + strncpy(name, f,MAXFILENAME-1); + /* strncpy doesnt append the trailing NULL, of the string is too long. */ + name[ MAXFILENAME ] = '\0'; + + if (name[len - 1] == '/') + name[len - 1] = '\0'; + /* not all systems allow stat'ing a file with / appended */ + if (stat(name,&s)==0) + { + tm_t = s.st_mtime; + ret = 1; + } + } + filedate = localtime(&tm_t); + + tmzip->tm_sec = filedate->tm_sec; + tmzip->tm_min = filedate->tm_min; + tmzip->tm_hour = filedate->tm_hour; + tmzip->tm_mday = filedate->tm_mday; + tmzip->tm_mon = filedate->tm_mon ; + tmzip->tm_year = filedate->tm_year; + + return ret; +} +#else +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + return 0; +} +#endif +#endif + + + + +int check_exist_file(filename) + const char* filename; +{ + FILE* ftestexist; + int ret = 1; + ftestexist = FOPEN_FUNC(filename,"rb"); + if (ftestexist==NULL) + ret = 0; + else + fclose(ftestexist); + return ret; +} + +void do_banner() +{ + printf("MiniZip 1.1, demo of zLib + MiniZip64 package, written by Gilles Vollant\n"); + printf("more info on MiniZip at http://www.winimage.com/zLibDll/minizip.html\n\n"); +} + +void do_help() +{ + printf("Usage : minizip [-o] [-a] [-0 to -9] [-p password] [-j] file.zip [files_to_add]\n\n" \ + " -o Overwrite existing file.zip\n" \ + " -a Append to existing file.zip\n" \ + " -0 Store only\n" \ + " -1 Compress faster\n" \ + " -9 Compress better\n\n" \ + " -j exclude path. store only the file name.\n\n"); +} + +/* calculate the CRC32 of a file, + because to encrypt a file, we need known the CRC32 of the file before */ +int getFileCrc(const char* filenameinzip,void*buf,unsigned long size_buf,unsigned long* result_crc) +{ + unsigned long calculate_crc=0; + int err=ZIP_OK; + FILE * fin = FOPEN_FUNC(filenameinzip,"rb"); + + unsigned long size_read = 0; + unsigned long total_read = 0; + if (fin==NULL) + { + err = ZIP_ERRNO; + } + + if (err == ZIP_OK) + do + { + err = ZIP_OK; + size_read = (int)fread(buf,1,size_buf,fin); + if (size_read < size_buf) + if (feof(fin)==0) + { + printf("error in reading %s\n",filenameinzip); + err = ZIP_ERRNO; + } + + if (size_read>0) + calculate_crc = crc32(calculate_crc,buf,size_read); + total_read += size_read; + + } while ((err == ZIP_OK) && (size_read>0)); + + if (fin) + fclose(fin); + + *result_crc=calculate_crc; + printf("file %s crc %lx\n", filenameinzip, calculate_crc); + return err; +} + +int isLargeFile(const char* filename) +{ + int largeFile = 0; + ZPOS64_T pos = 0; + FILE* pFile = FOPEN_FUNC(filename, "rb"); + + if(pFile != NULL) + { + int n = FSEEKO_FUNC(pFile, 0, SEEK_END); + pos = FTELLO_FUNC(pFile); + + printf("File : %s is %lld bytes\n", filename, pos); + + if(pos >= 0xffffffff) + largeFile = 1; + + fclose(pFile); + } + + return largeFile; +} + +int main(argc,argv) + int argc; + char *argv[]; +{ + int i; + int opt_overwrite=0; + int opt_compress_level=Z_DEFAULT_COMPRESSION; + int opt_exclude_path=0; + int zipfilenamearg = 0; + char filename_try[MAXFILENAME+16]; + int zipok; + int err=0; + int size_buf=0; + void* buf=NULL; + const char* password=NULL; + + + do_banner(); + if (argc==1) + { + do_help(); + return 0; + } + else + { + for (i=1;i='0') && (c<='9')) + opt_compress_level = c-'0'; + if ((c=='j') || (c=='J')) + opt_exclude_path = 1; + + if (((c=='p') || (c=='P')) && (i+1='a') && (rep<='z')) + rep -= 0x20; + } + while ((rep!='Y') && (rep!='N') && (rep!='A')); + if (rep=='N') + zipok = 0; + if (rep=='A') + opt_overwrite = 2; + } + } + + if (zipok==1) + { + zipFile zf; + int errclose; +# ifdef USEWIN32IOAPI + zlib_filefunc64_def ffunc; + fill_win32_filefunc64A(&ffunc); + zf = zipOpen2_64(filename_try,(opt_overwrite==2) ? 2 : 0,NULL,&ffunc); +# else + zf = zipOpen64(filename_try,(opt_overwrite==2) ? 2 : 0); +# endif + + if (zf == NULL) + { + printf("error opening %s\n",filename_try); + err= ZIP_ERRNO; + } + else + printf("creating %s\n",filename_try); + + for (i=zipfilenamearg+1;(i='0') || (argv[i][1]<='9'))) && + (strlen(argv[i]) == 2))) + { + FILE * fin; + int size_read; + const char* filenameinzip = argv[i]; + const char *savefilenameinzip; + zip_fileinfo zi; + unsigned long crcFile=0; + int zip64 = 0; + + zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour = + zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0; + zi.dosDate = 0; + zi.internal_fa = 0; + zi.external_fa = 0; + filetime(filenameinzip,&zi.tmz_date,&zi.dosDate); + +/* + err = zipOpenNewFileInZip(zf,filenameinzip,&zi, + NULL,0,NULL,0,NULL / * comment * /, + (opt_compress_level != 0) ? Z_DEFLATED : 0, + opt_compress_level); +*/ + if ((password != NULL) && (err==ZIP_OK)) + err = getFileCrc(filenameinzip,buf,size_buf,&crcFile); + + zip64 = isLargeFile(filenameinzip); + + /* The path name saved, should not include a leading slash. */ + /*if it did, windows/xp and dynazip couldn't read the zip file. */ + savefilenameinzip = filenameinzip; + while( savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/' ) + { + savefilenameinzip++; + } + + /*should the zip file contain any path at all?*/ + if( opt_exclude_path ) + { + const char *tmpptr; + const char *lastslash = 0; + for( tmpptr = savefilenameinzip; *tmpptr; tmpptr++) + { + if( *tmpptr == '\\' || *tmpptr == '/') + { + lastslash = tmpptr; + } + } + if( lastslash != NULL ) + { + savefilenameinzip = lastslash+1; // base filename follows last slash. + } + } + + /**/ + err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi, + NULL,0,NULL,0,NULL /* comment*/, + (opt_compress_level != 0) ? Z_DEFLATED : 0, + opt_compress_level,0, + /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */ + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + password,crcFile, zip64); + + if (err != ZIP_OK) + printf("error in opening %s in zipfile\n",filenameinzip); + else + { + fin = FOPEN_FUNC(filenameinzip,"rb"); + if (fin==NULL) + { + err=ZIP_ERRNO; + printf("error in opening %s for reading\n",filenameinzip); + } + } + + if (err == ZIP_OK) + do + { + err = ZIP_OK; + size_read = (int)fread(buf,1,size_buf,fin); + if (size_read < size_buf) + if (feof(fin)==0) + { + printf("error in reading %s\n",filenameinzip); + err = ZIP_ERRNO; + } + + if (size_read>0) + { + err = zipWriteInFileInZip (zf,buf,size_read); + if (err<0) + { + printf("error in writing %s in the zipfile\n", + filenameinzip); + } + + } + } while ((err == ZIP_OK) && (size_read>0)); + + if (fin) + fclose(fin); + + if (err<0) + err=ZIP_ERRNO; + else + { + err = zipCloseFileInZip(zf); + if (err!=ZIP_OK) + printf("error in closing %s in the zipfile\n", + filenameinzip); + } + } + } + errclose = zipClose(zf,NULL); + if (errclose != ZIP_OK) + printf("error in closing %s\n",filename_try); + } + else + { + do_help(); + } + + free(buf); + return 0; +} diff --git a/src/minizip/mztools.c b/src/minizip/mztools.c new file mode 100644 index 0000000..96891c2 --- /dev/null +++ b/src/minizip/mztools.c @@ -0,0 +1,291 @@ +/* + Additional tools for Minizip + Code: Xavier Roche '2004 + License: Same as ZLIB (www.gzip.org) +*/ + +/* Code */ +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#define READ_8(adr) ((unsigned char)*(adr)) +#define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) ) +#define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) ) + +#define WRITE_8(buff, n) do { \ + *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \ +} while(0) +#define WRITE_16(buff, n) do { \ + WRITE_8((unsigned char*)(buff), n); \ + WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \ +} while(0) +#define WRITE_32(buff, n) do { \ + WRITE_16((unsigned char*)(buff), (n) & 0xffff); \ + WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \ +} while(0) + +extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered) +const char* file; +const char* fileOut; +const char* fileOutTmp; +uLong* nRecovered; +uLong* bytesRecovered; +{ + int err = Z_OK; + FILE* fpZip = fopen(file, "rb"); + FILE* fpOut = fopen(fileOut, "wb"); + FILE* fpOutCD = fopen(fileOutTmp, "wb"); + if (fpZip != NULL && fpOut != NULL) { + int entries = 0; + uLong totalBytes = 0; + char header[30]; + char filename[1024]; + char extra[1024]; + int offset = 0; + int offsetCD = 0; + while ( fread(header, 1, 30, fpZip) == 30 ) { + int currentOffset = offset; + + /* File entry */ + if (READ_32(header) == 0x04034b50) { + unsigned int version = READ_16(header + 4); + unsigned int gpflag = READ_16(header + 6); + unsigned int method = READ_16(header + 8); + unsigned int filetime = READ_16(header + 10); + unsigned int filedate = READ_16(header + 12); + unsigned int crc = READ_32(header + 14); /* crc */ + unsigned int cpsize = READ_32(header + 18); /* compressed size */ + unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */ + unsigned int fnsize = READ_16(header + 26); /* file name length */ + unsigned int extsize = READ_16(header + 28); /* extra field length */ + filename[0] = extra[0] = '\0'; + + /* Header */ + if (fwrite(header, 1, 30, fpOut) == 30) { + offset += 30; + } else { + err = Z_ERRNO; + break; + } + + /* Filename */ + if (fnsize > 0) { + if (fnsize < sizeof(filename)) { + if (fread(filename, 1, fnsize, fpZip) == fnsize) { + if (fwrite(filename, 1, fnsize, fpOut) == fnsize) { + offset += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (extsize < sizeof(extra)) { + if (fread(extra, 1, extsize, fpZip) == extsize) { + if (fwrite(extra, 1, extsize, fpOut) == extsize) { + offset += extsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } + + /* Data */ + { + int dataSize = cpsize; + if (dataSize == 0) { + dataSize = uncpsize; + } + if (dataSize > 0) { + char* data = malloc(dataSize); + if (data != NULL) { + if ((int)fread(data, 1, dataSize, fpZip) == dataSize) { + if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) { + offset += dataSize; + totalBytes += dataSize; + } else { + err = Z_ERRNO; + } + } else { + err = Z_ERRNO; + } + free(data); + if (err != Z_OK) { + break; + } + } else { + err = Z_MEM_ERROR; + break; + } + } + } + + /* Central directory entry */ + { + char header[46]; + char* comment = ""; + int comsize = (int) strlen(comment); + WRITE_32(header, 0x02014b50); + WRITE_16(header + 4, version); + WRITE_16(header + 6, version); + WRITE_16(header + 8, gpflag); + WRITE_16(header + 10, method); + WRITE_16(header + 12, filetime); + WRITE_16(header + 14, filedate); + WRITE_32(header + 16, crc); + WRITE_32(header + 20, cpsize); + WRITE_32(header + 24, uncpsize); + WRITE_16(header + 28, fnsize); + WRITE_16(header + 30, extsize); + WRITE_16(header + 32, comsize); + WRITE_16(header + 34, 0); /* disk # */ + WRITE_16(header + 36, 0); /* int attrb */ + WRITE_32(header + 38, 0); /* ext attrb */ + WRITE_32(header + 42, currentOffset); + /* Header */ + if (fwrite(header, 1, 46, fpOutCD) == 46) { + offsetCD += 46; + + /* Filename */ + if (fnsize > 0) { + if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) { + offsetCD += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (fwrite(extra, 1, extsize, fpOutCD) == extsize) { + offsetCD += extsize; + } else { + err = Z_ERRNO; + break; + } + } + + /* Comment field */ + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) { + offsetCD += comsize; + } else { + err = Z_ERRNO; + break; + } + } + + + } else { + err = Z_ERRNO; + break; + } + } + + /* Success */ + entries++; + + } else { + break; + } + } + + /* Final central directory */ + { + int entriesZip = entries; + char header[22]; + char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools"; + int comsize = (int) strlen(comment); + if (entriesZip > 0xffff) { + entriesZip = 0xffff; + } + WRITE_32(header, 0x06054b50); + WRITE_16(header + 4, 0); /* disk # */ + WRITE_16(header + 6, 0); /* disk # */ + WRITE_16(header + 8, entriesZip); /* hack */ + WRITE_16(header + 10, entriesZip); /* hack */ + WRITE_32(header + 12, offsetCD); /* size of CD */ + WRITE_32(header + 16, offset); /* offset to CD */ + WRITE_16(header + 20, comsize); /* comment */ + + /* Header */ + if (fwrite(header, 1, 22, fpOutCD) == 22) { + + /* Comment field */ + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) { + err = Z_ERRNO; + } + } + + } else { + err = Z_ERRNO; + } + } + + /* Final merge (file + central directory) */ + fclose(fpOutCD); + if (err == Z_OK) { + fpOutCD = fopen(fileOutTmp, "rb"); + if (fpOutCD != NULL) { + int nRead; + char buffer[8192]; + while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) { + if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) { + err = Z_ERRNO; + break; + } + } + fclose(fpOutCD); + } + } + + /* Close */ + fclose(fpZip); + fclose(fpOut); + + /* Wipe temporary file */ + (void)remove(fileOutTmp); + + /* Number of recovered entries */ + if (err == Z_OK) { + if (nRecovered != NULL) { + *nRecovered = entries; + } + if (bytesRecovered != NULL) { + *bytesRecovered = totalBytes; + } + } + } else { + err = Z_STREAM_ERROR; + } + return err; +} diff --git a/src/minizip/mztools.h b/src/minizip/mztools.h new file mode 100644 index 0000000..a49a426 --- /dev/null +++ b/src/minizip/mztools.h @@ -0,0 +1,37 @@ +/* + Additional tools for Minizip + Code: Xavier Roche '2004 + License: Same as ZLIB (www.gzip.org) +*/ + +#ifndef _zip_tools_H +#define _zip_tools_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#include "unzip.h" + +/* Repair a ZIP file (missing central directory) + file: file to recover + fileOut: output file after recovery + fileOutTmp: temporary file name used for recovery +*/ +extern int ZEXPORT unzRepair(const char* file, + const char* fileOut, + const char* fileOutTmp, + uLong* nRecovered, + uLong* bytesRecovered); + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/minizip/unzip.c b/src/minizip/unzip.c new file mode 100644 index 0000000..9093504 --- /dev/null +++ b/src/minizip/unzip.c @@ -0,0 +1,2125 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + + ------------------------------------------------------------------------------------ + Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of + compatibility with older software. The following is from the original crypt.c. + Code woven in by Terry Thorsen 1/2003. + + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html + + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + ------------------------------------------------------------------------------------ + + Changes in unzip.c + + 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos + 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* + 2007-2008 - Even Rouault - Remove old C style function prototypes + 2007-2008 - Even Rouault - Add unzip support for ZIP64 + + Copyright (C) 2007-2008 Even Rouault + + + Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). + Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G + should only read the compressed/uncompressed size from the Zip64 format if + the size from normal header was 0xFFFFFFFF + Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant + Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) + Patch created by Daniel Borca + + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + + Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson + +*/ + + +#include +#include +#include + +#ifndef NOUNCRYPT + #define NOUNCRYPT +#endif + +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info64_internal_s +{ + ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ +} unz_file_info64_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ + ZPOS64_T total_out_64; + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ + ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip64_read_info_s; + + +/* unz64_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + int is64bitOpenFunction; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info64 gi; /* public global information */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + ZPOS64_T num_file; /* number of the current file in the zipfile*/ + ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ + ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ + ZPOS64_T central_pos; /* position of the beginning of the central dir*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info64 cur_file_info; /* public info about the current file in zip*/ + unz_file_info64_internal cur_file_info_internal; /* private info about it*/ + file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; + + int isZip64; + +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const z_crc_t* pcrc_32_tab; +# endif +} unz64_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unz64local_getByte OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unz64local_getShort OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX)); + + +local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX) +{ + ZPOS64_T x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<24; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<32; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<40; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<48; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<56; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, + const char* fileName2, + int iCaseSensitivity) + +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); +local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + + +/* + Locate the Central directory 64 of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream)); + +local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) + return 0; + + /* total number of disks */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + if (uL != 0x06064b50) + return 0; + + return relativeOffset; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +local unzFile unzOpenInternal (const void *path, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def, + int is64bitOpenFunction) +{ + unz64_s us; + unz64_s *s; + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + us.z_filefunc.zseek32_file = NULL; + us.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); + else + us.z_filefunc = *pzlib_filefunc64_32_def; + us.is64bitOpenFunction = is64bitOpenFunction; + + + + us.filestream = ZOPEN64(us.z_filefunc, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); + if (central_pos) + { + uLong uS; + ZPOS64_T uL64; + + us.isZip64 = 1; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* size of zip64 end of central directory record */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version made by */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version needed to extract */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + us.gi.size_comment = 0; + } + else + { + central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + us.isZip64 = 0; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.gi.number_entry = uL; + + /* total number of entries in the central dir */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + number_entry_CD = uL; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.size_central_dir = uL; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.offset_central_dir = uL; + + /* zipfile comment length */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + } + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE64(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + /* to do : check if number_entry is not truncated */ + pglobal_info32->number_entry = (uLong)s->gi.number_entry; + pglobal_info32->size_comment = s->gi.size_comment; + return UNZ_OK; +} +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) +{ + ZPOS64_T uDate; + uDate = (ZPOS64_T)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unz64local_GetCurrentFileInfoInternal (unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz64_s* s; + unz_file_info64 file_info; + unz_file_info64_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + uLong uL; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.compressed_size = uL; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.uncompressed_size = uL; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + // relative offset of local header + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info_internal.offset_curfile = uL; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + // Read extrafield + if ((err==UNZ_OK) && (extraField!=NULL)) + { + ZPOS64_T uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + + lSeek += file_info.size_file_extra - (uLong)uSizeRead; + } + else + lSeek += file_info.size_file_extra; + + + if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) + { + uLong acc = 0; + + // since lSeek now points to after the extra field we need to move back + lSeek -= file_info.size_file_extra; + + if (lSeek!=0) + { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + while(acc < file_info.size_file_extra) + { + uLong headerId; + uLong dataSize; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) + err=UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { + uLong uL; + + if(file_info.uncompressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.compressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info_internal.offset_curfile == MAXU32) + { + /* Relative Header offset */ + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.disk_num_start == MAXU32) + { + /* Disk Start Number */ + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + } + + } + else + { + if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + + acc += 2 + 2 + dataSize; + } + } + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, + unz_file_info64 * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + int err; + unz_file_info64 file_info64; + err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); + if ((err==UNZ_OK) && (pfile_info != NULL)) + { + pfile_info->version = file_info64.version; + pfile_info->version_needed = file_info64.version_needed; + pfile_info->flag = file_info64.flag; + pfile_info->compression_method = file_info64.compression_method; + pfile_info->dosDate = file_info64.dosDate; + pfile_info->crc = file_info64.crc; + + pfile_info->size_filename = file_info64.size_filename; + pfile_info->size_file_extra = file_info64.size_file_extra; + pfile_info->size_file_comment = file_info64.size_file_comment; + + pfile_info->disk_num_start = file_info64.disk_num_start; + pfile_info->internal_fa = file_info64.internal_fa; + pfile_info->external_fa = file_info64.external_fa; + + pfile_info->tmu_date = file_info64.tmu_date, + + + pfile_info->compressed_size = (uLong)file_info64.compressed_size; + pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; + + } + return err; +} +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz64_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info64 cur_file_infoSaved; + unz_file_info64_internal cur_file_info_internalSaved; + ZPOS64_T num_fileSaved; + ZPOS64_T pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo64(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; // offset in file + ZPOS64_T num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) +{ + unz64_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + int err = unzGetFilePos64(file,&file_pos64); + if (err==UNZ_OK) + { + file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; + file_pos->num_of_file = (uLong)file_pos64.num_of_file; + } + return err; +} + +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) +{ + unz64_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + if (file_pos == NULL) + return UNZ_PARAMERROR; + + file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; + file_pos64.num_of_file = file_pos->num_of_file; + return unzGoToFilePos64(file,&file_pos64); +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, + ZPOS64_T * poffset_local_extrafield, + uInt * psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, + int* level, int raw, const char* password) +{ + int err=UNZ_OK; + uInt iSizeVar; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->total_out_64=0; + pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) + { +#ifdef HAVE_BZIP2 + pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; + pfile_in_zip_read_info->bstream.bzfree = (free_func)0; + pfile_in_zip_read_info->bstream.opaque = (voidpf)0; + pfile_in_zip_read_info->bstream.state = (voidpf)0; + + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } +#else + pfile_in_zip_read_info->raw=1; +#endif + } + else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = 0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + s->encrypted = 0; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + s=(unz64_s*)file; + if (file==NULL) + return 0; //UNZ_PARAMERROR; + pfile_in_zip_read_info=s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) + return 0; //UNZ_PARAMERROR; + return pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile; +} + +/** Addition for GDAL : END */ + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->read_buffer == NULL) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + + pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; + pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; + pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; + pfile_in_zip_read_info->bstream.total_in_hi32 = 0; + pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; + pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; + pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; + pfile_in_zip_read_info->bstream.total_out_hi32 = 0; + + uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; + bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; + + err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); + + uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; + pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; + pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; + pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; + pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; + pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; + + if (err==BZ_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=BZ_OK) + break; +#endif + } // end Z_BZIP2ED + else + { + ZPOS64_T uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + ZPOS64_T uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + +extern ZPOS64_T ZEXPORT unztell64 (unzFile file) +{ + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return (ZPOS64_T)-1; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return (ZPOS64_T)-1; + + return pfile_in_zip_read_info->total_out_64; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* +Read extra field from the current file (opened by unzOpenCurrentFile) +This is the local-header version of the extra field (sometimes, there is +more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + uInt read_now; + ZPOS64_T size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + inflateEnd(&pfile_in_zip_read_info->stream); +#ifdef HAVE_BZIP2 + else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) + BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); +#endif + + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) +{ + unz64_s* s; + uLong uReadThis ; + if (file==NULL) + return (int)UNZ_PARAMERROR; + s=(unz64_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) +{ + unz64_s* s; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern uLong ZEXPORT unzGetOffset (unzFile file) +{ + ZPOS64_T offset64; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + offset64 = unzGetOffset64(file); + return (uLong)offset64; +} + +extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) +{ + return unzSetOffset64(file,pos); +} diff --git a/src/minizip/unzip.h b/src/minizip/unzip.h new file mode 100644 index 0000000..2104e39 --- /dev/null +++ b/src/minizip/unzip.h @@ -0,0 +1,437 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------------- + + Changes + + See header of unzip64.c + +*/ + +#ifndef _unz64_H +#define _unz64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info64_s +{ + ZPOS64_T number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info64; + +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info64_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + ZPOS64_T compressed_size; /* compressed size 8 bytes */ + ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info64; + +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +extern unzFile ZEXPORT unzOpen64 OF((const void *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. + the "64" function take a const void* pointer, because the path is just the + value passed to the open64_file_func callback. + Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* + does not describe the reality +*/ + + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, + zlib_filefunc64_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unz64Open, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzCloseCurrentFile before call unzClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); + +extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, + unz_global_info64 *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +typedef struct unz64_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ + ZPOS64_T num_of_file; /* # of file */ +} unz64_file_pos; + +extern int ZEXPORT unzGetFilePos64( + unzFile file, + unz64_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos64( + unzFile file, + const unz64_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, + unz_file_info64 *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); + +/** Addition for GDAL : END */ + + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); + +extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz64_H */ diff --git a/src/minizip/zip.c b/src/minizip/zip.c new file mode 100644 index 0000000..337bcd5 --- /dev/null +++ b/src/minizip/zip.c @@ -0,0 +1,2007 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + Oct-2009 - Mathias Svensson - Remove old C style function prototypes + Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives + Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. + Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data + It is used when recreting zip archive with RAW when deleting items from a zip. + ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed. + Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + +*/ + + +#include +#include +#include +#include +#include "zlib.h" +#include "zip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (64*1024) //(16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + + +// NOT sure that this work on ALL platform +#define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32)) + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) +#define ZIP64ENDHEADERMAGIC (0x6064b50) +#define ZIP64ENDLOCHEADERMAGIC (0x7064b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignement */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + ZPOS64_T pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralExtra; + uLong size_centralheader; /* size of the central header for cur file */ + uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; + int zip64; /* Add ZIP64 extened information in the extra field */ + ZPOS64_T pos_zip64extrainfo; + ZPOS64_T totalCompressedData; + ZPOS64_T totalUncompressedData; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const z_crc_t* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile64_info; + +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile64_info ci; /* info on the file curretly writing */ + + ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ + ZPOS64_T add_position_when_writting_offset; + ZPOS64_T number_entry; + +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif + +} zip64_internal; + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(linkedlist_datablock_internal* ldi) +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(linkedlist_data* ll) +{ + ll->first_block = ll->last_block = NULL; +} + +local void free_linkedlist(linkedlist_data* ll) +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} + + +local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;ifilled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) +*/ + +local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); +local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) +{ + unsigned char buf[8]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); +local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) +{ + uLong year = (uLong)ptm->tm_year; + if (year>=1980) + year-=1980; + else if (year>=80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); + +local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); + + +local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) +{ + ZPOS64_T x; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<24; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<32; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<40; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<48; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<56; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* +Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before +the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + { + // Signature "0x07064b50" Zip64 end of central directory locater + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + } + + if (uPosFound!=0) + break; + } + + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK) + return 0; + + /* total number of disks */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto Zip64 end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + if (uL != 0x06064b50) // signature of 'Zip64 end of central directory' + return 0; + + return relativeOffset; +} + +int LoadCentralDirectoryRecord(zip64_internal* pziinit) +{ + int err=ZIP_OK; + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory */ + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry; + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong VersionMadeBy; + uLong VersionNeeded; + uLong size_comment; + + int hasZIP64Record = 0; + + // check first if we find a ZIP64 record + central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream); + if(central_pos > 0) + { + hasZIP64Record = 1; + } + else if(central_pos == 0) + { + central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream); + } + +/* disable to allow appending to empty ZIP archive + if (central_pos==0) + err=ZIP_ERRNO; +*/ + + if(hasZIP64Record) + { + ZPOS64_T sizeEndOfCentralDirectory; + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* size of zip64 end of central directory record */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version made by */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version needed to extract */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + // TODO.. + // read the comment from the standard central header. + size_comment = 0; + } + else + { + // Read End of central Directory info + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + number_entry = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry = uL; + + /* total number of entries in the central dir */ + number_entry_CD = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry_CD = uL; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + size_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + size_central_dir = uL; + + /* offset of start of central directory with respect to the starting disk number */ + offset_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + offset_central_dir = uL; + + + /* zipfile global comment length */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + } + + if ((central_posz_filefunc, pziinit->filestream); + return ZIP_ERRNO; + } + + if (size_comment>0) + { + pziinit->globalcomment = (char*)ALLOC(size_comment+1); + if (pziinit->globalcomment) + { + size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment); + pziinit->globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); + pziinit->add_position_when_writting_offset = byte_before_the_zipfile; + + { + ZPOS64_T size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + ZPOS64_T read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + + if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this); + + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + pziinit->begin_pos = byte_before_the_zipfile; + pziinit->number_entry = number_entry_CD; + + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + return err; +} + + +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + + +/************************************************************/ +extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) +{ + zip64_internal ziinit; + zip64_internal* zi; + int err=ZIP_OK; + + ziinit.z_filefunc.zseek32_file = NULL; + ziinit.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64); + else + ziinit.z_filefunc = *pzlib_filefunc64_32_def; + + ziinit.filestream = ZOPEN64(ziinit.z_filefunc, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + + if (append == APPEND_STATUS_CREATEAFTER) + ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); + + ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writting_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + + + zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); + if (zi==NULL) + { + ZCLOSE64(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + // Read and Cache Central Directory Records + err = LoadCentralDirectoryRecord(&ziinit); + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + zlib_filefunc64_32_def_fill.ztell32_file = NULL; + zlib_filefunc64_32_def_fill.zseek32_file = NULL; + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + + + +extern zipFile ZEXPORT zipOpen (const char* pathname, int append) +{ + return zipOpen3((const void*)pathname,append,NULL,NULL); +} + +extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) +{ + return zipOpen3(pathname,append,NULL,NULL); +} + +int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) +{ + /* write the local header */ + int err; + uInt size_filename = (uInt)strlen(filename); + uInt size_extrafield = size_extrafield_local; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4); + + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + // CRC / Compressed size / Uncompressed size will be filled in later and rewritten later + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + } + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if(zi->ci.zip64) + { + size_extrafield += 20; + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2); + + if ((err==ZIP_OK) && (size_filename > 0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + } + + if ((err==ZIP_OK) && (size_extrafield_local > 0)) + { + if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) + err = ZIP_ERRNO; + } + + + if ((err==ZIP_OK) && (zi->ci.zip64)) + { + // write the Zip64 extended info + short HeaderID = 1; + short DataSize = 16; + ZPOS64_T CompressedSize = 0; + ZPOS64_T UncompressedSize = 0; + + // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) + zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); + } + + return err; +} + +/* + NOTE. + When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped + before calling this function it can be done with zipRemoveExtraInfoBlock + + It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize + unnecessary allocations. + */ +extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase, int zip64) +{ + zip64_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + (void)(crcForCrypting); + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + +#ifdef HAVE_BZIP2 + if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED)) + return ZIP_PARAMERROR; +#else + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; +#endif + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else + zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date); + } + + zi->ci.flag = flagBase; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if (level==2) + zi->ci.flag |= 4; + if (level==1) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream); + + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; + zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data + + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); + + zi->ci.size_centralExtra = size_extrafield_global; + zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + if(zi->ci.pos_local_header >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4); + + for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + zi->ci.zip64 = zip64; + zi->ci.totalCompressedData = 0; + zi->ci.totalUncompressedData = 0; + zi->ci.pos_zip64extrainfo = 0; + + err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local); + +#ifdef HAVE_BZIP2 + zi->ci.bstream.avail_in = (uInt)0; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + zi->ci.bstream.total_in_hi32 = 0; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_out_hi32 = 0; + zi->ci.bstream.total_out_lo32 = 0; +#endif + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + zi->ci.stream.data_type = Z_BINARY; + +#ifdef HAVE_BZIP2 + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) +#else + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) +#endif + { + if(zi->ci.method == Z_DEFLATED) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = Z_DEFLATED; + } + else if(zi->ci.method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + // Init BZip stuff here + zi->ci.bstream.bzalloc = 0; + zi->ci.bstream.bzfree = 0; + zi->ci.bstream.opaque = (voidpf)0; + + err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35); + if(err == BZ_OK) + zi->ci.stream_initialised = Z_BZIP2ED; +#endif + } + + } + +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, versionMadeBy, flagBase, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +local int zip64FlushWriteBuffer(zip64_internal* zi) +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;ici.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); +#endif + } + + if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + + zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data; + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED) + { + zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_in_hi32 = 0; + } + else +#endif + { + zi->ci.totalUncompressedData += zi->ci.stream.total_in; + zi->ci.stream.total_in = 0; + } + + + zi->ci.pos_in_buffered_data = 0; + + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) +{ + zip64_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw)) + { + zi->ci.bstream.next_in = (void*)buf; + zi->ci.bstream.avail_in = len; + err = BZ_RUN_OK; + + while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0)) + { + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + + + if(err != BZ_RUN_OK) + break; + + if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32; +// uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ; + } + } + + if(err == BZ_RUN_OK) + err = ZIP_OK; + } + else +#endif + { + zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.avail_in = len; + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + if(uTotalOutBefore > zi->ci.stream.total_out) + { + int bBreak = 0; + bBreak++; + } + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + + for (i = 0; i < copy_this; i++) + *(((char*)zi->ci.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + }// while(...) + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) +{ + return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); +} + +extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) +{ + zip64_internal* zi; + ZPOS64_T compressed_size; + uLong invalidValue = 0xffffffff; + short datasize = 0; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + } + else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { +#ifdef HAVE_BZIP2 + err = BZ_FINISH_OK; + while (err==BZ_FINISH_OK) + { + uLong uTotalOutBefore; + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.bstream.total_out_lo32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); + if(err == BZ_STREAM_END) + err = Z_STREAM_END; + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore); + } + + if(err == BZ_FINISH_OK) + err = ZIP_OK; +#endif + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + { + if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + } + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + int tmp_err = deflateEnd(&zi->ci.stream); + if (err == ZIP_OK) + err = tmp_err; + zi->ci.stream_initialised = 0; + } +#ifdef HAVE_BZIP2 + else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); + if (err==ZIP_OK) + err = tmperr; + zi->ci.stream_initialised = 0; + } +#endif + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = zi->ci.totalUncompressedData; + } + compressed_size = zi->ci.totalCompressedData; + +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + // update Current Item crc and sizes, + if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff) + { + /*version Made by*/ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); + /*version needed*/ + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); + + } + + zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + + + if(compressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ + + /// set internal file attributes field + if (zi->ci.stream.data_type == Z_ASCII) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + + if(uncompressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ + + // Add ZIP64 extra info field for uncompressed size + if(uncompressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for compressed size + if(compressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for relative offset to local file header of current file + if(zi->ci.pos_local_header >= 0xffffffff) + datasize += 8; + + if(datasize > 0) + { + char* p = NULL; + + if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) + { + // we can not write more data to the buffer that we have room for. + return ZIP_BADZIPFILE; + } + + p = zi->ci.central_header + zi->ci.size_centralheader; + + // Add Extra Information Header for 'ZIP64 information' + zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID + p += 2; + zip64local_putValue_inmemory(p, datasize, 2); // DataSize + p += 2; + + if(uncompressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, uncompressed_size, 8); + p += 8; + } + + if(compressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, compressed_size, 8); + p += 8; + } + + if(zi->ci.pos_local_header >= 0xffffffff) + { + zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8); + p += 8; + } + + // Update how much extra free space we got in the memory buffer + // and increase the centralheader size so the new ZIP64 fields are included + // ( 4 below is the size of HeaderID and DataSize field ) + zi->ci.size_centralExtraFree -= datasize + 4; + zi->ci.size_centralheader += datasize + 4; + + // Update the extra info size field + zi->ci.size_centralExtra += datasize + 4; + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2); + } + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader); + + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + // Update the LocalFileHeader with the new values. + + ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if(uncompressed_size >= 0xffffffff || compressed_size >= 0xffffffff ) + { + if(zi->ci.pos_zip64extrainfo > 0) + { + // Update the size in the ZIP64 extended field. + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8); + } + else + err = ZIP_BADZIPFILE; // Caller passed zip64 = 0, so no room for zip64 info -> fatal + } + else + { + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + } + + if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (zipFile file) +{ + return zipCloseFileInZipRaw (file,0,0); +} + +int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) +{ + int err = ZIP_OK; + ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); + + /*num disks*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + /*relative offset*/ + if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8); + + /*total disks*/ /* Do not support spawning of disk so always say 1 here*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4); + + return err; +} + +int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + uLong Zip64DataSize = 44; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ? + + if (err==ZIP_OK) /* version made by */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* version needed */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); + } + return err; +} +int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + /*signature*/ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + { + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + } + + if (err==ZIP_OK) /* total number of entries in the central dir */ + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff) + { + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); + } + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + } + + return err; +} + +int Write_GlobalComment(zip64_internal* zi, const char* global_comment) +{ + int err = ZIP_OK; + uInt size_global_comment = 0; + + if(global_comment != NULL) + size_global_comment = (uInt)strlen(global_comment); + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if (err == ZIP_OK && size_global_comment > 0) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + } + return err; +} + +extern int ZEXPORT zipClose (zipFile file, const char* global_comment) +{ + zip64_internal* zi; + int err = 0; + uLong size_centraldir = 0; + ZPOS64_T centraldir_pos_inzip; + ZPOS64_T pos; + + if (file == NULL) + return ZIP_PARAMERROR; + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + + centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block) + err = ZIP_ERRNO; + } + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_linkedlist(&(zi->central_dir)); + + pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff || zi->number_entry > 0xFFFF) + { + ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); + Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos); + } + + if (err==ZIP_OK) + err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + if(err == ZIP_OK) + err = Write_GlobalComment(zi, global_comment); + + if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} + +extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) +{ + char* p = pData; + int size = 0; + char* pNewHeader; + char* pTmp; + short header; + short dataSize; + + int retVal = ZIP_OK; + + if(pData == NULL || *dataLen < 4) + return ZIP_PARAMERROR; + + pNewHeader = (char*)ALLOC(*dataLen); + pTmp = pNewHeader; + + while(p < (pData + *dataLen)) + { + header = *(short*)p; + dataSize = *(((short*)p)+1); + + if( header == sHeader ) // Header found. + { + p += dataSize + 4; // skip it. do not copy to temp buffer + } + else + { + // Extra Info block should not be removed, So copy it to the temp buffer. + memcpy(pTmp, p, dataSize + 4); + p += dataSize + 4; + size += dataSize + 4; + } + + } + + if(size < *dataLen) + { + // clean old extra info block. + memset(pData,0, *dataLen); + + // copy the new extra info block over the old + if(size > 0) + memcpy(pData, pNewHeader, size); + + // set the new extra info size + *dataLen = size; + + retVal = ZIP_OK; + } + else + retVal = ZIP_ERRNO; + + TRYFREE(pNewHeader); + + return retVal; +} diff --git a/src/minizip/zip.h b/src/minizip/zip.h new file mode 100644 index 0000000..067b912 --- /dev/null +++ b/src/minizip/zip.h @@ -0,0 +1,367 @@ +/* zip.h -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------- + + Changes + + See header of zip.h + +*/ + +/* Pragma added by libxlsxwriter project to avoid warnings with -pedantic -ansi. */ +#ifndef _WIN32 +#pragma GCC system_header +#endif + +#ifndef _zip12_H +#define _zip12_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* #define HAVE_BZIP2 */ + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); +extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); + +extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64)); + +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) + zip64 is set to 1 if a zip64 extended information block should be added to the local file header. + this MUST be '1' if the uncompressed size is >= 0xffffffff. + +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + + +extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64)); +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting)); + +extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64 + )); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCrypting : crc of file to compress (needed for crypting) + */ + +extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase + )); + + +extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64 + )); +/* + Same than zipOpenNewFileInZip4, except + versionMadeBy : value for Version made by field + flag : value for flag field (compression level info will be added) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); + +extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32)); + +/* + Close the current file in the zipfile, for file opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + + +extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); +/* + zipRemoveExtraInfoBlock - Added by Mathias Svensson + + Remove extra information block from a extra information data for the local file header or central directory header + + It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. + + 0x0001 is the signature header for the ZIP64 extra information blocks + + usage. + Remove ZIP64 Extra information from a central director extra field data + zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); + + Remove ZIP64 Extra information from a Local File Header extra field data + zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip64_H */ diff --git a/src/tmpfileplus/tmpfileplus.c b/src/tmpfileplus/tmpfileplus.c new file mode 100644 index 0000000..e1ff7c7 --- /dev/null +++ b/src/tmpfileplus/tmpfileplus.c @@ -0,0 +1,342 @@ +/* $Id: tmpfileplus.c $ */ +/* + * $Date: 2016-06-01 03:31Z $ + * $Revision: 2.0.0 $ + * $Author: dai $ + */ + +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) 2012-16 David Ireland, DI Management Services Pty Ltd + * . + */ + + +/* +* NAME +* tmpfileplus - create a unique temporary file +* +* SYNOPSIS +* FILE *tmpfileplus(const char *dir, const char *prefix, char **pathname, int keep) +* +* DESCRIPTION +* The tmpfileplus() function opens a unique temporary file in binary +* read/write (w+b) mode. The file is opened with the O_EXCL flag, +* guaranteeing that the caller is the only user. The filename will consist +* of the string given by `prefix` followed by 10 random characters. If +* `prefix` is NULL, then the string "tmp." will be used instead. The file +* will be created in an appropriate directory chosen by the first +* successful attempt in the following sequence: +* +* a) The directory given by the `dir` argument (so the caller can specify +* a secure directory to take precedence). +* +* b) The directory name in the environment variables: +* +* (i) "TMP" [Windows only] +* (ii) "TEMP" [Windows only] +* (iii) "TMPDIR" [Unix only] +* +* c) `P_tmpdir` as defined in [Unix only] (in Windows, this is +* usually "\", which is no good). +* +* d) The current working directory. +* +* If a file cannot be created in any of the above directories, then the +* function fails and NULL is returned. +* +* If the argument `pathname` is not a null pointer, then it will point to +* the full pathname of the file. The pathname is allocated using `malloc` +* and therefore should be freed by `free`. +* +* If `keep` is nonzero and `pathname` is not a null pointer, then the file +* will be kept after it is closed. Otherwise the file will be +* automatically deleted when it is closed or the program terminates. +* +* +* RETURN VALUE +* The tmpfileplus() function returns a pointer to the open file stream, +* or NULL if a unique file cannot be opened. +* +* +* ERRORS +* ENOMEM Not enough memory to allocate filename. +* +*/ + +/* ADDED IN v2.0 */ + +/* +* NAME +* tmpfileplus_f - create a unique temporary file with filename stored in a fixed-length buffer +* +* SYNOPSIS +* FILE *tmpfileplus_f(const char *dir, const char *prefix, char *pathnamebuf, size_t pathsize, int keep); +* +* DESCRIPTION +* Same as tmpfileplus() except receives filename in a fixed-length buffer. No allocated memory to free. + +* ERRORS +* E2BIG Resulting filename is too big for the buffer `pathnamebuf`. + +*/ + +#include "tmpfileplus.h" + +#include +#include +#include +#include +#include + +/* Non-ANSI include files that seem to work in both MSVC and Linux */ +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#ifdef _WIN32 +/* MSVC nags to enforce ISO C++ conformant function names with leading "_", + * so we define our own function names to avoid whingeing compilers... + */ +#define OPEN_ _open +#define FDOPEN_ _fdopen +#else +#define OPEN_ open +#define FDOPEN_ fdopen +#endif + + +/* DEBUGGING STUFF */ +#if defined(_DEBUG) && defined(SHOW_DPRINTF) +#define DPRINTF1(s, a1) printf(s, a1) +#else +#define DPRINTF1(s, a1) +#endif + + +#ifdef _WIN32 +#define FILE_SEPARATOR "\\" +#else +#define FILE_SEPARATOR "/" +#endif + +#define RANDCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +#define NRANDCHARS (sizeof(RANDCHARS) - 1) + +/** Replace each byte in string s with a random character from TEMPCHARS */ +static char *set_randpart(char *s) +{ + size_t i; + unsigned int r; + static unsigned int seed; /* NB static */ + + if (seed == 0) + { /* First time set our seed using current time and clock */ + seed = ((unsigned)time(NULL)<<8) ^ (unsigned)clock(); + } + srand(seed++); + for (i = 0; i < strlen(s); i++) + { + r = rand() % NRANDCHARS; + s[i] = (RANDCHARS)[r]; + } + return s; +} + +/** Return 1 if path is a valid directory otherwise 0 */ +static int is_valid_dir(const char *path) +{ + struct stat st; + if ((stat(path, &st) == 0) && (st.st_mode & S_IFDIR)) + return 1; + + return 0; +} + +/** Call getenv and save a copy in buf */ +static char *getenv_save(const char *varname, char *buf, size_t bufsize) +{ + char *ptr = getenv(varname); + buf[0] = '\0'; + if (ptr) + { + strncpy(buf, ptr, bufsize); + buf[bufsize-1] = '\0'; + return buf; + } + return NULL; +} + +/** + * Try and create a randomly-named file in directory `tmpdir`. + * If successful, allocate memory and set `tmpname_ptr` to full filepath, and return file pointer; + * otherwise return NULL. + * If `keep` is zero then create the file as temporary and it should not exist once closed. + */ +static FILE *mktempfile_internal(const char *tmpdir, const char *pfx, char **tmpname_ptr, int keep) +/* PRE: + * pfx is not NULL and points to a valid null-terminated string + * tmpname_ptr is not NULL. + */ +{ + FILE *fp; + int fd; + char randpart[] = "1234567890"; + size_t lentempname; + int i; + char *tmpname = NULL; + int oflag, pmode; + +/* In Windows, we use the _O_TEMPORARY flag with `open` to ensure the file is deleted when closed. + * In Unix, we use the unlink function after opening the file. (This does not work in Windows, + * which does not allow an open file to be unlinked.) + */ +#ifdef _WIN32 + /* MSVC flags */ + oflag = _O_BINARY|_O_CREAT|_O_EXCL|_O_RDWR; + if (!keep) + oflag |= _O_TEMPORARY; + pmode = _S_IREAD | _S_IWRITE; +#else + /* Standard POSIX flags */ + oflag = O_CREAT|O_EXCL|O_RDWR; + pmode = S_IRUSR|S_IWUSR; +#endif + + if (!tmpdir || !is_valid_dir(tmpdir)) { + errno = ENOENT; + return NULL; + } + + lentempname = strlen(tmpdir) + strlen(FILE_SEPARATOR) + strlen(pfx) + strlen(randpart); + DPRINTF1("lentempname=%d\n", lentempname); + tmpname = malloc(lentempname + 1); + if (!tmpname) + { + errno = ENOMEM; + return NULL; + } + /* If we don't manage to create a file after 10 goes, there is something wrong... */ + for (i = 0; i < 10; i++) + { + sprintf(tmpname, "%s%s%s%s", tmpdir, FILE_SEPARATOR, pfx, set_randpart(randpart)); + DPRINTF1("[%s]\n", tmpname); + fd = OPEN_(tmpname, oflag, pmode); + if (fd != -1) break; + } + DPRINTF1("strlen(tmpname)=%d\n", strlen(tmpname)); + if (fd != -1) + { /* Success, so return user a proper ANSI C file pointer */ + fp = FDOPEN_(fd, "w+b"); + errno = 0; + +#ifndef _WIN32 + /* [Unix only] And make sure the file will be deleted once closed */ + if (!keep) unlink(tmpname); +#endif + + } + else + { /* We failed */ + fp = NULL; + } + if (!fp) + { + free(tmpname); + tmpname = NULL; + } + + *tmpname_ptr = tmpname; + return fp; +} + +/**********************/ +/* EXPORTED FUNCTIONS */ +/**********************/ + +FILE *tmpfileplus(const char *dir, const char *prefix, char **pathname, int keep) +{ + FILE *fp = NULL; + char *tmpname = NULL; + char *tmpdir = NULL; + const char *pfx = (prefix ? prefix : "tmp."); + char *tempdirs[12] = { 0 }; +#ifdef _WIN32 + char env1[FILENAME_MAX+1] = { 0 }; + char env2[FILENAME_MAX+1] = { 0 }; +#else + char env3[FILENAME_MAX+1] = { 0 }; +#endif + int ntempdirs = 0; + int i; + + /* Set up a list of temp directories we will try in order */ + i = 0; + tempdirs[i++] = (char *)dir; +#ifdef _WIN32 + tempdirs[i++] = getenv_save("TMP", env1, sizeof(env1)); + tempdirs[i++] = getenv_save("TEMP", env2, sizeof(env2)); +#else + tempdirs[i++] = getenv_save("TMPDIR", env3, sizeof(env3)); + tempdirs[i++] = P_tmpdir; +#endif + tempdirs[i++] = "."; + ntempdirs = i; + + errno = 0; + + /* Work through list we set up before, and break once we are successful */ + for (i = 0; i < ntempdirs; i++) + { + tmpdir = tempdirs[i]; + DPRINTF1("Trying tmpdir=[%s]\n", tmpdir); + fp = mktempfile_internal(tmpdir, pfx, &tmpname, keep); + if (fp) break; + } + /* If we succeeded and the user passed a pointer, set it to the alloc'd pathname: the user must free this */ + if (fp && pathname) + *pathname = tmpname; + else /* Otherwise, free the alloc'd memory */ + free(tmpname); + + return fp; +} + +/* Same as tmpfileplus() but with fixed length buffer for output filename and no memory allocation */ +FILE *tmpfileplus_f(const char *dir, const char *prefix, char *pathnamebuf, size_t pathsize, int keep) +{ + char *tmpbuf = NULL; + FILE *fp; + + /* If no buffer provided, do the normal way */ + if (!pathnamebuf || (int)pathsize <= 0) { + return tmpfileplus(dir, prefix, NULL, keep); + } + /* Call with a temporary buffer */ + fp = tmpfileplus(dir, prefix, &tmpbuf, keep); + if (fp && strlen(tmpbuf) > pathsize - 1) { + /* Succeeded but not enough room in output buffer, so clean up and return an error */ + pathnamebuf[0] = 0; + fclose(fp); + if (keep) remove(tmpbuf); + free(tmpbuf); + errno = E2BIG; + return NULL; + } + /* Copy name into buffer */ + strcpy(pathnamebuf, tmpbuf); + free(tmpbuf); + + return fp; +} + + diff --git a/src/tmpfileplus/tmpfileplus.h b/src/tmpfileplus/tmpfileplus.h new file mode 100644 index 0000000..6c6ac38 --- /dev/null +++ b/src/tmpfileplus/tmpfileplus.h @@ -0,0 +1,53 @@ +/* $Id: tmpfileplus.h $ */ +/* + * $Date: 2016-06-01 03:31Z $ + * $Revision: 2.0.0 $ + * $Author: dai $ + */ + +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) 2012-16 David Ireland, DI Management Services Pty Ltd + * . + */ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#ifndef TMPFILEPLUS_H_ +#define TMPFILEPLUS_H_ + +#include + +/** Create a unique temporary file. +@param dir (optional) directory to create file. If NULL use default TMP directory. +@param prefix (optional) prefix for file name. If NULL use "tmp.". +@param pathname (optional) pointer to a buffer to receive the temp filename. + Allocated using `malloc()`; user to free. Ignored if NULL. +@param keep If `keep` is nonzero and `pathname` is not NULL, then keep the file after closing. + Otherwise file is automatically deleted when closed. +@return Pointer to stream opened in binary read/write (w+b) mode, or a null pointer on error. +@exception ENOMEM Not enough memory to allocate filename. +*/ +FILE *tmpfileplus(const char *dir, const char *prefix, char **pathname, int keep); + + +/** Create a unique temporary file with filename stored in a fixed-length buffer. +@param dir (optional) directory to create file. If NULL use default directory. +@param prefix (optional) prefix for file name. If NULL use "tmp.". +@param pathnamebuf (optional) buffer to receive full pathname of temporary file. Ignored if NULL. +@param pathsize Size of buffer to receive filename and its terminating null character. +@param keep If `keep` is nonzero and `pathname` is not NULL, then keep the file after closing. + Otherwise file is automatically deleted when closed. +@return Pointer to stream opened in binary read/write (w+b) mode, or a null pointer on error. +@exception E2BIG Resulting filename is too big for the buffer `pathnamebuf`. +*/ +FILE *tmpfileplus_f(const char *dir, const char *prefix, char *pathnamebuf, size_t pathsize, int keep); + +#define TMPFILE_KEEP 1 + +#endif /* end TMPFILEPLUS_H_ */ diff --git a/src/write_xlsx.c b/src/write_xlsx.c new file mode 100644 index 0000000..128054d --- /dev/null +++ b/src/write_xlsx.c @@ -0,0 +1,100 @@ +#define R_NO_REMAP +#define STRICT_R_HEADERS +#include +#include +#include +#include + +#define assert_that(a, b) bail_if(!a, b) + +void bail_if(int check, const char * error){ + if(check) + Rf_error("Error %s", error); +} + +void assert_lxw(lxw_error err){ + if(err != LXW_NO_ERROR) + Rf_errorcall(R_NilValue, "Error in libxlsxwriter: '%s'", lxw_strerror(err)); +} + +attribute_visible SEXP C_write_data_frame(SEXP df, SEXP file, SEXP headers){ + assert_that(Rf_inherits(df, "data.frame"), "Object is not a data frame"); + assert_that(Rf_isString(file) && Rf_length(file), "Invalid file path"); + assert_that(Rf_isString(headers), "Headers must be character vector"); + + //create workbook + lxw_workbook *workbook = workbook_new(Rf_translateChar(STRING_ELT(file, 0))); + assert_that(workbook, "failed to create workbook"); + + //create sheet + lxw_worksheet *sheet = workbook_add_worksheet(workbook, NULL); + assert_that(sheet, "failed to create workbook"); + + //for headers + lxw_format * bold = workbook_add_format(workbook); + format_set_bold(bold); + + //for dates + lxw_format * date = workbook_add_format(workbook); + format_set_num_format(date, "yyyy-mm-d HH:MM AM/PM"); + + //create header row + size_t cursor = 0; + if(Rf_isString(headers) && Rf_length(headers)){ + for(size_t i = 0; i < Rf_length(headers); i++) + worksheet_write_string(sheet, cursor, i, CHAR(STRING_ELT(headers, i)), bold); + cursor++; + } + + // number of records + size_t cols = Rf_length(df); + size_t rows = cols ? Rf_length(VECTOR_ELT(df, 0)) : 0; + if(rows == 0 || cols == 0) + goto done; + + // Need to iterate by row first + for (size_t i = 0; i < rows; i++) { + for(size_t j = 0; j < cols; j++){ + SEXP col = VECTOR_ELT(df, j); + + // unclassed types + switch(TYPEOF(col)){ + case STRSXP: + assert_lxw(worksheet_write_string(sheet, cursor, j, CHAR(STRING_ELT(col, i)), NULL)); + continue; + case INTSXP: + assert_lxw(worksheet_write_number(sheet, cursor, j, INTEGER(col)[i], NULL)); + continue; + case REALSXP: + if(Rf_inherits(col, "POSIXct")){ + double val = REAL(col)[i]; + assert_lxw(worksheet_write_number(sheet, cursor, j, 25569 + val / (24*60*60) , date)); + } else { + assert_lxw(worksheet_write_number(sheet, cursor, j, REAL(col)[i], NULL)); + } + continue; + case LGLSXP: + assert_lxw(worksheet_write_boolean(sheet, cursor, j, LOGICAL(col)[i], NULL)); + continue; + default: + assert_lxw(worksheet_write_blank(sheet, cursor, j, NULL)); + continue; + }; + } + cursor++; + } + + //this both writes the xlsx file and frees the memory + done: + workbook_close(workbook); + return file; +} + +attribute_visible SEXP C_lxw_version(){ + return Rf_mkString(LXW_VERSION); +} + +attribute_visible void R_init_writexl(DllInfo* info) { + R_registerRoutines(info, NULL, NULL, NULL, NULL); + R_useDynamicSymbols(info, TRUE); +} diff --git a/writexl.Rproj b/writexl.Rproj new file mode 100644 index 0000000..497f8bf --- /dev/null +++ b/writexl.Rproj @@ -0,0 +1,20 @@ +Version: 1.0 + +RestoreWorkspace: Default +SaveWorkspace: Default +AlwaysSaveHistory: Default + +EnableCodeIndexing: Yes +UseSpacesForTab: Yes +NumSpacesForTab: 2 +Encoding: UTF-8 + +RnwWeave: Sweave +LaTeX: pdfLaTeX + +AutoAppendNewline: Yes +StripTrailingWhitespace: Yes + +BuildType: Package +PackageUseDevtools: Yes +PackageInstallArgs: --no-multiarch --with-keep.source