From 186862a138a5865544f36c48140467184c30e786 Mon Sep 17 00:00:00 2001 From: John McNamara Date: Mon, 28 Dec 2015 11:22:17 +0000 Subject: [PATCH] Initial png file parsing. --- .indent.pro | 1 + include/xlsxwriter/common.h | 22 +++-- include/xlsxwriter/workbook.h | 2 + include/xlsxwriter/worksheet.h | 16 ++++ src/workbook.c | 168 +++++++++++++++++++++++++++++++++ src/worksheet.c | 38 +++----- 6 files changed, 213 insertions(+), 34 deletions(-) diff --git a/.indent.pro b/.indent.pro index 500ca1d7..b3c65827 100644 --- a/.indent.pro +++ b/.indent.pro @@ -65,6 +65,7 @@ -T lxw_hash_table -T lxw_header_footer_options -T lxw_heading_pair +-T lxw_image_meta -T lxw_image_options -T lxw_merged_range -T lxw_packager diff --git a/include/xlsxwriter/common.h b/include/xlsxwriter/common.h index 0eef59df..0b562873 100644 --- a/include/xlsxwriter/common.h +++ b/include/xlsxwriter/common.h @@ -19,16 +19,15 @@ #define STATIC #endif -#define LXW_SHEETNAME_MAX 32 -#define LXW_SHEETNAME_LEN 65 -#define LXW_UINT32_T_LEN 11 /* Length of 4294967296. */ - enum lxw_boolean { LXW_FALSE, LXW_TRUE }; -#define LXW_IGNORE 1 +#define LXW_SHEETNAME_MAX 32 +#define LXW_SHEETNAME_LEN 65 +#define LXW_UINT32_T_LEN 11 /* Length of 4294967296\0. */ +#define LXW_IGNORE 1 #define LXW_ERROR(message) \ fprintf(stderr, "[ERROR][%s:%d]: " message "\n", __FILE__, __LINE__) @@ -55,10 +54,19 @@ enum lxw_boolean { } #define LXW_WARN(message) \ - fprintf(stderr, "[WARN] %s(): " message "\n", __func__) + fprintf(stderr, "[WARN]: " message "\n") #define LXW_WARN_FORMAT(message, var) \ - fprintf(stderr, "[WARN] %s(): " message "\n", __func__, var) + fprintf(stderr, "[WARN]: " message "\n", var) + +#ifndef LXW_BIG_ENDIAN +#define LXW_UINT32_NETWORK(n) ((((n) & 0xFF) << 24) | \ + (((n) & 0xFF00) << 8) | \ + (((n) & 0xFF0000) >> 8) | \ + (((n) & 0xFF000000) >> 24)) +#else +#define LXW_UINT32_NETWORK(n) (n) +#endif /* Compilers that have a native snprintf() can use it directly. */ #ifdef _MSC_VER diff --git a/include/xlsxwriter/workbook.h b/include/xlsxwriter/workbook.h index 0e520e1d..59692348 100644 --- a/include/xlsxwriter/workbook.h +++ b/include/xlsxwriter/workbook.h @@ -44,6 +44,7 @@ #include #include #include +#include #include "worksheet.h" #include "shared_strings.h" @@ -192,6 +193,7 @@ typedef struct lxw_workbook { uint16_t active_sheet; uint16_t num_xf_formats; uint16_t num_format_count; + uint16_t drawing_count; uint16_t font_count; uint16_t border_count; diff --git a/include/xlsxwriter/worksheet.h b/include/xlsxwriter/worksheet.h index c22677d1..a9551294 100644 --- a/include/xlsxwriter/worksheet.h +++ b/include/xlsxwriter/worksheet.h @@ -137,6 +137,13 @@ enum pane_types { FREEZE_SPLIT_PANES }; +enum image_types { + IMAGE_UNKNOWN = 0, + IMAGE_PNG, + IMAGE_JPG, + IMAGE_BMP +}; + /* Define the tree.h RB structs for the red-black head types. */ RB_HEAD(lxw_table_cells, lxw_cell); @@ -271,6 +278,15 @@ typedef struct lxw_image_options { char *tip; uint8_t anchor; + /* Internal metadata. */ + FILE *stream; + uint8_t image_type; + uint32_t width; + uint32_t height; + char *short_name; + double x_dpi; + double y_dpi; + STAILQ_ENTRY (lxw_image_options) list_pointers; } lxw_image_options; diff --git a/src/workbook.c b/src/workbook.c index 5a2ece82..d88d07bb 100644 --- a/src/workbook.c +++ b/src/workbook.c @@ -538,6 +538,171 @@ mem_error: return 1; } +/* + * Extract width and height information from a PNG file. + */ +STATIC int +_process_png(lxw_image_options *image_options) +{ + uint32_t length; + uint32_t offset; + char type[4]; + uint32_t width = 0; + uint32_t height = 0; + double x_dpi = 96; + double y_dpi = 96; + + FILE *stream = image_options->stream; + + /* Skip another 4 bytes to the end of the PNG header. */ + fseek(stream, 4, SEEK_CUR); + + while (!feof(stream)) { + + /* Read the PNG length and type fields for the sub-section. */ + if (fread(&length, sizeof(length), 1, stream) < 1) + break; + + if (fread(&type, 1, 4, stream) < 4) + break; + + /* Convert the length to network order. */ + length = LXW_UINT32_NETWORK(length); + + /* The offset for next fseek() is the field length + type length. */ + offset = length + 4; + + if (memcmp(type, "IHDR", 4) == 0) { + if (fread(&width, sizeof(width), 1, stream) < 1) + break; + + if (fread(&height, sizeof(height), 1, stream) < 1) + break; + + width = LXW_UINT32_NETWORK(width); + height = LXW_UINT32_NETWORK(height); + + /* Reduce the offset by the length of previous freads(). */ + offset -= 8; + } + + if (memcmp(type, "pHYs", 4) == 0) { + uint32_t x_ppu = 0; + uint32_t y_ppu = 0; + uint8_t units = 1; + + if (fread(&x_ppu, sizeof(x_ppu), 1, stream) < 1) + break; + + if (fread(&y_ppu, sizeof(y_ppu), 1, stream) < 1) + break; + + if (fread(&units, sizeof(units), 1, stream) < 1) + break; + + if (units == 1) { + x_dpi = (double) x_ppu *0.0254; + y_dpi = (double) y_ppu *0.0254; + } + + /* Reduce the offset by the length of previous freads(). */ + offset -= 9; + } + + if (memcmp(type, "IEND", 4) == 0) + break; + + if (!feof(stream)) + fseek(stream, offset, SEEK_CUR); + } + + /* Ensure that we read some valid data from the file. */ + if (width == 0) { + LXW_WARN_FORMAT("worksheet_insert_image()/_opts(): " + "no size data found in file: %s\n", + image_options->filename); + return -1; + } + + /* Set the image metadata. */ + image_options->image_type = IMAGE_PNG; + image_options->width = width; + image_options->height = height; + image_options->x_dpi = x_dpi; + image_options->y_dpi = y_dpi; + + return 0; +} + +/* + * Extract information from the image file such as dimension, type, filename, + * and extension. + */ +STATIC int +_get_image_properties(lxw_image_options *image_options) +{ + char *short_name; + char signature[4]; + + /* Read 4 bytes to look for the file header/signature. */ + if (fread(signature, 1, 4, image_options->stream) < 4) + return -1; + + if (memcmp(&signature[1], "PNG", 3) == 0) { + _process_png(image_options); + } + else { + LXW_WARN_FORMAT("worksheet_insert_image()/_opts(): " + "unsupported image format for file: %s\n", + image_options->filename); + return -1; + } + + /* Get the filename from the full path to add to the Drawing object. */ + short_name = basename(image_options->filename); + if (short_name) + image_options->short_name = lxw_strdup(short_name); + else + return -1; + + return 0; +} + +/* + * Iterate through the worksheets and set up any chart or image drawings. + */ +STATIC void +_prepare_drawings(lxw_workbook *self) +{ + lxw_worksheet *worksheet; + lxw_image_options *image_options; + uint16_t image_ref_id = 0; + uint16_t drawing_id = 0; + + STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) { + + if (STAILQ_EMPTY(worksheet->images)) + continue; + + drawing_id++; + + STAILQ_FOREACH(image_options, worksheet->images, list_pointers) { + + if (_get_image_properties(image_options) != 0) + continue; + + image_ref_id++; + + /*sheet->_prepare_image(index, image_ref_id, drawing_id, width, + height, name, type); + */ + } + + } + + self->drawing_count = drawing_id; +} + /* * Iterate through the worksheets and store any defined names used for print * ranges or repeat rows/columns. @@ -1117,6 +1282,9 @@ workbook_close(lxw_workbook *self) /* Set the defined names for the worksheets such as Print Titles. */ _prepare_defined_names(self); + /* Prepare the drawings, charts and images. */ + _prepare_drawings(self); + /* Create a packager object to assemble sub-elements into a zip file. */ packager = _new_packager(self->filename); diff --git a/src/worksheet.c b/src/worksheet.c index 95b81a5d..b7a0d4f6 100644 --- a/src/worksheet.c +++ b/src/worksheet.c @@ -206,6 +206,7 @@ _free_image_options(lxw_image_options *image) return; free(image->filename); + free(image->short_name); free(image->url); free(image->tip); free(image); @@ -3718,7 +3719,8 @@ worksheet_set_zoom(lxw_worksheet *self, uint16_t scale) { /* Confine the scale to Excel"s range */ if (scale < 10 || scale > 400) { - LXW_WARN("Zoom factor scale outside range: 10 <= zoom <= 400"); + LXW_WARN("worksheet_set_zoom(): " + "Zoom factor scale outside range: 10 <= zoom <= 400"); return; } @@ -3807,24 +3809,23 @@ worksheet_insert_image_opt(lxw_worksheet *self, const char *filename, lxw_image_options *user_options) { - FILE *file; + FILE *image_stream; lxw_image_options *options; if (!filename) { - LXW_WARN("filename must be specified"); + LXW_WARN("worksheet_insert_image()/_opts(): " + "filename must be specified"); return -1; } /* Check that the image file exists and can be opened. */ - file = fopen(filename, "rb"); - if (!file) { - LXW_WARN_FORMAT("file doesn't exist or can't be opened: %s", + image_stream = fopen(filename, "rb"); + if (!image_stream) { + LXW_WARN_FORMAT("worksheet_insert_image()/_opts(): " + "file doesn't exist or can't be opened: %s", filename); return -1; } - else { - fclose(file); - } /* Create a new object to hold the image options. */ options = calloc(1, sizeof(lxw_image_options)); @@ -3838,6 +3839,7 @@ worksheet_insert_image_opt(lxw_worksheet *self, /* Copy other options or set defaults. */ options->filename = lxw_strdup(filename); + options->stream = image_stream; if (!options->x_scale) options->x_scale = 1; @@ -3861,23 +3863,5 @@ worksheet_insert_image(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, const char *filename) { - FILE *file; - - if (!filename) { - LXW_WARN("filename must be specified"); - return -1; - } - - /* Check that the image file exists and can be opened. */ - file = fopen(filename, "rb"); - if (!file) { - LXW_WARN_FORMAT("file doesn't exist or can't be opened: %s", - filename); - return -1; - } - else { - fclose(file); - } - return worksheet_insert_image_opt(self, row_num, col_num, filename, NULL); }