This commit is contained in:
Nick 2026-01-03 12:48:57 -06:00
commit d6e295f220
507 changed files with 8950 additions and 1199 deletions

1
.indent.pro vendored
View file

@ -109,6 +109,7 @@
-T lxw_drawing_object
-T lxw_drawing_rel_id
-T lxw_error
-T lxw_feature_property_bag
-T lxw_fill
-T lxw_filter_rule
-T lxw_filter_rule_obj

View file

@ -3,16 +3,92 @@
## Information for Packagers
- This is serial version 1.2.3. This is not a semver version number.
- This is serial version 1.2.4. This is not a semver version number.
- This is shared object library version `libxlsxwriter.so.10`. This should be
- This is shared object library version `libxlsxwriter.so.11`. This should be
updated automatically via `make` and `cmake` as part of the build process.
This version contains ABI changes since the previous release.
- Please report any downstream patches back upstream to help improve the overall
build process.
## 1.2.4 January 2 2026
- Added support for secondary Y axis in charts. Series can be assigned to a
secondary Y axis using an optional fourth parameter to `chart_add_series()`.
Secondary axes are accessible via the `chart->x2_axis` and `chart->y2_axis`
pointers, and all existing `chart_axis_set_*()` functions work with these
secondary axis pointers. The API is backward compatible with existing code.
- Added support for combined charts via `chart_combine()`. Combined charts
allow overlaying two different chart types (e.g., Column + Line, Bar + Line,
Area + Column) on the same plot area. For combined charts with secondary
axes, set `chart->is_secondary = LXW_TRUE` on the secondary chart.
- Added `chart_set_series_gap_y2()` and `chart_set_series_overlap_y2()` to set
the gap and overlap for series on a secondary Y axis in Bar/Column charts.
- Added support for Stock charts via `LXW_CHART_STOCK`. Stock charts display
High-Low-Close (HLC) or Open-High-Low-Close (OHLC) data. Default series
formatting is applied automatically (line with no fill, appropriate markers).
Stock charts support hi-low lines, drop lines, and up-down bars.
- Added `LXW_CHART_MARKER_DOT` marker type used by Stock charts.
- Added support for gradient fills in chart series, plotarea, and chartarea.
New functions: `chart_series_set_gradient()`, `chart_plotarea_set_gradient()`,
and `chart_chartarea_set_gradient()`. Supports linear, radial, rectangular,
and path gradient types with 2-10 color stops.
- Added support for textboxes via `worksheet_insert_textbox()` and
`worksheet_insert_textbox_opt()`. Textboxes allow inserting text with a
rectangular shape into a worksheet. The optional `lxw_textbox_options`
struct allows setting width, height, offsets, scale, and description.
- Added new chart examples:
- `chart_stock.c`: Demonstrates stock chart creation with High-Low-Close data.
- `chart_pareto.c`: Shows how to create a Pareto chart using combined Column
and Line charts with a secondary Y axis.
- `chart_gauge.c`: Demonstrates creating a gauge chart by combining Doughnut
and Pie charts with custom point colors.
- `chart_gradient.c`: Shows how to apply gradient fills to chart series
and the plot area.
- Added `textbox.c` example demonstrating textbox insertion.
- Added support for sparklines via `worksheet_add_sparkline()`. Sparklines are
mini-charts that fit in a single cell to show data trends at a glance. The
`lxw_sparkline_options` struct allows configuring:
- Sparkline type: line, column, or win/loss
- Predefined styles (0-35)
- Point markers: high, low, negative, first, last, and all markers
- Custom colors for series, markers, and axis
- Axis options: min, max, date axis, reverse, show hidden data
Added new example:
- `sparklines.c`: Demonstrates creating different sparkline types with
various options and custom colors.
- Added support for chart date axis via `chart_axis_set_date_axis()`. This
enables date-based X axis on charts with automatic date formatting. Related
functions for configuring time units:
- `chart_axis_set_major_unit_type()`: Set major time unit (days/months/years)
- `chart_axis_set_minor_unit_type()`: Set minor time unit (days/months/years)
- Added autofit helper utility functions for calculating column widths based
on string content:
- `lxw_pixel_width()`: Calculate pixel width of a string (Calibri 11 font)
- `lxw_autofit_width()`: Calculate column width suitable for
`worksheet_set_column()`
Note: Excel does not store autofit information in files; these functions
help users calculate appropriate column widths programmatically.
- Added support for inserting checkboxes in worksheet cells via
`worksheet_insert_checkbox()`. Checkboxes are a feature of Excel 365
from 2024 onwards. They are boolean values displayed as interactive
checkboxes in cells. Added new example:
- `checkbox.c`: Demonstrates inserting checkboxes with different states.
## 1.2.3 Jun 30 2025
- Added support for handling dates in the Excel 1904 epoch. See

View file

@ -1,16 +1,14 @@
const std = @import("std");
const zon_version = @import("build.zig.zon").version;
const xlsxw_version: std.SemanticVersion = .{
.major = 1,
.minor = 1,
.patch = 9,
};
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const options: BuildConfig = .{
.target = target,
.optimize = optimize,
};
const xlsxw_version = std.SemanticVersion.parse(zon_version) catch |err| {
std.debug.panic("Error: {s}", .{@errorName(err)});
};
const shared = b.option(bool, "SHARED_LIBRARY", "Build the Shared Library [default: false]") orelse false;
const examples = b.option(bool, "BUILD_EXAMPLES", "Build libxlsxwriter examples [default: false]") orelse false;
@ -20,11 +18,15 @@ pub fn build(b: *std.Build) void {
const md5 = b.option(bool, "USE_OPENSSL_MD5", "Build libxlsxwriter with the OpenSSL MD5 lib [default: off]") orelse false;
const stdtmpfile = b.option(bool, "USE_STANDARD_TMPFILE", "Use the C standard library's tmpfile() [default: off]") orelse false;
const lib = b.addLibrary(.{
const lib = if (shared) b.addSharedLibrary(.{
.name = "xlsxwriter",
.root_module = createModule(b, options),
.linkage = if (shared) .dynamic else .static,
.target = target,
.optimize = optimize,
.version = xlsxw_version,
}) else b.addStaticLibrary(.{
.name = "xlsxwriter",
.target = target,
.optimize = optimize,
});
lib.pie = true;
switch (optimize) {
@ -78,7 +80,7 @@ pub fn build(b: *std.Build) void {
});
}
const zlib = buildZlib(b, options);
const zlib = buildZlib(b, .{ target, optimize });
lib.linkLibrary(zlib);
lib.installLibraryHeaders(zlib);
@ -120,52 +122,42 @@ pub fn build(b: *std.Build) void {
// build examples
if (examples) {
buildExe(b, .{
.options = options,
.lib = lib,
.path = "examples/anatomy.c",
});
buildExe(b, .{
.options = options,
.lib = lib,
.path = "examples/array_formula.c",
});
buildExe(b, .{
.options = options,
.lib = lib,
.path = "examples/autofilter.c",
});
buildExe(b, .{
.options = options,
.lib = lib,
.path = "examples/background.c",
});
buildExe(b, .{
.options = options,
.lib = lib,
.path = "examples/chart_area.c",
});
buildExe(b, .{
.options = options,
.lib = lib,
.path = "examples/chart_column.c",
});
buildExe(b, .{
.options = options,
.lib = lib,
.path = "examples/data_validate.c",
});
buildExe(b, .{
.options = options,
.lib = lib,
.path = "examples/hello.c",
});
buildExe(b, .{
.options = options,
.lib = lib,
.path = "examples/watermark.c",
});
buildExe(b, .{
.options = options,
.lib = lib,
.path = "examples/worksheet_protection.c",
});
@ -173,112 +165,93 @@ pub fn build(b: *std.Build) void {
// build tests
if (tests) {
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/app/test_app.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/chart/test_chart.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/chartsheet/test_chartsheet.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/content_types/test_content_types.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/content_types/test_content_types_write_default.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/content_types/test_content_types_write_override.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/relationships/test_relationships.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/app/test_app_xml_declaration.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/relationships/test_relationships_xml_declaration.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/custom/test_custom_xml_declaration.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/metadata/test_metadata_xml_declaration.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/core/test_core_xml_declaration.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/sst/test_shared_strings.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/workbook/test_workbook.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/xmlwriter/test_xmlwriter.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/table/test_table01.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/table/test_table02.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/table/test_table03.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/table/test_table04.c",
});
buildTest(b, .{
.options = options,
.lib = lib,
.path = "test/unit/styles/test_styles_write_border.c",
});
}
}
fn buildExe(b: *std.Build, info: BuildExec) void {
fn buildExe(b: *std.Build, info: BuildInfo) void {
const exe = b.addExecutable(.{
.name = info.filename(),
.root_module = createModule(b, info.options),
.optimize = info.lib.root_module.optimize.?,
.target = info.lib.root_module.resolved_target.?,
});
exe.addCSourceFile(.{
.file = b.path(info.path),
@ -304,10 +277,11 @@ fn buildExe(b: *std.Build, info: BuildExec) void {
run_step.dependOn(&run_cmd.step);
}
fn buildTest(b: *std.Build, info: BuildExec) void {
fn buildTest(b: *std.Build, info: BuildInfo) void {
const exe = b.addExecutable(.{
.name = info.filename(),
.root_module = createModule(b, info.options),
.optimize = info.lib.root_module.optimize.?,
.target = info.lib.root_module.resolved_target.?,
});
exe.root_module.addCMacro("TESTING", "");
exe.addCSourceFile(.{
@ -319,10 +293,10 @@ fn buildTest(b: *std.Build, info: BuildExec) void {
.flags = cflags,
});
exe.addIncludePath(b.path("test/unit"));
exe.linkLibrary(info.lib);
for (info.lib.root_module.include_dirs.items) |include| {
exe.root_module.include_dirs.append(b.allocator, include) catch @panic("OOM");
exe.root_module.include_dirs.append(b.allocator, include) catch {};
}
exe.linkLibrary(info.lib);
exe.linkLibC();
b.installArtifact(exe);
@ -339,13 +313,6 @@ fn buildTest(b: *std.Build, info: BuildExec) void {
run_step.dependOn(&run_cmd.step);
}
fn createModule(b: *std.Build, options: BuildConfig) *std.Build.Module {
return b.createModule(.{
.target = options.target,
.optimize = options.optimize,
});
}
const cflags = &.{
"-std=c89",
"-Wall",
@ -359,29 +326,25 @@ const minizip_src: []const []const u8 = &.{
"third_party/minizip/zip.c",
};
const BuildExec = struct {
options: BuildConfig,
const BuildInfo = struct {
lib: *std.Build.Step.Compile,
path: []const u8,
fn filename(self: BuildExec) []const u8 {
fn filename(self: BuildInfo) []const u8 {
var split = std.mem.splitSequence(u8, std.fs.path.basename(self.path), ".");
return split.first();
}
};
const BuildConfig = struct {
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
};
fn buildZlib(b: *std.Build, options: BuildConfig) *std.Build.Step.Compile {
const libz = b.addLibrary(.{
fn buildZlib(b: *std.Build, options: anytype) *std.Build.Step.Compile {
const libz = b.addStaticLibrary(.{
.name = "z",
.root_module = createModule(b, options),
.target = options[0],
.optimize = options[1],
});
if (b.lazyDependency("zlib", .{
.target = options.target,
.optimize = options.optimize,
.target = options[0],
.optimize = options[1],
})) |zlib_path| {
libz.addIncludePath(zlib_path.path(""));
libz.addCSourceFiles(.{

View file

@ -2,7 +2,7 @@
.name = .libxlsxwriter,
.version = "1.1.9",
.fingerprint = 0xa28d9a85f22fad0e,
.minimum_zig_version = "0.15.1",
.minimum_zig_version = "0.14.0",
.dependencies = .{
.zlib = .{
.url = "git+https://github.com/madler/zlib#v1.3.1",

View file

@ -3,7 +3,7 @@ cd $SRC/libxlsxwriter
printenv
mkdir -p build
cmake -S . -B build -DBUILD_FUZZERS=ON -DBUILD_TESTS=ON && cmake --build build --target install
cmake -S . -B build -DBUILD_FUZZERS=ON && cmake --build build --target install
# Build the corpus using the existing xlsx files in the source
mkdir -p corpus

368
docs/WINDOWS_BUILD.md Normal file
View file

@ -0,0 +1,368 @@
# Building libxlsxwriter on Windows with Visual Studio 2026
This guide provides step-by-step instructions for building libxlsxwriter as a DLL on Windows using Visual Studio 18 2026, with static linking to zlib.
## Prerequisites
- Visual Studio 2026 (VS 18) with C++ Desktop Development workload
- CMake 3.15 or later (included with Visual Studio or install separately)
- Git for Windows
## Setting Up the Build Environment
Before building, you must initialize the Visual C++ environment for your target architecture.
### For 32-bit Builds
Open a **Command Prompt** and run:
```cmd
"C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvars32.bat"
```
### For 64-bit Builds
Open a **Command Prompt** and run:
```cmd
"C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvars64.bat"
```
> **Note:** If you have Visual Studio Professional or Enterprise edition, replace `Community` with `Professional` or `Enterprise` in the path.
> **Important:** You must use separate command prompt windows for 32-bit and 64-bit builds, or re-run the appropriate vcvars batch file when switching architectures.
## Directory Structure
After following these instructions, you will have:
```
C:\dev\
├── zlib\
│ ├── build32\ # 32-bit zlib static library
│ └── build64\ # 64-bit zlib static library
└── libxlsxwriter\
├── build32\ # 32-bit libxlsxwriter DLL
└── build64\ # 64-bit libxlsxwriter DLL
```
## Step 1: Create Working Directory
Open **Developer Command Prompt for VS 2026** or **PowerShell** and run:
```cmd
mkdir C:\dev
cd C:\dev
```
## Step 2: Clone and Build zlib
### Clone zlib Repository
```cmd
git clone https://github.com/madler/zlib.git
cd zlib
```
### Build 32-bit Static zlib Library
First, set up the 32-bit build environment:
```cmd
"C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvars32.bat"
```
Then build zlib:
```cmd
mkdir build32
cd build32
cmake .. -G "Visual Studio 18 2026" -A Win32 ^
-DCMAKE_INSTALL_PREFIX=C:/dev/zlib/install32 ^
-DBUILD_SHARED_LIBS=OFF
cmake --build . --config Release
cmake --install . --config Release
cd ..
```
### Build 64-bit Static zlib Library
Set up the 64-bit build environment (open a new command prompt or re-initialize):
```cmd
"C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvars64.bat"
```
Then build zlib:
```cmd
cd C:\dev\zlib
mkdir build64
cd build64
cmake .. -G "Visual Studio 18 2026" -A x64 ^
-DCMAKE_INSTALL_PREFIX=C:/dev/zlib/install64 ^
-DBUILD_SHARED_LIBS=OFF
cmake --build . --config Release
cmake --install . --config Release
cd ../..
```
## Step 3: Clone and Build libxlsxwriter
### Clone libxlsxwriter Repository
```cmd
git clone https://github.com/jmcnamara/libxlsxwriter.git
cd libxlsxwriter
```
### Build 32-bit libxlsxwriter DLL
Set up the 32-bit build environment:
```cmd
"C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvars32.bat"
```
Then build libxlsxwriter:
```cmd
cd C:\dev\libxlsxwriter
mkdir build32
cd build32
cmake .. -G "Visual Studio 18 2026" -A Win32 ^
-DZLIB_ROOT=C:/dev/zlib/install32 ^
-DZLIB_LIBRARY=C:/dev/zlib/install32/lib/zlibstatic.lib ^
-DZLIB_INCLUDE_DIR=C:/dev/zlib/install32/include ^
-DBUILD_SHARED_LIBS=ON ^
-DUSE_STATIC_MSVC_RUNTIME=OFF
cmake --build . --config Release
cd ..
```
The 32-bit DLL will be located at: `build32\Release\xlsxwriter.dll`
### Build 64-bit libxlsxwriter DLL
Set up the 64-bit build environment (open a new command prompt or re-initialize):
```cmd
"C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvars64.bat"
```
Then build libxlsxwriter:
```cmd
cd C:\dev\libxlsxwriter
mkdir build64
cd build64
cmake .. -G "Visual Studio 18 2026" -A x64 ^
-DZLIB_ROOT=C:/dev/zlib/install64 ^
-DZLIB_LIBRARY=C:/dev/zlib/install64/lib/zlibstatic.lib ^
-DZLIB_INCLUDE_DIR=C:/dev/zlib/install64/include ^
-DBUILD_SHARED_LIBS=ON ^
-DUSE_STATIC_MSVC_RUNTIME=OFF
cmake --build . --config Release
cd ..
```
The 64-bit DLL will be located at: `build64\Release\xlsxwriter.dll`
## Step 4: Verify Build Output
After successful builds, you should have the following files:
### 32-bit Build
```
libxlsxwriter\build32\Release\
├── xlsxwriter.dll # Dynamic library
├── xlsxwriter.lib # Import library for linking
└── xlsxwriter.pdb # Debug symbols (optional)
```
### 64-bit Build
```
libxlsxwriter\build64\Release\
├── xlsxwriter.dll # Dynamic library
├── xlsxwriter.lib # Import library for linking
└── xlsxwriter.pdb # Debug symbols (optional)
```
## Optional: Build Static Library with Static MSVC Runtime
If you prefer a fully static build (no C runtime DLL dependency), use these options:
```cmd
cmake .. -G "Visual Studio 18 2026" -A x64 ^
-DZLIB_ROOT=C:/dev/zlib/install64 ^
-DZLIB_LIBRARY=C:/dev/zlib/install64/lib/zlibstatic.lib ^
-DZLIB_INCLUDE_DIR=C:/dev/zlib/install64/include ^
-DBUILD_SHARED_LIBS=OFF ^
-DUSE_STATIC_MSVC_RUNTIME=ON
cmake --build . --config Release
```
## Optional: Build Debug Configuration
To build debug versions, replace `Release` with `Debug`:
```cmd
cmake --build . --config Debug
```
## Using the Library in Your Project
### For DLL Builds
1. Copy `xlsxwriter.dll` to your application directory or system PATH
2. Link against `xlsxwriter.lib` (import library)
3. Include headers from `libxlsxwriter\include\`
### CMake Integration
```cmake
find_package(ZLIB REQUIRED)
add_subdirectory(path/to/libxlsxwriter)
target_link_libraries(your_app PRIVATE xlsxwriter)
```
### Visual Studio Project Settings
1. Add to **Additional Include Directories**: `C:\dev\libxlsxwriter\include`
2. Add to **Additional Library Directories**: `C:\dev\libxlsxwriter\build64\Release`
3. Add to **Additional Dependencies**: `xlsxwriter.lib`
## Troubleshooting
### CMake Cannot Find zlib
Ensure the paths are correct and use forward slashes (`/`) in CMake paths:
```cmd
-DZLIB_ROOT=C:/dev/zlib/install64
```
### Linker Errors with zlib
If you get unresolved external symbols for zlib functions, verify:
- You're linking against `zlibstatic.lib` (not `zlib.lib`)
- The architecture matches (Win32 with Win32, x64 with x64)
### Missing vcruntime DLLs
If the DLL requires Visual C++ runtime, either:
- Install the Visual C++ Redistributable on target machines
- Build with `-DUSE_STATIC_MSVC_RUNTIME=ON` for static runtime
### Build Errors with Examples
To skip building examples:
```cmd
cmake .. -DBUILD_EXAMPLES=OFF ...
```
## Complete Build Script
Save as `build_all.bat`:
```batch
@echo off
setlocal
set DEV_DIR=C:\dev
set GENERATOR="Visual Studio 18 2026"
set VCVARS_PATH=C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build
:: Create dev directory
mkdir %DEV_DIR% 2>nul
cd /d %DEV_DIR%
:: Clone zlib
echo === Cloning zlib ===
git clone https://github.com/madler/zlib.git
:: Clone libxlsxwriter
echo === Cloning libxlsxwriter ===
git clone https://github.com/jmcnamara/libxlsxwriter.git
:: ============================================================
:: Build 32-bit versions
:: ============================================================
echo === Setting up 32-bit build environment ===
call "%VCVARS_PATH%\vcvars32.bat"
echo === Building 32-bit zlib ===
cd /d %DEV_DIR%\zlib
mkdir build32 2>nul
cd build32
cmake .. -G %GENERATOR% -A Win32 -DCMAKE_INSTALL_PREFIX=%DEV_DIR%/zlib/install32 -DBUILD_SHARED_LIBS=OFF
cmake --build . --config Release
cmake --install . --config Release
echo === Building 32-bit libxlsxwriter ===
cd /d %DEV_DIR%\libxlsxwriter
mkdir build32 2>nul
cd build32
cmake .. -G %GENERATOR% -A Win32 ^
-DZLIB_ROOT=%DEV_DIR%/zlib/install32 ^
-DZLIB_LIBRARY=%DEV_DIR%/zlib/install32/lib/zlibstatic.lib ^
-DZLIB_INCLUDE_DIR=%DEV_DIR%/zlib/install32/include ^
-DBUILD_SHARED_LIBS=ON
cmake --build . --config Release
:: ============================================================
:: Build 64-bit versions
:: ============================================================
echo === Setting up 64-bit build environment ===
call "%VCVARS_PATH%\vcvars64.bat"
echo === Building 64-bit zlib ===
cd /d %DEV_DIR%\zlib
mkdir build64 2>nul
cd build64
cmake .. -G %GENERATOR% -A x64 -DCMAKE_INSTALL_PREFIX=%DEV_DIR%/zlib/install64 -DBUILD_SHARED_LIBS=OFF
cmake --build . --config Release
cmake --install . --config Release
echo === Building 64-bit libxlsxwriter ===
cd /d %DEV_DIR%\libxlsxwriter
mkdir build64 2>nul
cd build64
cmake .. -G %GENERATOR% -A x64 ^
-DZLIB_ROOT=%DEV_DIR%/zlib/install64 ^
-DZLIB_LIBRARY=%DEV_DIR%/zlib/install64/lib/zlibstatic.lib ^
-DZLIB_INCLUDE_DIR=%DEV_DIR%/zlib/install64/include ^
-DBUILD_SHARED_LIBS=ON
cmake --build . --config Release
:: ============================================================
:: Done
:: ============================================================
echo.
echo === Build Complete ===
echo 32-bit DLL: %DEV_DIR%\libxlsxwriter\build32\Release\xlsxwriter.dll
echo 64-bit DLL: %DEV_DIR%\libxlsxwriter\build64\Release\xlsxwriter.dll
echo.
pause
```
## Additional Resources
- [libxlsxwriter Documentation](https://libxlsxwriter.github.io/)
- [CMake Documentation](https://cmake.org/documentation/)
- [zlib Home Page](https://zlib.net/)

View file

@ -1294,17 +1294,7 @@ The chart functions that support #lxw_chart_layout are:
@section ww_charts_limitations Chart Limitations
The following chart features aren't currently supported in libxlsxwriter but
will be in time. See the [GitHub Chart Feature Requests][1].
[1]: https://github.com/jmcnamara/libxlsxwriter/issues
- Secondary axis.
- Combined charts.
- Chartsheets.
If required these features are all available in the Perl and Python versions
of this library.
All chart features in the original Excel specification are supported.
Next: @ref working_with_object_positioning

View file

@ -40,9 +40,9 @@ int main() {
/* Configure the chart. In simplest case we just add some value data
* series. The NULL categories will default to 1 to 5 like in Excel.
*/
chart_add_series(chart, NULL, "Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "Sheet1!$C$1:$C$5", 0);
lxw_chart_font font = {.bold = LXW_EXPLICIT_FALSE, .color = LXW_COLOR_BLUE};

View file

@ -55,14 +55,14 @@ int main() {
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_AREA);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add a second series but leave the categories and values undefined. They
* can be defined later using the alternative syntax shown below. */
series = chart_add_series(chart, NULL, NULL);
series = chart_add_series(chart, NULL, NULL, 0);
/* Configure the series using a syntax that is easier to define programmatically. */
chart_series_set_categories(series, "Sheet1", 1, 0, 6, 0); /* "=Sheet1!$A$2:$A$7" */
@ -87,13 +87,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_AREA_STACKED);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");
@ -116,13 +116,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_AREA_STACKED_PERCENT);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");

View file

@ -55,14 +55,14 @@ int main() {
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_BAR);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add a second series but leave the categories and values undefined. They
* can be defined later using the alternative syntax shown below. */
series = chart_add_series(chart, NULL, NULL);
series = chart_add_series(chart, NULL, NULL, 0);
/* Configure the series using a syntax that is easier to define programmatically. */
chart_series_set_categories(series, "Sheet1", 1, 0, 6, 0); /* "=Sheet1!$A$2:$A$7" */
@ -87,13 +87,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_BAR_STACKED);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");
@ -116,13 +116,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_BAR_STACKED_PERCENT);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");

View file

@ -71,15 +71,15 @@ int main() {
*/
chart_add_series(chart,
"=Sheet1!$A$2:$B$6",
"=Sheet1!$C$2:$C$6");
"=Sheet1!$C$2:$C$6", 0);
chart_add_series(chart,
"=Sheet1!$A$2:$B$6",
"=Sheet1!$D$2:$D$6");
"=Sheet1!$D$2:$D$6", 0);
chart_add_series(chart,
"=Sheet1!$A$2:$B$6",
"=Sheet1!$E$2:$E$6");
"=Sheet1!$E$2:$E$6", 0);
/* Set an Excel chart style. */
chart_set_style(chart, 37);

View file

@ -55,14 +55,14 @@ int main() {
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add a second series but leave the categories and values undefined. They
* can be defined later using the alternative syntax shown below. */
series = chart_add_series(chart, NULL, NULL);
series = chart_add_series(chart, NULL, NULL, 0);
/* Configure the series using a syntax that is easier to define programmatically. */
chart_series_set_categories(series, "Sheet1", 1, 0, 6, 0); /* "=Sheet1!$A$2:$A$7" */
@ -87,13 +87,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_COLUMN_STACKED);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");
@ -116,13 +116,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_COLUMN_STACKED_PERCENT);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");

146
examples/chart_combined.c Normal file
View file

@ -0,0 +1,146 @@
/*
* An example of creating combined charts using the libxlsxwriter library.
*
* A combined chart is one that shows two chart types, such as a column chart
* combined with a line chart. They can share the same Y axis or have a
* secondary Y axis.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
/*
* Write some data to the worksheet.
*/
void write_worksheet_data(lxw_worksheet *worksheet, lxw_format *bold) {
int row, col;
uint8_t data[6][3] = {
/* Three columns of data. */
{2, 10, 30},
{3, 40, 60},
{4, 50, 70},
{5, 20, 50},
{6, 10, 40},
{7, 50, 30}
};
worksheet_write_string(worksheet, CELL("A1"), "Number", bold);
worksheet_write_string(worksheet, CELL("B1"), "Batch 1", bold);
worksheet_write_string(worksheet, CELL("C1"), "Batch 2", bold);
for (row = 0; row < 6; row++)
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row + 1, col, data[row][col], NULL);
}
/*
* Create a worksheet with combined chart examples.
*/
int main() {
lxw_workbook *workbook = workbook_new("chart_combined.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart_series *series;
/* Add a bold format to use to highlight the header cells. */
lxw_format *bold = workbook_add_format(workbook);
format_set_bold(bold);
/* Write some data for the chart. */
write_worksheet_data(worksheet, bold);
/*
* Chart 1. Create a combined column and line chart with shared Y axis.
*
* In this example we will create a combined column and line chart.
* They will share the same X and Y axes.
*/
/* Create a column chart - this will be the primary chart. */
lxw_chart *column_chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* Add the first series to the primary chart. */
series = chart_add_series(column_chart,
"=Sheet1!$A$2:$A$7",
"=Sheet1!$B$2:$B$7",
0); /* Primary Y axis */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Create a line chart - this will be the secondary (combined) chart. */
lxw_chart *line_chart = workbook_add_chart(workbook, LXW_CHART_LINE);
/* Add the first series to the secondary chart. */
series = chart_add_series(line_chart,
"=Sheet1!$A$2:$A$7",
"=Sheet1!$C$2:$C$7",
0); /* Primary Y axis (shared with column chart) */
chart_series_set_name(series, "=Sheet1!$C$1");
/* Combine the charts. The line chart is overlaid on the column chart. */
chart_combine(column_chart, line_chart);
/* Add a chart title and axis labels. Note: these are set on the
* primary chart. */
chart_title_set_name(column_chart, "Combined chart - same Y axis");
chart_axis_set_name(column_chart->x_axis, "Test number");
chart_axis_set_name(column_chart->y_axis, "Sample length (mm)");
/* Insert the chart into the worksheet. */
worksheet_insert_chart(worksheet, CELL("E2"), column_chart);
/*
* Chart 2. Create a combined column and line chart with secondary Y axis.
*
* In this example we create a similar combined column and line chart
* except that the line chart has a secondary Y axis on the right side.
*/
/* Create a column chart - this will be the primary chart. */
lxw_chart *column_chart2 = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* Add the first series to the primary chart. */
series = chart_add_series(column_chart2,
"=Sheet1!$A$2:$A$7",
"=Sheet1!$B$2:$B$7",
0); /* Primary Y axis */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Create a line chart - this will be the secondary (combined) chart. */
lxw_chart *line_chart2 = workbook_add_chart(workbook, LXW_CHART_LINE);
/* Add the first series to the secondary chart.
*
* Set y2_axis = 1 to place this series on the secondary Y axis.
* This tells the library to:
* 1. Use secondary axis IDs for this series
* 2. Write the secondary axis elements (Y2 on right, X2 hidden)
*/
series = chart_add_series(line_chart2,
"=Sheet1!$A$2:$A$7",
"=Sheet1!$C$2:$C$7",
1); /* y2_axis = 1: Use secondary Y axis */
chart_series_set_name(series, "=Sheet1!$C$1");
/* Combine the charts. */
chart_combine(column_chart2, line_chart2);
/* Add a chart title and axis labels. */
chart_title_set_name(column_chart2, "Combined chart - secondary Y axis");
chart_axis_set_name(column_chart2->x_axis, "Test number");
chart_axis_set_name(column_chart2->y_axis, "Sample length (mm)");
/* Configure the secondary Y axis (accessed via the primary chart).
* The secondary Y axis appears on the right side of the chart. */
chart_axis_set_name(column_chart2->y2_axis, "Target length (mm)");
/* Insert the chart into the worksheet. */
worksheet_insert_chart(worksheet, CELL("E18"), column_chart2);
return workbook_close(workbook);
}

View file

@ -59,7 +59,7 @@ int main() {
/* Add a data series to the chart. */
lxw_chart_series *series = chart_add_series(chart, "=Sheet1!$A$2:$A$7",
"=Sheet1!$B$2:$B$7");
"=Sheet1!$B$2:$B$7", 0);
/* Add the series data labels. */
chart_series_set_labels(series);
@ -81,7 +81,7 @@ int main() {
/* Add a data series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7",
"=Sheet1!$B$2:$B$7");
"=Sheet1!$B$2:$B$7", 0);
/* Add the series data labels. */
chart_series_set_labels(series);
@ -106,7 +106,7 @@ int main() {
/* Add a data series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7",
"=Sheet1!$B$2:$B$7");
"=Sheet1!$B$2:$B$7", 0);
/* Add the series data labels. */
chart_series_set_labels(series);
@ -131,7 +131,7 @@ int main() {
/* Add a data series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7",
"=Sheet1!$B$2:$B$7");
"=Sheet1!$B$2:$B$7", 0);
/* Add the series data labels. */
chart_series_set_labels(series);
@ -160,7 +160,7 @@ int main() {
/* Add a data series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7",
"=Sheet1!$B$2:$B$7");
"=Sheet1!$B$2:$B$7", 0);
/* Add the series data labels. */
chart_series_set_labels(series);
@ -204,7 +204,7 @@ int main() {
/* Add a data series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7",
"=Sheet1!$B$2:$B$7");
"=Sheet1!$B$2:$B$7", 0);
/* Add the series data labels. */
chart_series_set_labels(series);
@ -248,7 +248,7 @@ int main() {
/* Add a data series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7",
"=Sheet1!$B$2:$B$7");
"=Sheet1!$B$2:$B$7", 0);
lxw_chart_font font2 = {.color = LXW_COLOR_RED};
@ -296,7 +296,7 @@ int main() {
/* Add a data series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7",
"=Sheet1!$B$2:$B$7");
"=Sheet1!$B$2:$B$7", 0);
/* Add the series data labels. */
chart_series_set_labels(series);
@ -339,7 +339,7 @@ int main() {
/* Add a data series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7",
"=Sheet1!$B$2:$B$7");
"=Sheet1!$B$2:$B$7", 0);
/* Add the series data labels. */
chart_series_set_labels(series);

View file

@ -56,14 +56,14 @@ int main() {
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add a second series but leave the categories and values undefined. They
* can be defined later using the alternative syntax shown below. */
series = chart_add_series(chart, NULL, NULL);
series = chart_add_series(chart, NULL, NULL, 0);
/* Configure the series using a syntax that is easier to define programmatically. */
chart_series_set_categories(series, "Sheet1", 1, 0, 6, 0); /* "=Sheet1!$A$2:$A$7" */
@ -88,13 +88,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");

View file

@ -60,8 +60,8 @@ int main() {
chart_title_set_name(chart, "Chart with High-Low Lines");
/* Add the first series to the chart. */
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Add high-low lines to the chart. */
chart_set_high_low_lines(chart, NULL);
@ -79,8 +79,8 @@ int main() {
chart_title_set_name(chart, "Chart with Drop Lines");
/* Add the first series to the chart. */
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Add drop lines to the chart. */
chart_set_drop_lines(chart, NULL);
@ -98,8 +98,8 @@ int main() {
chart_title_set_name(chart, "Chart with Up-Down bars");
/* Add the first series to the chart. */
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Add Up-Down bars to the chart. */
chart_set_up_down_bars(chart);
@ -117,8 +117,8 @@ int main() {
chart_title_set_name(chart, "Chart with Up-Down bars");
/* Add the first series to the chart. */
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Add Up-Down bars to the chart, with formatting. */
lxw_chart_line line = {.color = LXW_COLOR_BLACK};
@ -140,8 +140,8 @@ int main() {
chart_title_set_name(chart, "Chart with Data Labels and Markers");
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
chart_add_series( chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
chart_add_series( chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Add series markers. */
chart_series_set_marker_type(series, LXW_CHART_MARKER_CIRCLE);
@ -162,8 +162,8 @@ int main() {
chart_title_set_name(chart, "Chart with Error Bars");
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
chart_add_series( chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
chart_add_series( chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Add error bars to show Standard Error. */
chart_series_set_error_bars(series->y_error_bars,
@ -185,8 +185,8 @@ int main() {
chart_title_set_name(chart, "Chart with a Trendline");
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
chart_add_series( chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
chart_add_series( chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Add a polynomial trendline. */
lxw_chart_line poly_line = {.color = LXW_COLOR_GRAY,

View file

@ -54,7 +54,7 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_DOUGHNUT);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "Doughnut sales data");
@ -75,7 +75,7 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_DOUGHNUT);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "Doughnut sales data");
@ -112,7 +112,7 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_DOUGHNUT);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "Doughnut sales data");
@ -135,7 +135,7 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_DOUGHNUT);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "Doughnut sales data");

View file

@ -27,7 +27,7 @@ int main() {
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE);
/* Configure the chart. */
chart_add_series(chart, NULL, "Sheet1!$A$1:$A$6");
chart_add_series(chart, NULL, "Sheet1!$A$1:$A$6", 0);
/* Create some fonts to use in the chart. */
lxw_chart_font font1 = {.name = "Calibri", .color = LXW_COLOR_BLUE};

105
examples/chart_gauge.c Normal file
View file

@ -0,0 +1,105 @@
/*
* An example of creating a Gauge Chart in Excel with libxlsxwriter.
*
* A Gauge Chart isn't a native chart type in Excel. It is constructed by
* combining a doughnut chart and a pie chart and by using some non-filled
* elements. This example follows the following online example of how to create
* a Gauge Chart in Excel: https://www.excel-easy.com/examples/gauge-chart.html
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("chart_gauge.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart_series *series;
/* Add some data for the Doughnut and Pie charts. This is set up so the
* gauge goes from 0-100. It is initially set at 75%. */
worksheet_write_string(worksheet, 1, 7, "Donut", NULL);
worksheet_write_number(worksheet, 2, 7, 25, NULL);
worksheet_write_number(worksheet, 3, 7, 50, NULL);
worksheet_write_number(worksheet, 4, 7, 25, NULL);
worksheet_write_number(worksheet, 5, 7, 100, NULL);
worksheet_write_string(worksheet, 1, 8, "Pie", NULL);
worksheet_write_number(worksheet, 2, 8, 75, NULL);
worksheet_write_number(worksheet, 3, 8, 1, NULL);
worksheet_write_formula(worksheet, 4, 8, "=200-I4-I3", NULL);
/* Create a doughnut chart for the background of the gauge. */
lxw_chart *chart_doughnut = workbook_add_chart(workbook, LXW_CHART_DOUGHNUT);
/* Add the doughnut chart series with colors for the segments. */
series = chart_add_series(chart_doughnut,
NULL, "=Sheet1!$H$3:$H$6", 0);
chart_series_set_name(series, "=Sheet1!$H$2");
/* Create fills for the doughnut segments: green, yellow, red, and no fill. */
lxw_chart_fill green_fill = {.color = 0x00B050};
lxw_chart_fill yellow_fill = {.color = 0xFFFF00};
lxw_chart_fill red_fill = {.color = 0xFF0000};
lxw_chart_fill no_fill = {.none = LXW_TRUE};
lxw_chart_point green_point = {.fill = &green_fill};
lxw_chart_point yellow_point = {.fill = &yellow_fill};
lxw_chart_point red_point = {.fill = &red_fill};
lxw_chart_point empty_point = {.fill = &no_fill};
lxw_chart_point *doughnut_points[] = {&green_point,
&yellow_point,
&red_point,
&empty_point,
NULL};
chart_series_set_points(series, doughnut_points);
/* Rotate the doughnut chart so the gauge segments are above the horizontal. */
chart_set_rotation(chart_doughnut, 270);
/* Turn off the chart legend. */
chart_legend_set_position(chart_doughnut, LXW_CHART_LEGEND_NONE);
/* Turn off the chart fill and border. */
lxw_chart_fill chart_no_fill = {.none = LXW_TRUE};
lxw_chart_line chart_no_line = {.none = LXW_TRUE};
chart_chartarea_set_fill(chart_doughnut, &chart_no_fill);
chart_chartarea_set_line(chart_doughnut, &chart_no_line);
/* Create a pie chart for the needle of the gauge. */
lxw_chart *chart_pie = workbook_add_chart(workbook, LXW_CHART_PIE);
/* Add the pie chart series for the needle. */
series = chart_add_series(chart_pie,
NULL, "=Sheet1!$I$3:$I$6", 0);
chart_series_set_name(series, "=Sheet1!$I$2");
/* Create fills for the pie segments: no fill, black (needle), no fill. */
lxw_chart_fill black_fill = {.color = 0x000000};
lxw_chart_point pie_empty1 = {.fill = &no_fill};
lxw_chart_point pie_needle = {.fill = &black_fill};
lxw_chart_point pie_empty2 = {.fill = &no_fill};
lxw_chart_point *pie_points[] = {&pie_empty1,
&pie_needle,
&pie_empty2,
NULL};
chart_series_set_points(series, pie_points);
/* Rotate the pie chart to align the needle with the doughnut gauge. */
chart_set_rotation(chart_pie, 270);
/* Combine the doughnut and pie charts. */
chart_combine(chart_doughnut, chart_pie);
/* Insert the combined chart into the worksheet. */
worksheet_insert_chart(worksheet, CELL("A1"), chart_doughnut);
return workbook_close(workbook);
}

97
examples/chart_gradient.c Normal file
View file

@ -0,0 +1,97 @@
/*
* An example of creating an Excel column chart with gradient fills using
* libxlsxwriter.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("chart_gradient.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart_series *series;
/* Add a bold format for the headers. */
lxw_format *bold = workbook_add_format(workbook);
format_set_bold(bold);
/* Write the worksheet data that the chart will refer to. */
worksheet_write_string(worksheet, 0, 0, "Number", bold);
worksheet_write_string(worksheet, 0, 1, "Batch 1", bold);
worksheet_write_string(worksheet, 0, 2, "Batch 2", bold);
worksheet_write_number(worksheet, 1, 0, 2, NULL);
worksheet_write_number(worksheet, 2, 0, 3, NULL);
worksheet_write_number(worksheet, 3, 0, 4, NULL);
worksheet_write_number(worksheet, 4, 0, 5, NULL);
worksheet_write_number(worksheet, 5, 0, 6, NULL);
worksheet_write_number(worksheet, 6, 0, 7, NULL);
worksheet_write_number(worksheet, 1, 1, 10, NULL);
worksheet_write_number(worksheet, 2, 1, 40, NULL);
worksheet_write_number(worksheet, 3, 1, 50, NULL);
worksheet_write_number(worksheet, 4, 1, 20, NULL);
worksheet_write_number(worksheet, 5, 1, 10, NULL);
worksheet_write_number(worksheet, 6, 1, 50, NULL);
worksheet_write_number(worksheet, 1, 2, 30, NULL);
worksheet_write_number(worksheet, 2, 2, 60, NULL);
worksheet_write_number(worksheet, 3, 2, 70, NULL);
worksheet_write_number(worksheet, 4, 2, 50, NULL);
worksheet_write_number(worksheet, 5, 2, 40, NULL);
worksheet_write_number(worksheet, 6, 2, 30, NULL);
/* Create a column chart. */
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* Configure the first series with a gradient fill. */
series = chart_add_series(chart,
"=Sheet1!$A$2:$A$7",
"=Sheet1!$B$2:$B$7", 0);
chart_series_set_name(series, "=Sheet1!$B$1");
/* Set gradient: dark red to light red. */
lxw_chart_gradient_fill gradient1 = {
.type = LXW_CHART_GRADIENT_FILL_LINEAR,
.colors = {0x963735, 0xF1DCDB},
.num_stops = 2,
};
chart_series_set_gradient(series, &gradient1);
/* Configure the second series with a gradient fill. */
series = chart_add_series(chart,
"=Sheet1!$A$2:$A$7",
"=Sheet1!$C$2:$C$7", 0);
chart_series_set_name(series, "=Sheet1!$C$1");
/* Set gradient: dark orange to light orange. */
lxw_chart_gradient_fill gradient2 = {
.type = LXW_CHART_GRADIENT_FILL_LINEAR,
.colors = {0xE36C0A, 0xFCEADA},
.num_stops = 2,
};
chart_series_set_gradient(series, &gradient2);
/* Set a gradient fill for the plotarea. */
lxw_chart_gradient_fill plotarea_gradient = {
.type = LXW_CHART_GRADIENT_FILL_LINEAR,
.colors = {0xFFEFD1, 0xF0EBD5, 0xB69F66},
.num_stops = 3,
};
chart_plotarea_set_gradient(chart, &plotarea_gradient);
/* Add axis labels. */
chart_axis_set_name(chart->x_axis, "Test number");
chart_axis_set_name(chart->y_axis, "Sample length (mm)");
/* Turn off the chart legend. */
chart_legend_set_position(chart, LXW_CHART_LEGEND_NONE);
/* Insert the chart into the worksheet. */
worksheet_insert_chart(worksheet, CELL("E2"), chart);
return workbook_close(workbook);
}

View file

@ -55,14 +55,14 @@ int main() {
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add a second series but leave the categories and values undefined. They
* can be defined later using the alternative syntax shown below. */
series = chart_add_series(chart, NULL, NULL);
series = chart_add_series(chart, NULL, NULL, 0);
/* Configure the series using a syntax that is easier to define programmatically. */
chart_series_set_categories(series, "Sheet1", 1, 0, 6, 0); /* "=Sheet1!$A$2:$A$7" */
@ -87,13 +87,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_LINE_STACKED);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");
@ -116,13 +116,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_LINE_STACKED_PERCENT);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");

102
examples/chart_pareto.c Normal file
View file

@ -0,0 +1,102 @@
/*
* An example of creating a Pareto chart with libxlsxwriter.
*
* A Pareto chart is a type of chart that combines a column chart with a line
* chart. The columns represent individual values in descending order, and the
* line represents the cumulative percentage.
*
* This example uses combined charts with a secondary Y axis.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("chart_pareto.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
/* Add formats for the worksheet data. */
lxw_format *bold = workbook_add_format(workbook);
format_set_bold(bold);
lxw_format *percent_format = workbook_add_format(workbook);
format_set_num_format(percent_format, "0.0%");
/* Widen the columns for visibility. */
worksheet_set_column(worksheet, COLS("A:A"), 15, NULL);
worksheet_set_column(worksheet, COLS("B:C"), 10, NULL);
/* Add the worksheet data that the charts will refer to. */
worksheet_write_string(worksheet, 0, 0, "Reason", bold);
worksheet_write_string(worksheet, 0, 1, "Number", bold);
worksheet_write_string(worksheet, 0, 2, "Percentage", bold);
/* Reasons for lateness data. */
worksheet_write_string(worksheet, 1, 0, "Traffic", NULL);
worksheet_write_string(worksheet, 2, 0, "Child care", NULL);
worksheet_write_string(worksheet, 3, 0, "Public Transport", NULL);
worksheet_write_string(worksheet, 4, 0, "Weather", NULL);
worksheet_write_string(worksheet, 5, 0, "Overslept", NULL);
worksheet_write_string(worksheet, 6, 0, "Emergency", NULL);
/* Number of occurrences. */
worksheet_write_number(worksheet, 1, 1, 60, NULL);
worksheet_write_number(worksheet, 2, 1, 40, NULL);
worksheet_write_number(worksheet, 3, 1, 20, NULL);
worksheet_write_number(worksheet, 4, 1, 15, NULL);
worksheet_write_number(worksheet, 5, 1, 10, NULL);
worksheet_write_number(worksheet, 6, 1, 5, NULL);
/* Cumulative percentages. */
worksheet_write_number(worksheet, 1, 2, 0.400, percent_format);
worksheet_write_number(worksheet, 2, 2, 0.667, percent_format);
worksheet_write_number(worksheet, 3, 2, 0.800, percent_format);
worksheet_write_number(worksheet, 4, 2, 0.900, percent_format);
worksheet_write_number(worksheet, 5, 2, 0.967, percent_format);
worksheet_write_number(worksheet, 6, 2, 1.000, percent_format);
/* Create a new column chart. This will be the primary chart. */
lxw_chart *column_chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* Add a series for the column chart. */
chart_add_series(column_chart,
"=Sheet1!$A$2:$A$7",
"=Sheet1!$B$2:$B$7", 0);
/* Add a chart title. */
chart_title_set_name(column_chart, "Reasons for lateness");
/* Turn off the chart legend. */
chart_legend_set_position(column_chart, LXW_CHART_LEGEND_NONE);
/* Set the title and scale of the Y axes. */
chart_axis_set_name(column_chart->y_axis, "Respondents (number)");
chart_axis_set_min(column_chart->y_axis, 0);
chart_axis_set_max(column_chart->y_axis, 120);
chart_axis_set_max(column_chart->y2_axis, 1);
/* Create a new line chart. This will be the secondary chart. */
lxw_chart *line_chart = workbook_add_chart(workbook, LXW_CHART_LINE);
/* Mark the line chart as secondary so it uses the secondary axes. */
line_chart->is_secondary = LXW_TRUE;
/* Add a series for the line chart, on the secondary axis. */
lxw_chart_series *series = chart_add_series(line_chart,
"=Sheet1!$A$2:$A$7",
"=Sheet1!$C$2:$C$7", 1);
/* Add markers to the line series. */
chart_series_set_marker_type(series, LXW_CHART_MARKER_AUTOMATIC);
/* Combine the column and line charts. */
chart_combine(column_chart, line_chart);
/* Insert the chart into the worksheet. */
worksheet_insert_chart(worksheet, CELL("F2"), column_chart);
return workbook_close(workbook);
}

View file

@ -37,8 +37,8 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* Configure the chart. */
lxw_chart_series *series1 = chart_add_series(chart, NULL, "Sheet1!$A$2:$A$5");
lxw_chart_series *series2 = chart_add_series(chart, NULL, "Sheet1!$B$2:$B$5");
lxw_chart_series *series1 = chart_add_series(chart, NULL, "Sheet1!$A$2:$A$5", 0);
lxw_chart_series *series2 = chart_add_series(chart, NULL, "Sheet1!$B$2:$B$5", 0);
chart_series_set_name(series1, "=Sheet1!$A$1");
chart_series_set_name(series2, "=Sheet1!$B$1");

View file

@ -54,7 +54,7 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_PIE);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "Pie sales data");
@ -75,7 +75,7 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_PIE);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "Pie sales data");
@ -112,7 +112,7 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_PIE);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$4", "=Sheet1!$B$2:$B$4", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "Pie sales data");

View file

@ -35,7 +35,7 @@ int main() {
/* Add the data series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$1:$A$2",
"=Sheet1!$B$1:$B$2");
"=Sheet1!$B$1:$B$2", 0);
/* Create some fills for the chart points/segments. */
lxw_chart_fill red_fill = {.color = LXW_COLOR_RED };

View file

@ -55,14 +55,14 @@ int main() {
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_RADAR);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add a second series but leave the categories and values undefined. They
* can be defined later using the alternative syntax shown below. */
series = chart_add_series(chart, NULL, NULL);
series = chart_add_series(chart, NULL, NULL, 0);
/* Configure the series using a syntax that is easier to define programmatically. */
chart_series_set_categories(series, "Sheet1", 1, 0, 6, 0); /* "=Sheet1!$A$2:$A$7" */
@ -86,13 +86,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_RADAR_WITH_MARKERS);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");
@ -113,13 +113,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_RADAR_FILLED);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");

View file

@ -55,14 +55,14 @@ int main() {
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_SCATTER);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add a second series but leave the categories and values undefined. They
* can be defined later using the alternative syntax shown below. */
series = chart_add_series(chart, NULL, NULL);
series = chart_add_series(chart, NULL, NULL, 0);
/* Configure the series using a syntax that is easier to define programmatically. */
chart_series_set_categories(series, "Sheet1", 1, 0, 6, 0); /* "=Sheet1!$A$2:$A$7" */
@ -88,13 +88,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_SCATTER_STRAIGHT_WITH_MARKERS);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");
@ -118,13 +118,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_SCATTER_STRAIGHT);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");
@ -148,13 +148,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_SCATTER_SMOOTH_WITH_MARKERS);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");
@ -178,13 +178,13 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_SCATTER_SMOOTH);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add the second series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7", 0);
/* Set the name for the series instead of the default "Series 2". */
chart_series_set_name(series, "=Sheet1!$C$1");

View file

@ -0,0 +1,84 @@
/*
* An example of creating an Excel line chart with a secondary axis using
* the libxlsxwriter library.
*
* A secondary Y axis allows you to display two series with different scales
* on the same chart. The secondary Y axis appears on the right side of the
* chart.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("chart_secondary_axis.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart_series *series;
/* Add a bold format for the headings. */
lxw_format *bold = workbook_add_format(workbook);
format_set_bold(bold);
/* Write the worksheet data that the chart will refer to. */
worksheet_write_string(worksheet, CELL("A1"), "Aliens", bold);
worksheet_write_string(worksheet, CELL("B1"), "Humans", bold);
/* Data for the "Aliens" series - small scale (2-7). */
worksheet_write_number(worksheet, 1, 0, 2, NULL);
worksheet_write_number(worksheet, 2, 0, 3, NULL);
worksheet_write_number(worksheet, 3, 0, 4, NULL);
worksheet_write_number(worksheet, 4, 0, 5, NULL);
worksheet_write_number(worksheet, 5, 0, 6, NULL);
worksheet_write_number(worksheet, 6, 0, 7, NULL);
/* Data for the "Humans" series - larger scale (10-50). */
worksheet_write_number(worksheet, 1, 1, 10, NULL);
worksheet_write_number(worksheet, 2, 1, 40, NULL);
worksheet_write_number(worksheet, 3, 1, 50, NULL);
worksheet_write_number(worksheet, 4, 1, 20, NULL);
worksheet_write_number(worksheet, 5, 1, 10, NULL);
worksheet_write_number(worksheet, 6, 1, 50, NULL);
/* Create a line chart. */
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE);
/* Add the first series to the SECONDARY Y axis (y2_axis = 1).
*
* When y2_axis is set to 1 (LXW_TRUE), the series will be plotted
* against the secondary Y axis on the right side of the chart.
* This is useful when you have data with different scales.
*/
series = chart_add_series(chart,
NULL,
"=Sheet1!$A$2:$A$7",
1); /* y2_axis = 1: Use secondary Y axis */
chart_series_set_name(series, "=Sheet1!$A$1");
/* Add the second series to the PRIMARY Y axis (y2_axis = 0). */
series = chart_add_series(chart,
NULL,
"=Sheet1!$B$2:$B$7",
0); /* y2_axis = 0: Use primary Y axis */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Configure the chart legend position. */
chart_legend_set_position(chart, LXW_CHART_LEGEND_RIGHT);
/* Add a chart title and axis labels. */
chart_title_set_name(chart, "Survey results");
chart_axis_set_name(chart->x_axis, "Days");
chart_axis_set_name(chart->y_axis, "Population");
chart_axis_set_name(chart->y2_axis, "Laser wounds");
/* Hide the major gridlines on the primary Y axis for clarity. */
chart_axis_major_gridlines_set_visible(chart->y_axis, LXW_FALSE);
/* Insert the chart into the worksheet. */
worksheet_insert_chart(worksheet, CELL("D2"), chart);
return workbook_close(workbook);
}

66
examples/chart_stock.c Normal file
View file

@ -0,0 +1,66 @@
/*
* An example of creating Excel Stock charts with libxlsxwriter.
*
* Stock charts display High-Low-Close data and are suitable for financial
* and other data that shows variation over time.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("chart_stock.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_STOCK);
/* Add a bold format for the headers. */
lxw_format *bold = workbook_add_format(workbook);
format_set_bold(bold);
/* Add a date format for the date column. */
lxw_format *date_format = workbook_add_format(workbook);
format_set_num_format(date_format, "dd/mm/yyyy");
/* Add the worksheet data that the chart will refer to. */
/* Dates as Excel serial numbers (Jan 1-5, 2007). */
double dates[5] = {39083, 39084, 39085, 39086, 39087};
double high[5] = {27.2, 25.03, 19.05, 20.34, 18.5};
double low[5] = {23.49, 19.55, 15.12, 17.84, 16.34};
double close[5] = {25.45, 23.05, 17.32, 20.45, 17.34};
/* Write the column headers. */
worksheet_write_string(worksheet, 0, 0, "Date", bold);
worksheet_write_string(worksheet, 0, 1, "High", bold);
worksheet_write_string(worksheet, 0, 2, "Low", bold);
worksheet_write_string(worksheet, 0, 3, "Close", bold);
/* Write the data columns. */
for (int row = 0; row < 5; row++) {
worksheet_write_number(worksheet, row + 1, 0, dates[row], date_format);
worksheet_write_number(worksheet, row + 1, 1, high[row], NULL);
worksheet_write_number(worksheet, row + 1, 2, low[row], NULL);
worksheet_write_number(worksheet, row + 1, 3, close[row], NULL);
}
/* Widen the columns for visibility. */
worksheet_set_column(worksheet, COLS("A:D"), 11, NULL);
/* Add a series for each of the High-Low-Close columns. Stock charts
* should have 3 series (High-Low-Close) or 4 series (Open-High-Low-Close). */
chart_add_series(chart, "=Sheet1!$A$2:$A$6", "=Sheet1!$B$2:$B$6", 0);
chart_add_series(chart, "=Sheet1!$A$2:$A$6", "=Sheet1!$C$2:$C$6", 0);
chart_add_series(chart, "=Sheet1!$A$2:$A$6", "=Sheet1!$D$2:$D$6", 0);
/* Add a chart title and axis labels. */
chart_title_set_name(chart, "High-Low-Close");
chart_axis_set_name(chart->x_axis, "Date");
chart_axis_set_name(chart->y_axis, "Share price");
/* Insert the chart into the worksheet. */
worksheet_insert_chart(worksheet, CELL("E9"), chart);
return workbook_close(workbook);
}

View file

@ -38,7 +38,7 @@ int main() {
chart = workbook_add_chart(workbook, chart_types[chart_num]);
snprintf(chart_title, 32, "Style %d", style_num);
chart_add_series(chart, NULL, "=Data!$A$1:$A$6");
chart_add_series(chart, NULL, "=Data!$A$1:$A$6", 0);
chart_title_set_name(chart, chart_title);
chart_set_style(chart, style_num);

View file

@ -29,7 +29,7 @@ int main() {
chart = workbook_add_chart(workbook, LXW_CHART_LINE);
/* Configure the chart. */
series = chart_add_series(chart, NULL, "Sheet1!$A$1:$A$6");
series = chart_add_series(chart, NULL, "Sheet1!$A$1:$A$6", 0);
(void)series; /* Do something with series in the real examples. */

View file

@ -53,14 +53,14 @@ int main() {
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_BAR);
/* Add the first series to the chart. */
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7");
series = chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$B$2:$B$7", 0);
/* Set the name for the series instead of the default "Series 1". */
chart_series_set_name(series, "=Sheet1!$B$1");
/* Add a second series but leave the categories and values undefined. They
* can be defined later using the alternative syntax shown below. */
series = chart_add_series(chart, NULL, NULL);
series = chart_add_series(chart, NULL, NULL, 0);
/* Configure the series using a syntax that is easier to define programmatically. */
chart_series_set_categories(series, "Sheet1", 1, 0, 6, 0); /* "=Sheet1!$A$2:$A$7" */

32
examples/checkbox.c Normal file
View file

@ -0,0 +1,32 @@
/*
* An example of inserting checkboxes into a worksheet using the
* libxlsxwriter library.
*
* Checkboxes in Excel are a feature of Excel 365 versions from 2024 onwards.
* They are boolean values displayed as checkboxes in the cells.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("checkbox.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
/* Widen the column for clarity. */
worksheet_set_column(worksheet, 0, 0, 15, NULL);
/* Write some checkboxes with different states. */
worksheet_insert_checkbox(worksheet, 0, 0, LXW_FALSE);
worksheet_insert_checkbox(worksheet, 1, 0, LXW_TRUE);
worksheet_insert_checkbox(worksheet, 2, 0, LXW_FALSE);
worksheet_insert_checkbox(worksheet, 3, 0, LXW_TRUE);
worksheet_insert_checkbox(worksheet, 4, 0, LXW_TRUE);
workbook_close(workbook);
return 0;
}

77
examples/colors.c Normal file
View file

@ -0,0 +1,77 @@
/*
* A demonstration of the available colors and how to use them in
* libxlsxwriter.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("colors.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
/* Set the column width for clarity. */
worksheet_set_column(worksheet, 0, 0, 16, NULL);
worksheet_set_column(worksheet, 1, 1, 10, NULL);
/* Define some named colors with their RGB values. */
struct {
const char *name;
lxw_color_t color;
} named_colors[] = {
{"Black", LXW_COLOR_BLACK},
{"Blue", LXW_COLOR_BLUE},
{"Brown", LXW_COLOR_BROWN},
{"Cyan", LXW_COLOR_CYAN},
{"Gray", LXW_COLOR_GRAY},
{"Green", LXW_COLOR_GREEN},
{"Lime", LXW_COLOR_LIME},
{"Magenta", LXW_COLOR_MAGENTA},
{"Navy", LXW_COLOR_NAVY},
{"Orange", LXW_COLOR_ORANGE},
{"Pink", LXW_COLOR_PINK},
{"Purple", LXW_COLOR_PURPLE},
{"Red", LXW_COLOR_RED},
{"Silver", LXW_COLOR_SILVER},
{"White", LXW_COLOR_WHITE},
{"Yellow", LXW_COLOR_YELLOW},
};
int num_colors = sizeof(named_colors) / sizeof(named_colors[0]);
/* Write the named colors. */
for (int row = 0; row < num_colors; row++) {
lxw_format *color_format = workbook_add_format(workbook);
format_set_bg_color(color_format, named_colors[row].color);
worksheet_write_string(worksheet, row, 0, named_colors[row].name, NULL);
worksheet_write_blank(worksheet, row, 1, color_format);
}
/* Write some user-defined RGB colors. */
struct {
const char *name;
lxw_color_t color;
} user_colors[] = {
{"#FF7F50", 0xFF7F50}, /* Coral */
{"#DCDCDC", 0xDCDCDC}, /* Gainsboro */
{"#6495ED", 0x6495ED}, /* CornflowerBlue */
{"#DAA520", 0xDAA520}, /* GoldenRod */
};
int num_user_colors = sizeof(user_colors) / sizeof(user_colors[0]);
for (int i = 0; i < num_user_colors; i++) {
int row = num_colors + i;
lxw_format *color_format = workbook_add_format(workbook);
format_set_bg_color(color_format, user_colors[i].color);
worksheet_write_string(worksheet, row, 0, user_colors[i].name, NULL);
worksheet_write_blank(worksheet, row, 1, color_format);
}
return workbook_close(workbook);
}

57
examples/right_to_left.c Normal file
View file

@ -0,0 +1,57 @@
/*
* Example of how to use libxlsxwriter to change the default worksheet and
* cell text direction from left-to-right to right-to-left as required by
* some middle eastern versions of Excel.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("right_to_left.xlsx");
lxw_worksheet *worksheet1 = workbook_add_worksheet(workbook, NULL);
lxw_worksheet *worksheet2 = workbook_add_worksheet(workbook, NULL);
/* Create formats for text direction. */
lxw_format *format_left_to_right = workbook_add_format(workbook);
lxw_format *format_right_to_left = workbook_add_format(workbook);
/* Set reading order: 1 = left-to-right, 2 = right-to-left. */
format_set_reading_order(format_left_to_right, 1);
format_set_reading_order(format_right_to_left, 2);
/* Make the columns wider for clarity. */
worksheet_set_column(worksheet1, 0, 0, 25, NULL);
worksheet_set_column(worksheet2, 0, 0, 25, NULL);
/* Change the direction for worksheet2. */
worksheet_right_to_left(worksheet2);
/* Write some data to show the difference. */
/* Standard direction: | A1 | B1 | C1 | ... */
worksheet_write_string(worksheet1, 0, 0,
"\xd9\x86\xd8\xb5 \xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a / English text",
NULL); /* Arabic text / English text - Default direction */
worksheet_write_string(worksheet1, 1, 0,
"\xd9\x86\xd8\xb5 \xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a / English text",
format_left_to_right);
worksheet_write_string(worksheet1, 2, 0,
"\xd9\x86\xd8\xb5 \xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a / English text",
format_right_to_left);
/* Right to left direction: ... | C1 | B1 | A1 | */
worksheet_write_string(worksheet2, 0, 0,
"\xd9\x86\xd8\xb5 \xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a / English text",
NULL); /* Default direction */
worksheet_write_string(worksheet2, 1, 0,
"\xd9\x86\xd8\xb5 \xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a / English text",
format_left_to_right);
worksheet_write_string(worksheet2, 2, 0,
"\xd9\x86\xd8\xb5 \xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a / English text",
format_right_to_left);
return workbook_close(workbook);
}

86
examples/sparklines.c Normal file
View file

@ -0,0 +1,86 @@
/*
* An example of creating Excel sparklines with libxlsxwriter.
*
* Sparklines are small charts that fit in a single cell and allow you to
* show trends in data at a glance.
*
* SPDX-License-Identifier: BSD-2-Clause
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org.
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("sparklines.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
/* Set the column widths for the data and sparklines. */
worksheet_set_column(worksheet, COLS("A:F"), 12, NULL);
worksheet_set_column(worksheet, COLS("G:G"), 16, NULL);
/* Add some row headers. */
worksheet_write_string(worksheet, 0, 0, "Line", NULL);
worksheet_write_string(worksheet, 1, 0, "Column", NULL);
worksheet_write_string(worksheet, 2, 0, "Win/Loss", NULL);
worksheet_write_string(worksheet, 3, 0, "With options", NULL);
worksheet_write_string(worksheet, 4, 0, "Custom colors", NULL);
/* Write the sample data. */
int data[][5] = {
{-2, 2, 3, -1, 0},
{30, 20, 33, 20, 15},
{1, -1, -1, 1, -1},
{5, 10, 3, 8, 6},
{2, 4, 6, 8, 10},
};
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 5; col++) {
worksheet_write_number(worksheet, row, col + 1, data[row][col], NULL);
}
}
/* Add a simple line sparkline. */
lxw_sparkline_options line_options = {
.range = "Sheet1!B1:F1",
};
worksheet_add_sparkline(worksheet, 0, 6, &line_options);
/* Add a column sparkline. */
lxw_sparkline_options column_options = {
.range = "Sheet1!B2:F2",
.type = LXW_SPARKLINE_COLUMN,
};
worksheet_add_sparkline(worksheet, 1, 6, &column_options);
/* Add a win/loss sparkline. */
lxw_sparkline_options winloss_options = {
.range = "Sheet1!B3:F3",
.type = LXW_SPARKLINE_WIN_LOSS,
};
worksheet_add_sparkline(worksheet, 2, 6, &winloss_options);
/* Add a sparkline with options: markers, high/low points, line weight. */
lxw_sparkline_options markers_options = {
.range = "Sheet1!B4:F4",
.markers = LXW_TRUE,
.high_point = LXW_TRUE,
.low_point = LXW_TRUE,
.first_point = LXW_TRUE,
.last_point = LXW_TRUE,
.weight = 1.5,
};
worksheet_add_sparkline(worksheet, 3, 6, &markers_options);
/* Add a sparkline with custom colors. */
lxw_sparkline_options color_options = {
.range = "Sheet1!B5:F5",
.series_color = 0x0000FF, /* Blue line */
.markers = LXW_TRUE,
.markers_color = 0xFF0000, /* Red markers */
};
worksheet_add_sparkline(worksheet, 4, 6, &color_options);
return workbook_close(workbook);
}

32
examples/text_indent.c Normal file
View file

@ -0,0 +1,32 @@
/*
* A simple formatting example using libxlsxwriter.
*
* This program demonstrates the indentation cell format.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("text_indent.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
/* Create formats with different indentation levels. */
lxw_format *indent1 = workbook_add_format(workbook);
lxw_format *indent2 = workbook_add_format(workbook);
format_set_indent(indent1, 1);
format_set_indent(indent2, 2);
/* Make the column wider for clarity. */
worksheet_set_column(worksheet, 0, 0, 40, NULL);
/* Write some indented text. */
worksheet_write_string(worksheet, 0, 0, "This text is indented 1 level", indent1);
worksheet_write_string(worksheet, 1, 0, "This text is indented 2 levels", indent2);
return workbook_close(workbook);
}

30
examples/textbox.c Normal file
View file

@ -0,0 +1,30 @@
/*
* An example of inserting textboxes into a worksheet using the
* libxlsxwriter library.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("textbox.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
/* Insert a simple textbox with default size. */
worksheet_insert_textbox(worksheet, 1, 1, "This is a textbox");
/* Insert a textbox with custom size. */
lxw_textbox_options options = {.width = 256, .height = 100};
worksheet_insert_textbox_opt(worksheet, 8, 1, "A larger textbox", &options);
/* Insert a textbox with offset. */
lxw_textbox_options options2 = {.x_offset = 10, .y_offset = 10};
worksheet_insert_textbox_opt(worksheet, 15, 1, "Offset textbox", &options2);
workbook_close(workbook);
return 0;
}

View file

@ -19,8 +19,8 @@
#include "xlsxwriter/format.h"
#include "xlsxwriter/utility.h"
#define LXW_VERSION "1.2.3"
#define LXW_VERSION_ID 123
#define LXW_SOVERSION "10"
#define LXW_VERSION "1.2.4"
#define LXW_VERSION_ID 124
#define LXW_SOVERSION "11"
#endif /* __LXW_XLSXWRITER_H__ */

View file

@ -156,7 +156,10 @@ typedef enum lxw_chart_type {
LXW_CHART_RADAR_WITH_MARKERS,
/** Radar chart - filled. */
LXW_CHART_RADAR_FILLED
LXW_CHART_RADAR_FILLED,
/** Stock chart. */
LXW_CHART_STOCK
} lxw_chart_type;
/**
@ -266,7 +269,10 @@ typedef enum lxw_chart_marker_type {
LXW_CHART_MARKER_CIRCLE,
/** Plus (+) marker type. */
LXW_CHART_MARKER_PLUS
LXW_CHART_MARKER_PLUS,
/** Dot marker type. */
LXW_CHART_MARKER_DOT
} lxw_chart_marker_type;
/**
@ -485,7 +491,13 @@ typedef enum lxw_chart_axis_type {
LXW_CHART_AXIS_TYPE_X,
/** Chart Y axis. */
LXW_CHART_AXIS_TYPE_Y
LXW_CHART_AXIS_TYPE_Y,
/** Chart secondary X axis. */
LXW_CHART_AXIS_TYPE_X2,
/** Chart secondary Y axis. */
LXW_CHART_AXIS_TYPE_Y2
} lxw_chart_axis_type;
enum lxw_chart_subtype {
@ -607,6 +619,21 @@ typedef enum lxw_chart_axis_tick_mark {
LXW_CHART_AXIS_TICK_MARK_CROSSING
} lxw_chart_tick_mark;
/**
* @brief Time units for date axis major/minor units.
*/
typedef enum lxw_chart_axis_time_unit {
/** Time unit: Days. The default. */
LXW_CHART_AXIS_TIME_UNIT_DAYS,
/** Time unit: Months. */
LXW_CHART_AXIS_TIME_UNIT_MONTHS,
/** Time unit: Years. */
LXW_CHART_AXIS_TIME_UNIT_YEARS
} lxw_chart_axis_time_unit;
typedef struct lxw_series_range {
char *formula;
char *sheetname;
@ -692,6 +719,52 @@ typedef struct lxw_chart_pattern {
} lxw_chart_pattern;
/**
* @brief Gradient fill types for chart elements.
*/
typedef enum lxw_chart_gradient_fill_type {
/** Linear gradient fill. */
LXW_CHART_GRADIENT_FILL_LINEAR,
/** Radial gradient fill. */
LXW_CHART_GRADIENT_FILL_RADIAL,
/** Rectangular gradient fill. */
LXW_CHART_GRADIENT_FILL_RECTANGULAR,
/** Path gradient fill. */
LXW_CHART_GRADIENT_FILL_PATH
} lxw_chart_gradient_fill_type;
/** Maximum number of gradient stops. */
#define LXW_CHART_GRADIENT_MAX_STOPS 10
/**
* @brief Struct to represent a chart gradient fill.
*
* See @ref chart_gradient_fills.
*/
typedef struct lxw_chart_gradient_fill {
/** The gradient fill type. See #lxw_chart_gradient_fill_type. */
uint8_t type;
/** Array of gradient colors. See @ref working_with_colors. */
lxw_color_t colors[LXW_CHART_GRADIENT_MAX_STOPS];
/** Array of gradient stop positions (0-100). */
uint8_t positions[LXW_CHART_GRADIENT_MAX_STOPS];
/** Number of gradient stops (2-10). */
uint8_t num_stops;
/** Angle for linear gradients (0-359). Default is 90. */
uint16_t angle;
} lxw_chart_gradient_fill;
/**
* @brief Struct to represent a chart font.
*
@ -1040,6 +1113,7 @@ typedef struct lxw_chart_series {
lxw_chart_line *line;
lxw_chart_fill *fill;
lxw_chart_pattern *pattern;
lxw_chart_gradient_fill *gradient;
lxw_chart_marker *marker;
lxw_chart_point *points;
lxw_chart_custom_label *data_labels;
@ -1065,6 +1139,7 @@ typedef struct lxw_chart_series {
lxw_chart_line *label_line;
lxw_chart_fill *label_fill;
lxw_chart_pattern *label_pattern;
lxw_chart_gradient_fill *label_gradient;
lxw_series_error_bars *x_error_bars;
lxw_series_error_bars *y_error_bars;
@ -1083,6 +1158,10 @@ typedef struct lxw_chart_series {
lxw_chart_line *trendline_line;
double trendline_intercept;
/* Secondary axis flags. */
uint8_t x2_axis;
uint8_t y2_axis;
STAILQ_ENTRY (lxw_chart_series) list_pointers;
} lxw_chart_series;
@ -1140,6 +1219,8 @@ typedef struct lxw_chart_axis {
double major_unit;
uint8_t has_minor_unit;
double minor_unit;
uint8_t major_unit_type;
uint8_t minor_unit_type;
uint16_t interval_unit;
uint16_t interval_tick;
@ -1170,7 +1251,7 @@ typedef struct lxw_chart {
uint8_t subtype;
uint16_t series_index;
void (*write_chart_type)(struct lxw_chart *);
void (*write_chart_type)(struct lxw_chart *, uint8_t primary_axes);
void (*write_plot_area)(struct lxw_chart *);
/**
@ -1185,6 +1266,18 @@ typedef struct lxw_chart {
*/
lxw_chart_axis *y_axis;
/**
* A pointer to the chart secondary x2_axis object which can be used in
* functions that configure the secondary X axis.
*/
lxw_chart_axis *x2_axis;
/**
* A pointer to the chart secondary y2_axis object which can be used in
* functions that configure the secondary Y axis.
*/
lxw_chart_axis *y2_axis;
lxw_chart_title title;
uint32_t id;
@ -1193,6 +1286,10 @@ typedef struct lxw_chart {
uint32_t axis_id_3;
uint32_t axis_id_4;
uint8_t has_secondary_axis;
struct lxw_chart *combined;
uint8_t is_secondary;
uint8_t in_use;
uint8_t chart_group;
uint8_t cat_has_num_fmt;
@ -1200,6 +1297,7 @@ typedef struct lxw_chart {
uint8_t has_horiz_cat_axis;
uint8_t has_horiz_val_axis;
uint8_t date_category;
uint8_t style_id;
uint16_t rotation;
@ -1223,11 +1321,13 @@ typedef struct lxw_chart {
lxw_chart_line *chartarea_line;
lxw_chart_fill *chartarea_fill;
lxw_chart_pattern *chartarea_pattern;
lxw_chart_gradient_fill *chartarea_gradient;
lxw_chart_line *plotarea_line;
lxw_chart_fill *plotarea_fill;
lxw_chart_layout *plotarea_layout;
lxw_chart_pattern *plotarea_pattern;
lxw_chart_gradient_fill *plotarea_gradient;
uint8_t has_drop_lines;
lxw_chart_line *drop_lines_line;
@ -1278,6 +1378,8 @@ void lxw_chart_assemble_xml_file(lxw_chart *chart);
* @param chart Pointer to a lxw_chart instance to be configured.
* @param categories The range of categories in the data series.
* @param values The range of values in the data series.
* @param y2_axis (Optional) Set to 1 (LXW_TRUE) to plot the series on the
* secondary Y axis. Defaults to 0 (primary axis) if omitted.
*
* @return A lxw_chart_series object pointer.
*
@ -1289,7 +1391,11 @@ void lxw_chart_assemble_xml_file(lxw_chart *chart);
* used to set the categories and values of the series:
*
* @code
* // Add series on primary Y axis (y2_axis parameter is optional).
* chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$C$2:$C$7");
*
* // Add series on secondary Y axis (explicit y2_axis = 1).
* chart_add_series(chart, "=Sheet1!$A$2:$A$7", "=Sheet1!$D$2:$D$7", 1);
* @endcode
*
*
@ -1309,6 +1415,10 @@ void lxw_chart_assemble_xml_file(lxw_chart *chart);
* only mandatory option for every chart object. This parameter links the
* chart with the worksheet data that it displays.
*
* - `y2_axis`: (Optional) Set to 1 to plot the series on the secondary Y
* axis (Y2). Defaults to 0 (primary axis) if omitted. The secondary axis
* appears on the right side of the chart and can have a different scale.
*
* The `categories` and `values` should be a string formula like
* `"=Sheet1!$A$2:$A$7"` in the same way it is represented in Excel. This is
* convenient when recreating a chart from an example in Excel but it is
@ -1350,9 +1460,31 @@ void lxw_chart_assemble_xml_file(lxw_chart *chart);
* @endcode
*
*/
lxw_chart_series *chart_add_series(lxw_chart *chart,
const char *categories,
const char *values);
lxw_chart_series *chart_add_series_impl(lxw_chart *chart,
const char *categories,
const char *values,
uint8_t y2_axis);
/**
* @brief Macro wrapper for chart_add_series_impl with optional y2_axis parameter.
*
* This macro provides backward compatibility by defaulting y2_axis to 0
* (primary axis) when not specified.
*
* Usage:
* @code
* // 3 arguments (y2_axis defaults to 0):
* chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
*
* // 4 arguments (explicit y2_axis):
* chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 1);
* @endcode
*/
#define chart_add_series(...) \
_LXW_CHART_ADD_SERIES(__VA_ARGS__, 0)
#define _LXW_CHART_ADD_SERIES(chart, categories, values, y2_axis, ...) \
chart_add_series_impl((chart), (categories), (values), (y2_axis))
/**
* @brief Set a series "categories" range using row and column values.
@ -1555,6 +1687,30 @@ void chart_series_set_invert_if_negative(lxw_chart_series *series);
void chart_series_set_pattern(lxw_chart_series *series,
lxw_chart_pattern *pattern);
/**
* @brief Set the gradient fill properties for a chart series.
*
* @param series A series object created via `chart_add_series()`.
* @param gradient A #lxw_chart_gradient_fill struct.
*
* Set the gradient fill properties of a chart series:
*
* @code
* lxw_chart_gradient_fill gradient = {
* .type = LXW_CHART_GRADIENT_FILL_LINEAR,
* .colors = {0x963735, 0xF1DCDB},
* .num_stops = 2,
* .angle = 90
* };
*
* chart_series_set_gradient(series, &gradient);
* @endcode
*
* For more information see #lxw_chart_gradient_fill_type and @ref chart_gradient_fills.
*/
void chart_series_set_gradient(lxw_chart_series *series,
lxw_chart_gradient_fill *gradient);
/**
* @brief Set the data marker type for a series.
*
@ -2493,18 +2649,52 @@ void chart_series_set_error_bars_endcap(lxw_series_error_bars *error_bars,
void chart_series_set_error_bars_line(lxw_series_error_bars *error_bars,
lxw_chart_line *line);
/**
* @brief Combine two charts into a single combined chart.
*
* @param chart Pointer to the primary lxw_chart instance.
* @param combined_chart Pointer to the secondary lxw_chart to combine.
*
* The `%chart_combine()` function is used to combine two charts into a
* single chart, often with a shared category axis but different value axes.
* This is useful for showing two different data series with different
* scales on the same chart.
*
* @code
* // Create primary and secondary charts.
* lxw_chart *column_chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
* lxw_chart *line_chart = workbook_add_chart(workbook, LXW_CHART_LINE);
*
* // Add data series to each chart.
* chart_add_series(column_chart, NULL, "=Sheet1!$A$1:$A$5", 0);
* chart_add_series(line_chart, NULL, "=Sheet1!$B$1:$B$5", 1); // Secondary axis
*
* // Combine the charts.
* chart_combine(column_chart, line_chart);
*
* // Insert the combined chart into the worksheet.
* worksheet_insert_chart(worksheet, CELL("E2"), column_chart);
* @endcode
*
* Note: Only the primary chart should be inserted into the worksheet.
* The secondary chart is managed internally.
*/
void chart_combine(lxw_chart *chart, lxw_chart *combined_chart);
/**
* @brief Get an axis pointer from a chart.
*
* @param chart Pointer to a lxw_chart instance to be configured.
* @param axis_type The axis type (X or Y): #lxw_chart_axis_type.
* @param axis_type The axis type (X, Y, X2 or Y2): #lxw_chart_axis_type.
*
* The `%chart_axis_get()` function returns a pointer to a chart axis based
* on the #lxw_chart_axis_type:
*
* @code
* lxw_chart_axis *x_axis = chart_axis_get(chart, LXW_CHART_AXIS_TYPE_X);
* lxw_chart_axis *y_axis = chart_axis_get(chart, LXW_CHART_AXIS_TYPE_Y);
* lxw_chart_axis *x_axis = chart_axis_get(chart, LXW_CHART_AXIS_TYPE_X);
* lxw_chart_axis *y_axis = chart_axis_get(chart, LXW_CHART_AXIS_TYPE_Y);
* lxw_chart_axis *x2_axis = chart_axis_get(chart, LXW_CHART_AXIS_TYPE_X2);
* lxw_chart_axis *y2_axis = chart_axis_get(chart, LXW_CHART_AXIS_TYPE_Y2);
*
* // Use the axis pointer in other functions.
* chart_axis_major_gridlines_set_visible(x_axis, LXW_TRUE);
@ -3121,6 +3311,74 @@ void chart_axis_set_major_unit(lxw_chart_axis *axis, double unit);
*/
void chart_axis_set_minor_unit(lxw_chart_axis *axis, double unit);
/**
* @brief Set the category axis as a date axis.
*
* @param axis A pointer to a chart #lxw_chart_axis object.
*
* Set the category axis type as a date axis. A date axis is similar to a
* category axis but it displays dates in chronological order rather than
* just as equal categories.
*
* @code
* chart_axis_set_date_axis(chart->x_axis);
* @endcode
*
* This is often used with min/max, major/minor unit settings:
*
* @code
* chart_axis_set_date_axis(chart->x_axis);
* chart_axis_set_min(chart->x_axis, 41275); // Date serial number
* chart_axis_set_max(chart->x_axis, 41282); // Date serial number
* chart_axis_set_major_unit(chart->x_axis, 1);
* chart_axis_set_major_unit_type(chart->x_axis, LXW_CHART_AXIS_TIME_UNIT_DAYS);
* @endcode
*
* **Axis types**: This function is applicable to category axes only.
* See @ref ww_charts_axes.
*/
void chart_axis_set_date_axis(lxw_chart_axis *axis);
/**
* @brief Set the time unit type for the major unit of a date axis.
*
* @param axis A pointer to a chart #lxw_chart_axis object.
* @param type The time unit type: #lxw_chart_axis_time_unit.
*
* Set the time unit type for the major units of a date axis. The default is
* LXW_CHART_AXIS_TIME_UNIT_DAYS.
*
* @code
* chart_axis_set_date_axis(chart->x_axis);
* chart_axis_set_major_unit(chart->x_axis, 1);
* chart_axis_set_major_unit_type(chart->x_axis, LXW_CHART_AXIS_TIME_UNIT_MONTHS);
* @endcode
*
* **Axis types**: This function is applicable to date axes only.
* See @ref ww_charts_axes.
*/
void chart_axis_set_major_unit_type(lxw_chart_axis *axis, uint8_t type);
/**
* @brief Set the time unit type for the minor unit of a date axis.
*
* @param axis A pointer to a chart #lxw_chart_axis object.
* @param type The time unit type: #lxw_chart_axis_time_unit.
*
* Set the time unit type for the minor units of a date axis. The default is
* LXW_CHART_AXIS_TIME_UNIT_DAYS.
*
* @code
* chart_axis_set_date_axis(chart->x_axis);
* chart_axis_set_minor_unit(chart->x_axis, 1);
* chart_axis_set_minor_unit_type(chart->x_axis, LXW_CHART_AXIS_TIME_UNIT_DAYS);
* @endcode
*
* **Axis types**: This function is applicable to date axes only.
* See @ref ww_charts_axes.
*/
void chart_axis_set_minor_unit_type(lxw_chart_axis *axis, uint8_t type);
/**
* @brief Set the display units for a value axis.
*
@ -3545,6 +3803,19 @@ void chart_chartarea_set_fill(lxw_chart *chart, lxw_chart_fill *fill);
void chart_chartarea_set_pattern(lxw_chart *chart,
lxw_chart_pattern *pattern);
/**
* @brief Set the gradient fill properties for a chart area.
*
* @param chart Pointer to a lxw_chart instance to be configured.
* @param gradient A #lxw_chart_gradient_fill struct.
*
* Set the gradient fill properties of a chart area.
*
* For more information see #lxw_chart_gradient_fill_type.
*/
void chart_chartarea_set_gradient(lxw_chart *chart,
lxw_chart_gradient_fill *gradient);
/**
* @brief Set the line properties for a plotarea.
*
@ -3605,6 +3876,19 @@ void chart_plotarea_set_fill(lxw_chart *chart, lxw_chart_fill *fill);
*/
void chart_plotarea_set_pattern(lxw_chart *chart, lxw_chart_pattern *pattern);
/**
* @brief Set the gradient fill properties for a chart plotarea.
*
* @param chart Pointer to a lxw_chart instance to be configured.
* @param gradient A #lxw_chart_gradient_fill struct.
*
* Set the gradient fill properties of a chart plotarea.
*
* For more information see #lxw_chart_gradient_fill_type.
*/
void chart_plotarea_set_gradient(lxw_chart *chart,
lxw_chart_gradient_fill *gradient);
/**
* @brief Set the manual layout of the chart plotarea.
*
@ -3867,6 +4151,50 @@ void chart_set_series_overlap(lxw_chart *chart, int8_t overlap);
*/
void chart_set_series_gap(lxw_chart *chart, uint16_t gap);
/**
* @brief Set the overlap between series in a Bar/Column chart for the
* secondary axis.
*
* @param chart Pointer to a lxw_chart instance to be configured.
* @param overlap The overlap between the series. -100 to 100.
*
* The `%chart_set_series_overlap_y2()` function sets the overlap between
* series in Bar and Column charts for the secondary axis. It is identical
* to `chart_set_series_overlap()` but applies to the secondary Y axis.
*
* @code
* chart_set_series_overlap_y2(chart, -27);
* @endcode
*
* The overlap value must be in the range `-100 <= overlap <= 100`.
* The default value is 0.
*
* This option is only available for Bar/Column charts with a secondary axis.
*/
void chart_set_series_overlap_y2(lxw_chart *chart, int8_t overlap);
/**
* @brief Set the gap between series in a Bar/Column chart for the
* secondary axis.
*
* @param chart Pointer to a lxw_chart instance to be configured.
* @param gap The gap between the series. 0 to 500.
*
* The `%chart_set_series_gap_y2()` function sets the gap between series in
* Bar and Column charts for the secondary axis. It is identical to
* `chart_set_series_gap()` but applies to the secondary Y axis.
*
* @code
* chart_set_series_gap_y2(chart, 251);
* @endcode
*
* The gap value must be in the range `0 <= gap <= 500`. The default value
* is 150.
*
* This option is only available for Bar/Column charts with a secondary axis.
*/
void chart_set_series_gap_y2(lxw_chart *chart, uint16_t gap);
/**
* @brief Set the option for displaying blank data in a chart.
*

View file

@ -65,6 +65,7 @@ void lxw_ct_add_calc_chain(lxw_content_types *content_types);
void lxw_ct_add_custom_properties(lxw_content_types *content_types);
void lxw_ct_add_metadata(lxw_content_types *content_types);
void lxw_ct_add_rich_value(lxw_content_types *content_types);
void lxw_ct_add_feature_property_bag(lxw_content_types *content_types);
/* Declarations required for unit testing. */
#ifdef TESTING

View file

@ -32,6 +32,42 @@ enum image_types {
LXW_IMAGE_GIF
};
/**
* @brief Options for inserted textboxes.
*
*/
typedef struct lxw_textbox_options {
/** Width of the textbox in pixels. Default is 192. */
uint32_t width;
/** Height of the textbox in pixels. Default is 120. */
uint32_t height;
/** X offset from the cell in pixels. Default is 0. */
int32_t x_offset;
/** Y offset from the cell in pixels. Default is 0. */
int32_t y_offset;
/** X scale of the textbox. Default is 1.0. */
double x_scale;
/** Y scale of the textbox. Default is 1.0. */
double y_scale;
/** Object position/anchor: #lxw_object_position. Default is
LXW_OBJECT_MOVE_AND_SIZE. */
uint8_t object_position;
/** Alt text description of the textbox. */
const char *description;
/** Mark textbox as decorative. */
uint8_t decorative;
} lxw_textbox_options;
/* Coordinates used in a drawing object. */
typedef struct lxw_drawing_coords {
uint32_t col;
@ -56,6 +92,7 @@ typedef struct lxw_drawing_object {
char *description;
char *tip;
uint8_t decorative;
char *text;
STAILQ_ENTRY (lxw_drawing_object) list_pointers;

View file

@ -0,0 +1,56 @@
/*
* libxlsxwriter
*
* SPDX-License-Identifier: BSD-2-Clause
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org.
*
* feature_property_bag - A libxlsxwriter library for creating Excel XLSX
* featurePropertyBag files.
*
*/
#ifndef __LXW_FEATURE_PROPERTY_BAG_H__
#define __LXW_FEATURE_PROPERTY_BAG_H__
#include <stdint.h>
#include "common.h"
/* Feature property bag types. */
#define LXW_FEATURE_BAG_NONE 0
#define LXW_FEATURE_BAG_XF_COMPLEMENTS 1
#define LXW_FEATURE_BAG_DXF_COMPLEMENTS 2
/*
* Struct to represent a feature_property_bag.
*/
typedef struct lxw_feature_property_bag {
FILE *file;
uint8_t has_xf_complements;
uint8_t has_dxf_complements;
} lxw_feature_property_bag;
/* *INDENT-OFF* */
#ifdef __cplusplus
extern "C" {
#endif
/* *INDENT-ON* */
lxw_feature_property_bag *lxw_feature_property_bag_new(void);
void lxw_feature_property_bag_free(lxw_feature_property_bag *feature_property_bag);
void lxw_feature_property_bag_assemble_xml_file(lxw_feature_property_bag *self);
/* Declarations required for unit testing. */
#ifdef TESTING
#endif /* TESTING */
/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif
/* *INDENT-ON* */
#endif /* __LXW_FEATURE_PROPERTY_BAG_H__ */

View file

@ -438,6 +438,8 @@ typedef struct lxw_format {
uint8_t quote_prefix;
uint8_t checkbox;
STAILQ_ENTRY (lxw_format) list_pointers;
} lxw_format;
@ -1333,6 +1335,26 @@ void format_set_diag_color(lxw_format *format, lxw_color_t color);
*/
void format_set_quote_prefix(lxw_format *format);
/**
* @brief Set the checkbox property for a cell format.
*
* @param format Pointer to a Format instance.
*
* Set the checkbox property of a format. This allows a boolean value to be
* displayed as a checkbox in Excel. It is generally easier to create a
* checkbox using the worksheet_insert_checkbox() function which
* automatically sets this property.
*
* @code
* lxw_format *checkbox_format = workbook_add_format(workbook);
* format_set_checkbox(checkbox_format);
*
* worksheet_write_boolean(worksheet, 0, 0, 1, checkbox_format);
* @endcode
*
*/
void format_set_checkbox(lxw_format *format);
void format_set_font_outline(lxw_format *format);
void format_set_font_shadow(lxw_format *format);
void format_set_font_scheme(lxw_format *format, const char *font_scheme);

View file

@ -41,6 +41,7 @@
#include "rich_value_rel.h"
#include "rich_value_types.h"
#include "rich_value_structure.h"
#include "feature_property_bag.h"
#define LXW_ZIP_BUFFER_SIZE (16384)

View file

@ -62,6 +62,7 @@ void lxw_add_worksheet_relationship(lxw_relationships *self, const char *type,
const char *target,
const char *target_mode);
void lxw_add_rich_value_relationship(lxw_relationships *self);
void lxw_add_feature_property_bag_relationship(lxw_relationships *self);
/* Declarations required for unit testing. */
#ifdef TESTING

View file

@ -0,0 +1,205 @@
/*
* libxlsxwriter
*
* SPDX-License-Identifier: BSD-2-Clause
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org.
*/
/**
* @file sparkline.h
*
* @brief Sparkline options and types for libxlsxwriter.
*
* Sparklines are small charts that fit in a single cell and allow you to
* show trends in data at a glance.
*
*/
#ifndef __LXW_SPARKLINE_H__
#define __LXW_SPARKLINE_H__
#include "common.h"
/**
* @brief Sparkline types.
*/
typedef enum lxw_sparkline_type {
/** Line sparkline (default). */
LXW_SPARKLINE_LINE = 0,
/** Column sparkline. */
LXW_SPARKLINE_COLUMN,
/** Win/Loss sparkline. */
LXW_SPARKLINE_WIN_LOSS
} lxw_sparkline_type;
/**
* @brief Sparkline empty cell handling.
*/
typedef enum lxw_sparkline_empty_cells {
/** Show empty cells as gaps (default). */
LXW_SPARKLINE_EMPTY_CELLS_GAP = 0,
/** Show empty cells as zeros. */
LXW_SPARKLINE_EMPTY_CELLS_ZERO,
/** Connect points across empty cells. */
LXW_SPARKLINE_EMPTY_CELLS_SPAN
} lxw_sparkline_empty_cells;
/**
* @brief Sparkline axis type for min/max.
*/
typedef enum lxw_sparkline_axis_type {
/** Axis value is automatic (default). */
LXW_SPARKLINE_AXIS_AUTOMATIC = 0,
/** Axis value is same for all sparklines in group. */
LXW_SPARKLINE_AXIS_GROUP,
/** Axis value is set to a custom value. */
LXW_SPARKLINE_AXIS_CUSTOM
} lxw_sparkline_axis_type;
/**
* @brief Sparkline options.
*
* The members of this struct are used to define a sparkline that is inserted
* into a worksheet via worksheet_add_sparkline().
*/
typedef struct lxw_sparkline_options {
/** A string containing the range of data for the sparkline in a format
* like "Sheet1!$A$1:$E$1". Required. */
const char *range;
/** A string containing the cell location of the sparkline. If this is
* NULL, the location is specified by row/col parameters. */
const char *location;
/** The type of sparkline: line, column, or win/loss.
* See #lxw_sparkline_type. Default is LXW_SPARKLINE_LINE. */
uint8_t type;
/** The style index (0-35) from the predefined Excel sparkline styles.
* Default is 0. */
uint8_t style;
/** Show high point marker. */
uint8_t high_point;
/** Show low point marker. */
uint8_t low_point;
/** Show negative point markers. */
uint8_t negative_points;
/** Show first point marker. */
uint8_t first_point;
/** Show last point marker. */
uint8_t last_point;
/** Show markers on all points (line sparkline only). */
uint8_t markers;
/** Custom color for sparkline series (RGB). Use 0 for style default. */
lxw_color_t series_color;
/** Custom color for negative values (RGB). Use 0 for style default. */
lxw_color_t negative_color;
/** Custom color for markers (RGB). Use 0 for style default. */
lxw_color_t markers_color;
/** Custom color for first point (RGB). Use 0 for style default. */
lxw_color_t first_color;
/** Custom color for last point (RGB). Use 0 for style default. */
lxw_color_t last_color;
/** Custom color for high point (RGB). Use 0 for style default. */
lxw_color_t high_color;
/** Custom color for low point (RGB). Use 0 for style default. */
lxw_color_t low_color;
/** Minimum axis value for sparkline. */
double min;
/** Maximum axis value for sparkline. */
double max;
/** How min axis is calculated. See #lxw_sparkline_axis_type. */
uint8_t min_axis_type;
/** How max axis is calculated. See #lxw_sparkline_axis_type. */
uint8_t max_axis_type;
/** Show the horizontal axis. */
uint8_t show_axis;
/** Reverse the order of the data (right to left). */
uint8_t reverse;
/** Show data in hidden rows. */
uint8_t show_hidden;
/** How empty cells are handled. See #lxw_sparkline_empty_cells. */
uint8_t empty_cells;
/** Line weight in points (e.g., 0.75, 1.0, 2.25). Default is 0.75. */
double weight;
/** Range containing date axis data. */
const char *date_axis;
} lxw_sparkline_options;
/* Internal sparkline structure with resolved values */
typedef struct lxw_sparkline {
STAILQ_ENTRY(lxw_sparkline) list_pointers;
char *range;
lxw_row_t row;
lxw_col_t col;
char *location;
uint8_t type;
uint8_t style;
uint8_t high_point;
uint8_t low_point;
uint8_t negative_points;
uint8_t first_point;
uint8_t last_point;
uint8_t markers;
lxw_color_t series_color;
lxw_color_t negative_color;
lxw_color_t markers_color;
lxw_color_t first_color;
lxw_color_t last_color;
lxw_color_t high_color;
lxw_color_t low_color;
double min;
double max;
uint8_t min_axis_type;
uint8_t max_axis_type;
uint8_t show_axis;
uint8_t reverse;
uint8_t show_hidden;
uint8_t empty_cells;
double weight;
char *date_axis;
} lxw_sparkline;
STAILQ_HEAD(lxw_sparklines, lxw_sparkline);
#endif /* __LXW_SPARKLINE_H__ */

View file

@ -300,6 +300,47 @@ int lxw_sprintf_dbl(char *data, double number);
uint16_t lxw_hash_password(const char *password);
/**
* @brief Calculate the pixel width of a string based on character widths.
*
* @param string The string to calculate width for.
*
* @return The pixel width of the string using Calibri 11 font metrics.
* Returns 0 if the string is NULL or empty.
* Unhandled characters (including UTF-8) default to width 8.
*
* This function is useful for calculating column widths when implementing
* autofit-like functionality. Excel stores column widths, not autofit state,
* so applications must calculate widths themselves.
*
* Example:
* @code
* uint16_t pixels = lxw_pixel_width("Hello World");
* double width = lxw_autofit_width("Hello World");
* worksheet_set_column(worksheet, 0, 0, width, NULL);
* @endcode
*/
uint16_t lxw_pixel_width(const char *string);
/**
* @brief Calculate the column width required to autofit a string.
*
* @param string The string to calculate width for.
*
* @return The column width in Excel character units, suitable for use with
* worksheet_set_column(). Returns 0 if the string is NULL or empty.
*
* This function calculates the pixel width of the string, adds 7 pixels of
* padding (as Excel does), and converts to column units.
*
* Example:
* @code
* double width = lxw_autofit_width("Hello World");
* worksheet_set_column(worksheet, 0, 0, width, NULL);
* @endcode
*/
double lxw_autofit_width(const char *string);
/* *INDENT-OFF* */
#ifdef __cplusplus
}

View file

@ -343,6 +343,7 @@ typedef struct lxw_workbook {
uint8_t has_metadata;
uint8_t has_embedded_images;
uint8_t has_dynamic_functions;
uint8_t has_feature_property_bags;
uint8_t has_embedded_image_descriptions;
lxw_hash_table *used_xf_formats;
@ -356,6 +357,8 @@ typedef struct lxw_workbook {
lxw_format *default_url_format;
lxw_format *checkbox_format;
} lxw_workbook;

View file

@ -56,6 +56,7 @@
#include "styles.h"
#include "utility.h"
#include "relationships.h"
#include "sparkline.h"
#define LXW_ROW_MAX 1048576
#define LXW_COL_MAX 16384
@ -821,6 +822,7 @@ STAILQ_HEAD(lxw_cond_format_list, lxw_cond_format_obj);
STAILQ_HEAD(lxw_image_props, lxw_object_properties);
STAILQ_HEAD(lxw_embedded_image_props, lxw_object_properties);
STAILQ_HEAD(lxw_chart_props, lxw_object_properties);
STAILQ_HEAD(lxw_textbox_props, lxw_object_properties);
STAILQ_HEAD(lxw_comment_objs, lxw_vml_obj);
STAILQ_HEAD(lxw_table_objs, lxw_table_obj);
@ -1814,6 +1816,8 @@ typedef struct lxw_object_properties {
char *image_position;
uint8_t decorative;
lxw_format *format;
char *text;
uint8_t is_textbox;
STAILQ_ENTRY (lxw_object_properties) list_pointers;
} lxw_object_properties;
@ -2129,6 +2133,7 @@ typedef struct lxw_worksheet {
struct lxw_image_props *image_props;
struct lxw_image_props *embedded_image_props;
struct lxw_chart_props *chart_data;
struct lxw_textbox_props *textbox_data;
struct lxw_drawing_rel_ids *drawing_rel_ids;
struct lxw_vml_drawing_rel_ids *vml_drawing_rel_ids;
struct lxw_comment_objs *comment_objs;
@ -2136,6 +2141,8 @@ typedef struct lxw_worksheet {
struct lxw_comment_objs *button_objs;
struct lxw_table_objs *table_objs;
uint16_t table_count;
struct lxw_sparklines *sparklines;
uint8_t has_sparklines;
lxw_row_t dim_rowmin;
lxw_row_t dim_rowmax;
@ -2254,6 +2261,7 @@ typedef struct lxw_worksheet {
lxw_drawing *drawing;
lxw_format *default_url_format;
lxw_format *checkbox_format;
uint8_t has_vml;
uint8_t has_comments;
@ -2287,6 +2295,9 @@ typedef struct lxw_worksheet {
uint8_t use_1904_epoch;
uint8_t has_checkboxes;
uint8_t has_textboxes;
uint16_t excel_version;
lxw_object_properties **header_footer_objs[LXW_HEADER_FOOTER_OBJS_MAX];
@ -2319,6 +2330,7 @@ typedef struct lxw_worksheet_init_data {
const char *quoted_name;
const char *tmpdir;
lxw_format *default_url_format;
lxw_format *checkbox_format;
uint16_t max_url_length;
uint8_t use_1904_epoch;
@ -2943,6 +2955,37 @@ lxw_error worksheet_write_boolean(lxw_worksheet *worksheet,
lxw_row_t row, lxw_col_t col,
int value, lxw_format *format);
/**
* @brief Write a checkbox to a worksheet cell.
*
* @param worksheet Pointer to a lxw_worksheet instance to be updated.
* @param row The zero indexed row number.
* @param col The zero indexed column number.
* @param value The checkbox state: 0 = unchecked, non-zero = checked.
*
* @return A #lxw_error code.
*
* Insert a checkbox into a worksheet cell. The `value` parameter can be
* 0 for an unchecked checkbox or any non-zero value for a checked checkbox:
*
* @code
* // Insert an unchecked checkbox in cell A1.
* worksheet_insert_checkbox(worksheet, 0, 0, 0);
*
* // Insert a checked checkbox in cell A2.
* worksheet_insert_checkbox(worksheet, 1, 0, 1);
* @endcode
*
* @image html checkbox01.png
*
* Note: Checkbox is a feature introduced in Excel 365 and may not work
* in older versions of Excel.
*
*/
lxw_error worksheet_insert_checkbox(lxw_worksheet *worksheet,
lxw_row_t row, lxw_col_t col,
int value);
/**
* @brief Write a formatted blank worksheet cell.
*
@ -4058,6 +4101,62 @@ lxw_error worksheet_insert_chart_opt(lxw_worksheet *worksheet,
lxw_chart *chart,
lxw_chart_options *user_options);
/**
* @brief Insert a textbox object into a worksheet.
*
* @param worksheet Pointer to a lxw_worksheet instance to be updated.
* @param row The zero indexed row number.
* @param col The zero indexed column number.
* @param text The text string to display in the textbox.
*
* @return A #lxw_error code.
*
* This function can be used to insert a textbox into a worksheet:
*
* @code
* worksheet_insert_textbox(worksheet, 1, 1, "This is a textbox");
* @endcode
*
* The default textbox size is 192 x 120 pixels (approximately 3 columns x 6
* rows). The size and position of the textbox can be modified using the
* `worksheet_insert_textbox_opt()` function and the #lxw_textbox_options
* struct.
*
*/
lxw_error worksheet_insert_textbox(lxw_worksheet *worksheet,
lxw_row_t row, lxw_col_t col,
const char *text);
/**
* @brief Insert a textbox object into a worksheet, with options.
*
* @param worksheet Pointer to a lxw_worksheet instance to be updated.
* @param row The zero indexed row number.
* @param col The zero indexed column number.
* @param text The text string to display in the textbox.
* @param user_options Optional textbox parameters.
*
* @return A #lxw_error code.
*
* The `%worksheet_insert_textbox_opt()` function is like
* `worksheet_insert_textbox()` function except that it takes an optional
* #lxw_textbox_options struct to set the size and position of the textbox:
*
* @code
* lxw_textbox_options options = {.width = 256, .height = 100};
*
* worksheet_insert_textbox_opt(worksheet, 1, 1, "A textbox", &options);
*
* @endcode
*
* The #lxw_textbox_options struct is defined in drawing.h.
*
*/
lxw_error worksheet_insert_textbox_opt(lxw_worksheet *worksheet,
lxw_row_t row, lxw_col_t col,
const char *text,
lxw_textbox_options *user_options);
/**
* @brief Merge a range of cells.
*
@ -4517,6 +4616,44 @@ lxw_error worksheet_add_table(lxw_worksheet *worksheet, lxw_row_t first_row,
lxw_col_t first_col, lxw_row_t last_row,
lxw_col_t last_col, lxw_table_options *options);
/**
* @brief Add a sparkline to a worksheet cell.
*
* @param worksheet Pointer to a lxw_worksheet instance to be updated.
* @param row The zero indexed row number.
* @param col The zero indexed column number.
* @param options A #lxw_sparkline_options struct to define the sparkline.
*
* @return A #lxw_error code.
*
* The `%worksheet_add_sparkline()` function is used to add a sparkline to a
* worksheet cell. Sparklines are small charts that fit in a single cell and
* allow you to show trends in data at a glance.
*
* @code
* lxw_sparkline_options options = {
* .range = "Sheet1!A1:E1",
* .type = LXW_SPARKLINE_COLUMN,
* };
*
* worksheet_add_sparkline(worksheet, 0, 5, &options);
* @endcode
*
* The `range` parameter is required and specifies the data range that the
* sparkline will display. It should be a string like "Sheet1!A1:E1".
*
* The `type` parameter specifies the sparkline type: #LXW_SPARKLINE_LINE
* (default), #LXW_SPARKLINE_COLUMN, or #LXW_SPARKLINE_WIN_LOSS.
*
* The `style` parameter specifies one of 36 predefined styles (0-35).
*
* See @ref sparkline.h for all available options.
*
*/
lxw_error worksheet_add_sparkline(lxw_worksheet *worksheet,
lxw_row_t row, lxw_col_t col,
lxw_sparkline_options *options);
/**
* @brief Make a worksheet the active, i.e., visible worksheet.
*
@ -5926,6 +6063,10 @@ void lxw_worksheet_prepare_chart(lxw_worksheet *worksheet,
lxw_object_properties *object_props,
uint8_t is_chartsheet);
void lxw_worksheet_prepare_textbox(lxw_worksheet *worksheet,
uint32_t drawing_id,
lxw_object_properties *object_props);
uint32_t lxw_worksheet_prepare_vml_objects(lxw_worksheet *worksheet,
uint32_t vml_data_id,
uint32_t vml_shape_id,

File diff suppressed because it is too large Load diff

View file

@ -411,3 +411,14 @@ lxw_ct_add_rich_value(lxw_content_types *self)
LXW_APP_MSEXCEL "richvaluerel+xml");
}
/*
* Add the featurePropertyBag file to the ContentTypes overrides.
*/
void
lxw_ct_add_feature_property_bag(lxw_content_types *self)
{
lxw_ct_add_override(self,
"/xl/featurePropertyBag/featurePropertyBag.xml",
LXW_APP_MSEXCEL "featurepropertybag+xml");
}

View file

@ -57,6 +57,7 @@ lxw_free_drawing_object(lxw_drawing_object *drawing_object)
free(drawing_object->description);
free(drawing_object->tip);
free(drawing_object->text);
free(drawing_object);
}
@ -603,6 +604,324 @@ _drawing_write_pic(lxw_drawing *self, uint32_t index,
lxw_xml_end_tag(self->file, "xdr:pic");
}
/*
* Write the <xdr:cNvSpPr> element for textbox shapes.
*/
STATIC void
_drawing_write_c_nv_sp_pr_textbox(lxw_drawing *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_INT("txBox", 1);
lxw_xml_empty_tag(self->file, "xdr:cNvSpPr", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <xdr:nvSpPr> element for shapes.
*/
STATIC void
_drawing_write_nv_sp_pr(lxw_drawing *self, uint32_t index,
lxw_drawing_object *drawing_object)
{
lxw_xml_start_tag(self->file, "xdr:nvSpPr", NULL);
/* Write the xdr:cNvPr element. */
_drawing_write_c_nv_pr(self, "TextBox", index, drawing_object);
/* Write the xdr:cNvSpPr element. */
_drawing_write_c_nv_sp_pr_textbox(self);
lxw_xml_end_tag(self->file, "xdr:nvSpPr");
}
/*
* Write the <a:prstGeom> element for shapes.
*/
STATIC void
_drawing_write_a_prst_geom_rect(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);
lxw_xml_empty_tag(self->file, "a:avLst", NULL);
lxw_xml_end_tag(self->file, "a:prstGeom");
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <xdr:spPr> element for shapes.
*/
STATIC void
_drawing_write_sp_pr_shape(lxw_drawing *self, lxw_drawing_object *drawing_object)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
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_rect(self);
/* Write the a:solidFill element with scheme color. */
lxw_xml_start_tag(self->file, "a:solidFill", NULL);
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("val", "lt1");
lxw_xml_empty_tag(self->file, "a:schemeClr", &attributes);
LXW_FREE_ATTRIBUTES();
lxw_xml_end_tag(self->file, "a:solidFill");
/* Write the a:ln element with line styling. */
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_INT("w", 9525);
LXW_PUSH_ATTRIBUTES_STR("cmpd", "sng");
lxw_xml_start_tag(self->file, "a:ln", &attributes);
LXW_FREE_ATTRIBUTES();
/* Write line solid fill with shaded scheme color. */
lxw_xml_start_tag(self->file, "a:solidFill", NULL);
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("val", "lt1");
lxw_xml_start_tag(self->file, "a:schemeClr", &attributes);
LXW_FREE_ATTRIBUTES();
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_INT("val", 50000);
lxw_xml_empty_tag(self->file, "a:shade", &attributes);
LXW_FREE_ATTRIBUTES();
lxw_xml_end_tag(self->file, "a:schemeClr");
lxw_xml_end_tag(self->file, "a:solidFill");
lxw_xml_end_tag(self->file, "a:ln");
lxw_xml_end_tag(self->file, "xdr:spPr");
}
/*
* Write the <a:t> element for text.
*/
STATIC void
_drawing_write_a_t(lxw_drawing *self, const char *text)
{
lxw_xml_data_element(self->file, "a:t", text, NULL);
}
/*
* Write the <a:rPr> element for text run properties.
*/
STATIC void
_drawing_write_a_r_pr(lxw_drawing *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("lang", "en-US");
LXW_PUSH_ATTRIBUTES_INT("sz", 1100);
lxw_xml_empty_tag(self->file, "a:rPr", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <a:r> element for text run.
*/
STATIC void
_drawing_write_a_r(lxw_drawing *self, const char *text)
{
lxw_xml_start_tag(self->file, "a:r", NULL);
/* Write the a:rPr element. */
_drawing_write_a_r_pr(self);
/* Write the a:t element. */
_drawing_write_a_t(self, text);
lxw_xml_end_tag(self->file, "a:r");
}
/*
* Write the <a:pPr> element for paragraph properties.
*/
STATIC void
_drawing_write_a_p_pr(lxw_drawing *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("algn", "l");
lxw_xml_empty_tag(self->file, "a:pPr", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <a:p> element for paragraph.
*/
STATIC void
_drawing_write_a_p_textbox(lxw_drawing *self, const char *text)
{
lxw_xml_start_tag(self->file, "a:p", NULL);
/* Write the a:r element. */
_drawing_write_a_r(self, text);
lxw_xml_end_tag(self->file, "a:p");
}
/*
* Write the <a:bodyPr> element for text body properties.
*/
STATIC void
_drawing_write_a_body_pr(lxw_drawing *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("wrap", "square");
LXW_PUSH_ATTRIBUTES_INT("rtlCol", 0);
LXW_PUSH_ATTRIBUTES_STR("anchor", "t");
lxw_xml_empty_tag(self->file, "a:bodyPr", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <xdr:txBody> element for textbox.
*/
STATIC void
_drawing_write_tx_body(lxw_drawing *self, const char *text)
{
lxw_xml_start_tag(self->file, "xdr:txBody", NULL);
/* Write the a:bodyPr element. */
_drawing_write_a_body_pr(self);
/* Write the a:lstStyle element. */
lxw_xml_empty_tag(self->file, "a:lstStyle", NULL);
/* Write the a:p element. */
_drawing_write_a_p_textbox(self, text);
lxw_xml_end_tag(self->file, "xdr:txBody");
}
/*
* Write the <xdr:style> element for shapes.
*/
STATIC void
_drawing_write_style(lxw_drawing *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
lxw_xml_start_tag(self->file, "xdr:style", NULL);
/* Write the a:lnRef element. */
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_INT("idx", 0);
lxw_xml_start_tag(self->file, "a:lnRef", &attributes);
LXW_FREE_ATTRIBUTES();
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_INT("r", 0);
LXW_PUSH_ATTRIBUTES_INT("g", 0);
LXW_PUSH_ATTRIBUTES_INT("b", 0);
lxw_xml_empty_tag(self->file, "a:scrgbClr", &attributes);
LXW_FREE_ATTRIBUTES();
lxw_xml_end_tag(self->file, "a:lnRef");
/* Write the a:fillRef element. */
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_INT("idx", 0);
lxw_xml_start_tag(self->file, "a:fillRef", &attributes);
LXW_FREE_ATTRIBUTES();
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_INT("r", 0);
LXW_PUSH_ATTRIBUTES_INT("g", 0);
LXW_PUSH_ATTRIBUTES_INT("b", 0);
lxw_xml_empty_tag(self->file, "a:scrgbClr", &attributes);
LXW_FREE_ATTRIBUTES();
lxw_xml_end_tag(self->file, "a:fillRef");
/* Write the a:effectRef element. */
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_INT("idx", 0);
lxw_xml_start_tag(self->file, "a:effectRef", &attributes);
LXW_FREE_ATTRIBUTES();
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_INT("r", 0);
LXW_PUSH_ATTRIBUTES_INT("g", 0);
LXW_PUSH_ATTRIBUTES_INT("b", 0);
lxw_xml_empty_tag(self->file, "a:scrgbClr", &attributes);
LXW_FREE_ATTRIBUTES();
lxw_xml_end_tag(self->file, "a:effectRef");
/* Write the a:fontRef element. */
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("idx", "minor");
lxw_xml_start_tag(self->file, "a:fontRef", &attributes);
LXW_FREE_ATTRIBUTES();
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("val", "dk1");
lxw_xml_empty_tag(self->file, "a:schemeClr", &attributes);
LXW_FREE_ATTRIBUTES();
lxw_xml_end_tag(self->file, "a:fontRef");
lxw_xml_end_tag(self->file, "xdr:style");
}
/*
* Write the <xdr:sp> element for shapes.
*/
STATIC void
_drawing_write_sp(lxw_drawing *self, uint32_t index,
lxw_drawing_object *drawing_object)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("macro", "");
LXW_PUSH_ATTRIBUTES_STR("textlink", "");
lxw_xml_start_tag(self->file, "xdr:sp", &attributes);
/* Write the xdr:nvSpPr element. */
_drawing_write_nv_sp_pr(self, index, drawing_object);
/* Write the xdr:spPr element. */
_drawing_write_sp_pr_shape(self, drawing_object);
/* Write the xdr:style element. */
_drawing_write_style(self);
/* Write the xdr:txBody element if there is text. */
if (drawing_object->text)
_drawing_write_tx_body(self, drawing_object->text);
lxw_xml_end_tag(self->file, "xdr:sp");
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <xdr:clientData> element.
*/
@ -842,10 +1161,9 @@ _drawing_write_two_cell_anchor(lxw_drawing *self, uint32_t index,
/* 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); */
else if (drawing_object->type == LXW_DRAWING_SHAPE) {
/* Write the xdr:sp element for shapes/textboxes. */
_drawing_write_sp(self, index, drawing_object);
}
/* Write the xdr:clientData element. */

259
src/feature_property_bag.c Normal file
View file

@ -0,0 +1,259 @@
/*
* libxlsxwriter
*
* SPDX-License-Identifier: BSD-2-Clause
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org.
*
* feature_property_bag - A libxlsxwriter library for creating Excel XLSX
* featurePropertyBag files.
*
*/
#include "xlsxwriter/xmlwriter.h"
#include "xlsxwriter/feature_property_bag.h"
#include "xlsxwriter/utility.h"
/*
* Forward declarations.
*/
/*****************************************************************************
*
* Private functions.
*
****************************************************************************/
/*
* Create a new feature_property_bag object.
*/
lxw_feature_property_bag *
lxw_feature_property_bag_new(void)
{
lxw_feature_property_bag *feature_property_bag =
calloc(1, sizeof(lxw_feature_property_bag));
GOTO_LABEL_ON_MEM_ERROR(feature_property_bag, mem_error);
return feature_property_bag;
mem_error:
lxw_feature_property_bag_free(feature_property_bag);
return NULL;
}
/*
* Free a feature_property_bag object.
*/
void
lxw_feature_property_bag_free(lxw_feature_property_bag *feature_property_bag)
{
if (!feature_property_bag)
return;
free(feature_property_bag);
}
/*****************************************************************************
*
* XML functions.
*
****************************************************************************/
/*
* Write the XML declaration.
*/
STATIC void
_feature_property_bag_xml_declaration(lxw_feature_property_bag *self)
{
lxw_xml_declaration(self->file);
}
/*
* Write the <FeaturePropertyBags> element.
*/
STATIC void
_write_feature_property_bags(lxw_feature_property_bag *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("xmlns",
"http://schemas.microsoft.com/office/spreadsheetml/2022/featurepropertybag");
lxw_xml_start_tag(self->file, "FeaturePropertyBags", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the Checkbox <bag> element.
*/
STATIC void
_write_checkbox_bag(lxw_feature_property_bag *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("type", "Checkbox");
lxw_xml_empty_tag(self->file, "bag", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <bagId> element.
*/
STATIC void
_write_bag_id(lxw_feature_property_bag *self, const char *key, int id)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
char id_str[LXW_UINT32_T_LENGTH];
lxw_snprintf(id_str, LXW_UINT32_T_LENGTH, "%d", id);
LXW_INIT_ATTRIBUTES();
if (key && key[0] != '\0')
LXW_PUSH_ATTRIBUTES_STR("k", key);
lxw_xml_data_element(self->file, "bagId", id_str, &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the XFControls <bag> element.
*/
STATIC void
_write_xf_controls_bag(lxw_feature_property_bag *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("type", "XFControls");
lxw_xml_start_tag(self->file, "bag", &attributes);
_write_bag_id(self, "CellControl", 0);
lxw_xml_end_tag(self->file, "bag");
LXW_FREE_ATTRIBUTES();
}
/*
* Write the XFComplement <bag> element.
*/
STATIC void
_write_xf_complement_bag(lxw_feature_property_bag *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("type", "XFComplement");
lxw_xml_start_tag(self->file, "bag", &attributes);
_write_bag_id(self, "XFControls", 1);
lxw_xml_end_tag(self->file, "bag");
LXW_FREE_ATTRIBUTES();
}
/*
* Write the XFComplements <bag> element.
*/
STATIC void
_write_xf_complements_bag(lxw_feature_property_bag *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("type", "XFComplements");
LXW_PUSH_ATTRIBUTES_STR("extRef", "XFComplementsMapperExtRef");
lxw_xml_start_tag(self->file, "bag", &attributes);
LXW_FREE_ATTRIBUTES();
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("k", "MappedFeaturePropertyBags");
lxw_xml_start_tag(self->file, "a", &attributes);
LXW_FREE_ATTRIBUTES();
_write_bag_id(self, NULL, 2);
lxw_xml_end_tag(self->file, "a");
lxw_xml_end_tag(self->file, "bag");
}
/*
* Write the DXFComplements <bag> element.
*/
STATIC void
_write_dxf_complements_bag(lxw_feature_property_bag *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("type", "DXFComplements");
LXW_PUSH_ATTRIBUTES_STR("extRef", "DXFComplementsMapperExtRef");
lxw_xml_start_tag(self->file, "bag", &attributes);
LXW_FREE_ATTRIBUTES();
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("k", "MappedFeaturePropertyBags");
lxw_xml_start_tag(self->file, "a", &attributes);
LXW_FREE_ATTRIBUTES();
_write_bag_id(self, NULL, 2);
lxw_xml_end_tag(self->file, "a");
lxw_xml_end_tag(self->file, "bag");
}
/*****************************************************************************
*
* XML file assembly functions.
*
****************************************************************************/
/*
* Assemble and write the XML file.
*/
void
lxw_feature_property_bag_assemble_xml_file(lxw_feature_property_bag *self)
{
/* Write the XML declaration. */
_feature_property_bag_xml_declaration(self);
/* Write the FeaturePropertyBags element. */
_write_feature_property_bags(self);
/* Write the Checkbox bag element. */
_write_checkbox_bag(self);
/* Write the XFControls bag element. */
_write_xf_controls_bag(self);
/* Write the XFComplement bag element. */
_write_xf_complement_bag(self);
/* Write the XFComplements bag element. */
_write_xf_complements_bag(self);
/* Write the DXFComplements bag element if required. */
if (self->has_dxf_complements)
_write_dxf_complements_bag(self);
lxw_xml_end_tag(self->file, "FeaturePropertyBags");
}

View file

@ -101,6 +101,8 @@ lxw_format_new(void)
format->quote_prefix = LXW_FALSE;
format->checkbox = LXW_FALSE;
return format;
mem_error:
@ -830,3 +832,12 @@ format_set_quote_prefix(lxw_format *self)
{
self->quote_prefix = LXW_TRUE;
}
/*
* Set the checkbox property.
*/
void
format_set_checkbox(lxw_format *self)
{
self->checkbox = LXW_TRUE;
}

654
src/labview_wrappers.c Normal file
View file

@ -0,0 +1,654 @@
/*
* labview_wrappers.c - LabVIEW wrapper functions with ANSI to UTF-8 conversion
*
* These functions wrap the standard libxlsxwriter functions to convert
* ANSI-encoded strings (from LabVIEW) to UTF-8 before passing to the library.
*
* LabVIEW uses unsigned long (32-bit) to represent opaque pointer handles.
* These wrapper functions cast between the handle representation and actual pointers.
*
* SPDX-License-Identifier: BSD-2-Clause
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org.
*/
#include "xlsxwriter.h"
#ifdef _WIN32
#include <windows.h>
#include <stdlib.h>
#include <string.h>
/* Handle type for LabVIEW compatibility (32-bit on x86, 64-bit handles need uintptr_t) */
typedef uintptr_t lxw_handle;
/*
* Convert ANSI string to UTF-8.
* Returns newly allocated UTF-8 string (caller must free) or NULL on failure.
*/
static char *
ansi_to_utf8(const char *ansi_str)
{
if (!ansi_str || !*ansi_str)
return NULL;
/* First convert ANSI to UTF-16 */
int wide_len = MultiByteToWideChar(CP_ACP, 0, ansi_str, -1, NULL, 0);
if (wide_len == 0)
return NULL;
wchar_t *wide_str = (wchar_t *)malloc(wide_len * sizeof(wchar_t));
if (!wide_str)
return NULL;
if (MultiByteToWideChar(CP_ACP, 0, ansi_str, -1, wide_str, wide_len) == 0) {
free(wide_str);
return NULL;
}
/* Then convert UTF-16 to UTF-8 */
int utf8_len = WideCharToMultiByte(CP_UTF8, 0, wide_str, -1, NULL, 0, NULL, NULL);
if (utf8_len == 0) {
free(wide_str);
return NULL;
}
char *utf8_str = (char *)malloc(utf8_len);
if (!utf8_str) {
free(wide_str);
return NULL;
}
if (WideCharToMultiByte(CP_UTF8, 0, wide_str, -1, utf8_str, utf8_len, NULL, NULL) == 0) {
free(wide_str);
free(utf8_str);
return NULL;
}
free(wide_str);
return utf8_str;
}
#else
/* On non-Windows, assume strings are already UTF-8 */
#include <stdlib.h>
#include <string.h>
static char *
ansi_to_utf8(const char *str)
{
if (!str)
return NULL;
return strdup(str);
}
#endif
/* ============================================================================
* Worksheet write functions
* ============================================================================ */
lxw_error
worksheet_write_string_lv(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col,
const char *string, lxw_format *format)
{
char *utf8 = ansi_to_utf8(string);
lxw_error err = worksheet_write_string(worksheet, row, col, utf8 ? utf8 : string, format);
free(utf8);
return err;
}
lxw_error
worksheet_write_formula_lv(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col,
const char *formula, lxw_format *format)
{
char *utf8 = ansi_to_utf8(formula);
lxw_error err = worksheet_write_formula(worksheet, row, col, utf8 ? utf8 : formula, format);
free(utf8);
return err;
}
lxw_error
worksheet_write_url_lv(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col,
const char *url, lxw_format *format)
{
char *utf8 = ansi_to_utf8(url);
lxw_error err = worksheet_write_url(worksheet, row, col, utf8 ? utf8 : url, format);
free(utf8);
return err;
}
lxw_error
worksheet_write_comment_lv(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col,
const char *string)
{
char *utf8 = ansi_to_utf8(string);
lxw_error err = worksheet_write_comment(worksheet, row, col, utf8 ? utf8 : string);
free(utf8);
return err;
}
lxw_error
worksheet_set_header_lv(lxw_worksheet *worksheet, const char *header)
{
char *utf8 = ansi_to_utf8(header);
lxw_error err = worksheet_set_header(worksheet, utf8 ? utf8 : header);
free(utf8);
return err;
}
lxw_error
worksheet_set_footer_lv(lxw_worksheet *worksheet, const char *footer)
{
char *utf8 = ansi_to_utf8(footer);
lxw_error err = worksheet_set_footer(worksheet, utf8 ? utf8 : footer);
free(utf8);
return err;
}
lxw_error
worksheet_merge_range_lv(lxw_worksheet *worksheet, lxw_row_t first_row, lxw_col_t first_col,
lxw_row_t last_row, lxw_col_t last_col, const char *string,
lxw_format *format)
{
char *utf8 = ansi_to_utf8(string);
lxw_error err = worksheet_merge_range(worksheet, first_row, first_col, last_row, last_col,
utf8 ? utf8 : string, format);
free(utf8);
return err;
}
void
worksheet_set_comments_author_lv(lxw_worksheet *worksheet, const char *author)
{
char *utf8 = ansi_to_utf8(author);
worksheet_set_comments_author(worksheet, utf8 ? utf8 : author);
free(utf8);
}
lxw_error
worksheet_insert_textbox_lv(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col,
const char *text)
{
char *utf8 = ansi_to_utf8(text);
lxw_error err = worksheet_insert_textbox(worksheet, row, col, utf8 ? utf8 : text);
free(utf8);
return err;
}
lxw_error
worksheet_insert_textbox_opt_lv(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col,
const char *text, lxw_textbox_options *options)
{
char *utf8 = ansi_to_utf8(text);
lxw_error err = worksheet_insert_textbox_opt(worksheet, row, col, utf8 ? utf8 : text, options);
free(utf8);
return err;
}
/* ============================================================================
* Chart functions
* ============================================================================ */
lxw_chart_series *
chart_add_series_lv(lxw_chart *chart, const char *categories, const char *values,
uint8_t y2_axis)
{
char *utf8_cat = ansi_to_utf8(categories);
char *utf8_val = ansi_to_utf8(values);
lxw_chart_series *series = chart_add_series_impl(chart,
utf8_cat ? utf8_cat : categories,
utf8_val ? utf8_val : values,
y2_axis);
free(utf8_cat);
free(utf8_val);
return series;
}
void
chart_series_set_name_lv(lxw_chart_series *series, const char *name)
{
char *utf8 = ansi_to_utf8(name);
chart_series_set_name(series, utf8 ? utf8 : name);
free(utf8);
}
void
chart_axis_set_name_lv(lxw_chart_axis *axis, const char *name)
{
char *utf8 = ansi_to_utf8(name);
chart_axis_set_name(axis, utf8 ? utf8 : name);
free(utf8);
}
void
chart_title_set_name_lv(lxw_chart *chart, const char *name)
{
char *utf8 = ansi_to_utf8(name);
chart_title_set_name(chart, utf8 ? utf8 : name);
free(utf8);
}
void
chart_series_set_trendline_name_lv(lxw_chart_series *series, const char *name)
{
char *utf8 = ansi_to_utf8(name);
chart_series_set_trendline_name(series, utf8 ? utf8 : name);
free(utf8);
}
void
chart_axis_set_num_format_lv(lxw_chart_axis *axis, const char *num_format)
{
char *utf8 = ansi_to_utf8(num_format);
chart_axis_set_num_format(axis, utf8 ? utf8 : num_format);
free(utf8);
}
void
chart_series_set_labels_num_format_lv(lxw_chart_series *series, const char *num_format)
{
char *utf8 = ansi_to_utf8(num_format);
chart_series_set_labels_num_format(series, utf8 ? utf8 : num_format);
free(utf8);
}
void
chart_series_set_categories_lv(lxw_chart_series *series, const char *sheetname,
lxw_row_t first_row, lxw_col_t first_col,
lxw_row_t last_row, lxw_col_t last_col)
{
char *utf8 = ansi_to_utf8(sheetname);
chart_series_set_categories(series, utf8 ? utf8 : sheetname,
first_row, first_col, last_row, last_col);
free(utf8);
}
void
chart_series_set_values_lv(lxw_chart_series *series, const char *sheetname,
lxw_row_t first_row, lxw_col_t first_col,
lxw_row_t last_row, lxw_col_t last_col)
{
char *utf8 = ansi_to_utf8(sheetname);
chart_series_set_values(series, utf8 ? utf8 : sheetname,
first_row, first_col, last_row, last_col);
free(utf8);
}
void
chart_series_set_name_range_lv(lxw_chart_series *series, const char *sheetname,
lxw_row_t row, lxw_col_t col)
{
char *utf8 = ansi_to_utf8(sheetname);
chart_series_set_name_range(series, utf8 ? utf8 : sheetname, row, col);
free(utf8);
}
void
chart_axis_set_name_range_lv(lxw_chart_axis *axis, const char *sheetname,
lxw_row_t row, lxw_col_t col)
{
char *utf8 = ansi_to_utf8(sheetname);
chart_axis_set_name_range(axis, utf8 ? utf8 : sheetname, row, col);
free(utf8);
}
void
chart_title_set_name_range_lv(lxw_chart *chart, const char *sheetname,
lxw_row_t row, lxw_col_t col)
{
char *utf8 = ansi_to_utf8(sheetname);
chart_title_set_name_range(chart, utf8 ? utf8 : sheetname, row, col);
free(utf8);
}
/* ============================================================================
* Format functions
* ============================================================================ */
void
format_set_font_name_lv(lxw_format *format, const char *font_name)
{
char *utf8 = ansi_to_utf8(font_name);
format_set_font_name(format, utf8 ? utf8 : font_name);
free(utf8);
}
void
format_set_num_format_lv(lxw_format *format, const char *num_format)
{
char *utf8 = ansi_to_utf8(num_format);
format_set_num_format(format, utf8 ? utf8 : num_format);
free(utf8);
}
/* ============================================================================
* Workbook functions
* ============================================================================ */
lxw_worksheet *
workbook_add_worksheet_lv(lxw_workbook *workbook, const char *sheetname)
{
/* Pass NULL to get default Sheet1, Sheet2, etc. names */
if (!sheetname || !*sheetname) {
return workbook_add_worksheet(workbook, NULL);
}
char *utf8 = ansi_to_utf8(sheetname);
lxw_worksheet *ws = workbook_add_worksheet(workbook, utf8 ? utf8 : sheetname);
free(utf8);
return ws;
}
lxw_chartsheet *
workbook_add_chartsheet_lv(lxw_workbook *workbook, const char *sheetname)
{
/* Pass NULL to get default Chart1, Chart2, etc. names */
if (!sheetname || !*sheetname) {
return workbook_add_chartsheet(workbook, NULL);
}
char *utf8 = ansi_to_utf8(sheetname);
lxw_chartsheet *cs = workbook_add_chartsheet(workbook, utf8 ? utf8 : sheetname);
free(utf8);
return cs;
}
lxw_error
workbook_define_name_lv(lxw_workbook *workbook, const char *name, const char *formula)
{
char *utf8_name = ansi_to_utf8(name);
char *utf8_formula = ansi_to_utf8(formula);
lxw_error err = workbook_define_name(workbook,
utf8_name ? utf8_name : name,
utf8_formula ? utf8_formula : formula);
free(utf8_name);
free(utf8_formula);
return err;
}
lxw_worksheet *
workbook_get_worksheet_by_name_lv(lxw_workbook *workbook, const char *name)
{
char *utf8 = ansi_to_utf8(name);
lxw_worksheet *ws = workbook_get_worksheet_by_name(workbook, utf8 ? utf8 : name);
free(utf8);
return ws;
}
lxw_chartsheet *
workbook_get_chartsheet_by_name_lv(lxw_workbook *workbook, const char *name)
{
char *utf8 = ansi_to_utf8(name);
lxw_chartsheet *cs = workbook_get_chartsheet_by_name(workbook, utf8 ? utf8 : name);
free(utf8);
return cs;
}
lxw_error
workbook_validate_sheet_name_lv(lxw_workbook *workbook, const char *sheetname)
{
char *utf8 = ansi_to_utf8(sheetname);
lxw_error err = workbook_validate_sheet_name(workbook, utf8 ? utf8 : sheetname);
free(utf8);
return err;
}
lxw_error
workbook_set_custom_property_string_lv(lxw_workbook *workbook, const char *name,
const char *value)
{
char *utf8_name = ansi_to_utf8(name);
char *utf8_value = ansi_to_utf8(value);
lxw_error err = workbook_set_custom_property_string(workbook,
utf8_name ? utf8_name : name,
utf8_value ? utf8_value : value);
free(utf8_name);
free(utf8_value);
return err;
}
/* ============================================================================
* Chartsheet functions
* ============================================================================ */
lxw_error
chartsheet_set_header_lv(lxw_chartsheet *chartsheet, const char *header)
{
char *utf8 = ansi_to_utf8(header);
lxw_error err = chartsheet_set_header(chartsheet, utf8 ? utf8 : header);
free(utf8);
return err;
}
lxw_error
chartsheet_set_footer_lv(lxw_chartsheet *chartsheet, const char *footer)
{
char *utf8 = ansi_to_utf8(footer);
lxw_error err = chartsheet_set_footer(chartsheet, utf8 ? utf8 : footer);
free(utf8);
return err;
}
/* ============================================================================
* File path functions (ANSI to UTF-8 conversion for lxw_fopen compatibility)
* ============================================================================ */
lxw_workbook *
workbook_new_lv(const char *filename)
{
char *utf8 = ansi_to_utf8(filename);
lxw_workbook *wb = workbook_new(utf8 ? utf8 : filename);
free(utf8);
return wb;
}
lxw_workbook *
workbook_new_opt_lv(const char *filename, lxw_workbook_options *options)
{
char *utf8 = ansi_to_utf8(filename);
lxw_workbook *wb = workbook_new_opt(utf8 ? utf8 : filename, options);
free(utf8);
return wb;
}
lxw_error
worksheet_insert_image_lv(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col,
const char *filename)
{
char *utf8 = ansi_to_utf8(filename);
lxw_error err = worksheet_insert_image(worksheet, row, col, utf8 ? utf8 : filename);
free(utf8);
return err;
}
lxw_error
worksheet_insert_image_opt_lv(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col,
const char *filename, lxw_image_options *options)
{
char *utf8 = ansi_to_utf8(filename);
lxw_error err = worksheet_insert_image_opt(worksheet, row, col, utf8 ? utf8 : filename, options);
free(utf8);
return err;
}
lxw_error
worksheet_embed_image_lv(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col,
const char *filename)
{
char *utf8 = ansi_to_utf8(filename);
lxw_error err = worksheet_embed_image(worksheet, row, col, utf8 ? utf8 : filename);
free(utf8);
return err;
}
lxw_error
worksheet_embed_image_opt_lv(lxw_worksheet *worksheet, lxw_row_t row, lxw_col_t col,
const char *filename, lxw_image_options *options)
{
char *utf8 = ansi_to_utf8(filename);
lxw_error err = worksheet_embed_image_opt(worksheet, row, col, utf8 ? utf8 : filename, options);
free(utf8);
return err;
}
lxw_error
worksheet_set_background_lv(lxw_worksheet *worksheet, const char *filename)
{
char *utf8 = ansi_to_utf8(filename);
lxw_error err = worksheet_set_background(worksheet, utf8 ? utf8 : filename);
free(utf8);
return err;
}
lxw_error
workbook_add_vba_project_lv(lxw_workbook *workbook, const char *filename)
{
char *utf8 = ansi_to_utf8(filename);
lxw_error err = workbook_add_vba_project(workbook, utf8 ? utf8 : filename);
free(utf8);
return err;
}
lxw_error
workbook_add_signed_vba_project_lv(lxw_workbook *workbook, const char *vba_project,
const char *signature)
{
char *utf8_vba = ansi_to_utf8(vba_project);
char *utf8_sig = ansi_to_utf8(signature);
lxw_error err = workbook_add_signed_vba_project(workbook,
utf8_vba ? utf8_vba : vba_project,
utf8_sig ? utf8_sig : signature);
free(utf8_vba);
free(utf8_sig);
return err;
}
/* ============================================================================
* Autofilter functions
* ============================================================================ */
lxw_error
worksheet_filter_column_lv(lxw_worksheet *worksheet, lxw_col_t col,
uint8_t criteria, const char *value_string, double value)
{
lxw_filter_rule rule = {0};
rule.criteria = criteria;
rule.value = value;
if (value_string && *value_string) {
char *utf8 = ansi_to_utf8(value_string);
rule.value_string = utf8 ? utf8 : value_string;
lxw_error err = worksheet_filter_column(worksheet, col, &rule);
free(utf8);
return err;
}
else {
rule.value_string = NULL;
return worksheet_filter_column(worksheet, col, &rule);
}
}
lxw_error
worksheet_filter_column2_lv(lxw_worksheet *worksheet, lxw_col_t col,
uint8_t criteria1, const char *value_string1, double value1,
uint8_t criteria2, const char *value_string2, double value2,
uint8_t and_or)
{
lxw_filter_rule rule1 = {0};
lxw_filter_rule rule2 = {0};
char *utf8_1 = NULL;
char *utf8_2 = NULL;
rule1.criteria = criteria1;
rule1.value = value1;
rule2.criteria = criteria2;
rule2.value = value2;
if (value_string1 && *value_string1) {
utf8_1 = ansi_to_utf8(value_string1);
rule1.value_string = utf8_1 ? utf8_1 : value_string1;
}
if (value_string2 && *value_string2) {
utf8_2 = ansi_to_utf8(value_string2);
rule2.value_string = utf8_2 ? utf8_2 : value_string2;
}
lxw_error err = worksheet_filter_column2(worksheet, col, &rule1, &rule2, and_or);
free(utf8_1);
free(utf8_2);
return err;
}
/* ============================================================================
* Chart data label functions
* ============================================================================ */
lxw_error
chart_series_set_labels_custom_lv(lxw_chart_series *series,
uintptr_t *values,
uint8_t *hide_flags,
uint16_t count)
{
lxw_error err = LXW_NO_ERROR;
uint16_t i;
lxw_chart_data_label *labels = NULL;
lxw_chart_data_label **label_ptrs = NULL;
char **utf8_values = NULL;
if (!series || count == 0)
return LXW_ERROR_NULL_PARAMETER_IGNORED;
/* Allocate arrays */
labels = (lxw_chart_data_label *)calloc(count, sizeof(lxw_chart_data_label));
label_ptrs = (lxw_chart_data_label **)calloc(count + 1, sizeof(lxw_chart_data_label *));
utf8_values = (char **)calloc(count, sizeof(char *));
if (!labels || !label_ptrs || !utf8_values) {
free(labels);
free(label_ptrs);
free(utf8_values);
return LXW_ERROR_MEMORY_MALLOC_FAILED;
}
/* Build the label structs */
for (i = 0; i < count; i++) {
const char *str;
/* Set up pointer array */
label_ptrs[i] = &labels[i];
/* Get string pointer from uintptr_t array */
str = (values && values[i]) ? (const char *)values[i] : NULL;
/* Convert value string if provided */
if (str && str[0]) {
utf8_values[i] = ansi_to_utf8(str);
labels[i].value = utf8_values[i] ? utf8_values[i] : str;
}
else {
labels[i].value = NULL;
}
/* Set hide flag */
if (hide_flags) {
labels[i].hide = hide_flags[i];
}
/* Other fields remain NULL (font, line, fill, pattern) */
}
/* NULL-terminate the pointer array */
label_ptrs[count] = NULL;
/* Call the library function */
err = chart_series_set_labels_custom(series, label_ptrs);
/* Free allocated memory */
for (i = 0; i < count; i++) {
free(utf8_values[i]);
}
free(utf8_values);
free(labels);
free(label_ptrs);
return err;
}

View file

@ -1276,6 +1276,48 @@ mem_error:
return err;
}
/*
* Write the featurePropertyBag.xml file.
*/
STATIC lxw_error
_write_feature_property_bag_file(lxw_packager *self)
{
lxw_error err = LXW_NO_ERROR;
lxw_feature_property_bag *feature_property_bag;
char *buffer = NULL;
size_t buffer_size = 0;
if (!self->workbook->has_feature_property_bags)
return LXW_NO_ERROR;
feature_property_bag = lxw_feature_property_bag_new();
if (!feature_property_bag) {
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
goto mem_error;
}
feature_property_bag->file =
lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir);
if (!feature_property_bag->file) {
err = LXW_ERROR_CREATING_TMPFILE;
goto mem_error;
}
lxw_feature_property_bag_assemble_xml_file(feature_property_bag);
err = _add_to_zip(self, feature_property_bag->file, &buffer, &buffer_size,
"xl/featurePropertyBag/featurePropertyBag.xml");
fclose(feature_property_bag->file);
free(buffer);
mem_error:
lxw_feature_property_bag_free(feature_property_bag);
return err;
}
/*
* Write the custom.xml file.
*/
@ -1539,6 +1581,9 @@ _write_content_types_file(lxw_packager *self)
if (workbook->has_embedded_images)
lxw_ct_add_rich_value(content_types);
if (workbook->has_feature_property_bags)
lxw_ct_add_feature_property_bag(content_types);
lxw_content_types_assemble_xml_file(content_types);
err = _add_to_zip(self, content_types->file, &buffer, &buffer_size,
@ -1612,6 +1657,9 @@ _write_workbook_rels_file(lxw_packager *self)
if (workbook->has_embedded_images)
lxw_add_rich_value_relationship(rels);
if (workbook->has_feature_property_bags)
lxw_add_feature_property_bag_relationship(rels);
lxw_relationships_assemble_xml_file(rels);
err = _add_to_zip(self, rels->file, &buffer, &buffer_size,
@ -2250,6 +2298,9 @@ lxw_create_package(lxw_packager *self)
error = _write_rich_value_structure_file(self);
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
error = _write_feature_property_bag_file(self);
RETURN_AND_ZIPCLOSE_ON_ERROR(error);
error = _write_rich_value_rels_file(self);
RETURN_AND_ZIPCLOSE_ON_ERROR(error);

View file

@ -267,3 +267,15 @@ lxw_add_rich_value_relationship(lxw_relationships *self)
NULL);
}
/*
* Add a featurePropertyBag relationship to workbook .rels xml files.
*/
void
lxw_add_feature_property_bag_relationship(lxw_relationships *self)
{
_add_relationship(self,
"http://schemas.microsoft.com/office/2022/11/relationships/",
"FeaturePropertyBag",
"featurePropertyBag/featurePropertyBag.xml", NULL);
}

View file

@ -1187,6 +1187,34 @@ _write_protection(lxw_styles *self, lxw_format *format)
LXW_FREE_ATTRIBUTES();
}
/*
* Write the xfComplement <extLst> elements for checkbox formats.
*/
STATIC void
_write_xf_format_extensions(lxw_styles *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
lxw_xml_start_tag(self->file, "extLst", NULL);
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("uri", "{C7286773-470A-42A8-94C5-96B5CB345126}");
LXW_PUSH_ATTRIBUTES_STR("xmlns:xfpb",
"http://schemas.microsoft.com/office/spreadsheetml/2022/featurepropertybag");
lxw_xml_start_tag(self->file, "ext", &attributes);
LXW_FREE_ATTRIBUTES();
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("i", "0");
lxw_xml_empty_tag(self->file, "xfpb:xfComplement", &attributes);
LXW_FREE_ATTRIBUTES();
lxw_xml_end_tag(self->file, "ext");
lxw_xml_end_tag(self->file, "extLst");
}
/*
* Write the <xf> element.
*/
@ -1198,6 +1226,7 @@ _write_xf(lxw_styles *self, lxw_format *format)
uint8_t has_protection = (!format->locked) | format->hidden;
uint8_t has_alignment = _has_alignment(format);
uint8_t apply_alignment = _apply_alignment(format);
uint8_t has_checkbox = format->checkbox;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_INT("numFmtId", format->num_format_index);
@ -1232,7 +1261,7 @@ _write_xf(lxw_styles *self, lxw_format *format)
LXW_PUSH_ATTRIBUTES_STR("applyProtection", "1");
/* Write XF with sub-elements if required. */
if (has_alignment || has_protection) {
if (has_alignment || has_protection || has_checkbox) {
lxw_xml_start_tag(self->file, "xf", &attributes);
if (has_alignment)
@ -1241,6 +1270,9 @@ _write_xf(lxw_styles *self, lxw_format *format)
if (has_protection)
_write_protection(self, format);
if (has_checkbox)
_write_xf_format_extensions(self);
lxw_xml_end_tag(self->file, "xf");
}
else {

View file

@ -41,7 +41,7 @@ char *error_strings[LXW_MAX_ERRNO + 1] = {
"NULL function parameter ignored.",
"Function parameter validation error.",
"Function string parameter is empty.",
"Datetime struct parameter has an invalid field value.",
"A lxw_datetime parameter has a validation error.",
"Worksheet name exceeds Excel's limit of 31 characters.",
"Worksheet name cannot contain invalid characters: '[ ] : * ? / \\'",
"Worksheet name cannot start or end with an apostrophe.",
@ -333,6 +333,9 @@ lxw_name_to_col_2(const char *col_str)
return 0;
}
/*
* Validate a lxw_datetime struct.
*/
lxw_error
lxw_datetime_validate(lxw_datetime *datetime)
{
@ -749,6 +752,163 @@ lxw_hash_password(const char *password)
return hash;
}
/*
* Character widths for Calibri 11 font, used for autofit calculations.
* Index 0 = space (ASCII 32), through index 94 = tilde (ASCII 126).
* Unhandled characters (including UTF-8) default to width 8.
*/
static const uint8_t char_widths[95] = {
3, /* space */
5, /* ! */
6, /* " */
7, /* # */
7, /* $ */
11, /* % */
10, /* & */
3, /* ' */
5, /* ( */
5, /* ) */
7, /* * */
7, /* + */
4, /* , */
5, /* - */
4, /* . */
6, /* / */
7, /* 0 */
7, /* 1 */
7, /* 2 */
7, /* 3 */
7, /* 4 */
7, /* 5 */
7, /* 6 */
7, /* 7 */
7, /* 8 */
7, /* 9 */
4, /* : */
4, /* ; */
7, /* < */
7, /* = */
7, /* > */
7, /* ? */
13, /* @ */
9, /* A */
8, /* B */
8, /* C */
9, /* D */
7, /* E */
7, /* F */
9, /* G */
9, /* H */
4, /* I */
5, /* J */
8, /* K */
6, /* L */
12, /* M */
10, /* N */
10, /* O */
8, /* P */
10, /* Q */
8, /* R */
7, /* S */
7, /* T */
9, /* U */
9, /* V */
13, /* W */
8, /* X */
7, /* Y */
7, /* Z */
5, /* [ */
6, /* backslash */
5, /* ] */
7, /* ^ */
7, /* _ */
4, /* ` */
7, /* a */
8, /* b */
6, /* c */
8, /* d */
8, /* e */
5, /* f */
7, /* g */
8, /* h */
4, /* i */
4, /* j */
7, /* k */
4, /* l */
12, /* m */
8, /* n */
8, /* o */
8, /* p */
8, /* q */
5, /* r */
6, /* s */
5, /* t */
8, /* u */
7, /* v */
11, /* w */
7, /* x */
7, /* y */
6, /* z */
5, /* { */
7, /* | */
5, /* } */
7 /* ~ */
};
/*
* Calculate the pixel width of a string based on character widths.
*/
uint16_t
lxw_pixel_width(const char *string)
{
uint16_t length = 0;
unsigned char c;
if (string == NULL || *string == '\0')
return 0;
while ((c = (unsigned char) *string++) != '\0') {
/* Check if character is in the printable ASCII range (32-126) */
if (c >= 32 && c <= 126) {
length += char_widths[c - 32];
}
else {
/* Default width for non-ASCII characters (UTF-8, etc.) */
length += 8;
}
}
return length;
}
/*
* Calculate the column width required to autofit a string.
*/
double
lxw_autofit_width(const char *string)
{
uint16_t pixels;
double width;
const double max_digit_width = 7.0; /* For Calibri 11 */
const double padding = 5.0;
if (string == NULL || *string == '\0')
return 0.0;
/* Get pixel width and add 7 pixels padding like Excel */
pixels = lxw_pixel_width(string) + 7;
/* Convert pixels to column width using Excel's formula */
if (pixels <= 12) {
width = (double) pixels / (max_digit_width + padding);
}
else {
width = ((double) pixels - padding) / max_digit_width;
}
return width;
}
/* Make a simple portable version of fopen() for Windows. */
#ifdef __MINGW32__
#undef _WIN32

View file

@ -13,7 +13,6 @@
#include "xlsxwriter/utility.h"
#include "xlsxwriter/packager.h"
#include "xlsxwriter/hash_table.h"
#include "xlsxwriter/hash_table.h"
STATIC int _worksheet_name_cmp(lxw_worksheet_name *name1,
lxw_worksheet_name *name2);
@ -300,8 +299,8 @@ lxw_workbook_set_default_xf_indices(lxw_workbook *self)
STAILQ_FOREACH(format, self->formats, list_pointers) {
/* Skip the hyperlink format. */
if (index != 1)
/* Skip the hyperlink format (index 1) and checkbox format (index 2). */
if (index != 1 && index != 2)
lxw_format_get_xf_index(format);
index++;
@ -1038,6 +1037,8 @@ _add_chart_cache_data(lxw_workbook *self)
_populate_range(self, chart->title.range);
_populate_range(self, chart->x_axis->title.range);
_populate_range(self, chart->y_axis->title.range);
_populate_range(self, chart->x2_axis->title.range);
_populate_range(self, chart->y2_axis->title.range);
if (STAILQ_EMPTY(chart->series_list))
continue;
@ -1052,6 +1053,31 @@ _add_chart_cache_data(lxw_workbook *self)
_populate_range(self, data_label->range);
}
}
/* Also populate data for combined charts. */
if (chart->combined) {
lxw_chart *combined = chart->combined;
_populate_range(self, combined->title.range);
_populate_range(self, combined->x_axis->title.range);
_populate_range(self, combined->y_axis->title.range);
_populate_range(self, combined->x2_axis->title.range);
_populate_range(self, combined->y2_axis->title.range);
if (!STAILQ_EMPTY(combined->series_list)) {
STAILQ_FOREACH(series, combined->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);
}
}
}
}
}
}
@ -1106,6 +1132,7 @@ _prepare_drawings(lxw_workbook *self)
if (STAILQ_EMPTY(worksheet->image_props)
&& STAILQ_EMPTY(worksheet->embedded_image_props)
&& STAILQ_EMPTY(worksheet->chart_data)
&& STAILQ_EMPTY(worksheet->textbox_data)
&& !worksheet->has_header_vml && !worksheet->has_background_image) {
continue;
}
@ -1243,6 +1270,11 @@ _prepare_drawings(lxw_workbook *self)
ordered_list_pointers);
}
/* Prepare worksheet textboxes. */
STAILQ_FOREACH(object_props, worksheet->textbox_data, list_pointers) {
lxw_worksheet_prepare_textbox(worksheet, drawing_id, object_props);
}
/* Prepare worksheet header/footer images. */
for (i = 0; i < LXW_HEADER_FOOTER_OBJS_MAX; i++) {
@ -1968,6 +2000,12 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
format_set_hyperlink(format);
workbook->default_url_format = format;
/* Add the default checkbox format. */
format = workbook_add_format(workbook);
GOTO_LABEL_ON_MEM_ERROR(format, mem_error);
format_set_checkbox(format);
workbook->checkbox_format = format;
if (options) {
workbook->options.constant_memory = options->constant_memory;
workbook->options.tmpdir = lxw_strdup(options->tmpdir);
@ -2039,6 +2077,7 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
init_data.first_sheet = &self->first_sheet;
init_data.tmpdir = self->options.tmpdir;
init_data.default_url_format = self->default_url_format;
init_data.checkbox_format = self->checkbox_format;
init_data.max_url_length = self->max_url_length;
init_data.use_1904_epoch = self->use_1904_epoch;
@ -2167,7 +2206,7 @@ workbook_add_chart(lxw_workbook *self, uint8_t type)
{
lxw_chart *chart;
if (type == LXW_CHART_NONE || type > LXW_CHART_RADAR_FILLED) {
if (type == LXW_CHART_NONE || type > LXW_CHART_STOCK) {
LXW_WARN_FORMAT1("workbook_add_chart(): invalid chart type: %d",
type);
return NULL;
@ -2247,6 +2286,9 @@ workbook_close(lxw_workbook *self)
self->has_metadata = LXW_TRUE;
self->has_embedded_images = LXW_TRUE;
}
if (worksheet->has_checkboxes)
self->has_feature_property_bags = LXW_TRUE;
}
/* Set workbook and worksheet VBA codenames if a macro has been added. */

View file

@ -151,6 +151,10 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
GOTO_LABEL_ON_MEM_ERROR(worksheet->chart_data, mem_error);
STAILQ_INIT(worksheet->chart_data);
worksheet->textbox_data = calloc(1, sizeof(struct lxw_textbox_props));
GOTO_LABEL_ON_MEM_ERROR(worksheet->textbox_data, mem_error);
STAILQ_INIT(worksheet->textbox_data);
worksheet->comment_objs = calloc(1, sizeof(struct lxw_comment_objs));
GOTO_LABEL_ON_MEM_ERROR(worksheet->comment_objs, mem_error);
STAILQ_INIT(worksheet->comment_objs);
@ -176,6 +180,10 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
GOTO_LABEL_ON_MEM_ERROR(worksheet->table_objs, mem_error);
STAILQ_INIT(worksheet->table_objs);
worksheet->sparklines = calloc(1, sizeof(struct lxw_sparklines));
GOTO_LABEL_ON_MEM_ERROR(worksheet->sparklines, mem_error);
STAILQ_INIT(worksheet->sparklines);
worksheet->external_hyperlinks = calloc(1, sizeof(struct lxw_rel_tuples));
GOTO_LABEL_ON_MEM_ERROR(worksheet->external_hyperlinks, mem_error);
STAILQ_INIT(worksheet->external_hyperlinks);
@ -293,6 +301,7 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
worksheet->active_sheet = init_data->active_sheet;
worksheet->first_sheet = init_data->first_sheet;
worksheet->default_url_format = init_data->default_url_format;
worksheet->checkbox_format = init_data->checkbox_format;
worksheet->max_url_length = init_data->max_url_length;
worksheet->use_1904_epoch = init_data->use_1904_epoch;
}
@ -427,6 +436,7 @@ _free_object_properties(lxw_object_properties *object_property)
free(object_property->image_buffer);
free(object_property->md5);
free(object_property->image_position);
free(object_property->text);
free(object_property);
object_property = NULL;
}
@ -635,6 +645,16 @@ lxw_worksheet_free(lxw_worksheet *worksheet)
free(worksheet->chart_data);
}
if (worksheet->textbox_data) {
while (!STAILQ_EMPTY(worksheet->textbox_data)) {
object_props = STAILQ_FIRST(worksheet->textbox_data);
STAILQ_REMOVE_HEAD(worksheet->textbox_data, list_pointers);
_free_object_properties(object_props);
}
free(worksheet->textbox_data);
}
/* Just free the list. The list objects are freed from the RB tree. */
free(worksheet->comment_objs);
@ -678,6 +698,20 @@ lxw_worksheet_free(lxw_worksheet *worksheet)
free(worksheet->table_objs);
}
if (worksheet->sparklines) {
lxw_sparkline *sparkline;
while (!STAILQ_EMPTY(worksheet->sparklines)) {
sparkline = STAILQ_FIRST(worksheet->sparklines);
STAILQ_REMOVE_HEAD(worksheet->sparklines, list_pointers);
free(sparkline->range);
free(sparkline->location);
free(sparkline->date_axis);
free(sparkline);
}
free(worksheet->sparklines);
}
if (worksheet->data_validations) {
while (!STAILQ_EMPTY(worksheet->data_validations)) {
data_validation = STAILQ_FIRST(worksheet->data_validations);
@ -3007,15 +3041,13 @@ _worksheet_position_object_pixels(lxw_worksheet *self,
height = height + y1;
/* Subtract the underlying cell widths to find the end cell. */
while (width >= _worksheet_size_col(self, col_end, anchor)
&& col_end < LXW_COL_MAX) {
while (width >= _worksheet_size_col(self, col_end, anchor)) {
width -= _worksheet_size_col(self, col_end, anchor);
col_end++;
}
/* Subtract the underlying cell heights to find the end cell. */
while (height >= _worksheet_size_row(self, row_end, anchor)
&& row_end < LXW_ROW_MAX) {
while (height >= _worksheet_size_row(self, row_end, anchor)) {
height -= _worksheet_size_row(self, row_end, anchor);
row_end++;
}
@ -3705,6 +3737,83 @@ mem_error:
}
}
/*
* Set up a textbox in the worksheet.
*/
void
lxw_worksheet_prepare_textbox(lxw_worksheet *self,
uint32_t drawing_id,
lxw_object_properties *object_props)
{
lxw_drawing_object *drawing_object;
lxw_rel_tuple *relationship;
double width;
double height;
char filename[LXW_FILENAME_LENGTH];
if (!self->drawing) {
self->drawing = lxw_drawing_new();
RETURN_VOID_ON_MEM_ERROR(self->drawing);
self->drawing->embedded = LXW_TRUE;
relationship = calloc(1, sizeof(lxw_rel_tuple));
RETURN_VOID_ON_MEM_ERROR(relationship);
relationship->type = lxw_strdup("/drawing");
GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
"../drawings/drawing%d.xml", drawing_id);
relationship->target = lxw_strdup(filename);
GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
STAILQ_INSERT_TAIL(self->external_drawing_links, relationship,
list_pointers);
}
drawing_object = calloc(1, sizeof(lxw_drawing_object));
RETURN_VOID_ON_MEM_ERROR(drawing_object);
drawing_object->anchor = LXW_OBJECT_MOVE_AND_SIZE;
if (object_props->object_position)
drawing_object->anchor = object_props->object_position;
drawing_object->type = LXW_DRAWING_SHAPE;
drawing_object->description = lxw_strdup(object_props->description);
drawing_object->tip = NULL;
drawing_object->rel_index = 0;
drawing_object->url_rel_index = 0;
drawing_object->decorative = object_props->decorative;
drawing_object->text = lxw_strdup(object_props->text);
/* Scale to user scale. */
width = object_props->width * object_props->x_scale;
height = object_props->height * object_props->y_scale;
/* Convert to the nearest pixel. */
object_props->width = width;
object_props->height = height;
_worksheet_position_object_emus(self, object_props, drawing_object);
/* Convert from pixels to emus. */
drawing_object->width = (uint32_t) (0.5 + width * 9525);
drawing_object->height = (uint32_t) (0.5 + height * 9525);
lxw_add_drawing_object(self->drawing, drawing_object);
return;
mem_error:
if (relationship) {
free(relationship->type);
free(relationship->target);
free(relationship->target_mode);
free(relationship);
}
}
/*
* Set up VML objects, such as comments, in the worksheet.
*/
@ -7156,6 +7265,217 @@ _worksheet_write_conditional_formatting_2010(lxw_worksheet *self, lxw_cond_forma
LXW_FREE_ATTRIBUTES();
}
/*
* Write a sparkline color element like <x14:colorSeries>.
* Color of 0 (LXW_COLOR_UNSET) means "not set, use default".
*/
STATIC void
_worksheet_write_sparkline_color(lxw_worksheet *self, const char *tag,
lxw_color_t color)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
char rgb_str[LXW_ATTR_32];
/* 0 means not set, use Excel's default for this style. */
if (color == 0)
return;
lxw_snprintf(rgb_str, LXW_ATTR_32, "FF%06X", color & LXW_COLOR_MASK);
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("rgb", rgb_str);
lxw_xml_empty_tag(self->file, tag, &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <x14:sparklineGroup> element.
*/
STATIC void
_worksheet_write_sparkline_group(lxw_worksheet *self, lxw_sparkline *sparkline)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
char double_str[LXW_ATTR_32];
LXW_INIT_ATTRIBUTES();
/* Write manualMax if custom max. */
if (sparkline->max_axis_type == LXW_SPARKLINE_AXIS_CUSTOM) {
lxw_sprintf_dbl(double_str, sparkline->max);
LXW_PUSH_ATTRIBUTES_STR("manualMax", double_str);
}
/* Write manualMin if custom min. */
if (sparkline->min_axis_type == LXW_SPARKLINE_AXIS_CUSTOM) {
lxw_sprintf_dbl(double_str, sparkline->min);
LXW_PUSH_ATTRIBUTES_STR("manualMin", double_str);
}
/* Write lineWeight (default is 0.75 but we always write if non-default). */
if (sparkline->weight != 0.75) {
lxw_sprintf_dbl(double_str, sparkline->weight);
LXW_PUSH_ATTRIBUTES_STR("lineWeight", double_str);
}
/* Write type (line is default, so only write column or win/loss). */
if (sparkline->type == LXW_SPARKLINE_COLUMN) {
LXW_PUSH_ATTRIBUTES_STR("type", "column");
}
else if (sparkline->type == LXW_SPARKLINE_WIN_LOSS) {
LXW_PUSH_ATTRIBUTES_STR("type", "stacked");
}
/* Write dateAxis if date axis data is present. */
if (sparkline->date_axis) {
LXW_PUSH_ATTRIBUTES_STR("dateAxis", "1");
}
/* Write displayEmptyCellsAs. */
if (sparkline->empty_cells == LXW_SPARKLINE_EMPTY_CELLS_ZERO) {
LXW_PUSH_ATTRIBUTES_STR("displayEmptyCellsAs", "zero");
}
else if (sparkline->empty_cells == LXW_SPARKLINE_EMPTY_CELLS_SPAN) {
LXW_PUSH_ATTRIBUTES_STR("displayEmptyCellsAs", "span");
}
/* Write markers. */
if (sparkline->markers) {
LXW_PUSH_ATTRIBUTES_STR("markers", "1");
}
/* Write high. */
if (sparkline->high_point) {
LXW_PUSH_ATTRIBUTES_STR("high", "1");
}
/* Write low. */
if (sparkline->low_point) {
LXW_PUSH_ATTRIBUTES_STR("low", "1");
}
/* Write first. */
if (sparkline->first_point) {
LXW_PUSH_ATTRIBUTES_STR("first", "1");
}
/* Write last. */
if (sparkline->last_point) {
LXW_PUSH_ATTRIBUTES_STR("last", "1");
}
/* Write negative. */
if (sparkline->negative_points) {
LXW_PUSH_ATTRIBUTES_STR("negative", "1");
}
/* Write displayXAxis. */
if (sparkline->show_axis) {
LXW_PUSH_ATTRIBUTES_STR("displayXAxis", "1");
}
/* Write displayHidden. */
if (sparkline->show_hidden) {
LXW_PUSH_ATTRIBUTES_STR("displayHidden", "1");
}
/* Write minAxisType. */
if (sparkline->min_axis_type == LXW_SPARKLINE_AXIS_GROUP) {
LXW_PUSH_ATTRIBUTES_STR("minAxisType", "group");
}
else if (sparkline->min_axis_type == LXW_SPARKLINE_AXIS_CUSTOM) {
LXW_PUSH_ATTRIBUTES_STR("minAxisType", "custom");
}
/* Write maxAxisType. */
if (sparkline->max_axis_type == LXW_SPARKLINE_AXIS_GROUP) {
LXW_PUSH_ATTRIBUTES_STR("maxAxisType", "group");
}
else if (sparkline->max_axis_type == LXW_SPARKLINE_AXIS_CUSTOM) {
LXW_PUSH_ATTRIBUTES_STR("maxAxisType", "custom");
}
/* Write rightToLeft. */
if (sparkline->reverse) {
LXW_PUSH_ATTRIBUTES_STR("rightToLeft", "1");
}
lxw_xml_start_tag(self->file, "x14:sparklineGroup", &attributes);
LXW_FREE_ATTRIBUTES();
/* Write colors. */
_worksheet_write_sparkline_color(self, "x14:colorSeries",
sparkline->series_color);
_worksheet_write_sparkline_color(self, "x14:colorNegative",
sparkline->negative_color);
/* Write the colorAxis element (always black FF000000). */
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("rgb", "FF000000");
lxw_xml_empty_tag(self->file, "x14:colorAxis", &attributes);
LXW_FREE_ATTRIBUTES();
}
_worksheet_write_sparkline_color(self, "x14:colorMarkers",
sparkline->markers_color);
_worksheet_write_sparkline_color(self, "x14:colorFirst",
sparkline->first_color);
_worksheet_write_sparkline_color(self, "x14:colorLast",
sparkline->last_color);
_worksheet_write_sparkline_color(self, "x14:colorHigh",
sparkline->high_color);
_worksheet_write_sparkline_color(self, "x14:colorLow",
sparkline->low_color);
/* Write date axis formula if present. */
if (sparkline->date_axis) {
lxw_xml_data_element(self->file, "xm:f", sparkline->date_axis, NULL);
}
/* Write the sparklines container. */
lxw_xml_start_tag(self->file, "x14:sparklines", NULL);
/* Write the sparkline element. */
lxw_xml_start_tag(self->file, "x14:sparkline", NULL);
lxw_xml_data_element(self->file, "xm:f", sparkline->range, NULL);
lxw_xml_data_element(self->file, "xm:sqref", sparkline->location, NULL);
lxw_xml_end_tag(self->file, "x14:sparkline");
lxw_xml_end_tag(self->file, "x14:sparklines");
lxw_xml_end_tag(self->file, "x14:sparklineGroup");
}
/*
* Write the <extLst> element for sparklines.
*/
STATIC void
_worksheet_write_ext_list_sparklines(lxw_worksheet *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
lxw_sparkline *sparkline;
char xmlns_xm[] = "http://schemas.microsoft.com/office/excel/2006/main";
_worksheet_write_ext(self, "{05C60535-1F16-4fd2-B633-F4F36F0B64E0}");
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("xmlns:xm", xmlns_xm);
lxw_xml_start_tag(self->file, "x14:sparklineGroups", &attributes);
LXW_FREE_ATTRIBUTES();
STAILQ_FOREACH(sparkline, self->sparklines, list_pointers) {
_worksheet_write_sparkline_group(self, sparkline);
}
lxw_xml_end_tag(self->file, "x14:sparklineGroups");
lxw_xml_end_tag(self->file, "ext");
}
/*
* Write the <extLst> element for Excel 2010 conditional formatting data bars.
*/
@ -7187,12 +7507,16 @@ _worksheet_write_ext_list_data_bars(lxw_worksheet *self)
STATIC void
_worksheet_write_ext_list(lxw_worksheet *self)
{
if (self->data_bar_2010_index == 0)
if (self->data_bar_2010_index == 0 && !self->has_sparklines)
return;
lxw_xml_start_tag(self->file, "extLst", NULL);
_worksheet_write_ext_list_data_bars(self);
if (self->data_bar_2010_index > 0)
_worksheet_write_ext_list_data_bars(self);
if (self->has_sparklines)
_worksheet_write_ext_list_sparklines(self);
lxw_xml_end_tag(self->file, "extLst");
}
@ -8317,6 +8641,31 @@ worksheet_write_boolean(lxw_worksheet *self,
return LXW_NO_ERROR;
}
/*
* Write a checkbox to a cell in Excel.
*/
lxw_error
worksheet_insert_checkbox(lxw_worksheet *self,
lxw_row_t row_num, lxw_col_t col_num,
int value)
{
lxw_cell *cell;
lxw_error err;
err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
if (err)
return err;
cell = _new_boolean_cell(row_num, col_num, value ? 1 : 0,
self->checkbox_format);
_insert_cell(self, row_num, col_num, cell);
self->has_checkboxes = LXW_TRUE;
return LXW_NO_ERROR;
}
/*
* Write a date and or time to a cell in Excel.
*/
@ -8334,9 +8683,9 @@ worksheet_write_datetime(lxw_worksheet *self,
if (err)
return err;
err = lxw_datetime_validate(datetime);
if (err)
return err;
printf("worksheet_write_datetime(): %d-%02d-%02d - 1904: %d\n",
datetime->year, datetime->month, datetime->day,
self->use_1904_epoch);
excel_date =
lxw_datetime_to_excel_date_with_epoch(datetime, self->use_1904_epoch);
@ -9556,6 +9905,103 @@ error:
}
/*
* Add a sparkline to the worksheet.
*/
lxw_error
worksheet_add_sparkline(lxw_worksheet *self, lxw_row_t row, lxw_col_t col,
lxw_sparkline_options *user_options)
{
lxw_sparkline *sparkline;
lxw_error err;
char location[LXW_MAX_CELL_NAME_LENGTH];
if (!user_options) {
LXW_WARN("worksheet_add_sparkline(): options parameter cannot be NULL");
return LXW_ERROR_NULL_PARAMETER_IGNORED;
}
if (!user_options->range || user_options->range[0] == '\0') {
LXW_WARN("worksheet_add_sparkline(): range parameter is required");
return LXW_ERROR_NULL_PARAMETER_IGNORED;
}
/* Check that row and column are valid. */
err = _check_dimensions(self, row, col, LXW_FALSE, LXW_FALSE);
if (err)
return err;
/* Create sparkline object. */
sparkline = calloc(1, sizeof(lxw_sparkline));
RETURN_ON_MEM_ERROR(sparkline, LXW_ERROR_MEMORY_MALLOC_FAILED);
/* Copy required range. */
sparkline->range = lxw_strdup(user_options->range);
if (!sparkline->range) {
free(sparkline);
return LXW_ERROR_MEMORY_MALLOC_FAILED;
}
/* Set location from row/col. */
lxw_rowcol_to_cell(location, row, col);
sparkline->location = lxw_strdup(location);
if (!sparkline->location) {
free(sparkline->range);
free(sparkline);
return LXW_ERROR_MEMORY_MALLOC_FAILED;
}
sparkline->row = row;
sparkline->col = col;
/* Copy options. */
sparkline->type = user_options->type;
sparkline->style = user_options->style;
sparkline->high_point = user_options->high_point;
sparkline->low_point = user_options->low_point;
sparkline->negative_points = user_options->negative_points;
sparkline->first_point = user_options->first_point;
sparkline->last_point = user_options->last_point;
sparkline->markers = user_options->markers;
sparkline->series_color = user_options->series_color;
sparkline->negative_color = user_options->negative_color;
sparkline->markers_color = user_options->markers_color;
sparkline->first_color = user_options->first_color;
sparkline->last_color = user_options->last_color;
sparkline->high_color = user_options->high_color;
sparkline->low_color = user_options->low_color;
sparkline->min = user_options->min;
sparkline->max = user_options->max;
sparkline->min_axis_type = user_options->min_axis_type;
sparkline->max_axis_type = user_options->max_axis_type;
sparkline->show_axis = user_options->show_axis;
sparkline->reverse = user_options->reverse;
sparkline->show_hidden = user_options->show_hidden;
sparkline->empty_cells = user_options->empty_cells;
sparkline->weight = user_options->weight;
if (sparkline->weight == 0.0)
sparkline->weight = 0.75; /* Default line weight */
if (user_options->date_axis) {
sparkline->date_axis = lxw_strdup(user_options->date_axis);
if (!sparkline->date_axis) {
free(sparkline->range);
free(sparkline->location);
free(sparkline);
return LXW_ERROR_MEMORY_MALLOC_FAILED;
}
}
STAILQ_INSERT_TAIL(self->sparklines, sparkline, list_pointers);
self->has_sparklines = LXW_TRUE;
return LXW_NO_ERROR;
}
/*
* Set this worksheet as a selected worksheet, i.e. the worksheet has its tab
* highlighted.
@ -9795,12 +10241,6 @@ worksheet_set_page_view(lxw_worksheet *self)
void
worksheet_set_paper(lxw_worksheet *self, uint8_t paper_size)
{
if (paper_size > 118) {
LXW_WARN_FORMAT1("worksheet_set_paper(): invalid paper size: %d. "
"Valid range is 0-118", paper_size);
return;
}
self->paper_size = paper_size;
self->page_setup_changed = LXW_TRUE;
}
@ -11139,6 +11579,79 @@ worksheet_insert_chart(lxw_worksheet *self,
return worksheet_insert_chart_opt(self, row_num, col_num, chart, NULL);
}
/*
* Insert a textbox into the worksheet, with options.
*/
lxw_error
worksheet_insert_textbox_opt(lxw_worksheet *self,
lxw_row_t row_num, lxw_col_t col_num,
const char *text,
lxw_textbox_options *user_options)
{
lxw_object_properties *object_props;
if (!text) {
LXW_WARN("worksheet_insert_textbox()/_opt(): "
"text must be specified.");
return LXW_ERROR_NULL_PARAMETER_IGNORED;
}
/* Create a new object to hold the textbox properties. */
object_props = calloc(1, sizeof(lxw_object_properties));
RETURN_ON_MEM_ERROR(object_props, LXW_ERROR_MEMORY_MALLOC_FAILED);
if (user_options) {
object_props->x_offset = user_options->x_offset;
object_props->y_offset = user_options->y_offset;
object_props->x_scale = user_options->x_scale;
object_props->y_scale = user_options->y_scale;
object_props->object_position = user_options->object_position;
object_props->description = lxw_strdup(user_options->description);
object_props->decorative = user_options->decorative;
if (user_options->width)
object_props->width = user_options->width;
if (user_options->height)
object_props->height = user_options->height;
}
/* Copy other options or set defaults. */
object_props->row = row_num;
object_props->col = col_num;
object_props->text = lxw_strdup(text);
object_props->is_textbox = LXW_TRUE;
/* Default textbox size is 192 x 120 pixels. */
if (object_props->width == 0)
object_props->width = 192;
if (object_props->height == 0)
object_props->height = 120;
if (object_props->x_scale == 0.0)
object_props->x_scale = 1;
if (object_props->y_scale == 0.0)
object_props->y_scale = 1;
STAILQ_INSERT_TAIL(self->textbox_data, object_props, list_pointers);
self->has_textboxes = LXW_TRUE;
return LXW_NO_ERROR;
}
/*
* Insert a textbox into the worksheet.
*/
lxw_error
worksheet_insert_textbox(lxw_worksheet *self,
lxw_row_t row_num, lxw_col_t col_num,
const char *text)
{
return worksheet_insert_textbox_opt(self, row_num, col_num, text, NULL);
}
/*
* Add a data validation to a worksheet, for a range. Ironically this requires
* a lot of validation of the user input.

View file

@ -0,0 +1,25 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* SPDX-License-Identifier: BSD-2-Clause
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org.
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("test_autofit01.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
/* Write a single character. */
worksheet_write_string(worksheet, 0, 0, "A", NULL);
/* Set column width (simulating what autofit would do after user override). */
worksheet_set_column(worksheet, 0, 0, 1.57143, NULL);
return workbook_close(workbook);
}

View file

@ -36,12 +36,12 @@ int main() {
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
, 0);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
, 0);
worksheet_insert_chart(worksheet, CELL("E9"), chart);

View file

@ -36,12 +36,12 @@ int main() {
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
, 0);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
, 0);
worksheet_insert_chart(worksheet, CELL("E9"), chart);

View file

@ -36,12 +36,12 @@ int main() {
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
, 0);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
, 0);
worksheet_insert_chart(worksheet, CELL("E9"), chart);

View file

@ -0,0 +1,40 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* SPDX-License-Identifier: BSD-2-Clause
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org.
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("test_chart_area04.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_AREA);
/* For testing, copy the randomly generated axis ids in the target file. */
chart->axis_id_1 = 63591168;
chart->axis_id_2 = 63592704;
chart->axis_id_3 = 74921856;
chart->axis_id_4 = 73764224;
uint8_t data1[5] = {1, 2, 3, 4, 5};
uint8_t data2[5] = {6, 8, 6, 4, 2};
int row;
for (row = 0; row < 5; row++) {
worksheet_write_number(worksheet, row, 0, data1[row], NULL);
worksheet_write_number(worksheet, row, 1, data2[row], NULL);
}
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 1);
worksheet_insert_chart(worksheet, CELL("E9"), chart);
return workbook_close(workbook);
}

View file

@ -36,12 +36,12 @@ int main() {
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
, 0);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
, 0);
lxw_chart_options chart_options = {.description = "Some alternative text"};
worksheet_insert_chart_opt(worksheet, CELL("E9"), chart, &chart_options);

View file

@ -36,12 +36,12 @@ int main() {
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
, 0);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
, 0);
lxw_chart_options chart_options = {.description = "Some alternative text", .decorative = LXW_TRUE};
worksheet_insert_chart_opt(worksheet, CELL("E9"), chart, &chart_options);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_name(chart->x_axis, "XXX");
chart_axis_set_name(chart->y_axis, "YYY");

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_name(chart->x_axis, "XXX");
chart_axis_set_name(chart->y_axis, "YYY");

View file

@ -36,12 +36,12 @@ int main() {
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
, 0);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
, 0);
chart_axis_set_name(chart->x_axis, "XXX");
chart_axis_set_name(chart->y_axis, "YYY");

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_name(chart->x_axis, "XXX");
chart_axis_set_name(chart->y_axis, "YYY");

View file

@ -30,7 +30,7 @@ int main() {
chart_add_series(chart,
"=Sheet1!$A$1:$A$3",
"=Sheet1!$B$1:$B$3"
);
, 0);
chart_title_set_name(chart, "Title");

View file

@ -36,12 +36,12 @@ int main() {
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
, 0);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
, 0);
chart_axis_set_name(chart->x_axis, "XXX");
chart_axis_set_name(chart->y_axis, "YYY");

View file

@ -36,12 +36,12 @@ int main() {
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
, 0);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
, 0);
chart_axis_set_reverse(chart->y_axis);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_reverse(chart->x_axis);

View file

@ -36,12 +36,12 @@ int main() {
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
, 0);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
, 0);
chart_axis_set_reverse(chart->x_axis);
chart_axis_set_reverse(chart->y_axis);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_min(chart->x_axis, 0);
chart_axis_set_max(chart->x_axis, 20);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_min(chart->y_axis, 0);
chart_axis_set_max(chart->y_axis, 16);

View file

@ -36,12 +36,12 @@ int main() {
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
, 0);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
, 0);
chart_axis_set_min(chart->x_axis, 0);
chart_axis_set_max(chart->x_axis, 6);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_major_unit(chart->y_axis, 2);
chart_axis_set_minor_unit(chart->y_axis, 0.4);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_log_base(chart->y_axis, 10);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
lxw_chart_series *series = chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
lxw_chart_series *series = chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_series_set_invert_if_negative(series);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_label_position(chart->x_axis, LXW_CHART_AXIS_LABEL_POSITION_HIGH);
chart_axis_set_label_position(chart->y_axis, LXW_CHART_AXIS_LABEL_POSITION_LOW);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_label_position(chart->x_axis, LXW_CHART_AXIS_LABEL_POSITION_NEXT_TO);
chart_axis_set_label_position(chart->y_axis, LXW_CHART_AXIS_LABEL_POSITION_NONE);

View file

@ -36,12 +36,12 @@ int main() {
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
, 0);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
, 0);
chart_axis_set_reverse(chart->x_axis);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_num_format(chart->x_axis, "#,##0.00");

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_num_format(chart->x_axis, "dd/mm/yyyy");
chart_axis_set_num_format(chart->y_axis, "0.00%");

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_num_format(chart->x_axis, "dd/mm/yyyy");
chart_axis_set_num_format(chart->y_axis, "0.00%");

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_num_format(chart->x_axis, "[$¥-411]#,##0.00");
chart_axis_set_num_format(chart->y_axis, "0.00%");

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
lxw_chart_font font1 = {.rotation = 45, .baseline = -1};
chart_axis_set_num_font(chart->x_axis, &font1);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
lxw_chart_font font1 = {.rotation = -35, .baseline = -1};
chart_axis_set_num_font(chart->x_axis, &font1);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
lxw_chart_font font1 = {.rotation = 90, .baseline = -1};
chart_axis_set_num_font(chart->x_axis, &font1);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
lxw_chart_font font1 = {.rotation = -90, .baseline = -1};
chart_axis_set_num_font(chart->x_axis, &font1);

View file

@ -35,9 +35,9 @@ int main() {
chart_axis_set_position(chart->x_axis, LXW_CHART_AXIS_POSITION_ON_TICK);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
worksheet_insert_chart(worksheet, CELL("E9"), chart);

View file

@ -35,9 +35,9 @@ int main() {
chart_axis_set_position(chart->y_axis, LXW_CHART_AXIS_POSITION_ON_TICK);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
worksheet_insert_chart(worksheet, CELL("E9"), chart);

View file

@ -35,9 +35,9 @@ int main() {
chart_axis_set_position(chart->x_axis, LXW_CHART_AXIS_POSITION_BETWEEN);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
worksheet_insert_chart(worksheet, CELL("E9"), chart);

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
lxw_chart_font font1 = {.rotation = -45, .baseline = -1};

View file

@ -33,9 +33,9 @@ int main() {
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5");
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5");
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5");
chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5", 0);
chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5", 0);
chart_axis_set_interval_unit(chart->x_axis, 2);

Some files were not shown because too many files have changed in this diff Show more