Added support for headers and footers.

This commit is contained in:
John McNamara 2015-04-09 02:16:02 +01:00
parent dcd2f60189
commit 0bc6eeb66a
8 changed files with 605 additions and 1 deletions

View file

@ -25,6 +25,7 @@ my @examples = (
[ 'utf8.c', 'A example of some UTF-8 text' ],
[ 'constant_memory.c', 'Write a large file with constant memory usage' ],
[ 'merge1.c', 'Create a merged range of cells' ],
[ 'headers_footers.c', 'Example of adding worksheet headers/footers' ],
[ 'defined_name.c', 'Example of how to create defined names' ],
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View file

@ -104,10 +104,17 @@ Next example: @ref merge1.c
@example merge1.c
Example of merging cells in a worksheet.
Next example: @ref defined_name.c
Next example: @ref headers_footers.c
@image html merge1.png
@example headers_footers.c
Example of adding worksheet headers and footers to worksheets.
Next example: @ref defined_name.c
@image html headers_footers.png
@example defined_name.c
Example of how to create defined names (named ranges) using libxlsxwriter.

101
examples/headers_footers.c Normal file
View file

@ -0,0 +1,101 @@
/*
* This program shows several examples of how to set up headers and
* footers with libxlsxwriter.
*
* The control characters used in the header/footer strings are:
*
* Control Category Description
* ======= ======== ===========
* &L Justification Left
* &C Center
* &R Right
* &P Information Page number
* &N Total number of pages
* &D Date
* &T Time
* &F File name
* &A Worksheet name
* &fontsize Font Font size
* &"font,style" Font name and style
* &U Single underline
* &E Double underline
* &S Strikethrough
* &X Superscript
* &Y Subscript
* &[Picture] Images Image placeholder
* &G Same as &[Picture]
* && Miscellaneous Literal ampersand &
*
* Copyright 2014-2015, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = new_workbook("headers_footers.xlsx");
char preview[] = "Select Print Preview to see the header and footer";
/* A simple example to start */
lxw_worksheet *worksheet1 = workbook_add_worksheet(workbook, "Simple");
char header1[] = "&CHere is some centred text.";
char footer1[] = "&LHere is some left aligned text.";
worksheet_set_header(worksheet1, header1);
worksheet_set_footer(worksheet1, footer1);
worksheet_set_column(worksheet1, 0, 0, 50, NULL, NULL);
worksheet_write_string(worksheet1, 0, 0, preview, NULL);
/* This is an example of some of the header/footer variables. */
lxw_worksheet *worksheet2 = workbook_add_worksheet(workbook, "Variables");
char header2[] = "&LPage &P of &N" "&CFilename: &F" "&RSheetname: &A";
char footer2[] = "&LCurrent date: &D" "&RCurrent time: &T";
worksheet_set_header(worksheet2, header2);
worksheet_set_footer(worksheet2, footer2);
worksheet_set_column(worksheet2, 0, 0, 50, NULL, NULL);
worksheet_write_string(worksheet2, 0, 0, preview, NULL);
/* This example shows how to use more than one font. */
lxw_worksheet *worksheet3 = workbook_add_worksheet(workbook, "Mixed fonts");
char header3[] = "&C&\"Courier New,Bold\"Hello &\"Arial,Italic\"World";
char footer3[] = "&C&\"Symbol\"e&\"Arial\" = mc&X2";
worksheet_set_header(worksheet3, header3);
worksheet_set_footer(worksheet3, footer3);
worksheet_set_column(worksheet3, 0, 0, 50, NULL, NULL);
worksheet_write_string(worksheet3, 0, 0, preview, NULL);
/*Example of line wrapping. */
lxw_worksheet *worksheet4 = workbook_add_worksheet(workbook, "Word wrap");
char header4[] = "&CHeading 1\nHeading 2";
worksheet_set_header(worksheet4, header4);
worksheet_set_column(worksheet4, 0, 0, 50, NULL, NULL);
worksheet_write_string(worksheet4, 0, 0, preview, NULL);
/* Example of inserting a literal ampersand & */
lxw_worksheet *worksheet5 = workbook_add_worksheet(workbook, "Ampersand");
char header5[] = "&CCuriouser && Curiouser - Attorneys at Law";
worksheet_set_header(worksheet5, header5);
worksheet_set_column(worksheet5, 0, 0, 50, NULL, NULL);
worksheet_write_string(worksheet5, 0, 0, preview, NULL);
workbook_close(workbook);
return 0;
}

View file

@ -54,6 +54,7 @@
#include "utility.h"
#define LXW_COL_META_MAX 128
#define LXW_HEADER_FOOTER_MAX 255
/** Default column width in Excel */
#define LXW_DEF_COL_WIDTH 8.43
@ -134,6 +135,18 @@ typedef struct lxw_merged_range {
STAILQ_ENTRY (lxw_merged_range) list_pointers;
} lxw_merged_range;
/**
* @brief Header and footer options.
*
* Optional parameters used in the worksheet_set_header_opt() and
* worksheet_set_footer_opt() functions.
*
*/
typedef struct lxw_header_footer_options {
/** Header or footer margin in inches. Excel default is 0.3. */
double margin;
} lxw_header_footer_options;
/**
* @brief Struct to represent an Excel worksheet.
*
@ -195,6 +208,10 @@ typedef struct lxw_worksheet {
double margin_header;
double margin_footer;
uint8_t header_footer_changed;
char header[LXW_HEADER_FOOTER_MAX];
char footer[LXW_HEADER_FOOTER_MAX];
uint16_t merged_range_count;
STAILQ_ENTRY (lxw_worksheet) list_pointers;
@ -938,6 +955,242 @@ void worksheet_set_paper(lxw_worksheet *worksheet, uint8_t paper_type);
void worksheet_set_margins(lxw_worksheet *worksheet, double left,
double right, double top, double bottom);
/**
* @brief Set the printed page header caption.
*
* @param worksheet Pointer to a lxw_worksheet instance to be updated.
* @param string The header string.
*
* @return 0 for success, non-zero on error.
*
* Headers and footers are generated using a string which is a combination of
* plain text and control characters.
*
* The available control character are:
*
*
* | Control | Category | Description |
* | --------------- | ------------- | --------------------- |
* | `&L` | Justification | Left |
* | `&C` | | Center |
* | `&R` | | Right |
* | `&P` | Information | Page number |
* | `&N` | | Total number of pages |
* | `&D` | | Date |
* | `&T` | | Time |
* | `&F` | | File name |
* | `&A` | | Worksheet name |
* | `&Z` | | Workbook path |
* | `&fontsize` | Font | Font size |
* | `&"font,style"` | | Font name and style |
* | `&U` | | Single underline |
* | `&E` | | Double underline |
* | `&S` | | Strikethrough |
* | `&X` | | Superscript |
* | `&Y` | | Subscript |
*
*
* Text in headers and footers can be justified (aligned) to the left, center
* and right by prefixing the text with the control characters `&L`, `&C` and
* `&R`.
*
* For example (with ASCII art representation of the results):
*
* @code
* worksheet_set_header(worksheet, "&LHello");
*
* ---------------------------------------------------------------
* | |
* | Hello |
* | |
*
*
* worksheet_set_header(worksheet, "&CHello");
*
* ---------------------------------------------------------------
* | |
* | Hello |
* | |
*
*
* worksheet_set_header(worksheet, "&RHello");
*
* ---------------------------------------------------------------
* | |
* | Hello |
* | |
*
*
* @endcode
*
* For simple text, if you do not specify any justification the text will be
* centred. However, you must prefix the text with `&C` if you specify a font
* name or any other formatting:
*
* @code
* worksheet_set_header(worksheet, "Hello");
*
* ---------------------------------------------------------------
* | |
* | Hello |
* | |
*
* @endcode
*
* You can have text in each of the justification regions:
*
* @code
* worksheet_set_header(worksheet, "&LCiao&CBello&RCielo");
*
* ---------------------------------------------------------------
* | |
* | Ciao Bello Cielo |
* | |
*
* @endcode
*
* The information control characters act as variables that Excel will update
* as the workbook or worksheet changes. Times and dates are in the users
* default format:
*
* @code
* worksheet_set_header(worksheet, "&CPage &P of &N");
*
* ---------------------------------------------------------------
* | |
* | Page 1 of 6 |
* | |
*
* worksheet_set_header(worksheet, "&CUpdated at &T");
*
* ---------------------------------------------------------------
* | |
* | Updated at 12:30 PM |
* | |
*
* @endcode
*
* You can specify the font size of a section of the text by prefixing it with
* the control character `&n` where `n` is the font size:
*
* @code
* worksheet_set_header(worksheet1, "&C&30Hello Big");
* worksheet_set_header(worksheet2, "&C&10Hello Small");
*
* @endcode
*
* You can specify the font of a section of the text by prefixing it with the
* control sequence `&"font,style"` where `fontname` is a font name such as
* Windows font descriptions: "Regular", "Italic", "Bold" or "Bold Italic":
* "Courier New" or "Times New Roman" and `style` is one of the standard
*
* @code
* worksheet_set_header(worksheet1, "&C&\"Courier New,Italic\"Hello");
* worksheet_set_header(worksheet2, "&C&\"Courier New,Bold Italic\"Hello");
* worksheet_set_header(worksheet3, "&C&\"Times New Roman,Regular\"Hello");
*
* @endcode
*
* It is possible to combine all of these features together to create
* sophisticated headers and footers. As an aid to setting up complicated
* headers and footers you can record a page set-up as a macro in Excel and
* look at the format strings that VBA produces. Remember however that VBA
* uses two double quotes `""` to indicate a single double quote. For the last
* example above the equivalent VBA code looks like this:
*
* @code
* .LeftHeader = ""
* .CenterHeader = "&""Times New Roman,Regular""Hello"
* .RightHeader = ""
*
* @endcode
*
* Alternatively you can inspect the header and footer strings in an Excel
* file by unzipping it and grepping the XML sub-files. The following shows
* how to do that using libxml's xmllint to format the XML for clarity:
*
* @code
*
* $ unzip myfile.xlsm -d myfile
* $ xmllint --format `find myfile -name "*.xml" | xargs` \
* | egrep "Header|Footer"
*
* <headerFooter scaleWithDoc="0">
* <oddHeader>&amp;L&amp;P</oddHeader>
* </headerFooter>
*
* @endcode
*
* Note that in this case you need to unescape the Html. In the above example
* the header string would be `&L&P`.
*
* To include a single literal ampersand `&` in a header or footer you should
* use a double ampersand `&&`:
*
* @code
* worksheet_set_header(worksheet, "&CCuriouser && Curiouser - Attorneys at Law");
* @endcode
*
* Note, the header or footer string must be less than 255 characters. Strings
* longer than this will not be written.
*
*/
uint8_t worksheet_set_header(lxw_worksheet *worksheet, char *string);
/**
* @brief Set the printed page footer caption.
*
* @param worksheet Pointer to a lxw_worksheet instance to be updated.
* @param string The footer string.
*
* @return 0 for success, non-zero on error.
*
* The syntax of this function is the same as worksheet_set_header().
*
*/
uint8_t worksheet_set_footer(lxw_worksheet *worksheet, char *string);
/**
* @brief Set the printed page header caption with additional options.
*
* @param worksheet Pointer to a lxw_worksheet instance to be updated.
* @param string The header string.
* @param options Header options.
*
* @return 0 for success, non-zero on error.
*
* The syntax of this function is the same as worksheet_set_header() with an
* additional parameter to specify options for the header.
*
* Currently, the only available option is the header margin:
*
* @code
*
* lxw_header_footer_options header_options = { 0.2 };
*
* worksheet_set_header_opt(worksheet, "Some text", &header_options);
*
* @endcode
*
*/
uint8_t worksheet_set_header_opt(lxw_worksheet *worksheet, char *string,
lxw_header_footer_options * options);
/**
* @brief Set the printed page footer caption with additional options.
*
* @param worksheet Pointer to a lxw_worksheet instance to be updated.
* @param string The footer string.
* @param options Footer options.
*
* @return 0 for success, non-zero on error.
*
* The syntax of this function is the same as worksheet_set_header_opt().
*
*/
uint8_t worksheet_set_footer_opt(lxw_worksheet *worksheet, char *string,
lxw_header_footer_options * options);
/**
* @brief Set the order in which pages are printed.
*
@ -991,6 +1244,10 @@ STATIC void _write_merge_cell(lxw_worksheet *worksheet,
lxw_merged_range * merged_range);
STATIC void _write_merge_cells(lxw_worksheet *worksheet);
STATIC void _worksheet_write_odd_header(lxw_worksheet *worksheet);
STATIC void _worksheet_write_odd_footer(lxw_worksheet *worksheet);
STATIC void _worksheet_write_header_footer(lxw_worksheet *worksheet);
#endif /* TESTING */
/* *INDENT-OFF* */

View file

@ -1235,6 +1235,44 @@ _write_merge_cells(lxw_worksheet *self)
}
}
/*
* Write the <oddHeader> element.
*/
STATIC void
_worksheet_write_odd_header(lxw_worksheet *self)
{
_xml_data_element(self->file, "oddHeader", self->header, NULL);
}
/*
* Write the <oddFooter> element.
*/
STATIC void
_worksheet_write_odd_footer(lxw_worksheet *self)
{
_xml_data_element(self->file, "oddFooter", self->footer, NULL);
}
/*
* Write the <headerFooter> element.
*/
STATIC void
_worksheet_write_header_footer(lxw_worksheet *self)
{
if (!self->header_footer_changed)
return;
_xml_start_tag(self->file, "headerFooter", NULL);
if (self->header[0] != '\0')
_worksheet_write_odd_header(self);
if (self->footer[0] != '\0')
_worksheet_write_odd_footer(self);
_xml_end_tag(self->file, "headerFooter");
}
/*
* Check that row and col are within the allowed Excel range and store max
* and min values for use in other methods/elements.
@ -1316,6 +1354,9 @@ _worksheet_assemble_xml_file(lxw_worksheet *self)
/* Write the worksheet page setup. */
_worksheet_write_page_setup(self);
/* Write the headerFooter element. */
_worksheet_write_header_footer(self);
/* Close the worksheet tag. */
_xml_end_tag(self->file, "worksheet");
}
@ -1830,3 +1871,69 @@ worksheet_set_margins(lxw_worksheet *self, double left, double right,
if (bottom >= 0)
self->margin_bottom = bottom;
}
/*
* Set the page header caption and options.
*/
uint8_t
worksheet_set_header_opt(lxw_worksheet *self, char *string,
lxw_header_footer_options * options)
{
if (options) {
if (options->margin > 0)
self->margin_header = options->margin;
}
if (!string)
return 1;
if (strlen(string) >= LXW_HEADER_FOOTER_MAX)
return 1;
strcpy(self->header, string);
self->header_footer_changed = 1;
return 0;
}
/*
* Set the page footer caption and options.
*/
uint8_t
worksheet_set_footer_opt(lxw_worksheet *self, char *string,
lxw_header_footer_options * options)
{
if (options) {
if (options->margin > 0)
self->margin_footer = options->margin;
}
if (!string)
return 1;
if (strlen(string) >= LXW_HEADER_FOOTER_MAX)
return 1;
strcpy(self->footer, string);
self->header_footer_changed = 1;
return 0;
}
/*
* Set the page header caption.
*/
uint8_t
worksheet_set_header(lxw_worksheet *self, char *string)
{
return worksheet_set_header_opt(self, string, NULL);
}
/*
* Set the page footer caption.
*/
uint8_t
worksheet_set_footer(lxw_worksheet *self, char *string)
{
return worksheet_set_footer_opt(self, string, NULL);
}

View file

@ -0,0 +1,108 @@
/*
* Tests for the lib_xlsx_writer library.
*
* Copyright 2014, John McNamara, jmcnamara@cpan.org
*
*/
#include "../ctest.h"
#include "../helper.h"
#include "xlsxwriter/worksheet.h"
// Test the header and footer functions.
CTEST(worksheet, write_odd_header) {
char* got;
char exp[] = "<oddHeader>Page &amp;P of &amp;N</oddHeader>";
FILE* testfile = tmpfile();
lxw_worksheet *worksheet = _new_worksheet(NULL);
worksheet->file = testfile;
worksheet_set_header(worksheet, "Page &P of &N");
_worksheet_write_odd_header(worksheet);
RUN_XLSX_STREQ(exp, got);
_free_worksheet(worksheet);
}
// Test the header and footer functions.
CTEST(worksheet, write_odd_footer) {
char* got;
char exp[] = "<oddFooter>&amp;F</oddFooter>";
FILE* testfile = tmpfile();
lxw_worksheet *worksheet = _new_worksheet(NULL);
worksheet->file = testfile;
worksheet_set_footer(worksheet, "&F");
_worksheet_write_odd_footer(worksheet);
RUN_XLSX_STREQ(exp, got);
_free_worksheet(worksheet);
}
// Test the header and footer functions.
CTEST(worksheet, _worksheet_write_header_footer1) {
char* got;
char exp[] = "<headerFooter><oddHeader>Page &amp;P of &amp;N</oddHeader></headerFooter>";
FILE* testfile = tmpfile();
lxw_worksheet *worksheet = _new_worksheet(NULL);
worksheet->file = testfile;
worksheet_set_header(worksheet, "Page &P of &N");
_worksheet_write_header_footer(worksheet);
RUN_XLSX_STREQ(exp, got);
_free_worksheet(worksheet);
}
// Test the header and footer functions.
CTEST(worksheet, _worksheet_write_header_footer2) {
char* got;
char exp[] = "<headerFooter><oddFooter>&amp;F</oddFooter></headerFooter>";
FILE* testfile = tmpfile();
lxw_worksheet *worksheet = _new_worksheet(NULL);
worksheet->file = testfile;
worksheet_set_footer(worksheet, "&F");
_worksheet_write_header_footer(worksheet);
RUN_XLSX_STREQ(exp, got);
_free_worksheet(worksheet);
}
// Test the header and footer functions.
CTEST(worksheet, _worksheet_write_header_footer3) {
char* got;
char exp[] = "<headerFooter><oddHeader>Page &amp;P of &amp;N</oddHeader><oddFooter>&amp;F</oddFooter></headerFooter>";
FILE* testfile = tmpfile();
lxw_worksheet *worksheet = _new_worksheet(NULL);
worksheet->file = testfile;
worksheet_set_header(worksheet, "Page &P of &N");
worksheet_set_footer(worksheet, "&F");
_worksheet_write_header_footer(worksheet);
RUN_XLSX_STREQ(exp, got);
_free_worksheet(worksheet);
}

View file

@ -116,3 +116,26 @@ CTEST(worksheet, write_page_margin06) {
_free_worksheet(worksheet);
}
/* Test the _write_page_margins() method. */
CTEST(worksheet, write_page_margin07) {
char* got;
char exp[] = "<pageMargins left=\"0.7\" right=\"0.7\" top=\"0.75\" "
"bottom=\"0.75\" header=\"0.2\" footer=\"0.4\"/>";
FILE* testfile = tmpfile();
lxw_header_footer_options header_options = {0.2};
lxw_header_footer_options footer_options = {0.4};
lxw_worksheet *worksheet = _new_worksheet(NULL);
worksheet->file = testfile;
worksheet_set_header_opt(worksheet, "", &header_options);
worksheet_set_footer_opt(worksheet, "", &footer_options);
_worksheet_write_page_margins(worksheet);
RUN_XLSX_STREQ(exp, got);
_free_worksheet(worksheet);
}