writexl/ 0000755 0001762 0000144 00000000000 14561444652 011763 5 ustar ligges users writexl/NAMESPACE 0000644 0001762 0000144 00000000606 14356054020 013170 0 ustar ligges users # 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/LICENSE 0000644 0001762 0000144 00000000051 14356054020 012750 0 ustar ligges users YEAR: 2017
COPYRIGHT HOLDER: Jeroen Ooms
writexl/man/ 0000755 0001762 0000144 00000000000 14356054020 012522 5 ustar ligges users writexl/man/write_xlsx.Rd 0000644 0001762 0000144 00000002475 14356054020 015231 0 ustar ligges users % 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/man/xl_formula.Rd 0000644 0001762 0000144 00000001455 14356054020 015166 0 ustar ligges users % 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.Rd 0000644 0001762 0000144 00000000337 14356054020 014512 0 ustar ligges users % 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/DESCRIPTION 0000644 0001762 0000144 00000002275 14561444652 013477 0 ustar ligges users Package: writexl
Type: Package
Title: Export Data Frames to Excel 'xlsx' Format
Version: 1.5.0
Authors@R: c(
person("Jeroen", "Ooms", ,"jeroen@berkeley.edu", 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://docs.ropensci.org/writexl/ (website)
https://github.com/ropensci/writexl (devel)
https://libxlsxwriter.github.io (upstream)
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: 2024-02-09 14:40:26 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: 2024-02-09 15:50:02 UTC
writexl/tests/ 0000755 0001762 0000144 00000000000 14356054020 013111 5 ustar ligges users writexl/tests/spelling.R 0000644 0001762 0000144 00000000074 14356054020 015052 0 ustar ligges users spelling::spell_check_test(vignettes = TRUE, error = FALSE)
writexl/tests/testthat/ 0000755 0001762 0000144 00000000000 14561444652 014765 5 ustar ligges users writexl/tests/testthat/test-types.R 0000644 0001762 0000144 00000002300 14356054020 017210 0 ustar ligges users context("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.R 0000644 0001762 0000144 00000000432 14356054020 020351 0 ustar ligges users context("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.R 0000644 0001762 0000144 00000000072 14356054020 015073 0 ustar ligges users library(testthat)
library(writexl)
test_check("writexl")
writexl/src/ 0000755 0001762 0000144 00000000000 14561434521 012544 5 ustar ligges users writexl/src/md5/ 0000755 0001762 0000144 00000000000 14561434521 013231 5 ustar ligges users writexl/src/md5/md5.c 0000644 0001762 0000144 00000021047 14561432531 014065 0 ustar ligges users /*
* 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.h 0000644 0001762 0000144 00000002602 14561432531 014066 0 ustar ligges users /*
* 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/libxlsxwriter/ 0000755 0001762 0000144 00000000000 14561434521 015466 5 ustar ligges users writexl/src/libxlsxwriter/relationships.c 0000644 0001762 0000144 00000014342 14561432517 020525 0 ustar ligges users /*****************************************************************************
* 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/table.c 0000644 0001762 0000144 00000020273 14561432517 016730 0 ustar ligges users /*****************************************************************************
* 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/workbook.c 0000644 0001762 0000144 00000251421 14561432517 017477 0 ustar ligges users /*****************************************************************************
* 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/hash_table.c 0000644 0001762 0000144 00000014746 14561432517 017743 0 ustar ligges users /*****************************************************************************
* 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/libxlsxwriter/content_types.c 0000644 0001762 0000144 00000023146 14561432517 020541 0 ustar ligges users /*****************************************************************************
* 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/core.c 0000644 0001762 0000144 00000015215 14561432517 016571 0 ustar ligges users /*****************************************************************************
* 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/theme.c 0000644 0001762 0000144 00000026251 14561432517 016745 0 ustar ligges users /*****************************************************************************
* 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/styles.c 0000644 0001762 0000144 00000107656 14561432517 017177 0 ustar ligges users /*****************************************************************************
* 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/custom.c 0000644 0001762 0000144 00000012632 14561432517 017153 0 ustar ligges users /*****************************************************************************
* 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/xmlwriter.c 0000644 0001762 0000144 00000026672 14561432517 017707 0 ustar ligges users /*****************************************************************************
* 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, "%s>", 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, "%s>", 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/drawing.c 0000644 0001762 0000144 00000055774 14561432517 017312 0 ustar ligges users /*****************************************************************************
* 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/format.c 0000644 0001762 0000144 00000041132 14561432517 017126 0 ustar ligges users /*****************************************************************************
* 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.c 0000644 0001762 0000144 00000023757 14561432517 017315 0 ustar ligges users /*****************************************************************************
* 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/metadata.c 0000644 0001762 0000144 00000015022 14561432517 017415 0 ustar ligges users /*****************************************************************************
* 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/chart.c 0000644 0001762 0000144 00000522414 14561432517 016746 0 ustar ligges users /*****************************************************************************
* 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