First functional test of insert_image().

This commit is contained in:
John McNamara 2015-12-30 00:35:03 +00:00
parent 79505061eb
commit 03e5deaa8f
18 changed files with 306 additions and 33 deletions

1
.indent.pro vendored
View file

@ -65,7 +65,6 @@
-T lxw_hash_table -T lxw_hash_table
-T lxw_header_footer_options -T lxw_header_footer_options
-T lxw_heading_pair -T lxw_heading_pair
-T lxw_image_meta
-T lxw_image_options -T lxw_image_options
-T lxw_merged_range -T lxw_merged_range
-T lxw_packager -T lxw_packager

View file

@ -28,6 +28,7 @@ enum lxw_boolean {
#define LXW_SHEETNAME_LEN 65 #define LXW_SHEETNAME_LEN 65
#define LXW_UINT32_T_LEN 11 /* Length of 4294967296\0. */ #define LXW_UINT32_T_LEN 11 /* Length of 4294967296\0. */
#define LXW_IGNORE 1 #define LXW_IGNORE 1
#define FILENAME_LEN 128
#define LXW_ERROR(message) \ #define LXW_ERROR(message) \
fprintf(stderr, "[ERROR][%s:%d]: " message "\n", __FILE__, __LINE__) fprintf(stderr, "[ERROR][%s:%d]: " message "\n", __FILE__, __LINE__)

View file

@ -45,7 +45,8 @@ void _ct_add_default(lxw_content_types *self, const char *key,
const char *value); const char *value);
void _ct_add_override(lxw_content_types *self, const char *key, void _ct_add_override(lxw_content_types *self, const char *key,
const char *value); const char *value);
void _ct_add_worksheet_name(lxw_content_types *self, const char *str); void _ct_add_worksheet_name(lxw_content_types *self, const char *name);
void _ct_add_drawing_name(lxw_content_types *self, const char *name);
void _ct_add_shared_strings(lxw_content_types *self); void _ct_add_shared_strings(lxw_content_types *self);
void _ct_add_calc_chain(lxw_content_types *self); void _ct_add_calc_chain(lxw_content_types *self);

View file

@ -86,6 +86,8 @@ lxw_drawing *_new_drawing();
void _free_drawing(lxw_drawing *drawing); void _free_drawing(lxw_drawing *drawing);
void _drawing_assemble_xml_file(lxw_drawing *self); void _drawing_assemble_xml_file(lxw_drawing *self);
void _free_drawing_object(struct lxw_drawing_object *drawing_object); void _free_drawing_object(struct lxw_drawing_object *drawing_object);
void _add_drawing_object(lxw_drawing *drawing,
lxw_drawing_object *drawing_object);
/* Declarations required for unit testing. */ /* Declarations required for unit testing. */
#ifdef TESTING #ifdef TESTING

View file

@ -24,7 +24,6 @@
#include "content_types.h" #include "content_types.h"
#include "relationships.h" #include "relationships.h"
#define FILENAME_LEN 128
#define LXW_ZIP_BUFFER_SIZE (16384) #define LXW_ZIP_BUFFER_SIZE (16384)
/* /*
@ -41,6 +40,8 @@ typedef struct lxw_packager {
char *filename; char *filename;
char *buffer; char *buffer;
uint16_t drawing_count;
} lxw_packager; } lxw_packager;

View file

@ -200,6 +200,10 @@ typedef struct lxw_workbook {
uint16_t fill_count; uint16_t fill_count;
uint8_t optimize; uint8_t optimize;
uint8_t has_png;
uint8_t has_jpg;
uint8_t has_bmp;
lxw_hash_table *used_xf_formats; lxw_hash_table *used_xf_formats;
} lxw_workbook; } lxw_workbook;

View file

@ -49,6 +49,7 @@
#include <string.h> #include <string.h>
#include "shared_strings.h" #include "shared_strings.h"
#include "drawing.h"
#include "common.h" #include "common.h"
#include "format.h" #include "format.h"
#include "utility.h" #include "utility.h"
@ -465,11 +466,15 @@ typedef struct lxw_worksheet {
lxw_col_t *vbreaks; lxw_col_t *vbreaks;
struct lxw_rel_tuples *external_hyperlinks; struct lxw_rel_tuples *external_hyperlinks;
struct lxw_rel_tuples *external_drawing_links;
struct lxw_rel_tuples *drawing_links;
struct lxw_panes panes; struct lxw_panes panes;
struct lxw_protection protection; struct lxw_protection protection;
lxw_drawing *drawing;
STAILQ_ENTRY (lxw_worksheet) list_pointers; STAILQ_ENTRY (lxw_worksheet) list_pointers;
} lxw_worksheet; } lxw_worksheet;

View file

@ -284,15 +284,21 @@ mem_error:
* Add the name of a worksheet to the ContentTypes overrides. * Add the name of a worksheet to the ContentTypes overrides.
*/ */
void void
_ct_add_worksheet_name(lxw_content_types *self, const char *str) _ct_add_worksheet_name(lxw_content_types *self, const char *name)
{ {
char name[MAX_ATTRIBUTE_LENGTH];
lxw_snprintf(name, MAX_ATTRIBUTE_LENGTH, "/xl/worksheets/%s.xml", str);
_ct_add_override(self, name, _ct_add_override(self, name,
LXW_APP_DOCUMENT "spreadsheetml.worksheet+xml"); LXW_APP_DOCUMENT "spreadsheetml.worksheet+xml");
} }
/*
* Add the name of a drawing to the ContentTypes overrides.
*/
void
_ct_add_drawing_name(lxw_content_types *self, const char *name)
{
_ct_add_override(self, name, LXW_APP_DOCUMENT "drawing+xml");
}
/* /*
* Add the sharedStrings link to the ContentTypes overrides. * Add the sharedStrings link to the ContentTypes overrides.
*/ */

View file

@ -86,6 +86,16 @@ _free_drawing(lxw_drawing *drawing)
free(drawing); free(drawing);
} }
/*
* Add a drawing object to the drawing collection.
*/
void
_add_drawing_object(lxw_drawing *drawing, lxw_drawing_object *drawing_object)
{
STAILQ_INSERT_TAIL(drawing->drawing_objects, drawing_object,
list_pointers);
}
/***************************************************************************** /*****************************************************************************
* *
* XML functions. * XML functions.

View file

@ -169,6 +169,71 @@ _write_worksheet_files(lxw_packager *self)
return 0; return 0;
} }
/*
* Write the /xl/media/image?.xml files.
*/
STATIC uint8_t
_write_image_files(lxw_packager *self)
{
lxw_workbook *workbook = self->workbook;
lxw_worksheet *worksheet;
lxw_image_options *image;
char filename[FILENAME_LEN] = { 0 };
uint16_t index = 1;
STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
if (STAILQ_EMPTY(worksheet->images))
continue;
STAILQ_FOREACH(image, worksheet->images, list_pointers) {
lxw_snprintf(filename, FILENAME_LEN,
"xl/media/image%d.png", index++);
rewind(image->stream);
_add_file_to_zip(self, image->stream, filename);
fclose(image->stream);
}
}
return 0;
}
/*
* Write the drawing files.
*/
STATIC uint8_t
_write_drawing_files(lxw_packager *self)
{
lxw_workbook *workbook = self->workbook;
lxw_worksheet *worksheet;
lxw_drawing *drawing;
char filename[FILENAME_LEN] = { 0 };
uint16_t index = 1;
STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
drawing = worksheet->drawing;
if (drawing) {
lxw_snprintf(filename, FILENAME_LEN,
"xl/drawings/drawing%d.xml", index++);
drawing->file = lxw_tmpfile();
_drawing_assemble_xml_file(drawing);
_add_file_to_zip(self, drawing->file, filename);
fclose(drawing->file);
self->drawing_count++;
}
}
return 0;
}
/* /*
* Write the sharedStrings.xml file. * Write the sharedStrings.xml file.
*/ */
@ -340,14 +405,24 @@ _write_content_types_file(lxw_packager *self)
lxw_content_types *content_types = _new_content_types(); lxw_content_types *content_types = _new_content_types();
lxw_workbook *workbook = self->workbook; lxw_workbook *workbook = self->workbook;
lxw_worksheet *worksheet; lxw_worksheet *worksheet;
char sheetname[FILENAME_LEN] = { 0 }; char filename[MAX_ATTRIBUTE_LENGTH] = { 0 };
uint16_t index = 1; uint16_t index = 1;
content_types->file = lxw_tmpfile(); content_types->file = lxw_tmpfile();
if (workbook->has_png)
_ct_add_default(content_types, "png", "image/png");
STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) { STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
lxw_snprintf(sheetname, FILENAME_LEN, "sheet%d", index++); lxw_snprintf(filename, FILENAME_LEN,
_ct_add_worksheet_name(content_types, sheetname); "/xl/worksheets/sheet%d.xml", index++);
_ct_add_worksheet_name(content_types, filename);
}
for (index = 1; index <= self->drawing_count; index++) {
lxw_snprintf(filename, FILENAME_LEN,
"/xl/drawings/drawing%d.xml", index++);
_ct_add_drawing_name(content_types, filename);
} }
if (workbook->sst->string_count) if (workbook->sst->string_count)
@ -409,7 +484,7 @@ STATIC uint8_t
_write_worksheet_rels_file(lxw_packager *self) _write_worksheet_rels_file(lxw_packager *self)
{ {
lxw_relationships *rels; lxw_relationships *rels;
lxw_rel_tuple *hlink; lxw_rel_tuple *rel;
lxw_workbook *workbook = self->workbook; lxw_workbook *workbook = self->workbook;
lxw_worksheet *worksheet; lxw_worksheet *worksheet;
char sheetname[FILENAME_LEN] = { 0 }; char sheetname[FILENAME_LEN] = { 0 };
@ -417,16 +492,21 @@ _write_worksheet_rels_file(lxw_packager *self)
STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) { STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
if (STAILQ_EMPTY(worksheet->external_hyperlinks)) if (STAILQ_EMPTY(worksheet->external_hyperlinks) &&
STAILQ_EMPTY(worksheet->external_drawing_links))
continue; continue;
rels = _new_relationships(); rels = _new_relationships();
rels->file = lxw_tmpfile(); rels->file = lxw_tmpfile();
STAILQ_FOREACH(hlink, worksheet->external_hyperlinks, list_pointers) { STAILQ_FOREACH(rel, worksheet->external_hyperlinks, list_pointers) {
_add_worksheet_relationship(rels, hlink->type, hlink->target, _add_worksheet_relationship(rels, rel->type, rel->target,
hlink->target_mode); rel->target_mode);
}
STAILQ_FOREACH(rel, worksheet->external_drawing_links, list_pointers) {
_add_worksheet_relationship(rels, rel->type, rel->target,
rel->target_mode);
} }
lxw_snprintf(sheetname, FILENAME_LEN, lxw_snprintf(sheetname, FILENAME_LEN,
@ -443,6 +523,48 @@ _write_worksheet_rels_file(lxw_packager *self)
return 0; return 0;
} }
/*
* Write the drawing .rels files for worksheets that contain charts or
* drawings.
*/
STATIC uint8_t
_write_drawing_rels_file(lxw_packager *self)
{
lxw_relationships *rels;
lxw_rel_tuple *rel;
lxw_workbook *workbook = self->workbook;
lxw_worksheet *worksheet;
char sheetname[FILENAME_LEN] = { 0 };
uint16_t index = 1;
STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
if (STAILQ_EMPTY(worksheet->drawing_links))
continue;
rels = _new_relationships();
rels->file = lxw_tmpfile();
STAILQ_FOREACH(rel, worksheet->drawing_links, list_pointers) {
_add_worksheet_relationship(rels, rel->type, rel->target,
rel->target_mode);
}
lxw_snprintf(sheetname, FILENAME_LEN,
"xl/drawings/_rels/drawing%d.xml.rels", index++);
_relationships_assemble_xml_file(rels);
_add_file_to_zip(self, rels->file, sheetname);
fclose(rels->file);
_free_relationships(rels);
}
return 0;
}
/* /*
* Write the _rels/.rels xml file. * Write the _rels/.rels xml file.
*/ */
@ -543,6 +665,7 @@ _create_package(lxw_packager *self)
_write_worksheet_files(self); _write_worksheet_files(self);
_write_workbook_file(self); _write_workbook_file(self);
_write_drawing_files(self);
_write_shared_strings_file(self); _write_shared_strings_file(self);
_write_app_file(self); _write_app_file(self);
_write_core_file(self); _write_core_file(self);
@ -551,6 +674,8 @@ _create_package(lxw_packager *self)
_write_content_types_file(self); _write_content_types_file(self);
_write_workbook_rels_file(self); _write_workbook_rels_file(self);
_write_worksheet_rels_file(self); _write_worksheet_rels_file(self);
_write_drawing_rels_file(self);
_write_image_files(self);
_write_root_rels_file(self); _write_root_rels_file(self);
zipClose(self->zipfile, NULL); zipClose(self->zipfile, NULL);

View file

@ -691,11 +691,13 @@ _prepare_drawings(lxw_workbook *self)
if (_get_image_properties(image_options) != 0) if (_get_image_properties(image_options) != 0)
continue; continue;
if (image_options->image_type == IMAGE_PNG)
self->has_png = LXW_TRUE;
image_ref_id++; image_ref_id++;
/*sheet->_prepare_image(index, image_ref_id, drawing_id, width, _worksheet_prepare_image(worksheet, image_ref_id, drawing_id,
height, name, type); image_options);
*/
} }
} }

View file

@ -12,7 +12,6 @@
#include "xlsxwriter/xmlwriter.h" #include "xlsxwriter/xmlwriter.h"
#include "xlsxwriter/worksheet.h" #include "xlsxwriter/worksheet.h"
#include "xlsxwriter/format.h" #include "xlsxwriter/format.h"
#include "xlsxwriter/drawing.h"
#include "xlsxwriter/utility.h" #include "xlsxwriter/utility.h"
#include "xlsxwriter/relationships.h" #include "xlsxwriter/relationships.h"
@ -86,12 +85,21 @@ _new_worksheet(lxw_worksheet_init_data *init_data)
worksheet->external_hyperlinks = calloc(1, sizeof(struct lxw_rel_tuples)); worksheet->external_hyperlinks = calloc(1, sizeof(struct lxw_rel_tuples));
GOTO_LABEL_ON_MEM_ERROR(worksheet->external_hyperlinks, mem_error); GOTO_LABEL_ON_MEM_ERROR(worksheet->external_hyperlinks, mem_error);
worksheet->external_drawing_links =
calloc(1, sizeof(struct lxw_rel_tuples));
GOTO_LABEL_ON_MEM_ERROR(worksheet->external_drawing_links, mem_error);
worksheet->drawing_links = calloc(1, sizeof(struct lxw_rel_tuples));
GOTO_LABEL_ON_MEM_ERROR(worksheet->drawing_links, mem_error);
RB_INIT(worksheet->table); RB_INIT(worksheet->table);
RB_INIT(worksheet->hyperlinks); RB_INIT(worksheet->hyperlinks);
STAILQ_INIT(worksheet->merged_ranges); STAILQ_INIT(worksheet->merged_ranges);
STAILQ_INIT(worksheet->images); STAILQ_INIT(worksheet->images);
STAILQ_INIT(worksheet->selections); STAILQ_INIT(worksheet->selections);
STAILQ_INIT(worksheet->external_hyperlinks); STAILQ_INIT(worksheet->external_hyperlinks);
STAILQ_INIT(worksheet->external_drawing_links);
STAILQ_INIT(worksheet->drawing_links);
if (init_data && init_data->optimize) { if (init_data && init_data->optimize) {
worksheet->optimize_tmpfile = lxw_tmpfile(); worksheet->optimize_tmpfile = lxw_tmpfile();
@ -225,7 +233,7 @@ _free_worksheet(lxw_worksheet *worksheet)
lxw_merged_range *merged_range; lxw_merged_range *merged_range;
lxw_image_options *image; lxw_image_options *image;
lxw_selection *selection; lxw_selection *selection;
lxw_rel_tuple *external_hyperlink; lxw_rel_tuple *relationship;
if (!worksheet) if (!worksheet)
return; return;
@ -295,17 +303,37 @@ _free_worksheet(lxw_worksheet *worksheet)
free(worksheet->selections); free(worksheet->selections);
} }
/* TODO. Add function for freeing the relationship lists. */
while (!STAILQ_EMPTY(worksheet->external_hyperlinks)) { while (!STAILQ_EMPTY(worksheet->external_hyperlinks)) {
external_hyperlink = STAILQ_FIRST(worksheet->external_hyperlinks); relationship = STAILQ_FIRST(worksheet->external_hyperlinks);
STAILQ_REMOVE_HEAD(worksheet->external_hyperlinks, list_pointers); STAILQ_REMOVE_HEAD(worksheet->external_hyperlinks, list_pointers);
free(external_hyperlink->type); free(relationship->type);
free(external_hyperlink->target); free(relationship->target);
free(external_hyperlink->target_mode); free(relationship->target_mode);
free(external_hyperlink); free(relationship);
} }
free(worksheet->external_hyperlinks); free(worksheet->external_hyperlinks);
while (!STAILQ_EMPTY(worksheet->external_drawing_links)) {
relationship = STAILQ_FIRST(worksheet->external_drawing_links);
STAILQ_REMOVE_HEAD(worksheet->external_drawing_links, list_pointers);
free(relationship->type);
free(relationship->target);
free(relationship->target_mode);
free(relationship);
}
free(worksheet->external_drawing_links);
while (!STAILQ_EMPTY(worksheet->drawing_links)) {
relationship = STAILQ_FIRST(worksheet->drawing_links);
STAILQ_REMOVE_HEAD(worksheet->drawing_links, list_pointers);
free(relationship->type);
free(relationship->target);
free(relationship->target_mode);
free(relationship);
}
free(worksheet->drawing_links);
if (worksheet->array) { if (worksheet->array) {
for (col = 0; col < LXW_COL_MAX; col++) { for (col = 0; col < LXW_COL_MAX; col++) {
_free_cell(worksheet->array[col]); _free_cell(worksheet->array[col]);
@ -316,6 +344,9 @@ _free_worksheet(lxw_worksheet *worksheet)
if (worksheet->optimize_row) if (worksheet->optimize_row)
free(worksheet->optimize_row); free(worksheet->optimize_row);
if (worksheet->drawing)
_free_drawing(worksheet->drawing);
free(worksheet->name); free(worksheet->name);
free(worksheet->quoted_name); free(worksheet->quoted_name);
@ -1700,12 +1731,39 @@ _worksheet_prepare_image(lxw_worksheet *self,
lxw_image_options *image) lxw_image_options *image)
{ {
lxw_drawing_object *drawing_object; lxw_drawing_object *drawing_object;
lxw_rel_tuple *relationship;
double width; double width;
double height; double height;
char filename[FILENAME_LEN];
if (!self->drawing) {
self->drawing = _new_drawing();
self->drawing->embedded = LXW_TRUE;
RETURN_VOID_ON_MEM_ERROR(self->drawing);
relationship = calloc(1, sizeof(lxw_rel_tuple));
GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
relationship->type = lxw_strdup("/drawing");
GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
lxw_snprintf(filename, FILENAME_LEN, "../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)); drawing_object = calloc(1, sizeof(lxw_drawing_object));
RETURN_VOID_ON_MEM_ERROR(drawing_object); RETURN_VOID_ON_MEM_ERROR(drawing_object);
drawing_object->anchor_type = LXW_ANCHOR_TYPE_IMAGE;
drawing_object->edit_as = LXW_ANCHOR_EDIT_AS_ONE_CELL;
drawing_object->description = lxw_strdup(image->short_name);
/* Scale to user scale. */ /* Scale to user scale. */
width = image->width * image->x_scale; width = image->width * image->x_scale;
height = image->height * image->y_scale; height = image->height * image->y_scale;
@ -1721,12 +1779,33 @@ _worksheet_prepare_image(lxw_worksheet *self,
_worksheet_position_object_emus(self, image, drawing_object); _worksheet_position_object_emus(self, image, drawing_object);
/* Convert from pixels to emus. */ /* Convert from pixels to emus. */
image->width *= 9525; drawing_object->width = image->width * 9525;
image->height *= 9525; drawing_object->height = image->height * 9525;
image_ref_id++; _add_drawing_object(self->drawing, drawing_object);
drawing_id++;
relationship = calloc(1, sizeof(lxw_rel_tuple));
GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
relationship->type = lxw_strdup("/image");
GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
lxw_snprintf(filename, 32, "../media/image%d.png", image_ref_id);
relationship->target = lxw_strdup(filename);
GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
STAILQ_INSERT_TAIL(self->drawing_links, relationship, list_pointers);
return;
mem_error:
if (relationship) {
free(relationship->type);
free(relationship->target);
free(relationship->target_mode);
free(relationship);
}
} }
/***************************************************************************** /*****************************************************************************

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

View file

@ -0,0 +1,20 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2015, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = new_workbook("test_image01.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
worksheet_insert_image(worksheet, CELL("E9"), "images/red.png");
return workbook_close(workbook);
}

View file

@ -0,0 +1,18 @@
###############################################################################
#
# Tests for libxlsxwriter.
#
# Copyright 2014-2015, 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_image01(self):
self.run_exe_test('test_image01')

Binary file not shown.

View file

@ -37,7 +37,7 @@ CTEST(content_types, content_types01) {
lxw_content_types *content_types = _new_content_types(); lxw_content_types *content_types = _new_content_types();
content_types->file = testfile; content_types->file = testfile;
_ct_add_worksheet_name(content_types, "sheet1"); _ct_add_worksheet_name(content_types, "/xl/worksheets/sheet1.xml");
_ct_add_default(content_types, "jpeg", "image/jpeg"); _ct_add_default(content_types, "jpeg", "image/jpeg");
_ct_add_shared_strings(content_types); _ct_add_shared_strings(content_types);
_ct_add_calc_chain(content_types); _ct_add_calc_chain(content_types);

View file

@ -5,7 +5,7 @@
* *
*/ */
#include <string.h>; #include <string.h>
#include "../ctest.h" #include "../ctest.h"
#include "../helper.h" #include "../helper.h"
@ -88,7 +88,7 @@ CTEST(drawing, drawing_image01) {
drawing_object->width = 1142857; drawing_object->width = 1142857;
drawing_object->height = 1142857; drawing_object->height = 1142857;
STAILQ_INSERT_TAIL(drawing->drawing_objects, drawing_object, list_pointers); _add_drawing_object(drawing, drawing_object);
_drawing_assemble_xml_file(drawing); _drawing_assemble_xml_file(drawing);