chart: add layout support

Feature: #477
This commit is contained in:
John McNamara 2025-03-28 00:33:17 +00:00
parent 61a3ceaf9a
commit 0bae4be666
22 changed files with 921 additions and 57 deletions

1
.indent.pro vendored
View file

@ -70,6 +70,7 @@
-T lxw_chart_gridline
-T lxw_chart_label_position
-T lxw_chart_label_separator
-T lxw_chart_layout
-T lxw_chart_legend
-T lxw_chart_legend_position
-T lxw_chart_line

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View file

@ -1232,6 +1232,66 @@ The following patterns #lxw_chart_pattern_type can be applied:
@note If a pattern fill is used on a chart object it overrides the solid fill
properties of the object.
@section chart_layout Chart Layout
Excel allows the user to positions chart objects like axis labels or the legend
is two ways. The first method is via standard positions such as top, bottom,
left and right. The `libxlsxwriter` library replicates this via enums such as
#lxw_chart_axis_label_position and #lxw_chart_legend_position and the associated
functions that use them.
The second method Excel supports is manual positioning of elements such as the
chart axis labels, the chart legend, the chart plot area and the chart title.
The `libxlsxwriter` library replicates this type of positioning via the
#lxw_chart_layout struct:
@code
lxw_chart_layout layout = {
.x = 0.13,
.y = 0.26,
.width = 0.73,
.height = 0.57,
};
@endcode
The layout units used by Excel are relative units expressed as a percentage of
the chart dimensions and are double values in the range `0.0 < x <= 1.0`. Excel
calculates these dimensions as shown below:
@image html chart_layout.png
With reference to the above figure the layout units are calculated as follows:
```text
x = a / W
y = b / H
```
These units are cumbersome and can vary depending on other elements in the chart
such as text lengths. However, these are the units that are required by Excel to
allow relative positioning. Some trial and error is generally required.
For the chart `chart_plotarea_set_layout()` and `chart_legend_set_layout()`
functions you can also set the width and height based on the following
calculation:
```text
width = w / W
height = h / H
```
For other text based objects the width and height are changed by the font
dimensions.
The chart functions that support #lxw_chart_layout are:
- `chart_title_set_layout()`
- `chart_legend_set_layout()`
- `chart_plotarea_set_layout()`
- `chart_axis_set_name_layout()`
@section ww_charts_limitations Chart Limitations
The following chart features aren't currently supported in libxlsxwriter but

View file

@ -737,6 +737,70 @@ typedef struct lxw_chart_font {
} lxw_chart_font;
/**
* @brief Struct to represent Excel chart element layout dimensions.
*
* Excel supports manual positioning of elements such as the chart axis labels,
* the chart legend, the chart plot area and the chart title. The
* `lxw_chart_layout` struct represents the layout dimension for these elements.
*
* The layout units used by Excel are relative units expressed as a percentage
* of the chart dimensions and are double values in the range `0.0 < x <= 1.0`.
* Excel calculates these dimensions as shown below:
*
* @image html chart_layout.png
*
* With reference to the above figure the layout units are calculated as
* follows:
*
* ```text
* x = a / W
* y = b / H
* ```
*
* These units are cumbersome and can vary depending on other elements in the
* chart such as text lengths. However, these are the units that are required by
* Excel to allow relative positioning. Some trial and error is generally
* required.
*
* For the chart `chart_plotarea_set_layout()` and `chart_legend_set_layout()`
* functions you can also set the width and height based on the following
* calculation:
*
* ```text
* width = w / W
* height = h / H
* ```
*
* For other text based objects the width and height are changed by the font
* dimensions.
*
* The chart functions that support `lxw_chart_layout` are:
*
* - `chart_title_set_layout()`
* - `chart_legend_set_layout()`
* - `chart_plotarea_set_layout()`
* - `chart_axis_set_name_layout()`
*
*/
typedef struct lxw_chart_layout {
/** The x offset in the range `0.0 < x <= 1.0` */
double x;
/** The y offset in the range `0.0 < y <= 1.0` */
double y;
/** The width of the plotarea or legend in the range `0.0 < x <= 1.0` */
double width;
/** The height of the plotarea or legend in the range `0.0 < x <= 1.0` */
double height;
uint8_t has_inner;
} lxw_chart_layout;
typedef struct lxw_chart_marker {
uint8_t type;
@ -751,6 +815,7 @@ typedef struct lxw_chart_legend {
lxw_chart_font *font;
uint8_t position;
lxw_chart_layout *layout;
} lxw_chart_legend;
@ -763,12 +828,14 @@ typedef struct lxw_chart_title {
uint8_t off;
uint8_t is_horizontal;
uint8_t ignore_cache;
uint8_t has_overlay;
/* We use a range to hold the title formula properties even though it
* will only have 1 point in order to re-use similar functions.*/
lxw_series_range *range;
struct lxw_series_data_point data_point;
lxw_chart_layout *layout;
} lxw_chart_title;
@ -861,6 +928,13 @@ enum lxw_chart_position {
LXW_CHART_AXIS_BOTTOM
};
enum lxw_chart_layout_type {
LXW_CHART_LAYOUT_TITLE,
LXW_CHART_LAYOUT_LEGEND,
LXW_CHART_LAYOUT_PLOTAREA,
LXW_CHART_LAYOUT_AXIS_NAME
};
/**
* @brief Type/amount of data series error bar.
*/
@ -1149,8 +1223,10 @@ typedef struct lxw_chart {
lxw_chart_line *chartarea_line;
lxw_chart_fill *chartarea_fill;
lxw_chart_pattern *chartarea_pattern;
lxw_chart_line *plotarea_line;
lxw_chart_fill *plotarea_fill;
lxw_chart_layout *plotarea_layout;
lxw_chart_pattern *plotarea_pattern;
uint8_t has_drop_lines;
@ -2503,6 +2579,18 @@ void chart_axis_set_name(lxw_chart_axis *axis, const char *name);
void chart_axis_set_name_range(lxw_chart_axis *axis, const char *sheetname,
lxw_row_t row, lxw_col_t col);
/**
* @brief Set the manual position of the chart axis name.
*
* @param axis A pointer to a chart #lxw_chart_axis object.
* @param layout A pointer to a chart #lxw_chart_layout struct.
*
* This function is used to simulate setting the manual position of a chart
* axis name. See @ref chart_layout for more information.
*/
void chart_axis_set_name_layout(lxw_chart_axis *axis,
lxw_chart_layout *layout);
/**
* @brief Set the font properties for a chart axis name.
*
@ -3279,6 +3367,25 @@ void chart_title_set_name_font(lxw_chart *chart, lxw_chart_font *font);
*/
void chart_title_off(lxw_chart *chart);
/**
* @brief Set the manual position of the chart title.
*
* @param chart Pointer to a lxw_chart instance to be configured.
* @param layout A pointer to a chart #lxw_chart_layout struct.
*
* This function is used to simulate setting the manual position of the chart
* title. See @ref chart_layout for more information.
*/
void chart_title_set_layout(lxw_chart *chart, lxw_chart_layout *layout);
/**
* @brief
*
* @param chart
* @param overlay
*/
void chart_title_set_overlay(lxw_chart *chart, uint8_t overlay);
/**
* @brief Set the position of the chart legend.
*
@ -3317,6 +3424,17 @@ void chart_title_off(lxw_chart *chart);
*/
void chart_legend_set_position(lxw_chart *chart, uint8_t position);
/**
* @brief Set the manual layout of the chart legend.
*
* @param chart Pointer to a lxw_chart instance to be configured.
* @param layout A pointer to a chart #lxw_chart_layout struct.
*
* This function is used to simulate setting the manual position of the chart
* legend. See @ref chart_layout for more information.
*/
void chart_legend_set_layout(lxw_chart *chart, lxw_chart_layout *layout);
/**
* @brief Set the font properties for a chart legend.
*
@ -3484,6 +3602,17 @@ 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 manual layout of the chart plotarea.
*
* @param chart Pointer to a lxw_chart instance to be configured.
* @param layout A pointer to a chart #lxw_chart_layout struct.
*
* This function is used to simulate setting the manual position of the chart
* plotarea. See @ref chart_layout for more information.
*/
void chart_plotarea_set_layout(lxw_chart *chart, lxw_chart_layout *layout);
/**
* @brief Set the chart style type.
*

View file

@ -103,6 +103,29 @@ _chart_free_data_labels(lxw_chart_series *series)
free(series->data_labels);
}
STATIC void
_chart_free_axis(lxw_chart_axis *axis)
{
if (!axis)
return;
_chart_free_font(axis->num_font);
_chart_free_font(axis->title.font);
_chart_free_range(axis->title.range);
free(axis->fill);
free(axis->line);
free(axis->pattern);
free(axis->title.name);
free(axis->title.layout);
free(axis->num_format);
free(axis->default_num_format);
free(axis->major_gridlines.line);
free(axis->minor_gridlines.line);
free(axis);
}
/*
* Free a series object.
*/
@ -189,54 +212,30 @@ lxw_chart_free(lxw_chart *chart)
free(chart->series_list);
}
/* X Axis. */
if (chart->x_axis) {
_chart_free_font(chart->x_axis->title.font);
_chart_free_font(chart->x_axis->num_font);
_chart_free_range(chart->x_axis->title.range);
free(chart->x_axis->title.name);
free(chart->x_axis->line);
free(chart->x_axis->fill);
free(chart->x_axis->pattern);
free(chart->x_axis->major_gridlines.line);
free(chart->x_axis->minor_gridlines.line);
free(chart->x_axis->num_format);
free(chart->x_axis->default_num_format);
free(chart->x_axis);
}
/* Y Axis. */
if (chart->y_axis) {
_chart_free_font(chart->y_axis->title.font);
_chart_free_font(chart->y_axis->num_font);
_chart_free_range(chart->y_axis->title.range);
free(chart->y_axis->title.name);
free(chart->y_axis->line);
free(chart->y_axis->fill);
free(chart->y_axis->pattern);
free(chart->y_axis->major_gridlines.line);
free(chart->y_axis->minor_gridlines.line);
free(chart->y_axis->num_format);
free(chart->y_axis->default_num_format);
free(chart->y_axis);
}
/* X and Y Axis. */
_chart_free_axis(chart->x_axis);
_chart_free_axis(chart->y_axis);
/* Chart title. */
_chart_free_font(chart->title.font);
_chart_free_range(chart->title.range);
free(chart->title.name);
free(chart->title.layout);
/* Chart legend. */
_chart_free_font(chart->legend.font);
free(chart->delete_series);
free(chart->legend.layout);
free(chart->delete_series);
free(chart->default_marker);
free(chart->chartarea_line);
free(chart->chartarea_fill);
free(chart->chartarea_pattern);
free(chart->plotarea_line);
free(chart->plotarea_fill);
free(chart->plotarea_layout);
free(chart->plotarea_pattern);
free(chart->drop_lines_line);
@ -449,6 +448,44 @@ _chart_convert_pattern_args(lxw_chart_pattern *user_pattern)
return pattern;
}
/*
* Create a copy of a user supplied layout.
*/
STATIC lxw_chart_layout *
_chart_convert_layout_args(lxw_chart_layout *user_layout,
enum lxw_chart_layout_type type)
{
lxw_chart_layout *layout = calloc(1, sizeof(struct lxw_chart_layout));
RETURN_ON_MEM_ERROR(layout, NULL);
/* Copy the user supplied properties. */
switch (type) {
case LXW_CHART_LAYOUT_LEGEND:
layout->x = user_layout->x;
layout->y = user_layout->y;
layout->width = user_layout->width;
layout->height = user_layout->height;
layout->has_inner = LXW_FALSE;
break;
case LXW_CHART_LAYOUT_PLOTAREA:
layout->x = user_layout->x;
layout->y = user_layout->y;
layout->width = user_layout->width;
layout->height = user_layout->height;
layout->has_inner = LXW_TRUE;
break;
default:
layout->x = user_layout->x;
layout->y = user_layout->y;
layout->width = 0.0;
layout->height = 0.0;
layout->has_inner = LXW_FALSE;
break;
}
return layout;
}
/*
* Set a marker type for a series.
*/
@ -645,13 +682,101 @@ _chart_write_style(lxw_chart *self)
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <c:layoutTarget> element.
*/
STATIC void
_chart_write_layout_target(lxw_chart *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("val", "inner");
lxw_xml_empty_tag(self->file, "c:layoutTarget", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <c:xMode> and <c:yMode> element.
*/
STATIC void
_chart_write_layout_mode(lxw_chart *self, char *mode)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("val", "edge");
lxw_xml_empty_tag(self->file, mode, &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the layout dimension elements.
*/
STATIC void
_chart_write_layout_dimension(lxw_chart *self, char *dimension, double value)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_DBL("val", value);
lxw_xml_empty_tag(self->file, dimension, &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <c:manualLayout> element.
*/
STATIC void
_chart_write_manual_layout(lxw_chart *self, lxw_chart_layout *layout)
{
lxw_xml_start_tag(self->file, "c:manualLayout", NULL);
/* Write the c:layoutTarget element. */
if (layout->has_inner)
_chart_write_layout_target(self);
/* Write the c:xMode and c:yMode elements. */
_chart_write_layout_mode(self, "c:xMode");
_chart_write_layout_mode(self, "c:yMode");
/* Write the dimension elements. */
_chart_write_layout_dimension(self, "c:x", layout->x);
_chart_write_layout_dimension(self, "c:y", layout->y);
if (layout->width > 0.0)
_chart_write_layout_dimension(self, "c:w", layout->width);
if (layout->height > 0.0)
_chart_write_layout_dimension(self, "c:h", layout->height);
lxw_xml_end_tag(self->file, "c:manualLayout");
}
/*
* Write the <c:layout> element.
*/
STATIC void
_chart_write_layout(lxw_chart *self)
_chart_write_layout(lxw_chart *self, lxw_chart_layout *layout)
{
if (layout == NULL) {
lxw_xml_empty_tag(self->file, "c:layout", NULL);
}
else {
lxw_xml_start_tag(self->file, "c:layout", NULL);
/* Write the c:manualLayout element. */
_chart_write_manual_layout(self, layout);
lxw_xml_end_tag(self->file, "c:layout");
}
}
/*
@ -1525,6 +1650,23 @@ _chart_write_tx_rich(lxw_chart *self, char *name, uint8_t is_horizontal,
lxw_xml_end_tag(self->file, "c:tx");
}
/*
* Write the <c:overlay> element.
*/
STATIC void
_chart_write_overlay(lxw_chart *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("val", "1");
lxw_xml_empty_tag(self->file, "c:overlay", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <c:title> element for rich strings.
*/
@ -1538,7 +1680,11 @@ _chart_write_title_rich(lxw_chart *self, lxw_chart_title *title)
title->font);
/* Write the c:layout element. */
_chart_write_layout(self);
_chart_write_layout(self, title->layout);
/* Write the c:overlay element. */
if (title->has_overlay)
_chart_write_overlay(self);
lxw_xml_end_tag(self->file, "c:title");
}
@ -1555,7 +1701,11 @@ _chart_write_title_formula(lxw_chart *self, lxw_chart_title *title)
_chart_write_tx_formula(self, title);
/* Write the c:layout element. */
_chart_write_layout(self);
_chart_write_layout(self, title->layout);
/* Write the c:overlay element. */
if (title->has_overlay)
_chart_write_overlay(self);
/* Write the c:txPr element. */
_chart_write_tx_pr(self, title->is_horizontal, title->font);
@ -3693,23 +3843,6 @@ _chart_write_cross_between(lxw_chart *self, uint8_t position)
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <c:overlay> element.
*/
STATIC void
_chart_write_overlay(lxw_chart *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("val", "1");
lxw_xml_empty_tag(self->file, "c:overlay", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <c:legendPos> element.
*/
@ -3796,7 +3929,7 @@ _chart_write_legend(lxw_chart *self)
}
/* Write the c:layout element. */
_chart_write_layout(self);
_chart_write_layout(self, self->legend.layout);
if (self->chart_group == LXW_CHART_PIE
|| self->chart_group == LXW_CHART_DOUGHNUT) {
@ -4737,7 +4870,7 @@ _chart_write_scatter_plot_area(lxw_chart *self)
lxw_xml_start_tag(self->file, "c:plotArea", NULL);
/* Write the c:layout element. */
_chart_write_layout(self);
_chart_write_layout(self, self->plotarea_layout);
/* Write subclass chart type elements for primary and secondary axes. */
self->write_chart_type(self);
@ -4769,7 +4902,7 @@ _chart_write_pie_plot_area(lxw_chart *self)
lxw_xml_start_tag(self->file, "c:plotArea", NULL);
/* Write the c:layout element. */
_chart_write_layout(self);
_chart_write_layout(self, self->plotarea_layout);
/* Write subclass chart type elements for primary and secondary axes. */
self->write_chart_type(self);
@ -4790,7 +4923,7 @@ _chart_write_plot_area(lxw_chart *self)
lxw_xml_start_tag(self->file, "c:plotArea", NULL);
/* Write the c:layout element. */
_chart_write_layout(self);
_chart_write_layout(self, self->plotarea_layout);
/* Write subclass chart type elements for primary and secondary axes. */
self->write_chart_type(self);
@ -6043,6 +6176,22 @@ chart_axis_set_name_range(lxw_chart_axis *axis, const char *sheetname,
_chart_set_range(axis->title.range, sheetname, row, col, row, col);
}
/*
* Set a layout for the chart axis name.
*/
void
chart_axis_set_name_layout(lxw_chart_axis *axis, lxw_chart_layout *layout)
{
if (!layout)
return;
/* Free any previously allocated resource. */
free(axis->title.layout);
axis->title.layout =
_chart_convert_layout_args(layout, LXW_CHART_LAYOUT_AXIS_NAME);
}
/*
* Set an axis title/name font.
*/
@ -6444,6 +6593,31 @@ chart_title_off(lxw_chart *self)
self->title.off = LXW_TRUE;
}
/*
* Set a layout for the chart title.
*/
void
chart_title_set_layout(lxw_chart *self, lxw_chart_layout *layout)
{
if (!layout)
return;
/* Free any previously allocated resource. */
free(self->title.layout);
self->title.layout =
_chart_convert_layout_args(layout, LXW_CHART_LAYOUT_TITLE);
}
/*
* Overlay the chart title on the chart.
*/
void
chart_title_set_overlay(lxw_chart *self, uint8_t overlay)
{
self->title.has_overlay = overlay;
}
/*
* Set the chart legend position.
*/
@ -6453,6 +6627,22 @@ chart_legend_set_position(lxw_chart *self, uint8_t position)
self->legend.position = position;
}
/*
* Set a layout for the chart legend.
*/
void
chart_legend_set_layout(lxw_chart *self, lxw_chart_layout *layout)
{
if (!layout)
return;
/* Free any previously allocated resource. */
free(self->legend.layout);
self->legend.layout =
_chart_convert_layout_args(layout, LXW_CHART_LAYOUT_LEGEND);
}
/*
* Set the legend font.
*/
@ -6584,6 +6774,23 @@ chart_plotarea_set_pattern(lxw_chart *self, lxw_chart_pattern *pattern)
self->plotarea_pattern = _chart_convert_pattern_args(pattern);
}
/*
* Set a layout for the plotarea.
*/
void
chart_plotarea_set_layout(lxw_chart *self, lxw_chart_layout *layout)
{
if (!layout)
return;
/* Free any previously allocated resource. */
free(self->plotarea_layout);
self->plotarea_layout =
_chart_convert_layout_args(layout, LXW_CHART_LAYOUT_PLOTAREA);
}
/*
* Turn on the chart data table.
*/

View file

@ -0,0 +1,51 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("test_chart_layout01.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* For testing, copy the randomly generated axis ids in the target file. */
chart->axis_id_1 = 69198592;
chart->axis_id_2 = 69200128;
uint8_t data[5][3] = {
{1, 2, 3},
{2, 4, 6},
{3, 6, 9},
{4, 8, 12},
{5, 10, 15}
};
int row, col;
for (row = 0; row < 5; row++)
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");
lxw_chart_layout layout = {
.x = 0.13171062992125,
.y = 0.26436351706036,
.width = 0.73970734908136,
.height = 0.5713732137649,
};
chart_plotarea_set_layout(chart, &layout);
worksheet_insert_chart(worksheet, CELL("E9"), chart);
return workbook_close(workbook);
}

View file

@ -0,0 +1,51 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("test_chart_layout02.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* For testing, copy the randomly generated axis ids in the target file. */
chart->axis_id_1 = 68311296;
chart->axis_id_2 = 69198208;
uint8_t data[5][3] = {
{1, 2, 3},
{2, 4, 6},
{3, 6, 9},
{4, 8, 12},
{5, 10, 15}
};
int row, col;
for (row = 0; row < 5; row++)
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");
lxw_chart_layout layout = {
.x = 0.80197353455818,
.y = 0.37442403032954,
.width = 0.12858202099737,
.height = 0.25115157480314,
};
chart_legend_set_layout(chart, &layout);
worksheet_insert_chart(worksheet, CELL("E9"), chart);
return workbook_close(workbook);
}

View file

@ -0,0 +1,52 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("test_chart_layout03.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* For testing, copy the randomly generated axis ids in the target file. */
chart->axis_id_1 = 68312064;
chart->axis_id_2 = 69198592;
uint8_t data[5][3] = {
{1, 2, 3},
{2, 4, 6},
{3, 6, 9},
{4, 8, 12},
{5, 10, 15}
};
int row, col;
for (row = 0; row < 5; row++)
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");
lxw_chart_layout layout = {
.x = 0.80197353455818,
.y = 0.37442403032954,
.width = 0.12858202099737,
.height = 0.25115157480314,
};
chart_legend_set_layout(chart, &layout);
chart_legend_set_position(chart, LXW_CHART_LEGEND_OVERLAY_RIGHT);
worksheet_insert_chart(worksheet, CELL("E9"), chart);
return workbook_close(workbook);
}

View file

@ -0,0 +1,52 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("test_chart_layout04.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* For testing, copy the randomly generated axis ids in the target file. */
chart->axis_id_1 = 68311296;
chart->axis_id_2 = 69198208;
uint8_t data[5][3] = {
{1, 2, 3},
{2, 4, 6},
{3, 6, 9},
{4, 8, 12},
{5, 10, 15}
};
int row, col;
for (row = 0; row < 5; row++)
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");
lxw_chart_layout layout = {
.x = 0.426319335083114,
.y = 0.143518518518518,
};
chart_title_set_layout(chart, &layout);
chart_title_set_name(chart, "Title");
worksheet_insert_chart(worksheet, CELL("E9"), chart);
return workbook_close(workbook);
}

View file

@ -0,0 +1,68 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("test_chart_layout05.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 = 43495808;
chart->axis_id_2 = 43497728;
uint8_t data[5][3] = {
{1, 8, 3},
{2, 7, 6},
{3, 6, 9},
{4, 8, 12},
{5, 10, 15}
};
int row, col;
for (row = 0; row < 5; row++)
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
lxw_chart_layout layout_x = {
.x = 0.346203193350831,
.y = 0.850902595508894,
};
chart_axis_set_name(chart->x_axis, "XXX");
chart_axis_set_name_layout(chart->x_axis, &layout_x);
lxw_chart_layout layout_y = {
.x = 0.213888888888888,
.y = 0.263499198016914,
};
chart_axis_set_name(chart->y_axis, "YYY");
chart_axis_set_name_layout(chart->y_axis, &layout_y);
worksheet_insert_chart(worksheet, CELL("E9"), chart);
return workbook_close(workbook);
}

View file

@ -0,0 +1,52 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("test_chart_layout06.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* For testing, copy the randomly generated axis ids in the target file. */
chart->axis_id_1 = 43496576;
chart->axis_id_2 = 45486080;
uint8_t data[5][3] = {
{1, 2, 3},
{2, 4, 6},
{3, 6, 9},
{4, 8, 12},
{5, 10, 15}
};
int row, col;
for (row = 0; row < 5; row++)
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");
lxw_chart_layout layout = {
.x = 0.42354155730533,
.y = 0.16203703703703,
};
chart_title_set_name(chart, "Title");
chart_title_set_layout(chart, &layout);
chart_title_set_overlay(chart, LXW_TRUE);
worksheet_insert_chart(worksheet, CELL("E9"), chart);
return workbook_close(workbook);
}

View file

@ -0,0 +1,50 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("test_chart_layout07.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* For testing, copy the randomly generated axis ids in the target file. */
chart->axis_id_1 = 45861120;
chart->axis_id_2 = 45867008;
uint8_t data[5][3] = {
{1, 2, 3},
{2, 4, 6},
{3, 6, 9},
{4, 8, 12},
{5, 10, 15}
};
int row, col;
for (row = 0; row < 5; row++)
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");
lxw_chart_layout layout = {
.x = 0.359652668416448,
.y = 0.1388888888888889,
};
chart_title_set_name_range(chart, "Sheet1", 0, 0);
chart_title_set_layout(chart, &layout);
worksheet_insert_chart(worksheet, CELL("E9"), chart);
return workbook_close(workbook);
}

View file

@ -0,0 +1,51 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = workbook_new("test_chart_layout08.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_COLUMN);
/* For testing, copy the randomly generated axis ids in the target file. */
chart->axis_id_1 = 46317568;
chart->axis_id_2 = 46319488;
uint8_t data[5][3] = {
{1, 2, 3},
{2, 4, 6},
{3, 6, 9},
{4, 8, 12},
{5, 10, 15}
};
int row, col;
for (row = 0; row < 5; row++)
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");
lxw_chart_layout layout = {
.x = 0.359652668416448,
.y = 0.162037037037037,
};
chart_title_set_name_range(chart, "Sheet1", 0, 0);
chart_title_set_layout(chart, &layout);
chart_title_set_overlay(chart, LXW_TRUE);
worksheet_insert_chart(worksheet, CELL("E9"), chart);
return workbook_close(workbook);
}

View file

@ -0,0 +1,40 @@
###############################################################################
#
# Tests for libxlsxwriter.
#
# SPDX-License-Identifier: BSD-2-Clause
# Copyright 2014-2025, John McNamara, jmcnamara@cpan.org.
#
import base_test_class
class TestCompareXLSXFiles(base_test_class.XLSXBaseTest):
"""
Test file created with libxlsxwriter against a file created by Excel.
"""
def test_chart_layout01(self):
self.run_exe_test('test_chart_layout01')
def test_chart_layout02(self):
self.run_exe_test('test_chart_layout02')
def test_chart_layout03(self):
self.run_exe_test('test_chart_layout03')
def test_chart_layout04(self):
self.run_exe_test('test_chart_layout04')
def test_chart_layout05(self):
self.run_exe_test('test_chart_layout05')
def test_chart_layout06(self):
self.run_exe_test('test_chart_layout06')
def test_chart_layout07(self):
self.run_exe_test('test_chart_layout07')
def test_chart_layout08(self):
self.run_exe_test('test_chart_layout08')

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.