writexl/0000755000176200001440000000000014766116671011767 5ustar liggesuserswritexl/tests/0000755000176200001440000000000014766110233013115 5ustar liggesuserswritexl/tests/testthat/0000755000176200001440000000000014766116671014771 5ustar liggesuserswritexl/tests/testthat/test-types.R0000644000176200001440000000230014747162622017224 0ustar liggesuserscontext("Types") roundtrip <- function(df){ readxl::read_xlsx(writexl::write_xlsx(df)) } test_that("Types roundtrip properly",{ kremlin <- "http://\u043F\u0440\u0435\u0437\u0438\u0434\u0435\u043D\u0442.\u0440\u0444" num <- c(NA_real_, pi, 1.2345e80) int <- c(NA_integer_, 0L, -100L) str <- c(NA_character_, "foo", kremlin) #note empty strings don't work yet time <- Sys.time() + 1:3 bigint <- bit64::as.integer64(.Machine$integer.max) ^ c(0,1,1.5) input <- data.frame(num = num, int = int, bigint = bigint, str = str, time = time, stringsAsFactors = FALSE) expect_warning(output <- roundtrip(input), "int64") output$bigint <- bit64::as.integer64(output$bigint) attr(output$time, 'tzone') <- attr(time, 'tzone') expect_equal(input, as.data.frame(output)) }) test_that("Writing formulas", { df <- data.frame( name = c("UCLA", "Berkeley"), founded = c(1919, 1868), website = xl_hyperlink(c("http://www.ucla.edu", "http://www.berkeley.edu"), "website") ) # repeats a formula for entire column df$age <- xl_formula('=(YEAR(TODAY()) - INDIRECT("B" & ROW()))') # currently readxl does not support formulas so inspect manually expect_true(file.exists(write_xlsx(df))) }) writexl/tests/testthat/test-performance.R0000644000176200001440000000043214747162622020365 0ustar liggesuserscontext("Performance") test_that("Performance is OK", { tmp <- writexl::write_xlsx(nycflights13::flights) out <- readxl::read_xlsx(tmp) unlink(tmp) attr(out$time_hour, 'tzone') <- attr(nycflights13::flights$time_hour, 'tzone') expect_equal(out, nycflights13::flights) }) writexl/tests/testthat.R0000644000176200001440000000007214747162622015107 0ustar liggesuserslibrary(testthat) library(writexl) test_check("writexl") writexl/tests/spelling.R0000644000176200001440000000007414747162622015066 0ustar liggesusersspelling::spell_check_test(vignettes = TRUE, error = FALSE) writexl/MD50000644000176200001440000001133514766116671012302 0ustar liggesusersb01873e09579d9f3c07231c779b65af0 *DESCRIPTION 1ee0683cce6d3479250337954c075d63 *LICENSE 90b3275c0bf168216e46188c580c1830 *NAMESPACE e3c346df734133dc4fc4f192f1f76bde *NEWS 64565fbf5e6d3a999d0b1a2e3a916b3f *R/excel_types.R 737b6d239bec61b4b19e2e8b3b015a39 *R/version.R bb2947c21d98f2cce4f1c4acc7dffce8 *R/write_xlsx.R 140af18107fff96197be0916c7435874 *inst/AUTHORS 71b6e204131683c365f4d8cad6dfb2ee *inst/COPYRIGHT ee7896b1dfafac481ebdf4d546fff4df *inst/WORDLIST 5d6cd0aede82ee5950a2fc383d41f6ef *man/write_xlsx.Rd 12ee667a4a5b48eff478e33b3aed6871 *man/writexl.Rd ca36be80c0269ac56ba7dcf8bcc92553 *man/xl_formula.Rd f8dfd99994dfba815561a2dbb6cfad9c *src/Makevars 31a7709e2db76fe96b0882140c0b4c38 *src/include/xlsxwriter.h 10b5c3a7a260f6f743083781efd7700b *src/include/xlsxwriter/app.h c20fb1f17597f5bf83f64439781e4b37 *src/include/xlsxwriter/chart.h e4cfcdd589c1bb413736f64db5d2bf6b *src/include/xlsxwriter/chartsheet.h 071537b3ce0cc852a5946821f1ecfe67 *src/include/xlsxwriter/comment.h 8f54f1736db150b65a75aac49a152cc7 *src/include/xlsxwriter/common.h a73bfc39bc562535088e1d6784fbc89b *src/include/xlsxwriter/content_types.h f37b9b0112ac91f8231d9216eb9e5fe9 *src/include/xlsxwriter/core.h 0b2b733fee0227e9dbe02d683b78e148 *src/include/xlsxwriter/custom.h 2b6be1831bbe6f474223a82f9fcf4a45 *src/include/xlsxwriter/drawing.h 8ce9ec993f8991feacec63bc3260f571 *src/include/xlsxwriter/format.h 7e607de3bb08942f5026443932a2a597 *src/include/xlsxwriter/hash_table.h 672801489f8cc638672176d0a29110fe *src/include/xlsxwriter/metadata.h 37c9818b4a18bdb983e994aa26c3d07c *src/include/xlsxwriter/packager.h 795b637effc5f4eb933faaf138073e40 *src/include/xlsxwriter/relationships.h cc681c406908cfaded98606bca983ab4 *src/include/xlsxwriter/shared_strings.h 39a266941448948034bbbcf68b18f6c6 *src/include/xlsxwriter/styles.h fc39d05afe656cd3e2e3a8b849922eb3 *src/include/xlsxwriter/table.h 7a7a583c11fd42670ecc940520d8a20e *src/include/xlsxwriter/theme.h 418b9368e13d819a0b3111f9712cd175 *src/include/xlsxwriter/third_party/ioapi.h 9a78fb091fa245b64434692197e9b4f2 *src/include/xlsxwriter/third_party/md5.h e396c26d747f73186edf08d48c83996b *src/include/xlsxwriter/third_party/queue.h 91ecc39f822ba460d99a451fec030cfa *src/include/xlsxwriter/third_party/tmpfileplus.h de224c89e67700652f117cab66f4acbb *src/include/xlsxwriter/third_party/tree.h 6dc6de0874d78350319224058bf1ab51 *src/include/xlsxwriter/third_party/zip.h 5ad6668d4ab700b1032ec47147ef5345 *src/include/xlsxwriter/utility.h 4b7b607a4874f3c80c5f62e00500cacc *src/include/xlsxwriter/vml.h ef1ffb06103bbcb28143c035757a5274 *src/include/xlsxwriter/workbook.h 7f1d4a420142941f39ab6c76086af533 *src/include/xlsxwriter/worksheet.h edc7069a40bc558257b6ed11634978e8 *src/include/xlsxwriter/xmlwriter.h 885074bb45268485041f804e30f3a8a1 *src/libxlsxwriter/app.c d272111e429a9abca52ea980e9c9f4f7 *src/libxlsxwriter/chart.c a2e8dc5be6a4f0cbf5945125356ff19a *src/libxlsxwriter/chartsheet.c d08a25a937a2fa206a81631a51fac32a *src/libxlsxwriter/comment.c fd837352689d6e3a7e0938582e2b2c3d *src/libxlsxwriter/content_types.c 76f83de9800415ec5310ea6606cf47a7 *src/libxlsxwriter/core.c c0a1928cfaa035513e6d38f72f57532f *src/libxlsxwriter/custom.c 188c52dc16d40b7b57011fba380a0a7d *src/libxlsxwriter/drawing.c 212f5abdf5b5b2be6963c26ff84ea567 *src/libxlsxwriter/format.c fb9dcc18596b4b59a67114e30c4dbc7f *src/libxlsxwriter/hash_table.c 8a32f28028bef4d45afa448d8eb5f7f4 *src/libxlsxwriter/metadata.c f1c1faf7759fda8ccdf0db2f734c0367 *src/libxlsxwriter/packager.c 2363bd3e88318fc34bac899c00641502 *src/libxlsxwriter/relationships.c 5da46bcea6824ab9d9e644fd41365f76 *src/libxlsxwriter/shared_strings.c e487b52229bb8c482e6f03d59f81ecee *src/libxlsxwriter/styles.c 212b50ae2ae4fe04e5864184bf7116da *src/libxlsxwriter/table.c f2c007cf68dd40c7b4f17ab870db3ed7 *src/libxlsxwriter/theme.c ac80feed2f2dc692d791891555e713a3 *src/libxlsxwriter/utility.c aa5a62f4e5b331571fb1ac9e9de27fdc *src/libxlsxwriter/vml.c a4cd12f5732089282c851dc83ec7c100 *src/libxlsxwriter/workbook.c 0f1e7f3695b88b06a04aa0bee48f6a45 *src/libxlsxwriter/worksheet.c 42b2387ccb88ee991efc1c70764c9665 *src/libxlsxwriter/xmlwriter.c 4d6bbe6eab64e6a0b90ea116883c1fe7 *src/md5/md5.c 9a78fb091fa245b64434692197e9b4f2 *src/md5/md5.h 0bb4267f9320e96ad7bf9c7dbe4c7912 *src/minizip/ioapi.c 418b9368e13d819a0b3111f9712cd175 *src/minizip/ioapi.h 412000864e4eb2a753353b0ad02f18f0 *src/minizip/zip.c 6dc6de0874d78350319224058bf1ab51 *src/minizip/zip.h 636907d331156d62e8e750411db3a87b *src/tmpfileplus/tmpfileplus.c 91ecc39f822ba460d99a451fec030cfa *src/tmpfileplus/tmpfileplus.h 300f7fc9d58721c94793adf9e97098d6 *src/write_xlsx.c bc882e5235bfdccc9e49f99290365b40 *tests/spelling.R 71190c953f4f20bb7683748096679dc4 *tests/testthat.R f593ee0b058391fe787bf07f62604ef2 *tests/testthat/test-performance.R f7ed43304acb6af25f548ba738349064 *tests/testthat/test-types.R writexl/R/0000755000176200001440000000000014750415353012160 5ustar liggesuserswritexl/R/version.R0000644000176200001440000000050314747162622013772 0ustar liggesusers#' 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) } #' @useDynLib writexl C_set_tempdir .onLoad <- function(lib, pkg){ .Call(C_set_tempdir, tempdir()) } writexl/R/excel_types.R0000644000176200001440000000410314747162622014631 0ustar liggesusers#' Excel Types #' #' Create special column types to write to a spreadsheet #' #' @family writexl #' @param x character vector to be interpreted as formula #' @export #' @rdname xl_formula #' @examples #' df <- data.frame( #' name = c("UCLA", "Berkeley", "Jeroen"), #' founded = c(1919, 1868, 2030), #' website = xl_hyperlink(c("http://www.ucla.edu", "http://www.berkeley.edu", NA), "homepage") #' ) #' df$age <- xl_formula('=(YEAR(TODAY()) - INDIRECT("B" & ROW()))') #' write_xlsx(df, 'universities.xlsx') #' #' # cleanup #' unlink('universities.xlsx') xl_formula <- function(x){ if(is.factor(x)) x <- as.character(x) stopifnot(is.character(x)) if(!all(grepl("^=",x) | is.na(x))) stop("Formulas must start with '='") structure(x, class = c('xl_formula', 'xl_object')) } #' @rdname xl_formula #' @export #' @param url character vector of URLs #' @param name character vector of friendly names xl_hyperlink <- function(url, name = NULL){ if(is.factor(url)) url <- as.character(url) stopifnot(is.character(url)) hyperlink <- dubquote(url) if(length(name)){ hyperlink <- paste(hyperlink, dubquote(name), sep = ",") } out <- xl_formula(sprintf("=HYPERLINK(%s)", hyperlink)) out[is.na(url)] <- NA structure(out, class = c('xl_hyperlink', 'xl_formula', 'xl_object')) } #' @export print.xl_formula <- function(x, max = 10, ...){ cat(sprintf(" [:%s:]\n", class(x)[1])) if(length(x) > max) x <- c(x[1:max], "...", sprintf("(total: %s)", length(x))) cat(x, sep = "\n") } #' @export rep.xl_object <- function(x, ...){ structure(rep(unclass(x), ...), class = class(x)) } #' @export `[.xl_object` <- function(x, ...){ structure(`[`(unclass(x), ...), class = class(x)) } #' @export `[[.xl_object` <- function(x, ...){ structure(`[[`(unclass(x), ...), class = class(x)) } #' @export c.xl_object <- function(x, ...){ structure(c(unclass(x), ...), class = class(x)) } #' @export as.data.frame.xl_object <- function(x, ..., stringsAsFactors = FALSE){ as.data.frame.character(x, ..., stringsAsFactors = FALSE) } dubquote <- function(x){ paste0('"', x, '"') } writexl/R/write_xlsx.R0000644000176200001440000000530314750415353014514 0ustar liggesusers#' Export to xlsx #' #' Writes a data frame to an xlsx file. To create an xlsx with (multiple) named #' sheets, simply set \code{x} to a named list of data frames. #' #' Currently supports strings, numbers, booleans and dates. Formatting options #' may be added in future versions. #' #' \if{html}{ #' \out{ #' #' #' }} #' #' @export #' @aliases writexl #' @useDynLib writexl C_write_data_frame_list #' @param x data frame or named list of data frames that will be sheets in the xlsx #' @param path a file name to write to #' @param col_names write column names at the top of the file? #' @param format_headers make the \code{col_names} in the xlsx centered and bold #' @param use_zip64 use \href{https://en.wikipedia.org/wiki/Zip_(file_format)#ZIP64}{zip64} #' to enable support for 4GB+ xlsx files. Not all platforms can read this. #' @examples # Roundtrip example with single excel sheet named 'mysheet' #' tmp <- write_xlsx(list(mysheet = iris)) #' readxl::read_xlsx(tmp) write_xlsx <- function(x, path = tempfile(fileext = ".xlsx"), col_names = TRUE, format_headers = TRUE, use_zip64 = FALSE){ if(is.data.frame(x)) x <- list(x) if(!is.list(x) || !all(vapply(x, is.data.frame, logical(1)))) stop("Argument x must be a data frame or list of data frames") x <- lapply(x, normalize_df) if(any(nchar(names(x)) > 31)){ warning("Truncating sheet name(s) to 31 characters") names(x) <- substring(names(x), 1, 29) } nm <- names(x) if(length(unique(nm)) < length(nm)){ warning("Deduplicating sheet names") names(x) <- make.unique(substring(names(x), 1, 28), sep = "_") } stopifnot(is.character(path) && length(path)) path <- normalizePath(path, mustWork = FALSE) ret <- .Call(C_write_data_frame_list, x, path, col_names, format_headers, use_zip64) invisible(ret) } normalize_df <- function(df){ if(nrow(df) > 1024^2){ stop("the xlsx format does not support tables with 1M+ rows") } # Types to coerce to strings for(i in which(vapply(df, inherits, logical(1), c("factor", "hms")))){ df[[i]] <- as.character(df[[i]]) } for(i in which(vapply(df, function(x){is.integer(x) && inherits(x, "POSIXct")}, logical(1)))){ df[[i]] <- as.POSIXct(as.double(df[[i]])) } for(i in which(vapply(df, inherits, logical(1), "POSIXlt"))){ df[[i]] <- as.POSIXct(df[[i]]) } for(i in which(vapply(df, inherits, logical(1), "integer64"))){ warning(sprintf("Coercing column %s from int64 to double", names(df)[i]), call. = FALSE) getNamespace("bit64") df[[i]] <- as.double(df[[i]]) } df } writexl/NEWS0000644000176200001440000000214714766110236012461 0ustar liggesusers1.5.2 - Fix parallel make; cleanup after build 1.5.0 - Update libxlsxwriter from b0c76b33 1.4.2 - Bugfix for NA timestamps 1.4.1 - Fix strict-prototypes warnings 1.4.0 - Update libxlsxwriter to 1.0.3 1.3.1 - Fix a unit test in R-devel for timezone attribute comparisons 1.3 - write_xlsx gains option use_zip64 for 4GB+ file support - libxslxwriter error messages are printed to REprintf instead of fprintf - Handle overly long or duplicate sheet names - The help assistant only appears once per session 1.2 - Update bundled libxlswriter 0.8.8 - xl_formula and xl_hyperlink now correctly support NA - Oil clippy a bit 1.1 - Update bundled libxlswriter 0.8.4 - Do not write blank xlsx strings for NA and "" character values - Coerce bit64 vectors to double with warning (xlsx does not have int64) 1.0 - Save R 'Date' types as proper datetime strings - Update vendored libxlswriter to 0.7.6 0.2 - Add support for lists in write_xlsx() to create xlsx with multiple sheets - Automatically coerce columns of type 'Date' and 'hms' to strings 0.1 - Initial CRAN release with clippy writexl/src/0000755000176200001440000000000014766110236012545 5ustar liggesuserswritexl/src/include/0000755000176200001440000000000014747162622014175 5ustar liggesuserswritexl/src/include/xlsxwriter.h0000644000176200001440000000076714747162622016613 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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 "1.1.5" #define LXW_VERSION_ID 115 #define LXW_SOVERSION "5" #endif /* __LXW_XLSXWRITER_H__ */ writexl/src/include/xlsxwriter/0000755000176200001440000000000014766116671016434 5ustar liggesuserswritexl/src/include/xlsxwriter/metadata.h0000644000176200001440000000153614747162622020366 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * metadata - A libxlsxwriter library for creating Excel XLSX metadata files. * */ #ifndef __LXW_METADATA_H__ #define __LXW_METADATA_H__ #include #include "common.h" /* * Struct to represent a metadata object. */ typedef struct lxw_metadata { FILE *file; } lxw_metadata; /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ lxw_metadata *lxw_metadata_new(void); void lxw_metadata_free(lxw_metadata *metadata); void lxw_metadata_assemble_xml_file(lxw_metadata *self); /* Declarations required for unit testing. */ #ifdef TESTING STATIC void _metadata_xml_declaration(lxw_metadata *self); #endif /* TESTING */ /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* __LXW_METADATA_H__ */ writexl/src/include/xlsxwriter/xmlwriter.h0000644000176200001440000001455714747162622020652 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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 "utility.h" #define LXW_MAX_ATTRIBUTE_LENGTH 2080 /* Max URL length. */ #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, uint64_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() \ do { \ while (!STAILQ_EMPTY(&attributes)) { \ attribute = STAILQ_FIRST(&attributes); \ STAILQ_REMOVE_HEAD(&attributes, list_entries); \ free(attribute); \ } \ } while (0) /** * 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); void lxw_xml_rich_si_element(FILE * xmlfile, const char *string); uint8_t lxw_has_control_characters(const char *string); char *lxw_escape_control_characters(const char *string); char *lxw_escape_url_characters(const char *string, uint8_t escape_hash); char *lxw_escape_data(const char *data); /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* __XMLWRITER_H__ */ writexl/src/include/xlsxwriter/worksheet.h0000644000176200001440000062733314747162622020632 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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 "shared_strings.h" #include "chart.h" #include "drawing.h" #include "common.h" #include "format.h" #include "styles.h" #include "utility.h" #include "relationships.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 */ #define LXW_IMAGE_BUFFER_SIZE 1024 #define LXW_HEADER_FOOTER_OBJS_MAX 6 /* Header/footer image objs. */ /* 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 Excel column width in character units. */ #define LXW_DEF_COL_WIDTH (double)8.43 /** Default Excel column height in character units. */ #define LXW_DEF_ROW_HEIGHT (double)15.0 /** Default Excel column width in pixels. */ #define LXW_DEF_COL_WIDTH_PIXELS 64 /** Default Excel column height in pixels. */ #define LXW_DEF_ROW_HEIGHT_PIXELS 20 /** 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 }; /** Data validation property values. */ enum lxw_validation_boolean { LXW_VALIDATION_DEFAULT, /** Turn a data validation property off. */ LXW_VALIDATION_OFF, /** Turn a data validation property on. Data validation properties are * generally on by default. */ LXW_VALIDATION_ON }; /** Data validation types. */ enum lxw_validation_types { LXW_VALIDATION_TYPE_NONE, /** Restrict cell input to whole/integer numbers only. */ LXW_VALIDATION_TYPE_INTEGER, /** Restrict cell input to whole/integer numbers only, using a cell * reference. */ LXW_VALIDATION_TYPE_INTEGER_FORMULA, /** Restrict cell input to decimal numbers only. */ LXW_VALIDATION_TYPE_DECIMAL, /** Restrict cell input to decimal numbers only, using a cell * reference. */ LXW_VALIDATION_TYPE_DECIMAL_FORMULA, /** Restrict cell input to a list of strings in a dropdown. */ LXW_VALIDATION_TYPE_LIST, /** Restrict cell input to a list of strings in a dropdown, using a * cell range. */ LXW_VALIDATION_TYPE_LIST_FORMULA, /** Restrict cell input to date values only, using a lxw_datetime type. */ LXW_VALIDATION_TYPE_DATE, /** Restrict cell input to date values only, using a cell reference. */ LXW_VALIDATION_TYPE_DATE_FORMULA, /* Restrict cell input to date values only, as a serial number. * Undocumented. */ LXW_VALIDATION_TYPE_DATE_NUMBER, /** Restrict cell input to time values only, using a lxw_datetime type. */ LXW_VALIDATION_TYPE_TIME, /** Restrict cell input to time values only, using a cell reference. */ LXW_VALIDATION_TYPE_TIME_FORMULA, /* Restrict cell input to time values only, as a serial number. * Undocumented. */ LXW_VALIDATION_TYPE_TIME_NUMBER, /** Restrict cell input to strings of defined length, using a cell * reference. */ LXW_VALIDATION_TYPE_LENGTH, /** Restrict cell input to strings of defined length, using a cell * reference. */ LXW_VALIDATION_TYPE_LENGTH_FORMULA, /** Restrict cell to input controlled by a custom formula that returns * `TRUE/FALSE`. */ LXW_VALIDATION_TYPE_CUSTOM_FORMULA, /** Allow any type of input. Mainly only useful for pop-up messages. */ LXW_VALIDATION_TYPE_ANY }; /** Data validation criteria uses to control the selection of data. */ enum lxw_validation_criteria { LXW_VALIDATION_CRITERIA_NONE, /** Select data between two values. */ LXW_VALIDATION_CRITERIA_BETWEEN, /** Select data that is not between two values. */ LXW_VALIDATION_CRITERIA_NOT_BETWEEN, /** Select data equal to a value. */ LXW_VALIDATION_CRITERIA_EQUAL_TO, /** Select data not equal to a value. */ LXW_VALIDATION_CRITERIA_NOT_EQUAL_TO, /** Select data greater than a value. */ LXW_VALIDATION_CRITERIA_GREATER_THAN, /** Select data less than a value. */ LXW_VALIDATION_CRITERIA_LESS_THAN, /** Select data greater than or equal to a value. */ LXW_VALIDATION_CRITERIA_GREATER_THAN_OR_EQUAL_TO, /** Select data less than or equal to a value. */ LXW_VALIDATION_CRITERIA_LESS_THAN_OR_EQUAL_TO }; /** Data validation error types for pop-up messages. */ enum lxw_validation_error_types { /** Show a "Stop" data validation pop-up message. This is the default. */ LXW_VALIDATION_ERROR_TYPE_STOP, /** Show an "Error" data validation pop-up message. */ LXW_VALIDATION_ERROR_TYPE_WARNING, /** Show an "Information" data validation pop-up message. */ LXW_VALIDATION_ERROR_TYPE_INFORMATION }; /** Set the display type for a cell comment. This is hidden by default but * can be set to visible with the `worksheet_show_comments()` function. */ enum lxw_comment_display_types { /** Default to the worksheet default which can be hidden or visible.*/ LXW_COMMENT_DISPLAY_DEFAULT, /** Hide the cell comment. Usually the default. */ LXW_COMMENT_DISPLAY_HIDDEN, /** Show the cell comment. Can also be set for the worksheet with the * `worksheet_show_comments()` function.*/ LXW_COMMENT_DISPLAY_VISIBLE }; /** @brief Type definitions for conditional formats. * * Values used to set the "type" field of conditional format. */ enum lxw_conditional_format_types { LXW_CONDITIONAL_TYPE_NONE, /** The Cell type is the most common conditional formatting type. It is * used when a format is applied to a cell based on a simple * criterion. */ LXW_CONDITIONAL_TYPE_CELL, /** The Text type is used to specify Excel's "Specific Text" style * conditional format. */ LXW_CONDITIONAL_TYPE_TEXT, /** The Time Period type is used to specify Excel's "Dates Occurring" * style conditional format. */ LXW_CONDITIONAL_TYPE_TIME_PERIOD, /** The Average type is used to specify Excel's "Average" style * conditional format. */ LXW_CONDITIONAL_TYPE_AVERAGE, /** The Duplicate type is used to highlight duplicate cells in a range. */ LXW_CONDITIONAL_TYPE_DUPLICATE, /** The Unique type is used to highlight unique cells in a range. */ LXW_CONDITIONAL_TYPE_UNIQUE, /** The Top type is used to specify the top n values by number or * percentage in a range. */ LXW_CONDITIONAL_TYPE_TOP, /** The Bottom type is used to specify the bottom n values by number or * percentage in a range. */ LXW_CONDITIONAL_TYPE_BOTTOM, /** The Blanks type is used to highlight blank cells in a range. */ LXW_CONDITIONAL_TYPE_BLANKS, /** The No Blanks type is used to highlight non blank cells in a range. */ LXW_CONDITIONAL_TYPE_NO_BLANKS, /** The Errors type is used to highlight error cells in a range. */ LXW_CONDITIONAL_TYPE_ERRORS, /** The No Errors type is used to highlight non error cells in a range. */ LXW_CONDITIONAL_TYPE_NO_ERRORS, /** The Formula type is used to specify a conditional format based on a * user defined formula. */ LXW_CONDITIONAL_TYPE_FORMULA, /** The 2 Color Scale type is used to specify Excel's "2 Color Scale" * style conditional format. */ LXW_CONDITIONAL_2_COLOR_SCALE, /** The 3 Color Scale type is used to specify Excel's "3 Color Scale" * style conditional format. */ LXW_CONDITIONAL_3_COLOR_SCALE, /** The Data Bar type is used to specify Excel's "Data Bar" style * conditional format. */ LXW_CONDITIONAL_DATA_BAR, /** The Icon Set type is used to specify a conditional format with a set * of icons such as traffic lights or arrows. */ LXW_CONDITIONAL_TYPE_ICON_SETS, LXW_CONDITIONAL_TYPE_LAST }; /** @brief The criteria used in a conditional format. * * Criteria used to define how a conditional format works. */ enum lxw_conditional_criteria { LXW_CONDITIONAL_CRITERIA_NONE, /** Format cells equal to a value. */ LXW_CONDITIONAL_CRITERIA_EQUAL_TO, /** Format cells not equal to a value. */ LXW_CONDITIONAL_CRITERIA_NOT_EQUAL_TO, /** Format cells greater than a value. */ LXW_CONDITIONAL_CRITERIA_GREATER_THAN, /** Format cells less than a value. */ LXW_CONDITIONAL_CRITERIA_LESS_THAN, /** Format cells greater than or equal to a value. */ LXW_CONDITIONAL_CRITERIA_GREATER_THAN_OR_EQUAL_TO, /** Format cells less than or equal to a value. */ LXW_CONDITIONAL_CRITERIA_LESS_THAN_OR_EQUAL_TO, /** Format cells between two values. */ LXW_CONDITIONAL_CRITERIA_BETWEEN, /** Format cells that is not between two values. */ LXW_CONDITIONAL_CRITERIA_NOT_BETWEEN, /** Format cells that contain the specified text. */ LXW_CONDITIONAL_CRITERIA_TEXT_CONTAINING, /** Format cells that don't contain the specified text. */ LXW_CONDITIONAL_CRITERIA_TEXT_NOT_CONTAINING, /** Format cells that begin with the specified text. */ LXW_CONDITIONAL_CRITERIA_TEXT_BEGINS_WITH, /** Format cells that end with the specified text. */ LXW_CONDITIONAL_CRITERIA_TEXT_ENDS_WITH, /** Format cells with a date of yesterday. */ LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_YESTERDAY, /** Format cells with a date of today. */ LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_TODAY, /** Format cells with a date of tomorrow. */ LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_TOMORROW, /** Format cells with a date in the last 7 days. */ LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_7_DAYS, /** Format cells with a date in the last week. */ LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_WEEK, /** Format cells with a date in the current week. */ LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_THIS_WEEK, /** Format cells with a date in the next week. */ LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_NEXT_WEEK, /** Format cells with a date in the last month. */ LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_MONTH, /** Format cells with a date in the current month. */ LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_THIS_MONTH, /** Format cells with a date in the next month. */ LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_NEXT_MONTH, /** Format cells above the average for the range. */ LXW_CONDITIONAL_CRITERIA_AVERAGE_ABOVE, /** Format cells below the average for the range. */ LXW_CONDITIONAL_CRITERIA_AVERAGE_BELOW, /** Format cells above or equal to the average for the range. */ LXW_CONDITIONAL_CRITERIA_AVERAGE_ABOVE_OR_EQUAL, /** Format cells below or equal to the average for the range. */ LXW_CONDITIONAL_CRITERIA_AVERAGE_BELOW_OR_EQUAL, /** Format cells 1 standard deviation above the average for the range. */ LXW_CONDITIONAL_CRITERIA_AVERAGE_1_STD_DEV_ABOVE, /** Format cells 1 standard deviation below the average for the range. */ LXW_CONDITIONAL_CRITERIA_AVERAGE_1_STD_DEV_BELOW, /** Format cells 2 standard deviation above the average for the range. */ LXW_CONDITIONAL_CRITERIA_AVERAGE_2_STD_DEV_ABOVE, /** Format cells 2 standard deviation below the average for the range. */ LXW_CONDITIONAL_CRITERIA_AVERAGE_2_STD_DEV_BELOW, /** Format cells 3 standard deviation above the average for the range. */ LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_ABOVE, /** Format cells 3 standard deviation below the average for the range. */ LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_BELOW, /** Format cells in the top of bottom percentage. */ LXW_CONDITIONAL_CRITERIA_TOP_OR_BOTTOM_PERCENT }; /** @brief Conditional format rule types. * * Conditional format rule types that apply to Color Scale and Data Bars. */ enum lxw_conditional_format_rule_types { LXW_CONDITIONAL_RULE_TYPE_NONE, /** Conditional format rule type: matches the minimum values in the * range. Can only be applied to min_rule_type.*/ LXW_CONDITIONAL_RULE_TYPE_MINIMUM, /** Conditional format rule type: use a number to set the bound.*/ LXW_CONDITIONAL_RULE_TYPE_NUMBER, /** Conditional format rule type: use a percentage to set the bound.*/ LXW_CONDITIONAL_RULE_TYPE_PERCENT, /** Conditional format rule type: use a percentile to set the bound.*/ LXW_CONDITIONAL_RULE_TYPE_PERCENTILE, /** Conditional format rule type: use a formula to set the bound.*/ LXW_CONDITIONAL_RULE_TYPE_FORMULA, /** Conditional format rule type: matches the maximum values in the * range. Can only be applied to max_rule_type.*/ LXW_CONDITIONAL_RULE_TYPE_MAXIMUM, /* Used internally for Excel2010 bars. Not documented. */ LXW_CONDITIONAL_RULE_TYPE_AUTO_MIN, /* Used internally for Excel2010 bars. Not documented. */ LXW_CONDITIONAL_RULE_TYPE_AUTO_MAX }; /** @brief Conditional format data bar directions. * * Values used to set the bar direction of a conditional format data bar. */ enum lxw_conditional_format_bar_direction { /** Data bar direction is set by Excel based on the context of the data * displayed. */ LXW_CONDITIONAL_BAR_DIRECTION_CONTEXT, /** Data bar direction is from right to left. */ LXW_CONDITIONAL_BAR_DIRECTION_RIGHT_TO_LEFT, /** Data bar direction is from left to right. */ LXW_CONDITIONAL_BAR_DIRECTION_LEFT_TO_RIGHT }; /** @brief Conditional format data bar axis options. * * Values used to set the position of the axis in a conditional format data * bar. */ enum lxw_conditional_bar_axis_position { /** Data bar axis position is set by Excel based on the context of the * data displayed. */ LXW_CONDITIONAL_BAR_AXIS_AUTOMATIC, /** Data bar axis position is set at the midpoint. */ LXW_CONDITIONAL_BAR_AXIS_MIDPOINT, /** Data bar axis is turned off. */ LXW_CONDITIONAL_BAR_AXIS_NONE }; /** @brief Icon types used in the #lxw_conditional_format icon_style field. * * Definitions of icon styles used with Icon Set conditional formats. */ enum lxw_conditional_icon_types { /** Icon style: 3 colored arrows showing up, sideways and down. */ LXW_CONDITIONAL_ICONS_3_ARROWS_COLORED, /** Icon style: 3 gray arrows showing up, sideways and down. */ LXW_CONDITIONAL_ICONS_3_ARROWS_GRAY, /** Icon style: 3 colored flags in red, yellow and green. */ LXW_CONDITIONAL_ICONS_3_FLAGS, /** Icon style: 3 traffic lights - rounded. */ LXW_CONDITIONAL_ICONS_3_TRAFFIC_LIGHTS_UNRIMMED, /** Icon style: 3 traffic lights with a rim - squarish. */ LXW_CONDITIONAL_ICONS_3_TRAFFIC_LIGHTS_RIMMED, /** Icon style: 3 colored shapes - a circle, triangle and diamond. */ LXW_CONDITIONAL_ICONS_3_SIGNS, /** Icon style: 3 circled symbols with tick mark, exclamation and * cross. */ LXW_CONDITIONAL_ICONS_3_SYMBOLS_CIRCLED, /** Icon style: 3 symbols with tick mark, exclamation and cross. */ LXW_CONDITIONAL_ICONS_3_SYMBOLS_UNCIRCLED, /** Icon style: 4 colored arrows showing up, diagonal up, diagonal down * and down. */ LXW_CONDITIONAL_ICONS_4_ARROWS_COLORED, /** Icon style: 4 gray arrows showing up, diagonal up, diagonal down and * down. */ LXW_CONDITIONAL_ICONS_4_ARROWS_GRAY, /** Icon style: 4 circles in 4 colors going from red to black. */ LXW_CONDITIONAL_ICONS_4_RED_TO_BLACK, /** Icon style: 4 histogram ratings. */ LXW_CONDITIONAL_ICONS_4_RATINGS, /** Icon style: 4 traffic lights. */ LXW_CONDITIONAL_ICONS_4_TRAFFIC_LIGHTS, /** Icon style: 5 colored arrows showing up, diagonal up, sideways, * diagonal down and down. */ LXW_CONDITIONAL_ICONS_5_ARROWS_COLORED, /** Icon style: 5 gray arrows showing up, diagonal up, sideways, diagonal * down and down. */ LXW_CONDITIONAL_ICONS_5_ARROWS_GRAY, /** Icon style: 5 histogram ratings. */ LXW_CONDITIONAL_ICONS_5_RATINGS, /** Icon style: 5 quarters, from 0 to 4 quadrants filled. */ LXW_CONDITIONAL_ICONS_5_QUARTERS }; /** @brief The type of table style. * * The type of table style (Light, Medium or Dark). */ enum lxw_table_style_type { LXW_TABLE_STYLE_TYPE_DEFAULT, /** Light table style. */ LXW_TABLE_STYLE_TYPE_LIGHT, /** Light table style. */ LXW_TABLE_STYLE_TYPE_MEDIUM, /** Light table style. */ LXW_TABLE_STYLE_TYPE_DARK }; /** * @brief Standard Excel functions for totals in tables. * * Definitions for the standard Excel functions that are available via the * dropdown in the total row of an Excel table. * */ enum lxw_table_total_functions { LXW_TABLE_FUNCTION_NONE = 0, /** Use the average function as the table total. */ LXW_TABLE_FUNCTION_AVERAGE = 101, /** Use the count numbers function as the table total. */ LXW_TABLE_FUNCTION_COUNT_NUMS = 102, /** Use the count function as the table total. */ LXW_TABLE_FUNCTION_COUNT = 103, /** Use the max function as the table total. */ LXW_TABLE_FUNCTION_MAX = 104, /** Use the min function as the table total. */ LXW_TABLE_FUNCTION_MIN = 105, /** Use the standard deviation function as the table total. */ LXW_TABLE_FUNCTION_STD_DEV = 107, /** Use the sum function as the table total. */ LXW_TABLE_FUNCTION_SUM = 109, /** Use the var function as the table total. */ LXW_TABLE_FUNCTION_VAR = 110 }; /** @brief The criteria used in autofilter rules. * * Criteria used to define an autofilter rule condition. */ enum lxw_filter_criteria { LXW_FILTER_CRITERIA_NONE, /** Filter cells equal to a value. */ LXW_FILTER_CRITERIA_EQUAL_TO, /** Filter cells not equal to a value. */ LXW_FILTER_CRITERIA_NOT_EQUAL_TO, /** Filter cells greater than a value. */ LXW_FILTER_CRITERIA_GREATER_THAN, /** Filter cells less than a value. */ LXW_FILTER_CRITERIA_LESS_THAN, /** Filter cells greater than or equal to a value. */ LXW_FILTER_CRITERIA_GREATER_THAN_OR_EQUAL_TO, /** Filter cells less than or equal to a value. */ LXW_FILTER_CRITERIA_LESS_THAN_OR_EQUAL_TO, /** Filter cells that are blank. */ LXW_FILTER_CRITERIA_BLANKS, /** Filter cells that are not blank. */ LXW_FILTER_CRITERIA_NON_BLANKS }; /** * @brief And/or operator when using 2 filter rules. * * And/or operator conditions when using 2 filter rules with * worksheet_filter_column2(). In general LXW_FILTER_OR is used with * LXW_FILTER_CRITERIA_EQUAL_TO and LXW_FILTER_AND is used with the other * filter criteria. */ enum lxw_filter_operator { /** Logical "and" of 2 filter rules. */ LXW_FILTER_AND, /** Logical "or" of 2 filter rules. */ LXW_FILTER_OR }; /* Internal filter types. */ enum lxw_filter_type { LXW_FILTER_TYPE_NONE, LXW_FILTER_TYPE_SINGLE, LXW_FILTER_TYPE_AND, LXW_FILTER_TYPE_OR, LXW_FILTER_TYPE_STRING_LIST }; /** Options to control the positioning of worksheet objects such as images * or charts. See @ref working_with_object_positioning. */ enum lxw_object_position { /** Default positioning for the object. */ LXW_OBJECT_POSITION_DEFAULT, /** Move and size the worksheet object with the cells. */ LXW_OBJECT_MOVE_AND_SIZE, /** Move but don't size the worksheet object with the cells. */ LXW_OBJECT_MOVE_DONT_SIZE, /** Don't move or size the worksheet object with the cells. */ LXW_OBJECT_DONT_MOVE_DONT_SIZE, /** Same as #LXW_OBJECT_MOVE_AND_SIZE except libxlsxwriter applies hidden * cells after the object is inserted. */ LXW_OBJECT_MOVE_AND_SIZE_AFTER }; /** Options for ignoring worksheet errors/warnings. See worksheet_ignore_errors(). */ enum lxw_ignore_errors { /** Turn off errors/warnings for numbers stores as text. */ LXW_IGNORE_NUMBER_STORED_AS_TEXT = 1, /** Turn off errors/warnings for formula errors (such as divide by * zero). */ LXW_IGNORE_EVAL_ERROR, /** Turn off errors/warnings for formulas that differ from surrounding * formulas. */ LXW_IGNORE_FORMULA_DIFFERS, /** Turn off errors/warnings for formulas that omit cells in a range. */ LXW_IGNORE_FORMULA_RANGE, /** Turn off errors/warnings for unlocked cells that contain formulas. */ LXW_IGNORE_FORMULA_UNLOCKED, /** Turn off errors/warnings for formulas that refer to empty cells. */ LXW_IGNORE_EMPTY_CELL_REFERENCE, /** Turn off errors/warnings for cells in a table that do not comply with * applicable data validation rules. */ LXW_IGNORE_LIST_DATA_VALIDATION, /** Turn off errors/warnings for cell formulas that differ from the column * formula. */ LXW_IGNORE_CALCULATED_COLUMN, /** Turn off errors/warnings for formulas that contain a two digit text * representation of a year. */ LXW_IGNORE_TWO_DIGIT_TEXT_YEAR, LXW_IGNORE_LAST_OPTION }; enum cell_types { NUMBER_CELL = 1, STRING_CELL, INLINE_STRING_CELL, INLINE_RICH_STRING_CELL, FORMULA_CELL, ARRAY_FORMULA_CELL, DYNAMIC_ARRAY_FORMULA_CELL, BLANK_CELL, BOOLEAN_CELL, COMMENT, HYPERLINK_URL, HYPERLINK_INTERNAL, HYPERLINK_EXTERNAL }; enum pane_types { NO_PANES = 0, FREEZE_PANES, SPLIT_PANES, FREEZE_SPLIT_PANES }; enum lxw_image_position { HEADER_LEFT = 0, HEADER_CENTER, HEADER_RIGHT, FOOTER_LEFT, FOOTER_CENTER, FOOTER_RIGHT }; /* Define the tree.h RB structs for the red-black head types. */ RB_HEAD(lxw_table_cells, lxw_cell); RB_HEAD(lxw_drawing_rel_ids, lxw_drawing_rel_id); RB_HEAD(lxw_vml_drawing_rel_ids, lxw_drawing_rel_id); RB_HEAD(lxw_cond_format_hash, lxw_cond_format_hash_element); /* 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;} #define LXW_RB_GENERATE_DRAWING_REL_IDS(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_drawing_rel_ids{int unused;} #define LXW_RB_GENERATE_VML_DRAWING_REL_IDS(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_vml_drawing_rel_ids{int unused;} #define LXW_RB_GENERATE_COND_FORMAT_HASH(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_cond_format_hash{int unused;} STAILQ_HEAD(lxw_merged_ranges, lxw_merged_range); STAILQ_HEAD(lxw_selections, lxw_selection); STAILQ_HEAD(lxw_data_validations, lxw_data_val_obj); STAILQ_HEAD(lxw_cond_format_list, lxw_cond_format_obj); STAILQ_HEAD(lxw_image_props, lxw_object_properties); STAILQ_HEAD(lxw_chart_props, lxw_object_properties); STAILQ_HEAD(lxw_comment_objs, lxw_vml_obj); STAILQ_HEAD(lxw_table_objs, lxw_table_obj); /** * @brief Options for rows and columns. * * Options struct for the worksheet_set_column() and worksheet_set_row() * functions. * * It has the following members: * * * `hidden` * * `level` * * `collapsed` * * The members of this struct are explained in @ref ww_outlines_grouping. * */ typedef struct lxw_row_col_options { /** Hide the row/column. @ref ww_outlines_grouping.*/ uint8_t hidden; /** Outline level. See @ref ww_outlines_grouping.*/ uint8_t level; /** Set the outline row as collapsed. See @ref ww_outlines_grouping.*/ 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; uint8_t has_rules; 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 Worksheet data validation options. */ typedef struct lxw_data_validation { /** * Set the validation type. Should be a #lxw_validation_types value. */ uint8_t validate; /** * Set the validation criteria type to select the data. Should be a * #lxw_validation_criteria value. */ uint8_t criteria; /** Controls whether a data validation is not applied to blank data in the * cell. Should be a #lxw_validation_boolean value. It is on by * default. */ uint8_t ignore_blank; /** * This parameter is used to toggle on and off the 'Show input message * when cell is selected' option in the Excel data validation dialog. When * the option is off an input message is not displayed even if it has been * set using input_message. Should be a #lxw_validation_boolean value. It * is on by default. */ uint8_t show_input; /** * This parameter is used to toggle on and off the 'Show error alert * after invalid data is entered' option in the Excel data validation * dialog. When the option is off an error message is not displayed even * if it has been set using error_message. Should be a * #lxw_validation_boolean value. It is on by default. */ uint8_t show_error; /** * This parameter is used to specify the type of error dialog that is * displayed. Should be a #lxw_validation_error_types value. */ uint8_t error_type; /** * This parameter is used to toggle on and off the 'In-cell dropdown' * option in the Excel data validation dialog. When the option is on a * dropdown list will be shown for list validations. Should be a * #lxw_validation_boolean value. It is on by default. */ uint8_t dropdown; /** * This parameter is used to set the limiting value to which the criteria * is applied using a whole or decimal number. */ double value_number; /** * This parameter is used to set the limiting value to which the criteria * is applied using a cell reference. It is valid for any of the * `_FORMULA` validation types. */ const char *value_formula; /** * This parameter is used to set a list of strings for a drop down list. * The list should be a `NULL` terminated array of char* strings: * * @code * char *list[] = {"open", "high", "close", NULL}; * * data_validation->validate = LXW_VALIDATION_TYPE_LIST; * data_validation->value_list = list; * @endcode * * The `value_formula` parameter can also be used to specify a list from * an Excel cell range. * * Note, the string list is restricted by Excel to 255 characters, * including comma separators. */ const char **value_list; /** * This parameter is used to set the limiting value to which the date or * time criteria is applied using a #lxw_datetime struct. */ lxw_datetime value_datetime; /** * This parameter is the same as `value_number` but for the minimum value * when a `BETWEEN` criteria is used. */ double minimum_number; /** * This parameter is the same as `value_formula` but for the minimum value * when a `BETWEEN` criteria is used. */ const char *minimum_formula; /** * This parameter is the same as `value_datetime` but for the minimum value * when a `BETWEEN` criteria is used. */ lxw_datetime minimum_datetime; /** * This parameter is the same as `value_number` but for the maximum value * when a `BETWEEN` criteria is used. */ double maximum_number; /** * This parameter is the same as `value_formula` but for the maximum value * when a `BETWEEN` criteria is used. */ const char *maximum_formula; /** * This parameter is the same as `value_datetime` but for the maximum value * when a `BETWEEN` criteria is used. */ lxw_datetime maximum_datetime; /** * The input_title parameter is used to set the title of the input message * that is displayed when a cell is entered. It has no default value and * is only displayed if the input message is displayed. See the * `input_message` parameter below. * * The maximum title length is 32 characters. */ const char *input_title; /** * The input_message parameter is used to set the input message that is * displayed when a cell is entered. It has no default value. * * The message can be split over several lines using newlines. The maximum * message length is 255 characters. */ const char *input_message; /** * The error_title parameter is used to set the title of the error message * that is displayed when the data validation criteria is not met. The * default error title is 'Microsoft Excel'. The maximum title length is * 32 characters. */ const char *error_title; /** * The error_message parameter is used to set the error message that is * displayed when a cell is entered. The default error message is "The * value you entered is not valid. A user has restricted values that can * be entered into the cell". * * The message can be split over several lines using newlines. The maximum * message length is 255 characters. */ const char *error_message; } lxw_data_validation; /* A copy of lxw_data_validation which is used internally and which contains * some additional fields. */ typedef struct lxw_data_val_obj { uint8_t validate; uint8_t criteria; uint8_t ignore_blank; uint8_t show_input; uint8_t show_error; uint8_t error_type; uint8_t dropdown; double value_number; char *value_formula; char **value_list; double minimum_number; char *minimum_formula; lxw_datetime minimum_datetime; double maximum_number; char *maximum_formula; lxw_datetime maximum_datetime; char *input_title; char *input_message; char *error_title; char *error_message; char sqref[LXW_MAX_CELL_RANGE_LENGTH]; STAILQ_ENTRY (lxw_data_val_obj) list_pointers; } lxw_data_val_obj; /** * @brief Worksheet conditional formatting options. * * The fields/options in the the lxw_conditional_format are used to define a * worksheet conditional format. It is used in conjunction with * `worksheet_conditional_format()`. * */ typedef struct lxw_conditional_format { /** The type of conditional format such as #LXW_CONDITIONAL_TYPE_CELL or * #LXW_CONDITIONAL_DATA_BAR. Should be a #lxw_conditional_format_types * value.*/ uint8_t type; /** The criteria parameter is used to set the criteria by which the cell * data will be evaluated. For example in the expression `a > 5 the * criteria is `>` or, in libxlsxwriter terms, * #LXW_CONDITIONAL_CRITERIA_GREATER_THAN. The criteria that are * applicable depend on the conditional format type. The criteria * options are defined in #lxw_conditional_criteria. */ uint8_t criteria; /** The number value to which the condition refers. For example in the * expression `a > 5`, the value is 5.*/ double value; /** The string value to which the condition refers, such as `"=A1"`. If a * value_string exists in the struct then the number value is * ignored. Note, if the condition refers to a text string then it must * be double quoted like this `"foo"`. */ const char *value_string; /** The format field is used to specify the #lxw_format format that will * be applied to the cell when the conditional formatting criterion is * met. The #lxw_format is created using the `workbook_add_format()` * method in the same way as cell formats. * * @note In Excel, a conditional format is superimposed over the existing * cell format and not all cell format properties can be * modified. Properties that @b cannot be modified, in Excel, by a * conditional format are: font name, font size, superscript and * subscript, diagonal borders, all alignment properties and all * protection properties. */ lxw_format *format; /** The minimum value used for Cell, Color Scale and Data Bar conditional * formats. For Cell types this is usually used with a "Between" style criteria. */ double min_value; /** The minimum string value used for Cell, Color Scale and Data Bar conditional * formats. Usually used to set ranges like `=A1`. */ const char *min_value_string; /** The rule used for the minimum condition in Color Scale and Data Bar * conditional formats. The rule types are defined in * #lxw_conditional_format_rule_types. */ uint8_t min_rule_type; /** The color used for the minimum Color Scale conditional format. * See @ref working_with_colors. */ lxw_color_t min_color; /** The middle value used for Color Scale and Data Bar conditional * formats. */ double mid_value; /** The middle string value used for Color Scale and Data Bar conditional * formats. Usually used to set ranges like `=A1`. */ const char *mid_value_string; /** The rule used for the middle condition in Color Scale and Data Bar * conditional formats. The rule types are defined in * #lxw_conditional_format_rule_types. */ uint8_t mid_rule_type; /** The color used for the middle Color Scale conditional format. * See @ref working_with_colors. */ lxw_color_t mid_color; /** The maximum value used for Cell, Color Scale and Data Bar conditional * formats. For Cell types this is usually used with a "Between" style * criteria. */ double max_value; /** The maximum string value used for Cell, Color Scale and Data Bar conditional * formats. Usually used to set ranges like `=A1`. */ const char *max_value_string; /** The rule used for the maximum condition in Color Scale and Data Bar * conditional formats. The rule types are defined in * #lxw_conditional_format_rule_types. */ uint8_t max_rule_type; /** The color used for the maximum Color Scale conditional format. * See @ref working_with_colors. */ lxw_color_t max_color; /** The bar_color field sets the fill color for data bars. See @ref * working_with_colors. */ lxw_color_t bar_color; /** The bar_only field sets The bar_only field displays a bar data but * not the data in the cells. */ uint8_t bar_only; /** In Excel 2010 additional data bar properties were added such as solid * (non-gradient) bars and control over how negative values are * displayed. These properties can shown below. * * The data_bar_2010 field sets Excel 2010 style data bars even when * Excel 2010 specific properties aren't used. */ uint8_t data_bar_2010; /** The bar_solid field turns on a solid (non-gradient) fill for data * bars. Set to LXW_TRUE to turn on. Excel 2010 only. */ uint8_t bar_solid; /** The bar_negative_color field sets the color fill for the negative * portion of a data bar. See @ref working_with_colors. Excel 2010 only. */ lxw_color_t bar_negative_color; /** The bar_border_color field sets the color for the border line of a * data bar. See @ref working_with_colors. Excel 2010 only. */ lxw_color_t bar_border_color; /** The bar_negative_border_color field sets the color for the border of * the negative portion of a data bar. See @ref * working_with_colors. Excel 2010 only. */ lxw_color_t bar_negative_border_color; /** The bar_negative_color_same field sets the fill color for the negative * portion of a data bar to be the same as the fill color for the * positive portion of the data bar. Set to LXW_TRUE to turn on. Excel * 2010 only. */ uint8_t bar_negative_color_same; /** The bar_negative_border_color_same field sets the border color for the * negative portion of a data bar to be the same as the border color for * the positive portion of the data bar. Set to LXW_TRUE to turn * on. Excel 2010 only. */ uint8_t bar_negative_border_color_same; /** The bar_no_border field turns off the border for data bars. Set to * LXW_TRUE to enable. Excel 2010 only. */ uint8_t bar_no_border; /** The bar_direction field sets the direction for data bars. This * property can be either left for left-to-right or right for * right-to-left. If the property isn't set then Excel will adjust the * position automatically based on the context. Should be a * #lxw_conditional_format_bar_direction value. Excel 2010 only. */ uint8_t bar_direction; /** The bar_axis_position field sets the position within the cells for the * axis that is shown in data bars when there are negative values to * display. The property can be either middle or none. If the property * isn't set then Excel will position the axis based on the range of * positive and negative values. Should be a * lxw_conditional_bar_axis_position value. Excel 2010 only. */ uint8_t bar_axis_position; /** The bar_axis_color field sets the color for the axis that is shown * in data bars when there are negative values to display. See @ref * working_with_colors. Excel 2010 only. */ lxw_color_t bar_axis_color; /** The Icons Sets style is specified by the icon_style parameter. Should * be a #lxw_conditional_icon_types. */ uint8_t icon_style; /** The order of Icon Sets icons can be reversed by setting reverse_icons * to LXW_TRUE. */ uint8_t reverse_icons; /** The icons can be displayed without the cell value by settings the * icons_only parameter to LXW_TRUE. */ uint8_t icons_only; /** The multi_range field is used to extend a conditional format over * non-contiguous ranges. * * It is possible to apply the conditional format to different cell * ranges in a worksheet using multiple calls to * `worksheet_conditional_format()`. However, as a minor optimization it * is also possible in Excel to apply the same conditional format to * different non-contiguous cell ranges. * * This is replicated in `worksheet_conditional_format()` using the * multi_range option. The range must contain the primary range for the * conditional format and any others separated by spaces. For example * `"A1 C1:C5 E2 G1:G100"`. */ const char *multi_range; /** The stop_if_true parameter can be used to set the "stop if true" * feature of a conditional formatting rule when more than one rule is * applied to a cell or a range of cells. When this parameter is set then * subsequent rules are not evaluated if the current rule is true. Set to * LXW_TRUE to turn on. */ uint8_t stop_if_true; } lxw_conditional_format; /* Internal */ typedef struct lxw_cond_format_obj { uint8_t type; uint8_t criteria; double min_value; char *min_value_string; uint8_t min_rule_type; lxw_color_t min_color; double mid_value; char *mid_value_string; uint8_t mid_value_type; uint8_t mid_rule_type; lxw_color_t mid_color; double max_value; char *max_value_string; uint8_t max_value_type; uint8_t max_rule_type; lxw_color_t max_color; uint8_t data_bar_2010; uint8_t auto_min; uint8_t auto_max; uint8_t bar_only; uint8_t bar_solid; uint8_t bar_negative_color_same; uint8_t bar_negative_border_color_same; uint8_t bar_no_border; uint8_t bar_direction; uint8_t bar_axis_position; lxw_color_t bar_color; lxw_color_t bar_negative_color; lxw_color_t bar_border_color; lxw_color_t bar_negative_border_color; lxw_color_t bar_axis_color; uint8_t icon_style; uint8_t reverse_icons; uint8_t icons_only; uint8_t stop_if_true; uint8_t has_max; char *type_string; char *guid; int32_t dxf_index; uint32_t dxf_priority; char first_cell[LXW_MAX_CELL_NAME_LENGTH]; char sqref[LXW_MAX_ATTRIBUTE_LENGTH]; STAILQ_ENTRY (lxw_cond_format_obj) list_pointers; } lxw_cond_format_obj; typedef struct lxw_cond_format_hash_element { char sqref[LXW_MAX_ATTRIBUTE_LENGTH]; struct lxw_cond_format_list *cond_formats; RB_ENTRY (lxw_cond_format_hash_element) tree_pointers; } lxw_cond_format_hash_element; /** * @brief Table columns options. * * Structure to set the options of a table column added with * worksheet_add_table(). See @ref ww_tables_columns. */ typedef struct lxw_table_column { /** Set the header name/caption for the column. If NULL the header defaults * to Column 1, Column 2, etc. */ const char *header; /** Set the formula for the column. */ const char *formula; /** Set the string description for the column total. */ const char *total_string; /** Set the function for the column total. */ uint8_t total_function; /** Set the format for the column header. */ lxw_format *header_format; /** Set the format for the data rows in the column. */ lxw_format *format; /** Set the formula value for the column total (not generally required). */ double total_value; } lxw_table_column; /** * @brief Worksheet table options. * * Options used to define worksheet tables. See @ref working_with_tables for * more information. * */ typedef struct lxw_table_options { /** * The `name` parameter is used to set the name of the table. This * parameter is optional and by default tables are named `Table1`, * `Table2`, etc. in the worksheet order that they are added. * * @code * lxw_table_options options = {.name = "Sales"}; * * worksheet_add_table(worksheet, RANGE("B3:G8"), &options); * @endcode * * If you override the table name you must ensure that it doesn't clash * with an existing table name and that it follows Excel's requirements * for table names, see the Microsoft Office documentation on * [Naming an Excel Table] * (https://support.microsoft.com/en-us/office/rename-an-excel-table-fbf49a4f-82a3-43eb-8ba2-44d21233b114). */ const char *name; /** * The `no_header_row` parameter can be used to turn off the header row in * the table. It is on by default: * * @code * lxw_table_options options = {.no_header_row = LXW_TRUE}; * * worksheet_add_table(worksheet, RANGE("B4:F7"), &options); * @endcode * * @image html tables4.png * * Without this option the header row will contain default captions such * as `Column 1`, ``Column 2``, etc. These captions can be overridden * using the `columns` parameter shown below. * */ uint8_t no_header_row; /** * The `no_autofilter` parameter can be used to turn off the autofilter in * the header row. It is on by default: * * @code * lxw_table_options options = {.no_autofilter = LXW_TRUE}; * * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); * @endcode * * @image html tables3.png * * The autofilter is only shown if the `no_header_row` parameter is off * (the default). Filter conditions within the table are not supported. * */ uint8_t no_autofilter; /** * The `no_banded_rows` parameter can be used to turn off the rows of alternating * color in the table. It is on by default: * * @code * lxw_table_options options = {.no_banded_rows = LXW_TRUE}; * * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); * @endcode * * @image html tables6.png * */ uint8_t no_banded_rows; /** * The `banded_columns` parameter can be used to used to create columns of * alternating color in the table. It is off by default: * * @code * lxw_table_options options = {.banded_columns = LXW_TRUE}; * * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); * @endcode * * The banded columns formatting is shown in the image in the previous * section above. */ uint8_t banded_columns; /** * The `first_column` parameter can be used to highlight the first column * of the table. The type of highlighting will depend on the `style_type` * of the table. It may be bold text or a different color. It is off by * default: * * @code * lxw_table_options options = {.first_column = LXW_TRUE, .last_column = LXW_TRUE}; * * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); * @endcode * * @image html tables5.png */ uint8_t first_column; /** * The `last_column` parameter can be used to highlight the last column of * the table. The type of highlighting will depend on the `style` of the * table. It may be bold text or a different color. It is off by default: * * @code * lxw_table_options options = {.first_column = LXW_TRUE, .last_column = LXW_TRUE}; * * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); * @endcode * * The `last_column` formatting is shown in the image in the previous * section above. */ uint8_t last_column; /** * The `style_type` parameter can be used to set the style of the table, * in conjunction with the `style_type_number` parameter: * * @code * lxw_table_options options = { * .style_type = LXW_TABLE_STYLE_TYPE_LIGHT, * .style_type_number = 11, * }; * * worksheet_add_table(worksheet, RANGE("B3:G8"), &options); * @endcode * * * @image html tables11.png * * There are three types of table style in Excel: Light, Medium and Dark * which are represented using the #lxw_table_style_type enum values: * * - #LXW_TABLE_STYLE_TYPE_LIGHT * * - #LXW_TABLE_STYLE_TYPE_MEDIUM * * - #LXW_TABLE_STYLE_TYPE_DARK * * Within those ranges there are between 11 and 28 other style types which * can be set with `style_type_number` (depending on the style type). * Check Excel to find the style that you want. The dialog with the * options laid out in numeric order are shown below: * * @image html tables14.png * * The default table style in Excel is 'Table Style Medium 9' (highlighted * with a green border in the image above), which is set by default in * libxlsxwriter as: * * @code * lxw_table_options options = { * .style_type = LXW_TABLE_STYLE_TYPE_MEDIUM, * .style_type_number = 9, * }; * @endcode * * You can also turn the table style off by setting it to Light 0: * * @code * lxw_table_options options = { * .style_type = LXW_TABLE_STYLE_TYPE_LIGHT, * .style_type_number = 0, * }; * @endcode * * @image html tables13.png * */ uint8_t style_type; /** * The `style_type_number` parameter is used with `style_type` to set the * style of a worksheet table. */ uint8_t style_type_number; /** * The `total_row` parameter can be used to turn on the total row in the * last row of a table. It is distinguished from the other rows by a * different formatting and also with dropdown `SUBTOTAL` functions: * * @code * lxw_table_options options = {.total_row = LXW_TRUE}; * * worksheet_add_table(worksheet, RANGE("B3:G8"), &options); * @endcode * * @image html tables9.png * * The default total row doesn't have any captions or functions. These * must by specified via the `columns` parameter below. */ uint8_t total_row; /** * The `columns` parameter can be used to set properties for columns * within the table. See @ref ww_tables_columns for a detailed * explanation. */ lxw_table_column **columns; } lxw_table_options; typedef struct lxw_table_obj { char *name; char *total_string; lxw_table_column **columns; uint8_t banded_columns; uint8_t first_column; uint8_t last_column; uint8_t no_autofilter; uint8_t no_banded_rows; uint8_t no_header_row; uint8_t style_type; uint8_t style_type_number; uint8_t total_row; lxw_row_t first_row; lxw_col_t first_col; lxw_row_t last_row; lxw_col_t last_col; lxw_col_t num_cols; uint32_t id; char sqref[LXW_MAX_ATTRIBUTE_LENGTH]; char filter_sqref[LXW_MAX_ATTRIBUTE_LENGTH]; STAILQ_ENTRY (lxw_table_obj) list_pointers; } lxw_table_obj; /** * @brief Options for autofilter rules. * * Options to define an autofilter rule. * */ typedef struct lxw_filter_rule { /** The #lxw_filter_criteria to define the rule. */ uint8_t criteria; /** String value to which the criteria applies. */ const char *value_string; /** Numeric value to which the criteria applies (if value_string isn't used). */ double value; } lxw_filter_rule; typedef struct lxw_filter_rule_obj { uint8_t type; uint8_t is_custom; uint8_t has_blanks; lxw_col_t col_num; uint8_t criteria1; uint8_t criteria2; double value1; double value2; char *value1_string; char *value2_string; uint16_t num_list_filters; char **list; } lxw_filter_rule_obj; /** * @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; /** Object position - use one of the values of #lxw_object_position. * See @ref working_with_object_positioning.*/ uint8_t object_position; /** Optional description or "Alt text" for the image. This field can be * used to provide a text description of the image to help * accessibility. Defaults to the image filename as in Excel. Set to "" * to ignore the description field. */ const char *description; /** Optional parameter to help accessibility. It is used to mark the image * as decorative, and thus uninformative, for automated screen * readers. As in Excel, if this parameter is in use the `description` * field isn't written. */ uint8_t decorative; /** Add an optional hyperlink to the image. Follows the same URL rules * and types as `worksheet_write_url()`. */ const char *url; /** Add an optional mouseover tip for a hyperlink to the image. */ const char *tip; } lxw_image_options; /** * @brief Options for inserted charts. * * Options for modifying charts inserted via `worksheet_insert_chart_opt()`. * */ typedef struct lxw_chart_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 chart as a decimal. */ double x_scale; /** Y scale of the chart as a decimal. */ double y_scale; /** Object position - use one of the values of #lxw_object_position. * See @ref working_with_object_positioning.*/ uint8_t object_position; /** Optional description or "Alt text" for the chart. This field can be * used to provide a text description of the chart to help * accessibility. Defaults to the image filename as in Excel. Set to NULL * to ignore the description field. */ const char *description; /** Optional parameter to help accessibility. It is used to mark the chart * as decorative, and thus uninformative, for automated screen * readers. As in Excel, if this parameter is in use the `description` * field isn't written. */ uint8_t decorative; } lxw_chart_options; /* Internal struct to represent lxw_image_options and lxw_chart_options * values as well as internal metadata. */ typedef struct lxw_object_properties { int32_t x_offset; int32_t y_offset; double x_scale; double y_scale; lxw_row_t row; lxw_col_t col; char *filename; char *description; char *url; char *tip; uint8_t object_position; FILE *stream; uint8_t image_type; uint8_t is_image_buffer; char *image_buffer; size_t image_buffer_size; double width; double height; char *extension; double x_dpi; double y_dpi; lxw_chart *chart; uint8_t is_duplicate; uint8_t is_background; char *md5; char *image_position; uint8_t decorative; STAILQ_ENTRY (lxw_object_properties) list_pointers; } lxw_object_properties; /** * @brief Options for inserted comments. * * Options for modifying comments inserted via `worksheet_write_comment_opt()`. * */ typedef struct lxw_comment_options { /** This option is used to make a cell comment visible when the worksheet * is opened. The default behavior in Excel is that comments are * initially hidden. However, it is also possible in Excel to make * individual comments or all comments visible. You can make all * comments in the worksheet visible using the * `worksheet_show_comments()` function. Defaults to * LXW_COMMENT_DISPLAY_DEFAULT. See also @ref ww_comments_visible. */ uint8_t visible; /** This option is used to indicate the author of the cell comment. Excel * displays the author in the status bar at the bottom of the * worksheet. The default author for all cell comments in a worksheet can * be set using the `worksheet_set_comments_author()` function. Set to * NULL if not required. See also @ref ww_comments_author. */ const char *author; /** This option is used to set the width of the cell comment box * explicitly in pixels. The default width is 128 pixels. See also @ref * ww_comments_width. */ uint16_t width; /** This option is used to set the height of the cell comment box * explicitly in pixels. The default height is 74 pixels. See also @ref * ww_comments_height. */ uint16_t height; /** X scale of the comment as a decimal. See also * @ref ww_comments_x_scale. */ double x_scale; /** Y scale of the comment as a decimal. See also * @ref ww_comments_y_scale. */ double y_scale; /** This option is used to set the background color of cell comment * box. The color should be an RGB integer value, see @ref * working_with_colors. See also @ref ww_comments_color. */ lxw_color_t color; /** This option is used to set the font for the comment. The default font * is 'Tahoma'. See also @ref ww_comments_font_name. */ const char *font_name; /** This option is used to set the font size for the comment. The default * is 8. See also @ref ww_comments_font_size. */ double font_size; /** This option is used to set the font family number for the comment. * Not required very often. Set to 0. */ uint8_t font_family; /** This option is used to set the row in which the comment will * appear. By default Excel displays comments one cell to the right and * one cell above the cell to which the comment relates. The `start_row` * and `start_col` options should both be set to 0 if not used. See also * @ref ww_comments_start_row. */ lxw_row_t start_row; /** This option is used to set the column in which the comment will * appear. See the `start_row` option for more information and see also * @ref ww_comments_start_col. */ lxw_col_t start_col; /** Offset from the left of the cell in pixels. See also * @ref ww_comments_x_offset. */ int32_t x_offset; /** Offset from the top of the cell in pixels. See also * @ref ww_comments_y_offset. */ int32_t y_offset; } lxw_comment_options; /** * @brief Options for inserted buttons. * * Options for modifying buttons inserted via `worksheet_insert_button()`. * */ typedef struct lxw_button_options { /** Sets the caption on the button. The default is "Button n" where n is * the current number of buttons in the worksheet, including this * button. */ const char *caption; /** Name of the macro to run when the button is pressed. The macro must be * included with workbook_add_vba_project(). */ const char *macro; /** Optional description or "Alt text" for the button. This field can be * used to provide a text description of the button to help * accessibility. Set to NULL to ignore the description field. */ const char *description; /** This option is used to set the width of the cell button box * explicitly in pixels. The default width is 64 pixels. */ uint16_t width; /** This option is used to set the height of the cell button box * explicitly in pixels. The default height is 20 pixels. */ uint16_t height; /** X scale of the button as a decimal. */ double x_scale; /** Y scale of the button as a decimal. */ double y_scale; /** 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; } lxw_button_options; /* Internal structure for VML object options. */ typedef struct lxw_vml_obj { lxw_row_t row; lxw_col_t col; lxw_row_t start_row; lxw_col_t start_col; int32_t x_offset; int32_t y_offset; uint64_t col_absolute; uint64_t row_absolute; uint32_t width; uint32_t height; double x_dpi; double y_dpi; lxw_color_t color; uint8_t font_family; uint8_t visible; uint32_t author_id; uint32_t rel_index; double font_size; struct lxw_drawing_coords from; struct lxw_drawing_coords to; char *author; char *font_name; char *text; char *image_position; char *name; char *macro; STAILQ_ENTRY (lxw_vml_obj) list_pointers; } lxw_vml_obj; /** * @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. Must by * larger than 0.0. See `worksheet_set_header_opt()`. */ double margin; /** The left header image filename, with path if required. This should * have a corresponding `&G/&[Picture]` placeholder in the `&L` section of * the header/footer string. See `worksheet_set_header_opt()`. */ const char *image_left; /** The center header image filename, with path if required. This should * have a corresponding `&G/&[Picture]` placeholder in the `&C` section of * the header/footer string. See `worksheet_set_header_opt()`. */ const char *image_center; /** The right header image filename, with path if required. This should * have a corresponding `&G/&[Picture]` placeholder in the `&R` section of * the header/footer string. See `worksheet_set_header_opt()`. */ const char *image_right; } 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. Worksheets only. */ uint8_t objects; /** Turn off chartsheet content protection. */ uint8_t no_content; /** Turn off chartsheet objects. */ uint8_t no_objects; } lxw_protection; /* Internal struct to copy lxw_protection options and internal metadata. */ typedef struct lxw_protection_obj { uint8_t no_select_locked_cells; uint8_t no_select_unlocked_cells; uint8_t format_cells; uint8_t format_columns; uint8_t format_rows; uint8_t insert_columns; uint8_t insert_rows; uint8_t insert_hyperlinks; uint8_t delete_columns; uint8_t delete_rows; uint8_t sort; uint8_t autofilter; uint8_t pivot_tables; uint8_t scenarios; uint8_t objects; uint8_t no_content; uint8_t no_objects; uint8_t no_sheet; uint8_t is_configured; char hash[5]; } lxw_protection_obj; /** * @brief Struct to represent a rich string format/string pair. * * Arrays of this struct are used to define "rich" multi-format strings that * are passed to `worksheet_write_rich_string()`. Each struct represents a * fragment of the rich multi-format string with a lxw_format to define the * format for the string part. If the string fragment is unformatted then * `NULL` can be used for the format. */ typedef struct lxw_rich_string_tuple { /** The format for a string fragment in a rich string. NULL if the string * isn't formatted. */ lxw_format *format; /** The string fragment. */ const char *string; } lxw_rich_string_tuple; /** * @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; char *optimize_buffer; size_t optimize_buffer_size; struct lxw_table_rows *table; struct lxw_table_rows *hyperlinks; struct lxw_table_rows *comments; struct lxw_cell **array; struct lxw_merged_ranges *merged_ranges; struct lxw_selections *selections; struct lxw_data_validations *data_validations; struct lxw_cond_format_hash *conditional_formats; struct lxw_image_props *image_props; struct lxw_chart_props *chart_data; struct lxw_drawing_rel_ids *drawing_rel_ids; struct lxw_vml_drawing_rel_ids *vml_drawing_rel_ids; struct lxw_comment_objs *comment_objs; struct lxw_comment_objs *header_image_objs; struct lxw_comment_objs *button_objs; struct lxw_table_objs *table_objs; uint16_t table_count; lxw_row_t dim_rowmin; lxw_row_t dim_rowmax; lxw_col_t dim_colmin; lxw_col_t dim_colmax; lxw_sst *sst; const char *name; const char *quoted_name; const char *tmpdir; uint16_t index; uint8_t active; uint8_t selected; uint8_t hidden; uint16_t *active_sheet; uint16_t *first_sheet; uint8_t is_chartsheet; 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 outline_style; uint8_t outline_below; uint8_t outline_right; 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 vcenter; uint8_t zoom_scale_normal; uint8_t black_white; uint8_t num_validations; uint8_t has_dynamic_arrays; char *vba_codename; uint16_t num_buttons; 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 outline_row_level; uint8_t outline_col_level; uint8_t header_footer_changed; char *header; char *footer; 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; uint16_t max_url_length; lxw_row_t *hbreaks; lxw_col_t *vbreaks; uint16_t hbreaks_count; uint16_t vbreaks_count; uint32_t drawing_rel_id; uint32_t vml_drawing_rel_id; struct lxw_rel_tuples *external_hyperlinks; struct lxw_rel_tuples *external_drawing_links; struct lxw_rel_tuples *drawing_links; struct lxw_rel_tuples *vml_drawing_links; struct lxw_rel_tuples *external_table_links; struct lxw_panes panes; char top_left_cell[LXW_MAX_CELL_NAME_LENGTH]; struct lxw_protection_obj protection; lxw_drawing *drawing; lxw_format *default_url_format; uint8_t has_vml; uint8_t has_comments; uint8_t has_header_vml; uint8_t has_background_image; uint8_t has_buttons; lxw_rel_tuple *external_vml_comment_link; lxw_rel_tuple *external_comment_link; lxw_rel_tuple *external_vml_header_link; lxw_rel_tuple *external_background_link; char *comment_author; char *vml_data_id_str; char *vml_header_id_str; uint32_t vml_shape_id; uint32_t vml_header_id; uint32_t dxf_priority; uint8_t comment_display_default; uint32_t data_bar_2010_index; uint8_t has_ignore_errors; char *ignore_number_stored_as_text; char *ignore_eval_error; char *ignore_formula_differs; char *ignore_formula_range; char *ignore_formula_unlocked; char *ignore_empty_cell_reference; char *ignore_list_data_validation; char *ignore_calculated_column; char *ignore_two_digit_text_year; uint16_t excel_version; lxw_object_properties **header_footer_objs[LXW_HEADER_FOOTER_OBJS_MAX]; lxw_object_properties *header_left_object_props; lxw_object_properties *header_center_object_props; lxw_object_properties *header_right_object_props; lxw_object_properties *footer_left_object_props; lxw_object_properties *footer_center_object_props; lxw_object_properties *footer_right_object_props; lxw_object_properties *background_image; lxw_filter_rule_obj **filter_rules; lxw_col_t num_filter_rules; STAILQ_ENTRY (lxw_worksheet) list_pointers; } lxw_worksheet; /* * Worksheet initialization data. */ typedef struct lxw_worksheet_init_data { uint16_t index; uint8_t hidden; uint8_t optimize; uint16_t *active_sheet; uint16_t *first_sheet; lxw_sst *sst; const char *name; const char *quoted_name; const char *tmpdir; lxw_format *default_url_format; uint16_t max_url_length; } 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; lxw_vml_obj *comment; union { double number; int32_t string_id; const 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; /* Struct to represent a drawing Target/ID pair. */ typedef struct lxw_drawing_rel_id { uint32_t id; char *target; RB_ENTRY (lxw_drawing_rel_id) tree_pointers; } lxw_drawing_rel_id; /* *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 * * @note Excel doesn't support `NaN`, `Inf` or `-Inf` as a number value. If * you are writing data that contains these values then your application * should convert them to a string or handle them in some other way. * */ 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 * * See also @ref working_with_formulas. */ 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); /** * @brief Write an Excel 365 dynamic array formula to a worksheet range. * * @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 Dynamic Array formula to write to cell. * @param format A pointer to a Format instance or NULL. * * @return A #lxw_error code. * * * The `%worksheet_write_dynamic_array_formula()` function writes an Excel 365 * dynamic array formula to a cell range. Some examples of functions that * return dynamic arrays are: * * - `FILTER` * - `RANDARRAY` * - `SEQUENCE` * - `SORTBY` * - `SORT` * - `UNIQUE` * - `XLOOKUP` * - `XMATCH` * * Dynamic array formulas and their usage in libxlsxwriter is explained in * detail @ref ww_formulas_dynamic_arrays. The following is a example usage: * * @code * worksheet_write_dynamic_array_formula(worksheet, 1, 5, 1, 5, * "=_xlfn._xlws.FILTER(A1:D17,C1:C17=K2)", * NULL); * @endcode * * This formula gives the results shown in the image below. * * @image html dynamic_arrays02.png * * The need for the `_xlfn._xlws.` prefix in the formula is explained in @ref * ww_formulas_future. */ lxw_error worksheet_write_dynamic_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); /** * @brief Write an Excel 365 dynamic array 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_dynamic_formula()` function is similar to the * `worksheet_write_dynamic_array_formula()` function, shown above, except * that it writes a dynamic array formula to a single cell, rather than a * range. This is a syntactic shortcut since the array range isn't generally * known for a dynamic range and specifying the initial cell is sufficient for * Excel, as shown in the example below: * * @code * worksheet_write_dynamic_formula(worksheet, 7, 1, * "=_xlfn._xlws.SORT(_xlfn.UNIQUE(B2:B17))", * NULL); * @endcode * * This formula gives the following result: * * @image html dynamic_arrays01.png * * The need for the `_xlfn.` and `_xlfn._xlws.` prefixes in the formula is * explained in @ref ww_formulas_future. */ lxw_error worksheet_write_dynamic_formula(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t 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); lxw_error worksheet_write_dynamic_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); lxw_error worksheet_write_dynamic_formula_num(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t 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); /** * @brief Write a Unix datetime 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 unixtime The Unix datetime to write to the cell. * @param format A pointer to a Format instance or NULL. * * @return A #lxw_error code. * * The `%worksheet_write_unixtime()` function can be used to write dates and * times in Unix date format to the cell specified by `row` and * `column`. [Unix Time](https://en.wikipedia.org/wiki/Unix_time) which is a * common integer time format. It is defined as the number of seconds since * the Unix epoch (1970-01-01 00:00 UTC). Negative values can also be used for * dates prior to 1970: * * @dontinclude dates_and_times03.c * @skip 1970 * @until 2208988800 * * 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. * * The output from this code sample is: * * @image html date_example03.png * * Unixtime is generally represented with a 32 bit `time_t` type which has a * range of approximately 1900-12-14 to 2038-01-19. To access the full Excel * date range of 1900-01-01 to 9999-12-31 this function uses a 64 bit * parameter. * * See @ref working_with_dates for more information about handling dates and * times in libxlsxwriter. */ lxw_error worksheet_write_unixtime(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col, int64_t unixtime, lxw_format *format); /** * * @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", NULL); * @endcode * * @image html hyperlinks_short.png * * The `format` parameter is used to apply formatting to the cell. This * parameter can be `NULL`, in which case the default Excel blue underlined * hyperlink style will be used. If required a user defined @ref format.h * "Format" object can be used: * 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_RED); * * @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/", NULL); * worksheet_write_url(worksheet, 1, 0, "http://www.python.org/", NULL); * worksheet_write_url(worksheet, 2, 0, "https://www.python.org/", NULL); * worksheet_write_url(worksheet, 3, 0, "mailto:jmcnamara@cpan.org", NULL); * * @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. To do this we must also match the default URL format using * `workbook_get_default_url_format()`: * * @code * // Write a hyperlink with the default blue underline format. * worksheet_write_url(worksheet, 2, 0, "http://libxlsxwriter.github.io", NULL); * * // Get the default url format. * lxw_format *url_format = workbook_get_default_url_format(workbook); * * // Overwrite the hyperlink with a user defined string and default 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", NULL); * worksheet_write_url(worksheet, 1, 0, "internal:Sheet2!B2", NULL); * worksheet_write_url(worksheet, 2, 0, "internal:Sheet2!A1:B2", NULL); * worksheet_write_url(worksheet, 3, 0, "internal:'Sales Data'!A1", NULL); * worksheet_write_url(worksheet, 4, 0, "external:c:\\temp\\foo.xlsx", NULL); * worksheet_write_url(worksheet, 5, 0, "external:c:\\foo.xlsx#Sheet2!A1", NULL); * worksheet_write_url(worksheet, 6, 0, "external:..\\foo.xlsx", NULL); * worksheet_write_url(worksheet, 7, 0, "external:..\\foo.xlsx#Sheet2!A1", NULL); * worksheet_write_url(worksheet, 8, 0, "external:\\\\NET\\share\\foo.xlsx", NULL); * * @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", NULL); * @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", NULL); * * @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", NULL); * @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", NULL); * @endcode * * * Alternatively, you can use Unix style forward slashes. These are * translated internally to backslashes: * * @code * worksheet_write_url(worksheet, 0, 0, "external:c:/temp/foo.xlsx", NULL); * worksheet_write_url(worksheet, 1, 0, "external://NET/share/foo.xlsx", NULL); * * @endcode * * * **Note:** * * libxlsxwriter will escape the following characters in URLs as required * by Excel: `\s " < > \ [ ] ^ { }`. Existing URL `%%xx` style escapes in * the string are ignored to allow for user-escaped strings. * * **Note:** * * The maximum allowable URL length in recent versions of Excel is 2079 * characters. In older versions of Excel (and libxlsxwriter <= 0.8.8) the * limit was 255 characters. */ lxw_error worksheet_write_url(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col, const char *url, lxw_format *format); /* Don't document for now since the string option can be achieved by a * subsequent cell worksheet_write() as shown in the docs, and the * tooltip option isn't very useful. */ 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); /** * @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 numeric * 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 numeric result for the 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 * numeric 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. * * See also @ref working_with_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 Write a formula to a worksheet cell with a user defined string * 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 string result for the formula. * * @return A #lxw_error code. * * The `%worksheet_write_formula_str()` function writes a formula or Excel * function to the cell specified by `row` and `column` with a user defined * string result: * * @code * // The example formula is A & B -> AB. * worksheet_write_formula_str(worksheet, 0, 0, "=\"A\" & \"B\"", NULL, "AB"); * @endcode * * The `%worksheet_write_formula_str()` function is similar to the * `%worksheet_write_formula_num()` function except it writes a string result * instead or a numeric result. See `worksheet_write_formula_num()` for more * details on why/when these functions are required. * * One place where the `%worksheet_write_formula_str()` function may be required * is to specify an empty result which will force a recalculation of the formula * when loaded in LibreOffice. * * @code * worksheet_write_formula_str(worksheet, 0, 0, "=Sheet1!$A$1", NULL, ""); * @endcode * * See the FAQ @ref faq_formula_zero. * * See also @ref working_with_formulas. */ lxw_error worksheet_write_formula_str(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col, const char *formula, lxw_format *format, const char *result); /** * @brief Write a "Rich" multi-format 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 rich_string An array of format/string lxw_rich_string_tuple fragments. * @param format A pointer to a Format instance or NULL. * * @return A #lxw_error code. * * The `%worksheet_write_rich_string()` function is used to write strings with * multiple formats. For example to write the string 'This is **bold** * and this is *italic*' you would use the following: * * @code * lxw_format *bold = workbook_add_format(workbook); * format_set_bold(bold); * * lxw_format *italic = workbook_add_format(workbook); * format_set_italic(italic); * * lxw_rich_string_tuple fragment11 = {.format = NULL, .string = "This is " }; * lxw_rich_string_tuple fragment12 = {.format = bold, .string = "bold" }; * lxw_rich_string_tuple fragment13 = {.format = NULL, .string = " and this is "}; * lxw_rich_string_tuple fragment14 = {.format = italic, .string = "italic" }; * * lxw_rich_string_tuple *rich_string1[] = {&fragment11, &fragment12, * &fragment13, &fragment14, NULL}; * * worksheet_write_rich_string(worksheet, CELL("A1"), rich_string1, NULL); * * @endcode * * @image html rich_strings_small.png * * The basic rule is to break the string into fragments and put a lxw_format * object before the fragment that you want to format. So if we look at the * above example again: * * This is **bold** and this is *italic* * * The would be broken down into 4 fragments: * * default: |This is | * bold: |bold| * default: | and this is | * italic: |italic| * * This in then converted to the lxw_rich_string_tuple fragments shown in the * example above. For the default format we use `NULL`. * * The fragments are passed to `%worksheet_write_rich_string()` as a `NULL` * terminated array: * * @code * lxw_rich_string_tuple *rich_string1[] = {&fragment11, &fragment12, * &fragment13, &fragment14, NULL}; * * worksheet_write_rich_string(worksheet, CELL("A1"), rich_string1, NULL); * * @endcode * * **Note**: * Excel doesn't allow the use of two consecutive formats in a rich string or * an empty string fragment. For either of these conditions a warning is * raised and the input to `%worksheet_write_rich_string()` is ignored. * */ lxw_error worksheet_write_rich_string(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col, lxw_rich_string_tuple *rich_string[], lxw_format *format); /** * @brief Write a comment 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 The comment string to be written. * * @return A #lxw_error code. * * The `%worksheet_write_comment()` function is used to add a comment to a * cell. A comment is indicated in Excel by a small red triangle in the upper * right-hand corner of the cell. Moving the cursor over the red triangle will * reveal the comment. * * The following example shows how to add a comment to a cell: * * @code * worksheet_write_comment(worksheet, 0, 0, "This is a comment"); * @endcode * * @image html comments1.png * * See also @ref working_with_comments * */ lxw_error worksheet_write_comment(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col, const char *string); /** * @brief Write a comment to 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 string The comment string to be written. * @param options #lxw_comment_options to control position and format * of the comment. * * @return A #lxw_error code. * * The `%worksheet_write_comment_opt()` function is used to add a comment to a * cell with option that control the position, format and metadata of the * comment. A comment is indicated in Excel by a small red triangle in the * upper right-hand corner of the cell. Moving the cursor over the red * triangle will reveal the comment. * * The following example shows how to add a comment to a cell with options: * * @code * lxw_comment_options options = {.visible = LXW_COMMENT_DISPLAY_VISIBLE}; * * worksheet_write_comment_opt(worksheet, CELL("C6"), "Hello.", &options); * @endcode * * The following options are available in #lxw_comment_options: * * - `author` * - `visible` * - `width` * - `height` * - `x_scale` * - `y_scale` * - `color` * - `font_name` * - `font_size` * - `start_row` * - `start_col` * - `x_offset` * - `y_offset` * * @image html comments2.png * * Comment options are explained in detail in the @ref ww_comments_properties * section of the docs. */ lxw_error worksheet_write_comment_opt(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col, const char *string, lxw_comment_options *options); /** * @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, in character units. * @param format A pointer to a Format instance or NULL. * * @return A #lxw_error code. * * 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 height is specified in character units. To specify the height in pixels * use the `worksheet_set_row_pixels()` function. * * 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. * * @return A #lxw_error code. * * 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: * * - `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 options1 = {.hidden = 1, .level = 0, .collapsed = 0}; * * // Hide the fourth and fifth (zero indexed) rows. * worksheet_set_row_opt(worksheet, 3, LXW_DEF_ROW_HEIGHT, NULL, &options1); * worksheet_set_row_opt(worksheet, 4, LXW_DEF_ROW_HEIGHT, NULL, &options1); * * @endcode * * @image html hide_row_col2.png * * The `"hidden"`, `"level"`, and `"collapsed"`, options can also be used to * create Outlines and Grouping. See @ref working_with_outlines. * * @code * // The option structs with the outline level set. * lxw_row_col_options options1 = {.hidden = 0, .level = 2, .collapsed = 0}; * lxw_row_col_options options2 = {.hidden = 0, .level = 1, .collapsed = 0}; * * * // Set the row options with the outline level. * worksheet_set_row_opt(worksheet, 1, LXW_DEF_ROW_HEIGHT, NULL, &options1); * worksheet_set_row_opt(worksheet, 2, LXW_DEF_ROW_HEIGHT, NULL, &options1); * worksheet_set_row_opt(worksheet, 3, LXW_DEF_ROW_HEIGHT, NULL, &options1); * worksheet_set_row_opt(worksheet, 4, LXW_DEF_ROW_HEIGHT, NULL, &options1); * worksheet_set_row_opt(worksheet, 5, LXW_DEF_ROW_HEIGHT, NULL, &options2); * * worksheet_set_row_opt(worksheet, 6, LXW_DEF_ROW_HEIGHT, NULL, &options1); * worksheet_set_row_opt(worksheet, 7, LXW_DEF_ROW_HEIGHT, NULL, &options1); * worksheet_set_row_opt(worksheet, 8, LXW_DEF_ROW_HEIGHT, NULL, &options1); * worksheet_set_row_opt(worksheet, 9, LXW_DEF_ROW_HEIGHT, NULL, &options1); * worksheet_set_row_opt(worksheet, 10, LXW_DEF_ROW_HEIGHT, NULL, &options2); * @endcode * * @image html outline1.png * */ 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 a row of cells, with the height in pixels. * * @param worksheet Pointer to a lxw_worksheet instance to be updated. * @param row The zero indexed row number. * @param pixels The row height in pixels. * @param format A pointer to a Format instance or NULL. * * @return A #lxw_error code. * * The `%worksheet_set_row_pixels()` function is the same as the * `worksheet_set_row()` function except that the height can be set in pixels * * @code * // Set the height of Row 1 to 20 pixels. * worksheet_set_row_pixels(worksheet, 0, 20, NULL); * @endcode * * If you wish to set the format of a row without changing the height you can * pass the default row height in pixels: #LXW_DEF_ROW_HEIGHT_PIXELS. */ lxw_error worksheet_set_row_pixels(lxw_worksheet *worksheet, lxw_row_t row, uint32_t pixels, lxw_format *format); /** * @brief Set the properties for a row of cells, with the height in pixels. * * @param worksheet Pointer to a lxw_worksheet instance to be updated. * @param row The zero indexed row number. * @param pixels The row height in pixels. * @param format A pointer to a Format instance or NULL. * @param options Optional row parameters: hidden, level, collapsed. * * @return A #lxw_error code. * * The `%worksheet_set_row_pixels_opt()` function is the same as the * `worksheet_set_row_opt()` function except that the height can be set in * pixels. * */ lxw_error worksheet_set_row_pixels_opt(lxw_worksheet *worksheet, lxw_row_t row, uint32_t pixels, 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. * * @return A #lxw_error code. * * 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. To set the width * in pixels use the `worksheet_set_column_pixels()` function. * * 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_WIDTH, 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. * * @return A #lxw_error code. * * 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: * * - `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 options1 = {.hidden = 1, .level = 0, .collapsed = 0}; * * worksheet_set_column_opt(worksheet, COLS("D:E"), LXW_DEF_COL_WIDTH, NULL, &options1); * @endcode * * @image html hide_row_col3.png * * The `"hidden"`, `"level"`, and `"collapsed"`, options can also be used to * create Outlines and Grouping. See @ref working_with_outlines. * * @code * lxw_row_col_options options1 = {.hidden = 0, .level = 1, .collapsed = 0}; * * worksheet_set_column_opt(worksheet, COLS("B:G"), 5, NULL, &options1); * @endcode * * @image html outline8.png */ 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 Set the properties for one or more columns of cells, with the width * in pixels. * * @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 pixels The width of the column(s) in pixels. * @param format A pointer to a Format instance or NULL. * * @return A #lxw_error code. * * The `%worksheet_set_column_pixels()` function is the same as * `worksheet_set_column()` function except that the width can be set in * pixels: * * @code * // Column width set to 75 pixels, the same as 10 character units. * worksheet_set_column(worksheet, 5, 5, 75, NULL); * @endcode * * @image html set_column_pixels.png * * If you wish to set the format of a column without changing the width you can * pass the default column width in pixels: #LXW_DEF_COL_WIDTH_PIXELS. */ lxw_error worksheet_set_column_pixels(lxw_worksheet *worksheet, lxw_col_t first_col, lxw_col_t last_col, uint32_t pixels, lxw_format *format); /** * @brief Set the properties for one or more columns of cells with options, * with the width in pixels. * * @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 pixels The width of the column(s) in pixels. * @param format A pointer to a Format instance or NULL. * @param options Optional row parameters: hidden, level, collapsed. * * @return A #lxw_error code. * * The `%worksheet_set_column_pixels_opt()` function is the same as the * `worksheet_set_column_opt()` function except that the width can be set in * pixels. * */ lxw_error worksheet_set_column_pixels_opt(lxw_worksheet *worksheet, lxw_col_t first_col, lxw_col_t last_col, uint32_t pixels, 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, GIF 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. See @ref working_with_object_positioning. * * 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 with the following members/options: * * - `x_offset`: Offset from the left of the cell in pixels. * - `y_offset`: Offset from the top of the cell in pixels. * - `x_scale`: X scale of the image as a decimal. * - `y_scale`: Y scale of the image as a decimal. * - `object_position`: See @ref working_with_object_positioning. * - `description`: Optional description or "Alt text" for the image. * - `decorative`: Optional parameter to mark image as decorative. * - `url`: Add an optional hyperlink to the image. * - `tip`: Add an optional mouseover tip for a hyperlink to the image. * * For example, 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 * * The `url` field of lxw_image_options can be use to used to add a hyperlink * to an image: * * @code * lxw_image_options options = {.url = "https://github.com/jmcnamara"}; * * worksheet_insert_image_opt(worksheet, 3, 1, "logo.png", &options); * @endcode * * The supported URL formats are the same as those supported by the * `worksheet_write_url()` method and the same rules/limits apply. * * The `tip` field of lxw_image_options can be use to used to add a mouseover * tip to the hyperlink: * * @code * lxw_image_options options = {.url = "https://github.com/jmcnamara", .tip = "GitHub"}; * * worksheet_insert_image_opt(worksheet, 4, 1, "logo.png", &options); * @endcode * * @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 an image in a worksheet cell, from a memory buffer. * * @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 image_buffer Pointer to an array of bytes that holds the image data. * @param image_size The size of the array of bytes. * * @return A #lxw_error code. * * This function can be used to insert a image into a worksheet from a memory * buffer: * * @code * worksheet_insert_image_buffer(worksheet, CELL("B3"), image_buffer, image_size); * @endcode * * @image html image_buffer.png * * The buffer should be a pointer to an array of unsigned char data with a * specified size. * * See `worksheet_insert_image()` for details about the supported image * formats, and other image features. */ lxw_error worksheet_insert_image_buffer(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col, const unsigned char *image_buffer, size_t image_size); /** * @brief Insert an image in a worksheet cell, from a memory buffer. * * @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 image_buffer Pointer to an array of bytes that holds the image data. * @param image_size The size of the array of bytes. * @param options Optional image parameters. * * @return A #lxw_error code. * * The `%worksheet_insert_image_buffer_opt()` function is like * `worksheet_insert_image_buffer()` function except that it takes an optional * #lxw_image_options struct * #lxw_image_options struct with the following members/options: * * - `x_offset`: Offset from the left of the cell in pixels. * - `y_offset`: Offset from the top of the cell in pixels. * - `x_scale`: X scale of the image as a decimal. * - `y_scale`: Y scale of the image as a decimal. * - `object_position`: See @ref working_with_object_positioning. * - `description`: Optional description or "Alt text" for the image. * - `decorative`: Optional parameter to mark image as decorative. * - `url`: Add an optional hyperlink to the image. * - `tip`: Add an optional mouseover tip for a hyperlink to the image. * * For example, to scale and position the image: * * @code * lxw_image_options options = {.x_offset = 32, .y_offset = 4, * .x_scale = 2, .y_scale = 1}; * * worksheet_insert_image_buffer_opt(worksheet, CELL("B3"), image_buffer, image_size, &options); * @endcode * * @image html image_buffer_opt.png * * The buffer should be a pointer to an array of unsigned char data with a * specified size. * * See `worksheet_insert_image_buffer_opt()` for details about the supported * image formats, and other image options. */ lxw_error worksheet_insert_image_buffer_opt(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col, const unsigned char *image_buffer, size_t image_size, lxw_image_options *options); /** * @brief Set the background image for a worksheet. * * @param worksheet Pointer to a lxw_worksheet instance to be updated. * @param filename The image filename, with path if required. * * @return A #lxw_error code. * * The `%worksheet_set_background()` function can be used to set the * background image for a worksheet: * * @code * worksheet_set_background(worksheet, "logo.png"); * @endcode * * @image html background.png * * The ``set_background()`` method supports all the image formats supported by * `worksheet_insert_image()`. * * Some people use this method to add a watermark background to their * document. However, Microsoft recommends using a header image [to set a * watermark][watermark]. The choice of method depends on whether you want the * watermark to be visible in normal viewing mode or just when the file is * printed. In libxlsxwriter you can get the header watermark effect using * `worksheet_set_header()`: * * @code * lxw_header_footer_options header_options = {.image_center = "watermark.png"}; * worksheet_set_header_opt(worksheet, "&C&G", &header_options); * @endcode * * [watermark]:https://support.microsoft.com/en-us/office/add-a-watermark-in-excel-a372182a-d733-484e-825c-18ddf3edf009 * */ lxw_error worksheet_set_background(lxw_worksheet *worksheet, const char *filename); /** * @brief Set the background image for a worksheet, from a buffer. * * @param worksheet Pointer to a lxw_worksheet instance to be updated. * @param image_buffer Pointer to an array of bytes that holds the image data. * @param image_size The size of the array of bytes. * * @return A #lxw_error code. * * This function can be used to insert a background image into a worksheet * from a memory buffer: * * @code * worksheet_set_background_buffer(worksheet, image_buffer, image_size); * @endcode * * The buffer should be a pointer to an array of unsigned char data with a * specified size. * * See `worksheet_set_background()` for more details. * */ lxw_error worksheet_set_background_buffer(lxw_worksheet *worksheet, const unsigned char *image_buffer, size_t image_size); /** * @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()` function 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_chart_options struct to scale and position the chart: * * @code * lxw_chart_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 * */ lxw_error worksheet_insert_chart_opt(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col, lxw_chart *chart, lxw_chart_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 autofilter3.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 * * In order to apply a filter condition it is necessary to add filter rules to * the columns using either the `%worksheet_filter_column()`, * `%worksheet_filter_column2()` or `%worksheet_filter_list()` functions: * * - `worksheet_filter_column()`: filter on a single criterion such as "Column == * East". More complex conditions such as "<=" or ">=" can also be use. * * - `worksheet_filter_column2()`: filter on two criteria such as "Column == East * or Column == West". Complex conditions can also be used. * * - `worksheet_filter_list()`: filter on a list of values such as "Column in (East, West, * North)". * * These functions are explained below. It isn't sufficient to just specify * the filter condition. You must also hide any rows that don't match the * filter condition. See @ref ww_autofilters_data for more details. * */ 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 Write a filter rule to an autofilter column. * * @param worksheet Pointer to a lxw_worksheet instance to be updated. * @param col The column in the autofilter that the rule applies to. * @param rule The lxw_filter_rule autofilter rule. * * @return A #lxw_error code. * * The `worksheet_filter_column` function can be used to filter columns in a * autofilter range based on single rule conditions: * * @code * lxw_filter_rule filter_rule = {.criteria = LXW_FILTER_CRITERIA_EQUAL_TO, * .value_string = "East"}; * * worksheet_filter_column(worksheet, 0, &filter_rule); *@endcode * * @image html autofilter4.png * * The rules and criteria are explained in more detail in @ref * ww_autofilters_criteria in @ref working_with_autofilters. * * The `col` parameter is a zero indexed column number and must refer to a * column in an existing autofilter created with `worksheet_autofilter()`. * * It isn't sufficient to just specify the filter condition. You must also * hide any rows that don't match the filter condition. See @ref * ww_autofilters_data for more details. */ lxw_error worksheet_filter_column(lxw_worksheet *worksheet, lxw_col_t col, lxw_filter_rule *rule); /** * @brief Write two filter rules to an autofilter column. * * @param worksheet Pointer to a lxw_worksheet instance to be updated. * @param col The column in the autofilter that the rules applies to. * @param rule1 First lxw_filter_rule autofilter rule. * @param rule2 Second lxw_filter_rule autofilter rule. * @param and_or A #lxw_filter_operator and/or operator. * * @return A #lxw_error code. * * The `worksheet_filter_column2` function can be used to filter columns in a autofilter * range based on two rule conditions: * * @code * lxw_filter_rule filter_rule1 = {.criteria = LXW_FILTER_CRITERIA_EQUAL_TO, * .value_string = "East"}; * * lxw_filter_rule filter_rule2 = {.criteria = LXW_FILTER_CRITERIA_EQUAL_TO, * .value_string = "South"}; * * worksheet_filter_column2(worksheet, 0, &filter_rule1, &filter_rule2, LXW_FILTER_OR); * @endcode * * @image html autofilter5.png * * The rules and criteria are explained in more detail in @ref * ww_autofilters_criteria in @ref working_with_autofilters. * * The `col` parameter is a zero indexed column number and must refer to a * column in an existing autofilter created with `worksheet_autofilter()`. * * The `and_or` parameter is either "and (LXW_FILTER_AND)" or "or (LXW_FILTER_OR)". * * It isn't sufficient to just specify the filter condition. You must also * hide any rows that don't match the filter condition. See @ref * ww_autofilters_data for more details. */ lxw_error worksheet_filter_column2(lxw_worksheet *worksheet, lxw_col_t col, lxw_filter_rule *rule1, lxw_filter_rule *rule2, uint8_t and_or); /** * @brief Write multiple string filters to an autofilter column. * * @param worksheet Pointer to a lxw_worksheet instance to be updated. * @param col The column in the autofilter that the rules applies to. * @param list A NULL terminated array of strings to filter on. * * @return A #lxw_error code. * * The `worksheet_filter_column_list()` function can be used specify multiple * string matching criteria. This is a newer type of filter introduced in * Excel 2007. Prior to that it was only possible to have either 1 or 2 filter * conditions, such as the ones used by `worksheet_filter_column()` and * `worksheet_filter_column2()`. * * As an example, consider a column that contains data for the months of the * year. The `%worksheet_filter_list()` function can be used to filter out * data rows for different months: * * @code * char* list[] = {"March", "April", "May", NULL}; * * worksheet_filter_list(worksheet, 0, list); * @endcode * * @image html autofilter2.png * * * Note, the array must be NULL terminated to indicate the end of the array of * strings. To filter blanks as part of the list use `Blanks` as a list item: * * @code * char* list[] = {"March", "April", "May", "Blanks", NULL}; * * worksheet_filter_list(worksheet, 0, list); * @endcode * * It isn't sufficient to just specify the filter condition. You must also * hide any rows that don't match the filter condition. See @ref * ww_autofilters_data for more details. */ lxw_error worksheet_filter_list(lxw_worksheet *worksheet, lxw_col_t col, const char **list); /** * @brief Add a data validation to a 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 validation A #lxw_data_validation object to control the validation. * * @return A #lxw_error code. * * The `%worksheet_data_validation_cell()` function is used to construct an * Excel data validation or to limit the user input to a dropdown list of * values: * * @code * * lxw_data_validation *data_validation = calloc(1, sizeof(lxw_data_validation)); * * data_validation->validate = LXW_VALIDATION_TYPE_INTEGER; * data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN; * data_validation->minimum_number = 1; * data_validation->maximum_number = 10; * * worksheet_data_validation_cell(worksheet, 2, 1, data_validation); * * // Same as above with the CELL() macro. * worksheet_data_validation_cell(worksheet, CELL("B3"), data_validation); * * @endcode * * @image html data_validate4.png * * Data validation and the various options of #lxw_data_validation are * described in more detail in @ref working_with_data_validation. */ lxw_error worksheet_data_validation_cell(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col, lxw_data_validation *validation); /** * @brief Add a data validation to a range. * * @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 validation A #lxw_data_validation object to control the validation. * * @return A #lxw_error code. * * The `%worksheet_data_validation_range()` function is the same as the * `%worksheet_data_validation_cell()`, see above, except the data validation * is applied to a range of cells: * * @code * * lxw_data_validation *data_validation = calloc(1, sizeof(lxw_data_validation)); * * data_validation->validate = LXW_VALIDATION_TYPE_INTEGER; * data_validation->criteria = LXW_VALIDATION_CRITERIA_BETWEEN; * data_validation->minimum_number = 1; * data_validation->maximum_number = 10; * * worksheet_data_validation_range(worksheet, 2, 1, 4, 1, data_validation); * * // Same as above with the RANGE() macro. * worksheet_data_validation_range(worksheet, RANGE("B3:B5"), data_validation); * * @endcode * * Data validation and the various options of #lxw_data_validation are * described in more detail in @ref working_with_data_validation. */ lxw_error worksheet_data_validation_range(lxw_worksheet *worksheet, lxw_row_t first_row, lxw_col_t first_col, lxw_row_t last_row, lxw_col_t last_col, lxw_data_validation *validation); /** * @brief Add a conditional format 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 conditional_format A #lxw_conditional_format object to control the * conditional format. * * @return A #lxw_error code. * * The `%worksheet_conditional_format_cell()` function is used to set a * conditional format for a cell in a worksheet: * * @code * conditional_format->type = LXW_CONDITIONAL_TYPE_CELL; * conditional_format->criteria = LXW_CONDITIONAL_CRITERIA_GREATER_THAN_OR_EQUAL_TO; * conditional_format->value = 50; * conditional_format->format = format1; * worksheet_conditional_format_cell(worksheet, CELL("A1"), conditional_format); * @endcode * * The conditional format parameters is specified in #lxw_conditional_format. * * See @ref working_with_conditional_formatting for full details. */ lxw_error worksheet_conditional_format_cell(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col, lxw_conditional_format *conditional_format); /** * @brief Add a conditional format to a worksheet range. * * @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 conditional_format A #lxw_conditional_format object to control the * conditional format. * * @return A #lxw_error code. * * The `%worksheet_conditional_format_cell()` function is used to set a * conditional format for a range of cells in a worksheet: * * @code * conditional_format->type = LXW_CONDITIONAL_TYPE_CELL; * conditional_format->criteria = LXW_CONDITIONAL_CRITERIA_GREATER_THAN_OR_EQUAL_TO; * conditional_format->value = 50; * conditional_format->format = format1; * worksheet_conditional_format_range(worksheet1, RANGE("B3:K12"), conditional_format); * * conditional_format->type = LXW_CONDITIONAL_TYPE_CELL; * conditional_format->criteria = LXW_CONDITIONAL_CRITERIA_LESS_THAN; * conditional_format->value = 50; * conditional_format->format = format2; * worksheet_conditional_format_range(worksheet1, RANGE("B3:K12"), conditional_format); * @endcode * * Output: * * @image html conditional_format1.png * * * The conditional format parameters is specified in #lxw_conditional_format. * * See @ref working_with_conditional_formatting for full details. */ lxw_error worksheet_conditional_format_range(lxw_worksheet *worksheet, lxw_row_t first_row, lxw_col_t first_col, lxw_row_t last_row, lxw_col_t last_col, lxw_conditional_format *conditional_format); /** * @brief Insert a button 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 options A #lxw_button_options object to set the button properties. * * @return A #lxw_error code. * * The `%worksheet_insert_button()` function can be used to insert an Excel * form button into a worksheet. This function is generally only useful when * used in conjunction with the `workbook_add_vba_project()` function to tie * the button to a macro from an embedded VBA project: * * @code * lxw_button_options options = {.caption = "Press Me", * .macro = "say_hello"}; * * worksheet_insert_button(worksheet, 2, 1, &options); * @endcode * * @image html macros.png * * The button properties are set using the lxw_button_options struct. * * See also @ref working_with_macros */ lxw_error worksheet_insert_button(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col, lxw_button_options *options); /** * @brief Add an Excel table to 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. * @param options A #lxw_table_options struct to define the table options. * * @return A #lxw_error code. * * The `%worksheet_add_table()` function is used to add a table to a * worksheet. Tables in Excel are a way of grouping a range of cells into a * single entity that has common formatting or that can be referenced from * formulas. Tables can have column headers, autofilters, total rows, column * formulas and default formatting. * * @code * worksheet_add_table(worksheet, 2, 1, 6, 5, NULL); * @endcode * * Output: * * @image html tables1.png * * See @ref working_with_tables for more detailed usage information and also * @ref tables.c. * */ lxw_error worksheet_add_table(lxw_worksheet *worksheet, lxw_row_t first_row, lxw_col_t first_col, lxw_row_t last_row, lxw_col_t last_col, lxw_table_options *options); /** * @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 A 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 first visible cell at the top left of a worksheet. * * @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_set_top_left_cell()` function can be used to set the * top leftmost visible cell in the worksheet: * * @code * worksheet_set_top_left_cell(worksheet, 31, 26); * worksheet_set_top_left_cell(worksheet, CELL("AA32")); // Same as above. * @endcode * * @image html top_left_cell.png * */ void worksheet_set_top_left_cell(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t 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 | * | `&[Picture]` | Images | Image placeholder | * | `&G` | | Same as `&[Picture]` | * | `&&` | Miscellaneous | Literal ampersand & | * * Note: inserting images requires the `worksheet_set_header_opt()` function. * * 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" | sed 's/&/\&/g' * * * &L&P * * * @endcode * * 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 * Excel requires that the header or footer string cannot be longer than 255 * characters, including the control 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. * * The #lxw_header_footer_options options are: * * - `margin`: Header or footer margin in inches. The value must by larger * than 0.0. The Excel default is 0.3. * * - `image_left`: The left header image filename, with path if required. This * should have a corresponding `&G/&[Picture]` placeholder in the `&L` * section of the header/footer string. * * - `image_center`: The center header image filename, with path if * required. This should have a corresponding `&G/&[Picture]` placeholder in * the `&C` section of the header/footer string. * * - `image_right`: The right header image filename, with path if * required. This should have a corresponding `&G/&[Picture]` placeholder in * the `&R` section of the header/footer string. * * @code * lxw_header_footer_options header_options = { .margin = 0.2 }; * * worksheet_set_header_opt(worksheet, "Some text", &header_options); * @endcode * * Images can be inserted in the header by specifying the `&[Picture]` * placeholder and a filename/path to the image: * * @code * lxw_header_footer_options header_options = {.image_left = "logo.png"}; * * worksheet_set_header_opt(worksheet, "&L&[Picture]", &header_options); * @endcode * * @image html headers_footers.png * */ 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/first page number when printing. * * @param worksheet Pointer to a lxw_worksheet instance to be updated. * @param start_page Page number of the starting page when printing. * * The `%worksheet_set_start_page()` function is used to set the number number * of the first page when the worksheet is printed out. It is the same as the * "First Page Number" option in Excel: * * @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 Set the worksheet to print in black and white * * @param worksheet Pointer to a lxw_worksheet instance to be updated. * * Set the option to print the worksheet in black and white: * @code * worksheet_print_black_and_white(worksheet); * @endcode */ void worksheet_print_black_and_white(lxw_worksheet *worksheet); /** * @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:** Sheet 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. */ void worksheet_protect(lxw_worksheet *worksheet, const char *password, lxw_protection *options); /** * @brief Set the Outline and Grouping display properties. * * @param worksheet Pointer to a lxw_worksheet instance to be updated. * @param visible Outlines are visible. Optional, defaults to True. * @param symbols_below Show row outline symbols below the outline bar. * @param symbols_right Show column outline symbols to the right of outline. * @param auto_style Use Automatic outline style. * * The `%worksheet_outline_settings()` method is used to control the * appearance of outlines in Excel. Outlines are described the section on * @ref working_with_outlines. * * The `visible` parameter is used to control whether or not outlines are * visible. Setting this parameter to False will cause all outlines on the * worksheet to be hidden. They can be un-hidden in Excel by means of the * "Show Outline Symbols" command button. The default Excel setting is True * for visible outlines. * * The `symbols_below` parameter is used to control whether the row outline * symbol will appear above or below the outline level bar. The default Excel * setting is True for symbols to appear below the outline level bar. * * The `symbols_right` parameter is used to control whether the column outline * symbol will appear to the left or the right of the outline level bar. The * default Excel setting is True for symbols to appear to the right of the * outline level bar. * * The `auto_style` parameter is used to control whether the automatic outline * generator in Excel uses automatic styles when creating an outline. This has * no effect on a file generated by XlsxWriter but it does have an effect on * how the worksheet behaves after it is created. The default Excel setting is * False for "Automatic Styles" to be turned off. * * The default settings for all of these parameters in libxlsxwriter * correspond to Excel's default parameters and are shown below: * * @code * worksheet_outline_settings(worksheet1, LXW_TRUE, LXW_TRUE, LXW_TRUE, LXW_FALSE); * @endcode * * The worksheet parameters controlled by `worksheet_outline_settings()` are * rarely used. */ void worksheet_outline_settings(lxw_worksheet *worksheet, uint8_t visible, uint8_t symbols_below, uint8_t symbols_right, uint8_t auto_style); /** * @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); /** * @brief Set the VBA name for the worksheet. * * @param worksheet Pointer to a lxw_worksheet instance. * @param name Name of the worksheet used by VBA. * * @return A #lxw_error. * * The `worksheet_set_vba_name()` function can be used to set the VBA name for * the worksheet. This is sometimes required when a vbaProject macro included * via `workbook_add_vba_project()` refers to the worksheet by a name other * than the worksheet name: * * @code * workbook_set_vba_name (workbook, "MyWorkbook"); * worksheet_set_vba_name(worksheet, "MySheet1"); * @endcode * * In general Excel uses the worksheet name such as "Sheet1" as the VBA name. * However, this can be changed in the VBA environment or if the the macro was * extracted from a foreign language version of Excel. * * See also @ref working_with_macros */ lxw_error worksheet_set_vba_name(lxw_worksheet *worksheet, const char *name); /** * @brief Make all comments in the worksheet visible. * * @param worksheet Pointer to a lxw_worksheet instance. * * This `%worksheet_show_comments()` function is used to make all cell * comments visible when a worksheet is opened: * * @code * worksheet_show_comments(worksheet); * @endcode * * Individual comments can be made visible or hidden using the `visible` * option of the #lxw_comment_options struct and the `worksheet_write_comment_opt()` * function (see above and @ref ww_comments_visible). */ void worksheet_show_comments(lxw_worksheet *worksheet); /** * @brief Set the default author of the cell comments. * * @param worksheet Pointer to a lxw_worksheet instance. * @param author The name of the comment author. * * This `%worksheet_set_comments_author()` function is used to set the * default author of all cell comments: * * @code * worksheet_set_comments_author(worksheet, "Jane Gloriana Villanueva") * @endcode * * Individual authors can be set using the `author` option of the * #lxw_comment_options struct and the `worksheet_write_comment_opt()` * function (see above and @ref ww_comments_author). */ void worksheet_set_comments_author(lxw_worksheet *worksheet, const char *author); /** * @brief Ignore various Excel errors/warnings in a worksheet for user * defined ranges. * * @param worksheet Pointer to a lxw_worksheet instance. * @param type The type of error/warning to ignore. See #lxw_ignore_errors. * @param range The range(s) for which the error/warning should be ignored. * * @return A #lxw_error. * * * The `%worksheet_ignore_errors()` function can be used to ignore various * worksheet cell errors/warnings. For example the following code writes a string * that looks like a number: * * @code * worksheet_write_string(worksheet, CELL("D2"), "123", NULL); * @endcode * * This causes Excel to display a small green triangle in the top left hand * corner of the cell to indicate an error/warning: * * @image html ignore_errors1.png * * Sometimes these warnings are useful indicators that there is an issue in * the spreadsheet but sometimes it is preferable to turn them off. Warnings * can be turned off at the Excel level for all workbooks and worksheets by * using the using "Excel options -> Formulas -> Error checking * rules". Alternatively you can turn them off for individual cells in a * worksheet, or ranges of cells, using the `%worksheet_ignore_errors()` * function with different #lxw_ignore_errors options and ranges like this: * * @code * worksheet_ignore_errors(worksheet, LXW_IGNORE_NUMBER_STORED_AS_TEXT, "C3"); * worksheet_ignore_errors(worksheet, LXW_IGNORE_EVAL_ERROR, "C6"); * @endcode * * The range can be a single cell, a range of cells, or multiple cells and ranges * separated by spaces: * * @code * // Single cell. * worksheet_ignore_errors(worksheet, LXW_IGNORE_NUMBER_STORED_AS_TEXT, "C6"); * * // Or a single range: * worksheet_ignore_errors(worksheet, LXW_IGNORE_NUMBER_STORED_AS_TEXT, "C6:G8"); * * // Or multiple cells and ranges: * worksheet_ignore_errors(worksheet, LXW_IGNORE_NUMBER_STORED_AS_TEXT, "C6 E6 G1:G20 J2:J6"); * @endcode * * @note Calling `%worksheet_ignore_errors()` more than once for the same * #lxw_ignore_errors type will overwrite the previous range. * * You can turn off warnings for an entire column by specifying the range from * the first cell in the column to the last cell in the column: * * @code * worksheet_ignore_errors(worksheet, LXW_IGNORE_NUMBER_STORED_AS_TEXT, "A1:A1048576"); * @endcode * * Or for the entire worksheet by specifying the range from the first cell in * the worksheet to the last cell in the worksheet: * * @code * worksheet_ignore_errors(worksheet, LXW_IGNORE_NUMBER_STORED_AS_TEXT, "A1:XFD1048576"); * @endcode * * The worksheet errors/warnings that can be ignored are: * * - #LXW_IGNORE_NUMBER_STORED_AS_TEXT: Turn off errors/warnings for numbers * stores as text. * * - #LXW_IGNORE_EVAL_ERROR: Turn off errors/warnings for formula errors (such * as divide by zero). * * - #LXW_IGNORE_FORMULA_DIFFERS: Turn off errors/warnings for formulas that * differ from surrounding formulas. * * - #LXW_IGNORE_FORMULA_RANGE: Turn off errors/warnings for formulas that * omit cells in a range. * * - #LXW_IGNORE_FORMULA_UNLOCKED: Turn off errors/warnings for unlocked cells * that contain formulas. * * - #LXW_IGNORE_EMPTY_CELL_REFERENCE: Turn off errors/warnings for formulas * that refer to empty cells. * * - #LXW_IGNORE_LIST_DATA_VALIDATION: Turn off errors/warnings for cells in a * table that do not comply with applicable data validation rules. * * - #LXW_IGNORE_CALCULATED_COLUMN: Turn off errors/warnings for cell formulas * that differ from the column formula. * * - #LXW_IGNORE_TWO_DIGIT_TEXT_YEAR: Turn off errors/warnings for formulas * that contain a two digit text representation of a year. * */ lxw_error worksheet_ignore_errors(lxw_worksheet *worksheet, uint8_t type, const char *range); 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, uint32_t image_ref_id, uint32_t drawing_id, lxw_object_properties *object_props); void lxw_worksheet_prepare_header_image(lxw_worksheet *worksheet, uint32_t image_ref_id, lxw_object_properties *object_props); void lxw_worksheet_prepare_background(lxw_worksheet *worksheet, uint32_t image_ref_id, lxw_object_properties *object_props); void lxw_worksheet_prepare_chart(lxw_worksheet *worksheet, uint32_t chart_ref_id, uint32_t drawing_id, lxw_object_properties *object_props, uint8_t is_chartsheet); uint32_t lxw_worksheet_prepare_vml_objects(lxw_worksheet *worksheet, uint32_t vml_data_id, uint32_t vml_shape_id, uint32_t vml_drawing_id, uint32_t comment_id); void lxw_worksheet_prepare_header_vml_objects(lxw_worksheet *worksheet, uint32_t vml_header_id, uint32_t vml_drawing_id); void lxw_worksheet_prepare_tables(lxw_worksheet *worksheet, uint32_t table_id); lxw_row *lxw_worksheet_find_row(lxw_worksheet *worksheet, lxw_row_t row_num); lxw_cell *lxw_worksheet_find_cell_in_row(lxw_row *row, lxw_col_t col_num); /* * External functions to call intern XML functions shared with chartsheet. */ void lxw_worksheet_write_sheet_views(lxw_worksheet *worksheet); void lxw_worksheet_write_page_margins(lxw_worksheet *worksheet); void lxw_worksheet_write_drawings(lxw_worksheet *worksheet); void lxw_worksheet_write_sheet_protection(lxw_worksheet *worksheet, lxw_protection_obj *protect); void lxw_worksheet_write_sheet_pr(lxw_worksheet *worksheet); void lxw_worksheet_write_page_setup(lxw_worksheet *worksheet); void lxw_worksheet_write_header_footer(lxw_worksheet *worksheet); /* 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, lxw_protection_obj *protect); STATIC void _worksheet_write_data_validations(lxw_worksheet *self); STATIC double _pixels_to_height(double pixels); STATIC double _pixels_to_width(double pixels); STATIC void _worksheet_write_auto_filter(lxw_worksheet *worksheet); #endif /* TESTING */ /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* __LXW_WORKSHEET_H__ */ writexl/src/include/xlsxwriter/drawing.h0000644000176200001440000000415014747162622020234 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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 #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 image_types { LXW_IMAGE_UNKNOWN = 0, LXW_IMAGE_PNG, LXW_IMAGE_JPEG, LXW_IMAGE_BMP, LXW_IMAGE_GIF }; /* 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 type; uint8_t anchor; struct lxw_drawing_coords from; struct lxw_drawing_coords to; uint64_t col_absolute; uint64_t row_absolute; uint32_t width; uint32_t height; uint8_t shape; uint32_t rel_index; uint32_t url_rel_index; char *description; char *tip; uint8_t decorative; 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; uint8_t orientation; struct lxw_drawing_objects *drawing_objects; } lxw_drawing; /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ lxw_drawing *lxw_drawing_new(void); 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__ */ writexl/src/include/xlsxwriter/core.h0000644000176200001440000000151514747162622017533 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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); 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__ */ writexl/src/include/xlsxwriter/app.h0000644000176200001440000000311114747162622017355 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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; uint8_t doc_security; } lxw_app; /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ lxw_app *lxw_app_new(void); 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__ */ writexl/src/include/xlsxwriter/workbook.h0000644000176200001440000011673014747162622020446 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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 "chartsheet.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); RB_HEAD(lxw_chartsheet_names, lxw_chartsheet_name); RB_HEAD(lxw_image_md5s, lxw_image_md5); /* Define the queue.h structs for the workbook lists. */ STAILQ_HEAD(lxw_sheets, lxw_sheet); STAILQ_HEAD(lxw_worksheets, lxw_worksheet); STAILQ_HEAD(lxw_chartsheets, lxw_chartsheet); STAILQ_HEAD(lxw_charts, lxw_chart); TAILQ_HEAD(lxw_defined_names, lxw_defined_name); /* Struct to hold the 2 sheet types. */ typedef struct lxw_sheet { uint8_t is_chartsheet; union { lxw_worksheet *worksheet; lxw_chartsheet *chartsheet; } u; STAILQ_ENTRY (lxw_sheet) list_pointers; } lxw_sheet; /* 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; /* Struct to represent a chartsheet name/pointer pair. */ typedef struct lxw_chartsheet_name { const char *name; lxw_chartsheet *chartsheet; RB_ENTRY (lxw_chartsheet_name) tree_pointers; } lxw_chartsheet_name; /* Struct to represent an image MD5/ID pair. */ typedef struct lxw_image_md5 { uint32_t id; char *md5; RB_ENTRY (lxw_image_md5) tree_pointers; } lxw_image_md5; /* 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_WORKSHEET_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_worksheet_names{int unused;} #define LXW_RB_GENERATE_CHARTSHEET_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_charsheet_names{int unused;} #define LXW_RB_GENERATE_IMAGE_MD5S(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_image_md5s{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. Set any unused fields to NULL or 0. */ typedef struct lxw_doc_properties { /** The title of the Excel Document. */ const char *title; /** The subject of the Excel Document. */ const char *subject; /** The author of the Excel Document. */ const char *author; /** The manager field of the Excel Document. */ const char *manager; /** The company field of the Excel Document. */ const char *company; /** The category of the Excel Document. */ const char *category; /** The keywords of the Excel Document. */ const char *keywords; /** The comment field of the Excel Document. */ const char *comments; /** The status of the Excel Document. */ const char *status; /** The hyperlink base URL of the Excel Document. */ const char *hyperlink_base; /** The file creation date/time shown in Excel. This defaults to the * current time and date if set to 0. If you wish to create files that are * binary equivalent (for the same input data) then you should set this * creation date/time to a known value. */ 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`: This option reduces the amount of data stored in * memory so that large files can be written efficiently. This option is off * by default. See the notes below for limitations when this mode is on. * * - `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 `tmpdir` option. * * - `use_zip64`: Make the zip library use ZIP64 extensions when writing very * large xlsx files to allow the zip container, or individual XML files * within it, to be greater than 4 GB. See [ZIP64 on Wikipedia][zip64_wiki] * for more information. This option is off by default. * * [zip64_wiki]: https://en.wikipedia.org/wiki/Zip_(file_format)#ZIP64 * - `output_buffer`: Output to a buffer instead of a file. The buffer must be * freed manually by calling free(). This option can only be used if filename * is NULL. * * - `output_buffer_size`: Used with output_buffer to get the size of the * created buffer. This option can only be used if filename is NULL. * * @note In `constant_memory` mode each row of in-memory data is written to * disk and then freed when a new row is started via one of the * `worksheet_write_*()` functions. Therefore, once this option is active data * should be written in sequential row by row order. For this reason * `worksheet_merge_range()` and some other row based functionality doesn't * work in this mode. See @ref ww_mem_constant for more details. * * @note Also, in `constant_memory` mode the library uses temp file storage * for worksheet data. This can lead to an issue on OSes that map the `/tmp` * directory into memory since it is possible to consume the "system" memory * even though the "process" memory remains constant. In these cases you * should use an alternative temp file location by using the `tmpdir` option * shown above. See @ref ww_mem_temp for more details. */ 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. */ const char *tmpdir; /** Allow ZIP64 extensions when creating the xlsx file zip container. */ uint8_t use_zip64; /** Output buffer to use instead of writing to a file */ const char **output_buffer; /** Used with output_buffer to get the size of the created buffer */ size_t *output_buffer_size; } 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_sheets *sheets; struct lxw_worksheets *worksheets; struct lxw_chartsheets *chartsheets; struct lxw_worksheet_names *worksheet_names; struct lxw_chartsheet_names *chartsheet_names; struct lxw_image_md5s *image_md5s; struct lxw_image_md5s *header_image_md5s; struct lxw_image_md5s *background_md5s; 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 num_worksheets; uint16_t num_chartsheets; uint16_t first_sheet; uint16_t active_sheet; uint16_t num_xf_formats; uint16_t num_dxf_formats; uint16_t num_format_count; uint16_t drawing_count; uint16_t comment_count; uint16_t font_count; uint16_t border_count; uint16_t fill_count; uint8_t optimize; uint16_t max_url_length; uint8_t read_only; uint8_t has_png; uint8_t has_jpeg; uint8_t has_bmp; uint8_t has_gif; uint8_t has_vml; uint8_t has_comments; uint8_t has_metadata; lxw_hash_table *used_xf_formats; lxw_hash_table *used_dxf_formats; char *vba_project; char *vba_project_signature; char *vba_codename; lxw_format *default_url_format; } 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 = LXW_TRUE, * .tmpdir = "C:\\Temp", * .use_zip64 = LXW_FALSE, * .output_buffer = NULL, * .output_buffer_size = NULL}; * * lxw_workbook *workbook = workbook_new_opt("filename.xlsx", &options); * @endcode * * The options that can be set via #lxw_workbook_options are: * * - `constant_memory`: This option reduces the amount of data stored in * memory so that large files can be written efficiently. This option is off * by default. See the note below for limitations when this mode is on. * * - `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 `tmpdir` option. * * - `use_zip64`: Make the zip library use ZIP64 extensions when writing very * large xlsx files to allow the zip container, or individual XML files * within it, to be greater than 4 GB. See [ZIP64 on Wikipedia][zip64_wiki] * for more information. This option is off by default. * * [zip64_wiki]: https://en.wikipedia.org/wiki/Zip_(file_format)#ZIP64 * * - `output_buffer`: Output to a memory buffer instead of a file. The buffer * must be freed manually by calling `free()`. This option can only be used if * filename is NULL. * * - `output_buffer_size`: Used with output_buffer to get the size of the * created buffer. This option can only be used if filename is `NULL`. * * @note In `constant_memory` mode each row of in-memory data is written to * disk and then freed when a new row is started via one of the * `worksheet_write_*()` functions. Therefore, once this option is active data * should be written in sequential row by row order. For this reason * `worksheet_merge_range()` and some other row based functionality doesn't * work in this mode. See @ref ww_mem_constant for more details. * * @note Also, in `constant_memory` mode the library uses temp file storage * for worksheet data. This can lead to an issue on OSes that map the `/tmp` * directory into memory since it is possible to consume the "system" memory * even though the "process" memory remains constant. In these cases you * should use an alternative temp file location by using the `tmpdir` option * shown above. See @ref ww_mem_temp for more details. */ lxw_workbook *workbook_new_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: * * - The name is less than or equal to 31 UTF-8 characters. * - The name doesn't contain any of the characters: ` [ ] : * ? / \ ` * - The name doesn't start or end with an apostrophe. * - The name isn't already in use. (Case insensitive). * * If any of these errors are encountered the function will return NULL. * You can check for valid name using the `workbook_validate_sheet_name()` * function. * * @note You should also avoid using the worksheet name "History" (case * insensitive) which is reserved in English language versions of * Excel. Non-English versions may have restrictions on the equivalent word. */ lxw_worksheet *workbook_add_worksheet(lxw_workbook *workbook, const char *sheetname); /** * @brief Add a new chartsheet to a workbook. * * @param workbook Pointer to a lxw_workbook instance. * @param sheetname Optional chartsheet name, defaults to Chart1, etc. * * @return A lxw_chartsheet object. * * The `%workbook_add_chartsheet()` function adds a new chartsheet to a * workbook. The @ref chartsheet.h "Chartsheet" object is like a worksheet * except it displays a chart instead of cell data. * * @image html chartsheet.png * * The `sheetname` parameter is optional. If it is `NULL` the default * Excel convention will be followed, i.e. Chart1, Chart2, etc.: * * @code * chartsheet = workbook_add_chartsheet(workbook, NULL ); // Chart1 * chartsheet = workbook_add_chartsheet(workbook, "My Chart"); // My Chart * chartsheet = workbook_add_chartsheet(workbook, NULL ); // Chart3 * * @endcode * * The chartsheet name must be a valid Excel worksheet name, i.e.: * * - The name is less than or equal to 31 UTF-8 characters. * - The name doesn't contain any of the characters: ` [ ] : * ? / \ ` * - The name doesn't start or end with an apostrophe. * - The name isn't already in use. (Case insensitive). * * If any of these errors are encountered the function will return NULL. * You can check for valid name using the `workbook_validate_sheet_name()` * function. * * @note You should also avoid using the worksheet name "History" (case * insensitive) which is reserved in English language versions of * Excel. Non-English versions may have restrictions on the equivalent word. * * At least one worksheet should be added to a new workbook when creating a * chartsheet in order to provide data for the chart. The @ref worksheet.h * "Worksheet" object is used to write data and configure a worksheet in the * workbook. */ lxw_chartsheet *workbook_add_chartsheet(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_LINE_STACKED | Line chart - stacked. | * | #LXW_CHART_LINE_STACKED_PERCENT | Line chart - percentage stacked. | * | #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` * - `created` * * The properties are specified via a `lxw_doc_properties` struct. All the * fields 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 * * The `created` parameter sets the file creation date/time shown in * Excel. This defaults to the current time and date if set to 0. If you wish * to create files that are binary equivalent (for the same input data) then * you should set this creation date/time to a known value using a `time_t` * value. * */ 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 the default URL format used with `worksheet_write_url()`. * * @param workbook Pointer to a lxw_workbook instance. * @return A lxw_format instance that has hyperlink properties set. * * This function returns a lxw_format instance that is used for the default * blue underline hyperlink in the `worksheet_write_url()` function when a * format isn't specified: * * @code * lxw_format *url_format = workbook_get_default_url_format(workbook); * @endcode * * The format is the hyperlink style defined by Excel for the default theme. * This format is only ever required when overwriting a string URL with * data of a different type. See the example below. */ lxw_format *workbook_get_default_url_format(lxw_workbook *workbook); /** * @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 Get a chartsheet object from its name. * * @param workbook Pointer to a lxw_workbook instance. * @param name chartsheet name. * * @return A lxw_chartsheet object. * * This function returns a lxw_chartsheet object reference based on its name: * * @code * chartsheet = workbook_get_chartsheet_by_name(workbook, "Chart1"); * @endcode * */ lxw_chartsheet *workbook_get_chartsheet_by_name(lxw_workbook *workbook, const char *name); /** * @brief Validate a worksheet or chartsheet name. * * @param workbook Pointer to a lxw_workbook instance. * @param sheetname Sheet name to validate. * * @return A #lxw_error. * * This function is used to validate a worksheet or chartsheet 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 doesn't start or end with an apostrophe. * - The name isn't already in use. (Case insensitive, see the note below). * * @code * lxw_error err = workbook_validate_sheet_name(workbook, "Foglio"); * @endcode * * This function is called by `workbook_add_worksheet()` and * `workbook_add_chartsheet()` but it can be explicitly called by the user * beforehand to ensure that the sheet name is valid. * * @note You should also avoid using the worksheet name "History" (case * insensitive) which is reserved in English language versions of * Excel. Non-English versions may have restrictions on the equivalent word. * * @note This function does an ASCII lowercase string comparison to determine * if the sheet name is already in use. It doesn't take UTF-8 characters into * account. Thus it would flag "Café" and "café" as a duplicate (just like * Excel) but it wouldn't catch "CAFÉ". If you need a full UTF-8 case * insensitive check you should use a third party library to implement it. * */ lxw_error workbook_validate_sheet_name(lxw_workbook *workbook, const char *sheetname); /** * @brief Add a vbaProject binary to the Excel workbook. * * @param workbook Pointer to a lxw_workbook instance. * @param filename The path/filename of the vbaProject.bin file. * * The `%workbook_add_vba_project()` function can be used to add macros or * functions to a workbook using a binary VBA project file that has been * extracted from an existing Excel xlsm file: * * @code * workbook_add_vba_project(workbook, "vbaProject.bin"); * @endcode * * Only one `vbaProject.bin` file can be added per workbook. The name doesn't * have to be `vbaProject.bin`. Any suitable path/name for an existing VBA bin * file will do. * * Once you add a VBA project had been add to an libxlsxwriter workbook you * should ensure that the file extension is `.xlsm` to prevent Excel from * giving a warning when it opens the file: * * @code * lxw_workbook *workbook = new_workbook("macro.xlsm"); * @endcode * * See also @ref working_with_macros * * @return A #lxw_error. */ lxw_error workbook_add_vba_project(lxw_workbook *workbook, const char *filename); /** * @brief Add a vbaProject binary and a vbaProjectSignature binary to the Excel * workbook. * * @param workbook Pointer to a lxw_workbook instance. * @param vba_project The path/filename of the vbaProject.bin file. * @param signature The path/filename of the vbaProjectSignature.bin file. * * The `%workbook_add_signed_vba_project()` function can be used to add digitally * signed macros or functions to a workbook. The function adds a binary VBA project * file and a binary VBA project signature file that have been extracted from an * existing Excel xlsm file with digitally signed macros: * * @code * workbook_add_signed_vba_project(workbook, "vbaProject.bin", "vbaProjectSignature.bin"); * @endcode * * Only one `vbaProject.bin` file can be added per workbook. The name doesn't * have to be `vbaProject.bin`. Any suitable path/name for an existing VBA bin * file will do. The same applies for `vbaProjectSignature.bin`. * * See also @ref working_with_macros * * @return A #lxw_error. */ lxw_error workbook_add_signed_vba_project(lxw_workbook *workbook, const char *vba_project, const char *signature); /** * @brief Set the VBA name for the workbook. * * @param workbook Pointer to a lxw_workbook instance. * @param name Name of the workbook used by VBA. * * The `workbook_set_vba_name()` function can be used to set the VBA name for * the workbook. This is sometimes required when a vbaProject macro included * via `workbook_add_vba_project()` refers to the workbook by a name other * than `ThisWorkbook`. * * @code * workbook_set_vba_name(workbook, "MyWorkbook"); * @endcode * * If an Excel VBA name for the workbook isn't specified then libxlsxwriter * will use `ThisWorkbook`. * * See also @ref working_with_macros * * @return A #lxw_error. */ lxw_error workbook_set_vba_name(lxw_workbook *workbook, const char *name); /** * @brief Add a recommendation to open the file in "read-only" mode. * * @param workbook Pointer to a lxw_workbook instance. * * This function can be used to set the Excel "Read-only Recommended" option * that is available when saving a file. This presents the user of the file * with an option to open it in "read-only" mode. This means that any changes * to the file can't be saved back to the same file and must be saved to a new * file. It can be set as follows: * * @code * workbook_read_only_recommended(workbook); * @endcode * * Which will raise a dialog like the following when opening the file: * * @image html read_only.png */ void workbook_read_only_recommended(lxw_workbook *workbook); 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); void workbook_unset_default_url_format(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__ */ writexl/src/include/xlsxwriter/chart.h0000644000176200001440000034542314747162622017715 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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, /** Line chart - stacked. */ LXW_CHART_LINE_STACKED, /** Line chart - percentage stacked. */ LXW_CHART_LINE_STACKED_PERCENT, /** 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 positioned at top right. */ LXW_CHART_LEGEND_TOP_RIGHT, /** Chart legend overlaid at right side. */ LXW_CHART_LEGEND_OVERLAY_RIGHT, /** Chart legend overlaid at left side. */ LXW_CHART_LEGEND_OVERLAY_LEFT, /** Chart legend overlaid at top right. */ LXW_CHART_LEGEND_OVERLAY_TOP_RIGHT } 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; /** * @brief Chart axis types. */ typedef enum lxw_chart_axis_type { /** Chart X axis. */ LXW_CHART_AXIS_TYPE_X, /** Chart Y axis. */ LXW_CHART_AXIS_TYPE_Y } lxw_chart_axis_type; 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 Axis label alignments. */ typedef enum lxw_chart_axis_label_alignment { /** Chart axis label alignment: center. */ LXW_CHART_AXIS_LABEL_ALIGN_CENTER, /** Chart axis label alignment: left. */ LXW_CHART_AXIS_LABEL_ALIGN_LEFT, /** Chart axis label alignment: right. */ LXW_CHART_AXIS_LABEL_ALIGN_RIGHT } lxw_chart_axis_label_alignment; /** * @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; /** Set the transparency of the line. 0 - 100. Default 0. */ uint8_t transparency; } 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; } 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; } 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". */ const 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, and 270, 271 and 360: * * - The angles -90 to 90 are the normal range shown in the Excel user interface. * - The angle 270 gives a stacked (top to bottom) alignment. * - The angle 271 gives a stacked alignment for East Asian fonts. * - The angle 360 gives an explicit angle of 0 to override the y axis default. * */ int32_t rotation; /** The chart font color. See @ref working_with_colors. */ lxw_color_t color; /** The chart font pitch family property. Rarely required, set to 0. */ uint8_t pitch_family; /** The chart font character set property. Rarely required, set to 0. */ uint8_t charset; /** The chart font baseline property. Rarely required, set to 0. */ int8_t baseline; } 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 Struct to represent an Excel chart data label. * * The lxw_chart_data_label struct is used to represent a data label in a * chart series so that custom properties can be set for it. */ typedef struct lxw_chart_data_label { /** The string or formula value for the data label. See * @ref chart_custom_labels. */ const char *value; /** Option to hide/delete the data label from the chart series. * See @ref chart_custom_labels. */ uint8_t hide; /** The font properties for the chart data label. @ref chart_fonts. */ lxw_chart_font *font; /** The line/border for the chart data label. See @ref chart_lines. */ lxw_chart_line *line; /** The fill for the chart data label. See @ref chart_fills. */ lxw_chart_fill *fill; /** The pattern for the chart data label. See @ref chart_patterns.*/ lxw_chart_pattern *pattern; } lxw_chart_data_label; /* Internal version of lxw_chart_data_label with more metadata. */ typedef struct lxw_chart_custom_label { char *value; uint8_t hide; lxw_chart_font *font; lxw_chart_line *line; lxw_chart_fill *fill; lxw_chart_pattern *pattern; /* We use a range to hold the label 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_custom_label; /** * @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 Direction for a data series error bar. */ typedef enum lxw_chart_error_bar_axis { /** X axis error bar. */ LXW_CHART_ERROR_BAR_AXIS_X, /** Y axis error bar. */ LXW_CHART_ERROR_BAR_AXIS_Y } lxw_chart_error_bar_axis; /** * @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; lxw_chart_custom_label *data_labels; uint16_t point_count; uint16_t data_label_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_chart_line *label_line; lxw_chart_fill *label_fill; lxw_chart_pattern *label_pattern; 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 label_align; 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_min; 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 is_chartsheet; 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; uint8_t is_protected; 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!$B$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_data_labels1.png * * By default data labels are displayed in Excel with only the values shown: * * @image html chart_data_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_data_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 properties for data labels in a series. * * @param series A series object created via `chart_add_series()`. * @param data_labels An NULL terminated array of #lxw_chart_data_label pointers. * * @return A #lxw_error. * * The `%chart_series_set_labels_custom()` function is used to set the properties * for data labels in a series. It can also be used to delete individual data * labels in a series. * * In general properties are set for all the data labels in a chart * series. However, it is also possible to set properties for individual data * labels in a series using `%chart_series_set_labels_custom()`. * * The `%chart_series_set_labels_custom()` function takes a pointer to an array * of #lxw_chart_data_label pointers. The list should be `NULL` terminated: * * @code * // Add the series data labels. * chart_series_set_labels(series); * * // Create some custom labels. * lxw_chart_data_label data_label1 = {.value = "Jan"}; * lxw_chart_data_label data_label2 = {.value = "Feb"}; * lxw_chart_data_label data_label3 = {.value = "Mar"}; * lxw_chart_data_label data_label4 = {.value = "Apr"}; * lxw_chart_data_label data_label5 = {.value = "May"}; * lxw_chart_data_label data_label6 = {.value = "Jun"}; * * // Create an array of label pointers. NULL indicates the end of the array. * lxw_chart_data_label *data_labels[] = { * &data_label1, * &data_label2, * &data_label3, * &data_label4, * &data_label5, * &data_label6, * NULL * }; * * // Set the custom labels. * chart_series_set_labels_custom(series, data_labels); * @endcode * * @image html chart_data_labels18.png * * @note The array of #lxw_chart_point pointers should be NULL terminated as * shown in the example. Any #lxw_chart_data_label items set to a default * initialization or omitted from the list will be assigned the default data * label value. * * For more details see @ref chart_custom_labels. */ lxw_error chart_series_set_labels_custom(lxw_chart_series *series, lxw_chart_data_label *data_labels[]); /** * @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_data_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_data_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_data_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_data_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_data_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_data_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 Set the line properties for the data labels in 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 the data labels in a chart series: * * @code * lxw_chart_line line = {.color = LXW_COLOR_RED}; * lxw_chart_fill fill = {.color = LXW_COLOR_YELLOW}; * * chart_series_set_labels_line(series, &line); * chart_series_set_labels_fill(series, &fill); * * @endcode * * @image html chart_data_labels24.png * * For more information see @ref chart_lines and @ref chart_labels. */ void chart_series_set_labels_line(lxw_chart_series *series, lxw_chart_line *line); /** * @brief Set the fill properties for the data labels in 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 the data labels in a chart series: * * @code * lxw_chart_fill fill = {.color = LXW_COLOR_YELLOW}; * * chart_series_set_labels_fill(series, &fill); * @endcode * * See the example and image above and also see @ref chart_fills and * @ref chart_labels. */ void chart_series_set_labels_fill(lxw_chart_series *series, lxw_chart_fill *fill); /** * @brief Set the pattern properties for the data labels in 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 the data labels in a chart series: * * @code * chart_series_set_labels_pattern(series, &pattern); * @endcode * * For more information see #lxw_chart_pattern_type and @ref chart_patterns. */ void chart_series_set_labels_pattern(lxw_chart_series *series, lxw_chart_pattern *pattern); /** * @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); /** * @brief Get a pointer to X or Y error bars from a chart series. * * @param series A series object created via `chart_add_series()`. * @param axis_type The axis type (X or Y): #lxw_chart_error_bar_axis. * * The `%chart_series_get_error_bars()` function returns a pointer to the * error bars of a series based on the type of #lxw_chart_error_bar_axis: * * @code * lxw_series_error_bars *x_error_bars; * lxw_series_error_bars *y_error_bars; * * x_error_bars = chart_series_get_error_bars(series, LXW_CHART_ERROR_BAR_AXIS_X); * y_error_bars = chart_series_get_error_bars(series, LXW_CHART_ERROR_BAR_AXIS_Y); * * // Use the error bar pointers. * chart_series_set_error_bars(x_error_bars, * LXW_CHART_ERROR_BAR_TYPE_STD_DEV, 1); * * chart_series_set_error_bars(y_error_bars, * LXW_CHART_ERROR_BAR_TYPE_STD_ERROR, 0); * @endcode * * Note, the series error bars can also be accessed directly: * * @code * // Equivalent to the above example, without function calls. * chart_series_set_error_bars(series->x_error_bars, * LXW_CHART_ERROR_BAR_TYPE_STD_DEV, 1); * * chart_series_set_error_bars(series->y_error_bars, * LXW_CHART_ERROR_BAR_TYPE_STD_ERROR, 0); * @endcode * * @return Pointer to the series error bars, or NULL if not found. */ lxw_series_error_bars *chart_series_get_error_bars(lxw_chart_series *series, lxw_chart_error_bar_axis axis_type); /** * 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 Get an axis pointer from a chart. * * @param chart Pointer to a lxw_chart instance to be configured. * @param axis_type The axis type (X or Y): #lxw_chart_axis_type. * * The `%chart_axis_get()` function returns a pointer to a chart axis based * on the #lxw_chart_axis_type: * * @code * lxw_chart_axis *x_axis = chart_axis_get(chart, LXW_CHART_AXIS_TYPE_X); * lxw_chart_axis *y_axis = chart_axis_get(chart, LXW_CHART_AXIS_TYPE_Y); * * // Use the axis pointer in other functions. * chart_axis_major_gridlines_set_visible(x_axis, LXW_TRUE); * chart_axis_major_gridlines_set_visible(y_axis, LXW_TRUE); * @endcode * * Note, the axis pointer can also be accessed directly: * * @code * // Equivalent to the above example, without function calls. * chart_axis_major_gridlines_set_visible(chart->x_axis, LXW_TRUE); * chart_axis_major_gridlines_set_visible(chart->y_axis, LXW_TRUE); * @endcode * * @return Pointer to the chart axis, or NULL if not found. */ lxw_chart_axis *chart_axis_get(lxw_chart *chart, lxw_chart_axis_type axis_type); /** * @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!$B$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 Set the opposite axis crossing position as the axis minimum. * * @param axis A pointer to a chart #lxw_chart_axis object. * * Set the position that the opposite axis will cross as the axis minimum. * 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_min(chart->x_axis); * chart_axis_set_crossing_min(chart->y_axis); * @endcode * * **Axis types**: This function is applicable to to all axes types. * See @ref ww_charts_axes. */ void chart_axis_set_crossing_min(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 alignment of the axis labels. * * @param axis A pointer to a chart #lxw_chart_axis object. * @param align A #lxw_chart_axis_label_alignment value. * * Position the category axis labels for the chart. The labels are the * numbers, or strings or dates, on the axis that indicate the categories * of the axis. * * The allowable values: * * - #LXW_CHART_AXIS_LABEL_ALIGN_CENTER - Align label center (default). * - #LXW_CHART_AXIS_LABEL_ALIGN_LEFT - Align label left. * - #LXW_CHART_AXIS_LABEL_ALIGN_RIGHT - Align label right. * * @code * chart_axis_set_label_align(chart->x_axis, LXW_CHART_AXIS_LABEL_ALIGN_RIGHT); * @endcode * * **Axis types**: This function is applicable to category axes only. * See @ref ww_charts_axes. */ void chart_axis_set_label_align(lxw_chart_axis *axis, uint8_t align); /** * @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!$B$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 = {.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 * * In Excel a chart title font is bold by default (as shown in the image * above). To turn off bold in the font you cannot use #LXW_FALSE (0) since * that is indistinguishable from an uninitialized value. Instead you should * use #LXW_EXPLICIT_FALSE: * * @code * lxw_chart_font font = {.bold = LXW_EXPLICIT_FALSE, .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_font2.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_TOP_RIGHT * LXW_CHART_LEGEND_OVERLAY_RIGHT * LXW_CHART_LEGEND_OVERLAY_LEFT * LXW_CHART_LEGEND_OVERLAY_TOP_RIGHT * * 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__ */ writexl/src/include/xlsxwriter/table.h0000644000176200001440000000152114747162622017667 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * table - A libxlsxwriter library for creating Excel XLSX table files. * */ #ifndef __LXW_TABLE_H__ #define __LXW_TABLE_H__ #include #include "common.h" /* * Struct to represent a table object. */ typedef struct lxw_table { FILE *file; struct lxw_table_obj *table_obj; } lxw_table; /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ lxw_table *lxw_table_new(void); void lxw_table_free(lxw_table *table); void lxw_table_assemble_xml_file(lxw_table *self); /* Declarations required for unit testing. */ #ifdef TESTING STATIC void _table_xml_declaration(lxw_table *self); #endif /* TESTING */ /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* __LXW_TABLE_H__ */ writexl/src/include/xlsxwriter/styles.h0000644000176200001440000000527214747162622020132 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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 #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; uint8_t has_hyperlink; uint16_t hyperlink_font_id; uint8_t has_comments; } lxw_styles; /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ lxw_styles *lxw_styles_new(void); void lxw_styles_free(lxw_styles *styles); void lxw_styles_assemble_xml_file(lxw_styles *self); void lxw_styles_write_string_fragment(lxw_styles *self, const char *string); void lxw_styles_write_rich_font(lxw_styles *styles, lxw_format *format); /* 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, uint8_t is_rich_string); 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, uint8_t is_dxf, uint8_t is_rich_string); 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, uint8_t is_dxf); STATIC void _write_borders(lxw_styles *self); STATIC void _write_style_xf(lxw_styles *self, uint8_t has_hyperlink, uint16_t font_id); 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, char *name, uint8_t xf_id, uint8_t builtin_id); 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__ */ writexl/src/include/xlsxwriter/custom.h0000644000176200001440000000162614747162622020120 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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); 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__ */ writexl/src/include/xlsxwriter/relationships.h0000644000176200001440000000355214747162622021472 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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); 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__ */ writexl/src/include/xlsxwriter/third_party/0000755000176200001440000000000014747162622020761 5ustar liggesuserswritexl/src/include/xlsxwriter/third_party/ioapi.h0000644000176200001440000001607214747162622022241 0ustar liggesusers/* 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 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64) #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 chosen 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 #if defined(_MSC_VER) || defined(__BORLANDC__) typedef unsigned __int64 ZPOS64_T; #else typedef unsigned long long int ZPOS64_T; #endif #endif #endif /* Maximum unsigned 32-bit value used as placeholder for zip64 */ #ifndef MAXU32 #define MAXU32 (0xffffffff) #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) (voidpf opaque, const char* filename, int mode); typedef uLong (ZCALLBACK *read_file_func) (voidpf opaque, voidpf stream, void* buf, uLong size); typedef uLong (ZCALLBACK *write_file_func) (voidpf opaque, voidpf stream, const void* buf, uLong size); typedef int (ZCALLBACK *close_file_func) (voidpf opaque, voidpf stream); typedef int (ZCALLBACK *testerror_file_func) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uLong offset, int origin); /* here is the "old" 32 bits 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) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *seek64_file_func) (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin); typedef voidpf (ZCALLBACK *open64_file_func) (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(zlib_filefunc64_def* pzlib_filefunc_def); void fill_fopen_filefunc(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(const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode); long call_zseek64(const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin); ZPOS64_T call_ztell64(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 writexl/src/include/xlsxwriter/third_party/zip.h0000644000176200001440000003663514747162622021751 0ustar liggesusers/* 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 { int tm_sec; /* seconds after the minute - [0,59] */ int tm_min; /* minutes after the hour - [0,59] */ int tm_hour; /* hours since midnight - [0,23] */ int tm_mday; /* day of the month - [1,31] */ int tm_mon; /* months since January - [0,11] */ int 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(const char *pathname, int append); extern zipFile ZEXPORT zipOpen64(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 course, you can use RAW reading and writing to copy the file you did not want delete */ extern zipFile ZEXPORT zipOpen2(const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc_def); extern zipFile ZEXPORT zipOpen2_64(const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def); extern zipFile ZEXPORT zipOpen3(const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def); 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); 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); /* 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 for the local header if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global contains the extrafield data for the global 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(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(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(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(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(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(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(zipFile file, const void* buf, unsigned len); /* Write data in the zipfile */ extern int ZEXPORT zipCloseFileInZip(zipFile file); /* Close the current file in the zipfile */ extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, uLong uncompressed_size, uLong crc32); extern int ZEXPORT zipCloseFileInZipRaw64(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(zipFile file, const char* global_comment); /* Close the zipfile */ extern int ZEXPORT zipRemoveExtraInfoBlock(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 */ writexl/src/include/xlsxwriter/third_party/tree.h0000644000176200001440000006545414747162622022107 0ustar liggesusers/* $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_ */ writexl/src/include/xlsxwriter/third_party/queue.h0000644000176200001440000005635314747162622022272 0ustar liggesusers/*- * 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_ */ writexl/src/include/xlsxwriter/third_party/md5.h0000644000176200001440000000260214747162622021617 0ustar liggesusers/* * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. * MD5 Message-Digest Algorithm (RFC 1321). * * Homepage: * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 * * Author: * Alexander Peslyak, better known as Solar Designer * * This software was written by Alexander Peslyak in 2001. No copyright is * claimed, and the software is hereby placed in the public domain. * In case this attempt to disclaim copyright and place the software in the * public domain is deemed null and void, then the software is * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the * general public under the following terms: * * Redistribution and use in source and binary forms, with or without * modification, are permitted. * * There's ABSOLUTELY NO WARRANTY, express or implied. * * See md5.c for more information. */ #ifdef HAVE_OPENSSL #include #elif !defined(_MD5_H) #define _MD5_H /* Any 32-bit or wider unsigned integer data type will do */ typedef unsigned int MD5_u32plus; typedef struct { MD5_u32plus lo, hi; MD5_u32plus a, b, c, d; unsigned char buffer[64]; MD5_u32plus block[16]; } MD5_CTX; extern void MD5_Init(MD5_CTX *ctx); extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); #endif writexl/src/include/xlsxwriter/third_party/tmpfileplus.h0000644000176200001440000000414414747162622023501 0ustar liggesusers/* $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_ */ writexl/src/include/xlsxwriter/utility.h0000644000176200001440000001566214747162622020316 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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 #ifndef _MSC_VER #include #endif #include "common.h" #include "xmlwriter.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 Retrieve the library version. * * @return The "X.Y.Z" version string. * * Get the library version as a "X.Y.Z" version string * * @code * printf("Libxlsxwriter version = %s\n", lxw_version()); * @endcode * */ const char *lxw_version(void); /** * @brief Retrieve the library version ID. * * @return The version ID. * * Get the library version such as "X.Y.Z" as a XYZ integer. * * @code * printf("Libxlsxwriter version id = %d\n", lxw_version_id()); * @endcode * */ uint16_t lxw_version_id(void); /** * @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); /** * @brief Converts a #lxw_datetime to an Excel datetime number. * * @param datetime A pointer to a #lxw_datetime struct. * * @return A double representing an Excel datetime. * * The `%lxw_datetime_to_excel_datetime()` function converts a datetime in * #lxw_datetime to an Excel datetime number: * * @code * lxw_datetime datetime = {2013, 2, 28, 12, 0, 0.0}; * * double excel_datetime = lxw_datetime_to_excel_date(&datetime); * @endcode * * See @ref working_with_dates for more details on the Excel datetime format. */ double lxw_datetime_to_excel_datetime(lxw_datetime *datetime); double lxw_datetime_to_excel_date_epoch(lxw_datetime *datetime, uint8_t date_1904); /** * @brief Converts a unix datetime to an Excel datetime number. * * @param unixtime Unix time (seconds since 1970-01-01) * * @return A double representing an Excel datetime. * * The `%lxw_unixtime_to_excel_date()` function converts a unix datetime to * an Excel datetime number: * * @code * double excel_datetime = lxw_unixtime_to_excel_date(946684800); * @endcode * * See @ref working_with_dates for more details. */ double lxw_unixtime_to_excel_date(int64_t unixtime); double lxw_unixtime_to_excel_date_epoch(int64_t unixtime, uint8_t date_1904); char *lxw_strdup(const char *str); char *lxw_strdup_formula(const char *formula); size_t lxw_utf8_strlen(const char *str); void lxw_str_tolower(char *str); /* Define a portable version of strcasecmp(). */ #ifdef _MSC_VER #define lxw_strcasecmp _stricmp #else #define lxw_strcasecmp strcasecmp #endif FILE *lxw_tmpfile(const char *tmpdir); FILE *lxw_get_filehandle(char **buf, size_t *size, const char *tmpdir); FILE *lxw_fopen(const char *filename, const char *mode); /* Use the third party dtoa function to avoid locale issues with sprintf * double formatting. Otherwise we use a simple macro that falls back to the * default c-lib sprintf. */ #ifdef USE_DTOA_LIBRARY int lxw_sprintf_dbl(char *data, double number); #else #define lxw_sprintf_dbl(data, number) \ lxw_snprintf(data, LXW_ATTR_32, "%.16G", number) #endif uint16_t lxw_hash_password(const char *password); /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* __LXW_UTILITY_H__ */ writexl/src/include/xlsxwriter/packager.h0000644000176200001440000000460714747162622020365 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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 #ifdef __GNUC__ #pragma GCC system_header #endif #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 "table.h" #include "theme.h" #include "styles.h" #include "format.h" #include "content_types.h" #include "relationships.h" #include "vml.h" #include "comment.h" #include "metadata.h" #define LXW_ZIP_BUFFER_SIZE (16384) /* If zip returns a ZIP_XXX error then errno is set and we can trap that in * workbook.c. Otherwise return a default libxlsxwriter error. */ #define RETURN_ON_ZIP_ERROR(err, default_err) \ do { \ if (err == ZIP_ERRNO) \ return LXW_ERROR_ZIP_FILE_OPERATION; \ else if (err == ZIP_PARAMERROR) \ return LXW_ERROR_ZIP_PARAMETER_ERROR; \ else if (err == ZIP_BADZIPFILE) \ return LXW_ERROR_ZIP_BAD_ZIP_FILE; \ else if (err == ZIP_INTERNALERROR) \ return LXW_ERROR_ZIP_INTERNAL_ERROR; \ else \ return default_err; \ } while (0) /* * Struct to represent a packager. */ typedef struct lxw_packager { FILE *file; lxw_workbook *workbook; size_t buffer_size; size_t output_buffer_size; zipFile zipfile; zip_fileinfo zipfile_info; const char *filename; const char *buffer; char *output_buffer; const char *tmpdir; uint8_t use_zip64; } lxw_packager; /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ lxw_packager *lxw_packager_new(const char *filename, const char *tmpdir, uint8_t use_zip64); 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__ */ writexl/src/include/xlsxwriter/theme.h0000644000176200001440000000143614747162622017707 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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); 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__ */ writexl/src/include/xlsxwriter/comment.h0000644000176200001440000000351114747162622020243 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * comment - A libxlsxwriter library for creating Excel XLSX comment files. * */ #ifndef __LXW_COMMENT_H__ #define __LXW_COMMENT_H__ #include #include "common.h" #include "worksheet.h" /* Define the tree.h RB structs for the red-black head types. */ RB_HEAD(lxw_author_ids, lxw_author_id); /* * Struct to represent a comment object. */ typedef struct lxw_comment { FILE *file; struct lxw_comment_objs *comment_objs; struct lxw_author_ids *author_ids; char *comment_author; uint32_t author_id; } lxw_comment; /* Struct to an author id */ typedef struct lxw_author_id { uint32_t id; char *author; RB_ENTRY (lxw_author_id) tree_pointers; } lxw_author_id; #define LXW_RB_GENERATE_AUTHOR_IDS(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_author_ids{int unused;} /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ lxw_comment *lxw_comment_new(void); void lxw_comment_free(lxw_comment *comment); void lxw_comment_assemble_xml_file(lxw_comment *self); /* Declarations required for unit testing. */ #ifdef TESTING STATIC void _comment_xml_declaration(lxw_comment *self); #endif /* TESTING */ /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* __LXW_COMMENT_H__ */ writexl/src/include/xlsxwriter/shared_strings.h0000644000176200001440000000405714747162622021626 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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; uint8_t is_rich_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); void lxw_sst_free(lxw_sst *sst); struct sst_element *lxw_get_sst_index(lxw_sst *sst, const char *string, uint8_t is_rich_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__ */ writexl/src/include/xlsxwriter/vml.h0000644000176200001440000000173114747162622017401 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * vml - A libxlsxwriter library for creating Excel XLSX vml files. * */ #ifndef __LXW_VML_H__ #define __LXW_VML_H__ #include #include "common.h" #include "worksheet.h" /* * Struct to represent a vml object. */ typedef struct lxw_vml { FILE *file; uint8_t type; struct lxw_comment_objs *button_objs; struct lxw_comment_objs *comment_objs; struct lxw_comment_objs *image_objs; char *vml_data_id_str; uint32_t vml_shape_id; uint8_t comment_display_default; } lxw_vml; /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ lxw_vml *lxw_vml_new(void); void lxw_vml_free(lxw_vml *vml); void lxw_vml_assemble_xml_file(lxw_vml *self); /* Declarations required for unit testing. */ #ifdef TESTING #endif /* TESTING */ /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* __LXW_VML_H__ */ writexl/src/include/xlsxwriter/chartsheet.h0000644000176200001440000004162314747162622020741 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * * chartsheet - A libxlsxwriter library for creating Excel XLSX chartsheet files. * */ /** * @page chartsheet_page The Chartsheet object * * The Chartsheet object represents an Excel chartsheet, which is a type of * worksheet that only contains a chart. The Chartsheet object handles * operations such as adding a chart and setting the page layout. * * See @ref chartsheet.h for full details of the functionality. * * @file chartsheet.h * * @brief Functions related to adding data and formatting to a chartsheet. * * The Chartsheet object represents an Excel chartsheet. It handles operations * such as adding a chart and setting the page layout. * * A Chartsheet object isn't created directly. Instead a chartsheet is created * by calling the workbook_add_chartsheet() function from a Workbook object. A * chartsheet object functions as a worksheet and not as a chart. In order to * have it display data a #lxw_chart object must be created and added to the * chartsheet: * * @code * #include "xlsxwriter.h" * * int main() { * * lxw_workbook *workbook = new_workbook("chartsheet.xlsx"); * lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); * lxw_chartsheet *chartsheet = workbook_add_chartsheet(workbook, NULL); * lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_BAR); * * //... Set up the chart. * * // Add the chart to the chartsheet. * return workbook_close(workbook); * * } * @endcode * * @image html chartsheet.png * * The data for the chartsheet chart must be contained on a separate * worksheet. That is why it is always created in conjunction with at least * one data worksheet, as shown above. */ #ifndef __LXW_CHARTSHEET_H__ #define __LXW_CHARTSHEET_H__ #include #include "common.h" #include "worksheet.h" #include "drawing.h" #include "utility.h" /** * @brief Struct to represent an Excel chartsheet. * * The members of the lxw_chartsheet struct aren't modified directly. Instead * the chartsheet properties are set by calling the functions shown in * chartsheet.h. */ typedef struct lxw_chartsheet { FILE *file; lxw_worksheet *worksheet; lxw_chart *chart; struct lxw_protection_obj protection; uint8_t is_protected; const char *name; const char *quoted_name; const char *tmpdir; uint16_t index; uint8_t active; uint8_t selected; uint8_t hidden; uint16_t *active_sheet; uint16_t *first_sheet; uint16_t rel_count; STAILQ_ENTRY (lxw_chartsheet) list_pointers; } lxw_chartsheet; /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ /** * @brief Insert a chart object into a chartsheet. * * @param chartsheet Pointer to a lxw_chartsheet instance to be updated. * @param chart A #lxw_chart object created via workbook_add_chart(). * * @return A #lxw_error code. * * The `%chartsheet_set_chart()` function can be used to insert a chart into a * chartsheet. The chart object must be created first using the * `workbook_add_chart()` function and configured using the @ref chart.h * functions. * * @code * // Create the chartsheet. * lxw_chartsheet *chartsheet = workbook_add_chartsheet(workbook, NULL); * * // 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 chartsheet. * chartsheet_set_chart(chartsheet, chart); * @endcode * * @image html chartsheet2.png * * **Note:** * * A chart may only be inserted once into a chartsheet or a worksheet. If * several similar charts are required then each one must be created * separately. * */ lxw_error chartsheet_set_chart(lxw_chartsheet *chartsheet, lxw_chart *chart); /* Not currently required since scale options aren't useful in a chartsheet. */ lxw_error chartsheet_set_chart_opt(lxw_chartsheet *chartsheet, lxw_chart *chart, lxw_chart_options *user_options); /** * @brief Make a chartsheet the active, i.e., visible chartsheet. * * @param chartsheet Pointer to a lxw_chartsheet instance to be updated. * * The `%chartsheet_activate()` function is used to specify which chartsheet * is initially visible in a multi-sheet workbook: * * @code * lxw_worksheet *worksheet1 = workbook_add_worksheet(workbook, NULL); * lxw_chartsheet *chartsheet1 = workbook_add_chartsheet(workbook, NULL); * lxw_chartsheet *chartsheet2 = workbook_add_chartsheet(workbook, NULL); * lxw_chartsheet *chartsheet3 = workbook_add_chartsheet(workbook, NULL); * * chartsheet_activate(chartsheet3); * @endcode * * @image html chartsheet_activate.png * * More than one chartsheet can be selected via the `chartsheet_select()` * function, see below, however only one chartsheet can be active. * * The default active chartsheet is the first chartsheet. * * See also `worksheet_activate()`. * */ void chartsheet_activate(lxw_chartsheet *chartsheet); /** * @brief Set a chartsheet tab as selected. * * @param chartsheet Pointer to a lxw_chartsheet instance to be updated. * * The `%chartsheet_select()` function is used to indicate that a chartsheet * is selected in a multi-sheet workbook: * * @code * chartsheet_activate(chartsheet1); * chartsheet_select(chartsheet2); * chartsheet_select(chartsheet3); * * @endcode * * A selected chartsheet has its tab highlighted. Selecting chartsheets is a * way of grouping them together so that, for example, several chartsheets * could be printed in one go. A chartsheet that has been activated via the * `chartsheet_activate()` function will also appear as selected. * * See also `worksheet_select()`. * */ void chartsheet_select(lxw_chartsheet *chartsheet); /** * @brief Hide the current chartsheet. * * @param chartsheet Pointer to a lxw_chartsheet instance to be updated. * * The `%chartsheet_hide()` function is used to hide a chartsheet: * * @code * chartsheet_hide(chartsheet2); * @endcode * * You may wish to hide a chartsheet in order to avoid confusing a user with * intermediate data or calculations. * * @image html hide_sheet.png * * A hidden chartsheet can not be activated or selected so this function is * mutually exclusive with the `chartsheet_activate()` and * `chartsheet_select()` functions. In addition, since the first chartsheet * will default to being the active chartsheet, you cannot hide the first * chartsheet without activating another sheet: * * @code * chartsheet_activate(chartsheet2); * chartsheet_hide(chartsheet1); * @endcode * * See also `worksheet_hide()`. * */ void chartsheet_hide(lxw_chartsheet *chartsheet); /** * @brief Set current chartsheet as the first visible sheet tab. * * @param chartsheet Pointer to a lxw_chartsheet instance to be updated. * * The `chartsheet_activate()` function determines which chartsheet is * initially selected. However, if there are a large number of chartsheets the * selected chartsheet may not appear on the screen. To avoid this you can * select the leftmost visible chartsheet tab using * `%chartsheet_set_first_sheet()`: * * @code * chartsheet_set_first_sheet(chartsheet19); // First visible chartsheet tab. * chartsheet_activate(chartsheet20); // First visible chartsheet. * @endcode * * This function is not required very often. The default value is the first * chartsheet. * * See also `worksheet_set_first_sheet()`. * */ void chartsheet_set_first_sheet(lxw_chartsheet *chartsheet); /** * @brief Set the color of the chartsheet tab. * * @param chartsheet Pointer to a lxw_chartsheet instance to be updated. * @param color The tab color. * * The `%chartsheet_set_tab_color()` function is used to change the color of * the chartsheet tab: * * @code * chartsheet_set_tab_color(chartsheet1, LXW_COLOR_RED); * chartsheet_set_tab_color(chartsheet2, LXW_COLOR_GREEN); * chartsheet_set_tab_color(chartsheet3, 0xFF9900); // Orange. * @endcode * * The color should be an RGB integer value, see @ref working_with_colors. * * See also `worksheet_set_tab_color()`. */ void chartsheet_set_tab_color(lxw_chartsheet *chartsheet, lxw_color_t color); /** * @brief Protect elements of a chartsheet from modification. * * @param chartsheet Pointer to a lxw_chartsheet instance to be updated. * @param password A chartsheet password. * @param options Chartsheet elements to protect. * * The `%chartsheet_protect()` function protects chartsheet elements from * modification: * * @code * chartsheet_protect(chartsheet, "Some Password", options); * @endcode * * The `password` and lxw_protection pointer are both optional: * * @code * chartsheet_protect(chartsheet2, NULL, my_options); * chartsheet_protect(chartsheet3, "password", NULL); * chartsheet_protect(chartsheet4, "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 had no effect on * chartsheets. * * You can specify which chartsheet elements you wish to protect by passing a * lxw_protection pointer in the `options` argument. In Excel chartsheets only * have two protection options: * * no_content * no_objects * * All parameters are off by default. Individual elements can be protected as * follows: * * @code * lxw_protection options = { * .no_content = 1, * .no_objects = 1, * }; * * chartsheet_protect(chartsheet, NULL, &options); * * @endcode * * See also worksheet_protect(). * * **Note:** Sheet 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. */ void chartsheet_protect(lxw_chartsheet *chartsheet, const char *password, lxw_protection *options); /** * @brief Set the chartsheet zoom factor. * * @param chartsheet Pointer to a lxw_chartsheet instance to be updated. * @param scale Chartsheet zoom factor. * * Set the chartsheet zoom factor in the range `10 <= zoom <= 400`: * * @code * chartsheet_set_zoom(chartsheet, 75); * @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. * * See also `worksheet_set_zoom()`. */ void chartsheet_set_zoom(lxw_chartsheet *chartsheet, uint16_t scale); /** * @brief Set the page orientation as landscape. * * @param chartsheet Pointer to a lxw_chartsheet instance to be updated. * * This function is used to set the orientation of a chartsheet's printed page * to landscape. The default chartsheet orientation is landscape, so this * function isn't generally required: * * @code * chartsheet_set_landscape(chartsheet); * @endcode */ void chartsheet_set_landscape(lxw_chartsheet *chartsheet); /** * @brief Set the page orientation as portrait. * * @param chartsheet Pointer to a lxw_chartsheet instance to be updated. * * This function is used to set the orientation of a chartsheet's printed page * to portrait: * * @code * chartsheet_set_portrait(chartsheet); * @endcode */ void chartsheet_set_portrait(lxw_chartsheet *chartsheet); /** * @brief Set the paper type for printing. * * @param chartsheet Pointer to a lxw_chartsheet 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 * chartsheet: * * @code * chartsheet_set_paper(chartsheet1, 1); // US Letter * chartsheet_set_paper(chartsheet2, 9); // A4 * @endcode * * If you do not specify a paper type the chartsheet will print using the * printer's default paper style. * * See `worksheet_set_paper()` for a full list of available paper sizes. */ void chartsheet_set_paper(lxw_chartsheet *chartsheet, uint8_t paper_type); /** * @brief Set the chartsheet margins for the printed page. * * @param chartsheet Pointer to a lxw_chartsheet 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 `%chartsheet_set_margins()` function is used to set the margins of the * chartsheet when it is printed. The units are in inches. Specifying `-1` for * any parameter will give the default Excel value as shown above. * * @code * chartsheet_set_margins(chartsheet, 1.3, 1.2, -1, -1); * @endcode * */ void chartsheet_set_margins(lxw_chartsheet *chartsheet, double left, double right, double top, double bottom); /** * @brief Set the printed page header caption. * * @param chartsheet Pointer to a lxw_chartsheet 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 * * @code * chartsheet_set_header(chartsheet, "&LHello"); * * // --------------------------------------------------------------- * // | | * // | Hello | * // | | * * * chartsheet_set_header(chartsheet, "&CHello"); * * // --------------------------------------------------------------- * // | | * // | Hello | * // | | * * * chartsheet_set_header(chartsheet, "&RHello"); * * // --------------------------------------------------------------- * // | | * // | Hello | * // | | * * * @endcode * * See `worksheet_set_header()` for a full explanation of the syntax of * Excel's header formatting and control characters. * */ lxw_error chartsheet_set_header(lxw_chartsheet *chartsheet, const char *string); /** * @brief Set the printed page footer caption. * * @param chartsheet Pointer to a lxw_chartsheet instance to be updated. * @param string The footer string. * * @return A #lxw_error code. * * The syntax of this function is the same as chartsheet_set_header(). * */ lxw_error chartsheet_set_footer(lxw_chartsheet *chartsheet, const char *string); /** * @brief Set the printed page header caption with additional options. * * @param chartsheet Pointer to a lxw_chartsheet 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 chartsheet_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 }; * * chartsheet_set_header_opt(chartsheet, "Some text", &header_options); * * @endcode * */ lxw_error chartsheet_set_header_opt(lxw_chartsheet *chartsheet, const char *string, lxw_header_footer_options *options); /** * @brief Set the printed page footer caption with additional options. * * @param chartsheet Pointer to a lxw_chartsheet 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 chartsheet_set_header_opt(). * */ lxw_error chartsheet_set_footer_opt(lxw_chartsheet *chartsheet, const char *string, lxw_header_footer_options *options); lxw_chartsheet *lxw_chartsheet_new(lxw_worksheet_init_data *init_data); void lxw_chartsheet_free(lxw_chartsheet *chartsheet); void lxw_chartsheet_assemble_xml_file(lxw_chartsheet *chartsheet); /* Declarations required for unit testing. */ #ifdef TESTING STATIC void _chartsheet_xml_declaration(lxw_chartsheet *chartsheet); STATIC void _chartsheet_write_sheet_protection(lxw_chartsheet *chartsheet); #endif /* TESTING */ /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* __LXW_CHARTSHEET_H__ */ writexl/src/include/xlsxwriter/content_types.h0000644000176200001440000000524014747162622021500 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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." #define LXW_APP_MSEXCEL "application/vnd.ms-excel." /* * 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); 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_chartsheet_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_table_name(lxw_content_types *content_types, const char *name); void lxw_ct_add_comment_name(lxw_content_types *content_types, const char *name); void lxw_ct_add_vml_name(lxw_content_types *content_types); 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); void lxw_ct_add_metadata(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__ */ writexl/src/include/xlsxwriter/hash_table.h0000644000176200001440000000371014747162622020674 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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__ */ writexl/src/include/xlsxwriter/format.h0000644000176200001440000011403214747162622020072 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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 uint32_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 0x000000 #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 { LXW_UNDERLINE_NONE = 0, /** Single underline */ LXW_UNDERLINE_SINGLE, /** 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 }; /** * Diagonal border types. * */ enum lxw_format_diagonal_types { /** Cell diagonal border from bottom left to top right. */ LXW_DIAGONAL_BORDER_UP = 1, /** Cell diagonal border from top left to bottom right. */ LXW_DIAGONAL_BORDER_DOWN, /** Cell diagonal border in both directions. */ 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; lxw_hash_table *dxf_format_indices; uint16_t *num_xf_formats; uint16_t *num_dxf_formats; int32_t xf_index; int32_t dxf_index; int32_t xf_id; 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; lxw_color_t dxf_fg_color; lxw_color_t dxf_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; uint8_t quote_prefix; 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 theme; 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); void lxw_format_free(lxw_format *format); int32_t lxw_format_get_xf_index(lxw_format *format); int32_t lxw_format_get_dxf_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); /** * @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 * * To set a number format that matches an Excel format category such as "Date" * or "Currency" see @ref ww_formats_categories. * * 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_index(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(). * - See also @ref ww_formats_categories. */ 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); /** * @brief Set the diagonal cell border type. * * @param format Pointer to a Format instance. * @param type The #lxw_format_diagonal_types diagonal border type. * * Set the diagonal cell border type: * * @code * lxw_format *format1 = workbook_add_format(workbook); * format_set_diag_type( format1, LXW_DIAGONAL_BORDER_UP); * * lxw_format *format2 = workbook_add_format(workbook); * format_set_diag_type( format2, LXW_DIAGONAL_BORDER_DOWN); * * lxw_format *format3 = workbook_add_format(workbook); * format_set_diag_type( format3, LXW_DIAGONAL_BORDER_UP_DOWN); * * lxw_format *format4 = workbook_add_format(workbook); * format_set_diag_type( format4, LXW_DIAGONAL_BORDER_UP_DOWN); * format_set_diag_border(format4, LXW_BORDER_HAIR); * format_set_diag_color( format4, LXW_COLOR_RED); * * worksheet_write_string(worksheet, CELL("B3"), "Text", format1); * worksheet_write_string(worksheet, CELL("B6"), "Text", format2); * worksheet_write_string(worksheet, CELL("B9"), "Text", format3); * worksheet_write_string(worksheet, CELL("B12"), "Text", format4); * @endcode * * @image html diagonal_border.png * * The allowable border types are defined in #lxw_format_diagonal_types: * * - #LXW_DIAGONAL_BORDER_UP: Cell diagonal border from bottom left to top * right. * * - #LXW_DIAGONAL_BORDER_DOWN: Cell diagonal border from top left to bottom * right. * * - #LXW_DIAGONAL_BORDER_UP_DOWN: Cell diagonal border from top left to * bottom right. A combination of the 2 previous types. * * If the border style isn't specified with `format_set_diag_border()` then it * will default to #LXW_BORDER_THIN. */ void format_set_diag_type(lxw_format *format, uint8_t type); /** * @brief Set the diagonal cell border style. * * @param format Pointer to a Format instance. * @param style The #lxw_format_borders style. * * Set the diagonal border style. This should be a #lxw_format_borders value. * See the example above. * */ void format_set_diag_border(lxw_format *format, uint8_t style); /** * @brief Set the diagonal cell border color. * * @param format Pointer to a Format instance. * @param color The cell diagonal border color. * * Set the diagonal border color. The color should be an RGB integer value, * see @ref working_with_colors and the above example. */ void format_set_diag_color(lxw_format *format, lxw_color_t color); /** * @brief Turn on quote prefix for the format. * * @param format Pointer to a Format instance. * * Set the quote prefix property of a format to ensure a string is treated * as a string after editing. This is the same as prefixing the string with * a single quote in Excel. You don't need to add the quote to the * string but you do need to add the format. * * @code * format = workbook_add_format(workbook); * format_set_quote_prefix(format); * * worksheet_write_string(worksheet, 0, 0, "=Foo", format); * @endcode * */ void format_set_quote_prefix(lxw_format *format); 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); void format_set_hyperlink(lxw_format *format); void format_set_color_indexed(lxw_format *format, uint8_t value); void format_set_font_only(lxw_format *format); /* Declarations required for unit testing. */ #ifdef TESTING #endif /* TESTING */ /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* __LXW_FORMAT_H__ */ writexl/src/include/xlsxwriter/common.h0000644000176200001440000003503414747162622020076 0ustar liggesusers/* * libxlsxwriter * * Copyright 2014-2022, 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 #if __GNUC__ >= 5 #define DEPRECATED(func, msg) func __attribute__ ((deprecated(msg))) #elif defined(_MSC_VER) #define DEPRECATED(func, msg) __declspec(deprecated, msg) func #else #define DEPRECATED(func, msg) func #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, /** False value. Used to turn off a property that is default on, in order * to distinguish it from an uninitialized value. */ LXW_EXPLICIT_FALSE }; /** * @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, /** Error reading a tmpfile. */ LXW_ERROR_READING_TMPFILE, /** Zip generic error ZIP_ERRNO while creating the xlsx file. */ LXW_ERROR_ZIP_FILE_OPERATION, /** Zip error ZIP_PARAMERROR while creating the xlsx file. */ LXW_ERROR_ZIP_PARAMETER_ERROR, /** Zip error ZIP_BADZIPFILE (use_zip64 option may be required). */ LXW_ERROR_ZIP_BAD_ZIP_FILE, /** Zip error ZIP_INTERNALERROR while creating the xlsx file. */ LXW_ERROR_ZIP_INTERNAL_ERROR, /** File error or unknown zip error when adding sub file to xlsx file. */ LXW_ERROR_ZIP_FILE_ADD, /** Unknown zip error when closing xlsx file. */ LXW_ERROR_ZIP_CLOSE, /** Feature is not currently supported in this configuration. */ LXW_ERROR_FEATURE_NOT_SUPPORTED, /** 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 cannot contain invalid characters: '[ ] : * ? / \\' */ LXW_ERROR_INVALID_SHEETNAME_CHARACTER, /** Worksheet name cannot start or end with an apostrophe. */ LXW_ERROR_SHEETNAME_START_END_APOSTROPHE, /** Worksheet name is already in use. */ LXW_ERROR_SHEETNAME_ALREADY_USED, /** Parameter exceeds Excel's limit of 32 characters. */ LXW_ERROR_32_STRING_LENGTH_EXCEEDED, /** 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 hyperlink length (2079) exceeded. */ LXW_ERROR_WORKSHEET_MAX_URL_LENGTH_EXCEEDED, /** 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 }; /* Size of MD5 byte arrays. */ #define LXW_MD5_SIZE 16 /* 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") /* GUID string length. */ #define LXW_GUID_LENGTH sizeof("{12345678-1234-1234-1234-1234567890AB}\0") #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_PORTRAIT 1 #define LXW_LANDSCAPE 0 #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" /* Use REprintf() for error handling when compiled as an R library. */ #ifdef USE_R_LANG #include #define LXW_PRINTF REprintf #define LXW_STDERR #else #define LXW_PRINTF fprintf #define LXW_STDERR stderr, #endif #define LXW_ERROR(message) \ LXW_PRINTF(LXW_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) \ do { \ if (!pointer) { \ LXW_MEM_ERROR(); \ goto label; \ } \ } while (0) #define RETURN_ON_MEM_ERROR(pointer, error) \ do { \ if (!pointer) { \ LXW_MEM_ERROR(); \ return error; \ } \ } while (0) #define RETURN_VOID_ON_MEM_ERROR(pointer) \ do { \ if (!pointer) { \ LXW_MEM_ERROR(); \ return; \ } \ } while (0) #define RETURN_ON_ERROR(error) \ do { \ if (error) \ return error; \ } while (0) #define RETURN_AND_ZIPCLOSE_ON_ERROR(error) \ do { \ if (error) { \ zipClose(self->zipfile, NULL); \ return error; \ } \ } while (0) #define LXW_WARN(message) \ LXW_PRINTF(LXW_STDERR "[WARNING]: " message "\n") /* We can't use variadic macros here since we support ANSI C. */ #define LXW_WARN_FORMAT(message) \ LXW_PRINTF(LXW_STDERR "[WARNING]: " message "\n") #define LXW_WARN_FORMAT1(message, var) \ LXW_PRINTF(LXW_STDERR "[WARNING]: " message "\n", var) #define LXW_WARN_FORMAT2(message, var1, var2) \ LXW_PRINTF(LXW_STDERR "[WARNING]: " message "\n", var1, var2) #define LXW_WARN_FORMAT3(message, var1, var2, var3) \ LXW_PRINTF(LXW_STDERR "[WARNING]: " message "\n", var1, var2, var3) /* Chart axis type checks. */ #define LXW_WARN_CAT_AXIS_ONLY(function) \ do { \ if (!axis->is_category) { \ LXW_PRINTF(LXW_STDERR "[WARNING]: " \ function "() is only valid for category axes\n"); \ return; \ } \ } while (0) #define LXW_WARN_VALUE_AXIS_ONLY(function) \ do { \ if (!axis->is_value) { \ LXW_PRINTF(LXW_STDERR "[WARNING]: " \ function "() is only valid for value axes\n"); \ return; \ } \ } while (0) #define LXW_WARN_DATE_AXIS_ONLY(function) \ do { \ if (!axis->is_date) { \ LXW_PRINTF(LXW_STDERR "[WARNING]: " \ function "() is only valid for date axes\n"); \ return; \ } \ } while (0) #define LXW_WARN_CAT_AND_DATE_AXIS_ONLY(function) \ do { \ if (!axis->is_category && !axis->is_date) { \ LXW_PRINTF(LXW_STDERR "[WARNING]: " \ function "() is only valid for category and date axes\n"); \ return; \ } \ } while (0) #define LXW_WARN_VALUE_AND_DATE_AXIS_ONLY(function) \ do { \ if (!axis->is_value && !axis->is_date) { \ LXW_PRINTF(LXW_STDERR "[WARNING]: " \ function "() is only valid for value and date axes\n"); \ return; \ } \ } while (0) #ifndef LXW_BIG_ENDIAN #define LXW_UINT16_HOST(n) (n) #define LXW_UINT32_HOST(n) (n) #define LXW_UINT16_NETWORK(n) ((((n) & 0x00FF) << 8) | (((n) & 0xFF00) >> 8)) #define LXW_UINT32_NETWORK(n) ((((n) & 0xFF) << 24) | \ (((n) & 0xFF00) << 8) | \ (((n) & 0xFF0000) >> 8) | \ (((n) & 0xFF000000) >> 24)) #else #define LXW_UINT16_NETWORK(n) (n) #define LXW_UINT32_NETWORK(n) (n) #define LXW_UINT16_HOST(n) ((((n) & 0x00FF) << 8) | (((n) & 0xFF00) >> 8)) #define LXW_UINT32_HOST(n) ((((n) & 0xFF) << 24) | \ (((n) & 0xFF00) << 8) | \ (((n) & 0xFF0000) >> 8) | \ (((n) & 0xFF000000) >> 24)) #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__ */ writexl/src/tmpfileplus/0000755000176200001440000000000014766110233015106 5ustar liggesuserswritexl/src/tmpfileplus/tmpfileplus.c0000644000176200001440000002147314747162622017635 0ustar liggesusers/* $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-1); 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 = 0; 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; } writexl/src/tmpfileplus/tmpfileplus.h0000644000176200001440000000414414747162622017636 0ustar liggesusers/* $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_ */ writexl/src/write_xlsx.c0000644000176200001440000001650414747162622015134 0ustar liggesusers#define R_NO_REMAP #define STRICT_R_HEADERS #include #include #include #include #include typedef enum { COL_LOGICAL, COL_REAL, COL_INTEGER, COL_STRING, COL_DATE, COL_POSIXCT, COL_HYPERLINK, COL_FORMULA, COL_BLANK, COL_UNKNOWN } R_COL_TYPE; #define assert_that(a, b) bail_if(!a, b) #define max(a,b) (a > b) ? a : b static void bail_if(int check, const char * error){ if(check) Rf_errorcall(R_NilValue, "Error in writexl: %s", error); } static void assert_lxw(lxw_error err){ if(err != LXW_NO_ERROR) Rf_errorcall(R_NilValue, "Error in libxlsxwriter: '%s'", lxw_strerror(err)); } static R_COL_TYPE get_type(SEXP col){ if(Rf_inherits(col, "Date")) return COL_DATE; if(Rf_inherits(col, "POSIXct")) return COL_POSIXCT; if(Rf_inherits(col, "xl_hyperlink")) return COL_HYPERLINK; if(Rf_isString(col) && Rf_inherits(col, "xl_formula")) return COL_FORMULA; switch(TYPEOF(col)){ case STRSXP: return COL_STRING; case INTSXP: return COL_INTEGER; case REALSXP: return COL_REAL; case LGLSXP: return COL_LOGICAL; default: return COL_UNKNOWN; }; } //global options static char TEMPDIR[2048] = {0}; //set to R tempdir when pkg is loaded SEXP C_set_tempdir(SEXP dir){ strcpy(TEMPDIR, Rf_translateChar(STRING_ELT(dir, 0))); return Rf_mkString(TEMPDIR); } SEXP C_write_data_frame_list(SEXP df_list, SEXP file, SEXP col_names, SEXP format_headers, SEXP use_zip64){ assert_that(Rf_isVectorList(df_list), "Object is not a list"); assert_that(Rf_isString(file) && Rf_length(file), "Invalid file path"); assert_that(Rf_isLogical(col_names), "col_names must be logical"); assert_that(Rf_isLogical(format_headers), "format_headers must be logical"); //create workbook lxw_workbook_options options = { .constant_memory = 1, .tmpdir = TEMPDIR, .use_zip64 = Rf_asLogical(use_zip64) }; lxw_workbook *workbook = workbook_new_opt(Rf_translateChar(STRING_ELT(file, 0)), &options); assert_that(workbook, "failed to create workbook"); //how to format dates lxw_format * date = workbook_add_format(workbook); format_set_num_format(date, "yyyy-mm-dd"); //how to format timetamps lxw_format * datetime = workbook_add_format(workbook); format_set_num_format(datetime, "yyyy-mm-dd HH:mm:ss UTC"); //how to format headers (bold + center) lxw_format * title = workbook_add_format(workbook); format_set_bold(title); format_set_align(title, LXW_ALIGN_CENTER); //how to format hyperlinks (underline + blue) lxw_format * hyperlink = workbook_add_format(workbook); format_set_underline(hyperlink, LXW_UNDERLINE_SINGLE); format_set_font_color(hyperlink, LXW_COLOR_BLUE); //iterate over sheets SEXP df_names = PROTECT(Rf_getAttrib(df_list, R_NamesSymbol)); for(size_t s = 0; s < Rf_length(df_list); s++){ //create sheet const char * sheet_name = Rf_length(df_names) > s && Rf_length(STRING_ELT(df_names, s)) ? \ Rf_translateCharUTF8(STRING_ELT(df_names, s)) : NULL; lxw_worksheet *sheet = workbook_add_worksheet(workbook, sheet_name); assert_that(sheet, "failed to create workbook"); //get data frame size_t cursor = 0; SEXP df = VECTOR_ELT(df_list, s); assert_that(Rf_inherits(df, "data.frame"), "object is not a data frame"); //create header row if(Rf_asLogical(col_names)){ SEXP names = PROTECT(Rf_getAttrib(df, R_NamesSymbol)); for(size_t i = 0; i < Rf_length(names); i++) worksheet_write_string(sheet, cursor, i, Rf_translateCharUTF8(STRING_ELT(names, i)), NULL); if(Rf_asLogical(format_headers)) assert_lxw(worksheet_set_row(sheet, cursor, 15, title)); UNPROTECT(1); cursor++; } // number of records size_t cols = Rf_length(df); size_t rows = 0; // determinte how to format each column R_COL_TYPE coltypes[cols]; for(size_t i = 0; i < cols; i++){ SEXP COL = VECTOR_ELT(df, i); coltypes[i] = get_type(COL); if(!Rf_isMatrix(COL) && !Rf_inherits(COL, "data.frame")) rows = max(rows, Rf_length(COL)); if(coltypes[i] == COL_DATE) assert_lxw(worksheet_set_column(sheet, i, i, 20, date)); if(coltypes[i] == COL_POSIXCT) assert_lxw(worksheet_set_column(sheet, i, i, 20, datetime)); } // Need to iterate by row first for performance for (size_t i = 0; i < rows; i++) { for(size_t j = 0; j < cols; j++){ SEXP col = VECTOR_ELT(df, j); switch(coltypes[j]){ case COL_DATE:{ double val = Rf_isReal(col) ? REAL(col)[i] : INTEGER(col)[i]; if(Rf_isReal(col) ? R_FINITE(val) : val != NA_INTEGER) assert_lxw(worksheet_write_number(sheet, cursor, j, 25569 + val, NULL)); }; continue; case COL_POSIXCT: { double val = REAL(col)[i]; if(R_FINITE(val)){ val = 25568.0 + val / (24*60*60); if(val >= 60.0) val = val + 1.0; assert_lxw(worksheet_write_number(sheet, cursor, j, val , NULL)); } }; continue; case COL_STRING:{ SEXP val = STRING_ELT(col, i); // NB: xlsx does distinguish between empty string and NA if(val != NA_STRING && Rf_length(val)) assert_lxw(worksheet_write_string(sheet, cursor, j, Rf_translateCharUTF8(val), NULL)); }; continue; case COL_FORMULA:{ SEXP val = STRING_ELT(col, i); if(val != NA_STRING && Rf_length(val)) assert_lxw(worksheet_write_formula(sheet, cursor, j, Rf_translateCharUTF8(val), NULL)); }; continue; case COL_HYPERLINK:{ SEXP val = STRING_ELT(col, i); if(val != NA_STRING && Rf_length(val)) assert_lxw(worksheet_write_formula(sheet, cursor, j, Rf_translateCharUTF8(val), hyperlink)); }; continue; case COL_REAL:{ double val = REAL(col)[i]; if(val == R_PosInf) assert_lxw(worksheet_write_string(sheet, cursor, j, "Inf", NULL)); else if(val == R_NegInf) assert_lxw(worksheet_write_string(sheet, cursor, j, "-Inf", NULL)); else if(R_FINITE(val)) // skips NA and NAN assert_lxw(worksheet_write_number(sheet, cursor, j, val, NULL)); }; continue; case COL_INTEGER:{ int val = INTEGER(col)[i]; if(val != NA_INTEGER) assert_lxw(worksheet_write_number(sheet, cursor, j, val, NULL)); }; continue; case COL_LOGICAL:{ int val = LOGICAL(col)[i]; if(val != NA_LOGICAL) assert_lxw(worksheet_write_boolean(sheet, cursor, j, val, NULL)); }; continue; default: continue; }; } cursor++; } } //this both writes the xlsx file and frees the memory assert_lxw(workbook_close(workbook)); UNPROTECT(1); return file; } SEXP C_lxw_version(void){ return Rf_mkString(LXW_VERSION); } static const R_CallMethodDef CallEntries[] = { {"C_lxw_version", (DL_FUNC) &C_lxw_version, 0}, {"C_set_tempdir", (DL_FUNC) &C_set_tempdir, 1}, {"C_write_data_frame_list", (DL_FUNC) &C_write_data_frame_list, 5}, {NULL, NULL, 0} }; attribute_visible void R_init_writexl(DllInfo *dll) { R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); R_forceSymbols(dll, TRUE); } writexl/src/libxlsxwriter/0000755000176200001440000000000014766110233015464 5ustar liggesuserswritexl/src/libxlsxwriter/content_types.c0000644000176200001440000002314614747162622020544 0ustar liggesusers/***************************************************************************** * content_types - A library for creating Excel XLSX content_types files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, 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(void) { 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"); 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 chartsheet to the ContentTypes overrides. */ void lxw_ct_add_chartsheet_name(lxw_content_types *self, const char *name) { lxw_ct_add_override(self, name, LXW_APP_DOCUMENT "spreadsheetml.chartsheet+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 name of a table to the ContentTypes overrides. */ void lxw_ct_add_table_name(lxw_content_types *self, const char *name) { lxw_ct_add_override(self, name, LXW_APP_DOCUMENT "spreadsheetml.table+xml"); } /* * Add the name of a VML drawing to the ContentTypes overrides. */ void lxw_ct_add_vml_name(lxw_content_types *self) { lxw_ct_add_default(self, "vml", LXW_APP_DOCUMENT "vmlDrawing"); } /* * Add the name of a comment to the ContentTypes overrides. */ void lxw_ct_add_comment_name(lxw_content_types *self, const char *name) { lxw_ct_add_override(self, name, LXW_APP_DOCUMENT "spreadsheetml.comments+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"); } /* * Add the metadata file to the ContentTypes overrides. */ void lxw_ct_add_metadata(lxw_content_types *self) { lxw_ct_add_override(self, "/xl/metadata.xml", LXW_APP_DOCUMENT "spreadsheetml.sheetMetadata+xml"); } writexl/src/libxlsxwriter/utility.c0000644000176200001440000004217114747162622017350 0ustar liggesusers/***************************************************************************** * utility - Utility functions for libxlsxwriter. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ #ifdef USE_FMEMOPEN #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include "xlsxwriter.h" #include "xlsxwriter/common.h" #include "xlsxwriter/third_party/tmpfileplus.h" #ifdef USE_DTOA_LIBRARY #include "xlsxwriter/third_party/emyg_dtoa.h" #endif 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.", "Error reading a tmpfile.", "Zip generic error ZIP_ERRNO while creating the xlsx file.", "Zip error ZIP_PARAMERROR while creating the xlsx file.", "Zip error ZIP_BADZIPFILE (use_zip64 option may be required).", "Zip error ZIP_INTERNALERROR while creating the xlsx file.", "File error or unknown zip error when adding sub file to xlsx file.", "Unknown zip error when closing xlsx file.", "Feature is not currently supported in this configuration.", "NULL function parameter ignored.", "Function parameter validation error.", "Worksheet name exceeds Excel's limit of 31 characters.", "Worksheet name cannot contain invalid characters: '[ ] : * ? / \\'", "Worksheet name cannot start or end with an apostrophe.", "Worksheet name is already in use.", "Parameter exceeds Excel's limit of 32 characters.", "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 hyperlink length (2079) exceeded.", "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; size_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, with a 1900 * or 1904 epoch. */ double lxw_datetime_to_excel_date_epoch(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; } /* * Convert a lxw_datetime struct to an Excel serial date, for the 1900 epoch. */ double lxw_datetime_to_excel_datetime(lxw_datetime *datetime) { return lxw_datetime_to_excel_date_epoch(datetime, LXW_FALSE); } /* * Convert a unix datetime (1970/01/01 epoch) to an Excel serial date, with a * 1900 epoch. */ double lxw_unixtime_to_excel_date(int64_t unixtime) { return lxw_unixtime_to_excel_date_epoch(unixtime, LXW_FALSE); } /* * Convert a unix datetime (1970/01/01 epoch) to an Excel serial date, with a * 1900 or 1904 epoch. */ double lxw_unixtime_to_excel_date_epoch(int64_t unixtime, uint8_t date_1904) { double excel_datetime = 0.0; double epoch = date_1904 ? 24107.0 : 25568.0; excel_datetime = epoch + (unixtime / (24 * 60 * 60.0)); if (!date_1904 && excel_datetime >= 60.0) excel_datetime = excel_datetime + 1.0; return excel_datetime; } /* 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 function to strdup() a formula string without the leading "=". */ char * lxw_strdup_formula(const char *formula) { if (!formula) return NULL; if (formula[0] == '=') return lxw_strdup(formula + 1); else return lxw_strdup(formula); } /* 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(const char *tmpdir) { #ifndef USE_STANDARD_TMPFILE return tmpfileplus(tmpdir, NULL, NULL, 0); #else (void) tmpdir; return tmpfile(); #endif } /** * Return a memory-backed file if supported, otherwise a temporary one */ FILE * lxw_get_filehandle(char **buf, size_t *size, const char *tmpdir) { static size_t s; if (!size) size = &s; *buf = NULL; *size = 0; #ifdef USE_FMEMOPEN (void) tmpdir; return open_memstream(buf, size); #else return lxw_tmpfile(tmpdir); #endif } /* * Use third party function to handle sprintf of doubles for locale portable * code. */ #ifdef USE_DTOA_LIBRARY int lxw_sprintf_dbl(char *data, double number) { emyg_dtoa(number, data); return 0; } #endif /* * Retrieve runtime library version. */ const char * lxw_version(void) { return LXW_VERSION; } /* * Retrieve runtime library version ID. */ uint16_t lxw_version_id(void) { return LXW_VERSION_ID; } /* * Hash a worksheet password. Based on the algorithm in ECMA-376-4:2016, * Office Open XML File Formats - Transitional Migration Features, * Additional attributes for workbookProtection element (Part 1, §18.2.29). */ uint16_t lxw_hash_password(const char *password) { uint16_t byte_count = (uint16_t) strlen(password); uint16_t hash = 0; const char *p = &password[byte_count]; if (!byte_count) return hash; while (p-- != password) { hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff); hash ^= *p & 0xFF; } hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff); hash ^= byte_count; hash ^= 0xCE4B; return hash; } /* Make a simple portable version of fopen() for Windows. */ #ifdef __MINGW32__ #undef _WIN32 #endif #ifdef _WIN32 #include FILE * lxw_fopen(const char *filename, const char *mode) { int n; wchar_t wide_filename[_MAX_PATH + 1] = L""; wchar_t wide_mode[_MAX_PATH + 1] = L""; n = MultiByteToWideChar(CP_UTF8, 0, filename, (int) strlen(filename), wide_filename, _MAX_PATH); if (n == 0) { LXW_ERROR("MultiByteToWideChar error: filename"); return NULL; } n = MultiByteToWideChar(CP_UTF8, 0, mode, (int) strlen(mode), wide_mode, _MAX_PATH); if (n == 0) { LXW_ERROR("MultiByteToWideChar error: mode"); return NULL; } return _wfopen(wide_filename, wide_mode); } #else FILE * lxw_fopen(const char *filename, const char *mode) { return fopen(filename, mode); } #endif writexl/src/libxlsxwriter/table.c0000644000176200001440000002027314747162622016733 0ustar liggesusers/***************************************************************************** * table - A library for creating Excel XLSX table files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ #include "xlsxwriter/xmlwriter.h" #include "xlsxwriter/worksheet.h" #include "xlsxwriter/table.h" #include "xlsxwriter/utility.h" /* * Forward declarations. */ /***************************************************************************** * * Private functions. * ****************************************************************************/ /* * Create a new table object. */ lxw_table * lxw_table_new(void) { lxw_table *table = calloc(1, sizeof(lxw_table)); GOTO_LABEL_ON_MEM_ERROR(table, mem_error); return table; mem_error: lxw_table_free(table); return NULL; } /* * Free a table object. */ void lxw_table_free(lxw_table *table) { if (!table) return; free(table); } /***************************************************************************** * * XML functions. * ****************************************************************************/ /* * Write the XML declaration. */ STATIC void _table_xml_declaration(lxw_table *self) { lxw_xml_declaration(self->file); } /* * Write the element. */ STATIC void _table_write_table(lxw_table *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char xmlns[] = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; lxw_table_obj *table_obj = self->table_obj; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); LXW_PUSH_ATTRIBUTES_INT("id", table_obj->id); if (table_obj->name) LXW_PUSH_ATTRIBUTES_STR("name", table_obj->name); else LXW_PUSH_ATTRIBUTES_STR("name", "Table1"); if (table_obj->name) LXW_PUSH_ATTRIBUTES_STR("displayName", table_obj->name); else LXW_PUSH_ATTRIBUTES_STR("displayName", "Table1"); LXW_PUSH_ATTRIBUTES_STR("ref", table_obj->sqref); if (table_obj->no_header_row) LXW_PUSH_ATTRIBUTES_STR("headerRowCount", "0"); if (table_obj->total_row) LXW_PUSH_ATTRIBUTES_STR("totalsRowCount", "1"); else LXW_PUSH_ATTRIBUTES_STR("totalsRowShown", "0"); lxw_xml_start_tag(self->file, "table", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _table_write_auto_filter(lxw_table *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; if (self->table_obj->no_autofilter) return; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("ref", self->table_obj->filter_sqref); lxw_xml_empty_tag(self->file, "autoFilter", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _table_write_table_column(lxw_table *self, uint16_t id, lxw_table_column *column) { struct xml_attribute_list attributes; struct xml_attribute *attribute; int32_t dfx_id; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("id", id); LXW_PUSH_ATTRIBUTES_STR("name", column->header); if (column->total_string) { LXW_PUSH_ATTRIBUTES_STR("totalsRowLabel", column->total_string); } else if (column->total_function) { if (column->total_function == LXW_TABLE_FUNCTION_AVERAGE) LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "average"); if (column->total_function == LXW_TABLE_FUNCTION_COUNT_NUMS) LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "countNums"); if (column->total_function == LXW_TABLE_FUNCTION_COUNT) LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "count"); if (column->total_function == LXW_TABLE_FUNCTION_MAX) LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "max"); if (column->total_function == LXW_TABLE_FUNCTION_MIN) LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "min"); if (column->total_function == LXW_TABLE_FUNCTION_STD_DEV) LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "stdDev"); if (column->total_function == LXW_TABLE_FUNCTION_SUM) LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "sum"); if (column->total_function == LXW_TABLE_FUNCTION_VAR) LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "var"); } if (column->format) { dfx_id = lxw_format_get_dxf_index(column->format); LXW_PUSH_ATTRIBUTES_INT("dataDxfId", dfx_id); } if (column->formula) { lxw_xml_start_tag(self->file, "tableColumn", &attributes); lxw_xml_data_element(self->file, "calculatedColumnFormula", column->formula, NULL); lxw_xml_end_tag(self->file, "tableColumn"); } else { lxw_xml_empty_tag(self->file, "tableColumn", &attributes); } LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _table_write_table_columns(lxw_table *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; uint16_t i; uint16_t num_cols = self->table_obj->num_cols; lxw_table_column **columns = self->table_obj->columns; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("count", num_cols); lxw_xml_start_tag(self->file, "tableColumns", &attributes); for (i = 0; i < num_cols; i++) _table_write_table_column(self, i + 1, columns[i]); lxw_xml_end_tag(self->file, "tableColumns"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _table_write_table_style_info(lxw_table *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char name[LXW_ATTR_32]; lxw_table_obj *table_obj = self->table_obj; LXW_INIT_ATTRIBUTES(); if (table_obj->style_type == LXW_TABLE_STYLE_TYPE_LIGHT) { if (table_obj->style_type_number != 0) { lxw_snprintf(name, LXW_ATTR_32, "TableStyleLight%d", table_obj->style_type_number); LXW_PUSH_ATTRIBUTES_STR("name", name); } } else if (table_obj->style_type == LXW_TABLE_STYLE_TYPE_MEDIUM) { lxw_snprintf(name, LXW_ATTR_32, "TableStyleMedium%d", table_obj->style_type_number); LXW_PUSH_ATTRIBUTES_STR("name", name); } else if (table_obj->style_type == LXW_TABLE_STYLE_TYPE_DARK) { lxw_snprintf(name, LXW_ATTR_32, "TableStyleDark%d", table_obj->style_type_number); LXW_PUSH_ATTRIBUTES_STR("name", name); } else { LXW_PUSH_ATTRIBUTES_STR("name", "TableStyleMedium9"); } if (table_obj->first_column) LXW_PUSH_ATTRIBUTES_STR("showFirstColumn", "1"); else LXW_PUSH_ATTRIBUTES_STR("showFirstColumn", "0"); if (table_obj->last_column) LXW_PUSH_ATTRIBUTES_STR("showLastColumn", "1"); else LXW_PUSH_ATTRIBUTES_STR("showLastColumn", "0"); if (table_obj->no_banded_rows) LXW_PUSH_ATTRIBUTES_STR("showRowStripes", "0"); else LXW_PUSH_ATTRIBUTES_STR("showRowStripes", "1"); if (table_obj->banded_columns) LXW_PUSH_ATTRIBUTES_STR("showColumnStripes", "1"); else LXW_PUSH_ATTRIBUTES_STR("showColumnStripes", "0"); lxw_xml_empty_tag(self->file, "tableStyleInfo", &attributes); LXW_FREE_ATTRIBUTES(); } /***************************************************************************** * * XML file assembly functions. * ****************************************************************************/ /* * Assemble and write the XML file. */ void lxw_table_assemble_xml_file(lxw_table *self) { /* Write the XML declaration. */ _table_xml_declaration(self); /* Write the table element. */ _table_write_table(self); /* Write the autoFilter element. */ _table_write_auto_filter(self); /* Write the tableColumns element. */ _table_write_table_columns(self); /* Write the tableStyleInfo element. */ _table_write_table_style_info(self); lxw_xml_end_tag(self->file, "table"); } /***************************************************************************** * * Public functions. * ****************************************************************************/ writexl/src/libxlsxwriter/relationships.c0000644000176200001440000001434214747162622020530 0ustar liggesusers/***************************************************************************** * relationships - A library for creating Excel XLSX relationships files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, 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(void) { 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); } writexl/src/libxlsxwriter/custom.c0000644000176200001440000001263214747162622017156 0ustar liggesusers/***************************************************************************** * custom - A library for creating Excel custom property files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, 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(void) { 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_sprintf_dbl(data, 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. * ****************************************************************************/ writexl/src/libxlsxwriter/packager.c0000644000176200001440000015370714747162622017432 0ustar liggesusers/***************************************************************************** * packager - A library for assembling xml files into an Excel XLSX file. * * A class for writing the Excel XLSX Packager file. * * This module is used in conjunction with libxlsxwriter to create an * Excel XLSX container file. * * From Wikipedia: The Open Packaging Conventions (OPC) is a * container-file technology initially created by Microsoft to store * a combination of XML and non-XML files that together form a single * entity such as an Open XML Paper Specification (OpenXPS) * document. http://en.wikipedia.org/wiki/Open_Packaging_Conventions. * * At its simplest an Excel XLSX file contains the following elements:: * * ____ [Content_Types].xml * | * |____ docProps * | |____ app.xml * | |____ core.xml * | * |____ xl * | |____ workbook.xml * | |____ worksheets * | | |____ sheet1.xml * | | * | |____ styles.xml * | | * | |____ theme * | | |____ theme1.xml * | | * | |_____rels * | |____ workbook.xml.rels * | * |_____rels * |____ .rels * * The Packager class coordinates the classes that represent the * elements of the package and writes them into the XLSX file. * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ #include #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); STATIC lxw_error _add_buffer_to_zip(lxw_packager *self, const char *buffer, size_t buffer_size, const char *filename); STATIC lxw_error _add_to_zip(lxw_packager *self, FILE * file, char **buffer, size_t *buffer_size, const char *filename); STATIC lxw_error _write_vml_drawing_rels_file(lxw_packager *self, lxw_worksheet *worksheet, uint32_t index); /* * 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 windows.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_filefunc64W(&filefunc); return zipOpen2_64(wide_filename, 0, NULL, &filefunc); } #endif STATIC voidpf ZCALLBACK _fopen_memstream(voidpf opaque, const char *filename, int mode) { lxw_packager *packager = (lxw_packager *) opaque; (void) filename; (void) mode; return lxw_get_filehandle(&packager->output_buffer, &packager->output_buffer_size, packager->tmpdir); } STATIC int ZCALLBACK _fclose_memstream(voidpf opaque, voidpf stream) { lxw_packager *packager = (lxw_packager *) opaque; FILE *file = (FILE *) stream; long size; /* Ensure memstream buffer is updated */ if (fflush(file)) goto mem_error; /* If the memstream is backed by a temporary file, no buffer is created, so create it manually. */ if (!packager->output_buffer) { if (fseek(file, 0L, SEEK_END)) goto mem_error; size = ftell(file); if (size == -1) goto mem_error; packager->output_buffer = malloc(size); GOTO_LABEL_ON_MEM_ERROR(packager->output_buffer, mem_error); rewind(file); if (fread((void *) packager->output_buffer, size, 1, file) < 1) goto mem_error; packager->output_buffer_size = size; } return fclose(file); mem_error: fclose(file); return EOF; } /* * Create a new packager object. */ lxw_packager * lxw_packager_new(const char *filename, const char *tmpdir, uint8_t use_zip64) { zlib_filefunc_def filefunc; lxw_packager *packager = calloc(1, sizeof(lxw_packager)); GOTO_LABEL_ON_MEM_ERROR(packager, mem_error); packager->buffer = calloc(1, LXW_ZIP_BUFFER_SIZE); GOTO_LABEL_ON_MEM_ERROR(packager->buffer, mem_error); packager->filename = NULL; packager->tmpdir = tmpdir; if (filename) { packager->filename = lxw_strdup(filename); GOTO_LABEL_ON_MEM_ERROR(packager->filename, mem_error); } packager->buffer_size = LXW_ZIP_BUFFER_SIZE; packager->use_zip64 = use_zip64; /* 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; packager->output_buffer = NULL; packager->output_buffer_size = 0; /* Create a zip container for the xlsx file. */ if (packager->filename) { #ifdef _WIN32 packager->zipfile = _open_zipfile_win32(packager->filename); #else packager->zipfile = zipOpen(packager->filename, 0); #endif } else { fill_fopen_filefunc(&filefunc); filefunc.opaque = packager; filefunc.zopen_file = _fopen_memstream; filefunc.zclose_file = _fclose_memstream; packager->zipfile = zipOpen2(packager->filename, 0, NULL, &filefunc); } if (packager->zipfile == NULL) goto mem_error; 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((void *) packager->buffer); free((void *) 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; char *buffer = NULL; size_t buffer_size = 0; workbook->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!workbook->file) return LXW_ERROR_CREATING_TMPFILE; lxw_workbook_assemble_xml_file(workbook); err = _add_to_zip(self, workbook->file, &buffer, &buffer_size, "xl/workbook.xml"); fclose(workbook->file); free(buffer); RETURN_ON_ERROR(err); return LXW_NO_ERROR; } /* * Write the worksheet files. */ STATIC lxw_error _write_worksheet_files(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; char *buffer = NULL; size_t buffer_size = 0; uint32_t index = 1; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) continue; else worksheet = sheet->u.worksheet; 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_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!worksheet->file) return LXW_ERROR_CREATING_TMPFILE; lxw_worksheet_assemble_xml_file(worksheet); err = _add_to_zip(self, worksheet->file, &buffer, &buffer_size, sheetname); fclose(worksheet->file); free(buffer); RETURN_ON_ERROR(err); } return LXW_NO_ERROR; } /* * Write the chartsheet files. */ STATIC lxw_error _write_chartsheet_files(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_chartsheet *chartsheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; char *buffer = NULL; size_t buffer_size = 0; uint32_t index = 1; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) chartsheet = sheet->u.chartsheet; else continue; lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "xl/chartsheets/sheet%d.xml", index++); chartsheet->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!chartsheet->file) return LXW_ERROR_CREATING_TMPFILE; lxw_chartsheet_assemble_xml_file(chartsheet); err = _add_to_zip(self, chartsheet->file, &buffer, &buffer_size, sheetname); fclose(chartsheet->file); free(buffer); RETURN_ON_ERROR(err); } 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_sheet *sheet; lxw_worksheet *worksheet; lxw_object_properties *object_props; lxw_error err; FILE *image_stream; char filename[LXW_FILENAME_LENGTH] = { 0 }; uint32_t index = 1; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) continue; else worksheet = sheet->u.worksheet; if (STAILQ_EMPTY(worksheet->image_props)) continue; STAILQ_FOREACH(object_props, worksheet->image_props, list_pointers) { if (object_props->is_duplicate) continue; lxw_snprintf(filename, LXW_FILENAME_LENGTH, "xl/media/image%d.%s", index++, object_props->extension); if (!object_props->is_image_buffer) { /* Check that the image file exists and can be opened. */ image_stream = lxw_fopen(object_props->filename, "rb"); if (!image_stream) { LXW_WARN_FORMAT1("Error adding image to xlsx file: file " "doesn't exist or can't be opened: %s.", object_props->filename); return LXW_ERROR_CREATING_TMPFILE; } err = _add_file_to_zip(self, image_stream, filename); fclose(image_stream); } else { err = _add_buffer_to_zip(self, object_props->image_buffer, object_props->image_buffer_size, filename); } RETURN_ON_ERROR(err); } } return LXW_NO_ERROR; } /* * Write the xl/vbaProject.bin file. */ STATIC lxw_error _add_vba_project(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_error err; FILE *image_stream; if (!workbook->vba_project) return LXW_NO_ERROR; /* Check that the image file exists and can be opened. */ image_stream = lxw_fopen(workbook->vba_project, "rb"); if (!image_stream) { LXW_WARN_FORMAT1("Error adding vbaProject.bin to xlsx file: " "file doesn't exist or can't be opened: %s.", workbook->vba_project); return LXW_ERROR_CREATING_TMPFILE; } err = _add_file_to_zip(self, image_stream, "xl/vbaProject.bin"); fclose(image_stream); RETURN_ON_ERROR(err); return LXW_NO_ERROR; } /* * Write the xl/vbaProjectSignature.bin file. */ STATIC lxw_error _add_vba_project_signature(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_error err; FILE *image_stream; if (!workbook->vba_project_signature) return LXW_NO_ERROR; /* Check that the image file exists and can be opened. */ image_stream = lxw_fopen(workbook->vba_project_signature, "rb"); if (!image_stream) { LXW_WARN_FORMAT1("Error adding vbaProjectSignature.bin to xlsx file: " "file doesn't exist or can't be opened: %s.", workbook->vba_project_signature); return LXW_ERROR_CREATING_TMPFILE; } err = _add_file_to_zip(self, image_stream, "xl/vbaProjectSignature.bin"); fclose(image_stream); RETURN_ON_ERROR(err); return LXW_NO_ERROR; } /* * 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 }; char *buffer = NULL; size_t buffer_size = 0; uint32_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_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!chart->file) return LXW_ERROR_CREATING_TMPFILE; lxw_chart_assemble_xml_file(chart); err = _add_to_zip(self, chart->file, &buffer, &buffer_size, sheetname); fclose(chart->file); free(buffer); RETURN_ON_ERROR(err); } return LXW_NO_ERROR; } /* * Count the chart files. */ uint32_t _get_chart_count(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_chart *chart; uint32_t chart_count = 0; STAILQ_FOREACH(chart, workbook->ordered_charts, ordered_list_pointers) { chart_count++; } return chart_count; } /* * Write the drawing files. */ STATIC lxw_error _write_drawing_files(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; lxw_drawing *drawing; char filename[LXW_FILENAME_LENGTH] = { 0 }; char *buffer = NULL; size_t buffer_size = 0; uint32_t index = 1; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) worksheet = sheet->u.chartsheet->worksheet; else worksheet = sheet->u.worksheet; drawing = worksheet->drawing; if (drawing) { lxw_snprintf(filename, LXW_FILENAME_LENGTH, "xl/drawings/drawing%d.xml", index++); drawing->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!drawing->file) return LXW_ERROR_CREATING_TMPFILE; lxw_drawing_assemble_xml_file(drawing); err = _add_to_zip(self, drawing->file, &buffer, &buffer_size, filename); fclose(drawing->file); free(buffer); RETURN_ON_ERROR(err); } } return LXW_NO_ERROR; } /* * Count the drawing files. */ uint32_t _get_drawing_count(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; lxw_drawing *drawing; uint32_t drawing_count = 0; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) worksheet = sheet->u.chartsheet->worksheet; else worksheet = sheet->u.worksheet; drawing = worksheet->drawing; if (drawing) drawing_count++; } return drawing_count; } /* * Write the worksheet table files. */ STATIC lxw_error _write_table_files(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; lxw_table *table; lxw_table_obj *table_obj; lxw_error err; char filename[LXW_FILENAME_LENGTH] = { 0 }; char *buffer = NULL; size_t buffer_size = 0; uint32_t index = 1; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) continue; else worksheet = sheet->u.worksheet; if (STAILQ_EMPTY(worksheet->table_objs)) continue; STAILQ_FOREACH(table_obj, worksheet->table_objs, list_pointers) { lxw_snprintf(filename, LXW_FILENAME_LENGTH, "xl/tables/table%d.xml", index++); table = lxw_table_new(); if (!table) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; RETURN_ON_ERROR(err); } table->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!table->file) { lxw_table_free(table); return LXW_ERROR_CREATING_TMPFILE; } table->table_obj = table_obj; lxw_table_assemble_xml_file(table); err = _add_to_zip(self, table->file, &buffer, &buffer_size, filename); fclose(table->file); free(buffer); lxw_table_free(table); RETURN_ON_ERROR(err); } } return LXW_NO_ERROR; } /* * Count the table files. */ uint32_t _get_table_count(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; uint32_t table_count = 0; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) worksheet = sheet->u.chartsheet->worksheet; else worksheet = sheet->u.worksheet; table_count += worksheet->table_count; } return table_count; } /* * Write the comment/header VML files. */ STATIC lxw_error _write_vml_files(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; lxw_vml *vml; char filename[LXW_FILENAME_LENGTH] = { 0 }; char *buffer = NULL; size_t buffer_size = 0; uint32_t index = 1; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) continue; else worksheet = sheet->u.worksheet; if (!worksheet->has_vml && !worksheet->has_header_vml) continue; if (worksheet->has_vml) { vml = lxw_vml_new(); if (!vml) return LXW_ERROR_MEMORY_MALLOC_FAILED; lxw_snprintf(filename, LXW_FILENAME_LENGTH, "xl/drawings/vmlDrawing%d.vml", index++); vml->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!vml->file) { lxw_vml_free(vml); return LXW_ERROR_CREATING_TMPFILE; } vml->comment_objs = worksheet->comment_objs; vml->button_objs = worksheet->button_objs; vml->vml_shape_id = worksheet->vml_shape_id; vml->comment_display_default = worksheet->comment_display_default; if (worksheet->vml_data_id_str) { vml->vml_data_id_str = worksheet->vml_data_id_str; } else { fclose(vml->file); free(buffer); lxw_vml_free(vml); return LXW_ERROR_MEMORY_MALLOC_FAILED; } lxw_vml_assemble_xml_file(vml); err = _add_to_zip(self, vml->file, &buffer, &buffer_size, filename); fclose(vml->file); free(buffer); lxw_vml_free(vml); RETURN_ON_ERROR(err); } if (worksheet->has_header_vml) { err = _write_vml_drawing_rels_file(self, worksheet, index); RETURN_ON_ERROR(err); vml = lxw_vml_new(); if (!vml) return LXW_ERROR_MEMORY_MALLOC_FAILED; lxw_snprintf(filename, LXW_FILENAME_LENGTH, "xl/drawings/vmlDrawing%d.vml", index++); vml->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!vml->file) { lxw_vml_free(vml); return LXW_ERROR_CREATING_TMPFILE; } vml->image_objs = worksheet->header_image_objs; vml->vml_shape_id = worksheet->vml_header_id * 1024; if (worksheet->vml_header_id_str) { vml->vml_data_id_str = worksheet->vml_header_id_str; } else { fclose(vml->file); free(buffer); lxw_vml_free(vml); return LXW_ERROR_MEMORY_MALLOC_FAILED; } lxw_vml_assemble_xml_file(vml); err = _add_to_zip(self, vml->file, &buffer, &buffer_size, filename); fclose(vml->file); free(buffer); lxw_vml_free(vml); RETURN_ON_ERROR(err); } } return LXW_NO_ERROR; } /* * Write the comment files. */ STATIC lxw_error _write_comment_files(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; lxw_comment *comment; char filename[LXW_FILENAME_LENGTH] = { 0 }; char *buffer = NULL; size_t buffer_size = 0; uint32_t index = 1; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) continue; else worksheet = sheet->u.worksheet; if (!worksheet->has_comments) continue; comment = lxw_comment_new(); if (!comment) return LXW_ERROR_MEMORY_MALLOC_FAILED; lxw_snprintf(filename, LXW_FILENAME_LENGTH, "xl/comments%d.xml", index++); comment->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!comment->file) { lxw_comment_free(comment); return LXW_ERROR_CREATING_TMPFILE; } comment->comment_objs = worksheet->comment_objs; comment->comment_author = worksheet->comment_author; lxw_comment_assemble_xml_file(comment); err = _add_to_zip(self, comment->file, &buffer, &buffer_size, filename); fclose(comment->file); free(buffer); lxw_comment_free(comment); RETURN_ON_ERROR(err); } 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; char *buffer = NULL; size_t buffer_size = 0; lxw_error err; /* Skip the sharedStrings file if there are no shared strings. */ if (!sst->string_count) return LXW_NO_ERROR; sst->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!sst->file) return LXW_ERROR_CREATING_TMPFILE; lxw_sst_assemble_xml_file(sst); err = _add_to_zip(self, sst->file, &buffer, &buffer_size, "xl/sharedStrings.xml"); fclose(sst->file); free(buffer); RETURN_ON_ERROR(err); return LXW_NO_ERROR; } /* * Write the app.xml file. */ STATIC lxw_error _write_app_file(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; lxw_chartsheet *chartsheet; lxw_defined_name *defined_name; lxw_app *app; char *buffer = NULL; size_t buffer_size = 0; uint32_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_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!app->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } if (self->workbook->num_worksheets) { lxw_snprintf(number, LXW_ATTR_32, "%d", self->workbook->num_worksheets); lxw_app_add_heading_pair(app, "Worksheets", number); } if (self->workbook->num_chartsheets) { lxw_snprintf(number, LXW_ATTR_32, "%d", self->workbook->num_chartsheets); lxw_app_add_heading_pair(app, "Charts", number); } STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (!sheet->is_chartsheet) { worksheet = sheet->u.worksheet; lxw_app_add_part_name(app, worksheet->name); } } STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) { chartsheet = sheet->u.chartsheet; lxw_app_add_part_name(app, chartsheet->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; app->doc_security = workbook->read_only; lxw_app_assemble_xml_file(app); err = _add_to_zip(self, app->file, &buffer, &buffer_size, "docProps/app.xml"); fclose(app->file); free(buffer); 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(); char *buffer = NULL; size_t buffer_size = 0; if (!core) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } core->file = lxw_get_filehandle(&buffer, &buffer_size, 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_to_zip(self, core->file, &buffer, &buffer_size, "docProps/core.xml"); fclose(core->file); free(buffer); mem_error: lxw_core_free(core); return err; } /* * Write the metadata.xml file. */ STATIC lxw_error _write_metadata_file(lxw_packager *self) { lxw_error err = LXW_NO_ERROR; lxw_metadata *metadata; char *buffer = NULL; size_t buffer_size = 0; if (!self->workbook->has_metadata) return LXW_NO_ERROR; metadata = lxw_metadata_new(); if (!metadata) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } metadata->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!metadata->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } lxw_metadata_assemble_xml_file(metadata); err = _add_to_zip(self, metadata->file, &buffer, &buffer_size, "xl/metadata.xml"); fclose(metadata->file); free(buffer); mem_error: lxw_metadata_free(metadata); return err; } /* * Write the custom.xml file. */ STATIC lxw_error _write_custom_file(lxw_packager *self) { lxw_custom *custom; char *buffer = NULL; size_t buffer_size = 0; 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_get_filehandle(&buffer, &buffer_size, 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_to_zip(self, custom->file, &buffer, &buffer_size, "docProps/custom.xml"); fclose(custom->file); free(buffer); 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(); char *buffer = NULL; size_t buffer_size = 0; if (!theme) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } theme->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!theme->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } lxw_theme_assemble_xml_file(theme); err = _add_to_zip(self, theme->file, &buffer, &buffer_size, "xl/theme/theme1.xml"); fclose(theme->file); free(buffer); 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(); char *buffer = NULL; size_t buffer_size = 0; 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); } /* Copy the unique and in-use dxf formats from the workbook to the styles * dxf_format list. */ LXW_FOREACH_ORDERED(hash_element, self->workbook->used_dxf_formats) { lxw_format *workbook_format = (lxw_format *) hash_element->value; lxw_format *style_format = lxw_format_new(); if (!style_format) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } memcpy(style_format, workbook_format, sizeof(lxw_format)); STAILQ_INSERT_TAIL(styles->dxf_formats, style_format, list_pointers); } 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->dxf_count = self->workbook->used_dxf_formats->unique_count; styles->has_comments = self->workbook->has_comments; styles->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!styles->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } lxw_styles_assemble_xml_file(styles); err = _add_to_zip(self, styles->file, &buffer, &buffer_size, "xl/styles.xml"); fclose(styles->file); free(buffer); 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(); char *buffer = NULL; size_t buffer_size = 0; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; char filename[LXW_MAX_ATTRIBUTE_LENGTH] = { 0 }; uint32_t index = 1; uint32_t worksheet_index = 1; uint32_t chartsheet_index = 1; uint32_t drawing_count = _get_drawing_count(self); uint32_t chart_count = _get_chart_count(self); uint32_t table_count = _get_table_count(self); lxw_error err = LXW_NO_ERROR; if (!content_types) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } content_types->file = lxw_get_filehandle(&buffer, &buffer_size, 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"); if (workbook->has_gif) lxw_ct_add_default(content_types, "gif", "image/gif"); if (workbook->vba_project) lxw_ct_add_default(content_types, "bin", "application/vnd.ms-office.vbaProject"); if (workbook->vba_project) lxw_ct_add_override(content_types, "/xl/workbook.xml", LXW_APP_MSEXCEL "sheet.macroEnabled.main+xml"); else lxw_ct_add_override(content_types, "/xl/workbook.xml", LXW_APP_DOCUMENT "spreadsheetml.sheet.main+xml"); if (workbook->vba_project_signature) lxw_ct_add_override(content_types, "/xl/vbaProjectSignature.bin", "application/vnd.ms-office.vbaProjectSignature"); STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) { lxw_snprintf(filename, LXW_FILENAME_LENGTH, "/xl/chartsheets/sheet%d.xml", chartsheet_index++); lxw_ct_add_chartsheet_name(content_types, filename); } else { lxw_snprintf(filename, LXW_FILENAME_LENGTH, "/xl/worksheets/sheet%d.xml", worksheet_index++); lxw_ct_add_worksheet_name(content_types, filename); } } for (index = 1; index <= 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 <= drawing_count; index++) { lxw_snprintf(filename, LXW_FILENAME_LENGTH, "/xl/drawings/drawing%d.xml", index); lxw_ct_add_drawing_name(content_types, filename); } for (index = 1; index <= table_count; index++) { lxw_snprintf(filename, LXW_FILENAME_LENGTH, "/xl/tables/table%d.xml", index); lxw_ct_add_table_name(content_types, filename); } if (workbook->has_vml) lxw_ct_add_vml_name(content_types); for (index = 1; index <= workbook->comment_count; index++) { lxw_snprintf(filename, LXW_FILENAME_LENGTH, "/xl/comments%d.xml", index); lxw_ct_add_comment_name(content_types, filename); } 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); if (workbook->has_metadata) lxw_ct_add_metadata(content_types); lxw_content_types_assemble_xml_file(content_types); err = _add_to_zip(self, content_types->file, &buffer, &buffer_size, "[Content_Types].xml"); fclose(content_types->file); free(buffer); 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(); char *buffer = NULL; size_t buffer_size = 0; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; uint32_t worksheet_index = 1; uint32_t chartsheet_index = 1; lxw_error err = LXW_NO_ERROR; if (!rels) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) { lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "chartsheets/sheet%d.xml", chartsheet_index++); lxw_add_document_relationship(rels, "/chartsheet", sheetname); } else { lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "worksheets/sheet%d.xml", worksheet_index++); lxw_add_document_relationship(rels, "/worksheet", sheetname); } } 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"); if (workbook->vba_project) lxw_add_ms_package_relationship(rels, "/vbaProject", "vbaProject.bin"); if (workbook->has_metadata) lxw_add_document_relationship(rels, "/sheetMetadata", "metadata.xml"); lxw_relationships_assemble_xml_file(rels); err = _add_to_zip(self, rels->file, &buffer, &buffer_size, "xl/_rels/workbook.xml.rels"); fclose(rels->file); free(buffer); 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; char *buffer = NULL; size_t buffer_size = 0; lxw_rel_tuple *rel; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; uint32_t index = 0; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) continue; else worksheet = sheet->u.worksheet; index++; if (STAILQ_EMPTY(worksheet->external_hyperlinks) && STAILQ_EMPTY(worksheet->external_drawing_links) && STAILQ_EMPTY(worksheet->external_table_links) && !worksheet->external_vml_header_link && !worksheet->external_vml_comment_link && !worksheet->external_background_link && !worksheet->external_comment_link) continue; rels = lxw_relationships_new(); rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { lxw_free_relationships(rels); return LXW_ERROR_CREATING_TMPFILE; } STAILQ_FOREACH(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); } rel = worksheet->external_vml_comment_link; if (rel) lxw_add_worksheet_relationship(rels, rel->type, rel->target, rel->target_mode); rel = worksheet->external_vml_header_link; if (rel) lxw_add_worksheet_relationship(rels, rel->type, rel->target, rel->target_mode); rel = worksheet->external_background_link; if (rel) lxw_add_worksheet_relationship(rels, rel->type, rel->target, rel->target_mode); STAILQ_FOREACH(rel, worksheet->external_table_links, list_pointers) { lxw_add_worksheet_relationship(rels, rel->type, rel->target, rel->target_mode); } rel = worksheet->external_comment_link; if (rel) 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_to_zip(self, rels->file, &buffer, &buffer_size, sheetname); fclose(rels->file); free(buffer); lxw_free_relationships(rels); RETURN_ON_ERROR(err); } return LXW_NO_ERROR; } /* * Write the chartsheet .rels files for chartsheets that contain links to * external data such as drawings. */ STATIC lxw_error _write_chartsheet_rels_file(lxw_packager *self) { lxw_relationships *rels; char *buffer = NULL; size_t buffer_size = 0; lxw_rel_tuple *rel; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; uint32_t index = 0; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) worksheet = sheet->u.chartsheet->worksheet; else continue; index++; if (STAILQ_EMPTY(worksheet->external_drawing_links)) continue; rels = lxw_relationships_new(); rels->file = lxw_get_filehandle(&buffer, &buffer_size, 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/chartsheets/_rels/sheet%d.xml.rels", index); lxw_relationships_assemble_xml_file(rels); err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname); fclose(rels->file); free(buffer); lxw_free_relationships(rels); RETURN_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; char *buffer = NULL; size_t buffer_size = 0; lxw_rel_tuple *rel; lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; uint32_t index = 1; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) worksheet = sheet->u.chartsheet->worksheet; else worksheet = sheet->u.worksheet; if (STAILQ_EMPTY(worksheet->drawing_links)) continue; rels = lxw_relationships_new(); rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { lxw_free_relationships(rels); return LXW_ERROR_CREATING_TMPFILE; } STAILQ_FOREACH(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_to_zip(self, rels->file, &buffer, &buffer_size, sheetname); fclose(rels->file); free(buffer); lxw_free_relationships(rels); RETURN_ON_ERROR(err); } return LXW_NO_ERROR; } /* * Write the vmlDrawing .rels files for worksheets that contain images in * headers or footers. */ STATIC lxw_error _write_vml_drawing_rels_file(lxw_packager *self, lxw_worksheet *worksheet, uint32_t index) { lxw_relationships *rels; char *buffer = NULL; size_t buffer_size = 0; lxw_rel_tuple *rel; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; lxw_error err = LXW_NO_ERROR; rels = lxw_relationships_new(); rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { lxw_free_relationships(rels); return LXW_ERROR_CREATING_TMPFILE; } STAILQ_FOREACH(rel, worksheet->vml_drawing_links, list_pointers) { lxw_add_worksheet_relationship(rels, rel->type, rel->target, rel->target_mode); } lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "xl/drawings/_rels/vmlDrawing%d.vml.rels", index); lxw_relationships_assemble_xml_file(rels); err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname); fclose(rels->file); free(buffer); lxw_free_relationships(rels); return err; } /* * Write the vbaProject .rels xml file. */ STATIC lxw_error _write_vba_project_rels_file(lxw_packager *self) { lxw_relationships *rels; lxw_workbook *workbook = self->workbook; lxw_error err = LXW_NO_ERROR; char *buffer = NULL; size_t buffer_size = 0; if (!workbook->vba_project_signature) return LXW_NO_ERROR; rels = lxw_relationships_new(); if (!rels) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } lxw_add_ms_package_relationship(rels, "/vbaProjectSignature", "vbaProjectSignature.bin"); lxw_relationships_assemble_xml_file(rels); err = _add_to_zip(self, rels->file, &buffer, &buffer_size, "xl/_rels/vbaProject.bin.rels"); fclose(rels->file); free(buffer); mem_error: lxw_free_relationships(rels); return err; } /* * Write the _rels/.rels xml file. */ STATIC lxw_error _write_root_rels_file(lxw_packager *self) { lxw_relationships *rels = lxw_relationships_new(); char *buffer = NULL; size_t buffer_size = 0; lxw_error err = LXW_NO_ERROR; if (!rels) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rels->file) { err = LXW_ERROR_CREATING_TMPFILE; goto mem_error; } lxw_add_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_to_zip(self, rels->file, &buffer, &buffer_size, "_rels/.rels"); fclose(rels->file); free(buffer); 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, self->use_zip64); 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((void *) self->buffer, 1, self->buffer_size, file); while (size_read) { if (size_read < self->buffer_size) { if (ferror(file)) { 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((void *) (void *) self->buffer, 1, self->buffer_size, file); } 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; } STATIC lxw_error _add_buffer_to_zip(lxw_packager *self, const char *buffer, size_t buffer_size, const char *filename) { int16_t error = ZIP_OK; error = zipOpenNewFileInZip4_64(self->zipfile, filename, &self->zipfile_info, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, 0, 0, self->use_zip64); if (error != ZIP_OK) { LXW_ERROR("Error adding member to zipfile"); RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD); } error = zipWriteInFileInZip(self->zipfile, buffer, (unsigned int) buffer_size); if (error < 0) { LXW_ERROR("Error in writing member in the zipfile"); RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD); } 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; } STATIC lxw_error _add_to_zip(lxw_packager *self, FILE * file, char **buffer, size_t *buffer_size, const char *filename) { /* Flush to ensure buffer is updated when using a memory-backed file. */ fflush(file); return *buffer ? _add_buffer_to_zip(self, *buffer, *buffer_size, filename) : _add_file_to_zip(self, file, filename); } /* * Write the xml files that make up the XLSX OPC package. */ lxw_error lxw_create_package(lxw_packager *self) { lxw_error error; int8_t zip_error; error = _write_content_types_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_root_rels_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_workbook_rels_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_worksheet_files(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_chartsheet_files(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_workbook_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_chart_files(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_drawing_files(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_vml_files(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_comment_files(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_table_files(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_shared_strings_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_custom_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_theme_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_styles_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_worksheet_rels_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_chartsheet_rels_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_drawing_rels_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_image_files(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _add_vba_project(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _add_vba_project_signature(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_vba_project_rels_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_core_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_metadata_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); error = _write_app_file(self); RETURN_AND_ZIPCLOSE_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; } writexl/src/libxlsxwriter/app.c0000644000176200001440000002410214747162622016417 0ustar liggesusers/***************************************************************************** * app - A library for creating Excel XLSX app files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, 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(void) { 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) { if (self->doc_security == 2) lxw_xml_data_element(self->file, "DocSecurity", "2", NULL); else 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); } } writexl/src/libxlsxwriter/styles.c0000644000176200001440000010765614747162622017202 0ustar liggesusers/***************************************************************************** * styles - A library for creating Excel XLSX styles files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ #include "xlsxwriter/xmlwriter.h" #include "xlsxwriter/styles.h" #include "xlsxwriter/utility.h" /* * Forward declarations. */ STATIC void _write_font(lxw_styles *self, lxw_format *format, uint8_t is_dxf, uint8_t is_rich_string); /***************************************************************************** * * Private functions. * ****************************************************************************/ /* * Create a new styles object. */ lxw_styles * lxw_styles_new(void) { 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); styles->dxf_formats = calloc(1, sizeof(struct lxw_formats)); GOTO_LABEL_ON_MEM_ERROR(styles->dxf_formats, mem_error); STAILQ_INIT(styles->dxf_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 xf 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 the dxf formats in the styles. */ if (styles->dxf_formats) { while (!STAILQ_EMPTY(styles->dxf_formats)) { format = STAILQ_FIRST(styles->dxf_formats); STAILQ_REMOVE_HEAD(styles->dxf_formats, list_pointers); free(format); } free(styles->dxf_formats); } free(styles); } /* * Write the element for rich strings. */ void lxw_styles_write_string_fragment(lxw_styles *self, const 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(); } void lxw_styles_write_rich_font(lxw_styles *self, lxw_format *format) { _write_font(self, format, LXW_FALSE, LXW_TRUE); } /***************************************************************************** * * 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; char *format_codes[] = { "General", "0", "0.00", "#,##0", "#,##0.00", "($#,##0_);($#,##0)", "($#,##0_);[Red]($#,##0)", "($#,##0.00_);($#,##0.00)", "($#,##0.00_);[Red]($#,##0.00)", "0%", "0.00%", "0.00E+00", "# ?/?", "# ?" "?/?" "?", /* Split string to avoid unintentional trigraph. */ "m/d/yy", "d-mmm-yy", "d-mmm", "mmm-yy", "h:mm AM/PM", "h:mm:ss AM/PM", "h:mm", "h:mm:ss", "m/d/yy h:mm", "General", "General", "General", "General", "General", "General", "General", "General", "General", "General", "General", "General", "General", "General", "(#,##0_);(#,##0)", "(#,##0_);[Red](#,##0)", "(#,##0.00_);(#,##0.00)", "(#,##0.00_);[Red](#,##0.00)", "_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)", "_($* #,##0_);_($* (#,##0);_($* \"-\"_);_(@_)", "_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)", "_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(@_)", "mm:ss", "[h]:mm:ss", "mm:ss.0", "##0.0E+0", "@" }; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("numFmtId", num_fmt_id); if (num_fmt_id < 50) LXW_PUSH_ATTRIBUTES_STR("formatCode", format_codes[num_fmt_id]); else if (num_fmt_id < 164) LXW_PUSH_ATTRIBUTES_STR("formatCode", "General"); else 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; uint16_t last_format_index = 0; 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; /* Ignore duplicates which have an already used index. */ if (format->num_format_index <= last_format_index) continue; _write_num_fmt(self, format->num_format_index, format->num_format); last_format_index = format->num_format_index; } 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 for indexed colors. */ STATIC void _write_font_color_indexed(lxw_styles *self, uint8_t index) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("indexed", index); 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, uint8_t is_rich_string) { 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); if (is_rich_string) lxw_xml_empty_tag(self->file, "rFont", &attributes); else 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 element. */ STATIC void _write_font_condense(lxw_styles *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("val", "0"); lxw_xml_empty_tag(self->file, "condense", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the font element. */ STATIC void _write_font_extend(lxw_styles *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("val", "0"); lxw_xml_empty_tag(self->file, "extend", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the font sub-element. */ STATIC void _write_font_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, uint8_t is_dxf, uint8_t is_rich_string) { if (is_rich_string) lxw_xml_start_tag(self->file, "rPr", NULL); else lxw_xml_start_tag(self->file, "font", NULL); if (format->font_condense) _write_font_condense(self); if (format->font_extend) _write_font_extend(self); 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_font_vert_align(self, "superscript"); if (format->font_script == LXW_FONT_SUBSCRIPT) _write_font_vert_align(self, "subscript"); if (!is_dxf && 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->color_indexed) _write_font_color_indexed(self, format->color_indexed); else if (format->font_color != LXW_COLOR_UNSET) _write_font_color_rgb(self, format->font_color); else if (!is_dxf) _write_font_color_theme(self, LXW_DEFAULT_FONT_THEME); if (!is_dxf) { _write_font_name(self, format->font_name, is_rich_string); _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); } } if (format->hyperlink) { self->has_hyperlink = LXW_TRUE; if (self->hyperlink_font_id == 0) self->hyperlink_font_id = format->font_index; } if (is_rich_string) lxw_xml_end_tag(self->file, "rPr"); else lxw_xml_end_tag(self->file, "font"); } /* * Write the element for comments. */ STATIC void _write_comment_font(lxw_styles *self) { lxw_xml_start_tag(self->file, "font", NULL); _write_font_size(self, 8); _write_font_color_indexed(self, 81); _write_font_name(self, "Tahoma", LXW_FALSE); _write_font_family(self, 2); 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; uint32_t count; LXW_INIT_ATTRIBUTES(); count = self->font_count; if (self->has_comments) count++; LXW_PUSH_ATTRIBUTES_INT("count", 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_FALSE, LXW_FALSE); } if (self->has_comments) _write_comment_font(self); 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, uint8_t pattern) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char rgb_str[LXW_ATTR_32]; LXW_INIT_ATTRIBUTES(); if (color == LXW_COLOR_UNSET) { if (pattern <= LXW_PATTERN_SOLID) { LXW_PUSH_ATTRIBUTES_STR("indexed", "64"); lxw_xml_empty_tag(self->file, "bgColor", &attributes); } } 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, uint8_t is_dxf) { 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", }; if (is_dxf) { bg_color = format->dxf_bg_color; fg_color = format->dxf_fg_color; } LXW_INIT_ATTRIBUTES(); /* Special handling for pattern only case. */ if (!bg_color && !fg_color && pattern) { _write_default_fill(self, patterns[pattern]); LXW_FREE_ATTRIBUTES(); return; } lxw_xml_start_tag(self->file, "fill", NULL); /* None/Solid patterns are handled differently for dxf formats. */ if (pattern && !(is_dxf && pattern <= LXW_PATTERN_SOLID)) 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, pattern); 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_FALSE); } 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, uint8_t is_dxf) { 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 = LXW_BORDER_THIN; } /* 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); if (is_dxf) { _write_sub_border(self, "vertical", 0, LXW_COLOR_UNSET); _write_sub_border(self, "horizontal", 0, LXW_COLOR_UNSET); } /* Conditional DXF formats don't allow diagonal borders. */ if (!is_dxf) _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_FALSE); } lxw_xml_end_tag(self->file, "borders"); LXW_FREE_ATTRIBUTES(); } /* * Write the element for hyperlinks. */ STATIC void _write_hyperlink_alignment(lxw_styles *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("vertical", "top"); lxw_xml_empty_tag(self->file, "alignment", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element for hyperlinks. */ STATIC void _write_hyperlink_protection(lxw_styles *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("locked", "0"); lxw_xml_empty_tag(self->file, "protection", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element for styles. */ STATIC void _write_style_xf(lxw_styles *self, uint8_t has_hyperlink, uint16_t font_id) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("numFmtId", "0"); LXW_PUSH_ATTRIBUTES_INT("fontId", font_id); LXW_PUSH_ATTRIBUTES_STR("fillId", "0"); LXW_PUSH_ATTRIBUTES_STR("borderId", "0"); if (has_hyperlink) { LXW_PUSH_ATTRIBUTES_STR("applyNumberFormat", "0"); LXW_PUSH_ATTRIBUTES_STR("applyFill", "0"); LXW_PUSH_ATTRIBUTES_STR("applyBorder", "0"); LXW_PUSH_ATTRIBUTES_STR("applyAlignment", "0"); LXW_PUSH_ATTRIBUTES_STR("applyProtection", "0"); lxw_xml_start_tag(self->file, "xf", &attributes); _write_hyperlink_alignment(self); _write_hyperlink_protection(self); 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_style_xfs(lxw_styles *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); if (self->has_hyperlink) LXW_PUSH_ATTRIBUTES_STR("count", "2"); else LXW_PUSH_ATTRIBUTES_STR("count", "1"); lxw_xml_start_tag(self->file, "cellStyleXfs", &attributes); _write_style_xf(self, LXW_FALSE, 0); if (self->has_hyperlink) _write_style_xf(self, self->has_hyperlink, self->hyperlink_font_id); 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 some alignment properties. */ /* 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_v_align != LXW_ALIGN_VERTICAL_TOP && format->text_v_align != LXW_ALIGN_VERTICAL_BOTTOM && format->text_v_align != LXW_ALIGN_VERTICAL_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"); /* 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->indent) LXW_PUSH_ATTRIBUTES_INT("indent", format->indent); 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_INT("xfId", format->xf_id); if (format->quote_prefix) LXW_PUSH_ATTRIBUTES_STR("quotePrefix", "1"); 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 && !format->hyperlink) 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 || format->hyperlink) LXW_PUSH_ATTRIBUTES_STR("applyAlignment", "1"); if (has_protection || format->hyperlink) 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; uint32_t count = self->xf_count; uint32_t i = 0; /* If the last format is "font_only" it is for the comment font and * shouldn't be counted. This is a workaround to get the last object * in the list since STAILQ_LAST() requires __containerof and isn't * ANSI compatible. */ STAILQ_FOREACH(format, self->xf_formats, list_pointers) { i++; if (i == self->xf_count && format->font_only) count--; } LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("count", count); lxw_xml_start_tag(self->file, "cellXfs", &attributes); STAILQ_FOREACH(format, self->xf_formats, list_pointers) { if (!format->font_only) _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, char *name, uint8_t xf_id, uint8_t builtin_id) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("name", name); LXW_PUSH_ATTRIBUTES_INT("xfId", xf_id); LXW_PUSH_ATTRIBUTES_INT("builtinId", builtin_id); 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(); if (self->has_hyperlink) LXW_PUSH_ATTRIBUTES_STR("count", "2"); else LXW_PUSH_ATTRIBUTES_STR("count", "1"); lxw_xml_start_tag(self->file, "cellStyles", &attributes); if (self->has_hyperlink) _write_cell_style(self, "Hyperlink", 1, 8); _write_cell_style(self, "Normal", 0, 0); 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_format *format; uint32_t count = self->dxf_count; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("count", count); if (count) { lxw_xml_start_tag(self->file, "dxfs", &attributes); STAILQ_FOREACH(format, self->dxf_formats, list_pointers) { lxw_xml_start_tag(self->file, "dxf", NULL); if (format->has_dxf_font) _write_font(self, format, LXW_TRUE, LXW_FALSE); if (format->num_format_index) _write_num_fmt(self, format->num_format_index, format->num_format); if (format->has_dxf_fill) _write_fill(self, format, LXW_TRUE); if (format->has_dxf_border) _write_border(self, format, LXW_TRUE); lxw_xml_end_tag(self->file, "dxf"); } lxw_xml_end_tag(self->file, "dxfs"); } else { 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); /* Close the style sheet tag. */ lxw_xml_end_tag(self->file, "styleSheet"); } /***************************************************************************** * * Public functions. * ****************************************************************************/ writexl/src/libxlsxwriter/worksheet.c0000644000176200001440000123326014747162622017662 0ustar liggesusers/***************************************************************************** * worksheet - A library for creating Excel XLSX worksheet files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ #ifdef USE_FMEMOPEN #define _POSIX_C_SOURCE 200809L #endif #include "xlsxwriter/xmlwriter.h" #include "xlsxwriter/worksheet.h" #include "xlsxwriter/format.h" #include "xlsxwriter/utility.h" #ifdef USE_OPENSSL_MD5 #include #else #ifndef USE_NO_MD5 #include "xlsxwriter/third_party/md5.h" #endif #endif #define LXW_STR_MAX 32767 #define LXW_BUFFER_SIZE 4096 #define LXW_PRINT_ACROSS 1 #define LXW_VALIDATION_MAX_TITLE_LENGTH 32 #define LXW_VALIDATION_MAX_STRING_LENGTH 255 #define LXW_THIS_ROW "[#This Row]," /* * 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); STATIC int _drawing_rel_id_cmp(lxw_drawing_rel_id *tuple1, lxw_drawing_rel_id *tuple2); STATIC int _cond_format_hash_cmp(lxw_cond_format_hash_element *elem_1, lxw_cond_format_hash_element *elem_2); #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); LXW_RB_GENERATE_DRAWING_REL_IDS(lxw_drawing_rel_ids, lxw_drawing_rel_id, tree_pointers, _drawing_rel_id_cmp); LXW_RB_GENERATE_VML_DRAWING_REL_IDS(lxw_vml_drawing_rel_ids, lxw_drawing_rel_id, tree_pointers, _drawing_rel_id_cmp); LXW_RB_GENERATE_COND_FORMAT_HASH(lxw_cond_format_hash, lxw_cond_format_hash_element, tree_pointers, _cond_format_hash_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 tmp_row; tmp_row.row_num = row_num; return RB_FIND(lxw_table_rows, self->table, &tmp_row); } /* * Find but don't create a cell object for a given row object and col number. */ lxw_cell * lxw_worksheet_find_cell_in_row(lxw_row *row, lxw_col_t col_num) { lxw_cell tmp_cell; if (!row) return NULL; tmp_cell.col_num = col_num; return RB_FIND(lxw_table_cells, row->cells, &tmp_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); worksheet->comments = calloc(1, sizeof(struct lxw_table_rows)); GOTO_LABEL_ON_MEM_ERROR(worksheet->comments, mem_error); RB_INIT(worksheet->comments); /* Initialize the cached rows. */ worksheet->table->cached_row_num = LXW_ROW_MAX + 1; worksheet->hyperlinks->cached_row_num = LXW_ROW_MAX + 1; worksheet->comments->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_props = calloc(1, sizeof(struct lxw_image_props)); GOTO_LABEL_ON_MEM_ERROR(worksheet->image_props, mem_error); STAILQ_INIT(worksheet->image_props); worksheet->chart_data = calloc(1, sizeof(struct lxw_chart_props)); GOTO_LABEL_ON_MEM_ERROR(worksheet->chart_data, mem_error); STAILQ_INIT(worksheet->chart_data); worksheet->comment_objs = calloc(1, sizeof(struct lxw_comment_objs)); GOTO_LABEL_ON_MEM_ERROR(worksheet->comment_objs, mem_error); STAILQ_INIT(worksheet->comment_objs); worksheet->header_image_objs = calloc(1, sizeof(struct lxw_comment_objs)); GOTO_LABEL_ON_MEM_ERROR(worksheet->header_image_objs, mem_error); STAILQ_INIT(worksheet->header_image_objs); worksheet->button_objs = calloc(1, sizeof(struct lxw_comment_objs)); GOTO_LABEL_ON_MEM_ERROR(worksheet->button_objs, mem_error); STAILQ_INIT(worksheet->button_objs); worksheet->selections = calloc(1, sizeof(struct lxw_selections)); GOTO_LABEL_ON_MEM_ERROR(worksheet->selections, mem_error); STAILQ_INIT(worksheet->selections); worksheet->data_validations = calloc(1, sizeof(struct lxw_data_validations)); GOTO_LABEL_ON_MEM_ERROR(worksheet->data_validations, mem_error); STAILQ_INIT(worksheet->data_validations); worksheet->table_objs = calloc(1, sizeof(struct lxw_table_objs)); GOTO_LABEL_ON_MEM_ERROR(worksheet->table_objs, mem_error); STAILQ_INIT(worksheet->table_objs); 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); worksheet->vml_drawing_links = calloc(1, sizeof(struct lxw_rel_tuples)); GOTO_LABEL_ON_MEM_ERROR(worksheet->vml_drawing_links, mem_error); STAILQ_INIT(worksheet->vml_drawing_links); worksheet->external_table_links = calloc(1, sizeof(struct lxw_rel_tuples)); GOTO_LABEL_ON_MEM_ERROR(worksheet->external_table_links, mem_error); STAILQ_INIT(worksheet->external_table_links); if (init_data && init_data->optimize) { FILE *tmpfile; worksheet->optimize_buffer = NULL; worksheet->optimize_buffer_size = 0; tmpfile = lxw_get_filehandle(&worksheet->optimize_buffer, &worksheet->optimize_buffer_size, 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; } worksheet->drawing_rel_ids = calloc(1, sizeof(struct lxw_drawing_rel_ids)); GOTO_LABEL_ON_MEM_ERROR(worksheet->drawing_rel_ids, mem_error); RB_INIT(worksheet->drawing_rel_ids); worksheet->vml_drawing_rel_ids = calloc(1, sizeof(struct lxw_vml_drawing_rel_ids)); GOTO_LABEL_ON_MEM_ERROR(worksheet->vml_drawing_rel_ids, mem_error); RB_INIT(worksheet->vml_drawing_rel_ids); worksheet->conditional_formats = calloc(1, sizeof(struct lxw_cond_format_hash)); GOTO_LABEL_ON_MEM_ERROR(worksheet->conditional_formats, mem_error); RB_INIT(worksheet->conditional_formats); /* 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->outline_style = LXW_TRUE; worksheet->outline_below = LXW_TRUE; worksheet->outline_right = LXW_FALSE; worksheet->tab_color = LXW_COLOR_UNSET; worksheet->max_url_length = 2079; worksheet->comment_display_default = LXW_COMMENT_DISPLAY_HIDDEN; worksheet->header_footer_objs[0] = &worksheet->header_left_object_props; worksheet->header_footer_objs[1] = &worksheet->header_center_object_props; worksheet->header_footer_objs[2] = &worksheet->header_right_object_props; worksheet->header_footer_objs[3] = &worksheet->footer_left_object_props; worksheet->header_footer_objs[4] = &worksheet->footer_center_object_props; worksheet->header_footer_objs[5] = &worksheet->footer_right_object_props; 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; worksheet->default_url_format = init_data->default_url_format; worksheet->max_url_length = init_data->max_url_length; } return worksheet; mem_error: lxw_worksheet_free(worksheet); return NULL; } /* * Free vml object. */ STATIC void _free_vml_object(lxw_vml_obj *vml_obj) { if (!vml_obj) return; free(vml_obj->author); free(vml_obj->font_name); free(vml_obj->text); free(vml_obj->image_position); free(vml_obj->name); free(vml_obj->macro); free(vml_obj); } /* * Free autofilter rule object. */ STATIC void _free_filter_rule(lxw_filter_rule_obj *rule_obj) { uint16_t i; if (!rule_obj) return; free(rule_obj->value1_string); free(rule_obj->value2_string); if (rule_obj->list) { for (i = 0; i < rule_obj->num_list_filters; i++) free(rule_obj->list[i]); free(rule_obj->list); } free(rule_obj); } /* * Free autofilter rules. */ STATIC void _free_filter_rules(lxw_worksheet *worksheet) { uint16_t i; if (!worksheet->filter_rules) return; for (i = 0; i < worksheet->num_filter_rules; i++) _free_filter_rule(worksheet->filter_rules[i]); free(worksheet->filter_rules); } /* * 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((void *) cell->u.string); } free(cell->user_data1); free(cell->user_data2); _free_vml_object(cell->comment); 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_object_properties(lxw_object_properties *object_property) { if (!object_property) return; free(object_property->filename); free(object_property->description); free(object_property->extension); free(object_property->url); free(object_property->tip); free(object_property->image_buffer); free(object_property->md5); free(object_property->image_position); free(object_property); object_property = NULL; } /* * Free a worksheet data_validation. */ STATIC void _free_data_validation(lxw_data_val_obj *data_validation) { if (!data_validation) return; free(data_validation->value_formula); free(data_validation->maximum_formula); free(data_validation->input_title); free(data_validation->input_message); free(data_validation->error_title); free(data_validation->error_message); free(data_validation->minimum_formula); free(data_validation); } /* * Free a worksheet conditional format obj. */ STATIC void _free_cond_format(lxw_cond_format_obj *cond_format) { if (!cond_format) return; free(cond_format->min_value_string); free(cond_format->mid_value_string); free(cond_format->max_value_string); free(cond_format->type_string); free(cond_format->guid); free(cond_format); } /* * Free a relationship structure. */ STATIC void _free_relationship(lxw_rel_tuple *relationship) { if (!relationship) return; free(relationship->type); free(relationship->target); free(relationship->target_mode); free(relationship); } /* * Free a worksheet table column object. */ STATIC void _free_worksheet_table_column(lxw_table_column *column) { if (!column) return; free((void *) column->header); free((void *) column->formula); free((void *) column->total_string); free(column); } /* * Free a worksheet table object. */ STATIC void _free_worksheet_table(lxw_table_obj *table) { uint16_t i; if (!table) return; for (i = 0; i < table->num_cols; i++) _free_worksheet_table_column(table->columns[i]); free(table->name); free(table->total_string); free(table->columns); free(table); } /* * 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_object_properties *object_props; lxw_vml_obj *vml_obj; lxw_selection *selection; lxw_data_val_obj *data_validation; lxw_rel_tuple *relationship; lxw_cond_format_obj *cond_format; lxw_table_obj *table_obj; struct lxw_drawing_rel_id *drawing_rel_id; struct lxw_drawing_rel_id *next_drawing_rel_id; struct lxw_cond_format_hash_element *cond_format_elem; struct lxw_cond_format_hash_element *next_cond_format_elem; 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->comments) { for (row = RB_MIN(lxw_table_rows, worksheet->comments); row; row = next_row) { next_row = RB_NEXT(lxw_table_rows, worksheet->comments, row); RB_REMOVE(lxw_table_rows, worksheet->comments, row); _free_row(row); } free(worksheet->comments); } 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_props) { while (!STAILQ_EMPTY(worksheet->image_props)) { object_props = STAILQ_FIRST(worksheet->image_props); STAILQ_REMOVE_HEAD(worksheet->image_props, list_pointers); _free_object_properties(object_props); } free(worksheet->image_props); } if (worksheet->chart_data) { while (!STAILQ_EMPTY(worksheet->chart_data)) { object_props = STAILQ_FIRST(worksheet->chart_data); STAILQ_REMOVE_HEAD(worksheet->chart_data, list_pointers); _free_object_properties(object_props); } free(worksheet->chart_data); } /* Just free the list. The list objects are freed from the RB tree. */ free(worksheet->comment_objs); if (worksheet->header_image_objs) { while (!STAILQ_EMPTY(worksheet->header_image_objs)) { vml_obj = STAILQ_FIRST(worksheet->header_image_objs); STAILQ_REMOVE_HEAD(worksheet->header_image_objs, list_pointers); _free_vml_object(vml_obj); } free(worksheet->header_image_objs); } if (worksheet->button_objs) { while (!STAILQ_EMPTY(worksheet->button_objs)) { vml_obj = STAILQ_FIRST(worksheet->button_objs); STAILQ_REMOVE_HEAD(worksheet->button_objs, list_pointers); _free_vml_object(vml_obj); } free(worksheet->button_objs); } 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); } if (worksheet->table_objs) { while (!STAILQ_EMPTY(worksheet->table_objs)) { table_obj = STAILQ_FIRST(worksheet->table_objs); STAILQ_REMOVE_HEAD(worksheet->table_objs, list_pointers); _free_worksheet_table(table_obj); } free(worksheet->table_objs); } if (worksheet->data_validations) { while (!STAILQ_EMPTY(worksheet->data_validations)) { data_validation = STAILQ_FIRST(worksheet->data_validations); STAILQ_REMOVE_HEAD(worksheet->data_validations, list_pointers); _free_data_validation(data_validation); } free(worksheet->data_validations); } while (!STAILQ_EMPTY(worksheet->external_hyperlinks)) { relationship = STAILQ_FIRST(worksheet->external_hyperlinks); STAILQ_REMOVE_HEAD(worksheet->external_hyperlinks, list_pointers); _free_relationship(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(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(relationship); } free(worksheet->drawing_links); while (!STAILQ_EMPTY(worksheet->vml_drawing_links)) { relationship = STAILQ_FIRST(worksheet->vml_drawing_links); STAILQ_REMOVE_HEAD(worksheet->vml_drawing_links, list_pointers); _free_relationship(relationship); } free(worksheet->vml_drawing_links); while (!STAILQ_EMPTY(worksheet->external_table_links)) { relationship = STAILQ_FIRST(worksheet->external_table_links); STAILQ_REMOVE_HEAD(worksheet->external_table_links, list_pointers); _free_relationship(relationship); } free(worksheet->external_table_links); if (worksheet->drawing_rel_ids) { for (drawing_rel_id = RB_MIN(lxw_drawing_rel_ids, worksheet->drawing_rel_ids); drawing_rel_id; drawing_rel_id = next_drawing_rel_id) { next_drawing_rel_id = RB_NEXT(lxw_drawing_rel_ids, worksheet->drawing_rel_id, drawing_rel_id); RB_REMOVE(lxw_drawing_rel_ids, worksheet->drawing_rel_ids, drawing_rel_id); free(drawing_rel_id->target); free(drawing_rel_id); } free(worksheet->drawing_rel_ids); } if (worksheet->vml_drawing_rel_ids) { for (drawing_rel_id = RB_MIN(lxw_vml_drawing_rel_ids, worksheet->vml_drawing_rel_ids); drawing_rel_id; drawing_rel_id = next_drawing_rel_id) { next_drawing_rel_id = RB_NEXT(lxw_vml_drawing_rel_ids, worksheet->drawing_rel_id, drawing_rel_id); RB_REMOVE(lxw_vml_drawing_rel_ids, worksheet->vml_drawing_rel_ids, drawing_rel_id); free(drawing_rel_id->target); free(drawing_rel_id); } free(worksheet->vml_drawing_rel_ids); } if (worksheet->conditional_formats) { for (cond_format_elem = RB_MIN(lxw_cond_format_hash, worksheet->conditional_formats); cond_format_elem; cond_format_elem = next_cond_format_elem) { next_cond_format_elem = RB_NEXT(lxw_cond_format_hash, worksheet->conditional_formats, cond_format_elem); RB_REMOVE(lxw_cond_format_hash, worksheet->conditional_formats, cond_format_elem); while (!STAILQ_EMPTY(cond_format_elem->cond_formats)) { cond_format = STAILQ_FIRST(cond_format_elem->cond_formats); STAILQ_REMOVE_HEAD(cond_format_elem->cond_formats, list_pointers); _free_cond_format(cond_format); } free(cond_format_elem->cond_formats); free(cond_format_elem); } free(worksheet->conditional_formats); } _free_relationship(worksheet->external_vml_comment_link); _free_relationship(worksheet->external_comment_link); _free_relationship(worksheet->external_vml_header_link); _free_relationship(worksheet->external_background_link); _free_filter_rules(worksheet); 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((void *) worksheet->name); free((void *) worksheet->quoted_name); free(worksheet->vba_codename); free(worksheet->vml_data_id_str); free(worksheet->vml_header_id_str); free(worksheet->comment_author); free(worksheet->ignore_number_stored_as_text); free(worksheet->ignore_eval_error); free(worksheet->ignore_formula_differs); free(worksheet->ignore_formula_range); free(worksheet->ignore_formula_unlocked); free(worksheet->ignore_empty_cell_reference); free(worksheet->ignore_list_data_validation); free(worksheet->ignore_calculated_column); free(worksheet->ignore_two_digit_text_year); free(worksheet->header); free(worksheet->footer); 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 inline_string cell object for rich strings. */ STATIC lxw_cell * _new_inline_rich_string_cell(lxw_row_t row_num, lxw_col_t col_num, const 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_RICH_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, uint8_t is_dynamic) { 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->format = format; cell->u.string = formula; cell->user_data1 = range; if (is_dynamic) cell->type = DYNAMIC_ARRAY_FORMULA_CELL; else cell->type = ARRAY_FORMULA_CELL; 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 comment cell object. */ STATIC lxw_cell * _new_comment_cell(lxw_row_t row_num, lxw_col_t col_num, lxw_vml_obj *comment_obj) { 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 = COMMENT; cell->comment = comment_obj; 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 blank placeholder cell in the cells RB tree in the same position * as a comment so that the rows "spans" calculation is correct. Since the * blank cell doesn't have a format it is ignored when writing. If there is * already a cell in the required position we don't have add a new cell. */ STATIC void _insert_cell_placeholder(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num) { lxw_row *row; lxw_cell *cell; /* The spans calculation isn't required in constant_memory mode. */ if (self->optimize) return; cell = _new_blank_cell(row_num, col_num, NULL); if (!cell) return; /* Only add a cell if one doesn't already exist. */ row = _get_row(self, row_num); if (!RB_FIND(lxw_table_cells, row->cells, cell)) { _insert_cell_list(row->cells, cell, col_num); } else { _free_cell(cell); } } /* * Insert a hyperlink object into the hyperlink RB tree. */ 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); } /* * Insert a comment into the comment RB tree. */ STATIC void _insert_comment(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, lxw_cell *link) { lxw_row *row = _get_row_list(self->comments, 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; } /* * Comparator for the image/hyperlink relationship ids. */ STATIC int _drawing_rel_id_cmp(lxw_drawing_rel_id *rel_id1, lxw_drawing_rel_id *rel_id2) { return strcmp(rel_id1->target, rel_id2->target); } /* * Comparator for the conditional format RB hash elements. */ STATIC int _cond_format_hash_cmp(lxw_cond_format_hash_element *elem_1, lxw_cond_format_hash_element *elem_2) { return strcmp(elem_1->sqref, elem_2->sqref); } /* * Get the index used to address a drawing rel link. */ STATIC uint32_t _get_drawing_rel_index(lxw_worksheet *self, char *target) { lxw_drawing_rel_id tmp_drawing_rel_id; lxw_drawing_rel_id *found_duplicate_target = NULL; lxw_drawing_rel_id *new_drawing_rel_id = NULL; if (target) { tmp_drawing_rel_id.target = target; found_duplicate_target = RB_FIND(lxw_drawing_rel_ids, self->drawing_rel_ids, &tmp_drawing_rel_id); } if (found_duplicate_target) { return found_duplicate_target->id; } else { self->drawing_rel_id++; if (target) { new_drawing_rel_id = calloc(1, sizeof(lxw_drawing_rel_id)); if (new_drawing_rel_id) { new_drawing_rel_id->id = self->drawing_rel_id; new_drawing_rel_id->target = lxw_strdup(target); RB_INSERT(lxw_drawing_rel_ids, self->drawing_rel_ids, new_drawing_rel_id); } } return self->drawing_rel_id; } } /* * find the index used to address a drawing rel link. */ STATIC uint32_t _find_drawing_rel_index(lxw_worksheet *self, char *target) { lxw_drawing_rel_id tmp_drawing_rel_id; lxw_drawing_rel_id *found_duplicate_target = NULL; if (!target) return 0; tmp_drawing_rel_id.target = target; found_duplicate_target = RB_FIND(lxw_drawing_rel_ids, self->drawing_rel_ids, &tmp_drawing_rel_id); if (found_duplicate_target) return found_duplicate_target->id; else return 0; } /* * Get the index used to address a VMLdrawing rel link. */ STATIC uint32_t _get_vml_drawing_rel_index(lxw_worksheet *self, char *target) { lxw_drawing_rel_id tmp_drawing_rel_id; lxw_drawing_rel_id *found_duplicate_target = NULL; lxw_drawing_rel_id *new_drawing_rel_id = NULL; if (target) { tmp_drawing_rel_id.target = target; found_duplicate_target = RB_FIND(lxw_vml_drawing_rel_ids, self->vml_drawing_rel_ids, &tmp_drawing_rel_id); } if (found_duplicate_target) { return found_duplicate_target->id; } else { self->vml_drawing_rel_id++; if (target) { new_drawing_rel_id = calloc(1, sizeof(lxw_drawing_rel_id)); if (new_drawing_rel_id) { new_drawing_rel_id->id = self->vml_drawing_rel_id; new_drawing_rel_id->target = lxw_strdup(target); RB_INSERT(lxw_vml_drawing_rel_ids, self->vml_drawing_rel_ids, new_drawing_rel_id); } } return self->vml_drawing_rel_id; } } /* * find the index used to address a VML drawing rel link. */ STATIC uint32_t _find_vml_drawing_rel_index(lxw_worksheet *self, char *target) { lxw_drawing_rel_id tmp_drawing_rel_id; lxw_drawing_rel_id *found_duplicate_target = NULL; if (!target) return 0; tmp_drawing_rel_id.target = target; found_duplicate_target = RB_FIND(lxw_vml_drawing_rel_ids, self->vml_drawing_rel_ids, &tmp_drawing_rel_id); if (found_duplicate_target) return found_duplicate_target->id; else return 0; } /* * 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(). */ const char * lxw_basename(const char *path) { const char *forward_slash; const char *back_slash; if (!path) return NULL; forward_slash = strrchr(path, '/'); back_slash = strrchr(path, '\\'); if (!forward_slash && !back_slash) return path; if (forward_slash > back_slash) return forward_slash + 1; else return back_slash + 1; } /* Function to count the total concatenated length of the strings in a * validation list array, including commas. */ size_t _validation_list_length(const char **list) { uint8_t i = 0; size_t length = 0; if (!list || !list[0]) return 0; while (list[i] && length < LXW_VALIDATION_MAX_STRING_LENGTH) { /* Include commas in the length. */ length += 1 + lxw_utf8_strlen(list[i]); i++; } /* Adjust the count for extraneous comma at end. */ length--; return length; } /* Function to convert an array of strings into a CSV string for data * validation lists. */ char * _validation_list_to_csv(const char **list) { uint8_t i = 0; char *str; /* Create a buffer for the concatenated, and quoted, string. */ /* Allow for 4 byte UTF-8 chars and add 3 bytes for quotes and EOL. */ str = calloc(1, LXW_VALIDATION_MAX_STRING_LENGTH * 4 + 3); if (!str) return NULL; /* Add the start quote and first element. */ strcat(str, "\""); strcat(str, list[0]); /* Add the other elements preceded by a comma. */ i = 1; while (list[i]) { strcat(str, ","); strcat(str, list[i]); i++; } /* Add the end quote. */ strcat(str, "\""); return str; } STATIC double _pixels_to_width(double pixels) { double max_digit_width = 7.0; double padding = 5.0; double width; if (pixels == LXW_DEF_COL_WIDTH_PIXELS) width = LXW_DEF_COL_WIDTH; else if (pixels <= 12.0) width = pixels / (max_digit_width + padding); else width = (pixels - padding) / max_digit_width; return width; } STATIC double _pixels_to_height(double pixels) { if (pixels == LXW_DEF_ROW_HEIGHT_PIXELS) return LXW_DEF_ROW_HEIGHT; else return pixels * 0.75; } /* Check and set if an autofilter is a standard or custom filter. */ void _set_custom_filter(lxw_filter_rule_obj *rule_obj) { rule_obj->is_custom = LXW_TRUE; if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_EQUAL_TO) rule_obj->is_custom = LXW_FALSE; if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_BLANKS) rule_obj->is_custom = LXW_FALSE; if (rule_obj->criteria2 != LXW_FILTER_CRITERIA_NONE) { if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_EQUAL_TO) rule_obj->is_custom = LXW_FALSE; if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_BLANKS) rule_obj->is_custom = LXW_FALSE; if (rule_obj->type == LXW_FILTER_TYPE_AND) rule_obj->is_custom = LXW_TRUE; } if (rule_obj->value1_string && strpbrk(rule_obj->value1_string, "*?")) rule_obj->is_custom = LXW_TRUE; if (rule_obj->value2_string && strpbrk(rule_obj->value2_string, "*?")) rule_obj->is_custom = LXW_TRUE; } /* Check and copy user input for table styles in worksheet_add_table(). */ void _check_and_copy_table_style(lxw_table_obj *table_obj, lxw_table_options *user_options) { if (!user_options) return; /* Set the defaults. */ table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; table_obj->style_type_number = 9; if (user_options->style_type > LXW_TABLE_STYLE_TYPE_DARK) { LXW_WARN_FORMAT1 ("worksheet_add_table(): invalid style_type = %d. " "Using default TableStyleMedium9", user_options->style_type); table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; table_obj->style_type_number = 9; } else { table_obj->style_type = user_options->style_type; } /* Each type (light, medium and dark) has a different number of styles. */ if (user_options->style_type == LXW_TABLE_STYLE_TYPE_LIGHT) { if (user_options->style_type_number > 21) { LXW_WARN_FORMAT1("worksheet_add_table(): " "invalid style_type_number = %d for style type " "LXW_TABLE_STYLE_TYPE_LIGHT. " "Using default TableStyleMedium9", user_options->style_type); table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; table_obj->style_type_number = 9; } else { table_obj->style_type_number = user_options->style_type_number; } } if (user_options->style_type == LXW_TABLE_STYLE_TYPE_MEDIUM) { if (user_options->style_type_number < 1 || user_options->style_type_number > 28) { LXW_WARN_FORMAT1("worksheet_add_table(): " "invalid style_type_number = %d for style type " "LXW_TABLE_STYLE_TYPE_MEDIUM. " "Using default TableStyleMedium9", user_options->style_type_number); table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; table_obj->style_type_number = 9; } else { table_obj->style_type_number = user_options->style_type_number; } } if (user_options->style_type == LXW_TABLE_STYLE_TYPE_DARK) { if (user_options->style_type_number < 1 || user_options->style_type_number > 11) { LXW_WARN_FORMAT1("worksheet_add_table(): " "invalid style_type_number = %d for style type " "LXW_TABLE_STYLE_TYPE_DARK. " "Using default TableStyleMedium9", user_options->style_type_number); table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; table_obj->style_type_number = 9; } else { table_obj->style_type_number = user_options->style_type_number; } } } /* Set the defaults for table columns in worksheet_add_table(). */ lxw_error _set_default_table_columns(lxw_table_obj *table_obj) { char col_name[LXW_ATTR_32]; char *header; uint16_t i; lxw_table_column *column; uint16_t num_cols = table_obj->num_cols; lxw_table_column **columns = table_obj->columns; for (i = 0; i < num_cols; i++) { lxw_snprintf(col_name, LXW_ATTR_32, "Column%d", i + 1); column = calloc(num_cols, sizeof(lxw_table_column)); RETURN_ON_MEM_ERROR(column, LXW_ERROR_MEMORY_MALLOC_FAILED); header = lxw_strdup(col_name); if (!header) { free(column); RETURN_ON_MEM_ERROR(header, LXW_ERROR_MEMORY_MALLOC_FAILED); } columns[i] = column; columns[i]->header = header; } return LXW_NO_ERROR; } /* Convert Excel 2010 style "@" structural references to the Excel 2007 style * "[#This Row]" in table formulas. This is the format that Excel uses to * store the references. */ char * _expand_table_formula(const char *formula) { char *expanded; const char *ptr; size_t i; size_t ref_count = 0; size_t expanded_len; ptr = formula; while (*ptr++) { if (*ptr == '@') ref_count++; } if (ref_count == 0) { /* String doesn't need to be expanded. Just copy it. */ expanded = lxw_strdup_formula(formula); } else { /* Convert "@" in the formula string to "[#This Row],". */ expanded_len = strlen(formula) + (sizeof(LXW_THIS_ROW) * ref_count); expanded = calloc(1, expanded_len); if (!expanded) return NULL; i = 0; ptr = formula; /* Ignore the = in the formula. */ if (*ptr == '=') ptr++; /* Do the "@" expansion. */ while (*ptr) { if (*ptr == '@') { strcat(&expanded[i], LXW_THIS_ROW); i += sizeof(LXW_THIS_ROW) - 1; } else { expanded[i] = *ptr; i++; } ptr++; } } return expanded; } /* Set user values for table columns in worksheet_add_table(). */ lxw_error _set_custom_table_columns(lxw_table_obj *table_obj, lxw_table_options *user_options) { char *str; uint16_t i; lxw_table_column *table_column; lxw_table_column *user_column; uint16_t num_cols = table_obj->num_cols; lxw_table_column **user_columns = user_options->columns; for (i = 0; i < num_cols; i++) { user_column = user_columns[i]; table_column = table_obj->columns[i]; /* NULL indicates end of user input array. */ if (user_column == NULL) return LXW_NO_ERROR; if (user_column->header) { if (lxw_utf8_strlen(user_column->header) > 255) { LXW_WARN_FORMAT("worksheet_add_table(): column parameter " "'header' exceeds Excel length limit of 255."); return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; } str = lxw_strdup(user_column->header); RETURN_ON_MEM_ERROR(str, LXW_ERROR_MEMORY_MALLOC_FAILED); /* Free the default column header. */ free((void *) table_column->header); table_column->header = str; } if (user_column->total_string) { str = lxw_strdup(user_column->total_string); RETURN_ON_MEM_ERROR(str, LXW_ERROR_MEMORY_MALLOC_FAILED); table_column->total_string = str; } if (user_column->formula) { str = _expand_table_formula(user_column->formula); RETURN_ON_MEM_ERROR(str, LXW_ERROR_MEMORY_MALLOC_FAILED); table_column->formula = str; } table_column->format = user_column->format; table_column->total_value = user_column->total_value; table_column->header_format = user_column->header_format; table_column->total_function = user_column->total_function; } return LXW_NO_ERROR; } /* Write a worksheet table column formula like SUBTOTAL(109,[Column1]). */ void _write_column_function(lxw_worksheet *self, lxw_row_t row, lxw_col_t col, lxw_table_column *column) { size_t offset; char formula[LXW_MAX_ATTRIBUTE_LENGTH]; lxw_format *format = column->format; uint8_t total_function = column->total_function; double value = column->total_value; const char *header = column->header; /* Write the subtotal formula number. */ lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "SUBTOTAL(%d,[", total_function); /* Copy the header string but escape any special characters. Note, this is * guaranteed to fit in the 2k buffer since the header is max 255 * characters, checked in _set_custom_table_columns(). */ offset = strlen(formula); while (*header) { switch (*header) { case '\'': case '#': case '[': case ']': formula[offset++] = '\''; formula[offset] = *header; break; default: formula[offset] = *header; break; } offset++; header++; } /* Write the end of the string. */ memcpy(&formula[offset], "])\0", sizeof("])\0")); worksheet_write_formula_num(self, row, col, formula, format, value); } /* Write a worksheet table column formula. */ void _write_column_formula(lxw_worksheet *self, lxw_row_t first_row, lxw_row_t last_row, lxw_col_t col, lxw_table_column *column) { lxw_row_t row; const char *formula = column->formula; lxw_format *format = column->format; for (row = first_row; row <= last_row; row++) worksheet_write_formula(self, row, col, formula, format); } /* Set the defaults for table columns in worksheet_add_table(). */ void _write_table_column_data(lxw_worksheet *self, lxw_table_obj *table_obj) { uint16_t i; lxw_table_column *column; lxw_table_column **columns = table_obj->columns; lxw_col_t col; lxw_row_t first_row = table_obj->first_row; lxw_col_t first_col = table_obj->first_col; lxw_row_t last_row = table_obj->last_row; lxw_row_t first_data_row = first_row; lxw_row_t last_data_row = last_row; if (!table_obj->no_header_row) first_data_row++; if (table_obj->total_row) last_data_row--; for (i = 0; i < table_obj->num_cols; i++) { col = first_col + i; column = columns[i]; if (table_obj->no_header_row == LXW_FALSE) worksheet_write_string(self, first_row, col, column->header, column->header_format); if (column->total_string) worksheet_write_string(self, last_row, col, column->total_string, NULL); if (column->total_function) _write_column_function(self, last_row, col, column); if (column->formula) _write_column_formula(self, first_data_row, last_data_row, col, column); } } /* * Check that there are sufficient data rows in a worksheet table. */ lxw_error _check_table_rows(lxw_row_t first_row, lxw_row_t last_row, lxw_table_options *user_options) { lxw_row_t num_non_header_rows = last_row - first_row; if (user_options && user_options->no_header_row == LXW_TRUE) num_non_header_rows++; if (num_non_header_rows == 0) { LXW_WARN_FORMAT("worksheet_add_table(): " "table must have at least 1 non-header row."); return LXW_ERROR_PARAMETER_VALIDATION; } return LXW_NO_ERROR; } /* * Check that the the table name is valid. */ lxw_error _check_table_name(lxw_table_options *user_options) { const char *name; char *ptr; char first[2] = { 0, 0 }; if (!user_options) return LXW_NO_ERROR; if (!user_options->name) return LXW_NO_ERROR; name = user_options->name; /* Check table name length. */ if (lxw_utf8_strlen(name) > 255) { LXW_WARN_FORMAT("worksheet_add_table(): " "Table name exceeds Excel's limit of 255."); return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; } /* Check some short invalid names. */ if (strlen(name) == 1 && (name[0] == 'C' || name[0] == 'c' || name[0] == 'R' || name[0] == 'r')) { LXW_WARN_FORMAT1("worksheet_add_table(): " "invalid table name \"%s\".", name); return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; } /* Check for invalid characters in Table name, while trying to allow * for utf8 strings. */ ptr = strpbrk(name, " !\"#$%&'()*+,-/:;<=>?@[\\]^`{|}~"); if (ptr) { LXW_WARN_FORMAT2("worksheet_add_table(): " "invalid character '%c' in table name \"%s\".", *ptr, name); return LXW_ERROR_PARAMETER_VALIDATION; } /* Check for invalid initial character in Table name, while trying to allow * for utf8 strings. */ first[0] = name[0]; ptr = strpbrk(first, " !\"#$%&'()*+,-./0123456789:;<=>?@[\\]^`{|}~"); if (ptr) { LXW_WARN_FORMAT2("worksheet_add_table(): " "invalid first character '%c' in table name \"%s\".", *ptr, name); return LXW_ERROR_PARAMETER_VALIDATION; } return LXW_NO_ERROR; } /***************************************************************************** * * 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"; char xmlns_mc[] = "http://schemas.openxmlformats.org/" "markup-compatibility/2006"; char xmlns_x14ac[] = "http://schemas.microsoft.com/" "office/spreadsheetml/2009/9/ac"; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); LXW_PUSH_ATTRIBUTES_STR("xmlns:r", xmlns_r); if (self->excel_version == 2010) { LXW_PUSH_ATTRIBUTES_STR("xmlns:mc", xmlns_mc); LXW_PUSH_ATTRIBUTES_STR("xmlns:x14ac", xmlns_x14ac); LXW_PUSH_ATTRIBUTES_STR("mc:Ignorable", "x14ac"); } 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 top left cell if required. */ if (self->top_left_cell[0]) LXW_PUSH_ATTRIBUTES_STR("topLeftCell", self->top_left_cell); /* Set the zoom level. */ if (self->zoom != 100 && !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"); if (self->outline_row_level) LXW_PUSH_ATTRIBUTES_INT("outlineLevelRow", self->outline_row_level); if (self->outline_col_level) LXW_PUSH_ATTRIBUTES_INT("outlineLevelCol", self->outline_col_level); if (self->excel_version == 2010) LXW_PUSH_ATTRIBUTES_STR("x14ac:dyDescent", "0.25"); 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 the temp file. */ fflush(self->optimize_tmpfile); if (self->optimize_buffer) { /* Ignore return value. There is no easy way to raise error. */ (void) fwrite(self->optimize_buffer, self->optimize_buffer_size, 1, self->file); } else { /* Rewind the temp file. */ rewind(self->optimize_tmpfile); while (read_size) { read_size = fread(buffer, 1, LXW_BUFFER_SIZE, self->optimize_tmpfile); /* Ignore return value. There is no easy way to raise error. */ (void) fwrite(buffer, 1, read_size, self->file); } } fclose(self->optimize_tmpfile); free(self->optimize_buffer); 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"); if (self->black_white) LXW_PUSH_ATTRIBUTES_STR("blackAndWhite", "1"); /* 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->level) LXW_PUSH_ATTRIBUTES_INT("outlineLevel", row->level); if (row->collapsed) LXW_PUSH_ATTRIBUTES_STR("collapsed", "1"); if (self->excel_version == 2010) LXW_PUSH_ATTRIBUTES_STR("x14ac:dyDescent", "0.25"); 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, uint8_t anchor) { 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 (col_opt->hidden && anchor != LXW_OBJECT_MOVE_AND_SIZE_AFTER) { 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, uint8_t anchor) { lxw_row *row; uint32_t pixels; row = lxw_worksheet_find_row(self, row_num); /* Note, the 0.75 below is due to the difference between 72/96 DPI. */ if (row) { if (row->hidden && anchor != LXW_OBJECT_MOVE_AND_SIZE_AFTER) pixels = 0; else pixels = (uint32_t) (row->height / 0.75); } else { pixels = (uint32_t) (self->default_row_height / 0.75); } 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_object_properties *object_props, 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; uint8_t anchor = drawing_object->anchor; uint8_t ignore_anchor = LXW_OBJECT_POSITION_DEFAULT; col_start = object_props->col; row_start = object_props->row; x1 = object_props->x_offset; y1 = object_props->y_offset; width = object_props->width; height = object_props->height; /* Adjust start column for negative offsets. */ while (x1 < 0 && col_start > 0) { x1 += _worksheet_size_col(self, col_start - 1, ignore_anchor); col_start--; } /* Adjust start row for negative offsets. */ while (y1 < 0 && row_start > 0) { y1 += _worksheet_size_row(self, row_start - 1, ignore_anchor); 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, ignore_anchor); } 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, ignore_anchor); } 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, anchor)) { x1 -= _worksheet_size_col(self, col_start, ignore_anchor); col_start++; } /* Adjust start row for offsets that are greater than the row height. */ while (y1 >= _worksheet_size_row(self, row_start, anchor)) { y1 -= _worksheet_size_row(self, row_start, ignore_anchor); row_start++; } /* Initialize end cell to the same as the start cell. */ col_end = col_start; row_end = row_start; /* Only offset the image in the cell if the row/col is hidden. */ if (_worksheet_size_col(self, col_start, anchor) > 0) width = width + x1; if (_worksheet_size_row(self, row_start, anchor) > 0) height = height + y1; /* Subtract the underlying cell widths to find the end cell. */ while (width >= _worksheet_size_col(self, col_end, anchor)) { width -= _worksheet_size_col(self, col_end, anchor); col_end++; } /* Subtract the underlying cell heights to find the end cell. */ while (height >= _worksheet_size_row(self, row_end, anchor)) { height -= _worksheet_size_row(self, row_end, anchor); 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_object_properties *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; } /* * This function handles the additional optional parameters to * worksheet_write_comment_opt() as well as calculating the comment object * position and vertices. */ void _get_comment_params(lxw_vml_obj *comment, lxw_comment_options *options) { lxw_row_t start_row; lxw_col_t start_col; int32_t x_offset; int32_t y_offset; uint32_t height = 74; uint32_t width = 128; double x_scale = 1.0; double y_scale = 1.0; lxw_row_t row = comment->row; lxw_col_t col = comment->col;; /* Set the default start cell and offsets for the comment. These are * generally fixed in relation to the parent cell. However there are some * edge cases for cells at the, well yes, edges. */ if (row == 0) y_offset = 2; else if (row == LXW_ROW_MAX - 3) y_offset = 16; else if (row == LXW_ROW_MAX - 2) y_offset = 16; else if (row == LXW_ROW_MAX - 1) y_offset = 14; else y_offset = 10; if (col == LXW_COL_MAX - 3) x_offset = 49; else if (col == LXW_COL_MAX - 2) x_offset = 49; else if (col == LXW_COL_MAX - 1) x_offset = 49; else x_offset = 15; if (row == 0) start_row = 0; else if (row == LXW_ROW_MAX - 3) start_row = LXW_ROW_MAX - 7; else if (row == LXW_ROW_MAX - 2) start_row = LXW_ROW_MAX - 6; else if (row == LXW_ROW_MAX - 1) start_row = LXW_ROW_MAX - 5; else start_row = row - 1; if (col == LXW_COL_MAX - 3) start_col = LXW_COL_MAX - 6; else if (col == LXW_COL_MAX - 2) start_col = LXW_COL_MAX - 5; else if (col == LXW_COL_MAX - 1) start_col = LXW_COL_MAX - 4; else start_col = col + 1; /* Set the default font properties. */ comment->font_size = 8; comment->font_family = 2; /* Set any user defined options. */ if (options) { if (options->width > 0.0) width = options->width; if (options->height > 0.0) height = options->height; if (options->x_scale > 0.0) x_scale = options->x_scale; if (options->y_scale > 0.0) y_scale = options->y_scale; if (options->x_offset != 0) x_offset = options->x_offset; if (options->y_offset != 0) y_offset = options->y_offset; if (options->start_row > 0 || options->start_col > 0) { start_row = options->start_row; start_col = options->start_col; } if (options->font_size > 0.0) comment->font_size = options->font_size; if (options->font_family > 0) comment->font_family = options->font_family; comment->visible = options->visible; comment->color = options->color; comment->author = lxw_strdup(options->author); comment->font_name = lxw_strdup(options->font_name); } /* Scale the width/height to the default/user scale and round to the * nearest pixel. */ width = (uint32_t) (0.5 + x_scale * width); height = (uint32_t) (0.5 + y_scale * height); comment->width = width; comment->height = height; comment->start_col = start_col; comment->start_row = start_row; comment->x_offset = x_offset; comment->y_offset = y_offset; } /* * This function handles the additional optional parameters to * worksheet_insert_button() as well as calculating the button object * position and vertices. */ lxw_error _get_button_params(lxw_vml_obj *button, uint16_t button_number, lxw_button_options *options) { int32_t x_offset = 0; int32_t y_offset = 0; uint32_t height = LXW_DEF_ROW_HEIGHT_PIXELS; uint32_t width = LXW_DEF_COL_WIDTH_PIXELS; double x_scale = 1.0; double y_scale = 1.0; lxw_row_t row = button->row; lxw_col_t col = button->col; char buffer[LXW_ATTR_32]; uint8_t has_caption = LXW_FALSE; uint8_t has_macro = LXW_FALSE; size_t len; /* Set any user defined options. */ if (options) { if (options->width > 0.0) width = options->width; if (options->height > 0.0) height = options->height; if (options->x_scale > 0.0) x_scale = options->x_scale; if (options->y_scale > 0.0) y_scale = options->y_scale; if (options->x_offset != 0) x_offset = options->x_offset; if (options->y_offset != 0) y_offset = options->y_offset; if (options->caption) { button->name = lxw_strdup(options->caption); RETURN_ON_MEM_ERROR(button->name, LXW_ERROR_MEMORY_MALLOC_FAILED); has_caption = LXW_TRUE; } if (options->macro) { len = sizeof("[0]!") + strlen(options->macro); button->macro = calloc(1, len); RETURN_ON_MEM_ERROR(button->macro, LXW_ERROR_MEMORY_MALLOC_FAILED); if (button->macro) lxw_snprintf(button->macro, len, "[0]!%s", options->macro); has_macro = LXW_TRUE; } if (options->description) { button->text = lxw_strdup(options->description); RETURN_ON_MEM_ERROR(button->text, LXW_ERROR_MEMORY_MALLOC_FAILED); } } if (!has_caption) { lxw_snprintf(buffer, LXW_ATTR_32, "Button %d", button_number); button->name = lxw_strdup(buffer); RETURN_ON_MEM_ERROR(button->name, LXW_ERROR_MEMORY_MALLOC_FAILED); } if (!has_macro) { lxw_snprintf(buffer, LXW_ATTR_32, "[0]!Button%d_Click", button_number); button->macro = lxw_strdup(buffer); RETURN_ON_MEM_ERROR(button->macro, LXW_ERROR_MEMORY_MALLOC_FAILED); } /* Scale the width/height to the default/user scale and round to the * nearest pixel. */ width = (uint32_t) (0.5 + x_scale * width); height = (uint32_t) (0.5 + y_scale * height); button->width = width; button->height = height; button->start_col = col; button->start_row = row; button->x_offset = x_offset; button->y_offset = y_offset; return LXW_NO_ERROR; } /* * Calculate the vml_obj object position and vertices. */ void _worksheet_position_vml_object(lxw_worksheet *self, lxw_vml_obj *vml_obj) { lxw_object_properties object_props; lxw_drawing_object drawing_object; object_props.col = vml_obj->start_col; object_props.row = vml_obj->start_row; object_props.x_offset = vml_obj->x_offset; object_props.y_offset = vml_obj->y_offset; object_props.width = vml_obj->width; object_props.height = vml_obj->height; drawing_object.anchor = LXW_OBJECT_DONT_MOVE_DONT_SIZE; _worksheet_position_object_pixels(self, &object_props, &drawing_object); vml_obj->from.col = drawing_object.from.col; vml_obj->from.row = drawing_object.from.row; vml_obj->from.col_offset = drawing_object.from.col_offset; vml_obj->from.row_offset = drawing_object.from.row_offset; vml_obj->to.col = drawing_object.to.col; vml_obj->to.row = drawing_object.to.row; vml_obj->to.col_offset = drawing_object.to.col_offset; vml_obj->to.row_offset = drawing_object.to.row_offset; vml_obj->col_absolute = drawing_object.col_absolute; vml_obj->row_absolute = drawing_object.row_absolute; } /* * Set up image/drawings. */ void lxw_worksheet_prepare_image(lxw_worksheet *self, uint32_t image_ref_id, uint32_t drawing_id, lxw_object_properties *object_props) { lxw_drawing_object *drawing_object; lxw_rel_tuple *relationship; double width; double height; char *url; char *found_string; size_t i; char filename[LXW_FILENAME_LENGTH]; enum cell_types link_type = HYPERLINK_URL; 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 = LXW_OBJECT_MOVE_DONT_SIZE; if (object_props->object_position) drawing_object->anchor = object_props->object_position; drawing_object->type = LXW_DRAWING_IMAGE; drawing_object->description = lxw_strdup(object_props->description); drawing_object->tip = lxw_strdup(object_props->tip); drawing_object->rel_index = 0; drawing_object->url_rel_index = 0; drawing_object->decorative = object_props->decorative; /* Scale to user scale. */ width = object_props->width * object_props->x_scale; height = object_props->height * object_props->y_scale; /* Scale by non 96dpi resolutions. */ width *= 96.0 / object_props->x_dpi; height *= 96.0 / object_props->y_dpi; object_props->width = width; object_props->height = height; _worksheet_position_object_emus(self, object_props, 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); if (object_props->url) { url = object_props->url; 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); /* Check the link type. Default to external hyperlinks. */ if (strstr(url, "internal:")) link_type = HYPERLINK_INTERNAL; else if (strstr(url, "external:")) link_type = HYPERLINK_EXTERNAL; else link_type = HYPERLINK_URL; /* Set the relationship object for each type of link. */ if (link_type == HYPERLINK_INTERNAL) { relationship->target_mode = NULL; relationship->target = lxw_strdup(url + sizeof("internal") - 1); GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); /* We need to prefix the internal link/range with #. */ relationship->target[0] = '#'; } else if (link_type == HYPERLINK_EXTERNAL) { relationship->target_mode = lxw_strdup("External"); GOTO_LABEL_ON_MEM_ERROR(relationship->target_mode, mem_error); /* Look for Windows style "C:/" link or Windows share "\\" link. */ found_string = strchr(url + sizeof("external:") - 1, ':'); if (!found_string) found_string = strstr(url, "\\\\"); if (found_string) { /* Copy the url with some space at the start to overwrite * "external:" with "file:///". */ relationship->target = lxw_escape_url_characters(url + 1, LXW_TRUE); GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); /* Add the file:/// URI to the url if absolute path. */ memcpy(relationship->target, "file:///", sizeof("file:///") - 1); } else { /* Copy the relative url without "external:". */ relationship->target = lxw_escape_url_characters(url + sizeof("external:") - 1, LXW_TRUE); GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); /* Switch backslash to forward slash. */ for (i = 0; i <= strlen(relationship->target); i++) if (relationship->target[i] == '\\') relationship->target[i] = '/'; } } else { relationship->target_mode = lxw_strdup("External"); GOTO_LABEL_ON_MEM_ERROR(relationship->target_mode, mem_error); relationship->target = lxw_escape_url_characters(object_props->url, LXW_FALSE); GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); } /* Check if URL exceeds Excel's length limit. */ if (lxw_utf8_strlen(url) > self->max_url_length) { LXW_WARN_FORMAT2("worksheet_insert_image()/_opt(): URL exceeds " "Excel's allowable length of %d characters: %s", self->max_url_length, url); goto mem_error; } if (!_find_drawing_rel_index(self, url)) { STAILQ_INSERT_TAIL(self->drawing_links, relationship, list_pointers); } else { free(relationship->type); free(relationship->target); free(relationship->target_mode); free(relationship); } drawing_object->url_rel_index = _get_drawing_rel_index(self, url); } if (!_find_drawing_rel_index(self, object_props->md5)) { 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, object_props->extension); relationship->target = lxw_strdup(filename); GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); STAILQ_INSERT_TAIL(self->drawing_links, relationship, list_pointers); } drawing_object->rel_index = _get_drawing_rel_index(self, object_props->md5); return; mem_error: if (relationship) { free(relationship->type); free(relationship->target); free(relationship->target_mode); free(relationship); } } /* * Set up image/drawings for header/footer images. */ void lxw_worksheet_prepare_header_image(lxw_worksheet *self, uint32_t image_ref_id, lxw_object_properties *object_props) { lxw_rel_tuple *relationship = NULL; char filename[LXW_FILENAME_LENGTH]; lxw_vml_obj *header_image_vml; char *extension; STAILQ_INSERT_TAIL(self->image_props, object_props, list_pointers); if (!_find_vml_drawing_rel_index(self, object_props->md5)) { relationship = calloc(1, sizeof(lxw_rel_tuple)); RETURN_VOID_ON_MEM_ERROR(relationship); 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, object_props->extension); relationship->target = lxw_strdup(filename); GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); STAILQ_INSERT_TAIL(self->vml_drawing_links, relationship, list_pointers); } header_image_vml = calloc(1, sizeof(lxw_vml_obj)); GOTO_LABEL_ON_MEM_ERROR(header_image_vml, mem_error); header_image_vml->width = (uint32_t) object_props->width; header_image_vml->height = (uint32_t) object_props->height; header_image_vml->x_dpi = object_props->x_dpi; header_image_vml->y_dpi = object_props->y_dpi; header_image_vml->rel_index = 1; header_image_vml->image_position = lxw_strdup(object_props->image_position); header_image_vml->name = lxw_strdup(object_props->description); /* Strip the extension from the filename. */ extension = strchr(header_image_vml->name, '.'); if (extension) *extension = '\0'; header_image_vml->rel_index = _get_vml_drawing_rel_index(self, object_props->md5); STAILQ_INSERT_TAIL(self->header_image_objs, header_image_vml, list_pointers); return; mem_error: if (relationship) { free(relationship->type); free(relationship->target); free(relationship->target_mode); free(relationship); } } /* * Set up background image. */ void lxw_worksheet_prepare_background(lxw_worksheet *self, uint32_t image_ref_id, lxw_object_properties *object_props) { lxw_rel_tuple *relationship = NULL; char filename[LXW_FILENAME_LENGTH]; STAILQ_INSERT_TAIL(self->image_props, object_props, list_pointers); relationship = calloc(1, sizeof(lxw_rel_tuple)); RETURN_VOID_ON_MEM_ERROR(relationship); 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, object_props->extension); relationship->target = lxw_strdup(filename); GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); self->external_background_link = relationship; 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, uint32_t chart_ref_id, uint32_t drawing_id, lxw_object_properties *object_props, uint8_t is_chartsheet) { 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(); RETURN_VOID_ON_MEM_ERROR(self->drawing); if (is_chartsheet) { self->drawing->embedded = LXW_FALSE; self->drawing->orientation = self->orientation; } else { self->drawing->embedded = LXW_TRUE; } 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 = LXW_OBJECT_MOVE_AND_SIZE; if (object_props->object_position) drawing_object->anchor = object_props->object_position; drawing_object->type = LXW_DRAWING_CHART; drawing_object->description = lxw_strdup(object_props->description); drawing_object->tip = NULL; drawing_object->rel_index = _get_drawing_rel_index(self, NULL); drawing_object->url_rel_index = 0; drawing_object->decorative = object_props->decorative; /* Scale to user scale. */ width = object_props->width * object_props->x_scale; height = object_props->height * object_props->y_scale; /* Convert to the nearest pixel. */ object_props->width = width; object_props->height = height; _worksheet_position_object_emus(self, object_props, 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); } } /* * Set up VML objects, such as comments, in the worksheet. */ uint32_t lxw_worksheet_prepare_vml_objects(lxw_worksheet *self, uint32_t vml_data_id, uint32_t vml_shape_id, uint32_t vml_drawing_id, uint32_t comment_id) { lxw_row *row; lxw_cell *cell; lxw_rel_tuple *relationship; char filename[LXW_FILENAME_LENGTH]; uint32_t comment_count = 0; uint32_t i; uint32_t tmp_data_id; size_t data_str_len = 0; size_t used = 0; char *vml_data_id_str; RB_FOREACH(row, lxw_table_rows, self->comments) { RB_FOREACH(cell, lxw_table_cells, row->cells) { /* Calculate the worksheet position of the comment. */ _worksheet_position_vml_object(self, cell->comment); /* Store comment in a simple list for use by packager. */ STAILQ_INSERT_TAIL(self->comment_objs, cell->comment, list_pointers); comment_count++; } } /* Set up the VML relationship for comments/buttons/header images. */ relationship = calloc(1, sizeof(lxw_rel_tuple)); GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); relationship->type = lxw_strdup("/vmlDrawing"); GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); lxw_snprintf(filename, 32, "../drawings/vmlDrawing%d.vml", vml_drawing_id); relationship->target = lxw_strdup(filename); GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); self->external_vml_comment_link = relationship; if (self->has_comments) { /* Only need this relationship object for comment VMLs. */ relationship = calloc(1, sizeof(lxw_rel_tuple)); GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); relationship->type = lxw_strdup("/comments"); GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); lxw_snprintf(filename, 32, "../comments%d.xml", comment_id); relationship->target = lxw_strdup(filename); GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); self->external_comment_link = relationship; } /* The vml.c element data id contains a comma separated range * when there is more than one 1024 block of comments, like this: * data="1,2,3". Since this could potentially (but unlikely) exceed * LXW_MAX_ATTRIBUTE_LENGTH we need to allocate space dynamically. */ /* Calculate the total space required for the ID for each 1024 block. */ for (i = 0; i <= comment_count / 1024; i++) { tmp_data_id = vml_data_id + i; /* Calculate the space required for the digits in the id. */ while (tmp_data_id) { data_str_len++; tmp_data_id /= 10; } /* Add an extra char for comma separator or '\O'. */ data_str_len++; }; /* If this allocation fails it will be dealt with in packager.c. */ vml_data_id_str = calloc(1, data_str_len + 2); GOTO_LABEL_ON_MEM_ERROR(vml_data_id_str, mem_error); /* Create the CSV list in the allocated space. */ for (i = 0; i <= comment_count / 1024; i++) { tmp_data_id = vml_data_id + i; lxw_snprintf(vml_data_id_str + used, data_str_len - used, "%d,", tmp_data_id); used = strlen(vml_data_id_str); }; self->vml_shape_id = vml_shape_id; self->vml_data_id_str = vml_data_id_str; return comment_count; mem_error: if (relationship) { free(relationship->type); free(relationship->target); free(relationship->target_mode); free(relationship); } return 0; } /* * Set up external linkage for VML header/footer images. */ void lxw_worksheet_prepare_header_vml_objects(lxw_worksheet *self, uint32_t vml_header_id, uint32_t vml_drawing_id) { lxw_rel_tuple *relationship; char filename[LXW_FILENAME_LENGTH]; char *vml_data_id_str; self->vml_header_id = vml_header_id; /* Set up the VML relationship for header images. */ relationship = calloc(1, sizeof(lxw_rel_tuple)); GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); relationship->type = lxw_strdup("/vmlDrawing"); GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); lxw_snprintf(filename, 32, "../drawings/vmlDrawing%d.vml", vml_drawing_id); relationship->target = lxw_strdup(filename); GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); self->external_vml_header_link = relationship; /* If this allocation fails it will be dealt with in packager.c. */ vml_data_id_str = calloc(1, sizeof("4294967295")); GOTO_LABEL_ON_MEM_ERROR(vml_data_id_str, mem_error); lxw_snprintf(vml_data_id_str, sizeof("4294967295"), "%d", vml_header_id); self->vml_header_id_str = vml_data_id_str; return; mem_error: if (relationship) { free(relationship->type); free(relationship->target); free(relationship->target_mode); free(relationship); } return; } /* * Set up external linkage for VML header/footer images. */ void lxw_worksheet_prepare_tables(lxw_worksheet *self, uint32_t table_id) { lxw_table_obj *table_obj; lxw_rel_tuple *relationship; char name[LXW_ATTR_32]; char filename[LXW_FILENAME_LENGTH]; STAILQ_FOREACH(table_obj, self->table_objs, list_pointers) { relationship = calloc(1, sizeof(lxw_rel_tuple)); GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); relationship->type = lxw_strdup("/table"); GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); lxw_snprintf(filename, LXW_FILENAME_LENGTH, "../tables/table%d.xml", table_id); relationship->target = lxw_strdup(filename); GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); STAILQ_INSERT_TAIL(self->external_table_links, relationship, list_pointers); if (!table_obj->name) { lxw_snprintf(name, LXW_ATTR_32, "Table%d", table_id); table_obj->name = lxw_strdup(name); GOTO_LABEL_ON_MEM_ERROR(table_obj->name, mem_error); } table_obj->id = table_id; table_id++; } return; mem_error: if (relationship) { free(relationship->type); free(relationship->target); free(relationship->target_mode); free(relationship); } return; } /* * Extract width and height information from a PNG file. */ STATIC lxw_error _process_png(lxw_object_properties *object_props) { 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 = object_props->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. */ object_props->image_type = LXW_IMAGE_PNG; object_props->width = width; object_props->height = height; object_props->x_dpi = x_dpi ? x_dpi : 96; object_props->y_dpi = y_dpi ? y_dpi : 96; object_props->extension = lxw_strdup("png"); return LXW_NO_ERROR; file_error: LXW_WARN_FORMAT1("worksheet image insertion: " "no size data found in: %s.", object_props->filename); return LXW_ERROR_IMAGE_DIMENSIONS; } /* * Extract width and height information from a JPEG file. */ STATIC lxw_error _process_jpeg(lxw_object_properties *image_props) { 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_props->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 and read the JPEG markers. */ 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; /* Read the height and width in the 0xFFCn elements (except C4, C8 */ /* and CC which aren't SOF markers). */ if ((marker & 0xFFF0) == 0xFFC0 && marker != 0xFFC4 && marker != 0xFFC8 && marker != 0xFFCC) { /* 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; } /* Read the DPI in the 0xFFE0 element. */ 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) break; } } /* Ensure that we read some valid data from the file. */ if (width == 0) goto file_error; /* Set the image metadata. */ image_props->image_type = LXW_IMAGE_JPEG; image_props->width = width; image_props->height = height; image_props->x_dpi = x_dpi ? x_dpi : 96; image_props->y_dpi = y_dpi ? y_dpi : 96; image_props->extension = lxw_strdup("jpeg"); return LXW_NO_ERROR; file_error: LXW_WARN_FORMAT1("worksheet image insertion: " "no size data found in: %s.", image_props->filename); return LXW_ERROR_IMAGE_DIMENSIONS; } /* * Extract width and height information from a BMP file. */ STATIC lxw_error _process_bmp(lxw_object_properties *image_props) { uint32_t width = 0; uint32_t height = 0; double x_dpi = 96; double y_dpi = 96; int fseek_err; FILE *stream = image_props->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; height = LXW_UINT32_HOST(height); width = LXW_UINT32_HOST(width); /* Set the image metadata. */ image_props->image_type = LXW_IMAGE_BMP; image_props->width = width; image_props->height = height; image_props->x_dpi = x_dpi; image_props->y_dpi = y_dpi; image_props->extension = lxw_strdup("bmp"); return LXW_NO_ERROR; file_error: LXW_WARN_FORMAT1("worksheet image insertion: " "no size data found in: %s.", image_props->filename); return LXW_ERROR_IMAGE_DIMENSIONS; } /* * Extract width and height information from a GIF file. */ STATIC lxw_error _process_gif(lxw_object_properties *image_props) { uint16_t width = 0; uint16_t height = 0; double x_dpi = 96; double y_dpi = 96; int fseek_err; FILE *stream = image_props->stream; /* Skip another 2 bytes to the start of the GIF height/width. */ fseek_err = fseek(stream, 2, 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; height = LXW_UINT16_HOST(height); width = LXW_UINT16_HOST(width); /* Set the image metadata. */ image_props->image_type = LXW_IMAGE_GIF; image_props->width = width; image_props->height = height; image_props->x_dpi = x_dpi; image_props->y_dpi = y_dpi; image_props->extension = lxw_strdup("gif"); return LXW_NO_ERROR; file_error: LXW_WARN_FORMAT1("worksheet image insertion: " "no size data found in: %s.", image_props->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_object_properties *image_props) { unsigned char signature[4]; #ifndef USE_NO_MD5 uint8_t i; MD5_CTX md5_context; size_t size_read; char buffer[LXW_IMAGE_BUFFER_SIZE]; unsigned char md5_checksum[LXW_MD5_SIZE]; #endif /* Read 4 bytes to look for the file header/signature. */ if (fread(signature, 1, 4, image_props->stream) < 4) { LXW_WARN_FORMAT1("worksheet image insertion: " "couldn't read image type for: %s.", image_props->filename); return LXW_ERROR_IMAGE_DIMENSIONS; } if (memcmp(&signature[1], "PNG", 3) == 0) { if (_process_png(image_props) != LXW_NO_ERROR) return LXW_ERROR_IMAGE_DIMENSIONS; } else if (signature[0] == 0xFF && signature[1] == 0xD8) { if (_process_jpeg(image_props) != LXW_NO_ERROR) return LXW_ERROR_IMAGE_DIMENSIONS; } else if (memcmp(signature, "BM", 2) == 0) { if (_process_bmp(image_props) != LXW_NO_ERROR) return LXW_ERROR_IMAGE_DIMENSIONS; } else if (memcmp(signature, "GIF8", 4) == 0) { if (_process_gif(image_props) != LXW_NO_ERROR) return LXW_ERROR_IMAGE_DIMENSIONS; } else { LXW_WARN_FORMAT1("worksheet image insertion: " "unsupported image format for: %s.", image_props->filename); return LXW_ERROR_IMAGE_DIMENSIONS; } #ifndef USE_NO_MD5 /* Calculate an MD5 checksum for the image so that we can remove duplicate * images to reduce the xlsx file size.*/ rewind(image_props->stream); MD5_Init(&md5_context); size_read = fread(buffer, 1, LXW_IMAGE_BUFFER_SIZE, image_props->stream); while (size_read) { MD5_Update(&md5_context, buffer, (unsigned long) size_read); size_read = fread(buffer, 1, LXW_IMAGE_BUFFER_SIZE, image_props->stream); } MD5_Final(md5_checksum, &md5_context); /* Create a 32 char hex string buffer for the MD5 checksum. */ image_props->md5 = calloc(1, LXW_MD5_SIZE * 2 + 1); /* If this calloc fails we just return and don't remove duplicates. */ RETURN_ON_MEM_ERROR(image_props->md5, LXW_NO_ERROR); /* Convert the 16 byte MD5 buffer to a 32 char hex string. */ for (i = 0; i < LXW_MD5_SIZE; i++) { lxw_snprintf(&image_props->md5[2 * i], 3, "%02x", md5_checksum[i]); } #endif return LXW_NO_ERROR; } /* Conditional formats that refer to the same cell sqref range, like A or * B1:B9, need to be written as part of one xml structure. Therefore we need * to store them in a RB hash/tree keyed by sqref. Within the RB hash element * we then store conditional formats that refer to sqref in a STAILQ list. */ lxw_error _store_conditional_format_object(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { lxw_cond_format_hash_element tmp_hash_element; lxw_cond_format_hash_element *found_hash_element = NULL; lxw_cond_format_hash_element *new_hash_element = NULL; /* Create a temp hash element to do the lookup. */ LXW_ATTRIBUTE_COPY(tmp_hash_element.sqref, cond_format->sqref); found_hash_element = RB_FIND(lxw_cond_format_hash, self->conditional_formats, &tmp_hash_element); if (found_hash_element) { /* If the RB element exists then add the conditional format to the * list for the sqref range.*/ STAILQ_INSERT_TAIL(found_hash_element->cond_formats, cond_format, list_pointers); } else { /* Create a new RB hash element. */ new_hash_element = calloc(1, sizeof(lxw_cond_format_hash_element)); GOTO_LABEL_ON_MEM_ERROR(new_hash_element, mem_error); /* Use the sqref as the key. */ LXW_ATTRIBUTE_COPY(new_hash_element->sqref, cond_format->sqref); /* Also create the list where we store the cond format objects. */ new_hash_element->cond_formats = calloc(1, sizeof(struct lxw_cond_format_list)); GOTO_LABEL_ON_MEM_ERROR(new_hash_element->cond_formats, mem_error); /* Initialize the list and add the conditional format object. */ STAILQ_INIT(new_hash_element->cond_formats); STAILQ_INSERT_TAIL(new_hash_element->cond_formats, cond_format, list_pointers); /* Now insert the RB hash element into the tree. */ RB_INSERT(lxw_cond_format_hash, self->conditional_formats, new_hash_element); } return LXW_NO_ERROR; mem_error: free(new_hash_element); return LXW_ERROR_MEMORY_MALLOC_FAILED; } /***************************************************************************** * * 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) { #ifdef USE_DTOA_LIBRARY char data[LXW_ATTR_32]; lxw_sprintf_dbl(data, cell->u.number); if (style_index) fprintf(self->file, "%s", range, style_index, data); else fprintf(self->file, "%s", range, data); #else if (style_index) fprintf(self->file, "%.16G", range, style_index, cell->u.number); else fprintf(self->file, "%.16G", range, cell->u.number); #endif } /* * 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 an inline rich string. Doesn't use the xml functions as an * optimization in the inner cell writing loop. */ STATIC void _write_inline_rich_string_cell(lxw_worksheet *self, char *range, int32_t style_index, lxw_cell *cell) { const char *string = cell->u.string; if (style_index) fprintf(self->file, "" "%s", range, style_index, string); else fprintf(self->file, "" "%s", range, 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_sprintf_dbl(data, 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 a formula worksheet cell with a numeric result. */ STATIC void _write_formula_str_cell(lxw_worksheet *self, lxw_cell *cell) { lxw_xml_data_element(self->file, "f", cell->u.string, NULL); lxw_xml_data_element(self->file, "v", cell->user_data2, 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_sprintf_dbl(data, 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 == 0.0) data[0] = '0'; else data[0] = '1'; 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_cell *cell_min = RB_MIN(lxw_table_cells, row->cells); lxw_cell *cell_max = RB_MAX(lxw_table_cells, row->cells); lxw_col_t span_col_min = cell_min->col_num; lxw_col_t span_col_max = cell_max->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)) { cell_min = RB_MIN(lxw_table_cells, row->cells); cell_max = RB_MAX(lxw_table_cells, row->cells); col_min = cell_min->col_num; col_max = cell_max->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; } if (cell->type == INLINE_RICH_STRING_CELL) { _write_inline_rich_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) { /* If user_data2 is set then the formula has a string result. */ if (cell->user_data2) LXW_PUSH_ATTRIBUTES_STR("t", "str"); lxw_xml_start_tag(self->file, "c", &attributes); if (cell->user_data2) _write_formula_str_cell(self, cell); else _write_formula_num_cell(self, cell); lxw_xml_end_tag(self->file, "c"); } else if (cell->type == BLANK_CELL) { if (cell->format) 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"); } else if (cell->type == DYNAMIC_ARRAY_FORMULA_CELL) { LXW_PUSH_ATTRIBUTES_STR("cm", "1"); 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); if (row->data_changed) { 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; } /* Process a header/footer image and store it in the correct slot. */ lxw_error _worksheet_set_header_footer_image(lxw_worksheet *self, const char *filename, uint8_t image_position) { FILE *image_stream; const char *description; lxw_object_properties *object_props; char *image_strings[] = { "LH", "CH", "RH", "LF", "CF", "RF" }; /* Not all slots will have image files. */ if (!filename) return LXW_NO_ERROR; /* Check that the image file exists and can be opened. */ image_stream = lxw_fopen(filename, "rb"); if (!image_stream) { LXW_WARN_FORMAT1("worksheet_set_header_opt/footer_opt(): " "file doesn't exist or can't be opened: %s.", filename); return LXW_ERROR_PARAMETER_VALIDATION; } /* Use the filename as the default description, like Excel. */ description = lxw_basename(filename); if (!description) { LXW_WARN_FORMAT1("worksheet_set_header_opt/footer_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 properties. */ object_props = calloc(1, sizeof(lxw_object_properties)); if (!object_props) { fclose(image_stream); return LXW_ERROR_MEMORY_MALLOC_FAILED; } /* Copy other options or set defaults. */ object_props->filename = lxw_strdup(filename); object_props->description = lxw_strdup(description); object_props->stream = image_stream; /* Set VML image position string based on the header/footer/position. */ object_props->image_position = lxw_strdup(image_strings[image_position]); if (_get_image_properties(object_props) == LXW_NO_ERROR) { *self->header_footer_objs[image_position] = object_props; self->has_header_vml = LXW_TRUE; fclose(image_stream); return LXW_NO_ERROR; } else { _free_object_properties(object_props); fclose(image_stream); return LXW_ERROR_IMAGE_DIMENSIONS; } } /* * 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) _worksheet_write_odd_header(self); if (self->footer) _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. */ STATIC void _worksheet_write_outline_pr(lxw_worksheet *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; if (!self->outline_changed) return; LXW_INIT_ATTRIBUTES(); if (self->outline_style) LXW_PUSH_ATTRIBUTES_STR("applyStyles", "1"); if (!self->outline_below) LXW_PUSH_ATTRIBUTES_STR("summaryBelow", "0"); if (!self->outline_right) LXW_PUSH_ATTRIBUTES_STR("summaryRight", "0"); if (!self->outline_on) LXW_PUSH_ATTRIBUTES_STR("showOutlineSymbols", "0"); lxw_xml_empty_tag(self->file, "outlinePr", &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 && !self->is_chartsheet) { return; } LXW_INIT_ATTRIBUTES(); if (self->vba_codename) LXW_PUSH_ATTRIBUTES_STR("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_filter(lxw_worksheet *self, const char *str, double num, uint8_t criteria) { struct xml_attribute_list attributes; struct xml_attribute *attribute; if (criteria == LXW_FILTER_CRITERIA_BLANKS) return; LXW_INIT_ATTRIBUTES(); if (str) LXW_PUSH_ATTRIBUTES_STR("val", str); else LXW_PUSH_ATTRIBUTES_DBL("val", num); lxw_xml_empty_tag(self->file, "filter", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element as simple equality. */ STATIC void _worksheet_write_filter_standard(lxw_worksheet *self, lxw_filter_rule_obj *filter) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); if (filter->has_blanks) { LXW_PUSH_ATTRIBUTES_STR("blank", "1"); } if (filter->type == LXW_FILTER_TYPE_SINGLE && filter->has_blanks) { lxw_xml_empty_tag(self->file, "filters", &attributes); } else { lxw_xml_start_tag(self->file, "filters", &attributes); /* Write the filter element. */ if (filter->type == LXW_FILTER_TYPE_SINGLE) { _worksheet_write_filter(self, filter->value1_string, filter->value1, filter->criteria1); } else if (filter->type == LXW_FILTER_TYPE_AND || filter->type == LXW_FILTER_TYPE_OR) { _worksheet_write_filter(self, filter->value1_string, filter->value1, filter->criteria1); _worksheet_write_filter(self, filter->value2_string, filter->value2, filter->criteria2); } lxw_xml_end_tag(self->file, "filters"); } LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_custom_filter(lxw_worksheet *self, const char *str, double num, uint8_t criteria) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); if (criteria == LXW_FILTER_CRITERIA_NOT_EQUAL_TO) LXW_PUSH_ATTRIBUTES_STR("operator", "notEqual"); if (criteria == LXW_FILTER_CRITERIA_GREATER_THAN) LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThan"); else if (criteria == LXW_FILTER_CRITERIA_GREATER_THAN_OR_EQUAL_TO) LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThanOrEqual"); else if (criteria == LXW_FILTER_CRITERIA_LESS_THAN) LXW_PUSH_ATTRIBUTES_STR("operator", "lessThan"); else if (criteria == LXW_FILTER_CRITERIA_LESS_THAN_OR_EQUAL_TO) LXW_PUSH_ATTRIBUTES_STR("operator", "lessThanOrEqual"); if (str) LXW_PUSH_ATTRIBUTES_STR("val", str); else LXW_PUSH_ATTRIBUTES_DBL("val", num); lxw_xml_empty_tag(self->file, "customFilter", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element as a list. */ STATIC void _worksheet_write_filter_list(lxw_worksheet *self, lxw_filter_rule_obj *filter) { struct xml_attribute_list attributes; struct xml_attribute *attribute; uint16_t i; LXW_INIT_ATTRIBUTES(); if (filter->has_blanks) { LXW_PUSH_ATTRIBUTES_STR("blank", "1"); } lxw_xml_start_tag(self->file, "filters", &attributes); for (i = 0; i < filter->num_list_filters; i++) { _worksheet_write_filter(self, filter->list[i], 0, 0); } lxw_xml_end_tag(self->file, "filters"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_filter_custom(lxw_worksheet *self, lxw_filter_rule_obj *filter) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); if (filter->type == LXW_FILTER_TYPE_AND) LXW_PUSH_ATTRIBUTES_STR("and", "1"); lxw_xml_start_tag(self->file, "customFilters", &attributes); /* Write the filter element. */ if (filter->type == LXW_FILTER_TYPE_SINGLE) { _worksheet_write_custom_filter(self, filter->value1_string, filter->value1, filter->criteria1); } else if (filter->type == LXW_FILTER_TYPE_AND || filter->type == LXW_FILTER_TYPE_OR) { _worksheet_write_custom_filter(self, filter->value1_string, filter->value1, filter->criteria1); _worksheet_write_custom_filter(self, filter->value2_string, filter->value2, filter->criteria2); } lxw_xml_end_tag(self->file, "customFilters"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_filter_column(lxw_worksheet *self, lxw_filter_rule_obj *filter) { struct xml_attribute_list attributes; struct xml_attribute *attribute; if (!filter) return; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("colId", filter->col_num); lxw_xml_start_tag(self->file, "filterColumn", &attributes); if (filter->list) _worksheet_write_filter_list(self, filter); else if (filter->is_custom) _worksheet_write_filter_custom(self, filter); else _worksheet_write_filter_standard(self, filter); lxw_xml_end_tag(self->file, "filterColumn"); 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]; uint16_t i; 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); if (self->autofilter.has_rules) { lxw_xml_start_tag(self->file, "autoFilter", &attributes); for (i = 0; i < self->num_filter_rules; i++) _worksheet_write_filter_column(self, self->filter_rules[i]); lxw_xml_end_tag(self->file, "autoFilter"); } else { 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, lxw_protection_obj *protect) { struct xml_attribute_list attributes; struct xml_attribute *attribute; 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->no_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 _worksheet_write_legacy_drawing(lxw_worksheet *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; if (!self->has_vml) return; else self->rel_count++; lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", self->rel_count); LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); lxw_xml_empty_tag(self->file, "legacyDrawing", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_legacy_drawing_hf(lxw_worksheet *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; if (!self->has_header_vml) return; else self->rel_count++; lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", self->rel_count); LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); lxw_xml_empty_tag(self->file, "legacyDrawingHF", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_picture(lxw_worksheet *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; if (!self->has_background_image) return; else self->rel_count++; lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", self->rel_count); LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); lxw_xml_empty_tag(self->file, "picture", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_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 _worksheet_write_drawings(lxw_worksheet *self) { if (!self->drawing) return; self->rel_count++; _worksheet_write_drawing(self, self->rel_count); } /* * Write the element for numbers. */ STATIC void _worksheet_write_formula1_num(lxw_worksheet *self, double number) { char data[LXW_ATTR_32]; lxw_sprintf_dbl(data, number); lxw_xml_data_element(self->file, "formula1", data, NULL); } /* * Write the element for strings/formulas. */ STATIC void _worksheet_write_formula1_str(lxw_worksheet *self, char *str) { lxw_xml_data_element(self->file, "formula1", str, NULL); } /* * Write the element for numbers. */ STATIC void _worksheet_write_formula2_num(lxw_worksheet *self, double number) { char data[LXW_ATTR_32]; lxw_sprintf_dbl(data, number); lxw_xml_data_element(self->file, "formula2", data, NULL); } /* * Write the element for strings/formulas. */ STATIC void _worksheet_write_formula2_str(lxw_worksheet *self, char *str) { lxw_xml_data_element(self->file, "formula2", str, NULL); } /* * Write the element. */ STATIC void _worksheet_write_data_validation(lxw_worksheet *self, lxw_data_val_obj *validation) { struct xml_attribute_list attributes; struct xml_attribute *attribute; uint8_t is_between = 0; LXW_INIT_ATTRIBUTES(); switch (validation->validate) { case LXW_VALIDATION_TYPE_INTEGER: case LXW_VALIDATION_TYPE_INTEGER_FORMULA: LXW_PUSH_ATTRIBUTES_STR("type", "whole"); break; case LXW_VALIDATION_TYPE_DECIMAL: case LXW_VALIDATION_TYPE_DECIMAL_FORMULA: LXW_PUSH_ATTRIBUTES_STR("type", "decimal"); break; case LXW_VALIDATION_TYPE_LIST: case LXW_VALIDATION_TYPE_LIST_FORMULA: LXW_PUSH_ATTRIBUTES_STR("type", "list"); break; case LXW_VALIDATION_TYPE_DATE: case LXW_VALIDATION_TYPE_DATE_FORMULA: case LXW_VALIDATION_TYPE_DATE_NUMBER: LXW_PUSH_ATTRIBUTES_STR("type", "date"); break; case LXW_VALIDATION_TYPE_TIME: case LXW_VALIDATION_TYPE_TIME_FORMULA: case LXW_VALIDATION_TYPE_TIME_NUMBER: LXW_PUSH_ATTRIBUTES_STR("type", "time"); break; case LXW_VALIDATION_TYPE_LENGTH: case LXW_VALIDATION_TYPE_LENGTH_FORMULA: LXW_PUSH_ATTRIBUTES_STR("type", "textLength"); break; case LXW_VALIDATION_TYPE_CUSTOM_FORMULA: LXW_PUSH_ATTRIBUTES_STR("type", "custom"); break; } switch (validation->criteria) { case LXW_VALIDATION_CRITERIA_EQUAL_TO: LXW_PUSH_ATTRIBUTES_STR("operator", "equal"); break; case LXW_VALIDATION_CRITERIA_NOT_EQUAL_TO: LXW_PUSH_ATTRIBUTES_STR("operator", "notEqual"); break; case LXW_VALIDATION_CRITERIA_LESS_THAN: LXW_PUSH_ATTRIBUTES_STR("operator", "lessThan"); break; case LXW_VALIDATION_CRITERIA_LESS_THAN_OR_EQUAL_TO: LXW_PUSH_ATTRIBUTES_STR("operator", "lessThanOrEqual"); break; case LXW_VALIDATION_CRITERIA_GREATER_THAN: LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThan"); break; case LXW_VALIDATION_CRITERIA_GREATER_THAN_OR_EQUAL_TO: LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThanOrEqual"); break; case LXW_VALIDATION_CRITERIA_BETWEEN: /* Between is the default for 2 formulas and isn't added. */ is_between = 1; break; case LXW_VALIDATION_CRITERIA_NOT_BETWEEN: is_between = 1; LXW_PUSH_ATTRIBUTES_STR("operator", "notBetween"); break; } if (validation->error_type == LXW_VALIDATION_ERROR_TYPE_WARNING) LXW_PUSH_ATTRIBUTES_STR("errorStyle", "warning"); if (validation->error_type == LXW_VALIDATION_ERROR_TYPE_INFORMATION) LXW_PUSH_ATTRIBUTES_STR("errorStyle", "information"); if (validation->ignore_blank) LXW_PUSH_ATTRIBUTES_INT("allowBlank", 1); if (validation->dropdown == LXW_VALIDATION_OFF) LXW_PUSH_ATTRIBUTES_INT("showDropDown", 1); if (validation->show_input) LXW_PUSH_ATTRIBUTES_INT("showInputMessage", 1); if (validation->show_error) LXW_PUSH_ATTRIBUTES_INT("showErrorMessage", 1); if (validation->error_title) LXW_PUSH_ATTRIBUTES_STR("errorTitle", validation->error_title); if (validation->error_message) LXW_PUSH_ATTRIBUTES_STR("error", validation->error_message); if (validation->input_title) LXW_PUSH_ATTRIBUTES_STR("promptTitle", validation->input_title); if (validation->input_message) LXW_PUSH_ATTRIBUTES_STR("prompt", validation->input_message); LXW_PUSH_ATTRIBUTES_STR("sqref", validation->sqref); if (validation->validate == LXW_VALIDATION_TYPE_ANY) lxw_xml_empty_tag(self->file, "dataValidation", &attributes); else lxw_xml_start_tag(self->file, "dataValidation", &attributes); /* Write the formula1 and formula2 elements. */ switch (validation->validate) { case LXW_VALIDATION_TYPE_INTEGER: case LXW_VALIDATION_TYPE_DECIMAL: case LXW_VALIDATION_TYPE_LENGTH: case LXW_VALIDATION_TYPE_DATE: case LXW_VALIDATION_TYPE_TIME: case LXW_VALIDATION_TYPE_DATE_NUMBER: case LXW_VALIDATION_TYPE_TIME_NUMBER: _worksheet_write_formula1_num(self, validation->value_number); if (is_between) _worksheet_write_formula2_num(self, validation->maximum_number); break; case LXW_VALIDATION_TYPE_INTEGER_FORMULA: case LXW_VALIDATION_TYPE_DECIMAL_FORMULA: case LXW_VALIDATION_TYPE_LENGTH_FORMULA: case LXW_VALIDATION_TYPE_DATE_FORMULA: case LXW_VALIDATION_TYPE_TIME_FORMULA: case LXW_VALIDATION_TYPE_LIST: case LXW_VALIDATION_TYPE_LIST_FORMULA: case LXW_VALIDATION_TYPE_CUSTOM_FORMULA: _worksheet_write_formula1_str(self, validation->value_formula); if (is_between) _worksheet_write_formula2_str(self, validation->maximum_formula); break; } if (validation->validate != LXW_VALIDATION_TYPE_ANY) lxw_xml_end_tag(self->file, "dataValidation"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_data_validations(lxw_worksheet *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; lxw_data_val_obj *data_validation; if (self->num_validations == 0) return; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("count", self->num_validations); lxw_xml_start_tag(self->file, "dataValidations", &attributes); STAILQ_FOREACH(data_validation, self->data_validations, list_pointers) { /* Write the dataValidation element. */ _worksheet_write_data_validation(self, data_validation); } lxw_xml_end_tag(self->file, "dataValidations"); LXW_FREE_ATTRIBUTES(); } /* * Write the element for strings. */ STATIC void _worksheet_write_formula_str(lxw_worksheet *self, char *data) { lxw_xml_data_element(self->file, "formula", data, NULL); } /* * Write the element for numbers. */ STATIC void _worksheet_write_formula_num(lxw_worksheet *self, double num) { char data[LXW_ATTR_32]; lxw_sprintf_dbl(data, num); lxw_xml_data_element(self->file, "formula", data, NULL); } /* * Write the element. */ STATIC void _worksheet_write_ext(lxw_worksheet *self, char *uri) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char xmlns_x_14[] = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("xmlns:x14", xmlns_x_14); LXW_PUSH_ATTRIBUTES_STR("uri", uri); lxw_xml_start_tag(self->file, "ext", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the dataBar extension element. */ STATIC void _worksheet_write_data_bar_ext(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { /* Create a pseudo GUID for each unique Excel 2010 data bar. */ cond_format->guid = calloc(1, LXW_GUID_LENGTH); lxw_snprintf(cond_format->guid, LXW_GUID_LENGTH, "{DA7ABA51-AAAA-BBBB-%04X-%012X}", self->index + 1, ++self->data_bar_2010_index); lxw_xml_start_tag(self->file, "extLst", NULL); _worksheet_write_ext(self, "{B025F937-C7B1-47D3-B67F-A62EFF666E3E}"); lxw_xml_data_element(self->file, "x14:id", cond_format->guid, NULL); lxw_xml_end_tag(self->file, "ext"); lxw_xml_end_tag(self->file, "extLst"); } /* * Write the element. */ STATIC void _worksheet_write_color(lxw_worksheet *self, lxw_color_t color) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char rgb[LXW_ATTR_32]; lxw_snprintf(rgb, LXW_ATTR_32, "FF%06X", color & LXW_COLOR_MASK); LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("rgb", rgb); lxw_xml_empty_tag(self->file, "color", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element for strings. */ STATIC void _worksheet_write_cfvo_str(lxw_worksheet *self, uint8_t rule_type, char *value, uint8_t data_bar_2010) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); if (rule_type == LXW_CONDITIONAL_RULE_TYPE_MINIMUM) LXW_PUSH_ATTRIBUTES_STR("type", "min"); else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_NUMBER) LXW_PUSH_ATTRIBUTES_STR("type", "num"); else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_PERCENT) LXW_PUSH_ATTRIBUTES_STR("type", "percent"); else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_PERCENTILE) LXW_PUSH_ATTRIBUTES_STR("type", "percentile"); else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_FORMULA) LXW_PUSH_ATTRIBUTES_STR("type", "formula"); else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) LXW_PUSH_ATTRIBUTES_STR("type", "max"); if (!data_bar_2010 || (rule_type != LXW_CONDITIONAL_RULE_TYPE_MINIMUM && rule_type != LXW_CONDITIONAL_RULE_TYPE_MAXIMUM)) LXW_PUSH_ATTRIBUTES_STR("val", value); lxw_xml_empty_tag(self->file, "cfvo", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element for numbers. */ STATIC void _worksheet_write_cfvo_num(lxw_worksheet *self, uint8_t rule_type, double value, uint8_t data_bar_2010) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); if (rule_type == LXW_CONDITIONAL_RULE_TYPE_MINIMUM) LXW_PUSH_ATTRIBUTES_STR("type", "min"); else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_NUMBER) LXW_PUSH_ATTRIBUTES_STR("type", "num"); else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_PERCENT) LXW_PUSH_ATTRIBUTES_STR("type", "percent"); else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_PERCENTILE) LXW_PUSH_ATTRIBUTES_STR("type", "percentile"); else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_FORMULA) LXW_PUSH_ATTRIBUTES_STR("type", "formula"); else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) LXW_PUSH_ATTRIBUTES_STR("type", "max"); if (!data_bar_2010 || (rule_type != LXW_CONDITIONAL_RULE_TYPE_MINIMUM && rule_type != LXW_CONDITIONAL_RULE_TYPE_MAXIMUM)) LXW_PUSH_ATTRIBUTES_DBL("val", value); lxw_xml_empty_tag(self->file, "cfvo", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_icon_set(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char *icon_set[] = { "3Arrows", "3ArrowsGray", "3Flags", "3TrafficLights", "3TrafficLights2", "3Signs", "3Symbols", "3Symbols2", "4Arrows", "4ArrowsGray", "4RedToBlack", "4Rating", "4TrafficLights", "5Arrows", "5ArrowsGray", "5Rating", "5Quarters", }; uint8_t percent = LXW_CONDITIONAL_RULE_TYPE_PERCENT; uint8_t style = cond_format->icon_style; LXW_INIT_ATTRIBUTES(); if (style != LXW_CONDITIONAL_ICONS_3_TRAFFIC_LIGHTS_UNRIMMED) LXW_PUSH_ATTRIBUTES_STR("iconSet", icon_set[style]); if (cond_format->reverse_icons == LXW_TRUE) LXW_PUSH_ATTRIBUTES_STR("reverse", "1"); if (cond_format->icons_only == LXW_TRUE) LXW_PUSH_ATTRIBUTES_STR("showValue", "0"); lxw_xml_start_tag(self->file, "iconSet", &attributes); if (style < LXW_CONDITIONAL_ICONS_4_ARROWS_COLORED) { _worksheet_write_cfvo_num(self, percent, 0, LXW_FALSE); _worksheet_write_cfvo_num(self, percent, 33, LXW_FALSE); _worksheet_write_cfvo_num(self, percent, 67, LXW_FALSE); } if (style >= LXW_CONDITIONAL_ICONS_4_ARROWS_COLORED && style < LXW_CONDITIONAL_ICONS_5_ARROWS_COLORED) { _worksheet_write_cfvo_num(self, percent, 0, LXW_FALSE); _worksheet_write_cfvo_num(self, percent, 25, LXW_FALSE); _worksheet_write_cfvo_num(self, percent, 50, LXW_FALSE); _worksheet_write_cfvo_num(self, percent, 75, LXW_FALSE); } if (style >= LXW_CONDITIONAL_ICONS_5_ARROWS_COLORED && style <= LXW_CONDITIONAL_ICONS_5_QUARTERS) { _worksheet_write_cfvo_num(self, percent, 0, LXW_FALSE); _worksheet_write_cfvo_num(self, percent, 20, LXW_FALSE); _worksheet_write_cfvo_num(self, percent, 40, LXW_FALSE); _worksheet_write_cfvo_num(self, percent, 60, LXW_FALSE); _worksheet_write_cfvo_num(self, percent, 80, LXW_FALSE); } LXW_FREE_ATTRIBUTES(); } /* * Write the element for data bar rules. */ STATIC void _worksheet_write_cf_rule_icons(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); lxw_xml_start_tag(self->file, "cfRule", &attributes); _worksheet_write_icon_set(self, cond_format); lxw_xml_end_tag(self->file, "iconSet"); lxw_xml_end_tag(self->file, "cfRule"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_data_bar(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); if (cond_format->bar_only) LXW_PUSH_ATTRIBUTES_STR("showValue", "0"); lxw_xml_start_tag(self->file, "dataBar", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element for data bar rules. */ STATIC void _worksheet_write_cf_rule_data_bar(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); lxw_xml_start_tag(self->file, "cfRule", &attributes); _worksheet_write_data_bar(self, cond_format); if (cond_format->min_value_string) { _worksheet_write_cfvo_str(self, cond_format->min_rule_type, cond_format->min_value_string, cond_format->data_bar_2010); } else { _worksheet_write_cfvo_num(self, cond_format->min_rule_type, cond_format->min_value, cond_format->data_bar_2010); } if (cond_format->max_value_string) { _worksheet_write_cfvo_str(self, cond_format->max_rule_type, cond_format->max_value_string, cond_format->data_bar_2010); } else { _worksheet_write_cfvo_num(self, cond_format->max_rule_type, cond_format->max_value, cond_format->data_bar_2010); } _worksheet_write_color(self, cond_format->bar_color); lxw_xml_end_tag(self->file, "dataBar"); if (cond_format->data_bar_2010) _worksheet_write_data_bar_ext(self, cond_format); lxw_xml_end_tag(self->file, "cfRule"); LXW_FREE_ATTRIBUTES(); } /* * Write the element for 2 and 3 color scale rules. */ STATIC void _worksheet_write_cf_rule_color_scale(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); lxw_xml_start_tag(self->file, "cfRule", &attributes); lxw_xml_start_tag(self->file, "colorScale", NULL); if (cond_format->min_value_string) { _worksheet_write_cfvo_str(self, cond_format->min_rule_type, cond_format->min_value_string, LXW_FALSE); } else { _worksheet_write_cfvo_num(self, cond_format->min_rule_type, cond_format->min_value, LXW_FALSE); } if (cond_format->type == LXW_CONDITIONAL_3_COLOR_SCALE) { if (cond_format->mid_value_string) { _worksheet_write_cfvo_str(self, cond_format->mid_rule_type, cond_format->mid_value_string, LXW_FALSE); } else { _worksheet_write_cfvo_num(self, cond_format->mid_rule_type, cond_format->mid_value, LXW_FALSE); } } if (cond_format->max_value_string) { _worksheet_write_cfvo_str(self, cond_format->max_rule_type, cond_format->max_value_string, LXW_FALSE); } else { _worksheet_write_cfvo_num(self, cond_format->max_rule_type, cond_format->max_value, LXW_FALSE); } _worksheet_write_color(self, cond_format->min_color); if (cond_format->type == LXW_CONDITIONAL_3_COLOR_SCALE) _worksheet_write_color(self, cond_format->mid_color); _worksheet_write_color(self, cond_format->max_color); lxw_xml_end_tag(self->file, "colorScale"); lxw_xml_end_tag(self->file, "cfRule"); LXW_FREE_ATTRIBUTES(); } /* * Write the element for formula rules. */ STATIC void _worksheet_write_cf_rule_formula(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); if (cond_format->dxf_index != LXW_PROPERTY_UNSET) LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); if (cond_format->stop_if_true) LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); lxw_xml_start_tag(self->file, "cfRule", &attributes); _worksheet_write_formula_str(self, cond_format->min_value_string); lxw_xml_end_tag(self->file, "cfRule"); LXW_FREE_ATTRIBUTES(); } /* * Write the element for top and bottom rules. */ STATIC void _worksheet_write_cf_rule_top(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); if (cond_format->dxf_index != LXW_PROPERTY_UNSET) LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); if (cond_format->stop_if_true) LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); if (cond_format->criteria == LXW_CONDITIONAL_CRITERIA_TOP_OR_BOTTOM_PERCENT) LXW_PUSH_ATTRIBUTES_INT("percent", 1); if (cond_format->type == LXW_CONDITIONAL_TYPE_BOTTOM) LXW_PUSH_ATTRIBUTES_INT("bottom", 1); /* Rank must be an int in the range 1-1000 . */ if (cond_format->min_value < 1.0 || cond_format->min_value > 1000.0) LXW_PUSH_ATTRIBUTES_DBL("rank", 10); else LXW_PUSH_ATTRIBUTES_DBL("rank", (uint16_t) cond_format->min_value); lxw_xml_empty_tag(self->file, "cfRule", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element for unique/duplicate rules. */ STATIC void _worksheet_write_cf_rule_duplicate(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); /* Set the attributes common to all rule types. */ if (cond_format->dxf_index != LXW_PROPERTY_UNSET) LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); lxw_xml_empty_tag(self->file, "cfRule", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element for averages rules. */ STATIC void _worksheet_write_cf_rule_average(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; uint8_t criteria = cond_format->criteria; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); if (cond_format->dxf_index != LXW_PROPERTY_UNSET) LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); if (cond_format->stop_if_true) LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); if (criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_BELOW || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_BELOW_OR_EQUAL || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_1_STD_DEV_BELOW || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_2_STD_DEV_BELOW || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_BELOW) LXW_PUSH_ATTRIBUTES_INT("aboveAverage", 0); if (criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_ABOVE_OR_EQUAL || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_BELOW_OR_EQUAL) LXW_PUSH_ATTRIBUTES_INT("equalAverage", 1); if (criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_1_STD_DEV_ABOVE || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_1_STD_DEV_BELOW) LXW_PUSH_ATTRIBUTES_INT("stdDev", 1); if (criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_2_STD_DEV_ABOVE || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_2_STD_DEV_BELOW) LXW_PUSH_ATTRIBUTES_INT("stdDev", 2); if (criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_ABOVE || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_BELOW) LXW_PUSH_ATTRIBUTES_INT("stdDev", 3); lxw_xml_empty_tag(self->file, "cfRule", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element for time_period rules. */ STATIC void _worksheet_write_cf_rule_time_period(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char formula[LXW_MAX_ATTRIBUTE_LENGTH]; uint8_t pos; uint8_t criteria = cond_format->criteria; char *first_cell = cond_format->first_cell; char *time_periods[] = { "yesterday", "today", "tomorrow", "last7Days", "lastWeek", "thisWeek", "nextWeek", "lastMonth", "thisMonth", "nextMonth", }; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); if (cond_format->dxf_index != LXW_PROPERTY_UNSET) LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); pos = criteria - LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_YESTERDAY; LXW_PUSH_ATTRIBUTES_STR("timePeriod", time_periods[pos]); if (cond_format->stop_if_true) LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); lxw_xml_start_tag(self->file, "cfRule", &attributes); if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_YESTERDAY) { lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "FLOOR(%s,1)=TODAY()-1", first_cell); _worksheet_write_formula_str(self, formula); } else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_TODAY) { lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "FLOOR(%s,1)=TODAY()", first_cell); _worksheet_write_formula_str(self, formula); } else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_TOMORROW) { lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "FLOOR(%s,1)=TODAY()+1", first_cell); _worksheet_write_formula_str(self, formula); } else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_7_DAYS) { lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "AND(TODAY()-FLOOR(%s,1)<=6,FLOOR(%s,1)<=TODAY())", first_cell, first_cell); _worksheet_write_formula_str(self, formula); } else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_WEEK) { lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "AND(TODAY()-ROUNDDOWN(%s,0)>=(WEEKDAY(TODAY()))," "TODAY()-ROUNDDOWN(%s,0)<(WEEKDAY(TODAY())+7))", first_cell, first_cell); _worksheet_write_formula_str(self, formula); } else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_THIS_WEEK) { lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "AND(TODAY()-ROUNDDOWN(%s,0)<=WEEKDAY(TODAY())-1," "ROUNDDOWN(%s,0)-TODAY()<=7-WEEKDAY(TODAY()))", first_cell, first_cell); _worksheet_write_formula_str(self, formula); } else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_NEXT_WEEK) { lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "AND(ROUNDDOWN(%s,0)-TODAY()>(7-WEEKDAY(TODAY()))," "ROUNDDOWN(%s,0)-TODAY()<(15-WEEKDAY(TODAY())))", first_cell, first_cell); _worksheet_write_formula_str(self, formula); } else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_MONTH) { lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "AND(MONTH(%s)=MONTH(TODAY())-1,OR(YEAR(%s)=YEAR(" "TODAY()),AND(MONTH(%s)=1,YEAR(A1)=YEAR(TODAY())-1)))", first_cell, first_cell, first_cell); _worksheet_write_formula_str(self, formula); } else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_THIS_MONTH) { lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "AND(MONTH(%s)=MONTH(TODAY()),YEAR(%s)=YEAR(TODAY()))", first_cell, first_cell); _worksheet_write_formula_str(self, formula); } else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_NEXT_MONTH) { lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "AND(MONTH(%s)=MONTH(TODAY())+1,OR(YEAR(%s)=YEAR(" "TODAY()),AND(MONTH(%s)=12,YEAR(%s)=YEAR(TODAY())+1)))", first_cell, first_cell, first_cell, first_cell); _worksheet_write_formula_str(self, formula); } lxw_xml_end_tag(self->file, "cfRule"); LXW_FREE_ATTRIBUTES(); } /* * Write the element for blanks/no_blanks, errors/no_errors rules. */ STATIC void _worksheet_write_cf_rule_blanks(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char formula[LXW_ATTR_32]; uint8_t type = cond_format->type; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); if (cond_format->dxf_index != LXW_PROPERTY_UNSET) LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); if (cond_format->stop_if_true) LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); lxw_xml_start_tag(self->file, "cfRule", &attributes); if (type == LXW_CONDITIONAL_TYPE_BLANKS) { lxw_snprintf(formula, LXW_ATTR_32, "LEN(TRIM(%s))=0", cond_format->first_cell); _worksheet_write_formula_str(self, formula); } else if (type == LXW_CONDITIONAL_TYPE_NO_BLANKS) { lxw_snprintf(formula, LXW_ATTR_32, "LEN(TRIM(%s))>0", cond_format->first_cell); _worksheet_write_formula_str(self, formula); } else if (type == LXW_CONDITIONAL_TYPE_ERRORS) { lxw_snprintf(formula, LXW_ATTR_32, "ISERROR(%s)", cond_format->first_cell); _worksheet_write_formula_str(self, formula); } else if (type == LXW_CONDITIONAL_TYPE_NO_ERRORS) { lxw_snprintf(formula, LXW_ATTR_32, "NOT(ISERROR(%s))", cond_format->first_cell); _worksheet_write_formula_str(self, formula); } lxw_xml_end_tag(self->file, "cfRule"); LXW_FREE_ATTRIBUTES(); } /* * Write the element for text rules. */ STATIC void _worksheet_write_cf_rule_text(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; uint8_t pos; char formula[LXW_ATTR_32 * 2]; char *operators[] = { "containsText", "notContains", "beginsWith", "endsWith", }; uint8_t criteria = cond_format->criteria; LXW_INIT_ATTRIBUTES(); if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_CONTAINING) LXW_PUSH_ATTRIBUTES_STR("type", "containsText"); else if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_NOT_CONTAINING) LXW_PUSH_ATTRIBUTES_STR("type", "notContainsText"); else if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_BEGINS_WITH) LXW_PUSH_ATTRIBUTES_STR("type", "beginsWith"); else if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_ENDS_WITH) LXW_PUSH_ATTRIBUTES_STR("type", "endsWith"); if (cond_format->dxf_index != LXW_PROPERTY_UNSET) LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); if (cond_format->stop_if_true) LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); pos = criteria - LXW_CONDITIONAL_CRITERIA_TEXT_CONTAINING; LXW_PUSH_ATTRIBUTES_STR("operator", operators[pos]); LXW_PUSH_ATTRIBUTES_STR("text", cond_format->min_value_string); lxw_xml_start_tag(self->file, "cfRule", &attributes); if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_CONTAINING) { lxw_snprintf(formula, LXW_ATTR_32 * 2, "NOT(ISERROR(SEARCH(\"%s\",%s)))", cond_format->min_value_string, cond_format->first_cell); _worksheet_write_formula_str(self, formula); } else if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_NOT_CONTAINING) { lxw_snprintf(formula, LXW_ATTR_32 * 2, "ISERROR(SEARCH(\"%s\",%s))", cond_format->min_value_string, cond_format->first_cell); _worksheet_write_formula_str(self, formula); } else if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_BEGINS_WITH) { lxw_snprintf(formula, LXW_ATTR_32 * 2, "LEFT(%s,%d)=\"%s\"", cond_format->first_cell, (uint16_t) strlen(cond_format->min_value_string), cond_format->min_value_string); _worksheet_write_formula_str(self, formula); } else if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_ENDS_WITH) { lxw_snprintf(formula, LXW_ATTR_32 * 2, "RIGHT(%s,%d)=\"%s\"", cond_format->first_cell, (uint16_t) strlen(cond_format->min_value_string), cond_format->min_value_string); _worksheet_write_formula_str(self, formula); } lxw_xml_end_tag(self->file, "cfRule"); LXW_FREE_ATTRIBUTES(); } /* * Write the element for cell rules. */ STATIC void _worksheet_write_cf_rule_cell(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char *operators[] = { "none", "equal", "notEqual", "greaterThan", "lessThan", "greaterThanOrEqual", "lessThanOrEqual", "between", "notBetween", }; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); if (cond_format->dxf_index != LXW_PROPERTY_UNSET) LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); if (cond_format->stop_if_true) LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); LXW_PUSH_ATTRIBUTES_STR("operator", operators[cond_format->criteria]); lxw_xml_start_tag(self->file, "cfRule", &attributes); if (cond_format->min_value_string) _worksheet_write_formula_str(self, cond_format->min_value_string); else _worksheet_write_formula_num(self, cond_format->min_value); if (cond_format->has_max) { if (cond_format->max_value_string) _worksheet_write_formula_str(self, cond_format->max_value_string); else _worksheet_write_formula_num(self, cond_format->max_value); } lxw_xml_end_tag(self->file, "cfRule"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_cf_rule(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { if (cond_format->type == LXW_CONDITIONAL_TYPE_CELL) { _worksheet_write_cf_rule_cell(self, cond_format); } else if (cond_format->type == LXW_CONDITIONAL_TYPE_TEXT) { _worksheet_write_cf_rule_text(self, cond_format); } else if (cond_format->type == LXW_CONDITIONAL_TYPE_TIME_PERIOD) { _worksheet_write_cf_rule_time_period(self, cond_format); } else if (cond_format->type == LXW_CONDITIONAL_TYPE_DUPLICATE || cond_format->type == LXW_CONDITIONAL_TYPE_UNIQUE) { _worksheet_write_cf_rule_duplicate(self, cond_format); } else if (cond_format->type == LXW_CONDITIONAL_TYPE_AVERAGE) { _worksheet_write_cf_rule_average(self, cond_format); } else if (cond_format->type == LXW_CONDITIONAL_TYPE_TOP || cond_format->type == LXW_CONDITIONAL_TYPE_BOTTOM) { _worksheet_write_cf_rule_top(self, cond_format); } else if (cond_format->type == LXW_CONDITIONAL_TYPE_BLANKS || cond_format->type == LXW_CONDITIONAL_TYPE_NO_BLANKS || cond_format->type == LXW_CONDITIONAL_TYPE_ERRORS || cond_format->type == LXW_CONDITIONAL_TYPE_NO_ERRORS) { _worksheet_write_cf_rule_blanks(self, cond_format); } else if (cond_format->type == LXW_CONDITIONAL_TYPE_FORMULA) { _worksheet_write_cf_rule_formula(self, cond_format); } else if (cond_format->type == LXW_CONDITIONAL_2_COLOR_SCALE || cond_format->type == LXW_CONDITIONAL_3_COLOR_SCALE) { _worksheet_write_cf_rule_color_scale(self, cond_format); } else if (cond_format->type == LXW_CONDITIONAL_DATA_BAR) { _worksheet_write_cf_rule_data_bar(self, cond_format); } else if (cond_format->type == LXW_CONDITIONAL_TYPE_ICON_SETS) { _worksheet_write_cf_rule_icons(self, cond_format); } } /* * Write the element. */ STATIC void _worksheet_write_conditional_formatting(lxw_worksheet *self, lxw_cond_format_hash_element *element) { struct xml_attribute_list attributes; struct xml_attribute *attribute; lxw_cond_format_obj *cond_format; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("sqref", element->sqref); lxw_xml_start_tag(self->file, "conditionalFormatting", &attributes); STAILQ_FOREACH(cond_format, element->cond_formats, list_pointers) { /* Write the cfRule element. */ _worksheet_write_cf_rule(self, cond_format); } lxw_xml_end_tag(self->file, "conditionalFormatting"); LXW_FREE_ATTRIBUTES(); } /* * Write the conditional formatting> elements. */ STATIC void _worksheet_write_conditional_formats(lxw_worksheet *self) { lxw_cond_format_hash_element *element; lxw_cond_format_hash_element *next_element; for (element = RB_MIN(lxw_cond_format_hash, self->conditional_formats); element; element = next_element) { _worksheet_write_conditional_formatting(self, element); next_element = RB_NEXT(lxw_cond_format_hash, self->conditional_formats, element); } } /* * Write the elements for data bar conditional formats. */ STATIC void _worksheet_write_x14_color(lxw_worksheet *self, char *type, lxw_color_t color) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char rgb[LXW_ATTR_32]; lxw_snprintf(rgb, LXW_ATTR_32, "FF%06X", color & LXW_COLOR_MASK); LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("rgb", rgb); lxw_xml_empty_tag(self->file, type, &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_x14_cfvo(lxw_worksheet *self, uint8_t rule_type, double number, char *string) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char data[LXW_ATTR_32]; uint8_t has_value = LXW_FALSE; LXW_INIT_ATTRIBUTES(); if (!string) lxw_sprintf_dbl(data, number); if (rule_type == LXW_CONDITIONAL_RULE_TYPE_AUTO_MIN) { LXW_PUSH_ATTRIBUTES_STR("type", "autoMin"); has_value = LXW_FALSE; } else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_MINIMUM) { LXW_PUSH_ATTRIBUTES_STR("type", "min"); has_value = LXW_FALSE; } else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_NUMBER) { LXW_PUSH_ATTRIBUTES_STR("type", "num"); has_value = LXW_TRUE; } else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_PERCENT) { LXW_PUSH_ATTRIBUTES_STR("type", "percent"); has_value = LXW_TRUE; } else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_PERCENTILE) { LXW_PUSH_ATTRIBUTES_STR("type", "percentile"); has_value = LXW_TRUE; } else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_FORMULA) { LXW_PUSH_ATTRIBUTES_STR("type", "formula"); has_value = LXW_TRUE; } else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) { LXW_PUSH_ATTRIBUTES_STR("type", "max"); has_value = LXW_FALSE; } else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_AUTO_MAX) { LXW_PUSH_ATTRIBUTES_STR("type", "autoMax"); has_value = LXW_FALSE; } if (has_value) { lxw_xml_start_tag(self->file, "x14:cfvo", &attributes); if (string) lxw_xml_data_element(self->file, "xm:f", string, NULL); else lxw_xml_data_element(self->file, "xm:f", data, NULL); lxw_xml_end_tag(self->file, "x14:cfvo"); } else { lxw_xml_empty_tag(self->file, "x14:cfvo", &attributes); } LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_x14_data_bar(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char min_length[] = "0"; char max_length[] = "100"; char border[] = "1"; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("minLength", min_length); LXW_PUSH_ATTRIBUTES_STR("maxLength", max_length); if (!cond_format->bar_no_border) LXW_PUSH_ATTRIBUTES_STR("border", border); if (cond_format->bar_solid) LXW_PUSH_ATTRIBUTES_STR("gradient", "0"); if (cond_format->bar_direction == LXW_CONDITIONAL_BAR_DIRECTION_RIGHT_TO_LEFT) LXW_PUSH_ATTRIBUTES_STR("direction", "rightToLeft"); if (cond_format->bar_direction == LXW_CONDITIONAL_BAR_DIRECTION_LEFT_TO_RIGHT) LXW_PUSH_ATTRIBUTES_STR("direction", "leftToRight"); if (cond_format->bar_negative_color_same) LXW_PUSH_ATTRIBUTES_STR("negativeBarColorSameAsPositive", "1"); if (!cond_format->bar_no_border && !cond_format->bar_negative_border_color_same) LXW_PUSH_ATTRIBUTES_STR("negativeBarBorderColorSameAsPositive", "0"); if (cond_format->bar_axis_position == LXW_CONDITIONAL_BAR_AXIS_MIDPOINT) LXW_PUSH_ATTRIBUTES_STR("axisPosition", "middle"); if (cond_format->bar_axis_position == LXW_CONDITIONAL_BAR_AXIS_NONE) LXW_PUSH_ATTRIBUTES_STR("axisPosition", "none"); lxw_xml_start_tag(self->file, "x14:dataBar", &attributes); if (cond_format->auto_min) cond_format->min_rule_type = LXW_CONDITIONAL_RULE_TYPE_AUTO_MIN; _worksheet_write_x14_cfvo(self, cond_format->min_rule_type, cond_format->min_value, cond_format->min_value_string); if (cond_format->auto_max) cond_format->max_rule_type = LXW_CONDITIONAL_RULE_TYPE_AUTO_MAX; _worksheet_write_x14_cfvo(self, cond_format->max_rule_type, cond_format->max_value, cond_format->max_value_string); if (!cond_format->bar_no_border) _worksheet_write_x14_color(self, "x14:borderColor", cond_format->bar_border_color); if (!cond_format->bar_negative_color_same) _worksheet_write_x14_color(self, "x14:negativeFillColor", cond_format->bar_negative_color); if (!cond_format->bar_no_border && !cond_format->bar_negative_border_color_same) _worksheet_write_x14_color(self, "x14:negativeBorderColor", cond_format->bar_negative_border_color); if (cond_format->bar_axis_position != LXW_CONDITIONAL_BAR_AXIS_NONE) _worksheet_write_x14_color(self, "x14:axisColor", cond_format->bar_axis_color); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_x14_cf_rule(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("type", "dataBar"); LXW_PUSH_ATTRIBUTES_STR("id", cond_format->guid); lxw_xml_start_tag(self->file, "x14:cfRule", &attributes); /* Write the x14:dataBar element. */ _worksheet_write_x14_data_bar(self, cond_format); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_xm_sqref(lxw_worksheet *self, lxw_cond_format_obj *cond_format) { lxw_xml_data_element(self->file, "xm:sqref", cond_format->sqref, NULL); } /* * Write the element. */ STATIC void _worksheet_write_conditional_formatting_2010(lxw_worksheet *self, lxw_cond_format_hash_element *element) { struct xml_attribute_list attributes; struct xml_attribute *attribute; lxw_cond_format_obj *cond_format; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("xmlns:xm", "http://schemas.microsoft.com/office/excel/2006/main"); STAILQ_FOREACH(cond_format, element->cond_formats, list_pointers) { if (!cond_format->data_bar_2010) continue; lxw_xml_start_tag(self->file, "x14:conditionalFormatting", &attributes); _worksheet_write_x14_cf_rule(self, cond_format); lxw_xml_end_tag(self->file, "x14:dataBar"); lxw_xml_end_tag(self->file, "x14:cfRule"); _worksheet_write_xm_sqref(self, cond_format); lxw_xml_end_tag(self->file, "x14:conditionalFormatting"); } LXW_FREE_ATTRIBUTES(); } /* * Write the element for Excel 2010 conditional formatting data bars. */ STATIC void _worksheet_write_ext_list_data_bars(lxw_worksheet *self) { lxw_cond_format_hash_element *element; lxw_cond_format_hash_element *next_element; _worksheet_write_ext(self, "{78C0D931-6437-407d-A8EE-F0AAD7539E65}"); lxw_xml_start_tag(self->file, "x14:conditionalFormattings", NULL); for (element = RB_MIN(lxw_cond_format_hash, self->conditional_formats); element; element = next_element) { _worksheet_write_conditional_formatting_2010(self, element); next_element = RB_NEXT(lxw_cond_format_hash, self->conditional_formats, element); } lxw_xml_end_tag(self->file, "x14:conditionalFormattings"); lxw_xml_end_tag(self->file, "ext"); } /* * Write the element. */ STATIC void _worksheet_write_ext_list(lxw_worksheet *self) { if (self->data_bar_2010_index == 0) return; lxw_xml_start_tag(self->file, "extLst", NULL); _worksheet_write_ext_list_data_bars(self); lxw_xml_end_tag(self->file, "extLst"); } /* * Write the element. */ STATIC void _worksheet_write_ignored_error(lxw_worksheet *self, char *ignore_error, char *range) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("sqref", range); LXW_PUSH_ATTRIBUTES_STR(ignore_error, "1"); lxw_xml_empty_tag(self->file, "ignoredError", &attributes); LXW_FREE_ATTRIBUTES(); } lxw_error _validate_conditional_icons(lxw_conditional_format *user) { if (user->icon_style > LXW_CONDITIONAL_ICONS_5_QUARTERS) { LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " "For type = LXW_CONDITIONAL_TYPE_ICON_SETS, " "invalid icon_style (%d).", user->icon_style); return LXW_ERROR_PARAMETER_VALIDATION; } else { return LXW_NO_ERROR; } } lxw_error _validate_conditional_data_bar(lxw_worksheet *self, lxw_cond_format_obj *cond_format, lxw_conditional_format *user_options) { uint8_t min_rule_type = user_options->min_rule_type; uint8_t max_rule_type = user_options->max_rule_type; if (user_options->data_bar_2010 || user_options->bar_solid || user_options->bar_no_border || user_options->bar_direction || user_options->bar_axis_position || user_options->bar_negative_color_same || user_options->bar_negative_border_color_same || user_options->bar_negative_color || user_options->bar_border_color || user_options->bar_negative_border_color || user_options->bar_axis_color) { cond_format->data_bar_2010 = LXW_TRUE; self->excel_version = 2010; } if (min_rule_type > LXW_CONDITIONAL_RULE_TYPE_MINIMUM && min_rule_type < LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) { cond_format->min_rule_type = min_rule_type; cond_format->min_value = user_options->min_value; cond_format->min_value_string = lxw_strdup_formula(user_options->min_value_string); } else { cond_format->min_rule_type = LXW_CONDITIONAL_RULE_TYPE_MINIMUM; cond_format->min_value = 0; } if (max_rule_type > LXW_CONDITIONAL_RULE_TYPE_MINIMUM && max_rule_type < LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) { cond_format->max_rule_type = max_rule_type; cond_format->max_value = user_options->max_value; cond_format->max_value_string = lxw_strdup_formula(user_options->max_value_string); } else { cond_format->max_rule_type = LXW_CONDITIONAL_RULE_TYPE_MAXIMUM; cond_format->max_value = 0; } if (cond_format->data_bar_2010) { if (min_rule_type == LXW_CONDITIONAL_RULE_TYPE_NONE) cond_format->auto_min = LXW_TRUE; if (max_rule_type == LXW_CONDITIONAL_RULE_TYPE_NONE) cond_format->auto_max = LXW_TRUE; } cond_format->bar_only = user_options->bar_only; cond_format->bar_solid = user_options->bar_solid; cond_format->bar_no_border = user_options->bar_no_border; cond_format->bar_direction = user_options->bar_direction; cond_format->bar_axis_position = user_options->bar_axis_position; cond_format->bar_negative_color_same = user_options->bar_negative_color_same; cond_format->bar_negative_border_color_same = user_options->bar_negative_border_color_same; if (user_options->bar_color != LXW_COLOR_UNSET) cond_format->bar_color = user_options->bar_color; else cond_format->bar_color = 0x638EC6; if (user_options->bar_negative_color != LXW_COLOR_UNSET) cond_format->bar_negative_color = user_options->bar_negative_color; else cond_format->bar_negative_color = 0xFF0000; if (user_options->bar_border_color != LXW_COLOR_UNSET) cond_format->bar_border_color = user_options->bar_border_color; else cond_format->bar_border_color = cond_format->bar_color; if (user_options->bar_negative_border_color != LXW_COLOR_UNSET) cond_format->bar_negative_border_color = user_options->bar_negative_border_color; else cond_format->bar_negative_border_color = 0xFF0000; if (user_options->bar_axis_color != LXW_COLOR_UNSET) cond_format->bar_axis_color = user_options->bar_axis_color; else cond_format->bar_axis_color = 0x000000; return LXW_NO_ERROR; } lxw_error _validate_conditional_scale(lxw_cond_format_obj *cond_format, lxw_conditional_format *user_options) { uint8_t min_rule_type = user_options->min_rule_type; uint8_t mid_rule_type = user_options->mid_rule_type; uint8_t max_rule_type = user_options->max_rule_type; if (min_rule_type > LXW_CONDITIONAL_RULE_TYPE_MINIMUM && min_rule_type < LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) { cond_format->min_rule_type = min_rule_type; cond_format->min_value = user_options->min_value; cond_format->min_value_string = lxw_strdup_formula(user_options->min_value_string); } else { cond_format->min_rule_type = LXW_CONDITIONAL_RULE_TYPE_MINIMUM; cond_format->min_value = 0; } if (max_rule_type > LXW_CONDITIONAL_RULE_TYPE_MINIMUM && max_rule_type < LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) { cond_format->max_rule_type = max_rule_type; cond_format->max_value = user_options->max_value; cond_format->max_value_string = lxw_strdup_formula(user_options->max_value_string); } else { cond_format->max_rule_type = LXW_CONDITIONAL_RULE_TYPE_MAXIMUM; cond_format->max_value = 0; } if (cond_format->type == LXW_CONDITIONAL_3_COLOR_SCALE) { if (mid_rule_type > LXW_CONDITIONAL_RULE_TYPE_MINIMUM && mid_rule_type < LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) { cond_format->mid_rule_type = mid_rule_type; cond_format->mid_value = user_options->mid_value; cond_format->mid_value_string = lxw_strdup_formula(user_options->mid_value_string); } else { cond_format->mid_rule_type = LXW_CONDITIONAL_RULE_TYPE_PERCENTILE; cond_format->mid_value = 50; } } if (user_options->min_color != LXW_COLOR_UNSET) cond_format->min_color = user_options->min_color; else cond_format->min_color = 0xFF7128; if (user_options->max_color != LXW_COLOR_UNSET) cond_format->max_color = user_options->max_color; else cond_format->max_color = 0xFFEF9C; if (cond_format->type == LXW_CONDITIONAL_3_COLOR_SCALE) { if (user_options->min_color == LXW_COLOR_UNSET) cond_format->min_color = 0xF8696B; if (user_options->mid_color != LXW_COLOR_UNSET) cond_format->mid_color = user_options->mid_color; else cond_format->mid_color = 0xFFEB84; if (user_options->max_color == LXW_COLOR_UNSET) cond_format->max_color = 0x63BE7B; } return LXW_NO_ERROR; } lxw_error _validate_conditional_top(lxw_cond_format_obj *cond_format, lxw_conditional_format *user_options) { /* Restrict the range of rank values to Excel's allowed range. */ if (user_options->criteria == LXW_CONDITIONAL_CRITERIA_TOP_OR_BOTTOM_PERCENT) { if (user_options->value < 0.0 || user_options->value > 100.0) { LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " "For type = LXW_CONDITIONAL_TYPE_TOP/BOTTOM, " "top/bottom percent (%g%%) must by in range 0-100", user_options->value); return LXW_ERROR_PARAMETER_VALIDATION; } } else { if (user_options->value < 1.0 || user_options->value > 1000.0) { LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " "For type = LXW_CONDITIONAL_TYPE_TOP/BOTTOM, " "top/bottom items (%g) must by in range 1-1000", user_options->value); return LXW_ERROR_PARAMETER_VALIDATION; } } cond_format->min_value = (uint16_t) user_options->value; return LXW_NO_ERROR; } lxw_error _validate_conditional_average(lxw_conditional_format *user) { if (user->criteria < LXW_CONDITIONAL_CRITERIA_AVERAGE_ABOVE || user->criteria > LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_BELOW) { LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " "For type = LXW_CONDITIONAL_TYPE_AVERAGE, " "invalid criteria value (%d).", user->criteria); return LXW_ERROR_PARAMETER_VALIDATION; } else { return LXW_NO_ERROR; } } lxw_error _validate_conditional_time_period(lxw_conditional_format *user) { if (user->criteria < LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_YESTERDAY || user->criteria > LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_NEXT_MONTH) { LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " "For type = LXW_CONDITIONAL_TYPE_TIME_PERIOD, " "invalid criteria value (%d).", user->criteria); return LXW_ERROR_PARAMETER_VALIDATION; } else { return LXW_NO_ERROR; } } lxw_error _validate_conditional_text(lxw_cond_format_obj *cond_format, lxw_conditional_format *user_options) { if (!user_options->value_string) { LXW_WARN_FORMAT("worksheet_conditional_format_cell()/_range(): " "For type = LXW_CONDITIONAL_TYPE_TEXT, " "value_string can not be NULL. " "Text must be specified."); return LXW_ERROR_PARAMETER_VALIDATION; } if (strlen(user_options->value_string) >= LXW_MAX_ATTRIBUTE_LENGTH) { LXW_WARN_FORMAT2("worksheet_conditional_format_cell()/_range(): " "For type = LXW_CONDITIONAL_TYPE_TEXT, " "value_string length (%d) must be less than %d.", (uint16_t) strlen(user_options->value_string), LXW_MAX_ATTRIBUTE_LENGTH); return LXW_ERROR_PARAMETER_VALIDATION; } if (user_options->criteria < LXW_CONDITIONAL_CRITERIA_TEXT_CONTAINING || user_options->criteria > LXW_CONDITIONAL_CRITERIA_TEXT_ENDS_WITH) { LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " "For type = LXW_CONDITIONAL_TYPE_TEXT, " "invalid criteria value (%d).", user_options->criteria); return LXW_ERROR_PARAMETER_VALIDATION; } cond_format->min_value_string = lxw_strdup_formula(user_options->value_string); return LXW_NO_ERROR; } lxw_error _validate_conditional_formula(lxw_cond_format_obj *cond_format, lxw_conditional_format *user_options) { if (!user_options->value_string) { LXW_WARN_FORMAT("worksheet_conditional_format_cell()/_range(): " "For type = LXW_CONDITIONAL_TYPE_FORMULA, " "value_string can not be NULL. " "Formula must be specified."); return LXW_ERROR_PARAMETER_VALIDATION; } cond_format->min_value_string = lxw_strdup_formula(user_options->value_string); return LXW_NO_ERROR; } lxw_error _validate_conditional_cell(lxw_cond_format_obj *cond_format, lxw_conditional_format *user_options) { cond_format->min_value = user_options->value; cond_format->min_value_string = lxw_strdup_formula(user_options->value_string); if (cond_format->criteria == LXW_CONDITIONAL_CRITERIA_BETWEEN || cond_format->criteria == LXW_CONDITIONAL_CRITERIA_NOT_BETWEEN) { cond_format->has_max = LXW_TRUE; cond_format->min_value = user_options->min_value; cond_format->max_value = user_options->max_value; cond_format->min_value_string = lxw_strdup_formula(user_options->min_value_string); cond_format->max_value_string = lxw_strdup_formula(user_options->max_value_string); } return LXW_NO_ERROR; } /* * Write the element. */ STATIC void _worksheet_write_ignored_errors(lxw_worksheet *self) { if (!self->has_ignore_errors) return; lxw_xml_start_tag(self->file, "ignoredErrors", NULL); if (self->ignore_number_stored_as_text) { _worksheet_write_ignored_error(self, "numberStoredAsText", self->ignore_number_stored_as_text); } if (self->ignore_eval_error) { _worksheet_write_ignored_error(self, "evalError", self->ignore_eval_error); } if (self->ignore_formula_differs) { _worksheet_write_ignored_error(self, "formula", self->ignore_formula_differs); } if (self->ignore_formula_range) { _worksheet_write_ignored_error(self, "formulaRange", self->ignore_formula_range); } if (self->ignore_formula_unlocked) { _worksheet_write_ignored_error(self, "unlockedFormula", self->ignore_formula_unlocked); } if (self->ignore_empty_cell_reference) { _worksheet_write_ignored_error(self, "emptyCellReference", self->ignore_empty_cell_reference); } if (self->ignore_list_data_validation) { _worksheet_write_ignored_error(self, "listDataValidation", self->ignore_list_data_validation); } if (self->ignore_calculated_column) { _worksheet_write_ignored_error(self, "calculatedColumn", self->ignore_calculated_column); } if (self->ignore_two_digit_text_year) { _worksheet_write_ignored_error(self, "twoDigitTextYear", self->ignore_two_digit_text_year); } lxw_xml_end_tag(self->file, "ignoredErrors"); } /* * Write the element. */ STATIC void _worksheet_write_table_part(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, "tablePart", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _worksheet_write_table_parts(lxw_worksheet *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; lxw_table_obj *table_obj; if (!self->table_count) return; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("count", self->table_count); lxw_xml_start_tag(self->file, "tableParts", &attributes); STAILQ_FOREACH(table_obj, self->table_objs, list_pointers) { self->rel_count++; /* Write the tablePart element. */ _worksheet_write_table_part(self, self->rel_count); } lxw_xml_end_tag(self->file, "tableParts"); LXW_FREE_ATTRIBUTES(); } /* * External functions to call intern XML methods shared with chartsheet. */ void lxw_worksheet_write_sheet_views(lxw_worksheet *self) { _worksheet_write_sheet_views(self); } void lxw_worksheet_write_page_margins(lxw_worksheet *self) { _worksheet_write_page_margins(self); } void lxw_worksheet_write_drawings(lxw_worksheet *self) { _worksheet_write_drawings(self); } void lxw_worksheet_write_sheet_protection(lxw_worksheet *self, lxw_protection_obj *protect) { _worksheet_write_sheet_protection(self, protect); } void lxw_worksheet_write_sheet_pr(lxw_worksheet *self) { _worksheet_write_sheet_pr(self); } void lxw_worksheet_write_page_setup(lxw_worksheet *self) { _worksheet_write_page_setup(self); } void lxw_worksheet_write_header_footer(lxw_worksheet *self) { _worksheet_write_header_footer(self); } /* * 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, &self->protection); /* Write the autoFilter element. */ _worksheet_write_auto_filter(self); /* Write the mergeCells element. */ _worksheet_write_merge_cells(self); /* Write the conditionalFormatting elements. */ _worksheet_write_conditional_formats(self); /* Write the dataValidations element. */ _worksheet_write_data_validations(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 ignoredErrors element. */ _worksheet_write_ignored_errors(self); /* Write the drawing element. */ _worksheet_write_drawings(self); /* Write the legacyDrawing element. */ _worksheet_write_legacy_drawing(self); /* Write the legacyDrawingHF element. */ _worksheet_write_legacy_drawing_hf(self); /* Write the picture element. */ _worksheet_write_picture(self); /* Write the tableParts element. */ _worksheet_write_table_parts(self); /* Write the extLst element. */ _worksheet_write_ext_list(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_NO_ERROR; } 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, LXW_FALSE); 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 (lxw_has_control_characters(string)) { 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 string result to a cell in Excel. */ lxw_error worksheet_write_formula_str(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, const char *formula, lxw_format *format, const char *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->user_data2 = lxw_strdup(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); } /* * Internal shared function for various array formula functions. */ lxw_error _store_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, double result, uint8_t is_dynamic) { 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 row and col are valid and store max and min values. */ err = _check_dimensions(self, first_row, first_col, LXW_FALSE, LXW_FALSE); if (err) return err; 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, first_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(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, is_dynamic); cell->formula_result = result; _insert_cell(self, first_row, first_col, cell); if (is_dynamic) self->has_dynamic_arrays = LXW_TRUE; /* 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 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) { return _store_array_formula(self, first_row, first_col, last_row, last_col, formula, format, result, LXW_FALSE); } /* * 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 _store_array_formula(self, first_row, first_col, last_row, last_col, formula, format, 0, LXW_FALSE); } /* * Write a single cell dynamic array formula with a default result to a cell. */ lxw_error worksheet_write_dynamic_formula(lxw_worksheet *self, lxw_row_t row, lxw_col_t col, const char *formula, lxw_format *format) { return _store_array_formula(self, row, col, row, col, formula, format, 0, LXW_TRUE); } /* * Write a single cell dynamic array formula with a numerical result to a cell. */ lxw_error worksheet_write_dynamic_formula_num(lxw_worksheet *self, lxw_row_t row, lxw_col_t col, const char *formula, lxw_format *format, double result) { return _store_array_formula(self, row, col, row, col, formula, format, result, LXW_TRUE); } /* * Write a dynamic array formula with a numerical result to a cell in Excel. */ lxw_error worksheet_write_dynamic_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) { return _store_array_formula(self, first_row, first_col, last_row, last_col, formula, format, result, LXW_TRUE); } /* * Write a dynamic array formula with a default result to a cell in Excel. */ lxw_error worksheet_write_dynamic_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 _store_array_formula(self, first_row, first_col, last_row, last_col, formula, format, 0, LXW_TRUE); } /* * 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_epoch(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 date and or time to a cell in Excel. */ lxw_error worksheet_write_unixtime(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, int64_t unixtime, 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_unixtime_to_excel_date_epoch(unixtime, 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 *user_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; char *tmp_string = NULL; lxw_format *format = NULL; size_t string_size; size_t i; lxw_error err = LXW_ERROR_MEMORY_MALLOC_FAILED; 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) { LXW_WARN("worksheet_write_url()/_opt(): URL ignored since it exceeds " "the maximum number of allowed worksheet URLs (65530)."); return LXW_ERROR_WORKSHEET_MAX_NUMBER_URLS_EXCEEDED; } err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); if (err) return err; /* Reset default error condition. */ err = LXW_ERROR_MEMORY_MALLOC_FAILED; /* 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); } /* Split url into the link and optional anchor/location. */ found_string = strchr(url_copy, '#'); if (found_string) { free(url_string); url_string = lxw_strdup(found_string + 1); GOTO_LABEL_ON_MEM_ERROR(url_string, mem_error); *found_string = '\0'; } /* Escape the URL. */ if (link_type == HYPERLINK_URL || link_type == HYPERLINK_EXTERNAL) { tmp_string = lxw_escape_url_characters(url_copy, LXW_FALSE); GOTO_LABEL_ON_MEM_ERROR(tmp_string, mem_error); free(url_copy); url_copy = tmp_string; } 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". */ /* 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] = '\\'; /* 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; } } /* Check if URL exceeds Excel's length limit. */ if (lxw_utf8_strlen(url_copy) > self->max_url_length) { LXW_WARN_FORMAT2("worksheet_write_url()/_opt(): URL exceeds " "Excel's allowable length of %d characters: %s", self->max_url_length, url_copy); err = LXW_ERROR_WORKSHEET_MAX_URL_LENGTH_EXCEEDED; goto mem_error; } /* Use the default URL format if none is specified. */ if (!user_format) format = self->default_url_format; else format = user_format; err = worksheet_write_string(self, row_num, col_num, string_copy, format); if (err) goto mem_error; /* Reset default error condition. */ err = LXW_ERROR_MEMORY_MALLOC_FAILED; 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 err; } /* * 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); } /* * Write a rich string to an Excel file. * * Rather than duplicate several of the styles.c font xml methods of styles.c * and write the data to a memory buffer this function creates a temporary * styles object and uses it to write the data to a file. It then reads that * data back into memory and closes the file. */ lxw_error worksheet_write_rich_string(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, lxw_rich_string_tuple *rich_strings[], lxw_format *format) { lxw_cell *cell; int32_t string_id; struct sst_element *sst_element; lxw_error err; uint8_t i; long file_size; char *rich_string = NULL; const char *string_copy = NULL; lxw_styles *styles = NULL; lxw_format *default_format = NULL; lxw_rich_string_tuple *rich_string_tuple = NULL; FILE *tmpfile; err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); if (err) return err; /* Iterate through rich string fragments to check for input errors. */ i = 0; err = LXW_NO_ERROR; while ((rich_string_tuple = rich_strings[i++]) != NULL) { /* Check for NULL or empty strings. */ if (!rich_string_tuple->string || !*rich_string_tuple->string) { err = LXW_ERROR_PARAMETER_VALIDATION; } } /* If there are less than 2 fragments it isn't a rich string. */ if (i <= 2) err = LXW_ERROR_PARAMETER_VALIDATION; if (err) return err; /* Create a tmp file for the styles object. */ tmpfile = lxw_get_filehandle(&rich_string, NULL, self->tmpdir); if (!tmpfile) return LXW_ERROR_CREATING_TMPFILE; /* Create a temp styles object for writing the font data. */ styles = lxw_styles_new(); GOTO_LABEL_ON_MEM_ERROR(styles, mem_error); styles->file = tmpfile; /* Create a default format for non-formatted text. */ default_format = lxw_format_new(); GOTO_LABEL_ON_MEM_ERROR(default_format, mem_error); /* Iterate through the rich string fragments and write each one out. */ i = 0; while ((rich_string_tuple = rich_strings[i++]) != NULL) { lxw_xml_start_tag(tmpfile, "r", NULL); if (rich_string_tuple->format) { /* Write the user defined font format. */ lxw_styles_write_rich_font(styles, rich_string_tuple->format); } else { /* Write a default font format. Except for the first fragment. */ if (i > 1) lxw_styles_write_rich_font(styles, default_format); } lxw_styles_write_string_fragment(styles, rich_string_tuple->string); lxw_xml_end_tag(tmpfile, "r"); } /* Free the temp objects. */ lxw_styles_free(styles); lxw_format_free(default_format); /* Flush the file. */ fflush(tmpfile); if (!rich_string) { /* Read the size to calculate the required memory. */ file_size = ftell(tmpfile); /* Allocate a buffer for the rich string xml data. */ rich_string = calloc(file_size + 1, 1); GOTO_LABEL_ON_MEM_ERROR(rich_string, mem_error); /* Rewind the file and read the data into the memory buffer. */ rewind(tmpfile); if (fread((void *) rich_string, file_size, 1, tmpfile) < 1) { fclose(tmpfile); free((void *) rich_string); return LXW_ERROR_READING_TMPFILE; } } /* Close the temp file. */ fclose(tmpfile); if (lxw_utf8_strlen(rich_string) > LXW_STR_MAX) { free((void *) rich_string); 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, rich_string, LXW_TRUE); free((void *) rich_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 (lxw_has_control_characters(rich_string)) { string_copy = lxw_escape_control_characters(rich_string); free((void *) rich_string); } else { string_copy = rich_string; } cell = _new_inline_rich_string_cell(row_num, col_num, string_copy, format); } _insert_cell(self, row_num, col_num, cell); return LXW_NO_ERROR; mem_error: lxw_styles_free(styles); lxw_format_free(default_format); fclose(tmpfile); return LXW_ERROR_MEMORY_MALLOC_FAILED; } /* * Write a comment to a worksheet cell in Excel. */ lxw_error worksheet_write_comment_opt(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, const char *text, lxw_comment_options *options) { lxw_cell *cell; lxw_error err; lxw_vml_obj *comment; err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); if (err) return err; if (!text) return LXW_ERROR_NULL_PARAMETER_IGNORED; if (lxw_utf8_strlen(text) > LXW_STR_MAX) return LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED; comment = calloc(1, sizeof(lxw_vml_obj)); GOTO_LABEL_ON_MEM_ERROR(comment, mem_error); comment->text = lxw_strdup(text); GOTO_LABEL_ON_MEM_ERROR(comment->text, mem_error); comment->row = row_num; comment->col = col_num; cell = _new_comment_cell(row_num, col_num, comment); GOTO_LABEL_ON_MEM_ERROR(cell, mem_error); _insert_comment(self, row_num, col_num, cell); /* Set user and default parameters for the comment. */ _get_comment_params(comment, options); self->has_vml = LXW_TRUE; self->has_comments = LXW_TRUE; /* Insert a placeholder in the cell RB table in the same position so * that the worksheet row "spans" calculations are correct. */ _insert_cell_placeholder(self, row_num, col_num); return LXW_NO_ERROR; mem_error: if (comment) _free_vml_object(comment); return LXW_ERROR_MEMORY_MALLOC_FAILED; } /* * Write a comment to a worksheet cell in Excel. */ lxw_error worksheet_write_comment(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, const char *string) { return worksheet_write_comment_opt(self, row_num, col_num, string, 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_tmp; 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_tmp = old_size; col_tmp < new_size; col_tmp++) new_ptr[col_tmp] = 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); /* Ensure the level is <= 7). */ if (level > 7) level = 7; if (level > self->outline_col_level) self->outline_col_level = level; /* Set the column properties. */ 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; free(self->col_options[firstcol]); 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 single column or a range of columns, with the * width in pixels. */ lxw_error worksheet_set_column_pixels(lxw_worksheet *self, lxw_col_t firstcol, lxw_col_t lastcol, uint32_t pixels, lxw_format *format) { double width = _pixels_to_width(pixels); return worksheet_set_column_opt(self, firstcol, lastcol, width, format, NULL); } /* * Set the properties of a single column or a range of columns with options, * with the width in pixels. */ lxw_error worksheet_set_column_pixels_opt(lxw_worksheet *self, lxw_col_t firstcol, lxw_col_t lastcol, uint32_t pixels, lxw_format *format, lxw_row_col_options *user_options) { double width = _pixels_to_width(pixels); return worksheet_set_column_opt(self, firstcol, lastcol, width, format, user_options); } /* * 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; } /* Ensure the level is <= 7). */ if (level > 7) level = 7; if (level > self->outline_row_level) self->outline_row_level = level; /* Store the row properties. */ 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); } /* * Set the properties of a row, with the height in pixels. */ lxw_error worksheet_set_row_pixels(lxw_worksheet *self, lxw_row_t row_num, uint32_t pixels, lxw_format *format) { double height = _pixels_to_height(pixels); return worksheet_set_row_opt(self, row_num, height, format, NULL); } /* * Set the properties of a row with options, with the height in pixels. */ lxw_error worksheet_set_row_pixels_opt(lxw_worksheet *self, lxw_row_t row_num, uint32_t pixels, lxw_format *format, lxw_row_col_options *user_options) { double height = _pixels_to_height(pixels); return worksheet_set_row_opt(self, row_num, height, format, user_options); } /* * 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; lxw_filter_rule_obj **filter_rules; lxw_col_t num_filter_rules; /* 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; /* Create a array to hold filter rules. */ self->autofilter.in_use = LXW_FALSE; self->autofilter.has_rules = LXW_FALSE; _free_filter_rules(self); num_filter_rules = last_col - first_col + 1; filter_rules = calloc(num_filter_rules, sizeof(lxw_filter_rule_obj *)); RETURN_ON_MEM_ERROR(filter_rules, LXW_ERROR_MEMORY_MALLOC_FAILED); 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; self->filter_rules = filter_rules; self->num_filter_rules = num_filter_rules; return LXW_NO_ERROR; } /* * Set a autofilter rule for a filter column. */ lxw_error worksheet_filter_column(lxw_worksheet *self, lxw_col_t col, lxw_filter_rule *rule) { lxw_filter_rule_obj *rule_obj; uint16_t rule_index; if (!rule) { LXW_WARN("worksheet_filter_column(): rule parameter cannot be NULL"); return LXW_ERROR_PARAMETER_VALIDATION; } if (self->autofilter.in_use == LXW_FALSE) { LXW_WARN("worksheet_filter_column(): " "Worksheet autofilter range hasn't been defined. " "Use worksheet_autofilter() first."); return LXW_ERROR_PARAMETER_VALIDATION; } if (col < self->autofilter.first_col || col > self->autofilter.last_col) { LXW_WARN_FORMAT3("worksheet_filter_column(): " "Column '%d' is outside autofilter range " "'%d <= col <= %d'.", col, self->autofilter.first_col, self->autofilter.last_col); return LXW_ERROR_PARAMETER_VALIDATION; } /* Free any previous rule in the column slot. */ rule_index = col - self->autofilter.first_col; _free_filter_rule(self->filter_rules[rule_index]); /* Create a new rule and copy user input. */ rule_obj = calloc(1, sizeof(lxw_filter_rule_obj)); RETURN_ON_MEM_ERROR(rule_obj, LXW_ERROR_MEMORY_MALLOC_FAILED); rule_obj->col_num = rule_index; rule_obj->type = LXW_FILTER_TYPE_SINGLE; rule_obj->criteria1 = rule->criteria; rule_obj->value1 = rule->value; if (rule_obj->criteria1 != LXW_FILTER_CRITERIA_NON_BLANKS) { rule_obj->value1_string = lxw_strdup(rule->value_string); } else { rule_obj->criteria1 = LXW_FILTER_CRITERIA_NOT_EQUAL_TO; rule_obj->value1_string = lxw_strdup(" "); } if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_BLANKS) rule_obj->has_blanks = LXW_TRUE; _set_custom_filter(rule_obj); self->filter_rules[rule_index] = rule_obj; self->filter_on = LXW_TRUE; self->autofilter.has_rules = LXW_TRUE; return LXW_NO_ERROR; } /* * Set two autofilter rules for a filter column. */ lxw_error worksheet_filter_column2(lxw_worksheet *self, lxw_col_t col, lxw_filter_rule *rule1, lxw_filter_rule *rule2, uint8_t and_or) { lxw_filter_rule_obj *rule_obj; uint16_t rule_index; if (!rule1 || !rule2) { LXW_WARN("worksheet_filter_column2(): rule parameter cannot be NULL"); return LXW_ERROR_PARAMETER_VALIDATION; } if (self->autofilter.in_use == LXW_FALSE) { LXW_WARN("worksheet_filter_column2(): " "Worksheet autofilter range hasn't been defined. " "Use worksheet_autofilter() first."); return LXW_ERROR_PARAMETER_VALIDATION; } if (col < self->autofilter.first_col || col > self->autofilter.last_col) { LXW_WARN_FORMAT3("worksheet_filter_column2(): " "Column '%d' is outside autofilter range " "'%d <= col <= %d'.", col, self->autofilter.first_col, self->autofilter.last_col); return LXW_ERROR_PARAMETER_VALIDATION; } /* Free any previous rule in the column slot. */ rule_index = col - self->autofilter.first_col; _free_filter_rule(self->filter_rules[rule_index]); /* Create a new rule and copy user input. */ rule_obj = calloc(1, sizeof(lxw_filter_rule_obj)); RETURN_ON_MEM_ERROR(rule_obj, LXW_ERROR_MEMORY_MALLOC_FAILED); if (and_or == LXW_FILTER_AND) rule_obj->type = LXW_FILTER_TYPE_AND; else rule_obj->type = LXW_FILTER_TYPE_OR; rule_obj->col_num = rule_index; rule_obj->criteria1 = rule1->criteria; rule_obj->value1 = rule1->value; rule_obj->criteria2 = rule2->criteria; rule_obj->value2 = rule2->value; if (rule_obj->criteria1 != LXW_FILTER_CRITERIA_NON_BLANKS) { rule_obj->value1_string = lxw_strdup(rule1->value_string); } else { rule_obj->criteria1 = LXW_FILTER_CRITERIA_NOT_EQUAL_TO; rule_obj->value1_string = lxw_strdup(" "); } if (rule_obj->criteria2 != LXW_FILTER_CRITERIA_NON_BLANKS) { rule_obj->value2_string = lxw_strdup(rule2->value_string); } else { rule_obj->criteria2 = LXW_FILTER_CRITERIA_NOT_EQUAL_TO; rule_obj->value2_string = lxw_strdup(" "); } if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_BLANKS) rule_obj->has_blanks = LXW_TRUE; if (rule_obj->criteria2 == LXW_FILTER_CRITERIA_BLANKS) rule_obj->has_blanks = LXW_TRUE; _set_custom_filter(rule_obj); self->filter_rules[rule_index] = rule_obj; self->filter_on = LXW_TRUE; self->autofilter.has_rules = LXW_TRUE; return LXW_NO_ERROR; } /* * Set two autofilter rules for a filter column. */ lxw_error worksheet_filter_list(lxw_worksheet *self, lxw_col_t col, const char **list) { lxw_filter_rule_obj *rule_obj; uint16_t rule_index; uint8_t has_blanks = LXW_FALSE; uint16_t num_filters = 0; uint16_t input_list_index; uint16_t rule_obj_list_index; const char *str; char **tmp_list; if (!list) { LXW_WARN("worksheet_filter_list(): list parameter cannot be NULL"); return LXW_ERROR_PARAMETER_VALIDATION; } if (self->autofilter.in_use == LXW_FALSE) { LXW_WARN("worksheet_filter_list(): " "Worksheet autofilter range hasn't been defined. " "Use worksheet_autofilter() first."); return LXW_ERROR_PARAMETER_VALIDATION; } if (col < self->autofilter.first_col || col > self->autofilter.last_col) { LXW_WARN_FORMAT3("worksheet_filter_list(): " "Column '%d' is outside autofilter range " "'%d <= col <= %d'.", col, self->autofilter.first_col, self->autofilter.last_col); return LXW_ERROR_PARAMETER_VALIDATION; } /* Count the number of non "Blanks" strings in the input list. */ input_list_index = 0; while ((str = list[input_list_index]) != NULL) { if (strncmp(str, "Blanks", 6) == 0) has_blanks = LXW_TRUE; else num_filters++; input_list_index++; } /* There should be at least one filter string. */ if (num_filters == 0) { LXW_WARN("worksheet_filter_list(): " "list must have at least 1 non-blanks item."); return LXW_ERROR_PARAMETER_VALIDATION; } /* Free any previous rule in the column slot. */ rule_index = col - self->autofilter.first_col; _free_filter_rule(self->filter_rules[rule_index]); /* Create a new rule and copy user input. */ rule_obj = calloc(1, sizeof(lxw_filter_rule_obj)); RETURN_ON_MEM_ERROR(rule_obj, LXW_ERROR_MEMORY_MALLOC_FAILED); tmp_list = calloc(num_filters + 1, sizeof(char *)); GOTO_LABEL_ON_MEM_ERROR(tmp_list, mem_error); /* Copy input list (without any "Blanks" command) to an internal list. */ input_list_index = 0; rule_obj_list_index = 0; while ((str = list[input_list_index]) != NULL) { if (strncmp(str, "Blanks", 6) != 0) { tmp_list[rule_obj_list_index] = lxw_strdup(str); rule_obj_list_index++; } input_list_index++; } rule_obj->list = tmp_list; rule_obj->num_list_filters = num_filters; rule_obj->is_custom = LXW_FALSE; rule_obj->col_num = rule_index; rule_obj->type = LXW_FILTER_TYPE_STRING_LIST; rule_obj->has_blanks = has_blanks; self->filter_rules[rule_index] = rule_obj; self->filter_on = LXW_TRUE; self->autofilter.has_rules = LXW_TRUE; return LXW_NO_ERROR; mem_error: free(rule_obj); return LXW_ERROR_MEMORY_MALLOC_FAILED; } /* * Add an Excel table to the worksheet. */ lxw_error worksheet_add_table(lxw_worksheet *self, lxw_row_t first_row, lxw_col_t first_col, lxw_row_t last_row, lxw_col_t last_col, lxw_table_options *user_options) { lxw_row_t tmp_row; lxw_col_t tmp_col; lxw_col_t num_cols; lxw_error err; lxw_table_obj *table_obj; lxw_table_column **columns; if (self->optimize) { LXW_WARN_FORMAT("worksheet_add_table(): " "worksheet tables aren't supported in " "'constant_memory' mode"); return LXW_ERROR_FEATURE_NOT_SUPPORTED; } /* 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_TRUE, LXW_TRUE); if (err) return err; num_cols = last_col - first_col + 1; /* Check that there are sufficient data rows. */ err = _check_table_rows(first_row, last_row, user_options); if (err) return err; /* Check that the the table name is valid. */ err = _check_table_name(user_options); if (err) return err; /* Create a table object to copy from the user options. */ table_obj = calloc(1, sizeof(lxw_table_obj)); RETURN_ON_MEM_ERROR(table_obj, LXW_ERROR_MEMORY_MALLOC_FAILED); columns = calloc(num_cols, sizeof(lxw_table_column *)); GOTO_LABEL_ON_MEM_ERROR(columns, error); table_obj->columns = columns; table_obj->num_cols = num_cols; table_obj->first_row = first_row; table_obj->first_col = first_col; table_obj->last_row = last_row; table_obj->last_col = last_col; err = _set_default_table_columns(table_obj); if (err) goto error; /* Create the table range. */ lxw_rowcol_to_range(table_obj->sqref, first_row, first_col, last_row, last_col); lxw_rowcol_to_range(table_obj->filter_sqref, first_row, first_col, last_row, last_col); /* Validate and copy user options to an internal object. */ if (user_options) { _check_and_copy_table_style(table_obj, user_options); table_obj->total_row = user_options->total_row; table_obj->last_column = user_options->last_column; table_obj->first_column = user_options->first_column; table_obj->no_autofilter = user_options->no_autofilter; table_obj->no_header_row = user_options->no_header_row; table_obj->no_banded_rows = user_options->no_banded_rows; table_obj->banded_columns = user_options->banded_columns; if (user_options->no_header_row) table_obj->no_autofilter = LXW_TRUE; if (user_options->columns) { err = _set_custom_table_columns(table_obj, user_options); if (err) goto error; } if (user_options->total_row) { lxw_rowcol_to_range(table_obj->filter_sqref, first_row, first_col, last_row - 1, last_col); } if (user_options->name) { table_obj->name = lxw_strdup(user_options->name); if (!table_obj->name) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto error; } } } _write_table_column_data(self, table_obj); STAILQ_INSERT_TAIL(self->table_objs, table_obj, list_pointers); self->table_count++; return LXW_NO_ERROR; error: _free_worksheet_table(table_obj); return err; } /* * 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 the first visible cell at the top left of the worksheet. */ void worksheet_set_top_left_cell(lxw_worksheet *self, lxw_row_t row, lxw_col_t col) { if (row == 0 && col == 0) return; lxw_rowcol_to_cell(self->top_left_cell, row, col); } /* * 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) { lxw_error err; char *tmp_header; char *found_string; char *offset_string; uint8_t placeholder_count = 0; uint8_t image_count = 0; if (!string) { LXW_WARN_FORMAT("worksheet_set_header_opt/footer_opt(): " "header/footer string cannot be NULL."); return LXW_ERROR_NULL_PARAMETER_IGNORED; } if (lxw_utf8_strlen(string) > LXW_HEADER_FOOTER_MAX) { LXW_WARN_FORMAT("worksheet_set_header_opt/footer_opt(): " "header/footer string exceeds Excel's limit of " "255 characters."); return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; } tmp_header = lxw_strdup(string); RETURN_ON_MEM_ERROR(tmp_header, LXW_ERROR_MEMORY_MALLOC_FAILED); /* Replace &[Picture] with &G which is used internally by Excel. */ while ((found_string = strstr(tmp_header, "&[Picture]"))) { found_string++; *found_string = 'G'; do { found_string++; offset_string = found_string + sizeof("Picture"); *found_string = *offset_string; } while (*offset_string); } /* Count &G placeholders and ensure there are sufficient images. */ found_string = tmp_header; while (*found_string) { if (*found_string == '&' && *(found_string + 1) == 'G') placeholder_count++; found_string++; } if (placeholder_count > 0 && !options) { LXW_WARN_FORMAT1("worksheet_set_header_opt/footer_opt(): " "the number of &G/&[Picture] placeholders in option " "string \"%s\" does not match the number of supplied " "images.", string); free(tmp_header); return LXW_ERROR_PARAMETER_VALIDATION; } /* Free any previous header string so we can overwrite it. */ free(self->header); self->header = NULL; if (options) { /* Ensure there are enough images to match the placeholders. There is * a potential bug where there are sufficient images but in the wrong * positions but we don't currently try to deal with that.*/ if (options->image_left) image_count++; if (options->image_center) image_count++; if (options->image_right) image_count++; if (placeholder_count != image_count) { LXW_WARN_FORMAT1("worksheet_set_header_opt/footer_opt(): " "the number of &G/&[Picture] placeholders in option " "string \"%s\" does not match the number of supplied " "images.", string); free(tmp_header); return LXW_ERROR_PARAMETER_VALIDATION; } /* Free any existing header image objects. */ _free_object_properties(self->header_left_object_props); _free_object_properties(self->header_center_object_props); _free_object_properties(self->header_right_object_props); if (options->margin > 0.0) self->margin_header = options->margin; err = _worksheet_set_header_footer_image(self, options->image_left, HEADER_LEFT); if (err) { free(tmp_header); return err; } err = _worksheet_set_header_footer_image(self, options->image_center, HEADER_CENTER); if (err) { free(tmp_header); return err; } err = _worksheet_set_header_footer_image(self, options->image_right, HEADER_RIGHT); if (err) { free(tmp_header); return err; } } self->header = tmp_header; self->header_footer_changed = LXW_TRUE; 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) { lxw_error err; char *tmp_footer; char *found_string; char *offset_string; uint8_t placeholder_count = 0; uint8_t image_count = 0; if (!string) { LXW_WARN_FORMAT("worksheet_set_header_opt/footer_opt(): " "header/footer string cannot be NULL."); return LXW_ERROR_NULL_PARAMETER_IGNORED; } if (lxw_utf8_strlen(string) > LXW_HEADER_FOOTER_MAX) { LXW_WARN_FORMAT("worksheet_set_header_opt/footer_opt(): " "header/footer string exceeds Excel's limit of " "255 characters."); return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; } tmp_footer = lxw_strdup(string); RETURN_ON_MEM_ERROR(tmp_footer, LXW_ERROR_MEMORY_MALLOC_FAILED); /* Replace &[Picture] with &G which is used internally by Excel. */ while ((found_string = strstr(tmp_footer, "&[Picture]"))) { found_string++; *found_string = 'G'; do { found_string++; offset_string = found_string + sizeof("Picture"); *found_string = *offset_string; } while (*offset_string); } /* Count &G placeholders and ensure there are sufficient images. */ found_string = tmp_footer; while (*found_string) { if (*found_string == '&' && *(found_string + 1) == 'G') placeholder_count++; found_string++; } if (placeholder_count > 0 && !options) { LXW_WARN_FORMAT1("worksheet_set_header_opt/footer_opt(): " "the number of &G/&[Picture] placeholders in option " "string \"%s\" does not match the number of supplied " "images.", string); free(tmp_footer); return LXW_ERROR_PARAMETER_VALIDATION; } /* Free any previous footer string so we can overwrite it. */ free(self->footer); self->footer = NULL; if (options) { /* Ensure there are enough images to match the placeholders. There is * a potential bug where there are sufficient images but in the wrong * positions but we don't currently try to deal with that.*/ if (options->image_left) image_count++; if (options->image_center) image_count++; if (options->image_right) image_count++; if (placeholder_count != image_count) { LXW_WARN_FORMAT1("worksheet_set_header_opt/footer_opt(): " "the number of &G/&[Picture] placeholders in option " "string \"%s\" does not match the number of supplied " "images.", string); free(tmp_footer); return LXW_ERROR_PARAMETER_VALIDATION; } /* Free any existing footer image objects. */ _free_object_properties(self->footer_left_object_props); _free_object_properties(self->footer_center_object_props); _free_object_properties(self->footer_right_object_props); if (options->margin > 0.0) self->margin_footer = options->margin; err = _worksheet_set_header_footer_image(self, options->image_left, FOOTER_LEFT); if (err) { free(tmp_footer); return err; } err = _worksheet_set_header_footer_image(self, options->image_center, FOOTER_CENTER); if (err) { free(tmp_footer); return err; } err = _worksheet_set_header_footer_image(self, options->image_right, FOOTER_RIGHT); if (err) { free(tmp_footer); return err; } } self->footer = tmp_footer; self->header_footer_changed = LXW_TRUE; 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; } /* * Set the print in black and white option. */ void worksheet_print_black_and_white(lxw_worksheet *self) { self->black_white = LXW_TRUE; 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_obj *protect = &self->protection; /* Copy any user parameters to the internal structure. */ if (options) { protect->no_select_locked_cells = options->no_select_locked_cells; protect->no_select_unlocked_cells = options->no_select_unlocked_cells; protect->format_cells = options->format_cells; protect->format_columns = options->format_columns; protect->format_rows = options->format_rows; protect->insert_columns = options->insert_columns; protect->insert_rows = options->insert_rows; protect->insert_hyperlinks = options->insert_hyperlinks; protect->delete_columns = options->delete_columns; protect->delete_rows = options->delete_rows; protect->sort = options->sort; protect->autofilter = options->autofilter; protect->pivot_tables = options->pivot_tables; protect->scenarios = options->scenarios; protect->objects = options->objects; } if (password) { uint16_t hash = lxw_hash_password(password); lxw_snprintf(protect->hash, 5, "%X", hash); } protect->no_sheet = LXW_FALSE; protect->no_content = LXW_TRUE; protect->is_configured = LXW_TRUE; } /* * Set the worksheet properties for outlines and grouping. */ void worksheet_outline_settings(lxw_worksheet *self, uint8_t visible, uint8_t symbols_below, uint8_t symbols_right, uint8_t auto_style) { self->outline_on = visible; self->outline_below = symbols_below; self->outline_right = symbols_right; self->outline_style = auto_style; self->outline_changed = 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 with options 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; const char *description; lxw_object_properties *object_props; 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 = lxw_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; } /* Use the filename as the default description, like Excel. */ description = lxw_basename(filename); if (!description) { 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 properties. */ object_props = calloc(1, sizeof(lxw_object_properties)); if (!object_props) { fclose(image_stream); return LXW_ERROR_MEMORY_MALLOC_FAILED; } if (user_options) { object_props->x_offset = user_options->x_offset; object_props->y_offset = user_options->y_offset; object_props->x_scale = user_options->x_scale; object_props->y_scale = user_options->y_scale; object_props->object_position = user_options->object_position; object_props->url = lxw_strdup(user_options->url); object_props->tip = lxw_strdup(user_options->tip); object_props->decorative = user_options->decorative; if (user_options->description) description = user_options->description; } /* Copy other options or set defaults. */ object_props->filename = lxw_strdup(filename); object_props->description = lxw_strdup(description); object_props->stream = image_stream; object_props->row = row_num; object_props->col = col_num; if (object_props->x_scale == 0.0) object_props->x_scale = 1; if (object_props->y_scale == 0.0) object_props->y_scale = 1; if (_get_image_properties(object_props) == LXW_NO_ERROR) { STAILQ_INSERT_TAIL(self->image_props, object_props, list_pointers); fclose(image_stream); return LXW_NO_ERROR; } else { _free_object_properties(object_props); 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 image buffer, with options, into the worksheet. */ lxw_error worksheet_insert_image_buffer_opt(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, const unsigned char *image_buffer, size_t image_size, lxw_image_options *user_options) { FILE *image_stream; lxw_object_properties *object_props; if (!image_size) { LXW_WARN("worksheet_insert_image_buffer()/_opt(): " "size must be non-zero."); return LXW_ERROR_NULL_PARAMETER_IGNORED; } /* Write the image buffer to a file (preferably in memory) so we can read * the dimensions like an ordinary file. */ #ifdef USE_FMEMOPEN image_stream = fmemopen((void *) image_buffer, image_size, "rb"); if (!image_stream) return LXW_ERROR_CREATING_TMPFILE; #else image_stream = lxw_tmpfile(self->tmpdir); if (!image_stream) return LXW_ERROR_CREATING_TMPFILE; if (fwrite(image_buffer, 1, image_size, image_stream) != image_size) { fclose(image_stream); return LXW_ERROR_CREATING_TMPFILE; } rewind(image_stream); #endif /* Create a new object to hold the image properties. */ object_props = calloc(1, sizeof(lxw_object_properties)); if (!object_props) { fclose(image_stream); return LXW_ERROR_MEMORY_MALLOC_FAILED; } /* Store the image data in the properties structure. */ object_props->image_buffer = calloc(1, image_size); if (!object_props->image_buffer) { _free_object_properties(object_props); fclose(image_stream); return LXW_ERROR_MEMORY_MALLOC_FAILED; } else { memcpy(object_props->image_buffer, image_buffer, image_size); object_props->image_buffer_size = image_size; object_props->is_image_buffer = LXW_TRUE; } if (user_options) { object_props->x_offset = user_options->x_offset; object_props->y_offset = user_options->y_offset; object_props->x_scale = user_options->x_scale; object_props->y_scale = user_options->y_scale; object_props->url = lxw_strdup(user_options->url); object_props->tip = lxw_strdup(user_options->tip); object_props->object_position = user_options->object_position; object_props->description = lxw_strdup(user_options->description); object_props->decorative = user_options->decorative; } /* Copy other options or set defaults. */ object_props->filename = lxw_strdup("image_buffer"); object_props->stream = image_stream; object_props->row = row_num; object_props->col = col_num; if (object_props->x_scale == 0.0) object_props->x_scale = 1; if (object_props->y_scale == 0.0) object_props->y_scale = 1; if (_get_image_properties(object_props) == LXW_NO_ERROR) { STAILQ_INSERT_TAIL(self->image_props, object_props, list_pointers); fclose(image_stream); return LXW_NO_ERROR; } else { _free_object_properties(object_props); fclose(image_stream); return LXW_ERROR_IMAGE_DIMENSIONS; } } /* * Insert an image buffer into the worksheet. */ lxw_error worksheet_insert_image_buffer(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, const unsigned char *image_buffer, size_t image_size) { return worksheet_insert_image_buffer_opt(self, row_num, col_num, image_buffer, image_size, NULL); } /* * Set an image as a worksheet background. */ lxw_error worksheet_set_background(lxw_worksheet *self, const char *filename) { FILE *image_stream; lxw_object_properties *object_props; if (!filename) { LXW_WARN("worksheet_set_background(): " "filename must be specified."); return LXW_ERROR_NULL_PARAMETER_IGNORED; } /* Check that the image file exists and can be opened. */ image_stream = lxw_fopen(filename, "rb"); if (!image_stream) { LXW_WARN_FORMAT1("worksheet_set_background(): " "file doesn't exist or can't be opened: %s.", filename); return LXW_ERROR_PARAMETER_VALIDATION; } /* Create a new object to hold the image properties. */ object_props = calloc(1, sizeof(lxw_object_properties)); if (!object_props) { fclose(image_stream); return LXW_ERROR_MEMORY_MALLOC_FAILED; } /* Copy other options or set defaults. */ object_props->filename = lxw_strdup(filename); object_props->stream = image_stream; object_props->is_background = LXW_TRUE; if (_get_image_properties(object_props) == LXW_NO_ERROR) { _free_object_properties(self->background_image); self->background_image = object_props; self->has_background_image = LXW_TRUE; fclose(image_stream); return LXW_NO_ERROR; } else { _free_object_properties(object_props); fclose(image_stream); return LXW_ERROR_IMAGE_DIMENSIONS; } } /* * Set an image buffer as a worksheet background. */ lxw_error worksheet_set_background_buffer(lxw_worksheet *self, const unsigned char *image_buffer, size_t image_size) { FILE *image_stream; lxw_object_properties *object_props; if (!image_size) { LXW_WARN("worksheet_set_background(): " "size must be non-zero."); return LXW_ERROR_NULL_PARAMETER_IGNORED; } /* Write the image buffer to a file (preferably in memory) so we can read * the dimensions like an ordinary file. */ #ifdef USE_FMEMOPEN image_stream = fmemopen((void *) image_buffer, image_size, "rb"); if (!image_stream) return LXW_ERROR_CREATING_TMPFILE; #else image_stream = lxw_tmpfile(self->tmpdir); if (!image_stream) return LXW_ERROR_CREATING_TMPFILE; if (fwrite(image_buffer, 1, image_size, image_stream) != image_size) { fclose(image_stream); return LXW_ERROR_CREATING_TMPFILE; } rewind(image_stream); #endif /* Create a new object to hold the image properties. */ object_props = calloc(1, sizeof(lxw_object_properties)); if (!object_props) { fclose(image_stream); return LXW_ERROR_MEMORY_MALLOC_FAILED; } /* Store the image data in the properties structure. */ object_props->image_buffer = calloc(1, image_size); if (!object_props->image_buffer) { _free_object_properties(object_props); fclose(image_stream); return LXW_ERROR_MEMORY_MALLOC_FAILED; } else { memcpy(object_props->image_buffer, image_buffer, image_size); object_props->image_buffer_size = image_size; object_props->is_image_buffer = LXW_TRUE; } /* Copy other options or set defaults. */ object_props->filename = lxw_strdup("image_buffer"); object_props->stream = image_stream; object_props->is_background = LXW_TRUE; if (_get_image_properties(object_props) == LXW_NO_ERROR) { _free_object_properties(self->background_image); self->background_image = object_props; self->has_background_image = LXW_TRUE; fclose(image_stream); return LXW_NO_ERROR; } else { _free_object_properties(object_props); fclose(image_stream); return LXW_ERROR_IMAGE_DIMENSIONS; } } /* * 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_chart_options *user_options) { lxw_object_properties *object_props; 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 properties. */ object_props = calloc(1, sizeof(lxw_object_properties)); RETURN_ON_MEM_ERROR(object_props, LXW_ERROR_MEMORY_MALLOC_FAILED); if (user_options) { object_props->x_offset = user_options->x_offset; object_props->y_offset = user_options->y_offset; object_props->x_scale = user_options->x_scale; object_props->y_scale = user_options->y_scale; object_props->object_position = user_options->object_position; object_props->description = lxw_strdup(user_options->description); object_props->decorative = user_options->decorative; } /* Copy other options or set defaults. */ object_props->row = row_num; object_props->col = col_num; object_props->width = 480; object_props->height = 288; if (object_props->x_scale == 0.0) object_props->x_scale = 1; if (object_props->y_scale == 0.0) object_props->y_scale = 1; /* Store chart references so they can be ordered in the workbook. */ object_props->chart = chart; STAILQ_INSERT_TAIL(self->chart_data, object_props, 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); } /* * Add a data validation to a worksheet, for a range. Ironically this requires * a lot of validation of the user input. */ lxw_error worksheet_data_validation_range(lxw_worksheet *self, lxw_row_t first_row, lxw_col_t first_col, lxw_row_t last_row, lxw_col_t last_col, lxw_data_validation *validation) { lxw_data_val_obj *copy; uint8_t is_between = LXW_FALSE; uint8_t is_formula = LXW_FALSE; uint8_t has_criteria = LXW_TRUE; lxw_error err; lxw_row_t tmp_row; lxw_col_t tmp_col; size_t length; /* No action is required for validation type 'any' unless there are * input messages to display.*/ if (validation->validate == LXW_VALIDATION_TYPE_ANY && !(validation->input_title || validation->input_message)) { return LXW_NO_ERROR; } /* Check for formula types. */ switch (validation->validate) { case LXW_VALIDATION_TYPE_INTEGER_FORMULA: case LXW_VALIDATION_TYPE_DECIMAL_FORMULA: case LXW_VALIDATION_TYPE_LIST_FORMULA: case LXW_VALIDATION_TYPE_LENGTH_FORMULA: case LXW_VALIDATION_TYPE_DATE_FORMULA: case LXW_VALIDATION_TYPE_TIME_FORMULA: case LXW_VALIDATION_TYPE_CUSTOM_FORMULA: is_formula = LXW_TRUE; break; } /* Check for types without a criteria. */ switch (validation->validate) { case LXW_VALIDATION_TYPE_LIST: case LXW_VALIDATION_TYPE_LIST_FORMULA: case LXW_VALIDATION_TYPE_ANY: case LXW_VALIDATION_TYPE_CUSTOM_FORMULA: has_criteria = LXW_FALSE; break; } /* Check that a validation parameter has been specified * except for 'list', 'any' and 'custom'. */ if (has_criteria && validation->criteria == LXW_VALIDATION_CRITERIA_NONE) { LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): " "criteria parameter must be specified."); return LXW_ERROR_PARAMETER_VALIDATION; } /* Check for "between" criteria so we can do additional checks. */ if (has_criteria && (validation->criteria == LXW_VALIDATION_CRITERIA_BETWEEN || validation->criteria == LXW_VALIDATION_CRITERIA_NOT_BETWEEN)) { is_between = LXW_TRUE; } /* Check that formula values are non NULL. */ if (is_formula) { if (is_between) { if (!validation->minimum_formula) { LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): " "minimum_formula parameter cannot be NULL."); return LXW_ERROR_PARAMETER_VALIDATION; } if (!validation->maximum_formula) { LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): " "maximum_formula parameter cannot be NULL."); return LXW_ERROR_PARAMETER_VALIDATION; } } else { if (!validation->value_formula) { LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): " "formula parameter cannot be NULL."); return LXW_ERROR_PARAMETER_VALIDATION; } } } /* Check Excel limitations on input strings. */ if (validation->input_title) { length = lxw_utf8_strlen(validation->input_title); if (length > LXW_VALIDATION_MAX_TITLE_LENGTH) { LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): " "input_title length > Excel limit of %d.", LXW_VALIDATION_MAX_TITLE_LENGTH); return LXW_ERROR_32_STRING_LENGTH_EXCEEDED; } } if (validation->error_title) { length = lxw_utf8_strlen(validation->error_title); if (length > LXW_VALIDATION_MAX_TITLE_LENGTH) { LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): " "error_title length > Excel limit of %d.", LXW_VALIDATION_MAX_TITLE_LENGTH); return LXW_ERROR_32_STRING_LENGTH_EXCEEDED; } } if (validation->input_message) { length = lxw_utf8_strlen(validation->input_message); if (length > LXW_VALIDATION_MAX_STRING_LENGTH) { LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): " "input_message length > Excel limit of %d.", LXW_VALIDATION_MAX_STRING_LENGTH); return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; } } if (validation->error_message) { length = lxw_utf8_strlen(validation->error_message); if (length > LXW_VALIDATION_MAX_STRING_LENGTH) { LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): " "error_message length > Excel limit of %d.", LXW_VALIDATION_MAX_STRING_LENGTH); return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; } } if (validation->validate == LXW_VALIDATION_TYPE_LIST) { length = _validation_list_length(validation->value_list); if (length == 0) { LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): " "list parameters cannot be zero."); return LXW_ERROR_PARAMETER_VALIDATION; } if (length > LXW_VALIDATION_MAX_STRING_LENGTH) { LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): " "list length with commas > Excel limit of %d.", LXW_VALIDATION_MAX_STRING_LENGTH); return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; } } /* 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 dimensions are valid but don't store them. */ err = _check_dimensions(self, last_row, last_col, LXW_TRUE, LXW_TRUE); if (err) return err; /* Create a copy of the parameters from the user data validation. */ copy = calloc(1, sizeof(lxw_data_val_obj)); GOTO_LABEL_ON_MEM_ERROR(copy, mem_error); /* Create the data validation range. */ if (first_row == last_row && first_col == last_col) lxw_rowcol_to_cell(copy->sqref, first_row, first_col); else lxw_rowcol_to_range(copy->sqref, first_row, first_col, last_row, last_col); /* Copy the parameters from the user data validation. */ copy->validate = validation->validate; copy->value_number = validation->value_number; copy->error_type = validation->error_type; copy->dropdown = validation->dropdown; if (has_criteria) copy->criteria = validation->criteria; if (is_between) { copy->value_number = validation->minimum_number; copy->maximum_number = validation->maximum_number; } /* Copy the input/error titles and messages. */ if (validation->input_title) { copy->input_title = lxw_strdup_formula(validation->input_title); GOTO_LABEL_ON_MEM_ERROR(copy->input_title, mem_error); } if (validation->input_message) { copy->input_message = lxw_strdup_formula(validation->input_message); GOTO_LABEL_ON_MEM_ERROR(copy->input_message, mem_error); } if (validation->error_title) { copy->error_title = lxw_strdup_formula(validation->error_title); GOTO_LABEL_ON_MEM_ERROR(copy->error_title, mem_error); } if (validation->error_message) { copy->error_message = lxw_strdup_formula(validation->error_message); GOTO_LABEL_ON_MEM_ERROR(copy->error_message, mem_error); } /* Copy the formula strings. */ if (is_formula) { if (is_between) { copy->value_formula = lxw_strdup_formula(validation->minimum_formula); GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error); copy->maximum_formula = lxw_strdup_formula(validation->maximum_formula); GOTO_LABEL_ON_MEM_ERROR(copy->maximum_formula, mem_error); } else { copy->value_formula = lxw_strdup_formula(validation->value_formula); GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error); } } /* Copy the validation list as a csv string. */ if (validation->validate == LXW_VALIDATION_TYPE_LIST) { copy->value_formula = _validation_list_to_csv(validation->value_list); GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error); } if (validation->validate == LXW_VALIDATION_TYPE_DATE || validation->validate == LXW_VALIDATION_TYPE_TIME) { if (is_between) { copy->value_number = lxw_datetime_to_excel_date_epoch (&validation->minimum_datetime, LXW_EPOCH_1900); copy->maximum_number = lxw_datetime_to_excel_date_epoch (&validation->maximum_datetime, LXW_EPOCH_1900); } else { copy->value_number = lxw_datetime_to_excel_date_epoch(&validation->value_datetime, LXW_EPOCH_1900); } } /* These options are on by default so we can't take plain booleans. */ copy->ignore_blank = validation->ignore_blank ^ 1; copy->show_input = validation->show_input ^ 1; copy->show_error = validation->show_error ^ 1; STAILQ_INSERT_TAIL(self->data_validations, copy, list_pointers); self->num_validations++; return LXW_NO_ERROR; mem_error: _free_data_validation(copy); return LXW_ERROR_MEMORY_MALLOC_FAILED; } /* * Add a data validation to a worksheet, for a cell. */ lxw_error worksheet_data_validation_cell(lxw_worksheet *self, lxw_row_t row, lxw_col_t col, lxw_data_validation *validation) { return worksheet_data_validation_range(self, row, col, row, col, validation); } /* * Add a conditional format to a worksheet, for a range. */ lxw_error worksheet_conditional_format_range(lxw_worksheet *self, lxw_row_t first_row, lxw_col_t first_col, lxw_row_t last_row, lxw_col_t last_col, lxw_conditional_format *user_options) { lxw_cond_format_obj *cond_format; lxw_row_t tmp_row; lxw_col_t tmp_col; lxw_error err = LXW_NO_ERROR; char *type_strings[] = { "none", "cellIs", "containsText", "timePeriod", "aboveAverage", "duplicateValues", "uniqueValues", "top10", "top10", "containsBlanks", "notContainsBlanks", "containsErrors", "notContainsErrors", "expression", "colorScale", "colorScale", "dataBar", "iconSet", }; /* 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 dimensions are valid but don't store them. */ err = _check_dimensions(self, last_row, last_col, LXW_TRUE, LXW_TRUE); if (err) return err; /* Check the validation type is in correct enum range. */ if (user_options->type <= LXW_CONDITIONAL_TYPE_NONE || user_options->type >= LXW_CONDITIONAL_TYPE_LAST) { LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " "invalid type value (%d).", user_options->type); return LXW_ERROR_PARAMETER_VALIDATION; } /* Create a copy of the parameters from the user data validation. */ cond_format = calloc(1, sizeof(lxw_cond_format_obj)); GOTO_LABEL_ON_MEM_ERROR(cond_format, error); /* Create the data validation range. */ if (first_row == last_row && first_col == last_col) lxw_rowcol_to_cell(cond_format->sqref, first_row, first_col); else lxw_rowcol_to_range(cond_format->sqref, first_row, first_col, last_row, last_col); /* Store the first cell string for text and date rules. */ lxw_rowcol_to_cell(cond_format->first_cell, first_row, first_col); /* Overwrite the sqref range with a user supplied set of ranges. */ if (user_options->multi_range) { if (strlen(user_options->multi_range) >= LXW_MAX_ATTRIBUTE_LENGTH) { LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " "multi_range >= limit = %d.", LXW_MAX_ATTRIBUTE_LENGTH); err = LXW_ERROR_PARAMETER_VALIDATION; goto error; } LXW_ATTRIBUTE_COPY(cond_format->sqref, user_options->multi_range); } /* Get the conditional format dxf format index. */ if (user_options->format) cond_format->dxf_index = lxw_format_get_dxf_index(user_options->format); else cond_format->dxf_index = LXW_PROPERTY_UNSET; /* Set some common option for all validation types. */ cond_format->type = user_options->type; cond_format->criteria = user_options->criteria; cond_format->stop_if_true = user_options->stop_if_true; cond_format->type_string = lxw_strdup(type_strings[cond_format->type]); /* Validate the user input for various types of rules. */ if (user_options->type == LXW_CONDITIONAL_TYPE_CELL || cond_format->type == LXW_CONDITIONAL_TYPE_DUPLICATE || cond_format->type == LXW_CONDITIONAL_TYPE_UNIQUE) { err = _validate_conditional_cell(cond_format, user_options); if (err) goto error; } else if (user_options->type == LXW_CONDITIONAL_TYPE_TEXT) { err = _validate_conditional_text(cond_format, user_options); if (err) goto error; } else if (user_options->type == LXW_CONDITIONAL_TYPE_TIME_PERIOD) { err = _validate_conditional_time_period(user_options); if (err) goto error; } else if (user_options->type == LXW_CONDITIONAL_TYPE_AVERAGE) { err = _validate_conditional_average(user_options); if (err) goto error; } else if (cond_format->type == LXW_CONDITIONAL_TYPE_TOP || cond_format->type == LXW_CONDITIONAL_TYPE_BOTTOM) { err = _validate_conditional_top(cond_format, user_options); if (err) goto error; } else if (user_options->type == LXW_CONDITIONAL_TYPE_FORMULA) { err = _validate_conditional_formula(cond_format, user_options); if (err) goto error; } else if (cond_format->type == LXW_CONDITIONAL_2_COLOR_SCALE || cond_format->type == LXW_CONDITIONAL_3_COLOR_SCALE) { err = _validate_conditional_scale(cond_format, user_options); if (err) goto error; } else if (cond_format->type == LXW_CONDITIONAL_DATA_BAR) { err = _validate_conditional_data_bar(self, cond_format, user_options); if (err) goto error; } else if (cond_format->type == LXW_CONDITIONAL_TYPE_ICON_SETS) { err = _validate_conditional_icons(user_options); if (err) goto error; cond_format->icon_style = user_options->icon_style; cond_format->reverse_icons = user_options->reverse_icons; cond_format->icons_only = user_options->icons_only; } /* Set the priority based on the order of adding. */ cond_format->dxf_priority = ++self->dxf_priority; /* Store the conditional format object. */ err = _store_conditional_format_object(self, cond_format); if (err) goto error; else return LXW_NO_ERROR; error: _free_cond_format(cond_format); return err; } /* * Add a conditional format to a worksheet, for a cell. */ lxw_error worksheet_conditional_format_cell(lxw_worksheet *self, lxw_row_t row, lxw_col_t col, lxw_conditional_format *options) { return worksheet_conditional_format_range(self, row, col, row, col, options); } /* * Insert a button object into the worksheet. */ lxw_error worksheet_insert_button(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, lxw_button_options *options) { lxw_error err; lxw_vml_obj *button; err = _check_dimensions(self, row_num, col_num, LXW_TRUE, LXW_TRUE); if (err) return err; button = calloc(1, sizeof(lxw_vml_obj)); GOTO_LABEL_ON_MEM_ERROR(button, mem_error); button->row = row_num; button->col = col_num; /* Set user and default parameters for the button. */ err = _get_button_params(button, 1 + self->num_buttons, options); if (err) goto mem_error; /* Calculate the worksheet position of the button. */ _worksheet_position_vml_object(self, button); self->has_vml = LXW_TRUE; self->has_buttons = LXW_TRUE; self->num_buttons++; STAILQ_INSERT_TAIL(self->button_objs, button, list_pointers); return LXW_NO_ERROR; mem_error: if (button) _free_vml_object(button); return LXW_ERROR_MEMORY_MALLOC_FAILED; } /* * Set the VBA name for the worksheet. */ lxw_error worksheet_set_vba_name(lxw_worksheet *self, const char *name) { if (!name) { LXW_WARN("worksheet_set_vba_name(): " "name must be specified."); return LXW_ERROR_NULL_PARAMETER_IGNORED; } self->vba_codename = lxw_strdup(name); return LXW_NO_ERROR; } /* * Set the default author of the cell comments. */ void worksheet_set_comments_author(lxw_worksheet *self, const char *author) { self->comment_author = lxw_strdup(author); } /* * Make any comments in the worksheet visible, unless explicitly hidden. */ void worksheet_show_comments(lxw_worksheet *self) { self->comment_display_default = LXW_COMMENT_DISPLAY_VISIBLE; } /* * Ignore various Excel errors/warnings in a worksheet for user defined ranges. */ lxw_error worksheet_ignore_errors(lxw_worksheet *self, uint8_t type, const char *range) { if (!range) { LXW_WARN("worksheet_ignore_errors(): " "'range' must be specified."); return LXW_ERROR_NULL_PARAMETER_IGNORED; } if (type <= 0 || type >= LXW_IGNORE_LAST_OPTION) { LXW_WARN("worksheet_ignore_errors(): " "unknown option in 'type'."); return LXW_ERROR_NULL_PARAMETER_IGNORED; } /* Set the ranges to be ignored. */ if (type == LXW_IGNORE_NUMBER_STORED_AS_TEXT) { free(self->ignore_number_stored_as_text); self->ignore_number_stored_as_text = lxw_strdup(range); } else if (type == LXW_IGNORE_EVAL_ERROR) { free(self->ignore_eval_error); self->ignore_eval_error = lxw_strdup(range); } else if (type == LXW_IGNORE_FORMULA_DIFFERS) { free(self->ignore_formula_differs); self->ignore_formula_differs = lxw_strdup(range); } else if (type == LXW_IGNORE_FORMULA_RANGE) { free(self->ignore_formula_range); self->ignore_formula_range = lxw_strdup(range); } else if (type == LXW_IGNORE_FORMULA_UNLOCKED) { free(self->ignore_formula_unlocked); self->ignore_formula_unlocked = lxw_strdup(range); } else if (type == LXW_IGNORE_EMPTY_CELL_REFERENCE) { free(self->ignore_empty_cell_reference); self->ignore_empty_cell_reference = lxw_strdup(range); } else if (type == LXW_IGNORE_LIST_DATA_VALIDATION) { free(self->ignore_list_data_validation); self->ignore_list_data_validation = lxw_strdup(range); } else if (type == LXW_IGNORE_CALCULATED_COLUMN) { free(self->ignore_calculated_column); self->ignore_calculated_column = lxw_strdup(range); } else if (type == LXW_IGNORE_TWO_DIGIT_TEXT_YEAR) { free(self->ignore_two_digit_text_year); self->ignore_two_digit_text_year = lxw_strdup(range); } self->has_ignore_errors = LXW_TRUE; return LXW_NO_ERROR; } writexl/src/libxlsxwriter/metadata.c0000644000176200001440000001502214747162622017420 0ustar liggesusers/***************************************************************************** * metadata - A library for creating Excel XLSX metadata files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ #include "xlsxwriter/xmlwriter.h" #include "xlsxwriter/metadata.h" #include "xlsxwriter/utility.h" /* * Forward declarations. */ /***************************************************************************** * * Private functions. * ****************************************************************************/ /* * Create a new metadata object. */ lxw_metadata * lxw_metadata_new(void) { lxw_metadata *metadata = calloc(1, sizeof(lxw_metadata)); GOTO_LABEL_ON_MEM_ERROR(metadata, mem_error); return metadata; mem_error: lxw_metadata_free(metadata); return NULL; } /* * Free a metadata object. */ void lxw_metadata_free(lxw_metadata *metadata) { if (!metadata) return; free(metadata); } /***************************************************************************** * * XML functions. * ****************************************************************************/ /* * Write the XML declaration. */ STATIC void _metadata_xml_declaration(lxw_metadata *self) { lxw_xml_declaration(self->file); } /* * Write the element. */ STATIC void _metadata_write_metadata(lxw_metadata *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char xmlns[] = "http://schemas.openxmlformats.org/" "spreadsheetml/2006/main"; char xmlns_xda[] = "http://schemas.microsoft.com/office/" "spreadsheetml/2017/dynamicarray"; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); LXW_PUSH_ATTRIBUTES_STR("xmlns:xda", xmlns_xda); lxw_xml_start_tag(self->file, "metadata", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _metadata_write_metadata_type(lxw_metadata *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("name", "XLDAPR"); LXW_PUSH_ATTRIBUTES_INT("minSupportedVersion", 120000); LXW_PUSH_ATTRIBUTES_INT("copy", 1); LXW_PUSH_ATTRIBUTES_INT("pasteAll", 1); LXW_PUSH_ATTRIBUTES_INT("pasteValues", 1); LXW_PUSH_ATTRIBUTES_INT("merge", 1); LXW_PUSH_ATTRIBUTES_INT("splitFirst", 1); LXW_PUSH_ATTRIBUTES_INT("rowColShift", 1); LXW_PUSH_ATTRIBUTES_INT("clearFormats", 1); LXW_PUSH_ATTRIBUTES_INT("clearComments", 1); LXW_PUSH_ATTRIBUTES_INT("assign", 1); LXW_PUSH_ATTRIBUTES_INT("coerce", 1); LXW_PUSH_ATTRIBUTES_INT("cellMeta", 1); lxw_xml_empty_tag(self->file, "metadataType", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _metadata_write_metadata_types(lxw_metadata *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("count", 1); lxw_xml_start_tag(self->file, "metadataTypes", &attributes); /* Write the metadataType element. */ _metadata_write_metadata_type(self); lxw_xml_end_tag(self->file, "metadataTypes"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _metadata_write_xda_dynamic_array_properties(lxw_metadata *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("fDynamic", "1"); LXW_PUSH_ATTRIBUTES_STR("fCollapsed", "0"); lxw_xml_empty_tag(self->file, "xda:dynamicArrayProperties", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _metadata_write_ext(lxw_metadata *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("uri", "{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}"); lxw_xml_start_tag(self->file, "ext", &attributes); /* Write the xda:dynamicArrayProperties element. */ _metadata_write_xda_dynamic_array_properties(self); lxw_xml_end_tag(self->file, "ext"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _metadata_write_future_metadata(lxw_metadata *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("name", "XLDAPR"); LXW_PUSH_ATTRIBUTES_INT("count", 1); lxw_xml_start_tag(self->file, "futureMetadata", &attributes); lxw_xml_start_tag(self->file, "bk", NULL); lxw_xml_start_tag(self->file, "extLst", NULL); /* Write the ext element. */ _metadata_write_ext(self); lxw_xml_end_tag(self->file, "extLst"); lxw_xml_end_tag(self->file, "bk"); lxw_xml_end_tag(self->file, "futureMetadata"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _metadata_write_rc(lxw_metadata *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("t", "1"); LXW_PUSH_ATTRIBUTES_STR("v", "0"); lxw_xml_empty_tag(self->file, "rc", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _metadata_write_cell_metadata(lxw_metadata *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, "cellMetadata", &attributes); lxw_xml_start_tag(self->file, "bk", NULL); /* Write the rc element. */ _metadata_write_rc(self); lxw_xml_end_tag(self->file, "bk"); lxw_xml_end_tag(self->file, "cellMetadata"); LXW_FREE_ATTRIBUTES(); } /***************************************************************************** * * XML file assembly functions. * ****************************************************************************/ /* * Assemble and write the XML file. */ void lxw_metadata_assemble_xml_file(lxw_metadata *self) { /* Write the XML declaration. */ _metadata_xml_declaration(self); /* Write the metadata element. */ _metadata_write_metadata(self); /* Write the metadataTypes element. */ _metadata_write_metadata_types(self); /* Write the futureMetadata element. */ _metadata_write_future_metadata(self); /* Write the cellMetadata element. */ _metadata_write_cell_metadata(self); lxw_xml_end_tag(self->file, "metadata"); } writexl/src/libxlsxwriter/xmlwriter.c0000644000176200001440000002667214747162622017712 0ustar liggesusers/***************************************************************************** * xmlwriter - A base library for libxlsxwriter libraries. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ #include #include #include #include #include "xlsxwriter/xmlwriter.h" #define LXW_AMP "&" #define LXW_LT "<" #define LXW_GT ">" #define LXW_QUOT """ #define LXW_NL " " /* 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); } /* * Write an XML element for rich strings, without encoding. */ void lxw_xml_rich_si_element(FILE * xmlfile, const char *string) { fprintf(xmlfile, "%s", string); } /* * 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 '&': memcpy(p_encoded, LXW_AMP, sizeof(LXW_AMP) - 1); p_encoded += sizeof(LXW_AMP) - 1; break; case '<': memcpy(p_encoded, LXW_LT, sizeof(LXW_LT) - 1); p_encoded += sizeof(LXW_LT) - 1; break; case '>': memcpy(p_encoded, LXW_GT, sizeof(LXW_GT) - 1); p_encoded += sizeof(LXW_GT) - 1; break; case '"': memcpy(p_encoded, LXW_QUOT, sizeof(LXW_QUOT) - 1); p_encoded += sizeof(LXW_QUOT) - 1; break; case '\n': memcpy(p_encoded, LXW_NL, sizeof(LXW_NL) - 1); p_encoded += sizeof(LXW_NL) - 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 '&': memcpy(p_encoded, LXW_AMP, sizeof(LXW_AMP) - 1); p_encoded += sizeof(LXW_AMP) - 1; break; case '<': memcpy(p_encoded, LXW_LT, sizeof(LXW_LT) - 1); p_encoded += sizeof(LXW_LT) - 1; break; case '>': memcpy(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; } /* * Check for control characters in strings. */ uint8_t lxw_has_control_characters(const char *string) { while (string) { /* 0xE0 == 0b11100000 masks values > 0x19 == 0b00011111. */ if (!(*string & 0xE0) && *string != 0x0A && *string != 0x09) return LXW_TRUE; string++; } return LXW_FALSE; } /* * Escape control characters in strings 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; } /* * Escape special characters in URL strings with with %XX. */ char * lxw_escape_url_characters(const char *string, uint8_t escape_hash) { size_t escape_len = sizeof("%XX") - 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 ' ': case '"': case '<': case '>': case '[': case ']': case '`': case '^': case '{': case '}': lxw_snprintf(p_encoded, escape_len + 1, "%%%2x", *string); p_encoded += escape_len; break; case '#': /* This is only escaped for "external:" style links. */ if (escape_hash) { lxw_snprintf(p_encoded, escape_len + 1, "%%%2x", *string); p_encoded += escape_len; } else { *p_encoded = *string; p_encoded++; } break; case '%': /* Only escape % if it isn't already an escape. */ if (!isxdigit(*(string + 1)) || !isxdigit(*(string + 2))) { lxw_snprintf(p_encoded, escape_len + 1, "%%%2x", *string); p_encoded += escape_len; } else { *p_encoded = *string; p_encoded++; } 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, "&<>\"\n")) { 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, uint64_t value) { struct xml_attribute *attribute = malloc(sizeof(struct xml_attribute)); LXW_ATTRIBUTE_COPY(attribute->key, key); #if defined(_MSC_VER) lxw_snprintf(attribute->value, LXW_MAX_ATTRIBUTE_LENGTH, "%lld", value); #else lxw_snprintf(attribute->value, LXW_MAX_ATTRIBUTE_LENGTH, "%ld", (long) value); #endif 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_sprintf_dbl(attribute->value, value); return attribute; } writexl/src/libxlsxwriter/shared_strings.c0000644000176200001440000001543514747162622020667 0ustar liggesusers/***************************************************************************** * shared_strings - A library for creating Excel XLSX sst files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, 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(void) { /* 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 (lxw_has_control_characters(string)) { 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 for rich strings. */ STATIC void _write_rich_si(lxw_sst *self, char *string) { lxw_xml_rich_si_element(self->file, 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. */ if (sst_element->is_rich_string) _write_rich_si(self, sst_element->string); else _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, uint8_t is_rich_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); element->is_rich_string = is_rich_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; } writexl/src/libxlsxwriter/drawing.c0000644000176200001440000005577414747162622017315 0ustar liggesusers/***************************************************************************** * drawing - A library for creating Excel XLSX drawing files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ #include "xlsxwriter/xmlwriter.h" #include "xlsxwriter/common.h" #include "xlsxwriter/drawing.h" #include "xlsxwriter/worksheet.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(void) { 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->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 main part of the and elements. */ STATIC void _drawing_write_coords(lxw_drawing *self, lxw_drawing_coords *coords) { char data[LXW_UINT32_T_LENGTH]; 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); } /* * Write the element. */ STATIC void _drawing_write_from(lxw_drawing *self, lxw_drawing_coords *coords) { lxw_xml_start_tag(self->file, "xdr:from", NULL); _drawing_write_coords(self, coords); lxw_xml_end_tag(self->file, "xdr:from"); } /* * Write the element. */ STATIC void _drawing_write_to(lxw_drawing *self, lxw_drawing_coords *coords) { lxw_xml_start_tag(self->file, "xdr:to", NULL); _drawing_write_coords(self, coords); lxw_xml_end_tag(self->file, "xdr:to"); } /* * Write the element. */ STATIC void _drawing_write_a_hlink_click(lxw_drawing *self, uint32_t rel_index, char *tip) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char xmlns_r[] = "http://schemas.openxmlformats.org/" "officeDocument/2006/relationships"; char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", rel_index); LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("xmlns:r", xmlns_r); LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); if (tip) LXW_PUSH_ATTRIBUTES_STR("tooltip", tip); lxw_xml_empty_tag(self->file, "a:hlinkClick", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _drawing_write_a16_creation_id(lxw_drawing *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char xmlns[] = "http://schemas.microsoft.com/office/drawing/2014/main"; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("xmlns:a16", xmlns); LXW_PUSH_ATTRIBUTES_STR("id", "{00000000-0008-0000-0000-000002000000}"); lxw_xml_empty_tag(self->file, "a16:creationId", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _workbook_write_adec_decorative(lxw_drawing *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char xmlns[] = "http://schemas.microsoft.com/office/drawing/2017/decorative"; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("xmlns:adec", xmlns); LXW_PUSH_ATTRIBUTES_STR("val", "1"); lxw_xml_empty_tag(self->file, "adec:decorative", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _drawing_write_uri_ext(lxw_drawing *self, char *uri) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("uri", uri); lxw_xml_start_tag(self->file, "a:ext", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the decorative elements. */ STATIC void _workbook_write_decorative(lxw_drawing *self) { lxw_xml_start_tag(self->file, "a:extLst", NULL); _drawing_write_uri_ext(self, "{FF2B5EF4-FFF2-40B4-BE49-F238E27FC236}"); _drawing_write_a16_creation_id(self); lxw_xml_end_tag(self->file, "a:ext"); _drawing_write_uri_ext(self, "{C183D7F6-B498-43B3-948B-1728B52AA6E4}"); _workbook_write_adec_decorative(self); lxw_xml_end_tag(self->file, "a:ext"); lxw_xml_end_tag(self->file, "a:extLst"); } /* * Write the element. */ STATIC void _drawing_write_c_nv_pr(lxw_drawing *self, char *object_name, uint32_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 && drawing_object->description && strlen(drawing_object->description) && !drawing_object->decorative) { LXW_PUSH_ATTRIBUTES_STR("descr", drawing_object->description); } if (drawing_object && (drawing_object->url_rel_index || drawing_object->decorative)) { lxw_xml_start_tag(self->file, "xdr:cNvPr", &attributes); if (drawing_object->url_rel_index) { /* Write the a:hlinkClick element. */ _drawing_write_a_hlink_click(self, drawing_object->url_rel_index, drawing_object->tip); } if (drawing_object->decorative) { _workbook_write_decorative(self); } lxw_xml_end_tag(self->file, "xdr:cNvPr"); } else { 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, uint32_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, uint32_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, uint32_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, uint32_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, drawing_object->rel_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_a_graphic_frame_locks(lxw_drawing *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("noGrp", 1); lxw_xml_empty_tag(self->file, "a:graphicFrameLocks", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _drawing_write_c_nv_graphic_frame_pr(lxw_drawing *self) { if (self->embedded) { lxw_xml_empty_tag(self->file, "xdr:cNvGraphicFramePr", NULL); } else { lxw_xml_start_tag(self->file, "xdr:cNvGraphicFramePr", NULL); /* Write the a:graphicFrameLocks element. */ _drawing_write_a_graphic_frame_locks(self); lxw_xml_end_tag(self->file, "xdr:cNvGraphicFramePr"); } } /* * Write the element. */ STATIC void _drawing_write_nv_graphic_frame_pr(lxw_drawing *self, uint32_t index, lxw_drawing_object *drawing_object) { lxw_xml_start_tag(self->file, "xdr:nvGraphicFramePr", NULL); /* Write the xdr:cNvPr element. */ _drawing_write_c_nv_pr(self, "Chart", index, drawing_object); /* 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, uint32_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, uint32_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, uint32_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, uint32_t index, uint32_t rel_index, lxw_drawing_object *drawing_object) { 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, drawing_object); /* Write the xdr:xfrm element. */ _drawing_write_xfrm(self); /* Write the a:graphic element. */ _drawing_write_a_graphic(self, rel_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, uint32_t index, lxw_drawing_object *drawing_object) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); if (drawing_object->anchor == LXW_OBJECT_MOVE_DONT_SIZE) LXW_PUSH_ATTRIBUTES_STR("editAs", "oneCell"); else if (drawing_object->anchor == LXW_OBJECT_DONT_MOVE_DONT_SIZE) LXW_PUSH_ATTRIBUTES_STR("editAs", "absolute"); 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->type == LXW_DRAWING_CHART) { /* Write the xdr:graphicFrame element for charts. */ _drawing_write_graphic_frame(self, index, drawing_object->rel_index, drawing_object); } else if (drawing_object->type == LXW_DRAWING_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(); } /* * Write the element. */ STATIC void _drawing_write_ext(lxw_drawing *self, uint32_t cx, uint32_t cy) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("cx", cx); LXW_PUSH_ATTRIBUTES_INT("cy", cy); lxw_xml_empty_tag(self->file, "xdr:ext", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _drawing_write_pos(lxw_drawing *self, int32_t x, int32_t y) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("x", x); LXW_PUSH_ATTRIBUTES_INT("y", y); lxw_xml_empty_tag(self->file, "xdr:pos", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _drawing_write_absolute_anchor(lxw_drawing *self, uint32_t frame_index) { lxw_xml_start_tag(self->file, "xdr:absoluteAnchor", NULL); if (self->orientation == LXW_LANDSCAPE) { /* Write the xdr:pos element. */ _drawing_write_pos(self, 0, 0); /* Write the xdr:ext element. */ _drawing_write_ext(self, 9308969, 6078325); } else { /* Write the xdr:pos element. */ _drawing_write_pos(self, 0, -47625); /* Write the xdr:ext element. */ _drawing_write_ext(self, 6162675, 6124575); } _drawing_write_graphic_frame(self, frame_index, frame_index, NULL); /* Write the xdr:clientData element. */ _drawing_write_client_data(self); lxw_xml_end_tag(self->file, "xdr:absoluteAnchor"); } /***************************************************************************** * * XML file assembly functions. * ****************************************************************************/ /* * Assemble and write the XML file. */ void lxw_drawing_assemble_xml_file(lxw_drawing *self) { uint32_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++; } } else { /* Write the xdr:absoluteAnchor element. Mainly for chartsheets. */ _drawing_write_absolute_anchor(self, 1); } lxw_xml_end_tag(self->file, "xdr:wsDr"); } /***************************************************************************** * * Public functions. * ****************************************************************************/ writexl/src/libxlsxwriter/workbook.c0000644000176200001440000025142114747162622017502 0ustar liggesusers/***************************************************************************** * workbook - A library for creating Excel XLSX workbook files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, 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 _worksheet_name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2); STATIC int _chartsheet_name_cmp(lxw_chartsheet_name *name1, lxw_chartsheet_name *name2); STATIC int _image_md5_cmp(lxw_image_md5 *tuple1, lxw_image_md5 *tuple2); #ifndef __clang_analyzer__ LXW_RB_GENERATE_WORKSHEET_NAMES(lxw_worksheet_names, lxw_worksheet_name, tree_pointers, _worksheet_name_cmp); LXW_RB_GENERATE_CHARTSHEET_NAMES(lxw_chartsheet_names, lxw_chartsheet_name, tree_pointers, _chartsheet_name_cmp); LXW_RB_GENERATE_IMAGE_MD5S(lxw_image_md5s, lxw_image_md5, tree_pointers, _image_md5_cmp); #endif /* * Forward declarations. */ /***************************************************************************** * * Private functions. * ****************************************************************************/ /* * Comparators for the sheet names structure red/black tree. */ STATIC int _worksheet_name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2) { return lxw_strcasecmp(name1->name, name2->name); } STATIC int _chartsheet_name_cmp(lxw_chartsheet_name *name1, lxw_chartsheet_name *name2) { return lxw_strcasecmp(name1->name, name2->name); } STATIC int _image_md5_cmp(lxw_image_md5 *tuple1, lxw_image_md5 *tuple2) { return strcmp(tuple1->md5, tuple2->md5); } /* * Free workbook properties. */ STATIC void _free_doc_properties(lxw_doc_properties *properties) { if (properties) { free((void *) properties->title); free((void *) properties->subject); free((void *) properties->author); free((void *) properties->manager); free((void *) properties->company); free((void *) properties->category); free((void *) properties->keywords); free((void *) properties->comments); free((void *) properties->status); free((void *) 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_sheet *sheet; struct lxw_worksheet_name *worksheet_name; struct lxw_worksheet_name *next_worksheet_name; struct lxw_chartsheet_name *chartsheet_name; struct lxw_chartsheet_name *next_chartsheet_name; struct lxw_image_md5 *image_md5; struct lxw_image_md5 *next_image_md5; 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 sheets in the workbook. */ if (workbook->sheets) { while (!STAILQ_EMPTY(workbook->sheets)) { sheet = STAILQ_FIRST(workbook->sheets); if (sheet->is_chartsheet) lxw_chartsheet_free(sheet->u.chartsheet); else lxw_worksheet_free(sheet->u.worksheet); STAILQ_REMOVE_HEAD(workbook->sheets, list_pointers); free(sheet); } free(workbook->sheets); } /* Free the sheet lists. The worksheet objects are freed above. */ free(workbook->worksheets); free(workbook->chartsheets); /* 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_worksheet_name) { next_worksheet_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); } if (workbook->chartsheet_names) { for (chartsheet_name = RB_MIN(lxw_chartsheet_names, workbook->chartsheet_names); chartsheet_name; chartsheet_name = next_chartsheet_name) { next_chartsheet_name = RB_NEXT(lxw_chartsheet_names, workbook->chartsheet_name, chartsheet_name); RB_REMOVE(lxw_chartsheet_names, workbook->chartsheet_names, chartsheet_name); free(chartsheet_name); } free(workbook->chartsheet_names); } if (workbook->image_md5s) { for (image_md5 = RB_MIN(lxw_image_md5s, workbook->image_md5s); image_md5; image_md5 = next_image_md5) { next_image_md5 = RB_NEXT(lxw_image_md5s, workbook->image_md5, image_md5); RB_REMOVE(lxw_image_md5s, workbook->image_md5s, image_md5); free(image_md5->md5); free(image_md5); } free(workbook->image_md5s); } if (workbook->header_image_md5s) { for (image_md5 = RB_MIN(lxw_image_md5s, workbook->header_image_md5s); image_md5; image_md5 = next_image_md5) { next_image_md5 = RB_NEXT(lxw_image_md5s, workbook->image_md5, image_md5); RB_REMOVE(lxw_image_md5s, workbook->header_image_md5s, image_md5); free(image_md5->md5); free(image_md5); } free(workbook->header_image_md5s); } if (workbook->background_md5s) { for (image_md5 = RB_MIN(lxw_image_md5s, workbook->background_md5s); image_md5; image_md5 = next_image_md5) { next_image_md5 = RB_NEXT(lxw_image_md5s, workbook->image_md5, image_md5); RB_REMOVE(lxw_image_md5s, workbook->background_md5s, image_md5); free(image_md5->md5); free(image_md5); } free(workbook->background_md5s); } lxw_hash_free(workbook->used_xf_formats); lxw_hash_free(workbook->used_dxf_formats); lxw_sst_free(workbook->sst); free((void *) workbook->options.tmpdir); free(workbook->ordered_charts); free(workbook->vba_project); free(workbook->vba_project_signature); free(workbook->vba_codename); 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; int32_t index = 0; STAILQ_FOREACH(format, self->formats, list_pointers) { /* Skip the hyperlink format. */ if (index != 1) lxw_format_get_xf_index(format); index++; } } /* * 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 = LXW_TRUE; lxw_insert_hash_element(fonts, key, font_index, sizeof(lxw_font)); index++; } } } lxw_hash_free(fonts); /* For DXF formats we only need to check if the properties have changed. */ LXW_FOREACH_ORDERED(used_format_element, self->used_dxf_formats) { lxw_format *format = (lxw_format *) used_format_element->value; /* The only font properties that can change for a DXF format are: * color, bold, italic, underline and strikethrough. */ if (format->font_color || format->bold || format->italic || format->underline || format->font_strikeout) { format->has_dxf_font = LXW_TRUE; } } 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++; } } } /* For DXF formats we only need to check if the properties have changed. */ LXW_FOREACH_ORDERED(used_format_element, self->used_dxf_formats) { lxw_format *format = (lxw_format *) used_format_element->value; if (format->left || format->right || format->top || format->bottom) { format->has_dxf_border = LXW_TRUE; } } 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)); /* For DXF formats we only need to check if the properties have changed. */ LXW_FOREACH_ORDERED(used_format_element, self->used_dxf_formats) { lxw_format *format = (lxw_format *) used_format_element->value; if (format->pattern || format->bg_color || format->fg_color) { format->has_dxf_fill = LXW_TRUE; format->dxf_bg_color = format->bg_color; format->dxf_fg_color = format->fg_color; } } 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->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; 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. */ if (*format->num_format) { char num_format[LXW_FORMAT_FIELD_LEN] = { 0 }; lxw_snprintf(num_format, LXW_FORMAT_FIELD_LEN, "%s", format->num_format); /* Look up the num_format in the hash table. */ hash_element = lxw_hash_key_exists(num_formats, num_format, LXW_FORMAT_FIELD_LEN); 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, format->num_format, num_format_index, LXW_FORMAT_FIELD_LEN); index++; num_format_count++; } } } LXW_FOREACH_ORDERED(used_format_element, self->used_dxf_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. */ if (*format->num_format) { char num_format[LXW_FORMAT_FIELD_LEN] = { 0 }; lxw_snprintf(num_format, LXW_FORMAT_FIELD_LEN, "%s", format->num_format); /* Look up the num_format in the hash table. */ hash_element = lxw_hash_key_exists(num_formats, num_format, LXW_FORMAT_FIELD_LEN); 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, format->num_format, num_format_index, LXW_FORMAT_FIELD_LEN); index++; /* Don't update num_format_count for DXF formats. */ } } } 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_sheet *sheet; 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(sheet, self->sheets, list_pointers) { if (sheet->is_chartsheet) continue; else worksheet = sheet->u.worksheet; 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_in_row(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. Note, 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) { if (!range) return; _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; uint16_t i; 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); for (i = 0; i < series->data_label_count; i++) { lxw_chart_custom_label *data_label = &series->data_labels[i]; _populate_range(self, data_label->range); } } } } /* * Store the image types used in the workbook to update the content types. */ STATIC void _store_image_type(lxw_workbook *self, uint8_t image_type) { if (image_type == LXW_IMAGE_PNG) self->has_png = LXW_TRUE; if (image_type == LXW_IMAGE_JPEG) self->has_jpeg = LXW_TRUE; if (image_type == LXW_IMAGE_BMP) self->has_bmp = LXW_TRUE; if (image_type == LXW_IMAGE_GIF) self->has_gif = LXW_TRUE; } /* * Iterate through the worksheets and set up any chart or image drawings. */ STATIC void _prepare_drawings(lxw_workbook *self) { lxw_sheet *sheet; lxw_worksheet *worksheet; lxw_object_properties *object_props; uint32_t chart_ref_id = 0; uint32_t image_ref_id = 0; uint32_t ref_id = 0; uint32_t drawing_id = 0; uint8_t is_chartsheet; lxw_image_md5 tmp_image_md5; lxw_image_md5 *new_image_md5 = NULL; lxw_image_md5 *found_duplicate_image = NULL; uint8_t i; STAILQ_FOREACH(sheet, self->sheets, list_pointers) { if (sheet->is_chartsheet) { worksheet = sheet->u.chartsheet->worksheet; is_chartsheet = LXW_TRUE; } else { worksheet = sheet->u.worksheet; is_chartsheet = LXW_FALSE; } if (STAILQ_EMPTY(worksheet->image_props) && STAILQ_EMPTY(worksheet->chart_data) && !worksheet->has_header_vml && !worksheet->has_background_image) { continue; } drawing_id++; /* Prepare background images. */ if (worksheet->has_background_image) { object_props = worksheet->background_image; _store_image_type(self, object_props->image_type); /* Check for duplicate images and only store the first instance. */ if (object_props->md5) { tmp_image_md5.md5 = object_props->md5; found_duplicate_image = RB_FIND(lxw_image_md5s, self->background_md5s, &tmp_image_md5); } if (found_duplicate_image) { ref_id = found_duplicate_image->id; object_props->is_duplicate = LXW_TRUE; } else { image_ref_id++; ref_id = image_ref_id; #ifndef USE_NO_MD5 new_image_md5 = calloc(1, sizeof(lxw_image_md5)); #endif if (new_image_md5 && object_props->md5) { new_image_md5->id = ref_id; new_image_md5->md5 = lxw_strdup(object_props->md5); RB_INSERT(lxw_image_md5s, self->background_md5s, new_image_md5); } } lxw_worksheet_prepare_background(worksheet, ref_id, object_props); } /* Prepare worksheet images. */ STAILQ_FOREACH(object_props, worksheet->image_props, list_pointers) { /* Ignore background image added above. */ if (object_props->is_background) continue; _store_image_type(self, object_props->image_type); /* Check for duplicate images and only store the first instance. */ if (object_props->md5) { tmp_image_md5.md5 = object_props->md5; found_duplicate_image = RB_FIND(lxw_image_md5s, self->image_md5s, &tmp_image_md5); } if (found_duplicate_image) { ref_id = found_duplicate_image->id; object_props->is_duplicate = LXW_TRUE; } else { image_ref_id++; ref_id = image_ref_id; #ifndef USE_NO_MD5 new_image_md5 = calloc(1, sizeof(lxw_image_md5)); #endif if (new_image_md5 && object_props->md5) { new_image_md5->id = ref_id; new_image_md5->md5 = lxw_strdup(object_props->md5); RB_INSERT(lxw_image_md5s, self->image_md5s, new_image_md5); } } lxw_worksheet_prepare_image(worksheet, ref_id, drawing_id, object_props); } /* Prepare worksheet charts. */ STAILQ_FOREACH(object_props, worksheet->chart_data, list_pointers) { chart_ref_id++; lxw_worksheet_prepare_chart(worksheet, chart_ref_id, drawing_id, object_props, is_chartsheet); if (object_props->chart) STAILQ_INSERT_TAIL(self->ordered_charts, object_props->chart, ordered_list_pointers); } /* Prepare worksheet header/footer images. */ for (i = 0; i < LXW_HEADER_FOOTER_OBJS_MAX; i++) { object_props = *worksheet->header_footer_objs[i]; if (!object_props) continue; _store_image_type(self, object_props->image_type); /* Check for duplicate images and only store the first instance. */ if (object_props->md5) { tmp_image_md5.md5 = object_props->md5; found_duplicate_image = RB_FIND(lxw_image_md5s, self->header_image_md5s, &tmp_image_md5); } if (found_duplicate_image) { ref_id = found_duplicate_image->id; object_props->is_duplicate = LXW_TRUE; } else { image_ref_id++; ref_id = image_ref_id; #ifndef USE_NO_MD5 new_image_md5 = calloc(1, sizeof(lxw_image_md5)); #endif if (new_image_md5 && object_props->md5) { new_image_md5->id = ref_id; new_image_md5->md5 = lxw_strdup(object_props->md5); RB_INSERT(lxw_image_md5s, self->header_image_md5s, new_image_md5); } } lxw_worksheet_prepare_header_image(worksheet, ref_id, object_props); } } self->drawing_count = drawing_id; } /* * Iterate through the worksheets and set up the VML objects. */ STATIC void _prepare_vml(lxw_workbook *self) { lxw_worksheet *worksheet; lxw_sheet *sheet; uint32_t comment_id = 0; uint32_t vml_drawing_id = 0; uint32_t vml_data_id = 1; uint32_t vml_header_id = 0; uint32_t vml_shape_id = 1024; uint32_t comment_count = 0; STAILQ_FOREACH(sheet, self->sheets, list_pointers) { if (sheet->is_chartsheet) continue; else worksheet = sheet->u.worksheet; if (!worksheet->has_vml && !worksheet->has_header_vml) continue; if (worksheet->has_vml) { self->has_vml = LXW_TRUE; if (worksheet->has_comments) { self->comment_count++; comment_id++; self->has_comments = LXW_TRUE; } vml_drawing_id++; comment_count = lxw_worksheet_prepare_vml_objects(worksheet, vml_data_id, vml_shape_id, vml_drawing_id, comment_id); /* Each VML should start with a shape id incremented by 1024. */ vml_data_id += 1 * ((1024 + comment_count) / 1024); vml_shape_id += 1024 * ((1024 + comment_count) / 1024); } if (worksheet->has_header_vml) { self->has_vml = LXW_TRUE; vml_drawing_id++; vml_header_id++; lxw_worksheet_prepare_header_vml_objects(worksheet, vml_header_id, vml_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; lxw_sheet *sheet; 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(sheet, self->sheets, list_pointers) { if (sheet->is_chartsheet) continue; else worksheet = sheet->u.worksheet; /* * 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); } } } } /* * Iterate through the worksheets and set up the table objects. */ STATIC void _prepare_tables(lxw_workbook *self) { lxw_worksheet *worksheet; lxw_sheet *sheet; uint32_t table_id = 0; uint32_t table_count = 0; STAILQ_FOREACH(sheet, self->sheets, list_pointers) { if (sheet->is_chartsheet) continue; else worksheet = sheet->u.worksheet; table_count = worksheet->table_count; if (table_count == 0) continue; lxw_worksheet_prepare_tables(worksheet, table_id + 1); table_id += table_count; } } /***************************************************************************** * * 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"); if (self->vba_project) LXW_PUSH_ATTRIBUTES_STR("codeName", "{37E998C4-C9E5-D4B9-71C8-EB1FF731991C}"); lxw_xml_empty_tag(self->file, "fileVersion", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _workbook_write_file_sharing(lxw_workbook *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; if (self->read_only == 0) return; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("readOnlyRecommended", "1"); lxw_xml_empty_tag(self->file, "fileSharing", &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(); if (self->vba_codename) LXW_PUSH_ATTRIBUTES_STR("codeName", self->vba_codename); 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_sheet *sheet; lxw_worksheet *worksheet; lxw_chartsheet *chartsheet; lxw_xml_start_tag(self->file, "sheets", NULL); STAILQ_FOREACH(sheet, self->sheets, list_pointers) { if (sheet->is_chartsheet) { chartsheet = sheet->u.chartsheet; _write_sheet(self, chartsheet->name, chartsheet->index + 1, chartsheet->hidden); } else { worksheet = sheet->u.worksheet; _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(); } 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 fileSharing element. */ _workbook_write_file_sharing(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 sheets list. */ workbook->sheets = calloc(1, sizeof(struct lxw_sheets)); GOTO_LABEL_ON_MEM_ERROR(workbook->sheets, mem_error); STAILQ_INIT(workbook->sheets); /* 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 chartsheets list. */ workbook->chartsheets = calloc(1, sizeof(struct lxw_chartsheets)); GOTO_LABEL_ON_MEM_ERROR(workbook->chartsheets, mem_error); STAILQ_INIT(workbook->chartsheets); /* 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 chartsheet names tree. */ workbook->chartsheet_names = calloc(1, sizeof(struct lxw_chartsheet_names)); GOTO_LABEL_ON_MEM_ERROR(workbook->chartsheet_names, mem_error); RB_INIT(workbook->chartsheet_names); /* Add the image MD5 tree. */ workbook->image_md5s = calloc(1, sizeof(struct lxw_image_md5s)); GOTO_LABEL_ON_MEM_ERROR(workbook->image_md5s, mem_error); RB_INIT(workbook->image_md5s); /* Add the header image MD5 tree. */ workbook->header_image_md5s = calloc(1, sizeof(struct lxw_image_md5s)); GOTO_LABEL_ON_MEM_ERROR(workbook->header_image_md5s, mem_error); RB_INIT(workbook->header_image_md5s); /* Add the background image MD5 tree. */ workbook->background_md5s = calloc(1, sizeof(struct lxw_image_md5s)); GOTO_LABEL_ON_MEM_ERROR(workbook->background_md5s, mem_error); RB_INIT(workbook->background_md5s); /* 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 a hash table to track format indices. */ workbook->used_dxf_formats = lxw_hash_new(128, 1, 0); GOTO_LABEL_ON_MEM_ERROR(workbook->used_dxf_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); /* Add the default hyperlink format. */ format = workbook_add_format(workbook); GOTO_LABEL_ON_MEM_ERROR(format, mem_error); format_set_hyperlink(format); workbook->default_url_format = format; if (options) { workbook->options.constant_memory = options->constant_memory; workbook->options.tmpdir = lxw_strdup(options->tmpdir); workbook->options.use_zip64 = options->use_zip64; workbook->options.output_buffer = options->output_buffer; workbook->options.output_buffer_size = options->output_buffer_size; } workbook->max_url_length = 2079; 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_sheet *sheet = NULL; lxw_worksheet *worksheet = NULL; lxw_worksheet_name *worksheet_name = NULL; lxw_error error; lxw_worksheet_init_data init_data = { 0, 0, 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(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_worksheets + 1); init_data.name = new_name; init_data.quoted_name = lxw_strdup(new_name); } /* Check that the worksheet name is valid. */ error = workbook_validate_sheet_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; init_data.default_url_format = self->default_url_format; init_data.max_url_length = self->max_url_length; /* Create a new worksheet object. */ worksheet = lxw_worksheet_new(&init_data); GOTO_LABEL_ON_MEM_ERROR(worksheet, mem_error); /* Add it to the worksheet list. */ self->num_worksheets++; STAILQ_INSERT_TAIL(self->worksheets, worksheet, list_pointers); /* Create a new sheet object. */ sheet = calloc(1, sizeof(lxw_sheet)); GOTO_LABEL_ON_MEM_ERROR(sheet, mem_error); sheet->u.worksheet = worksheet; /* Add it to the worksheet list. */ self->num_sheets++; STAILQ_INSERT_TAIL(self->sheets, sheet, 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((void *) init_data.name); free((void *) init_data.quoted_name); free(worksheet_name); free(worksheet); return NULL; } /* * Add a new chartsheet to the Excel workbook. */ lxw_chartsheet * workbook_add_chartsheet(lxw_workbook *self, const char *sheetname) { lxw_sheet *sheet = NULL; lxw_chartsheet *chartsheet = NULL; lxw_chartsheet_name *chartsheet_name = NULL; lxw_error error; lxw_worksheet_init_data init_data = { 0, 0, 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(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, "Chart%d", self->num_chartsheets + 1); init_data.name = new_name; init_data.quoted_name = lxw_strdup(new_name); } /* Check that the chartsheet name is valid. */ error = workbook_validate_sheet_name(self, init_data.name); if (error) { LXW_WARN_FORMAT2 ("workbook_add_chartsheet(): chartsheet name '%s' has " "error: %s", init_data.name, lxw_strerror(error)); goto mem_error; } /* Create a struct to find/store the chartsheet name/pointer. */ chartsheet_name = calloc(1, sizeof(struct lxw_chartsheet_name)); GOTO_LABEL_ON_MEM_ERROR(chartsheet_name, mem_error); /* Initialize the metadata to pass to the chartsheet. */ 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 chartsheet object. */ chartsheet = lxw_chartsheet_new(&init_data); GOTO_LABEL_ON_MEM_ERROR(chartsheet, mem_error); /* Add it to the chartsheet list. */ self->num_chartsheets++; STAILQ_INSERT_TAIL(self->chartsheets, chartsheet, list_pointers); /* Create a new sheet object. */ sheet = calloc(1, sizeof(lxw_sheet)); GOTO_LABEL_ON_MEM_ERROR(sheet, mem_error); sheet->is_chartsheet = LXW_TRUE; sheet->u.chartsheet = chartsheet; /* Add it to the chartsheet list. */ self->num_sheets++; STAILQ_INSERT_TAIL(self->sheets, sheet, list_pointers); /* Store the chartsheet so we can look it up by name. */ chartsheet_name->name = init_data.name; chartsheet_name->chartsheet = chartsheet; RB_INSERT(lxw_chartsheet_names, self->chartsheet_names, chartsheet_name); return chartsheet; mem_error: free((void *) init_data.name); free((void *) init_data.quoted_name); free(chartsheet_name); free(chartsheet); 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->dxf_format_indices = self->used_dxf_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_sheet *sheet = NULL; lxw_worksheet *worksheet = NULL; lxw_packager *packager = NULL; lxw_error error = LXW_NO_ERROR; char codename[LXW_MAX_SHEETNAME_LENGTH] = { 0 }; /* 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) { sheet = STAILQ_FIRST(self->sheets); if (!sheet->is_chartsheet) { worksheet = sheet->u.worksheet; worksheet->selected = LXW_TRUE; worksheet->hidden = 0; } } /* Set the active sheet and check if a metadata file is needed. */ STAILQ_FOREACH(sheet, self->sheets, list_pointers) { if (sheet->is_chartsheet) continue; else worksheet = sheet->u.worksheet; if (worksheet->index == self->active_sheet) worksheet->active = LXW_TRUE; if (worksheet->has_dynamic_arrays) self->has_metadata = LXW_TRUE; } /* Set workbook and worksheet VBA codenames if a macro has been added. */ if (self->vba_project) { if (!self->vba_codename) workbook_set_vba_name(self, "ThisWorkbook"); STAILQ_FOREACH(sheet, self->sheets, list_pointers) { if (sheet->is_chartsheet) continue; else worksheet = sheet->u.worksheet; if (!worksheet->vba_codename) { lxw_snprintf(codename, LXW_MAX_SHEETNAME_LENGTH, "Sheet%d", worksheet->index + 1); worksheet_set_vba_name(worksheet, codename); } } } /* Prepare the worksheet VML elements such as comments. */ _prepare_vml(self); /* 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); /* Set the table ids for the worksheet tables. */ _prepare_tables(self); /* Create a packager object to assemble sub-elements into a zip file. */ packager = lxw_packager_new(self->filename, self->options.tmpdir, self->options.use_zip64); /* If the packager fails it is generally due to a zip permission error. */ if (packager == NULL) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " "Error creating '%s'. " "System 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); if (!self->filename) { *self->options.output_buffer = packager->output_buffer; *self->options.output_buffer_size = packager->output_buffer_size; } /* Error and non-error conditions fall through to the cleanup code. */ if (error == LXW_ERROR_CREATING_TMPFILE) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " "Error creating tmpfile(s) to assemble '%s'. " "System error = %s\n", self->filename, strerror(errno)); } /* If LXW_ERROR_ZIP_FILE_OPERATION then errno is set by zip. */ if (error == LXW_ERROR_ZIP_FILE_OPERATION) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " "Zip ZIP_ERRNO error while creating xlsx file '%s'. " "System error = %s\n", self->filename, strerror(errno)); } /* If LXW_ERROR_ZIP_PARAMETER_ERROR then errno is set by zip. */ if (error == LXW_ERROR_ZIP_PARAMETER_ERROR) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " "Zip ZIP_PARAMERROR error while creating xlsx file '%s'. " "System error = %s\n", self->filename, strerror(errno)); } /* If LXW_ERROR_ZIP_BAD_ZIP_FILE then errno is set by zip. */ if (error == LXW_ERROR_ZIP_BAD_ZIP_FILE) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " "Zip ZIP_BADZIPFILE error while creating xlsx file '%s'. " "This may require the use_zip64 option for large files. " "System error = %s\n", self->filename, strerror(errno)); } /* If LXW_ERROR_ZIP_INTERNAL_ERROR then errno is set by zip. */ if (error == LXW_ERROR_ZIP_INTERNAL_ERROR) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " "Zip ZIP_INTERNALERROR error while creating xlsx file '%s'. " "System error = %s\n", self->filename, strerror(errno)); } /* The next 2 error conditions don't set errno. */ if (error == LXW_ERROR_ZIP_FILE_ADD) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " "Zip error adding file to xlsx file '%s'.\n", self->filename); } if (error == LXW_ERROR_ZIP_CLOSE) { LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): " "Zip 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); } doc_props->created = user_props->created; 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; } /* * Get a chartsheet object from its name. */ lxw_chartsheet * workbook_get_chartsheet_by_name(lxw_workbook *self, const char *name) { lxw_chartsheet_name chartsheet_name; lxw_chartsheet_name *found; if (!name) return NULL; chartsheet_name.name = name; found = RB_FIND(lxw_chartsheet_names, self->chartsheet_names, &chartsheet_name); if (found) return found->chartsheet; else return NULL; } /* * Get the default URL format. */ lxw_format * workbook_get_default_url_format(lxw_workbook *self) { return self->default_url_format; } /* * Unset the default URL format. */ void workbook_unset_default_url_format(lxw_workbook *self) { self->default_url_format->hyperlink = LXW_FALSE; self->default_url_format->xf_id = 0; self->default_url_format->underline = LXW_UNDERLINE_NONE; self->default_url_format->theme = 0; } /* * Validate the worksheet name based on Excel's rules. */ lxw_error workbook_validate_sheet_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 that the worksheet doesn't start or end with an apostrophe. */ if (sheetname[0] == '\'' || sheetname[strlen(sheetname) - 1] == '\'') return LXW_ERROR_SHEETNAME_START_END_APOSTROPHE; /* Check if the worksheet name is already in use. */ if (workbook_get_worksheet_by_name(self, sheetname)) return LXW_ERROR_SHEETNAME_ALREADY_USED; /* Check if the chartsheet name is already in use. */ if (workbook_get_chartsheet_by_name(self, sheetname)) return LXW_ERROR_SHEETNAME_ALREADY_USED; return LXW_NO_ERROR; } /* * Add a vbaProject binary to the Excel workbook. */ lxw_error workbook_add_vba_project(lxw_workbook *self, const char *filename) { FILE *filehandle; if (!filename) { LXW_WARN("workbook_add_vba_project(): " "project filename must be specified."); return LXW_ERROR_NULL_PARAMETER_IGNORED; } /* Check that the vbaProject file exists and can be opened. */ filehandle = lxw_fopen(filename, "rb"); if (!filehandle) { LXW_WARN_FORMAT1("workbook_add_vba_project(): " "project file doesn't exist or can't be opened: %s.", filename); return LXW_ERROR_PARAMETER_VALIDATION; } fclose(filehandle); self->vba_project = lxw_strdup(filename); return LXW_NO_ERROR; } /* * Add a vbaProject binary and a vbaProjectSignature binary to the Excel workbook. */ lxw_error workbook_add_signed_vba_project(lxw_workbook *self, const char *vba_project, const char *signature) { FILE *filehandle; lxw_error error = workbook_add_vba_project(self, vba_project); if (error != LXW_NO_ERROR) return error; if (!signature) { LXW_WARN("workbook_add_signed_vba_project(): " "signature filename must be specified."); return LXW_ERROR_NULL_PARAMETER_IGNORED; } /* Check that the vbaProjectSignature file exists and can be opened. */ filehandle = lxw_fopen(signature, "rb"); if (!filehandle) { LXW_WARN_FORMAT1("workbook_add_signed_vba_project(): " "signature file doesn't exist or can't be opened: %s.", signature); return LXW_ERROR_PARAMETER_VALIDATION; } fclose(filehandle); self->vba_project_signature = lxw_strdup(signature); return LXW_NO_ERROR; } /* * Set the VBA name for the workbook. */ lxw_error workbook_set_vba_name(lxw_workbook *self, const char *name) { if (!name) { LXW_WARN("workbook_set_vba_name(): " "name must be specified."); return LXW_ERROR_NULL_PARAMETER_IGNORED; } self->vba_codename = lxw_strdup(name); return LXW_NO_ERROR; } /* * Set the Excel "Read-only recommended" save option. */ void workbook_read_only_recommended(lxw_workbook *self) { self->read_only = 2; } writexl/src/libxlsxwriter/chartsheet.c0000644000176200001440000003060114747162622017772 0ustar liggesusers/***************************************************************************** * chartsheet - A library for creating Excel XLSX chartsheet files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ #include "xlsxwriter/xmlwriter.h" #include "xlsxwriter/chartsheet.h" #include "xlsxwriter/utility.h" /* * Forward declarations. */ /***************************************************************************** * * Private functions. * ****************************************************************************/ /* * Create a new chartsheet object. */ lxw_chartsheet * lxw_chartsheet_new(lxw_worksheet_init_data *init_data) { lxw_chartsheet *chartsheet = calloc(1, sizeof(lxw_chartsheet)); GOTO_LABEL_ON_MEM_ERROR(chartsheet, mem_error); /* Use an embedded worksheet instance to write XML records that are * shared with worksheet.c. */ chartsheet->worksheet = lxw_worksheet_new(NULL); GOTO_LABEL_ON_MEM_ERROR(chartsheet->worksheet, mem_error); if (init_data) { chartsheet->name = init_data->name; chartsheet->quoted_name = init_data->quoted_name; chartsheet->tmpdir = init_data->tmpdir; chartsheet->index = init_data->index; chartsheet->hidden = init_data->hidden; chartsheet->active_sheet = init_data->active_sheet; chartsheet->first_sheet = init_data->first_sheet; } chartsheet->worksheet->is_chartsheet = LXW_TRUE; chartsheet->worksheet->zoom_scale_normal = LXW_FALSE; chartsheet->worksheet->orientation = LXW_LANDSCAPE; return chartsheet; mem_error: lxw_chartsheet_free(chartsheet); return NULL; } /* * Free a chartsheet object. */ void lxw_chartsheet_free(lxw_chartsheet *chartsheet) { if (!chartsheet) return; lxw_worksheet_free(chartsheet->worksheet); free((void *) chartsheet->name); free((void *) chartsheet->quoted_name); free(chartsheet); } /***************************************************************************** * * XML functions. * ****************************************************************************/ /* * Write the XML declaration. */ STATIC void _chartsheet_xml_declaration(lxw_chartsheet *self) { lxw_xml_declaration(self->file); } /* * Write the element. */ STATIC void _chartsheet_write_chartsheet(lxw_chartsheet *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, "chartsheet", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _chartsheet_write_sheet_pr(lxw_chartsheet *self) { lxw_worksheet_write_sheet_pr(self->worksheet); } /* * Write the element. */ STATIC void _chartsheet_write_sheet_views(lxw_chartsheet *self) { lxw_worksheet_write_sheet_views(self->worksheet); } /* * Write the element. */ STATIC void _chartsheet_write_page_margins(lxw_chartsheet *self) { lxw_worksheet_write_page_margins(self->worksheet); } /* * Write the elements. */ STATIC void _chartsheet_write_drawings(lxw_chartsheet *self) { lxw_worksheet_write_drawings(self->worksheet); } /* * Write the element. */ STATIC void _chartsheet_write_sheet_protection(lxw_chartsheet *self) { lxw_worksheet_write_sheet_protection(self->worksheet, &self->protection); } /* * Write the element. */ STATIC void _chartsheet_write_page_setup(lxw_chartsheet *self) { lxw_worksheet_write_page_setup(self->worksheet); } /* * Write the element. */ STATIC void _chartsheet_write_header_footer(lxw_chartsheet *self) { lxw_worksheet_write_header_footer(self->worksheet); } /***************************************************************************** * * XML file assembly functions. * ****************************************************************************/ /* * Assemble and write the XML file. */ void lxw_chartsheet_assemble_xml_file(lxw_chartsheet *self) { /* Set the embedded worksheet filehandle to the same as the chartsheet. */ self->worksheet->file = self->file; /* Write the XML declaration. */ _chartsheet_xml_declaration(self); /* Write the chartsheet element. */ _chartsheet_write_chartsheet(self); /* Write the sheetPr element. */ _chartsheet_write_sheet_pr(self); /* Write the sheetViews element. */ _chartsheet_write_sheet_views(self); /* Write the sheetProtection element. */ _chartsheet_write_sheet_protection(self); /* Write the pageMargins element. */ _chartsheet_write_page_margins(self); /* Write the chartsheet page setup. */ _chartsheet_write_page_setup(self); /* Write the headerFooter element. */ _chartsheet_write_header_footer(self); /* Write the drawing element. */ _chartsheet_write_drawings(self); lxw_xml_end_tag(self->file, "chartsheet"); } /***************************************************************************** * * Public functions. * ****************************************************************************/ /* * Set a chartsheet chart, with options. */ lxw_error chartsheet_set_chart_opt(lxw_chartsheet *self, lxw_chart *chart, lxw_chart_options *user_options) { lxw_object_properties *object_props; lxw_chart_series *series; if (!chart) { LXW_WARN("chartsheet_set_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("chartsheet_set_chart()/_opt(): the same chart object " "cannot be set for a chartsheet more than once."); return LXW_ERROR_PARAMETER_VALIDATION; } /* Check that the chart has a data series. */ if (STAILQ_EMPTY(chart->series_list)) { LXW_WARN("chartsheet_set_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("chartsheet_set_chart()/_opt(): chart must have a " "'values' series."); return LXW_ERROR_PARAMETER_VALIDATION; } } /* Create a new object to hold the chart image properties. */ object_props = calloc(1, sizeof(lxw_object_properties)); RETURN_ON_MEM_ERROR(object_props, LXW_ERROR_MEMORY_MALLOC_FAILED); if (user_options) { object_props->x_offset = user_options->x_offset; object_props->y_offset = user_options->y_offset; object_props->x_scale = user_options->x_scale; object_props->y_scale = user_options->y_scale; } object_props->width = 480; object_props->height = 288; if (object_props->x_scale == 0.0) object_props->x_scale = 1; if (object_props->y_scale == 0.0) object_props->y_scale = 1; /* Store chart references so they can be ordered in the workbook. */ object_props->chart = chart; /* Store the chart data in the embedded worksheet. */ STAILQ_INSERT_TAIL(self->worksheet->chart_data, object_props, list_pointers); chart->in_use = LXW_TRUE; chart->is_chartsheet = LXW_TRUE; chart->is_protected = self->is_protected; self->chart = chart; return LXW_NO_ERROR; } /* * Set a chartsheet charts. */ lxw_error chartsheet_set_chart(lxw_chartsheet *self, lxw_chart *chart) { return chartsheet_set_chart_opt(self, chart, NULL); } /* * Set this chartsheet as a selected worksheet, i.e. the worksheet has its tab * highlighted. */ void chartsheet_select(lxw_chartsheet *self) { self->selected = LXW_TRUE; /* Selected worksheet can't be hidden. */ self->hidden = LXW_FALSE; } /* * Set this chartsheet as the active worksheet, i.e. the worksheet that is * displayed when the workbook is opened. Also set it as selected. */ void chartsheet_activate(lxw_chartsheet *self) { self->worksheet->selected = LXW_TRUE; self->worksheet->active = LXW_TRUE; /* Active worksheet can't be hidden. */ self->worksheet->hidden = LXW_FALSE; *self->active_sheet = self->index; } /* * Set this chartsheet 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 chartsheet_set_first_sheet(lxw_chartsheet *self) { /* Active worksheet can't be hidden. */ self->hidden = LXW_FALSE; *self->first_sheet = self->index; } /* * Hide this chartsheet. */ void chartsheet_hide(lxw_chartsheet *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 the color of the chartsheet tab. */ void chartsheet_set_tab_color(lxw_chartsheet *self, lxw_color_t color) { self->worksheet->tab_color = color; } /* * Set the chartsheet protection flags to prevent modification of chartsheet * objects. */ void chartsheet_protect(lxw_chartsheet *self, const char *password, lxw_protection *options) { struct lxw_protection_obj *protect = &self->protection; /* Copy any user parameters to the internal structure. */ if (options) { protect->objects = options->no_objects; protect->no_content = options->no_content; } else { protect->objects = LXW_FALSE; protect->no_content = LXW_FALSE; } if (password) { uint16_t hash = lxw_hash_password(password); lxw_snprintf(protect->hash, 5, "%X", hash); } else { if (protect->objects && protect->no_content) return; } protect->no_sheet = LXW_TRUE; protect->scenarios = LXW_TRUE; protect->is_configured = LXW_TRUE; if (self->chart) self->chart->is_protected = LXW_TRUE; else self->is_protected = LXW_TRUE; } /* * Set the chartsheet zoom factor. */ void chartsheet_set_zoom(lxw_chartsheet *self, uint16_t scale) { /* Confine the scale to Excel"s range */ if (scale < 10 || scale > 400) { LXW_WARN("chartsheet_set_zoom(): " "Zoom factor scale outside range: 10 <= zoom <= 400."); return; } self->worksheet->zoom = scale; } /* * Set the page orientation as portrait. */ void chartsheet_set_portrait(lxw_chartsheet *self) { worksheet_set_portrait(self->worksheet); } /* * Set the page orientation as landscape. */ void chartsheet_set_landscape(lxw_chartsheet *self) { worksheet_set_landscape(self->worksheet); } /* * Set the paper type. Example. 1 = US Letter, 9 = A4 */ void chartsheet_set_paper(lxw_chartsheet *self, uint8_t paper_size) { worksheet_set_paper(self->worksheet, paper_size); } /* * Set all the page margins in inches. */ void chartsheet_set_margins(lxw_chartsheet *self, double left, double right, double top, double bottom) { worksheet_set_margins(self->worksheet, left, right, top, bottom); } /* * Set the page header caption and options. */ lxw_error chartsheet_set_header_opt(lxw_chartsheet *self, const char *string, lxw_header_footer_options *options) { return worksheet_set_header_opt(self->worksheet, string, options); } /* * Set the page footer caption and options. */ lxw_error chartsheet_set_footer_opt(lxw_chartsheet *self, const char *string, lxw_header_footer_options *options) { return worksheet_set_footer_opt(self->worksheet, string, options); } /* * Set the page header caption. */ lxw_error chartsheet_set_header(lxw_chartsheet *self, const char *string) { return chartsheet_set_header_opt(self, string, NULL); } /* * Set the page footer caption. */ lxw_error chartsheet_set_footer(lxw_chartsheet *self, const char *string) { return chartsheet_set_footer_opt(self, string, NULL); } writexl/src/libxlsxwriter/vml.c0000644000176200001440000006450614747162622016451 0ustar liggesusers/***************************************************************************** * vml - A library for creating Excel XLSX vml files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ #include "xlsxwriter/xmlwriter.h" #include "xlsxwriter/vml.h" #include "xlsxwriter/utility.h" /* * Forward declarations. */ /***************************************************************************** * * Private functions. * ****************************************************************************/ /* * Create a new vml object. */ lxw_vml * lxw_vml_new(void) { lxw_vml *vml = calloc(1, sizeof(lxw_vml)); GOTO_LABEL_ON_MEM_ERROR(vml, mem_error); return vml; mem_error: lxw_vml_free(vml); return NULL; } /* * Free a vml object. */ void lxw_vml_free(lxw_vml *vml) { if (!vml) return; free(vml); } /***************************************************************************** * * XML functions. * ****************************************************************************/ /* * Write the element. */ STATIC void _vml_write_visible(lxw_vml *self) { lxw_xml_empty_tag(self->file, "x:Visible", NULL); } /* * Write the element. */ STATIC void _vml_write_formula(lxw_vml *self, char *equation) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("eqn", equation); lxw_xml_empty_tag(self->file, "v:f", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_formulas(lxw_vml *self) { lxw_xml_start_tag(self->file, "v:formulas", NULL); _vml_write_formula(self, "if lineDrawn pixelLineWidth 0"); _vml_write_formula(self, "sum @0 1 0"); _vml_write_formula(self, "sum 0 0 @1"); _vml_write_formula(self, "prod @2 1 2"); _vml_write_formula(self, "prod @3 21600 pixelWidth"); _vml_write_formula(self, "prod @3 21600 pixelHeight"); _vml_write_formula(self, "sum @0 0 1"); _vml_write_formula(self, "prod @6 1 2"); _vml_write_formula(self, "prod @7 21600 pixelWidth"); _vml_write_formula(self, "sum @8 21600 0"); _vml_write_formula(self, "prod @7 21600 pixelHeight"); _vml_write_formula(self, "sum @10 21600 0"); lxw_xml_end_tag(self->file, "v:formulas"); } /* * Write the element. */ STATIC void _vml_write_text_halign(lxw_vml *self) { lxw_xml_data_element(self->file, "x:TextHAlign", "Center", NULL); } /* * Write the element. */ STATIC void _vml_write_text_valign(lxw_vml *self) { lxw_xml_data_element(self->file, "x:TextVAlign", "Center", NULL); } /* * Write the element. */ STATIC void _vml_write_fmla_macro(lxw_vml *self, lxw_vml_obj *vml_obj) { lxw_xml_data_element(self->file, "x:FmlaMacro", vml_obj->macro, NULL); } /* * Write the element. */ STATIC void _vml_write_print_object(lxw_vml *self) { lxw_xml_data_element(self->file, "x:PrintObject", "False", NULL); } /* * Write the element. */ STATIC void _vml_write_aspect_ratio_lock(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("v:ext", "edit"); LXW_PUSH_ATTRIBUTES_STR("aspectratio", "t"); lxw_xml_empty_tag(self->file, "o:lock", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_rotation_lock(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("v:ext", "edit"); LXW_PUSH_ATTRIBUTES_STR("rotation", "t"); lxw_xml_empty_tag(self->file, "o:lock", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_column(lxw_vml *self, lxw_vml_obj *vml_obj) { char data[LXW_ATTR_32]; lxw_snprintf(data, LXW_ATTR_32, "%d", vml_obj->col); lxw_xml_data_element(self->file, "x:Column", data, NULL); } /* * Write the element. */ STATIC void _vml_write_row(lxw_vml *self, lxw_vml_obj *vml_obj) { char data[LXW_ATTR_32]; lxw_snprintf(data, LXW_ATTR_32, "%d", vml_obj->row); lxw_xml_data_element(self->file, "x:Row", data, NULL); } /* * Write the element. */ STATIC void _vml_write_auto_fill(lxw_vml *self) { lxw_xml_data_element(self->file, "x:AutoFill", "False", NULL); } /* * Write the element. */ STATIC void _vml_write_anchor(lxw_vml *self, lxw_vml_obj *vml_obj) { char anchor_data[LXW_MAX_ATTRIBUTE_LENGTH]; lxw_snprintf(anchor_data, LXW_MAX_ATTRIBUTE_LENGTH, "%d, %d, %d, %d, %d, %d, %d, %d", vml_obj->from.col, (uint32_t) vml_obj->from.col_offset, vml_obj->from.row, (uint32_t) vml_obj->from.row_offset, vml_obj->to.col, (uint32_t) vml_obj->to.col_offset, vml_obj->to.row, (uint32_t) vml_obj->to.row_offset); lxw_xml_data_element(self->file, "x:Anchor", anchor_data, NULL); } /* * Write the element. */ STATIC void _vml_write_size_with_cells(lxw_vml *self) { lxw_xml_empty_tag(self->file, "x:SizeWithCells", NULL); } /* * Write the element. */ STATIC void _vml_write_move_with_cells(lxw_vml *self) { lxw_xml_empty_tag(self->file, "x:MoveWithCells", NULL); } /* * Write the element. */ STATIC void _vml_write_shadow(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("on", "t"); LXW_PUSH_ATTRIBUTES_STR("color", "black"); LXW_PUSH_ATTRIBUTES_STR("obscured", "t"); lxw_xml_empty_tag(self->file, "v:shadow", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_stroke(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("joinstyle", "miter"); lxw_xml_empty_tag(self->file, "v:stroke", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_shapetype_lock(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("v:ext", "edit"); LXW_PUSH_ATTRIBUTES_STR("shapetype", "t"); lxw_xml_empty_tag(self->file, "o:lock", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_font(lxw_vml *self, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("face", "Calibri"); LXW_PUSH_ATTRIBUTES_STR("size", "220"); LXW_PUSH_ATTRIBUTES_STR("color", "#000000"); lxw_xml_data_element(self->file, "font", vml_obj->name, &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_imagedata(lxw_vml *self, uint32_t rel_index, char *name) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char rel_id[LXW_ATTR_32]; lxw_snprintf(rel_id, LXW_ATTR_32, "rId%d", rel_index); LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("o:relid", rel_id); LXW_PUSH_ATTRIBUTES_STR("o:title", name); lxw_xml_empty_tag(self->file, "v:imagedata", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_image_path(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("o:extrusionok", "f"); LXW_PUSH_ATTRIBUTES_STR("gradientshapeok", "t"); LXW_PUSH_ATTRIBUTES_STR("o:connecttype", "rect"); lxw_xml_empty_tag(self->file, "v:path", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_image_shape(lxw_vml *self, uint32_t vml_shape_id, uint32_t z_index, lxw_vml_obj *image_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char width_str[LXW_ATTR_32]; char height_str[LXW_ATTR_32]; char style[LXW_MAX_ATTRIBUTE_LENGTH]; char o_spid[LXW_ATTR_32]; char type[] = "#_x0000_t75"; double width; double height; /* Scale the height/width by the resolution, relative to 72dpi. */ width = image_obj->width * (72.0 / image_obj->x_dpi); height = image_obj->height * (72.0 / image_obj->y_dpi); /* Excel uses a rounding based around 72 and 96 dpi. */ width = 72.0 / 96.0 * (uint32_t) (width * 96.0 / 72 + 0.25); height = 72.0 / 96.0 * (uint32_t) (height * 96.0 / 72 + 0.25); lxw_sprintf_dbl(width_str, width); lxw_sprintf_dbl(height_str, height); lxw_snprintf(o_spid, LXW_ATTR_32, "_x0000_s%d", vml_shape_id); lxw_snprintf(style, LXW_MAX_ATTRIBUTE_LENGTH, "position:absolute;" "margin-left:0;" "margin-top:0;" "width:%spt;" "height:%spt;" "z-index:%d", width_str, height_str, z_index); LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("id", image_obj->image_position); LXW_PUSH_ATTRIBUTES_STR("o:spid", o_spid); LXW_PUSH_ATTRIBUTES_STR("type", type); LXW_PUSH_ATTRIBUTES_STR("style", style); lxw_xml_start_tag(self->file, "v:shape", &attributes); /* Write the v:imagedata element. */ _vml_write_imagedata(self, image_obj->rel_index, image_obj->name); /* Write the o:lock element. */ _vml_write_rotation_lock(self); lxw_xml_end_tag(self->file, "v:shape"); LXW_FREE_ATTRIBUTES(); } /* * Write the element for images. */ STATIC void _vml_write_image_shapetype(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char id[] = "_x0000_t75"; char coordsize[] = "21600,21600"; char o_spt[] = "75"; char o_preferrelative[] = "t"; char path[] = "m@4@5l@4@11@9@11@9@5xe"; char filled[] = "f"; char stroked[] = "f"; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("id", id); LXW_PUSH_ATTRIBUTES_STR("coordsize", coordsize); LXW_PUSH_ATTRIBUTES_STR("o:spt", o_spt); LXW_PUSH_ATTRIBUTES_STR("o:preferrelative", o_preferrelative); LXW_PUSH_ATTRIBUTES_STR("path", path); LXW_PUSH_ATTRIBUTES_STR("filled", filled); LXW_PUSH_ATTRIBUTES_STR("stroked", stroked); lxw_xml_start_tag(self->file, "v:shapetype", &attributes); /* Write the v:stroke element. */ _vml_write_stroke(self); /* Write the v:formulas element. */ _vml_write_formulas(self); /* Write the v:path element. */ _vml_write_image_path(self); /* Write the o:lock element. */ _vml_write_aspect_ratio_lock(self); lxw_xml_end_tag(self->file, "v:shapetype"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_button_client_data(lxw_vml *self, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("ObjectType", "Button"); lxw_xml_start_tag(self->file, "x:ClientData", &attributes); /* Write the element. */ _vml_write_anchor(self, vml_obj); /* Write the x:PrintObject element. */ _vml_write_print_object(self); /* Write the x:AutoFill element. */ _vml_write_auto_fill(self); /* Write the x:FmlaMacro element. */ _vml_write_fmla_macro(self, vml_obj); /* Write the x:TextHAlign element. */ _vml_write_text_halign(self); /* Write the x:TextVAlign element. */ _vml_write_text_valign(self); lxw_xml_end_tag(self->file, "x:ClientData"); LXW_FREE_ATTRIBUTES(); } /* * Write the
element. */ STATIC void _vml_write_button_div(lxw_vml *self, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("style", "text-align:center"); lxw_xml_start_tag(self->file, "div", &attributes); /* Write the font element. */ _vml_write_font(self, vml_obj); lxw_xml_end_tag(self->file, "div"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_button_textbox(lxw_vml *self, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("style", "mso-direction-alt:auto"); LXW_PUSH_ATTRIBUTES_STR("o:singleclick", "f"); lxw_xml_start_tag(self->file, "v:textbox", &attributes); /* Write the div element. */ _vml_write_button_div(self, vml_obj); lxw_xml_end_tag(self->file, "v:textbox"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_button_fill(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("color2", "buttonFace [67]"); LXW_PUSH_ATTRIBUTES_STR("o:detectmouseclick", "t"); lxw_xml_empty_tag(self->file, "v:fill", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element for buttons. */ STATIC void _vml_write_button_path(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("shadowok", "f"); LXW_PUSH_ATTRIBUTES_STR("o:extrusionok", "f"); LXW_PUSH_ATTRIBUTES_STR("strokeok", "f"); LXW_PUSH_ATTRIBUTES_STR("fillok", "f"); LXW_PUSH_ATTRIBUTES_STR("o:connecttype", "rect"); lxw_xml_empty_tag(self->file, "v:path", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element for buttons. */ STATIC void _vml_write_button_shape(lxw_vml *self, uint32_t vml_shape_id, uint32_t z_index, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char type[] = "#_x0000_t201"; char o_button[] = "t"; char fillcolor[] = "buttonFace [67]"; char strokecolor[] = "windowText [64]"; char o_insetmode[] = "auto"; char id[LXW_ATTR_32]; char margin_left[LXW_ATTR_32]; char margin_top[LXW_ATTR_32]; char width[LXW_ATTR_32]; char height[LXW_ATTR_32]; char style[LXW_MAX_ATTRIBUTE_LENGTH]; lxw_sprintf_dbl(margin_left, vml_obj->col_absolute * 0.75); lxw_sprintf_dbl(margin_top, vml_obj->row_absolute * 0.75); lxw_sprintf_dbl(width, vml_obj->width * 0.75); lxw_sprintf_dbl(height, vml_obj->height * 0.75); lxw_snprintf(id, LXW_ATTR_32, "_x0000_s%d", vml_shape_id); lxw_snprintf(style, LXW_MAX_ATTRIBUTE_LENGTH, "position:absolute;" "margin-left:%spt;" "margin-top:%spt;" "width:%spt;" "height:%spt;" "z-index:%d;" "mso-wrap-style:tight", margin_left, margin_top, width, height, z_index); LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("id", id); LXW_PUSH_ATTRIBUTES_STR("type", type); if (vml_obj->text) LXW_PUSH_ATTRIBUTES_STR("alt", vml_obj->text); LXW_PUSH_ATTRIBUTES_STR("style", style); LXW_PUSH_ATTRIBUTES_STR("o:button", o_button); LXW_PUSH_ATTRIBUTES_STR("fillcolor", fillcolor); LXW_PUSH_ATTRIBUTES_STR("strokecolor", strokecolor); LXW_PUSH_ATTRIBUTES_STR("o:insetmode", o_insetmode); lxw_xml_start_tag(self->file, "v:shape", &attributes); /* Write the v:fill element. */ _vml_write_button_fill(self); /* Write the o:lock element. */ _vml_write_rotation_lock(self); /* Write the v:textbox element. */ _vml_write_button_textbox(self, vml_obj); /* Write the x:ClientData element. */ _vml_write_button_client_data(self, vml_obj); lxw_xml_end_tag(self->file, "v:shape"); LXW_FREE_ATTRIBUTES(); } /* * Write the element for buttons. */ STATIC void _vml_write_button_shapetype(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char id[] = "_x0000_t201"; char coordsize[] = "21600,21600"; char o_spt[] = "201"; char path[] = "m,l,21600r21600,l21600,xe"; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("id", id); LXW_PUSH_ATTRIBUTES_STR("coordsize", coordsize); LXW_PUSH_ATTRIBUTES_STR("o:spt", o_spt); LXW_PUSH_ATTRIBUTES_STR("path", path); lxw_xml_start_tag(self->file, "v:shapetype", &attributes); /* Write the v:stroke element. */ _vml_write_stroke(self); /* Write the v:path element. */ _vml_write_button_path(self); /* Write the o:lock element. */ _vml_write_shapetype_lock(self); lxw_xml_end_tag(self->file, "v:shapetype"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_comment_client_data(lxw_vml *self, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("ObjectType", "Note"); lxw_xml_start_tag(self->file, "x:ClientData", &attributes); /* Write the element. */ _vml_write_move_with_cells(self); /* Write the element. */ _vml_write_size_with_cells(self); /* Write the element. */ _vml_write_anchor(self, vml_obj); /* Write the element. */ _vml_write_auto_fill(self); /* Write the element. */ _vml_write_row(self, vml_obj); /* Write the element. */ _vml_write_column(self, vml_obj); /* Write the x:Visible element. */ if (vml_obj->visible == LXW_COMMENT_DISPLAY_VISIBLE) _vml_write_visible(self); lxw_xml_end_tag(self->file, "x:ClientData"); LXW_FREE_ATTRIBUTES(); } /* * Write the
element. */ STATIC void _vml_write_comment_div(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("style", "text-align:left"); lxw_xml_start_tag(self->file, "div", &attributes); lxw_xml_end_tag(self->file, "div"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_comment_textbox(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("style", "mso-direction-alt:auto"); lxw_xml_start_tag(self->file, "v:textbox", &attributes); /* Write the div element. */ _vml_write_comment_div(self); lxw_xml_end_tag(self->file, "v:textbox"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_comment_fill(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("color2", "#ffffe1"); lxw_xml_empty_tag(self->file, "v:fill", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_comment_path(lxw_vml *self, uint8_t has_gradient, char *type) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); if (has_gradient) LXW_PUSH_ATTRIBUTES_STR("gradientshapeok", "t"); LXW_PUSH_ATTRIBUTES_STR("o:connecttype", type); lxw_xml_empty_tag(self->file, "v:path", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element for comments. */ STATIC void _vml_write_comment_shape(lxw_vml *self, uint32_t vml_shape_id, uint32_t z_index, lxw_vml_obj *vml_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char id[LXW_ATTR_32]; char margin_left[LXW_ATTR_32]; char margin_top[LXW_ATTR_32]; char width[LXW_ATTR_32]; char height[LXW_ATTR_32]; char visible[LXW_ATTR_32]; char fillcolor[LXW_ATTR_32]; char style[LXW_MAX_ATTRIBUTE_LENGTH]; char type[] = "#_x0000_t202"; char o_insetmode[] = "auto"; lxw_sprintf_dbl(margin_left, vml_obj->col_absolute * 0.75); lxw_sprintf_dbl(margin_top, vml_obj->row_absolute * 0.75); lxw_sprintf_dbl(width, vml_obj->width * 0.75); lxw_sprintf_dbl(height, vml_obj->height * 0.75); lxw_snprintf(id, LXW_ATTR_32, "_x0000_s%d", vml_shape_id); if (vml_obj->visible == LXW_COMMENT_DISPLAY_DEFAULT) vml_obj->visible = self->comment_display_default; if (vml_obj->visible == LXW_COMMENT_DISPLAY_VISIBLE) lxw_snprintf(visible, LXW_ATTR_32, "visible"); else lxw_snprintf(visible, LXW_ATTR_32, "hidden"); if (vml_obj->color) lxw_snprintf(fillcolor, LXW_ATTR_32, "#%06x", vml_obj->color & LXW_COLOR_MASK); else lxw_snprintf(fillcolor, LXW_ATTR_32, "#%06x", 0xffffe1); lxw_snprintf(style, LXW_MAX_ATTRIBUTE_LENGTH, "position:absolute;" "margin-left:%spt;" "margin-top:%spt;" "width:%spt;" "height:%spt;" "z-index:%d;" "visibility:%s", margin_left, margin_top, width, height, z_index, visible); LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("id", id); LXW_PUSH_ATTRIBUTES_STR("type", type); LXW_PUSH_ATTRIBUTES_STR("style", style); LXW_PUSH_ATTRIBUTES_STR("fillcolor", fillcolor); LXW_PUSH_ATTRIBUTES_STR("o:insetmode", o_insetmode); lxw_xml_start_tag(self->file, "v:shape", &attributes); /* Write the v:fill element. */ _vml_write_comment_fill(self); /* Write the v:shadow element. */ _vml_write_shadow(self); /* Write the v:path element. */ _vml_write_comment_path(self, LXW_FALSE, "none"); /* Write the v:textbox element. */ _vml_write_comment_textbox(self); /* Write the x:ClientData element. */ _vml_write_comment_client_data(self, vml_obj); lxw_xml_end_tag(self->file, "v:shape"); LXW_FREE_ATTRIBUTES(); } /* * Write the element for comments. */ STATIC void _vml_write_comment_shapetype(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char id[] = "_x0000_t202"; char coordsize[] = "21600,21600"; char o_spt[] = "202"; char path[] = "m,l,21600r21600,l21600,xe"; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("id", id); LXW_PUSH_ATTRIBUTES_STR("coordsize", coordsize); LXW_PUSH_ATTRIBUTES_STR("o:spt", o_spt); LXW_PUSH_ATTRIBUTES_STR("path", path); lxw_xml_start_tag(self->file, "v:shapetype", &attributes); /* Write the v:stroke element. */ _vml_write_stroke(self); /* Write the v:path element. */ _vml_write_comment_path(self, LXW_TRUE, "rect"); lxw_xml_end_tag(self->file, "v:shapetype"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_idmap(lxw_vml *self) { /* Since the vml_data_id_str may exceed the LXW_MAX_ATTRIBUTE_LENGTH we * write it directly without the xml helper functions. */ fprintf(self->file, "", self->vml_data_id_str); } /* * Write the element. */ STATIC void _vml_write_shapelayout(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("v:ext", "edit"); lxw_xml_start_tag(self->file, "o:shapelayout", &attributes); /* Write the o:idmap element. */ _vml_write_idmap(self); lxw_xml_end_tag(self->file, "o:shapelayout"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _vml_write_xml_namespace(lxw_vml *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char xmlns_v[] = "urn:schemas-microsoft-com:vml"; char xmlns_o[] = "urn:schemas-microsoft-com:office:office"; char xmlns_x[] = "urn:schemas-microsoft-com:office:excel"; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("xmlns:v", xmlns_v); LXW_PUSH_ATTRIBUTES_STR("xmlns:o", xmlns_o); LXW_PUSH_ATTRIBUTES_STR("xmlns:x", xmlns_x); lxw_xml_start_tag(self->file, "xml", &attributes); LXW_FREE_ATTRIBUTES(); } /***************************************************************************** * * XML file assembly functions. * ****************************************************************************/ /* * Assemble and write the XML file. */ void lxw_vml_assemble_xml_file(lxw_vml *self) { lxw_vml_obj *comment_obj; lxw_vml_obj *button_obj; lxw_vml_obj *image_obj; uint32_t z_index = 1; /* Write the xml namespace element. Note, the VML files have no * XML declaration.*/ _vml_write_xml_namespace(self); /* Write the o:shapelayout element. */ _vml_write_shapelayout(self); if (self->button_objs && !STAILQ_EMPTY(self->button_objs)) { /* Write the element. */ _vml_write_button_shapetype(self); STAILQ_FOREACH(button_obj, self->button_objs, list_pointers) { self->vml_shape_id++; /* Write the element. */ _vml_write_button_shape(self, self->vml_shape_id, z_index, button_obj); z_index++; } } if (self->comment_objs && !STAILQ_EMPTY(self->comment_objs)) { /* Write the element. */ _vml_write_comment_shapetype(self); STAILQ_FOREACH(comment_obj, self->comment_objs, list_pointers) { self->vml_shape_id++; /* Write the element. */ _vml_write_comment_shape(self, self->vml_shape_id, z_index, comment_obj); z_index++; } } if (self->image_objs && !STAILQ_EMPTY(self->image_objs)) { /* Write the element. */ _vml_write_image_shapetype(self); STAILQ_FOREACH(image_obj, self->image_objs, list_pointers) { self->vml_shape_id++; /* Write the element. */ _vml_write_image_shape(self, self->vml_shape_id, z_index, image_obj); z_index++; } } lxw_xml_end_tag(self->file, "xml"); } /***************************************************************************** * * Public functions. * ****************************************************************************/ writexl/src/libxlsxwriter/chart.c0000644000176200001440000052241414747162622016751 0ustar liggesusers/***************************************************************************** * chart - A library for creating Excel XLSX chart files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, 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((void *) font->name); free(font); } STATIC void _chart_free_data_labels(lxw_chart_series *series) { uint16_t index; for (index = 0; index < series->data_label_count; index++) { lxw_chart_custom_label *data_label = &series->data_labels[index]; free(data_label->value); _chart_free_range(data_label->range); _chart_free_font(data_label->font); free(data_label->line); free(data_label->fill); free(data_label->pattern); } series->data_label_count = 0; free(series->data_labels); } /* * 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); free(series->label_line); free(series->label_fill); free(series->label_pattern); _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); _chart_free_data_labels(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); /* Copy the user supplied properties. */ font->name = lxw_strdup(user_font->name); font->size = user_font->size; font->bold = user_font->bold; font->italic = user_font->italic; font->underline = user_font->underline; font->rotation = user_font->rotation; font->color = user_font->color; font->pitch_family = user_font->pitch_family; font->charset = user_font->charset; font->baseline = user_font->baseline; /* 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; 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); /* Copy the user supplied properties. */ line->color = user_line->color; line->none = user_line->none; line->width = user_line->width; line->dash_type = user_line->dash_type; line->transparency = user_line->transparency; 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); /* Copy the user supplied properties. */ fill->color = user_fill->color; fill->none = user_fill->none; fill->transparency = user_fill->transparency; 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); /* Copy the user supplied properties. */ pattern->fg_color = user_pattern->fg_color; pattern->bg_color = user_pattern->bg_color; pattern->type = user_pattern->type; if (!pattern->bg_color) { /* Default background color in Excel is white, when unspecified. */ pattern->bg_color = LXW_COLOR_WHITE; } 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. */ STATIC 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_protection(lxw_chart *self) { lxw_xml_empty_tag(self->file, "c:protection", NULL); } /* * 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; 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; 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, uint8_t ignore_rich_pr) { lxw_xml_start_tag(self->file, "a:p", NULL); /* Write the a:pPr element. */ if (!ignore_rich_pr) _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) { if (rotation == 16200000) { /* 270 deg/stacked angle. */ LXW_PUSH_ATTRIBUTES_STR("rot", "0"); LXW_PUSH_ATTRIBUTES_STR("vert", "wordArtVert"); } else if (rotation == 16260000) { /* 271 deg/East Asian vertical. */ LXW_PUSH_ATTRIBUTES_STR("rot", "0"); LXW_PUSH_ATTRIBUTES_STR("vert", "eaVert"); } else if (rotation == 21600000) { /* 360 deg = 0 for y axis. */ LXW_PUSH_ATTRIBUTES_STR("rot", "0"); LXW_PUSH_ATTRIBUTES_STR("vert", "horz"); } else { LXW_PUSH_ATTRIBUTES_INT("rot", rotation); 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_sprintf_dbl(data, 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, lxw_chart_font *font, uint8_t is_horizontal, uint8_t ignore_rich_pr) { 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, ignore_rich_pr); 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, font, is_horizontal, LXW_FALSE); 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 (line->width > 0.0) LXW_PUSH_ATTRIBUTES_INT("w", width_int); if (line->none || line->color || line->dash_type) { 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->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"); } else { lxw_xml_empty_tag(self->file, "a:ln", &attributes); } 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->fg_color) _chart_write_a_fg_clr(self, pattern->fg_color); if (pattern->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 parts of the elements where only formatting is changed. */ STATIC void _chart_write_custom_label_format_only(lxw_chart *self, lxw_chart_custom_label *data_label) { if (data_label->line || data_label->fill || data_label->pattern) { _chart_write_sp_pr(self, data_label->line, data_label->fill, data_label->pattern); _chart_write_tx_pr(self, LXW_FALSE, data_label->font); } else if (data_label->font) { lxw_xml_empty_tag(self->file, "c:spPr", NULL); _chart_write_tx_pr(self, LXW_FALSE, data_label->font); } } /* * Write parts of the elements for formula custom labels. */ STATIC void _chart_write_custom_label_formula(lxw_chart *self, lxw_chart_series *series, lxw_chart_custom_label *data_label) { lxw_xml_empty_tag(self->file, "c:layout", NULL); lxw_xml_start_tag(self->file, "c:tx", NULL); _chart_write_str_ref(self, data_label->range); lxw_xml_end_tag(self->file, "c:tx"); _chart_write_custom_label_format_only(self, data_label); /* Write the c:dLblPos element. */ if (series->label_position) _chart_write_d_lbl_pos(self, series->label_position); /* 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 parts of the elements for string custom labels. */ STATIC void _chart_write_custom_label_str(lxw_chart *self, lxw_chart_series *series, lxw_chart_custom_label *data_label) { uint8_t ignore_rich_pr = LXW_TRUE; if (data_label->line || data_label->fill || data_label->pattern) ignore_rich_pr = LXW_FALSE; lxw_xml_empty_tag(self->file, "c:layout", NULL); lxw_xml_start_tag(self->file, "c:tx", NULL); /* Write the c:rich element. */ _chart_write_rich(self, data_label->value, data_label->font, LXW_FALSE, ignore_rich_pr); lxw_xml_end_tag(self->file, "c:tx"); /* Write the c:spPr element. */ _chart_write_sp_pr(self, data_label->line, data_label->fill, data_label->pattern); /* Write the c:dLblPos element. */ if (series->label_position) _chart_write_d_lbl_pos(self, series->label_position); /* 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 elements for custom labels. */ STATIC void _chart_write_custom_labels(lxw_chart *self, lxw_chart_series *series) { uint16_t index = 0; for (index = 0; index < series->data_label_count; index++) { lxw_chart_custom_label *data_label = &series->data_labels[index]; if (!data_label->value && !data_label->range && !data_label->hide && !data_label->font) { continue; } lxw_xml_start_tag(self->file, "c:dLbl", NULL); /* Write the c:idx element. */ _chart_write_idx(self, index); if (data_label->hide) { /* Write the c:delete element. */ _chart_write_delete(self); } else if (data_label->value) { _chart_write_custom_label_str(self, series, data_label); } else if (data_label->range) { _chart_write_custom_label_formula(self, series, data_label); } else if (data_label->font) { _chart_write_custom_label_format_only(self, data_label); } lxw_xml_end_tag(self->file, "c:dLbl"); } } /* * 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); if (series->data_labels) _chart_write_custom_labels(self, series); /* Write the c:numFmt element. */ if (series->label_num_format) _chart_write_label_num_fmt(self, series->label_num_format); /* Write the c:spPr element. */ _chart_write_sp_pr(self, series->label_line, series->label_fill, series->label_pattern); 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_min) LXW_PUSH_ATTRIBUTES_STR("val", "min"); else 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, lxw_chart_axis *axis) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); if (axis->label_align == LXW_CHART_AXIS_LABEL_ALIGN_LEFT) LXW_PUSH_ATTRIBUTES_STR("val", "l"); else if (axis->label_align == LXW_CHART_AXIS_LABEL_ALIGN_RIGHT) LXW_PUSH_ATTRIBUTES_STR("val", "r"); else 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:dst_label 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_TOP_RIGHT: _chart_write_legend_pos(self, "tr"); 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; case LXW_CHART_LEGEND_OVERLAY_TOP_RIGHT: _chart_write_legend_pos(self, "tr"); 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_min || 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, self->x_axis); /* 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_min || 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_min || 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 && !series->line) { lxw_chart_line line = { 0x000000, LXW_TRUE, 2.25, LXW_CHART_LINE_DASH_SOLID, 0 }; 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); /* 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_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, uint8_t type) { 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; if (type == LXW_CHART_LINE_STACKED) { self->grouping = LXW_GROUPING_STACKED; self->subtype = LXW_CHART_SUBTYPE_STACKED; } if (type == LXW_CHART_LINE_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_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: case LXW_CHART_LINE_STACKED: case LXW_CHART_LINE_STACKED_PERCENT: _chart_initialize_line_chart(self, type); 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:protection element. */ if (self->is_protected) _chart_write_protection(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. */ if (!self->is_chartsheet) _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; } /* * Add a series to the chart. */ 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 points for a chart. */ 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; } /* * Store the custom data_labels for a chart. */ lxw_error chart_series_set_labels_custom(lxw_chart_series *series, lxw_chart_data_label *data_labels[]) { uint16_t i = 0; uint16_t data_label_count = 0; if (data_labels == NULL) return LXW_ERROR_NULL_PARAMETER_IGNORED; while (data_labels[data_label_count]) data_label_count++; if (data_label_count == 0) return LXW_ERROR_NULL_PARAMETER_IGNORED; series->has_labels = LXW_TRUE; /* Set the Value label type if no other type is set. */ if (!series->show_labels_name && !series->show_labels_category && !series->show_labels_value) { series->show_labels_value = LXW_TRUE; } /* Free any existing resource. */ _chart_free_data_labels(series); series->data_labels = calloc(data_label_count, sizeof(lxw_chart_custom_label)); RETURN_ON_MEM_ERROR(series->data_labels, LXW_ERROR_MEMORY_MALLOC_FAILED); /* Copy the user data into the array of new structs. The struct types * are different since the internal version has more fields. */ for (i = 0; i < data_label_count; i++) { lxw_chart_data_label *user_label = data_labels[i]; lxw_chart_custom_label *data_label = &series->data_labels[i]; const char *src_value = user_label->value; data_label->hide = user_label->hide; data_label->font = _chart_convert_font_args(user_label->font); data_label->line = _chart_convert_line_args(user_label->line); data_label->fill = _chart_convert_fill_args(user_label->fill); data_label->pattern = _chart_convert_pattern_args(user_label->pattern); if (src_value) { if (*src_value == '=') { /* The value is a formula. Handle like other chart ranges. */ data_label->range = calloc(1, sizeof(lxw_series_range)); GOTO_LABEL_ON_MEM_ERROR(data_label->range, mem_error); data_label->range->formula = lxw_strdup(src_value + 1); /* Add the formula to the data cache to allow value to be looked * up and filled in when the file is closed. */ if (_chart_init_data_cache(data_label->range) != LXW_NO_ERROR) goto mem_error; } else { /* The value is a simple string. */ data_label->value = lxw_strdup(src_value); } } } series->data_label_count = data_label_count; return LXW_NO_ERROR; mem_error: _chart_free_data_labels(series); return LXW_ERROR_MEMORY_MALLOC_FAILED; } /* * 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 a line type for a series data labels. */ void chart_series_set_labels_line(lxw_chart_series *series, lxw_chart_line *line) { if (!line) return; /* Free any previously allocated resource. */ free(series->label_line); series->label_line = _chart_convert_line_args(line); } /* * Set a fill type for a series data labels. */ void chart_series_set_labels_fill(lxw_chart_series *series, lxw_chart_fill *fill) { if (!fill) return; /* Free any previously allocated resource. */ free(series->label_fill); series->label_fill = _chart_convert_fill_args(fill); } /* * Set a pattern type for a series data labels. */ void chart_series_set_labels_pattern(lxw_chart_series *series, lxw_chart_pattern *pattern) { if (!pattern) return; /* Free any previously allocated resource. */ free(series->label_pattern); series->label_pattern = _chart_convert_pattern_args(pattern); } /* * 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 X or Y error bars from a chart series. */ lxw_series_error_bars * chart_series_get_error_bars(lxw_chart_series *series, lxw_chart_error_bar_axis axis_type) { if (!series) return NULL; if (axis_type == LXW_CHART_ERROR_BAR_AXIS_X) return series->x_error_bars; else if (axis_type == LXW_CHART_ERROR_BAR_AXIS_Y) return series->y_error_bars; else return NULL; } /* * 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); } /* * Get an axis pointer from a chart. */ lxw_chart_axis * chart_axis_get(lxw_chart *self, lxw_chart_axis_type axis_type) { if (!self) return NULL; if (axis_type == LXW_CHART_AXIS_TYPE_X) return self->x_axis; else if (axis_type == LXW_CHART_AXIS_TYPE_Y) return self->y_axis; else return NULL; } /* * 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 minimum possible value. */ void chart_axis_set_crossing_min(lxw_chart_axis *axis) { axis->has_crossing = LXW_TRUE; axis->crossing_min = LXW_TRUE; } /* * Set the axis crossing position as the maximum 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 axis label alignment. */ void chart_axis_set_label_align(lxw_chart_axis *axis, uint8_t align) { axis->label_align = align; } /* * 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); } writexl/src/libxlsxwriter/theme.c0000644000176200001440000002625114747162622016750 0ustar liggesusers/***************************************************************************** * theme - A library for creating Excel XLSX theme files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, 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(void) { 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. * ****************************************************************************/ writexl/src/libxlsxwriter/core.c0000644000176200001440000001521514747162622016574 0ustar liggesusers/***************************************************************************** * core - A library for creating Excel XLSX core files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, 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(void) { 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 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. * ****************************************************************************/ writexl/src/libxlsxwriter/format.c0000644000176200001440000004113214747162622017131 0ustar liggesusers/***************************************************************************** * format - A library for creating Excel XLSX format files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, 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(void) { lxw_format *format = calloc(1, sizeof(lxw_format)); GOTO_LABEL_ON_MEM_ERROR(format, mem_error); format->xf_format_indices = NULL; format->dxf_format_indices = NULL; format->xf_index = LXW_PROPERTY_UNSET; format->dxf_index = LXW_PROPERTY_UNSET; format->xf_id = 0; 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_UNDERLINE_NONE; 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 = 0; 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; format->quote_prefix = 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 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->dxf_format_indices = NULL; key->num_xf_formats = NULL; key->num_dxf_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->underline = self->underline; key->theme = self->theme; key->font_color = self->font_color; 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; } } /* * Returns the DXF index number used by Excel to identify a format. */ int32_t lxw_format_get_dxf_index(lxw_format *self) { lxw_format *format_key; lxw_format *existing_format; lxw_hash_element *hash_element; lxw_hash_table *formats_hash_table = self->dxf_format_indices; int32_t index; /* Note: The formats_hash_table/dxf_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->dxf_index != LXW_PROPERTY_UNSET) { return self->dxf_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->dxf_index; } else { /* New format requiring an index. */ index = formats_hash_table->unique_count; self->dxf_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 = 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 = color; } /* * Set the fg_color property. */ void format_set_fg_color(lxw_format *self, lxw_color_t color) { self->fg_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) { 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 = 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 = 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 = 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 = 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 = 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; } /* * Set the color_indexed property. */ void format_set_color_indexed(lxw_format *self, uint8_t value) { self->color_indexed = value; } /* * Set the font_only property. */ void format_set_font_only(lxw_format *self) { self->font_only = LXW_TRUE; } /* * Set the theme property. */ void format_set_hyperlink(lxw_format *self) { self->hyperlink = LXW_TRUE; self->xf_id = 1; self->underline = LXW_UNDERLINE_SINGLE; self->theme = 10; } /* * Set the quote_prefix property. */ void format_set_quote_prefix(lxw_format *self) { self->quote_prefix = LXW_TRUE; } writexl/src/libxlsxwriter/comment.c0000644000176200001440000002375714747162622017320 0ustar liggesusers/***************************************************************************** * comment - A library for creating Excel XLSX comment files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ #include "xlsxwriter/xmlwriter.h" #include "xlsxwriter/comment.h" #include "xlsxwriter/utility.h" /* * Forward declarations. */ STATIC int _author_id_cmp(lxw_author_id *tuple1, lxw_author_id *tuple2); #ifndef __clang_analyzer__ LXW_RB_GENERATE_AUTHOR_IDS(lxw_author_ids, lxw_author_id, tree_pointers, _author_id_cmp); #endif /***************************************************************************** * * Private functions. * ****************************************************************************/ /* * Comparator for the author ids. */ STATIC int _author_id_cmp(lxw_author_id *author_id1, lxw_author_id *author_id2) { return strcmp(author_id1->author, author_id2->author); } /* * Check if an author already existing in the author/id table. */ STATIC uint8_t _check_author(lxw_comment *self, char *author) { lxw_author_id tmp_author_id; lxw_author_id *existing_author = NULL; if (!author) return LXW_TRUE; tmp_author_id.author = author; existing_author = RB_FIND(lxw_author_ids, self->author_ids, &tmp_author_id); if (existing_author) return LXW_TRUE; else return LXW_FALSE; } /* * Get the index used for an author name. */ STATIC uint32_t _get_author_index(lxw_comment *self, char *author) { lxw_author_id tmp_author_id; lxw_author_id *existing_author = NULL; lxw_author_id *new_author_id = NULL; if (!author) return 0; tmp_author_id.author = author; existing_author = RB_FIND(lxw_author_ids, self->author_ids, &tmp_author_id); if (existing_author) { return existing_author->id; } else { new_author_id = calloc(1, sizeof(lxw_author_id)); if (new_author_id) { new_author_id->id = self->author_id; new_author_id->author = lxw_strdup(author); self->author_id++; RB_INSERT(lxw_author_ids, self->author_ids, new_author_id); return new_author_id->id; } else { return 0; } } } /* * Create a new comment object. */ lxw_comment * lxw_comment_new(void) { lxw_comment *comment = calloc(1, sizeof(lxw_comment)); GOTO_LABEL_ON_MEM_ERROR(comment, mem_error); comment->author_ids = calloc(1, sizeof(struct lxw_author_ids)); GOTO_LABEL_ON_MEM_ERROR(comment->author_ids, mem_error); RB_INIT(comment->author_ids); return comment; mem_error: lxw_comment_free(comment); return NULL; } /* * Free a comment object. */ void lxw_comment_free(lxw_comment *comment) { struct lxw_author_id *author_id; struct lxw_author_id *next_author_id; if (!comment) return; if (comment->author_ids) { for (author_id = RB_MIN(lxw_author_ids, comment->author_ids); author_id; author_id = next_author_id) { next_author_id = RB_NEXT(lxw_author_ids, worksheet->author_id, author_id); RB_REMOVE(lxw_author_ids, comment->author_ids, author_id); free(author_id->author); free(author_id); } free(comment->author_ids); } free(comment); } /***************************************************************************** * * XML functions. * ****************************************************************************/ /* * Write the XML declaration. */ STATIC void _comment_xml_declaration(lxw_comment *self) { lxw_xml_declaration(self->file); } /***************************************************************************** * * XML file assembly functions. * ****************************************************************************/ /* * Write the element. */ STATIC void _comment_write_text_t(lxw_comment *self, lxw_vml_obj *comment_obj) { lxw_xml_data_element(self->file, "t", comment_obj->text, NULL); } /* * Write the element. */ STATIC void _comment_write_family(lxw_comment *self, lxw_vml_obj *comment_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_INT("val", comment_obj->font_family); lxw_xml_empty_tag(self->file, "family", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _comment_write_r_font(lxw_comment *self, lxw_vml_obj *comment_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char font_name[LXW_ATTR_32]; if (comment_obj->font_name) lxw_snprintf(font_name, LXW_ATTR_32, "%s", comment_obj->font_name); else lxw_snprintf(font_name, LXW_ATTR_32, "Tahoma"); LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("val", font_name); lxw_xml_empty_tag(self->file, "rFont", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _comment_write_color(lxw_comment *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char indexed[] = "81"; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("indexed", indexed); lxw_xml_empty_tag(self->file, "color", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _comment_write_sz(lxw_comment *self, lxw_vml_obj *comment_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_DBL("val", comment_obj->font_size); lxw_xml_empty_tag(self->file, "sz", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _comment_write_r_pr(lxw_comment *self, lxw_vml_obj *comment_obj) { lxw_xml_start_tag(self->file, "rPr", NULL); /* Write the sz element. */ _comment_write_sz(self, comment_obj); /* Write the color element. */ _comment_write_color(self); /* Write the rFont element. */ _comment_write_r_font(self, comment_obj); /* Write the family element. */ _comment_write_family(self, comment_obj); lxw_xml_end_tag(self->file, "rPr"); } /* * Write the element. */ STATIC void _comment_write_r(lxw_comment *self, lxw_vml_obj *comment_obj) { lxw_xml_start_tag(self->file, "r", NULL); /* Write the rPr element. */ _comment_write_r_pr(self, comment_obj); /* Write the t element. */ _comment_write_text_t(self, comment_obj); lxw_xml_end_tag(self->file, "r"); } /* * Write the element. */ STATIC void _comment_write_text(lxw_comment *self, lxw_vml_obj *comment_obj) { lxw_xml_start_tag(self->file, "text", NULL); /* Write the r element. */ _comment_write_r(self, comment_obj); lxw_xml_end_tag(self->file, "text"); } /* * Write the element. */ STATIC void _comment_write_comment(lxw_comment *self, lxw_vml_obj *comment_obj) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char ref[LXW_MAX_CELL_NAME_LENGTH]; lxw_rowcol_to_cell(ref, comment_obj->row, comment_obj->col); LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("ref", ref); LXW_PUSH_ATTRIBUTES_INT("authorId", comment_obj->author_id); lxw_xml_start_tag(self->file, "comment", &attributes); /* Write the text element. */ _comment_write_text(self, comment_obj); lxw_xml_end_tag(self->file, "comment"); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _comment_write_comment_list(lxw_comment *self) { lxw_vml_obj *comment_obj; lxw_xml_start_tag(self->file, "commentList", NULL); STAILQ_FOREACH(comment_obj, self->comment_objs, list_pointers) { /* Write the comment element. */ _comment_write_comment(self, comment_obj); } lxw_xml_end_tag(self->file, "commentList"); } /* * Write the element. */ STATIC void _comment_write_author(lxw_comment *self, char *author) { lxw_xml_data_element(self->file, "author", author, NULL); } /* * Write the element. */ STATIC void _comment_write_authors(lxw_comment *self) { lxw_vml_obj *comment_obj; char *author; lxw_xml_start_tag(self->file, "authors", NULL); /* Set the default author (from worksheet_set_comments_author()). */ if (self->comment_author) { _get_author_index(self, self->comment_author); _comment_write_author(self, self->comment_author); } else { _get_author_index(self, ""); _comment_write_author(self, ""); } STAILQ_FOREACH(comment_obj, self->comment_objs, list_pointers) { author = comment_obj->author; if (author) { if (!_check_author(self, author)) _comment_write_author(self, author); comment_obj->author_id = _get_author_index(self, author); } } lxw_xml_end_tag(self->file, "authors"); } /* * Write the element. */ STATIC void _comment_write_comments(lxw_comment *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_xml_start_tag(self->file, "comments", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Assemble and write the XML file. */ void lxw_comment_assemble_xml_file(lxw_comment *self) { /* Write the XML declaration. */ _comment_xml_declaration(self); /* Write the comments element. */ _comment_write_comments(self); /* Write the authors element. */ _comment_write_authors(self); /* Write the commentList element. */ _comment_write_comment_list(self); lxw_xml_end_tag(self->file, "comments"); } /***************************************************************************** * * Public functions. * ****************************************************************************/ writexl/src/libxlsxwriter/hash_table.c0000644000176200001440000001474614747162622017746 0ustar liggesusers/***************************************************************************** * hash_table - Hash table functions for libxlsxwriter. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2022, 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); } writexl/src/minizip/0000755000176200001440000000000014766110233014221 5ustar liggesuserswritexl/src/minizip/zip.c0000644000176200001440000017762114747162622015215 0ustar liggesusers/* 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 recreating zip archive with RAW when deleting items from a zip. ZIP64 data is automatically 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 #include "zlib.h" #include "zip.h" #ifdef STDC # 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 dependent */ #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 /* #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 alignment */ 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 currently 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 currently 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 extended 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; unsigned crypt_header_size; #endif } curfile64_info; typedef struct { zlib_filefunc64_32_def z_filefunc; voidpf filestream; /* io structure 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 currently writing */ ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ ZPOS64_T add_position_when_writing_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(void) { 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; free(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 = (const 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(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,(uLong)nbByte)!=(uLong)nbByte) return ZIP_ERRNO; else return ZIP_OK; } 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) (((uLong)(ptm->tm_mday) + (32 * (uLong)(ptm->tm_mon+1)) + (512 * year)) << 16) | (((uLong)ptm->tm_sec/2) + (32 * (uLong)ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); } /****************************************************************************/ 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(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(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(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(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+(unsigned)i; break; } if (uPosFound!=0) break; } free(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(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+(unsigned)i; break; } } if (uPosFound!=0) break; } free(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; } local 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 disk, used for spanning ZIP, unsupported, always 0*/ uLong number_disk_with_CD; /* number of the disk with central dir, used for spanning 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_writing_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; } free(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_writing_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 free(ziinit.globalcomment); # endif /* !NO_ADDFILEINEXISTINGZIP*/ free(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); } local 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, (ZPOS64_T)HeaderID,2); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)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 // The filename and comment length must fit in 16 bits. if ((filename!=NULL) && (strlen(filename)>0xffff)) return ZIP_PARAMERROR; if ((comment!=NULL) && (strlen(comment)>0xffff)) return ZIP_PARAMERROR; // The extra field length must fit in 16 bits. If the member also requires // a Zip64 extra block, that will also need to fit within that 16-bit // length, but that will be checked for later. if ((size_extrafield_local>0xffff) || (size_extrafield_global>0xffff)) return ZIP_PARAMERROR; 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_writing_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*)(uintptr_t)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); 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; unsigned 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 cannot 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); } local 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_writing_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; } local 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_writing_offset; err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); } return err; } local 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_writing_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_writing_offset),4); } return err; } local 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_writing_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 free(zi->globalcomment); #endif free(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 == NULL || *dataLen < 4) return ZIP_PARAMERROR; pNewHeader = (char*)ALLOC((unsigned)*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; free(pNewHeader); return retVal; } writexl/src/minizip/ioapi.h0000644000176200001440000001607214747162622015511 0ustar liggesusers/* 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 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64) #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 chosen 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 #if defined(_MSC_VER) || defined(__BORLANDC__) typedef unsigned __int64 ZPOS64_T; #else typedef unsigned long long int ZPOS64_T; #endif #endif #endif /* Maximum unsigned 32-bit value used as placeholder for zip64 */ #ifndef MAXU32 #define MAXU32 (0xffffffff) #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) (voidpf opaque, const char* filename, int mode); typedef uLong (ZCALLBACK *read_file_func) (voidpf opaque, voidpf stream, void* buf, uLong size); typedef uLong (ZCALLBACK *write_file_func) (voidpf opaque, voidpf stream, const void* buf, uLong size); typedef int (ZCALLBACK *close_file_func) (voidpf opaque, voidpf stream); typedef int (ZCALLBACK *testerror_file_func) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uLong offset, int origin); /* here is the "old" 32 bits 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) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *seek64_file_func) (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin); typedef voidpf (ZCALLBACK *open64_file_func) (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(zlib_filefunc64_def* pzlib_filefunc_def); void fill_fopen_filefunc(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(const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode); long call_zseek64(const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin); ZPOS64_T call_ztell64(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 writexl/src/minizip/zip.h0000644000176200001440000003663514747162622015221 0ustar liggesusers/* 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 { int tm_sec; /* seconds after the minute - [0,59] */ int tm_min; /* minutes after the hour - [0,59] */ int tm_hour; /* hours since midnight - [0,23] */ int tm_mday; /* day of the month - [1,31] */ int tm_mon; /* months since January - [0,11] */ int 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(const char *pathname, int append); extern zipFile ZEXPORT zipOpen64(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 course, you can use RAW reading and writing to copy the file you did not want delete */ extern zipFile ZEXPORT zipOpen2(const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc_def); extern zipFile ZEXPORT zipOpen2_64(const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def); extern zipFile ZEXPORT zipOpen3(const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def); 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); 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); /* 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 for the local header if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global contains the extrafield data for the global 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(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(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(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(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(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(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(zipFile file, const void* buf, unsigned len); /* Write data in the zipfile */ extern int ZEXPORT zipCloseFileInZip(zipFile file); /* Close the current file in the zipfile */ extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, uLong uncompressed_size, uLong crc32); extern int ZEXPORT zipCloseFileInZipRaw64(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(zipFile file, const char* global_comment); /* Close the zipfile */ extern int ZEXPORT zipRemoveExtraInfoBlock(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 */ writexl/src/minizip/ioapi.c0000644000176200001440000001707414747162622015507 0ustar liggesusers/* 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) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_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 = (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.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(voidpf opaque, const char* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; (void)opaque; 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; (void)opaque; 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; (void)opaque; 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; (void)opaque; ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); return ret; } static long ZCALLBACK ftell_file_func(voidpf opaque, voidpf stream) { long ret; (void)opaque; ret = ftell((FILE *)stream); return ret; } static ZPOS64_T ZCALLBACK ftell64_file_func(voidpf opaque, voidpf stream) { ZPOS64_T ret; (void)opaque; ret = (ZPOS64_T)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; (void)opaque; 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, (long)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; (void)opaque; 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, (z_off64_t)offset, fseek_origin) != 0) ret = -1; return ret; } static int ZCALLBACK fclose_file_func(voidpf opaque, voidpf stream) { int ret; (void)opaque; ret = fclose((FILE *)stream); return ret; } static int ZCALLBACK ferror_file_func(voidpf opaque, voidpf stream) { int ret; (void)opaque; ret = ferror((FILE *)stream); return ret; } void fill_fopen_filefunc(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; } writexl/src/Makevars0000644000176200001440000000172314766110236014244 0ustar liggesusersLIBXLSXWRITER = \ 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 \ libxlsxwriter/chartsheet.o minizip/ioapi.o minizip/zip.o libxlsxwriter/metadata.o \ libxlsxwriter/comment.o libxlsxwriter/vml.o md5/md5.o libxlsxwriter/table.o \ STATLIB = libxlsxwriter/libstatxlsxwriter.a all: $(SHLIB) cleanup $(SHLIB): $(STATLIB) PKG_CFLAGS=$(C_VISIBILITY) PKG_CPPFLAGS=-Iinclude -DUSE_R_LANG PKG_LIBS=-Llibxlsxwriter -lstatxlsxwriter -lz $(STATLIB): $(LIBXLSXWRITER) $(AR) rcs $(STATLIB) $(LIBXLSXWRITER) cleanup: $(SHLIB) rm -f $(LIBXLSXWRITER) $(STATLIB) writexl/src/md5/0000755000176200001440000000000014766110233013227 5ustar liggesuserswritexl/src/md5/md5.c0000644000176200001440000002104714747162622014074 0ustar liggesusers/* * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. * MD5 Message-Digest Algorithm (RFC 1321). * * Homepage: * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 * * Author: * Alexander Peslyak, better known as Solar Designer * * This software was written by Alexander Peslyak in 2001. No copyright is * claimed, and the software is hereby placed in the public domain. * In case this attempt to disclaim copyright and place the software in the * public domain is deemed null and void, then the software is * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the * general public under the following terms: * * Redistribution and use in source and binary forms, with or without * modification, are permitted. * * There's ABSOLUTELY NO WARRANTY, express or implied. * * (This is a heavily cut-down "BSD license".) * * This differs from Colin Plumb's older public domain implementation in that * no exactly 32-bit integer data type is required (any 32-bit or wider * unsigned integer data type will do), there's no compile-time endianness * configuration, and the function prototypes match OpenSSL's. No code from * Colin Plumb's implementation has been reused; this comment merely compares * the properties of the two independent implementations. * * The primary goals of this implementation are portability and ease of use. * It is meant to be fast, but not as fast as possible. Some known * optimizations are not included to reduce source code size and avoid * compile-time configuration. */ #ifndef HAVE_OPENSSL #include #include "md5.h" /* * The basic MD5 functions. * * F and G are optimized compared to their RFC 1321 definitions for * architectures that lack an AND-NOT instruction, just like in Colin Plumb's * implementation. */ #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) #define H(x, y, z) (((x) ^ (y)) ^ (z)) #define H2(x, y, z) ((x) ^ ((y) ^ (z))) #define I(x, y, z) ((y) ^ ((x) | ~(z))) /* * The MD5 transformation for all four rounds. */ #define STEP(f, a, b, c, d, x, t, s) \ (a) += f((b), (c), (d)) + (x) + (t); \ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ (a) += (b); /* * SET reads 4 input bytes in little-endian byte order and stores them in a * properly aligned word in host byte order. * * The check for little-endian architectures that tolerate unaligned memory * accesses is just an optimization. Nothing will break if it fails to detect * a suitable architecture. * * Unfortunately, this optimization may be a C strict aliasing rules violation * if the caller's data buffer has effective type that cannot be aliased by * MD5_u32plus. In practice, this problem may occur if these MD5 routines are * inlined into a calling function, or with future and dangerously advanced * link-time optimizations. For the time being, keeping these MD5 routines in * their own translation unit avoids the problem. */ #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) #define SET(n) \ (*(MD5_u32plus *)&ptr[(n) * 4]) #define GET(n) \ SET(n) #else #define SET(n) \ (ctx->block[(n)] = \ (MD5_u32plus)ptr[(n) * 4] | \ ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) #define GET(n) \ (ctx->block[(n)]) #endif /* * This processes one or more 64-byte data blocks, but does NOT update the bit * counters. There are no alignment requirements. */ static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) { const unsigned char *ptr; MD5_u32plus a, b, c, d; MD5_u32plus saved_a, saved_b, saved_c, saved_d; ptr = (const unsigned char *)data; a = ctx->a; b = ctx->b; c = ctx->c; d = ctx->d; do { saved_a = a; saved_b = b; saved_c = c; saved_d = d; /* Round 1 */ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) STEP(F, c, d, a, b, SET(2), 0x242070db, 17) STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) /* Round 2 */ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) STEP(G, d, a, b, c, GET(10), 0x02441453, 9) STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) /* Round 3 */ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) /* Round 4 */ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) a += saved_a; b += saved_b; c += saved_c; d += saved_d; ptr += 64; } while (size -= 64); ctx->a = a; ctx->b = b; ctx->c = c; ctx->d = d; return ptr; } void MD5_Init(MD5_CTX *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; ctx->d = 0x10325476; ctx->lo = 0; ctx->hi = 0; } void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) { MD5_u32plus saved_lo; unsigned long used, available; saved_lo = ctx->lo; if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) ctx->hi++; ctx->hi += size >> 29; used = saved_lo & 0x3f; if (used) { available = 64 - used; if (size < available) { memcpy(&ctx->buffer[used], data, size); return; } memcpy(&ctx->buffer[used], data, available); data = (const unsigned char *)data + available; size -= available; body(ctx, ctx->buffer, 64); } if (size >= 64) { data = body(ctx, data, size & ~(unsigned long)0x3f); size &= 0x3f; } memcpy(ctx->buffer, data, size); } #define OUT(dst, src) \ (dst)[0] = (unsigned char)(src); \ (dst)[1] = (unsigned char)((src) >> 8); \ (dst)[2] = (unsigned char)((src) >> 16); \ (dst)[3] = (unsigned char)((src) >> 24); void MD5_Final(unsigned char *result, MD5_CTX *ctx) { unsigned long used, available; used = ctx->lo & 0x3f; ctx->buffer[used++] = 0x80; available = 64 - used; if (available < 8) { memset(&ctx->buffer[used], 0, available); body(ctx, ctx->buffer, 64); used = 0; available = 64; } memset(&ctx->buffer[used], 0, available - 8); ctx->lo <<= 3; OUT(&ctx->buffer[56], ctx->lo) OUT(&ctx->buffer[60], ctx->hi) body(ctx, ctx->buffer, 64); OUT(&result[0], ctx->a) OUT(&result[4], ctx->b) OUT(&result[8], ctx->c) OUT(&result[12], ctx->d) memset(ctx, 0, sizeof(*ctx)); } #endif writexl/src/md5/md5.h0000644000176200001440000000260214747162622014075 0ustar liggesusers/* * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. * MD5 Message-Digest Algorithm (RFC 1321). * * Homepage: * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 * * Author: * Alexander Peslyak, better known as Solar Designer * * This software was written by Alexander Peslyak in 2001. No copyright is * claimed, and the software is hereby placed in the public domain. * In case this attempt to disclaim copyright and place the software in the * public domain is deemed null and void, then the software is * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the * general public under the following terms: * * Redistribution and use in source and binary forms, with or without * modification, are permitted. * * There's ABSOLUTELY NO WARRANTY, express or implied. * * See md5.c for more information. */ #ifdef HAVE_OPENSSL #include #elif !defined(_MD5_H) #define _MD5_H /* Any 32-bit or wider unsigned integer data type will do */ typedef unsigned int MD5_u32plus; typedef struct { MD5_u32plus lo, hi; MD5_u32plus a, b, c, d; unsigned char buffer[64]; MD5_u32plus block[16]; } MD5_CTX; extern void MD5_Init(MD5_CTX *ctx); extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); #endif writexl/NAMESPACE0000644000176200001440000000060614747162622013204 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method("[",xl_object) S3method("[[",xl_object) S3method(as.data.frame,xl_object) S3method(c,xl_object) S3method(print,xl_formula) S3method(rep,xl_object) export(lxw_version) export(write_xlsx) export(xl_formula) export(xl_hyperlink) useDynLib(writexl,C_lxw_version) useDynLib(writexl,C_set_tempdir) useDynLib(writexl,C_write_data_frame_list) writexl/LICENSE0000644000176200001440000000005114747162622012764 0ustar liggesusersYEAR: 2017 COPYRIGHT HOLDER: Jeroen Ooms writexl/inst/0000755000176200001440000000000014747162622012740 5ustar liggesuserswritexl/inst/AUTHORS0000644000176200001440000000033014747162622014004 0ustar liggesusersAuthors of bundled libxlsxwriter code (including 3rd party components): - libxlsxwriter: John McNamara - minizip: Gilles Vollant, Mathias Svensson - tmpfileplus: David Ireland, Mozilla Inc - tree.h: Niels Provos writexl/inst/COPYRIGHT0000644000176200001440000001537314747162622014244 0ustar liggesusersLicenses and copyrightholders of code included with 'libxlsxwriter' are available from: https://libxlsxwriter.github.io/license.html Below a copy of this text ------------------------->8-------------------------------------------- Libxlsxwriter is released under a FreeBSD license: Copyright 2014-2017, John McNamara 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. Libxlsxwriter includes `queue.h` and `tree.h` from FreeBSD, the `minizip` component of `zlib` and `tmpfileplus` which have the following licenses: Queue.h from FreeBSD: 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. Tree.h from 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. [Zlib](http://www.zlib.net) has the following License/Copyright: (C) 1995-2013 Jean-loup Gailly and Mark Adler 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. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu [MiniZip](http://www.winimage.com/zLibDll/minizip.html) (part of Zlib) Copyright (C) 1998-2010 Gilles Vollant Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) Condition of use and distribution are the same than Zlib (above). [Tmpfileplus](http://www.di-mgt.com.au/c_function_to_create_temp_file.html) has the following license: 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 . See the [Mozilla Public License, v. 2.0](http://mozilla.org/MPL/2.0/). writexl/inst/WORDLIST0000644000176200001440000000005614747162622014133 0ustar liggesusersAppVeyor libxlsxwriter roundtrip RStudio xlsx writexl/man/0000755000176200001440000000000014747162622012536 5ustar liggesuserswritexl/man/xl_formula.Rd0000644000176200001440000000145514747162622015202 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/excel_types.R \name{xl_formula} \alias{xl_formula} \alias{xl_hyperlink} \title{Excel Types} \usage{ xl_formula(x) xl_hyperlink(url, name = NULL) } \arguments{ \item{x}{character vector to be interpreted as formula} \item{url}{character vector of URLs} \item{name}{character vector of friendly names} } \description{ Create special column types to write to a spreadsheet } \examples{ df <- data.frame( name = c("UCLA", "Berkeley", "Jeroen"), founded = c(1919, 1868, 2030), website = xl_hyperlink(c("http://www.ucla.edu", "http://www.berkeley.edu", NA), "homepage") ) df$age <- xl_formula('=(YEAR(TODAY()) - INDIRECT("B" & ROW()))') write_xlsx(df, 'universities.xlsx') # cleanup unlink('universities.xlsx') } \concept{writexl} writexl/man/writexl.Rd0000644000176200001440000000033714747162622014526 0ustar liggesusers% 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. } writexl/man/write_xlsx.Rd0000644000176200001440000000247514747162622015245 0ustar liggesusers% 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, format_headers = TRUE, use_zip64 = FALSE ) } \arguments{ \item{x}{data frame or named list of data frames that will be sheets in the xlsx} \item{path}{a file name to write to} \item{col_names}{write column names at the top of the file?} \item{format_headers}{make the \code{col_names} in the xlsx centered and bold} \item{use_zip64}{use \href{https://en.wikipedia.org/wiki/Zip_(file_format)#ZIP64}{zip64} to enable support for 4GB+ xlsx files. Not all platforms can read this.} } \description{ Writes a data frame to an xlsx file. To create an xlsx with (multiple) named sheets, simply set \code{x} to a named list of data frames. } \details{ Currently supports strings, numbers, booleans and dates. Formatting options may be added in future versions. \if{html}{ \out{ }} } \examples{ # Roundtrip example with single excel sheet named 'mysheet' tmp <- write_xlsx(list(mysheet = iris)) readxl::read_xlsx(tmp) } writexl/DESCRIPTION0000644000176200001440000000223714766116671013501 0ustar liggesusersPackage: writexl Type: Package Title: Export Data Frames to Excel 'xlsx' Format Version: 1.5.2 Authors@R: c( person("Jeroen", "Ooms", ,"jeroenooms@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-4035-0289")), person("John McNamara", role = "cph", comment = "Author of libxlsxwriter (see AUTHORS and COPYRIGHT files for details)")) Description: Zero-dependency data frame to xlsx exporter based on 'libxlsxwriter' . Fast and no Java or Excel required. License: BSD_2_clause + file LICENSE Encoding: UTF-8 URL: https://ropensci.r-universe.dev/writexl https://docs.ropensci.org/writexl/ BugReports: https://github.com/ropensci/writexl/issues RoxygenNote: 7.0.2 Suggests: spelling, readxl, nycflights13, testthat, bit64 Language: en-US SystemRequirements: zlib NeedsCompilation: yes Packaged: 2025-03-17 21:14:10 UTC; jeroen Author: Jeroen Ooms [aut, cre] (), John McNamara [cph] (Author of libxlsxwriter (see AUTHORS and COPYRIGHT files for details)) Maintainer: Jeroen Ooms Repository: CRAN Date/Publication: 2025-03-17 22:10:01 UTC