From b8efb1cf3ce4c57fd4a0396c2a9102630d3d6e36 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Tue, 22 Mar 2011 18:26:07 +0100 Subject: xps: import ghostxps source --- xps/ghostxps.h | 412 ++++++++++++++++++++ xps/xpsanalyze.c | 321 ++++++++++++++++ xps/xpscolor.c | 248 ++++++++++++ xps/xpscommon.c | 115 ++++++ xps/xpscrc.c | 95 +++++ xps/xpsdoc.c | 277 ++++++++++++++ xps/xpsfont.c | 533 ++++++++++++++++++++++++++ xps/xpsglyphs.c | 675 +++++++++++++++++++++++++++++++++ xps/xpsgradient.c | 978 +++++++++++++++++++++++++++++++++++++++++++++++ xps/xpshash.c | 217 +++++++++++ xps/xpsimage.c | 470 +++++++++++++++++++++++ xps/xpsjpeg.c | 143 +++++++ xps/xpsjxr.c | 259 +++++++++++++ xps/xpsmem.c | 182 +++++++++ xps/xpsopacity.c | 102 +++++ xps/xpspage.c | 281 ++++++++++++++ xps/xpspath.c | 1036 ++++++++++++++++++++++++++++++++++++++++++++++++++ xps/xpspng.c | 293 ++++++++++++++ xps/xpsresource.c | 204 ++++++++++ xps/xpstiff.c | 1091 +++++++++++++++++++++++++++++++++++++++++++++++++++++ xps/xpstile.c | 399 ++++++++++++++++++++ xps/xpstop.c | 576 ++++++++++++++++++++++++++++ xps/xpsutf.c | 69 ++++ xps/xpsvisual.c | 62 +++ xps/xpsxml.c | 353 +++++++++++++++++ xps/xpszip.c | 568 ++++++++++++++++++++++++++++ 26 files changed, 9959 insertions(+) create mode 100644 xps/ghostxps.h create mode 100644 xps/xpsanalyze.c create mode 100644 xps/xpscolor.c create mode 100644 xps/xpscommon.c create mode 100644 xps/xpscrc.c create mode 100644 xps/xpsdoc.c create mode 100644 xps/xpsfont.c create mode 100644 xps/xpsglyphs.c create mode 100644 xps/xpsgradient.c create mode 100644 xps/xpshash.c create mode 100644 xps/xpsimage.c create mode 100644 xps/xpsjpeg.c create mode 100644 xps/xpsjxr.c create mode 100644 xps/xpsmem.c create mode 100644 xps/xpsopacity.c create mode 100644 xps/xpspage.c create mode 100644 xps/xpspath.c create mode 100644 xps/xpspng.c create mode 100644 xps/xpsresource.c create mode 100644 xps/xpstiff.c create mode 100644 xps/xpstile.c create mode 100644 xps/xpstop.c create mode 100644 xps/xpsutf.c create mode 100644 xps/xpsvisual.c create mode 100644 xps/xpsxml.c create mode 100644 xps/xpszip.c diff --git a/xps/ghostxps.h b/xps/ghostxps.h new file mode 100644 index 00000000..3f06a80d --- /dev/null +++ b/xps/ghostxps.h @@ -0,0 +1,412 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* combined internal header for the XPS interpreter */ + +#include "memory_.h" +#include "math_.h" + +#include +#include /* for toupper() */ + +#include "gp.h" + +#include "gsgc.h" +#include "gstypes.h" +#include "gsstate.h" +#include "gsmatrix.h" +#include "gscoord.h" +#include "gsmemory.h" +#include "gsparam.h" +#include "gsdevice.h" +#include "scommon.h" +#include "gdebug.h" +#include "gserror.h" +#include "gserrors.h" +#include "gspaint.h" +#include "gspath.h" +#include "gsimage.h" +#include "gscspace.h" +#include "gsptype1.h" +#include "gscolor2.h" +#include "gscolor3.h" +#include "gsutil.h" +#include "gsicc.h" + +#include "gstrans.h" + +#include "gxpath.h" /* gsshade.h depends on it */ +#include "gxfixed.h" /* gsshade.h depends on it */ +#include "gxmatrix.h" /* gxtype1.h depends on it */ +#include "gsshade.h" +#include "gsfunc.h" +#include "gsfunc3.h" /* we use stitching and exponential interp */ + +#include "gxfont.h" +#include "gxchar.h" +#include "gxcolor2.h" /* Required for definition of gs_pattern1_instance_t */ +#include "gxtype1.h" +#include "gxfont1.h" +#include "gxfont42.h" +#include "gxfcache.h" +#include "gxistate.h" + +#include "gzstate.h" +#include "gzpath.h" +#include "gzcpath.h" + +#include "gsicc_manage.h" +#include "gscms.h" +#include "gsicc_cache.h" + +#include "zlib.h" + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#endif +#ifndef ABS +#define ABS(a) ((a) < 0 ? -(a) : (a)) +#endif + +/* + * XPS and ZIP contants. + */ + +typedef struct xps_context_s xps_context_t; + +#define REL_START_PART \ + "http://schemas.microsoft.com/xps/2005/06/fixedrepresentation" +#define REL_REQUIRED_RESOURCE \ + "http://schemas.microsoft.com/xps/2005/06/required-resource" +#define REL_REQUIRED_RESOURCE_RECURSIVE \ + "http://schemas.microsoft.com/xps/2005/06/required-resource#recursive" + +#define ZIP_LOCAL_FILE_SIG 0x04034b50 +#define ZIP_DATA_DESC_SIG 0x08074b50 +#define ZIP_CENTRAL_DIRECTORY_SIG 0x02014b50 +#define ZIP_END_OF_CENTRAL_DIRECTORY_SIG 0x06054b50 + +/* + * Memory, and string functions. + */ + +void * xps_realloc_imp(xps_context_t *ctx, void *ptr, int size, const char *func); + +#define xps_alloc(ctx, size) \ + ((void*)gs_alloc_bytes(ctx->memory, size, __func__)) +#define xps_realloc(ctx, ptr, size) \ + xps_realloc_imp(ctx, ptr, size, __func__) +#define xps_strdup(ctx, str) \ + xps_strdup_imp(ctx, str, __func__) +#define xps_free(ctx, ptr) \ + gs_free_object(ctx->memory, ptr, __func__) + +size_t xps_strlcpy(char *destination, const char *source, size_t size); +size_t xps_strlcat(char *destination, const char *source, size_t size); +int xps_strcasecmp(char *a, char *b); +char *xps_strdup_imp(xps_context_t *ctx, const char *str, const char *function); +void xps_absolute_path(char *output, char *base_uri, char *path, int output_size); + +int xps_utf8_to_ucs(int *p, const char *s, int n); + +/* + * Generic hashtable. + */ + +typedef struct xps_hash_table_s xps_hash_table_t; + +xps_hash_table_t *xps_hash_new(xps_context_t *ctx); +void *xps_hash_lookup(xps_hash_table_t *table, char *key); +int xps_hash_insert(xps_context_t *ctx, xps_hash_table_t *table, char *key, void *value); +void xps_hash_free(xps_context_t *ctx, xps_hash_table_t *table, + void (*free_key)(xps_context_t *ctx, void *), + void (*free_value)(xps_context_t *ctx, void *)); +void xps_hash_debug(xps_hash_table_t *table); + +/* + * Container parts. + */ + +typedef struct xps_part_s xps_part_t; + +struct xps_part_s +{ + char *name; + int size; + int cap; + byte *data; +}; + +xps_part_t *xps_new_part(xps_context_t *ctx, char *name, int size); +xps_part_t *xps_read_part(xps_context_t *ctx, char *partname); +void xps_free_part(xps_context_t *ctx, xps_part_t *part); + +/* + * Document structure. + */ + +typedef struct xps_document_s xps_document_t; +typedef struct xps_page_s xps_page_t; + +struct xps_document_s +{ + char *name; + xps_document_t *next; +}; + +struct xps_page_s +{ + char *name; + int width; + int height; + xps_page_t *next; +}; + +int xps_parse_metadata(xps_context_t *ctx, xps_part_t *part); +void xps_free_fixed_pages(xps_context_t *ctx); +void xps_free_fixed_documents(xps_context_t *ctx); +void xps_debug_fixdocseq(xps_context_t *ctx); + +/* + * Images. + */ + +typedef struct xps_image_s xps_image_t; + +/* type for the information derived directly from the raster file format */ + +struct xps_image_s +{ + int width; + int height; + int stride; + gs_color_space *colorspace; + int comps; + int hasalpha; /* chunky alpha */ + int bits; + int xres; + int yres; + byte *samples; + byte *alpha; /* isolated alpha plane */ + byte *profile; + int profilesize; +}; + +int xps_decode_jpeg(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image); +int xps_decode_png(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image); +int xps_decode_tiff(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image); +int xps_decode_jpegxr(xps_context_t *ctx, byte *buf, int len, xps_image_t *image); + +int xps_png_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen); +int xps_tiff_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen); +int xps_jpegxr_has_alpha(xps_context_t *ctx, byte *buf, int len); + +void xps_free_image(xps_context_t *ctx, xps_image_t *image); + +/* + * Fonts. + */ + +typedef struct xps_font_s xps_font_t; +typedef struct xps_glyph_metrics_s xps_glyph_metrics_t; + +struct xps_font_s +{ + byte *data; + int length; + gs_font *font; + + int subfontid; + int cmaptable; + int cmapsubcount; + int cmapsubtable; + int usepua; + + /* these are for CFF opentypes only */ + byte *cffdata; + byte *cffend; + byte *gsubrs; + byte *subrs; + byte *charstrings; +}; + +struct xps_glyph_metrics_s +{ + float hadv, vadv, vorg; +}; + +xps_font_t *xps_new_font(xps_context_t *ctx, byte *buf, int buflen, int index); +void xps_free_font(xps_context_t *ctx, xps_font_t *font); + +int xps_count_font_encodings(xps_font_t *font); +void xps_identify_font_encoding(xps_font_t *font, int idx, int *pid, int *eid); +void xps_select_font_encoding(xps_font_t *font, int idx); +int xps_encode_font_char(xps_font_t *font, int key); + +void xps_measure_font_glyph(xps_context_t *ctx, xps_font_t *font, int gid, xps_glyph_metrics_t *mtx); + +int xps_find_sfnt_table(xps_font_t *font, const char *name, int *lengthp); +void xps_load_sfnt_name(xps_font_t *font, char *namep); +int xps_init_truetype_font(xps_context_t *ctx, xps_font_t *font); +int xps_init_postscript_font(xps_context_t *ctx, xps_font_t *font); + +void xps_debug_path(xps_context_t *ctx); + +/* + * Colorspaces and colors. + */ + +gs_color_space *xps_read_icc_colorspace(xps_context_t *ctx, char *base_uri, char *profile); +void xps_parse_color(xps_context_t *ctx, char *base_uri, char *hexstring, gs_color_space **csp, float *samples); +void xps_set_color(xps_context_t *ctx, gs_color_space *colorspace, float *samples); + +/* + * XML document model + */ + +typedef struct xps_item_s xps_item_t; + +xps_item_t * xps_parse_xml(xps_context_t *ctx, byte *buf, int len); +xps_item_t * xps_next(xps_item_t *item); +xps_item_t * xps_down(xps_item_t *item); +char * xps_tag(xps_item_t *item); +char * xps_att(xps_item_t *item, const char *att); +void xps_free_item(xps_context_t *ctx, xps_item_t *item); +void xps_debug_item(xps_item_t *item, int level); + +/* + * Resource dictionaries. + */ + +typedef struct xps_resource_s xps_resource_t; + +struct xps_resource_s +{ + char *name; + char *base_uri; /* only used in the head nodes */ + xps_item_t *base_xml; /* only used in the head nodes, to free the xml document */ + xps_item_t *data; + xps_resource_t *next; + xps_resource_t *parent; /* up to the previous dict in the stack */ +}; + +int xps_parse_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, char *base_uri, xps_item_t *root); +void xps_free_resource_dictionary(xps_context_t *ctx, xps_resource_t *dict); +void xps_resolve_resource_reference(xps_context_t *ctx, xps_resource_t *dict, char **attp, xps_item_t **tagp, char **urip); + +void xps_debug_resource_dictionary(xps_resource_t *dict); + +/* + * Fixed page/graphics parsing. + */ + +int xps_parse_fixed_page(xps_context_t *ctx, xps_part_t *part); +int xps_parse_canvas(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_path(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_glyphs(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_solid_color_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_image_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_visual_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_linear_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_radial_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); + +int xps_parse_tiling_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root, int (*func)(xps_context_t*, char*, xps_resource_t*, xps_item_t*, void*), void *user); + +void xps_parse_matrix_transform(xps_context_t *ctx, xps_item_t *root, gs_matrix *matrix); +void xps_parse_render_transform(xps_context_t *ctx, char *text, gs_matrix *matrix); +void xps_parse_rectangle(xps_context_t *ctx, char *text, gs_rect *rect); +void xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom); +void xps_parse_path_geometry(xps_context_t *ctx, xps_resource_t *dict, xps_item_t *root, int stroking); + +int xps_begin_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, char *opacity_att, xps_item_t *opacity_mask_tag); +void xps_end_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, char *opacity_att, xps_item_t *opacity_mask_tag); + +int xps_parse_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_element(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); + +void xps_clip(xps_context_t *ctx); +void xps_fill(xps_context_t *ctx); +void xps_bounds_in_user_space(xps_context_t *ctx, gs_rect *user); + +int xps_element_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *node); +int xps_resource_dictionary_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *node); +int xps_image_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root); + +/* + * The interpreter context. + */ + +typedef struct xps_entry_s xps_entry_t; + +struct xps_entry_s +{ + char *name; + int offset; + int csize; + int usize; +}; + +struct xps_context_s +{ + void *instance; + gs_memory_t *memory; + gs_state *pgs; + gs_font_dir *fontdir; + + gs_color_space *gray; + gs_color_space *srgb; + gs_color_space *scrgb; + gs_color_space *cmyk; + + char *directory; + FILE *file; + int zip_count; + xps_entry_t *zip_table; + + char *start_part; /* fixed document sequence */ + xps_document_t *first_fixdoc; /* first fixed document */ + xps_document_t *last_fixdoc; /* last fixed document */ + xps_page_t *first_page; /* first page of document */ + xps_page_t *last_page; /* last page of document */ + + char *base_uri; /* base uri for parsing XML and resolving relative paths */ + char *part_uri; /* part uri for parsing metadata relations */ + + /* We cache font and colorspace resources */ + xps_hash_table_t *font_table; + xps_hash_table_t *colorspace_table; + + /* Global toggle for transparency */ + int use_transparency; + + /* Hack to workaround ghostscript's lack of understanding + * the pdf 1.4 specification of Alpha only transparency groups. + * We have to force all colors to be grayscale whenever we are computing + * opacity masks. + */ + int opacity_only; + + /* The fill_rule is set by path parsing. + * It is used by clip/fill functions. + * 1=nonzero, 0=evenodd + */ + int fill_rule; +}; + +int xps_process_file(xps_context_t *ctx, char *filename); + +/* end of page device callback foo */ +int xps_show_page(xps_context_t *ctx, int num_copies, int flush); diff --git a/xps/xpsanalyze.c b/xps/xpsanalyze.c new file mode 100644 index 00000000..6e2f9d3e --- /dev/null +++ b/xps/xpsanalyze.c @@ -0,0 +1,321 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - analyze page checking for transparency. + * This is a stripped down parser that looks for alpha values < 1.0 in + * any part of the page. + */ + +#include "ghostxps.h" + +static int +xps_remote_resource_dictionary_has_transparency(xps_context_t *ctx, char *base_uri, char *source_att) +{ + //dputs("page has transparency: uses a remote resource; not parsed; being conservative\n"); + return 1; +} + +int +xps_resource_dictionary_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +{ + char *source; + xps_item_t *node; + + source = xps_att(root, "Source"); + if (source) + return xps_remote_resource_dictionary_has_transparency(ctx, base_uri, source); + + for (node = xps_down(root); node; node = xps_next(node)) + { + // TODO: ... all kinds of stuff can be here, brushes, elements, whatnot + } + + return 1; +} + +static int +xps_gradient_stops_have_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +{ + xps_item_t *node; + gs_color_space *colorspace; + char *color_att; + float samples[32]; + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "GradientStop")) + { + color_att = xps_att(node, "Color"); + if (color_att) + { + xps_parse_color(ctx, base_uri, color_att, &colorspace, samples); + if (samples[0] < 1.0) + { + //dputs("page has transparency: GradientStop has alpha\n"); + return 1; + } + } + } + } + + return 0; +} + +static int +xps_gradient_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +{ + xps_item_t *node; + char *opacity_att; + + opacity_att = xps_att(root, "Opacity"); + if (opacity_att) + { + if (atof(opacity_att) < 1.0) + { + //dputs("page has transparency: GradientBrush Opacity\n"); + return 1; + } + } + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "RadialGradientBrush.GradientStops")) + { + if (xps_gradient_stops_have_transparency(ctx, base_uri, node)) + return 1; + } + if (!strcmp(xps_tag(node), "LinearGradientBrush.GradientStops")) + { + if (xps_gradient_stops_have_transparency(ctx, base_uri, node)) + return 1; + } + } + + return 0; +} + +static int +xps_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +{ + char *opacity_att; + char *color_att; + xps_item_t *node; + + gs_color_space *colorspace; + float samples[32]; + + if (!strcmp(xps_tag(root), "SolidColorBrush")) + { + opacity_att = xps_att(root, "Opacity"); + if (opacity_att) + { + if (atof(opacity_att) < 1.0) + { + //dputs("page has transparency: SolidColorBrush Opacity\n"); + return 1; + } + } + + color_att = xps_att(root, "Color"); + if (color_att) + { + xps_parse_color(ctx, base_uri, color_att, &colorspace, samples); + if (samples[0] < 1.0) + { + //dputs("page has transparency: SolidColorBrush Color has alpha\n"); + return 1; + } + } + } + + if (!strcmp(xps_tag(root), "VisualBrush")) + { + char *opacity_att = xps_att(root, "Opacity"); + if (opacity_att) + { + if (atof(opacity_att) < 1.0) + { + //dputs("page has transparency: VisualBrush Opacity\n"); + return 1; + } + } + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "VisualBrush.Visual")) + { + if (xps_element_has_transparency(ctx, base_uri, xps_down(node))) + return 1; + } + } + } + + if (!strcmp(xps_tag(root), "ImageBrush")) + { + if (xps_image_brush_has_transparency(ctx, base_uri, root)) + return 1; + } + + if (!strcmp(xps_tag(root), "LinearGradientBrush")) + { + if (xps_gradient_brush_has_transparency(ctx, base_uri, root)) + return 1; + } + + if (!strcmp(xps_tag(root), "RadialGradientBrush")) + { + if (xps_gradient_brush_has_transparency(ctx, base_uri, root)) + return 1; + } + + return 0; +} + +static int +xps_path_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +{ + xps_item_t *node; + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "Path.OpacityMask")) + { + //dputs("page has transparency: Path.OpacityMask\n"); + return 1; + } + + if (!strcmp(xps_tag(node), "Path.Stroke")) + { + if (xps_brush_has_transparency(ctx, base_uri, xps_down(node))) + return 1; + } + + if (!strcmp(xps_tag(node), "Path.Fill")) + { + if (xps_brush_has_transparency(ctx, base_uri, xps_down(node))) + return 1; + } + } + + return 0; +} + +static int +xps_glyphs_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +{ + xps_item_t *node; + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "Glyphs.OpacityMask")) + { + //dputs("page has transparency: Glyphs.OpacityMask\n"); + return 1; + } + + if (!strcmp(xps_tag(node), "Glyphs.Fill")) + { + if (xps_brush_has_transparency(ctx, base_uri, xps_down(node))) + return 1; + } + } + + return 0; +} + +static int +xps_canvas_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +{ + xps_item_t *node; + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "Canvas.Resources")) + { + if (xps_resource_dictionary_has_transparency(ctx, base_uri, xps_down(node))) + return 1; + } + + if (!strcmp(xps_tag(node), "Canvas.OpacityMask")) + { + //dputs("page has transparency: Canvas.OpacityMask\n"); + return 1; + } + + if (xps_element_has_transparency(ctx, base_uri, node)) + return 1; + } + + return 0; +} + +int +xps_element_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *node) +{ + char *opacity_att; + char *stroke_att; + char *fill_att; + + gs_color_space *colorspace; + float samples[32]; + + stroke_att = xps_att(node, "Stroke"); + if (stroke_att) + { + xps_parse_color(ctx, base_uri, stroke_att, &colorspace, samples); + if (samples[0] < 1.0) + { + //dprintf1("page has transparency: Stroke alpha=%g\n", samples[0]); + return 1; + } + } + + fill_att = xps_att(node, "Fill"); + if (fill_att) + { + xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples); + if (samples[0] < 1.0) + { + //dprintf1("page has transparency: Fill alpha=%g\n", samples[0]); + return 1; + } + } + + opacity_att = xps_att(node, "Opacity"); + if (opacity_att) + { + if (atof(opacity_att) < 1.0) + { + //dprintf1("page has transparency: Opacity=%g\n", atof(opacity_att)); + return 1; + } + } + + if (xps_att(node, "OpacityMask")) + { + //dputs("page has transparency: OpacityMask\n"); + return 1; + } + + if (!strcmp(xps_tag(node), "Path")) + if (xps_path_has_transparency(ctx, base_uri, node)) + return 1; + if (!strcmp(xps_tag(node), "Glyphs")) + if (xps_glyphs_has_transparency(ctx, base_uri, node)) + return 1; + if (!strcmp(xps_tag(node), "Canvas")) + if (xps_canvas_has_transparency(ctx, base_uri, node)) + return 1; + + return 0; +} diff --git a/xps/xpscolor.c b/xps/xpscolor.c new file mode 100644 index 00000000..dcc7b0ca --- /dev/null +++ b/xps/xpscolor.c @@ -0,0 +1,248 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - color functions */ + +#include "ghostxps.h" + +#include "stream.h" /* for sizeof(stream) to work */ + +void +xps_set_color(xps_context_t *ctx, gs_color_space *cs, float *samples) +{ + gs_client_color cc; + int i, n; + + if (ctx->opacity_only) + { + gs_setopacityalpha(ctx->pgs, 1.0); + gs_setgray(ctx->pgs, samples[0]); + } + else + { + n = cs_num_components(cs); + cc.pattern = 0; + for (i = 0; i < n; i++) + cc.paint.values[i] = samples[i + 1]; + + gs_setopacityalpha(ctx->pgs, samples[0]); + gs_setcolorspace(ctx->pgs, cs); + gs_setcolor(ctx->pgs, &cc); + } +} + +static int unhex(int chr) +{ + const char *hextable = "0123456789ABCDEF"; + return strchr(hextable, (toupper(chr))) - hextable; +} + +static int count_commas(char *s) +{ + int n = 0; + while (*s) + { + if (*s == ',') + n ++; + s ++; + } + return n; +} + +void +xps_parse_color(xps_context_t *ctx, char *base_uri, char *string, + gs_color_space **csp, float *samples) +{ + char *p; + int i, n; + char buf[1024]; + char *profile; + + *csp = ctx->srgb; + + samples[0] = 1.0; + samples[1] = 0.0; + samples[2] = 0.0; + samples[3] = 0.0; + + if (string[0] == '#') + { + if (strlen(string) == 9) + { + samples[0] = unhex(string[1]) * 16 + unhex(string[2]); + samples[1] = unhex(string[3]) * 16 + unhex(string[4]); + samples[2] = unhex(string[5]) * 16 + unhex(string[6]); + samples[3] = unhex(string[7]) * 16 + unhex(string[8]); + } + else + { + samples[0] = 255.0; + samples[1] = unhex(string[1]) * 16 + unhex(string[2]); + samples[2] = unhex(string[3]) * 16 + unhex(string[4]); + samples[3] = unhex(string[5]) * 16 + unhex(string[6]); + } + + samples[0] /= 255.0; + samples[1] /= 255.0; + samples[2] /= 255.0; + samples[3] /= 255.0; + } + + else if (string[0] == 's' && string[1] == 'c' && string[2] == '#') + { + *csp = ctx->scrgb; + + if (count_commas(string) == 2) + sscanf(string, "sc#%g,%g,%g", samples + 1, samples + 2, samples + 3); + if (count_commas(string) == 3) + sscanf(string, "sc#%g,%g,%g,%g", samples, samples + 1, samples + 2, samples + 3); + } + + else if (strstr(string, "ContextColor ") == string) + { + /* Crack the string for profile name and sample values */ + strcpy(buf, string); + + profile = strchr(buf, ' '); + if (!profile) + { + gs_warn1("cannot find icc profile uri in '%s'", string); + return; + } + + *profile++ = 0; + p = strchr(profile, ' '); + if (!p) + { + gs_warn1("cannot find component values in '%s'", profile); + return; + } + + *p++ = 0; + n = count_commas(p) + 1; + i = 0; + while (i < n) + { + samples[i++] = atof(p); + p = strchr(p, ','); + if (!p) + break; + p ++; + if (*p == ' ') + p ++; + } + while (i < n) + { + samples[i++] = 0.0; + } + + *csp = xps_read_icc_colorspace(ctx, base_uri, profile); + if (!*csp) + { + /* Default fallbacks if the ICC stuff fails */ + switch (n) + { + case 2: *csp = ctx->gray; break; /* alpha + tint */ + case 4: *csp = ctx->srgb; break; /* alpha + RGB */ + case 5: *csp = ctx->cmyk; break; /* alpha + CMYK */ + default: *csp = ctx->gray; break; + } + } + } +} + +gs_color_space * +xps_read_icc_colorspace(xps_context_t *ctx, char *base_uri, char *profilename) +{ + gs_color_space *space; + cmm_profile_t *profile; + xps_part_t *part; + char partname[1024]; + + /* Find ICC colorspace part */ + xps_absolute_path(partname, base_uri, profilename, sizeof partname); + + /* See if we cached the profile */ + space = xps_hash_lookup(ctx->colorspace_table, partname); + if (!space) + { + part = xps_read_part(ctx, partname); + + /* Problem finding profile. Don't fail, just use default */ + if (!part) { + gs_warn1("cannot find icc profile part: %s", partname); + return NULL; + } + + /* Create the profile */ + profile = gsicc_profile_new(NULL, ctx->memory, NULL, 0); + + /* Set buffer */ + profile->buffer = part->data; + profile->buffer_size = part->size; + + /* Parse */ + gsicc_init_profile_info(profile); + + /* Problem with profile. Don't fail, just use the default */ + if (profile->profile_handle == NULL) + { + gsicc_profile_reference(profile, -1); + gs_warn1("there was a problem with the profile: %s", partname); + return NULL; + } + + /* Create a new colorspace and associate with the profile */ + gs_cspace_build_ICC(&space, NULL, ctx->memory); + space->cmm_icc_profile_data = profile; + + /* Steal the buffer data before freeing the part */ + part->data = NULL; + xps_free_part(ctx, part); + + /* Add colorspace to xps color cache. */ + xps_hash_insert(ctx, ctx->colorspace_table, xps_strdup(ctx, partname), space); + } + + return space; +} + +int +xps_parse_solid_color_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node) +{ + char *opacity_att; + char *color_att; + gs_color_space *colorspace; + float samples[32]; + + color_att = xps_att(node, "Color"); + opacity_att = xps_att(node, "Opacity"); + + colorspace = ctx->srgb; + samples[0] = 1.0; + samples[1] = 0.0; + samples[2] = 0.0; + samples[3] = 0.0; + + if (color_att) + xps_parse_color(ctx, base_uri, color_att, &colorspace, samples); + + if (opacity_att) + samples[0] = atof(opacity_att); + + xps_set_color(ctx, colorspace, samples); + + xps_fill(ctx); + + return 0; +} diff --git a/xps/xpscommon.c b/xps/xpscommon.c new file mode 100644 index 00000000..6908b3c5 --- /dev/null +++ b/xps/xpscommon.c @@ -0,0 +1,115 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - common parse functions */ + +#include "ghostxps.h" + +int +xps_parse_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node) +{ + if (!strcmp(xps_tag(node), "SolidColorBrush")) + return xps_parse_solid_color_brush(ctx, base_uri, dict, node); + if (!strcmp(xps_tag(node), "ImageBrush")) + { + int code = xps_parse_image_brush(ctx, base_uri, dict, node); + if (code) + gs_catch(code, "ignoring error in image brush"); + return gs_okay; + } + if (!strcmp(xps_tag(node), "VisualBrush")) + return xps_parse_visual_brush(ctx, base_uri, dict, node); + if (!strcmp(xps_tag(node), "LinearGradientBrush")) + return xps_parse_linear_gradient_brush(ctx, base_uri, dict, node); + if (!strcmp(xps_tag(node), "RadialGradientBrush")) + return xps_parse_radial_gradient_brush(ctx, base_uri, dict, node); + return gs_throw1(-1, "unknown brush tag: %s", xps_tag(node)); +} + +int +xps_parse_element(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node) +{ + if (!strcmp(xps_tag(node), "Path")) + return xps_parse_path(ctx, base_uri, dict, node); + if (!strcmp(xps_tag(node), "Glyphs")) + return xps_parse_glyphs(ctx, base_uri, dict, node); + if (!strcmp(xps_tag(node), "Canvas")) + return xps_parse_canvas(ctx, base_uri, dict, node); + /* skip unknown tags (like Foo.Resources and similar) */ + return 0; +} + +void +xps_parse_render_transform(xps_context_t *ctx, char *transform, gs_matrix *matrix) +{ + float args[6]; + char *s = transform; + int i; + + args[0] = 1.0; args[1] = 0.0; + args[2] = 0.0; args[3] = 1.0; + args[4] = 0.0; args[5] = 0.0; + + for (i = 0; i < 6 && *s; i++) + { + args[i] = atof(s); + while (*s && *s != ',') + s++; + if (*s == ',') + s++; + } + + matrix->xx = args[0]; matrix->xy = args[1]; + matrix->yx = args[2]; matrix->yy = args[3]; + matrix->tx = args[4]; matrix->ty = args[5]; +} + +void +xps_parse_matrix_transform(xps_context_t *ctx, xps_item_t *root, gs_matrix *matrix) +{ + char *transform; + + gs_make_identity(matrix); + + if (!strcmp(xps_tag(root), "MatrixTransform")) + { + transform = xps_att(root, "Matrix"); + if (transform) + xps_parse_render_transform(ctx, transform, matrix); + } +} + +void +xps_parse_rectangle(xps_context_t *ctx, char *text, gs_rect *rect) +{ + float args[4]; + char *s = text; + int i; + + args[0] = 0.0; args[1] = 0.0; + args[2] = 1.0; args[3] = 1.0; + + for (i = 0; i < 4 && *s; i++) + { + args[i] = atof(s); + while (*s && *s != ',') + s++; + if (*s == ',') + s++; + } + + rect->p.x = args[0]; + rect->p.y = args[1]; + rect->q.x = args[0] + args[2]; + rect->q.y = args[1] + args[3]; +} diff --git a/xps/xpscrc.c b/xps/xpscrc.c new file mode 100644 index 00000000..7dea3c32 --- /dev/null +++ b/xps/xpscrc.c @@ -0,0 +1,95 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - CRC-32 implementation */ + +#include "ghostxps.h" + +static const unsigned long crctab[256] = +{ + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +#define DO1(buf) crc = crctab[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); + +unsigned int +xps_crc32(unsigned int crc, unsigned char *buf, int len) +{ + if (buf == NULL) + return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) + { + DO8(buf); + len -= 8; + } + if (len) + { + do { DO1(buf); } while (--len); + } + return crc ^ 0xffffffffL; +} diff --git a/xps/xpsdoc.c b/xps/xpsdoc.c new file mode 100644 index 00000000..3e9f7c96 --- /dev/null +++ b/xps/xpsdoc.c @@ -0,0 +1,277 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - document parsing */ + +#include "ghostxps.h" + +#include + +xps_part_t * +xps_new_part(xps_context_t *ctx, char *name, int size) +{ + xps_part_t *part; + + part = xps_alloc(ctx, sizeof(xps_part_t)); + part->name = xps_strdup(ctx, name); + part->size = size; + part->data = xps_alloc(ctx, size); + + return part; +} + +void +xps_free_part(xps_context_t *ctx, xps_part_t *part) +{ + xps_free(ctx, part->name); + xps_free(ctx, part->data); + xps_free(ctx, part); +} + +/* + * The FixedDocumentSequence and FixedDocument parts determine + * which parts correspond to actual pages, and the page order. + */ + +void +xps_debug_fixdocseq(xps_context_t *ctx) +{ + xps_document_t *fixdoc = ctx->first_fixdoc; + xps_page_t *page = ctx->first_page; + + if (ctx->start_part) + dprintf1("start part %s\n", ctx->start_part); + + while (fixdoc) + { + dprintf1("fixdoc %s\n", fixdoc->name); + fixdoc = fixdoc->next; + } + + while (page) + { + dprintf3("page %s w=%d h=%d\n", page->name, page->width, page->height); + page = page->next; + } +} + +static void +xps_add_fixed_document(xps_context_t *ctx, char *name) +{ + xps_document_t *fixdoc; + + /* Check for duplicates first */ + for (fixdoc = ctx->first_fixdoc; fixdoc; fixdoc = fixdoc->next) + if (!strcmp(fixdoc->name, name)) + return; + + if_debug1('|', "doc: adding fixdoc %s\n", name); + + fixdoc = xps_alloc(ctx, sizeof(xps_document_t)); + fixdoc->name = xps_strdup(ctx, name); + fixdoc->next = NULL; + + if (!ctx->first_fixdoc) + { + ctx->first_fixdoc = fixdoc; + ctx->last_fixdoc = fixdoc; + } + else + { + ctx->last_fixdoc->next = fixdoc; + ctx->last_fixdoc = fixdoc; + } +} + +void +xps_free_fixed_documents(xps_context_t *ctx) +{ + xps_document_t *node = ctx->first_fixdoc; + while (node) + { + xps_document_t *next = node->next; + xps_free(ctx, node->name); + xps_free(ctx, node); + node = next; + } + ctx->first_fixdoc = NULL; + ctx->last_fixdoc = NULL; +} + +static void +xps_add_fixed_page(xps_context_t *ctx, char *name, int width, int height) +{ + xps_page_t *page; + + /* Check for duplicates first */ + for (page = ctx->first_page; page; page = page->next) + if (!strcmp(page->name, name)) + return; + + if_debug1('|', "doc: adding page %s\n", name); + + page = xps_alloc(ctx, sizeof(xps_page_t)); + page->name = xps_strdup(ctx, name); + page->width = width; + page->height = height; + page->next = NULL; + + if (!ctx->first_page) + { + ctx->first_page = page; + ctx->last_page = page; + } + else + { + ctx->last_page->next = page; + ctx->last_page = page; + } +} + +void +xps_free_fixed_pages(xps_context_t *ctx) +{ + xps_page_t *node = ctx->first_page; + while (node) + { + xps_page_t *next = node->next; + xps_free(ctx, node->name); + xps_free(ctx, node); + node = next; + } + ctx->first_page = NULL; + ctx->last_page = NULL; +} + +/* + * Parse the fixed document sequence structure and _rels/.rels to find the + * start part. We hook up unique expat handlers for this, since we don't need + * the full document model. + */ + +static void +xps_parse_metadata_imp(void *zp, char *name, char **atts) +{ + xps_context_t *ctx = zp; + int i; + + if (!strcmp(name, "Relationship")) + { + char tgtbuf[1024]; + char *target = NULL; + char *type = NULL; + + for (i = 0; atts[i]; i += 2) + { + if (!strcmp(atts[i], "Target")) + target = atts[i + 1]; + if (!strcmp(atts[i], "Type")) + type = atts[i + 1]; + } + + if (target && type) + { + xps_absolute_path(tgtbuf, ctx->base_uri, target, sizeof tgtbuf); + if (!strcmp(type, REL_START_PART)) + ctx->start_part = xps_strdup(ctx, tgtbuf); + } + } + + if (!strcmp(name, "DocumentReference")) + { + char *source = NULL; + char srcbuf[1024]; + + for (i = 0; atts[i]; i += 2) + { + if (!strcmp(atts[i], "Source")) + source = atts[i + 1]; + } + + if (source) + { + xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf); + xps_add_fixed_document(ctx, srcbuf); + } + } + + if (!strcmp(name, "PageContent")) + { + char *source = NULL; + char srcbuf[1024]; + int width = 0; + int height = 0; + + for (i = 0; atts[i]; i += 2) + { + if (!strcmp(atts[i], "Source")) + source = atts[i + 1]; + if (!strcmp(atts[i], "Width")) + width = atoi(atts[i + 1]); + if (!strcmp(atts[i], "Height")) + height = atoi(atts[i + 1]); + } + + if (source) + { + xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf); + xps_add_fixed_page(ctx, srcbuf, width, height); + } + } +} + +int +xps_parse_metadata(xps_context_t *ctx, xps_part_t *part) +{ + XML_Parser xp; + int code; + char buf[1024]; + char *s; + + /* Save directory name part */ + xps_strlcpy(buf, part->name, sizeof buf); + s = strrchr(buf, '/'); + if (s) + s[0] = 0; + + /* _rels parts are voodoo: their URI references are from + * the part they are associated with, not the actual _rels + * part being parsed. + */ + s = strstr(buf, "/_rels"); + if (s) + *s = 0; + + ctx->base_uri = buf; + ctx->part_uri = part->name; + + xp = XML_ParserCreate(NULL); + if (!xp) + return gs_throw(-1, "cannot create XML parser"); + + XML_SetUserData(xp, ctx); + XML_SetParamEntityParsing(xp, XML_PARAM_ENTITY_PARSING_NEVER); + XML_SetStartElementHandler(xp, (XML_StartElementHandler)xps_parse_metadata_imp); + + code = XML_Parse(xp, (char*)part->data, part->size, 1); + + XML_ParserFree(xp); + + ctx->base_uri = NULL; + ctx->part_uri = NULL; + + if (code == 0) + return gs_throw1(-1, "cannot parse XML in part: %s", part->name); + + return 0; +} diff --git a/xps/xpsfont.c b/xps/xpsfont.c new file mode 100644 index 00000000..93adf71d --- /dev/null +++ b/xps/xpsfont.c @@ -0,0 +1,533 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - general font functions */ + +#include "ghostxps.h" + +static void xps_load_sfnt_cmap(xps_font_t *font); + +/* + * Big-endian memory accessor functions + */ + +static inline int s16(byte *p) +{ + return (signed short)( (p[0] << 8) | p[1] ); +} + +static inline int u16(byte *p) +{ + return (p[0] << 8) | p[1]; +} + +static inline int u24(byte *p) +{ + return (p[0] << 16) | (p[1] << 8) | p[2]; +} + +static inline int u32(byte *p) +{ + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; +} + +xps_font_t * +xps_new_font(xps_context_t *ctx, byte *buf, int buflen, int index) +{ + xps_font_t *font; + int code; + + font = xps_alloc(ctx, sizeof(xps_font_t)); + if (!font) + { + gs_throw(-1, "out of memory"); + return NULL; + } + + font->data = buf; + font->length = buflen; + font->font = NULL; + + font->subfontid = index; + font->cmaptable = 0; + font->cmapsubcount = 0; + font->cmapsubtable = 0; + font->usepua = 0; + + font->cffdata = 0; + font->cffend = 0; + font->gsubrs = 0; + font->subrs = 0; + font->charstrings = 0; + + if (memcmp(font->data, "OTTO", 4) == 0) + code = xps_init_postscript_font(ctx, font); + else if (memcmp(font->data, "\0\1\0\0", 4) == 0) + code = xps_init_truetype_font(ctx, font); + else if (memcmp(font->data, "true", 4) == 0) + code = xps_init_truetype_font(ctx, font); + else if (memcmp(font->data, "ttcf", 4) == 0) + code = xps_init_truetype_font(ctx, font); + else + { + xps_free_font(ctx, font); + gs_throw(-1, "not an opentype font"); + return NULL; + } + + if (code < 0) + { + xps_free_font(ctx, font); + gs_rethrow(-1, "cannot init font"); + return NULL; + } + + xps_load_sfnt_cmap(font); + + return font; +} + +void +xps_free_font(xps_context_t *ctx, xps_font_t *font) +{ + if (font->font) + { + gs_font_finalize(font->font); + gs_free_object(ctx->memory, font->font, "font object"); + } + xps_free(ctx, font); +} + +/* + * Find the offset and length of an SFNT table. + * Return -1 if no table by the specified name is found. + */ + +int +xps_find_sfnt_table(xps_font_t *font, const char *name, int *lengthp) +{ + int offset; + int ntables; + int i; + + if (font->length < 12) + return -1; + + if (!memcmp(font->data, "ttcf", 4)) + { + int nfonts = u32(font->data + 8); + if (font->subfontid < 0 || font->subfontid >= nfonts) + { + gs_warn("Invalid subfont ID"); + return -1; + } + offset = u32(font->data + 12 + font->subfontid * 4); + } + else + { + offset = 0; + } + + ntables = u16(font->data + offset + 4); + if (font->length < offset + 12 + ntables * 16) + return -1; + + for (i = 0; i < ntables; i++) + { + byte *entry = font->data + offset + 12 + i * 16; + if (!memcmp(entry, name, 4)) + { + if (lengthp) + *lengthp = u32(entry + 12); + return u32(entry + 8); + } + } + + return -1; +} + +/* + * Get the windows truetype font file name - position 4 in the name table. + */ +void +xps_load_sfnt_name(xps_font_t *font, char *namep) +{ + byte *namedata; + int offset, length; + int format, count, stringoffset; + int i; + + strcpy(namep, "Unknown"); + + offset = xps_find_sfnt_table(font, "name", &length); + if (offset < 0 || length < 6) + { + gs_warn("cannot find name table"); + return; + } + + namedata = font->data + offset; + + format = u16(namedata + 0); + count = u16(namedata + 2); + stringoffset = u16(namedata + 4); + + for (i = 0; i < count; i++) + { + byte *record = namedata + 6 + i * 12; + int pid = u16(record + 0); + int eid = u16(record + 2); + int langid = u16(record + 4); + int nameid = u16(record + 6); + length = u16(record + 8); + offset = u16(record + 10); + + /* Mac Roman English */ + if (pid == 1 && eid == 0 && langid == 0) + { + /* Full font name or postscript name */ + if (nameid == 4 || nameid == 6) + { + memcpy(namep, namedata + stringoffset + offset, length); + namep[length] = 0; + } + } + } +} + +/* + * Locate the 'cmap' table and count the number of subtables. + */ + +static void +xps_load_sfnt_cmap(xps_font_t *font) +{ + byte *cmapdata; + int offset, length; + int nsubtables; + + offset = xps_find_sfnt_table(font, "cmap", &length); + if (offset < 0 || length < 4) + { + gs_warn("cannot find cmap table"); + return; + } + + cmapdata = font->data + offset; + + nsubtables = u16(cmapdata + 2); + if (nsubtables < 0 || length < 4 + nsubtables * 8) + { + gs_warn("cannot find cmap sub-tables"); + return; + } + + font->cmaptable = offset; + font->cmapsubcount = nsubtables; + font->cmapsubtable = 0; +} + +/* + * Return the number of cmap subtables. + */ + +int +xps_count_font_encodings(xps_font_t *font) +{ + return font->cmapsubcount; +} + +/* + * Extract PlatformID and EncodingID for a cmap subtable. + */ + +void +xps_identify_font_encoding(xps_font_t *font, int idx, int *pid, int *eid) +{ + byte *cmapdata, *entry; + if (idx < 0 || idx >= font->cmapsubcount) + return; + cmapdata = font->data + font->cmaptable; + entry = cmapdata + 4 + idx * 8; + *pid = u16(entry + 0); + *eid = u16(entry + 2); +} + +/* + * Select a cmap subtable for use with encoding functions. + */ + +void +xps_select_font_encoding(xps_font_t *font, int idx) +{ + byte *cmapdata, *entry; + int pid, eid; + if (idx < 0 || idx >= font->cmapsubcount) + return; + cmapdata = font->data + font->cmaptable; + entry = cmapdata + 4 + idx * 8; + pid = u16(entry + 0); + eid = u16(entry + 2); + font->cmapsubtable = font->cmaptable + u32(entry + 4); + font->usepua = (pid == 3 && eid == 0); +} + +/* + * Encode a character using the selected cmap subtable. + * TODO: extend this to cover more cmap formats. + */ + +static int +xps_encode_font_char_imp(xps_font_t *font, int code) +{ + byte *table; + + /* no cmap selected: return identity */ + if (font->cmapsubtable <= 0) + return code; + + table = font->data + font->cmapsubtable; + + switch (u16(table)) + { + case 0: /* Apple standard 1-to-1 mapping. */ + return table[code + 6]; + + case 4: /* Microsoft/Adobe segmented mapping. */ + { + int segCount2 = u16(table + 6); + byte *endCount = table + 14; + byte *startCount = endCount + segCount2 + 2; + byte *idDelta = startCount + segCount2; + byte *idRangeOffset = idDelta + segCount2; + int i2; + + for (i2 = 0; i2 < segCount2 - 3; i2 += 2) + { + int delta, roff; + int start = u16(startCount + i2); + int glyph; + + if ( code < start ) + return 0; + if ( code > u16(endCount + i2) ) + continue; + delta = s16(idDelta + i2); + roff = s16(idRangeOffset + i2); + if ( roff == 0 ) + { + return ( code + delta ) & 0xffff; /* mod 65536 */ + return 0; + } + glyph = u16(idRangeOffset + i2 + roff + ((code - start) << 1)); + return (glyph == 0 ? 0 : glyph + delta); + } + + /* + * The TrueType documentation says that the last range is + * always supposed to end with 0xffff, so this shouldn't + * happen; however, in some real fonts, it does. + */ + return 0; + } + + case 6: /* Single interval lookup. */ + { + int firstCode = u16(table + 6); + int entryCount = u16(table + 8); + if ( code < firstCode || code >= firstCode + entryCount ) + return 0; + return u16(table + 10 + ((code - firstCode) << 1)); + } + + case 10: /* Trimmed array (like 6) */ + { + int startCharCode = u32(table + 12); + int numChars = u32(table + 16); + if ( code < startCharCode || code >= startCharCode + numChars ) + return 0; + return u32(table + 20 + (code - startCharCode) * 4); + } + + case 12: /* Segmented coverage. (like 4) */ + { + int nGroups = u32(table + 12); + byte *group = table + 16; + int i; + + for (i = 0; i < nGroups; i++) + { + int startCharCode = u32(group + 0); + int endCharCode = u32(group + 4); + int startGlyphID = u32(group + 8); + if ( code < startCharCode ) + return 0; + if ( code <= endCharCode ) + return startGlyphID + (code - startCharCode); + group += 12; + } + + return 0; + } + + case 2: /* High-byte mapping through table. */ + case 8: /* Mixed 16-bit and 32-bit coverage (like 2) */ + default: + gs_warn1("unknown cmap format: %d\n", u16(table)); + return 0; + } + + return 0; +} + +int +xps_encode_font_char(xps_font_t *font, int code) +{ + int gid = xps_encode_font_char_imp(font, code); + if (gid == 0 && font->usepua) + gid = xps_encode_font_char_imp(font, 0xF000 | code); + return gid; +} + +/* + * Get glyph metrics by parsing TTF tables manually. + * XPS needs more and different metrics than postscript/ghostscript + * use so the native ghostscript functions are not adequate. + */ + +void +xps_measure_font_glyph(xps_context_t *ctx, xps_font_t *font, int gid, xps_glyph_metrics_t *mtx) +{ + + int head, format, loca, glyf; + int ofs, len; + int idx, i, n; + int hadv, vadv, vorg; + int vtop, ymax, desc; + int scale; + + /* some insane defaults */ + + scale = 1000; /* units-per-em */ + hadv = 500; + vadv = -1000; + vorg = 1000; + + /* + * Horizontal metrics are easy. + */ + + ofs = xps_find_sfnt_table(font, "hhea", &len); + if (ofs < 0 || len < 2 * 18) + { + gs_warn("hhea table is too short"); + return; + } + + vorg = s16(font->data + ofs + 4); /* ascender is default vorg */ + desc = s16(font->data + ofs + 6); /* descender */ + if (desc < 0) + desc = -desc; + n = u16(font->data + ofs + 17 * 2); + + ofs = xps_find_sfnt_table(font, "hmtx", &len); + if (ofs < 0) + { + gs_warn("cannot find hmtx table"); + return; + } + + idx = gid; + if (idx > n - 1) + idx = n - 1; + + hadv = u16(font->data + ofs + idx * 4); + vadv = 0; + + /* + * Vertical metrics are hairy (with missing tables). + */ + + head = xps_find_sfnt_table(font, "head", &len); + if (head > 0) + { + scale = u16(font->data + head + 18); /* units per em */ + } + + ofs = xps_find_sfnt_table(font, "OS/2", &len); + if (ofs > 0 && len > 70) + { + vorg = s16(font->data + ofs + 68); /* sTypoAscender */ + desc = s16(font->data + ofs + 70); /* sTypoDescender */ + if (desc < 0) + desc = -desc; + } + + ofs = xps_find_sfnt_table(font, "vhea", &len); + if (ofs > 0 && len >= 2 * 18) + { + n = u16(font->data + ofs + 17 * 2); + + ofs = xps_find_sfnt_table(font, "vmtx", &len); + if (ofs < 0) + { + gs_warn("cannot find vmtx table"); + return; + } + + idx = gid; + if (idx > n - 1) + idx = n - 1; + + vadv = u16(font->data + ofs + idx * 4); + vtop = u16(font->data + ofs + idx * 4 + 2); + + glyf = xps_find_sfnt_table(font, "glyf", &len); + loca = xps_find_sfnt_table(font, "loca", &len); + if (head > 0 && glyf > 0 && loca > 0) + { + format = u16(font->data + head + 50); /* indexToLocaFormat */ + + if (format == 0) + ofs = u16(font->data + loca + gid * 2) * 2; + else + ofs = u32(font->data + loca + gid * 4); + + ymax = u16(font->data + glyf + ofs + 8); /* yMax */ + + vorg = ymax + vtop; + } + } + + ofs = xps_find_sfnt_table(font, "VORG", &len); + if (ofs > 0) + { + vorg = u16(font->data + ofs + 6); + n = u16(font->data + ofs + 6); + for (i = 0; i < n; i++) + { + if (u16(font->data + ofs + 8 + 4 * i) == gid) + { + vorg = s16(font->data + ofs + 8 + 4 * i + 2); + break; + } + } + } + + if (vadv == 0) + vadv = vorg + desc; + + mtx->hadv = hadv / (float) scale; + mtx->vadv = vadv / (float) scale; + mtx->vorg = vorg / (float) scale; +} diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c new file mode 100644 index 00000000..4709c94b --- /dev/null +++ b/xps/xpsglyphs.c @@ -0,0 +1,675 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - text drawing support */ + +#include "ghostxps.h" + +#include + +#define XPS_TEXT_BUFFER_SIZE 300 + +typedef struct xps_text_buffer_s xps_text_buffer_t; + +struct xps_text_buffer_s +{ + int count; + float x[XPS_TEXT_BUFFER_SIZE + 1]; + float y[XPS_TEXT_BUFFER_SIZE + 1]; + gs_glyph g[XPS_TEXT_BUFFER_SIZE]; +}; + +static inline int unhex(int i) +{ + if (isdigit(i)) + return i - '0'; + return tolower(i) - 'a' + 10; +} + +void +xps_debug_path(xps_context_t *ctx) +{ + segment *seg; + curve_segment *cseg; + + seg = (segment*)ctx->pgs->path->first_subpath; + while (seg) + { + switch (seg->type) + { + case s_start: + dprintf2("%g %g moveto\n", + fixed2float(seg->pt.x) * 0.001, + fixed2float(seg->pt.y) * 0.001); + break; + case s_line: + dprintf2("%g %g lineto\n", + fixed2float(seg->pt.x) * 0.001, + fixed2float(seg->pt.y) * 0.001); + break; + case s_line_close: + dputs("closepath\n"); + break; + case s_curve: + cseg = (curve_segment*)seg; + dprintf6("%g %g %g %g %g %g curveto\n", + fixed2float(cseg->p1.x) * 0.001, + fixed2float(cseg->p1.y) * 0.001, + fixed2float(cseg->p2.x) * 0.001, + fixed2float(cseg->p2.y) * 0.001, + fixed2float(seg->pt.x) * 0.001, + fixed2float(seg->pt.y) * 0.001); + break; + } + seg = seg->next; + } +} + +/* + * Some fonts in XPS are obfuscated by XOR:ing the first 32 bytes of the + * data with the GUID in the fontname. + */ +static void +xps_deobfuscate_font_resource(xps_context_t *ctx, xps_part_t *part) +{ + byte buf[33]; + byte key[16]; + char *p; + int i; + + p = strrchr(part->name, '/'); + if (!p) + p = part->name; + + for (i = 0; i < 32 && *p; p++) + { + if (isxdigit(*p)) + buf[i++] = *p; + } + buf[i] = 0; + + if (i != 32) + { + gs_warn("cannot extract GUID from obfuscated font part name"); + return; + } + + for (i = 0; i < 16; i++) + key[i] = unhex(buf[i*2+0]) * 16 + unhex(buf[i*2+1]); + + for (i = 0; i < 16; i++) + { + part->data[i] ^= key[15-i]; + part->data[i+16] ^= key[15-i]; + } +} + +static void +xps_select_best_font_encoding(xps_font_t *font) +{ + static struct { int pid, eid; } xps_cmap_list[] = + { + { 3, 10 }, /* Unicode with surrogates */ + { 3, 1 }, /* Unicode without surrogates */ + { 3, 5 }, /* Wansung */ + { 3, 4 }, /* Big5 */ + { 3, 3 }, /* Prc */ + { 3, 2 }, /* ShiftJis */ + { 3, 0 }, /* Symbol */ + // { 0, * }, -- Unicode (deprecated) + { 1, 0 }, + { -1, -1 }, + }; + + int i, k, n, pid, eid; + + n = xps_count_font_encodings(font); + for (k = 0; xps_cmap_list[k].pid != -1; k++) + { + for (i = 0; i < n; i++) + { + xps_identify_font_encoding(font, i, &pid, &eid); + if (pid == xps_cmap_list[k].pid && eid == xps_cmap_list[k].eid) + { + xps_select_font_encoding(font, i); + return; + } + } + } + + gs_warn("could not find a suitable cmap"); +} + +/* + * Call text drawing primitives. + */ + +static int +xps_flush_text_buffer(xps_context_t *ctx, xps_font_t *font, + xps_text_buffer_t *buf, int is_charpath) +{ + gs_text_params_t params; + gs_text_enum_t *textenum; + float x = buf->x[0]; + float y = buf->y[0]; + int code; + int i; + + // dprintf1("flushing text buffer (%d glyphs)\n", buf->count); + + gs_moveto(ctx->pgs, x, y); + + params.operation = TEXT_FROM_GLYPHS | TEXT_REPLACE_WIDTHS; + if (is_charpath) + params.operation |= TEXT_DO_FALSE_CHARPATH; + else + params.operation |= TEXT_DO_DRAW; + params.data.glyphs = buf->g; + params.size = buf->count; + params.x_widths = buf->x + 1; + params.y_widths = buf->y + 1; + params.widths_size = buf->count; + + for (i = 0; i < buf->count; i++) + { + buf->x[i] = buf->x[i] - x; + buf->y[i] = buf->y[i] - y; + x += buf->x[i]; + y += buf->y[i]; + } + buf->x[buf->count] = 0; + buf->y[buf->count] = 0; + + code = gs_text_begin(ctx->pgs, ¶ms, ctx->memory, &textenum); + if (code != 0) + return gs_throw1(-1, "cannot gs_text_begin() (%d)", code); + + code = gs_text_process(textenum); + + if (code != 0) + return gs_throw1(-1, "cannot gs_text_process() (%d)", code); + + gs_text_release(textenum, "gslt font render"); + + buf->count = 0; + + return 0; +} + +/* + * Parse and draw an XPS element. + * + * Indices syntax: + + GlyphIndices = GlyphMapping ( ";" GlyphMapping ) + GlyphMapping = ( [ClusterMapping] GlyphIndex ) [GlyphMetrics] + ClusterMapping = "(" ClusterCodeUnitCount [":" ClusterGlyphCount] ")" + ClusterCodeUnitCount = * DIGIT + ClusterGlyphCount = * DIGIT + GlyphIndex = * DIGIT + GlyphMetrics = "," AdvanceWidth ["," uOffset ["," vOffset]] + AdvanceWidth = ["+"] RealNum + uOffset = ["+" | "-"] RealNum + vOffset = ["+" | "-"] RealNum + RealNum = ((DIGIT ["." DIGIT]) | ("." DIGIT)) [Exponent] + Exponent = ( ("E"|"e") ("+"|"-") DIGIT ) + + */ + +static char * +xps_parse_digits(char *s, int *digit) +{ + *digit = 0; + while (*s >= '0' && *s <= '9') + { + *digit = *digit * 10 + (*s - '0'); + s ++; + } + return s; +} + +static inline int is_real_num_char(int c) +{ + return (c >= '0' && c <= '9') || c == 'e' || c == 'E' || c == '+' || c == '-' || c == '.'; +} + +static char * +xps_parse_real_num(char *s, float *number) +{ + char buf[64]; + char *p = buf; + while (is_real_num_char(*s)) + *p++ = *s++; + *p = 0; + if (buf[0]) + *number = atof(buf); + return s; +} + +static char * +xps_parse_cluster_mapping(char *s, int *code_count, int *glyph_count) +{ + if (*s == '(') + s = xps_parse_digits(s + 1, code_count); + if (*s == ':') + s = xps_parse_digits(s + 1, glyph_count); + if (*s == ')') + s ++; + return s; +} + +static char * +xps_parse_glyph_index(char *s, int *glyph_index) +{ + if (*s >= '0' && *s <= '9') + s = xps_parse_digits(s, glyph_index); + return s; +} + +static char * +xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs) +{ + if (*s == ',') + s = xps_parse_real_num(s + 1, advance); + if (*s == ',') + s = xps_parse_real_num(s + 1, uofs); + if (*s == ',') + s = xps_parse_real_num(s + 1, vofs); + return s; +} + +/* + * Parse unicode and indices strings and encode glyphs. + * Calculate metrics for positioning. + */ +static int +xps_parse_glyphs_imp(xps_context_t *ctx, xps_font_t *font, float size, + float originx, float originy, int is_sideways, int bidi_level, + char *indices, char *unicode, int is_charpath) +{ + xps_text_buffer_t buf; + xps_glyph_metrics_t mtx; + float x = originx; + float y = originy; + char *us = unicode; + char *is = indices; + int un = 0; + int code; + + buf.count = 0; + + if (!unicode && !indices) + return gs_throw(-1, "no text in glyphs element"); + + if (us) + { + if (us[0] == '{' && us[1] == '}') + us = us + 2; + un = strlen(us); + } + + while ((us && un > 0) || (is && *is)) + { + int code_count = 1; + int glyph_count = 1; + + if (is && *is) + { + is = xps_parse_cluster_mapping(is, &code_count, &glyph_count); + } + + if (code_count < 1) + code_count = 1; + if (glyph_count < 1) + glyph_count = 1; + + while (code_count > 0 || glyph_count > 0) + { + int char_code = '?'; + int glyph_index = -1; + float u_offset = 0.0; + float v_offset = 0.0; + float advance; + + if (glyph_count) + { + if (is && *is) + is = xps_parse_glyph_index(is, &glyph_index); + glyph_count --; + } + + if (code_count) + { + if (us && un > 0) + { + int t = xps_utf8_to_ucs(&char_code, us, un); + if (t < 0) + return gs_rethrow(-1, "error decoding UTF-8 string"); + us += t; un -= t; + } + code_count --; + } + + if (glyph_index == -1) + glyph_index = xps_encode_font_char(font, char_code); + + xps_measure_font_glyph(ctx, font, glyph_index, &mtx); + if (is_sideways) + advance = mtx.vadv * 100.0; + else if (bidi_level & 1) + advance = -mtx.hadv * 100.0; + else + advance = mtx.hadv * 100.0; + + if (is && *is) + { + is = xps_parse_glyph_metrics(is, &advance, &u_offset, &v_offset); + if (*is == ';') + is ++; + } + +#if 0 + dprintf6("glyph mapping (%d:%d)%d,%g,%g,%g\n", + code_count, glyph_count, glyph_index, + advance, u_offset, v_offset); +#endif + + if (bidi_level & 1) + u_offset = -mtx.hadv * 100 - u_offset; + + u_offset = u_offset * 0.01 * size; + v_offset = v_offset * 0.01 * size; + + if (buf.count == XPS_TEXT_BUFFER_SIZE) + { + code = xps_flush_text_buffer(ctx, font, &buf, is_charpath); + if (code) + return gs_rethrow(code, "cannot flush buffered text"); + } + + if (is_sideways) + { + buf.x[buf.count] = x + u_offset + (mtx.vorg * size); + buf.y[buf.count] = y - v_offset + (mtx.hadv * 0.5 * size); + } + else + { + buf.x[buf.count] = x + u_offset; + buf.y[buf.count] = y - v_offset; + } + buf.g[buf.count] = glyph_index; + buf.count ++; + + x += advance * 0.01 * size; + } + } + + if (buf.count > 0) + { + code = xps_flush_text_buffer(ctx, font, &buf, is_charpath); + if (code) + return gs_rethrow(code, "cannot flush buffered text"); + } + + return 0; +} + +int +xps_parse_glyphs(xps_context_t *ctx, + char *base_uri, xps_resource_t *dict, xps_item_t *root) +{ + xps_item_t *node; + int code; + + char *fill_uri; + char *opacity_mask_uri; + + char *bidi_level_att; + char *caret_stops_att; + char *fill_att; + char *font_size_att; + char *font_uri_att; + char *origin_x_att; + char *origin_y_att; + char *is_sideways_att; + char *indices_att; + char *unicode_att; + char *style_att; + char *transform_att; + char *clip_att; + char *opacity_att; + char *opacity_mask_att; + + xps_item_t *transform_tag = NULL; + xps_item_t *clip_tag = NULL; + xps_item_t *fill_tag = NULL; + xps_item_t *opacity_mask_tag = NULL; + + char *fill_opacity_att = NULL; + + xps_part_t *part; + xps_font_t *font; + + char partname[1024]; + char *subfont; + + gs_matrix matrix; + float font_size = 10.0; + int subfontid = 0; + int is_sideways = 0; + int bidi_level = 0; + + /* + * Extract attributes and extended attributes. + */ + + bidi_level_att = xps_att(root, "BidiLevel"); + caret_stops_att = xps_att(root, "CaretStops"); + fill_att = xps_att(root, "Fill"); + font_size_att = xps_att(root, "FontRenderingEmSize"); + font_uri_att = xps_att(root, "FontUri"); + origin_x_att = xps_att(root, "OriginX"); + origin_y_att = xps_att(root, "OriginY"); + is_sideways_att = xps_att(root, "IsSideways"); + indices_att = xps_att(root, "Indices"); + unicode_att = xps_att(root, "UnicodeString"); + style_att = xps_att(root, "StyleSimulations"); + transform_att = xps_att(root, "RenderTransform"); + clip_att = xps_att(root, "Clip"); + opacity_att = xps_att(root, "Opacity"); + opacity_mask_att = xps_att(root, "OpacityMask"); + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "Glyphs.RenderTransform")) + transform_tag = xps_down(node); + + if (!strcmp(xps_tag(node), "Glyphs.OpacityMask")) + opacity_mask_tag = xps_down(node); + + if (!strcmp(xps_tag(node), "Glyphs.Clip")) + clip_tag = xps_down(node); + + if (!strcmp(xps_tag(node), "Glyphs.Fill")) + fill_tag = xps_down(node); + } + + fill_uri = base_uri; + opacity_mask_uri = base_uri; + + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &fill_att, &fill_tag, &fill_uri); + xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); + + /* + * Check that we have all the necessary information. + */ + + if (!font_size_att || !font_uri_att || !origin_x_att || !origin_y_att) + return gs_throw(-1, "missing attributes in glyphs element"); + + if (!indices_att && !unicode_att) + return 0; /* nothing to draw */ + + if (is_sideways_att) + is_sideways = !strcmp(is_sideways_att, "true"); + + if (bidi_level_att) + bidi_level = atoi(bidi_level_att); + + /* + * Find and load the font resource + */ + + xps_absolute_path(partname, base_uri, font_uri_att, sizeof partname); + subfont = strrchr(partname, '#'); + if (subfont) + { + subfontid = atoi(subfont + 1); + *subfont = 0; + } + + font = xps_hash_lookup(ctx->font_table, partname); + if (!font) + { + part = xps_read_part(ctx, partname); + if (!part) + return gs_throw1(-1, "cannot find font resource part '%s'", partname); + + /* deobfuscate if necessary */ + if (strstr(part->name, ".odttf")) + xps_deobfuscate_font_resource(ctx, part); + if (strstr(part->name, ".ODTTF")) + xps_deobfuscate_font_resource(ctx, part); + + font = xps_new_font(ctx, part->data, part->size, subfontid); + if (!font) + return gs_rethrow1(-1, "cannot load font resource '%s'", partname); + + xps_select_best_font_encoding(font); + + xps_hash_insert(ctx, ctx->font_table, part->name, font); + + /* NOTE: we kept part->name in the hashtable and part->data in the font */ + xps_free(ctx, part); + } + + /* + * Set up graphics state. + */ + + gs_gsave(ctx->pgs); + + if (transform_att || transform_tag) + { + gs_matrix transform; + + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + + gs_concat(ctx->pgs, &transform); + } + + if (clip_att || clip_tag) + { + if (clip_att) + xps_parse_abbreviated_geometry(ctx, clip_att); + if (clip_tag) + xps_parse_path_geometry(ctx, dict, clip_tag, 0); + xps_clip(ctx); + } + + font_size = atof(font_size_att); + + gs_setfont(ctx->pgs, font->font); + gs_make_scaling(font_size, -font_size, &matrix); + if (is_sideways) + gs_matrix_rotate(&matrix, 90.0, &matrix); + + gs_setcharmatrix(ctx->pgs, &matrix); + + gs_matrix_multiply(&matrix, &font->font->orig_FontMatrix, &font->font->FontMatrix); + + code = xps_begin_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + if (code) + { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot create transparency group"); + } + + /* + * If it's a solid color brush fill/stroke do a simple fill + */ + + if (fill_tag && !strcmp(xps_tag(fill_tag), "SolidColorBrush")) + { + fill_opacity_att = xps_att(fill_tag, "Opacity"); + fill_att = xps_att(fill_tag, "Color"); + fill_tag = NULL; + } + + if (fill_att) + { + float samples[32]; + gs_color_space *colorspace; + xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples); + if (fill_opacity_att) + samples[0] = atof(fill_opacity_att); + xps_set_color(ctx, colorspace, samples); + code = xps_parse_glyphs_imp(ctx, font, font_size, + atof(origin_x_att), atof(origin_y_att), + is_sideways, bidi_level, + indices_att, unicode_att, 0); + if (code) + { + xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot parse glyphs data"); + } + } + + /* + * If it's a visual brush or image, use the charpath as a clip mask to paint brush + */ + + if (fill_tag) + { + ctx->fill_rule = 1; /* always use non-zero winding rule for char paths */ + code = xps_parse_glyphs_imp(ctx, font, font_size, + atof(origin_x_att), atof(origin_y_att), + is_sideways, bidi_level, indices_att, unicode_att, 1); + if (code) + { + xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot parse glyphs data"); + } + + code = xps_parse_brush(ctx, fill_uri, dict, fill_tag); + if (code) + { + xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot parse fill brush"); + } + } + + xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + + gs_grestore(ctx->pgs); + + return 0; +} diff --git a/xps/xpsgradient.c b/xps/xpsgradient.c new file mode 100644 index 00000000..0c6bcf60 --- /dev/null +++ b/xps/xpsgradient.c @@ -0,0 +1,978 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - gradient support */ + +#include "ghostxps.h" + +#define MAX_STOPS 256 + +enum { SPREAD_PAD, SPREAD_REPEAT, SPREAD_REFLECT }; + +/* + * Parse a list of GradientStop elements. + * Fill the offset and color arrays, and + * return the number of stops parsed. + */ + +struct stop +{ + float offset; + float color[4]; +}; + +static int cmp_stop(const void *a, const void *b) +{ + const struct stop *astop = a; + const struct stop *bstop = b; + float diff = astop->offset - bstop->offset; + if (diff < 0) + return -1; + if (diff > 0) + return 1; + return 0; +} + +static inline float lerp(float a, float b, float x) +{ + return a + (b - a) * x; +} + +static int +xps_parse_gradient_stops(xps_context_t *ctx, char *base_uri, xps_item_t *node, + struct stop *stops, int maxcount) +{ + unsigned short sample_in[8], sample_out[8]; /* XPS allows up to 8 bands */ + gsicc_rendering_param_t rendering_params; + gsicc_link_t *icclink; + gs_color_space *colorspace; + float sample[8]; + int before, after; + int count; + int i, k; + + /* We may have to insert 2 extra stops when postprocessing */ + maxcount -= 2; + + count = 0; + while (node && count < maxcount) + { + if (!strcmp(xps_tag(node), "GradientStop")) + { + char *offset = xps_att(node, "Offset"); + char *color = xps_att(node, "Color"); + if (offset && color) + { + stops[count].offset = atof(offset); + + xps_parse_color(ctx, base_uri, color, &colorspace, sample); + + /* Set the rendering parameters */ + rendering_params.black_point_comp = BP_ON; + rendering_params.object_type = GS_PATH_TAG; + rendering_params.rendering_intent = gsPERCEPTUAL; + + /* Get link to map from source to sRGB */ + icclink = gsicc_get_link((gs_imager_state*) ctx->pgs, + NULL, colorspace, ctx->srgb, + &rendering_params, ctx->memory, false); + + if (icclink != NULL && !icclink->is_identity) + { + /* Transform the color */ + int num_colors = gsicc_getsrc_channel_count(colorspace->cmm_icc_profile_data); + for (i = 0; i < num_colors; i++) + { + sample_in[i] = sample[i+1]*65535; + } + gscms_transform_color(icclink, sample_in, sample_out, 2, NULL); + + stops[count].color[0] = sample[0]; /* Alpha */ + stops[count].color[1] = (float) sample_out[0] / 65535.0; /* sRGB */ + stops[count].color[2] = (float) sample_out[1] / 65535.0; + stops[count].color[3] = (float) sample_out[2] / 65535.0; + } + else + { + stops[count].color[0] = sample[0]; + stops[count].color[1] = sample[1]; + stops[count].color[2] = sample[2]; + stops[count].color[3] = sample[3]; + } + + count ++; + } + } + + if (icclink != NULL) + gsicc_release_link(icclink); + icclink = NULL; + node = xps_next(node); + + } + + if (count == 0) + { + gs_warn("gradient brush has no gradient stops"); + stops[0].offset = 0; + stops[0].color[0] = 1; + stops[0].color[1] = 0; + stops[0].color[2] = 0; + stops[0].color[3] = 0; + stops[1].offset = 1; + stops[1].color[0] = 1; + stops[1].color[1] = 1; + stops[1].color[2] = 1; + stops[1].color[3] = 1; + return 2; + } + + if (count == maxcount) + gs_warn("gradient brush exceeded maximum number of gradient stops"); + + /* Postprocess to make sure the range of offsets is 0.0 to 1.0 */ + + qsort(stops, count, sizeof(struct stop), cmp_stop); + + before = -1; + after = -1; + + for (i = 0; i < count; i++) + { + if (stops[i].offset < 0) + before = i; + if (stops[i].offset > 1) + { + after = i; + break; + } + } + + /* Remove all stops < 0 except the largest one */ + if (before > 0) + { + memmove(stops, stops + before, (count - before) * sizeof(struct stop)); + count -= before; + } + + /* Remove all stops > 1 except the smallest one */ + if (after >= 0) + count = after + 1; + + /* Expand single stop to 0 .. 1 */ + if (count == 1) + { + stops[1] = stops[0]; + stops[0].offset = 0; + stops[1].offset = 1; + return 2; + } + + /* First stop < 0 -- interpolate value to 0 */ + if (stops[0].offset < 0) + { + float d = -stops[0].offset / (stops[1].offset - stops[0].offset); + stops[0].offset = 0; + for (k = 0; k < 4; k++) + stops[0].color[k] = lerp(stops[0].color[k], stops[1].color[k], d); + } + + /* Last stop > 1 -- interpolate value to 1 */ + if (stops[count-1].offset > 1) + { + float d = (1 - stops[count-2].offset) / (stops[count-1].offset - stops[count-2].offset); + stops[count-1].offset = 1; + for (k = 0; k < 4; k++) + stops[count-1].color[k] = lerp(stops[count-2].color[k], stops[count-1].color[k], d); + } + + /* First stop > 0 -- insert a duplicate at 0 */ + if (stops[0].offset > 0) + { + memmove(stops + 1, stops, count * sizeof(struct stop)); + stops[0] = stops[1]; + stops[0].offset = 0; + count++; + } + + /* Last stop < 1 -- insert a duplicate at 1 */ + if (stops[count-1].offset < 1) + { + stops[count] = stops[count-1]; + stops[count].offset = 1; + count++; + } + + return count; +} + +static int +xps_gradient_has_transparent_colors(struct stop *stops, int count) +{ + int i; + for (i = 0; i < count; i++) + if (stops[i].color[0] < 1) + return 1; + return 0; +} + +/* + * Create a Function object to map [0..1] to RGB colors + * based on the gradient stop arrays. + * + * We do this by creating a stitching function that joins + * a series of linear functions (one linear function + * for each gradient stop-pair). + */ + +static gs_function_t * +xps_create_gradient_stop_function(xps_context_t *ctx, struct stop *stops, int count, int opacity_only) +{ + gs_function_1ItSg_params_t sparams; + gs_function_ElIn_params_t lparams; + gs_function_t *sfunc; + gs_function_t *lfunc; + + float *domain, *range, *c0, *c1, *bounds, *encode; + const gs_function_t **functions; + + int code; + int k; + int i; + + k = count - 1; /* number of intervals / functions */ + + domain = xps_alloc(ctx, 2 * sizeof(float)); + domain[0] = 0.0; + domain[1] = 1.0; + sparams.m = 1; + sparams.Domain = domain; + + range = xps_alloc(ctx, 6 * sizeof(float)); + range[0] = 0.0; + range[1] = 1.0; + range[2] = 0.0; + range[3] = 1.0; + range[4] = 0.0; + range[5] = 1.0; + sparams.n = 3; + sparams.Range = range; + + functions = xps_alloc(ctx, k * sizeof(void*)); + bounds = xps_alloc(ctx, (k - 1) * sizeof(float)); + encode = xps_alloc(ctx, (k * 2) * sizeof(float)); + + sparams.k = k; + sparams.Functions = functions; + sparams.Bounds = bounds; + sparams.Encode = encode; + + for (i = 0; i < k; i++) + { + domain = xps_alloc(ctx, 2 * sizeof(float)); + domain[0] = 0.0; + domain[1] = 1.0; + lparams.m = 1; + lparams.Domain = domain; + + range = xps_alloc(ctx, 6 * sizeof(float)); + range[0] = 0.0; + range[1] = 1.0; + range[2] = 0.0; + range[3] = 1.0; + range[4] = 0.0; + range[5] = 1.0; + lparams.n = 3; + lparams.Range = range; + + c0 = xps_alloc(ctx, 3 * sizeof(float)); + lparams.C0 = c0; + + c1 = xps_alloc(ctx, 3 * sizeof(float)); + lparams.C1 = c1; + + if (opacity_only) + { + c0[0] = stops[i].color[0]; + c0[1] = stops[i].color[0]; + c0[2] = stops[i].color[0]; + + c1[0] = stops[i+1].color[0]; + c1[1] = stops[i+1].color[0]; + c1[2] = stops[i+1].color[0]; + } + else + { + c0[0] = stops[i].color[1]; + c0[1] = stops[i].color[2]; + c0[2] = stops[i].color[3]; + + c1[0] = stops[i+1].color[1]; + c1[1] = stops[i+1].color[2]; + c1[2] = stops[i+1].color[3]; + } + + lparams.N = 1; + + code = gs_function_ElIn_init(&lfunc, &lparams, ctx->memory); + if (code < 0) + { + gs_rethrow(code, "gs_function_ElIn_init failed"); + return NULL; + } + + functions[i] = lfunc; + + if (i > 0) + bounds[i - 1] = stops[i].offset; + + encode[i * 2 + 0] = 0.0; + encode[i * 2 + 1] = 1.0; + } + + code = gs_function_1ItSg_init(&sfunc, &sparams, ctx->memory); + if (code < 0) + { + gs_rethrow(code, "gs_function_1ItSg_init failed"); + return NULL; + } + + return sfunc; +} + +/* + * Shadings and functions are ghostscript type objects, + * and as such rely on the garbage collector for cleanup. + * We can't have none of that here, so we have to + * write our own destructors. + */ + +static void +xps_free_gradient_stop_function(xps_context_t *ctx, gs_function_t *func) +{ + gs_function_t *lfunc; + gs_function_1ItSg_params_t *sparams; + gs_function_ElIn_params_t *lparams; + int i; + + sparams = (gs_function_1ItSg_params_t*) &func->params; + xps_free(ctx, (void*)sparams->Domain); + xps_free(ctx, (void*)sparams->Range); + + for (i = 0; i < sparams->k; i++) + { + lfunc = (gs_function_t*) sparams->Functions[i]; /* discard const */ + lparams = (gs_function_ElIn_params_t*) &lfunc->params; + xps_free(ctx, (void*)lparams->Domain); + xps_free(ctx, (void*)lparams->Range); + xps_free(ctx, (void*)lparams->C0); + xps_free(ctx, (void*)lparams->C1); + xps_free(ctx, lfunc); + } + + xps_free(ctx, (void*)sparams->Bounds); + xps_free(ctx, (void*)sparams->Encode); + xps_free(ctx, (void*)sparams->Functions); + xps_free(ctx, func); +} + +/* + * For radial gradients that have a cone drawing we have to + * reverse the direction of the gradient because we draw + * the shading in the opposite direction with the + * big circle first. + */ +static gs_function_t * +xps_reverse_function(xps_context_t *ctx, gs_function_t *func, float *fary, void *vary) +{ + gs_function_1ItSg_params_t sparams; + gs_function_t *sfunc; + int code; + + /* take from stack allocated arrays that the caller provides */ + float *domain = fary + 0; + float *range = fary + 2; + float *encode = fary + 2 + 6; + const gs_function_t **functions = vary; + + domain[0] = 0.0; + domain[1] = 1.0; + + range[0] = 0.0; + range[1] = 1.0; + range[2] = 0.0; + range[3] = 1.0; + range[4] = 0.0; + range[5] = 1.0; + + functions[0] = func; + + encode[0] = 1.0; + encode[1] = 0.0; + + sparams.m = 1; + sparams.Domain = domain; + sparams.n = 3; + sparams.Range = range; + sparams.k = 1; + sparams.Functions = functions; + sparams.Bounds = NULL; + sparams.Encode = encode; + + code = gs_function_1ItSg_init(&sfunc, &sparams, ctx->memory); + if (code < 0) + { + gs_rethrow(code, "gs_function_1ItSg_init failed"); + return NULL; + } + + return sfunc; +} + +/* + * Radial gradients map more or less to Radial shadings. + * The inner circle is always a point. + * The outer circle is actually an ellipse, + * mess with the transform to squash the circle into the right aspect. + */ + +static int +xps_draw_one_radial_gradient(xps_context_t *ctx, + gs_function_t *func, int extend, + float x0, float y0, float r0, + float x1, float y1, float r1) +{ + gs_memory_t *mem = ctx->memory; + gs_shading_t *shading; + gs_shading_R_params_t params; + int code; + + gs_shading_R_params_init(¶ms); + { + params.ColorSpace = ctx->srgb; + + params.Coords[0] = x0; + params.Coords[1] = y0; + params.Coords[2] = r0; + params.Coords[3] = x1; + params.Coords[4] = y1; + params.Coords[5] = r1; + + params.Extend[0] = extend; + params.Extend[1] = extend; + + params.Function = func; + } + + code = gs_shading_R_init(&shading, ¶ms, mem); + if (code < 0) + return gs_rethrow(code, "gs_shading_R_init failed"); + + gs_setsmoothness(ctx->pgs, 0.02); + + code = gs_shfill(ctx->pgs, shading); + if (code < 0) + { + gs_free_object(mem, shading, "gs_shading_R"); + return gs_rethrow(code, "gs_shfill failed"); + } + + gs_free_object(mem, shading, "gs_shading_R"); + + return 0; +} + +/* + * Linear gradients map to Axial shadings. + */ + +static int +xps_draw_one_linear_gradient(xps_context_t *ctx, + gs_function_t *func, int extend, + float x0, float y0, float x1, float y1) +{ + gs_memory_t *mem = ctx->memory; + gs_shading_t *shading; + gs_shading_A_params_t params; + int code; + + gs_shading_A_params_init(¶ms); + { + params.ColorSpace = ctx->srgb; + + params.Coords[0] = x0; + params.Coords[1] = y0; + params.Coords[2] = x1; + params.Coords[3] = y1; + + params.Extend[0] = extend; + params.Extend[1] = extend; + + params.Function = func; + } + + code = gs_shading_A_init(&shading, ¶ms, mem); + if (code < 0) + return gs_rethrow(code, "gs_shading_A_init failed"); + + gs_setsmoothness(ctx->pgs, 0.02); + + code = gs_shfill(ctx->pgs, shading); + if (code < 0) + { + gs_free_object(mem, shading, "gs_shading_A"); + return gs_rethrow(code, "gs_shfill failed"); + } + + gs_free_object(mem, shading, "gs_shading_A"); + + return 0; +} + +/* + * We need to loop and create many shading objects to account + * for the Repeat and Reflect SpreadMethods. + * I'm not smart enough to calculate this analytically + * so we iterate and check each object until we + * reach a reasonable limit for infinite cases. + */ + +static inline float point_inside_circle(float px, float py, float x, float y, float r) +{ + float dx = px - x; + float dy = py - y; + return (dx * dx + dy * dy) <= (r * r); +} + +static int +xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_function_t *func) +{ + gs_rect bbox; + float x0, y0, r0; + float x1, y1, r1; + float xrad = 1; + float yrad = 1; + float invscale; + float dx, dy; + int code; + int i; + int done; + + char *center_att = xps_att(root, "Center"); + char *origin_att = xps_att(root, "GradientOrigin"); + char *radius_x_att = xps_att(root, "RadiusX"); + char *radius_y_att = xps_att(root, "RadiusY"); + + if (origin_att) + sscanf(origin_att, "%g,%g", &x0, &y0); + if (center_att) + sscanf(center_att, "%g,%g", &x1, &y1); + if (radius_x_att) + xrad = atof(radius_x_att); + if (radius_y_att) + yrad = atof(radius_y_att); + + /* scale the ctm to make ellipses */ + gs_gsave(ctx->pgs); + gs_scale(ctx->pgs, 1.0, yrad / xrad); + + invscale = xrad / yrad; + y0 = y0 * invscale; + y1 = y1 * invscale; + + r0 = 0.0; + r1 = xrad; + + dx = x1 - x0; + dy = y1 - y0; + + xps_bounds_in_user_space(ctx, &bbox); + + if (spread == SPREAD_PAD) + { + if (!point_inside_circle(x0, y0, x1, y1, r1)) + { + gs_function_t *reverse; + float in[1]; + float out[4]; + float fary[10]; + void *vary[1]; + + /* PDF shadings with extend doesn't work the same way as XPS + * gradients when the radial shading is a cone. In this case + * we fill the background ourselves. + */ + + in[0] = 1.0; + out[0] = 1.0; + out[1] = 0.0; + out[2] = 0.0; + out[3] = 0.0; + if (ctx->opacity_only) + gs_function_evaluate(func, in, out); + else + gs_function_evaluate(func, in, out + 1); + + xps_set_color(ctx, ctx->srgb, out); + + gs_moveto(ctx->pgs, bbox.p.x, bbox.p.y); + gs_lineto(ctx->pgs, bbox.q.x, bbox.p.y); + gs_lineto(ctx->pgs, bbox.q.x, bbox.q.y); + gs_lineto(ctx->pgs, bbox.p.x, bbox.q.y); + gs_closepath(ctx->pgs); + gs_fill(ctx->pgs); + + /* We also have to reverse the direction so the bigger circle + * comes first or the graphical results do not match. We also + * have to reverse the direction of the function to compensate. + */ + + reverse = xps_reverse_function(ctx, func, fary, vary); + if (!reverse) + { + gs_grestore(ctx->pgs); + return gs_rethrow(-1, "could not create the reversed function"); + } + + code = xps_draw_one_radial_gradient(ctx, reverse, 1, x1, y1, r1, x0, y0, r0); + if (code < 0) + { + xps_free(ctx, reverse); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "could not draw radial gradient"); + } + + xps_free(ctx, reverse); + } + else + { + code = xps_draw_one_radial_gradient(ctx, func, 1, x0, y0, r0, x1, y1, r1); + if (code < 0) + { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "could not draw radial gradient"); + } + } + } + else + { + for (i = 0; i < 100; i++) + { + /* Draw current circle */ + + if (!point_inside_circle(x0, y0, x1, y1, r1)) + dputs("xps: we should reverse gradient here too\n"); + + if (spread == SPREAD_REFLECT && (i & 1)) + code = xps_draw_one_radial_gradient(ctx, func, 0, x1, y1, r1, x0, y0, r0); + else + code = xps_draw_one_radial_gradient(ctx, func, 0, x0, y0, r0, x1, y1, r1); + if (code < 0) + { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "could not draw axial gradient"); + } + + /* Check if circle encompassed the entire bounding box (break loop if we do) */ + + done = 1; + if (!point_inside_circle(bbox.p.x, bbox.p.y, x1, y1, r1)) done = 0; + if (!point_inside_circle(bbox.p.x, bbox.q.y, x1, y1, r1)) done = 0; + if (!point_inside_circle(bbox.q.x, bbox.q.y, x1, y1, r1)) done = 0; + if (!point_inside_circle(bbox.q.x, bbox.p.y, x1, y1, r1)) done = 0; + if (done) + break; + + /* Prepare next circle */ + + r0 = r1; + r1 += xrad; + + x0 += dx; + y0 += dy; + x1 += dx; + y1 += dy; + } + } + + gs_grestore(ctx->pgs); + + return 0; +} + +/* + * Calculate how many iterations are needed to cover + * the bounding box. + */ + +static int +xps_draw_linear_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_function_t *func) +{ + gs_rect bbox; + float x0, y0, x1, y1; + float dx, dy; + int code; + int i; + + char *start_point_att = xps_att(root, "StartPoint"); + char *end_point_att = xps_att(root, "EndPoint"); + + x0 = 0; + y0 = 0; + x1 = 0; + y1 = 1; + + if (start_point_att) + sscanf(start_point_att, "%g,%g", &x0, &y0); + if (end_point_att) + sscanf(end_point_att, "%g,%g", &x1, &y1); + + dx = x1 - x0; + dy = y1 - y0; + + xps_bounds_in_user_space(ctx, &bbox); + + if (spread == SPREAD_PAD) + { + code = xps_draw_one_linear_gradient(ctx, func, 1, x0, y0, x1, y1); + if (code < 0) + return gs_rethrow(code, "could not draw axial gradient"); + } + else + { + float len; + float a, b; + float dist[4]; + float d0, d1; + int i0, i1; + + len = sqrt(dx * dx + dy * dy); + a = dx / len; + b = dy / len; + + dist[0] = a * (bbox.p.x - x0) + b * (bbox.p.y - y0); + dist[1] = a * (bbox.p.x - x0) + b * (bbox.q.y - y0); + dist[2] = a * (bbox.q.x - x0) + b * (bbox.q.y - y0); + dist[3] = a * (bbox.q.x - x0) + b * (bbox.p.y - y0); + + d0 = dist[0]; + d1 = dist[0]; + for (i = 1; i < 4; i++) + { + if (dist[i] < d0) d0 = dist[i]; + if (dist[i] > d1) d1 = dist[i]; + } + + i0 = floor(d0 / len); + i1 = ceil(d1 / len); + + for (i = i0; i < i1; i++) + { + if (spread == SPREAD_REFLECT && (i & 1)) + { + code = xps_draw_one_linear_gradient(ctx, func, 0, + x1 + dx * i, y1 + dy * i, + x0 + dx * i, y0 + dy * i); + } + else + { + code = xps_draw_one_linear_gradient(ctx, func, 0, + x0 + dx * i, y0 + dy * i, + x1 + dx * i, y1 + dy * i); + } + if (code < 0) + return gs_rethrow(code, "could not draw axial gradient"); + } + } + + return 0; +} + +/* + * Parse XML tag and attributes for a gradient brush, create color/opacity + * function objects and call gradient drawing primitives. + */ + +static int +xps_parse_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root, + int (*draw)(xps_context_t *, xps_item_t *, int, gs_function_t *)) +{ + xps_item_t *node; + + char *opacity_att; + char *interpolation_att; + char *spread_att; + char *mapping_att; + char *transform_att; + + xps_item_t *transform_tag = NULL; + xps_item_t *stop_tag = NULL; + + struct stop stop_list[MAX_STOPS]; + int stop_count; + gs_matrix transform; + int spread_method; + int code; + + gs_rect bbox; + + gs_function_t *color_func; + gs_function_t *opacity_func; + int has_opacity = 0; + + opacity_att = xps_att(root, "Opacity"); + interpolation_att = xps_att(root, "ColorInterpolationMode"); + spread_att = xps_att(root, "SpreadMethod"); + mapping_att = xps_att(root, "MappingMode"); + transform_att = xps_att(root, "Transform"); + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "LinearGradientBrush.Transform")) + transform_tag = xps_down(node); + if (!strcmp(xps_tag(node), "RadialGradientBrush.Transform")) + transform_tag = xps_down(node); + if (!strcmp(xps_tag(node), "LinearGradientBrush.GradientStops")) + stop_tag = xps_down(node); + if (!strcmp(xps_tag(node), "RadialGradientBrush.GradientStops")) + stop_tag = xps_down(node); + } + + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + + spread_method = SPREAD_PAD; + if (spread_att) + { + if (!strcmp(spread_att, "Pad")) + spread_method = SPREAD_PAD; + if (!strcmp(spread_att, "Reflect")) + spread_method = SPREAD_REFLECT; + if (!strcmp(spread_att, "Repeat")) + spread_method = SPREAD_REPEAT; + } + + gs_make_identity(&transform); + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + + if (!stop_tag) + return gs_throw(-1, "missing gradient stops tag"); + + stop_count = xps_parse_gradient_stops(ctx, base_uri, stop_tag, stop_list, MAX_STOPS); + if (stop_count == 0) + return gs_throw(-1, "no gradient stops found"); + + color_func = xps_create_gradient_stop_function(ctx, stop_list, stop_count, 0); + if (!color_func) + return gs_rethrow(-1, "could not create color gradient function"); + + opacity_func = xps_create_gradient_stop_function(ctx, stop_list, stop_count, 1); + if (!opacity_func) + return gs_rethrow(-1, "could not create opacity gradient function"); + + has_opacity = xps_gradient_has_transparent_colors(stop_list, stop_count); + + xps_clip(ctx); + + gs_gsave(ctx->pgs); + gs_concat(ctx->pgs, &transform); + + xps_bounds_in_user_space(ctx, &bbox); + + code = xps_begin_opacity(ctx, base_uri, dict, opacity_att, NULL); + if (code) + { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot create transparency group"); + } + + if (ctx->opacity_only) + { + code = draw(ctx, root, spread_method, opacity_func); + if (code) + { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot draw gradient opacity"); + } + } + else + { + if (has_opacity) + { + gs_transparency_mask_params_t params; + gs_transparency_group_params_t tgp; + + gs_trans_mask_params_init(¶ms, TRANSPARENCY_MASK_Luminosity); + gs_begin_transparency_mask(ctx->pgs, ¶ms, &bbox, 0); + code = draw(ctx, root, spread_method, opacity_func); + if (code) + { + gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot draw gradient opacity"); + } + gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); + + gs_trans_group_params_init(&tgp); + gs_begin_transparency_group(ctx->pgs, &tgp, &bbox); + code = draw(ctx, root, spread_method, color_func); + if (code) + { + gs_end_transparency_group(ctx->pgs); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot draw gradient color"); + } + gs_end_transparency_group(ctx->pgs); + } + else + { + code = draw(ctx, root, spread_method, color_func); + if (code) + { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot draw gradient color"); + } + } + } + + xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); + + gs_grestore(ctx->pgs); + + xps_free_gradient_stop_function(ctx, opacity_func); + xps_free_gradient_stop_function(ctx, color_func); + + return 0; +} + +int +xps_parse_linear_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root) +{ + int code; + code = xps_parse_gradient_brush(ctx, base_uri, dict, root, xps_draw_linear_gradient); + if (code < 0) + return gs_rethrow(code, "cannot parse linear gradient brush"); + return gs_okay; +} + +int +xps_parse_radial_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root) +{ + int code; + code = xps_parse_gradient_brush(ctx, base_uri, dict, root, xps_draw_radial_gradient); + if (code < 0) + return gs_rethrow(code, "cannot parse radial gradient brush"); + return gs_okay; +} diff --git a/xps/xpshash.c b/xps/xpshash.c new file mode 100644 index 00000000..459566c9 --- /dev/null +++ b/xps/xpshash.c @@ -0,0 +1,217 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* Linear probe hash table. + * + * Simple hashtable with open adressing linear probe. + * Does not manage memory of key/value pointers. + * Does not support deleting entries. + */ + +#include "ghostxps.h" + +static const unsigned primes[] = +{ + 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, + 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, 0 +}; + +typedef struct xps_hash_entry_s xps_hash_entry_t; + +struct xps_hash_entry_s +{ + char *key; + void *value; +}; + +struct xps_hash_table_s +{ + void *ctx; + unsigned int size; + unsigned int load; + xps_hash_entry_t *entries; +}; + +static inline int +xps_tolower(int c) +{ + if (c >= 'A' && c <= 'Z') + return c + 32; + return c; +} + +static unsigned int +xps_hash(char *s) +{ + unsigned int h = 0; + while (*s) + h = xps_tolower(*s++) + (h << 6) + (h << 16) - h; + return h; +} + +xps_hash_table_t * +xps_hash_new(xps_context_t *ctx) +{ + xps_hash_table_t *table; + + table = xps_alloc(ctx, sizeof(xps_hash_table_t)); + if (!table) + { + gs_throw(-1, "out of memory: hash table struct"); + return NULL; + } + + table->size = primes[0]; + table->load = 0; + + table->entries = xps_alloc(ctx, sizeof(xps_hash_entry_t) * table->size); + if (!table->entries) + { + xps_free(ctx, table); + gs_throw(-1, "out of memory: hash table entries array"); + return NULL; + } + + memset(table->entries, 0, sizeof(xps_hash_entry_t) * table->size); + + return table; +} + +static int +xps_hash_double(xps_context_t *ctx, xps_hash_table_t *table) +{ + xps_hash_entry_t *old_entries; + xps_hash_entry_t *new_entries; + unsigned int old_size = table->size; + unsigned int new_size = table->size * 2; + int i; + + for (i = 0; primes[i] != 0; i++) + { + if (primes[i] > old_size) + { + new_size = primes[i]; + break; + } + } + + old_entries = table->entries; + new_entries = xps_alloc(ctx, sizeof(xps_hash_entry_t) * new_size); + if (!new_entries) + return gs_throw(-1, "out of memory: hash table entries array"); + + table->size = new_size; + table->entries = new_entries; + table->load = 0; + + memset(table->entries, 0, sizeof(xps_hash_entry_t) * table->size); + + for (i = 0; i < old_size; i++) + if (old_entries[i].value) + xps_hash_insert(ctx, table, old_entries[i].key, old_entries[i].value); + + xps_free(ctx, old_entries); + + return 0; +} + +void +xps_hash_free(xps_context_t *ctx, xps_hash_table_t *table, + void (*free_key)(xps_context_t *ctx, void *), + void (*free_value)(xps_context_t *ctx, void *)) +{ + int i; + + for (i = 0; i < table->size; i++) + { + if (table->entries[i].key && free_key) + free_key(ctx, table->entries[i].key); + if (table->entries[i].value && free_value) + free_value(ctx, table->entries[i].value); + } + + xps_free(ctx, table->entries); + xps_free(ctx, table); +} + +void * +xps_hash_lookup(xps_hash_table_t *table, char *key) +{ + xps_hash_entry_t *entries = table->entries; + unsigned int size = table->size; + unsigned int pos = xps_hash(key) % size; + + while (1) + { + if (!entries[pos].value) + return NULL; + + if (xps_strcasecmp(key, entries[pos].key) == 0) + return entries[pos].value; + + pos = (pos + 1) % size; + } +} + +int +xps_hash_insert(xps_context_t *ctx, xps_hash_table_t *table, char *key, void *value) +{ + xps_hash_entry_t *entries; + unsigned int size, pos; + + /* Grow the table at 80% load */ + if (table->load > table->size * 8 / 10) + { + if (xps_hash_double(ctx, table) < 0) + return gs_rethrow(-1, "cannot grow hash table"); + } + + entries = table->entries; + size = table->size; + pos = xps_hash(key) % size; + + while (1) + { + if (!entries[pos].value) + { + entries[pos].key = key; + entries[pos].value = value; + table->load ++; + return 0; + } + + if (xps_strcasecmp(key, entries[pos].key) == 0) + { + return 0; + } + + pos = (pos + 1) % size; + } +} + +void +xps_hash_debug(xps_hash_table_t *table) +{ + int i; + + printf("hash table load %d / %d\n", table->load, table->size); + + for (i = 0; i < table->size; i++) + { + if (!table->entries[i].value) + printf("table % 4d: empty\n", i); + else + printf("table % 4d: key=%s value=%p\n", i, + table->entries[i].key, table->entries[i].value); + } +} diff --git a/xps/xpsimage.c b/xps/xpsimage.c new file mode 100644 index 00000000..7a69ba09 --- /dev/null +++ b/xps/xpsimage.c @@ -0,0 +1,470 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - image support */ + +/* TODO: we should be smarter here and do incremental decoding + * and rendering instead of uncompressing the whole image to + * memory before drawing. + */ + +#include "ghostxps.h" + +/* + * Un-interleave the alpha channel. + */ + +static void +xps_isolate_alpha_channel_8(xps_context_t *ctx, xps_image_t *image) +{ + int n = image->comps; + int y, x, k; + byte *sp, *dp, *ap; + + image->alpha = xps_alloc(ctx, image->width * image->height); + + for (y = 0; y < image->height; y++) + { + sp = image->samples + image->width * n * y; + dp = image->samples + image->width * (n - 1) * y; + ap = image->alpha + image->width * y; + for (x = 0; x < image->width; x++) + { + for (k = 0; k < n - 1; k++) + *dp++ = *sp++; + *ap++ = *sp++; + } + } + + image->hasalpha = 0; + image->comps --; + image->stride = image->width * image->comps; +} + +static void +xps_isolate_alpha_channel_16(xps_context_t *ctx, xps_image_t *image) +{ + int n = image->comps; + int y, x, k; + unsigned short *sp, *dp, *ap; + + image->alpha = xps_alloc(ctx, image->width * image->height * 2); + + for (y = 0; y < image->height; y++) + { + sp = ((unsigned short*)image->samples) + (image->width * n * y); + dp = ((unsigned short*)image->samples) + (image->width * (n - 1) * y); + ap = ((unsigned short*)image->alpha) + (image->width * y); + for (x = 0; x < image->width; x++) + { + for (k = 0; k < n - 1; k++) + *dp++ = *sp++; + *ap++ = *sp++; + } + } + + image->hasalpha = 0; + image->comps --; + image->stride = image->width * image->comps * 2; +} + +static int +xps_image_has_alpha(xps_context_t *ctx, xps_part_t *part) +{ + byte *buf = part->data; + int len = part->size; + + if (len < 8) + { + gs_warn("unknown image file format"); + return 0; + } + + if (buf[0] == 0xff && buf[1] == 0xd8) + return 0; /* JPEG never has an alpha channel */ + else if (memcmp(buf, "\211PNG\r\n\032\n", 8) == 0) + return xps_png_has_alpha(ctx, buf, len); + else if (memcmp(buf, "II", 2) == 0 && buf[2] == 0xBC) + return xps_jpegxr_has_alpha(ctx, buf, len); + else if (memcmp(buf, "MM", 2) == 0) + return xps_tiff_has_alpha(ctx, buf, len); + else if (memcmp(buf, "II", 2) == 0) + return xps_tiff_has_alpha(ctx, buf, len); + + return 0; +} + +static int +xps_decode_image(xps_context_t *ctx, xps_part_t *part, xps_image_t *image) +{ + byte *buf = part->data; + int len = part->size; + cmm_profile_t *profile; + int error; + + if (len < 8) + return gs_throw(-1, "unknown image file format"); + + memset(image, 0, sizeof(xps_image_t)); + image->samples = NULL; + image->alpha = NULL; + + if (buf[0] == 0xff && buf[1] == 0xd8) + { + error = xps_decode_jpeg(ctx, buf, len, image); + if (error) + return gs_rethrow(error, "could not decode jpeg image"); + } + else if (memcmp(buf, "\211PNG\r\n\032\n", 8) == 0) + { + error = xps_decode_png(ctx, buf, len, image); + if (error) + return gs_rethrow(error, "could not decode png image"); + } + else if (memcmp(buf, "II", 2) == 0 && buf[2] == 0xBC) + { + error = xps_decode_jpegxr(ctx, buf, len, image); + if (error) + return gs_rethrow(error, "could not decode jpeg-xr image"); + } + else if (memcmp(buf, "MM", 2) == 0 || memcmp(buf, "II", 2) == 0) + { + error = xps_decode_tiff(ctx, buf, len, image); + if (error) + return gs_rethrow(error, "could not decode tiff image"); + } + else + return gs_throw(-1, "unknown image file format"); + + // TODO: refcount image->colorspace + + /* See if we need to use the embedded profile. */ + if (image->profile) + { + /* + See if we can set up to use the embedded profile. + Note these profiles are NOT added to the xps color cache. + As such, they must be destroyed when the image brush ends. + */ + + /* Create the profile */ + profile = gsicc_profile_new(NULL, ctx->memory, NULL, 0); + + /* Set buffer */ + profile->buffer = image->profile; + profile->buffer_size = image->profilesize; + + /* Parse */ + gsicc_init_profile_info(profile); + + if (profile->profile_handle == NULL) + { + /* Problem with profile. Just ignore it */ + gs_warn("ignoring problem with icc profile embedded in an image"); + gsicc_profile_reference(profile, -1); + } + else + { + /* Check the profile is OK for channel data count. + * Need to be careful here since alpha is put into comps */ + if ((image->comps - image->hasalpha) == gsicc_getsrc_channel_count(profile)) + { + /* Create a new colorspace and associate with the profile */ + // TODO: refcount image->colorspace + gs_cspace_build_ICC(&image->colorspace, NULL, ctx->memory); + image->colorspace->cmm_icc_profile_data = profile; + } + else + { + /* Problem with profile. Just ignore it */ + gs_warn("ignoring icc profile embedded in an image with wrong number of components"); + gsicc_profile_reference(profile, -1); + } + } + } + + if (image->hasalpha) + { + if (image->bits < 8) + dprintf1("cannot isolate alpha channel in %d bpc images\n", image->bits); + if (image->bits == 8) + xps_isolate_alpha_channel_8(ctx, image); + if (image->bits == 16) + xps_isolate_alpha_channel_16(ctx, image); + } + + return gs_okay; +} + +static int +xps_paint_image_brush_imp(xps_context_t *ctx, xps_image_t *image, int alpha) +{ + gs_image_enum *penum; + gs_color_space *colorspace; + gs_image_t gsimage; + int code; + + unsigned int count; + unsigned int used; + byte *samples; + + if (alpha) + { + colorspace = ctx->gray; + samples = image->alpha; + count = (image->width * image->bits + 7) / 8 * image->height; + used = 0; + } + else + { + colorspace = image->colorspace; + samples = image->samples; + count = image->stride * image->height; + used = 0; + } + + memset(&gsimage, 0, sizeof(gsimage)); + gs_image_t_init(&gsimage, colorspace); + gsimage.ColorSpace = colorspace; + gsimage.BitsPerComponent = image->bits; + gsimage.Width = image->width; + gsimage.Height = image->height; + + gsimage.ImageMatrix.xx = image->xres / 96.0; + gsimage.ImageMatrix.yy = image->yres / 96.0; + + gsimage.Interpolate = 1; + + penum = gs_image_enum_alloc(ctx->memory, "xps_parse_image_brush (gs_image_enum_alloc)"); + if (!penum) + return gs_throw(-1, "gs_enum_allocate failed"); + + if ((code = gs_image_init(penum, &gsimage, false, ctx->pgs)) < 0) + return gs_throw(code, "gs_image_init failed"); + + if ((code = gs_image_next(penum, samples, count, &used)) < 0) + return gs_throw(code, "gs_image_next failed"); + + if (count < used) + return gs_throw2(-1, "not enough image data (image=%d used=%d)", count, used); + + if (count > used) + return gs_throw2(0, "too much image data (image=%d used=%d)", count, used); + + gs_image_cleanup_and_free_enum(penum, ctx->pgs); + + return 0; +} + +static int +xps_paint_image_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root, void *vimage) +{ + xps_image_t *image = vimage; + int code; + + if (ctx->opacity_only) + { + if (image->alpha) + { + code = xps_paint_image_brush_imp(ctx, image, 1); + if (code < 0) + return gs_rethrow(code, "cannot draw alpha channel image"); + } + return 0; + } + + if (image->alpha) + { + gs_transparency_mask_params_t params; + gs_transparency_group_params_t tgp; + gs_rect bbox; + + xps_bounds_in_user_space(ctx, &bbox); + + code = gs_gsave(ctx->pgs); + if (code < 0) + return gs_rethrow(code, "cannot gsave before transparency group"); + + gs_setcolorspace(ctx->pgs, ctx->gray); + gs_trans_mask_params_init(¶ms, TRANSPARENCY_MASK_Luminosity); + gs_begin_transparency_mask(ctx->pgs, ¶ms, &bbox, 0); + code = xps_paint_image_brush_imp(ctx, image, 1); + if (code < 0) + { + gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot draw alpha channel image"); + } + gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); + + gs_setcolorspace(ctx->pgs, image->colorspace); + gs_trans_group_params_init(&tgp); + gs_begin_transparency_group(ctx->pgs, &tgp, &bbox); + code = xps_paint_image_brush_imp(ctx, image, 0); + if (code < 0) + { + gs_end_transparency_group(ctx->pgs); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot draw color channel image"); + } + gs_end_transparency_group(ctx->pgs); + + code = gs_grestore(ctx->pgs); + if (code < 0) + return gs_rethrow(code, "cannot grestore after transparency group"); + } + else + { + code = xps_paint_image_brush_imp(ctx, image, 0); + if (code < 0) + return gs_rethrow(code, "cannot draw image"); + } + return 0; +} + +static int +xps_find_image_brush_source_part(xps_context_t *ctx, char *base_uri, xps_item_t *root, + xps_part_t **partp, char **profilep) +{ + xps_part_t *part; + char *image_source_att; + char buf[1024]; + char partname[1024]; + char *image_name; + char *profile_name; + char *p; + + image_source_att = xps_att(root, "ImageSource"); + if (!image_source_att) + return gs_throw(-1, "missing ImageSource attribute"); + + /* "{ColorConvertedBitmap /Resources/Image.tiff /Resources/Profile.icc}" */ + if (strstr(image_source_att, "{ColorConvertedBitmap") == image_source_att) + { + image_name = NULL; + profile_name = NULL; + + xps_strlcpy(buf, image_source_att, sizeof buf); + p = strchr(buf, ' '); + if (p) + { + image_name = p + 1; + p = strchr(p + 1, ' '); + if (p) + { + *p = 0; + profile_name = p + 1; + p = strchr(p + 1, '}'); + if (p) + *p = 0; + } + } + } + else + { + image_name = image_source_att; + profile_name = NULL; + } + + if (!image_name) + return gs_throw1(-1, "cannot parse image resource name '%s'", image_source_att); + + xps_absolute_path(partname, base_uri, image_name, sizeof partname); + part = xps_read_part(ctx, partname); + if (!part) + return gs_throw1(-1, "cannot find image resource part '%s'", partname); + + *partp = part; + *profilep = xps_strdup(ctx, profile_name); + + return 0; +} + +int +xps_parse_image_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root) +{ + xps_part_t *part; + xps_image_t *image; + gs_color_space *colorspace; + char *profilename; + int code; + + code = xps_find_image_brush_source_part(ctx, base_uri, root, &part, &profilename); + if (code < 0) + return gs_rethrow(code, "cannot find image source"); + + image = xps_alloc(ctx, sizeof(xps_image_t)); + if (!image) + return gs_throw(-1, "out of memory: image struct"); + + code = xps_decode_image(ctx, part, image); + if (code < 0) + return gs_rethrow1(code, "cannot decode image '%s'", part->name); + + /* Override any embedded colorspace profiles if the external one matches. */ + if (profilename) + { + colorspace = xps_read_icc_colorspace(ctx, base_uri, profilename); + if (colorspace && cs_num_components(colorspace) == cs_num_components(image->colorspace)) + { + // TODO: refcount image->colorspace + image->colorspace = colorspace; + } + } + + code = xps_parse_tiling_brush(ctx, base_uri, dict, root, xps_paint_image_brush, image); + if (code < 0) + return gs_rethrow(-1, "cannot parse tiling brush"); + + if (profilename) + xps_free(ctx, profilename); + xps_free_image(ctx, image); + xps_free_part(ctx, part); + + return 0; +} + +int +xps_image_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +{ + xps_part_t *imagepart; + int code; + int has_alpha; + char *profilename; + + code = xps_find_image_brush_source_part(ctx, base_uri, root, &imagepart, &profilename); + if (code < 0) + { + gs_catch(code, "cannot find image source"); + return 0; + } + + has_alpha = xps_image_has_alpha(ctx, imagepart); + + xps_free_part(ctx, imagepart); + + return has_alpha; +} + +void +xps_free_image(xps_context_t *ctx, xps_image_t *image) +{ + // TODO: refcount image->colorspace + if (image->samples) + xps_free(ctx, image->samples); + if (image->alpha) + xps_free(ctx, image->alpha); + if (image->profile) + xps_free(ctx, image->profile); + xps_free(ctx, image); +} diff --git a/xps/xpsjpeg.c b/xps/xpsjpeg.c new file mode 100644 index 00000000..878fd85a --- /dev/null +++ b/xps/xpsjpeg.c @@ -0,0 +1,143 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - JPEG image support */ + +#include "ghostxps.h" + +#include "stream.h" +#include "strimpl.h" +#include "gsstate.h" +#include "jpeglib_.h" +#include "sdct.h" +#include "sjpeg.h" + +static int +xps_report_error(stream_state * st, const char *str) +{ + (void) gs_throw1(-1, "%s", str); + return 0; +} + +int +xps_decode_jpeg(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image) +{ + jpeg_decompress_data jddp; + stream_DCT_state state; + stream_cursor_read rp; + stream_cursor_write wp; + int code; + int wlen; + byte *wbuf; + jpeg_saved_marker_ptr curr_marker; + + s_init_state((stream_state*)&state, &s_DCTD_template, ctx->memory); + state.report_error = xps_report_error; + + s_DCTD_template.set_defaults((stream_state*)&state); + + state.jpeg_memory = ctx->memory; + state.data.decompress = &jddp; + + jddp.template = s_DCTD_template; + jddp.memory = ctx->memory; + jddp.scanline_buffer = NULL; + + if ((code = gs_jpeg_create_decompress(&state)) < 0) + return gs_throw(-1, "cannot gs_jpeg_create_decompress"); + + s_DCTD_template.init((stream_state*)&state); + + rp.ptr = rbuf - 1; + rp.limit = rbuf + rlen - 1; + + /* read the header only by not having a write buffer */ + wp.ptr = 0; + wp.limit = 0; + + /* Set up to save the ICC marker APP2. + * According to the spec we should be getting APP1 APP2 and APP13. + * Library gets APP0 and APP14. */ + jpeg_save_markers(&(jddp.dinfo), 0xe2, 0xFFFF); + + code = s_DCTD_template.process((stream_state*)&state, &rp, &wp, true); + if (code != 1) + return gs_throw(-1, "premature EOF or error in jpeg"); + + /* Check if we had an ICC profile */ + curr_marker = jddp.dinfo.marker_list; + while (curr_marker != NULL) + { + if (curr_marker->marker == 0xe2) + { + /* Found ICC profile. Create a buffer and copy over now. + * Strip JPEG APP2 14 byte header */ + image->profilesize = curr_marker->data_length - 14; + image->profile = xps_alloc(ctx, image->profilesize); + if (image->profile) + { + /* If we can't create it, just ignore */ + memcpy(image->profile, &(curr_marker->data[14]), image->profilesize); + } + break; + } + curr_marker = curr_marker->next; + } + + image->width = jddp.dinfo.output_width; + image->height = jddp.dinfo.output_height; + image->comps = jddp.dinfo.output_components; + image->bits = 8; + image->stride = image->width * image->comps; + + if (image->comps == 1) + image->colorspace = ctx->gray; + if (image->comps == 3) + image->colorspace = ctx->srgb; + if (image->comps == 4) + image->colorspace = ctx->cmyk; + + if (jddp.dinfo.density_unit == 1) + { + image->xres = jddp.dinfo.X_density; + image->yres = jddp.dinfo.Y_density; + } + else if (jddp.dinfo.density_unit == 2) + { + image->xres = jddp.dinfo.X_density * 2.54; + image->yres = jddp.dinfo.Y_density * 2.54; + } + else + { + image->xres = 96; + image->yres = 96; + } + + wlen = image->stride * image->height; + wbuf = xps_alloc(ctx, wlen); + if (!wbuf) + return gs_throw1(-1, "out of memory allocating samples: %d", wlen); + + image->samples = wbuf; + + wp.ptr = wbuf - 1; + wp.limit = wbuf + wlen - 1; + + code = s_DCTD_template.process((stream_state*)&state, &rp, &wp, true); + if (code != EOFC) + return gs_throw1(-1, "error in jpeg (code = %d)", code); + + gs_jpeg_destroy(&state); + + return gs_okay; +} diff --git a/xps/xpsjxr.c b/xps/xpsjxr.c new file mode 100644 index 00000000..1e9b0e73 --- /dev/null +++ b/xps/xpsjxr.c @@ -0,0 +1,259 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* JPEG-XR (formerly HD-Photo (formerly Windows Media Photo)) support */ + +#include "ghostxps.h" + +#ifdef _MSC_VER +#undef _MSC_VER +#endif + +#include "jpegxr.h" + +struct state { xps_context_t *ctx; xps_image_t *output; }; + +static const char * +jxr_error_string(int code) +{ + switch (code) + { + case JXR_EC_OK: return "No error"; + default: + case JXR_EC_ERROR: return "Unspecified error"; + case JXR_EC_BADMAGIC: return "Stream lacks proper magic number"; + case JXR_EC_FEATURE_NOT_IMPLEMENTED: return "Feature not implemented"; + case JXR_EC_IO: return "Error reading/writing data"; + case JXR_EC_BADFORMAT: return "Bad file format"; + } +} + +#define CLAMP(v, mn, mx) (v < mn ? mn : v > mx ? mx : v) + +static inline int +scale_bits(int depth, int value) +{ + union { int iv; float fv; } bd32f; + + switch (depth) + { + case JXR_BD1WHITE1: + return value * 255; + case JXR_BD1BLACK1: + return value ? 0 : 255; + case JXR_BD8: + return value; + case JXR_BD16: + return value >> 8; + case JXR_BD16S: /* -4 .. 4 ; 8192 = 1.0 */ + value = value >> 5; + return CLAMP(value, 0, 255); + case JXR_BD32S: /* -128 .. 128 ; 16777216 = 1.0 */ + value = value >> 16; + return CLAMP(value, 0, 255); + case JXR_BD32F: + bd32f.iv = value; + value = bd32f.fv * 255; + return CLAMP(value, 0, 255); +#if 0 + case JXR_BDRESERVED: return value; + case JXR_BD16F: return value; + case JXR_BD5: return value; + case JXR_BD10: return value; + case JXR_BD565: return value; +#endif + } + return value; +} + +static void +xps_decode_jpegxr_block(jxr_image_t image, int mx, int my, int *data) +{ + struct state *state = jxr_get_user_data(image); + xps_context_t *ctx = state->ctx; + xps_image_t *output = state->output; + int depth; + unsigned char *p; + int x, y, k; + + if (!output->samples) + { + output->width = jxr_get_IMAGE_WIDTH(image); + output->height = jxr_get_IMAGE_HEIGHT(image); + output->comps = jxr_get_IMAGE_CHANNELS(image); + output->hasalpha = jxr_get_ALPHACHANNEL_FLAG(image); + output->bits = 8; + output->stride = output->width * output->comps; + output->samples = xps_alloc(ctx, output->stride * output->height); + + switch (output->comps) + { + default: + case 1: output->colorspace = ctx->gray; break; + case 3: output->colorspace = ctx->srgb; break; + case 4: output->colorspace = ctx->cmyk; break; + } + } + + depth = jxr_get_OUTPUT_BITDEPTH(image); + + my = my * 16; + mx = mx * 16; + + for (y = 0; y < 16; y++) + { + if (my + y >= output->height) + return; + p = output->samples + (my + y) * output->stride + mx * output->comps; + for (x = 0; x < 16; x++) + { + if (mx + x >= output->width) + data += output->comps; + else + for (k = 0; k < output->comps; k++) + *p++ = scale_bits(depth, *data++); + } + } +} + +static void +xps_decode_jpegxr_alpha_block(jxr_image_t image, int mx, int my, int *data) +{ + struct state *state = jxr_get_user_data(image); + xps_context_t *ctx = state->ctx; + xps_image_t *output = state->output; + int depth; + unsigned char *p; + int x, y, k; + + if (!output->alpha) + { + output->alpha = xps_alloc(ctx, output->width * output->height); + } + + depth = jxr_get_OUTPUT_BITDEPTH(image); + + my = my * 16; + mx = mx * 16; + + for (y = 0; y < 16; y++) + { + if (my + y >= output->height) + return; + p = output->alpha + (my + y) * output->width + mx; + for (x = 0; x < 16; x++) + { + if (mx + x >= output->width) + data ++; + else + *p++ = scale_bits(depth, *data++); + } + } +} + +int +xps_decode_jpegxr(xps_context_t *ctx, byte *buf, int len, xps_image_t *output) +{ + FILE *file; + char name[gp_file_name_sizeof]; + struct state state; + jxr_container_t container; + jxr_image_t image; + int offset, alpha_offset; + int rc; + + memset(output, 0, sizeof(*output)); + + file = gp_open_scratch_file(ctx->memory, "jpegxr-scratch-", name, "wb+"); + if (!file) + return gs_throw(gs_error_invalidfileaccess, "cannot open scratch file"); + rc = fwrite(buf, 1, len, file); + if (rc != len) + return gs_throw(gs_error_invalidfileaccess, "cannot write to scratch file"); + fseek(file, 0, SEEK_SET); + + container = jxr_create_container(); + rc = jxr_read_image_container(container, file); + if (rc < 0) + return gs_throw1(-1, "jxr_read_image_container: %s", jxr_error_string(rc)); + + offset = jxrc_image_offset(container, 0); + alpha_offset = jxrc_alpha_offset(container, 0); + + output->xres = jxrc_width_resolution(container, 0); + output->yres = jxrc_height_resolution(container, 0); + + image = jxr_create_input(); + jxr_set_PROFILE_IDC(image, 111); + jxr_set_LEVEL_IDC(image, 255); + jxr_set_pixel_format(image, jxrc_image_pixelformat(container, 0)); + jxr_set_container_parameters(image, + jxrc_image_pixelformat(container, 0), + jxrc_image_width(container, 0), + jxrc_image_height(container, 0), + jxrc_alpha_offset(container, 0), + jxrc_image_band_presence(container, 0), + jxrc_alpha_band_presence(container, 0), 0); + + jxr_set_block_output(image, xps_decode_jpegxr_block); + state.ctx = ctx; + state.output = output; + jxr_set_user_data(image, &state); + + fseek(file, offset, SEEK_SET); + rc = jxr_read_image_bitstream(image, file); + if (rc < 0) + return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc)); + + jxr_destroy(image); + + if (alpha_offset > 0) + { + image = jxr_create_input(); + jxr_set_PROFILE_IDC(image, 111); + jxr_set_LEVEL_IDC(image, 255); + jxr_set_pixel_format(image, jxrc_image_pixelformat(container, 0)); + jxr_set_container_parameters(image, + jxrc_image_pixelformat(container, 0), + jxrc_image_width(container, 0), + jxrc_image_height(container, 0), + jxrc_alpha_offset(container, 0), + jxrc_image_band_presence(container, 0), + jxrc_alpha_band_presence(container, 0), 0); + + jxr_set_block_output(image, xps_decode_jpegxr_alpha_block); + state.ctx = ctx; + state.output = output; + jxr_set_user_data(image, &state); + + fseek(file, alpha_offset, SEEK_SET); + rc = jxr_read_image_bitstream(image, file); + if (rc < 0) + return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc)); + + jxr_destroy(image); + } + + jxr_destroy_container(container); + + fclose(file); + unlink(name); + + return gs_okay; +} + +int +xps_jpegxr_has_alpha(xps_context_t *ctx, byte *buf, int len) +{ + return 1; +} diff --git a/xps/xpsmem.c b/xps/xpsmem.c new file mode 100644 index 00000000..95199f07 --- /dev/null +++ b/xps/xpsmem.c @@ -0,0 +1,182 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - string manipulation functions */ + +#include "ghostxps.h" + +void * +xps_realloc_imp(xps_context_t *ctx, void *ptr, int size, const char *func) +{ + if (!ptr) + return gs_alloc_bytes(ctx->memory, size, func); + return gs_resize_object(ctx->memory, ptr, size, func); +} + +static inline int +xps_tolower(int c) +{ + if (c >= 'A' && c <= 'Z') + return c + 32; + return c; +} + +int +xps_strcasecmp(char *a, char *b) +{ + while (xps_tolower(*a) == xps_tolower(*b)) + { + if (*a++ == 0) + return 0; + b++; + } + return xps_tolower(*a) - xps_tolower(*b); +} + +char * +xps_strdup_imp(xps_context_t *ctx, const char *str, const char *cname) +{ + char *cpy = NULL; + if (str) + cpy = (char*) gs_alloc_bytes(ctx->memory, strlen(str) + 1, cname); + if (cpy) + strcpy(cpy, str); + return cpy; +} + +size_t +xps_strlcpy(char *dst, const char *src, size_t siz) +{ + register char *d = dst; + register const char *s = src; + register int n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} + +size_t +xps_strlcat(char *dst, const char *src, size_t siz) +{ + register char *d = dst; + register const char *s = src; + register int n = siz; + int dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (*d != '\0' && n-- != 0) + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return dlen + strlen(s); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return dlen + (s - src); /* count does not include NUL */ +} + +#define SEP(x) ((x)=='/' || (x) == 0) + +static char * +xps_clean_path(char *name) +{ + char *p, *q, *dotdot; + int rooted; + + rooted = name[0] == '/'; + + /* + * invariants: + * p points at beginning of path element we're considering. + * q points just past the last path element we wrote (no slash). + * dotdot points just past the point where .. cannot backtrack + * any further (no slash). + */ + p = q = dotdot = name + rooted; + while (*p) + { + if(p[0] == '/') /* null element */ + p++; + else if (p[0] == '.' && SEP(p[1])) + p += 1; /* don't count the separator in case it is nul */ + else if (p[0] == '.' && p[1] == '.' && SEP(p[2])) + { + p += 2; + if (q > dotdot) /* can backtrack */ + { + while(--q > dotdot && *q != '/') + ; + } + else if (!rooted) /* /.. is / but ./../ is .. */ + { + if (q != name) + *q++ = '/'; + *q++ = '.'; + *q++ = '.'; + dotdot = q; + } + } + else /* real path element */ + { + if (q != name+rooted) + *q++ = '/'; + while ((*q = *p) != '/' && *q != 0) + p++, q++; + } + } + + if (q == name) /* empty string is really "." */ + *q++ = '.'; + *q = '\0'; + + return name; +} + +void +xps_absolute_path(char *output, char *base_uri, char *path, int output_size) +{ + if (path[0] == '/') + { + xps_strlcpy(output, path, output_size); + } + else + { + xps_strlcpy(output, base_uri, output_size); + xps_strlcat(output, "/", output_size); + xps_strlcat(output, path, output_size); + } + xps_clean_path(output); +} diff --git a/xps/xpsopacity.c b/xps/xpsopacity.c new file mode 100644 index 00000000..bd845efb --- /dev/null +++ b/xps/xpsopacity.c @@ -0,0 +1,102 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - transparency support */ + +#include "ghostxps.h" + +void +xps_bounds_in_user_space(xps_context_t *ctx, gs_rect *ubox) +{ + gx_clip_path *clip_path; + gs_rect dbox; + int code; + + code = gx_effective_clip_path(ctx->pgs, &clip_path); + if (code < 0) + gs_warn("gx_effective_clip_path failed"); + + dbox.p.x = fixed2float(clip_path->outer_box.p.x); + dbox.p.y = fixed2float(clip_path->outer_box.p.y); + dbox.q.x = fixed2float(clip_path->outer_box.q.x); + dbox.q.y = fixed2float(clip_path->outer_box.q.y); + gs_bbox_transform_inverse(&dbox, &ctm_only(ctx->pgs), ubox); +} + +int +xps_begin_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, + char *opacity_att, xps_item_t *opacity_mask_tag) +{ + gs_transparency_group_params_t tgp; + gs_transparency_mask_params_t tmp; + gs_rect bbox; + float opacity; + int save; + int code; + + if (!opacity_att && !opacity_mask_tag) + return 0; + + opacity = 1.0; + if (opacity_att) + opacity = atof(opacity_att); + gs_setopacityalpha(ctx->pgs, opacity); + + xps_bounds_in_user_space(ctx, &bbox); + + if (opacity_mask_tag) + { + gs_trans_mask_params_init(&tmp, TRANSPARENCY_MASK_Luminosity); + gs_begin_transparency_mask(ctx->pgs, &tmp, &bbox, 0); + + gs_gsave(ctx->pgs); + + /* Need a path to fill/clip for the brush */ + gs_moveto(ctx->pgs, bbox.p.x, bbox.p.y); + gs_lineto(ctx->pgs, bbox.p.x, bbox.q.y); + gs_lineto(ctx->pgs, bbox.q.x, bbox.q.y); + gs_lineto(ctx->pgs, bbox.q.x, bbox.p.y); + gs_closepath(ctx->pgs); + + /* opacity-only mode: use alpha value as gray color to create luminosity mask */ + save = ctx->opacity_only; + ctx->opacity_only = 1; + + code = xps_parse_brush(ctx, base_uri, dict, opacity_mask_tag); + if (code) + { + gs_grestore(ctx->pgs); + gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); + ctx->opacity_only = save; + return gs_rethrow(code, "cannot parse opacity mask brush"); + } + + gs_grestore(ctx->pgs); + gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); + ctx->opacity_only = save; + } + + gs_trans_group_params_init(&tgp); + gs_begin_transparency_group(ctx->pgs, &tgp, &bbox); + + return 0; +} + +void +xps_end_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, + char *opacity_att, xps_item_t *opacity_mask_tag) +{ + if (!opacity_att && !opacity_mask_tag) + return; + gs_end_transparency_group(ctx->pgs); +} diff --git a/xps/xpspage.c b/xps/xpspage.c new file mode 100644 index 00000000..c6424d7c --- /dev/null +++ b/xps/xpspage.c @@ -0,0 +1,281 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - page parsing */ + +#include "ghostxps.h" + +int +xps_parse_canvas(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root) +{ + xps_resource_t *new_dict = NULL; + xps_item_t *node; + char *opacity_mask_uri; + int code; + + char *transform_att; + char *clip_att; + char *opacity_att; + char *opacity_mask_att; + + xps_item_t *transform_tag = NULL; + xps_item_t *clip_tag = NULL; + xps_item_t *opacity_mask_tag = NULL; + + gs_matrix transform; + + transform_att = xps_att(root, "RenderTransform"); + clip_att = xps_att(root, "Clip"); + opacity_att = xps_att(root, "Opacity"); + opacity_mask_att = xps_att(root, "OpacityMask"); + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "Canvas.Resources") && xps_down(node)) + { + code = xps_parse_resource_dictionary(ctx, &new_dict, base_uri, xps_down(node)); + if (code) + return gs_rethrow(code, "cannot load Canvas.Resources"); + new_dict->parent = dict; + dict = new_dict; + } + + if (!strcmp(xps_tag(node), "Canvas.RenderTransform")) + transform_tag = xps_down(node); + if (!strcmp(xps_tag(node), "Canvas.Clip")) + clip_tag = xps_down(node); + if (!strcmp(xps_tag(node), "Canvas.OpacityMask")) + opacity_mask_tag = xps_down(node); + } + + opacity_mask_uri = base_uri; + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); + + gs_gsave(ctx->pgs); + + gs_make_identity(&transform); + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + gs_concat(ctx->pgs, &transform); + + if (clip_att || clip_tag) + { + if (clip_att) + xps_parse_abbreviated_geometry(ctx, clip_att); + if (clip_tag) + xps_parse_path_geometry(ctx, dict, clip_tag, 0); + xps_clip(ctx); + } + + code = xps_begin_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + if (code) + { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot create transparency group"); + } + + for (node = xps_down(root); node; node = xps_next(node)) + { + code = xps_parse_element(ctx, base_uri, dict, node); + if (code) + { + xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot parse child of Canvas"); + } + } + + xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + + gs_grestore(ctx->pgs); + + if (new_dict) + xps_free_resource_dictionary(ctx, new_dict); + + return 0; +} + +int +xps_parse_fixed_page(xps_context_t *ctx, xps_part_t *part) +{ + xps_item_t *root, *node; + xps_resource_t *dict; + char *width_att; + char *height_att; + int has_transparency; + char base_uri[1024]; + char *s; + int code; + + if_debug1('|', "doc: parsing page %s\n", part->name); + + xps_strlcpy(base_uri, part->name, sizeof base_uri); + s = strrchr(base_uri, '/'); + if (s) + s[1] = 0; + + root = xps_parse_xml(ctx, part->data, part->size); + if (!root) + return gs_rethrow(-1, "cannot parse xml"); + + if (strcmp(xps_tag(root), "FixedPage")) + return gs_throw1(-1, "expected FixedPage element (found %s)", xps_tag(root)); + + width_att = xps_att(root, "Width"); + height_att = xps_att(root, "Height"); + + if (!width_att) + return gs_throw(-1, "FixedPage missing required attribute: Width"); + if (!height_att) + return gs_throw(-1, "FixedPage missing required attribute: Height"); + + dict = NULL; + + /* Setup new page */ + { + gs_memory_t *mem = ctx->memory; + gs_state *pgs = ctx->pgs; + gx_device *dev = gs_currentdevice(pgs); + gs_param_float_array fa; + float fv[2]; + gs_c_param_list list; + + gs_c_param_list_write(&list, mem); + + fv[0] = atoi(width_att) / 96.0 * 72.0; + fv[1] = atoi(height_att) / 96.0 * 72.0; + fa.persistent = false; + fa.data = fv; + fa.size = 2; + + code = param_write_float_array((gs_param_list *)&list, ".MediaSize", &fa); + if ( code >= 0 ) + { + gs_c_param_list_read(&list); + code = gs_putdeviceparams(dev, (gs_param_list *)&list); + } + gs_c_param_list_release(&list); + + /* nb this is for the demo it is wrong and should be removed */ + gs_initgraphics(pgs); + + /* 96 dpi default - and put the origin at the top of the page */ + + gs_initmatrix(pgs); + + code = gs_scale(pgs, 72.0/96.0, -72.0/96.0); + if (code < 0) + return gs_rethrow(code, "cannot set page transform"); + + code = gs_translate(pgs, 0.0, -atoi(height_att)); + if (code < 0) + return gs_rethrow(code, "cannot set page transform"); + + code = gs_erasepage(pgs); + if (code < 0) + return gs_rethrow(code, "cannot clear page"); + } + + /* Pre-parse looking for transparency */ + + has_transparency = 0; + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "FixedPage.Resources") && xps_down(node)) + if (xps_resource_dictionary_has_transparency(ctx, base_uri, xps_down(node))) + has_transparency = 1; + if (xps_element_has_transparency(ctx, base_uri, node)) + has_transparency = 1; + } + + /* save the state with the original device before we push */ + gs_gsave(ctx->pgs); + + if (ctx->use_transparency && has_transparency) + { + code = gs_push_pdf14trans_device(ctx->pgs); + if (code < 0) + { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot install transparency device"); + } + } + + /* Initialize the default profiles in the ctx to what is in the manager */ + ctx->gray->cmm_icc_profile_data = ctx->pgs->icc_manager->default_gray; + ctx->srgb->cmm_icc_profile_data = ctx->pgs->icc_manager->default_rgb; + /* scrgb really needs to be a bit different. + * Unless we are handling nonlinearity before conversion from float. ToDo. */ + ctx->scrgb->cmm_icc_profile_data = ctx->pgs->icc_manager->default_rgb; + ctx->cmyk->cmm_icc_profile_data = ctx->pgs->icc_manager->default_cmyk; + + /* Draw contents */ + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "FixedPage.Resources") && xps_down(node)) + { + code = xps_parse_resource_dictionary(ctx, &dict, base_uri, xps_down(node)); + if (code) + { + gs_pop_pdf14trans_device(ctx->pgs); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot load FixedPage.Resources"); + } + } + code = xps_parse_element(ctx, base_uri, dict, node); + if (code) + { + gs_pop_pdf14trans_device(ctx->pgs); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot parse child of FixedPage"); + } + } + + if (ctx->use_transparency && has_transparency) + { + code = gs_pop_pdf14trans_device(ctx->pgs); + if (code < 0) + { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot uninstall transparency device"); + } + } + + /* Flush page */ + { + code = xps_show_page(ctx, 1, true); /* copies, flush */ + if (code < 0) + { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot flush page"); + } + } + + /* restore the original device, discarding the pdf14 compositor */ + gs_grestore(ctx->pgs); + + if (dict) + { + xps_free_resource_dictionary(ctx, dict); + } + + xps_free_item(ctx, root); + + return 0; +} diff --git a/xps/xpspath.c b/xps/xpspath.c new file mode 100644 index 00000000..89e9716a --- /dev/null +++ b/xps/xpspath.c @@ -0,0 +1,1036 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - path (vector drawing) support */ + +#include "ghostxps.h" + +void +xps_clip(xps_context_t *ctx) +{ + if (ctx->fill_rule == 0) + gs_eoclip(ctx->pgs); + else + gs_clip(ctx->pgs); + gs_newpath(ctx->pgs); +} + +void +xps_fill(xps_context_t *ctx) +{ + if (gs_currentopacityalpha(ctx->pgs) < 0.001) + gs_newpath(ctx->pgs); + else if (ctx->fill_rule == 0) { + if (gs_eofill(ctx->pgs) == gs_error_Remap_Color) + xps_high_level_pattern(ctx); + gs_eofill(ctx->pgs); + } + else { + if (gs_fill(ctx->pgs) == gs_error_Remap_Color) + xps_high_level_pattern(ctx); + gs_fill(ctx->pgs); + } +} + +/* Draw an arc segment transformed by the matrix, we approximate with straight + * line segments. We cannot use the gs_arc function because they only draw + * circular arcs, we need to transform the line to make them elliptical but + * without transforming the line width. + */ +static inline void +xps_draw_arc_segment(xps_context_t *ctx, gs_matrix *mtx, float th0, float th1, int iscw) +{ + float t, d; + gs_point p; + + while (th1 < th0) + th1 += M_PI * 2.0; + + d = 1 * (M_PI / 180.0); /* 1-degree precision */ + + if (iscw) + { + gs_point_transform(cos(th0), sin(th0), mtx, &p); + gs_lineto(ctx->pgs, p.x, p.y); + for (t = th0; t < th1; t += d) + { + gs_point_transform(cos(t), sin(t), mtx, &p); + gs_lineto(ctx->pgs, p.x, p.y); + } + gs_point_transform(cos(th1), sin(th1), mtx, &p); + gs_lineto(ctx->pgs, p.x, p.y); + } + else + { + th0 += M_PI * 2; + gs_point_transform(cos(th0), sin(th0), mtx, &p); + gs_lineto(ctx->pgs, p.x, p.y); + for (t = th0; t > th1; t -= d) + { + gs_point_transform(cos(t), sin(t), mtx, &p); + gs_lineto(ctx->pgs, p.x, p.y); + } + gs_point_transform(cos(th1), sin(th1), mtx, &p); + gs_lineto(ctx->pgs, p.x, p.y); + } +} + +/* Given two vectors find the angle between them. */ +static inline double +angle_between(const gs_point u, const gs_point v) +{ + double det = u.x * v.y - u.y * v.x; + double sign = (det < 0 ? -1.0 : 1.0); + double magu = u.x * u.x + u.y * u.y; + double magv = v.x * v.x + v.y * v.y; + double udotv = u.x * v.x + u.y * v.y; + double t = udotv / (magu * magv); + /* guard against rounding errors when near |1| (where acos will return NaN) */ + if (t < -1.0) t = -1.0; + if (t > 1.0) t = 1.0; + return sign * acos(t); +} + +static void +xps_draw_arc(xps_context_t *ctx, + float size_x, float size_y, float rotation_angle, + int is_large_arc, int is_clockwise, + float point_x, float point_y) +{ + gs_matrix rotmat, revmat; + gs_matrix mtx; + gs_point pt; + double rx, ry; + double x1, y1, x2, y2; + double x1t, y1t; + double cxt, cyt, cx, cy; + double t1, t2, t3; + double sign; + double th1, dth; + + gs_currentpoint(ctx->pgs, &pt); + x1 = pt.x; + y1 = pt.y; + x2 = point_x; + y2 = point_y; + rx = size_x; + ry = size_y; + + if (is_clockwise != is_large_arc) + sign = 1; + else + sign = -1; + + gs_make_rotation(rotation_angle, &rotmat); + gs_make_rotation(-rotation_angle, &revmat); + + /* http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes */ + /* Conversion from endpoint to center parameterization */ + + /* F.6.6.1 -- ensure radii are positive and non-zero */ + rx = fabsf(rx); + ry = fabsf(ry); + if (rx < 0.001 || ry < 0.001) + { + gs_lineto(ctx->pgs, x2, y2); + return; + } + + /* F.6.5.1 */ + gs_distance_transform((x1 - x2) / 2.0, (y1 - y2) / 2.0, &revmat, &pt); + x1t = pt.x; + y1t = pt.y; + + /* F.6.6.2 -- ensure radii are large enough */ + t1 = (x1t * x1t) / (rx * rx) + (y1t * y1t) / (ry * ry); + if (t1 > 1.0) + { + rx = rx * sqrtf(t1); + ry = ry * sqrtf(t1); + } + + /* F.6.5.2 */ + t1 = (rx * rx * ry * ry) - (rx * rx * y1t * y1t) - (ry * ry * x1t * x1t); + t2 = (rx * rx * y1t * y1t) + (ry * ry * x1t * x1t); + t3 = t1 / t2; + /* guard against rounding errors; sqrt of negative numbers is bad for your health */ + if (t3 < 0.0) t3 = 0.0; + t3 = sqrtf(t3); + + cxt = sign * t3 * (rx * y1t) / ry; + cyt = sign * t3 * -(ry * x1t) / rx; + + /* F.6.5.3 */ + gs_distance_transform(cxt, cyt, &rotmat, &pt); + cx = pt.x + (x1 + x2) / 2; + cy = pt.y + (y1 + y2) / 2; + + /* F.6.5.4 */ + { + gs_point coord1, coord2, coord3, coord4; + coord1.x = 1; + coord1.y = 0; + coord2.x = (x1t - cxt) / rx; + coord2.y = (y1t - cyt) / ry; + coord3.x = (x1t - cxt) / rx; + coord3.y = (y1t - cyt) / ry; + coord4.x = (-x1t - cxt) / rx; + coord4.y = (-y1t - cyt) / ry; + th1 = angle_between(coord1, coord2); + dth = angle_between(coord3, coord4); + if (dth < 0 && !is_clockwise) + dth += (degrees_to_radians * 360); + if (dth > 0 && is_clockwise) + dth -= (degrees_to_radians * 360); + } + + gs_make_identity(&mtx); + gs_matrix_translate(&mtx, cx, cy, &mtx); + gs_matrix_rotate(&mtx, rotation_angle, &mtx); + gs_matrix_scale(&mtx, rx, ry, &mtx); + xps_draw_arc_segment(ctx, &mtx, th1, th1 + dth, is_clockwise); + + gs_lineto(ctx->pgs, point_x, point_y); +} + +/* + * Parse an abbreviated geometry string, and call + * ghostscript moveto/lineto/curveto functions to + * build up a path. + */ + +void +xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom) +{ + char **args; + char **pargs; + char *s = geom; + gs_point pt; + int i, n; + int cmd, old; + float x1, y1, x2, y2, x3, y3; + float smooth_x, smooth_y; /* saved cubic bezier control point for smooth curves */ + int reset_smooth; + + args = xps_alloc(ctx, sizeof(char*) * (strlen(geom) + 1)); + pargs = args; + + //dprintf1("new path (%.70s)\n", geom); + gs_newpath(ctx->pgs); + + while (*s) + { + if ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')) + { + *pargs++ = s++; + } + else if ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E') + { + *pargs++ = s; + while ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E') + s ++; + } + else + { + s++; + } + } + + pargs[0] = s; + pargs[1] = 0; + + n = pargs - args; + i = 0; + + old = 0; + + reset_smooth = 1; + smooth_x = 0.0; + smooth_y = 0.0; + + while (i < n) + { + cmd = args[i][0]; + if (cmd == '+' || cmd == '.' || cmd == '-' || (cmd >= '0' && cmd <= '9')) + cmd = old; /* it's a number, repeat old command */ + else + i ++; + + if (reset_smooth) + { + smooth_x = 0.0; + smooth_y = 0.0; + } + + reset_smooth = 1; + + switch (cmd) + { + case 'F': + ctx->fill_rule = atoi(args[i]); + i ++; + break; + + case 'M': + gs_moveto(ctx->pgs, atof(args[i]), atof(args[i+1])); + //dprintf2("moveto %g %g\n", atof(args[i]), atof(args[i+1])); + i += 2; + break; + case 'm': + gs_rmoveto(ctx->pgs, atof(args[i]), atof(args[i+1])); + //dprintf2("rmoveto %g %g\n", atof(args[i]), atof(args[i+1])); + i += 2; + break; + + case 'L': + gs_lineto(ctx->pgs, atof(args[i]), atof(args[i+1])); + //dprintf2("lineto %g %g\n", atof(args[i]), atof(args[i+1])); + i += 2; + break; + case 'l': + gs_rlineto(ctx->pgs, atof(args[i]), atof(args[i+1])); + //dprintf2("rlineto %g %g\n", atof(args[i]), atof(args[i+1])); + i += 2; + break; + + case 'H': + gs_currentpoint(ctx->pgs, &pt); + gs_lineto(ctx->pgs, atof(args[i]), pt.y); + //dprintf1("hlineto %g\n", atof(args[i])); + i += 1; + break; + case 'h': + gs_rlineto(ctx->pgs, atof(args[i]), 0.0); + //dprintf1("rhlineto %g\n", atof(args[i])); + i += 1; + break; + + case 'V': + gs_currentpoint(ctx->pgs, &pt); + gs_lineto(ctx->pgs, pt.x, atof(args[i])); + //dprintf1("vlineto %g\n", atof(args[i])); + i += 1; + break; + case 'v': + gs_rlineto(ctx->pgs, 0.0, atof(args[i])); + //dprintf1("rvlineto %g\n", atof(args[i])); + i += 1; + break; + + case 'C': + x1 = atof(args[i+0]); + y1 = atof(args[i+1]); + x2 = atof(args[i+2]); + y2 = atof(args[i+3]); + x3 = atof(args[i+4]); + y3 = atof(args[i+5]); + gs_curveto(ctx->pgs, x1, y1, x2, y2, x3, y3); + i += 6; + reset_smooth = 0; + smooth_x = x3 - x2; + smooth_y = y3 - y2; + break; + + case 'c': + gs_currentpoint(ctx->pgs, &pt); + x1 = atof(args[i+0]) + pt.x; + y1 = atof(args[i+1]) + pt.y; + x2 = atof(args[i+2]) + pt.x; + y2 = atof(args[i+3]) + pt.y; + x3 = atof(args[i+4]) + pt.x; + y3 = atof(args[i+5]) + pt.y; + gs_curveto(ctx->pgs, x1, y1, x2, y2, x3, y3); + i += 6; + reset_smooth = 0; + smooth_x = x3 - x2; + smooth_y = y3 - y2; + break; + + case 'S': + gs_currentpoint(ctx->pgs, &pt); + x1 = atof(args[i+0]); + y1 = atof(args[i+1]); + x2 = atof(args[i+2]); + y2 = atof(args[i+3]); + //dprintf2("smooth %g %g\n", smooth_x, smooth_y); + gs_curveto(ctx->pgs, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); + i += 4; + reset_smooth = 0; + smooth_x = x2 - x1; + smooth_y = y2 - y1; + break; + + case 's': + gs_currentpoint(ctx->pgs, &pt); + x1 = atof(args[i+0]) + pt.x; + y1 = atof(args[i+1]) + pt.y; + x2 = atof(args[i+2]) + pt.x; + y2 = atof(args[i+3]) + pt.y; + //dprintf2("smooth %g %g\n", smooth_x, smooth_y); + gs_curveto(ctx->pgs, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); + i += 4; + reset_smooth = 0; + smooth_x = x2 - x1; + smooth_y = y2 - y1; + break; + + case 'Q': + gs_currentpoint(ctx->pgs, &pt); + x1 = atof(args[i+0]); + y1 = atof(args[i+1]); + x2 = atof(args[i+2]); + y2 = atof(args[i+3]); + //dprintf4("conicto %g %g %g %g\n", x1, y1, x2, y2); + gs_curveto(ctx->pgs, + (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, + (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, + x2, y2); + i += 4; + break; + case 'q': + gs_currentpoint(ctx->pgs, &pt); + x1 = atof(args[i+0]) + pt.x; + y1 = atof(args[i+1]) + pt.y; + x2 = atof(args[i+2]) + pt.x; + y2 = atof(args[i+3]) + pt.y; + //dprintf4("conicto %g %g %g %g\n", x1, y1, x2, y2); + gs_curveto(ctx->pgs, + (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, + (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, + x2, y2); + i += 4; + break; + + case 'A': + xps_draw_arc(ctx, + atof(args[i+0]), atof(args[i+1]), atof(args[i+2]), + atoi(args[i+3]), atoi(args[i+4]), + atof(args[i+5]), atof(args[i+6])); + i += 7; + break; + case 'a': + gs_currentpoint(ctx->pgs, &pt); + xps_draw_arc(ctx, + atof(args[i+0]), atof(args[i+1]), atof(args[i+2]), + atoi(args[i+3]), atoi(args[i+4]), + atof(args[i+5]) + pt.x, atof(args[i+6]) + pt.y); + i += 7; + break; + + case 'Z': + case 'z': + gs_closepath(ctx->pgs); + //dputs("closepath\n"); + break; + + default: + /* eek */ + break; + } + + old = cmd; + } + + xps_free(ctx, args); +} + +static void +xps_parse_arc_segment(xps_context_t *ctx, xps_item_t *root, int stroking, int *skipped_stroke) +{ + /* ArcSegment pretty much follows the SVG algorithm for converting an + * arc in endpoint representation to an arc in centerpoint + * representation. Once in centerpoint it can be given to the + * graphics library in the form of a postscript arc. */ + + float rotation_angle; + int is_large_arc, is_clockwise; + float point_x, point_y; + float size_x, size_y; + int is_stroked; + + char *point_att = xps_att(root, "Point"); + char *size_att = xps_att(root, "Size"); + char *rotation_angle_att = xps_att(root, "RotationAngle"); + char *is_large_arc_att = xps_att(root, "IsLargeArc"); + char *sweep_direction_att = xps_att(root, "SweepDirection"); + char *is_stroked_att = xps_att(root, "IsStroked"); + + if (!point_att || !size_att || !rotation_angle_att || !is_large_arc_att || !sweep_direction_att) + { + gs_warn("ArcSegment element is missing attributes"); + return; + } + + is_stroked = 1; + if (is_stroked_att && !strcmp(is_stroked_att, "false")) + is_stroked = 0; + if (!is_stroked) + *skipped_stroke = 1; + + sscanf(point_att, "%g,%g", &point_x, &point_y); + sscanf(size_att, "%g,%g", &size_x, &size_y); + rotation_angle = atof(rotation_angle_att); + is_large_arc = !strcmp(is_large_arc_att, "true"); + is_clockwise = !strcmp(sweep_direction_att, "Clockwise"); + + if (stroking && !is_stroked) + { + gs_moveto(ctx->pgs, point_x, point_y); + return; + } + + xps_draw_arc(ctx, size_x, size_y, rotation_angle, is_large_arc, is_clockwise, point_x, point_y); +} + +static void +xps_parse_poly_quadratic_bezier_segment(xps_context_t *ctx, xps_item_t *root, int stroking, int *skipped_stroke) +{ + char *points_att = xps_att(root, "Points"); + char *is_stroked_att = xps_att(root, "IsStroked"); + float x[2], y[2]; + int is_stroked; + gs_point pt; + char *s; + int n; + + if (!points_att) + { + gs_warn("PolyQuadraticBezierSegment element has no points"); + return; + } + + is_stroked = 1; + if (is_stroked_att && !strcmp(is_stroked_att, "false")) + is_stroked = 0; + if (!is_stroked) + *skipped_stroke = 1; + + s = points_att; + n = 0; + while (*s != 0) + { + while (*s == ' ') s++; + sscanf(s, "%g,%g", &x[n], &y[n]); + while (*s != ' ' && *s != 0) s++; + n ++; + if (n == 2) + { + if (stroking && !is_stroked) + { + gs_moveto(ctx->pgs, x[1], y[1]); + } + else + { + gs_currentpoint(ctx->pgs, &pt); + gs_curveto(ctx->pgs, + (pt.x + 2 * x[0]) / 3, (pt.y + 2 * y[0]) / 3, + (x[1] + 2 * x[0]) / 3, (y[1] + 2 * y[0]) / 3, + x[1], y[1]); + } + n = 0; + } + } +} + +static void +xps_parse_poly_bezier_segment(xps_context_t *ctx, xps_item_t *root, int stroking, int *skipped_stroke) +{ + char *points_att = xps_att(root, "Points"); + char *is_stroked_att = xps_att(root, "IsStroked"); + float x[3], y[3]; + int is_stroked; + char *s; + int n; + + if (!points_att) + { + gs_warn("PolyBezierSegment element has no points"); + return; + } + + is_stroked = 1; + if (is_stroked_att && !strcmp(is_stroked_att, "false")) + is_stroked = 0; + if (!is_stroked) + *skipped_stroke = 1; + + s = points_att; + n = 0; + while (*s != 0) + { + while (*s == ' ') s++; + sscanf(s, "%g,%g", &x[n], &y[n]); + while (*s != ' ' && *s != 0) s++; + n ++; + if (n == 3) + { + if (stroking && !is_stroked) + gs_moveto(ctx->pgs, x[2], y[2]); + else + gs_curveto(ctx->pgs, x[0], y[0], x[1], y[1], x[2], y[2]); + n = 0; + } + } +} + +static void +xps_parse_poly_line_segment(xps_context_t *ctx, xps_item_t *root, int stroking, int *skipped_stroke) +{ + char *points_att = xps_att(root, "Points"); + char *is_stroked_att = xps_att(root, "IsStroked"); + int is_stroked; + float x, y; + char *s; + + if (!points_att) + { + gs_warn("PolyLineSegment element has no points"); + return; + } + + is_stroked = 1; + if (is_stroked_att && !strcmp(is_stroked_att, "false")) + is_stroked = 0; + if (!is_stroked) + *skipped_stroke = 1; + + s = points_att; + while (*s != 0) + { + while (*s == ' ') s++; + sscanf(s, "%g,%g", &x, &y); + if (stroking && !is_stroked) + gs_moveto(ctx->pgs, x, y); + else + gs_lineto(ctx->pgs, x, y); + while (*s != ' ' && *s != 0) s++; + } +} + +static void +xps_parse_path_figure(xps_context_t *ctx, xps_item_t *root, int stroking) +{ + xps_item_t *node; + + char *is_closed_att; + char *start_point_att; + char *is_filled_att; + + int is_closed = 0; + int is_filled = 1; + float start_x = 0.0; + float start_y = 0.0; + + int skipped_stroke = 0; + + is_closed_att = xps_att(root, "IsClosed"); + start_point_att = xps_att(root, "StartPoint"); + is_filled_att = xps_att(root, "IsFilled"); + + if (is_closed_att) + is_closed = !strcmp(is_closed_att, "true"); + if (is_filled_att) + is_filled = !strcmp(is_filled_att, "true"); + if (start_point_att) + sscanf(start_point_att, "%g,%g", &start_x, &start_y); + + if (!stroking && !is_filled) /* not filled, when filling */ + return; + + gs_moveto(ctx->pgs, start_x, start_y); + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "ArcSegment")) + xps_parse_arc_segment(ctx, node, stroking, &skipped_stroke); + if (!strcmp(xps_tag(node), "PolyBezierSegment")) + xps_parse_poly_bezier_segment(ctx, node, stroking, &skipped_stroke); + if (!strcmp(xps_tag(node), "PolyLineSegment")) + xps_parse_poly_line_segment(ctx, node, stroking, &skipped_stroke); + if (!strcmp(xps_tag(node), "PolyQuadraticBezierSegment")) + xps_parse_poly_quadratic_bezier_segment(ctx, node, stroking, &skipped_stroke); + } + + if (is_closed) + { + if (stroking && skipped_stroke) + gs_lineto(ctx->pgs, start_x, start_y); /* we've skipped using gs_moveto... */ + else + gs_closepath(ctx->pgs); /* no skipped segments, safe to closepath properly */ + } +} + +void +xps_parse_path_geometry(xps_context_t *ctx, xps_resource_t *dict, xps_item_t *root, int stroking) +{ + xps_item_t *node; + + char *figures_att; + char *fill_rule_att; + char *transform_att; + + xps_item_t *transform_tag = NULL; + xps_item_t *figures_tag = NULL; /* only used by resource */ + + gs_matrix transform; + gs_matrix saved_transform; + + gs_newpath(ctx->pgs); + + figures_att = xps_att(root, "Figures"); + fill_rule_att = xps_att(root, "FillRule"); + transform_att = xps_att(root, "Transform"); + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "PathGeometry.Transform")) + transform_tag = xps_down(node); + } + + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &figures_att, &figures_tag, NULL); + + if (fill_rule_att) + { + if (!strcmp(fill_rule_att, "NonZero")) + ctx->fill_rule = 1; + if (!strcmp(fill_rule_att, "EvenOdd")) + ctx->fill_rule = 0; + } + + gs_make_identity(&transform); + if (transform_att || transform_tag) + { + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + } + + gs_currentmatrix(ctx->pgs, &saved_transform); + gs_concat(ctx->pgs, &transform); + + if (figures_att) + { + xps_parse_abbreviated_geometry(ctx, figures_att); + } + + if (figures_tag) + { + xps_parse_path_figure(ctx, figures_tag, stroking); + } + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "PathFigure")) + xps_parse_path_figure(ctx, node, stroking); + } + + gs_setmatrix(ctx->pgs, &saved_transform); +} + +static int +xps_parse_line_cap(char *attr) +{ + if (attr) + { + if (!strcmp(attr, "Flat")) return gs_cap_butt; + if (!strcmp(attr, "Square")) return gs_cap_square; + if (!strcmp(attr, "Round")) return gs_cap_round; + if (!strcmp(attr, "Triangle")) return gs_cap_triangle; + } + return gs_cap_butt; +} + +/* + * Parse an XPS element, and call relevant ghostscript + * functions for drawing and/or clipping the child elements. + */ + +int +xps_parse_path(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root) +{ + xps_item_t *node; + int code; + + char *fill_uri; + char *stroke_uri; + char *opacity_mask_uri; + + char *transform_att; + char *clip_att; + char *data_att; + char *fill_att; + char *stroke_att; + char *opacity_att; + char *opacity_mask_att; + + xps_item_t *transform_tag = NULL; + xps_item_t *clip_tag = NULL; + xps_item_t *data_tag = NULL; + xps_item_t *fill_tag = NULL; + xps_item_t *stroke_tag = NULL; + xps_item_t *opacity_mask_tag = NULL; + + char *fill_opacity_att = NULL; + char *stroke_opacity_att = NULL; + + char *stroke_dash_array_att; + char *stroke_dash_cap_att; + char *stroke_dash_offset_att; + char *stroke_end_line_cap_att; + char *stroke_start_line_cap_att; + char *stroke_line_join_att; + char *stroke_miter_limit_att; + char *stroke_thickness_att; + + gs_line_join linejoin; + float linewidth; + float miterlimit; + float samples[32]; + gs_color_space *colorspace; + + gs_gsave(ctx->pgs); + + ctx->fill_rule = 0; + + /* + * Extract attributes and extended attributes. + */ + + transform_att = xps_att(root, "RenderTransform"); + clip_att = xps_att(root, "Clip"); + data_att = xps_att(root, "Data"); + fill_att = xps_att(root, "Fill"); + stroke_att = xps_att(root, "Stroke"); + opacity_att = xps_att(root, "Opacity"); + opacity_mask_att = xps_att(root, "OpacityMask"); + + stroke_dash_array_att = xps_att(root, "StrokeDashArray"); + stroke_dash_cap_att = xps_att(root, "StrokeDashCap"); + stroke_dash_offset_att = xps_att(root, "StrokeDashOffset"); + stroke_end_line_cap_att = xps_att(root, "StrokeEndLineCap"); + stroke_start_line_cap_att = xps_att(root, "StrokeStartLineCap"); + stroke_line_join_att = xps_att(root, "StrokeLineJoin"); + stroke_miter_limit_att = xps_att(root, "StrokeMiterLimit"); + stroke_thickness_att = xps_att(root, "StrokeThickness"); + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "Path.RenderTransform")) + transform_tag = xps_down(node); + + if (!strcmp(xps_tag(node), "Path.OpacityMask")) + opacity_mask_tag = xps_down(node); + + if (!strcmp(xps_tag(node), "Path.Clip")) + clip_tag = xps_down(node); + + if (!strcmp(xps_tag(node), "Path.Fill")) + fill_tag = xps_down(node); + + if (!strcmp(xps_tag(node), "Path.Stroke")) + stroke_tag = xps_down(node); + + if (!strcmp(xps_tag(node), "Path.Data")) + data_tag = xps_down(node); + } + + fill_uri = base_uri; + stroke_uri = base_uri; + opacity_mask_uri = base_uri; + + xps_resolve_resource_reference(ctx, dict, &data_att, &data_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &fill_att, &fill_tag, &fill_uri); + xps_resolve_resource_reference(ctx, dict, &stroke_att, &stroke_tag, &stroke_uri); + xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); + + /* + * Act on the information we have gathered: + */ + + if (fill_tag && !strcmp(xps_tag(fill_tag), "SolidColorBrush")) + { + fill_opacity_att = xps_att(fill_tag, "Opacity"); + fill_att = xps_att(fill_tag, "Color"); + fill_tag = NULL; + } + + if (stroke_tag && !strcmp(xps_tag(stroke_tag), "SolidColorBrush")) + { + stroke_opacity_att = xps_att(stroke_tag, "Opacity"); + stroke_att = xps_att(stroke_tag, "Color"); + stroke_tag = NULL; + } + + gs_setlinestartcap(ctx->pgs, xps_parse_line_cap(stroke_start_line_cap_att)); + gs_setlineendcap(ctx->pgs, xps_parse_line_cap(stroke_end_line_cap_att)); + gs_setlinedashcap(ctx->pgs, xps_parse_line_cap(stroke_dash_cap_att)); + + linejoin = gs_join_miter; + if (stroke_line_join_att) + { + if (!strcmp(stroke_line_join_att, "Miter")) linejoin = gs_join_miter; + if (!strcmp(stroke_line_join_att, "Bevel")) linejoin = gs_join_bevel; + if (!strcmp(stroke_line_join_att, "Round")) linejoin = gs_join_round; + } + gs_setlinejoin(ctx->pgs, linejoin); + + miterlimit = 10.0; + if (stroke_miter_limit_att) + miterlimit = atof(stroke_miter_limit_att); + gs_setmiterlimit(ctx->pgs, miterlimit); + + linewidth = 1.0; + if (stroke_thickness_att) + linewidth = atof(stroke_thickness_att); + gs_setlinewidth(ctx->pgs, linewidth); + + if (stroke_dash_array_att) + { + char *s = stroke_dash_array_att; + float dash_array[100]; + float dash_offset = 0.0; + int dash_count = 0; + + if (stroke_dash_offset_att) + dash_offset = atof(stroke_dash_offset_att) * linewidth; + + while (*s) + { + while (*s == ' ') + s++; + dash_array[dash_count++] = atof(s) * linewidth; + while (*s && *s != ' ') + s++; + } + + gs_setdash(ctx->pgs, dash_array, dash_count, dash_offset); + } + else + { + gs_setdash(ctx->pgs, NULL, 0, 0.0); + } + + if (transform_att || transform_tag) + { + gs_matrix transform; + + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + + gs_concat(ctx->pgs, &transform); + } + + if (clip_att || clip_tag) + { + if (clip_att) + xps_parse_abbreviated_geometry(ctx, clip_att); + if (clip_tag) + xps_parse_path_geometry(ctx, dict, clip_tag, 0); + xps_clip(ctx); + } + +#if 0 // XXX + if (opacity_att || opacity_mask_tag) + { + /* clip the bounds with the actual path */ + if (data_att) + xps_parse_abbreviated_geometry(ctx, data_att); + if (data_tag) + xps_parse_path_geometry(ctx, dict, data_tag, 0); + xps_update_bounds(ctx, &saved_bounds_opacity); + gs_newpath(ctx->pgs); + } +#endif + + code = xps_begin_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + if (code) + { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot create transparency group"); + } + + if (fill_att) + { + xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples); + if (fill_opacity_att) + samples[0] = atof(fill_opacity_att); + xps_set_color(ctx, colorspace, samples); + + if (data_att) + xps_parse_abbreviated_geometry(ctx, data_att); + if (data_tag) + xps_parse_path_geometry(ctx, dict, data_tag, 0); + + xps_fill(ctx); + } + + if (fill_tag) + { + if (data_att) + xps_parse_abbreviated_geometry(ctx, data_att); + if (data_tag) + xps_parse_path_geometry(ctx, dict, data_tag, 0); + + code = xps_parse_brush(ctx, fill_uri, dict, fill_tag); + if (code < 0) + { + xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot parse fill brush"); + } + } + + if (stroke_att) + { + xps_parse_color(ctx, base_uri, stroke_att, &colorspace, samples); + if (stroke_opacity_att) + samples[0] = atof(stroke_opacity_att); + xps_set_color(ctx, colorspace, samples); + + if (data_att) + xps_parse_abbreviated_geometry(ctx, data_att); + if (data_tag) + xps_parse_path_geometry(ctx, dict, data_tag, 1); + + gs_stroke(ctx->pgs); + } + + if (stroke_tag) + { + if (data_att) + xps_parse_abbreviated_geometry(ctx, data_att); + if (data_tag) + xps_parse_path_geometry(ctx, dict, data_tag, 1); + + ctx->fill_rule = 1; /* over-ride fill rule when converting outline to stroked */ + gs_strokepath2(ctx->pgs); + + code = xps_parse_brush(ctx, stroke_uri, dict, stroke_tag); + if (code < 0) + { + xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot parse stroke brush"); + } + } + + xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + gs_grestore(ctx->pgs); + return 0; +} diff --git a/xps/xpspng.c b/xps/xpspng.c new file mode 100644 index 00000000..d88461cc --- /dev/null +++ b/xps/xpspng.c @@ -0,0 +1,293 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - PNG image support */ + +#include "ghostxps.h" + +#include "stream.h" +#include "strimpl.h" +#include "gsstate.h" + +/* silence a warning where #if SHARE_LIBPNG is used when it's undefined */ +#ifndef SHARE_LIBPNG +#define SHARE_LIBPNG 0 +#endif +#include "png_.h" + +/* + * PNG using libpng directly (no gs wrappers) + */ + +struct xps_png_io_s +{ + byte *ptr; + byte *lim; +}; + +static void +xps_png_read(png_structp png, png_bytep data, png_size_t length) +{ + struct xps_png_io_s *io = png_get_io_ptr(png); + if (io->ptr + length > io->lim) + png_error(png, "Read Error"); + memcpy(data, io->ptr, length); + io->ptr += length; +} + +static png_voidp +xps_png_malloc(png_structp png, png_size_t size) +{ + gs_memory_t *mem = png_get_mem_ptr(png); + return gs_alloc_bytes(mem, size, "libpng"); +} + +static void +xps_png_free(png_structp png, png_voidp ptr) +{ + gs_memory_t *mem = png_get_mem_ptr(png); + gs_free_object(mem, ptr, "libpng"); +} + +/* This only determines if we have an alpha value */ +int +xps_png_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen) +{ + png_structp png; + png_infop info; + struct xps_png_io_s io; + int has_alpha; + + /* + * Set up PNG structs and input source + */ + + io.ptr = rbuf; + io.lim = rbuf + rlen; + + png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL, + ctx->memory, xps_png_malloc, xps_png_free); + if (!png) { + gs_warn("png_create_read_struct"); + return 0; + } + + info = png_create_info_struct(png); + if (!info) { + gs_warn("png_create_info_struct"); + return 0; + } + + png_set_read_fn(png, &io, xps_png_read); + png_set_crc_action(png, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE); + + /* + * Jump to here on errors. + */ + + if (setjmp(png_jmpbuf(png))) + { + png_destroy_read_struct(&png, &info, NULL); + gs_warn("png reading failed"); + return 0; + } + + /* + * Read PNG header + */ + + png_read_info(png, info); + + switch (png_get_color_type(png, info)) + { + case PNG_COLOR_TYPE_PALETTE: + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_RGB: + has_alpha = 0; + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + case PNG_COLOR_TYPE_RGB_ALPHA: + has_alpha = 1; + break; + + default: + gs_warn("cannot handle this png color type"); + has_alpha = 0; + break; + } + + /* + * Clean up memory. + */ + + png_destroy_read_struct(&png, &info, NULL); + + return has_alpha; +} + +int +xps_decode_png(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image) +{ + png_structp png; + png_infop info; + struct xps_png_io_s io; + int npasses; + int pass; + int y; + + /* + * Set up PNG structs and input source + */ + + io.ptr = rbuf; + io.lim = rbuf + rlen; + + png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL, + ctx->memory, xps_png_malloc, xps_png_free); + if (!png) + return gs_throw(-1, "png_create_read_struct"); + + info = png_create_info_struct(png); + if (!info) + return gs_throw(-1, "png_create_info_struct"); + + png_set_read_fn(png, &io, xps_png_read); + png_set_crc_action(png, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE); + + /* + * Jump to here on errors. + */ + + if (setjmp(png_jmpbuf(png))) + { + png_destroy_read_struct(&png, &info, NULL); + return gs_throw(-1, "png reading failed"); + } + + /* + * Read PNG header + */ + + png_read_info(png, info); + + if (png_get_interlace_type(png, info) == PNG_INTERLACE_ADAM7) + { + npasses = png_set_interlace_handling(png); + } + else + { + npasses = 1; + } + + if (png_get_color_type(png, info) == PNG_COLOR_TYPE_PALETTE) + { + png_set_palette_to_rgb(png); + } + + if (png_get_valid(png, info, PNG_INFO_tRNS)) + { + /* this will also expand the depth to 8-bits */ + png_set_tRNS_to_alpha(png); + } + + png_read_update_info(png, info); + + image->width = png_get_image_width(png, info); + image->height = png_get_image_height(png, info); + image->comps = png_get_channels(png, info); + image->bits = png_get_bit_depth(png, info); + + /* See if we have an icc profile */ + if (info->iccp_profile != NULL) + { + image->profilesize = info->iccp_proflen; + image->profile = xps_alloc(ctx, info->iccp_proflen); + if (image->profile) + { + /* If we can't create it, just ignore */ + memcpy(image->profile, info->iccp_profile, info->iccp_proflen); + } + } + + switch (png_get_color_type(png, info)) + { + case PNG_COLOR_TYPE_GRAY: + image->colorspace = ctx->gray; + image->hasalpha = 0; + break; + + case PNG_COLOR_TYPE_RGB: + image->colorspace = ctx->srgb; + image->hasalpha = 0; + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + image->colorspace = ctx->gray; + image->hasalpha = 1; + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + image->colorspace = ctx->srgb; + image->hasalpha = 1; + break; + + default: + return gs_throw(-1, "cannot handle this png color type"); + } + + /* + * Extract DPI, default to 96 dpi + */ + + image->xres = 96; + image->yres = 96; + + if (info->valid & PNG_INFO_pHYs) + { + png_uint_32 xres, yres; + int unit; + png_get_pHYs(png, info, &xres, &yres, &unit); + if (unit == PNG_RESOLUTION_METER) + { + image->xres = xres * 0.0254 + 0.5; + image->yres = yres * 0.0254 + 0.5; + } + } + + /* + * Read rows, filling transformed output into image buffer. + */ + + image->stride = (image->width * image->comps * image->bits + 7) / 8; + + image->samples = xps_alloc(ctx, image->stride * image->height); + + for (pass = 0; pass < npasses; pass++) + { + for (y = 0; y < image->height; y++) + { + png_read_row(png, image->samples + (y * image->stride), NULL); + } + } + + /* + * Clean up memory. + */ + + png_destroy_read_struct(&png, &info, NULL); + + return gs_okay; +} diff --git a/xps/xpsresource.c b/xps/xpsresource.c new file mode 100644 index 00000000..a21d6bed --- /dev/null +++ b/xps/xpsresource.c @@ -0,0 +1,204 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - resource functions */ + +#include "ghostxps.h" + +static xps_item_t * +xps_find_resource(xps_context_t *ctx, xps_resource_t *dict, char *name, char **urip) +{ + xps_resource_t *head, *node; + for (head = dict; head; head = head->parent) + { + for (node = head; node; node = node->next) + { + if (!strcmp(node->name, name)) + { + if (urip && head->base_uri) + *urip = head->base_uri; + return node->data; + } + } + } + return NULL; +} + +static xps_item_t * +xps_parse_resource_reference(xps_context_t *ctx, xps_resource_t *dict, char *att, char **urip) +{ + char name[1024]; + char *s; + + if (strstr(att, "{StaticResource ") != att) + return NULL; + + xps_strlcpy(name, att + 16, sizeof name); + s = strrchr(name, '}'); + if (s) + *s = 0; + + return xps_find_resource(ctx, dict, name, urip); +} + +void +xps_resolve_resource_reference(xps_context_t *ctx, xps_resource_t *dict, + char **attp, xps_item_t **tagp, char **urip) +{ + if (*attp) + { + xps_item_t *rsrc = xps_parse_resource_reference(ctx, dict, *attp, urip); + if (rsrc) + { + *attp = NULL; + *tagp = rsrc; + } + } +} + +static int +xps_parse_remote_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, char *base_uri, char *source_att) +{ + char part_name[1024]; + char part_uri[1024]; + xps_resource_t *dict; + xps_part_t *part; + xps_item_t *xml; + char *s; + int code; + + /* External resource dictionaries MUST NOT reference other resource dictionaries */ + xps_absolute_path(part_name, base_uri, source_att, sizeof part_name); + part = xps_read_part(ctx, part_name); + if (!part) + { + return gs_throw1(-1, "cannot find remote resource part '%s'", part_name); + } + + xml = xps_parse_xml(ctx, part->data, part->size); + if (!xml) + { + xps_free_part(ctx, part); + return gs_rethrow(-1, "cannot parse xml"); + } + + if (strcmp(xps_tag(xml), "ResourceDictionary")) + { + xps_free_item(ctx, xml); + xps_free_part(ctx, part); + return gs_throw1(-1, "expected ResourceDictionary element (found %s)", xps_tag(xml)); + } + + xps_strlcpy(part_uri, part_name, sizeof part_uri); + s = strrchr(part_uri, '/'); + if (s) + s[1] = 0; + + code = xps_parse_resource_dictionary(ctx, &dict, part_uri, xml); + if (code) + { + xps_free_item(ctx, xml); + xps_free_part(ctx, part); + return gs_rethrow1(code, "cannot parse remote resource dictionary: %s", part_uri); + } + + dict->base_xml = xml; /* pass on ownership */ + + xps_free_part(ctx, part); + + *dictp = dict; + return gs_okay; +} + +int +xps_parse_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, char *base_uri, xps_item_t *root) +{ + xps_resource_t *head; + xps_resource_t *entry; + xps_item_t *node; + char *source; + char *key; + int code; + + source = xps_att(root, "Source"); + if (source) + { + code = xps_parse_remote_resource_dictionary(ctx, dictp, base_uri, source); + if (code) + return gs_rethrow(code, "cannot parse remote resource dictionary"); + return gs_okay; + } + + head = NULL; + + for (node = xps_down(root); node; node = xps_next(node)) + { + /* Usually "x:Key"; we have already processed and stripped namespace */ + key = xps_att(node, "Key"); + if (key) + { + entry = xps_alloc(ctx, sizeof(xps_resource_t)); + if (!entry) + return gs_throw(-1, "cannot allocate resource entry"); + entry->name = key; + entry->base_uri = NULL; + entry->base_xml = NULL; + entry->data = node; + entry->next = head; + entry->parent = NULL; + head = entry; + } + } + + if (head) + { + head->base_uri = xps_strdup(ctx, base_uri); + } + + *dictp = head; + return gs_okay; +} + +void +xps_free_resource_dictionary(xps_context_t *ctx, xps_resource_t *dict) +{ + xps_resource_t *next; + while (dict) + { + next = dict->next; + if (dict->base_xml) + xps_free_item(ctx, dict->base_xml); + if (dict->base_uri) + xps_free(ctx, dict->base_uri); + xps_free(ctx, dict); + dict = next; + } +} + +void +xps_debug_resource_dictionary(xps_resource_t *dict) +{ + while (dict) + { + if (dict->base_uri) + dprintf1("URI = '%s'\n", dict->base_uri); + dprintf2("KEY = '%s' VAL = %p\n", dict->name, dict->data); + if (dict->parent) + { + dputs("PARENT = {\n"); + xps_debug_resource_dictionary(dict->parent); + dputs("}\n"); + } + dict = dict->next; + } +} diff --git a/xps/xpstiff.c b/xps/xpstiff.c new file mode 100644 index 00000000..3e2627ef --- /dev/null +++ b/xps/xpstiff.c @@ -0,0 +1,1091 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - TIFF image support */ + +#include "ghostxps.h" + +#include "stream.h" +#include "strimpl.h" +#include "gsstate.h" +#include "jpeglib_.h" +#include "sdct.h" +#include "sjpeg.h" +#include "srlx.h" +#include "slzwx.h" +#include "szlibx.h" +#include "scfx.h" +#include "memory_.h" + +/* + * TIFF image loader. Should be enough to support TIFF files in XPS. + * Baseline TIFF 6.0 plus CMYK, LZW, Flate and JPEG support. + * Limited bit depths (1,2,4,8). + * Limited planar configurations (1=chunky). + * No tiles (easy fix if necessary). + * TODO: RGBPal images + */ + +typedef struct xps_tiff_s xps_tiff_t; + +struct xps_tiff_s +{ + /* "file" */ + byte *bp, *rp, *ep; + + /* byte order */ + unsigned order; + + /* where we can find the strips of image data */ + unsigned rowsperstrip; + unsigned *stripoffsets; + unsigned *stripbytecounts; + + /* colormap */ + unsigned *colormap; + + /* assorted tags */ + unsigned subfiletype; + unsigned photometric; + unsigned compression; + unsigned imagewidth; + unsigned imagelength; + unsigned samplesperpixel; + unsigned bitspersample; + unsigned planar; + unsigned extrasamples; + unsigned xresolution; + unsigned yresolution; + unsigned resolutionunit; + unsigned fillorder; + unsigned g3opts; + unsigned g4opts; + unsigned predictor; + + unsigned ycbcrsubsamp[2]; + + byte *jpegtables; /* point into "file" buffer */ + unsigned jpegtableslen; + + byte *profile; + int profilesize; +}; + +enum +{ + TII = 0x4949, /* 'II' */ + TMM = 0x4d4d, /* 'MM' */ + TBYTE = 1, + TASCII = 2, + TSHORT = 3, + TLONG = 4, + TRATIONAL = 5 +}; + +#define NewSubfileType 254 +#define ImageWidth 256 +#define ImageLength 257 +#define BitsPerSample 258 +#define Compression 259 +#define PhotometricInterpretation 262 +#define FillOrder 266 +#define StripOffsets 273 +#define SamplesPerPixel 277 +#define RowsPerStrip 278 +#define StripByteCounts 279 +#define XResolution 282 +#define YResolution 283 +#define PlanarConfiguration 284 +#define T4Options 292 +#define T6Options 293 +#define ResolutionUnit 296 +#define Predictor 317 +#define ColorMap 320 +#define TileWidth 322 +#define TileLength 323 +#define TileOffsets 324 +#define TileByteCounts 325 +#define ExtraSamples 338 +#define JPEGTables 347 +#define YCbCrSubSampling 520 +#define ICCProfile 34675 + +static const byte bitrev[256] = +{ + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + +static int +xps_report_error(stream_state * st, const char *str) +{ + (void) gs_throw1(-1, "%s", str); + return 0; +} + +static inline int +readbyte(xps_tiff_t *tiff) +{ + if (tiff->rp < tiff->ep) + return *tiff->rp++; + return EOF; +} + +static inline unsigned +readshort(xps_tiff_t *tiff) +{ + unsigned a = readbyte(tiff); + unsigned b = readbyte(tiff); + if (tiff->order == TII) + return (b << 8) | a; + return (a << 8) | b; +} + +static inline unsigned +readlong(xps_tiff_t *tiff) +{ + unsigned a = readbyte(tiff); + unsigned b = readbyte(tiff); + unsigned c = readbyte(tiff); + unsigned d = readbyte(tiff); + if (tiff->order == TII) + return (d << 24) | (c << 16) | (b << 8) | a; + return (a << 24) | (b << 16) | (c << 8) | d; +} + +static int +xps_decode_tiff_uncompressed(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +{ + memcpy(wp, rp, wl - wp); + return gs_okay; +} + +static int +xps_decode_tiff_packbits(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +{ + stream_RLD_state state; + stream_cursor_read scr; + stream_cursor_write scw; + int code; + + s_init_state((stream_state*)&state, &s_RLD_template, ctx->memory); + state.report_error = xps_report_error; + + s_RLD_template.set_defaults((stream_state*)&state); + s_RLD_template.init((stream_state*)&state); + + scr.ptr = rp - 1; + scr.limit = rl - 1; + scw.ptr = wp - 1; + scw.limit = wl - 1; + + code = s_RLD_template.process((stream_state*)&state, &scr, &scw, true); + if (code == ERRC) + return gs_throw1(-1, "error in packbits data (code = %d)", code); + + return gs_okay; +} + +static int +xps_decode_tiff_lzw(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +{ + stream_LZW_state state; + stream_cursor_read scr; + stream_cursor_write scw; + int code; + + s_init_state((stream_state*)&state, &s_LZWD_template, ctx->memory); + state.report_error = xps_report_error; + + s_LZWD_template.set_defaults((stream_state*)&state); + + /* old-style TIFF 5.0 reversed bit order, late change */ + if (rp[0] == 0 && rp[1] & 0x01) + { + state.EarlyChange = 0; + state.FirstBitLowOrder = 1; + } + + /* new-style TIFF 6.0 normal bit order, early change */ + else + { + state.EarlyChange = 1; + state.FirstBitLowOrder = 0; + } + + s_LZWD_template.init((stream_state*)&state); + + scr.ptr = rp - 1; + scr.limit = rl - 1; + scw.ptr = wp - 1; + scw.limit = wl - 1; + + code = s_LZWD_template.process((stream_state*)&state, &scr, &scw, true); + if (code == ERRC) + { + s_LZWD_template.release((stream_state*)&state); + return gs_throw1(-1, "error in lzw data (code = %d)", code); + } + + s_LZWD_template.release((stream_state*)&state); + + return gs_okay; +} + +static int +xps_decode_tiff_flate(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +{ + stream_zlib_state state; + stream_cursor_read scr; + stream_cursor_write scw; + int code; + + s_init_state((stream_state*)&state, &s_zlibD_template, ctx->memory); + state.report_error = xps_report_error; + + s_zlibD_template.set_defaults((stream_state*)&state); + + s_zlibD_template.init((stream_state*)&state); + + scr.ptr = rp - 1; + scr.limit = rl - 1; + scw.ptr = wp - 1; + scw.limit = wl - 1; + + code = s_zlibD_template.process((stream_state*)&state, &scr, &scw, true); + if (code == ERRC) + { + s_zlibD_template.release((stream_state*)&state); + return gs_throw1(-1, "error in flate data (code = %d)", code); + } + + s_zlibD_template.release((stream_state*)&state); + return gs_okay; +} + +static int +xps_decode_tiff_fax(xps_context_t *ctx, xps_tiff_t *tiff, int comp, byte *rp, byte *rl, byte *wp, byte *wl) +{ + stream_CFD_state state; + stream_cursor_read scr; + stream_cursor_write scw; + int code; + + s_init_state((stream_state*)&state, &s_CFD_template, ctx->memory); + state.report_error = xps_report_error; + + s_CFD_template.set_defaults((stream_state*)&state); + + state.EndOfLine = false; + state.EndOfBlock = false; + state.Columns = tiff->imagewidth; + state.Rows = tiff->imagelength; + state.BlackIs1 = tiff->photometric == 0; + + state.K = 0; + if (comp == 4) + state.K = -1; + if (comp == 2) + state.EncodedByteAlign = true; + + s_CFD_template.init((stream_state*)&state); + + scr.ptr = rp - 1; + scr.limit = rl - 1; + scw.ptr = wp - 1; + scw.limit = wl - 1; + + code = s_CFD_template.process((stream_state*)&state, &scr, &scw, true); + if (code == ERRC) + { + s_CFD_template.release((stream_state*)&state); + return gs_throw1(-1, "error in fax data (code = %d)", code); + } + + s_CFD_template.release((stream_state*)&state); + return gs_okay; +} + +/* + * We need more find control over JPEG decoding parameters than + * the s_DCTD_template filter will give us. So we abuse the + * filter, and take control after the filter setup (which sets up + * the memory manager and error handling) and call the gs_jpeg + * wrappers directly for doing the actual decoding. + */ + +static int +xps_decode_tiff_jpeg(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +{ + stream_DCT_state state; /* used by gs_jpeg_* wrappers */ + jpeg_decompress_data jddp; + struct jpeg_source_mgr *srcmgr; + JSAMPROW scanlines[1]; + int stride; + int code; + + /* + * Set up the JPEG and DCT filter voodoo. + */ + + s_init_state((stream_state*)&state, &s_DCTD_template, ctx->memory); + state.report_error = xps_report_error; + s_DCTD_template.set_defaults((stream_state*)&state); + + state.jpeg_memory = ctx->memory; + state.data.decompress = &jddp; + + jddp.template = s_DCTD_template; + jddp.memory = ctx->memory; + jddp.scanline_buffer = NULL; + + if ((code = gs_jpeg_create_decompress(&state)) < 0) + return gs_throw(-1, "error in gs_jpeg_create_decompress"); + + s_DCTD_template.init((stream_state*)&state); + + srcmgr = jddp.dinfo.src; + + /* + * Read the abbreviated table file. + */ + + if (tiff->jpegtables) + { + srcmgr->next_input_byte = tiff->jpegtables; + srcmgr->bytes_in_buffer = tiff->jpegtableslen; + + code = gs_jpeg_read_header(&state, FALSE); + if (code != JPEG_HEADER_TABLES_ONLY) + return gs_throw(-1, "error in jpeg table data"); + } + + /* + * Read the image jpeg header. + */ + + srcmgr->next_input_byte = rp; + srcmgr->bytes_in_buffer = rl - rp; + + if ((code = gs_jpeg_read_header(&state, TRUE)) < 0) + return gs_throw(-1, "error in jpeg_read_header"); + + /* when TIFF says RGB and libjpeg says YCbCr, libjpeg is wrong */ + if (tiff->photometric == 2 && jddp.dinfo.jpeg_color_space == JCS_YCbCr) + { + jddp.dinfo.jpeg_color_space = JCS_RGB; + } + + /* + * Decode the strip image data. + */ + + if ((code = gs_jpeg_start_decompress(&state)) < 0) + return gs_throw(-1, "error in jpeg_start_decompress"); + + stride = jddp.dinfo.output_width * jddp.dinfo.output_components; + + while (wp + stride <= wl && jddp.dinfo.output_scanline < jddp.dinfo.output_height) + { + scanlines[0] = wp; + code = gs_jpeg_read_scanlines(&state, scanlines, 1); + if (code < 0) + return gs_throw(01, "error in jpeg_read_scanlines"); + wp += stride; + } + + /* + * Clean up. + */ + + if ((code = gs_jpeg_finish_decompress(&state)) < 0) + return gs_throw(-1, "error in jpeg_finish_decompress"); + + gs_jpeg_destroy(&state); + + return gs_okay; +} + +static inline int +getcomp(byte *line, int x, int bpc) +{ + switch (bpc) + { + case 1: return line[x / 8] >> (7 - (x % 8)) & 0x01; + case 2: return line[x / 4] >> ((3 - (x % 4)) * 2) & 0x03; + case 4: return line[x / 2] >> ((1 - (x % 2)) * 4) & 0x0f; + case 8: return line[x]; + case 16: return ((line[x * 2 + 0]) << 8) | (line[x * 2 + 1]); + } + return 0; +} + +static inline void +putcomp(byte *line, int x, int bpc, int value) +{ + int maxval = (1 << bpc) - 1; + + // clear bits first + switch (bpc) + { + case 1: line[x / 8] &= ~(maxval << (7 - (x % 8))); break; + case 2: line[x / 4] &= ~(maxval << ((3 - (x % 4)) * 2)); break; + case 4: line[x / 2] &= ~(maxval << ((1 - (x % 2)) * 4)); break; + } + + switch (bpc) + { + case 1: line[x / 8] |= value << (7 - (x % 8)); break; + case 2: line[x / 4] |= value << ((3 - (x % 4)) * 2); break; + case 4: line[x / 2] |= value << ((1 - (x % 2)) * 4); break; + case 8: line[x] = value; break; + case 16: line[x * 2 + 0] = value >> 8; line[x * 2 + 1] = value & 0xFF; break; + } +} + +static void +xps_unpredict_tiff(byte *line, int width, int comps, int bits) +{ + byte left[32]; + int i, k, v; + + for (k = 0; k < comps; k++) + left[k] = 0; + + for (i = 0; i < width; i++) + { + for (k = 0; k < comps; k++) + { + v = getcomp(line, i * comps + k, bits); + v = v + left[k]; + v = v % (1 << bits); + putcomp(line, i * comps + k, bits, v); + left[k] = v; + } + } +} + +static void +xps_invert_tiff(byte *line, int width, int comps, int bits, int alpha) +{ + int i, k, v; + int m = (1 << bits) - 1; + + for (i = 0; i < width; i++) + { + for (k = 0; k < comps; k++) + { + v = getcomp(line, i * comps + k, bits); + if (!alpha || k < comps - 1) + v = m - v; + putcomp(line, i * comps + k, bits, v); + } + } +} + +static int +xps_expand_colormap(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) +{ + int maxval = 1 << image->bits; + byte *samples; + byte *src, *dst; + int stride; + int x, y; + + /* colormap has first all red, then all green, then all blue values */ + /* colormap values are 0..65535, bits is 4 or 8 */ + /* image can be with or without extrasamples: comps is 1 or 2 */ + + if (image->comps != 1 && image->comps != 2) + return gs_throw(-1, "invalid number of samples for RGBPal"); + + if (image->bits != 4 && image->bits != 8) + return gs_throw(-1, "invalid number of bits for RGBPal"); + + stride = image->width * (image->comps + 2); + + samples = xps_alloc(ctx, stride * image->height); + if (!samples) + return gs_throw(-1, "out of memory: samples"); + + for (y = 0; y < image->height; y++) + { + src = image->samples + (image->stride * y); + dst = samples + (stride * y); + + for (x = 0; x < image->width; x++) + { + if (tiff->extrasamples) + { + int c = getcomp(src, x * 2, image->bits); + int a = getcomp(src, x * 2 + 1, image->bits); + *dst++ = tiff->colormap[c + 0] >> 8; + *dst++ = tiff->colormap[c + maxval] >> 8; + *dst++ = tiff->colormap[c + maxval * 2] >> 8; + *dst++ = a << (8 - image->bits); + } + else + { + int c = getcomp(src, x, image->bits); + *dst++ = tiff->colormap[c + 0] >> 8; + *dst++ = tiff->colormap[c + maxval] >> 8; + *dst++ = tiff->colormap[c + maxval * 2] >> 8; + } + } + } + + image->bits = 8; + image->stride = stride; + image->samples = samples; + + return gs_okay; +} + +static int +xps_decode_tiff_strips(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) +{ + int error; + + /* switch on compression to create a filter */ + /* feed each strip to the filter */ + /* read out the data and pack the samples into an xps_image */ + + /* type 32773 / packbits -- nothing special (same row-padding as PDF) */ + /* type 2 / ccitt rle -- no EOL, no RTC, rows are byte-aligned */ + /* type 3 and 4 / g3 and g4 -- each strip starts new section */ + /* type 5 / lzw -- each strip is handled separately */ + + byte *wp; + unsigned row; + unsigned strip; + unsigned i; + + if (!tiff->rowsperstrip || !tiff->stripoffsets || !tiff->rowsperstrip) + return gs_throw(-1, "no image data in tiff; maybe it is tiled"); + + if (tiff->planar != 1) + return gs_throw(-1, "image data is not in chunky format"); + + image->width = tiff->imagewidth; + image->height = tiff->imagelength; + image->comps = tiff->samplesperpixel; + image->bits = tiff->bitspersample; + image->stride = (image->width * image->comps * image->bits + 7) / 8; + + switch (tiff->photometric) + { + case 0: /* WhiteIsZero -- inverted */ + image->colorspace = ctx->gray; + break; + case 1: /* BlackIsZero */ + image->colorspace = ctx->gray; + break; + case 2: /* RGB */ + image->colorspace = ctx->srgb; + break; + case 3: /* RGBPal */ + image->colorspace = ctx->srgb; + break; + case 5: /* CMYK */ + image->colorspace = ctx->cmyk; + break; + case 6: /* YCbCr */ + /* it's probably a jpeg ... we let jpeg convert to rgb */ + image->colorspace = ctx->srgb; + break; + default: + return gs_throw1(-1, "unknown photometric: %d", tiff->photometric); + } + + switch (tiff->resolutionunit) + { + case 2: + image->xres = tiff->xresolution; + image->yres = tiff->yresolution; + break; + case 3: + image->xres = tiff->xresolution * 2.54 + 0.5; + image->yres = tiff->yresolution * 2.54 + 0.5; + + break; + default: + image->xres = 96; + image->yres = 96; + break; + } + + /* Note xres and yres could be 0 even if unit was set. If so default to 96dpi */ + if (image->xres == 0 || image->yres == 0) + { + image->xres = 96; + image->yres = 96; + } + + image->samples = xps_alloc(ctx, image->stride * image->height); + if (!image->samples) + return gs_throw(-1, "could not allocate image samples"); + + memset(image->samples, 0x55, image->stride * image->height); + + wp = image->samples; + + strip = 0; + for (row = 0; row < tiff->imagelength; row += tiff->rowsperstrip) + { + unsigned offset = tiff->stripoffsets[strip]; + unsigned rlen = tiff->stripbytecounts[strip]; + unsigned wlen = image->stride * tiff->rowsperstrip; + byte *rp = tiff->bp + offset; + + if (wp + wlen > image->samples + image->stride * image->height) + wlen = image->samples + image->stride * image->height - wp; + + if (rp + rlen > tiff->ep) + return gs_throw(-1, "strip extends beyond the end of the file"); + + /* the bits are in un-natural order */ + if (tiff->fillorder == 2) + for (i = 0; i < rlen; i++) + rp[i] = bitrev[rp[i]]; + + switch (tiff->compression) + { + case 1: + error = xps_decode_tiff_uncompressed(ctx, tiff, rp, rp + rlen, wp, wp + wlen); + break; + case 2: + error = xps_decode_tiff_fax(ctx, tiff, 2, rp, rp + rlen, wp, wp + wlen); + break; + case 3: + error = xps_decode_tiff_fax(ctx, tiff, 3, rp, rp + rlen, wp, wp + wlen); + break; + case 4: + error = xps_decode_tiff_fax(ctx, tiff, 4, rp, rp + rlen, wp, wp + wlen); + break; + case 5: + error = xps_decode_tiff_lzw(ctx, tiff, rp, rp + rlen, wp, wp + wlen); + break; + case 6: + error = gs_throw(-1, "deprecated JPEG in TIFF compression not supported"); + break; + case 7: + error = xps_decode_tiff_jpeg(ctx, tiff, rp, rp + rlen, wp, wp + wlen); + break; + case 8: + error = xps_decode_tiff_flate(ctx, tiff, rp, rp + rlen, wp, wp + wlen); + break; + case 32773: + error = xps_decode_tiff_packbits(ctx, tiff, rp, rp + rlen, wp, wp + wlen); + break; + default: + error = gs_throw1(-1, "unknown TIFF compression: %d", tiff->compression); + } + + if (error) + return gs_rethrow1(error, "could not decode strip %d", row / tiff->rowsperstrip); + + /* scramble the bits back into original order */ + if (tiff->fillorder == 2) + for (i = 0; i < rlen; i++) + rp[i] = bitrev[rp[i]]; + + wp += image->stride * tiff->rowsperstrip; + strip ++; + } + + /* Predictor (only for LZW and Flate) */ + if ((tiff->compression == 5 || tiff->compression == 8) && tiff->predictor == 2) + { + byte *p = image->samples; + for (i = 0; i < image->height; i++) + { + xps_unpredict_tiff(p, image->width, tiff->samplesperpixel, image->bits); + p += image->stride; + } + } + + /* RGBPal */ + if (tiff->photometric == 3 && tiff->colormap) + { + error = xps_expand_colormap(ctx, tiff, image); + if (error) + return gs_rethrow(error, "could not expand colormap"); + } + + /* WhiteIsZero .. invert */ + if (tiff->photometric == 0) + { + byte *p = image->samples; + for (i = 0; i < image->height; i++) + { + xps_invert_tiff(p, image->width, image->comps, image->bits, tiff->extrasamples); + p += image->stride; + } + } + + /* Premultiplied transparency */ + if (tiff->extrasamples == 1) + { + image->hasalpha = 1; + } + + /* Non-pre-multiplied transparency */ + if (tiff->extrasamples == 2) + { + image->hasalpha = 1; + } + + return gs_okay; +} + +static void +xps_read_tiff_bytes(unsigned char *p, xps_tiff_t *tiff, unsigned ofs, unsigned n) +{ + tiff->rp = tiff->bp + ofs; + if (tiff->rp > tiff->ep) + tiff->rp = tiff->bp; + + while (n--) + { + *p++ = readbyte(tiff); + } +} + +static void +xps_read_tiff_tag_value(unsigned *p, xps_tiff_t *tiff, unsigned type, unsigned ofs, unsigned n) +{ + tiff->rp = tiff->bp + ofs; + if (tiff->rp > tiff->ep) + tiff->rp = tiff->bp; + + while (n--) + { + switch (type) + { + case TRATIONAL: + *p = readlong(tiff); + *p = *p / readlong(tiff); + p ++; + break; + case TBYTE: *p++ = readbyte(tiff); break; + case TSHORT: *p++ = readshort(tiff); break; + case TLONG: *p++ = readlong(tiff); break; + default: *p++ = 0; break; + } + } +} + +static int +xps_read_tiff_tag(xps_context_t *ctx, xps_tiff_t *tiff, unsigned offset) +{ + unsigned tag; + unsigned type; + unsigned count; + unsigned value; + + tiff->rp = tiff->bp + offset; + + tag = readshort(tiff); + type = readshort(tiff); + count = readlong(tiff); + + if ((type == TBYTE && count <= 4) || + (type == TSHORT && count <= 2) || + (type == TLONG && count <= 1)) + value = tiff->rp - tiff->bp; + else + value = readlong(tiff); + + switch (tag) + { + case NewSubfileType: + xps_read_tiff_tag_value(&tiff->subfiletype, tiff, type, value, 1); + break; + case ImageWidth: + xps_read_tiff_tag_value(&tiff->imagewidth, tiff, type, value, 1); + break; + case ImageLength: + xps_read_tiff_tag_value(&tiff->imagelength, tiff, type, value, 1); + break; + case BitsPerSample: + xps_read_tiff_tag_value(&tiff->bitspersample, tiff, type, value, 1); + break; + case Compression: + xps_read_tiff_tag_value(&tiff->compression, tiff, type, value, 1); + break; + case PhotometricInterpretation: + xps_read_tiff_tag_value(&tiff->photometric, tiff, type, value, 1); + break; + case FillOrder: + xps_read_tiff_tag_value(&tiff->fillorder, tiff, type, value, 1); + break; + case SamplesPerPixel: + xps_read_tiff_tag_value(&tiff->samplesperpixel, tiff, type, value, 1); + break; + case RowsPerStrip: + xps_read_tiff_tag_value(&tiff->rowsperstrip, tiff, type, value, 1); + break; + case XResolution: + xps_read_tiff_tag_value(&tiff->xresolution, tiff, type, value, 1); + break; + case YResolution: + xps_read_tiff_tag_value(&tiff->yresolution, tiff, type, value, 1); + break; + case PlanarConfiguration: + xps_read_tiff_tag_value(&tiff->planar, tiff, type, value, 1); + break; + case T4Options: + xps_read_tiff_tag_value(&tiff->g3opts, tiff, type, value, 1); + break; + case T6Options: + xps_read_tiff_tag_value(&tiff->g4opts, tiff, type, value, 1); + break; + case Predictor: + xps_read_tiff_tag_value(&tiff->predictor, tiff, type, value, 1); + break; + case ResolutionUnit: + xps_read_tiff_tag_value(&tiff->resolutionunit, tiff, type, value, 1); + break; + case YCbCrSubSampling: + xps_read_tiff_tag_value(tiff->ycbcrsubsamp, tiff, type, value, 2); + break; + case ExtraSamples: + xps_read_tiff_tag_value(&tiff->extrasamples, tiff, type, value, 1); + break; + case ICCProfile: + tiff->profile = xps_alloc(ctx, count); + if (!tiff->profile) + return gs_throw(-1, "could not allocate embedded icc profile"); + /* ICC profile data type is set to UNDEFINED. + * TBYTE reading not correct in xps_read_tiff_tag_value */ + xps_read_tiff_bytes(tiff->profile, tiff, value, count); + tiff->profilesize = count; + break; + + case JPEGTables: + tiff->jpegtables = tiff->bp + value; + tiff->jpegtableslen = count; + break; + + case StripOffsets: + tiff->stripoffsets = (unsigned*) xps_alloc(ctx, count * sizeof(unsigned)); + if (!tiff->stripoffsets) + return gs_throw(-1, "could not allocate strip offsets"); + xps_read_tiff_tag_value(tiff->stripoffsets, tiff, type, value, count); + break; + + case StripByteCounts: + tiff->stripbytecounts = (unsigned*) xps_alloc(ctx, count * sizeof(unsigned)); + if (!tiff->stripbytecounts) + return gs_throw(-1, "could not allocate strip byte counts"); + xps_read_tiff_tag_value(tiff->stripbytecounts, tiff, type, value, count); + break; + + case ColorMap: + tiff->colormap = (unsigned*) xps_alloc(ctx, count * sizeof(unsigned)); + if (!tiff->colormap) + return gs_throw(-1, "could not allocate color map"); + xps_read_tiff_tag_value(tiff->colormap, tiff, type, value, count); + break; + + case TileWidth: + case TileLength: + case TileOffsets: + case TileByteCounts: + return gs_throw(-1, "tiled tiffs not supported"); + + default: + /* printf("unknown tag: %d t=%d n=%d\n", tag, type, count); */ + break; + } + + return gs_okay; +} + +static void +xps_swap_byte_order(byte *buf, int n) +{ + int i, t; + for (i = 0; i < n; i++) + { + t = buf[i * 2 + 0]; + buf[i * 2 + 0] = buf[i * 2 + 1]; + buf[i * 2 + 1] = t; + } +} + +static int +xps_decode_tiff_header(xps_context_t *ctx, xps_tiff_t *tiff, byte *buf, int len) +{ + unsigned version; + unsigned offset; + unsigned count; + unsigned i; + int error; + + memset(tiff, 0, sizeof(xps_tiff_t)); + + tiff->bp = buf; + tiff->rp = buf; + tiff->ep = buf + len; + + /* tag defaults, where applicable */ + tiff->bitspersample = 1; + tiff->compression = 1; + tiff->samplesperpixel = 1; + tiff->resolutionunit = 2; + tiff->rowsperstrip = 0xFFFFFFFF; + tiff->fillorder = 1; + tiff->planar = 1; + tiff->subfiletype = 0; + tiff->predictor = 1; + tiff->ycbcrsubsamp[0] = 2; + tiff->ycbcrsubsamp[1] = 2; + + /* + * Read IFH + */ + + /* get byte order marker */ + tiff->order = TII; + tiff->order = readshort(tiff); + if (tiff->order != TII && tiff->order != TMM) + return gs_throw(-1, "not a TIFF file, wrong magic marker"); + + /* check version */ + version = readshort(tiff); + if (version != 42) + return gs_throw(-1, "not a TIFF file, wrong version marker"); + + /* get offset of IFD */ + offset = readlong(tiff); + + /* + * Read IFD + */ + + tiff->rp = tiff->bp + offset; + + count = readshort(tiff); + + offset += 2; + for (i = 0; i < count; i++) + { + error = xps_read_tiff_tag(ctx, tiff, offset); + if (error) + return gs_rethrow(error, "could not read TIFF header tag"); + offset += 12; + } + + return gs_okay; +} + +int +xps_decode_tiff(xps_context_t *ctx, byte *buf, int len, xps_image_t *image) +{ + int error; + xps_tiff_t tiffst; + xps_tiff_t *tiff = &tiffst; + + error = xps_decode_tiff_header(ctx, tiff, buf, len); + if (error) + return gs_rethrow(error, "cannot decode tiff header"); + + /* + * Decode the image strips + */ + + if (tiff->rowsperstrip > tiff->imagelength) + tiff->rowsperstrip = tiff->imagelength; + + error = xps_decode_tiff_strips(ctx, tiff, image); + if (error) + return gs_rethrow(error, "could not decode image data"); + + /* + * Byte swap 16-bit images to big endian if necessary. + */ + if (image->bits == 16) + { + if (tiff->order == TII) + xps_swap_byte_order(image->samples, image->width * image->height * image->comps); + } + + /* + * Save ICC profile data + */ + image->profile = tiff->profile; + image->profilesize = tiff->profilesize; + + /* + * Clean up scratch memory + */ + + if (tiff->colormap) xps_free(ctx, tiff->colormap); + if (tiff->stripoffsets) xps_free(ctx, tiff->stripoffsets); + if (tiff->stripbytecounts) xps_free(ctx, tiff->stripbytecounts); + + return gs_okay; +} + +int +xps_tiff_has_alpha(xps_context_t *ctx, byte *buf, int len) +{ + int error; + xps_tiff_t tiffst; + xps_tiff_t *tiff = &tiffst; + + error = xps_decode_tiff_header(ctx, tiff, buf, len); + if (error) + { + gs_catch(error, "cannot decode tiff header"); + return 0; + } + + if (tiff->profile) xps_free(ctx, tiff->profile); + if (tiff->colormap) xps_free(ctx, tiff->colormap); + if (tiff->stripoffsets) xps_free(ctx, tiff->stripoffsets); + if (tiff->stripbytecounts) xps_free(ctx, tiff->stripbytecounts); + + return tiff->extrasamples == 2 || tiff->extrasamples == 1; +} diff --git a/xps/xpstile.c b/xps/xpstile.c new file mode 100644 index 00000000..d7118b52 --- /dev/null +++ b/xps/xpstile.c @@ -0,0 +1,399 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - tiles for pattern rendering */ + +#include "ghostxps.h" + +/* + * Parse a tiling brush (visual and image brushes at this time) common + * properties. Use the callback to draw the individual tiles. + */ + +enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y }; + +struct tile_closure_s +{ + xps_context_t *ctx; + char *base_uri; + xps_resource_t *dict; + xps_item_t *tag; + gs_rect viewbox; + int tile_mode; + void *user; + int (*func)(xps_context_t*, char*, xps_resource_t*, xps_item_t*, void*); +}; + +static int +xps_paint_tiling_brush_clipped(struct tile_closure_s *c) +{ + xps_context_t *ctx = c->ctx; + int code; + + gs_moveto(ctx->pgs, c->viewbox.p.x, c->viewbox.p.y); + gs_lineto(ctx->pgs, c->viewbox.p.x, c->viewbox.q.y); + gs_lineto(ctx->pgs, c->viewbox.q.x, c->viewbox.q.y); + gs_lineto(ctx->pgs, c->viewbox.q.x, c->viewbox.p.y); + gs_closepath(ctx->pgs); + gs_clip(ctx->pgs); + gs_newpath(ctx->pgs); + + code = c->func(c->ctx, c->base_uri, c->dict, c->tag, c->user); + if (code < 0) + return gs_rethrow(code, "cannot draw clipped tile"); + + return 0; +} + +static int +xps_paint_tiling_brush(const gs_client_color *pcc, gs_state *pgs) +{ + const gs_client_pattern *ppat = gs_getpattern(pcc); + struct tile_closure_s *c = ppat->client_data; + xps_context_t *ctx = c->ctx; + gs_state *saved_pgs; + int code; + + saved_pgs = ctx->pgs; + ctx->pgs = pgs; + + gs_gsave(ctx->pgs); + code = xps_paint_tiling_brush_clipped(c); + if (code) + goto cleanup; + gs_grestore(ctx->pgs); + + if (c->tile_mode == TILE_FLIP_X || c->tile_mode == TILE_FLIP_X_Y) + { + gs_gsave(ctx->pgs); + gs_translate(ctx->pgs, c->viewbox.q.x * 2, 0.0); + gs_scale(ctx->pgs, -1.0, 1.0); + code = xps_paint_tiling_brush_clipped(c); + if (code) + goto cleanup; + gs_grestore(ctx->pgs); + } + + if (c->tile_mode == TILE_FLIP_Y || c->tile_mode == TILE_FLIP_X_Y) + { + gs_gsave(ctx->pgs); + gs_translate(ctx->pgs, 0.0, c->viewbox.q.y * 2); + gs_scale(ctx->pgs, 1.0, -1.0); + code = xps_paint_tiling_brush_clipped(c); + if (code) + goto cleanup; + gs_grestore(ctx->pgs); + } + + if (c->tile_mode == TILE_FLIP_X_Y) + { + gs_gsave(ctx->pgs); + gs_translate(ctx->pgs, c->viewbox.q.x * 2, c->viewbox.q.y * 2); + gs_scale(ctx->pgs, -1.0, -1.0); + code = xps_paint_tiling_brush_clipped(c); + if (code) + goto cleanup; + gs_grestore(ctx->pgs); + } + + ctx->pgs = saved_pgs; + + return 0; + +cleanup: + gs_grestore(ctx->pgs); + ctx->pgs = saved_pgs; + return gs_rethrow(code, "cannot draw tile"); +} + +int +xps_high_level_pattern(xps_context_t *ctx) +{ + gs_matrix m; + gs_rect bbox; + gs_fixed_rect clip_box; + int code; + gx_device_color *pdc = gs_currentdevicecolor_inline(ctx->pgs); + const gs_client_pattern *ppat = gs_getpattern(&pdc->ccolor); + gs_pattern1_instance_t *pinst = + (gs_pattern1_instance_t *)gs_currentcolor(ctx->pgs)->pattern; + + code = gx_pattern_cache_add_dummy_entry((gs_imager_state *)ctx->pgs, + pinst, ctx->pgs->device->color_info.depth); + if (code < 0) + return code; + + code = gs_gsave(ctx->pgs); + if (code < 0) + return code; + + dev_proc(ctx->pgs->device, get_initial_matrix)(ctx->pgs->device, &m); + gs_setmatrix(ctx->pgs, &m); + code = gs_bbox_transform(&ppat->BBox, &ctm_only(ctx->pgs), &bbox); + if (code < 0) { + gs_grestore(ctx->pgs); + return code; + } + clip_box.p.x = float2fixed(bbox.p.x); + clip_box.p.y = float2fixed(bbox.p.y); + clip_box.q.x = float2fixed(bbox.q.x); + clip_box.q.y = float2fixed(bbox.q.y); + code = gx_clip_to_rectangle(ctx->pgs, &clip_box); + if (code < 0) { + gs_grestore(ctx->pgs); + return code; + } + code = dev_proc(ctx->pgs->device, pattern_manage)(ctx->pgs->device, pinst->id, pinst, + pattern_manage__start_accum); + if (code < 0) { + gs_grestore(ctx->pgs); + return code; + } + + code = xps_paint_tiling_brush(&pdc->ccolor, ctx->pgs); + if (code) { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "high level pattern brush function failed"); + } + + code = gs_grestore(ctx->pgs); + if (code < 0) + return code; + + code = dev_proc(ctx->pgs->device, pattern_manage)(ctx->pgs->device, gx_no_bitmap_id, NULL, + pattern_manage__finish_accum); + + return code; +} + +static int +xps_remap_pattern(const gs_client_color *pcc, gs_state *pgs) +{ + const gs_client_pattern *ppat = gs_getpattern(pcc); + struct tile_closure_s *c = ppat->client_data; + xps_context_t *ctx = c->ctx; + int code; + + /* pgs->device is the newly created pattern accumulator, but we want to test the device + * that is 'behind' that, the actual output device, so we use the one from + * the saved XPS graphics state. + */ + code = dev_proc(ctx->pgs->device, pattern_manage)(ctx->pgs->device, ppat->uid.id, ppat, + pattern_manage__can_accum); + + if (code == 1) { + /* Device handles high-level patterns, so return 'remap'. + * This closes the internal accumulator device, as we no longer need + * it, and the error trickles back up to the PDL client. The client + * must then take action to start the device's accumulator, draw the + * pattern, close the device's accumulator and generate a cache entry. + */ + return gs_error_Remap_Color; + } else { + code = xps_paint_tiling_brush(pcc, pgs); + if (code) + return gs_rethrow(code, "remap pattern brush function failed"); + return 0; + } +} + +int +xps_parse_tiling_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root, + int (*func)(xps_context_t*, char*, xps_resource_t*, xps_item_t*, void*), void *user) +{ + xps_item_t *node; + int code; + + char *opacity_att; + char *transform_att; + char *viewbox_att; + char *viewport_att; + char *tile_mode_att; + char *viewbox_units_att; + char *viewport_units_att; + + xps_item_t *transform_tag = NULL; + + gs_matrix transform; + gs_rect viewbox; + gs_rect viewport; + float scalex, scaley; + int tile_mode; + + opacity_att = xps_att(root, "Opacity"); + transform_att = xps_att(root, "Transform"); + viewbox_att = xps_att(root, "Viewbox"); + viewport_att = xps_att(root, "Viewport"); + tile_mode_att = xps_att(root, "TileMode"); + viewbox_units_att = xps_att(root, "ViewboxUnits"); + viewport_units_att = xps_att(root, "ViewportUnits"); + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "ImageBrush.Transform")) + transform_tag = xps_down(node); + if (!strcmp(xps_tag(node), "VisualBrush.Transform")) + transform_tag = xps_down(node); + } + + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + + gs_make_identity(&transform); + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + + viewbox.p.x = 0.0; viewbox.p.y = 0.0; + viewbox.q.x = 1.0; viewbox.q.y = 1.0; + if (viewbox_att) + xps_parse_rectangle(ctx, viewbox_att, &viewbox); + + viewport.p.x = 0.0; viewport.p.y = 0.0; + viewport.q.x = 1.0; viewport.q.y = 1.0; + if (viewport_att) + xps_parse_rectangle(ctx, viewport_att, &viewport); + + /* some sanity checks on the viewport/viewbox size */ + if (fabs(viewport.q.x - viewport.p.x) < 0.01) return 0; + if (fabs(viewport.q.y - viewport.p.y) < 0.01) return 0; + if (fabs(viewbox.q.x - viewbox.p.x) < 0.01) return 0; + if (fabs(viewbox.q.y - viewbox.p.y) < 0.01) return 0; + + scalex = (viewport.q.x - viewport.p.x) / (viewbox.q.x - viewbox.p.x); + scaley = (viewport.q.y - viewport.p.y) / (viewbox.q.y - viewbox.p.y); + + tile_mode = TILE_NONE; + if (tile_mode_att) + { + if (!strcmp(tile_mode_att, "None")) + tile_mode = TILE_NONE; + if (!strcmp(tile_mode_att, "Tile")) + tile_mode = TILE_TILE; + if (!strcmp(tile_mode_att, "FlipX")) + tile_mode = TILE_FLIP_X; + if (!strcmp(tile_mode_att, "FlipY")) + tile_mode = TILE_FLIP_Y; + if (!strcmp(tile_mode_att, "FlipXY")) + tile_mode = TILE_FLIP_X_Y; + } + + gs_gsave(ctx->pgs); + + code = xps_begin_opacity(ctx, base_uri, dict, opacity_att, NULL); + if (code) + { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot create transparency group"); + } + + /* TODO(tor): check viewport and tiling to see if we can set it to TILE_NONE */ + + if (tile_mode != TILE_NONE) + { + struct tile_closure_s closure; + + gs_client_pattern gspat; + gs_client_color gscolor; + gs_color_space *cs; + + closure.ctx = ctx; + closure.base_uri = base_uri; + closure.dict = dict; + closure.tag = root; + closure.tile_mode = tile_mode; + closure.user = user; + closure.func = func; + + closure.viewbox.p.x = viewbox.p.x; + closure.viewbox.p.y = viewbox.p.y; + closure.viewbox.q.x = viewbox.q.x; + closure.viewbox.q.y = viewbox.q.y; + + gs_pattern1_init(&gspat); + uid_set_UniqueID(&gspat.uid, gs_next_ids(ctx->memory, 1)); + gspat.PaintType = 1; + gspat.TilingType = 1; + gspat.PaintProc = xps_remap_pattern; + gspat.client_data = &closure; + + gspat.XStep = viewbox.q.x - viewbox.p.x; + gspat.YStep = viewbox.q.y - viewbox.p.y; + gspat.BBox.p.x = viewbox.p.x; + gspat.BBox.p.y = viewbox.p.y; + gspat.BBox.q.x = viewbox.q.x; + gspat.BBox.q.y = viewbox.q.y; + + if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y) + { + gspat.BBox.q.x += gspat.XStep; + gspat.XStep *= 2; + } + + if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y) + { + gspat.BBox.q.y += gspat.YStep; + gspat.YStep *= 2; + } + + gs_matrix_translate(&transform, viewport.p.x, viewport.p.y, &transform); + gs_matrix_scale(&transform, scalex, scaley, &transform); + gs_matrix_translate(&transform, -viewbox.p.x, -viewbox.p.y, &transform); + + cs = ctx->srgb; + gs_setcolorspace(ctx->pgs, cs); + gs_makepattern(&gscolor, &gspat, &transform, ctx->pgs, NULL); + gs_setpattern(ctx->pgs, &gscolor); + + xps_fill(ctx); + + /* gs_makepattern increments the pattern count stored in the color + * structure. We will discard the color struct (its on the stack) + * so we need to decrement the reference before we throw away + * the structure. + */ + gs_pattern_reference(&gscolor, -1); + } + else + { + xps_clip(ctx); + + gs_concat(ctx->pgs, &transform); + + gs_translate(ctx->pgs, viewport.p.x, viewport.p.y); + gs_scale(ctx->pgs, scalex, scaley); + gs_translate(ctx->pgs, -viewbox.p.x, -viewbox.p.y); + + gs_moveto(ctx->pgs, viewbox.p.x, viewbox.p.y); + gs_lineto(ctx->pgs, viewbox.p.x, viewbox.q.y); + gs_lineto(ctx->pgs, viewbox.q.x, viewbox.q.y); + gs_lineto(ctx->pgs, viewbox.q.x, viewbox.p.y); + gs_closepath(ctx->pgs); + gs_clip(ctx->pgs); + gs_newpath(ctx->pgs); + + code = func(ctx, base_uri, dict, root, user); + if (code < 0) + { + xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot draw tile"); + } + } + + xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); + + gs_grestore(ctx->pgs); + + return 0; +} diff --git a/xps/xpstop.c b/xps/xpstop.c new file mode 100644 index 00000000..13149da6 --- /dev/null +++ b/xps/xpstop.c @@ -0,0 +1,576 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* Top-level API implementation of XML Paper Specification */ + +/* Language wrapper implementation (see pltop.h) */ + +#include "ghostxps.h" + +#include "pltop.h" +#include "plparse.h" /* for e_ExitLanguage */ + +#include "gxdevice.h" /* so we can include gxht.h below */ +#include "gxht.h" /* gsht1.h is incomplete, we need storage size of gs_halftone */ +#include "gsht1.h" + +int xps_zip_trace = 0; +int xps_doc_trace = 0; + +static int xps_install_halftone(xps_context_t *ctx, gx_device *pdevice); + +#define XPS_PARSER_MIN_INPUT_SIZE (8192 * 4) + +/* + * The XPS interpeter is identical to pl_interp_t. + * The XPS interpreter instance is derived from pl_interp_instance_t. + */ + +typedef struct xps_interp_instance_s xps_interp_instance_t; + +struct xps_interp_instance_s +{ + pl_interp_instance_t pl; /* common part: must be first */ + + pl_page_action_t pre_page_action; /* action before page out */ + void *pre_page_closure; /* closure to call pre_page_action with */ + pl_page_action_t post_page_action; /* action before page out */ + void *post_page_closure; /* closure to call post_page_action with */ + + xps_context_t *ctx; + FILE *scratch_file; + char scratch_name[gp_file_name_sizeof]; +}; + +/* version and build date are not currently used */ +#define XPS_VERSION NULL +#define XPS_BUILD_DATE NULL + +static const pl_interp_characteristics_t * +xps_imp_characteristics(const pl_interp_implementation_t *pimpl) +{ + static pl_interp_characteristics_t xps_characteristics = + { + "XPS", + "PK", /* string to recognize XPS files */ + "Artifex", + XPS_VERSION, + XPS_BUILD_DATE, + XPS_PARSER_MIN_INPUT_SIZE, /* Minimum input size */ + }; + return &xps_characteristics; +} + +static int +xps_imp_allocate_interp(pl_interp_t **ppinterp, + const pl_interp_implementation_t *pimpl, + gs_memory_t *pmem) +{ + static pl_interp_t interp; /* there's only one interpreter */ + *ppinterp = &interp; + return 0; +} + +/* Do per-instance interpreter allocation/init. No device is set yet */ +static int +xps_imp_allocate_interp_instance(pl_interp_instance_t **ppinstance, + pl_interp_t *pinterp, + gs_memory_t *pmem) +{ + xps_interp_instance_t *instance; + xps_context_t *ctx; + gs_state *pgs; + int code; + + instance = (xps_interp_instance_t *) gs_alloc_bytes(pmem, + sizeof(xps_interp_instance_t), "xps_imp_allocate_interp_instance"); + + ctx = (xps_context_t *) gs_alloc_bytes(pmem, + sizeof(xps_context_t), "xps_imp_allocate_interp_instance"); + + pgs = gs_state_alloc(pmem); +#ifdef ICCBRANCH + gsicc_init_iccmanager(pgs); +#endif + memset(ctx, 0, sizeof(xps_context_t)); + + if (!instance || !ctx || !pgs) + { + if (instance) + gs_free_object(pmem, instance, "xps_imp_allocate_interp_instance"); + if (ctx) + gs_free_object(pmem, ctx, "xps_imp_allocate_interp_instance"); + if (pgs) + gs_state_free(pgs); + return gs_error_VMerror; + } + + ctx->instance = instance; + ctx->memory = pmem; + ctx->pgs = pgs; + /* Declare PDL client support for high level patterns, for the benefit + * of pdfwrite and other high-level devices + */ + ctx->pgs->have_pattern_streams = true; + ctx->fontdir = NULL; + ctx->file = NULL; + ctx->zip_count = 0; + ctx->zip_table = NULL; + + /* Gray, RGB and CMYK profiles set when color spaces installed in graphics lib */ + ctx->gray = gs_cspace_new_DeviceGray(ctx->memory); + ctx->cmyk = gs_cspace_new_DeviceCMYK(ctx->memory); + ctx->srgb = gs_cspace_new_DeviceRGB(ctx->memory); + ctx->scrgb = gs_cspace_new_DeviceRGB(ctx->memory); /* This needs a different profile */ + + instance->pre_page_action = 0; + instance->pre_page_closure = 0; + instance->post_page_action = 0; + instance->post_page_closure = 0; + + instance->ctx = ctx; + instance->scratch_file = NULL; + instance->scratch_name[0] = 0; + + ctx->fontdir = gs_font_dir_alloc(ctx->memory); + gs_setaligntopixels(ctx->fontdir, 1); /* no subpixels */ + gs_setgridfittt(ctx->fontdir, 1); /* see gx_ttf_outline in gxttfn.c for values */ + + *ppinstance = (pl_interp_instance_t *)instance; + + return 0; +} + +/* Set a client language into an interperter instance */ +static int +xps_imp_set_client_instance(pl_interp_instance_t *pinstance, + pl_interp_instance_t *pclient, + pl_interp_instance_clients_t which_client) +{ + /* ignore */ + return 0; +} + +static int +xps_imp_set_pre_page_action(pl_interp_instance_t *pinstance, + pl_page_action_t action, void *closure) +{ + xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; + instance->pre_page_action = action; + instance->pre_page_closure = closure; + return 0; +} + +static int +xps_imp_set_post_page_action(pl_interp_instance_t *pinstance, + pl_page_action_t action, void *closure) +{ + xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; + instance->post_page_action = action; + instance->post_page_closure = closure; + return 0; +} + +static int +xps_imp_set_device(pl_interp_instance_t *pinstance, gx_device *pdevice) +{ + xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; + xps_context_t *ctx = instance->ctx; + int code; + + gs_opendevice(pdevice); + +#ifdef ICCBRANCH + code = gsicc_init_device_profile(ctx->pgs, pdevice); + if (code < 0) + return code; +#endif + + code = gs_setdevice_no_erase(ctx->pgs, pdevice); + if (code < 0) + goto cleanup_setdevice; + + gs_setaccuratecurves(ctx->pgs, true); /* NB not sure */ + gs_setfilladjust(ctx->pgs, 0, 0); + + /* gsave and grestore (among other places) assume that */ + /* there are at least 2 gstates on the graphics stack. */ + /* Ensure that now. */ + code = gs_gsave(ctx->pgs); + if (code < 0) + goto cleanup_gsave; + + code = gs_erasepage(ctx->pgs); + if (code < 0) + goto cleanup_erase; + + code = xps_install_halftone(ctx, pdevice); + if (code < 0) + goto cleanup_halftone; + + return 0; + +cleanup_halftone: +cleanup_erase: + /* undo gsave */ + gs_grestore_only(ctx->pgs); /* destroys gs_save stack */ + +cleanup_gsave: + /* undo setdevice */ + gs_nulldevice(ctx->pgs); + +cleanup_setdevice: + /* nothing to undo */ + return code; +} + +static int +xps_imp_get_device_memory(pl_interp_instance_t *pinstance, gs_memory_t **ppmem) +{ + /* huh? we do nothing here */ + return 0; +} + +/* Parse an entire random access file */ +static int +xps_imp_process_file(pl_interp_instance_t *pinstance, char *filename) +{ + xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; + xps_context_t *ctx = instance->ctx; + int code; + + code = xps_process_file(ctx, filename); + if (code) + gs_catch1(code, "cannot process xps file '%s'", filename); + + return code; +} + +/* Parse a cursor-full of data */ +static int +xps_imp_process(pl_interp_instance_t *pinstance, stream_cursor_read *cursor) +{ + xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; + xps_context_t *ctx = instance->ctx; + int avail, n; + + if (!instance->scratch_file) + { + instance->scratch_file = gp_open_scratch_file(ctx->memory, + "ghostxps-scratch-", instance->scratch_name, "wb"); + if (!instance->scratch_file) + { + gs_catch(gs_error_invalidfileaccess, "cannot open scratch file"); + return e_ExitLanguage; + } + if_debug1('|', "xps: open scratch file '%s'\n", instance->scratch_name); + } + + avail = cursor->limit - cursor->ptr; + n = fwrite(cursor->ptr + 1, 1, avail, instance->scratch_file); + if (n != avail) + { + gs_catch(gs_error_invalidfileaccess, "cannot write to scratch file"); + return e_ExitLanguage; + } + cursor->ptr = cursor->limit; + + return 0; +} + +/* Skip to end of job. + * Return 1 if done, 0 ok but EOJ not found, else negative error code. + */ +static int +xps_imp_flush_to_eoj(pl_interp_instance_t *pinstance, stream_cursor_read *pcursor) +{ + /* assume XPS cannot be pjl embedded */ + pcursor->ptr = pcursor->limit; + return 0; +} + +/* Parser action for end-of-file */ +static int +xps_imp_process_eof(pl_interp_instance_t *pinstance) +{ + xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; + xps_context_t *ctx = instance->ctx; + int code; + + if (instance->scratch_file) + { + if_debug0('|', "xps: executing scratch file\n"); + fclose(instance->scratch_file); + instance->scratch_file = NULL; + code = xps_process_file(ctx, instance->scratch_name); + unlink(instance->scratch_name); + if (code < 0) + { + gs_catch(code, "cannot process XPS file"); + return e_ExitLanguage; + } + } + + return 0; +} + +/* Report any errors after running a job */ +static int +xps_imp_report_errors(pl_interp_instance_t *pinstance, + int code, /* prev termination status */ + long file_position, /* file position of error, -1 if unknown */ + bool force_to_cout /* force errors to cout */ + ) +{ + return 0; +} + +/* Prepare interp instance for the next "job" */ +static int +xps_imp_init_job(pl_interp_instance_t *pinstance) +{ + xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; + xps_context_t *ctx = instance->ctx; + + if (gs_debug_c('|')) + xps_zip_trace = 1; + if (gs_debug_c('|')) + xps_doc_trace = 1; + + ctx->font_table = xps_hash_new(ctx); + ctx->colorspace_table = xps_hash_new(ctx); + + ctx->start_part = NULL; + + ctx->use_transparency = 1; + if (getenv("XPS_DISABLE_TRANSPARENCY")) + ctx->use_transparency = 0; + + ctx->opacity_only = 0; + ctx->fill_rule = 0; + + return 0; +} + +static void xps_free_key_func(xps_context_t *ctx, void *ptr) +{ + xps_free(ctx, ptr); +} + +static void xps_free_font_func(xps_context_t *ctx, void *ptr) +{ + xps_free_font(ctx, ptr); +} + +/* Wrap up interp instance after a "job" */ +static int +xps_imp_dnit_job(pl_interp_instance_t *pinstance) +{ + xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; + xps_context_t *ctx = instance->ctx; + int i; + + if (gs_debug_c('|')) + xps_debug_fixdocseq(ctx); + + for (i = 0; i < ctx->zip_count; i++) + xps_free(ctx, ctx->zip_table[i].name); + xps_free(ctx, ctx->zip_table); + + /* TODO: free resources too */ + xps_hash_free(ctx, ctx->font_table, xps_free_key_func, xps_free_font_func); + xps_hash_free(ctx, ctx->colorspace_table, xps_free_key_func, NULL); + + xps_free_fixed_pages(ctx); + xps_free_fixed_documents(ctx); + + return 0; +} + +/* Remove a device from an interperter instance */ +static int +xps_imp_remove_device(pl_interp_instance_t *pinstance) +{ + xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; + xps_context_t *ctx = instance->ctx; + + int code = 0; /* first error status encountered */ + int error; + + /* return to original gstate */ + gs_grestore_only(ctx->pgs); /* destroys gs_save stack */ + + /* Deselect device */ + /* NB */ + error = gs_nulldevice(ctx->pgs); + if (code >= 0) + code = error; + + return code; +} + +/* Deallocate a interpreter instance */ +static int +xps_imp_deallocate_interp_instance(pl_interp_instance_t *pinstance) +{ + xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; + xps_context_t *ctx = instance->ctx; + gs_memory_t *mem = ctx->memory; + + /* language clients don't free the font cache machinery */ + + // free gstate? + gs_free_object(mem, ctx, "xps_imp_deallocate_interp_instance"); + gs_free_object(mem, instance, "xps_imp_deallocate_interp_instance"); + + return 0; +} + +/* Do static deinit of XPS interpreter */ +static int +xps_imp_deallocate_interp(pl_interp_t *pinterp) +{ + /* nothing to do */ + return 0; +} + +/* Parser implementation descriptor */ +const pl_interp_implementation_t xps_implementation = +{ + xps_imp_characteristics, + xps_imp_allocate_interp, + xps_imp_allocate_interp_instance, + xps_imp_set_client_instance, + xps_imp_set_pre_page_action, + xps_imp_set_post_page_action, + xps_imp_set_device, + xps_imp_init_job, + xps_imp_process_file, + xps_imp_process, + xps_imp_flush_to_eoj, + xps_imp_process_eof, + xps_imp_report_errors, + xps_imp_dnit_job, + xps_imp_remove_device, + xps_imp_deallocate_interp_instance, + xps_imp_deallocate_interp, + xps_imp_get_device_memory, +}; + +/* + * End-of-page function called by XPS parser. + */ +int +xps_show_page(xps_context_t *ctx, int num_copies, int flush) +{ + pl_interp_instance_t *pinstance = ctx->instance; + xps_interp_instance_t *instance = ctx->instance; + + int code = 0; + + /* do pre-page action */ + if (instance->pre_page_action) + { + code = instance->pre_page_action(pinstance, instance->pre_page_closure); + if (code < 0) + return code; + if (code != 0) + return 0; /* code > 0 means abort w/no error */ + } + + /* output the page */ + code = gs_output_page(ctx->pgs, num_copies, flush); + if (code < 0) + return code; + + /* do post-page action */ + if (instance->post_page_action) + { + code = instance->post_page_action(pinstance, instance->post_page_closure); + if (code < 0) + return code; + } + + return 0; +} + +/* + * We need to install a halftone ourselves, this is not + * done automatically. + */ + +static float +identity_transfer(floatp tint, const gx_transfer_map *ignore_map) +{ + return tint; +} + +/* The following is a 45 degree spot screen with the spots enumerated + * in a defined order. */ +static byte order16x16[256] = { + 38, 11, 14, 32, 165, 105, 90, 171, 38, 12, 14, 33, 161, 101, 88, 167, + 30, 6, 0, 16, 61, 225, 231, 125, 30, 6, 1, 17, 63, 222, 227, 122, + 27, 3, 8, 19, 71, 242, 205, 110, 28, 4, 9, 20, 74, 246, 208, 106, + 35, 24, 22, 40, 182, 46, 56, 144, 36, 25, 22, 41, 186, 48, 58, 148, + 152, 91, 81, 174, 39, 12, 15, 34, 156, 95, 84, 178, 40, 13, 16, 34, + 69, 212, 235, 129, 31, 7, 2, 18, 66, 216, 239, 133, 32, 8, 2, 18, + 79, 254, 203, 114, 28, 4, 10, 20, 76, 250, 199, 118, 29, 5, 10, 21, + 193, 44, 54, 142, 36, 26, 23, 42, 189, 43, 52, 139, 37, 26, 24, 42, + 39, 12, 15, 33, 159, 99, 87, 169, 38, 11, 14, 33, 163, 103, 89, 172, + 31, 7, 1, 17, 65, 220, 229, 123, 30, 6, 1, 17, 62, 223, 233, 127, + 28, 4, 9, 20, 75, 248, 210, 108, 27, 3, 9, 19, 72, 244, 206, 112, + 36, 25, 23, 41, 188, 49, 60, 150, 35, 25, 22, 41, 184, 47, 57, 146, + 157, 97, 85, 180, 40, 13, 16, 35, 154, 93, 83, 176, 39, 13, 15, 34, + 67, 218, 240, 135, 32, 8, 3, 19, 70, 214, 237, 131, 31, 7, 2, 18, + 78, 252, 197, 120, 29, 5, 11, 21, 80, 255, 201, 116, 29, 5, 10, 21, + 191, 43, 51, 137, 37, 27, 24, 43, 195, 44, 53, 140, 37, 26, 23, 42 +}; + +#define source_phase_x 4 +#define source_phase_y 0 + +static int +xps_install_halftone(xps_context_t *ctx, gx_device *pdevice) +{ + gs_halftone ht; + gs_string thresh; + int code; + + int width = 16; + int height = 16; + thresh.data = order16x16; + thresh.size = width * height; + + if (gx_device_must_halftone(pdevice)) + { + ht.type = ht_type_threshold; + ht.params.threshold.width = width; + ht.params.threshold.height = height; + ht.params.threshold.thresholds.data = thresh.data; + ht.params.threshold.thresholds.size = thresh.size; + ht.params.threshold.transfer = 0; + ht.params.threshold.transfer_closure.proc = 0; + + gs_settransfer(ctx->pgs, identity_transfer); + + code = gs_sethalftone(ctx->pgs, &ht); + if (code < 0) + return gs_throw(code, "could not install halftone"); + + code = gs_sethalftonephase(ctx->pgs, 0, 0); + if (code < 0) + return gs_throw(code, "could not set halftone phase"); + } + + return 0; +} diff --git a/xps/xpsutf.c b/xps/xpsutf.c new file mode 100644 index 00000000..3e1c05c9 --- /dev/null +++ b/xps/xpsutf.c @@ -0,0 +1,69 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - unicode text functions */ + +#include "ghostxps.h" + +/* + * http://tools.ietf.org/html/rfc3629 + */ + +int +xps_utf8_to_ucs(int *p, const char *ss, int n) +{ + unsigned char *s = (unsigned char *)ss; + + if (s == NULL) + goto bad; + + if ((s[0] & 0x80) == 0) + { + *p = (s[0] & 0x7f); + return 1; + } + + if ((s[0] & 0xe0) == 0xc0) + { + if (n < 2 || s[1] < 0x80) + goto bad; + *p = (s[0] & 0x1f) << 6; + *p |= (s[1] & 0x3f); + return 2; + } + + if ((s[0] & 0xf0) == 0xe0) + { + if (n < 3 || s[1] < 0x80 || s[2] < 0x80) + goto bad; + *p = (s[0] & 0x0f) << 12; + *p |= (s[1] & 0x3f) << 6; + *p |= (s[2] & 0x3f); + return 3; + } + + if ((s[0] & 0xf8) == 0xf0) + { + if (n < 4 || s[1] < 0x80 || s[2] < 0x80 || s[3] < 0x80) + goto bad; + *p = (s[0] & 0x07) << 18; + *p |= (s[1] & 0x3f) << 12; + *p |= (s[2] & 0x3f) << 6; + *p |= (s[3] & 0x3f); + return 4; + } + +bad: + *p = 0x80; + return 1; +} diff --git a/xps/xpsvisual.c b/xps/xpsvisual.c new file mode 100644 index 00000000..f23ef991 --- /dev/null +++ b/xps/xpsvisual.c @@ -0,0 +1,62 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - visual brush functions */ + +#include "ghostxps.h" + +enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y }; + +struct userdata +{ + xps_context_t *ctx; + xps_resource_t *dict; + xps_item_t *visual_tag; +}; + +static int +xps_paint_visual_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root, void *visual_tag) +{ + return xps_parse_element(ctx, base_uri, dict, (xps_item_t *)visual_tag); +} + +int +xps_parse_visual_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root) +{ + xps_item_t *node; + int code; + + char *visual_uri; + char *visual_att; + xps_item_t *visual_tag = NULL; + + visual_att = xps_att(root, "Visual"); + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "VisualBrush.Visual")) + visual_tag = xps_down(node); + } + + visual_uri = base_uri; + xps_resolve_resource_reference(ctx, dict, &visual_att, &visual_tag, &visual_uri); + + if (visual_tag) + { + code = xps_parse_tiling_brush(ctx, visual_uri, dict, root, xps_paint_visual_brush, visual_tag); + if (code) + return gs_rethrow(code, "cannot parse tiling brush"); + } + + return 0; +} diff --git a/xps/xpsxml.c b/xps/xpsxml.c new file mode 100644 index 00000000..ce93bcfe --- /dev/null +++ b/xps/xpsxml.c @@ -0,0 +1,353 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* Simple XML document object model on top of Expat. */ + +#include "ghostxps.h" + +#include + +#define XMLBUFLEN 4096 + +#define NS_XPS "http://schemas.microsoft.com/xps/2005/06" +#define NS_MC "http://schemas.openxmlformats.org/markup-compatibility/2006" + +typedef struct xps_parser_s xps_parser_t; + +struct xps_parser_s +{ + xps_context_t *ctx; + xps_item_t *root; + xps_item_t *head; + char *error; + int compat; + char *base; /* base of relative URIs */ +}; + +struct xps_item_s +{ + char *name; + char **atts; + xps_item_t *up; + xps_item_t *down; + xps_item_t *next; +}; + +static char * +skip_namespace(char *s) +{ + char *p = strchr(s, ' '); + if (p) + return p + 1; + return s; +} + +static void +on_open_tag(void *zp, char *ns_name, char **atts) +{ + xps_parser_t *parser = zp; + xps_context_t *ctx = parser->ctx; + xps_item_t *item; + xps_item_t *tail; + int namelen; + int attslen; + int textlen; + char *name, *p; + int i; + + if (parser->error) + return; + + /* check namespace */ + + name = NULL; + + p = strstr(ns_name, NS_XPS); + if (p == ns_name) + { + name = strchr(ns_name, ' ') + 1; + } + + p = strstr(ns_name, NS_MC); + if (p == ns_name) + { + name = strchr(ns_name, ' ') + 1; + parser->compat = 1; + } + + if (!name) + { + dprintf1("unknown namespace: %s\n", ns_name); + name = ns_name; + } + + /* count size to alloc */ + + namelen = strlen(name) + 1; /* zero terminated */ + attslen = sizeof(char*); /* with space for sentinel */ + textlen = 0; + for (i = 0; atts[i]; i++) + { + attslen += sizeof(char*); + if ((i & 1) == 0) + textlen += strlen(skip_namespace(atts[i])) + 1; + else + textlen += strlen(atts[i]) + 1; + } + + item = xps_alloc(ctx, sizeof(xps_item_t) + attslen + namelen + textlen); + if (!item) + { + parser->error = "out of memory"; + } + + /* copy strings to new memory */ + + item->atts = (char**) (((char*)item) + sizeof(xps_item_t)); + item->name = ((char*)item) + sizeof(xps_item_t) + attslen; + p = ((char*)item) + sizeof(xps_item_t) + attslen + namelen; + + strcpy(item->name, name); + for (i = 0; atts[i]; i++) + { + item->atts[i] = p; + if ((i & 1) == 0) + strcpy(item->atts[i], skip_namespace(atts[i])); + else + strcpy(item->atts[i], atts[i]); + p += strlen(p) + 1; + } + + item->atts[i] = 0; + + /* link item into tree */ + + item->up = parser->head; + item->down = NULL; + item->next = NULL; + + if (!parser->head) + { + parser->root = item; + parser->head = item; + return; + } + + if (!parser->head->down) + { + parser->head->down = item; + parser->head = item; + return; + } + + tail = parser->head->down; + while (tail->next) + tail = tail->next; + tail->next = item; + parser->head = item; +} + +static void +on_close_tag(void *zp, char *name) +{ + xps_parser_t *parser = zp; + + if (parser->error) + return; + + if (parser->head) + parser->head = parser->head->up; +} + +static inline int +is_xml_space(int c) +{ + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; +} + +static void +on_text(void *zp, char *buf, int len) +{ + xps_parser_t *parser = zp; + xps_context_t *ctx = parser->ctx; + char *atts[3]; + int i; + + if (parser->error) + return; + + for (i = 0; i < len; i++) + { + if (!is_xml_space(buf[i])) + { + char *tmp = xps_alloc(ctx, len + 1); + if (!tmp) + { + parser->error = "out of memory"; + return; + } + + atts[0] = ""; + atts[1] = tmp; + atts[2] = NULL; + + memcpy(tmp, buf, len); + tmp[len] = 0; + on_open_tag(zp, "", atts); + on_close_tag(zp, ""); + xps_free(ctx, tmp); + return; + } + } +} + +static xps_item_t * +xps_process_compatibility(xps_context_t *ctx, xps_item_t *root) +{ + gs_warn("XPS document uses markup compatibility tags"); + return root; +} + +xps_item_t * +xps_parse_xml(xps_context_t *ctx, byte *buf, int len) +{ + xps_parser_t parser; + XML_Parser xp; + int code; + + parser.ctx = ctx; + parser.root = NULL; + parser.head = NULL; + parser.error = NULL; + parser.compat = 0; + + xp = XML_ParserCreateNS(NULL, ' '); + if (!xp) + { + gs_throw(-1, "xml error: could not create expat parser"); + return NULL; + } + + XML_SetUserData(xp, &parser); + XML_SetParamEntityParsing(xp, XML_PARAM_ENTITY_PARSING_NEVER); + XML_SetStartElementHandler(xp, (XML_StartElementHandler)on_open_tag); + XML_SetEndElementHandler(xp, (XML_EndElementHandler)on_close_tag); + XML_SetCharacterDataHandler(xp, (XML_CharacterDataHandler)on_text); + + code = XML_Parse(xp, (char*)buf, len, 1); + if (code == 0) + { + if (parser.root) + xps_free_item(ctx, parser.root); + XML_ParserFree(xp); + gs_throw1(-1, "xml error: %s", XML_ErrorString(XML_GetErrorCode(xp))); + return NULL; + } + + XML_ParserFree(xp); + + if (parser.compat) + xps_process_compatibility(ctx, parser.root); + + return parser.root; +} + +xps_item_t * +xps_next(xps_item_t *item) +{ + return item->next; +} + +xps_item_t * +xps_down(xps_item_t *item) +{ + return item->down; +} + +char * +xps_tag(xps_item_t *item) +{ + return item->name; +} + +char * +xps_att(xps_item_t *item, const char *att) +{ + int i; + for (i = 0; item->atts[i]; i += 2) + if (!strcmp(item->atts[i], att)) + return item->atts[i + 1]; + return NULL; +} + +void +xps_free_item(xps_context_t *ctx, xps_item_t *item) +{ + xps_item_t *next; + while (item) + { + next = item->next; + if (item->down) + xps_free_item(ctx, item->down); + xps_free(ctx, item); + item = next; + } +} + +static void indent(int n) +{ + while (n--) + printf(" "); +} + +static void +xps_debug_item_imp(xps_item_t *item, int level, int loop) +{ + int i; + + while (item) + { + indent(level); + + if (strlen(item->name) == 0) + printf("%s\n", item->atts[1]); + else + { + printf("<%s", item->name); + + for (i = 0; item->atts[i]; i += 2) + printf(" %s=\"%s\"", item->atts[i], item->atts[i+1]); + + if (item->down) + { + printf(">\n"); + xps_debug_item_imp(item->down, level + 1, 1); + indent(level); + printf("\n", item->name); + } + else + printf(" />\n"); + } + + item = item->next; + + if (!loop) + return; + } +} + +void +xps_debug_item(xps_item_t *item, int level) +{ + xps_debug_item_imp(item, level, 0); +} diff --git a/xps/xpszip.c b/xps/xpszip.c new file mode 100644 index 00000000..1aac7349 --- /dev/null +++ b/xps/xpszip.c @@ -0,0 +1,568 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - zip container parsing */ + +#include "ghostxps.h" + +static int isfile(char *path) +{ + FILE *file = fopen(path, "rb"); + if (file) + { + fclose(file); + return 1; + } + return 0; +} + +static inline int getshort(FILE *file) +{ + int a = getc(file); + int b = getc(file); + return a | (b << 8); +} + +static inline int getlong(FILE *file) +{ + int a = getc(file); + int b = getc(file); + int c = getc(file); + int d = getc(file); + return a | (b << 8) | (c << 16) | (d << 24); +} + +static void * +xps_zip_alloc_items(xps_context_t *ctx, int items, int size) +{ + return xps_alloc(ctx, items * size); +} + +static void +xps_zip_free(xps_context_t *ctx, void *ptr) +{ + xps_free(ctx, ptr); +} + +static int +xps_compare_entries(const void *a0, const void *b0) +{ + xps_entry_t *a = (xps_entry_t*) a0; + xps_entry_t *b = (xps_entry_t*) b0; + return xps_strcasecmp(a->name, b->name); +} + +static xps_entry_t * +xps_find_zip_entry(xps_context_t *ctx, char *name) +{ + int l = 0; + int r = ctx->zip_count - 1; + while (l <= r) + { + int m = (l + r) >> 1; + int c = xps_strcasecmp(name, ctx->zip_table[m].name); + if (c < 0) + r = m - 1; + else if (c > 0) + l = m + 1; + else + return &ctx->zip_table[m]; + } + return NULL; +} + +/* + * Inflate the data in a zip entry. + */ + +static int +xps_read_zip_entry(xps_context_t *ctx, xps_entry_t *ent, unsigned char *outbuf) +{ + z_stream stream; + unsigned char *inbuf; + int sig; + int version, general, method; + int namelength, extralength; + int code; + + if_debug1('|', "zip: inflating entry '%s'\n", ent->name); + + fseek(ctx->file, ent->offset, 0); + + sig = getlong(ctx->file); + if (sig != ZIP_LOCAL_FILE_SIG) + return gs_throw1(-1, "wrong zip local file signature (0x%x)", sig); + + version = getshort(ctx->file); + general = getshort(ctx->file); + method = getshort(ctx->file); + (void) getshort(ctx->file); /* file time */ + (void) getshort(ctx->file); /* file date */ + (void) getlong(ctx->file); /* crc-32 */ + (void) getlong(ctx->file); /* csize */ + (void) getlong(ctx->file); /* usize */ + namelength = getshort(ctx->file); + extralength = getshort(ctx->file); + + fseek(ctx->file, namelength + extralength, 1); + + if (method == 0) + { + fread(outbuf, 1, ent->usize, ctx->file); + } + else if (method == 8) + { + inbuf = xps_alloc(ctx, ent->csize); + + fread(inbuf, 1, ent->csize, ctx->file); + + memset(&stream, 0, sizeof(z_stream)); + stream.zalloc = (alloc_func) xps_zip_alloc_items; + stream.zfree = (free_func) xps_zip_free; + stream.opaque = ctx; + stream.next_in = inbuf; + stream.avail_in = ent->csize; + stream.next_out = outbuf; + stream.avail_out = ent->usize; + + code = inflateInit2(&stream, -15); + if (code != Z_OK) + return gs_throw1(-1, "zlib inflateInit2 error: %s", stream.msg); + code = inflate(&stream, Z_FINISH); + if (code != Z_STREAM_END) + { + inflateEnd(&stream); + return gs_throw1(-1, "zlib inflate error: %s", stream.msg); + } + code = inflateEnd(&stream); + if (code != Z_OK) + return gs_throw1(-1, "zlib inflateEnd error: %s", stream.msg); + + xps_free(ctx, inbuf); + } + else + { + return gs_throw1(-1, "unknown compression method (%d)", method); + } + + return gs_okay; +} + +/* + * Read the central directory in a zip file. + */ + +static int +xps_read_zip_dir(xps_context_t *ctx, int start_offset) +{ + int sig; + int offset, count; + int namesize, metasize, commentsize; + int i; + + fseek(ctx->file, start_offset, 0); + + sig = getlong(ctx->file); + if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG) + return gs_throw1(-1, "wrong zip end of central directory signature (0x%x)", sig); + + (void) getshort(ctx->file); /* this disk */ + (void) getshort(ctx->file); /* start disk */ + (void) getshort(ctx->file); /* entries in this disk */ + count = getshort(ctx->file); /* entries in central directory disk */ + (void) getlong(ctx->file); /* size of central directory */ + offset = getlong(ctx->file); /* offset to central directory */ + + ctx->zip_count = count; + ctx->zip_table = xps_alloc(ctx, sizeof(xps_entry_t) * count); + if (!ctx->zip_table) + return gs_throw(-1, "cannot allocate zip entry table"); + + memset(ctx->zip_table, 0, sizeof(xps_entry_t) * count); + + fseek(ctx->file, offset, 0); + + for (i = 0; i < count; i++) + { + sig = getlong(ctx->file); + if (sig != ZIP_CENTRAL_DIRECTORY_SIG) + return gs_throw1(-1, "wrong zip central directory signature (0x%x)", sig); + + (void) getshort(ctx->file); /* version made by */ + (void) getshort(ctx->file); /* version to extract */ + (void) getshort(ctx->file); /* general */ + (void) getshort(ctx->file); /* method */ + (void) getshort(ctx->file); /* last mod file time */ + (void) getshort(ctx->file); /* last mod file date */ + (void) getlong(ctx->file); /* crc-32 */ + ctx->zip_table[i].csize = getlong(ctx->file); + ctx->zip_table[i].usize = getlong(ctx->file); + namesize = getshort(ctx->file); + metasize = getshort(ctx->file); + commentsize = getshort(ctx->file); + (void) getshort(ctx->file); /* disk number start */ + (void) getshort(ctx->file); /* int file atts */ + (void) getlong(ctx->file); /* ext file atts */ + ctx->zip_table[i].offset = getlong(ctx->file); + + ctx->zip_table[i].name = xps_alloc(ctx, namesize + 1); + if (!ctx->zip_table[i].name) + return gs_throw(-1, "cannot allocate zip entry name"); + + fread(ctx->zip_table[i].name, 1, namesize, ctx->file); + ctx->zip_table[i].name[namesize] = 0; + + fseek(ctx->file, metasize, 1); + fseek(ctx->file, commentsize, 1); + } + + qsort(ctx->zip_table, count, sizeof(xps_entry_t), xps_compare_entries); + + for (i = 0; i < ctx->zip_count; i++) + { + if_debug3('|', "zip entry '%s' csize=%d usize=%d\n", + ctx->zip_table[i].name, + ctx->zip_table[i].csize, + ctx->zip_table[i].usize); + } + + return gs_okay; +} + +static int +xps_find_and_read_zip_dir(xps_context_t *ctx) +{ + int filesize, back, maxback; + int i, n; + char buf[512]; + + fseek(ctx->file, 0, SEEK_END); + filesize = ftell(ctx->file); + + maxback = MIN(filesize, 0xFFFF + sizeof buf); + back = MIN(maxback, sizeof buf); + + while (back < maxback) + { + fseek(ctx->file, filesize - back, 0); + + n = fread(buf, 1, sizeof buf, ctx->file); + if (n < 0) + return gs_throw(-1, "cannot read end of central directory"); + + for (i = n - 4; i > 0; i--) + if (!memcmp(buf + i, "PK\5\6", 4)) + return xps_read_zip_dir(ctx, filesize - back + i); + + back += sizeof buf - 4; + } + + return gs_throw(-1, "cannot find end of central directory"); +} + +/* + * Read and interleave split parts from a ZIP file. + */ + +static xps_part_t * +xps_read_zip_part(xps_context_t *ctx, char *partname) +{ + char buf[2048]; + xps_entry_t *ent; + xps_part_t *part; + int count, size, offset, i; + char *name; + + name = partname; + if (name[0] == '/') + name ++; + + /* All in one piece */ + ent = xps_find_zip_entry(ctx, name); + if (ent) + { + part = xps_new_part(ctx, partname, ent->usize); + xps_read_zip_entry(ctx, ent, part->data); + return part; + } + + /* Count the number of pieces and their total size */ + count = 0; + size = 0; + while (1) + { + sprintf(buf, "%s/[%d].piece", name, count); + ent = xps_find_zip_entry(ctx, buf); + if (!ent) + { + sprintf(buf, "%s/[%d].last.piece", name, count); + ent = xps_find_zip_entry(ctx, buf); + } + if (!ent) + break; + count ++; + size += ent->usize; + } + + /* Inflate the pieces */ + if (count) + { + part = xps_new_part(ctx, partname, size); + offset = 0; + for (i = 0; i < count; i++) + { + if (i < count - 1) + sprintf(buf, "%s/[%d].piece", name, i); + else + sprintf(buf, "%s/[%d].last.piece", name, i); + ent = xps_find_zip_entry(ctx, buf); + xps_read_zip_entry(ctx, ent, part->data + offset); + offset += ent->usize; + } + return part; + } + + return NULL; +} + +/* + * Read and interleave split parts from files in the directory. + */ + +static xps_part_t * +xps_read_dir_part(xps_context_t *ctx, char *name) +{ + char buf[2048]; + xps_part_t *part; + FILE *file; + int count, size, offset, i, n; + + xps_strlcpy(buf, ctx->directory, sizeof buf); + xps_strlcat(buf, name, sizeof buf); + + /* All in one piece */ + file = fopen(buf, "rb"); + if (file) + { + fseek(file, 0, SEEK_END); + size = ftell(file); + fseek(file, 0, SEEK_SET); + part = xps_new_part(ctx, name, size); + fread(part->data, 1, size, file); + fclose(file); + return part; + } + + /* Count the number of pieces and their total size */ + count = 0; + size = 0; + while (1) + { + sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, count); + file = fopen(buf, "rb"); + if (!file) + { + sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, count); + file = fopen(buf, "rb"); + } + if (!file) + break; + count ++; + fseek(file, 0, SEEK_END); + size += ftell(file); + fclose(file); + } + + /* Inflate the pieces */ + if (count) + { + part = xps_new_part(ctx, name, size); + offset = 0; + for (i = 0; i < count; i++) + { + if (i < count - 1) + sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, i); + else + sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, i); + file = fopen(buf, "rb"); + n = fread(part->data + offset, 1, size - offset, file); + offset += n; + fclose(file); + } + return part; + } + + return NULL; +} + +xps_part_t * +xps_read_part(xps_context_t *ctx, char *partname) +{ + if (ctx->directory) + return xps_read_dir_part(ctx, partname); + return xps_read_zip_part(ctx, partname); +} + +/* + * Read and process the XPS document. + */ + +static int +xps_read_and_process_metadata_part(xps_context_t *ctx, char *name) +{ + xps_part_t *part; + int code; + + part = xps_read_part(ctx, name); + if (!part) + return gs_rethrow1(-1, "cannot read zip part '%s'", name); + + code = xps_parse_metadata(ctx, part); + if (code) + return gs_rethrow1(code, "cannot process metadata part '%s'", name); + + xps_free_part(ctx, part); + + return gs_okay; +} + +static int +xps_read_and_process_page_part(xps_context_t *ctx, char *name) +{ + xps_part_t *part; + int code; + + part = xps_read_part(ctx, name); + if (!part) + return gs_rethrow1(-1, "cannot read zip part '%s'", name); + + code = xps_parse_fixed_page(ctx, part); + if (code) + return gs_rethrow1(code, "cannot parse fixed page part '%s'", name); + + xps_free_part(ctx, part); + + return gs_okay; +} + +/* + * Called by xpstop.c + */ + +int +xps_process_file(xps_context_t *ctx, char *filename) +{ + char buf[2048]; + xps_document_t *doc; + xps_page_t *page; + int code; + char *p; + + ctx->file = fopen(filename, "rb"); + if (!ctx->file) + return gs_throw1(-1, "cannot open file: '%s'", filename); + + if (strstr(filename, ".fpage")) + { + xps_part_t *part; + int size; + + if_debug0('|', "zip: single page mode\n"); + xps_strlcpy(buf, filename, sizeof buf); + while (1) + { + p = strrchr(buf, '/'); + if (!p) + p = strrchr(buf, '\\'); + if (!p) + break; + xps_strlcpy(p, "/_rels/.rels", buf + sizeof buf - p); + if_debug1('|', "zip: testing if '%s' exists\n", buf); + if (isfile(buf)) + { + *p = 0; + ctx->directory = xps_strdup(ctx, buf); + if_debug1('|', "zip: using '%s' as root directory\n", ctx->directory); + break; + } + *p = 0; + } + if (!ctx->directory) + { + if_debug0('|', "zip: no /_rels/.rels found; assuming absolute paths\n"); + ctx->directory = xps_strdup(ctx, ""); + } + + fseek(ctx->file, 0, SEEK_END); + size = ftell(ctx->file); + fseek(ctx->file, 0, SEEK_SET); + part = xps_new_part(ctx, filename, size); + fread(part->data, 1, size, ctx->file); + + code = xps_parse_fixed_page(ctx, part); + if (code) + return gs_rethrow1(code, "cannot parse fixed page part '%s'", part->name); + + xps_free_part(ctx, part); + return gs_okay; + } + + if (strstr(filename, "/_rels/.rels") || strstr(filename, "\\_rels\\.rels")) + { + xps_strlcpy(buf, filename, sizeof buf); + p = strstr(buf, "/_rels/.rels"); + if (!p) + p = strstr(buf, "\\_rels\\.rels"); + *p = 0; + ctx->directory = xps_strdup(ctx, buf); + if_debug1('|', "zip: using '%s' as root directory\n", ctx->directory); + } + else + { + code = xps_find_and_read_zip_dir(ctx); + if (code < 0) + return gs_rethrow(code, "cannot read zip central directory"); + } + + code = xps_read_and_process_metadata_part(ctx, "/_rels/.rels"); + if (code) + return gs_rethrow(code, "cannot process root relationship part"); + + if (!ctx->start_part) + return gs_throw(-1, "cannot find fixed document sequence start part"); + + code = xps_read_and_process_metadata_part(ctx, ctx->start_part); + if (code) + return gs_rethrow(code, "cannot process FixedDocumentSequence part"); + + for (doc = ctx->first_fixdoc; doc; doc = doc->next) + { + code = xps_read_and_process_metadata_part(ctx, doc->name); + if (code) + return gs_rethrow(code, "cannot process FixedDocument part"); + } + + for (page = ctx->first_page; page; page = page->next) + { + code = xps_read_and_process_page_part(ctx, page->name); + if (code) + return gs_rethrow(code, "cannot process FixedPage part"); + } + + if (ctx->directory) + xps_free(ctx, ctx->directory); + if (ctx->file) + fclose(ctx->file); + + return gs_okay; +} -- cgit v1.2.3 From b1c3f5a602f7be8e2db87f73c1e55052b58b1c6e Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Tue, 22 Mar 2011 19:42:01 +0100 Subject: xps: use fitz runtime and remove ghostscript specific code so it compiles. --- Makefile | 45 +++- Makerules | 2 +- Makethird | 21 ++ xps/ghostxps.h | 412 ---------------------------- xps/muxps.h | 309 +++++++++++++++++++++ xps/xpsanalyze.c | 22 +- xps/xpscolor.c | 85 +++--- xps/xpscommon.c | 67 ++--- xps/xpscrc.c | 18 +- xps/xpsdoc.c | 33 +-- xps/xpsfont.c | 519 ++---------------------------------- xps/xpsglyphs.c | 126 +++------ xps/xpsgradient.c | 440 +++++++----------------------- xps/xpshash.c | 24 +- xps/xpsimage.c | 308 +++------------------ xps/xpsjpeg.c | 142 +--------- xps/xpsjxr.c | 34 ++- xps/xpsmem.c | 51 +--- xps/xpsopacity.c | 60 ++--- xps/xpspage.c | 225 ++++------------ xps/xpspath.c | 440 +++++++++++++++--------------- xps/xpspng.c | 64 ++--- xps/xpsresource.c | 44 ++- xps/xpstiff.c | 87 +++--- xps/xpstile.c | 172 +++--------- xps/xpstop.c | 780 ++++++++++++++++++++---------------------------------- xps/xpsutf.c | 18 +- xps/xpsvisual.c | 29 +- xps/xpsxml.c | 24 +- xps/xpszip.c | 242 ++++++++--------- 30 files changed, 1505 insertions(+), 3338 deletions(-) delete mode 100644 xps/ghostxps.h create mode 100644 xps/muxps.h diff --git a/Makefile b/Makefile index 9479ffad..1e6135bb 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ endif # directory exists. LIBS := -lfreetype -ljbig2dec -lopenjpeg -ljpeg -lz -lm +XPSLIBS := -lexpat -lpng include Makerules include Makethird @@ -170,6 +171,35 @@ MUPDF_SRC := \ MUPDF_OBJ := $(MUPDF_SRC:mupdf/%.c=$(OBJDIR)/%.o) $(MUPDF_OBJ): $(MUPDF_HDR) +MUXPS_HDR := $(FITZ_HDR) xps/muxps.h +MUXPS_SRC := \ + xps/xpsanalyze.c \ + xps/xpscolor.c \ + xps/xpscommon.c \ + xps/xpscrc.c \ + xps/xpsdoc.c \ + xps/xpsfont.c \ + xps/xpsglyphs.c \ + xps/xpsgradient.c \ + xps/xpshash.c \ + xps/xpsjxr.c \ + xps/xpsimage.c \ + xps/xpsjpeg.c \ + xps/xpsmem.c \ + xps/xpsopacity.c \ + xps/xpspage.c \ + xps/xpspath.c \ + xps/xpspng.c \ + xps/xpsresource.c \ + xps/xpstiff.c \ + xps/xpstile.c \ + xps/xpsutf.c \ + xps/xpsvisual.c \ + xps/xpsxml.c \ + xps/xpszip.c +MUXPS_OBJ := $(MUXPS_SRC:xps/%.c=$(OBJDIR)/%.o) +$(MUXPS_OBJ): $(MUXPS_HDR) + $(OBJDIR)/%.o: fitz/%.c $(CC_CMD) $(OBJDIR)/%.o: draw/%.c @@ -178,6 +208,8 @@ $(OBJDIR)/%.o: draw/%.s $(CC_CMD) $(OBJDIR)/%.o: mupdf/%.c $(CC_CMD) +$(OBJDIR)/%.o: xps/%.c + $(CC_CMD) $(OBJDIR)/%.o: $(GENDIR)/%.c $(CC_CMD) @@ -298,11 +330,15 @@ MUPDF_LIB = $(OBJDIR)/libmupdf.a $(MUPDF_LIB): $(FITZ_OBJ) $(DRAW_OBJ) $(MUPDF_OBJ) $(CMAP_OBJ) $(FONT_OBJ) $(AR_CMD) +MUXPS_LIB = $(OBJDIR)/libmuxps.a +$(MUXPS_LIB): $(FITZ_OBJ) $(DRAW_OBJ) $(MUXPS_OBJ) + $(AR_CMD) + # # Applications # -APPS = $(PDFSHOW_EXE) $(PDFCLEAN_EXE) $(PDFDRAW_EXE) $(PDFEXTRACT_EXE) $(PDFINFO_EXE) $(PDFVIEW_EXE) +APPS = $(PDFSHOW_EXE) $(PDFCLEAN_EXE) $(PDFDRAW_EXE) $(PDFEXTRACT_EXE) $(PDFINFO_EXE) $(PDFVIEW_EXE) $(XPSDRAW_EXE) APPS_MAN = \ apps/man/mupdf.1 \ @@ -348,6 +384,13 @@ $(PDFINFO_OBJ): $(MUPDF_HDR) $(PDFINFO_EXE): $(PDFINFO_OBJ) $(MUPDF_LIB) $(THIRD_LIBS) $(LD_CMD) +XPSDRAW_SRC=xps/xpstop.c +XPSDRAW_OBJ=$(XPSDRAW_SRC:xps/%.c=$(OBJDIR)/%.o) +XPSDRAW_EXE=$(OBJDIR)/xpsdraw +$(XPSDRAW_OBJ): $(MUXPS_HDR) +$(XPSDRAW_EXE): $(XPSDRAW_OBJ) $(MUXPS_LIB) $(THIRD_LIBS) + $(LD_CMD) $(XPSLIBS) + PDFAPP_HDR = apps/pdfapp.h X11VIEW_SRC=apps/x11_main.c apps/x11_image.c apps/pdfapp.c diff --git a/Makerules b/Makerules index 34bf6ea6..e3e5a987 100644 --- a/Makerules +++ b/Makerules @@ -7,7 +7,7 @@ CC ?= CFLAGS ?= LDFLAGS ?= -CFLAGS += -Ifitz -Imupdf -Wall +CFLAGS += -Ifitz -Imupdf -Ixps -Wall ifeq "$(build)" "debug" CFLAGS += -pipe -g diff --git a/Makethird b/Makethird index d74f1176..bef43aea 100644 --- a/Makethird +++ b/Makethird @@ -9,6 +9,27 @@ openjpeg_dir := $(wildcard thirdparty/openjpeg*/libopenjpeg) freetype_dir := $(wildcard thirdparty/freetype*) jpeg_dir := $(wildcard thirdparty/jpeg*) zlib_dir := $(wildcard thirdparty/zlib*) +expat_dir := $(wildcard thirdparty/expat*/lib) + +ifneq "$(expat_dir)" "" + +THIRD_LIBS += $(EXPAT_LIB) +THIRD_INCS += -I$(expat_dir) +LIBS := $(filter-out -lexpat, $(LIBS)) +XPSLIBS := $(filter-out -lexpat, $(XPSLIBS)) + +EXPAT_SRC=$(addprefix $(expat_dir)/, \ + xmlparse.c \ + xmlrole.c \ + xmltok.c ) +EXPAT_OBJ=$(EXPAT_SRC:$(expat_dir)/%.c=$(OBJDIR)/%.o) +EXPAT_LIB=$(OBJDIR)/libexpat.a +$(EXPAT_LIB): $(EXPAT_OBJ) + $(AR_CMD) +$(OBJDIR)/%.o: $(expat_dir)/%.c + $(CC_CMD) -DHAVE_MEMMOVE + +endif ifneq "$(jbig2dec_dir)" "" diff --git a/xps/ghostxps.h b/xps/ghostxps.h deleted file mode 100644 index 3f06a80d..00000000 --- a/xps/ghostxps.h +++ /dev/null @@ -1,412 +0,0 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* combined internal header for the XPS interpreter */ - -#include "memory_.h" -#include "math_.h" - -#include -#include /* for toupper() */ - -#include "gp.h" - -#include "gsgc.h" -#include "gstypes.h" -#include "gsstate.h" -#include "gsmatrix.h" -#include "gscoord.h" -#include "gsmemory.h" -#include "gsparam.h" -#include "gsdevice.h" -#include "scommon.h" -#include "gdebug.h" -#include "gserror.h" -#include "gserrors.h" -#include "gspaint.h" -#include "gspath.h" -#include "gsimage.h" -#include "gscspace.h" -#include "gsptype1.h" -#include "gscolor2.h" -#include "gscolor3.h" -#include "gsutil.h" -#include "gsicc.h" - -#include "gstrans.h" - -#include "gxpath.h" /* gsshade.h depends on it */ -#include "gxfixed.h" /* gsshade.h depends on it */ -#include "gxmatrix.h" /* gxtype1.h depends on it */ -#include "gsshade.h" -#include "gsfunc.h" -#include "gsfunc3.h" /* we use stitching and exponential interp */ - -#include "gxfont.h" -#include "gxchar.h" -#include "gxcolor2.h" /* Required for definition of gs_pattern1_instance_t */ -#include "gxtype1.h" -#include "gxfont1.h" -#include "gxfont42.h" -#include "gxfcache.h" -#include "gxistate.h" - -#include "gzstate.h" -#include "gzpath.h" -#include "gzcpath.h" - -#include "gsicc_manage.h" -#include "gscms.h" -#include "gsicc_cache.h" - -#include "zlib.h" - -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif -#ifndef MAX -#define MAX(a,b) ((a) < (b) ? (b) : (a)) -#endif -#ifndef ABS -#define ABS(a) ((a) < 0 ? -(a) : (a)) -#endif - -/* - * XPS and ZIP contants. - */ - -typedef struct xps_context_s xps_context_t; - -#define REL_START_PART \ - "http://schemas.microsoft.com/xps/2005/06/fixedrepresentation" -#define REL_REQUIRED_RESOURCE \ - "http://schemas.microsoft.com/xps/2005/06/required-resource" -#define REL_REQUIRED_RESOURCE_RECURSIVE \ - "http://schemas.microsoft.com/xps/2005/06/required-resource#recursive" - -#define ZIP_LOCAL_FILE_SIG 0x04034b50 -#define ZIP_DATA_DESC_SIG 0x08074b50 -#define ZIP_CENTRAL_DIRECTORY_SIG 0x02014b50 -#define ZIP_END_OF_CENTRAL_DIRECTORY_SIG 0x06054b50 - -/* - * Memory, and string functions. - */ - -void * xps_realloc_imp(xps_context_t *ctx, void *ptr, int size, const char *func); - -#define xps_alloc(ctx, size) \ - ((void*)gs_alloc_bytes(ctx->memory, size, __func__)) -#define xps_realloc(ctx, ptr, size) \ - xps_realloc_imp(ctx, ptr, size, __func__) -#define xps_strdup(ctx, str) \ - xps_strdup_imp(ctx, str, __func__) -#define xps_free(ctx, ptr) \ - gs_free_object(ctx->memory, ptr, __func__) - -size_t xps_strlcpy(char *destination, const char *source, size_t size); -size_t xps_strlcat(char *destination, const char *source, size_t size); -int xps_strcasecmp(char *a, char *b); -char *xps_strdup_imp(xps_context_t *ctx, const char *str, const char *function); -void xps_absolute_path(char *output, char *base_uri, char *path, int output_size); - -int xps_utf8_to_ucs(int *p, const char *s, int n); - -/* - * Generic hashtable. - */ - -typedef struct xps_hash_table_s xps_hash_table_t; - -xps_hash_table_t *xps_hash_new(xps_context_t *ctx); -void *xps_hash_lookup(xps_hash_table_t *table, char *key); -int xps_hash_insert(xps_context_t *ctx, xps_hash_table_t *table, char *key, void *value); -void xps_hash_free(xps_context_t *ctx, xps_hash_table_t *table, - void (*free_key)(xps_context_t *ctx, void *), - void (*free_value)(xps_context_t *ctx, void *)); -void xps_hash_debug(xps_hash_table_t *table); - -/* - * Container parts. - */ - -typedef struct xps_part_s xps_part_t; - -struct xps_part_s -{ - char *name; - int size; - int cap; - byte *data; -}; - -xps_part_t *xps_new_part(xps_context_t *ctx, char *name, int size); -xps_part_t *xps_read_part(xps_context_t *ctx, char *partname); -void xps_free_part(xps_context_t *ctx, xps_part_t *part); - -/* - * Document structure. - */ - -typedef struct xps_document_s xps_document_t; -typedef struct xps_page_s xps_page_t; - -struct xps_document_s -{ - char *name; - xps_document_t *next; -}; - -struct xps_page_s -{ - char *name; - int width; - int height; - xps_page_t *next; -}; - -int xps_parse_metadata(xps_context_t *ctx, xps_part_t *part); -void xps_free_fixed_pages(xps_context_t *ctx); -void xps_free_fixed_documents(xps_context_t *ctx); -void xps_debug_fixdocseq(xps_context_t *ctx); - -/* - * Images. - */ - -typedef struct xps_image_s xps_image_t; - -/* type for the information derived directly from the raster file format */ - -struct xps_image_s -{ - int width; - int height; - int stride; - gs_color_space *colorspace; - int comps; - int hasalpha; /* chunky alpha */ - int bits; - int xres; - int yres; - byte *samples; - byte *alpha; /* isolated alpha plane */ - byte *profile; - int profilesize; -}; - -int xps_decode_jpeg(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image); -int xps_decode_png(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image); -int xps_decode_tiff(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image); -int xps_decode_jpegxr(xps_context_t *ctx, byte *buf, int len, xps_image_t *image); - -int xps_png_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen); -int xps_tiff_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen); -int xps_jpegxr_has_alpha(xps_context_t *ctx, byte *buf, int len); - -void xps_free_image(xps_context_t *ctx, xps_image_t *image); - -/* - * Fonts. - */ - -typedef struct xps_font_s xps_font_t; -typedef struct xps_glyph_metrics_s xps_glyph_metrics_t; - -struct xps_font_s -{ - byte *data; - int length; - gs_font *font; - - int subfontid; - int cmaptable; - int cmapsubcount; - int cmapsubtable; - int usepua; - - /* these are for CFF opentypes only */ - byte *cffdata; - byte *cffend; - byte *gsubrs; - byte *subrs; - byte *charstrings; -}; - -struct xps_glyph_metrics_s -{ - float hadv, vadv, vorg; -}; - -xps_font_t *xps_new_font(xps_context_t *ctx, byte *buf, int buflen, int index); -void xps_free_font(xps_context_t *ctx, xps_font_t *font); - -int xps_count_font_encodings(xps_font_t *font); -void xps_identify_font_encoding(xps_font_t *font, int idx, int *pid, int *eid); -void xps_select_font_encoding(xps_font_t *font, int idx); -int xps_encode_font_char(xps_font_t *font, int key); - -void xps_measure_font_glyph(xps_context_t *ctx, xps_font_t *font, int gid, xps_glyph_metrics_t *mtx); - -int xps_find_sfnt_table(xps_font_t *font, const char *name, int *lengthp); -void xps_load_sfnt_name(xps_font_t *font, char *namep); -int xps_init_truetype_font(xps_context_t *ctx, xps_font_t *font); -int xps_init_postscript_font(xps_context_t *ctx, xps_font_t *font); - -void xps_debug_path(xps_context_t *ctx); - -/* - * Colorspaces and colors. - */ - -gs_color_space *xps_read_icc_colorspace(xps_context_t *ctx, char *base_uri, char *profile); -void xps_parse_color(xps_context_t *ctx, char *base_uri, char *hexstring, gs_color_space **csp, float *samples); -void xps_set_color(xps_context_t *ctx, gs_color_space *colorspace, float *samples); - -/* - * XML document model - */ - -typedef struct xps_item_s xps_item_t; - -xps_item_t * xps_parse_xml(xps_context_t *ctx, byte *buf, int len); -xps_item_t * xps_next(xps_item_t *item); -xps_item_t * xps_down(xps_item_t *item); -char * xps_tag(xps_item_t *item); -char * xps_att(xps_item_t *item, const char *att); -void xps_free_item(xps_context_t *ctx, xps_item_t *item); -void xps_debug_item(xps_item_t *item, int level); - -/* - * Resource dictionaries. - */ - -typedef struct xps_resource_s xps_resource_t; - -struct xps_resource_s -{ - char *name; - char *base_uri; /* only used in the head nodes */ - xps_item_t *base_xml; /* only used in the head nodes, to free the xml document */ - xps_item_t *data; - xps_resource_t *next; - xps_resource_t *parent; /* up to the previous dict in the stack */ -}; - -int xps_parse_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, char *base_uri, xps_item_t *root); -void xps_free_resource_dictionary(xps_context_t *ctx, xps_resource_t *dict); -void xps_resolve_resource_reference(xps_context_t *ctx, xps_resource_t *dict, char **attp, xps_item_t **tagp, char **urip); - -void xps_debug_resource_dictionary(xps_resource_t *dict); - -/* - * Fixed page/graphics parsing. - */ - -int xps_parse_fixed_page(xps_context_t *ctx, xps_part_t *part); -int xps_parse_canvas(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_path(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_glyphs(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_solid_color_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_image_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_visual_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_linear_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_radial_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); - -int xps_parse_tiling_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root, int (*func)(xps_context_t*, char*, xps_resource_t*, xps_item_t*, void*), void *user); - -void xps_parse_matrix_transform(xps_context_t *ctx, xps_item_t *root, gs_matrix *matrix); -void xps_parse_render_transform(xps_context_t *ctx, char *text, gs_matrix *matrix); -void xps_parse_rectangle(xps_context_t *ctx, char *text, gs_rect *rect); -void xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom); -void xps_parse_path_geometry(xps_context_t *ctx, xps_resource_t *dict, xps_item_t *root, int stroking); - -int xps_begin_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, char *opacity_att, xps_item_t *opacity_mask_tag); -void xps_end_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, char *opacity_att, xps_item_t *opacity_mask_tag); - -int xps_parse_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_element(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node); - -void xps_clip(xps_context_t *ctx); -void xps_fill(xps_context_t *ctx); -void xps_bounds_in_user_space(xps_context_t *ctx, gs_rect *user); - -int xps_element_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *node); -int xps_resource_dictionary_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *node); -int xps_image_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root); - -/* - * The interpreter context. - */ - -typedef struct xps_entry_s xps_entry_t; - -struct xps_entry_s -{ - char *name; - int offset; - int csize; - int usize; -}; - -struct xps_context_s -{ - void *instance; - gs_memory_t *memory; - gs_state *pgs; - gs_font_dir *fontdir; - - gs_color_space *gray; - gs_color_space *srgb; - gs_color_space *scrgb; - gs_color_space *cmyk; - - char *directory; - FILE *file; - int zip_count; - xps_entry_t *zip_table; - - char *start_part; /* fixed document sequence */ - xps_document_t *first_fixdoc; /* first fixed document */ - xps_document_t *last_fixdoc; /* last fixed document */ - xps_page_t *first_page; /* first page of document */ - xps_page_t *last_page; /* last page of document */ - - char *base_uri; /* base uri for parsing XML and resolving relative paths */ - char *part_uri; /* part uri for parsing metadata relations */ - - /* We cache font and colorspace resources */ - xps_hash_table_t *font_table; - xps_hash_table_t *colorspace_table; - - /* Global toggle for transparency */ - int use_transparency; - - /* Hack to workaround ghostscript's lack of understanding - * the pdf 1.4 specification of Alpha only transparency groups. - * We have to force all colors to be grayscale whenever we are computing - * opacity masks. - */ - int opacity_only; - - /* The fill_rule is set by path parsing. - * It is used by clip/fill functions. - * 1=nonzero, 0=evenodd - */ - int fill_rule; -}; - -int xps_process_file(xps_context_t *ctx, char *filename); - -/* end of page device callback foo */ -int xps_show_page(xps_context_t *ctx, int num_copies, int flush); diff --git a/xps/muxps.h b/xps/muxps.h new file mode 100644 index 00000000..f2145d2f --- /dev/null +++ b/xps/muxps.h @@ -0,0 +1,309 @@ +#ifndef _MUXPS_H_ +#define _MUXPS_H_ + +#ifndef _FITZ_H_ +#error "fitz.h must be included before muxps.h" +#endif + +typedef unsigned char byte; + +/* + * XPS and ZIP constants. + */ + +typedef struct xps_context_s xps_context_t; + +#define REL_START_PART \ + "http://schemas.microsoft.com/xps/2005/06/fixedrepresentation" +#define REL_REQUIRED_RESOURCE \ + "http://schemas.microsoft.com/xps/2005/06/required-resource" +#define REL_REQUIRED_RESOURCE_RECURSIVE \ + "http://schemas.microsoft.com/xps/2005/06/required-resource#recursive" + +#define ZIP_LOCAL_FILE_SIG 0x04034b50 +#define ZIP_DATA_DESC_SIG 0x08074b50 +#define ZIP_CENTRAL_DIRECTORY_SIG 0x02014b50 +#define ZIP_END_OF_CENTRAL_DIRECTORY_SIG 0x06054b50 + +/* + * Memory, and string functions. + */ + +#define xps_alloc(ctx, size) fz_malloc(size) +#define xps_realloc(ctx, ptr, size) fz_realloc(ptr, size, 1) +#define xps_strdup(ctx, str) fz_strdup(str) +#define xps_free(ctx, ptr) fz_free(ptr) + +size_t xps_strlcpy(char *destination, const char *source, size_t size); +size_t xps_strlcat(char *destination, const char *source, size_t size); +int xps_strcasecmp(char *a, char *b); +char *xps_strdup_imp(xps_context_t *ctx, const char *str, const char *function); +void xps_absolute_path(char *output, char *base_uri, char *path, int output_size); + +int xps_utf8_to_ucs(int *p, const char *s, int n); + +/* + * Generic hashtable. + */ + +typedef struct xps_hash_table_s xps_hash_table_t; + +xps_hash_table_t *xps_hash_new(xps_context_t *ctx); +void *xps_hash_lookup(xps_hash_table_t *table, char *key); +int xps_hash_insert(xps_context_t *ctx, xps_hash_table_t *table, char *key, void *value); +void xps_hash_free(xps_context_t *ctx, xps_hash_table_t *table, + void (*free_key)(xps_context_t *ctx, void *), + void (*free_value)(xps_context_t *ctx, void *)); +void xps_hash_debug(xps_hash_table_t *table); + +/* + * Container parts. + */ + +typedef struct xps_part_s xps_part_t; + +struct xps_part_s +{ + char *name; + int size; + int cap; + byte *data; +}; + +xps_part_t *xps_new_part(xps_context_t *ctx, char *name, int size); +xps_part_t *xps_read_part(xps_context_t *ctx, char *partname); +void xps_free_part(xps_context_t *ctx, xps_part_t *part); + +/* + * Document structure. + */ + +typedef struct xps_document_s xps_document_t; +typedef struct xps_page_s xps_page_t; + +struct xps_document_s +{ + char *name; + xps_document_t *next; +}; + +struct xps_page_s +{ + char *name; + int width; + int height; + struct xps_item_s *root; + xps_page_t *next; +}; + +int xps_parse_metadata(xps_context_t *ctx, xps_part_t *part); +void xps_free_fixed_pages(xps_context_t *ctx); +void xps_free_fixed_documents(xps_context_t *ctx); +void xps_debug_fixdocseq(xps_context_t *ctx); + +/* + * Images. + */ + +typedef struct xps_image_s xps_image_t; + +/* type for the information derived directly from the raster file format */ + +struct xps_image_s +{ + int width; + int height; + int stride; + fz_colorspace *colorspace; + int comps; + int hasalpha; /* chunky alpha */ + int bits; + int xres; + int yres; + byte *samples; + byte *profile; + int profilesize; +}; + +int xps_decode_jpeg(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image); +int xps_decode_png(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image); +int xps_decode_tiff(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image); +int xps_decode_jpegxr(xps_context_t *ctx, byte *buf, int len, xps_image_t *image); + +int xps_png_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen); +int xps_tiff_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen); +int xps_jpegxr_has_alpha(xps_context_t *ctx, byte *buf, int len); + +void xps_free_image(xps_context_t *ctx, xps_image_t *image); + +/* + * Fonts. + */ + +typedef struct xps_glyph_metrics_s xps_glyph_metrics_t; + +struct xps_glyph_metrics_s +{ + float hadv, vadv, vorg; +}; + +int xps_count_font_encodings(fz_font *font); +void xps_identify_font_encoding(fz_font *font, int idx, int *pid, int *eid); +void xps_select_font_encoding(fz_font *font, int idx); +int xps_encode_font_char(fz_font *font, int key); + +void xps_measure_font_glyph(xps_context_t *ctx, fz_font *font, int gid, xps_glyph_metrics_t *mtx); + +void xps_debug_path(xps_context_t *ctx); + +/* + * Colorspaces and colors. + */ + +fz_colorspace *xps_read_icc_colorspace(xps_context_t *ctx, char *base_uri, char *profile); +void xps_parse_color(xps_context_t *ctx, char *base_uri, char *hexstring, fz_colorspace **csp, float *samples); +void xps_set_color(xps_context_t *ctx, fz_colorspace *colorspace, float *samples); + +/* + * XML document model + */ + +typedef struct xps_item_s xps_item_t; + +xps_item_t * xps_parse_xml(xps_context_t *ctx, byte *buf, int len); +xps_item_t * xps_next(xps_item_t *item); +xps_item_t * xps_down(xps_item_t *item); +char * xps_tag(xps_item_t *item); +char * xps_att(xps_item_t *item, const char *att); +void xps_free_item(xps_context_t *ctx, xps_item_t *item); +void xps_debug_item(xps_item_t *item, int level); + +/* + * Resource dictionaries. + */ + +typedef struct xps_resource_s xps_resource_t; + +struct xps_resource_s +{ + char *name; + char *base_uri; /* only used in the head nodes */ + xps_item_t *base_xml; /* only used in the head nodes, to free the xml document */ + xps_item_t *data; + xps_resource_t *next; + xps_resource_t *parent; /* up to the previous dict in the stack */ +}; + +int xps_parse_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, char *base_uri, xps_item_t *root); +void xps_free_resource_dictionary(xps_context_t *ctx, xps_resource_t *dict); +void xps_resolve_resource_reference(xps_context_t *ctx, xps_resource_t *dict, char **attp, xps_item_t **tagp, char **urip); + +void xps_debug_resource_dictionary(xps_resource_t *dict); + +/* + * Fixed page/graphics parsing. + */ + +int xps_load_fixed_page(xps_context_t *ctx, xps_page_t *page); +int xps_parse_fixed_page(xps_context_t *ctx, fz_matrix ctm, xps_page_t *page); +int xps_parse_canvas(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_path(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_solid_color_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_visual_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_linear_gradient_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_radial_gradient_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); + +int xps_parse_tiling_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root, int (*func)(xps_context_t*, fz_matrix, char*, xps_resource_t*, xps_item_t*, void*), void *user); + +void xps_parse_matrix_transform(xps_context_t *ctx, xps_item_t *root, fz_matrix *matrix); +void xps_parse_render_transform(xps_context_t *ctx, char *text, fz_matrix *matrix); +void xps_parse_rectangle(xps_context_t *ctx, char *text, fz_rect *rect); +void xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom); +void xps_parse_path_geometry(xps_context_t *ctx, xps_resource_t *dict, xps_item_t *root, int stroking); + +int xps_begin_opacity(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, char *opacity_att, xps_item_t *opacity_mask_tag); +void xps_end_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, char *opacity_att, xps_item_t *opacity_mask_tag); + +int xps_parse_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_parse_element(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); + +void xps_fill(xps_context_t *ctx, fz_matrix ctm); +void xps_clip(xps_context_t *ctx, fz_matrix ctm); +void xps_bounds_in_user_space(xps_context_t *ctx, fz_rect *user); + +int xps_element_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *node); +int xps_resource_dictionary_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *node); +int xps_image_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root); + +/* + * The interpreter context. + */ + +typedef struct xps_entry_s xps_entry_t; + +struct xps_entry_s +{ + char *name; + int offset; + int csize; + int usize; +}; + +struct xps_context_s +{ + char *directory; + FILE *file; + int zip_count; + xps_entry_t *zip_table; + + char *start_part; /* fixed document sequence */ + xps_document_t *first_fixdoc; /* first fixed document */ + xps_document_t *last_fixdoc; /* last fixed document */ + xps_page_t *first_page; /* first page of document */ + xps_page_t *last_page; /* last page of document */ + + char *base_uri; /* base uri for parsing XML and resolving relative paths */ + char *part_uri; /* part uri for parsing metadata relations */ + + /* We cache font and colorspace resources */ + xps_hash_table_t *font_table; + xps_hash_table_t *colorspace_table; + + /* Global toggle for transparency */ + int use_transparency; + + /* Hack to workaround ghostscript's lack of understanding + * the pdf 1.4 specification of Alpha only transparency groups. + * We have to force all colors to be grayscale whenever we are computing + * opacity masks. + */ + int opacity_only; + + /* The fill_rule is set by path parsing. + * It is used by clip/fill functions. + * 1=nonzero, 0=evenodd + */ + int fill_rule; + + /* Current path being accumulated */ + fz_path *path; + + /* Current color */ + fz_colorspace *colorspace; + float color[8]; + float alpha; + + /* Current device */ + fz_device *dev; +}; + +int xps_read_and_process_page_part(xps_context_t *ctx, fz_matrix ctm, char *name); +int xps_open_file(xps_context_t *ctx, char *filename); +int xps_count_pages(xps_context_t *ctx); +xps_page_t *xps_load_page(xps_context_t *ctx, int number); +xps_context_t *xps_new_context(void); +int xps_free_context(xps_context_t *ctx); + +#endif diff --git a/xps/xpsanalyze.c b/xps/xpsanalyze.c index 6e2f9d3e..41dc28c6 100644 --- a/xps/xpsanalyze.c +++ b/xps/xpsanalyze.c @@ -1,22 +1,10 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - /* XPS interpreter - analyze page checking for transparency. * This is a stripped down parser that looks for alpha values < 1.0 in * any part of the page. */ -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" static int xps_remote_resource_dictionary_has_transparency(xps_context_t *ctx, char *base_uri, char *source_att) @@ -47,7 +35,7 @@ static int xps_gradient_stops_have_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) { xps_item_t *node; - gs_color_space *colorspace; + fz_colorspace *colorspace; char *color_att; float samples[32]; @@ -111,7 +99,7 @@ xps_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) char *color_att; xps_item_t *node; - gs_color_space *colorspace; + fz_colorspace *colorspace; float samples[32]; if (!strcmp(xps_tag(root), "SolidColorBrush")) @@ -266,7 +254,7 @@ xps_element_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *nod char *stroke_att; char *fill_att; - gs_color_space *colorspace; + fz_colorspace *colorspace; float samples[32]; stroke_att = xps_att(node, "Stroke"); diff --git a/xps/xpscolor.c b/xps/xpscolor.c index dcc7b0ca..2c701c4a 100644 --- a/xps/xpscolor.c +++ b/xps/xpscolor.c @@ -1,43 +1,25 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. +#include "fitz.h" +#include "muxps.h" - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - color functions */ - -#include "ghostxps.h" - -#include "stream.h" /* for sizeof(stream) to work */ +#include /* for toupper() */ void -xps_set_color(xps_context_t *ctx, gs_color_space *cs, float *samples) +xps_set_color(xps_context_t *ctx, fz_colorspace *colorspace, float *samples) { - gs_client_color cc; - int i, n; + int i; if (ctx->opacity_only) { - gs_setopacityalpha(ctx->pgs, 1.0); - gs_setgray(ctx->pgs, samples[0]); + ctx->colorspace = fz_devicegray; + ctx->color[0] = samples[0]; + ctx->alpha = 1.0; } else { - n = cs_num_components(cs); - cc.pattern = 0; - for (i = 0; i < n; i++) - cc.paint.values[i] = samples[i + 1]; - - gs_setopacityalpha(ctx->pgs, samples[0]); - gs_setcolorspace(ctx->pgs, cs); - gs_setcolor(ctx->pgs, &cc); + ctx->colorspace = colorspace; + for (i = 0; i < colorspace->n; i++) + ctx->color[i] = samples[i + 1]; + ctx->alpha = samples[0]; } } @@ -61,14 +43,14 @@ static int count_commas(char *s) void xps_parse_color(xps_context_t *ctx, char *base_uri, char *string, - gs_color_space **csp, float *samples) + fz_colorspace **csp, float *samples) { char *p; int i, n; char buf[1024]; char *profile; - *csp = ctx->srgb; + *csp = fz_devicergb; samples[0] = 1.0; samples[1] = 0.0; @@ -100,8 +82,6 @@ xps_parse_color(xps_context_t *ctx, char *base_uri, char *string, else if (string[0] == 's' && string[1] == 'c' && string[2] == '#') { - *csp = ctx->scrgb; - if (count_commas(string) == 2) sscanf(string, "sc#%g,%g,%g", samples + 1, samples + 2, samples + 3); if (count_commas(string) == 3) @@ -116,7 +96,7 @@ xps_parse_color(xps_context_t *ctx, char *base_uri, char *string, profile = strchr(buf, ' '); if (!profile) { - gs_warn1("cannot find icc profile uri in '%s'", string); + fz_warn("cannot find icc profile uri in '%s'", string); return; } @@ -124,7 +104,7 @@ xps_parse_color(xps_context_t *ctx, char *base_uri, char *string, p = strchr(profile, ' '); if (!p) { - gs_warn1("cannot find component values in '%s'", profile); + fz_warn("cannot find component values in '%s'", profile); return; } @@ -152,20 +132,20 @@ xps_parse_color(xps_context_t *ctx, char *base_uri, char *string, /* Default fallbacks if the ICC stuff fails */ switch (n) { - case 2: *csp = ctx->gray; break; /* alpha + tint */ - case 4: *csp = ctx->srgb; break; /* alpha + RGB */ - case 5: *csp = ctx->cmyk; break; /* alpha + CMYK */ - default: *csp = ctx->gray; break; + case 2: *csp = fz_devicegray; break; /* alpha + tint */ + case 4: *csp = fz_devicergb; break; /* alpha + RGB */ + case 5: *csp = fz_devicecmyk; break; /* alpha + CMYK */ + default: *csp = fz_devicegray; break; } } } } -gs_color_space * +fz_colorspace * xps_read_icc_colorspace(xps_context_t *ctx, char *base_uri, char *profilename) { - gs_color_space *space; - cmm_profile_t *profile; +#if 0 + fz_colorspace *space; xps_part_t *part; char partname[1024]; @@ -178,9 +158,9 @@ xps_read_icc_colorspace(xps_context_t *ctx, char *base_uri, char *profilename) { part = xps_read_part(ctx, partname); - /* Problem finding profile. Don't fail, just use default */ + /* Problem finding profile. Don't fail, just use default */ if (!part) { - gs_warn1("cannot find icc profile part: %s", partname); + fz_warn("cannot find icc profile part: %s", partname); return NULL; } @@ -194,11 +174,11 @@ xps_read_icc_colorspace(xps_context_t *ctx, char *base_uri, char *profilename) /* Parse */ gsicc_init_profile_info(profile); - /* Problem with profile. Don't fail, just use the default */ + /* Problem with profile. Don't fail, just use the default */ if (profile->profile_handle == NULL) { gsicc_profile_reference(profile, -1); - gs_warn1("there was a problem with the profile: %s", partname); + fz_warn("there was a problem with the profile: %s", partname); return NULL; } @@ -215,20 +195,23 @@ xps_read_icc_colorspace(xps_context_t *ctx, char *base_uri, char *profilename) } return space; +#else + return NULL; +#endif } int -xps_parse_solid_color_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node) +xps_parse_solid_color_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node) { char *opacity_att; char *color_att; - gs_color_space *colorspace; + fz_colorspace *colorspace; float samples[32]; color_att = xps_att(node, "Color"); opacity_att = xps_att(node, "Opacity"); - colorspace = ctx->srgb; + colorspace = fz_devicergb; samples[0] = 1.0; samples[1] = 0.0; samples[2] = 0.0; @@ -242,7 +225,7 @@ xps_parse_solid_color_brush(xps_context_t *ctx, char *base_uri, xps_resource_t * xps_set_color(ctx, colorspace, samples); - xps_fill(ctx); + xps_fill(ctx, ctm); return 0; } diff --git a/xps/xpscommon.c b/xps/xpscommon.c index 6908b3c5..4ba38f7c 100644 --- a/xps/xpscommon.c +++ b/xps/xpscommon.c @@ -1,56 +1,37 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - common parse functions */ - -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" int -xps_parse_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node) +xps_parse_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node) { if (!strcmp(xps_tag(node), "SolidColorBrush")) - return xps_parse_solid_color_brush(ctx, base_uri, dict, node); + return xps_parse_solid_color_brush(ctx, ctm, base_uri, dict, node); if (!strcmp(xps_tag(node), "ImageBrush")) - { - int code = xps_parse_image_brush(ctx, base_uri, dict, node); - if (code) - gs_catch(code, "ignoring error in image brush"); - return gs_okay; - } + return xps_parse_image_brush(ctx, ctm, base_uri, dict, node); if (!strcmp(xps_tag(node), "VisualBrush")) - return xps_parse_visual_brush(ctx, base_uri, dict, node); + return xps_parse_visual_brush(ctx, ctm, base_uri, dict, node); if (!strcmp(xps_tag(node), "LinearGradientBrush")) - return xps_parse_linear_gradient_brush(ctx, base_uri, dict, node); + return xps_parse_linear_gradient_brush(ctx, ctm, base_uri, dict, node); if (!strcmp(xps_tag(node), "RadialGradientBrush")) - return xps_parse_radial_gradient_brush(ctx, base_uri, dict, node); - return gs_throw1(-1, "unknown brush tag: %s", xps_tag(node)); + return xps_parse_radial_gradient_brush(ctx, ctm, base_uri, dict, node); + return fz_throw("unknown brush tag: %s", xps_tag(node)); } int -xps_parse_element(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *node) +xps_parse_element(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node) { if (!strcmp(xps_tag(node), "Path")) - return xps_parse_path(ctx, base_uri, dict, node); + return xps_parse_path(ctx, ctm, base_uri, dict, node); if (!strcmp(xps_tag(node), "Glyphs")) - return xps_parse_glyphs(ctx, base_uri, dict, node); + return xps_parse_glyphs(ctx, ctm, base_uri, dict, node); if (!strcmp(xps_tag(node), "Canvas")) - return xps_parse_canvas(ctx, base_uri, dict, node); + return xps_parse_canvas(ctx, ctm, base_uri, dict, node); /* skip unknown tags (like Foo.Resources and similar) */ return 0; } void -xps_parse_render_transform(xps_context_t *ctx, char *transform, gs_matrix *matrix) +xps_parse_render_transform(xps_context_t *ctx, char *transform, fz_matrix *matrix) { float args[6]; char *s = transform; @@ -69,17 +50,17 @@ xps_parse_render_transform(xps_context_t *ctx, char *transform, gs_matrix *matri s++; } - matrix->xx = args[0]; matrix->xy = args[1]; - matrix->yx = args[2]; matrix->yy = args[3]; - matrix->tx = args[4]; matrix->ty = args[5]; + matrix->a = args[0]; matrix->b = args[1]; + matrix->c = args[2]; matrix->d = args[3]; + matrix->e = args[4]; matrix->f = args[5]; } void -xps_parse_matrix_transform(xps_context_t *ctx, xps_item_t *root, gs_matrix *matrix) +xps_parse_matrix_transform(xps_context_t *ctx, xps_item_t *root, fz_matrix *matrix) { char *transform; - gs_make_identity(matrix); + *matrix = fz_identity; if (!strcmp(xps_tag(root), "MatrixTransform")) { @@ -90,7 +71,7 @@ xps_parse_matrix_transform(xps_context_t *ctx, xps_item_t *root, gs_matrix *matr } void -xps_parse_rectangle(xps_context_t *ctx, char *text, gs_rect *rect) +xps_parse_rectangle(xps_context_t *ctx, char *text, fz_rect *rect) { float args[4]; char *s = text; @@ -108,8 +89,8 @@ xps_parse_rectangle(xps_context_t *ctx, char *text, gs_rect *rect) s++; } - rect->p.x = args[0]; - rect->p.y = args[1]; - rect->q.x = args[0] + args[2]; - rect->q.y = args[1] + args[3]; + rect->x0 = args[0]; + rect->y0 = args[1]; + rect->x1 = args[0] + args[2]; + rect->y1 = args[1] + args[3]; } diff --git a/xps/xpscrc.c b/xps/xpscrc.c index 7dea3c32..d5d4acd8 100644 --- a/xps/xpscrc.c +++ b/xps/xpscrc.c @@ -1,19 +1,5 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - CRC-32 implementation */ - -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" static const unsigned long crctab[256] = { diff --git a/xps/xpsdoc.c b/xps/xpsdoc.c index 3e9f7c96..c0e83e4a 100644 --- a/xps/xpsdoc.c +++ b/xps/xpsdoc.c @@ -1,19 +1,5 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - document parsing */ - -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" #include @@ -50,17 +36,17 @@ xps_debug_fixdocseq(xps_context_t *ctx) xps_page_t *page = ctx->first_page; if (ctx->start_part) - dprintf1("start part %s\n", ctx->start_part); + printf("start part %s\n", ctx->start_part); while (fixdoc) { - dprintf1("fixdoc %s\n", fixdoc->name); + printf("fixdoc %s\n", fixdoc->name); fixdoc = fixdoc->next; } while (page) { - dprintf3("page %s w=%d h=%d\n", page->name, page->width, page->height); + printf("page %s w=%d h=%d\n", page->name, page->width, page->height); page = page->next; } } @@ -75,8 +61,6 @@ xps_add_fixed_document(xps_context_t *ctx, char *name) if (!strcmp(fixdoc->name, name)) return; - if_debug1('|', "doc: adding fixdoc %s\n", name); - fixdoc = xps_alloc(ctx, sizeof(xps_document_t)); fixdoc->name = xps_strdup(ctx, name); fixdoc->next = NULL; @@ -118,12 +102,11 @@ xps_add_fixed_page(xps_context_t *ctx, char *name, int width, int height) if (!strcmp(page->name, name)) return; - if_debug1('|', "doc: adding page %s\n", name); - page = xps_alloc(ctx, sizeof(xps_page_t)); page->name = xps_strdup(ctx, name); page->width = width; page->height = height; + page->root = NULL; page->next = NULL; if (!ctx->first_page) @@ -257,7 +240,7 @@ xps_parse_metadata(xps_context_t *ctx, xps_part_t *part) xp = XML_ParserCreate(NULL); if (!xp) - return gs_throw(-1, "cannot create XML parser"); + return fz_throw("cannot create XML parser"); XML_SetUserData(xp, ctx); XML_SetParamEntityParsing(xp, XML_PARAM_ENTITY_PARSING_NEVER); @@ -271,7 +254,7 @@ xps_parse_metadata(xps_context_t *ctx, xps_part_t *part) ctx->part_uri = NULL; if (code == 0) - return gs_throw1(-1, "cannot parse XML in part: %s", part->name); + return fz_throw("cannot parse XML in part: %s", part->name); return 0; } diff --git a/xps/xpsfont.c b/xps/xpsfont.c index 93adf71d..ecedfabb 100644 --- a/xps/xpsfont.c +++ b/xps/xpsfont.c @@ -1,532 +1,53 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. +#include "fitz.h" +#include "muxps.h" - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - general font functions */ - -#include "ghostxps.h" - -static void xps_load_sfnt_cmap(xps_font_t *font); - -/* - * Big-endian memory accessor functions - */ - -static inline int s16(byte *p) -{ - return (signed short)( (p[0] << 8) | p[1] ); -} - -static inline int u16(byte *p) -{ - return (p[0] << 8) | p[1]; -} - -static inline int u24(byte *p) -{ - return (p[0] << 16) | (p[1] << 8) | p[2]; -} - -static inline int u32(byte *p) -{ - return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; -} - -xps_font_t * -xps_new_font(xps_context_t *ctx, byte *buf, int buflen, int index) -{ - xps_font_t *font; - int code; - - font = xps_alloc(ctx, sizeof(xps_font_t)); - if (!font) - { - gs_throw(-1, "out of memory"); - return NULL; - } - - font->data = buf; - font->length = buflen; - font->font = NULL; - - font->subfontid = index; - font->cmaptable = 0; - font->cmapsubcount = 0; - font->cmapsubtable = 0; - font->usepua = 0; - - font->cffdata = 0; - font->cffend = 0; - font->gsubrs = 0; - font->subrs = 0; - font->charstrings = 0; - - if (memcmp(font->data, "OTTO", 4) == 0) - code = xps_init_postscript_font(ctx, font); - else if (memcmp(font->data, "\0\1\0\0", 4) == 0) - code = xps_init_truetype_font(ctx, font); - else if (memcmp(font->data, "true", 4) == 0) - code = xps_init_truetype_font(ctx, font); - else if (memcmp(font->data, "ttcf", 4) == 0) - code = xps_init_truetype_font(ctx, font); - else - { - xps_free_font(ctx, font); - gs_throw(-1, "not an opentype font"); - return NULL; - } - - if (code < 0) - { - xps_free_font(ctx, font); - gs_rethrow(-1, "cannot init font"); - return NULL; - } - - xps_load_sfnt_cmap(font); - - return font; -} - -void -xps_free_font(xps_context_t *ctx, xps_font_t *font) -{ - if (font->font) - { - gs_font_finalize(font->font); - gs_free_object(ctx->memory, font->font, "font object"); - } - xps_free(ctx, font); -} - -/* - * Find the offset and length of an SFNT table. - * Return -1 if no table by the specified name is found. - */ - -int -xps_find_sfnt_table(xps_font_t *font, const char *name, int *lengthp) -{ - int offset; - int ntables; - int i; - - if (font->length < 12) - return -1; - - if (!memcmp(font->data, "ttcf", 4)) - { - int nfonts = u32(font->data + 8); - if (font->subfontid < 0 || font->subfontid >= nfonts) - { - gs_warn("Invalid subfont ID"); - return -1; - } - offset = u32(font->data + 12 + font->subfontid * 4); - } - else - { - offset = 0; - } - - ntables = u16(font->data + offset + 4); - if (font->length < offset + 12 + ntables * 16) - return -1; - - for (i = 0; i < ntables; i++) - { - byte *entry = font->data + offset + 12 + i * 16; - if (!memcmp(entry, name, 4)) - { - if (lengthp) - *lengthp = u32(entry + 12); - return u32(entry + 8); - } - } - - return -1; -} - -/* - * Get the windows truetype font file name - position 4 in the name table. - */ -void -xps_load_sfnt_name(xps_font_t *font, char *namep) -{ - byte *namedata; - int offset, length; - int format, count, stringoffset; - int i; - - strcpy(namep, "Unknown"); - - offset = xps_find_sfnt_table(font, "name", &length); - if (offset < 0 || length < 6) - { - gs_warn("cannot find name table"); - return; - } - - namedata = font->data + offset; - - format = u16(namedata + 0); - count = u16(namedata + 2); - stringoffset = u16(namedata + 4); - - for (i = 0; i < count; i++) - { - byte *record = namedata + 6 + i * 12; - int pid = u16(record + 0); - int eid = u16(record + 2); - int langid = u16(record + 4); - int nameid = u16(record + 6); - length = u16(record + 8); - offset = u16(record + 10); - - /* Mac Roman English */ - if (pid == 1 && eid == 0 && langid == 0) - { - /* Full font name or postscript name */ - if (nameid == 4 || nameid == 6) - { - memcpy(namep, namedata + stringoffset + offset, length); - namep[length] = 0; - } - } - } -} - -/* - * Locate the 'cmap' table and count the number of subtables. - */ - -static void -xps_load_sfnt_cmap(xps_font_t *font) -{ - byte *cmapdata; - int offset, length; - int nsubtables; - - offset = xps_find_sfnt_table(font, "cmap", &length); - if (offset < 0 || length < 4) - { - gs_warn("cannot find cmap table"); - return; - } - - cmapdata = font->data + offset; - - nsubtables = u16(cmapdata + 2); - if (nsubtables < 0 || length < 4 + nsubtables * 8) - { - gs_warn("cannot find cmap sub-tables"); - return; - } - - font->cmaptable = offset; - font->cmapsubcount = nsubtables; - font->cmapsubtable = 0; -} - -/* - * Return the number of cmap subtables. - */ +#include +#include FT_FREETYPE_H int -xps_count_font_encodings(xps_font_t *font) +xps_count_font_encodings(fz_font *font) { - return font->cmapsubcount; + FT_Face face = font->ftface; + return face->num_charmaps; } -/* - * Extract PlatformID and EncodingID for a cmap subtable. - */ - void -xps_identify_font_encoding(xps_font_t *font, int idx, int *pid, int *eid) +xps_identify_font_encoding(fz_font *font, int idx, int *pid, int *eid) { - byte *cmapdata, *entry; - if (idx < 0 || idx >= font->cmapsubcount) - return; - cmapdata = font->data + font->cmaptable; - entry = cmapdata + 4 + idx * 8; - *pid = u16(entry + 0); - *eid = u16(entry + 2); + FT_Face face = font->ftface; + *pid = face->charmaps[idx]->platform_id; + *eid = face->charmaps[idx]->encoding_id; } -/* - * Select a cmap subtable for use with encoding functions. - */ - void -xps_select_font_encoding(xps_font_t *font, int idx) +xps_select_font_encoding(fz_font *font, int idx) { - byte *cmapdata, *entry; - int pid, eid; - if (idx < 0 || idx >= font->cmapsubcount) - return; - cmapdata = font->data + font->cmaptable; - entry = cmapdata + 4 + idx * 8; - pid = u16(entry + 0); - eid = u16(entry + 2); - font->cmapsubtable = font->cmaptable + u32(entry + 4); - font->usepua = (pid == 3 && eid == 0); -} - -/* - * Encode a character using the selected cmap subtable. - * TODO: extend this to cover more cmap formats. - */ - -static int -xps_encode_font_char_imp(xps_font_t *font, int code) -{ - byte *table; - - /* no cmap selected: return identity */ - if (font->cmapsubtable <= 0) - return code; - - table = font->data + font->cmapsubtable; - - switch (u16(table)) - { - case 0: /* Apple standard 1-to-1 mapping. */ - return table[code + 6]; - - case 4: /* Microsoft/Adobe segmented mapping. */ - { - int segCount2 = u16(table + 6); - byte *endCount = table + 14; - byte *startCount = endCount + segCount2 + 2; - byte *idDelta = startCount + segCount2; - byte *idRangeOffset = idDelta + segCount2; - int i2; - - for (i2 = 0; i2 < segCount2 - 3; i2 += 2) - { - int delta, roff; - int start = u16(startCount + i2); - int glyph; - - if ( code < start ) - return 0; - if ( code > u16(endCount + i2) ) - continue; - delta = s16(idDelta + i2); - roff = s16(idRangeOffset + i2); - if ( roff == 0 ) - { - return ( code + delta ) & 0xffff; /* mod 65536 */ - return 0; - } - glyph = u16(idRangeOffset + i2 + roff + ((code - start) << 1)); - return (glyph == 0 ? 0 : glyph + delta); - } - - /* - * The TrueType documentation says that the last range is - * always supposed to end with 0xffff, so this shouldn't - * happen; however, in some real fonts, it does. - */ - return 0; - } - - case 6: /* Single interval lookup. */ - { - int firstCode = u16(table + 6); - int entryCount = u16(table + 8); - if ( code < firstCode || code >= firstCode + entryCount ) - return 0; - return u16(table + 10 + ((code - firstCode) << 1)); - } - - case 10: /* Trimmed array (like 6) */ - { - int startCharCode = u32(table + 12); - int numChars = u32(table + 16); - if ( code < startCharCode || code >= startCharCode + numChars ) - return 0; - return u32(table + 20 + (code - startCharCode) * 4); - } - - case 12: /* Segmented coverage. (like 4) */ - { - int nGroups = u32(table + 12); - byte *group = table + 16; - int i; - - for (i = 0; i < nGroups; i++) - { - int startCharCode = u32(group + 0); - int endCharCode = u32(group + 4); - int startGlyphID = u32(group + 8); - if ( code < startCharCode ) - return 0; - if ( code <= endCharCode ) - return startGlyphID + (code - startCharCode); - group += 12; - } - - return 0; - } - - case 2: /* High-byte mapping through table. */ - case 8: /* Mixed 16-bit and 32-bit coverage (like 2) */ - default: - gs_warn1("unknown cmap format: %d\n", u16(table)); - return 0; - } - - return 0; + FT_Face face = font->ftface; + FT_Set_Charmap(face, face->charmaps[idx]); } int -xps_encode_font_char(xps_font_t *font, int code) +xps_encode_font_char(fz_font *font, int code) { - int gid = xps_encode_font_char_imp(font, code); - if (gid == 0 && font->usepua) - gid = xps_encode_font_char_imp(font, 0xF000 | code); + FT_Face face = font->ftface; + int gid = FT_Get_Char_Index(face, code); + if (gid == 0 && face->charmap->platform_id == 3 && face->charmap->encoding_id == 0) + gid = FT_Get_Char_Index(face, 0xF000 | code); return gid; } -/* - * Get glyph metrics by parsing TTF tables manually. - * XPS needs more and different metrics than postscript/ghostscript - * use so the native ghostscript functions are not adequate. - */ - void -xps_measure_font_glyph(xps_context_t *ctx, xps_font_t *font, int gid, xps_glyph_metrics_t *mtx) +xps_measure_font_glyph(xps_context_t *ctx, fz_font *font, int gid, xps_glyph_metrics_t *mtx) { - int head, format, loca, glyf; - int ofs, len; - int idx, i, n; int hadv, vadv, vorg; - int vtop, ymax, desc; int scale; - /* some insane defaults */ - scale = 1000; /* units-per-em */ hadv = 500; vadv = -1000; vorg = 1000; - /* - * Horizontal metrics are easy. - */ - - ofs = xps_find_sfnt_table(font, "hhea", &len); - if (ofs < 0 || len < 2 * 18) - { - gs_warn("hhea table is too short"); - return; - } - - vorg = s16(font->data + ofs + 4); /* ascender is default vorg */ - desc = s16(font->data + ofs + 6); /* descender */ - if (desc < 0) - desc = -desc; - n = u16(font->data + ofs + 17 * 2); - - ofs = xps_find_sfnt_table(font, "hmtx", &len); - if (ofs < 0) - { - gs_warn("cannot find hmtx table"); - return; - } - - idx = gid; - if (idx > n - 1) - idx = n - 1; - - hadv = u16(font->data + ofs + idx * 4); - vadv = 0; - - /* - * Vertical metrics are hairy (with missing tables). - */ - - head = xps_find_sfnt_table(font, "head", &len); - if (head > 0) - { - scale = u16(font->data + head + 18); /* units per em */ - } - - ofs = xps_find_sfnt_table(font, "OS/2", &len); - if (ofs > 0 && len > 70) - { - vorg = s16(font->data + ofs + 68); /* sTypoAscender */ - desc = s16(font->data + ofs + 70); /* sTypoDescender */ - if (desc < 0) - desc = -desc; - } - - ofs = xps_find_sfnt_table(font, "vhea", &len); - if (ofs > 0 && len >= 2 * 18) - { - n = u16(font->data + ofs + 17 * 2); - - ofs = xps_find_sfnt_table(font, "vmtx", &len); - if (ofs < 0) - { - gs_warn("cannot find vmtx table"); - return; - } - - idx = gid; - if (idx > n - 1) - idx = n - 1; - - vadv = u16(font->data + ofs + idx * 4); - vtop = u16(font->data + ofs + idx * 4 + 2); - - glyf = xps_find_sfnt_table(font, "glyf", &len); - loca = xps_find_sfnt_table(font, "loca", &len); - if (head > 0 && glyf > 0 && loca > 0) - { - format = u16(font->data + head + 50); /* indexToLocaFormat */ - - if (format == 0) - ofs = u16(font->data + loca + gid * 2) * 2; - else - ofs = u32(font->data + loca + gid * 4); - - ymax = u16(font->data + glyf + ofs + 8); /* yMax */ - - vorg = ymax + vtop; - } - } - - ofs = xps_find_sfnt_table(font, "VORG", &len); - if (ofs > 0) - { - vorg = u16(font->data + ofs + 6); - n = u16(font->data + ofs + 6); - for (i = 0; i < n; i++) - { - if (u16(font->data + ofs + 8 + 4 * i) == gid) - { - vorg = s16(font->data + ofs + 8 + 4 * i + 2); - break; - } - } - } - - if (vadv == 0) - vadv = vorg + desc; - mtx->hadv = hadv / (float) scale; mtx->vadv = vadv / (float) scale; mtx->vorg = vorg / (float) scale; diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index 4709c94b..76da97b1 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -1,21 +1,7 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. +#include "fitz.h" +#include "muxps.h" - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - text drawing support */ - -#include "ghostxps.h" - -#include +#include /* for tolower() */ #define XPS_TEXT_BUFFER_SIZE 300 @@ -26,7 +12,7 @@ struct xps_text_buffer_s int count; float x[XPS_TEXT_BUFFER_SIZE + 1]; float y[XPS_TEXT_BUFFER_SIZE + 1]; - gs_glyph g[XPS_TEXT_BUFFER_SIZE]; + int g[XPS_TEXT_BUFFER_SIZE]; }; static inline int unhex(int i) @@ -36,45 +22,6 @@ static inline int unhex(int i) return tolower(i) - 'a' + 10; } -void -xps_debug_path(xps_context_t *ctx) -{ - segment *seg; - curve_segment *cseg; - - seg = (segment*)ctx->pgs->path->first_subpath; - while (seg) - { - switch (seg->type) - { - case s_start: - dprintf2("%g %g moveto\n", - fixed2float(seg->pt.x) * 0.001, - fixed2float(seg->pt.y) * 0.001); - break; - case s_line: - dprintf2("%g %g lineto\n", - fixed2float(seg->pt.x) * 0.001, - fixed2float(seg->pt.y) * 0.001); - break; - case s_line_close: - dputs("closepath\n"); - break; - case s_curve: - cseg = (curve_segment*)seg; - dprintf6("%g %g %g %g %g %g curveto\n", - fixed2float(cseg->p1.x) * 0.001, - fixed2float(cseg->p1.y) * 0.001, - fixed2float(cseg->p2.x) * 0.001, - fixed2float(cseg->p2.y) * 0.001, - fixed2float(seg->pt.x) * 0.001, - fixed2float(seg->pt.y) * 0.001); - break; - } - seg = seg->next; - } -} - /* * Some fonts in XPS are obfuscated by XOR:ing the first 32 bytes of the * data with the GUID in the fontname. @@ -100,7 +47,7 @@ xps_deobfuscate_font_resource(xps_context_t *ctx, xps_part_t *part) if (i != 32) { - gs_warn("cannot extract GUID from obfuscated font part name"); + fz_warn("cannot extract GUID from obfuscated font part name"); return; } @@ -115,7 +62,7 @@ xps_deobfuscate_font_resource(xps_context_t *ctx, xps_part_t *part) } static void -xps_select_best_font_encoding(xps_font_t *font) +xps_select_best_font_encoding(fz_font *font) { static struct { int pid, eid; } xps_cmap_list[] = { @@ -147,7 +94,7 @@ xps_select_best_font_encoding(xps_font_t *font) } } - gs_warn("could not find a suitable cmap"); + fz_warn("could not find a suitable cmap"); } /* @@ -155,9 +102,10 @@ xps_select_best_font_encoding(xps_font_t *font) */ static int -xps_flush_text_buffer(xps_context_t *ctx, xps_font_t *font, +xps_flush_text_buffer(xps_context_t *ctx, fz_font *font, xps_text_buffer_t *buf, int is_charpath) { +#if 0 gs_text_params_t params; gs_text_enum_t *textenum; float x = buf->x[0]; @@ -166,7 +114,6 @@ xps_flush_text_buffer(xps_context_t *ctx, xps_font_t *font, int i; // dprintf1("flushing text buffer (%d glyphs)\n", buf->count); - gs_moveto(ctx->pgs, x, y); params.operation = TEXT_FROM_GLYPHS | TEXT_REPLACE_WIDTHS; @@ -192,15 +139,15 @@ xps_flush_text_buffer(xps_context_t *ctx, xps_font_t *font, code = gs_text_begin(ctx->pgs, ¶ms, ctx->memory, &textenum); if (code != 0) - return gs_throw1(-1, "cannot gs_text_begin() (%d)", code); + return fz_throw("cannot gs_text_begin() (%d)", code); code = gs_text_process(textenum); if (code != 0) - return gs_throw1(-1, "cannot gs_text_process() (%d)", code); + return fz_throw("cannot gs_text_process() (%d)", code); gs_text_release(textenum, "gslt font render"); - +#endif buf->count = 0; return 0; @@ -293,7 +240,7 @@ xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs) * Calculate metrics for positioning. */ static int -xps_parse_glyphs_imp(xps_context_t *ctx, xps_font_t *font, float size, +xps_parse_glyphs_imp(xps_context_t *ctx, fz_font *font, float size, float originx, float originy, int is_sideways, int bidi_level, char *indices, char *unicode, int is_charpath) { @@ -309,7 +256,7 @@ xps_parse_glyphs_imp(xps_context_t *ctx, xps_font_t *font, float size, buf.count = 0; if (!unicode && !indices) - return gs_throw(-1, "no text in glyphs element"); + return fz_throw("no text in glyphs element"); if (us) { @@ -354,7 +301,7 @@ xps_parse_glyphs_imp(xps_context_t *ctx, xps_font_t *font, float size, { int t = xps_utf8_to_ucs(&char_code, us, un); if (t < 0) - return gs_rethrow(-1, "error decoding UTF-8 string"); + return fz_rethrow(-1, "error decoding UTF-8 string"); us += t; un -= t; } code_count --; @@ -379,7 +326,7 @@ xps_parse_glyphs_imp(xps_context_t *ctx, xps_font_t *font, float size, } #if 0 - dprintf6("glyph mapping (%d:%d)%d,%g,%g,%g\n", + printf("glyph mapping (%d:%d)%d,%g,%g,%g\n", code_count, glyph_count, glyph_index, advance, u_offset, v_offset); #endif @@ -394,7 +341,7 @@ xps_parse_glyphs_imp(xps_context_t *ctx, xps_font_t *font, float size, { code = xps_flush_text_buffer(ctx, font, &buf, is_charpath); if (code) - return gs_rethrow(code, "cannot flush buffered text"); + return fz_rethrow(code, "cannot flush buffered text"); } if (is_sideways) @@ -418,14 +365,14 @@ xps_parse_glyphs_imp(xps_context_t *ctx, xps_font_t *font, float size, { code = xps_flush_text_buffer(ctx, font, &buf, is_charpath); if (code) - return gs_rethrow(code, "cannot flush buffered text"); + return fz_rethrow(code, "cannot flush buffered text"); } return 0; } int -xps_parse_glyphs(xps_context_t *ctx, +xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) { xps_item_t *node; @@ -458,12 +405,12 @@ xps_parse_glyphs(xps_context_t *ctx, char *fill_opacity_att = NULL; xps_part_t *part; - xps_font_t *font; + fz_font *font; char partname[1024]; char *subfont; - gs_matrix matrix; + fz_matrix matrix; float font_size = 10.0; int subfontid = 0; int is_sideways = 0; @@ -517,7 +464,7 @@ xps_parse_glyphs(xps_context_t *ctx, */ if (!font_size_att || !font_uri_att || !origin_x_att || !origin_y_att) - return gs_throw(-1, "missing attributes in glyphs element"); + return fz_throw("missing attributes in glyphs element"); if (!indices_att && !unicode_att) return 0; /* nothing to draw */ @@ -545,7 +492,7 @@ xps_parse_glyphs(xps_context_t *ctx, { part = xps_read_part(ctx, partname); if (!part) - return gs_throw1(-1, "cannot find font resource part '%s'", partname); + return fz_throw("cannot find font resource part '%s'", partname); /* deobfuscate if necessary */ if (strstr(part->name, ".odttf")) @@ -553,9 +500,9 @@ xps_parse_glyphs(xps_context_t *ctx, if (strstr(part->name, ".ODTTF")) xps_deobfuscate_font_resource(ctx, part); - font = xps_new_font(ctx, part->data, part->size, subfontid); - if (!font) - return gs_rethrow1(-1, "cannot load font resource '%s'", partname); + code = fz_newfontfrombuffer(&font, part->data, part->size, subfontid); + if (code) + return fz_rethrow(code, "cannot load font resource '%s'", partname); xps_select_best_font_encoding(font); @@ -568,12 +515,12 @@ xps_parse_glyphs(xps_context_t *ctx, /* * Set up graphics state. */ - +#if 0 gs_gsave(ctx->pgs); if (transform_att || transform_tag) { - gs_matrix transform; + fz_matrix transform; if (transform_att) xps_parse_render_transform(ctx, transform_att, &transform); @@ -585,6 +532,7 @@ xps_parse_glyphs(xps_context_t *ctx, if (clip_att || clip_tag) { + ctx->path = fz_newpath(); if (clip_att) xps_parse_abbreviated_geometry(ctx, clip_att); if (clip_tag) @@ -597,17 +545,17 @@ xps_parse_glyphs(xps_context_t *ctx, gs_setfont(ctx->pgs, font->font); gs_make_scaling(font_size, -font_size, &matrix); if (is_sideways) - gs_matrix_rotate(&matrix, 90.0, &matrix); + fz_matrix_rotate(&matrix, 90.0, &matrix); gs_setcharmatrix(ctx->pgs, &matrix); - gs_matrix_multiply(&matrix, &font->font->orig_FontMatrix, &font->font->FontMatrix); + fz_matrix_multiply(&matrix, &font->font->orig_FontMatrix, &font->font->FontMatrix); code = xps_begin_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); if (code) { gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot create transparency group"); + return fz_rethrow(code, "cannot create transparency group"); } /* @@ -624,7 +572,7 @@ xps_parse_glyphs(xps_context_t *ctx, if (fill_att) { float samples[32]; - gs_color_space *colorspace; + fz_colorspace *colorspace; xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples); if (fill_opacity_att) samples[0] = atof(fill_opacity_att); @@ -637,7 +585,7 @@ xps_parse_glyphs(xps_context_t *ctx, { xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot parse glyphs data"); + return fz_rethrow(code, "cannot parse glyphs data"); } } @@ -655,7 +603,7 @@ xps_parse_glyphs(xps_context_t *ctx, { xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot parse glyphs data"); + return fz_rethrow(code, "cannot parse glyphs data"); } code = xps_parse_brush(ctx, fill_uri, dict, fill_tag); @@ -663,13 +611,13 @@ xps_parse_glyphs(xps_context_t *ctx, { xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot parse fill brush"); + return fz_rethrow(code, "cannot parse fill brush"); } } xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); gs_grestore(ctx->pgs); - +#endif return 0; } diff --git a/xps/xpsgradient.c b/xps/xpsgradient.c index 0c6bcf60..3bb95824 100644 --- a/xps/xpsgradient.c +++ b/xps/xpsgradient.c @@ -1,19 +1,5 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - gradient support */ - -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" #define MAX_STOPS 256 @@ -52,11 +38,9 @@ static int xps_parse_gradient_stops(xps_context_t *ctx, char *base_uri, xps_item_t *node, struct stop *stops, int maxcount) { - unsigned short sample_in[8], sample_out[8]; /* XPS allows up to 8 bands */ - gsicc_rendering_param_t rendering_params; - gsicc_link_t *icclink; - gs_color_space *colorspace; + fz_colorspace *colorspace; float sample[8]; + float rgb[3]; int before, after; int count; int i, k; @@ -77,53 +61,22 @@ xps_parse_gradient_stops(xps_context_t *ctx, char *base_uri, xps_item_t *node, xps_parse_color(ctx, base_uri, color, &colorspace, sample); - /* Set the rendering parameters */ - rendering_params.black_point_comp = BP_ON; - rendering_params.object_type = GS_PATH_TAG; - rendering_params.rendering_intent = gsPERCEPTUAL; - - /* Get link to map from source to sRGB */ - icclink = gsicc_get_link((gs_imager_state*) ctx->pgs, - NULL, colorspace, ctx->srgb, - &rendering_params, ctx->memory, false); - - if (icclink != NULL && !icclink->is_identity) - { - /* Transform the color */ - int num_colors = gsicc_getsrc_channel_count(colorspace->cmm_icc_profile_data); - for (i = 0; i < num_colors; i++) - { - sample_in[i] = sample[i+1]*65535; - } - gscms_transform_color(icclink, sample_in, sample_out, 2, NULL); - - stops[count].color[0] = sample[0]; /* Alpha */ - stops[count].color[1] = (float) sample_out[0] / 65535.0; /* sRGB */ - stops[count].color[2] = (float) sample_out[1] / 65535.0; - stops[count].color[3] = (float) sample_out[2] / 65535.0; - } - else - { - stops[count].color[0] = sample[0]; - stops[count].color[1] = sample[1]; - stops[count].color[2] = sample[2]; - stops[count].color[3] = sample[3]; - } + fz_convertcolor(colorspace, sample + 1, fz_devicergb, rgb); + + stops[count].color[0] = sample[0]; + stops[count].color[1] = rgb[0]; + stops[count].color[2] = rgb[1]; + stops[count].color[3] = rgb[2]; count ++; } } - - if (icclink != NULL) - gsicc_release_link(icclink); - icclink = NULL; node = xps_next(node); - } if (count == 0) { - gs_warn("gradient brush has no gradient stops"); + fz_warn("gradient brush has no gradient stops"); stops[0].offset = 0; stops[0].color[0] = 1; stops[0].color[1] = 0; @@ -138,7 +91,7 @@ xps_parse_gradient_stops(xps_context_t *ctx, char *base_uri, xps_item_t *node, } if (count == maxcount) - gs_warn("gradient brush exceeded maximum number of gradient stops"); + fz_warn("gradient brush exceeded maximum number of gradient stops"); /* Postprocess to make sure the range of offsets is 0.0 to 1.0 */ @@ -226,219 +179,6 @@ xps_gradient_has_transparent_colors(struct stop *stops, int count) return 0; } -/* - * Create a Function object to map [0..1] to RGB colors - * based on the gradient stop arrays. - * - * We do this by creating a stitching function that joins - * a series of linear functions (one linear function - * for each gradient stop-pair). - */ - -static gs_function_t * -xps_create_gradient_stop_function(xps_context_t *ctx, struct stop *stops, int count, int opacity_only) -{ - gs_function_1ItSg_params_t sparams; - gs_function_ElIn_params_t lparams; - gs_function_t *sfunc; - gs_function_t *lfunc; - - float *domain, *range, *c0, *c1, *bounds, *encode; - const gs_function_t **functions; - - int code; - int k; - int i; - - k = count - 1; /* number of intervals / functions */ - - domain = xps_alloc(ctx, 2 * sizeof(float)); - domain[0] = 0.0; - domain[1] = 1.0; - sparams.m = 1; - sparams.Domain = domain; - - range = xps_alloc(ctx, 6 * sizeof(float)); - range[0] = 0.0; - range[1] = 1.0; - range[2] = 0.0; - range[3] = 1.0; - range[4] = 0.0; - range[5] = 1.0; - sparams.n = 3; - sparams.Range = range; - - functions = xps_alloc(ctx, k * sizeof(void*)); - bounds = xps_alloc(ctx, (k - 1) * sizeof(float)); - encode = xps_alloc(ctx, (k * 2) * sizeof(float)); - - sparams.k = k; - sparams.Functions = functions; - sparams.Bounds = bounds; - sparams.Encode = encode; - - for (i = 0; i < k; i++) - { - domain = xps_alloc(ctx, 2 * sizeof(float)); - domain[0] = 0.0; - domain[1] = 1.0; - lparams.m = 1; - lparams.Domain = domain; - - range = xps_alloc(ctx, 6 * sizeof(float)); - range[0] = 0.0; - range[1] = 1.0; - range[2] = 0.0; - range[3] = 1.0; - range[4] = 0.0; - range[5] = 1.0; - lparams.n = 3; - lparams.Range = range; - - c0 = xps_alloc(ctx, 3 * sizeof(float)); - lparams.C0 = c0; - - c1 = xps_alloc(ctx, 3 * sizeof(float)); - lparams.C1 = c1; - - if (opacity_only) - { - c0[0] = stops[i].color[0]; - c0[1] = stops[i].color[0]; - c0[2] = stops[i].color[0]; - - c1[0] = stops[i+1].color[0]; - c1[1] = stops[i+1].color[0]; - c1[2] = stops[i+1].color[0]; - } - else - { - c0[0] = stops[i].color[1]; - c0[1] = stops[i].color[2]; - c0[2] = stops[i].color[3]; - - c1[0] = stops[i+1].color[1]; - c1[1] = stops[i+1].color[2]; - c1[2] = stops[i+1].color[3]; - } - - lparams.N = 1; - - code = gs_function_ElIn_init(&lfunc, &lparams, ctx->memory); - if (code < 0) - { - gs_rethrow(code, "gs_function_ElIn_init failed"); - return NULL; - } - - functions[i] = lfunc; - - if (i > 0) - bounds[i - 1] = stops[i].offset; - - encode[i * 2 + 0] = 0.0; - encode[i * 2 + 1] = 1.0; - } - - code = gs_function_1ItSg_init(&sfunc, &sparams, ctx->memory); - if (code < 0) - { - gs_rethrow(code, "gs_function_1ItSg_init failed"); - return NULL; - } - - return sfunc; -} - -/* - * Shadings and functions are ghostscript type objects, - * and as such rely on the garbage collector for cleanup. - * We can't have none of that here, so we have to - * write our own destructors. - */ - -static void -xps_free_gradient_stop_function(xps_context_t *ctx, gs_function_t *func) -{ - gs_function_t *lfunc; - gs_function_1ItSg_params_t *sparams; - gs_function_ElIn_params_t *lparams; - int i; - - sparams = (gs_function_1ItSg_params_t*) &func->params; - xps_free(ctx, (void*)sparams->Domain); - xps_free(ctx, (void*)sparams->Range); - - for (i = 0; i < sparams->k; i++) - { - lfunc = (gs_function_t*) sparams->Functions[i]; /* discard const */ - lparams = (gs_function_ElIn_params_t*) &lfunc->params; - xps_free(ctx, (void*)lparams->Domain); - xps_free(ctx, (void*)lparams->Range); - xps_free(ctx, (void*)lparams->C0); - xps_free(ctx, (void*)lparams->C1); - xps_free(ctx, lfunc); - } - - xps_free(ctx, (void*)sparams->Bounds); - xps_free(ctx, (void*)sparams->Encode); - xps_free(ctx, (void*)sparams->Functions); - xps_free(ctx, func); -} - -/* - * For radial gradients that have a cone drawing we have to - * reverse the direction of the gradient because we draw - * the shading in the opposite direction with the - * big circle first. - */ -static gs_function_t * -xps_reverse_function(xps_context_t *ctx, gs_function_t *func, float *fary, void *vary) -{ - gs_function_1ItSg_params_t sparams; - gs_function_t *sfunc; - int code; - - /* take from stack allocated arrays that the caller provides */ - float *domain = fary + 0; - float *range = fary + 2; - float *encode = fary + 2 + 6; - const gs_function_t **functions = vary; - - domain[0] = 0.0; - domain[1] = 1.0; - - range[0] = 0.0; - range[1] = 1.0; - range[2] = 0.0; - range[3] = 1.0; - range[4] = 0.0; - range[5] = 1.0; - - functions[0] = func; - - encode[0] = 1.0; - encode[1] = 0.0; - - sparams.m = 1; - sparams.Domain = domain; - sparams.n = 3; - sparams.Range = range; - sparams.k = 1; - sparams.Functions = functions; - sparams.Bounds = NULL; - sparams.Encode = encode; - - code = gs_function_1ItSg_init(&sfunc, &sparams, ctx->memory); - if (code < 0) - { - gs_rethrow(code, "gs_function_1ItSg_init failed"); - return NULL; - } - - return sfunc; -} - /* * Radial gradients map more or less to Radial shadings. * The inner circle is always a point. @@ -448,10 +188,11 @@ xps_reverse_function(xps_context_t *ctx, gs_function_t *func, float *fary, void static int xps_draw_one_radial_gradient(xps_context_t *ctx, - gs_function_t *func, int extend, + int extend, float x0, float y0, float r0, float x1, float y1, float r1) { +#if 0 gs_memory_t *mem = ctx->memory; gs_shading_t *shading; gs_shading_R_params_t params; @@ -476,7 +217,7 @@ xps_draw_one_radial_gradient(xps_context_t *ctx, code = gs_shading_R_init(&shading, ¶ms, mem); if (code < 0) - return gs_rethrow(code, "gs_shading_R_init failed"); + return fz_rethrow(code, "gs_shading_R_init failed"); gs_setsmoothness(ctx->pgs, 0.02); @@ -484,11 +225,11 @@ xps_draw_one_radial_gradient(xps_context_t *ctx, if (code < 0) { gs_free_object(mem, shading, "gs_shading_R"); - return gs_rethrow(code, "gs_shfill failed"); + return fz_rethrow(code, "gs_shfill failed"); } gs_free_object(mem, shading, "gs_shading_R"); - +#endif return 0; } @@ -498,9 +239,10 @@ xps_draw_one_radial_gradient(xps_context_t *ctx, static int xps_draw_one_linear_gradient(xps_context_t *ctx, - gs_function_t *func, int extend, + int extend, float x0, float y0, float x1, float y1) { +#if 0 gs_memory_t *mem = ctx->memory; gs_shading_t *shading; gs_shading_A_params_t params; @@ -523,7 +265,7 @@ xps_draw_one_linear_gradient(xps_context_t *ctx, code = gs_shading_A_init(&shading, ¶ms, mem); if (code < 0) - return gs_rethrow(code, "gs_shading_A_init failed"); + return fz_rethrow(code, "gs_shading_A_init failed"); gs_setsmoothness(ctx->pgs, 0.02); @@ -531,11 +273,11 @@ xps_draw_one_linear_gradient(xps_context_t *ctx, if (code < 0) { gs_free_object(mem, shading, "gs_shading_A"); - return gs_rethrow(code, "gs_shfill failed"); + return fz_rethrow(code, "gs_shfill failed"); } gs_free_object(mem, shading, "gs_shading_A"); - +#endif return 0; } @@ -555,9 +297,9 @@ static inline float point_inside_circle(float px, float py, float x, float y, fl } static int -xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_function_t *func) +xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread) { - gs_rect bbox; + fz_rect bbox; float x0, y0, r0; float x1, y1, r1; float xrad = 1; @@ -583,8 +325,8 @@ xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_fu yrad = atof(radius_y_att); /* scale the ctm to make ellipses */ - gs_gsave(ctx->pgs); - gs_scale(ctx->pgs, 1.0, yrad / xrad); +// gs_gsave(ctx->pgs); +// gs_scale(ctx->pgs, 1.0, yrad / xrad); invscale = xrad / yrad; y0 = y0 * invscale; @@ -602,7 +344,7 @@ xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_fu { if (!point_inside_circle(x0, y0, x1, y1, r1)) { - gs_function_t *reverse; +#if 0 float in[1]; float out[4]; float fary[10]; @@ -612,7 +354,6 @@ xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_fu * gradients when the radial shading is a cone. In this case * we fill the background ourselves. */ - in[0] = 1.0; out[0] = 1.0; out[1] = 0.0; @@ -637,30 +378,25 @@ xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_fu * have to reverse the direction of the function to compensate. */ - reverse = xps_reverse_function(ctx, func, fary, vary); - if (!reverse) - { - gs_grestore(ctx->pgs); - return gs_rethrow(-1, "could not create the reversed function"); - } +// reverse = xps_reverse_function(ctx, func, fary, vary); code = xps_draw_one_radial_gradient(ctx, reverse, 1, x1, y1, r1, x0, y0, r0); if (code < 0) { xps_free(ctx, reverse); gs_grestore(ctx->pgs); - return gs_rethrow(code, "could not draw radial gradient"); + return fz_rethrow(code, "could not draw radial gradient"); } - xps_free(ctx, reverse); +#endif } else { - code = xps_draw_one_radial_gradient(ctx, func, 1, x0, y0, r0, x1, y1, r1); + code = xps_draw_one_radial_gradient(ctx, 1, x0, y0, r0, x1, y1, r1); if (code < 0) { - gs_grestore(ctx->pgs); - return gs_rethrow(code, "could not draw radial gradient"); +// gs_grestore(ctx->pgs); + return fz_rethrow(code, "could not draw radial gradient"); } } } @@ -671,25 +407,25 @@ xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_fu /* Draw current circle */ if (!point_inside_circle(x0, y0, x1, y1, r1)) - dputs("xps: we should reverse gradient here too\n"); + printf("xps: we should reverse gradient here too\n"); if (spread == SPREAD_REFLECT && (i & 1)) - code = xps_draw_one_radial_gradient(ctx, func, 0, x1, y1, r1, x0, y0, r0); + code = xps_draw_one_radial_gradient(ctx, 0, x1, y1, r1, x0, y0, r0); else - code = xps_draw_one_radial_gradient(ctx, func, 0, x0, y0, r0, x1, y1, r1); + code = xps_draw_one_radial_gradient(ctx, 0, x0, y0, r0, x1, y1, r1); if (code < 0) { - gs_grestore(ctx->pgs); - return gs_rethrow(code, "could not draw axial gradient"); +// gs_grestore(ctx->pgs); + return fz_rethrow(code, "could not draw axial gradient"); } /* Check if circle encompassed the entire bounding box (break loop if we do) */ done = 1; - if (!point_inside_circle(bbox.p.x, bbox.p.y, x1, y1, r1)) done = 0; - if (!point_inside_circle(bbox.p.x, bbox.q.y, x1, y1, r1)) done = 0; - if (!point_inside_circle(bbox.q.x, bbox.q.y, x1, y1, r1)) done = 0; - if (!point_inside_circle(bbox.q.x, bbox.p.y, x1, y1, r1)) done = 0; + if (!point_inside_circle(bbox.x0, bbox.y0, x1, y1, r1)) done = 0; + if (!point_inside_circle(bbox.x0, bbox.y1, x1, y1, r1)) done = 0; + if (!point_inside_circle(bbox.x1, bbox.y1, x1, y1, r1)) done = 0; + if (!point_inside_circle(bbox.x1, bbox.y0, x1, y1, r1)) done = 0; if (done) break; @@ -705,7 +441,7 @@ xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_fu } } - gs_grestore(ctx->pgs); +// gs_grestore(ctx->pgs); return 0; } @@ -716,9 +452,9 @@ xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_fu */ static int -xps_draw_linear_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_function_t *func) +xps_draw_linear_gradient(xps_context_t *ctx, xps_item_t *root, int spread) { - gs_rect bbox; + fz_rect bbox; float x0, y0, x1, y1; float dx, dy; int code; @@ -744,9 +480,9 @@ xps_draw_linear_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_fu if (spread == SPREAD_PAD) { - code = xps_draw_one_linear_gradient(ctx, func, 1, x0, y0, x1, y1); + code = xps_draw_one_linear_gradient(ctx, 1, x0, y0, x1, y1); if (code < 0) - return gs_rethrow(code, "could not draw axial gradient"); + return fz_rethrow(code, "could not draw axial gradient"); } else { @@ -760,10 +496,10 @@ xps_draw_linear_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_fu a = dx / len; b = dy / len; - dist[0] = a * (bbox.p.x - x0) + b * (bbox.p.y - y0); - dist[1] = a * (bbox.p.x - x0) + b * (bbox.q.y - y0); - dist[2] = a * (bbox.q.x - x0) + b * (bbox.q.y - y0); - dist[3] = a * (bbox.q.x - x0) + b * (bbox.p.y - y0); + dist[0] = a * (bbox.x0 - x0) + b * (bbox.y0 - y0); + dist[1] = a * (bbox.x0 - x0) + b * (bbox.y1 - y0); + dist[2] = a * (bbox.x1 - x0) + b * (bbox.y1 - y0); + dist[3] = a * (bbox.x1 - x0) + b * (bbox.y0 - y0); d0 = dist[0]; d1 = dist[0]; @@ -780,18 +516,18 @@ xps_draw_linear_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_fu { if (spread == SPREAD_REFLECT && (i & 1)) { - code = xps_draw_one_linear_gradient(ctx, func, 0, + code = xps_draw_one_linear_gradient(ctx, 0, x1 + dx * i, y1 + dy * i, x0 + dx * i, y0 + dy * i); } else { - code = xps_draw_one_linear_gradient(ctx, func, 0, + code = xps_draw_one_linear_gradient(ctx, 0, x0 + dx * i, y0 + dy * i, x1 + dx * i, y1 + dy * i); } if (code < 0) - return gs_rethrow(code, "could not draw axial gradient"); + return fz_rethrow(code, "could not draw axial gradient"); } } @@ -804,8 +540,9 @@ xps_draw_linear_gradient(xps_context_t *ctx, xps_item_t *root, int spread, gs_fu */ static int -xps_parse_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root, - int (*draw)(xps_context_t *, xps_item_t *, int, gs_function_t *)) +xps_parse_gradient_brush(xps_context_t *ctx, fz_matrix ctm, + char *base_uri, xps_resource_t *dict, xps_item_t *root, + int (*draw)(xps_context_t *, xps_item_t *, int)) { xps_item_t *node; @@ -820,14 +557,11 @@ xps_parse_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dic struct stop stop_list[MAX_STOPS]; int stop_count; - gs_matrix transform; + fz_matrix transform; int spread_method; - int code; - gs_rect bbox; + fz_rect bbox; - gs_function_t *color_func; - gs_function_t *opacity_func; int has_opacity = 0; opacity_att = xps_att(root, "Opacity"); @@ -861,41 +595,42 @@ xps_parse_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dic spread_method = SPREAD_REPEAT; } - gs_make_identity(&transform); + xps_clip(ctx, ctm); + + transform = fz_identity; if (transform_att) xps_parse_render_transform(ctx, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); + ctm = fz_concat(ctm, transform); if (!stop_tag) - return gs_throw(-1, "missing gradient stops tag"); + return fz_throw("missing gradient stops tag"); stop_count = xps_parse_gradient_stops(ctx, base_uri, stop_tag, stop_list, MAX_STOPS); if (stop_count == 0) - return gs_throw(-1, "no gradient stops found"); + return fz_throw("no gradient stops found"); +/* color_func = xps_create_gradient_stop_function(ctx, stop_list, stop_count, 0); if (!color_func) - return gs_rethrow(-1, "could not create color gradient function"); + return fz_rethrow(-1, "could not create color gradient function"); opacity_func = xps_create_gradient_stop_function(ctx, stop_list, stop_count, 1); if (!opacity_func) - return gs_rethrow(-1, "could not create opacity gradient function"); + return fz_rethrow(-1, "could not create opacity gradient function"); +*/ has_opacity = xps_gradient_has_transparent_colors(stop_list, stop_count); - xps_clip(ctx); - - gs_gsave(ctx->pgs); - gs_concat(ctx->pgs, &transform); - xps_bounds_in_user_space(ctx, &bbox); +#if 0 code = xps_begin_opacity(ctx, base_uri, dict, opacity_att, NULL); if (code) { gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot create transparency group"); + return fz_rethrow(code, "cannot create transparency group"); } if (ctx->opacity_only) @@ -904,7 +639,7 @@ xps_parse_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dic if (code) { gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot draw gradient opacity"); + return fz_rethrow(code, "cannot draw gradient opacity"); } } else @@ -921,7 +656,7 @@ xps_parse_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dic { gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot draw gradient opacity"); + return fz_rethrow(code, "cannot draw gradient opacity"); } gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); @@ -932,7 +667,7 @@ xps_parse_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dic { gs_end_transparency_group(ctx->pgs); gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot draw gradient color"); + return fz_rethrow(code, "cannot draw gradient color"); } gs_end_transparency_group(ctx->pgs); } @@ -942,37 +677,40 @@ xps_parse_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dic if (code) { gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot draw gradient color"); + return fz_rethrow(code, "cannot draw gradient color"); } } } +#endif xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); - gs_grestore(ctx->pgs); +// gs_grestore(ctx->pgs); - xps_free_gradient_stop_function(ctx, opacity_func); - xps_free_gradient_stop_function(ctx, color_func); +// xps_free_gradient_stop_function(ctx, opacity_func); +// xps_free_gradient_stop_function(ctx, color_func); return 0; } int -xps_parse_linear_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root) +xps_parse_linear_gradient_brush(xps_context_t *ctx, fz_matrix ctm, + char *base_uri, xps_resource_t *dict, xps_item_t *root) { int code; - code = xps_parse_gradient_brush(ctx, base_uri, dict, root, xps_draw_linear_gradient); + code = xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_linear_gradient); if (code < 0) - return gs_rethrow(code, "cannot parse linear gradient brush"); - return gs_okay; + return fz_rethrow(code, "cannot parse linear gradient brush"); + return fz_okay; } int -xps_parse_radial_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root) +xps_parse_radial_gradient_brush(xps_context_t *ctx, fz_matrix ctm, + char *base_uri, xps_resource_t *dict, xps_item_t *root) { int code; - code = xps_parse_gradient_brush(ctx, base_uri, dict, root, xps_draw_radial_gradient); + code = xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_radial_gradient); if (code < 0) - return gs_rethrow(code, "cannot parse radial gradient brush"); - return gs_okay; + return fz_rethrow(code, "cannot parse radial gradient brush"); + return fz_okay; } diff --git a/xps/xpshash.c b/xps/xpshash.c index 459566c9..1e562ff3 100644 --- a/xps/xpshash.c +++ b/xps/xpshash.c @@ -1,16 +1,3 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - /* Linear probe hash table. * * Simple hashtable with open adressing linear probe. @@ -18,7 +5,8 @@ * Does not support deleting entries. */ -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" static const unsigned primes[] = { @@ -67,7 +55,7 @@ xps_hash_new(xps_context_t *ctx) table = xps_alloc(ctx, sizeof(xps_hash_table_t)); if (!table) { - gs_throw(-1, "out of memory: hash table struct"); + fz_throw("out of memory: hash table struct"); return NULL; } @@ -78,7 +66,7 @@ xps_hash_new(xps_context_t *ctx) if (!table->entries) { xps_free(ctx, table); - gs_throw(-1, "out of memory: hash table entries array"); + fz_throw("out of memory: hash table entries array"); return NULL; } @@ -108,7 +96,7 @@ xps_hash_double(xps_context_t *ctx, xps_hash_table_t *table) old_entries = table->entries; new_entries = xps_alloc(ctx, sizeof(xps_hash_entry_t) * new_size); if (!new_entries) - return gs_throw(-1, "out of memory: hash table entries array"); + return fz_throw("out of memory: hash table entries array"); table->size = new_size; table->entries = new_entries; @@ -173,7 +161,7 @@ xps_hash_insert(xps_context_t *ctx, xps_hash_table_t *table, char *key, void *va if (table->load > table->size * 8 / 10) { if (xps_hash_double(ctx, table) < 0) - return gs_rethrow(-1, "cannot grow hash table"); + return fz_rethrow(-1, "cannot grow hash table"); } entries = table->entries; diff --git a/xps/xpsimage.c b/xps/xpsimage.c index 7a69ba09..15257594 100644 --- a/xps/xpsimage.c +++ b/xps/xpsimage.c @@ -1,274 +1,68 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - image support */ - -/* TODO: we should be smarter here and do incremental decoding - * and rendering instead of uncompressing the whole image to - * memory before drawing. - */ - -#include "ghostxps.h" - -/* - * Un-interleave the alpha channel. - */ - -static void -xps_isolate_alpha_channel_8(xps_context_t *ctx, xps_image_t *image) -{ - int n = image->comps; - int y, x, k; - byte *sp, *dp, *ap; - - image->alpha = xps_alloc(ctx, image->width * image->height); - - for (y = 0; y < image->height; y++) - { - sp = image->samples + image->width * n * y; - dp = image->samples + image->width * (n - 1) * y; - ap = image->alpha + image->width * y; - for (x = 0; x < image->width; x++) - { - for (k = 0; k < n - 1; k++) - *dp++ = *sp++; - *ap++ = *sp++; - } - } - - image->hasalpha = 0; - image->comps --; - image->stride = image->width * image->comps; -} - -static void -xps_isolate_alpha_channel_16(xps_context_t *ctx, xps_image_t *image) -{ - int n = image->comps; - int y, x, k; - unsigned short *sp, *dp, *ap; - - image->alpha = xps_alloc(ctx, image->width * image->height * 2); - - for (y = 0; y < image->height; y++) - { - sp = ((unsigned short*)image->samples) + (image->width * n * y); - dp = ((unsigned short*)image->samples) + (image->width * (n - 1) * y); - ap = ((unsigned short*)image->alpha) + (image->width * y); - for (x = 0; x < image->width; x++) - { - for (k = 0; k < n - 1; k++) - *dp++ = *sp++; - *ap++ = *sp++; - } - } - - image->hasalpha = 0; - image->comps --; - image->stride = image->width * image->comps * 2; -} - -static int -xps_image_has_alpha(xps_context_t *ctx, xps_part_t *part) -{ - byte *buf = part->data; - int len = part->size; - - if (len < 8) - { - gs_warn("unknown image file format"); - return 0; - } - - if (buf[0] == 0xff && buf[1] == 0xd8) - return 0; /* JPEG never has an alpha channel */ - else if (memcmp(buf, "\211PNG\r\n\032\n", 8) == 0) - return xps_png_has_alpha(ctx, buf, len); - else if (memcmp(buf, "II", 2) == 0 && buf[2] == 0xBC) - return xps_jpegxr_has_alpha(ctx, buf, len); - else if (memcmp(buf, "MM", 2) == 0) - return xps_tiff_has_alpha(ctx, buf, len); - else if (memcmp(buf, "II", 2) == 0) - return xps_tiff_has_alpha(ctx, buf, len); - - return 0; -} +#include "fitz.h" +#include "muxps.h" static int xps_decode_image(xps_context_t *ctx, xps_part_t *part, xps_image_t *image) { byte *buf = part->data; int len = part->size; - cmm_profile_t *profile; int error; if (len < 8) - return gs_throw(-1, "unknown image file format"); + return fz_throw("unknown image file format"); memset(image, 0, sizeof(xps_image_t)); - image->samples = NULL; - image->alpha = NULL; if (buf[0] == 0xff && buf[1] == 0xd8) { error = xps_decode_jpeg(ctx, buf, len, image); if (error) - return gs_rethrow(error, "could not decode jpeg image"); + return fz_rethrow(error, "could not decode image"); } else if (memcmp(buf, "\211PNG\r\n\032\n", 8) == 0) { error = xps_decode_png(ctx, buf, len, image); if (error) - return gs_rethrow(error, "could not decode png image"); + return fz_rethrow(error, "could not decode image"); } else if (memcmp(buf, "II", 2) == 0 && buf[2] == 0xBC) { error = xps_decode_jpegxr(ctx, buf, len, image); if (error) - return gs_rethrow(error, "could not decode jpeg-xr image"); + return fz_rethrow(error, "could not decode image"); } else if (memcmp(buf, "MM", 2) == 0 || memcmp(buf, "II", 2) == 0) { error = xps_decode_tiff(ctx, buf, len, image); if (error) - return gs_rethrow(error, "could not decode tiff image"); + return fz_rethrow(error, "could not decode image"); } else - return gs_throw(-1, "unknown image file format"); - - // TODO: refcount image->colorspace - - /* See if we need to use the embedded profile. */ - if (image->profile) - { - /* - See if we can set up to use the embedded profile. - Note these profiles are NOT added to the xps color cache. - As such, they must be destroyed when the image brush ends. - */ + return fz_throw("unknown image file format"); - /* Create the profile */ - profile = gsicc_profile_new(NULL, ctx->memory, NULL, 0); - - /* Set buffer */ - profile->buffer = image->profile; - profile->buffer_size = image->profilesize; - - /* Parse */ - gsicc_init_profile_info(profile); - - if (profile->profile_handle == NULL) - { - /* Problem with profile. Just ignore it */ - gs_warn("ignoring problem with icc profile embedded in an image"); - gsicc_profile_reference(profile, -1); - } - else - { - /* Check the profile is OK for channel data count. - * Need to be careful here since alpha is put into comps */ - if ((image->comps - image->hasalpha) == gsicc_getsrc_channel_count(profile)) - { - /* Create a new colorspace and associate with the profile */ - // TODO: refcount image->colorspace - gs_cspace_build_ICC(&image->colorspace, NULL, ctx->memory); - image->colorspace->cmm_icc_profile_data = profile; - } - else - { - /* Problem with profile. Just ignore it */ - gs_warn("ignoring icc profile embedded in an image with wrong number of components"); - gsicc_profile_reference(profile, -1); - } - } - } - - if (image->hasalpha) - { - if (image->bits < 8) - dprintf1("cannot isolate alpha channel in %d bpc images\n", image->bits); - if (image->bits == 8) - xps_isolate_alpha_channel_8(ctx, image); - if (image->bits == 16) - xps_isolate_alpha_channel_16(ctx, image); - } - - return gs_okay; + return fz_okay; } static int -xps_paint_image_brush_imp(xps_context_t *ctx, xps_image_t *image, int alpha) +xps_paint_image_brush_imp(xps_context_t *ctx, fz_matrix ctm, xps_image_t *image) { - gs_image_enum *penum; - gs_color_space *colorspace; - gs_image_t gsimage; - int code; - + fz_colorspace *colorspace; unsigned int count; - unsigned int used; byte *samples; - if (alpha) - { - colorspace = ctx->gray; - samples = image->alpha; - count = (image->width * image->bits + 7) / 8 * image->height; - used = 0; - } - else - { - colorspace = image->colorspace; - samples = image->samples; - count = image->stride * image->height; - used = 0; - } - - memset(&gsimage, 0, sizeof(gsimage)); - gs_image_t_init(&gsimage, colorspace); - gsimage.ColorSpace = colorspace; - gsimage.BitsPerComponent = image->bits; - gsimage.Width = image->width; - gsimage.Height = image->height; + colorspace = image->colorspace; + samples = image->samples; + count = image->stride * image->height; - gsimage.ImageMatrix.xx = image->xres / 96.0; - gsimage.ImageMatrix.yy = image->yres / 96.0; - - gsimage.Interpolate = 1; - - penum = gs_image_enum_alloc(ctx->memory, "xps_parse_image_brush (gs_image_enum_alloc)"); - if (!penum) - return gs_throw(-1, "gs_enum_allocate failed"); - - if ((code = gs_image_init(penum, &gsimage, false, ctx->pgs)) < 0) - return gs_throw(code, "gs_image_init failed"); - - if ((code = gs_image_next(penum, samples, count, &used)) < 0) - return gs_throw(code, "gs_image_next failed"); - - if (count < used) - return gs_throw2(-1, "not enough image data (image=%d used=%d)", count, used); - - if (count > used) - return gs_throw2(0, "too much image data (image=%d used=%d)", count, used); - - gs_image_cleanup_and_free_enum(penum, ctx->pgs); + // xxx return 0; } static int -xps_paint_image_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root, void *vimage) +xps_paint_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root, void *vimage) { +#if 0 xps_image_t *image = vimage; int code; @@ -278,7 +72,7 @@ xps_paint_image_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, { code = xps_paint_image_brush_imp(ctx, image, 1); if (code < 0) - return gs_rethrow(code, "cannot draw alpha channel image"); + return fz_rethrow(code, "cannot draw alpha channel image"); } return 0; } @@ -287,13 +81,13 @@ xps_paint_image_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, { gs_transparency_mask_params_t params; gs_transparency_group_params_t tgp; - gs_rect bbox; + fz_rect bbox; xps_bounds_in_user_space(ctx, &bbox); code = gs_gsave(ctx->pgs); if (code < 0) - return gs_rethrow(code, "cannot gsave before transparency group"); + return fz_rethrow(code, "cannot gsave before transparency group"); gs_setcolorspace(ctx->pgs, ctx->gray); gs_trans_mask_params_init(¶ms, TRANSPARENCY_MASK_Luminosity); @@ -303,7 +97,7 @@ xps_paint_image_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, { gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot draw alpha channel image"); + return fz_rethrow(code, "cannot draw alpha channel image"); } gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); @@ -315,20 +109,21 @@ xps_paint_image_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, { gs_end_transparency_group(ctx->pgs); gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot draw color channel image"); + return fz_rethrow(code, "cannot draw color channel image"); } gs_end_transparency_group(ctx->pgs); code = gs_grestore(ctx->pgs); if (code < 0) - return gs_rethrow(code, "cannot grestore after transparency group"); + return fz_rethrow(code, "cannot grestore after transparency group"); } else { code = xps_paint_image_brush_imp(ctx, image, 0); if (code < 0) - return gs_rethrow(code, "cannot draw image"); + return fz_rethrow(code, "cannot draw image"); } +#endif return 0; } @@ -346,7 +141,7 @@ xps_find_image_brush_source_part(xps_context_t *ctx, char *base_uri, xps_item_t image_source_att = xps_att(root, "ImageSource"); if (!image_source_att) - return gs_throw(-1, "missing ImageSource attribute"); + return fz_throw("missing ImageSource attribute"); /* "{ColorConvertedBitmap /Resources/Image.tiff /Resources/Profile.icc}" */ if (strstr(image_source_att, "{ColorConvertedBitmap") == image_source_att) @@ -377,54 +172,59 @@ xps_find_image_brush_source_part(xps_context_t *ctx, char *base_uri, xps_item_t } if (!image_name) - return gs_throw1(-1, "cannot parse image resource name '%s'", image_source_att); + return fz_throw("cannot parse image resource name '%s'", image_source_att); xps_absolute_path(partname, base_uri, image_name, sizeof partname); part = xps_read_part(ctx, partname); if (!part) - return gs_throw1(-1, "cannot find image resource part '%s'", partname); + return fz_throw("cannot find image resource part '%s'", partname); *partp = part; - *profilep = xps_strdup(ctx, profile_name); + if (profile_name) + *profilep = xps_strdup(ctx, profile_name); return 0; } int -xps_parse_image_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root) +xps_parse_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) { xps_part_t *part; xps_image_t *image; - gs_color_space *colorspace; + fz_colorspace *colorspace; char *profilename; int code; + profilename = NULL; + code = xps_find_image_brush_source_part(ctx, base_uri, root, &part, &profilename); if (code < 0) - return gs_rethrow(code, "cannot find image source"); + return fz_rethrow(code, "cannot find image source"); image = xps_alloc(ctx, sizeof(xps_image_t)); if (!image) - return gs_throw(-1, "out of memory: image struct"); + return fz_throw("out of memory: image struct"); + +return 0; code = xps_decode_image(ctx, part, image); if (code < 0) - return gs_rethrow1(code, "cannot decode image '%s'", part->name); + return fz_rethrow(-1, "cannot decode image resource"); /* Override any embedded colorspace profiles if the external one matches. */ if (profilename) { colorspace = xps_read_icc_colorspace(ctx, base_uri, profilename); - if (colorspace && cs_num_components(colorspace) == cs_num_components(image->colorspace)) + if (colorspace && colorspace->n == image->colorspace->n) { // TODO: refcount image->colorspace image->colorspace = colorspace; } } - code = xps_parse_tiling_brush(ctx, base_uri, dict, root, xps_paint_image_brush, image); + code = xps_parse_tiling_brush(ctx, ctm, base_uri, dict, root, xps_paint_image_brush, image); if (code < 0) - return gs_rethrow(-1, "cannot parse tiling brush"); + return fz_rethrow(-1, "cannot parse tiling brush"); if (profilename) xps_free(ctx, profilename); @@ -434,36 +234,12 @@ xps_parse_image_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, return 0; } -int -xps_image_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) -{ - xps_part_t *imagepart; - int code; - int has_alpha; - char *profilename; - - code = xps_find_image_brush_source_part(ctx, base_uri, root, &imagepart, &profilename); - if (code < 0) - { - gs_catch(code, "cannot find image source"); - return 0; - } - - has_alpha = xps_image_has_alpha(ctx, imagepart); - - xps_free_part(ctx, imagepart); - - return has_alpha; -} - void xps_free_image(xps_context_t *ctx, xps_image_t *image) { // TODO: refcount image->colorspace if (image->samples) xps_free(ctx, image->samples); - if (image->alpha) - xps_free(ctx, image->alpha); if (image->profile) xps_free(ctx, image->profile); xps_free(ctx, image); diff --git a/xps/xpsjpeg.c b/xps/xpsjpeg.c index 878fd85a..937ac4f0 100644 --- a/xps/xpsjpeg.c +++ b/xps/xpsjpeg.c @@ -1,143 +1,11 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. +#include "fitz.h" +#include "muxps.h" - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - JPEG image support */ - -#include "ghostxps.h" - -#include "stream.h" -#include "strimpl.h" -#include "gsstate.h" -#include "jpeglib_.h" -#include "sdct.h" -#include "sjpeg.h" - -static int -xps_report_error(stream_state * st, const char *str) -{ - (void) gs_throw1(-1, "%s", str); - return 0; -} +#include +#include int xps_decode_jpeg(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image) { - jpeg_decompress_data jddp; - stream_DCT_state state; - stream_cursor_read rp; - stream_cursor_write wp; - int code; - int wlen; - byte *wbuf; - jpeg_saved_marker_ptr curr_marker; - - s_init_state((stream_state*)&state, &s_DCTD_template, ctx->memory); - state.report_error = xps_report_error; - - s_DCTD_template.set_defaults((stream_state*)&state); - - state.jpeg_memory = ctx->memory; - state.data.decompress = &jddp; - - jddp.template = s_DCTD_template; - jddp.memory = ctx->memory; - jddp.scanline_buffer = NULL; - - if ((code = gs_jpeg_create_decompress(&state)) < 0) - return gs_throw(-1, "cannot gs_jpeg_create_decompress"); - - s_DCTD_template.init((stream_state*)&state); - - rp.ptr = rbuf - 1; - rp.limit = rbuf + rlen - 1; - - /* read the header only by not having a write buffer */ - wp.ptr = 0; - wp.limit = 0; - - /* Set up to save the ICC marker APP2. - * According to the spec we should be getting APP1 APP2 and APP13. - * Library gets APP0 and APP14. */ - jpeg_save_markers(&(jddp.dinfo), 0xe2, 0xFFFF); - - code = s_DCTD_template.process((stream_state*)&state, &rp, &wp, true); - if (code != 1) - return gs_throw(-1, "premature EOF or error in jpeg"); - - /* Check if we had an ICC profile */ - curr_marker = jddp.dinfo.marker_list; - while (curr_marker != NULL) - { - if (curr_marker->marker == 0xe2) - { - /* Found ICC profile. Create a buffer and copy over now. - * Strip JPEG APP2 14 byte header */ - image->profilesize = curr_marker->data_length - 14; - image->profile = xps_alloc(ctx, image->profilesize); - if (image->profile) - { - /* If we can't create it, just ignore */ - memcpy(image->profile, &(curr_marker->data[14]), image->profilesize); - } - break; - } - curr_marker = curr_marker->next; - } - - image->width = jddp.dinfo.output_width; - image->height = jddp.dinfo.output_height; - image->comps = jddp.dinfo.output_components; - image->bits = 8; - image->stride = image->width * image->comps; - - if (image->comps == 1) - image->colorspace = ctx->gray; - if (image->comps == 3) - image->colorspace = ctx->srgb; - if (image->comps == 4) - image->colorspace = ctx->cmyk; - - if (jddp.dinfo.density_unit == 1) - { - image->xres = jddp.dinfo.X_density; - image->yres = jddp.dinfo.Y_density; - } - else if (jddp.dinfo.density_unit == 2) - { - image->xres = jddp.dinfo.X_density * 2.54; - image->yres = jddp.dinfo.Y_density * 2.54; - } - else - { - image->xres = 96; - image->yres = 96; - } - - wlen = image->stride * image->height; - wbuf = xps_alloc(ctx, wlen); - if (!wbuf) - return gs_throw1(-1, "out of memory allocating samples: %d", wlen); - - image->samples = wbuf; - - wp.ptr = wbuf - 1; - wp.limit = wbuf + wlen - 1; - - code = s_DCTD_template.process((stream_state*)&state, &rp, &wp, true); - if (code != EOFC) - return gs_throw1(-1, "error in jpeg (code = %d)", code); - - gs_jpeg_destroy(&state); - - return gs_okay; + return fz_throw("jpeg not available"); } diff --git a/xps/xpsjxr.c b/xps/xpsjxr.c index 1e9b0e73..f2f78ce0 100644 --- a/xps/xpsjxr.c +++ b/xps/xpsjxr.c @@ -1,19 +1,9 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - /* JPEG-XR (formerly HD-Photo (formerly Windows Media Photo)) support */ -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" + +#ifdef HAVE_JPEGXR #ifdef _MSC_VER #undef _MSC_VER @@ -257,3 +247,19 @@ xps_jpegxr_has_alpha(xps_context_t *ctx, byte *buf, int len) { return 1; } + +#else + +int +xps_decode_jpegxr(xps_context_t *ctx, byte *buf, int len, xps_image_t *image) +{ + return fz_throw("JPEG-XR codec is not available"); +} + +int +xps_jpegxr_has_alpha(xps_context_t *ctx, byte *buf, int len) +{ + return 0; +} + +#endif diff --git a/xps/xpsmem.c b/xps/xpsmem.c index 95199f07..3af3b3f6 100644 --- a/xps/xpsmem.c +++ b/xps/xpsmem.c @@ -1,27 +1,5 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - string manipulation functions */ - -#include "ghostxps.h" - -void * -xps_realloc_imp(xps_context_t *ctx, void *ptr, int size, const char *func) -{ - if (!ptr) - return gs_alloc_bytes(ctx->memory, size, func); - return gs_resize_object(ctx->memory, ptr, size, func); -} +#include "fitz.h" +#include "muxps.h" static inline int xps_tolower(int c) @@ -34,24 +12,13 @@ xps_tolower(int c) int xps_strcasecmp(char *a, char *b) { - while (xps_tolower(*a) == xps_tolower(*b)) - { - if (*a++ == 0) - return 0; - b++; - } - return xps_tolower(*a) - xps_tolower(*b); -} - -char * -xps_strdup_imp(xps_context_t *ctx, const char *str, const char *cname) -{ - char *cpy = NULL; - if (str) - cpy = (char*) gs_alloc_bytes(ctx->memory, strlen(str) + 1, cname); - if (cpy) - strcpy(cpy, str); - return cpy; + while (xps_tolower(*a) == xps_tolower(*b)) + { + if (*a++ == 0) + return 0; + b++; + } + return xps_tolower(*a) - xps_tolower(*b); } size_t diff --git a/xps/xpsopacity.c b/xps/xpsopacity.c index bd845efb..e4923f14 100644 --- a/xps/xpsopacity.c +++ b/xps/xpsopacity.c @@ -1,45 +1,31 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - transparency support */ - -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" void -xps_bounds_in_user_space(xps_context_t *ctx, gs_rect *ubox) +xps_bounds_in_user_space(xps_context_t *ctx, fz_rect *ubox) { +#if 0 gx_clip_path *clip_path; - gs_rect dbox; + fz_rect dbox; int code; code = gx_effective_clip_path(ctx->pgs, &clip_path); if (code < 0) - gs_warn("gx_effective_clip_path failed"); + fz_warn("gx_effective_clip_path failed"); dbox.p.x = fixed2float(clip_path->outer_box.p.x); dbox.p.y = fixed2float(clip_path->outer_box.p.y); dbox.q.x = fixed2float(clip_path->outer_box.q.x); dbox.q.y = fixed2float(clip_path->outer_box.q.y); gs_bbox_transform_inverse(&dbox, &ctm_only(ctx->pgs), ubox); +#endif } int -xps_begin_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, +xps_begin_opacity(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, char *opacity_att, xps_item_t *opacity_mask_tag) { - gs_transparency_group_params_t tgp; - gs_transparency_mask_params_t tmp; - gs_rect bbox; + fz_rect bbox; float opacity; int save; int code; @@ -50,44 +36,28 @@ xps_begin_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, opacity = 1.0; if (opacity_att) opacity = atof(opacity_att); - gs_setopacityalpha(ctx->pgs, opacity); +// gs_setopacityalpha(ctx->pgs, opacity); xps_bounds_in_user_space(ctx, &bbox); if (opacity_mask_tag) { - gs_trans_mask_params_init(&tmp, TRANSPARENCY_MASK_Luminosity); - gs_begin_transparency_mask(ctx->pgs, &tmp, &bbox, 0); - - gs_gsave(ctx->pgs); - - /* Need a path to fill/clip for the brush */ - gs_moveto(ctx->pgs, bbox.p.x, bbox.p.y); - gs_lineto(ctx->pgs, bbox.p.x, bbox.q.y); - gs_lineto(ctx->pgs, bbox.q.x, bbox.q.y); - gs_lineto(ctx->pgs, bbox.q.x, bbox.p.y); - gs_closepath(ctx->pgs); - /* opacity-only mode: use alpha value as gray color to create luminosity mask */ save = ctx->opacity_only; ctx->opacity_only = 1; - code = xps_parse_brush(ctx, base_uri, dict, opacity_mask_tag); + // begin mask + code = xps_parse_brush(ctx, ctm, base_uri, dict, opacity_mask_tag); if (code) { - gs_grestore(ctx->pgs); - gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); ctx->opacity_only = save; - return gs_rethrow(code, "cannot parse opacity mask brush"); + return fz_rethrow(code, "cannot parse opacity mask brush"); } - gs_grestore(ctx->pgs); - gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); ctx->opacity_only = save; } - gs_trans_group_params_init(&tgp); - gs_begin_transparency_group(ctx->pgs, &tgp, &bbox); + // begin group return 0; } @@ -98,5 +68,5 @@ xps_end_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, { if (!opacity_att && !opacity_mask_tag) return; - gs_end_transparency_group(ctx->pgs); + // end mask+group } diff --git a/xps/xpspage.c b/xps/xpspage.c index c6424d7c..858f327f 100644 --- a/xps/xpspage.c +++ b/xps/xpspage.c @@ -1,22 +1,8 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - page parsing */ - -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" int -xps_parse_canvas(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root) +xps_parse_canvas(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) { xps_resource_t *new_dict = NULL; xps_item_t *node; @@ -32,7 +18,7 @@ xps_parse_canvas(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_i xps_item_t *clip_tag = NULL; xps_item_t *opacity_mask_tag = NULL; - gs_matrix transform; + fz_matrix transform; transform_att = xps_att(root, "RenderTransform"); clip_att = xps_att(root, "Clip"); @@ -45,7 +31,7 @@ xps_parse_canvas(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_i { code = xps_parse_resource_dictionary(ctx, &new_dict, base_uri, xps_down(node)); if (code) - return gs_rethrow(code, "cannot load Canvas.Resources"); + return fz_rethrow(code, "cannot load Canvas.Resources"); new_dict->parent = dict; dict = new_dict; } @@ -63,45 +49,46 @@ xps_parse_canvas(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_i xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); - gs_gsave(ctx->pgs); +// gs_gsave(ctx->pgs); - gs_make_identity(&transform); + transform = fz_identity; if (transform_att) xps_parse_render_transform(ctx, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); - gs_concat(ctx->pgs, &transform); + ctm = fz_concat(ctm, transform); if (clip_att || clip_tag) { + ctx->path = fz_newpath(); if (clip_att) xps_parse_abbreviated_geometry(ctx, clip_att); if (clip_tag) xps_parse_path_geometry(ctx, dict, clip_tag, 0); - xps_clip(ctx); + xps_clip(ctx, ctm); } - code = xps_begin_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + code = xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); if (code) { - gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot create transparency group"); +// gs_grestore(ctx->pgs); + return fz_rethrow(code, "cannot create transparency group"); } for (node = xps_down(root); node; node = xps_next(node)) { - code = xps_parse_element(ctx, base_uri, dict, node); + code = xps_parse_element(ctx, ctm, base_uri, dict, node); if (code) { xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot parse child of Canvas"); +// gs_grestore(ctx->pgs); + return fz_rethrow(code, "cannot parse child of Canvas"); } } xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - gs_grestore(ctx->pgs); +// gs_grestore(ctx->pgs); if (new_dict) xps_free_resource_dictionary(ctx, new_dict); @@ -110,172 +97,74 @@ xps_parse_canvas(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_i } int -xps_parse_fixed_page(xps_context_t *ctx, xps_part_t *part) +xps_parse_fixed_page(xps_context_t *ctx, fz_matrix ctm, xps_page_t *page) { - xps_item_t *root, *node; + xps_item_t *node; xps_resource_t *dict; - char *width_att; - char *height_att; - int has_transparency; char base_uri[1024]; char *s; int code; - if_debug1('|', "doc: parsing page %s\n", part->name); - - xps_strlcpy(base_uri, part->name, sizeof base_uri); + xps_strlcpy(base_uri, page->name, sizeof base_uri); s = strrchr(base_uri, '/'); if (s) s[1] = 0; - root = xps_parse_xml(ctx, part->data, part->size); - if (!root) - return gs_rethrow(-1, "cannot parse xml"); - - if (strcmp(xps_tag(root), "FixedPage")) - return gs_throw1(-1, "expected FixedPage element (found %s)", xps_tag(root)); - - width_att = xps_att(root, "Width"); - height_att = xps_att(root, "Height"); - - if (!width_att) - return gs_throw(-1, "FixedPage missing required attribute: Width"); - if (!height_att) - return gs_throw(-1, "FixedPage missing required attribute: Height"); - dict = NULL; - /* Setup new page */ + for (node = xps_down(page->root); node; node = xps_next(node)) { - gs_memory_t *mem = ctx->memory; - gs_state *pgs = ctx->pgs; - gx_device *dev = gs_currentdevice(pgs); - gs_param_float_array fa; - float fv[2]; - gs_c_param_list list; - - gs_c_param_list_write(&list, mem); - - fv[0] = atoi(width_att) / 96.0 * 72.0; - fv[1] = atoi(height_att) / 96.0 * 72.0; - fa.persistent = false; - fa.data = fv; - fa.size = 2; - - code = param_write_float_array((gs_param_list *)&list, ".MediaSize", &fa); - if ( code >= 0 ) + if (!strcmp(xps_tag(node), "FixedPage.Resources") && xps_down(node)) { - gs_c_param_list_read(&list); - code = gs_putdeviceparams(dev, (gs_param_list *)&list); + code = xps_parse_resource_dictionary(ctx, &dict, base_uri, xps_down(node)); + if (code) + return fz_rethrow(code, "cannot load FixedPage.Resources"); } - gs_c_param_list_release(&list); - - /* nb this is for the demo it is wrong and should be removed */ - gs_initgraphics(pgs); - - /* 96 dpi default - and put the origin at the top of the page */ - - gs_initmatrix(pgs); - - code = gs_scale(pgs, 72.0/96.0, -72.0/96.0); - if (code < 0) - return gs_rethrow(code, "cannot set page transform"); - - code = gs_translate(pgs, 0.0, -atoi(height_att)); - if (code < 0) - return gs_rethrow(code, "cannot set page transform"); - - code = gs_erasepage(pgs); - if (code < 0) - return gs_rethrow(code, "cannot clear page"); + code = xps_parse_element(ctx, ctm, base_uri, dict, node); + if (code) + return fz_rethrow(code, "cannot parse child of FixedPage"); } - /* Pre-parse looking for transparency */ - - has_transparency = 0; - - for (node = xps_down(root); node; node = xps_next(node)) + if (dict) { - if (!strcmp(xps_tag(node), "FixedPage.Resources") && xps_down(node)) - if (xps_resource_dictionary_has_transparency(ctx, base_uri, xps_down(node))) - has_transparency = 1; - if (xps_element_has_transparency(ctx, base_uri, node)) - has_transparency = 1; + xps_free_resource_dictionary(ctx, dict); } - /* save the state with the original device before we push */ - gs_gsave(ctx->pgs); - - if (ctx->use_transparency && has_transparency) - { - code = gs_push_pdf14trans_device(ctx->pgs); - if (code < 0) - { - gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot install transparency device"); - } - } + return fz_okay; +} - /* Initialize the default profiles in the ctx to what is in the manager */ - ctx->gray->cmm_icc_profile_data = ctx->pgs->icc_manager->default_gray; - ctx->srgb->cmm_icc_profile_data = ctx->pgs->icc_manager->default_rgb; - /* scrgb really needs to be a bit different. - * Unless we are handling nonlinearity before conversion from float. ToDo. */ - ctx->scrgb->cmm_icc_profile_data = ctx->pgs->icc_manager->default_rgb; - ctx->cmyk->cmm_icc_profile_data = ctx->pgs->icc_manager->default_cmyk; +int +xps_load_fixed_page(xps_context_t *ctx, xps_page_t *page) +{ + xps_part_t *part; + xps_item_t *root; + char *width_att; + char *height_att; - /* Draw contents */ + part = xps_read_part(ctx, page->name); + if (!part) + return fz_rethrow(-1, "cannot read zip part '%s'", page->name); - for (node = xps_down(root); node; node = xps_next(node)) - { - if (!strcmp(xps_tag(node), "FixedPage.Resources") && xps_down(node)) - { - code = xps_parse_resource_dictionary(ctx, &dict, base_uri, xps_down(node)); - if (code) - { - gs_pop_pdf14trans_device(ctx->pgs); - gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot load FixedPage.Resources"); - } - } - code = xps_parse_element(ctx, base_uri, dict, node); - if (code) - { - gs_pop_pdf14trans_device(ctx->pgs); - gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot parse child of FixedPage"); - } - } + root = xps_parse_xml(ctx, part->data, part->size); + if (!root) + return fz_rethrow(-1, "cannot parse xml"); - if (ctx->use_transparency && has_transparency) - { - code = gs_pop_pdf14trans_device(ctx->pgs); - if (code < 0) - { - gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot uninstall transparency device"); - } - } + xps_free_part(ctx, part); - /* Flush page */ - { - code = xps_show_page(ctx, 1, true); /* copies, flush */ - if (code < 0) - { - gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot flush page"); - } - } + if (strcmp(xps_tag(root), "FixedPage")) + return fz_throw("expected FixedPage element (found %s)", xps_tag(root)); - /* restore the original device, discarding the pdf14 compositor */ - gs_grestore(ctx->pgs); + width_att = xps_att(root, "Width"); + if (!width_att) + return fz_throw("FixedPage missing required attribute: Width"); - if (dict) - { - xps_free_resource_dictionary(ctx, dict); - } + height_att = xps_att(root, "Height"); + if (!height_att) + return fz_throw("FixedPage missing required attribute: Height"); - xps_free_item(ctx, root); + page->width = atoi(width_att); + page->height = atoi(height_att); + page->root = root; return 0; } diff --git a/xps/xpspath.c b/xps/xpspath.c index 89e9716a..12377501 100644 --- a/xps/xpspath.c +++ b/xps/xpspath.c @@ -1,58 +1,88 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. +#include "fitz.h" +#include "muxps.h" - This software is provided AS-IS with no warranty, either express or - implied. +static fz_point +fz_currentpoint(fz_path *path) +{ + fz_point c, m; + int i; - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ + c.x = c.y = m.x = m.y = 0; + i = 0; -/* XPS interpreter - path (vector drawing) support */ + while (i < path->len) + { + switch (path->els[i++].k) + { + case FZ_MOVETO: + m.x = c.x = path->els[i++].v; + m.y = c.y = path->els[i++].v; + break; + case FZ_LINETO: + c.x = path->els[i++].v; + c.y = path->els[i++].v; + break; + case FZ_CURVETO: + i += 4; + c.x = path->els[i++].v; + c.y = path->els[i++].v; + break; + case FZ_CLOSEPATH: + c = m; + } + } -#include "ghostxps.h" + return c; +} void -xps_clip(xps_context_t *ctx) +xps_clip(xps_context_t *ctx, fz_matrix ctm) { - if (ctx->fill_rule == 0) - gs_eoclip(ctx->pgs); - else - gs_clip(ctx->pgs); - gs_newpath(ctx->pgs); +printf("xps_clip\n"); + assert(ctx->path); +// ctx->dev->clippath(ctx->dev, ctx->path, ctx->fill_rule == 0, ctm); + fz_freepath(ctx->path); + ctx->path = NULL; } void -xps_fill(xps_context_t *ctx) +xps_fill(xps_context_t *ctx, fz_matrix ctm) { - if (gs_currentopacityalpha(ctx->pgs) < 0.001) - gs_newpath(ctx->pgs); - else if (ctx->fill_rule == 0) { - if (gs_eofill(ctx->pgs) == gs_error_Remap_Color) - xps_high_level_pattern(ctx); - gs_eofill(ctx->pgs); - } - else { - if (gs_fill(ctx->pgs) == gs_error_Remap_Color) - xps_high_level_pattern(ctx); - gs_fill(ctx->pgs); - } +printf("xps_fill!\n"); + ctx->dev->fillpath(ctx->dev, ctx->path, ctx->fill_rule == 0, ctm, + ctx->colorspace, ctx->color, ctx->alpha); + fz_freepath(ctx->path); + ctx->path = NULL; +} + +static void +xps_stroke(xps_context_t *ctx, fz_matrix ctm, fz_strokestate *stroke) +{ + ctx->dev->strokepath(ctx->dev, ctx->path, stroke, ctm, + ctx->colorspace, ctx->color, ctx->alpha); + fz_freepath(ctx->path); + ctx->path = NULL; +} + +static void +xps_clipstroke(xps_context_t *ctx, fz_matrix ctm, fz_strokestate *stroke) +{ + ctx->dev->clipstrokepath(ctx->dev, ctx->path, stroke, ctm); + fz_freepath(ctx->path); + ctx->path = NULL; } /* Draw an arc segment transformed by the matrix, we approximate with straight - * line segments. We cannot use the gs_arc function because they only draw + * line segments. We cannot use the fz_arc function because they only draw * circular arcs, we need to transform the line to make them elliptical but * without transforming the line width. */ static inline void -xps_draw_arc_segment(xps_context_t *ctx, gs_matrix *mtx, float th0, float th1, int iscw) +xps_draw_arc_segment(xps_context_t *ctx, fz_matrix mtx, float th0, float th1, int iscw) { float t, d; - gs_point p; - + fz_point p; +return; // XXX broken while (th1 < th0) th1 += M_PI * 2.0; @@ -60,34 +90,46 @@ xps_draw_arc_segment(xps_context_t *ctx, gs_matrix *mtx, float th0, float th1, i if (iscw) { - gs_point_transform(cos(th0), sin(th0), mtx, &p); - gs_lineto(ctx->pgs, p.x, p.y); + p.x = cos(th0); + p.y = sin(th0); + p = fz_transformpoint(mtx, p); + fz_lineto(ctx->path, p.x, p.y); for (t = th0; t < th1; t += d) { - gs_point_transform(cos(t), sin(t), mtx, &p); - gs_lineto(ctx->pgs, p.x, p.y); + p.x = cos(t); + p.y = sin(t); + p = fz_transformpoint(mtx, p); + fz_lineto(ctx->path, p.x, p.y); } - gs_point_transform(cos(th1), sin(th1), mtx, &p); - gs_lineto(ctx->pgs, p.x, p.y); + p.x = cos(th1); + p.y = sin(th1); + p = fz_transformpoint(mtx, p); + fz_lineto(ctx->path, p.x, p.y); } else { th0 += M_PI * 2; - gs_point_transform(cos(th0), sin(th0), mtx, &p); - gs_lineto(ctx->pgs, p.x, p.y); + p.x = cos(th0); + p.y = sin(th0); + p = fz_transformpoint(mtx, p); + fz_lineto(ctx->path, p.x, p.y); for (t = th0; t > th1; t -= d) { - gs_point_transform(cos(t), sin(t), mtx, &p); - gs_lineto(ctx->pgs, p.x, p.y); + p.x = cos(t); + p.y = sin(t); + p = fz_transformpoint(mtx, p); + fz_lineto(ctx->path, p.x, p.y); } - gs_point_transform(cos(th1), sin(th1), mtx, &p); - gs_lineto(ctx->pgs, p.x, p.y); + p.x = cos(th1); + p.y = sin(th1); + p = fz_transformpoint(mtx, p); + fz_lineto(ctx->path, p.x, p.y); } } /* Given two vectors find the angle between them. */ static inline double -angle_between(const gs_point u, const gs_point v) +angle_between(const fz_point u, const fz_point v) { double det = u.x * v.y - u.y * v.x; double sign = (det < 0 ? -1.0 : 1.0); @@ -107,9 +149,9 @@ xps_draw_arc(xps_context_t *ctx, int is_large_arc, int is_clockwise, float point_x, float point_y) { - gs_matrix rotmat, revmat; - gs_matrix mtx; - gs_point pt; + fz_matrix rotmat, revmat; + fz_matrix mtx; + fz_point pt; double rx, ry; double x1, y1, x2, y2; double x1t, y1t; @@ -118,7 +160,7 @@ xps_draw_arc(xps_context_t *ctx, double sign; double th1, dth; - gs_currentpoint(ctx->pgs, &pt); + pt = fz_currentpoint(ctx->path); x1 = pt.x; y1 = pt.y; x2 = point_x; @@ -131,8 +173,8 @@ xps_draw_arc(xps_context_t *ctx, else sign = -1; - gs_make_rotation(rotation_angle, &rotmat); - gs_make_rotation(-rotation_angle, &revmat); + rotmat = fz_rotate(rotation_angle); + revmat = fz_rotate(-rotation_angle); /* http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes */ /* Conversion from endpoint to center parameterization */ @@ -142,12 +184,14 @@ xps_draw_arc(xps_context_t *ctx, ry = fabsf(ry); if (rx < 0.001 || ry < 0.001) { - gs_lineto(ctx->pgs, x2, y2); + fz_lineto(ctx->path, x2, y2); return; } /* F.6.5.1 */ - gs_distance_transform((x1 - x2) / 2.0, (y1 - y2) / 2.0, &revmat, &pt); + pt.x = (x1 - x2) / 2; + pt.y = (y1 - y2) / 2; + pt = fz_transformvector(revmat, pt); x1t = pt.x; y1t = pt.y; @@ -171,13 +215,15 @@ xps_draw_arc(xps_context_t *ctx, cyt = sign * t3 * -(ry * x1t) / rx; /* F.6.5.3 */ - gs_distance_transform(cxt, cyt, &rotmat, &pt); + pt.x = cxt; + pt.y = cyt; + pt = fz_transformvector(rotmat, pt); cx = pt.x + (x1 + x2) / 2; cy = pt.y + (y1 + y2) / 2; /* F.6.5.4 */ { - gs_point coord1, coord2, coord3, coord4; + fz_point coord1, coord2, coord3, coord4; coord1.x = 1; coord1.y = 0; coord2.x = (x1t - cxt) / rx; @@ -189,18 +235,18 @@ xps_draw_arc(xps_context_t *ctx, th1 = angle_between(coord1, coord2); dth = angle_between(coord3, coord4); if (dth < 0 && !is_clockwise) - dth += (degrees_to_radians * 360); + dth += ((M_PI / 180.0) * 360); if (dth > 0 && is_clockwise) - dth -= (degrees_to_radians * 360); + dth -= ((M_PI / 180.0) * 360); } - gs_make_identity(&mtx); - gs_matrix_translate(&mtx, cx, cy, &mtx); - gs_matrix_rotate(&mtx, rotation_angle, &mtx); - gs_matrix_scale(&mtx, rx, ry, &mtx); - xps_draw_arc_segment(ctx, &mtx, th1, th1 + dth, is_clockwise); + mtx = fz_identity; + mtx = fz_concat(mtx, fz_translate(cx, cy)); + mtx = fz_concat(mtx, fz_rotate(rotation_angle)); + mtx = fz_concat(mtx, fz_scale(rx, ry)); + xps_draw_arc_segment(ctx, mtx, th1, th1 + dth, is_clockwise); - gs_lineto(ctx->pgs, point_x, point_y); + fz_lineto(ctx->path, point_x, point_y); } /* @@ -215,18 +261,17 @@ xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom) char **args; char **pargs; char *s = geom; - gs_point pt; + fz_point pt; int i, n; int cmd, old; float x1, y1, x2, y2, x3, y3; float smooth_x, smooth_y; /* saved cubic bezier control point for smooth curves */ int reset_smooth; - args = xps_alloc(ctx, sizeof(char*) * (strlen(geom) + 1)); - pargs = args; +printf("xps_parse_abbreviated_geometry: %s\n", geom); - //dprintf1("new path (%.70s)\n", geom); - gs_newpath(ctx->pgs); + args = fz_calloc(strlen(geom) + 1, sizeof(char*)); + pargs = args; while (*s) { @@ -282,48 +327,44 @@ xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom) break; case 'M': - gs_moveto(ctx->pgs, atof(args[i]), atof(args[i+1])); - //dprintf2("moveto %g %g\n", atof(args[i]), atof(args[i+1])); + fz_moveto(ctx->path, atof(args[i]), atof(args[i+1])); i += 2; break; case 'm': - gs_rmoveto(ctx->pgs, atof(args[i]), atof(args[i+1])); - //dprintf2("rmoveto %g %g\n", atof(args[i]), atof(args[i+1])); + pt = fz_currentpoint(ctx->path); + fz_moveto(ctx->path, pt.x + atof(args[i]), pt.y + atof(args[i+1])); i += 2; break; case 'L': - gs_lineto(ctx->pgs, atof(args[i]), atof(args[i+1])); - //dprintf2("lineto %g %g\n", atof(args[i]), atof(args[i+1])); + fz_lineto(ctx->path, atof(args[i]), atof(args[i+1])); i += 2; break; case 'l': - gs_rlineto(ctx->pgs, atof(args[i]), atof(args[i+1])); - //dprintf2("rlineto %g %g\n", atof(args[i]), atof(args[i+1])); + pt = fz_currentpoint(ctx->path); + fz_lineto(ctx->path, pt.x + atof(args[i]), pt.y + atof(args[i+1])); i += 2; break; case 'H': - gs_currentpoint(ctx->pgs, &pt); - gs_lineto(ctx->pgs, atof(args[i]), pt.y); - //dprintf1("hlineto %g\n", atof(args[i])); + pt = fz_currentpoint(ctx->path); + fz_lineto(ctx->path, atof(args[i]), pt.y); i += 1; break; case 'h': - gs_rlineto(ctx->pgs, atof(args[i]), 0.0); - //dprintf1("rhlineto %g\n", atof(args[i])); + pt = fz_currentpoint(ctx->path); + fz_lineto(ctx->path, pt.x + atof(args[i]), pt.y); i += 1; break; case 'V': - gs_currentpoint(ctx->pgs, &pt); - gs_lineto(ctx->pgs, pt.x, atof(args[i])); - //dprintf1("vlineto %g\n", atof(args[i])); + pt = fz_currentpoint(ctx->path); + fz_lineto(ctx->path, pt.x, atof(args[i])); i += 1; break; case 'v': - gs_rlineto(ctx->pgs, 0.0, atof(args[i])); - //dprintf1("rvlineto %g\n", atof(args[i])); + pt = fz_currentpoint(ctx->path); + fz_lineto(ctx->path, pt.x, pt.y + atof(args[i])); i += 1; break; @@ -334,7 +375,7 @@ xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom) y2 = atof(args[i+3]); x3 = atof(args[i+4]); y3 = atof(args[i+5]); - gs_curveto(ctx->pgs, x1, y1, x2, y2, x3, y3); + fz_curveto(ctx->path, x1, y1, x2, y2, x3, y3); i += 6; reset_smooth = 0; smooth_x = x3 - x2; @@ -342,14 +383,14 @@ xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom) break; case 'c': - gs_currentpoint(ctx->pgs, &pt); + pt = fz_currentpoint(ctx->path); x1 = atof(args[i+0]) + pt.x; y1 = atof(args[i+1]) + pt.y; x2 = atof(args[i+2]) + pt.x; y2 = atof(args[i+3]) + pt.y; x3 = atof(args[i+4]) + pt.x; y3 = atof(args[i+5]) + pt.y; - gs_curveto(ctx->pgs, x1, y1, x2, y2, x3, y3); + fz_curveto(ctx->path, x1, y1, x2, y2, x3, y3); i += 6; reset_smooth = 0; smooth_x = x3 - x2; @@ -357,13 +398,12 @@ xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom) break; case 'S': - gs_currentpoint(ctx->pgs, &pt); + pt = fz_currentpoint(ctx->path); x1 = atof(args[i+0]); y1 = atof(args[i+1]); x2 = atof(args[i+2]); y2 = atof(args[i+3]); - //dprintf2("smooth %g %g\n", smooth_x, smooth_y); - gs_curveto(ctx->pgs, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); + fz_curveto(ctx->path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); i += 4; reset_smooth = 0; smooth_x = x2 - x1; @@ -371,13 +411,12 @@ xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom) break; case 's': - gs_currentpoint(ctx->pgs, &pt); + pt = fz_currentpoint(ctx->path); x1 = atof(args[i+0]) + pt.x; y1 = atof(args[i+1]) + pt.y; x2 = atof(args[i+2]) + pt.x; y2 = atof(args[i+3]) + pt.y; - //dprintf2("smooth %g %g\n", smooth_x, smooth_y); - gs_curveto(ctx->pgs, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); + fz_curveto(ctx->path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); i += 4; reset_smooth = 0; smooth_x = x2 - x1; @@ -385,26 +424,24 @@ xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom) break; case 'Q': - gs_currentpoint(ctx->pgs, &pt); + pt = fz_currentpoint(ctx->path); x1 = atof(args[i+0]); y1 = atof(args[i+1]); x2 = atof(args[i+2]); y2 = atof(args[i+3]); - //dprintf4("conicto %g %g %g %g\n", x1, y1, x2, y2); - gs_curveto(ctx->pgs, + fz_curveto(ctx->path, (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2); i += 4; break; case 'q': - gs_currentpoint(ctx->pgs, &pt); + pt = fz_currentpoint(ctx->path); x1 = atof(args[i+0]) + pt.x; y1 = atof(args[i+1]) + pt.y; x2 = atof(args[i+2]) + pt.x; y2 = atof(args[i+3]) + pt.y; - //dprintf4("conicto %g %g %g %g\n", x1, y1, x2, y2); - gs_curveto(ctx->pgs, + fz_curveto(ctx->path, (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2); @@ -419,7 +456,7 @@ xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom) i += 7; break; case 'a': - gs_currentpoint(ctx->pgs, &pt); + pt = fz_currentpoint(ctx->path); xps_draw_arc(ctx, atof(args[i+0]), atof(args[i+1]), atof(args[i+2]), atoi(args[i+3]), atoi(args[i+4]), @@ -429,8 +466,7 @@ xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom) case 'Z': case 'z': - gs_closepath(ctx->pgs); - //dputs("closepath\n"); + fz_closepath(ctx->path); break; default: @@ -467,7 +503,7 @@ xps_parse_arc_segment(xps_context_t *ctx, xps_item_t *root, int stroking, int *s if (!point_att || !size_att || !rotation_angle_att || !is_large_arc_att || !sweep_direction_att) { - gs_warn("ArcSegment element is missing attributes"); + fz_warn("ArcSegment element is missing attributes"); return; } @@ -485,7 +521,7 @@ xps_parse_arc_segment(xps_context_t *ctx, xps_item_t *root, int stroking, int *s if (stroking && !is_stroked) { - gs_moveto(ctx->pgs, point_x, point_y); + fz_moveto(ctx->path, point_x, point_y); return; } @@ -499,13 +535,13 @@ xps_parse_poly_quadratic_bezier_segment(xps_context_t *ctx, xps_item_t *root, in char *is_stroked_att = xps_att(root, "IsStroked"); float x[2], y[2]; int is_stroked; - gs_point pt; + fz_point pt; char *s; int n; if (!points_att) { - gs_warn("PolyQuadraticBezierSegment element has no points"); + fz_warn("PolyQuadraticBezierSegment element has no points"); return; } @@ -527,12 +563,12 @@ xps_parse_poly_quadratic_bezier_segment(xps_context_t *ctx, xps_item_t *root, in { if (stroking && !is_stroked) { - gs_moveto(ctx->pgs, x[1], y[1]); + fz_moveto(ctx->path, x[1], y[1]); } else { - gs_currentpoint(ctx->pgs, &pt); - gs_curveto(ctx->pgs, + pt = fz_currentpoint(ctx->path); + fz_curveto(ctx->path, (pt.x + 2 * x[0]) / 3, (pt.y + 2 * y[0]) / 3, (x[1] + 2 * x[0]) / 3, (y[1] + 2 * y[0]) / 3, x[1], y[1]); @@ -554,7 +590,7 @@ xps_parse_poly_bezier_segment(xps_context_t *ctx, xps_item_t *root, int stroking if (!points_att) { - gs_warn("PolyBezierSegment element has no points"); + fz_warn("PolyBezierSegment element has no points"); return; } @@ -575,9 +611,9 @@ xps_parse_poly_bezier_segment(xps_context_t *ctx, xps_item_t *root, int stroking if (n == 3) { if (stroking && !is_stroked) - gs_moveto(ctx->pgs, x[2], y[2]); + fz_moveto(ctx->path, x[2], y[2]); else - gs_curveto(ctx->pgs, x[0], y[0], x[1], y[1], x[2], y[2]); + fz_curveto(ctx->path, x[0], y[0], x[1], y[1], x[2], y[2]); n = 0; } } @@ -594,7 +630,7 @@ xps_parse_poly_line_segment(xps_context_t *ctx, xps_item_t *root, int stroking, if (!points_att) { - gs_warn("PolyLineSegment element has no points"); + fz_warn("PolyLineSegment element has no points"); return; } @@ -610,9 +646,9 @@ xps_parse_poly_line_segment(xps_context_t *ctx, xps_item_t *root, int stroking, while (*s == ' ') s++; sscanf(s, "%g,%g", &x, &y); if (stroking && !is_stroked) - gs_moveto(ctx->pgs, x, y); + fz_moveto(ctx->path, x, y); else - gs_lineto(ctx->pgs, x, y); + fz_lineto(ctx->path, x, y); while (*s != ' ' && *s != 0) s++; } } @@ -647,7 +683,7 @@ xps_parse_path_figure(xps_context_t *ctx, xps_item_t *root, int stroking) if (!stroking && !is_filled) /* not filled, when filling */ return; - gs_moveto(ctx->pgs, start_x, start_y); + fz_moveto(ctx->path, start_x, start_y); for (node = xps_down(root); node; node = xps_next(node)) { @@ -664,9 +700,9 @@ xps_parse_path_figure(xps_context_t *ctx, xps_item_t *root, int stroking) if (is_closed) { if (stroking && skipped_stroke) - gs_lineto(ctx->pgs, start_x, start_y); /* we've skipped using gs_moveto... */ + fz_lineto(ctx->path, start_x, start_y); /* we've skipped using fz_moveto... */ else - gs_closepath(ctx->pgs); /* no skipped segments, safe to closepath properly */ + fz_closepath(ctx->path); /* no skipped segments, safe to closepath properly */ } } @@ -682,10 +718,7 @@ xps_parse_path_geometry(xps_context_t *ctx, xps_resource_t *dict, xps_item_t *ro xps_item_t *transform_tag = NULL; xps_item_t *figures_tag = NULL; /* only used by resource */ - gs_matrix transform; - gs_matrix saved_transform; - - gs_newpath(ctx->pgs); + fz_matrix transform; figures_att = xps_att(root, "Figures"); fill_rule_att = xps_att(root, "FillRule"); @@ -708,17 +741,12 @@ xps_parse_path_geometry(xps_context_t *ctx, xps_resource_t *dict, xps_item_t *ro ctx->fill_rule = 0; } - gs_make_identity(&transform); - if (transform_att || transform_tag) - { - if (transform_att) - xps_parse_render_transform(ctx, transform_att, &transform); - if (transform_tag) - xps_parse_matrix_transform(ctx, transform_tag, &transform); - } - - gs_currentmatrix(ctx->pgs, &saved_transform); - gs_concat(ctx->pgs, &transform); + transform = fz_identity; + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + // TODO: apply matrix if (figures_att) { @@ -735,8 +763,6 @@ xps_parse_path_geometry(xps_context_t *ctx, xps_resource_t *dict, xps_item_t *ro if (!strcmp(xps_tag(node), "PathFigure")) xps_parse_path_figure(ctx, node, stroking); } - - gs_setmatrix(ctx->pgs, &saved_transform); } static int @@ -744,12 +770,12 @@ xps_parse_line_cap(char *attr) { if (attr) { - if (!strcmp(attr, "Flat")) return gs_cap_butt; - if (!strcmp(attr, "Square")) return gs_cap_square; - if (!strcmp(attr, "Round")) return gs_cap_round; - if (!strcmp(attr, "Triangle")) return gs_cap_triangle; + if (!strcmp(attr, "Flat")) return 0; + if (!strcmp(attr, "Round")) return 1; + if (!strcmp(attr, "Square")) return 2; + if (!strcmp(attr, "Triangle")) return 3; /* FIXME add triangle caps */ } - return gs_cap_butt; + return 0; } /* @@ -758,7 +784,7 @@ xps_parse_line_cap(char *attr) */ int -xps_parse_path(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root) +xps_parse_path(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) { xps_item_t *node; int code; @@ -794,16 +820,15 @@ xps_parse_path(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_ite char *stroke_miter_limit_att; char *stroke_thickness_att; - gs_line_join linejoin; - float linewidth; - float miterlimit; + fz_strokestate stroke; + fz_matrix transform; float samples[32]; - gs_color_space *colorspace; - - gs_gsave(ctx->pgs); + fz_colorspace *colorspace; ctx->fill_rule = 0; +printf("xps_parse_path\n"); + /* * Extract attributes and extended attributes. */ @@ -875,162 +900,145 @@ xps_parse_path(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_ite stroke_tag = NULL; } - gs_setlinestartcap(ctx->pgs, xps_parse_line_cap(stroke_start_line_cap_att)); - gs_setlineendcap(ctx->pgs, xps_parse_line_cap(stroke_end_line_cap_att)); - gs_setlinedashcap(ctx->pgs, xps_parse_line_cap(stroke_dash_cap_att)); + stroke.linecap = xps_parse_line_cap(stroke_start_line_cap_att); +// fz_setlineendcap(ctx->pgs, xps_parse_line_cap(stroke_end_line_cap_att)); +// fz_setlinedashcap(ctx->pgs, xps_parse_line_cap(stroke_dash_cap_att)); - linejoin = gs_join_miter; + stroke.linejoin = 0; if (stroke_line_join_att) { - if (!strcmp(stroke_line_join_att, "Miter")) linejoin = gs_join_miter; - if (!strcmp(stroke_line_join_att, "Bevel")) linejoin = gs_join_bevel; - if (!strcmp(stroke_line_join_att, "Round")) linejoin = gs_join_round; + if (!strcmp(stroke_line_join_att, "Miter")) stroke.linejoin = 0; + if (!strcmp(stroke_line_join_att, "Round")) stroke.linejoin = 1; + if (!strcmp(stroke_line_join_att, "Bevel")) stroke.linejoin = 2; } - gs_setlinejoin(ctx->pgs, linejoin); - miterlimit = 10.0; + stroke.miterlimit = 10.0; if (stroke_miter_limit_att) - miterlimit = atof(stroke_miter_limit_att); - gs_setmiterlimit(ctx->pgs, miterlimit); + stroke.miterlimit = atof(stroke_miter_limit_att); - linewidth = 1.0; + stroke.linewidth = 1.0; if (stroke_thickness_att) - linewidth = atof(stroke_thickness_att); - gs_setlinewidth(ctx->pgs, linewidth); + stroke.linewidth = atof(stroke_thickness_att); + stroke.dashphase = 0; + stroke.dashlen = 0; if (stroke_dash_array_att) { char *s = stroke_dash_array_att; - float dash_array[100]; - float dash_offset = 0.0; - int dash_count = 0; if (stroke_dash_offset_att) - dash_offset = atof(stroke_dash_offset_att) * linewidth; + stroke.dashphase = atof(stroke_dash_offset_att) * stroke.linewidth; - while (*s) + while (*s && stroke.dashlen < nelem(stroke.dashlist)) { while (*s == ' ') s++; - dash_array[dash_count++] = atof(s) * linewidth; + stroke.dashlist[stroke.dashlen++] = atof(s) * stroke.linewidth; while (*s && *s != ' ') s++; } - - gs_setdash(ctx->pgs, dash_array, dash_count, dash_offset); - } - else - { - gs_setdash(ctx->pgs, NULL, 0, 0.0); } - if (transform_att || transform_tag) - { - gs_matrix transform; - - if (transform_att) - xps_parse_render_transform(ctx, transform_att, &transform); - if (transform_tag) - xps_parse_matrix_transform(ctx, transform_tag, &transform); - - gs_concat(ctx->pgs, &transform); - } + transform = fz_identity; + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + ctm = fz_concat(ctm, transform); if (clip_att || clip_tag) { + ctx->path = fz_newpath(); if (clip_att) xps_parse_abbreviated_geometry(ctx, clip_att); if (clip_tag) xps_parse_path_geometry(ctx, dict, clip_tag, 0); - xps_clip(ctx); - } - -#if 0 // XXX - if (opacity_att || opacity_mask_tag) - { - /* clip the bounds with the actual path */ - if (data_att) - xps_parse_abbreviated_geometry(ctx, data_att); - if (data_tag) - xps_parse_path_geometry(ctx, dict, data_tag, 0); - xps_update_bounds(ctx, &saved_bounds_opacity); - gs_newpath(ctx->pgs); + xps_clip(ctx, ctm); } -#endif - code = xps_begin_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + code = xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); if (code) { - gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot create transparency group"); +// fz_grestore(ctx->pgs); + return fz_rethrow(code, "cannot create transparency group"); } if (fill_att) { +printf(" fill-att\n"); xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples); if (fill_opacity_att) samples[0] = atof(fill_opacity_att); xps_set_color(ctx, colorspace, samples); + ctx->path = fz_newpath(); if (data_att) xps_parse_abbreviated_geometry(ctx, data_att); if (data_tag) xps_parse_path_geometry(ctx, dict, data_tag, 0); - xps_fill(ctx); + xps_fill(ctx, ctm); } if (fill_tag) { +printf(" fill-tag\n"); + ctx->path = fz_newpath(); if (data_att) xps_parse_abbreviated_geometry(ctx, data_att); if (data_tag) xps_parse_path_geometry(ctx, dict, data_tag, 0); - code = xps_parse_brush(ctx, fill_uri, dict, fill_tag); + code = xps_parse_brush(ctx, ctm, fill_uri, dict, fill_tag); if (code < 0) { xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot parse fill brush"); +// fz_grestore(ctx->pgs); + return fz_rethrow(code, "cannot parse fill brush"); } } if (stroke_att) { +printf(" stroke_att\n"); xps_parse_color(ctx, base_uri, stroke_att, &colorspace, samples); if (stroke_opacity_att) samples[0] = atof(stroke_opacity_att); xps_set_color(ctx, colorspace, samples); + ctx->path = fz_newpath(); if (data_att) xps_parse_abbreviated_geometry(ctx, data_att); if (data_tag) xps_parse_path_geometry(ctx, dict, data_tag, 1); - gs_stroke(ctx->pgs); + xps_stroke(ctx, ctm, &stroke); } if (stroke_tag) { +printf(" stroke_tag\n"); + ctx->path = fz_newpath(); if (data_att) xps_parse_abbreviated_geometry(ctx, data_att); if (data_tag) xps_parse_path_geometry(ctx, dict, data_tag, 1); ctx->fill_rule = 1; /* over-ride fill rule when converting outline to stroked */ - gs_strokepath2(ctx->pgs); + xps_clipstroke(ctx, ctm, &stroke); - code = xps_parse_brush(ctx, stroke_uri, dict, stroke_tag); + code = xps_parse_brush(ctx, ctm, stroke_uri, dict, stroke_tag); if (code < 0) { xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot parse stroke brush"); +// fz_grestore(ctx->pgs); + return fz_rethrow(code, "cannot parse stroke brush"); } + + ctx->dev->popclip(ctx->dev); } xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - gs_grestore(ctx->pgs); +// fz_grestore(ctx->pgs); return 0; } diff --git a/xps/xpspng.c b/xps/xpspng.c index d88461cc..c7ed90bc 100644 --- a/xps/xpspng.c +++ b/xps/xpspng.c @@ -1,29 +1,7 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. +#include "fitz.h" +#include "muxps.h" - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - PNG image support */ - -#include "ghostxps.h" - -#include "stream.h" -#include "strimpl.h" -#include "gsstate.h" - -/* silence a warning where #if SHARE_LIBPNG is used when it's undefined */ -#ifndef SHARE_LIBPNG -#define SHARE_LIBPNG 0 -#endif -#include "png_.h" +#include "png.h" /* * PNG using libpng directly (no gs wrappers) @@ -48,15 +26,13 @@ xps_png_read(png_structp png, png_bytep data, png_size_t length) static png_voidp xps_png_malloc(png_structp png, png_size_t size) { - gs_memory_t *mem = png_get_mem_ptr(png); - return gs_alloc_bytes(mem, size, "libpng"); + return fz_malloc(size); } static void xps_png_free(png_structp png, png_voidp ptr) { - gs_memory_t *mem = png_get_mem_ptr(png); - gs_free_object(mem, ptr, "libpng"); + fz_free(ptr); } /* This only determines if we have an alpha value */ @@ -77,15 +53,15 @@ xps_png_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen) png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, - ctx->memory, xps_png_malloc, xps_png_free); + ctx, xps_png_malloc, xps_png_free); if (!png) { - gs_warn("png_create_read_struct"); + fz_warn("png_create_read_struct"); return 0; } info = png_create_info_struct(png); if (!info) { - gs_warn("png_create_info_struct"); + fz_warn("png_create_info_struct"); return 0; } @@ -99,7 +75,7 @@ xps_png_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen) if (setjmp(png_jmpbuf(png))) { png_destroy_read_struct(&png, &info, NULL); - gs_warn("png reading failed"); + fz_warn("png reading failed"); return 0; } @@ -123,7 +99,7 @@ xps_png_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen) break; default: - gs_warn("cannot handle this png color type"); + fz_warn("cannot handle this png color type"); has_alpha = 0; break; } @@ -156,13 +132,13 @@ xps_decode_png(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image) png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, - ctx->memory, xps_png_malloc, xps_png_free); + ctx, xps_png_malloc, xps_png_free); if (!png) - return gs_throw(-1, "png_create_read_struct"); + return fz_throw("png_create_read_struct"); info = png_create_info_struct(png); if (!info) - return gs_throw(-1, "png_create_info_struct"); + return fz_throw("png_create_info_struct"); png_set_read_fn(png, &io, xps_png_read); png_set_crc_action(png, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE); @@ -174,7 +150,7 @@ xps_decode_png(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image) if (setjmp(png_jmpbuf(png))) { png_destroy_read_struct(&png, &info, NULL); - return gs_throw(-1, "png reading failed"); + return fz_throw("png reading failed"); } /* @@ -225,27 +201,27 @@ xps_decode_png(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image) switch (png_get_color_type(png, info)) { case PNG_COLOR_TYPE_GRAY: - image->colorspace = ctx->gray; + image->colorspace = fz_devicegray; image->hasalpha = 0; break; case PNG_COLOR_TYPE_RGB: - image->colorspace = ctx->srgb; + image->colorspace = fz_devicergb; image->hasalpha = 0; break; case PNG_COLOR_TYPE_GRAY_ALPHA: - image->colorspace = ctx->gray; + image->colorspace = fz_devicegray; image->hasalpha = 1; break; case PNG_COLOR_TYPE_RGB_ALPHA: - image->colorspace = ctx->srgb; + image->colorspace = fz_devicergb; image->hasalpha = 1; break; default: - return gs_throw(-1, "cannot handle this png color type"); + return fz_throw("cannot handle this png color type"); } /* @@ -289,5 +265,5 @@ xps_decode_png(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image) png_destroy_read_struct(&png, &info, NULL); - return gs_okay; + return fz_okay; } diff --git a/xps/xpsresource.c b/xps/xpsresource.c index a21d6bed..399c067a 100644 --- a/xps/xpsresource.c +++ b/xps/xpsresource.c @@ -1,19 +1,5 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - resource functions */ - -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" static xps_item_t * xps_find_resource(xps_context_t *ctx, xps_resource_t *dict, char *name, char **urip) @@ -82,21 +68,21 @@ xps_parse_remote_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, part = xps_read_part(ctx, part_name); if (!part) { - return gs_throw1(-1, "cannot find remote resource part '%s'", part_name); + return fz_throw("cannot find remote resource part '%s'", part_name); } xml = xps_parse_xml(ctx, part->data, part->size); if (!xml) { xps_free_part(ctx, part); - return gs_rethrow(-1, "cannot parse xml"); + return fz_rethrow(-1, "cannot parse xml"); } if (strcmp(xps_tag(xml), "ResourceDictionary")) { xps_free_item(ctx, xml); xps_free_part(ctx, part); - return gs_throw1(-1, "expected ResourceDictionary element (found %s)", xps_tag(xml)); + return fz_throw("expected ResourceDictionary element (found %s)", xps_tag(xml)); } xps_strlcpy(part_uri, part_name, sizeof part_uri); @@ -109,7 +95,7 @@ xps_parse_remote_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, { xps_free_item(ctx, xml); xps_free_part(ctx, part); - return gs_rethrow1(code, "cannot parse remote resource dictionary: %s", part_uri); + return fz_rethrow(code, "cannot parse remote resource dictionary: %s", part_uri); } dict->base_xml = xml; /* pass on ownership */ @@ -117,7 +103,7 @@ xps_parse_remote_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, xps_free_part(ctx, part); *dictp = dict; - return gs_okay; + return fz_okay; } int @@ -135,8 +121,8 @@ xps_parse_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, char * { code = xps_parse_remote_resource_dictionary(ctx, dictp, base_uri, source); if (code) - return gs_rethrow(code, "cannot parse remote resource dictionary"); - return gs_okay; + return fz_rethrow(code, "cannot parse remote resource dictionary"); + return fz_okay; } head = NULL; @@ -149,7 +135,7 @@ xps_parse_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, char * { entry = xps_alloc(ctx, sizeof(xps_resource_t)); if (!entry) - return gs_throw(-1, "cannot allocate resource entry"); + return fz_throw("cannot allocate resource entry"); entry->name = key; entry->base_uri = NULL; entry->base_xml = NULL; @@ -166,7 +152,7 @@ xps_parse_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, char * } *dictp = head; - return gs_okay; + return fz_okay; } void @@ -191,13 +177,13 @@ xps_debug_resource_dictionary(xps_resource_t *dict) while (dict) { if (dict->base_uri) - dprintf1("URI = '%s'\n", dict->base_uri); - dprintf2("KEY = '%s' VAL = %p\n", dict->name, dict->data); + printf("URI = '%s'\n", dict->base_uri); + printf("KEY = '%s' VAL = %p\n", dict->name, dict->data); if (dict->parent) { - dputs("PARENT = {\n"); + printf("PARENT = {\n"); xps_debug_resource_dictionary(dict->parent); - dputs("}\n"); + printf("}\n"); } dict = dict->next; } diff --git a/xps/xpstiff.c b/xps/xpstiff.c index 3e2627ef..27e5ac2b 100644 --- a/xps/xpstiff.c +++ b/xps/xpstiff.c @@ -1,19 +1,13 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. +#include "fitz.h" +#include "muxps.h" - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - TIFF image support */ +int +xps_decode_tiff(xps_context_t *ctx, byte *buf, int len, xps_image_t *image) +{ + return fz_throw("TIFF codec is not available"); +} -#include "ghostxps.h" +#if 0 #include "stream.h" #include "strimpl.h" @@ -159,7 +153,7 @@ static const byte bitrev[256] = static int xps_report_error(stream_state * st, const char *str) { - (void) gs_throw1(-1, "%s", str); + (void) fz_throw("%s", str); return 0; } @@ -221,7 +215,7 @@ xps_decode_tiff_packbits(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *r code = s_RLD_template.process((stream_state*)&state, &scr, &scw, true); if (code == ERRC) - return gs_throw1(-1, "error in packbits data (code = %d)", code); + return fz_throw("error in packbits data (code = %d)", code); return gs_okay; } @@ -264,7 +258,7 @@ xps_decode_tiff_lzw(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, by if (code == ERRC) { s_LZWD_template.release((stream_state*)&state); - return gs_throw1(-1, "error in lzw data (code = %d)", code); + return fz_throw("error in lzw data (code = %d)", code); } s_LZWD_template.release((stream_state*)&state); @@ -296,7 +290,7 @@ xps_decode_tiff_flate(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, if (code == ERRC) { s_zlibD_template.release((stream_state*)&state); - return gs_throw1(-1, "error in flate data (code = %d)", code); + return fz_throw("error in flate data (code = %d)", code); } s_zlibD_template.release((stream_state*)&state); @@ -339,7 +333,7 @@ xps_decode_tiff_fax(xps_context_t *ctx, xps_tiff_t *tiff, int comp, byte *rp, by if (code == ERRC) { s_CFD_template.release((stream_state*)&state); - return gs_throw1(-1, "error in fax data (code = %d)", code); + return fz_throw("error in fax data (code = %d)", code); } s_CFD_template.release((stream_state*)&state); @@ -380,7 +374,7 @@ xps_decode_tiff_jpeg(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, b jddp.scanline_buffer = NULL; if ((code = gs_jpeg_create_decompress(&state)) < 0) - return gs_throw(-1, "error in gs_jpeg_create_decompress"); + return fz_throw("error in gs_jpeg_create_decompress"); s_DCTD_template.init((stream_state*)&state); @@ -397,7 +391,7 @@ xps_decode_tiff_jpeg(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, b code = gs_jpeg_read_header(&state, FALSE); if (code != JPEG_HEADER_TABLES_ONLY) - return gs_throw(-1, "error in jpeg table data"); + return fz_throw("error in jpeg table data"); } /* @@ -408,7 +402,7 @@ xps_decode_tiff_jpeg(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, b srcmgr->bytes_in_buffer = rl - rp; if ((code = gs_jpeg_read_header(&state, TRUE)) < 0) - return gs_throw(-1, "error in jpeg_read_header"); + return fz_throw("error in jpeg_read_header"); /* when TIFF says RGB and libjpeg says YCbCr, libjpeg is wrong */ if (tiff->photometric == 2 && jddp.dinfo.jpeg_color_space == JCS_YCbCr) @@ -421,7 +415,7 @@ xps_decode_tiff_jpeg(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, b */ if ((code = gs_jpeg_start_decompress(&state)) < 0) - return gs_throw(-1, "error in jpeg_start_decompress"); + return fz_throw("error in jpeg_start_decompress"); stride = jddp.dinfo.output_width * jddp.dinfo.output_components; @@ -439,7 +433,7 @@ xps_decode_tiff_jpeg(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, b */ if ((code = gs_jpeg_finish_decompress(&state)) < 0) - return gs_throw(-1, "error in jpeg_finish_decompress"); + return fz_throw("error in jpeg_finish_decompress"); gs_jpeg_destroy(&state); @@ -537,16 +531,16 @@ xps_expand_colormap(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) /* image can be with or without extrasamples: comps is 1 or 2 */ if (image->comps != 1 && image->comps != 2) - return gs_throw(-1, "invalid number of samples for RGBPal"); + return fz_throw("invalid number of samples for RGBPal"); if (image->bits != 4 && image->bits != 8) - return gs_throw(-1, "invalid number of bits for RGBPal"); + return fz_throw("invalid number of bits for RGBPal"); stride = image->width * (image->comps + 2); samples = xps_alloc(ctx, stride * image->height); if (!samples) - return gs_throw(-1, "out of memory: samples"); + return fz_throw("out of memory: samples"); for (y = 0; y < image->height; y++) { @@ -601,10 +595,10 @@ xps_decode_tiff_strips(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) unsigned i; if (!tiff->rowsperstrip || !tiff->stripoffsets || !tiff->rowsperstrip) - return gs_throw(-1, "no image data in tiff; maybe it is tiled"); + return fz_throw("no image data in tiff; maybe it is tiled"); if (tiff->planar != 1) - return gs_throw(-1, "image data is not in chunky format"); + return fz_throw("image data is not in chunky format"); image->width = tiff->imagewidth; image->height = tiff->imagelength; @@ -634,7 +628,7 @@ xps_decode_tiff_strips(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) image->colorspace = ctx->srgb; break; default: - return gs_throw1(-1, "unknown photometric: %d", tiff->photometric); + return fz_throw("unknown photometric: %d", tiff->photometric); } switch (tiff->resolutionunit) @@ -663,7 +657,7 @@ xps_decode_tiff_strips(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) image->samples = xps_alloc(ctx, image->stride * image->height); if (!image->samples) - return gs_throw(-1, "could not allocate image samples"); + return fz_throw("could not allocate image samples"); memset(image->samples, 0x55, image->stride * image->height); @@ -681,7 +675,7 @@ xps_decode_tiff_strips(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) wlen = image->samples + image->stride * image->height - wp; if (rp + rlen > tiff->ep) - return gs_throw(-1, "strip extends beyond the end of the file"); + return fz_throw("strip extends beyond the end of the file"); /* the bits are in un-natural order */ if (tiff->fillorder == 2) @@ -706,7 +700,7 @@ xps_decode_tiff_strips(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) error = xps_decode_tiff_lzw(ctx, tiff, rp, rp + rlen, wp, wp + wlen); break; case 6: - error = gs_throw(-1, "deprecated JPEG in TIFF compression not supported"); + error = fz_throw("deprecated JPEG in TIFF compression not supported"); break; case 7: error = xps_decode_tiff_jpeg(ctx, tiff, rp, rp + rlen, wp, wp + wlen); @@ -718,11 +712,11 @@ xps_decode_tiff_strips(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) error = xps_decode_tiff_packbits(ctx, tiff, rp, rp + rlen, wp, wp + wlen); break; default: - error = gs_throw1(-1, "unknown TIFF compression: %d", tiff->compression); + error = fz_throw("unknown TIFF compression: %d", tiff->compression); } if (error) - return gs_rethrow1(error, "could not decode strip %d", row / tiff->rowsperstrip); + return fz_rethrow(error, "could not decode strip %d", row / tiff->rowsperstrip); /* scramble the bits back into original order */ if (tiff->fillorder == 2) @@ -749,7 +743,7 @@ xps_decode_tiff_strips(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) { error = xps_expand_colormap(ctx, tiff, image); if (error) - return gs_rethrow(error, "could not expand colormap"); + return fz_rethrow(error, "could not expand colormap"); } /* WhiteIsZero .. invert */ @@ -895,7 +889,7 @@ xps_read_tiff_tag(xps_context_t *ctx, xps_tiff_t *tiff, unsigned offset) case ICCProfile: tiff->profile = xps_alloc(ctx, count); if (!tiff->profile) - return gs_throw(-1, "could not allocate embedded icc profile"); + return fz_throw("could not allocate embedded icc profile"); /* ICC profile data type is set to UNDEFINED. * TBYTE reading not correct in xps_read_tiff_tag_value */ xps_read_tiff_bytes(tiff->profile, tiff, value, count); @@ -910,21 +904,21 @@ xps_read_tiff_tag(xps_context_t *ctx, xps_tiff_t *tiff, unsigned offset) case StripOffsets: tiff->stripoffsets = (unsigned*) xps_alloc(ctx, count * sizeof(unsigned)); if (!tiff->stripoffsets) - return gs_throw(-1, "could not allocate strip offsets"); + return fz_throw("could not allocate strip offsets"); xps_read_tiff_tag_value(tiff->stripoffsets, tiff, type, value, count); break; case StripByteCounts: tiff->stripbytecounts = (unsigned*) xps_alloc(ctx, count * sizeof(unsigned)); if (!tiff->stripbytecounts) - return gs_throw(-1, "could not allocate strip byte counts"); + return fz_throw("could not allocate strip byte counts"); xps_read_tiff_tag_value(tiff->stripbytecounts, tiff, type, value, count); break; case ColorMap: tiff->colormap = (unsigned*) xps_alloc(ctx, count * sizeof(unsigned)); if (!tiff->colormap) - return gs_throw(-1, "could not allocate color map"); + return fz_throw("could not allocate color map"); xps_read_tiff_tag_value(tiff->colormap, tiff, type, value, count); break; @@ -932,7 +926,7 @@ xps_read_tiff_tag(xps_context_t *ctx, xps_tiff_t *tiff, unsigned offset) case TileLength: case TileOffsets: case TileByteCounts: - return gs_throw(-1, "tiled tiffs not supported"); + return fz_throw("tiled tiffs not supported"); default: /* printf("unknown tag: %d t=%d n=%d\n", tag, type, count); */ @@ -990,12 +984,12 @@ xps_decode_tiff_header(xps_context_t *ctx, xps_tiff_t *tiff, byte *buf, int len) tiff->order = TII; tiff->order = readshort(tiff); if (tiff->order != TII && tiff->order != TMM) - return gs_throw(-1, "not a TIFF file, wrong magic marker"); + return fz_throw("not a TIFF file, wrong magic marker"); /* check version */ version = readshort(tiff); if (version != 42) - return gs_throw(-1, "not a TIFF file, wrong version marker"); + return fz_throw("not a TIFF file, wrong version marker"); /* get offset of IFD */ offset = readlong(tiff); @@ -1013,7 +1007,7 @@ xps_decode_tiff_header(xps_context_t *ctx, xps_tiff_t *tiff, byte *buf, int len) { error = xps_read_tiff_tag(ctx, tiff, offset); if (error) - return gs_rethrow(error, "could not read TIFF header tag"); + return fz_rethrow(error, "could not read TIFF header tag"); offset += 12; } @@ -1029,7 +1023,7 @@ xps_decode_tiff(xps_context_t *ctx, byte *buf, int len, xps_image_t *image) error = xps_decode_tiff_header(ctx, tiff, buf, len); if (error) - return gs_rethrow(error, "cannot decode tiff header"); + return fz_rethrow(error, "cannot decode tiff header"); /* * Decode the image strips @@ -1040,7 +1034,7 @@ xps_decode_tiff(xps_context_t *ctx, byte *buf, int len, xps_image_t *image) error = xps_decode_tiff_strips(ctx, tiff, image); if (error) - return gs_rethrow(error, "could not decode image data"); + return fz_rethrow(error, "could not decode image data"); /* * Byte swap 16-bit images to big endian if necessary. @@ -1089,3 +1083,4 @@ xps_tiff_has_alpha(xps_context_t *ctx, byte *buf, int len) return tiff->extrasamples == 2 || tiff->extrasamples == 1; } +#endif diff --git a/xps/xpstile.c b/xps/xpstile.c index d7118b52..4f2f57ec 100644 --- a/xps/xpstile.c +++ b/xps/xpstile.c @@ -1,19 +1,5 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - tiles for pattern rendering */ - -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" /* * Parse a tiling brush (visual and image brushes at this time) common @@ -28,7 +14,7 @@ struct tile_closure_s char *base_uri; xps_resource_t *dict; xps_item_t *tag; - gs_rect viewbox; + fz_rect viewbox; int tile_mode; void *user; int (*func)(xps_context_t*, char*, xps_resource_t*, xps_item_t*, void*); @@ -40,6 +26,7 @@ xps_paint_tiling_brush_clipped(struct tile_closure_s *c) xps_context_t *ctx = c->ctx; int code; +#if 0 gs_moveto(ctx->pgs, c->viewbox.p.x, c->viewbox.p.y); gs_lineto(ctx->pgs, c->viewbox.p.x, c->viewbox.q.y); gs_lineto(ctx->pgs, c->viewbox.q.x, c->viewbox.q.y); @@ -47,14 +34,16 @@ xps_paint_tiling_brush_clipped(struct tile_closure_s *c) gs_closepath(ctx->pgs); gs_clip(ctx->pgs); gs_newpath(ctx->pgs); +#endif code = c->func(c->ctx, c->base_uri, c->dict, c->tag, c->user); if (code < 0) - return gs_rethrow(code, "cannot draw clipped tile"); + return fz_rethrow(code, "cannot draw clipped tile"); return 0; } +#if 0 static int xps_paint_tiling_brush(const gs_client_color *pcc, gs_state *pgs) { @@ -113,103 +102,14 @@ xps_paint_tiling_brush(const gs_client_color *pcc, gs_state *pgs) cleanup: gs_grestore(ctx->pgs); ctx->pgs = saved_pgs; - return gs_rethrow(code, "cannot draw tile"); -} - -int -xps_high_level_pattern(xps_context_t *ctx) -{ - gs_matrix m; - gs_rect bbox; - gs_fixed_rect clip_box; - int code; - gx_device_color *pdc = gs_currentdevicecolor_inline(ctx->pgs); - const gs_client_pattern *ppat = gs_getpattern(&pdc->ccolor); - gs_pattern1_instance_t *pinst = - (gs_pattern1_instance_t *)gs_currentcolor(ctx->pgs)->pattern; - - code = gx_pattern_cache_add_dummy_entry((gs_imager_state *)ctx->pgs, - pinst, ctx->pgs->device->color_info.depth); - if (code < 0) - return code; - - code = gs_gsave(ctx->pgs); - if (code < 0) - return code; - - dev_proc(ctx->pgs->device, get_initial_matrix)(ctx->pgs->device, &m); - gs_setmatrix(ctx->pgs, &m); - code = gs_bbox_transform(&ppat->BBox, &ctm_only(ctx->pgs), &bbox); - if (code < 0) { - gs_grestore(ctx->pgs); - return code; - } - clip_box.p.x = float2fixed(bbox.p.x); - clip_box.p.y = float2fixed(bbox.p.y); - clip_box.q.x = float2fixed(bbox.q.x); - clip_box.q.y = float2fixed(bbox.q.y); - code = gx_clip_to_rectangle(ctx->pgs, &clip_box); - if (code < 0) { - gs_grestore(ctx->pgs); - return code; - } - code = dev_proc(ctx->pgs->device, pattern_manage)(ctx->pgs->device, pinst->id, pinst, - pattern_manage__start_accum); - if (code < 0) { - gs_grestore(ctx->pgs); - return code; - } - - code = xps_paint_tiling_brush(&pdc->ccolor, ctx->pgs); - if (code) { - gs_grestore(ctx->pgs); - return gs_rethrow(code, "high level pattern brush function failed"); - } - - code = gs_grestore(ctx->pgs); - if (code < 0) - return code; - - code = dev_proc(ctx->pgs->device, pattern_manage)(ctx->pgs->device, gx_no_bitmap_id, NULL, - pattern_manage__finish_accum); - - return code; -} - -static int -xps_remap_pattern(const gs_client_color *pcc, gs_state *pgs) -{ - const gs_client_pattern *ppat = gs_getpattern(pcc); - struct tile_closure_s *c = ppat->client_data; - xps_context_t *ctx = c->ctx; - int code; - - /* pgs->device is the newly created pattern accumulator, but we want to test the device - * that is 'behind' that, the actual output device, so we use the one from - * the saved XPS graphics state. - */ - code = dev_proc(ctx->pgs->device, pattern_manage)(ctx->pgs->device, ppat->uid.id, ppat, - pattern_manage__can_accum); - - if (code == 1) { - /* Device handles high-level patterns, so return 'remap'. - * This closes the internal accumulator device, as we no longer need - * it, and the error trickles back up to the PDL client. The client - * must then take action to start the device's accumulator, draw the - * pattern, close the device's accumulator and generate a cache entry. - */ - return gs_error_Remap_Color; - } else { - code = xps_paint_tiling_brush(pcc, pgs); - if (code) - return gs_rethrow(code, "remap pattern brush function failed"); - return 0; - } + return fz_rethrow(code, "cannot draw tile"); } +#endif int -xps_parse_tiling_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root, - int (*func)(xps_context_t*, char*, xps_resource_t*, xps_item_t*, void*), void *user) +xps_parse_tiling_brush(xps_context_t *ctx, fz_matrix ctm, + char *base_uri, xps_resource_t *dict, xps_item_t *root, + int (*func)(xps_context_t*, fz_matrix, char*, xps_resource_t*, xps_item_t*, void*), void *user) { xps_item_t *node; int code; @@ -224,9 +124,9 @@ xps_parse_tiling_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *transform_tag = NULL; - gs_matrix transform; - gs_rect viewbox; - gs_rect viewport; + fz_matrix transform; + fz_rect viewbox; + fz_rect viewport; float scalex, scaley; int tile_mode; @@ -248,30 +148,28 @@ xps_parse_tiling_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); - gs_make_identity(&transform); + transform = fz_identity; if (transform_att) xps_parse_render_transform(ctx, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); - viewbox.p.x = 0.0; viewbox.p.y = 0.0; - viewbox.q.x = 1.0; viewbox.q.y = 1.0; + viewbox = fz_unitrect; if (viewbox_att) xps_parse_rectangle(ctx, viewbox_att, &viewbox); - viewport.p.x = 0.0; viewport.p.y = 0.0; - viewport.q.x = 1.0; viewport.q.y = 1.0; + viewport = fz_unitrect; if (viewport_att) xps_parse_rectangle(ctx, viewport_att, &viewport); /* some sanity checks on the viewport/viewbox size */ - if (fabs(viewport.q.x - viewport.p.x) < 0.01) return 0; - if (fabs(viewport.q.y - viewport.p.y) < 0.01) return 0; - if (fabs(viewbox.q.x - viewbox.p.x) < 0.01) return 0; - if (fabs(viewbox.q.y - viewbox.p.y) < 0.01) return 0; + if (fabs(viewport.x1 - viewport.x0) < 0.01) return 0; + if (fabs(viewport.y1 - viewport.y0) < 0.01) return 0; + if (fabs(viewbox.x1 - viewbox.x0) < 0.01) return 0; + if (fabs(viewbox.y1 - viewbox.y0) < 0.01) return 0; - scalex = (viewport.q.x - viewport.p.x) / (viewbox.q.x - viewbox.p.x); - scaley = (viewport.q.y - viewport.p.y) / (viewbox.q.y - viewbox.p.y); + scalex = (viewport.x1 - viewport.x0) / (viewbox.x1 - viewbox.x0); + scaley = (viewport.y1 - viewport.y0) / (viewbox.y1 - viewbox.y0); tile_mode = TILE_NONE; if (tile_mode_att) @@ -288,24 +186,25 @@ xps_parse_tiling_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, tile_mode = TILE_FLIP_X_Y; } - gs_gsave(ctx->pgs); +// gs_gsave(ctx->pgs); - code = xps_begin_opacity(ctx, base_uri, dict, opacity_att, NULL); + code = xps_begin_opacity(ctx, ctm, base_uri, dict, opacity_att, NULL); if (code) { - gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot create transparency group"); +// gs_grestore(ctx->pgs); + return fz_rethrow(code, "cannot create transparency group"); } /* TODO(tor): check viewport and tiling to see if we can set it to TILE_NONE */ +#if 0 if (tile_mode != TILE_NONE) { struct tile_closure_s closure; gs_client_pattern gspat; gs_client_color gscolor; - gs_color_space *cs; + fz_colorspace *cs; closure.ctx = ctx; closure.base_uri = base_uri; @@ -346,9 +245,9 @@ xps_parse_tiling_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, gspat.YStep *= 2; } - gs_matrix_translate(&transform, viewport.p.x, viewport.p.y, &transform); - gs_matrix_scale(&transform, scalex, scaley, &transform); - gs_matrix_translate(&transform, -viewbox.p.x, -viewbox.p.y, &transform); + fz_matrix_translate(&transform, viewport.p.x, viewport.p.y, &transform); + fz_matrix_scale(&transform, scalex, scaley, &transform); + fz_matrix_translate(&transform, -viewbox.p.x, -viewbox.p.y, &transform); cs = ctx->srgb; gs_setcolorspace(ctx->pgs, cs); @@ -387,13 +286,14 @@ xps_parse_tiling_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, { xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); gs_grestore(ctx->pgs); - return gs_rethrow(code, "cannot draw tile"); + return fz_rethrow(code, "cannot draw tile"); } } +#endif xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); - gs_grestore(ctx->pgs); +// gs_grestore(ctx->pgs); return 0; } diff --git a/xps/xpstop.c b/xps/xpstop.c index 13149da6..afc64569 100644 --- a/xps/xpstop.c +++ b/xps/xpstop.c @@ -1,576 +1,384 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. +#include "fitz.h" +#include "muxps.h" - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* Top-level API implementation of XML Paper Specification */ - -/* Language wrapper implementation (see pltop.h) */ - -#include "ghostxps.h" - -#include "pltop.h" -#include "plparse.h" /* for e_ExitLanguage */ - -#include "gxdevice.h" /* so we can include gxht.h below */ -#include "gxht.h" /* gsht1.h is incomplete, we need storage size of gs_halftone */ -#include "gsht1.h" - -int xps_zip_trace = 0; -int xps_doc_trace = 0; - -static int xps_install_halftone(xps_context_t *ctx, gx_device *pdevice); +#ifdef _MSC_VER +#include +#else +#include +#endif -#define XPS_PARSER_MIN_INPUT_SIZE (8192 * 4) +char *output = NULL; +float resolution = 72; -/* - * The XPS interpeter is identical to pl_interp_t. - * The XPS interpreter instance is derived from pl_interp_instance_t. - */ +int showxml = 0; +int showtext = 0; +int showtime = 0; +int showmd5 = 0; +int savealpha = 0; +int uselist = 1; -typedef struct xps_interp_instance_s xps_interp_instance_t; +fz_colorspace *colorspace; +fz_glyphcache *glyphcache; +char *filename; -struct xps_interp_instance_s +/* stub function for automatic resolution of indirect objects in obj_simple.c */ +int pdf_cacheobject(struct pdf_xref_s *xref, int num, int gen) { - pl_interp_instance_t pl; /* common part: must be first */ - - pl_page_action_t pre_page_action; /* action before page out */ - void *pre_page_closure; /* closure to call pre_page_action with */ - pl_page_action_t post_page_action; /* action before page out */ - void *post_page_closure; /* closure to call post_page_action with */ - - xps_context_t *ctx; - FILE *scratch_file; - char scratch_name[gp_file_name_sizeof]; -}; + return fz_throw("not a pdf"); +} -/* version and build date are not currently used */ -#define XPS_VERSION NULL -#define XPS_BUILD_DATE NULL +struct { + int count, total; + int min, max; + int minpage, maxpage; +} timing; -static const pl_interp_characteristics_t * -xps_imp_characteristics(const pl_interp_implementation_t *pimpl) +static void die(fz_error error) { - static pl_interp_characteristics_t xps_characteristics = - { - "XPS", - "PK", /* string to recognize XPS files */ - "Artifex", - XPS_VERSION, - XPS_BUILD_DATE, - XPS_PARSER_MIN_INPUT_SIZE, /* Minimum input size */ - }; - return &xps_characteristics; + fz_catch(error, "aborting"); + exit(1); } -static int -xps_imp_allocate_interp(pl_interp_t **ppinterp, - const pl_interp_implementation_t *pimpl, - gs_memory_t *pmem) +static void usage(void) { - static pl_interp_t interp; /* there's only one interpreter */ - *ppinterp = &interp; - return 0; + fprintf(stderr, + "usage: xpsdraw [options] input.xps [pages]\n" + "\t-o -\toutput filename (%%d for page number)\n" + "\t\tsupported formats: pgm, ppm, pam, png\n" + "\t-r -\tresolution in dpi (default: 72)\n" + "\t-a\tsave alpha channel (only pam and png)\n" + "\t-g\trender in grayscale\n" + "\t-m\tshow timing information\n" + "\t-t\tshow text (-tt for xml)\n" + "\t-x\tshow display list\n" + "\t-d\tdisable use of display list\n" + "\t-5\tshow md5 checksums\n" + "\tpages\tcomma separated list of ranges\n"); + exit(1); } -/* Do per-instance interpreter allocation/init. No device is set yet */ -static int -xps_imp_allocate_interp_instance(pl_interp_instance_t **ppinstance, - pl_interp_t *pinterp, - gs_memory_t *pmem) +static int gettime(void) { - xps_interp_instance_t *instance; - xps_context_t *ctx; - gs_state *pgs; - int code; - - instance = (xps_interp_instance_t *) gs_alloc_bytes(pmem, - sizeof(xps_interp_instance_t), "xps_imp_allocate_interp_instance"); - - ctx = (xps_context_t *) gs_alloc_bytes(pmem, - sizeof(xps_context_t), "xps_imp_allocate_interp_instance"); - - pgs = gs_state_alloc(pmem); -#ifdef ICCBRANCH - gsicc_init_iccmanager(pgs); -#endif - memset(ctx, 0, sizeof(xps_context_t)); - - if (!instance || !ctx || !pgs) + static struct timeval first; + static int once = 1; + struct timeval now; + if (once) { - if (instance) - gs_free_object(pmem, instance, "xps_imp_allocate_interp_instance"); - if (ctx) - gs_free_object(pmem, ctx, "xps_imp_allocate_interp_instance"); - if (pgs) - gs_state_free(pgs); - return gs_error_VMerror; + gettimeofday(&first, NULL); + once = 0; } - - ctx->instance = instance; - ctx->memory = pmem; - ctx->pgs = pgs; - /* Declare PDL client support for high level patterns, for the benefit - * of pdfwrite and other high-level devices - */ - ctx->pgs->have_pattern_streams = true; - ctx->fontdir = NULL; - ctx->file = NULL; - ctx->zip_count = 0; - ctx->zip_table = NULL; - - /* Gray, RGB and CMYK profiles set when color spaces installed in graphics lib */ - ctx->gray = gs_cspace_new_DeviceGray(ctx->memory); - ctx->cmyk = gs_cspace_new_DeviceCMYK(ctx->memory); - ctx->srgb = gs_cspace_new_DeviceRGB(ctx->memory); - ctx->scrgb = gs_cspace_new_DeviceRGB(ctx->memory); /* This needs a different profile */ - - instance->pre_page_action = 0; - instance->pre_page_closure = 0; - instance->post_page_action = 0; - instance->post_page_closure = 0; - - instance->ctx = ctx; - instance->scratch_file = NULL; - instance->scratch_name[0] = 0; - - ctx->fontdir = gs_font_dir_alloc(ctx->memory); - gs_setaligntopixels(ctx->fontdir, 1); /* no subpixels */ - gs_setgridfittt(ctx->fontdir, 1); /* see gx_ttf_outline in gxttfn.c for values */ - - *ppinstance = (pl_interp_instance_t *)instance; - - return 0; -} - -/* Set a client language into an interperter instance */ -static int -xps_imp_set_client_instance(pl_interp_instance_t *pinstance, - pl_interp_instance_t *pclient, - pl_interp_instance_clients_t which_client) -{ - /* ignore */ - return 0; + gettimeofday(&now, NULL); + return (now.tv_sec - first.tv_sec) * 1000 + (now.tv_usec - first.tv_usec) / 1000; } -static int -xps_imp_set_pre_page_action(pl_interp_instance_t *pinstance, - pl_page_action_t action, void *closure) +static int isrange(char *s) { - xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; - instance->pre_page_action = action; - instance->pre_page_closure = closure; - return 0; -} - -static int -xps_imp_set_post_page_action(pl_interp_instance_t *pinstance, - pl_page_action_t action, void *closure) -{ - xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; - instance->post_page_action = action; - instance->post_page_closure = closure; - return 0; + while (*s) + { + if ((*s < '0' || *s > '9') && *s != '-' && *s != ',') + return 0; + s++; + } + return 1; } static int -xps_imp_set_device(pl_interp_instance_t *pinstance, gx_device *pdevice) +xps_run_page(xps_context_t *ctx, xps_page_t *page, fz_device *dev, fz_matrix ctm) { - xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; - xps_context_t *ctx = instance->ctx; int code; - gs_opendevice(pdevice); - -#ifdef ICCBRANCH - code = gsicc_init_device_profile(ctx->pgs, pdevice); - if (code < 0) - return code; -#endif - - code = gs_setdevice_no_erase(ctx->pgs, pdevice); - if (code < 0) - goto cleanup_setdevice; - - gs_setaccuratecurves(ctx->pgs, true); /* NB not sure */ - gs_setfilladjust(ctx->pgs, 0, 0); - - /* gsave and grestore (among other places) assume that */ - /* there are at least 2 gstates on the graphics stack. */ - /* Ensure that now. */ - code = gs_gsave(ctx->pgs); - if (code < 0) - goto cleanup_gsave; + ctx->dev = dev; - code = gs_erasepage(ctx->pgs); - if (code < 0) - goto cleanup_erase; - - code = xps_install_halftone(ctx, pdevice); - if (code < 0) - goto cleanup_halftone; - - return 0; - -cleanup_halftone: -cleanup_erase: - /* undo gsave */ - gs_grestore_only(ctx->pgs); /* destroys gs_save stack */ - -cleanup_gsave: - /* undo setdevice */ - gs_nulldevice(ctx->pgs); + code = xps_parse_fixed_page(ctx, ctm, page); + if (code) + return fz_rethrow(code, "cannot draw page part %s", page->name); -cleanup_setdevice: - /* nothing to undo */ - return code; -} + ctx->dev = nil; -static int -xps_imp_get_device_memory(pl_interp_instance_t *pinstance, gs_memory_t **ppmem) -{ - /* huh? we do nothing here */ - return 0; + return fz_okay; } -/* Parse an entire random access file */ -static int -xps_imp_process_file(pl_interp_instance_t *pinstance, char *filename) +static void drawpage(xps_context_t *ctx, int pagenum) { - xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; - xps_context_t *ctx = instance->ctx; - int code; + fz_error error; + xps_page_t *page; + fz_displaylist *list; + fz_device *dev; + int start; - code = xps_process_file(ctx, filename); - if (code) - gs_catch1(code, "cannot process xps file '%s'", filename); + if (showtime) + { + start = gettime(); + } - return code; -} + page = xps_load_page(ctx, pagenum - 1); + if (!page) + die(fz_throw("cannot load page %d in file '%s'", pagenum, filename)); -/* Parse a cursor-full of data */ -static int -xps_imp_process(pl_interp_instance_t *pinstance, stream_cursor_read *cursor) -{ - xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; - xps_context_t *ctx = instance->ctx; - int avail, n; + list = nil; - if (!instance->scratch_file) + if (uselist) { - instance->scratch_file = gp_open_scratch_file(ctx->memory, - "ghostxps-scratch-", instance->scratch_name, "wb"); - if (!instance->scratch_file) - { - gs_catch(gs_error_invalidfileaccess, "cannot open scratch file"); - return e_ExitLanguage; - } - if_debug1('|', "xps: open scratch file '%s'\n", instance->scratch_name); + list = fz_newdisplaylist(); + dev = fz_newlistdevice(list); + error = xps_run_page(ctx, page, dev, fz_identity); + if (error) + die(fz_rethrow(error, "cannot draw page %d in file '%s'", pagenum, filename)); + fz_freedevice(dev); } - avail = cursor->limit - cursor->ptr; - n = fwrite(cursor->ptr + 1, 1, avail, instance->scratch_file); - if (n != avail) + if (showxml) { - gs_catch(gs_error_invalidfileaccess, "cannot write to scratch file"); - return e_ExitLanguage; + dev = fz_newtracedevice(); + printf("\n", pagenum); + if (list) + fz_executedisplaylist(list, dev, fz_identity); + else + xps_run_page(ctx, page, dev, fz_identity); + printf("\n"); + fz_freedevice(dev); } - cursor->ptr = cursor->limit; - - return 0; -} -/* Skip to end of job. - * Return 1 if done, 0 ok but EOJ not found, else negative error code. - */ -static int -xps_imp_flush_to_eoj(pl_interp_instance_t *pinstance, stream_cursor_read *pcursor) -{ - /* assume XPS cannot be pjl embedded */ - pcursor->ptr = pcursor->limit; - return 0; -} + if (showtext) + { + fz_textspan *text = fz_newtextspan(); + dev = fz_newtextdevice(text); + if (list) + fz_executedisplaylist(list, dev, fz_identity); + else + xps_run_page(ctx, page, dev, fz_identity); + fz_freedevice(dev); + printf("[Page %d]\n", pagenum); + if (showtext > 1) + fz_debugtextspanxml(text); + else + fz_debugtextspan(text); + printf("\n"); + fz_freetextspan(text); + } -/* Parser action for end-of-file */ -static int -xps_imp_process_eof(pl_interp_instance_t *pinstance) -{ - xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; - xps_context_t *ctx = instance->ctx; - int code; + if (showmd5 || showtime) + printf("page %s %d", filename, pagenum); - if (instance->scratch_file) + if (output || showmd5 || showtime) { - if_debug0('|', "xps: executing scratch file\n"); - fclose(instance->scratch_file); - instance->scratch_file = NULL; - code = xps_process_file(ctx, instance->scratch_name); - unlink(instance->scratch_name); - if (code < 0) + float zoom; + fz_matrix ctm; + fz_rect rect; + fz_bbox bbox; + fz_pixmap *pix; + + rect.x0 = rect.y0 = 0; + rect.x1 = page->width; + rect.y1 = page->height; + + zoom = resolution / 72; + ctm = fz_translate(0, -page->height); + ctm = fz_concat(ctm, fz_scale(zoom, -zoom)); + bbox = fz_roundrect(fz_transformrect(ctm, rect)); + + /* TODO: banded rendering and multi-page ppm */ + + pix = fz_newpixmapwithrect(colorspace, bbox); + + if (savealpha) + fz_clearpixmap(pix); + else + fz_clearpixmapwithcolor(pix, 255); + + dev = fz_newdrawdevice(glyphcache, pix); + if (list) + fz_executedisplaylist(list, dev, ctm); + else + xps_run_page(ctx, page, dev, ctm); + fz_freedevice(dev); + + if (output) { - gs_catch(code, "cannot process XPS file"); - return e_ExitLanguage; + char buf[512]; + sprintf(buf, output, pagenum); + if (strstr(output, ".pgm") || strstr(output, ".ppm") || strstr(output, ".pnm")) + fz_writepnm(pix, buf); + else if (strstr(output, ".pam")) + fz_writepam(pix, buf, savealpha); + else if (strstr(output, ".png")) + fz_writepng(pix, buf, savealpha); } - } - - return 0; -} -/* Report any errors after running a job */ -static int -xps_imp_report_errors(pl_interp_instance_t *pinstance, - int code, /* prev termination status */ - long file_position, /* file position of error, -1 if unknown */ - bool force_to_cout /* force errors to cout */ - ) -{ - return 0; -} + if (showmd5) + { + fz_md5 md5; + unsigned char digest[16]; + int i; -/* Prepare interp instance for the next "job" */ -static int -xps_imp_init_job(pl_interp_instance_t *pinstance) -{ - xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; - xps_context_t *ctx = instance->ctx; + fz_md5init(&md5); + fz_md5update(&md5, pix->samples, pix->w * pix->h * pix->n); + fz_md5final(&md5, digest); - if (gs_debug_c('|')) - xps_zip_trace = 1; - if (gs_debug_c('|')) - xps_doc_trace = 1; + printf(" "); + for (i = 0; i < 16; i++) + printf("%02x", digest[i]); + } - ctx->font_table = xps_hash_new(ctx); - ctx->colorspace_table = xps_hash_new(ctx); + fz_droppixmap(pix); + } - ctx->start_part = NULL; + if (list) + fz_freedisplaylist(list); - ctx->use_transparency = 1; - if (getenv("XPS_DISABLE_TRANSPARENCY")) - ctx->use_transparency = 0; + if (showtime) + { + int end = gettime(); + int diff = end - start; - ctx->opacity_only = 0; - ctx->fill_rule = 0; + if (diff < timing.min) + { + timing.min = diff; + timing.minpage = pagenum; + } + if (diff > timing.max) + { + timing.max = diff; + timing.maxpage = pagenum; + } + timing.total += diff; + timing.count ++; - return 0; -} + printf(" %dms", diff); + } -static void xps_free_key_func(xps_context_t *ctx, void *ptr) -{ - xps_free(ctx, ptr); + if (showmd5 || showtime) + printf("\n"); } -static void xps_free_font_func(xps_context_t *ctx, void *ptr) -{ - xps_free_font(ctx, ptr); -} -/* Wrap up interp instance after a "job" */ -static int -xps_imp_dnit_job(pl_interp_instance_t *pinstance) +static void drawrange(xps_context_t *ctx, char *range) { - xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; - xps_context_t *ctx = instance->ctx; - int i; - - if (gs_debug_c('|')) - xps_debug_fixdocseq(ctx); - - for (i = 0; i < ctx->zip_count; i++) - xps_free(ctx, ctx->zip_table[i].name); - xps_free(ctx, ctx->zip_table); + int page, spage, epage; + char *spec, *dash; - /* TODO: free resources too */ - xps_hash_free(ctx, ctx->font_table, xps_free_key_func, xps_free_font_func); - xps_hash_free(ctx, ctx->colorspace_table, xps_free_key_func, NULL); - - xps_free_fixed_pages(ctx); - xps_free_fixed_documents(ctx); - - return 0; -} + spec = fz_strsep(&range, ","); + while (spec) + { + dash = strchr(spec, '-'); -/* Remove a device from an interperter instance */ -static int -xps_imp_remove_device(pl_interp_instance_t *pinstance) -{ - xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; - xps_context_t *ctx = instance->ctx; + if (dash == spec) + spage = epage = xps_count_pages(ctx); + else + spage = epage = atoi(spec); - int code = 0; /* first error status encountered */ - int error; + if (dash) + { + if (strlen(dash) > 1) + epage = atoi(dash + 1); + else + epage = xps_count_pages(ctx); + } - /* return to original gstate */ - gs_grestore_only(ctx->pgs); /* destroys gs_save stack */ + spage = CLAMP(spage, 1, xps_count_pages(ctx)); + epage = CLAMP(epage, 1, xps_count_pages(ctx)); - /* Deselect device */ - /* NB */ - error = gs_nulldevice(ctx->pgs); - if (code >= 0) - code = error; + if (spage < epage) + for (page = spage; page <= epage; page++) + drawpage(ctx, page); + else + for (page = spage; page >= epage; page--) + drawpage(ctx, page); - return code; + spec = fz_strsep(&range, ","); + } } -/* Deallocate a interpreter instance */ -static int -xps_imp_deallocate_interp_instance(pl_interp_instance_t *pinstance) +int main(int argc, char **argv) { - xps_interp_instance_t *instance = (xps_interp_instance_t *)pinstance; - xps_context_t *ctx = instance->ctx; - gs_memory_t *mem = ctx->memory; + int grayscale = 0; + int accelerate = 1; + xps_context_t *ctx; + int code; + int c; - /* language clients don't free the font cache machinery */ + while ((c = fz_getopt(argc, argv, "o:p:r:Aadgmtx5")) != -1) + { + switch (c) + { + case 'o': output = fz_optarg; break; + case 'r': resolution = atof(fz_optarg); break; + case 'A': accelerate = 0; break; + case 'a': savealpha = 1; break; + case 'm': showtime++; break; + case 't': showtext++; break; + case 'x': showxml++; break; + case '5': showmd5++; break; + case 'g': grayscale++; break; + case 'd': uselist = 0; break; + default: usage(); break; + } + } - // free gstate? - gs_free_object(mem, ctx, "xps_imp_deallocate_interp_instance"); - gs_free_object(mem, instance, "xps_imp_deallocate_interp_instance"); + if (fz_optind == argc) + usage(); - return 0; -} + if (!showtext && !showxml && !showtime && !showmd5 && !output) + { + printf("nothing to do\n"); + exit(0); + } -/* Do static deinit of XPS interpreter */ -static int -xps_imp_deallocate_interp(pl_interp_t *pinterp) -{ - /* nothing to do */ - return 0; -} + if (accelerate) + fz_accelerate(); -/* Parser implementation descriptor */ -const pl_interp_implementation_t xps_implementation = -{ - xps_imp_characteristics, - xps_imp_allocate_interp, - xps_imp_allocate_interp_instance, - xps_imp_set_client_instance, - xps_imp_set_pre_page_action, - xps_imp_set_post_page_action, - xps_imp_set_device, - xps_imp_init_job, - xps_imp_process_file, - xps_imp_process, - xps_imp_flush_to_eoj, - xps_imp_process_eof, - xps_imp_report_errors, - xps_imp_dnit_job, - xps_imp_remove_device, - xps_imp_deallocate_interp_instance, - xps_imp_deallocate_interp, - xps_imp_get_device_memory, -}; - -/* - * End-of-page function called by XPS parser. - */ -int -xps_show_page(xps_context_t *ctx, int num_copies, int flush) -{ - pl_interp_instance_t *pinstance = ctx->instance; - xps_interp_instance_t *instance = ctx->instance; + glyphcache = fz_newglyphcache(); - int code = 0; + colorspace = fz_devicergb; + if (grayscale) + colorspace = fz_devicegray; + if (output && strstr(output, ".pgm")) + colorspace = fz_devicegray; + if (output && strstr(output, ".ppm")) + colorspace = fz_devicergb; - /* do pre-page action */ - if (instance->pre_page_action) - { - code = instance->pre_page_action(pinstance, instance->pre_page_closure); - if (code < 0) - return code; - if (code != 0) - return 0; /* code > 0 means abort w/no error */ - } + timing.count = 0; + timing.total = 0; + timing.min = 1 << 30; + timing.max = 0; + timing.minpage = 0; + timing.maxpage = 0; - /* output the page */ - code = gs_output_page(ctx->pgs, num_copies, flush); - if (code < 0) - return code; + if (showxml) + printf("\n"); - /* do post-page action */ - if (instance->post_page_action) + while (fz_optind < argc) { - code = instance->post_page_action(pinstance, instance->post_page_closure); - if (code < 0) - return code; - } - - return 0; -} + filename = argv[fz_optind++]; -/* - * We need to install a halftone ourselves, this is not - * done automatically. - */ + ctx = xps_new_context(); + code = xps_open_file(ctx, filename); + if (code) + die(fz_rethrow(code, "cannot open document: %s", filename)); -static float -identity_transfer(floatp tint, const gx_transfer_map *ignore_map) -{ - return tint; -} + if (showxml) + printf("\n", filename); -/* The following is a 45 degree spot screen with the spots enumerated - * in a defined order. */ -static byte order16x16[256] = { - 38, 11, 14, 32, 165, 105, 90, 171, 38, 12, 14, 33, 161, 101, 88, 167, - 30, 6, 0, 16, 61, 225, 231, 125, 30, 6, 1, 17, 63, 222, 227, 122, - 27, 3, 8, 19, 71, 242, 205, 110, 28, 4, 9, 20, 74, 246, 208, 106, - 35, 24, 22, 40, 182, 46, 56, 144, 36, 25, 22, 41, 186, 48, 58, 148, - 152, 91, 81, 174, 39, 12, 15, 34, 156, 95, 84, 178, 40, 13, 16, 34, - 69, 212, 235, 129, 31, 7, 2, 18, 66, 216, 239, 133, 32, 8, 2, 18, - 79, 254, 203, 114, 28, 4, 10, 20, 76, 250, 199, 118, 29, 5, 10, 21, - 193, 44, 54, 142, 36, 26, 23, 42, 189, 43, 52, 139, 37, 26, 24, 42, - 39, 12, 15, 33, 159, 99, 87, 169, 38, 11, 14, 33, 163, 103, 89, 172, - 31, 7, 1, 17, 65, 220, 229, 123, 30, 6, 1, 17, 62, 223, 233, 127, - 28, 4, 9, 20, 75, 248, 210, 108, 27, 3, 9, 19, 72, 244, 206, 112, - 36, 25, 23, 41, 188, 49, 60, 150, 35, 25, 22, 41, 184, 47, 57, 146, - 157, 97, 85, 180, 40, 13, 16, 35, 154, 93, 83, 176, 39, 13, 15, 34, - 67, 218, 240, 135, 32, 8, 3, 19, 70, 214, 237, 131, 31, 7, 2, 18, - 78, 252, 197, 120, 29, 5, 11, 21, 80, 255, 201, 116, 29, 5, 10, 21, - 191, 43, 51, 137, 37, 27, 24, 43, 195, 44, 53, 140, 37, 26, 23, 42 -}; - -#define source_phase_x 4 -#define source_phase_y 0 + if (fz_optind == argc || !isrange(argv[fz_optind])) + drawrange(ctx, "1-"); + if (fz_optind < argc && isrange(argv[fz_optind])) + drawrange(ctx, argv[fz_optind++]); -static int -xps_install_halftone(xps_context_t *ctx, gx_device *pdevice) -{ - gs_halftone ht; - gs_string thresh; - int code; + if (showxml) + printf("\n"); - int width = 16; - int height = 16; - thresh.data = order16x16; - thresh.size = width * height; + xps_free_context(ctx); + } - if (gx_device_must_halftone(pdevice)) + if (showtime) { - ht.type = ht_type_threshold; - ht.params.threshold.width = width; - ht.params.threshold.height = height; - ht.params.threshold.thresholds.data = thresh.data; - ht.params.threshold.thresholds.size = thresh.size; - ht.params.threshold.transfer = 0; - ht.params.threshold.transfer_closure.proc = 0; - - gs_settransfer(ctx->pgs, identity_transfer); - - code = gs_sethalftone(ctx->pgs, &ht); - if (code < 0) - return gs_throw(code, "could not install halftone"); - - code = gs_sethalftonephase(ctx->pgs, 0, 0); - if (code < 0) - return gs_throw(code, "could not set halftone phase"); + printf("total %dms / %d pages for an average of %dms\n", + timing.total, timing.count, timing.total / timing.count); + printf("fastest page %d: %dms\n", timing.minpage, timing.min); + printf("slowest page %d: %dms\n", timing.maxpage, timing.max); } + fz_freeglyphcache(glyphcache); + return 0; } diff --git a/xps/xpsutf.c b/xps/xpsutf.c index 3e1c05c9..d5c1974e 100644 --- a/xps/xpsutf.c +++ b/xps/xpsutf.c @@ -1,19 +1,5 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - unicode text functions */ - -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" /* * http://tools.ietf.org/html/rfc3629 diff --git a/xps/xpsvisual.c b/xps/xpsvisual.c index f23ef991..0b6fb4da 100644 --- a/xps/xpsvisual.c +++ b/xps/xpsvisual.c @@ -1,19 +1,5 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - visual brush functions */ - -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y }; @@ -25,13 +11,14 @@ struct userdata }; static int -xps_paint_visual_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root, void *visual_tag) +xps_paint_visual_brush(xps_context_t *ctx, fz_matrix ctm, + char *base_uri, xps_resource_t *dict, xps_item_t *root, void *visual_tag) { - return xps_parse_element(ctx, base_uri, dict, (xps_item_t *)visual_tag); + return xps_parse_element(ctx, ctm, base_uri, dict, (xps_item_t *)visual_tag); } int -xps_parse_visual_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root) +xps_parse_visual_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) { xps_item_t *node; int code; @@ -53,9 +40,9 @@ xps_parse_visual_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, if (visual_tag) { - code = xps_parse_tiling_brush(ctx, visual_uri, dict, root, xps_paint_visual_brush, visual_tag); + code = xps_parse_tiling_brush(ctx, ctm, visual_uri, dict, root, xps_paint_visual_brush, visual_tag); if (code) - return gs_rethrow(code, "cannot parse tiling brush"); + return fz_rethrow(code, "cannot parse tiling brush"); } return 0; diff --git a/xps/xpsxml.c b/xps/xpsxml.c index ce93bcfe..2295e08c 100644 --- a/xps/xpsxml.c +++ b/xps/xpsxml.c @@ -1,19 +1,7 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. - - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - /* Simple XML document object model on top of Expat. */ -#include "ghostxps.h" +#include "fitz.h" +#include "muxps.h" #include @@ -87,7 +75,7 @@ on_open_tag(void *zp, char *ns_name, char **atts) if (!name) { - dprintf1("unknown namespace: %s\n", ns_name); + fz_warn("unknown namespace: %s", ns_name); name = ns_name; } @@ -214,7 +202,7 @@ on_text(void *zp, char *buf, int len) static xps_item_t * xps_process_compatibility(xps_context_t *ctx, xps_item_t *root) { - gs_warn("XPS document uses markup compatibility tags"); + fz_warn("XPS document uses markup compatibility tags"); return root; } @@ -234,7 +222,7 @@ xps_parse_xml(xps_context_t *ctx, byte *buf, int len) xp = XML_ParserCreateNS(NULL, ' '); if (!xp) { - gs_throw(-1, "xml error: could not create expat parser"); + fz_throw("xml error: could not create expat parser"); return NULL; } @@ -250,7 +238,7 @@ xps_parse_xml(xps_context_t *ctx, byte *buf, int len) if (parser.root) xps_free_item(ctx, parser.root); XML_ParserFree(xp); - gs_throw1(-1, "xml error: %s", XML_ErrorString(XML_GetErrorCode(xp))); + fz_throw("xml error: %s", XML_ErrorString(XML_GetErrorCode(xp))); return NULL; } diff --git a/xps/xpszip.c b/xps/xpszip.c index 1aac7349..639134cf 100644 --- a/xps/xpszip.c +++ b/xps/xpszip.c @@ -1,30 +1,7 @@ -/* Copyright (C) 2006-2010 Artifex Software, Inc. - All Rights Reserved. +#include "fitz.h" +#include "muxps.h" - This software is provided AS-IS with no warranty, either express or - implied. - - This software is distributed under license and may not be copied, modified - or distributed except as expressly authorized under the terms of that - license. Refer to licensing information at http://www.artifex.com/ - or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, - San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. -*/ - -/* XPS interpreter - zip container parsing */ - -#include "ghostxps.h" - -static int isfile(char *path) -{ - FILE *file = fopen(path, "rb"); - if (file) - { - fclose(file); - return 1; - } - return 0; -} +#include static inline int getshort(FILE *file) { @@ -95,13 +72,11 @@ xps_read_zip_entry(xps_context_t *ctx, xps_entry_t *ent, unsigned char *outbuf) int namelength, extralength; int code; - if_debug1('|', "zip: inflating entry '%s'\n", ent->name); - fseek(ctx->file, ent->offset, 0); sig = getlong(ctx->file); if (sig != ZIP_LOCAL_FILE_SIG) - return gs_throw1(-1, "wrong zip local file signature (0x%x)", sig); + return fz_throw("wrong zip local file signature (0x%x)", sig); version = getshort(ctx->file); general = getshort(ctx->file); @@ -137,25 +112,25 @@ xps_read_zip_entry(xps_context_t *ctx, xps_entry_t *ent, unsigned char *outbuf) code = inflateInit2(&stream, -15); if (code != Z_OK) - return gs_throw1(-1, "zlib inflateInit2 error: %s", stream.msg); + return fz_throw("zlib inflateInit2 error: %s", stream.msg); code = inflate(&stream, Z_FINISH); if (code != Z_STREAM_END) { inflateEnd(&stream); - return gs_throw1(-1, "zlib inflate error: %s", stream.msg); + return fz_throw("zlib inflate error: %s", stream.msg); } code = inflateEnd(&stream); if (code != Z_OK) - return gs_throw1(-1, "zlib inflateEnd error: %s", stream.msg); + return fz_throw("zlib inflateEnd error: %s", stream.msg); xps_free(ctx, inbuf); } else { - return gs_throw1(-1, "unknown compression method (%d)", method); + return fz_throw("unknown compression method (%d)", method); } - return gs_okay; + return fz_okay; } /* @@ -174,7 +149,7 @@ xps_read_zip_dir(xps_context_t *ctx, int start_offset) sig = getlong(ctx->file); if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG) - return gs_throw1(-1, "wrong zip end of central directory signature (0x%x)", sig); + return fz_throw("wrong zip end of central directory signature (0x%x)", sig); (void) getshort(ctx->file); /* this disk */ (void) getshort(ctx->file); /* start disk */ @@ -186,7 +161,7 @@ xps_read_zip_dir(xps_context_t *ctx, int start_offset) ctx->zip_count = count; ctx->zip_table = xps_alloc(ctx, sizeof(xps_entry_t) * count); if (!ctx->zip_table) - return gs_throw(-1, "cannot allocate zip entry table"); + return fz_throw("cannot allocate zip entry table"); memset(ctx->zip_table, 0, sizeof(xps_entry_t) * count); @@ -196,7 +171,7 @@ xps_read_zip_dir(xps_context_t *ctx, int start_offset) { sig = getlong(ctx->file); if (sig != ZIP_CENTRAL_DIRECTORY_SIG) - return gs_throw1(-1, "wrong zip central directory signature (0x%x)", sig); + return fz_throw("wrong zip central directory signature (0x%x)", sig); (void) getshort(ctx->file); /* version made by */ (void) getshort(ctx->file); /* version to extract */ @@ -217,7 +192,7 @@ xps_read_zip_dir(xps_context_t *ctx, int start_offset) ctx->zip_table[i].name = xps_alloc(ctx, namesize + 1); if (!ctx->zip_table[i].name) - return gs_throw(-1, "cannot allocate zip entry name"); + return fz_throw("cannot allocate zip entry name"); fread(ctx->zip_table[i].name, 1, namesize, ctx->file); ctx->zip_table[i].name[namesize] = 0; @@ -228,15 +203,7 @@ xps_read_zip_dir(xps_context_t *ctx, int start_offset) qsort(ctx->zip_table, count, sizeof(xps_entry_t), xps_compare_entries); - for (i = 0; i < ctx->zip_count; i++) - { - if_debug3('|', "zip entry '%s' csize=%d usize=%d\n", - ctx->zip_table[i].name, - ctx->zip_table[i].csize, - ctx->zip_table[i].usize); - } - - return gs_okay; + return fz_okay; } static int @@ -258,7 +225,7 @@ xps_find_and_read_zip_dir(xps_context_t *ctx) n = fread(buf, 1, sizeof buf, ctx->file); if (n < 0) - return gs_throw(-1, "cannot read end of central directory"); + return fz_throw("cannot read end of central directory"); for (i = n - 4; i > 0; i--) if (!memcmp(buf + i, "PK\5\6", 4)) @@ -267,7 +234,7 @@ xps_find_and_read_zip_dir(xps_context_t *ctx) back += sizeof buf - 4; } - return gs_throw(-1, "cannot find end of central directory"); + return fz_throw("cannot find end of central directory"); } /* @@ -425,34 +392,15 @@ xps_read_and_process_metadata_part(xps_context_t *ctx, char *name) part = xps_read_part(ctx, name); if (!part) - return gs_rethrow1(-1, "cannot read zip part '%s'", name); + return fz_rethrow(-1, "cannot read zip part '%s'", name); code = xps_parse_metadata(ctx, part); if (code) - return gs_rethrow1(code, "cannot process metadata part '%s'", name); - - xps_free_part(ctx, part); - - return gs_okay; -} - -static int -xps_read_and_process_page_part(xps_context_t *ctx, char *name) -{ - xps_part_t *part; - int code; - - part = xps_read_part(ctx, name); - if (!part) - return gs_rethrow1(-1, "cannot read zip part '%s'", name); - - code = xps_parse_fixed_page(ctx, part); - if (code) - return gs_rethrow1(code, "cannot parse fixed page part '%s'", name); + return fz_rethrow(code, "cannot process metadata part '%s'", name); xps_free_part(ctx, part); - return gs_okay; + return fz_okay; } /* @@ -460,62 +408,16 @@ xps_read_and_process_page_part(xps_context_t *ctx, char *name) */ int -xps_process_file(xps_context_t *ctx, char *filename) +xps_open_file(xps_context_t *ctx, char *filename) { char buf[2048]; xps_document_t *doc; - xps_page_t *page; int code; char *p; ctx->file = fopen(filename, "rb"); if (!ctx->file) - return gs_throw1(-1, "cannot open file: '%s'", filename); - - if (strstr(filename, ".fpage")) - { - xps_part_t *part; - int size; - - if_debug0('|', "zip: single page mode\n"); - xps_strlcpy(buf, filename, sizeof buf); - while (1) - { - p = strrchr(buf, '/'); - if (!p) - p = strrchr(buf, '\\'); - if (!p) - break; - xps_strlcpy(p, "/_rels/.rels", buf + sizeof buf - p); - if_debug1('|', "zip: testing if '%s' exists\n", buf); - if (isfile(buf)) - { - *p = 0; - ctx->directory = xps_strdup(ctx, buf); - if_debug1('|', "zip: using '%s' as root directory\n", ctx->directory); - break; - } - *p = 0; - } - if (!ctx->directory) - { - if_debug0('|', "zip: no /_rels/.rels found; assuming absolute paths\n"); - ctx->directory = xps_strdup(ctx, ""); - } - - fseek(ctx->file, 0, SEEK_END); - size = ftell(ctx->file); - fseek(ctx->file, 0, SEEK_SET); - part = xps_new_part(ctx, filename, size); - fread(part->data, 1, size, ctx->file); - - code = xps_parse_fixed_page(ctx, part); - if (code) - return gs_rethrow1(code, "cannot parse fixed page part '%s'", part->name); - - xps_free_part(ctx, part); - return gs_okay; - } + return fz_throw("cannot open file: '%s'", filename); if (strstr(filename, "/_rels/.rels") || strstr(filename, "\\_rels\\.rels")) { @@ -525,44 +427,122 @@ xps_process_file(xps_context_t *ctx, char *filename) p = strstr(buf, "\\_rels\\.rels"); *p = 0; ctx->directory = xps_strdup(ctx, buf); - if_debug1('|', "zip: using '%s' as root directory\n", ctx->directory); } else { code = xps_find_and_read_zip_dir(ctx); if (code < 0) - return gs_rethrow(code, "cannot read zip central directory"); + return fz_rethrow(code, "cannot read zip central directory"); } code = xps_read_and_process_metadata_part(ctx, "/_rels/.rels"); if (code) - return gs_rethrow(code, "cannot process root relationship part"); + return fz_rethrow(code, "cannot process root relationship part"); if (!ctx->start_part) - return gs_throw(-1, "cannot find fixed document sequence start part"); + return fz_throw("cannot find fixed document sequence start part"); code = xps_read_and_process_metadata_part(ctx, ctx->start_part); if (code) - return gs_rethrow(code, "cannot process FixedDocumentSequence part"); + return fz_rethrow(code, "cannot process FixedDocumentSequence part"); for (doc = ctx->first_fixdoc; doc; doc = doc->next) { code = xps_read_and_process_metadata_part(ctx, doc->name); if (code) - return gs_rethrow(code, "cannot process FixedDocument part"); + return fz_rethrow(code, "cannot process FixedDocument part"); } + return fz_okay; +} + +int +xps_count_pages(xps_context_t *ctx) +{ + xps_page_t *page; + int n = 0; + for (page = ctx->first_page; page; page = page->next) + n ++; + return n; +} + +xps_page_t * +xps_load_page(xps_context_t *ctx, int number) +{ + xps_page_t *page; + int code; + int n = 0; + for (page = ctx->first_page; page; page = page->next) { - code = xps_read_and_process_page_part(ctx, page->name); - if (code) - return gs_rethrow(code, "cannot process FixedPage part"); + if (n == number) + { + if (!page->root) + { + code = xps_load_fixed_page(ctx, page); + if (code) + fz_catch(code, "ignoring errors on page"); + } + return page; + } + n ++; } + return nil; +} + +xps_context_t * +xps_new_context(void) +{ + xps_context_t *ctx; + + ctx = fz_malloc(sizeof(xps_context_t)); + + memset(ctx, 0, sizeof(xps_context_t)); + + ctx->font_table = xps_hash_new(ctx); + ctx->colorspace_table = xps_hash_new(ctx); + + ctx->start_part = NULL; + + ctx->use_transparency = 1; + if (getenv("XPS_DISABLE_TRANSPARENCY")) + ctx->use_transparency = 0; + + ctx->opacity_only = 0; + ctx->fill_rule = 0; + + return ctx; +} + +static void xps_free_key_func(xps_context_t *ctx, void *ptr) +{ + xps_free(ctx, ptr); +} + +static void xps_free_font_func(xps_context_t *ctx, void *ptr) +{ + fz_dropfont(ptr); +} + +/* Wrap up interp instance after a "job" */ +int +xps_free_context(xps_context_t *ctx) +{ + int i; - if (ctx->directory) - xps_free(ctx, ctx->directory); if (ctx->file) fclose(ctx->file); - return gs_okay; + for (i = 0; i < ctx->zip_count; i++) + xps_free(ctx, ctx->zip_table[i].name); + xps_free(ctx, ctx->zip_table); + + /* TODO: free resources too */ + xps_hash_free(ctx, ctx->font_table, xps_free_key_func, xps_free_font_func); + xps_hash_free(ctx, ctx->colorspace_table, xps_free_key_func, NULL); + + xps_free_fixed_pages(ctx); + xps_free_fixed_documents(ctx); + + return 0; } -- cgit v1.2.3 From 5fc4bcb1f361a2b60e6841472afaf4a3cbd82ea8 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Mon, 28 Mar 2011 22:51:15 +0200 Subject: xps: More fixes and cleanups to parsing. --- xps/muxps.h | 29 +++---- xps/xpscolor.c | 6 +- xps/xpscommon.c | 32 ++++---- xps/xpsglyphs.c | 106 ++++++++----------------- xps/xpsgradient.c | 91 ++++++--------------- xps/xpsimage.c | 34 ++++---- xps/xpsopacity.c | 16 ++-- xps/xpspage.c | 43 ++++------ xps/xpspath.c | 70 ++++++---------- xps/xpstile.c | 233 +++++++++++++----------------------------------------- xps/xpstop.c | 19 +---- xps/xpsvisual.c | 20 +---- xps/xpsxml.c | 8 +- 13 files changed, 212 insertions(+), 495 deletions(-) diff --git a/xps/muxps.h b/xps/muxps.h index f2145d2f..256ea702 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -205,17 +205,17 @@ void xps_debug_resource_dictionary(xps_resource_t *dict); */ int xps_load_fixed_page(xps_context_t *ctx, xps_page_t *page); -int xps_parse_fixed_page(xps_context_t *ctx, fz_matrix ctm, xps_page_t *page); -int xps_parse_canvas(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_path(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_solid_color_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_visual_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_linear_gradient_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_radial_gradient_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); - -int xps_parse_tiling_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root, int (*func)(xps_context_t*, fz_matrix, char*, xps_resource_t*, xps_item_t*, void*), void *user); +void xps_parse_fixed_page(xps_context_t *ctx, fz_matrix ctm, xps_page_t *page); +void xps_parse_canvas(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +void xps_parse_path(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +void xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +void xps_parse_solid_color_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +void xps_parse_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +void xps_parse_visual_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +void xps_parse_linear_gradient_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +void xps_parse_radial_gradient_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); + +void xps_parse_tiling_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root, void(*func)(xps_context_t*, fz_matrix, char*, xps_resource_t*, xps_item_t*, void*), void *user); void xps_parse_matrix_transform(xps_context_t *ctx, xps_item_t *root, fz_matrix *matrix); void xps_parse_render_transform(xps_context_t *ctx, char *text, fz_matrix *matrix); @@ -223,11 +223,11 @@ void xps_parse_rectangle(xps_context_t *ctx, char *text, fz_rect *rect); void xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom); void xps_parse_path_geometry(xps_context_t *ctx, xps_resource_t *dict, xps_item_t *root, int stroking); -int xps_begin_opacity(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, char *opacity_att, xps_item_t *opacity_mask_tag); +void xps_begin_opacity(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, char *opacity_att, xps_item_t *opacity_mask_tag); void xps_end_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, char *opacity_att, xps_item_t *opacity_mask_tag); -int xps_parse_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -int xps_parse_element(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +void xps_parse_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +void xps_parse_element(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); void xps_fill(xps_context_t *ctx, fz_matrix ctm); void xps_clip(xps_context_t *ctx, fz_matrix ctm); @@ -289,6 +289,7 @@ struct xps_context_s /* Current path being accumulated */ fz_path *path; + fz_text *text; /* ... or text, for clipping brushes */ /* Current color */ fz_colorspace *colorspace; diff --git a/xps/xpscolor.c b/xps/xpscolor.c index 2c701c4a..558bde7c 100644 --- a/xps/xpscolor.c +++ b/xps/xpscolor.c @@ -200,7 +200,7 @@ xps_read_icc_colorspace(xps_context_t *ctx, char *base_uri, char *profilename) #endif } -int +void xps_parse_solid_color_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node) { char *opacity_att; @@ -219,13 +219,9 @@ xps_parse_solid_color_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, x if (color_att) xps_parse_color(ctx, base_uri, color_att, &colorspace, samples); - if (opacity_att) samples[0] = atof(opacity_att); xps_set_color(ctx, colorspace, samples); - xps_fill(ctx, ctm); - - return 0; } diff --git a/xps/xpscommon.c b/xps/xpscommon.c index 4ba38f7c..dd8762e8 100644 --- a/xps/xpscommon.c +++ b/xps/xpscommon.c @@ -1,33 +1,33 @@ #include "fitz.h" #include "muxps.h" -int +void xps_parse_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node) { if (!strcmp(xps_tag(node), "SolidColorBrush")) - return xps_parse_solid_color_brush(ctx, ctm, base_uri, dict, node); - if (!strcmp(xps_tag(node), "ImageBrush")) - return xps_parse_image_brush(ctx, ctm, base_uri, dict, node); - if (!strcmp(xps_tag(node), "VisualBrush")) - return xps_parse_visual_brush(ctx, ctm, base_uri, dict, node); - if (!strcmp(xps_tag(node), "LinearGradientBrush")) - return xps_parse_linear_gradient_brush(ctx, ctm, base_uri, dict, node); - if (!strcmp(xps_tag(node), "RadialGradientBrush")) - return xps_parse_radial_gradient_brush(ctx, ctm, base_uri, dict, node); - return fz_throw("unknown brush tag: %s", xps_tag(node)); + xps_parse_solid_color_brush(ctx, ctm, base_uri, dict, node); + else if (!strcmp(xps_tag(node), "ImageBrush")) + xps_parse_image_brush(ctx, ctm, base_uri, dict, node); + else if (!strcmp(xps_tag(node), "VisualBrush")) + xps_parse_visual_brush(ctx, ctm, base_uri, dict, node); + else if (!strcmp(xps_tag(node), "LinearGradientBrush")) + xps_parse_linear_gradient_brush(ctx, ctm, base_uri, dict, node); + else if (!strcmp(xps_tag(node), "RadialGradientBrush")) + xps_parse_radial_gradient_brush(ctx, ctm, base_uri, dict, node); + else + fz_warn("unknown brush tag: %s", xps_tag(node)); } -int +void xps_parse_element(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node) { if (!strcmp(xps_tag(node), "Path")) - return xps_parse_path(ctx, ctm, base_uri, dict, node); + xps_parse_path(ctx, ctm, base_uri, dict, node); if (!strcmp(xps_tag(node), "Glyphs")) - return xps_parse_glyphs(ctx, ctm, base_uri, dict, node); + xps_parse_glyphs(ctx, ctm, base_uri, dict, node); if (!strcmp(xps_tag(node), "Canvas")) - return xps_parse_canvas(ctx, ctm, base_uri, dict, node); + xps_parse_canvas(ctx, ctm, base_uri, dict, node); /* skip unknown tags (like Foo.Resources and similar) */ - return 0; } void diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index 76da97b1..29ab1e02 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -101,7 +101,7 @@ xps_select_best_font_encoding(fz_font *font) * Call text drawing primitives. */ -static int +static void xps_flush_text_buffer(xps_context_t *ctx, fz_font *font, xps_text_buffer_t *buf, int is_charpath) { @@ -149,8 +149,6 @@ xps_flush_text_buffer(xps_context_t *ctx, fz_font *font, gs_text_release(textenum, "gslt font render"); #endif buf->count = 0; - - return 0; } /* @@ -239,7 +237,7 @@ xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs) * Parse unicode and indices strings and encode glyphs. * Calculate metrics for positioning. */ -static int +static void xps_parse_glyphs_imp(xps_context_t *ctx, fz_font *font, float size, float originx, float originy, int is_sideways, int bidi_level, char *indices, char *unicode, int is_charpath) @@ -251,12 +249,11 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_font *font, float size, char *us = unicode; char *is = indices; int un = 0; - int code; buf.count = 0; if (!unicode && !indices) - return fz_throw("no text in glyphs element"); + return; if (us) { @@ -300,8 +297,6 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_font *font, float size, if (us && un > 0) { int t = xps_utf8_to_ucs(&char_code, us, un); - if (t < 0) - return fz_rethrow(-1, "error decoding UTF-8 string"); us += t; un -= t; } code_count --; @@ -339,9 +334,7 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_font *font, float size, if (buf.count == XPS_TEXT_BUFFER_SIZE) { - code = xps_flush_text_buffer(ctx, font, &buf, is_charpath); - if (code) - return fz_rethrow(code, "cannot flush buffered text"); + xps_flush_text_buffer(ctx, font, &buf, is_charpath); } if (is_sideways) @@ -363,15 +356,11 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_font *font, float size, if (buf.count > 0) { - code = xps_flush_text_buffer(ctx, font, &buf, is_charpath); - if (code) - return fz_rethrow(code, "cannot flush buffered text"); + xps_flush_text_buffer(ctx, font, &buf, is_charpath); } - - return 0; } -int +void xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) { @@ -410,7 +399,6 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, char partname[1024]; char *subfont; - fz_matrix matrix; float font_size = 10.0; int subfontid = 0; int is_sideways = 0; @@ -440,13 +428,10 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, { if (!strcmp(xps_tag(node), "Glyphs.RenderTransform")) transform_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Glyphs.OpacityMask")) opacity_mask_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Glyphs.Clip")) clip_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Glyphs.Fill")) fill_tag = xps_down(node); } @@ -463,11 +448,13 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, * Check that we have all the necessary information. */ - if (!font_size_att || !font_uri_att || !origin_x_att || !origin_y_att) - return fz_throw("missing attributes in glyphs element"); + if (!font_size_att || !font_uri_att || !origin_x_att || !origin_y_att) { + fz_warn("missing attributes in glyphs element"); + return; + } if (!indices_att && !unicode_att) - return 0; /* nothing to draw */ + return; /* nothing to draw */ if (is_sideways_att) is_sideways = !strcmp(is_sideways_att, "true"); @@ -491,8 +478,10 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, if (!font) { part = xps_read_part(ctx, partname); - if (!part) - return fz_throw("cannot find font resource part '%s'", partname); + if (!part) { + fz_warn("cannot find font resource part '%s'", partname); + return; + } /* deobfuscate if necessary */ if (strstr(part->name, ".odttf")) @@ -501,8 +490,11 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, xps_deobfuscate_font_resource(ctx, part); code = fz_newfontfrombuffer(&font, part->data, part->size, subfontid); - if (code) - return fz_rethrow(code, "cannot load font resource '%s'", partname); + if (code) { + fz_catch(code, "cannot load font resource '%s'", partname); + xps_free_part(ctx, part); + return; + } xps_select_best_font_encoding(font); @@ -515,19 +507,15 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, /* * Set up graphics state. */ -#if 0 - gs_gsave(ctx->pgs); if (transform_att || transform_tag) { fz_matrix transform; - if (transform_att) xps_parse_render_transform(ctx, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); - - gs_concat(ctx->pgs, &transform); + ctm = fz_concat(ctm, transform); } if (clip_att || clip_tag) @@ -537,26 +525,15 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, xps_parse_abbreviated_geometry(ctx, clip_att); if (clip_tag) xps_parse_path_geometry(ctx, dict, clip_tag, 0); - xps_clip(ctx); + xps_clip(ctx, ctm); } font_size = atof(font_size_att); - gs_setfont(ctx->pgs, font->font); - gs_make_scaling(font_size, -font_size, &matrix); if (is_sideways) - fz_matrix_rotate(&matrix, 90.0, &matrix); - - gs_setcharmatrix(ctx->pgs, &matrix); + fz_warn("sideways text not implemented!"); - fz_matrix_multiply(&matrix, &font->font->orig_FontMatrix, &font->font->FontMatrix); - - code = xps_begin_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - if (code) - { - gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot create transparency group"); - } + xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); /* * If it's a solid color brush fill/stroke do a simple fill @@ -573,20 +550,16 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, { float samples[32]; fz_colorspace *colorspace; + xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples); if (fill_opacity_att) samples[0] = atof(fill_opacity_att); xps_set_color(ctx, colorspace, samples); - code = xps_parse_glyphs_imp(ctx, font, font_size, + + xps_parse_glyphs_imp(ctx, font, font_size, atof(origin_x_att), atof(origin_y_att), is_sideways, bidi_level, indices_att, unicode_att, 0); - if (code) - { - xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot parse glyphs data"); - } } /* @@ -595,29 +568,18 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, if (fill_tag) { + fz_warn("glyphs filled with general brushes not implemented!"); +#if 0 ctx->fill_rule = 1; /* always use non-zero winding rule for char paths */ - code = xps_parse_glyphs_imp(ctx, font, font_size, + xps_parse_glyphs_imp(ctx, font, font_size, atof(origin_x_att), atof(origin_y_att), is_sideways, bidi_level, indices_att, unicode_att, 1); - if (code) - { - xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot parse glyphs data"); - } - - code = xps_parse_brush(ctx, fill_uri, dict, fill_tag); - if (code) - { - xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot parse fill brush"); - } + xps_parse_brush(ctx, ctm, fill_uri, dict, fill_tag); +#endif } xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - gs_grestore(ctx->pgs); -#endif - return 0; + if (clip_att || clip_tag) + ctx->dev->popclip(ctx->dev->user); } diff --git a/xps/xpsgradient.c b/xps/xpsgradient.c index 3bb95824..8baf6805 100644 --- a/xps/xpsgradient.c +++ b/xps/xpsgradient.c @@ -186,7 +186,7 @@ xps_gradient_has_transparent_colors(struct stop *stops, int count) * mess with the transform to squash the circle into the right aspect. */ -static int +static void xps_draw_one_radial_gradient(xps_context_t *ctx, int extend, float x0, float y0, float r0, @@ -230,14 +230,13 @@ xps_draw_one_radial_gradient(xps_context_t *ctx, gs_free_object(mem, shading, "gs_shading_R"); #endif - return 0; } /* * Linear gradients map to Axial shadings. */ -static int +static void xps_draw_one_linear_gradient(xps_context_t *ctx, int extend, float x0, float y0, float x1, float y1) @@ -278,7 +277,6 @@ xps_draw_one_linear_gradient(xps_context_t *ctx, gs_free_object(mem, shading, "gs_shading_A"); #endif - return 0; } /* @@ -296,7 +294,7 @@ static inline float point_inside_circle(float px, float py, float x, float y, fl return (dx * dx + dy * dy) <= (r * r); } -static int +static void xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread) { fz_rect bbox; @@ -306,7 +304,6 @@ xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread) float yrad = 1; float invscale; float dx, dy; - int code; int i; int done; @@ -380,24 +377,12 @@ xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread) // reverse = xps_reverse_function(ctx, func, fary, vary); - code = xps_draw_one_radial_gradient(ctx, reverse, 1, x1, y1, r1, x0, y0, r0); - if (code < 0) - { - xps_free(ctx, reverse); - gs_grestore(ctx->pgs); - return fz_rethrow(code, "could not draw radial gradient"); - } - + xps_draw_one_radial_gradient(ctx, reverse, 1, x1, y1, r1, x0, y0, r0); #endif } else { - code = xps_draw_one_radial_gradient(ctx, 1, x0, y0, r0, x1, y1, r1); - if (code < 0) - { -// gs_grestore(ctx->pgs); - return fz_rethrow(code, "could not draw radial gradient"); - } + xps_draw_one_radial_gradient(ctx, 1, x0, y0, r0, x1, y1, r1); } } else @@ -410,14 +395,9 @@ xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread) printf("xps: we should reverse gradient here too\n"); if (spread == SPREAD_REFLECT && (i & 1)) - code = xps_draw_one_radial_gradient(ctx, 0, x1, y1, r1, x0, y0, r0); + xps_draw_one_radial_gradient(ctx, 0, x1, y1, r1, x0, y0, r0); else - code = xps_draw_one_radial_gradient(ctx, 0, x0, y0, r0, x1, y1, r1); - if (code < 0) - { -// gs_grestore(ctx->pgs); - return fz_rethrow(code, "could not draw axial gradient"); - } + xps_draw_one_radial_gradient(ctx, 0, x0, y0, r0, x1, y1, r1); /* Check if circle encompassed the entire bounding box (break loop if we do) */ @@ -440,10 +420,6 @@ xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread) y1 += dy; } } - -// gs_grestore(ctx->pgs); - - return 0; } /* @@ -451,13 +427,12 @@ xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread) * the bounding box. */ -static int +static void xps_draw_linear_gradient(xps_context_t *ctx, xps_item_t *root, int spread) { fz_rect bbox; float x0, y0, x1, y1; float dx, dy; - int code; int i; char *start_point_att = xps_att(root, "StartPoint"); @@ -480,9 +455,7 @@ xps_draw_linear_gradient(xps_context_t *ctx, xps_item_t *root, int spread) if (spread == SPREAD_PAD) { - code = xps_draw_one_linear_gradient(ctx, 1, x0, y0, x1, y1); - if (code < 0) - return fz_rethrow(code, "could not draw axial gradient"); + xps_draw_one_linear_gradient(ctx, 1, x0, y0, x1, y1); } else { @@ -515,23 +488,15 @@ xps_draw_linear_gradient(xps_context_t *ctx, xps_item_t *root, int spread) for (i = i0; i < i1; i++) { if (spread == SPREAD_REFLECT && (i & 1)) - { - code = xps_draw_one_linear_gradient(ctx, 0, + xps_draw_one_linear_gradient(ctx, 0, x1 + dx * i, y1 + dy * i, x0 + dx * i, y0 + dy * i); - } else - { - code = xps_draw_one_linear_gradient(ctx, 0, + xps_draw_one_linear_gradient(ctx, 0, x0 + dx * i, y0 + dy * i, x1 + dx * i, y1 + dy * i); - } - if (code < 0) - return fz_rethrow(code, "could not draw axial gradient"); } } - - return 0; } /* @@ -539,10 +504,10 @@ xps_draw_linear_gradient(xps_context_t *ctx, xps_item_t *root, int spread) * function objects and call gradient drawing primitives. */ -static int +static void xps_parse_gradient_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root, - int (*draw)(xps_context_t *, xps_item_t *, int)) + void (*draw)(xps_context_t *, xps_item_t *, int)) { xps_item_t *node; @@ -604,12 +569,16 @@ xps_parse_gradient_brush(xps_context_t *ctx, fz_matrix ctm, xps_parse_matrix_transform(ctx, transform_tag, &transform); ctm = fz_concat(ctm, transform); - if (!stop_tag) - return fz_throw("missing gradient stops tag"); + if (!stop_tag) { + fz_warn("missing gradient stops tag"); + return; + } stop_count = xps_parse_gradient_stops(ctx, base_uri, stop_tag, stop_list, MAX_STOPS); - if (stop_count == 0) - return fz_throw("no gradient stops found"); + if (stop_count == 0) { + fz_warn("no gradient stops found"); + return; + } /* color_func = xps_create_gradient_stop_function(ctx, stop_list, stop_count, 0); @@ -689,28 +658,18 @@ xps_parse_gradient_brush(xps_context_t *ctx, fz_matrix ctm, // xps_free_gradient_stop_function(ctx, opacity_func); // xps_free_gradient_stop_function(ctx, color_func); - - return 0; } -int +void xps_parse_linear_gradient_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) { - int code; - code = xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_linear_gradient); - if (code < 0) - return fz_rethrow(code, "cannot parse linear gradient brush"); - return fz_okay; + xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_linear_gradient); } -int +void xps_parse_radial_gradient_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) { - int code; - code = xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_radial_gradient); - if (code < 0) - return fz_rethrow(code, "cannot parse radial gradient brush"); - return fz_okay; + xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_radial_gradient); } diff --git a/xps/xpsimage.c b/xps/xpsimage.c index 15257594..5cd03418 100644 --- a/xps/xpsimage.c +++ b/xps/xpsimage.c @@ -43,7 +43,7 @@ xps_decode_image(xps_context_t *ctx, xps_part_t *part, xps_image_t *image) return fz_okay; } -static int +static void xps_paint_image_brush_imp(xps_context_t *ctx, fz_matrix ctm, xps_image_t *image) { fz_colorspace *colorspace; @@ -54,12 +54,10 @@ xps_paint_image_brush_imp(xps_context_t *ctx, fz_matrix ctm, xps_image_t *image) samples = image->samples; count = image->stride * image->height; - // xxx - - return 0; +printf("xps_paint_image_brush_imp!\n"); } -static int +static void xps_paint_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root, void *vimage) { #if 0 @@ -124,7 +122,6 @@ xps_paint_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_res return fz_rethrow(code, "cannot draw image"); } #endif - return 0; } static int @@ -186,7 +183,7 @@ xps_find_image_brush_source_part(xps_context_t *ctx, char *base_uri, xps_item_t return 0; } -int +void xps_parse_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) { xps_part_t *part; @@ -198,18 +195,19 @@ xps_parse_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_res profilename = NULL; code = xps_find_image_brush_source_part(ctx, base_uri, root, &part, &profilename); - if (code < 0) - return fz_rethrow(code, "cannot find image source"); + if (code < 0) { + fz_catch(code, "cannot find image source"); + return; + } image = xps_alloc(ctx, sizeof(xps_image_t)); - if (!image) - return fz_throw("out of memory: image struct"); - -return 0; code = xps_decode_image(ctx, part, image); - if (code < 0) - return fz_rethrow(-1, "cannot decode image resource"); + if (code < 0) { + fz_free(image); + fz_catch(-1, "cannot decode image resource"); + return; + } /* Override any embedded colorspace profiles if the external one matches. */ if (profilename) @@ -222,16 +220,12 @@ return 0; } } - code = xps_parse_tiling_brush(ctx, ctm, base_uri, dict, root, xps_paint_image_brush, image); - if (code < 0) - return fz_rethrow(-1, "cannot parse tiling brush"); + xps_parse_tiling_brush(ctx, ctm, base_uri, dict, root, xps_paint_image_brush, image); if (profilename) xps_free(ctx, profilename); xps_free_image(ctx, image); xps_free_part(ctx, part); - - return 0; } void diff --git a/xps/xpsopacity.c b/xps/xpsopacity.c index e4923f14..8bab138e 100644 --- a/xps/xpsopacity.c +++ b/xps/xpsopacity.c @@ -21,17 +21,18 @@ xps_bounds_in_user_space(xps_context_t *ctx, fz_rect *ubox) #endif } -int +void xps_begin_opacity(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, char *opacity_att, xps_item_t *opacity_mask_tag) { fz_rect bbox; float opacity; int save; - int code; + +return; if (!opacity_att && !opacity_mask_tag) - return 0; + return; opacity = 1.0; if (opacity_att) @@ -47,19 +48,12 @@ xps_begin_opacity(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resourc ctx->opacity_only = 1; // begin mask - code = xps_parse_brush(ctx, ctm, base_uri, dict, opacity_mask_tag); - if (code) - { - ctx->opacity_only = save; - return fz_rethrow(code, "cannot parse opacity mask brush"); - } + xps_parse_brush(ctx, ctm, base_uri, dict, opacity_mask_tag); ctx->opacity_only = save; } // begin group - - return 0; } void diff --git a/xps/xpspage.c b/xps/xpspage.c index 858f327f..f27b4287 100644 --- a/xps/xpspage.c +++ b/xps/xpspage.c @@ -1,7 +1,7 @@ #include "fitz.h" #include "muxps.h" -int +void xps_parse_canvas(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) { xps_resource_t *new_dict = NULL; @@ -31,9 +31,12 @@ xps_parse_canvas(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource { code = xps_parse_resource_dictionary(ctx, &new_dict, base_uri, xps_down(node)); if (code) - return fz_rethrow(code, "cannot load Canvas.Resources"); - new_dict->parent = dict; - dict = new_dict; + fz_catch(code, "cannot load Canvas.Resources"); + else + { + new_dict->parent = dict; + dict = new_dict; + } } if (!strcmp(xps_tag(node), "Canvas.RenderTransform")) @@ -49,8 +52,6 @@ xps_parse_canvas(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); -// gs_gsave(ctx->pgs); - transform = fz_identity; if (transform_att) xps_parse_render_transform(ctx, transform_att, &transform); @@ -68,35 +69,23 @@ xps_parse_canvas(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource xps_clip(ctx, ctm); } - code = xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - if (code) - { -// gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot create transparency group"); - } + xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); for (node = xps_down(root); node; node = xps_next(node)) { - code = xps_parse_element(ctx, ctm, base_uri, dict, node); - if (code) - { - xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); -// gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot parse child of Canvas"); - } + xps_parse_element(ctx, ctm, base_uri, dict, node); } xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); -// gs_grestore(ctx->pgs); + if (clip_att || clip_tag) + ctx->dev->popclip(ctx->dev->user); if (new_dict) xps_free_resource_dictionary(ctx, new_dict); - - return 0; } -int +void xps_parse_fixed_page(xps_context_t *ctx, fz_matrix ctm, xps_page_t *page) { xps_item_t *node; @@ -118,19 +107,15 @@ xps_parse_fixed_page(xps_context_t *ctx, fz_matrix ctm, xps_page_t *page) { code = xps_parse_resource_dictionary(ctx, &dict, base_uri, xps_down(node)); if (code) - return fz_rethrow(code, "cannot load FixedPage.Resources"); + fz_catch(code, "cannot load FixedPage.Resources"); } - code = xps_parse_element(ctx, ctm, base_uri, dict, node); - if (code) - return fz_rethrow(code, "cannot parse child of FixedPage"); + xps_parse_element(ctx, ctm, base_uri, dict, node); } if (dict) { xps_free_resource_dictionary(ctx, dict); } - - return fz_okay; } int diff --git a/xps/xpspath.c b/xps/xpspath.c index 12377501..78925a8b 100644 --- a/xps/xpspath.c +++ b/xps/xpspath.c @@ -38,18 +38,22 @@ fz_currentpoint(fz_path *path) void xps_clip(xps_context_t *ctx, fz_matrix ctm) { -printf("xps_clip\n"); - assert(ctx->path); -// ctx->dev->clippath(ctx->dev, ctx->path, ctx->fill_rule == 0, ctm); - fz_freepath(ctx->path); - ctx->path = NULL; + if (ctx->path) + { + ctx->dev->clippath(ctx->dev->user, ctx->path, ctx->fill_rule == 0, ctm); + fz_freepath(ctx->path); + ctx->path = NULL; + } + else + { + printf("clip not a path! (maybe a glyph, or image?)\n"); + } } void xps_fill(xps_context_t *ctx, fz_matrix ctm) { -printf("xps_fill!\n"); - ctx->dev->fillpath(ctx->dev, ctx->path, ctx->fill_rule == 0, ctm, + ctx->dev->fillpath(ctx->dev->user, ctx->path, ctx->fill_rule == 0, ctm, ctx->colorspace, ctx->color, ctx->alpha); fz_freepath(ctx->path); ctx->path = NULL; @@ -58,7 +62,7 @@ printf("xps_fill!\n"); static void xps_stroke(xps_context_t *ctx, fz_matrix ctm, fz_strokestate *stroke) { - ctx->dev->strokepath(ctx->dev, ctx->path, stroke, ctm, + ctx->dev->strokepath(ctx->dev->user, ctx->path, stroke, ctm, ctx->colorspace, ctx->color, ctx->alpha); fz_freepath(ctx->path); ctx->path = NULL; @@ -67,7 +71,7 @@ xps_stroke(xps_context_t *ctx, fz_matrix ctm, fz_strokestate *stroke) static void xps_clipstroke(xps_context_t *ctx, fz_matrix ctm, fz_strokestate *stroke) { - ctx->dev->clipstrokepath(ctx->dev, ctx->path, stroke, ctm); + ctx->dev->clipstrokepath(ctx->dev->user, ctx->path, stroke, ctm); fz_freepath(ctx->path); ctx->path = NULL; } @@ -268,8 +272,6 @@ xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom) float smooth_x, smooth_y; /* saved cubic bezier control point for smooth curves */ int reset_smooth; -printf("xps_parse_abbreviated_geometry: %s\n", geom); - args = fz_calloc(strlen(geom) + 1, sizeof(char*)); pargs = args; @@ -747,6 +749,8 @@ xps_parse_path_geometry(xps_context_t *ctx, xps_resource_t *dict, xps_item_t *ro if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); // TODO: apply matrix + if (transform_att || transform_tag) + fz_warn("ignoring PathGeometry.Transform attribute"); if (figures_att) { @@ -783,11 +787,10 @@ xps_parse_line_cap(char *attr) * functions for drawing and/or clipping the child elements. */ -int +void xps_parse_path(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) { xps_item_t *node; - int code; char *fill_uri; char *stroke_uri; @@ -827,8 +830,6 @@ xps_parse_path(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t ctx->fill_rule = 0; -printf("xps_parse_path\n"); - /* * Extract attributes and extended attributes. */ @@ -854,19 +855,14 @@ printf("xps_parse_path\n"); { if (!strcmp(xps_tag(node), "Path.RenderTransform")) transform_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Path.OpacityMask")) opacity_mask_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Path.Clip")) clip_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Path.Fill")) fill_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Path.Stroke")) stroke_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Path.Data")) data_tag = xps_down(node); } @@ -956,16 +952,10 @@ printf("xps_parse_path\n"); xps_clip(ctx, ctm); } - code = xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - if (code) - { -// fz_grestore(ctx->pgs); - return fz_rethrow(code, "cannot create transparency group"); - } + xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); if (fill_att) { -printf(" fill-att\n"); xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples); if (fill_opacity_att) samples[0] = atof(fill_opacity_att); @@ -982,25 +972,17 @@ printf(" fill-att\n"); if (fill_tag) { -printf(" fill-tag\n"); ctx->path = fz_newpath(); if (data_att) xps_parse_abbreviated_geometry(ctx, data_att); if (data_tag) xps_parse_path_geometry(ctx, dict, data_tag, 0); - code = xps_parse_brush(ctx, ctm, fill_uri, dict, fill_tag); - if (code < 0) - { - xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); -// fz_grestore(ctx->pgs); - return fz_rethrow(code, "cannot parse fill brush"); - } + xps_parse_brush(ctx, ctm, fill_uri, dict, fill_tag); } if (stroke_att) { -printf(" stroke_att\n"); xps_parse_color(ctx, base_uri, stroke_att, &colorspace, samples); if (stroke_opacity_att) samples[0] = atof(stroke_opacity_att); @@ -1017,7 +999,6 @@ printf(" stroke_att\n"); if (stroke_tag) { -printf(" stroke_tag\n"); ctx->path = fz_newpath(); if (data_att) xps_parse_abbreviated_geometry(ctx, data_att); @@ -1027,18 +1008,13 @@ printf(" stroke_tag\n"); ctx->fill_rule = 1; /* over-ride fill rule when converting outline to stroked */ xps_clipstroke(ctx, ctm, &stroke); - code = xps_parse_brush(ctx, ctm, stroke_uri, dict, stroke_tag); - if (code < 0) - { - xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); -// fz_grestore(ctx->pgs); - return fz_rethrow(code, "cannot parse stroke brush"); - } + xps_parse_brush(ctx, ctm, stroke_uri, dict, stroke_tag); - ctx->dev->popclip(ctx->dev); + ctx->dev->popclip(ctx->dev->user); } xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); -// fz_grestore(ctx->pgs); - return 0; + + if (clip_att || clip_tag) + ctx->dev->popclip(ctx->dev->user); } diff --git a/xps/xpstile.c b/xps/xpstile.c index 4f2f57ec..2c91afa9 100644 --- a/xps/xpstile.c +++ b/xps/xpstile.c @@ -8,111 +8,67 @@ enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y }; -struct tile_closure_s +struct closure { - xps_context_t *ctx; char *base_uri; xps_resource_t *dict; - xps_item_t *tag; - fz_rect viewbox; - int tile_mode; + xps_item_t *root; void *user; - int (*func)(xps_context_t*, char*, xps_resource_t*, xps_item_t*, void*); + void (*func)(xps_context_t*, fz_matrix, char*, xps_resource_t*, xps_item_t*, void*); }; -static int -xps_paint_tiling_brush_clipped(struct tile_closure_s *c) +static void +xps_paint_tiling_brush_clipped(xps_context_t *ctx, fz_matrix ctm, fz_rect viewbox, struct closure *c) { - xps_context_t *ctx = c->ctx; - int code; + ctx->path = fz_newpath(); + fz_moveto(ctx->path, viewbox.x0, viewbox.y0); + fz_lineto(ctx->path, viewbox.x0, viewbox.y1); + fz_lineto(ctx->path, viewbox.x1, viewbox.y1); + fz_lineto(ctx->path, viewbox.x1, viewbox.y0); + fz_closepath(ctx->path); + xps_clip(ctx, ctm); -#if 0 - gs_moveto(ctx->pgs, c->viewbox.p.x, c->viewbox.p.y); - gs_lineto(ctx->pgs, c->viewbox.p.x, c->viewbox.q.y); - gs_lineto(ctx->pgs, c->viewbox.q.x, c->viewbox.q.y); - gs_lineto(ctx->pgs, c->viewbox.q.x, c->viewbox.p.y); - gs_closepath(ctx->pgs); - gs_clip(ctx->pgs); - gs_newpath(ctx->pgs); -#endif + c->func(ctx, ctm, c->base_uri, c->dict, c->root, c->user); - code = c->func(c->ctx, c->base_uri, c->dict, c->tag, c->user); - if (code < 0) - return fz_rethrow(code, "cannot draw clipped tile"); - - return 0; + ctx->dev->popclip(ctx->dev->user); } -#if 0 -static int -xps_paint_tiling_brush(const gs_client_color *pcc, gs_state *pgs) +static void +xps_paint_tiling_brush(xps_context_t *ctx, fz_matrix ctm, fz_rect viewbox, int tile_mode, struct closure *c) { - const gs_client_pattern *ppat = gs_getpattern(pcc); - struct tile_closure_s *c = ppat->client_data; - xps_context_t *ctx = c->ctx; - gs_state *saved_pgs; - int code; - - saved_pgs = ctx->pgs; - ctx->pgs = pgs; + fz_matrix ttm; - gs_gsave(ctx->pgs); - code = xps_paint_tiling_brush_clipped(c); - if (code) - goto cleanup; - gs_grestore(ctx->pgs); + xps_paint_tiling_brush_clipped(ctx, ctm, viewbox, c); - if (c->tile_mode == TILE_FLIP_X || c->tile_mode == TILE_FLIP_X_Y) + if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y) { - gs_gsave(ctx->pgs); - gs_translate(ctx->pgs, c->viewbox.q.x * 2, 0.0); - gs_scale(ctx->pgs, -1.0, 1.0); - code = xps_paint_tiling_brush_clipped(c); - if (code) - goto cleanup; - gs_grestore(ctx->pgs); + ttm = fz_concat(ctm, fz_translate(viewbox.x1 * 2, 0)); + ttm = fz_concat(ttm, fz_scale(-1, 1)); + xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c); } - if (c->tile_mode == TILE_FLIP_Y || c->tile_mode == TILE_FLIP_X_Y) + if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y) { - gs_gsave(ctx->pgs); - gs_translate(ctx->pgs, 0.0, c->viewbox.q.y * 2); - gs_scale(ctx->pgs, 1.0, -1.0); - code = xps_paint_tiling_brush_clipped(c); - if (code) - goto cleanup; - gs_grestore(ctx->pgs); + ttm = fz_concat(ctm, fz_translate(0, viewbox.y1 * 2)); + ttm = fz_concat(ttm, fz_scale(1, -1)); + xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c); } - if (c->tile_mode == TILE_FLIP_X_Y) + if (tile_mode == TILE_FLIP_X_Y) { - gs_gsave(ctx->pgs); - gs_translate(ctx->pgs, c->viewbox.q.x * 2, c->viewbox.q.y * 2); - gs_scale(ctx->pgs, -1.0, -1.0); - code = xps_paint_tiling_brush_clipped(c); - if (code) - goto cleanup; - gs_grestore(ctx->pgs); + ttm = fz_concat(ctm, fz_translate(viewbox.x1 * 2, viewbox.y1 * 2)); + ttm = fz_concat(ttm, fz_scale(-1, -1)); + xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c); } - - ctx->pgs = saved_pgs; - - return 0; - -cleanup: - gs_grestore(ctx->pgs); - ctx->pgs = saved_pgs; - return fz_rethrow(code, "cannot draw tile"); } -#endif -int +void xps_parse_tiling_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root, - int (*func)(xps_context_t*, fz_matrix, char*, xps_resource_t*, xps_item_t*, void*), void *user) + void (*func)(xps_context_t*, fz_matrix, char*, xps_resource_t*, xps_item_t*, void*), void *user) { xps_item_t *node; - int code; + struct closure c; char *opacity_att; char *transform_att; @@ -138,6 +94,12 @@ xps_parse_tiling_brush(xps_context_t *ctx, fz_matrix ctm, viewbox_units_att = xps_att(root, "ViewboxUnits"); viewport_units_att = xps_att(root, "ViewportUnits"); + c.base_uri = base_uri; + c.dict = dict; + c.root = root; + c.user = user; + c.func = func; + for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "ImageBrush.Transform")) @@ -163,10 +125,10 @@ xps_parse_tiling_brush(xps_context_t *ctx, fz_matrix ctm, xps_parse_rectangle(ctx, viewport_att, &viewport); /* some sanity checks on the viewport/viewbox size */ - if (fabs(viewport.x1 - viewport.x0) < 0.01) return 0; - if (fabs(viewport.y1 - viewport.y0) < 0.01) return 0; - if (fabs(viewbox.x1 - viewbox.x0) < 0.01) return 0; - if (fabs(viewbox.y1 - viewbox.y0) < 0.01) return 0; + if (fabs(viewport.x1 - viewport.x0) < 0.01) return; + if (fabs(viewport.y1 - viewport.y0) < 0.01) return; + if (fabs(viewbox.x1 - viewbox.x0) < 0.01) return; + if (fabs(viewbox.y1 - viewbox.y0) < 0.01) return; scalex = (viewport.x1 - viewport.x0) / (viewbox.x1 - viewbox.x0); scaley = (viewport.y1 - viewport.y0) / (viewbox.y1 - viewbox.y0); @@ -186,114 +148,27 @@ xps_parse_tiling_brush(xps_context_t *ctx, fz_matrix ctm, tile_mode = TILE_FLIP_X_Y; } -// gs_gsave(ctx->pgs); + xps_clip(ctx, ctm); - code = xps_begin_opacity(ctx, ctm, base_uri, dict, opacity_att, NULL); - if (code) - { -// gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot create transparency group"); - } + xps_begin_opacity(ctx, ctm, base_uri, dict, opacity_att, NULL); - /* TODO(tor): check viewport and tiling to see if we can set it to TILE_NONE */ + ctm = fz_concat(ctm, transform); + ctm = fz_concat(ctm, fz_translate(viewport.x0, viewport.y0)); + ctm = fz_concat(ctm, fz_scale(scalex, scaley)); + ctm = fz_concat(ctm, fz_translate(-viewbox.x0, -viewbox.y0)); -#if 0 if (tile_mode != TILE_NONE) { - struct tile_closure_s closure; - - gs_client_pattern gspat; - gs_client_color gscolor; - fz_colorspace *cs; - - closure.ctx = ctx; - closure.base_uri = base_uri; - closure.dict = dict; - closure.tag = root; - closure.tile_mode = tile_mode; - closure.user = user; - closure.func = func; - - closure.viewbox.p.x = viewbox.p.x; - closure.viewbox.p.y = viewbox.p.y; - closure.viewbox.q.x = viewbox.q.x; - closure.viewbox.q.y = viewbox.q.y; - - gs_pattern1_init(&gspat); - uid_set_UniqueID(&gspat.uid, gs_next_ids(ctx->memory, 1)); - gspat.PaintType = 1; - gspat.TilingType = 1; - gspat.PaintProc = xps_remap_pattern; - gspat.client_data = &closure; - - gspat.XStep = viewbox.q.x - viewbox.p.x; - gspat.YStep = viewbox.q.y - viewbox.p.y; - gspat.BBox.p.x = viewbox.p.x; - gspat.BBox.p.y = viewbox.p.y; - gspat.BBox.q.x = viewbox.q.x; - gspat.BBox.q.y = viewbox.q.y; - - if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y) - { - gspat.BBox.q.x += gspat.XStep; - gspat.XStep *= 2; - } - - if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y) - { - gspat.BBox.q.y += gspat.YStep; - gspat.YStep *= 2; - } - - fz_matrix_translate(&transform, viewport.p.x, viewport.p.y, &transform); - fz_matrix_scale(&transform, scalex, scaley, &transform); - fz_matrix_translate(&transform, -viewbox.p.x, -viewbox.p.y, &transform); - - cs = ctx->srgb; - gs_setcolorspace(ctx->pgs, cs); - gs_makepattern(&gscolor, &gspat, &transform, ctx->pgs, NULL); - gs_setpattern(ctx->pgs, &gscolor); - - xps_fill(ctx); - - /* gs_makepattern increments the pattern count stored in the color - * structure. We will discard the color struct (its on the stack) - * so we need to decrement the reference before we throw away - * the structure. - */ - gs_pattern_reference(&gscolor, -1); + /* TODO: loop in visible area */ + fz_warn("tiling brushes not implemented!"); + xps_paint_tiling_brush(ctx, ctm, viewbox, tile_mode, &c); } else { - xps_clip(ctx); - - gs_concat(ctx->pgs, &transform); - - gs_translate(ctx->pgs, viewport.p.x, viewport.p.y); - gs_scale(ctx->pgs, scalex, scaley); - gs_translate(ctx->pgs, -viewbox.p.x, -viewbox.p.y); - - gs_moveto(ctx->pgs, viewbox.p.x, viewbox.p.y); - gs_lineto(ctx->pgs, viewbox.p.x, viewbox.q.y); - gs_lineto(ctx->pgs, viewbox.q.x, viewbox.q.y); - gs_lineto(ctx->pgs, viewbox.q.x, viewbox.p.y); - gs_closepath(ctx->pgs); - gs_clip(ctx->pgs); - gs_newpath(ctx->pgs); - - code = func(ctx, base_uri, dict, root, user); - if (code < 0) - { - xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); - gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot draw tile"); - } + xps_paint_tiling_brush(ctx, ctm, viewbox, tile_mode, &c); } -#endif xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); -// gs_grestore(ctx->pgs); - - return 0; + ctx->dev->popclip(ctx->dev->user); } diff --git a/xps/xpstop.c b/xps/xpstop.c index afc64569..e1a2fe1e 100644 --- a/xps/xpstop.c +++ b/xps/xpstop.c @@ -82,25 +82,16 @@ static int isrange(char *s) return 1; } -static int +static void xps_run_page(xps_context_t *ctx, xps_page_t *page, fz_device *dev, fz_matrix ctm) { - int code; - ctx->dev = dev; - - code = xps_parse_fixed_page(ctx, ctm, page); - if (code) - return fz_rethrow(code, "cannot draw page part %s", page->name); - + xps_parse_fixed_page(ctx, ctm, page); ctx->dev = nil; - - return fz_okay; } static void drawpage(xps_context_t *ctx, int pagenum) { - fz_error error; xps_page_t *page; fz_displaylist *list; fz_device *dev; @@ -121,9 +112,7 @@ static void drawpage(xps_context_t *ctx, int pagenum) { list = fz_newdisplaylist(); dev = fz_newlistdevice(list); - error = xps_run_page(ctx, page, dev, fz_identity); - if (error) - die(fz_rethrow(error, "cannot draw page %d in file '%s'", pagenum, filename)); + xps_run_page(ctx, page, dev, fz_identity); fz_freedevice(dev); } @@ -174,7 +163,7 @@ static void drawpage(xps_context_t *ctx, int pagenum) zoom = resolution / 72; ctm = fz_translate(0, -page->height); - ctm = fz_concat(ctm, fz_scale(zoom, -zoom)); + ctm = fz_concat(ctm, fz_scale(zoom, zoom)); bbox = fz_roundrect(fz_transformrect(ctm, rect)); /* TODO: banded rendering and multi-page ppm */ diff --git a/xps/xpsvisual.c b/xps/xpsvisual.c index 0b6fb4da..d9aa5340 100644 --- a/xps/xpsvisual.c +++ b/xps/xpsvisual.c @@ -3,25 +3,17 @@ enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y }; -struct userdata -{ - xps_context_t *ctx; - xps_resource_t *dict; - xps_item_t *visual_tag; -}; - -static int +static void xps_paint_visual_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root, void *visual_tag) { - return xps_parse_element(ctx, ctm, base_uri, dict, (xps_item_t *)visual_tag); + xps_parse_element(ctx, ctm, base_uri, dict, (xps_item_t *)visual_tag); } -int +void xps_parse_visual_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) { xps_item_t *node; - int code; char *visual_uri; char *visual_att; @@ -40,10 +32,6 @@ xps_parse_visual_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_re if (visual_tag) { - code = xps_parse_tiling_brush(ctx, ctm, visual_uri, dict, root, xps_paint_visual_brush, visual_tag); - if (code) - return fz_rethrow(code, "cannot parse tiling brush"); + xps_parse_tiling_brush(ctx, ctm, visual_uri, dict, root, xps_paint_visual_brush, visual_tag); } - - return 0; } diff --git a/xps/xpsxml.c b/xps/xpsxml.c index 2295e08c..94d1e351 100644 --- a/xps/xpsxml.c +++ b/xps/xpsxml.c @@ -44,7 +44,6 @@ static void on_open_tag(void *zp, char *ns_name, char **atts) { xps_parser_t *parser = zp; - xps_context_t *ctx = parser->ctx; xps_item_t *item; xps_item_t *tail; int namelen; @@ -93,7 +92,7 @@ on_open_tag(void *zp, char *ns_name, char **atts) textlen += strlen(atts[i]) + 1; } - item = xps_alloc(ctx, sizeof(xps_item_t) + attslen + namelen + textlen); + item = fz_malloc(sizeof(xps_item_t) + attslen + namelen + textlen); if (!item) { parser->error = "out of memory"; @@ -167,7 +166,6 @@ static void on_text(void *zp, char *buf, int len) { xps_parser_t *parser = zp; - xps_context_t *ctx = parser->ctx; char *atts[3]; int i; @@ -178,7 +176,7 @@ on_text(void *zp, char *buf, int len) { if (!is_xml_space(buf[i])) { - char *tmp = xps_alloc(ctx, len + 1); + char *tmp = fz_malloc(len + 1); if (!tmp) { parser->error = "out of memory"; @@ -193,7 +191,7 @@ on_text(void *zp, char *buf, int len) tmp[len] = 0; on_open_tag(zp, "", atts); on_close_tag(zp, ""); - xps_free(ctx, tmp); + fz_free(tmp); return; } } -- cgit v1.2.3 From 8fdbf5b37b59cb3a6df34ed1b81fe08cd9bcc8b9 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Mon, 28 Mar 2011 23:26:45 +0200 Subject: xps: Draw Glyphs elements. --- xps/xpsfont.c | 21 +++++------ xps/xpsglyphs.c | 110 ++++++++++---------------------------------------------- xps/xpspath.c | 8 ++++- 3 files changed, 34 insertions(+), 105 deletions(-) diff --git a/xps/xpsfont.c b/xps/xpsfont.c index ecedfabb..e983df35 100644 --- a/xps/xpsfont.c +++ b/xps/xpsfont.c @@ -3,6 +3,7 @@ #include #include FT_FREETYPE_H +#include FT_ADVANCES_H int xps_count_font_encodings(fz_font *font) @@ -39,16 +40,12 @@ xps_encode_font_char(fz_font *font, int code) void xps_measure_font_glyph(xps_context_t *ctx, fz_font *font, int gid, xps_glyph_metrics_t *mtx) { - - int hadv, vadv, vorg; - int scale; - - scale = 1000; /* units-per-em */ - hadv = 500; - vadv = -1000; - vorg = 1000; - - mtx->hadv = hadv / (float) scale; - mtx->vadv = vadv / (float) scale; - mtx->vorg = vorg / (float) scale; + int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM; + FT_Fixed adv; + + FT_Set_Char_Size(font->ftface, 64, 64, 72, 72); + FT_Get_Advance(font->ftface, gid, mask, &adv); + mtx->hadv = adv / 65536.0f; + mtx->vadv = -1000 / 1000.0f; + mtx->vorg = 880 / 1000.0f; } diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index 29ab1e02..c7431440 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -3,18 +3,6 @@ #include /* for tolower() */ -#define XPS_TEXT_BUFFER_SIZE 300 - -typedef struct xps_text_buffer_s xps_text_buffer_t; - -struct xps_text_buffer_s -{ - int count; - float x[XPS_TEXT_BUFFER_SIZE + 1]; - float y[XPS_TEXT_BUFFER_SIZE + 1]; - int g[XPS_TEXT_BUFFER_SIZE]; -}; - static inline int unhex(int i) { if (isdigit(i)) @@ -97,60 +85,6 @@ xps_select_best_font_encoding(fz_font *font) fz_warn("could not find a suitable cmap"); } -/* - * Call text drawing primitives. - */ - -static void -xps_flush_text_buffer(xps_context_t *ctx, fz_font *font, - xps_text_buffer_t *buf, int is_charpath) -{ -#if 0 - gs_text_params_t params; - gs_text_enum_t *textenum; - float x = buf->x[0]; - float y = buf->y[0]; - int code; - int i; - - // dprintf1("flushing text buffer (%d glyphs)\n", buf->count); - gs_moveto(ctx->pgs, x, y); - - params.operation = TEXT_FROM_GLYPHS | TEXT_REPLACE_WIDTHS; - if (is_charpath) - params.operation |= TEXT_DO_FALSE_CHARPATH; - else - params.operation |= TEXT_DO_DRAW; - params.data.glyphs = buf->g; - params.size = buf->count; - params.x_widths = buf->x + 1; - params.y_widths = buf->y + 1; - params.widths_size = buf->count; - - for (i = 0; i < buf->count; i++) - { - buf->x[i] = buf->x[i] - x; - buf->y[i] = buf->y[i] - y; - x += buf->x[i]; - y += buf->y[i]; - } - buf->x[buf->count] = 0; - buf->y[buf->count] = 0; - - code = gs_text_begin(ctx->pgs, ¶ms, ctx->memory, &textenum); - if (code != 0) - return fz_throw("cannot gs_text_begin() (%d)", code); - - code = gs_text_process(textenum); - - if (code != 0) - return fz_throw("cannot gs_text_process() (%d)", code); - - gs_text_release(textenum, "gslt font render"); -#endif - buf->count = 0; -} - /* * Parse and draw an XPS element. * @@ -238,20 +172,18 @@ xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs) * Calculate metrics for positioning. */ static void -xps_parse_glyphs_imp(xps_context_t *ctx, fz_font *font, float size, +xps_parse_glyphs_imp(xps_context_t *ctx, fz_matrix ctm, fz_font *font, float size, float originx, float originy, int is_sideways, int bidi_level, char *indices, char *unicode, int is_charpath) { - xps_text_buffer_t buf; xps_glyph_metrics_t mtx; + float e, f; float x = originx; float y = originy; char *us = unicode; char *is = indices; int un = 0; - buf.count = 0; - if (!unicode && !indices) return; @@ -262,6 +194,8 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_font *font, float size, un = strlen(us); } + ctx->text = fz_newtext(font, fz_scale(size, -size), is_sideways); + while ((us && un > 0) || (is && *is)) { int code_count = 1; @@ -332,32 +266,23 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_font *font, float size, u_offset = u_offset * 0.01 * size; v_offset = v_offset * 0.01 * size; - if (buf.count == XPS_TEXT_BUFFER_SIZE) - { - xps_flush_text_buffer(ctx, font, &buf, is_charpath); - } - if (is_sideways) { - buf.x[buf.count] = x + u_offset + (mtx.vorg * size); - buf.y[buf.count] = y - v_offset + (mtx.hadv * 0.5 * size); + e = x + u_offset + (mtx.vorg * size); + f = y - v_offset + (mtx.hadv * 0.5 * size); } else { - buf.x[buf.count] = x + u_offset; - buf.y[buf.count] = y - v_offset; + e = x + u_offset; + f = y - v_offset; } - buf.g[buf.count] = glyph_index; - buf.count ++; + + fz_addtext(ctx->text, glyph_index, char_code, e, f); + // TODO: cluster mapping x += advance * 0.01 * size; } } - - if (buf.count > 0) - { - xps_flush_text_buffer(ctx, font, &buf, is_charpath); - } } void @@ -556,10 +481,15 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, samples[0] = atof(fill_opacity_att); xps_set_color(ctx, colorspace, samples); - xps_parse_glyphs_imp(ctx, font, font_size, + xps_parse_glyphs_imp(ctx, ctm, font, font_size, atof(origin_x_att), atof(origin_y_att), is_sideways, bidi_level, indices_att, unicode_att, 0); + + ctx->dev->filltext(ctx->dev->user, ctx->text, ctm, + ctx->colorspace, ctx->color, ctx->alpha); + fz_freetext(ctx->text); + ctx->text = nil; } /* @@ -568,14 +498,10 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, if (fill_tag) { - fz_warn("glyphs filled with general brushes not implemented!"); -#if 0 - ctx->fill_rule = 1; /* always use non-zero winding rule for char paths */ - xps_parse_glyphs_imp(ctx, font, font_size, + xps_parse_glyphs_imp(ctx, ctm, font, font_size, atof(origin_x_att), atof(origin_y_att), is_sideways, bidi_level, indices_att, unicode_att, 1); xps_parse_brush(ctx, ctm, fill_uri, dict, fill_tag); -#endif } xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); diff --git a/xps/xpspath.c b/xps/xpspath.c index 78925a8b..bc1d3e34 100644 --- a/xps/xpspath.c +++ b/xps/xpspath.c @@ -44,9 +44,15 @@ xps_clip(xps_context_t *ctx, fz_matrix ctm) fz_freepath(ctx->path); ctx->path = NULL; } + else if (ctx->text) + { + ctx->dev->cliptext(ctx->dev->user, ctx->text, ctm, 0); + fz_freetext(ctx->text); + ctx->text = nil; + } else { - printf("clip not a path! (maybe a glyph, or image?)\n"); + fz_warn("clip not a path nor text"); } } -- cgit v1.2.3 From f549710eca496a720a1b6c2dcfe2b0565cf15a97 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Tue, 29 Mar 2011 01:39:44 +0200 Subject: xps: Fix cluster mapping bug. --- xps/xpsglyphs.c | 45 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index c7431440..1c67ebaa 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -185,7 +185,10 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_matrix ctm, fz_font *font, float siz int un = 0; if (!unicode && !indices) + { + fz_warn("glyphs element with neither characters nor indices"); return; + } if (us) { @@ -198,6 +201,7 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_matrix ctm, fz_font *font, float siz while ((us && un > 0) || (is && *is)) { + int char_code = '?'; int code_count = 1; int glyph_count = 1; @@ -211,30 +215,26 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_matrix ctm, fz_font *font, float siz if (glyph_count < 1) glyph_count = 1; - while (code_count > 0 || glyph_count > 0) + /* TODO: add code chars with cluster mappings for proper text extraction */ + + while (code_count--) + { + if (us && un > 0) + { + int t = xps_utf8_to_ucs(&char_code, us, un); + us += t; un -= t; + } + } + + while (glyph_count--) { - int char_code = '?'; int glyph_index = -1; float u_offset = 0.0; float v_offset = 0.0; float advance; - if (glyph_count) - { - if (is && *is) - is = xps_parse_glyph_index(is, &glyph_index); - glyph_count --; - } - - if (code_count) - { - if (us && un > 0) - { - int t = xps_utf8_to_ucs(&char_code, us, un); - us += t; un -= t; - } - code_count --; - } + if (is && *is) + is = xps_parse_glyph_index(is, &glyph_index); if (glyph_index == -1) glyph_index = xps_encode_font_char(font, char_code); @@ -254,12 +254,6 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_matrix ctm, fz_font *font, float siz is ++; } -#if 0 - printf("glyph mapping (%d:%d)%d,%g,%g,%g\n", - code_count, glyph_count, glyph_index, - advance, u_offset, v_offset); -#endif - if (bidi_level & 1) u_offset = -mtx.hadv * 100 - u_offset; @@ -278,7 +272,6 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_matrix ctm, fz_font *font, float siz } fz_addtext(ctx->text, glyph_index, char_code, e, f); - // TODO: cluster mapping x += advance * 0.01 * size; } @@ -440,7 +433,7 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, xps_parse_render_transform(ctx, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); - ctm = fz_concat(ctm, transform); + ctm = fz_concat(transform, ctm); } if (clip_att || clip_tag) -- cgit v1.2.3 From afde83ac20c0f2395708bfa8fda9fb287bb73cb0 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Tue, 29 Mar 2011 01:39:56 +0200 Subject: xps: Fix matrix concatenation order. --- xps/xpsgradient.c | 2 +- xps/xpspage.c | 2 +- xps/xpspath.c | 10 +++++----- xps/xpstile.c | 20 ++++++++++---------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/xps/xpsgradient.c b/xps/xpsgradient.c index 8baf6805..0c06ff15 100644 --- a/xps/xpsgradient.c +++ b/xps/xpsgradient.c @@ -567,7 +567,7 @@ xps_parse_gradient_brush(xps_context_t *ctx, fz_matrix ctm, xps_parse_render_transform(ctx, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); - ctm = fz_concat(ctm, transform); + ctm = fz_concat(transform, ctm); if (!stop_tag) { fz_warn("missing gradient stops tag"); diff --git a/xps/xpspage.c b/xps/xpspage.c index f27b4287..12547784 100644 --- a/xps/xpspage.c +++ b/xps/xpspage.c @@ -57,7 +57,7 @@ xps_parse_canvas(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource xps_parse_render_transform(ctx, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); - ctm = fz_concat(ctm, transform); + ctm = fz_concat(transform, ctm); if (clip_att || clip_tag) { diff --git a/xps/xpspath.c b/xps/xpspath.c index bc1d3e34..f99db248 100644 --- a/xps/xpspath.c +++ b/xps/xpspath.c @@ -92,7 +92,7 @@ xps_draw_arc_segment(xps_context_t *ctx, fz_matrix mtx, float th0, float th1, in { float t, d; fz_point p; -return; // XXX broken + while (th1 < th0) th1 += M_PI * 2.0; @@ -251,9 +251,9 @@ xps_draw_arc(xps_context_t *ctx, } mtx = fz_identity; - mtx = fz_concat(mtx, fz_translate(cx, cy)); - mtx = fz_concat(mtx, fz_rotate(rotation_angle)); - mtx = fz_concat(mtx, fz_scale(rx, ry)); + mtx = fz_concat(fz_translate(cx, cy), mtx); + mtx = fz_concat(fz_rotate(rotation_angle), mtx); + mtx = fz_concat(fz_scale(rx, ry), mtx); xps_draw_arc_segment(ctx, mtx, th1, th1 + dth, is_clockwise); fz_lineto(ctx->path, point_x, point_y); @@ -946,7 +946,7 @@ xps_parse_path(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t xps_parse_render_transform(ctx, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); - ctm = fz_concat(ctm, transform); + ctm = fz_concat(transform, ctm); if (clip_att || clip_tag) { diff --git a/xps/xpstile.c b/xps/xpstile.c index 2c91afa9..e6bcb8cf 100644 --- a/xps/xpstile.c +++ b/xps/xpstile.c @@ -42,22 +42,22 @@ xps_paint_tiling_brush(xps_context_t *ctx, fz_matrix ctm, fz_rect viewbox, int t if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y) { - ttm = fz_concat(ctm, fz_translate(viewbox.x1 * 2, 0)); - ttm = fz_concat(ttm, fz_scale(-1, 1)); + ttm = fz_concat(fz_translate(viewbox.x1 * 2, 0), ctm); + ttm = fz_concat(fz_scale(-1, 1), ttm); xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c); } if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y) { - ttm = fz_concat(ctm, fz_translate(0, viewbox.y1 * 2)); - ttm = fz_concat(ttm, fz_scale(1, -1)); + ttm = fz_concat(fz_translate(0, viewbox.y1 * 2), ctm); + ttm = fz_concat(fz_scale(1, -1), ttm); xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c); } if (tile_mode == TILE_FLIP_X_Y) { - ttm = fz_concat(ctm, fz_translate(viewbox.x1 * 2, viewbox.y1 * 2)); - ttm = fz_concat(ttm, fz_scale(-1, -1)); + ttm = fz_concat(fz_translate(viewbox.x1 * 2, viewbox.y1 * 2), ctm); + ttm = fz_concat(fz_scale(-1, -1), ttm); xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c); } } @@ -152,10 +152,10 @@ xps_parse_tiling_brush(xps_context_t *ctx, fz_matrix ctm, xps_begin_opacity(ctx, ctm, base_uri, dict, opacity_att, NULL); - ctm = fz_concat(ctm, transform); - ctm = fz_concat(ctm, fz_translate(viewport.x0, viewport.y0)); - ctm = fz_concat(ctm, fz_scale(scalex, scaley)); - ctm = fz_concat(ctm, fz_translate(-viewbox.x0, -viewbox.y0)); + ctm = fz_concat(transform, ctm); + ctm = fz_concat(fz_translate(viewport.x0, viewport.y0), ctm); + ctm = fz_concat(fz_scale(scalex, scaley), ctm); + ctm = fz_concat(fz_translate(-viewbox.x0, -viewbox.y0), ctm); if (tile_mode != TILE_NONE) { -- cgit v1.2.3 From 49b5087171894c97168b44b04c9d9b1f6d8f7df8 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Tue, 29 Mar 2011 01:56:27 +0200 Subject: xps: Rudimentary support for tiling with fixed number of repeats. --- xps/xpstile.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/xps/xpstile.c b/xps/xpstile.c index e6bcb8cf..89db1e37 100644 --- a/xps/xpstile.c +++ b/xps/xpstile.c @@ -159,9 +159,20 @@ xps_parse_tiling_brush(xps_context_t *ctx, fz_matrix ctm, if (tile_mode != TILE_NONE) { + float w = viewbox.x1 - viewbox.x0; + float h = viewbox.y1 - viewbox.y0; + fz_matrix ttm; + int x, y; + /* TODO: loop in visible area */ - fz_warn("tiling brushes not implemented!"); - xps_paint_tiling_brush(ctx, ctm, viewbox, tile_mode, &c); + for (y = -10; y < 20; y++) + { + for (x = -10; x < 20; x++) + { + ttm = fz_concat(fz_translate(w*x, h*y), ctm); + xps_paint_tiling_brush(ctx, ttm, viewbox, tile_mode, &c); + } + } } else { -- cgit v1.2.3 From 37d60c8460bfa3607f4621a71b85920f6b7b76f3 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 30 Mar 2011 13:24:22 +0200 Subject: xps: Support vertical writing mode. --- xps/xpsfont.c | 15 +++++++++------ xps/xpsglyphs.c | 11 +++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/xps/xpsfont.c b/xps/xpsfont.c index e983df35..561cc7ec 100644 --- a/xps/xpsfont.c +++ b/xps/xpsfont.c @@ -41,11 +41,14 @@ void xps_measure_font_glyph(xps_context_t *ctx, fz_font *font, int gid, xps_glyph_metrics_t *mtx) { int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM; - FT_Fixed adv; + FT_Face face = font->ftface; + FT_Fixed hadv, vadv; + + FT_Set_Char_Size(face, 64, 64, 72, 72); + FT_Get_Advance(face, gid, mask, &hadv); + FT_Get_Advance(face, gid, mask | FT_LOAD_VERTICAL_LAYOUT, &vadv); - FT_Set_Char_Size(font->ftface, 64, 64, 72, 72); - FT_Get_Advance(font->ftface, gid, mask, &adv); - mtx->hadv = adv / 65536.0f; - mtx->vadv = -1000 / 1000.0f; - mtx->vorg = 880 / 1000.0f; + mtx->hadv = hadv / 65536.0f; + mtx->vadv = vadv / 65536.0f; + mtx->vorg = face->ascender / (float) face->units_per_EM; } diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index 1c67ebaa..74d80f56 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -177,6 +177,7 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_matrix ctm, fz_font *font, float siz char *indices, char *unicode, int is_charpath) { xps_glyph_metrics_t mtx; + fz_matrix tm; float e, f; float x = originx; float y = originy; @@ -197,7 +198,12 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_matrix ctm, fz_font *font, float siz un = strlen(us); } - ctx->text = fz_newtext(font, fz_scale(size, -size), is_sideways); + if (is_sideways) + tm = fz_concat(fz_scale(-size, size), fz_rotate(90)); + else + tm = fz_scale(size, -size); + + ctx->text = fz_newtext(font, tm, is_sideways); while ((us && un > 0) || (is && *is)) { @@ -448,9 +454,6 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, font_size = atof(font_size_att); - if (is_sideways) - fz_warn("sideways text not implemented!"); - xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); /* -- cgit v1.2.3 From 7099f3ec854cdaa5f5f63759703d307fadcfd1a1 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 30 Mar 2011 15:17:02 +0200 Subject: xps: remove _t wart --- xps/muxps.h | 192 +++++++++++++++++++++++++++--------------------------- xps/xpsanalyze.c | 32 ++++----- xps/xpscolor.c | 10 +-- xps/xpscommon.c | 10 +-- xps/xpsdoc.c | 44 ++++++------- xps/xpsfont.c | 2 +- xps/xpsglyphs.c | 22 +++---- xps/xpsgradient.c | 46 ++++++------- xps/xpshash.c | 42 ++++++------ xps/xpsimage.c | 30 ++++----- xps/xpsjpeg.c | 2 +- xps/xpsjxr.c | 26 ++++---- xps/xpsmem.c | 16 ++--- xps/xpsopacity.c | 10 +-- xps/xpspage.c | 24 +++---- xps/xpspath.c | 50 +++++++------- xps/xpspng.c | 6 +- xps/xpsresource.c | 40 ++++++------ xps/xpstiff.c | 48 +++++++------- xps/xpstile.c | 20 +++--- xps/xpstop.c | 10 +-- xps/xpsvisual.c | 12 ++-- xps/xpsxml.c | 62 +++++++++--------- xps/xpszip.c | 74 ++++++++++----------- 24 files changed, 415 insertions(+), 415 deletions(-) diff --git a/xps/muxps.h b/xps/muxps.h index 256ea702..37dd03c6 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -11,7 +11,7 @@ typedef unsigned char byte; * XPS and ZIP constants. */ -typedef struct xps_context_s xps_context_t; +typedef struct xps_context_s xps_context; #define REL_START_PART \ "http://schemas.microsoft.com/xps/2005/06/fixedrepresentation" @@ -34,10 +34,10 @@ typedef struct xps_context_s xps_context_t; #define xps_strdup(ctx, str) fz_strdup(str) #define xps_free(ctx, ptr) fz_free(ptr) -size_t xps_strlcpy(char *destination, const char *source, size_t size); -size_t xps_strlcat(char *destination, const char *source, size_t size); +int xps_strlcpy(char *destination, const char *source, int size); +int xps_strlcat(char *destination, const char *source, int size); int xps_strcasecmp(char *a, char *b); -char *xps_strdup_imp(xps_context_t *ctx, const char *str, const char *function); +char *xps_strdup_imp(xps_context *ctx, const char *str, const char *function); void xps_absolute_path(char *output, char *base_uri, char *path, int output_size); int xps_utf8_to_ucs(int *p, const char *s, int n); @@ -46,21 +46,21 @@ int xps_utf8_to_ucs(int *p, const char *s, int n); * Generic hashtable. */ -typedef struct xps_hash_table_s xps_hash_table_t; +typedef struct xps_hash_table_s xps_hash_table; -xps_hash_table_t *xps_hash_new(xps_context_t *ctx); -void *xps_hash_lookup(xps_hash_table_t *table, char *key); -int xps_hash_insert(xps_context_t *ctx, xps_hash_table_t *table, char *key, void *value); -void xps_hash_free(xps_context_t *ctx, xps_hash_table_t *table, - void (*free_key)(xps_context_t *ctx, void *), - void (*free_value)(xps_context_t *ctx, void *)); -void xps_hash_debug(xps_hash_table_t *table); +xps_hash_table *xps_hash_new(xps_context *ctx); +void *xps_hash_lookup(xps_hash_table *table, char *key); +int xps_hash_insert(xps_context *ctx, xps_hash_table *table, char *key, void *value); +void xps_hash_free(xps_context *ctx, xps_hash_table *table, + void (*free_key)(xps_context *ctx, void *), + void (*free_value)(xps_context *ctx, void *)); +void xps_hash_debug(xps_hash_table *table); /* * Container parts. */ -typedef struct xps_part_s xps_part_t; +typedef struct xps_part_s xps_part; struct xps_part_s { @@ -70,21 +70,21 @@ struct xps_part_s byte *data; }; -xps_part_t *xps_new_part(xps_context_t *ctx, char *name, int size); -xps_part_t *xps_read_part(xps_context_t *ctx, char *partname); -void xps_free_part(xps_context_t *ctx, xps_part_t *part); +xps_part *xps_new_part(xps_context *ctx, char *name, int size); +xps_part *xps_read_part(xps_context *ctx, char *partname); +void xps_free_part(xps_context *ctx, xps_part *part); /* * Document structure. */ -typedef struct xps_document_s xps_document_t; -typedef struct xps_page_s xps_page_t; +typedef struct xps_document_s xps_document; +typedef struct xps_page_s xps_page; struct xps_document_s { char *name; - xps_document_t *next; + xps_document *next; }; struct xps_page_s @@ -93,19 +93,19 @@ struct xps_page_s int width; int height; struct xps_item_s *root; - xps_page_t *next; + xps_page *next; }; -int xps_parse_metadata(xps_context_t *ctx, xps_part_t *part); -void xps_free_fixed_pages(xps_context_t *ctx); -void xps_free_fixed_documents(xps_context_t *ctx); -void xps_debug_fixdocseq(xps_context_t *ctx); +int xps_parse_metadata(xps_context *ctx, xps_part *part); +void xps_free_fixed_pages(xps_context *ctx); +void xps_free_fixed_documents(xps_context *ctx); +void xps_debug_fixdocseq(xps_context *ctx); /* * Images. */ -typedef struct xps_image_s xps_image_t; +typedef struct xps_image_s xps_image; /* type for the information derived directly from the raster file format */ @@ -125,22 +125,22 @@ struct xps_image_s int profilesize; }; -int xps_decode_jpeg(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image); -int xps_decode_png(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image); -int xps_decode_tiff(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image); -int xps_decode_jpegxr(xps_context_t *ctx, byte *buf, int len, xps_image_t *image); +int xps_decode_jpeg(xps_context *ctx, byte *rbuf, int rlen, xps_image *image); +int xps_decode_png(xps_context *ctx, byte *rbuf, int rlen, xps_image *image); +int xps_decode_tiff(xps_context *ctx, byte *rbuf, int rlen, xps_image *image); +int xps_decode_jpegxr(xps_context *ctx, byte *buf, int len, xps_image *image); -int xps_png_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen); -int xps_tiff_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen); -int xps_jpegxr_has_alpha(xps_context_t *ctx, byte *buf, int len); +int xps_png_has_alpha(xps_context *ctx, byte *rbuf, int rlen); +int xps_tiff_has_alpha(xps_context *ctx, byte *rbuf, int rlen); +int xps_jpegxr_has_alpha(xps_context *ctx, byte *buf, int len); -void xps_free_image(xps_context_t *ctx, xps_image_t *image); +void xps_free_image(xps_context *ctx, xps_image *image); /* * Fonts. */ -typedef struct xps_glyph_metrics_s xps_glyph_metrics_t; +typedef struct xps_glyph_metrics_s xps_glyph_metrics; struct xps_glyph_metrics_s { @@ -152,96 +152,96 @@ void xps_identify_font_encoding(fz_font *font, int idx, int *pid, int *eid); void xps_select_font_encoding(fz_font *font, int idx); int xps_encode_font_char(fz_font *font, int key); -void xps_measure_font_glyph(xps_context_t *ctx, fz_font *font, int gid, xps_glyph_metrics_t *mtx); +void xps_measure_font_glyph(xps_context *ctx, fz_font *font, int gid, xps_glyph_metrics *mtx); -void xps_debug_path(xps_context_t *ctx); +void xps_debug_path(xps_context *ctx); /* * Colorspaces and colors. */ -fz_colorspace *xps_read_icc_colorspace(xps_context_t *ctx, char *base_uri, char *profile); -void xps_parse_color(xps_context_t *ctx, char *base_uri, char *hexstring, fz_colorspace **csp, float *samples); -void xps_set_color(xps_context_t *ctx, fz_colorspace *colorspace, float *samples); +fz_colorspace *xps_read_icc_colorspace(xps_context *ctx, char *base_uri, char *profile); +void xps_parse_color(xps_context *ctx, char *base_uri, char *hexstring, fz_colorspace **csp, float *samples); +void xps_set_color(xps_context *ctx, fz_colorspace *colorspace, float *samples); /* * XML document model */ -typedef struct xps_item_s xps_item_t; +typedef struct xps_item_s xps_item; -xps_item_t * xps_parse_xml(xps_context_t *ctx, byte *buf, int len); -xps_item_t * xps_next(xps_item_t *item); -xps_item_t * xps_down(xps_item_t *item); -char * xps_tag(xps_item_t *item); -char * xps_att(xps_item_t *item, const char *att); -void xps_free_item(xps_context_t *ctx, xps_item_t *item); -void xps_debug_item(xps_item_t *item, int level); +xps_item * xps_parse_xml(xps_context *ctx, byte *buf, int len); +xps_item * xps_next(xps_item *item); +xps_item * xps_down(xps_item *item); +char * xps_tag(xps_item *item); +char * xps_att(xps_item *item, const char *att); +void xps_free_item(xps_context *ctx, xps_item *item); +void xps_debug_item(xps_item *item, int level); /* * Resource dictionaries. */ -typedef struct xps_resource_s xps_resource_t; +typedef struct xps_resource_s xps_resource; struct xps_resource_s { char *name; char *base_uri; /* only used in the head nodes */ - xps_item_t *base_xml; /* only used in the head nodes, to free the xml document */ - xps_item_t *data; - xps_resource_t *next; - xps_resource_t *parent; /* up to the previous dict in the stack */ + xps_item *base_xml; /* only used in the head nodes, to free the xml document */ + xps_item *data; + xps_resource *next; + xps_resource *parent; /* up to the previous dict in the stack */ }; -int xps_parse_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, char *base_uri, xps_item_t *root); -void xps_free_resource_dictionary(xps_context_t *ctx, xps_resource_t *dict); -void xps_resolve_resource_reference(xps_context_t *ctx, xps_resource_t *dict, char **attp, xps_item_t **tagp, char **urip); +int xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, xps_item *root); +void xps_free_resource_dictionary(xps_context *ctx, xps_resource *dict); +void xps_resolve_resource_reference(xps_context *ctx, xps_resource *dict, char **attp, xps_item **tagp, char **urip); -void xps_debug_resource_dictionary(xps_resource_t *dict); +void xps_debug_resource_dictionary(xps_resource *dict); /* * Fixed page/graphics parsing. */ -int xps_load_fixed_page(xps_context_t *ctx, xps_page_t *page); -void xps_parse_fixed_page(xps_context_t *ctx, fz_matrix ctm, xps_page_t *page); -void xps_parse_canvas(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -void xps_parse_path(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -void xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -void xps_parse_solid_color_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -void xps_parse_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -void xps_parse_visual_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -void xps_parse_linear_gradient_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -void xps_parse_radial_gradient_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +int xps_load_fixed_page(xps_context *ctx, xps_page *page); +void xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page); +void xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_solid_color_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_tiling_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root, void(*func)(xps_context_t*, fz_matrix, char*, xps_resource_t*, xps_item_t*, void*), void *user); +void xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root, void(*func)(xps_context*, fz_matrix, char*, xps_resource*, xps_item*, void*), void *user); -void xps_parse_matrix_transform(xps_context_t *ctx, xps_item_t *root, fz_matrix *matrix); -void xps_parse_render_transform(xps_context_t *ctx, char *text, fz_matrix *matrix); -void xps_parse_rectangle(xps_context_t *ctx, char *text, fz_rect *rect); -void xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom); -void xps_parse_path_geometry(xps_context_t *ctx, xps_resource_t *dict, xps_item_t *root, int stroking); +void xps_parse_matrix_transform(xps_context *ctx, xps_item *root, fz_matrix *matrix); +void xps_parse_render_transform(xps_context *ctx, char *text, fz_matrix *matrix); +void xps_parse_rectangle(xps_context *ctx, char *text, fz_rect *rect); +void xps_parse_abbreviated_geometry(xps_context *ctx, char *geom); +void xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, int stroking); -void xps_begin_opacity(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, char *opacity_att, xps_item_t *opacity_mask_tag); -void xps_end_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, char *opacity_att, xps_item_t *opacity_mask_tag); +void xps_begin_opacity(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, char *opacity_att, xps_item *opacity_mask_tag); +void xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, char *opacity_att, xps_item *opacity_mask_tag); -void xps_parse_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); -void xps_parse_element(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node); +void xps_parse_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); -void xps_fill(xps_context_t *ctx, fz_matrix ctm); -void xps_clip(xps_context_t *ctx, fz_matrix ctm); -void xps_bounds_in_user_space(xps_context_t *ctx, fz_rect *user); +void xps_fill(xps_context *ctx, fz_matrix ctm); +void xps_clip(xps_context *ctx, fz_matrix ctm); +void xps_bounds_in_user_space(xps_context *ctx, fz_rect *user); -int xps_element_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *node); -int xps_resource_dictionary_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *node); -int xps_image_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root); +int xps_element_has_transparency(xps_context *ctx, char *base_uri, xps_item *node); +int xps_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri, xps_item *node); +int xps_image_brush_has_transparency(xps_context *ctx, char *base_uri, xps_item *root); /* * The interpreter context. */ -typedef struct xps_entry_s xps_entry_t; +typedef struct xps_entry_s xps_entry; struct xps_entry_s { @@ -256,20 +256,20 @@ struct xps_context_s char *directory; FILE *file; int zip_count; - xps_entry_t *zip_table; + xps_entry *zip_table; char *start_part; /* fixed document sequence */ - xps_document_t *first_fixdoc; /* first fixed document */ - xps_document_t *last_fixdoc; /* last fixed document */ - xps_page_t *first_page; /* first page of document */ - xps_page_t *last_page; /* last page of document */ + xps_document *first_fixdoc; /* first fixed document */ + xps_document *last_fixdoc; /* last fixed document */ + xps_page *first_page; /* first page of document */ + xps_page *last_page; /* last page of document */ char *base_uri; /* base uri for parsing XML and resolving relative paths */ char *part_uri; /* part uri for parsing metadata relations */ /* We cache font and colorspace resources */ - xps_hash_table_t *font_table; - xps_hash_table_t *colorspace_table; + xps_hash_table *font_table; + xps_hash_table *colorspace_table; /* Global toggle for transparency */ int use_transparency; @@ -300,11 +300,11 @@ struct xps_context_s fz_device *dev; }; -int xps_read_and_process_page_part(xps_context_t *ctx, fz_matrix ctm, char *name); -int xps_open_file(xps_context_t *ctx, char *filename); -int xps_count_pages(xps_context_t *ctx); -xps_page_t *xps_load_page(xps_context_t *ctx, int number); -xps_context_t *xps_new_context(void); -int xps_free_context(xps_context_t *ctx); +int xps_read_and_process_page_part(xps_context *ctx, fz_matrix ctm, char *name); +int xps_open_file(xps_context *ctx, char *filename); +int xps_count_pages(xps_context *ctx); +xps_page *xps_load_page(xps_context *ctx, int number); +xps_context *xps_new_context(void); +int xps_free_context(xps_context *ctx); #endif diff --git a/xps/xpsanalyze.c b/xps/xpsanalyze.c index 41dc28c6..9cd29840 100644 --- a/xps/xpsanalyze.c +++ b/xps/xpsanalyze.c @@ -7,17 +7,17 @@ #include "muxps.h" static int -xps_remote_resource_dictionary_has_transparency(xps_context_t *ctx, char *base_uri, char *source_att) +xps_remote_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri, char *source_att) { //dputs("page has transparency: uses a remote resource; not parsed; being conservative\n"); return 1; } int -xps_resource_dictionary_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +xps_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) { char *source; - xps_item_t *node; + xps_item *node; source = xps_att(root, "Source"); if (source) @@ -32,9 +32,9 @@ xps_resource_dictionary_has_transparency(xps_context_t *ctx, char *base_uri, xps } static int -xps_gradient_stops_have_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +xps_gradient_stops_have_transparency(xps_context *ctx, char *base_uri, xps_item *root) { - xps_item_t *node; + xps_item *node; fz_colorspace *colorspace; char *color_att; float samples[32]; @@ -60,9 +60,9 @@ xps_gradient_stops_have_transparency(xps_context_t *ctx, char *base_uri, xps_ite } static int -xps_gradient_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +xps_gradient_brush_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) { - xps_item_t *node; + xps_item *node; char *opacity_att; opacity_att = xps_att(root, "Opacity"); @@ -93,11 +93,11 @@ xps_gradient_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item } static int -xps_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +xps_brush_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) { char *opacity_att; char *color_att; - xps_item_t *node; + xps_item *node; fz_colorspace *colorspace; float samples[32]; @@ -170,9 +170,9 @@ xps_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) } static int -xps_path_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +xps_path_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) { - xps_item_t *node; + xps_item *node; for (node = xps_down(root); node; node = xps_next(node)) { @@ -199,9 +199,9 @@ xps_path_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) } static int -xps_glyphs_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +xps_glyphs_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) { - xps_item_t *node; + xps_item *node; for (node = xps_down(root); node; node = xps_next(node)) { @@ -222,9 +222,9 @@ xps_glyphs_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root } static int -xps_canvas_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root) +xps_canvas_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) { - xps_item_t *node; + xps_item *node; for (node = xps_down(root); node; node = xps_next(node)) { @@ -248,7 +248,7 @@ xps_canvas_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root } int -xps_element_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *node) +xps_element_has_transparency(xps_context *ctx, char *base_uri, xps_item *node) { char *opacity_att; char *stroke_att; diff --git a/xps/xpscolor.c b/xps/xpscolor.c index 558bde7c..513e7aab 100644 --- a/xps/xpscolor.c +++ b/xps/xpscolor.c @@ -4,7 +4,7 @@ #include /* for toupper() */ void -xps_set_color(xps_context_t *ctx, fz_colorspace *colorspace, float *samples) +xps_set_color(xps_context *ctx, fz_colorspace *colorspace, float *samples) { int i; @@ -42,7 +42,7 @@ static int count_commas(char *s) } void -xps_parse_color(xps_context_t *ctx, char *base_uri, char *string, +xps_parse_color(xps_context *ctx, char *base_uri, char *string, fz_colorspace **csp, float *samples) { char *p; @@ -142,11 +142,11 @@ xps_parse_color(xps_context_t *ctx, char *base_uri, char *string, } fz_colorspace * -xps_read_icc_colorspace(xps_context_t *ctx, char *base_uri, char *profilename) +xps_read_icc_colorspace(xps_context *ctx, char *base_uri, char *profilename) { #if 0 fz_colorspace *space; - xps_part_t *part; + xps_part *part; char partname[1024]; /* Find ICC colorspace part */ @@ -201,7 +201,7 @@ xps_read_icc_colorspace(xps_context_t *ctx, char *base_uri, char *profilename) } void -xps_parse_solid_color_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node) +xps_parse_solid_color_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node) { char *opacity_att; char *color_att; diff --git a/xps/xpscommon.c b/xps/xpscommon.c index dd8762e8..2d46caa4 100644 --- a/xps/xpscommon.c +++ b/xps/xpscommon.c @@ -2,7 +2,7 @@ #include "muxps.h" void -xps_parse_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node) +xps_parse_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node) { if (!strcmp(xps_tag(node), "SolidColorBrush")) xps_parse_solid_color_brush(ctx, ctm, base_uri, dict, node); @@ -19,7 +19,7 @@ xps_parse_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_ } void -xps_parse_element(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *node) +xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node) { if (!strcmp(xps_tag(node), "Path")) xps_parse_path(ctx, ctm, base_uri, dict, node); @@ -31,7 +31,7 @@ xps_parse_element(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resourc } void -xps_parse_render_transform(xps_context_t *ctx, char *transform, fz_matrix *matrix) +xps_parse_render_transform(xps_context *ctx, char *transform, fz_matrix *matrix) { float args[6]; char *s = transform; @@ -56,7 +56,7 @@ xps_parse_render_transform(xps_context_t *ctx, char *transform, fz_matrix *matri } void -xps_parse_matrix_transform(xps_context_t *ctx, xps_item_t *root, fz_matrix *matrix) +xps_parse_matrix_transform(xps_context *ctx, xps_item *root, fz_matrix *matrix) { char *transform; @@ -71,7 +71,7 @@ xps_parse_matrix_transform(xps_context_t *ctx, xps_item_t *root, fz_matrix *matr } void -xps_parse_rectangle(xps_context_t *ctx, char *text, fz_rect *rect) +xps_parse_rectangle(xps_context *ctx, char *text, fz_rect *rect) { float args[4]; char *s = text; diff --git a/xps/xpsdoc.c b/xps/xpsdoc.c index c0e83e4a..312cac8d 100644 --- a/xps/xpsdoc.c +++ b/xps/xpsdoc.c @@ -3,12 +3,12 @@ #include -xps_part_t * -xps_new_part(xps_context_t *ctx, char *name, int size) +xps_part * +xps_new_part(xps_context *ctx, char *name, int size) { - xps_part_t *part; + xps_part *part; - part = xps_alloc(ctx, sizeof(xps_part_t)); + part = xps_alloc(ctx, sizeof(xps_part)); part->name = xps_strdup(ctx, name); part->size = size; part->data = xps_alloc(ctx, size); @@ -17,7 +17,7 @@ xps_new_part(xps_context_t *ctx, char *name, int size) } void -xps_free_part(xps_context_t *ctx, xps_part_t *part) +xps_free_part(xps_context *ctx, xps_part *part) { xps_free(ctx, part->name); xps_free(ctx, part->data); @@ -30,10 +30,10 @@ xps_free_part(xps_context_t *ctx, xps_part_t *part) */ void -xps_debug_fixdocseq(xps_context_t *ctx) +xps_debug_fixdocseq(xps_context *ctx) { - xps_document_t *fixdoc = ctx->first_fixdoc; - xps_page_t *page = ctx->first_page; + xps_document *fixdoc = ctx->first_fixdoc; + xps_page *page = ctx->first_page; if (ctx->start_part) printf("start part %s\n", ctx->start_part); @@ -52,16 +52,16 @@ xps_debug_fixdocseq(xps_context_t *ctx) } static void -xps_add_fixed_document(xps_context_t *ctx, char *name) +xps_add_fixed_document(xps_context *ctx, char *name) { - xps_document_t *fixdoc; + xps_document *fixdoc; /* Check for duplicates first */ for (fixdoc = ctx->first_fixdoc; fixdoc; fixdoc = fixdoc->next) if (!strcmp(fixdoc->name, name)) return; - fixdoc = xps_alloc(ctx, sizeof(xps_document_t)); + fixdoc = xps_alloc(ctx, sizeof(xps_document)); fixdoc->name = xps_strdup(ctx, name); fixdoc->next = NULL; @@ -78,12 +78,12 @@ xps_add_fixed_document(xps_context_t *ctx, char *name) } void -xps_free_fixed_documents(xps_context_t *ctx) +xps_free_fixed_documents(xps_context *ctx) { - xps_document_t *node = ctx->first_fixdoc; + xps_document *node = ctx->first_fixdoc; while (node) { - xps_document_t *next = node->next; + xps_document *next = node->next; xps_free(ctx, node->name); xps_free(ctx, node); node = next; @@ -93,16 +93,16 @@ xps_free_fixed_documents(xps_context_t *ctx) } static void -xps_add_fixed_page(xps_context_t *ctx, char *name, int width, int height) +xps_add_fixed_page(xps_context *ctx, char *name, int width, int height) { - xps_page_t *page; + xps_page *page; /* Check for duplicates first */ for (page = ctx->first_page; page; page = page->next) if (!strcmp(page->name, name)) return; - page = xps_alloc(ctx, sizeof(xps_page_t)); + page = xps_alloc(ctx, sizeof(xps_page)); page->name = xps_strdup(ctx, name); page->width = width; page->height = height; @@ -122,12 +122,12 @@ xps_add_fixed_page(xps_context_t *ctx, char *name, int width, int height) } void -xps_free_fixed_pages(xps_context_t *ctx) +xps_free_fixed_pages(xps_context *ctx) { - xps_page_t *node = ctx->first_page; + xps_page *node = ctx->first_page; while (node) { - xps_page_t *next = node->next; + xps_page *next = node->next; xps_free(ctx, node->name); xps_free(ctx, node); node = next; @@ -145,7 +145,7 @@ xps_free_fixed_pages(xps_context_t *ctx) static void xps_parse_metadata_imp(void *zp, char *name, char **atts) { - xps_context_t *ctx = zp; + xps_context *ctx = zp; int i; if (!strcmp(name, "Relationship")) @@ -214,7 +214,7 @@ xps_parse_metadata_imp(void *zp, char *name, char **atts) } int -xps_parse_metadata(xps_context_t *ctx, xps_part_t *part) +xps_parse_metadata(xps_context *ctx, xps_part *part) { XML_Parser xp; int code; diff --git a/xps/xpsfont.c b/xps/xpsfont.c index 561cc7ec..a76cc10e 100644 --- a/xps/xpsfont.c +++ b/xps/xpsfont.c @@ -38,7 +38,7 @@ xps_encode_font_char(fz_font *font, int code) } void -xps_measure_font_glyph(xps_context_t *ctx, fz_font *font, int gid, xps_glyph_metrics_t *mtx) +xps_measure_font_glyph(xps_context *ctx, fz_font *font, int gid, xps_glyph_metrics *mtx) { int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM; FT_Face face = font->ftface; diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index 74d80f56..c53e3a25 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -15,7 +15,7 @@ static inline int unhex(int i) * data with the GUID in the fontname. */ static void -xps_deobfuscate_font_resource(xps_context_t *ctx, xps_part_t *part) +xps_deobfuscate_font_resource(xps_context *ctx, xps_part *part) { byte buf[33]; byte key[16]; @@ -172,11 +172,11 @@ xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs) * Calculate metrics for positioning. */ static void -xps_parse_glyphs_imp(xps_context_t *ctx, fz_matrix ctm, fz_font *font, float size, +xps_parse_glyphs_imp(xps_context *ctx, fz_matrix ctm, fz_font *font, float size, float originx, float originy, int is_sideways, int bidi_level, char *indices, char *unicode, int is_charpath) { - xps_glyph_metrics_t mtx; + xps_glyph_metrics mtx; fz_matrix tm; float e, f; float x = originx; @@ -285,10 +285,10 @@ xps_parse_glyphs_imp(xps_context_t *ctx, fz_matrix ctm, fz_font *font, float siz } void -xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, - char *base_uri, xps_resource_t *dict, xps_item_t *root) +xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, + char *base_uri, xps_resource *dict, xps_item *root) { - xps_item_t *node; + xps_item *node; int code; char *fill_uri; @@ -310,14 +310,14 @@ xps_parse_glyphs(xps_context_t *ctx, fz_matrix ctm, char *opacity_att; char *opacity_mask_att; - xps_item_t *transform_tag = NULL; - xps_item_t *clip_tag = NULL; - xps_item_t *fill_tag = NULL; - xps_item_t *opacity_mask_tag = NULL; + xps_item *transform_tag = NULL; + xps_item *clip_tag = NULL; + xps_item *fill_tag = NULL; + xps_item *opacity_mask_tag = NULL; char *fill_opacity_att = NULL; - xps_part_t *part; + xps_part *part; fz_font *font; char partname[1024]; diff --git a/xps/xpsgradient.c b/xps/xpsgradient.c index 0c06ff15..17881d6c 100644 --- a/xps/xpsgradient.c +++ b/xps/xpsgradient.c @@ -35,7 +35,7 @@ static inline float lerp(float a, float b, float x) } static int -xps_parse_gradient_stops(xps_context_t *ctx, char *base_uri, xps_item_t *node, +xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xps_item *node, struct stop *stops, int maxcount) { fz_colorspace *colorspace; @@ -187,15 +187,15 @@ xps_gradient_has_transparent_colors(struct stop *stops, int count) */ static void -xps_draw_one_radial_gradient(xps_context_t *ctx, +xps_draw_one_radial_gradient(xps_context *ctx, int extend, float x0, float y0, float r0, float x1, float y1, float r1) { #if 0 - gs_memory_t *mem = ctx->memory; - gs_shading_t *shading; - gs_shading_R_params_t params; + gs_memory *mem = ctx->memory; + gs_shading *shading; + gs_shading_R_params params; int code; gs_shading_R_params_init(¶ms); @@ -237,14 +237,14 @@ xps_draw_one_radial_gradient(xps_context_t *ctx, */ static void -xps_draw_one_linear_gradient(xps_context_t *ctx, +xps_draw_one_linear_gradient(xps_context *ctx, int extend, float x0, float y0, float x1, float y1) { #if 0 - gs_memory_t *mem = ctx->memory; - gs_shading_t *shading; - gs_shading_A_params_t params; + gs_memory *mem = ctx->memory; + gs_shading *shading; + gs_shading_A_params params; int code; gs_shading_A_params_init(¶ms); @@ -295,7 +295,7 @@ static inline float point_inside_circle(float px, float py, float x, float y, fl } static void -xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread) +xps_draw_radial_gradient(xps_context *ctx, xps_item *root, int spread) { fz_rect bbox; float x0, y0, r0; @@ -428,7 +428,7 @@ xps_draw_radial_gradient(xps_context_t *ctx, xps_item_t *root, int spread) */ static void -xps_draw_linear_gradient(xps_context_t *ctx, xps_item_t *root, int spread) +xps_draw_linear_gradient(xps_context *ctx, xps_item *root, int spread) { fz_rect bbox; float x0, y0, x1, y1; @@ -505,11 +505,11 @@ xps_draw_linear_gradient(xps_context_t *ctx, xps_item_t *root, int spread) */ static void -xps_parse_gradient_brush(xps_context_t *ctx, fz_matrix ctm, - char *base_uri, xps_resource_t *dict, xps_item_t *root, - void (*draw)(xps_context_t *, xps_item_t *, int)) +xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, + char *base_uri, xps_resource *dict, xps_item *root, + void (*draw)(xps_context *, xps_item *, int)) { - xps_item_t *node; + xps_item *node; char *opacity_att; char *interpolation_att; @@ -517,8 +517,8 @@ xps_parse_gradient_brush(xps_context_t *ctx, fz_matrix ctm, char *mapping_att; char *transform_att; - xps_item_t *transform_tag = NULL; - xps_item_t *stop_tag = NULL; + xps_item *transform_tag = NULL; + xps_item *stop_tag = NULL; struct stop stop_list[MAX_STOPS]; int stop_count; @@ -615,8 +615,8 @@ xps_parse_gradient_brush(xps_context_t *ctx, fz_matrix ctm, { if (has_opacity) { - gs_transparency_mask_params_t params; - gs_transparency_group_params_t tgp; + gs_transparency_mask_params params; + gs_transparency_group_params tgp; gs_trans_mask_params_init(¶ms, TRANSPARENCY_MASK_Luminosity); gs_begin_transparency_mask(ctx->pgs, ¶ms, &bbox, 0); @@ -661,15 +661,15 @@ xps_parse_gradient_brush(xps_context_t *ctx, fz_matrix ctm, } void -xps_parse_linear_gradient_brush(xps_context_t *ctx, fz_matrix ctm, - char *base_uri, xps_resource_t *dict, xps_item_t *root) +xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, + char *base_uri, xps_resource *dict, xps_item *root) { xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_linear_gradient); } void -xps_parse_radial_gradient_brush(xps_context_t *ctx, fz_matrix ctm, - char *base_uri, xps_resource_t *dict, xps_item_t *root) +xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, + char *base_uri, xps_resource *dict, xps_item *root) { xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_radial_gradient); } diff --git a/xps/xpshash.c b/xps/xpshash.c index 1e562ff3..24d05074 100644 --- a/xps/xpshash.c +++ b/xps/xpshash.c @@ -14,7 +14,7 @@ static const unsigned primes[] = 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, 0 }; -typedef struct xps_hash_entry_s xps_hash_entry_t; +typedef struct xps_hash_entry_s xps_hash_entry; struct xps_hash_entry_s { @@ -27,7 +27,7 @@ struct xps_hash_table_s void *ctx; unsigned int size; unsigned int load; - xps_hash_entry_t *entries; + xps_hash_entry *entries; }; static inline int @@ -47,12 +47,12 @@ xps_hash(char *s) return h; } -xps_hash_table_t * -xps_hash_new(xps_context_t *ctx) +xps_hash_table * +xps_hash_new(xps_context *ctx) { - xps_hash_table_t *table; + xps_hash_table *table; - table = xps_alloc(ctx, sizeof(xps_hash_table_t)); + table = xps_alloc(ctx, sizeof(xps_hash_table)); if (!table) { fz_throw("out of memory: hash table struct"); @@ -62,7 +62,7 @@ xps_hash_new(xps_context_t *ctx) table->size = primes[0]; table->load = 0; - table->entries = xps_alloc(ctx, sizeof(xps_hash_entry_t) * table->size); + table->entries = xps_alloc(ctx, sizeof(xps_hash_entry) * table->size); if (!table->entries) { xps_free(ctx, table); @@ -70,16 +70,16 @@ xps_hash_new(xps_context_t *ctx) return NULL; } - memset(table->entries, 0, sizeof(xps_hash_entry_t) * table->size); + memset(table->entries, 0, sizeof(xps_hash_entry) * table->size); return table; } static int -xps_hash_double(xps_context_t *ctx, xps_hash_table_t *table) +xps_hash_double(xps_context *ctx, xps_hash_table *table) { - xps_hash_entry_t *old_entries; - xps_hash_entry_t *new_entries; + xps_hash_entry *old_entries; + xps_hash_entry *new_entries; unsigned int old_size = table->size; unsigned int new_size = table->size * 2; int i; @@ -94,7 +94,7 @@ xps_hash_double(xps_context_t *ctx, xps_hash_table_t *table) } old_entries = table->entries; - new_entries = xps_alloc(ctx, sizeof(xps_hash_entry_t) * new_size); + new_entries = xps_alloc(ctx, sizeof(xps_hash_entry) * new_size); if (!new_entries) return fz_throw("out of memory: hash table entries array"); @@ -102,7 +102,7 @@ xps_hash_double(xps_context_t *ctx, xps_hash_table_t *table) table->entries = new_entries; table->load = 0; - memset(table->entries, 0, sizeof(xps_hash_entry_t) * table->size); + memset(table->entries, 0, sizeof(xps_hash_entry) * table->size); for (i = 0; i < old_size; i++) if (old_entries[i].value) @@ -114,9 +114,9 @@ xps_hash_double(xps_context_t *ctx, xps_hash_table_t *table) } void -xps_hash_free(xps_context_t *ctx, xps_hash_table_t *table, - void (*free_key)(xps_context_t *ctx, void *), - void (*free_value)(xps_context_t *ctx, void *)) +xps_hash_free(xps_context *ctx, xps_hash_table *table, + void (*free_key)(xps_context *ctx, void *), + void (*free_value)(xps_context *ctx, void *)) { int i; @@ -133,9 +133,9 @@ xps_hash_free(xps_context_t *ctx, xps_hash_table_t *table, } void * -xps_hash_lookup(xps_hash_table_t *table, char *key) +xps_hash_lookup(xps_hash_table *table, char *key) { - xps_hash_entry_t *entries = table->entries; + xps_hash_entry *entries = table->entries; unsigned int size = table->size; unsigned int pos = xps_hash(key) % size; @@ -152,9 +152,9 @@ xps_hash_lookup(xps_hash_table_t *table, char *key) } int -xps_hash_insert(xps_context_t *ctx, xps_hash_table_t *table, char *key, void *value) +xps_hash_insert(xps_context *ctx, xps_hash_table *table, char *key, void *value) { - xps_hash_entry_t *entries; + xps_hash_entry *entries; unsigned int size, pos; /* Grow the table at 80% load */ @@ -188,7 +188,7 @@ xps_hash_insert(xps_context_t *ctx, xps_hash_table_t *table, char *key, void *va } void -xps_hash_debug(xps_hash_table_t *table) +xps_hash_debug(xps_hash_table *table) { int i; diff --git a/xps/xpsimage.c b/xps/xpsimage.c index 5cd03418..6866518f 100644 --- a/xps/xpsimage.c +++ b/xps/xpsimage.c @@ -2,7 +2,7 @@ #include "muxps.h" static int -xps_decode_image(xps_context_t *ctx, xps_part_t *part, xps_image_t *image) +xps_decode_image(xps_context *ctx, xps_part *part, xps_image *image) { byte *buf = part->data; int len = part->size; @@ -11,7 +11,7 @@ xps_decode_image(xps_context_t *ctx, xps_part_t *part, xps_image_t *image) if (len < 8) return fz_throw("unknown image file format"); - memset(image, 0, sizeof(xps_image_t)); + memset(image, 0, sizeof(xps_image)); if (buf[0] == 0xff && buf[1] == 0xd8) { @@ -44,7 +44,7 @@ xps_decode_image(xps_context_t *ctx, xps_part_t *part, xps_image_t *image) } static void -xps_paint_image_brush_imp(xps_context_t *ctx, fz_matrix ctm, xps_image_t *image) +xps_paint_image_brush_imp(xps_context *ctx, fz_matrix ctm, xps_image *image) { fz_colorspace *colorspace; unsigned int count; @@ -58,10 +58,10 @@ printf("xps_paint_image_brush_imp!\n"); } static void -xps_paint_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root, void *vimage) +xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root, void *vimage) { #if 0 - xps_image_t *image = vimage; + xps_image *image = vimage; int code; if (ctx->opacity_only) @@ -77,8 +77,8 @@ xps_paint_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_res if (image->alpha) { - gs_transparency_mask_params_t params; - gs_transparency_group_params_t tgp; + gs_transparency_mask_params params; + gs_transparency_group_params tgp; fz_rect bbox; xps_bounds_in_user_space(ctx, &bbox); @@ -125,10 +125,10 @@ xps_paint_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_res } static int -xps_find_image_brush_source_part(xps_context_t *ctx, char *base_uri, xps_item_t *root, - xps_part_t **partp, char **profilep) +xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xps_item *root, + xps_part **partp, char **profilep) { - xps_part_t *part; + xps_part *part; char *image_source_att; char buf[1024]; char partname[1024]; @@ -184,10 +184,10 @@ xps_find_image_brush_source_part(xps_context_t *ctx, char *base_uri, xps_item_t } void -xps_parse_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) +xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root) { - xps_part_t *part; - xps_image_t *image; + xps_part *part; + xps_image *image; fz_colorspace *colorspace; char *profilename; int code; @@ -200,7 +200,7 @@ xps_parse_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_res return; } - image = xps_alloc(ctx, sizeof(xps_image_t)); + image = xps_alloc(ctx, sizeof(xps_image)); code = xps_decode_image(ctx, part, image); if (code < 0) { @@ -229,7 +229,7 @@ xps_parse_image_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_res } void -xps_free_image(xps_context_t *ctx, xps_image_t *image) +xps_free_image(xps_context *ctx, xps_image *image) { // TODO: refcount image->colorspace if (image->samples) diff --git a/xps/xpsjpeg.c b/xps/xpsjpeg.c index 937ac4f0..f89c2207 100644 --- a/xps/xpsjpeg.c +++ b/xps/xpsjpeg.c @@ -5,7 +5,7 @@ #include int -xps_decode_jpeg(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image) +xps_decode_jpeg(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) { return fz_throw("jpeg not available"); } diff --git a/xps/xpsjxr.c b/xps/xpsjxr.c index f2f78ce0..db0792ae 100644 --- a/xps/xpsjxr.c +++ b/xps/xpsjxr.c @@ -11,7 +11,7 @@ #include "jpegxr.h" -struct state { xps_context_t *ctx; xps_image_t *output; }; +struct state { xps_context *ctx; xps_image *output; }; static const char * jxr_error_string(int code) @@ -67,11 +67,11 @@ scale_bits(int depth, int value) } static void -xps_decode_jpegxr_block(jxr_image_t image, int mx, int my, int *data) +xps_decode_jpegxr_block(jxr_image image, int mx, int my, int *data) { struct state *state = jxr_get_user_data(image); - xps_context_t *ctx = state->ctx; - xps_image_t *output = state->output; + xps_context *ctx = state->ctx; + xps_image *output = state->output; int depth; unsigned char *p; int x, y, k; @@ -117,11 +117,11 @@ xps_decode_jpegxr_block(jxr_image_t image, int mx, int my, int *data) } static void -xps_decode_jpegxr_alpha_block(jxr_image_t image, int mx, int my, int *data) +xps_decode_jpegxr_alpha_block(jxr_image image, int mx, int my, int *data) { struct state *state = jxr_get_user_data(image); - xps_context_t *ctx = state->ctx; - xps_image_t *output = state->output; + xps_context *ctx = state->ctx; + xps_image *output = state->output; int depth; unsigned char *p; int x, y, k; @@ -152,13 +152,13 @@ xps_decode_jpegxr_alpha_block(jxr_image_t image, int mx, int my, int *data) } int -xps_decode_jpegxr(xps_context_t *ctx, byte *buf, int len, xps_image_t *output) +xps_decode_jpegxr(xps_context *ctx, byte *buf, int len, xps_image *output) { FILE *file; char name[gp_file_name_sizeof]; struct state state; - jxr_container_t container; - jxr_image_t image; + jxr_container container; + jxr_image image; int offset, alpha_offset; int rc; @@ -243,7 +243,7 @@ xps_decode_jpegxr(xps_context_t *ctx, byte *buf, int len, xps_image_t *output) } int -xps_jpegxr_has_alpha(xps_context_t *ctx, byte *buf, int len) +xps_jpegxr_has_alpha(xps_context *ctx, byte *buf, int len) { return 1; } @@ -251,13 +251,13 @@ xps_jpegxr_has_alpha(xps_context_t *ctx, byte *buf, int len) #else int -xps_decode_jpegxr(xps_context_t *ctx, byte *buf, int len, xps_image_t *image) +xps_decode_jpegxr(xps_context *ctx, byte *buf, int len, xps_image *image) { return fz_throw("JPEG-XR codec is not available"); } int -xps_jpegxr_has_alpha(xps_context_t *ctx, byte *buf, int len) +xps_jpegxr_has_alpha(xps_context *ctx, byte *buf, int len) { return 0; } diff --git a/xps/xpsmem.c b/xps/xpsmem.c index 3af3b3f6..baae4e80 100644 --- a/xps/xpsmem.c +++ b/xps/xpsmem.c @@ -21,12 +21,12 @@ xps_strcasecmp(char *a, char *b) return xps_tolower(*a) - xps_tolower(*b); } -size_t -xps_strlcpy(char *dst, const char *src, size_t siz) +int +xps_strlcpy(char *dst, const char *src, int size) { register char *d = dst; register const char *s = src; - register int n = siz; + register int n = size; /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { @@ -38,7 +38,7 @@ xps_strlcpy(char *dst, const char *src, size_t siz) /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { - if (siz != 0) + if (size != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; @@ -47,19 +47,19 @@ xps_strlcpy(char *dst, const char *src, size_t siz) return(s - src - 1); /* count does not include NUL */ } -size_t -xps_strlcat(char *dst, const char *src, size_t siz) +int +xps_strlcat(char *dst, const char *src, int size) { register char *d = dst; register const char *s = src; - register int n = siz; + register int n = size; int dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (*d != '\0' && n-- != 0) d++; dlen = d - dst; - n = siz - dlen; + n = size - dlen; if (n == 0) return dlen + strlen(s); diff --git a/xps/xpsopacity.c b/xps/xpsopacity.c index 8bab138e..19cb4ce3 100644 --- a/xps/xpsopacity.c +++ b/xps/xpsopacity.c @@ -2,7 +2,7 @@ #include "muxps.h" void -xps_bounds_in_user_space(xps_context_t *ctx, fz_rect *ubox) +xps_bounds_in_user_space(xps_context *ctx, fz_rect *ubox) { #if 0 gx_clip_path *clip_path; @@ -22,8 +22,8 @@ xps_bounds_in_user_space(xps_context_t *ctx, fz_rect *ubox) } void -xps_begin_opacity(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, - char *opacity_att, xps_item_t *opacity_mask_tag) +xps_begin_opacity(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, + char *opacity_att, xps_item *opacity_mask_tag) { fz_rect bbox; float opacity; @@ -57,8 +57,8 @@ return; } void -xps_end_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, - char *opacity_att, xps_item_t *opacity_mask_tag) +xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, + char *opacity_att, xps_item *opacity_mask_tag) { if (!opacity_att && !opacity_mask_tag) return; diff --git a/xps/xpspage.c b/xps/xpspage.c index 12547784..aa5a96b4 100644 --- a/xps/xpspage.c +++ b/xps/xpspage.c @@ -2,10 +2,10 @@ #include "muxps.h" void -xps_parse_canvas(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) +xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root) { - xps_resource_t *new_dict = NULL; - xps_item_t *node; + xps_resource *new_dict = NULL; + xps_item *node; char *opacity_mask_uri; int code; @@ -14,9 +14,9 @@ xps_parse_canvas(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource char *opacity_att; char *opacity_mask_att; - xps_item_t *transform_tag = NULL; - xps_item_t *clip_tag = NULL; - xps_item_t *opacity_mask_tag = NULL; + xps_item *transform_tag = NULL; + xps_item *clip_tag = NULL; + xps_item *opacity_mask_tag = NULL; fz_matrix transform; @@ -86,10 +86,10 @@ xps_parse_canvas(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource } void -xps_parse_fixed_page(xps_context_t *ctx, fz_matrix ctm, xps_page_t *page) +xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page) { - xps_item_t *node; - xps_resource_t *dict; + xps_item *node; + xps_resource *dict; char base_uri[1024]; char *s; int code; @@ -119,10 +119,10 @@ xps_parse_fixed_page(xps_context_t *ctx, fz_matrix ctm, xps_page_t *page) } int -xps_load_fixed_page(xps_context_t *ctx, xps_page_t *page) +xps_load_fixed_page(xps_context *ctx, xps_page *page) { - xps_part_t *part; - xps_item_t *root; + xps_part *part; + xps_item *root; char *width_att; char *height_att; diff --git a/xps/xpspath.c b/xps/xpspath.c index f99db248..c0f45527 100644 --- a/xps/xpspath.c +++ b/xps/xpspath.c @@ -36,7 +36,7 @@ fz_currentpoint(fz_path *path) } void -xps_clip(xps_context_t *ctx, fz_matrix ctm) +xps_clip(xps_context *ctx, fz_matrix ctm) { if (ctx->path) { @@ -57,7 +57,7 @@ xps_clip(xps_context_t *ctx, fz_matrix ctm) } void -xps_fill(xps_context_t *ctx, fz_matrix ctm) +xps_fill(xps_context *ctx, fz_matrix ctm) { ctx->dev->fillpath(ctx->dev->user, ctx->path, ctx->fill_rule == 0, ctm, ctx->colorspace, ctx->color, ctx->alpha); @@ -66,7 +66,7 @@ xps_fill(xps_context_t *ctx, fz_matrix ctm) } static void -xps_stroke(xps_context_t *ctx, fz_matrix ctm, fz_strokestate *stroke) +xps_stroke(xps_context *ctx, fz_matrix ctm, fz_strokestate *stroke) { ctx->dev->strokepath(ctx->dev->user, ctx->path, stroke, ctm, ctx->colorspace, ctx->color, ctx->alpha); @@ -75,7 +75,7 @@ xps_stroke(xps_context_t *ctx, fz_matrix ctm, fz_strokestate *stroke) } static void -xps_clipstroke(xps_context_t *ctx, fz_matrix ctm, fz_strokestate *stroke) +xps_clipstroke(xps_context *ctx, fz_matrix ctm, fz_strokestate *stroke) { ctx->dev->clipstrokepath(ctx->dev->user, ctx->path, stroke, ctm); fz_freepath(ctx->path); @@ -88,7 +88,7 @@ xps_clipstroke(xps_context_t *ctx, fz_matrix ctm, fz_strokestate *stroke) * without transforming the line width. */ static inline void -xps_draw_arc_segment(xps_context_t *ctx, fz_matrix mtx, float th0, float th1, int iscw) +xps_draw_arc_segment(xps_context *ctx, fz_matrix mtx, float th0, float th1, int iscw) { float t, d; fz_point p; @@ -154,7 +154,7 @@ angle_between(const fz_point u, const fz_point v) } static void -xps_draw_arc(xps_context_t *ctx, +xps_draw_arc(xps_context *ctx, float size_x, float size_y, float rotation_angle, int is_large_arc, int is_clockwise, float point_x, float point_y) @@ -266,7 +266,7 @@ xps_draw_arc(xps_context_t *ctx, */ void -xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom) +xps_parse_abbreviated_geometry(xps_context *ctx, char *geom) { char **args; char **pargs; @@ -489,7 +489,7 @@ xps_parse_abbreviated_geometry(xps_context_t *ctx, char *geom) } static void -xps_parse_arc_segment(xps_context_t *ctx, xps_item_t *root, int stroking, int *skipped_stroke) +xps_parse_arc_segment(xps_context *ctx, xps_item *root, int stroking, int *skipped_stroke) { /* ArcSegment pretty much follows the SVG algorithm for converting an * arc in endpoint representation to an arc in centerpoint @@ -537,7 +537,7 @@ xps_parse_arc_segment(xps_context_t *ctx, xps_item_t *root, int stroking, int *s } static void -xps_parse_poly_quadratic_bezier_segment(xps_context_t *ctx, xps_item_t *root, int stroking, int *skipped_stroke) +xps_parse_poly_quadratic_bezier_segment(xps_context *ctx, xps_item *root, int stroking, int *skipped_stroke) { char *points_att = xps_att(root, "Points"); char *is_stroked_att = xps_att(root, "IsStroked"); @@ -587,7 +587,7 @@ xps_parse_poly_quadratic_bezier_segment(xps_context_t *ctx, xps_item_t *root, in } static void -xps_parse_poly_bezier_segment(xps_context_t *ctx, xps_item_t *root, int stroking, int *skipped_stroke) +xps_parse_poly_bezier_segment(xps_context *ctx, xps_item *root, int stroking, int *skipped_stroke) { char *points_att = xps_att(root, "Points"); char *is_stroked_att = xps_att(root, "IsStroked"); @@ -628,7 +628,7 @@ xps_parse_poly_bezier_segment(xps_context_t *ctx, xps_item_t *root, int stroking } static void -xps_parse_poly_line_segment(xps_context_t *ctx, xps_item_t *root, int stroking, int *skipped_stroke) +xps_parse_poly_line_segment(xps_context *ctx, xps_item *root, int stroking, int *skipped_stroke) { char *points_att = xps_att(root, "Points"); char *is_stroked_att = xps_att(root, "IsStroked"); @@ -662,9 +662,9 @@ xps_parse_poly_line_segment(xps_context_t *ctx, xps_item_t *root, int stroking, } static void -xps_parse_path_figure(xps_context_t *ctx, xps_item_t *root, int stroking) +xps_parse_path_figure(xps_context *ctx, xps_item *root, int stroking) { - xps_item_t *node; + xps_item *node; char *is_closed_att; char *start_point_att; @@ -715,16 +715,16 @@ xps_parse_path_figure(xps_context_t *ctx, xps_item_t *root, int stroking) } void -xps_parse_path_geometry(xps_context_t *ctx, xps_resource_t *dict, xps_item_t *root, int stroking) +xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, int stroking) { - xps_item_t *node; + xps_item *node; char *figures_att; char *fill_rule_att; char *transform_att; - xps_item_t *transform_tag = NULL; - xps_item_t *figures_tag = NULL; /* only used by resource */ + xps_item *transform_tag = NULL; + xps_item *figures_tag = NULL; /* only used by resource */ fz_matrix transform; @@ -794,9 +794,9 @@ xps_parse_line_cap(char *attr) */ void -xps_parse_path(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) +xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root) { - xps_item_t *node; + xps_item *node; char *fill_uri; char *stroke_uri; @@ -810,12 +810,12 @@ xps_parse_path(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t char *opacity_att; char *opacity_mask_att; - xps_item_t *transform_tag = NULL; - xps_item_t *clip_tag = NULL; - xps_item_t *data_tag = NULL; - xps_item_t *fill_tag = NULL; - xps_item_t *stroke_tag = NULL; - xps_item_t *opacity_mask_tag = NULL; + xps_item *transform_tag = NULL; + xps_item *clip_tag = NULL; + xps_item *data_tag = NULL; + xps_item *fill_tag = NULL; + xps_item *stroke_tag = NULL; + xps_item *opacity_mask_tag = NULL; char *fill_opacity_att = NULL; char *stroke_opacity_att = NULL; diff --git a/xps/xpspng.c b/xps/xpspng.c index c7ed90bc..bfe6f85a 100644 --- a/xps/xpspng.c +++ b/xps/xpspng.c @@ -1,7 +1,7 @@ #include "fitz.h" #include "muxps.h" -#include "png.h" +#include /* * PNG using libpng directly (no gs wrappers) @@ -37,7 +37,7 @@ xps_png_free(png_structp png, png_voidp ptr) /* This only determines if we have an alpha value */ int -xps_png_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen) +xps_png_has_alpha(xps_context *ctx, byte *rbuf, int rlen) { png_structp png; png_infop info; @@ -114,7 +114,7 @@ xps_png_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen) } int -xps_decode_png(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image) +xps_decode_png(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) { png_structp png; png_infop info; diff --git a/xps/xpsresource.c b/xps/xpsresource.c index 399c067a..631b6cb7 100644 --- a/xps/xpsresource.c +++ b/xps/xpsresource.c @@ -1,10 +1,10 @@ #include "fitz.h" #include "muxps.h" -static xps_item_t * -xps_find_resource(xps_context_t *ctx, xps_resource_t *dict, char *name, char **urip) +static xps_item * +xps_find_resource(xps_context *ctx, xps_resource *dict, char *name, char **urip) { - xps_resource_t *head, *node; + xps_resource *head, *node; for (head = dict; head; head = head->parent) { for (node = head; node; node = node->next) @@ -20,8 +20,8 @@ xps_find_resource(xps_context_t *ctx, xps_resource_t *dict, char *name, char **u return NULL; } -static xps_item_t * -xps_parse_resource_reference(xps_context_t *ctx, xps_resource_t *dict, char *att, char **urip) +static xps_item * +xps_parse_resource_reference(xps_context *ctx, xps_resource *dict, char *att, char **urip) { char name[1024]; char *s; @@ -38,12 +38,12 @@ xps_parse_resource_reference(xps_context_t *ctx, xps_resource_t *dict, char *att } void -xps_resolve_resource_reference(xps_context_t *ctx, xps_resource_t *dict, - char **attp, xps_item_t **tagp, char **urip) +xps_resolve_resource_reference(xps_context *ctx, xps_resource *dict, + char **attp, xps_item **tagp, char **urip) { if (*attp) { - xps_item_t *rsrc = xps_parse_resource_reference(ctx, dict, *attp, urip); + xps_item *rsrc = xps_parse_resource_reference(ctx, dict, *attp, urip); if (rsrc) { *attp = NULL; @@ -53,13 +53,13 @@ xps_resolve_resource_reference(xps_context_t *ctx, xps_resource_t *dict, } static int -xps_parse_remote_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, char *base_uri, char *source_att) +xps_parse_remote_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, char *source_att) { char part_name[1024]; char part_uri[1024]; - xps_resource_t *dict; - xps_part_t *part; - xps_item_t *xml; + xps_resource *dict; + xps_part *part; + xps_item *xml; char *s; int code; @@ -107,11 +107,11 @@ xps_parse_remote_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, } int -xps_parse_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, char *base_uri, xps_item_t *root) +xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, xps_item *root) { - xps_resource_t *head; - xps_resource_t *entry; - xps_item_t *node; + xps_resource *head; + xps_resource *entry; + xps_item *node; char *source; char *key; int code; @@ -133,7 +133,7 @@ xps_parse_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, char * key = xps_att(node, "Key"); if (key) { - entry = xps_alloc(ctx, sizeof(xps_resource_t)); + entry = xps_alloc(ctx, sizeof(xps_resource)); if (!entry) return fz_throw("cannot allocate resource entry"); entry->name = key; @@ -156,9 +156,9 @@ xps_parse_resource_dictionary(xps_context_t *ctx, xps_resource_t **dictp, char * } void -xps_free_resource_dictionary(xps_context_t *ctx, xps_resource_t *dict) +xps_free_resource_dictionary(xps_context *ctx, xps_resource *dict) { - xps_resource_t *next; + xps_resource *next; while (dict) { next = dict->next; @@ -172,7 +172,7 @@ xps_free_resource_dictionary(xps_context_t *ctx, xps_resource_t *dict) } void -xps_debug_resource_dictionary(xps_resource_t *dict) +xps_debug_resource_dictionary(xps_resource *dict) { while (dict) { diff --git a/xps/xpstiff.c b/xps/xpstiff.c index 27e5ac2b..6d6050e6 100644 --- a/xps/xpstiff.c +++ b/xps/xpstiff.c @@ -2,7 +2,7 @@ #include "muxps.h" int -xps_decode_tiff(xps_context_t *ctx, byte *buf, int len, xps_image_t *image) +xps_decode_tiff(xps_context *ctx, byte *buf, int len, xps_image *image) { return fz_throw("TIFF codec is not available"); } @@ -30,7 +30,7 @@ xps_decode_tiff(xps_context_t *ctx, byte *buf, int len, xps_image_t *image) * TODO: RGBPal images */ -typedef struct xps_tiff_s xps_tiff_t; +typedef struct xps_tiff_s xps_tiff; struct xps_tiff_s { @@ -158,7 +158,7 @@ xps_report_error(stream_state * st, const char *str) } static inline int -readbyte(xps_tiff_t *tiff) +readbyte(xps_tiff *tiff) { if (tiff->rp < tiff->ep) return *tiff->rp++; @@ -166,7 +166,7 @@ readbyte(xps_tiff_t *tiff) } static inline unsigned -readshort(xps_tiff_t *tiff) +readshort(xps_tiff *tiff) { unsigned a = readbyte(tiff); unsigned b = readbyte(tiff); @@ -176,7 +176,7 @@ readshort(xps_tiff_t *tiff) } static inline unsigned -readlong(xps_tiff_t *tiff) +readlong(xps_tiff *tiff) { unsigned a = readbyte(tiff); unsigned b = readbyte(tiff); @@ -188,14 +188,14 @@ readlong(xps_tiff_t *tiff) } static int -xps_decode_tiff_uncompressed(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +xps_decode_tiff_uncompressed(xps_context *ctx, xps_tiff *tiff, byte *rp, byte *rl, byte *wp, byte *wl) { memcpy(wp, rp, wl - wp); return gs_okay; } static int -xps_decode_tiff_packbits(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +xps_decode_tiff_packbits(xps_context *ctx, xps_tiff *tiff, byte *rp, byte *rl, byte *wp, byte *wl) { stream_RLD_state state; stream_cursor_read scr; @@ -221,7 +221,7 @@ xps_decode_tiff_packbits(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *r } static int -xps_decode_tiff_lzw(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +xps_decode_tiff_lzw(xps_context *ctx, xps_tiff *tiff, byte *rp, byte *rl, byte *wp, byte *wl) { stream_LZW_state state; stream_cursor_read scr; @@ -267,7 +267,7 @@ xps_decode_tiff_lzw(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, by } static int -xps_decode_tiff_flate(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +xps_decode_tiff_flate(xps_context *ctx, xps_tiff *tiff, byte *rp, byte *rl, byte *wp, byte *wl) { stream_zlib_state state; stream_cursor_read scr; @@ -298,7 +298,7 @@ xps_decode_tiff_flate(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, } static int -xps_decode_tiff_fax(xps_context_t *ctx, xps_tiff_t *tiff, int comp, byte *rp, byte *rl, byte *wp, byte *wl) +xps_decode_tiff_fax(xps_context *ctx, xps_tiff *tiff, int comp, byte *rp, byte *rl, byte *wp, byte *wl) { stream_CFD_state state; stream_cursor_read scr; @@ -349,7 +349,7 @@ xps_decode_tiff_fax(xps_context_t *ctx, xps_tiff_t *tiff, int comp, byte *rp, by */ static int -xps_decode_tiff_jpeg(xps_context_t *ctx, xps_tiff_t *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +xps_decode_tiff_jpeg(xps_context *ctx, xps_tiff *tiff, byte *rp, byte *rl, byte *wp, byte *wl) { stream_DCT_state state; /* used by gs_jpeg_* wrappers */ jpeg_decompress_data jddp; @@ -518,7 +518,7 @@ xps_invert_tiff(byte *line, int width, int comps, int bits, int alpha) } static int -xps_expand_colormap(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) +xps_expand_colormap(xps_context *ctx, xps_tiff *tiff, xps_image *image) { int maxval = 1 << image->bits; byte *samples; @@ -576,7 +576,7 @@ xps_expand_colormap(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) } static int -xps_decode_tiff_strips(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) +xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) { int error; @@ -773,7 +773,7 @@ xps_decode_tiff_strips(xps_context_t *ctx, xps_tiff_t *tiff, xps_image_t *image) } static void -xps_read_tiff_bytes(unsigned char *p, xps_tiff_t *tiff, unsigned ofs, unsigned n) +xps_read_tiff_bytes(unsigned char *p, xps_tiff *tiff, unsigned ofs, unsigned n) { tiff->rp = tiff->bp + ofs; if (tiff->rp > tiff->ep) @@ -786,7 +786,7 @@ xps_read_tiff_bytes(unsigned char *p, xps_tiff_t *tiff, unsigned ofs, unsigned n } static void -xps_read_tiff_tag_value(unsigned *p, xps_tiff_t *tiff, unsigned type, unsigned ofs, unsigned n) +xps_read_tiff_tag_value(unsigned *p, xps_tiff *tiff, unsigned type, unsigned ofs, unsigned n) { tiff->rp = tiff->bp + ofs; if (tiff->rp > tiff->ep) @@ -810,7 +810,7 @@ xps_read_tiff_tag_value(unsigned *p, xps_tiff_t *tiff, unsigned type, unsigned o } static int -xps_read_tiff_tag(xps_context_t *ctx, xps_tiff_t *tiff, unsigned offset) +xps_read_tiff_tag(xps_context *ctx, xps_tiff *tiff, unsigned offset) { unsigned tag; unsigned type; @@ -949,7 +949,7 @@ xps_swap_byte_order(byte *buf, int n) } static int -xps_decode_tiff_header(xps_context_t *ctx, xps_tiff_t *tiff, byte *buf, int len) +xps_decode_tiff_header(xps_context *ctx, xps_tiff *tiff, byte *buf, int len) { unsigned version; unsigned offset; @@ -957,7 +957,7 @@ xps_decode_tiff_header(xps_context_t *ctx, xps_tiff_t *tiff, byte *buf, int len) unsigned i; int error; - memset(tiff, 0, sizeof(xps_tiff_t)); + memset(tiff, 0, sizeof(xps_tiff)); tiff->bp = buf; tiff->rp = buf; @@ -1015,11 +1015,11 @@ xps_decode_tiff_header(xps_context_t *ctx, xps_tiff_t *tiff, byte *buf, int len) } int -xps_decode_tiff(xps_context_t *ctx, byte *buf, int len, xps_image_t *image) +xps_decode_tiff(xps_context *ctx, byte *buf, int len, xps_image *image) { int error; - xps_tiff_t tiffst; - xps_tiff_t *tiff = &tiffst; + xps_tiff tiffst; + xps_tiff *tiff = &tiffst; error = xps_decode_tiff_header(ctx, tiff, buf, len); if (error) @@ -1063,11 +1063,11 @@ xps_decode_tiff(xps_context_t *ctx, byte *buf, int len, xps_image_t *image) } int -xps_tiff_has_alpha(xps_context_t *ctx, byte *buf, int len) +xps_tiff_has_alpha(xps_context *ctx, byte *buf, int len) { int error; - xps_tiff_t tiffst; - xps_tiff_t *tiff = &tiffst; + xps_tiff tiffst; + xps_tiff *tiff = &tiffst; error = xps_decode_tiff_header(ctx, tiff, buf, len); if (error) diff --git a/xps/xpstile.c b/xps/xpstile.c index 89db1e37..0bfff9b0 100644 --- a/xps/xpstile.c +++ b/xps/xpstile.c @@ -11,14 +11,14 @@ enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y }; struct closure { char *base_uri; - xps_resource_t *dict; - xps_item_t *root; + xps_resource *dict; + xps_item *root; void *user; - void (*func)(xps_context_t*, fz_matrix, char*, xps_resource_t*, xps_item_t*, void*); + void (*func)(xps_context*, fz_matrix, char*, xps_resource*, xps_item*, void*); }; static void -xps_paint_tiling_brush_clipped(xps_context_t *ctx, fz_matrix ctm, fz_rect viewbox, struct closure *c) +xps_paint_tiling_brush_clipped(xps_context *ctx, fz_matrix ctm, fz_rect viewbox, struct closure *c) { ctx->path = fz_newpath(); fz_moveto(ctx->path, viewbox.x0, viewbox.y0); @@ -34,7 +34,7 @@ xps_paint_tiling_brush_clipped(xps_context_t *ctx, fz_matrix ctm, fz_rect viewbo } static void -xps_paint_tiling_brush(xps_context_t *ctx, fz_matrix ctm, fz_rect viewbox, int tile_mode, struct closure *c) +xps_paint_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect viewbox, int tile_mode, struct closure *c) { fz_matrix ttm; @@ -63,11 +63,11 @@ xps_paint_tiling_brush(xps_context_t *ctx, fz_matrix ctm, fz_rect viewbox, int t } void -xps_parse_tiling_brush(xps_context_t *ctx, fz_matrix ctm, - char *base_uri, xps_resource_t *dict, xps_item_t *root, - void (*func)(xps_context_t*, fz_matrix, char*, xps_resource_t*, xps_item_t*, void*), void *user) +xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, + char *base_uri, xps_resource *dict, xps_item *root, + void (*func)(xps_context*, fz_matrix, char*, xps_resource*, xps_item*, void*), void *user) { - xps_item_t *node; + xps_item *node; struct closure c; char *opacity_att; @@ -78,7 +78,7 @@ xps_parse_tiling_brush(xps_context_t *ctx, fz_matrix ctm, char *viewbox_units_att; char *viewport_units_att; - xps_item_t *transform_tag = NULL; + xps_item *transform_tag = NULL; fz_matrix transform; fz_rect viewbox; diff --git a/xps/xpstop.c b/xps/xpstop.c index e1a2fe1e..2ae1d585 100644 --- a/xps/xpstop.c +++ b/xps/xpstop.c @@ -83,16 +83,16 @@ static int isrange(char *s) } static void -xps_run_page(xps_context_t *ctx, xps_page_t *page, fz_device *dev, fz_matrix ctm) +xps_run_page(xps_context *ctx, xps_page *page, fz_device *dev, fz_matrix ctm) { ctx->dev = dev; xps_parse_fixed_page(ctx, ctm, page); ctx->dev = nil; } -static void drawpage(xps_context_t *ctx, int pagenum) +static void drawpage(xps_context *ctx, int pagenum) { - xps_page_t *page; + xps_page *page; fz_displaylist *list; fz_device *dev; int start; @@ -241,7 +241,7 @@ static void drawpage(xps_context_t *ctx, int pagenum) } -static void drawrange(xps_context_t *ctx, char *range) +static void drawrange(xps_context *ctx, char *range) { int page, spage, epage; char *spec, *dash; @@ -282,7 +282,7 @@ int main(int argc, char **argv) { int grayscale = 0; int accelerate = 1; - xps_context_t *ctx; + xps_context *ctx; int code; int c; diff --git a/xps/xpsvisual.c b/xps/xpsvisual.c index d9aa5340..82d37691 100644 --- a/xps/xpsvisual.c +++ b/xps/xpsvisual.c @@ -4,20 +4,20 @@ enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y }; static void -xps_paint_visual_brush(xps_context_t *ctx, fz_matrix ctm, - char *base_uri, xps_resource_t *dict, xps_item_t *root, void *visual_tag) +xps_paint_visual_brush(xps_context *ctx, fz_matrix ctm, + char *base_uri, xps_resource *dict, xps_item *root, void *visual_tag) { - xps_parse_element(ctx, ctm, base_uri, dict, (xps_item_t *)visual_tag); + xps_parse_element(ctx, ctm, base_uri, dict, (xps_item *)visual_tag); } void -xps_parse_visual_brush(xps_context_t *ctx, fz_matrix ctm, char *base_uri, xps_resource_t *dict, xps_item_t *root) +xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root) { - xps_item_t *node; + xps_item *node; char *visual_uri; char *visual_att; - xps_item_t *visual_tag = NULL; + xps_item *visual_tag = NULL; visual_att = xps_att(root, "Visual"); diff --git a/xps/xpsxml.c b/xps/xpsxml.c index 94d1e351..70dbe49d 100644 --- a/xps/xpsxml.c +++ b/xps/xpsxml.c @@ -10,13 +10,13 @@ #define NS_XPS "http://schemas.microsoft.com/xps/2005/06" #define NS_MC "http://schemas.openxmlformats.org/markup-compatibility/2006" -typedef struct xps_parser_s xps_parser_t; +typedef struct xps_parser_s xps_parser; struct xps_parser_s { - xps_context_t *ctx; - xps_item_t *root; - xps_item_t *head; + xps_context *ctx; + xps_item *root; + xps_item *head; char *error; int compat; char *base; /* base of relative URIs */ @@ -26,9 +26,9 @@ struct xps_item_s { char *name; char **atts; - xps_item_t *up; - xps_item_t *down; - xps_item_t *next; + xps_item *up; + xps_item *down; + xps_item *next; }; static char * @@ -43,9 +43,9 @@ skip_namespace(char *s) static void on_open_tag(void *zp, char *ns_name, char **atts) { - xps_parser_t *parser = zp; - xps_item_t *item; - xps_item_t *tail; + xps_parser *parser = zp; + xps_item *item; + xps_item *tail; int namelen; int attslen; int textlen; @@ -92,7 +92,7 @@ on_open_tag(void *zp, char *ns_name, char **atts) textlen += strlen(atts[i]) + 1; } - item = fz_malloc(sizeof(xps_item_t) + attslen + namelen + textlen); + item = fz_malloc(sizeof(xps_item) + attslen + namelen + textlen); if (!item) { parser->error = "out of memory"; @@ -100,9 +100,9 @@ on_open_tag(void *zp, char *ns_name, char **atts) /* copy strings to new memory */ - item->atts = (char**) (((char*)item) + sizeof(xps_item_t)); - item->name = ((char*)item) + sizeof(xps_item_t) + attslen; - p = ((char*)item) + sizeof(xps_item_t) + attslen + namelen; + item->atts = (char**) (((char*)item) + sizeof(xps_item)); + item->name = ((char*)item) + sizeof(xps_item) + attslen; + p = ((char*)item) + sizeof(xps_item) + attslen + namelen; strcpy(item->name, name); for (i = 0; atts[i]; i++) @@ -147,7 +147,7 @@ on_open_tag(void *zp, char *ns_name, char **atts) static void on_close_tag(void *zp, char *name) { - xps_parser_t *parser = zp; + xps_parser *parser = zp; if (parser->error) return; @@ -165,7 +165,7 @@ is_xml_space(int c) static void on_text(void *zp, char *buf, int len) { - xps_parser_t *parser = zp; + xps_parser *parser = zp; char *atts[3]; int i; @@ -197,17 +197,17 @@ on_text(void *zp, char *buf, int len) } } -static xps_item_t * -xps_process_compatibility(xps_context_t *ctx, xps_item_t *root) +static xps_item * +xps_process_compatibility(xps_context *ctx, xps_item *root) { fz_warn("XPS document uses markup compatibility tags"); return root; } -xps_item_t * -xps_parse_xml(xps_context_t *ctx, byte *buf, int len) +xps_item * +xps_parse_xml(xps_context *ctx, byte *buf, int len) { - xps_parser_t parser; + xps_parser parser; XML_Parser xp; int code; @@ -248,26 +248,26 @@ xps_parse_xml(xps_context_t *ctx, byte *buf, int len) return parser.root; } -xps_item_t * -xps_next(xps_item_t *item) +xps_item * +xps_next(xps_item *item) { return item->next; } -xps_item_t * -xps_down(xps_item_t *item) +xps_item * +xps_down(xps_item *item) { return item->down; } char * -xps_tag(xps_item_t *item) +xps_tag(xps_item *item) { return item->name; } char * -xps_att(xps_item_t *item, const char *att) +xps_att(xps_item *item, const char *att) { int i; for (i = 0; item->atts[i]; i += 2) @@ -277,9 +277,9 @@ xps_att(xps_item_t *item, const char *att) } void -xps_free_item(xps_context_t *ctx, xps_item_t *item) +xps_free_item(xps_context *ctx, xps_item *item) { - xps_item_t *next; + xps_item *next; while (item) { next = item->next; @@ -297,7 +297,7 @@ static void indent(int n) } static void -xps_debug_item_imp(xps_item_t *item, int level, int loop) +xps_debug_item_imp(xps_item *item, int level, int loop) { int i; @@ -333,7 +333,7 @@ xps_debug_item_imp(xps_item_t *item, int level, int loop) } void -xps_debug_item(xps_item_t *item, int level) +xps_debug_item(xps_item *item, int level) { xps_debug_item_imp(item, level, 0); } diff --git a/xps/xpszip.c b/xps/xpszip.c index 639134cf..d3c03103 100644 --- a/xps/xpszip.c +++ b/xps/xpszip.c @@ -20,13 +20,13 @@ static inline int getlong(FILE *file) } static void * -xps_zip_alloc_items(xps_context_t *ctx, int items, int size) +xps_zip_alloc_items(xps_context *ctx, int items, int size) { return xps_alloc(ctx, items * size); } static void -xps_zip_free(xps_context_t *ctx, void *ptr) +xps_zip_free(xps_context *ctx, void *ptr) { xps_free(ctx, ptr); } @@ -34,13 +34,13 @@ xps_zip_free(xps_context_t *ctx, void *ptr) static int xps_compare_entries(const void *a0, const void *b0) { - xps_entry_t *a = (xps_entry_t*) a0; - xps_entry_t *b = (xps_entry_t*) b0; + xps_entry *a = (xps_entry*) a0; + xps_entry *b = (xps_entry*) b0; return xps_strcasecmp(a->name, b->name); } -static xps_entry_t * -xps_find_zip_entry(xps_context_t *ctx, char *name) +static xps_entry * +xps_find_zip_entry(xps_context *ctx, char *name) { int l = 0; int r = ctx->zip_count - 1; @@ -63,7 +63,7 @@ xps_find_zip_entry(xps_context_t *ctx, char *name) */ static int -xps_read_zip_entry(xps_context_t *ctx, xps_entry_t *ent, unsigned char *outbuf) +xps_read_zip_entry(xps_context *ctx, xps_entry *ent, unsigned char *outbuf) { z_stream stream; unsigned char *inbuf; @@ -138,7 +138,7 @@ xps_read_zip_entry(xps_context_t *ctx, xps_entry_t *ent, unsigned char *outbuf) */ static int -xps_read_zip_dir(xps_context_t *ctx, int start_offset) +xps_read_zip_dir(xps_context *ctx, int start_offset) { int sig; int offset, count; @@ -159,11 +159,11 @@ xps_read_zip_dir(xps_context_t *ctx, int start_offset) offset = getlong(ctx->file); /* offset to central directory */ ctx->zip_count = count; - ctx->zip_table = xps_alloc(ctx, sizeof(xps_entry_t) * count); + ctx->zip_table = xps_alloc(ctx, sizeof(xps_entry) * count); if (!ctx->zip_table) return fz_throw("cannot allocate zip entry table"); - memset(ctx->zip_table, 0, sizeof(xps_entry_t) * count); + memset(ctx->zip_table, 0, sizeof(xps_entry) * count); fseek(ctx->file, offset, 0); @@ -201,13 +201,13 @@ xps_read_zip_dir(xps_context_t *ctx, int start_offset) fseek(ctx->file, commentsize, 1); } - qsort(ctx->zip_table, count, sizeof(xps_entry_t), xps_compare_entries); + qsort(ctx->zip_table, count, sizeof(xps_entry), xps_compare_entries); return fz_okay; } static int -xps_find_and_read_zip_dir(xps_context_t *ctx) +xps_find_and_read_zip_dir(xps_context *ctx) { int filesize, back, maxback; int i, n; @@ -241,12 +241,12 @@ xps_find_and_read_zip_dir(xps_context_t *ctx) * Read and interleave split parts from a ZIP file. */ -static xps_part_t * -xps_read_zip_part(xps_context_t *ctx, char *partname) +static xps_part * +xps_read_zip_part(xps_context *ctx, char *partname) { char buf[2048]; - xps_entry_t *ent; - xps_part_t *part; + xps_entry *ent; + xps_part *part; int count, size, offset, i; char *name; @@ -306,11 +306,11 @@ xps_read_zip_part(xps_context_t *ctx, char *partname) * Read and interleave split parts from files in the directory. */ -static xps_part_t * -xps_read_dir_part(xps_context_t *ctx, char *name) +static xps_part * +xps_read_dir_part(xps_context *ctx, char *name) { char buf[2048]; - xps_part_t *part; + xps_part *part; FILE *file; int count, size, offset, i, n; @@ -372,8 +372,8 @@ xps_read_dir_part(xps_context_t *ctx, char *name) return NULL; } -xps_part_t * -xps_read_part(xps_context_t *ctx, char *partname) +xps_part * +xps_read_part(xps_context *ctx, char *partname) { if (ctx->directory) return xps_read_dir_part(ctx, partname); @@ -385,9 +385,9 @@ xps_read_part(xps_context_t *ctx, char *partname) */ static int -xps_read_and_process_metadata_part(xps_context_t *ctx, char *name) +xps_read_and_process_metadata_part(xps_context *ctx, char *name) { - xps_part_t *part; + xps_part *part; int code; part = xps_read_part(ctx, name); @@ -408,10 +408,10 @@ xps_read_and_process_metadata_part(xps_context_t *ctx, char *name) */ int -xps_open_file(xps_context_t *ctx, char *filename) +xps_open_file(xps_context *ctx, char *filename) { char buf[2048]; - xps_document_t *doc; + xps_document *doc; int code; char *p; @@ -457,19 +457,19 @@ xps_open_file(xps_context_t *ctx, char *filename) } int -xps_count_pages(xps_context_t *ctx) +xps_count_pages(xps_context *ctx) { - xps_page_t *page; + xps_page *page; int n = 0; for (page = ctx->first_page; page; page = page->next) n ++; return n; } -xps_page_t * -xps_load_page(xps_context_t *ctx, int number) +xps_page * +xps_load_page(xps_context *ctx, int number) { - xps_page_t *page; + xps_page *page; int code; int n = 0; @@ -490,14 +490,14 @@ xps_load_page(xps_context_t *ctx, int number) return nil; } -xps_context_t * +xps_context * xps_new_context(void) { - xps_context_t *ctx; + xps_context *ctx; - ctx = fz_malloc(sizeof(xps_context_t)); + ctx = fz_malloc(sizeof(xps_context)); - memset(ctx, 0, sizeof(xps_context_t)); + memset(ctx, 0, sizeof(xps_context)); ctx->font_table = xps_hash_new(ctx); ctx->colorspace_table = xps_hash_new(ctx); @@ -514,19 +514,19 @@ xps_new_context(void) return ctx; } -static void xps_free_key_func(xps_context_t *ctx, void *ptr) +static void xps_free_key_func(xps_context *ctx, void *ptr) { xps_free(ctx, ptr); } -static void xps_free_font_func(xps_context_t *ctx, void *ptr) +static void xps_free_font_func(xps_context *ctx, void *ptr) { fz_dropfont(ptr); } /* Wrap up interp instance after a "job" */ int -xps_free_context(xps_context_t *ctx) +xps_free_context(xps_context *ctx) { int i; -- cgit v1.2.3 From 59ff521faa134a5bd2c4de3621eeadc98d272bac Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 30 Mar 2011 15:25:03 +0200 Subject: xps: Use fitz memory and string functions. --- xps/muxps.h | 8 ------ xps/xpscolor.c | 2 +- xps/xpscrc.c | 81 ------------------------------------------------------- xps/xpsdoc.c | 32 +++++++++++----------- xps/xpsglyphs.c | 2 +- xps/xpshash.c | 14 +++++----- xps/xpsimage.c | 14 +++++----- xps/xpsjxr.c | 4 +-- xps/xpsmem.c | 62 +++--------------------------------------- xps/xpspage.c | 2 +- xps/xpspath.c | 2 +- xps/xpspng.c | 4 +-- xps/xpsresource.c | 12 ++++----- xps/xpstiff.c | 26 +++++++++--------- xps/xpsxml.c | 2 +- xps/xpszip.c | 26 +++++++++--------- 16 files changed, 75 insertions(+), 218 deletions(-) delete mode 100644 xps/xpscrc.c diff --git a/xps/muxps.h b/xps/muxps.h index 37dd03c6..c778be37 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -29,15 +29,7 @@ typedef struct xps_context_s xps_context; * Memory, and string functions. */ -#define xps_alloc(ctx, size) fz_malloc(size) -#define xps_realloc(ctx, ptr, size) fz_realloc(ptr, size, 1) -#define xps_strdup(ctx, str) fz_strdup(str) -#define xps_free(ctx, ptr) fz_free(ptr) - -int xps_strlcpy(char *destination, const char *source, int size); -int xps_strlcat(char *destination, const char *source, int size); int xps_strcasecmp(char *a, char *b); -char *xps_strdup_imp(xps_context *ctx, const char *str, const char *function); void xps_absolute_path(char *output, char *base_uri, char *path, int output_size); int xps_utf8_to_ucs(int *p, const char *s, int n); diff --git a/xps/xpscolor.c b/xps/xpscolor.c index 513e7aab..bb2f225f 100644 --- a/xps/xpscolor.c +++ b/xps/xpscolor.c @@ -191,7 +191,7 @@ xps_read_icc_colorspace(xps_context *ctx, char *base_uri, char *profilename) xps_free_part(ctx, part); /* Add colorspace to xps color cache. */ - xps_hash_insert(ctx, ctx->colorspace_table, xps_strdup(ctx, partname), space); + xps_hash_insert(ctx, ctx->colorspace_table, fz_strdup(partname), space); } return space; diff --git a/xps/xpscrc.c b/xps/xpscrc.c deleted file mode 100644 index d5d4acd8..00000000 --- a/xps/xpscrc.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -static const unsigned long crctab[256] = -{ - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, - 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, - 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, - 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, - 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, - 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, - 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, - 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, - 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, - 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, - 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, - 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, - 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, - 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, - 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, - 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, - 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, - 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, - 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, - 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, - 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, - 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, - 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, - 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, - 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, - 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, - 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, - 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, - 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, - 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, - 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, - 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, - 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, - 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, - 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, - 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, - 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, - 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, - 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, - 0x2d02ef8dL -}; - -#define DO1(buf) crc = crctab[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); -#define DO2(buf) DO1(buf); DO1(buf); -#define DO4(buf) DO2(buf); DO2(buf); -#define DO8(buf) DO4(buf); DO4(buf); - -unsigned int -xps_crc32(unsigned int crc, unsigned char *buf, int len) -{ - if (buf == NULL) - return 0L; - crc = crc ^ 0xffffffffL; - while (len >= 8) - { - DO8(buf); - len -= 8; - } - if (len) - { - do { DO1(buf); } while (--len); - } - return crc ^ 0xffffffffL; -} diff --git a/xps/xpsdoc.c b/xps/xpsdoc.c index 312cac8d..a37ed0e9 100644 --- a/xps/xpsdoc.c +++ b/xps/xpsdoc.c @@ -8,10 +8,10 @@ xps_new_part(xps_context *ctx, char *name, int size) { xps_part *part; - part = xps_alloc(ctx, sizeof(xps_part)); - part->name = xps_strdup(ctx, name); + part = fz_malloc(sizeof(xps_part)); + part->name = fz_strdup(name); part->size = size; - part->data = xps_alloc(ctx, size); + part->data = fz_malloc(size); return part; } @@ -19,9 +19,9 @@ xps_new_part(xps_context *ctx, char *name, int size) void xps_free_part(xps_context *ctx, xps_part *part) { - xps_free(ctx, part->name); - xps_free(ctx, part->data); - xps_free(ctx, part); + fz_free(part->name); + fz_free(part->data); + fz_free(part); } /* @@ -61,8 +61,8 @@ xps_add_fixed_document(xps_context *ctx, char *name) if (!strcmp(fixdoc->name, name)) return; - fixdoc = xps_alloc(ctx, sizeof(xps_document)); - fixdoc->name = xps_strdup(ctx, name); + fixdoc = fz_malloc(sizeof(xps_document)); + fixdoc->name = fz_strdup(name); fixdoc->next = NULL; if (!ctx->first_fixdoc) @@ -84,8 +84,8 @@ xps_free_fixed_documents(xps_context *ctx) while (node) { xps_document *next = node->next; - xps_free(ctx, node->name); - xps_free(ctx, node); + fz_free(node->name); + fz_free(node); node = next; } ctx->first_fixdoc = NULL; @@ -102,8 +102,8 @@ xps_add_fixed_page(xps_context *ctx, char *name, int width, int height) if (!strcmp(page->name, name)) return; - page = xps_alloc(ctx, sizeof(xps_page)); - page->name = xps_strdup(ctx, name); + page = fz_malloc(sizeof(xps_page)); + page->name = fz_strdup(name); page->width = width; page->height = height; page->root = NULL; @@ -128,8 +128,8 @@ xps_free_fixed_pages(xps_context *ctx) while (node) { xps_page *next = node->next; - xps_free(ctx, node->name); - xps_free(ctx, node); + fz_free(node->name); + fz_free(node); node = next; } ctx->first_page = NULL; @@ -166,7 +166,7 @@ xps_parse_metadata_imp(void *zp, char *name, char **atts) { xps_absolute_path(tgtbuf, ctx->base_uri, target, sizeof tgtbuf); if (!strcmp(type, REL_START_PART)) - ctx->start_part = xps_strdup(ctx, tgtbuf); + ctx->start_part = fz_strdup(tgtbuf); } } @@ -222,7 +222,7 @@ xps_parse_metadata(xps_context *ctx, xps_part *part) char *s; /* Save directory name part */ - xps_strlcpy(buf, part->name, sizeof buf); + fz_strlcpy(buf, part->name, sizeof buf); s = strrchr(buf, '/'); if (s) s[0] = 0; diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index c53e3a25..834a55f5 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -425,7 +425,7 @@ xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, xps_hash_insert(ctx, ctx->font_table, part->name, font); /* NOTE: we kept part->name in the hashtable and part->data in the font */ - xps_free(ctx, part); + fz_free(part); } /* diff --git a/xps/xpshash.c b/xps/xpshash.c index 24d05074..0fdf59db 100644 --- a/xps/xpshash.c +++ b/xps/xpshash.c @@ -52,7 +52,7 @@ xps_hash_new(xps_context *ctx) { xps_hash_table *table; - table = xps_alloc(ctx, sizeof(xps_hash_table)); + table = fz_malloc(sizeof(xps_hash_table)); if (!table) { fz_throw("out of memory: hash table struct"); @@ -62,10 +62,10 @@ xps_hash_new(xps_context *ctx) table->size = primes[0]; table->load = 0; - table->entries = xps_alloc(ctx, sizeof(xps_hash_entry) * table->size); + table->entries = fz_malloc(sizeof(xps_hash_entry) * table->size); if (!table->entries) { - xps_free(ctx, table); + fz_free(table); fz_throw("out of memory: hash table entries array"); return NULL; } @@ -94,7 +94,7 @@ xps_hash_double(xps_context *ctx, xps_hash_table *table) } old_entries = table->entries; - new_entries = xps_alloc(ctx, sizeof(xps_hash_entry) * new_size); + new_entries = fz_malloc(sizeof(xps_hash_entry) * new_size); if (!new_entries) return fz_throw("out of memory: hash table entries array"); @@ -108,7 +108,7 @@ xps_hash_double(xps_context *ctx, xps_hash_table *table) if (old_entries[i].value) xps_hash_insert(ctx, table, old_entries[i].key, old_entries[i].value); - xps_free(ctx, old_entries); + fz_free(old_entries); return 0; } @@ -128,8 +128,8 @@ xps_hash_free(xps_context *ctx, xps_hash_table *table, free_value(ctx, table->entries[i].value); } - xps_free(ctx, table->entries); - xps_free(ctx, table); + fz_free(table->entries); + fz_free(table); } void * diff --git a/xps/xpsimage.c b/xps/xpsimage.c index 6866518f..8add170e 100644 --- a/xps/xpsimage.c +++ b/xps/xpsimage.c @@ -146,7 +146,7 @@ xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xps_item *roo image_name = NULL; profile_name = NULL; - xps_strlcpy(buf, image_source_att, sizeof buf); + fz_strlcpy(buf, image_source_att, sizeof buf); p = strchr(buf, ' '); if (p) { @@ -178,7 +178,7 @@ xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xps_item *roo *partp = part; if (profile_name) - *profilep = xps_strdup(ctx, profile_name); + *profilep = fz_strdup(profile_name); return 0; } @@ -200,7 +200,7 @@ xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resou return; } - image = xps_alloc(ctx, sizeof(xps_image)); + image = fz_malloc(sizeof(xps_image)); code = xps_decode_image(ctx, part, image); if (code < 0) { @@ -223,7 +223,7 @@ xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resou xps_parse_tiling_brush(ctx, ctm, base_uri, dict, root, xps_paint_image_brush, image); if (profilename) - xps_free(ctx, profilename); + fz_free(profilename); xps_free_image(ctx, image); xps_free_part(ctx, part); } @@ -233,8 +233,8 @@ xps_free_image(xps_context *ctx, xps_image *image) { // TODO: refcount image->colorspace if (image->samples) - xps_free(ctx, image->samples); + fz_free(image->samples); if (image->profile) - xps_free(ctx, image->profile); - xps_free(ctx, image); + fz_free(image->profile); + fz_free(image); } diff --git a/xps/xpsjxr.c b/xps/xpsjxr.c index db0792ae..2ca9a22b 100644 --- a/xps/xpsjxr.c +++ b/xps/xpsjxr.c @@ -84,7 +84,7 @@ xps_decode_jpegxr_block(jxr_image image, int mx, int my, int *data) output->hasalpha = jxr_get_ALPHACHANNEL_FLAG(image); output->bits = 8; output->stride = output->width * output->comps; - output->samples = xps_alloc(ctx, output->stride * output->height); + output->samples = fz_malloc(output->stride * output->height); switch (output->comps) { @@ -128,7 +128,7 @@ xps_decode_jpegxr_alpha_block(jxr_image image, int mx, int my, int *data) if (!output->alpha) { - output->alpha = xps_alloc(ctx, output->width * output->height); + output->alpha = fz_malloc(output->width * output->height); } depth = jxr_get_OUTPUT_BITDEPTH(image); diff --git a/xps/xpsmem.c b/xps/xpsmem.c index baae4e80..a85b2117 100644 --- a/xps/xpsmem.c +++ b/xps/xpsmem.c @@ -21,60 +21,6 @@ xps_strcasecmp(char *a, char *b) return xps_tolower(*a) - xps_tolower(*b); } -int -xps_strlcpy(char *dst, const char *src, int size) -{ - register char *d = dst; - register const char *s = src; - register int n = size; - - /* Copy as many bytes as will fit */ - if (n != 0 && --n != 0) { - do { - if ((*d++ = *s++) == 0) - break; - } while (--n != 0); - } - - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (size != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } - - return(s - src - 1); /* count does not include NUL */ -} - -int -xps_strlcat(char *dst, const char *src, int size) -{ - register char *d = dst; - register const char *s = src; - register int n = size; - int dlen; - - /* Find the end of dst and adjust bytes left but don't go past end */ - while (*d != '\0' && n-- != 0) - d++; - dlen = d - dst; - n = size - dlen; - - if (n == 0) - return dlen + strlen(s); - while (*s != '\0') { - if (n != 1) { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; - - return dlen + (s - src); /* count does not include NUL */ -} - #define SEP(x) ((x)=='/' || (x) == 0) static char * @@ -137,13 +83,13 @@ xps_absolute_path(char *output, char *base_uri, char *path, int output_size) { if (path[0] == '/') { - xps_strlcpy(output, path, output_size); + fz_strlcpy(output, path, output_size); } else { - xps_strlcpy(output, base_uri, output_size); - xps_strlcat(output, "/", output_size); - xps_strlcat(output, path, output_size); + fz_strlcpy(output, base_uri, output_size); + fz_strlcat(output, "/", output_size); + fz_strlcat(output, path, output_size); } xps_clean_path(output); } diff --git a/xps/xpspage.c b/xps/xpspage.c index aa5a96b4..32439f19 100644 --- a/xps/xpspage.c +++ b/xps/xpspage.c @@ -94,7 +94,7 @@ xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page) char *s; int code; - xps_strlcpy(base_uri, page->name, sizeof base_uri); + fz_strlcpy(base_uri, page->name, sizeof base_uri); s = strrchr(base_uri, '/'); if (s) s[1] = 0; diff --git a/xps/xpspath.c b/xps/xpspath.c index c0f45527..86d37197 100644 --- a/xps/xpspath.c +++ b/xps/xpspath.c @@ -485,7 +485,7 @@ xps_parse_abbreviated_geometry(xps_context *ctx, char *geom) old = cmd; } - xps_free(ctx, args); + fz_free(args); } static void diff --git a/xps/xpspng.c b/xps/xpspng.c index bfe6f85a..526a5098 100644 --- a/xps/xpspng.c +++ b/xps/xpspng.c @@ -190,7 +190,7 @@ xps_decode_png(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) if (info->iccp_profile != NULL) { image->profilesize = info->iccp_proflen; - image->profile = xps_alloc(ctx, info->iccp_proflen); + image->profile = fz_malloc(info->iccp_proflen); if (image->profile) { /* If we can't create it, just ignore */ @@ -249,7 +249,7 @@ xps_decode_png(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) image->stride = (image->width * image->comps * image->bits + 7) / 8; - image->samples = xps_alloc(ctx, image->stride * image->height); + image->samples = fz_malloc(image->stride * image->height); for (pass = 0; pass < npasses; pass++) { diff --git a/xps/xpsresource.c b/xps/xpsresource.c index 631b6cb7..bda624e6 100644 --- a/xps/xpsresource.c +++ b/xps/xpsresource.c @@ -29,7 +29,7 @@ xps_parse_resource_reference(xps_context *ctx, xps_resource *dict, char *att, ch if (strstr(att, "{StaticResource ") != att) return NULL; - xps_strlcpy(name, att + 16, sizeof name); + fz_strlcpy(name, att + 16, sizeof name); s = strrchr(name, '}'); if (s) *s = 0; @@ -85,7 +85,7 @@ xps_parse_remote_resource_dictionary(xps_context *ctx, xps_resource **dictp, cha return fz_throw("expected ResourceDictionary element (found %s)", xps_tag(xml)); } - xps_strlcpy(part_uri, part_name, sizeof part_uri); + fz_strlcpy(part_uri, part_name, sizeof part_uri); s = strrchr(part_uri, '/'); if (s) s[1] = 0; @@ -133,7 +133,7 @@ xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base key = xps_att(node, "Key"); if (key) { - entry = xps_alloc(ctx, sizeof(xps_resource)); + entry = fz_malloc(sizeof(xps_resource)); if (!entry) return fz_throw("cannot allocate resource entry"); entry->name = key; @@ -148,7 +148,7 @@ xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base if (head) { - head->base_uri = xps_strdup(ctx, base_uri); + head->base_uri = fz_strdup(base_uri); } *dictp = head; @@ -165,8 +165,8 @@ xps_free_resource_dictionary(xps_context *ctx, xps_resource *dict) if (dict->base_xml) xps_free_item(ctx, dict->base_xml); if (dict->base_uri) - xps_free(ctx, dict->base_uri); - xps_free(ctx, dict); + fz_free(dict->base_uri); + fz_free(dict); dict = next; } } diff --git a/xps/xpstiff.c b/xps/xpstiff.c index 6d6050e6..7ab684fc 100644 --- a/xps/xpstiff.c +++ b/xps/xpstiff.c @@ -538,7 +538,7 @@ xps_expand_colormap(xps_context *ctx, xps_tiff *tiff, xps_image *image) stride = image->width * (image->comps + 2); - samples = xps_alloc(ctx, stride * image->height); + samples = fz_malloc(stride * image->height); if (!samples) return fz_throw("out of memory: samples"); @@ -655,7 +655,7 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) image->yres = 96; } - image->samples = xps_alloc(ctx, image->stride * image->height); + image->samples = fz_malloc(image->stride * image->height); if (!image->samples) return fz_throw("could not allocate image samples"); @@ -887,7 +887,7 @@ xps_read_tiff_tag(xps_context *ctx, xps_tiff *tiff, unsigned offset) xps_read_tiff_tag_value(&tiff->extrasamples, tiff, type, value, 1); break; case ICCProfile: - tiff->profile = xps_alloc(ctx, count); + tiff->profile = fz_malloc(count); if (!tiff->profile) return fz_throw("could not allocate embedded icc profile"); /* ICC profile data type is set to UNDEFINED. @@ -902,21 +902,21 @@ xps_read_tiff_tag(xps_context *ctx, xps_tiff *tiff, unsigned offset) break; case StripOffsets: - tiff->stripoffsets = (unsigned*) xps_alloc(ctx, count * sizeof(unsigned)); + tiff->stripoffsets = (unsigned*) fz_malloc(count * sizeof(unsigned)); if (!tiff->stripoffsets) return fz_throw("could not allocate strip offsets"); xps_read_tiff_tag_value(tiff->stripoffsets, tiff, type, value, count); break; case StripByteCounts: - tiff->stripbytecounts = (unsigned*) xps_alloc(ctx, count * sizeof(unsigned)); + tiff->stripbytecounts = (unsigned*) fz_malloc(count * sizeof(unsigned)); if (!tiff->stripbytecounts) return fz_throw("could not allocate strip byte counts"); xps_read_tiff_tag_value(tiff->stripbytecounts, tiff, type, value, count); break; case ColorMap: - tiff->colormap = (unsigned*) xps_alloc(ctx, count * sizeof(unsigned)); + tiff->colormap = (unsigned*) fz_malloc(count * sizeof(unsigned)); if (!tiff->colormap) return fz_throw("could not allocate color map"); xps_read_tiff_tag_value(tiff->colormap, tiff, type, value, count); @@ -1055,9 +1055,9 @@ xps_decode_tiff(xps_context *ctx, byte *buf, int len, xps_image *image) * Clean up scratch memory */ - if (tiff->colormap) xps_free(ctx, tiff->colormap); - if (tiff->stripoffsets) xps_free(ctx, tiff->stripoffsets); - if (tiff->stripbytecounts) xps_free(ctx, tiff->stripbytecounts); + if (tiff->colormap) fz_free(tiff->colormap); + if (tiff->stripoffsets) fz_free(tiff->stripoffsets); + if (tiff->stripbytecounts) fz_free(tiff->stripbytecounts); return gs_okay; } @@ -1076,10 +1076,10 @@ xps_tiff_has_alpha(xps_context *ctx, byte *buf, int len) return 0; } - if (tiff->profile) xps_free(ctx, tiff->profile); - if (tiff->colormap) xps_free(ctx, tiff->colormap); - if (tiff->stripoffsets) xps_free(ctx, tiff->stripoffsets); - if (tiff->stripbytecounts) xps_free(ctx, tiff->stripbytecounts); + if (tiff->profile) fz_free(tiff->profile); + if (tiff->colormap) fz_free(tiff->colormap); + if (tiff->stripoffsets) fz_free(tiff->stripoffsets); + if (tiff->stripbytecounts) fz_free(tiff->stripbytecounts); return tiff->extrasamples == 2 || tiff->extrasamples == 1; } diff --git a/xps/xpsxml.c b/xps/xpsxml.c index 70dbe49d..63d9af36 100644 --- a/xps/xpsxml.c +++ b/xps/xpsxml.c @@ -285,7 +285,7 @@ xps_free_item(xps_context *ctx, xps_item *item) next = item->next; if (item->down) xps_free_item(ctx, item->down); - xps_free(ctx, item); + fz_free(item); item = next; } } diff --git a/xps/xpszip.c b/xps/xpszip.c index d3c03103..a02ce302 100644 --- a/xps/xpszip.c +++ b/xps/xpszip.c @@ -22,13 +22,13 @@ static inline int getlong(FILE *file) static void * xps_zip_alloc_items(xps_context *ctx, int items, int size) { - return xps_alloc(ctx, items * size); + return fz_malloc(items * size); } static void xps_zip_free(xps_context *ctx, void *ptr) { - xps_free(ctx, ptr); + fz_free(ptr); } static int @@ -97,7 +97,7 @@ xps_read_zip_entry(xps_context *ctx, xps_entry *ent, unsigned char *outbuf) } else if (method == 8) { - inbuf = xps_alloc(ctx, ent->csize); + inbuf = fz_malloc(ent->csize); fread(inbuf, 1, ent->csize, ctx->file); @@ -123,7 +123,7 @@ xps_read_zip_entry(xps_context *ctx, xps_entry *ent, unsigned char *outbuf) if (code != Z_OK) return fz_throw("zlib inflateEnd error: %s", stream.msg); - xps_free(ctx, inbuf); + fz_free(inbuf); } else { @@ -159,7 +159,7 @@ xps_read_zip_dir(xps_context *ctx, int start_offset) offset = getlong(ctx->file); /* offset to central directory */ ctx->zip_count = count; - ctx->zip_table = xps_alloc(ctx, sizeof(xps_entry) * count); + ctx->zip_table = fz_malloc(sizeof(xps_entry) * count); if (!ctx->zip_table) return fz_throw("cannot allocate zip entry table"); @@ -190,7 +190,7 @@ xps_read_zip_dir(xps_context *ctx, int start_offset) (void) getlong(ctx->file); /* ext file atts */ ctx->zip_table[i].offset = getlong(ctx->file); - ctx->zip_table[i].name = xps_alloc(ctx, namesize + 1); + ctx->zip_table[i].name = fz_malloc(namesize + 1); if (!ctx->zip_table[i].name) return fz_throw("cannot allocate zip entry name"); @@ -314,8 +314,8 @@ xps_read_dir_part(xps_context *ctx, char *name) FILE *file; int count, size, offset, i, n; - xps_strlcpy(buf, ctx->directory, sizeof buf); - xps_strlcat(buf, name, sizeof buf); + fz_strlcpy(buf, ctx->directory, sizeof buf); + fz_strlcat(buf, name, sizeof buf); /* All in one piece */ file = fopen(buf, "rb"); @@ -421,12 +421,12 @@ xps_open_file(xps_context *ctx, char *filename) if (strstr(filename, "/_rels/.rels") || strstr(filename, "\\_rels\\.rels")) { - xps_strlcpy(buf, filename, sizeof buf); + fz_strlcpy(buf, filename, sizeof buf); p = strstr(buf, "/_rels/.rels"); if (!p) p = strstr(buf, "\\_rels\\.rels"); *p = 0; - ctx->directory = xps_strdup(ctx, buf); + ctx->directory = fz_strdup(buf); } else { @@ -516,7 +516,7 @@ xps_new_context(void) static void xps_free_key_func(xps_context *ctx, void *ptr) { - xps_free(ctx, ptr); + fz_free(ptr); } static void xps_free_font_func(xps_context *ctx, void *ptr) @@ -534,8 +534,8 @@ xps_free_context(xps_context *ctx) fclose(ctx->file); for (i = 0; i < ctx->zip_count; i++) - xps_free(ctx, ctx->zip_table[i].name); - xps_free(ctx, ctx->zip_table); + fz_free(ctx->zip_table[i].name); + fz_free(ctx->zip_table); /* TODO: free resources too */ xps_hash_free(ctx, ctx->font_table, xps_free_key_func, xps_free_font_func); -- cgit v1.2.3 From a903a2ed5572b86deb323cb5471520aba05a0189 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 30 Mar 2011 16:00:59 +0200 Subject: xps: Use fz_calloc for array allocations. --- Makefile | 1 - xps/xpshash.c | 23 ++++------------------- xps/xpsjxr.c | 2 +- xps/xpspng.c | 2 +- xps/xpsresource.c | 2 -- xps/xpszip.c | 10 ++-------- 6 files changed, 8 insertions(+), 32 deletions(-) diff --git a/Makefile b/Makefile index 1e6135bb..4dfb0e96 100644 --- a/Makefile +++ b/Makefile @@ -176,7 +176,6 @@ MUXPS_SRC := \ xps/xpsanalyze.c \ xps/xpscolor.c \ xps/xpscommon.c \ - xps/xpscrc.c \ xps/xpsdoc.c \ xps/xpsfont.c \ xps/xpsglyphs.c \ diff --git a/xps/xpshash.c b/xps/xpshash.c index 0fdf59db..71e97097 100644 --- a/xps/xpshash.c +++ b/xps/xpshash.c @@ -53,24 +53,11 @@ xps_hash_new(xps_context *ctx) xps_hash_table *table; table = fz_malloc(sizeof(xps_hash_table)); - if (!table) - { - fz_throw("out of memory: hash table struct"); - return NULL; - } - table->size = primes[0]; table->load = 0; - table->entries = fz_malloc(sizeof(xps_hash_entry) * table->size); - if (!table->entries) - { - fz_free(table); - fz_throw("out of memory: hash table entries array"); - return NULL; - } - - memset(table->entries, 0, sizeof(xps_hash_entry) * table->size); + table->entries = fz_calloc(table->size, sizeof(xps_hash_entry)); + memset(table->entries, 0, table->size * sizeof(xps_hash_entry)); return table; } @@ -94,15 +81,13 @@ xps_hash_double(xps_context *ctx, xps_hash_table *table) } old_entries = table->entries; - new_entries = fz_malloc(sizeof(xps_hash_entry) * new_size); - if (!new_entries) - return fz_throw("out of memory: hash table entries array"); + new_entries = fz_calloc(new_size, sizeof(xps_hash_entry)); table->size = new_size; table->entries = new_entries; table->load = 0; - memset(table->entries, 0, sizeof(xps_hash_entry) * table->size); + memset(table->entries, 0, table->size * sizeof(xps_hash_entry)); for (i = 0; i < old_size; i++) if (old_entries[i].value) diff --git a/xps/xpsjxr.c b/xps/xpsjxr.c index 2ca9a22b..bbe5f66e 100644 --- a/xps/xpsjxr.c +++ b/xps/xpsjxr.c @@ -239,7 +239,7 @@ xps_decode_jpegxr(xps_context *ctx, byte *buf, int len, xps_image *output) fclose(file); unlink(name); - return gs_okay; + return fz_okay; } int diff --git a/xps/xpspng.c b/xps/xpspng.c index 526a5098..641101fe 100644 --- a/xps/xpspng.c +++ b/xps/xpspng.c @@ -249,7 +249,7 @@ xps_decode_png(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) image->stride = (image->width * image->comps * image->bits + 7) / 8; - image->samples = fz_malloc(image->stride * image->height); + image->samples = fz_calloc(image->height, image->stride); for (pass = 0; pass < npasses; pass++) { diff --git a/xps/xpsresource.c b/xps/xpsresource.c index bda624e6..28c172bf 100644 --- a/xps/xpsresource.c +++ b/xps/xpsresource.c @@ -134,8 +134,6 @@ xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base if (key) { entry = fz_malloc(sizeof(xps_resource)); - if (!entry) - return fz_throw("cannot allocate resource entry"); entry->name = key; entry->base_uri = NULL; entry->base_xml = NULL; diff --git a/xps/xpszip.c b/xps/xpszip.c index a02ce302..5528f0f7 100644 --- a/xps/xpszip.c +++ b/xps/xpszip.c @@ -22,7 +22,7 @@ static inline int getlong(FILE *file) static void * xps_zip_alloc_items(xps_context *ctx, int items, int size) { - return fz_malloc(items * size); + return fz_calloc(items, size); } static void @@ -159,10 +159,7 @@ xps_read_zip_dir(xps_context *ctx, int start_offset) offset = getlong(ctx->file); /* offset to central directory */ ctx->zip_count = count; - ctx->zip_table = fz_malloc(sizeof(xps_entry) * count); - if (!ctx->zip_table) - return fz_throw("cannot allocate zip entry table"); - + ctx->zip_table = fz_calloc(count, sizeof(xps_entry)); memset(ctx->zip_table, 0, sizeof(xps_entry) * count); fseek(ctx->file, offset, 0); @@ -191,9 +188,6 @@ xps_read_zip_dir(xps_context *ctx, int start_offset) ctx->zip_table[i].offset = getlong(ctx->file); ctx->zip_table[i].name = fz_malloc(namesize + 1); - if (!ctx->zip_table[i].name) - return fz_throw("cannot allocate zip entry name"); - fread(ctx->zip_table[i].name, 1, namesize, ctx->file); ctx->zip_table[i].name[namesize] = 0; -- cgit v1.2.3 From 60a940e170eb1b3e00e28b76f1b9a86741ef9a25 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 30 Mar 2011 19:58:40 +0200 Subject: xps: Decode and draw images. --- xps/muxps.h | 1 + xps/xpsimage.c | 96 ++--------- xps/xpsjpeg.c | 118 ++++++++++++- xps/xpstiff.c | 515 +++++++++++++++++---------------------------------------- xps/xpstile.c | 4 +- 5 files changed, 289 insertions(+), 445 deletions(-) diff --git a/xps/muxps.h b/xps/muxps.h index c778be37..ba826b10 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -115,6 +115,7 @@ struct xps_image_s byte *samples; byte *profile; int profilesize; + fz_pixmap *pixmap; }; int xps_decode_jpeg(xps_context *ctx, byte *rbuf, int rlen, xps_image *image); diff --git a/xps/xpsimage.c b/xps/xpsimage.c index 8add170e..a4305734 100644 --- a/xps/xpsimage.c +++ b/xps/xpsimage.c @@ -17,111 +17,47 @@ xps_decode_image(xps_context *ctx, xps_part *part, xps_image *image) { error = xps_decode_jpeg(ctx, buf, len, image); if (error) - return fz_rethrow(error, "could not decode image"); + return fz_rethrow(error, "could not decode jpeg image"); } else if (memcmp(buf, "\211PNG\r\n\032\n", 8) == 0) { error = xps_decode_png(ctx, buf, len, image); if (error) - return fz_rethrow(error, "could not decode image"); + return fz_rethrow(error, "could not decode png image"); } else if (memcmp(buf, "II", 2) == 0 && buf[2] == 0xBC) { error = xps_decode_jpegxr(ctx, buf, len, image); if (error) - return fz_rethrow(error, "could not decode image"); + return fz_rethrow(error, "could not decode JPEG-XR image"); } else if (memcmp(buf, "MM", 2) == 0 || memcmp(buf, "II", 2) == 0) { error = xps_decode_tiff(ctx, buf, len, image); if (error) - return fz_rethrow(error, "could not decode image"); + return fz_rethrow(error, "could not decode TIFF image"); } else return fz_throw("unknown image file format"); - return fz_okay; -} - -static void -xps_paint_image_brush_imp(xps_context *ctx, fz_matrix ctm, xps_image *image) -{ - fz_colorspace *colorspace; - unsigned int count; - byte *samples; + image->pixmap = fz_newpixmap(image->colorspace, 0, 0, image->width, image->height); + fz_unpacktile(image->pixmap, image->samples, image->comps, image->bits, image->stride, 1); + fz_free(image->samples); + image->samples = NULL; - colorspace = image->colorspace; - samples = image->samples; - count = image->stride * image->height; - -printf("xps_paint_image_brush_imp!\n"); + return fz_okay; } static void xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root, void *vimage) { -#if 0 xps_image *image = vimage; - int code; - - if (ctx->opacity_only) - { - if (image->alpha) - { - code = xps_paint_image_brush_imp(ctx, image, 1); - if (code < 0) - return fz_rethrow(code, "cannot draw alpha channel image"); - } - return 0; - } - - if (image->alpha) - { - gs_transparency_mask_params params; - gs_transparency_group_params tgp; - fz_rect bbox; - - xps_bounds_in_user_space(ctx, &bbox); - - code = gs_gsave(ctx->pgs); - if (code < 0) - return fz_rethrow(code, "cannot gsave before transparency group"); - - gs_setcolorspace(ctx->pgs, ctx->gray); - gs_trans_mask_params_init(¶ms, TRANSPARENCY_MASK_Luminosity); - gs_begin_transparency_mask(ctx->pgs, ¶ms, &bbox, 0); - code = xps_paint_image_brush_imp(ctx, image, 1); - if (code < 0) - { - gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); - gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot draw alpha channel image"); - } - gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); - - gs_setcolorspace(ctx->pgs, image->colorspace); - gs_trans_group_params_init(&tgp); - gs_begin_transparency_group(ctx->pgs, &tgp, &bbox); - code = xps_paint_image_brush_imp(ctx, image, 0); - if (code < 0) - { - gs_end_transparency_group(ctx->pgs); - gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot draw color channel image"); - } - gs_end_transparency_group(ctx->pgs); - - code = gs_grestore(ctx->pgs); - if (code < 0) - return fz_rethrow(code, "cannot grestore after transparency group"); - } - else - { - code = xps_paint_image_brush_imp(ctx, image, 0); - if (code < 0) - return fz_rethrow(code, "cannot draw image"); - } -#endif + float xs = image->width * 96.0 / image->xres; + float ys = image->height * 96.0 / image->yres; + fz_matrix im = fz_scale(xs, -ys); + im.f = ys; + ctm = fz_concat(im, ctm); + ctx->dev->fillimage(ctx->dev->user, image->pixmap, ctm, 1.0); } static int @@ -232,6 +168,8 @@ void xps_free_image(xps_context *ctx, xps_image *image) { // TODO: refcount image->colorspace + if (image->pixmap) + fz_droppixmap(image->pixmap); if (image->samples) fz_free(image->samples); if (image->profile) diff --git a/xps/xpsjpeg.c b/xps/xpsjpeg.c index f89c2207..41534eeb 100644 --- a/xps/xpsjpeg.c +++ b/xps/xpsjpeg.c @@ -4,8 +4,124 @@ #include #include +struct jpeg_error_mgr_jmp +{ + struct jpeg_error_mgr super; + jmp_buf env; + char msg[JMSG_LENGTH_MAX]; +}; + +static void error_exit(j_common_ptr cinfo) +{ + struct jpeg_error_mgr_jmp *err = (struct jpeg_error_mgr_jmp *)cinfo->err; + cinfo->err->format_message(cinfo, err->msg); + longjmp(err->env, 1); +} + +static void init_source(j_decompress_ptr cinfo) +{ + /* nothing to do */ +} + +static void term_source(j_decompress_ptr cinfo) +{ + /* nothing to do */ +} + +static boolean fill_input_buffer(j_decompress_ptr cinfo) +{ + static unsigned char eoi[2] = { 0xFF, JPEG_EOI }; + struct jpeg_source_mgr *src = cinfo->src; + src->next_input_byte = eoi; + src->bytes_in_buffer = 2; + return 1; +} + +static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + struct jpeg_source_mgr *src = cinfo->src; + if (num_bytes > 0) + { + src->next_input_byte += num_bytes; + src->bytes_in_buffer -= num_bytes; + } +} + int xps_decode_jpeg(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) { - return fz_throw("jpeg not available"); + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr_jmp err; + struct jpeg_source_mgr src; + unsigned char *buffer[1]; + + if (setjmp(err.env)) + { + if (image->samples) + fz_free(image->samples); + image->samples = NULL; + return fz_throw("jpeg error: %s", err.msg); + } + + cinfo.err = jpeg_std_error(&err.super); + err.super.error_exit = error_exit; + + jpeg_create_decompress(&cinfo); + + cinfo.src = &src; + src.init_source = init_source; + src.fill_input_buffer = fill_input_buffer; + src.skip_input_data = skip_input_data; + src.resync_to_restart = jpeg_resync_to_restart; + src.term_source = term_source; + src.next_input_byte = rbuf; + src.bytes_in_buffer = rlen; + + jpeg_read_header(&cinfo, 1); + + jpeg_start_decompress(&cinfo); + + image->width = cinfo.output_width; + image->height = cinfo.output_height; + image->comps = cinfo.output_components; + image->stride = cinfo.output_width * cinfo.output_components; + image->bits = 8; + image->samples = fz_calloc(image->height, image->stride); + + if (image->comps == 1) + image->colorspace = fz_devicegray; + if (image->comps == 3) + image->colorspace = fz_devicergb; + if (image->comps == 4) + image->colorspace = fz_devicecmyk; + + if (cinfo.density_unit == 1) + { + image->xres = cinfo.X_density; + image->yres = cinfo.Y_density; + } + else if (cinfo.density_unit == 2) + { + image->xres = cinfo.X_density * 2.54; + image->yres = cinfo.Y_density * 2.54; + } + else + { + image->xres = 96; + image->yres = 96; + } + + memset(image->samples, 127, image->stride * image->height); + + buffer[0] = image->samples; + while (cinfo.output_scanline < cinfo.output_height) + { + jpeg_read_scanlines(&cinfo, buffer, 1); + buffer[0] += image->comps * image->width; + } + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + + return fz_okay; } diff --git a/xps/xpstiff.c b/xps/xpstiff.c index 7ab684fc..562f1747 100644 --- a/xps/xpstiff.c +++ b/xps/xpstiff.c @@ -1,26 +1,6 @@ #include "fitz.h" #include "muxps.h" -int -xps_decode_tiff(xps_context *ctx, byte *buf, int len, xps_image *image) -{ - return fz_throw("TIFF codec is not available"); -} - -#if 0 - -#include "stream.h" -#include "strimpl.h" -#include "gsstate.h" -#include "jpeglib_.h" -#include "sdct.h" -#include "sjpeg.h" -#include "srlx.h" -#include "slzwx.h" -#include "szlibx.h" -#include "scfx.h" -#include "memory_.h" - /* * TIFF image loader. Should be enough to support TIFF files in XPS. * Baseline TIFF 6.0 plus CMYK, LZW, Flate and JPEG support. @@ -86,33 +66,33 @@ enum TRATIONAL = 5 }; -#define NewSubfileType 254 -#define ImageWidth 256 -#define ImageLength 257 -#define BitsPerSample 258 -#define Compression 259 -#define PhotometricInterpretation 262 -#define FillOrder 266 -#define StripOffsets 273 -#define SamplesPerPixel 277 -#define RowsPerStrip 278 -#define StripByteCounts 279 -#define XResolution 282 -#define YResolution 283 -#define PlanarConfiguration 284 -#define T4Options 292 -#define T6Options 293 -#define ResolutionUnit 296 -#define Predictor 317 -#define ColorMap 320 -#define TileWidth 322 -#define TileLength 323 -#define TileOffsets 324 -#define TileByteCounts 325 -#define ExtraSamples 338 -#define JPEGTables 347 -#define YCbCrSubSampling 520 -#define ICCProfile 34675 +#define NewSubfileType 254 +#define ImageWidth 256 +#define ImageLength 257 +#define BitsPerSample 258 +#define Compression 259 +#define PhotometricInterpretation 262 +#define FillOrder 266 +#define StripOffsets 273 +#define SamplesPerPixel 277 +#define RowsPerStrip 278 +#define StripByteCounts 279 +#define XResolution 282 +#define YResolution 283 +#define PlanarConfiguration 284 +#define T4Options 292 +#define T6Options 293 +#define ResolutionUnit 296 +#define Predictor 317 +#define ColorMap 320 +#define TileWidth 322 +#define TileLength 323 +#define TileOffsets 324 +#define TileByteCounts 325 +#define ExtraSamples 338 +#define JPEGTables 347 +#define YCbCrSubSampling 520 +#define ICCProfile 34675 static const byte bitrev[256] = { @@ -151,293 +131,92 @@ static const byte bitrev[256] = }; static int -xps_report_error(stream_state * st, const char *str) -{ - (void) fz_throw("%s", str); - return 0; -} - -static inline int -readbyte(xps_tiff *tiff) -{ - if (tiff->rp < tiff->ep) - return *tiff->rp++; - return EOF; -} - -static inline unsigned -readshort(xps_tiff *tiff) -{ - unsigned a = readbyte(tiff); - unsigned b = readbyte(tiff); - if (tiff->order == TII) - return (b << 8) | a; - return (a << 8) | b; -} - -static inline unsigned -readlong(xps_tiff *tiff) +xps_decode_tiff_uncompressed(xps_context *ctx, xps_tiff *tiff, fz_stream *stm, byte *wp, int wlen) { - unsigned a = readbyte(tiff); - unsigned b = readbyte(tiff); - unsigned c = readbyte(tiff); - unsigned d = readbyte(tiff); - if (tiff->order == TII) - return (d << 24) | (c << 16) | (b << 8) | a; - return (a << 24) | (b << 16) | (c << 8) | d; + int n = fz_read(stm, wp, wlen); + if (n < 0) + return fz_rethrow(n, "cannot read uncompressed strip"); + return fz_okay; } static int -xps_decode_tiff_uncompressed(xps_context *ctx, xps_tiff *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +xps_decode_tiff_packbits(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) { - memcpy(wp, rp, wl - wp); - return gs_okay; + fz_stream *stm = fz_openrld(chain); + int n = fz_read(stm, wp, wlen); + fz_close(stm); + if (n < 0) + return fz_rethrow(n, "cannot read packbits strip"); + return fz_okay; } static int -xps_decode_tiff_packbits(xps_context *ctx, xps_tiff *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +xps_decode_tiff_lzw(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) { - stream_RLD_state state; - stream_cursor_read scr; - stream_cursor_write scw; - int code; - - s_init_state((stream_state*)&state, &s_RLD_template, ctx->memory); - state.report_error = xps_report_error; - - s_RLD_template.set_defaults((stream_state*)&state); - s_RLD_template.init((stream_state*)&state); - - scr.ptr = rp - 1; - scr.limit = rl - 1; - scw.ptr = wp - 1; - scw.limit = wl - 1; - - code = s_RLD_template.process((stream_state*)&state, &scr, &scw, true); - if (code == ERRC) - return fz_throw("error in packbits data (code = %d)", code); - - return gs_okay; + fz_stream *stm = fz_openlzwd(chain, NULL); + int n = fz_read(stm, wp, wlen); + fz_close(stm); + if (n < 0) + return fz_rethrow(n, "cannot read lzw strip"); + return fz_okay; } - static int -xps_decode_tiff_lzw(xps_context *ctx, xps_tiff *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +xps_decode_tiff_flate(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) { - stream_LZW_state state; - stream_cursor_read scr; - stream_cursor_write scw; - int code; - - s_init_state((stream_state*)&state, &s_LZWD_template, ctx->memory); - state.report_error = xps_report_error; - - s_LZWD_template.set_defaults((stream_state*)&state); - - /* old-style TIFF 5.0 reversed bit order, late change */ - if (rp[0] == 0 && rp[1] & 0x01) - { - state.EarlyChange = 0; - state.FirstBitLowOrder = 1; - } - - /* new-style TIFF 6.0 normal bit order, early change */ - else - { - state.EarlyChange = 1; - state.FirstBitLowOrder = 0; - } - - s_LZWD_template.init((stream_state*)&state); - - scr.ptr = rp - 1; - scr.limit = rl - 1; - scw.ptr = wp - 1; - scw.limit = wl - 1; - - code = s_LZWD_template.process((stream_state*)&state, &scr, &scw, true); - if (code == ERRC) - { - s_LZWD_template.release((stream_state*)&state); - return fz_throw("error in lzw data (code = %d)", code); - } - - s_LZWD_template.release((stream_state*)&state); - - return gs_okay; + fz_stream *stm = fz_openflated(chain); + int n = fz_read(stm, wp, wlen); + fz_close(stm); + if (n < 0) + return fz_rethrow(n, "cannot read flate strip"); + return fz_okay; } static int -xps_decode_tiff_flate(xps_context *ctx, xps_tiff *tiff, byte *rp, byte *rl, byte *wp, byte *wl) +xps_decode_tiff_fax(xps_context *ctx, xps_tiff *tiff, int comp, fz_stream *chain, byte *wp, int wlen) { - stream_zlib_state state; - stream_cursor_read scr; - stream_cursor_write scw; - int code; - - s_init_state((stream_state*)&state, &s_zlibD_template, ctx->memory); - state.report_error = xps_report_error; - - s_zlibD_template.set_defaults((stream_state*)&state); - - s_zlibD_template.init((stream_state*)&state); - - scr.ptr = rp - 1; - scr.limit = rl - 1; - scw.ptr = wp - 1; - scw.limit = wl - 1; - - code = s_zlibD_template.process((stream_state*)&state, &scr, &scw, true); - if (code == ERRC) - { - s_zlibD_template.release((stream_state*)&state); - return fz_throw("error in flate data (code = %d)", code); - } - - s_zlibD_template.release((stream_state*)&state); - return gs_okay; + fz_stream *stm; + fz_obj *params; + fz_obj *columns, *rows, *blackis1, *k, *encodedbytealign; + int n; + + columns = fz_newint(tiff->imagewidth); + rows = fz_newint(tiff->imagelength); + blackis1 = fz_newbool(tiff->photometric == 0); + k = fz_newint(comp == 4 ? -1 : 0); + encodedbytealign = fz_newbool(comp == 2); + + params = fz_newdict(5); + fz_dictputs(params, "Columns", columns); + fz_dictputs(params, "Rows", rows); + fz_dictputs(params, "BlackIs1", blackis1); + fz_dictputs(params, "K", k); + fz_dictputs(params, "EncodedByteAlign", encodedbytealign); + + fz_dropobj(columns); + fz_dropobj(rows); + fz_dropobj(blackis1); + fz_dropobj(k); + fz_dropobj(encodedbytealign); + + stm = fz_openfaxd(chain, params); + n = fz_read(stm, wp, wlen); + fz_close(stm); + fz_dropobj(params); + + if (n < 0) + return fz_rethrow(n, "cannot read fax strip"); + return fz_okay; } static int -xps_decode_tiff_fax(xps_context *ctx, xps_tiff *tiff, int comp, byte *rp, byte *rl, byte *wp, byte *wl) +xps_decode_tiff_jpeg(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) { - stream_CFD_state state; - stream_cursor_read scr; - stream_cursor_write scw; - int code; - - s_init_state((stream_state*)&state, &s_CFD_template, ctx->memory); - state.report_error = xps_report_error; - - s_CFD_template.set_defaults((stream_state*)&state); - - state.EndOfLine = false; - state.EndOfBlock = false; - state.Columns = tiff->imagewidth; - state.Rows = tiff->imagelength; - state.BlackIs1 = tiff->photometric == 0; - - state.K = 0; - if (comp == 4) - state.K = -1; - if (comp == 2) - state.EncodedByteAlign = true; - - s_CFD_template.init((stream_state*)&state); - - scr.ptr = rp - 1; - scr.limit = rl - 1; - scw.ptr = wp - 1; - scw.limit = wl - 1; - - code = s_CFD_template.process((stream_state*)&state, &scr, &scw, true); - if (code == ERRC) - { - s_CFD_template.release((stream_state*)&state); - return fz_throw("error in fax data (code = %d)", code); - } - - s_CFD_template.release((stream_state*)&state); - return gs_okay; -} - -/* - * We need more find control over JPEG decoding parameters than - * the s_DCTD_template filter will give us. So we abuse the - * filter, and take control after the filter setup (which sets up - * the memory manager and error handling) and call the gs_jpeg - * wrappers directly for doing the actual decoding. - */ - -static int -xps_decode_tiff_jpeg(xps_context *ctx, xps_tiff *tiff, byte *rp, byte *rl, byte *wp, byte *wl) -{ - stream_DCT_state state; /* used by gs_jpeg_* wrappers */ - jpeg_decompress_data jddp; - struct jpeg_source_mgr *srcmgr; - JSAMPROW scanlines[1]; - int stride; - int code; - - /* - * Set up the JPEG and DCT filter voodoo. - */ - - s_init_state((stream_state*)&state, &s_DCTD_template, ctx->memory); - state.report_error = xps_report_error; - s_DCTD_template.set_defaults((stream_state*)&state); - - state.jpeg_memory = ctx->memory; - state.data.decompress = &jddp; - - jddp.template = s_DCTD_template; - jddp.memory = ctx->memory; - jddp.scanline_buffer = NULL; - - if ((code = gs_jpeg_create_decompress(&state)) < 0) - return fz_throw("error in gs_jpeg_create_decompress"); - - s_DCTD_template.init((stream_state*)&state); - - srcmgr = jddp.dinfo.src; - - /* - * Read the abbreviated table file. - */ - - if (tiff->jpegtables) - { - srcmgr->next_input_byte = tiff->jpegtables; - srcmgr->bytes_in_buffer = tiff->jpegtableslen; - - code = gs_jpeg_read_header(&state, FALSE); - if (code != JPEG_HEADER_TABLES_ONLY) - return fz_throw("error in jpeg table data"); - } - - /* - * Read the image jpeg header. - */ - - srcmgr->next_input_byte = rp; - srcmgr->bytes_in_buffer = rl - rp; - - if ((code = gs_jpeg_read_header(&state, TRUE)) < 0) - return fz_throw("error in jpeg_read_header"); - - /* when TIFF says RGB and libjpeg says YCbCr, libjpeg is wrong */ - if (tiff->photometric == 2 && jddp.dinfo.jpeg_color_space == JCS_YCbCr) - { - jddp.dinfo.jpeg_color_space = JCS_RGB; - } - - /* - * Decode the strip image data. - */ - - if ((code = gs_jpeg_start_decompress(&state)) < 0) - return fz_throw("error in jpeg_start_decompress"); - - stride = jddp.dinfo.output_width * jddp.dinfo.output_components; - - while (wp + stride <= wl && jddp.dinfo.output_scanline < jddp.dinfo.output_height) - { - scanlines[0] = wp; - code = gs_jpeg_read_scanlines(&state, scanlines, 1); - if (code < 0) - return gs_throw(01, "error in jpeg_read_scanlines"); - wp += stride; - } - - /* - * Clean up. - */ - - if ((code = gs_jpeg_finish_decompress(&state)) < 0) - return fz_throw("error in jpeg_finish_decompress"); - - gs_jpeg_destroy(&state); - - return gs_okay; + fz_stream *stm = fz_opendctd(chain, NULL); + int n = fz_read(stm, wp, wlen); + fz_close(stm); + if (n < 0) + return fz_rethrow(n, "cannot read jpeg strip"); + return fz_okay; } static inline int @@ -572,12 +351,14 @@ xps_expand_colormap(xps_context *ctx, xps_tiff *tiff, xps_image *image) image->stride = stride; image->samples = samples; - return gs_okay; + return fz_okay; } static int xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) { + fz_buffer buf; + fz_stream *stm; int error; /* switch on compression to create a filter */ @@ -609,23 +390,23 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) switch (tiff->photometric) { case 0: /* WhiteIsZero -- inverted */ - image->colorspace = ctx->gray; + image->colorspace = fz_devicegray; break; case 1: /* BlackIsZero */ - image->colorspace = ctx->gray; + image->colorspace = fz_devicegray; break; case 2: /* RGB */ - image->colorspace = ctx->srgb; + image->colorspace = fz_devicergb; break; case 3: /* RGBPal */ - image->colorspace = ctx->srgb; + image->colorspace = fz_devicergb; break; case 5: /* CMYK */ - image->colorspace = ctx->cmyk; + image->colorspace = fz_devicecmyk; break; case 6: /* YCbCr */ /* it's probably a jpeg ... we let jpeg convert to rgb */ - image->colorspace = ctx->srgb; + image->colorspace = fz_devicergb; break; default: return fz_throw("unknown photometric: %d", tiff->photometric); @@ -656,8 +437,6 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) } image->samples = fz_malloc(image->stride * image->height); - if (!image->samples) - return fz_throw("could not allocate image samples"); memset(image->samples, 0x55, image->stride * image->height); @@ -682,39 +461,49 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) for (i = 0; i < rlen; i++) rp[i] = bitrev[rp[i]]; + /* create a fz_buffer on the stack */ + buf.refs = 1; + buf.data = rp; + buf.len = rlen; + buf.cap = rlen; + + stm = fz_openbuffer(&buf); + switch (tiff->compression) { case 1: - error = xps_decode_tiff_uncompressed(ctx, tiff, rp, rp + rlen, wp, wp + wlen); + error = xps_decode_tiff_uncompressed(ctx, tiff, stm, wp, wlen); break; case 2: - error = xps_decode_tiff_fax(ctx, tiff, 2, rp, rp + rlen, wp, wp + wlen); + error = xps_decode_tiff_fax(ctx, tiff, 2, stm, wp, wlen); break; case 3: - error = xps_decode_tiff_fax(ctx, tiff, 3, rp, rp + rlen, wp, wp + wlen); + error = xps_decode_tiff_fax(ctx, tiff, 3, stm, wp, wlen); break; case 4: - error = xps_decode_tiff_fax(ctx, tiff, 4, rp, rp + rlen, wp, wp + wlen); + error = xps_decode_tiff_fax(ctx, tiff, 4, stm, wp, wlen); break; case 5: - error = xps_decode_tiff_lzw(ctx, tiff, rp, rp + rlen, wp, wp + wlen); + error = xps_decode_tiff_lzw(ctx, tiff, stm, wp, wlen); break; case 6: error = fz_throw("deprecated JPEG in TIFF compression not supported"); break; case 7: - error = xps_decode_tiff_jpeg(ctx, tiff, rp, rp + rlen, wp, wp + wlen); + error = xps_decode_tiff_jpeg(ctx, tiff, stm, wp, wlen); break; case 8: - error = xps_decode_tiff_flate(ctx, tiff, rp, rp + rlen, wp, wp + wlen); + error = xps_decode_tiff_flate(ctx, tiff, stm, wp, wlen); break; case 32773: - error = xps_decode_tiff_packbits(ctx, tiff, rp, rp + rlen, wp, wp + wlen); + error = xps_decode_tiff_packbits(ctx, tiff, stm, wp, wlen); break; default: error = fz_throw("unknown TIFF compression: %d", tiff->compression); } + fz_close(stm); + if (error) return fz_rethrow(error, "could not decode strip %d", row / tiff->rowsperstrip); @@ -769,7 +558,34 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) image->hasalpha = 1; } - return gs_okay; + return fz_okay; +} + +static inline int readbyte(xps_tiff *tiff) +{ + if (tiff->rp < tiff->ep) + return *tiff->rp++; + return EOF; +} + +static inline unsigned readshort(xps_tiff *tiff) +{ + unsigned a = readbyte(tiff); + unsigned b = readbyte(tiff); + if (tiff->order == TII) + return (b << 8) | a; + return (a << 8) | b; +} + +static inline unsigned readlong(xps_tiff *tiff) +{ + unsigned a = readbyte(tiff); + unsigned b = readbyte(tiff); + unsigned c = readbyte(tiff); + unsigned d = readbyte(tiff); + if (tiff->order == TII) + return (d << 24) | (c << 16) | (b << 8) | a; + return (a << 24) | (b << 16) | (c << 8) | d; } static void @@ -778,11 +594,8 @@ xps_read_tiff_bytes(unsigned char *p, xps_tiff *tiff, unsigned ofs, unsigned n) tiff->rp = tiff->bp + ofs; if (tiff->rp > tiff->ep) tiff->rp = tiff->bp; - while (n--) - { *p++ = readbyte(tiff); - } } static void @@ -888,8 +701,6 @@ xps_read_tiff_tag(xps_context *ctx, xps_tiff *tiff, unsigned offset) break; case ICCProfile: tiff->profile = fz_malloc(count); - if (!tiff->profile) - return fz_throw("could not allocate embedded icc profile"); /* ICC profile data type is set to UNDEFINED. * TBYTE reading not correct in xps_read_tiff_tag_value */ xps_read_tiff_bytes(tiff->profile, tiff, value, count); @@ -897,6 +708,7 @@ xps_read_tiff_tag(xps_context *ctx, xps_tiff *tiff, unsigned offset) break; case JPEGTables: + fz_warn("jpeg tables in tiff not implemented"); tiff->jpegtables = tiff->bp + value; tiff->jpegtableslen = count; break; @@ -933,7 +745,7 @@ xps_read_tiff_tag(xps_context *ctx, xps_tiff *tiff, unsigned offset) break; } - return gs_okay; + return fz_okay; } static void @@ -1011,7 +823,7 @@ xps_decode_tiff_header(xps_context *ctx, xps_tiff *tiff, byte *buf, int len) offset += 12; } - return gs_okay; + return fz_okay; } int @@ -1059,28 +871,5 @@ xps_decode_tiff(xps_context *ctx, byte *buf, int len, xps_image *image) if (tiff->stripoffsets) fz_free(tiff->stripoffsets); if (tiff->stripbytecounts) fz_free(tiff->stripbytecounts); - return gs_okay; -} - -int -xps_tiff_has_alpha(xps_context *ctx, byte *buf, int len) -{ - int error; - xps_tiff tiffst; - xps_tiff *tiff = &tiffst; - - error = xps_decode_tiff_header(ctx, tiff, buf, len); - if (error) - { - gs_catch(error, "cannot decode tiff header"); - return 0; - } - - if (tiff->profile) fz_free(tiff->profile); - if (tiff->colormap) fz_free(tiff->colormap); - if (tiff->stripoffsets) fz_free(tiff->stripoffsets); - if (tiff->stripbytecounts) fz_free(tiff->stripbytecounts); - - return tiff->extrasamples == 2 || tiff->extrasamples == 1; + return fz_okay; } -#endif diff --git a/xps/xpstile.c b/xps/xpstile.c index 0bfff9b0..db6e6655 100644 --- a/xps/xpstile.c +++ b/xps/xpstile.c @@ -165,9 +165,9 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, int x, y; /* TODO: loop in visible area */ - for (y = -10; y < 20; y++) + for (y = 0; y < 2; y++) { - for (x = -10; x < 20; x++) + for (x = 0; x < 2; x++) { ttm = fz_concat(fz_translate(w*x, h*y), ctm); xps_paint_tiling_brush(ctx, ttm, viewbox, tile_mode, &c); -- cgit v1.2.3 From 4f484b32f3cf8682180ccb9e36f929edff175498 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 30 Mar 2011 20:01:31 +0200 Subject: xps: s/could not/cannot/ in error messages --- xps/xpsglyphs.c | 2 +- xps/xpsgradient.c | 4 ++-- xps/xpsimage.c | 8 ++++---- xps/xpstiff.c | 14 +++++++------- xps/xpsxml.c | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index 834a55f5..d30f016b 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -82,7 +82,7 @@ xps_select_best_font_encoding(fz_font *font) } } - fz_warn("could not find a suitable cmap"); + fz_warn("cannot find a suitable cmap"); } /* diff --git a/xps/xpsgradient.c b/xps/xpsgradient.c index 17881d6c..54516e72 100644 --- a/xps/xpsgradient.c +++ b/xps/xpsgradient.c @@ -583,11 +583,11 @@ xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, /* color_func = xps_create_gradient_stop_function(ctx, stop_list, stop_count, 0); if (!color_func) - return fz_rethrow(-1, "could not create color gradient function"); + return fz_rethrow(-1, "cannot create color gradient function"); opacity_func = xps_create_gradient_stop_function(ctx, stop_list, stop_count, 1); if (!opacity_func) - return fz_rethrow(-1, "could not create opacity gradient function"); + return fz_rethrow(-1, "cannot create opacity gradient function"); */ has_opacity = xps_gradient_has_transparent_colors(stop_list, stop_count); diff --git a/xps/xpsimage.c b/xps/xpsimage.c index a4305734..7dcb54aa 100644 --- a/xps/xpsimage.c +++ b/xps/xpsimage.c @@ -17,25 +17,25 @@ xps_decode_image(xps_context *ctx, xps_part *part, xps_image *image) { error = xps_decode_jpeg(ctx, buf, len, image); if (error) - return fz_rethrow(error, "could not decode jpeg image"); + return fz_rethrow(error, "cannot decode jpeg image"); } else if (memcmp(buf, "\211PNG\r\n\032\n", 8) == 0) { error = xps_decode_png(ctx, buf, len, image); if (error) - return fz_rethrow(error, "could not decode png image"); + return fz_rethrow(error, "cannot decode png image"); } else if (memcmp(buf, "II", 2) == 0 && buf[2] == 0xBC) { error = xps_decode_jpegxr(ctx, buf, len, image); if (error) - return fz_rethrow(error, "could not decode JPEG-XR image"); + return fz_rethrow(error, "cannot decode JPEG-XR image"); } else if (memcmp(buf, "MM", 2) == 0 || memcmp(buf, "II", 2) == 0) { error = xps_decode_tiff(ctx, buf, len, image); if (error) - return fz_rethrow(error, "could not decode TIFF image"); + return fz_rethrow(error, "cannot decode TIFF image"); } else return fz_throw("unknown image file format"); diff --git a/xps/xpstiff.c b/xps/xpstiff.c index 562f1747..30150a93 100644 --- a/xps/xpstiff.c +++ b/xps/xpstiff.c @@ -505,7 +505,7 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) fz_close(stm); if (error) - return fz_rethrow(error, "could not decode strip %d", row / tiff->rowsperstrip); + return fz_rethrow(error, "cannot decode strip %d", row / tiff->rowsperstrip); /* scramble the bits back into original order */ if (tiff->fillorder == 2) @@ -532,7 +532,7 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) { error = xps_expand_colormap(ctx, tiff, image); if (error) - return fz_rethrow(error, "could not expand colormap"); + return fz_rethrow(error, "cannot expand colormap"); } /* WhiteIsZero .. invert */ @@ -716,21 +716,21 @@ xps_read_tiff_tag(xps_context *ctx, xps_tiff *tiff, unsigned offset) case StripOffsets: tiff->stripoffsets = (unsigned*) fz_malloc(count * sizeof(unsigned)); if (!tiff->stripoffsets) - return fz_throw("could not allocate strip offsets"); + return fz_throw("cannot allocate strip offsets"); xps_read_tiff_tag_value(tiff->stripoffsets, tiff, type, value, count); break; case StripByteCounts: tiff->stripbytecounts = (unsigned*) fz_malloc(count * sizeof(unsigned)); if (!tiff->stripbytecounts) - return fz_throw("could not allocate strip byte counts"); + return fz_throw("cannot allocate strip byte counts"); xps_read_tiff_tag_value(tiff->stripbytecounts, tiff, type, value, count); break; case ColorMap: tiff->colormap = (unsigned*) fz_malloc(count * sizeof(unsigned)); if (!tiff->colormap) - return fz_throw("could not allocate color map"); + return fz_throw("cannot allocate color map"); xps_read_tiff_tag_value(tiff->colormap, tiff, type, value, count); break; @@ -819,7 +819,7 @@ xps_decode_tiff_header(xps_context *ctx, xps_tiff *tiff, byte *buf, int len) { error = xps_read_tiff_tag(ctx, tiff, offset); if (error) - return fz_rethrow(error, "could not read TIFF header tag"); + return fz_rethrow(error, "cannot read TIFF header tag"); offset += 12; } @@ -846,7 +846,7 @@ xps_decode_tiff(xps_context *ctx, byte *buf, int len, xps_image *image) error = xps_decode_tiff_strips(ctx, tiff, image); if (error) - return fz_rethrow(error, "could not decode image data"); + return fz_rethrow(error, "cannot decode image data"); /* * Byte swap 16-bit images to big endian if necessary. diff --git a/xps/xpsxml.c b/xps/xpsxml.c index 63d9af36..c6f41123 100644 --- a/xps/xpsxml.c +++ b/xps/xpsxml.c @@ -220,7 +220,7 @@ xps_parse_xml(xps_context *ctx, byte *buf, int len) xp = XML_ParserCreateNS(NULL, ' '); if (!xp) { - fz_throw("xml error: could not create expat parser"); + fz_throw("xml error: cannot create expat parser"); return NULL; } -- cgit v1.2.3 From b3f90095897b64f854efc4b2f37df428a71fd508 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Thu, 31 Mar 2011 03:18:51 +0200 Subject: xps: Clean up image loading code, and handle images with alpha. --- draw/imageunpack.c | 10 +++ fitz/fitz.h | 1 + fitz/res_pixmap.c | 18 ++++ xps/muxps.h | 28 ++---- xps/xpsimage.c | 76 ++++------------ xps/xpsjpeg.c | 59 ++++++------ xps/xpsjxr.c | 257 +---------------------------------------------------- xps/xpspng.c | 178 ++++++++----------------------------- xps/xpstiff.c | 207 ++++++++++++++++++++---------------------- 9 files changed, 221 insertions(+), 613 deletions(-) diff --git a/draw/imageunpack.c b/draw/imageunpack.c index bbc22c70..2c7cb452 100644 --- a/draw/imageunpack.c +++ b/draw/imageunpack.c @@ -58,6 +58,16 @@ fz_unpacktile(fz_pixmap *dst, unsigned char * restrict src, int n, int depth, in if (depth == 1) initget1tables(); + if (scale == 0) + { + switch (depth) + { + case 1: scale = 255; break; + case 2: scale = 85; break; + case 4: scale = 17; break; + } + } + for (y = 0; y < dst->h; y++) { unsigned char *sp = src + y * stride; diff --git a/fitz/fitz.h b/fitz/fitz.h index 72af4532..5d3ba1c1 100644 --- a/fitz/fitz.h +++ b/fitz/fitz.h @@ -685,6 +685,7 @@ fz_pixmap *fz_keeppixmap(fz_pixmap *pix); void fz_droppixmap(fz_pixmap *pix); void fz_clearpixmap(fz_pixmap *pix); void fz_clearpixmapwithcolor(fz_pixmap *pix, int value); +void fz_premultiplypixmap(fz_pixmap *pix); fz_pixmap *fz_alphafromgray(fz_pixmap *gray, int luminosity); fz_bbox fz_boundpixmap(fz_pixmap *pix); diff --git a/fitz/res_pixmap.c b/fitz/res_pixmap.c index e70d084d..b37b7a3e 100644 --- a/fitz/res_pixmap.c +++ b/fitz/res_pixmap.c @@ -97,6 +97,24 @@ fz_clearpixmapwithcolor(fz_pixmap *pix, int value) } } +void +fz_premultiplypixmap(fz_pixmap *pix) +{ + unsigned char *s = pix->samples; + unsigned char a; + int k, x, y; + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + a = s[pix->n - 1]; + for (k = 0; k < pix->n - 1; k++) + s[k] = fz_mul255(s[k], a); + s += pix->n; + } + } +} + fz_bbox fz_boundpixmap(fz_pixmap *pix) { diff --git a/xps/muxps.h b/xps/muxps.h index ba826b10..1b137a59 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -97,35 +97,21 @@ void xps_debug_fixdocseq(xps_context *ctx); * Images. */ -typedef struct xps_image_s xps_image; +typedef struct xps_image xps_image; /* type for the information derived directly from the raster file format */ -struct xps_image_s +struct xps_image { - int width; - int height; - int stride; - fz_colorspace *colorspace; - int comps; - int hasalpha; /* chunky alpha */ - int bits; + fz_pixmap *pixmap; int xres; int yres; - byte *samples; - byte *profile; - int profilesize; - fz_pixmap *pixmap; }; -int xps_decode_jpeg(xps_context *ctx, byte *rbuf, int rlen, xps_image *image); -int xps_decode_png(xps_context *ctx, byte *rbuf, int rlen, xps_image *image); -int xps_decode_tiff(xps_context *ctx, byte *rbuf, int rlen, xps_image *image); -int xps_decode_jpegxr(xps_context *ctx, byte *buf, int len, xps_image *image); - -int xps_png_has_alpha(xps_context *ctx, byte *rbuf, int rlen); -int xps_tiff_has_alpha(xps_context *ctx, byte *rbuf, int rlen); -int xps_jpegxr_has_alpha(xps_context *ctx, byte *buf, int len); +int xps_decode_jpeg(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen); +int xps_decode_png(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen); +int xps_decode_tiff(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen); +int xps_decode_jpegxr(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen); void xps_free_image(xps_context *ctx, xps_image *image); diff --git a/xps/xpsimage.c b/xps/xpsimage.c index 7dcb54aa..8fc2f7a1 100644 --- a/xps/xpsimage.c +++ b/xps/xpsimage.c @@ -2,7 +2,7 @@ #include "muxps.h" static int -xps_decode_image(xps_context *ctx, xps_part *part, xps_image *image) +xps_decode_image(xps_image **imagep, xps_context *ctx, xps_part *part) { byte *buf = part->data; int len = part->size; @@ -11,40 +11,33 @@ xps_decode_image(xps_context *ctx, xps_part *part, xps_image *image) if (len < 8) return fz_throw("unknown image file format"); - memset(image, 0, sizeof(xps_image)); - if (buf[0] == 0xff && buf[1] == 0xd8) { - error = xps_decode_jpeg(ctx, buf, len, image); + error = xps_decode_jpeg(imagep, ctx, buf, len); if (error) return fz_rethrow(error, "cannot decode jpeg image"); } else if (memcmp(buf, "\211PNG\r\n\032\n", 8) == 0) { - error = xps_decode_png(ctx, buf, len, image); + error = xps_decode_png(imagep, ctx, buf, len); if (error) return fz_rethrow(error, "cannot decode png image"); } else if (memcmp(buf, "II", 2) == 0 && buf[2] == 0xBC) { - error = xps_decode_jpegxr(ctx, buf, len, image); + error = xps_decode_jpegxr(imagep, ctx, buf, len); if (error) return fz_rethrow(error, "cannot decode JPEG-XR image"); } else if (memcmp(buf, "MM", 2) == 0 || memcmp(buf, "II", 2) == 0) { - error = xps_decode_tiff(ctx, buf, len, image); + error = xps_decode_tiff(imagep, ctx, buf, len); if (error) return fz_rethrow(error, "cannot decode TIFF image"); } else return fz_throw("unknown image file format"); - image->pixmap = fz_newpixmap(image->colorspace, 0, 0, image->width, image->height); - fz_unpacktile(image->pixmap, image->samples, image->comps, image->bits, image->stride, 1); - fz_free(image->samples); - image->samples = NULL; - return fz_okay; } @@ -52,19 +45,18 @@ static void xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root, void *vimage) { xps_image *image = vimage; - float xs = image->width * 96.0 / image->xres; - float ys = image->height * 96.0 / image->yres; + fz_pixmap *pixmap = image->pixmap; + float xs = pixmap->w * 96.0 / image->xres; + float ys = pixmap->h * 96.0 / image->yres; fz_matrix im = fz_scale(xs, -ys); im.f = ys; ctm = fz_concat(im, ctm); - ctx->dev->fillimage(ctx->dev->user, image->pixmap, ctm, 1.0); + ctx->dev->fillimage(ctx->dev->user, pixmap, ctm, 1.0); } -static int -xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xps_item *root, - xps_part **partp, char **profilep) +static xps_part * +xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xps_item *root) { - xps_part *part; char *image_source_att; char buf[1024]; char partname[1024]; @@ -74,7 +66,7 @@ xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xps_item *roo image_source_att = xps_att(root, "ImageSource"); if (!image_source_att) - return fz_throw("missing ImageSource attribute"); + return NULL; /* "{ColorConvertedBitmap /Resources/Image.tiff /Resources/Profile.icc}" */ if (strstr(image_source_att, "{ColorConvertedBitmap") == image_source_att) @@ -105,18 +97,11 @@ xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xps_item *roo } if (!image_name) - return fz_throw("cannot parse image resource name '%s'", image_source_att); + return NULL; xps_absolute_path(partname, base_uri, image_name, sizeof partname); - part = xps_read_part(ctx, partname); - if (!part) - return fz_throw("cannot find image resource part '%s'", partname); - *partp = part; - if (profile_name) - *profilep = fz_strdup(profile_name); - - return 0; + return xps_read_part(ctx, partname); } void @@ -124,42 +109,22 @@ xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resou { xps_part *part; xps_image *image; - fz_colorspace *colorspace; - char *profilename; int code; - profilename = NULL; - - code = xps_find_image_brush_source_part(ctx, base_uri, root, &part, &profilename); - if (code < 0) { - fz_catch(code, "cannot find image source"); + part = xps_find_image_brush_source_part(ctx, base_uri, root); + if (!part) { + fz_warn("cannot find image source"); return; } - image = fz_malloc(sizeof(xps_image)); - - code = xps_decode_image(ctx, part, image); + code = xps_decode_image(&image, ctx, part); if (code < 0) { - fz_free(image); fz_catch(-1, "cannot decode image resource"); return; } - /* Override any embedded colorspace profiles if the external one matches. */ - if (profilename) - { - colorspace = xps_read_icc_colorspace(ctx, base_uri, profilename); - if (colorspace && colorspace->n == image->colorspace->n) - { - // TODO: refcount image->colorspace - image->colorspace = colorspace; - } - } - xps_parse_tiling_brush(ctx, ctm, base_uri, dict, root, xps_paint_image_brush, image); - if (profilename) - fz_free(profilename); xps_free_image(ctx, image); xps_free_part(ctx, part); } @@ -167,12 +132,7 @@ xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resou void xps_free_image(xps_context *ctx, xps_image *image) { - // TODO: refcount image->colorspace if (image->pixmap) fz_droppixmap(image->pixmap); - if (image->samples) - fz_free(image->samples); - if (image->profile) - fz_free(image->profile); fz_free(image); } diff --git a/xps/xpsjpeg.c b/xps/xpsjpeg.c index 41534eeb..4dc2a7e4 100644 --- a/xps/xpsjpeg.c +++ b/xps/xpsjpeg.c @@ -48,18 +48,21 @@ static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) } int -xps_decode_jpeg(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) +xps_decode_jpeg(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr_jmp err; struct jpeg_source_mgr src; - unsigned char *buffer[1]; + unsigned char *row[1], *sp, *dp; + fz_colorspace *colorspace; + int x, k; + + xps_image *image = NULL; if (setjmp(err.env)) { - if (image->samples) - fz_free(image->samples); - image->samples = NULL; + if (image) + xps_free_image(ctx, image); return fz_throw("jpeg error: %s", err.msg); } @@ -81,19 +84,19 @@ xps_decode_jpeg(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) jpeg_start_decompress(&cinfo); - image->width = cinfo.output_width; - image->height = cinfo.output_height; - image->comps = cinfo.output_components; - image->stride = cinfo.output_width * cinfo.output_components; - image->bits = 8; - image->samples = fz_calloc(image->height, image->stride); + if (cinfo.output_components == 1) + colorspace = fz_devicegray; + else if (cinfo.output_components == 3) + colorspace = fz_devicergb; + else if (cinfo.output_components == 4) + colorspace = fz_devicecmyk; + else + return fz_throw("bad number of components in jpeg: %d", cinfo.output_components); - if (image->comps == 1) - image->colorspace = fz_devicegray; - if (image->comps == 3) - image->colorspace = fz_devicergb; - if (image->comps == 4) - image->colorspace = fz_devicecmyk; + image = fz_malloc(sizeof(xps_image)); + image->pixmap = fz_newpixmap(colorspace, 0, 0, cinfo.output_width, cinfo.output_height); + image->xres = 96; + image->yres = 96; if (cinfo.density_unit == 1) { @@ -105,23 +108,27 @@ xps_decode_jpeg(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) image->xres = cinfo.X_density * 2.54; image->yres = cinfo.Y_density * 2.54; } - else - { - image->xres = 96; - image->yres = 96; - } - memset(image->samples, 127, image->stride * image->height); + fz_clearpixmap(image->pixmap); - buffer[0] = image->samples; + row[0] = fz_malloc(cinfo.output_components * cinfo.output_width); + dp = image->pixmap->samples; while (cinfo.output_scanline < cinfo.output_height) { - jpeg_read_scanlines(&cinfo, buffer, 1); - buffer[0] += image->comps * image->width; + jpeg_read_scanlines(&cinfo, row, 1); + sp = row[0]; + for (x = 0; x < cinfo.output_width; x++) + { + for (k = 0; k < cinfo.output_components; k++) + *dp++ = *sp++; + *dp++ = 255; + } } + fz_free(row[0]); jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); + *imagep = image; return fz_okay; } diff --git a/xps/xpsjxr.c b/xps/xpsjxr.c index bbe5f66e..9499bdec 100644 --- a/xps/xpsjxr.c +++ b/xps/xpsjxr.c @@ -3,263 +3,8 @@ #include "fitz.h" #include "muxps.h" -#ifdef HAVE_JPEGXR - -#ifdef _MSC_VER -#undef _MSC_VER -#endif - -#include "jpegxr.h" - -struct state { xps_context *ctx; xps_image *output; }; - -static const char * -jxr_error_string(int code) -{ - switch (code) - { - case JXR_EC_OK: return "No error"; - default: - case JXR_EC_ERROR: return "Unspecified error"; - case JXR_EC_BADMAGIC: return "Stream lacks proper magic number"; - case JXR_EC_FEATURE_NOT_IMPLEMENTED: return "Feature not implemented"; - case JXR_EC_IO: return "Error reading/writing data"; - case JXR_EC_BADFORMAT: return "Bad file format"; - } -} - -#define CLAMP(v, mn, mx) (v < mn ? mn : v > mx ? mx : v) - -static inline int -scale_bits(int depth, int value) -{ - union { int iv; float fv; } bd32f; - - switch (depth) - { - case JXR_BD1WHITE1: - return value * 255; - case JXR_BD1BLACK1: - return value ? 0 : 255; - case JXR_BD8: - return value; - case JXR_BD16: - return value >> 8; - case JXR_BD16S: /* -4 .. 4 ; 8192 = 1.0 */ - value = value >> 5; - return CLAMP(value, 0, 255); - case JXR_BD32S: /* -128 .. 128 ; 16777216 = 1.0 */ - value = value >> 16; - return CLAMP(value, 0, 255); - case JXR_BD32F: - bd32f.iv = value; - value = bd32f.fv * 255; - return CLAMP(value, 0, 255); -#if 0 - case JXR_BDRESERVED: return value; - case JXR_BD16F: return value; - case JXR_BD5: return value; - case JXR_BD10: return value; - case JXR_BD565: return value; -#endif - } - return value; -} - -static void -xps_decode_jpegxr_block(jxr_image image, int mx, int my, int *data) -{ - struct state *state = jxr_get_user_data(image); - xps_context *ctx = state->ctx; - xps_image *output = state->output; - int depth; - unsigned char *p; - int x, y, k; - - if (!output->samples) - { - output->width = jxr_get_IMAGE_WIDTH(image); - output->height = jxr_get_IMAGE_HEIGHT(image); - output->comps = jxr_get_IMAGE_CHANNELS(image); - output->hasalpha = jxr_get_ALPHACHANNEL_FLAG(image); - output->bits = 8; - output->stride = output->width * output->comps; - output->samples = fz_malloc(output->stride * output->height); - - switch (output->comps) - { - default: - case 1: output->colorspace = ctx->gray; break; - case 3: output->colorspace = ctx->srgb; break; - case 4: output->colorspace = ctx->cmyk; break; - } - } - - depth = jxr_get_OUTPUT_BITDEPTH(image); - - my = my * 16; - mx = mx * 16; - - for (y = 0; y < 16; y++) - { - if (my + y >= output->height) - return; - p = output->samples + (my + y) * output->stride + mx * output->comps; - for (x = 0; x < 16; x++) - { - if (mx + x >= output->width) - data += output->comps; - else - for (k = 0; k < output->comps; k++) - *p++ = scale_bits(depth, *data++); - } - } -} - -static void -xps_decode_jpegxr_alpha_block(jxr_image image, int mx, int my, int *data) -{ - struct state *state = jxr_get_user_data(image); - xps_context *ctx = state->ctx; - xps_image *output = state->output; - int depth; - unsigned char *p; - int x, y, k; - - if (!output->alpha) - { - output->alpha = fz_malloc(output->width * output->height); - } - - depth = jxr_get_OUTPUT_BITDEPTH(image); - - my = my * 16; - mx = mx * 16; - - for (y = 0; y < 16; y++) - { - if (my + y >= output->height) - return; - p = output->alpha + (my + y) * output->width + mx; - for (x = 0; x < 16; x++) - { - if (mx + x >= output->width) - data ++; - else - *p++ = scale_bits(depth, *data++); - } - } -} - int -xps_decode_jpegxr(xps_context *ctx, byte *buf, int len, xps_image *output) -{ - FILE *file; - char name[gp_file_name_sizeof]; - struct state state; - jxr_container container; - jxr_image image; - int offset, alpha_offset; - int rc; - - memset(output, 0, sizeof(*output)); - - file = gp_open_scratch_file(ctx->memory, "jpegxr-scratch-", name, "wb+"); - if (!file) - return gs_throw(gs_error_invalidfileaccess, "cannot open scratch file"); - rc = fwrite(buf, 1, len, file); - if (rc != len) - return gs_throw(gs_error_invalidfileaccess, "cannot write to scratch file"); - fseek(file, 0, SEEK_SET); - - container = jxr_create_container(); - rc = jxr_read_image_container(container, file); - if (rc < 0) - return gs_throw1(-1, "jxr_read_image_container: %s", jxr_error_string(rc)); - - offset = jxrc_image_offset(container, 0); - alpha_offset = jxrc_alpha_offset(container, 0); - - output->xres = jxrc_width_resolution(container, 0); - output->yres = jxrc_height_resolution(container, 0); - - image = jxr_create_input(); - jxr_set_PROFILE_IDC(image, 111); - jxr_set_LEVEL_IDC(image, 255); - jxr_set_pixel_format(image, jxrc_image_pixelformat(container, 0)); - jxr_set_container_parameters(image, - jxrc_image_pixelformat(container, 0), - jxrc_image_width(container, 0), - jxrc_image_height(container, 0), - jxrc_alpha_offset(container, 0), - jxrc_image_band_presence(container, 0), - jxrc_alpha_band_presence(container, 0), 0); - - jxr_set_block_output(image, xps_decode_jpegxr_block); - state.ctx = ctx; - state.output = output; - jxr_set_user_data(image, &state); - - fseek(file, offset, SEEK_SET); - rc = jxr_read_image_bitstream(image, file); - if (rc < 0) - return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc)); - - jxr_destroy(image); - - if (alpha_offset > 0) - { - image = jxr_create_input(); - jxr_set_PROFILE_IDC(image, 111); - jxr_set_LEVEL_IDC(image, 255); - jxr_set_pixel_format(image, jxrc_image_pixelformat(container, 0)); - jxr_set_container_parameters(image, - jxrc_image_pixelformat(container, 0), - jxrc_image_width(container, 0), - jxrc_image_height(container, 0), - jxrc_alpha_offset(container, 0), - jxrc_image_band_presence(container, 0), - jxrc_alpha_band_presence(container, 0), 0); - - jxr_set_block_output(image, xps_decode_jpegxr_alpha_block); - state.ctx = ctx; - state.output = output; - jxr_set_user_data(image, &state); - - fseek(file, alpha_offset, SEEK_SET); - rc = jxr_read_image_bitstream(image, file); - if (rc < 0) - return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc)); - - jxr_destroy(image); - } - - jxr_destroy_container(container); - - fclose(file); - unlink(name); - - return fz_okay; -} - -int -xps_jpegxr_has_alpha(xps_context *ctx, byte *buf, int len) -{ - return 1; -} - -#else - -int -xps_decode_jpegxr(xps_context *ctx, byte *buf, int len, xps_image *image) +xps_decode_jpegxr(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen) { return fz_throw("JPEG-XR codec is not available"); } - -int -xps_jpegxr_has_alpha(xps_context *ctx, byte *buf, int len) -{ - return 0; -} - -#endif diff --git a/xps/xpspng.c b/xps/xpspng.c index 641101fe..6af62e63 100644 --- a/xps/xpspng.c +++ b/xps/xpspng.c @@ -3,10 +3,6 @@ #include -/* - * PNG using libpng directly (no gs wrappers) - */ - struct xps_png_io_s { byte *ptr; @@ -35,94 +31,20 @@ xps_png_free(png_structp png, png_voidp ptr) fz_free(ptr); } -/* This only determines if we have an alpha value */ -int -xps_png_has_alpha(xps_context *ctx, byte *rbuf, int rlen) -{ - png_structp png; - png_infop info; - struct xps_png_io_s io; - int has_alpha; - - /* - * Set up PNG structs and input source - */ - - io.ptr = rbuf; - io.lim = rbuf + rlen; - - png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, - NULL, NULL, NULL, - ctx, xps_png_malloc, xps_png_free); - if (!png) { - fz_warn("png_create_read_struct"); - return 0; - } - - info = png_create_info_struct(png); - if (!info) { - fz_warn("png_create_info_struct"); - return 0; - } - - png_set_read_fn(png, &io, xps_png_read); - png_set_crc_action(png, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE); - - /* - * Jump to here on errors. - */ - - if (setjmp(png_jmpbuf(png))) - { - png_destroy_read_struct(&png, &info, NULL); - fz_warn("png reading failed"); - return 0; - } - - /* - * Read PNG header - */ - - png_read_info(png, info); - - switch (png_get_color_type(png, info)) - { - case PNG_COLOR_TYPE_PALETTE: - case PNG_COLOR_TYPE_GRAY: - case PNG_COLOR_TYPE_RGB: - has_alpha = 0; - break; - - case PNG_COLOR_TYPE_GRAY_ALPHA: - case PNG_COLOR_TYPE_RGB_ALPHA: - has_alpha = 1; - break; - - default: - fz_warn("cannot handle this png color type"); - has_alpha = 0; - break; - } - - /* - * Clean up memory. - */ - - png_destroy_read_struct(&png, &info, NULL); - - return has_alpha; -} - int -xps_decode_png(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) +xps_decode_png(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen) { png_structp png; png_infop info; struct xps_png_io_s io; + int width, height, stride, premul; int npasses; int pass; int y; + fz_pixmap *pixmap = NULL; + xps_image *image = NULL; + /* * Set up PNG structs and input source */ @@ -149,8 +71,12 @@ xps_decode_png(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) if (setjmp(png_jmpbuf(png))) { + if (pixmap) + fz_droppixmap(pixmap); + if (image) + fz_free(image); png_destroy_read_struct(&png, &info, NULL); - return fz_throw("png reading failed"); + return fz_throw("cannot read png image"); } /* @@ -160,77 +86,49 @@ xps_decode_png(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) png_read_info(png, info); if (png_get_interlace_type(png, info) == PNG_INTERLACE_ADAM7) - { npasses = png_set_interlace_handling(png); - } else - { npasses = 1; - } - if (png_get_color_type(png, info) == PNG_COLOR_TYPE_PALETTE) - { - png_set_palette_to_rgb(png); - } + png_set_expand(png); + png_set_packing(png); + png_set_strip_16(png); - if (png_get_valid(png, info, PNG_INFO_tRNS)) - { - /* this will also expand the depth to 8-bits */ - png_set_tRNS_to_alpha(png); - } + premul = 0; + if (png_get_color_type(png, info) == PNG_COLOR_TYPE_GRAY) + png_set_add_alpha(png, 0xff, PNG_FILLER_AFTER); + else if (png_get_color_type(png, info) == PNG_COLOR_TYPE_RGB) + png_set_add_alpha(png, 0xff, PNG_FILLER_AFTER); + else + premul = 1; png_read_update_info(png, info); - image->width = png_get_image_width(png, info); - image->height = png_get_image_height(png, info); - image->comps = png_get_channels(png, info); - image->bits = png_get_bit_depth(png, info); - - /* See if we have an icc profile */ - if (info->iccp_profile != NULL) - { - image->profilesize = info->iccp_proflen; - image->profile = fz_malloc(info->iccp_proflen); - if (image->profile) - { - /* If we can't create it, just ignore */ - memcpy(image->profile, info->iccp_profile, info->iccp_proflen); - } - } + width = png_get_image_width(png, info); + height = png_get_image_height(png, info); + stride = png_get_rowbytes(png, info); switch (png_get_color_type(png, info)) { - case PNG_COLOR_TYPE_GRAY: - image->colorspace = fz_devicegray; - image->hasalpha = 0; - break; - - case PNG_COLOR_TYPE_RGB: - image->colorspace = fz_devicergb; - image->hasalpha = 0; - break; - case PNG_COLOR_TYPE_GRAY_ALPHA: - image->colorspace = fz_devicegray; - image->hasalpha = 1; + pixmap = fz_newpixmap(fz_devicegray, 0, 0, width, height); break; - case PNG_COLOR_TYPE_RGB_ALPHA: - image->colorspace = fz_devicergb; - image->hasalpha = 1; + pixmap = fz_newpixmap(fz_devicergb, 0, 0, width, height); break; - default: return fz_throw("cannot handle this png color type"); } + image = fz_malloc(sizeof(xps_image)); + image->pixmap = pixmap; + image->xres = 96; + image->yres = 96; + /* * Extract DPI, default to 96 dpi */ - image->xres = 96; - image->yres = 96; - if (info->valid & PNG_INFO_pHYs) { png_uint_32 xres, yres; @@ -247,17 +145,12 @@ xps_decode_png(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) * Read rows, filling transformed output into image buffer. */ - image->stride = (image->width * image->comps * image->bits + 7) / 8; - - image->samples = fz_calloc(image->height, image->stride); - for (pass = 0; pass < npasses; pass++) - { - for (y = 0; y < image->height; y++) - { - png_read_row(png, image->samples + (y * image->stride), NULL); - } - } + for (y = 0; y < height; y++) + png_read_row(png, pixmap->samples + (y * stride), NULL); + + if (premul) + fz_premultiplypixmap(pixmap); /* * Clean up memory. @@ -265,5 +158,6 @@ xps_decode_png(xps_context *ctx, byte *rbuf, int rlen, xps_image *image) png_destroy_read_struct(&png, &info, NULL); + *imagep = image; return fz_okay; } diff --git a/xps/xpstiff.c b/xps/xpstiff.c index 30150a93..4c9494f2 100644 --- a/xps/xpstiff.c +++ b/xps/xpstiff.c @@ -10,9 +10,9 @@ * TODO: RGBPal images */ -typedef struct xps_tiff_s xps_tiff; +typedef struct xps_tiff xps_tiff; -struct xps_tiff_s +struct xps_tiff { /* "file" */ byte *bp, *rp, *ep; @@ -53,6 +53,11 @@ struct xps_tiff_s byte *profile; int profilesize; + + /* decoded data */ + fz_colorspace *colorspace; + byte *samples; + int stride; }; enum @@ -134,6 +139,7 @@ static int xps_decode_tiff_uncompressed(xps_context *ctx, xps_tiff *tiff, fz_stream *stm, byte *wp, int wlen) { int n = fz_read(stm, wp, wlen); + fz_close(stm); if (n < 0) return fz_rethrow(n, "cannot read uncompressed strip"); return fz_okay; @@ -297,9 +303,9 @@ xps_invert_tiff(byte *line, int width, int comps, int bits, int alpha) } static int -xps_expand_colormap(xps_context *ctx, xps_tiff *tiff, xps_image *image) +xps_expand_tiff_colormap(xps_context *ctx, xps_tiff *tiff) { - int maxval = 1 << image->bits; + int maxval = 1 << tiff->bitspersample; byte *samples; byte *src, *dst; int stride; @@ -309,37 +315,37 @@ xps_expand_colormap(xps_context *ctx, xps_tiff *tiff, xps_image *image) /* colormap values are 0..65535, bits is 4 or 8 */ /* image can be with or without extrasamples: comps is 1 or 2 */ - if (image->comps != 1 && image->comps != 2) + if (tiff->samplesperpixel != 1 && tiff->samplesperpixel != 2) return fz_throw("invalid number of samples for RGBPal"); - if (image->bits != 4 && image->bits != 8) + if (tiff->bitspersample != 4 && tiff->bitspersample != 8) return fz_throw("invalid number of bits for RGBPal"); - stride = image->width * (image->comps + 2); + stride = tiff->imagewidth * (tiff->samplesperpixel + 2); - samples = fz_malloc(stride * image->height); + samples = fz_malloc(stride * tiff->imagelength); if (!samples) return fz_throw("out of memory: samples"); - for (y = 0; y < image->height; y++) + for (y = 0; y < tiff->imagelength; y++) { - src = image->samples + (image->stride * y); + src = tiff->samples + (tiff->stride * y); dst = samples + (stride * y); - for (x = 0; x < image->width; x++) + for (x = 0; x < tiff->imagewidth; x++) { if (tiff->extrasamples) { - int c = getcomp(src, x * 2, image->bits); - int a = getcomp(src, x * 2 + 1, image->bits); + int c = getcomp(src, x * 2, tiff->bitspersample); + int a = getcomp(src, x * 2 + 1, tiff->bitspersample); *dst++ = tiff->colormap[c + 0] >> 8; *dst++ = tiff->colormap[c + maxval] >> 8; *dst++ = tiff->colormap[c + maxval * 2] >> 8; - *dst++ = a << (8 - image->bits); + *dst++ = a << (8 - tiff->bitspersample); } else { - int c = getcomp(src, x, image->bits); + int c = getcomp(src, x, tiff->bitspersample); *dst++ = tiff->colormap[c + 0] >> 8; *dst++ = tiff->colormap[c + maxval] >> 8; *dst++ = tiff->colormap[c + maxval * 2] >> 8; @@ -347,15 +353,15 @@ xps_expand_colormap(xps_context *ctx, xps_tiff *tiff, xps_image *image) } } - image->bits = 8; - image->stride = stride; - image->samples = samples; + tiff->bitspersample = 8; + tiff->stride = stride; + tiff->samples = samples; return fz_okay; } static int -xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) +xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff) { fz_buffer buf; fz_stream *stm; @@ -381,32 +387,28 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) if (tiff->planar != 1) return fz_throw("image data is not in chunky format"); - image->width = tiff->imagewidth; - image->height = tiff->imagelength; - image->comps = tiff->samplesperpixel; - image->bits = tiff->bitspersample; - image->stride = (image->width * image->comps * image->bits + 7) / 8; + tiff->stride = (tiff->imagewidth * tiff->samplesperpixel * tiff->bitspersample + 7) / 8; switch (tiff->photometric) { case 0: /* WhiteIsZero -- inverted */ - image->colorspace = fz_devicegray; + tiff->colorspace = fz_devicegray; break; case 1: /* BlackIsZero */ - image->colorspace = fz_devicegray; + tiff->colorspace = fz_devicegray; break; case 2: /* RGB */ - image->colorspace = fz_devicergb; + tiff->colorspace = fz_devicergb; break; case 3: /* RGBPal */ - image->colorspace = fz_devicergb; + tiff->colorspace = fz_devicergb; break; case 5: /* CMYK */ - image->colorspace = fz_devicecmyk; + tiff->colorspace = fz_devicecmyk; break; case 6: /* YCbCr */ /* it's probably a jpeg ... we let jpeg convert to rgb */ - image->colorspace = fz_devicergb; + tiff->colorspace = fz_devicergb; break; default: return fz_throw("unknown photometric: %d", tiff->photometric); @@ -415,43 +417,39 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) switch (tiff->resolutionunit) { case 2: - image->xres = tiff->xresolution; - image->yres = tiff->yresolution; + /* no unit conversion needed */ break; case 3: - image->xres = tiff->xresolution * 2.54 + 0.5; - image->yres = tiff->yresolution * 2.54 + 0.5; - + tiff->xresolution *= 2.54; + tiff->yresolution *= 2.54; break; default: - image->xres = 96; - image->yres = 96; + tiff->xresolution = 96; + tiff->yresolution = 96; break; } - /* Note xres and yres could be 0 even if unit was set. If so default to 96dpi */ - if (image->xres == 0 || image->yres == 0) + /* Note xres and yres could be 0 even if unit was set. If so default to 96dpi. */ + if (tiff->xresolution == 0 || tiff->yresolution == 0) { - image->xres = 96; - image->yres = 96; + tiff->xresolution = 96; + tiff->yresolution = 96; } - image->samples = fz_malloc(image->stride * image->height); - - memset(image->samples, 0x55, image->stride * image->height); - - wp = image->samples; + tiff->samples = fz_calloc(tiff->imagelength, tiff->stride); + memset(tiff->samples, 0x55, tiff->imagelength * tiff->stride); + wp = tiff->samples; strip = 0; for (row = 0; row < tiff->imagelength; row += tiff->rowsperstrip) { unsigned offset = tiff->stripoffsets[strip]; unsigned rlen = tiff->stripbytecounts[strip]; - unsigned wlen = image->stride * tiff->rowsperstrip; + unsigned wlen = tiff->stride * tiff->rowsperstrip; byte *rp = tiff->bp + offset; - if (wp + wlen > image->samples + image->stride * image->height) - wlen = image->samples + image->stride * image->height - wp; + if (wp + wlen > tiff->samples + tiff->stride * tiff->imagelength) + wlen = tiff->samples + tiff->stride * tiff->imagelength - wp; if (rp + rlen > tiff->ep) return fz_throw("strip extends beyond the end of the file"); @@ -462,11 +460,12 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) rp[i] = bitrev[rp[i]]; /* create a fz_buffer on the stack */ - buf.refs = 1; + buf.refs = 2; buf.data = rp; buf.len = rlen; buf.cap = rlen; + /* the strip decoders will close this */ stm = fz_openbuffer(&buf); switch (tiff->compression) @@ -502,8 +501,6 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) error = fz_throw("unknown TIFF compression: %d", tiff->compression); } - fz_close(stm); - if (error) return fz_rethrow(error, "cannot decode strip %d", row / tiff->rowsperstrip); @@ -512,25 +509,25 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) for (i = 0; i < rlen; i++) rp[i] = bitrev[rp[i]]; - wp += image->stride * tiff->rowsperstrip; + wp += tiff->stride * tiff->rowsperstrip; strip ++; } /* Predictor (only for LZW and Flate) */ if ((tiff->compression == 5 || tiff->compression == 8) && tiff->predictor == 2) { - byte *p = image->samples; - for (i = 0; i < image->height; i++) + byte *p = tiff->samples; + for (i = 0; i < tiff->imagelength; i++) { - xps_unpredict_tiff(p, image->width, tiff->samplesperpixel, image->bits); - p += image->stride; + xps_unpredict_tiff(p, tiff->imagewidth, tiff->samplesperpixel, tiff->bitspersample); + p += tiff->stride; } } /* RGBPal */ if (tiff->photometric == 3 && tiff->colormap) { - error = xps_expand_colormap(ctx, tiff, image); + error = xps_expand_tiff_colormap(ctx, tiff); if (error) return fz_rethrow(error, "cannot expand colormap"); } @@ -538,26 +535,14 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff, xps_image *image) /* WhiteIsZero .. invert */ if (tiff->photometric == 0) { - byte *p = image->samples; - for (i = 0; i < image->height; i++) + byte *p = tiff->samples; + for (i = 0; i < tiff->imagelength; i++) { - xps_invert_tiff(p, image->width, image->comps, image->bits, tiff->extrasamples); - p += image->stride; + xps_invert_tiff(p, tiff->imagewidth, tiff->samplesperpixel, tiff->bitspersample, tiff->extrasamples); + p += tiff->stride; } } - /* Premultiplied transparency */ - if (tiff->extrasamples == 1) - { - image->hasalpha = 1; - } - - /* Non-pre-multiplied transparency */ - if (tiff->extrasamples == 2) - { - image->hasalpha = 1; - } - return fz_okay; } @@ -594,6 +579,7 @@ xps_read_tiff_bytes(unsigned char *p, xps_tiff *tiff, unsigned ofs, unsigned n) tiff->rp = tiff->bp + ofs; if (tiff->rp > tiff->ep) tiff->rp = tiff->bp; + while (n--) *p++ = readbyte(tiff); } @@ -699,6 +685,7 @@ xps_read_tiff_tag(xps_context *ctx, xps_tiff *tiff, unsigned offset) case ExtraSamples: xps_read_tiff_tag_value(&tiff->extrasamples, tiff, type, value, 1); break; + case ICCProfile: tiff->profile = fz_malloc(count); /* ICC profile data type is set to UNDEFINED. @@ -714,23 +701,17 @@ xps_read_tiff_tag(xps_context *ctx, xps_tiff *tiff, unsigned offset) break; case StripOffsets: - tiff->stripoffsets = (unsigned*) fz_malloc(count * sizeof(unsigned)); - if (!tiff->stripoffsets) - return fz_throw("cannot allocate strip offsets"); + tiff->stripoffsets = fz_calloc(count, sizeof(unsigned)); xps_read_tiff_tag_value(tiff->stripoffsets, tiff, type, value, count); break; case StripByteCounts: - tiff->stripbytecounts = (unsigned*) fz_malloc(count * sizeof(unsigned)); - if (!tiff->stripbytecounts) - return fz_throw("cannot allocate strip byte counts"); + tiff->stripbytecounts = fz_calloc(count, sizeof(unsigned)); xps_read_tiff_tag_value(tiff->stripbytecounts, tiff, type, value, count); break; case ColorMap: - tiff->colormap = (unsigned*) fz_malloc(count * sizeof(unsigned)); - if (!tiff->colormap) - return fz_throw("cannot allocate color map"); + tiff->colormap = fz_calloc(count, sizeof(unsigned)); xps_read_tiff_tag_value(tiff->colormap, tiff, type, value, count); break; @@ -827,49 +808,55 @@ xps_decode_tiff_header(xps_context *ctx, xps_tiff *tiff, byte *buf, int len) } int -xps_decode_tiff(xps_context *ctx, byte *buf, int len, xps_image *image) +xps_decode_tiff(xps_image **imagep, xps_context *ctx, byte *buf, int len) { int error; - xps_tiff tiffst; - xps_tiff *tiff = &tiffst; + fz_pixmap *pixmap; + xps_image *image; + xps_tiff tiff; - error = xps_decode_tiff_header(ctx, tiff, buf, len); + error = xps_decode_tiff_header(ctx, &tiff, buf, len); if (error) return fz_rethrow(error, "cannot decode tiff header"); - /* - * Decode the image strips - */ + /* Decode the image strips */ - if (tiff->rowsperstrip > tiff->imagelength) - tiff->rowsperstrip = tiff->imagelength; + if (tiff.rowsperstrip > tiff.imagelength) + tiff.rowsperstrip = tiff.imagelength; - error = xps_decode_tiff_strips(ctx, tiff, image); + error = xps_decode_tiff_strips(ctx, &tiff); if (error) return fz_rethrow(error, "cannot decode image data"); - /* - * Byte swap 16-bit images to big endian if necessary. - */ - if (image->bits == 16) + /* Byte swap 16-bit images to big endian if necessary */ + if (tiff.bitspersample == 16) { - if (tiff->order == TII) - xps_swap_byte_order(image->samples, image->width * image->height * image->comps); + if (tiff.order == TII) + xps_swap_byte_order(tiff.samples, tiff.imagewidth * tiff.imagelength * tiff.samplesperpixel); } - /* - * Save ICC profile data - */ - image->profile = tiff->profile; - image->profilesize = tiff->profilesize; + /* Expand into fz_pixmap struct */ - /* - * Clean up scratch memory - */ + pixmap = fz_newpixmap(tiff.colorspace, 0, 0, tiff.imagewidth, tiff.imagelength); + + fz_unpacktile(pixmap, tiff.samples, tiff.samplesperpixel, tiff.bitspersample, tiff.stride, 0); + + /* Non-pre-multiplied transparency */ + if (tiff.extrasamples == 2) + fz_premultiplypixmap(pixmap); + + image = fz_malloc(sizeof(xps_image)); + image->pixmap = pixmap; + image->xres = tiff.xresolution; + image->yres = tiff.yresolution; + + /* Clean up scratch memory */ - if (tiff->colormap) fz_free(tiff->colormap); - if (tiff->stripoffsets) fz_free(tiff->stripoffsets); - if (tiff->stripbytecounts) fz_free(tiff->stripbytecounts); + if (tiff.colormap) fz_free(tiff.colormap); + if (tiff.stripoffsets) fz_free(tiff.stripoffsets); + if (tiff.stripbytecounts) fz_free(tiff.stripbytecounts); + if (tiff.samples) fz_free(tiff.samples); + *imagep = image; return fz_okay; } -- cgit v1.2.3 From f158d944be90b6657c57f3ad850b814c1d5ade59 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Thu, 31 Mar 2011 03:19:27 +0200 Subject: xps: Draw linear and radial gradients (no pad/repeat/reflect). --- xps/xpsgradient.c | 346 +++++++++++++++++++++++------------------------------- xps/xpsopacity.c | 1 + 2 files changed, 147 insertions(+), 200 deletions(-) diff --git a/xps/xpsgradient.c b/xps/xpsgradient.c index 54516e72..5104ef1f 100644 --- a/xps/xpsgradient.c +++ b/xps/xpsgradient.c @@ -14,7 +14,7 @@ enum { SPREAD_PAD, SPREAD_REPEAT, SPREAD_REFLECT }; struct stop { float offset; - float color[4]; + float r, g, b, a; }; static int cmp_stop(const void *a, const void *b) @@ -43,7 +43,7 @@ xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xps_item *node, float rgb[3]; int before, after; int count; - int i, k; + int i; /* We may have to insert 2 extra stops when postprocessing */ maxcount -= 2; @@ -63,10 +63,10 @@ xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xps_item *node, fz_convertcolor(colorspace, sample + 1, fz_devicergb, rgb); - stops[count].color[0] = sample[0]; - stops[count].color[1] = rgb[0]; - stops[count].color[2] = rgb[1]; - stops[count].color[3] = rgb[2]; + stops[count].r = rgb[0]; + stops[count].g = rgb[1]; + stops[count].b = rgb[2]; + stops[count].a = sample[0]; count ++; } @@ -78,15 +78,15 @@ xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xps_item *node, { fz_warn("gradient brush has no gradient stops"); stops[0].offset = 0; - stops[0].color[0] = 1; - stops[0].color[1] = 0; - stops[0].color[2] = 0; - stops[0].color[3] = 0; + stops[0].r = 0; + stops[0].g = 0; + stops[0].b = 0; + stops[0].a = 1; stops[1].offset = 1; - stops[1].color[0] = 1; - stops[1].color[1] = 1; - stops[1].color[2] = 1; - stops[1].color[3] = 1; + stops[1].r = 1; + stops[1].g = 1; + stops[1].b = 1; + stops[1].a = 1; return 2; } @@ -136,8 +136,10 @@ xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xps_item *node, { float d = -stops[0].offset / (stops[1].offset - stops[0].offset); stops[0].offset = 0; - for (k = 0; k < 4; k++) - stops[0].color[k] = lerp(stops[0].color[k], stops[1].color[k], d); + stops[0].r = lerp(stops[0].r, stops[1].r, d); + stops[0].g = lerp(stops[0].g, stops[1].g, d); + stops[0].b = lerp(stops[0].b, stops[1].b, d); + stops[0].a = lerp(stops[0].a, stops[1].a, d); } /* Last stop > 1 -- interpolate value to 1 */ @@ -145,8 +147,10 @@ xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xps_item *node, { float d = (1 - stops[count-2].offset) / (stops[count-1].offset - stops[count-2].offset); stops[count-1].offset = 1; - for (k = 0; k < 4; k++) - stops[count-1].color[k] = lerp(stops[count-2].color[k], stops[count-1].color[k], d); + stops[0].r = lerp(stops[count-2].r, stops[count-1].r, d); + stops[0].g = lerp(stops[count-2].g, stops[count-1].g, d); + stops[0].b = lerp(stops[count-2].b, stops[count-1].b, d); + stops[0].a = lerp(stops[count-2].a, stops[count-1].a, d); } /* First stop > 0 -- insert a duplicate at 0 */ @@ -169,14 +173,26 @@ xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xps_item *node, return count; } -static int -xps_gradient_has_transparent_colors(struct stop *stops, int count) +static void +xps_sample_gradient_stops(fz_shade *shade, struct stop *stops, int count) { - int i; - for (i = 0; i < count; i++) - if (stops[i].color[0] < 1) - return 1; - return 0; + float offset, d; + int i, k; + + k = 0; + for (i = 0; i < 256; i++) + { + offset = i / 255.0f; + while (k + 1 < count && offset > stops[k+1].offset) + k++; + + d = (offset - stops[k].offset) / (stops[k+1].offset - stops[k].offset); + + shade->function[i][0] = lerp(stops[k].r, stops[k+1].r, d); + shade->function[i][1] = lerp(stops[k].g, stops[k+1].g, d); + shade->function[i][2] = lerp(stops[k].b, stops[k+1].b, d); + shade->function[i][3] = lerp(stops[k].a, stops[k+1].a, d); + } } /* @@ -187,49 +203,41 @@ xps_gradient_has_transparent_colors(struct stop *stops, int count) */ static void -xps_draw_one_radial_gradient(xps_context *ctx, - int extend, - float x0, float y0, float r0, - float x1, float y1, float r1) +xps_draw_one_radial_gradient(xps_context *ctx, fz_matrix ctm, + struct stop *stops, int count, + int extend, + float x0, float y0, float r0, + float x1, float y1, float r1) { -#if 0 - gs_memory *mem = ctx->memory; - gs_shading *shading; - gs_shading_R_params params; - int code; - - gs_shading_R_params_init(¶ms); - { - params.ColorSpace = ctx->srgb; - - params.Coords[0] = x0; - params.Coords[1] = y0; - params.Coords[2] = r0; - params.Coords[3] = x1; - params.Coords[4] = y1; - params.Coords[5] = r1; - - params.Extend[0] = extend; - params.Extend[1] = extend; - - params.Function = func; - } - - code = gs_shading_R_init(&shading, ¶ms, mem); - if (code < 0) - return fz_rethrow(code, "gs_shading_R_init failed"); - - gs_setsmoothness(ctx->pgs, 0.02); - - code = gs_shfill(ctx->pgs, shading); - if (code < 0) - { - gs_free_object(mem, shading, "gs_shading_R"); - return fz_rethrow(code, "gs_shfill failed"); - } - - gs_free_object(mem, shading, "gs_shading_R"); -#endif + fz_shade *shade; + + /* TODO: this (and the stuff in pdf_shade) should move to res_shade.c */ + shade = fz_malloc(sizeof(fz_shade)); + shade->refs = 1; + shade->cs = fz_devicergb; + shade->bbox = fz_infiniterect; + shade->matrix = fz_identity; + shade->usebackground = 0; + shade->usefunction = 1; + shade->type = FZ_RADIAL; + shade->extend[0] = extend; + shade->extend[1] = extend; + + xps_sample_gradient_stops(shade, stops, count); + + shade->meshlen = 6; + shade->meshcap = 6; + shade->mesh = fz_calloc(shade->meshcap, sizeof(float)); + shade->mesh[0] = x0; + shade->mesh[1] = y0; + shade->mesh[2] = r0; + shade->mesh[3] = x1; + shade->mesh[4] = y1; + shade->mesh[5] = r1; + + ctx->dev->fillshade(ctx->dev->user, shade, ctm, 1); + + fz_dropshade(shade); } /* @@ -237,46 +245,40 @@ xps_draw_one_radial_gradient(xps_context *ctx, */ static void -xps_draw_one_linear_gradient(xps_context *ctx, - int extend, - float x0, float y0, float x1, float y1) +xps_draw_one_linear_gradient(xps_context *ctx, fz_matrix ctm, + struct stop *stops, int count, + int extend, + float x0, float y0, float x1, float y1) { -#if 0 - gs_memory *mem = ctx->memory; - gs_shading *shading; - gs_shading_A_params params; - int code; - - gs_shading_A_params_init(¶ms); - { - params.ColorSpace = ctx->srgb; - - params.Coords[0] = x0; - params.Coords[1] = y0; - params.Coords[2] = x1; - params.Coords[3] = y1; - - params.Extend[0] = extend; - params.Extend[1] = extend; - - params.Function = func; - } - - code = gs_shading_A_init(&shading, ¶ms, mem); - if (code < 0) - return fz_rethrow(code, "gs_shading_A_init failed"); - - gs_setsmoothness(ctx->pgs, 0.02); - - code = gs_shfill(ctx->pgs, shading); - if (code < 0) - { - gs_free_object(mem, shading, "gs_shading_A"); - return fz_rethrow(code, "gs_shfill failed"); - } - - gs_free_object(mem, shading, "gs_shading_A"); -#endif + fz_shade *shade; + + /* TODO: this (and the stuff in pdf_shade) should move to res_shade.c */ + shade = fz_malloc(sizeof(fz_shade)); + shade->refs = 1; + shade->cs = fz_devicergb; + shade->bbox = fz_infiniterect; + shade->matrix = fz_identity; + shade->usebackground = 0; + shade->usefunction = 1; + shade->type = FZ_LINEAR; + shade->extend[0] = extend; + shade->extend[1] = extend; + + xps_sample_gradient_stops(shade, stops, count); + + shade->meshlen = 6; + shade->meshcap = 6; + shade->mesh = fz_calloc(shade->meshcap, sizeof(float)); + shade->mesh[0] = x0; + shade->mesh[1] = y0; + shade->mesh[2] = 0; + shade->mesh[3] = x1; + shade->mesh[4] = y1; + shade->mesh[5] = 0; + + ctx->dev->fillshade(ctx->dev->user, shade, ctm, 1); + + fz_dropshade(shade); } /* @@ -291,11 +293,13 @@ static inline float point_inside_circle(float px, float py, float x, float y, fl { float dx = px - x; float dy = py - y; - return (dx * dx + dy * dy) <= (r * r); + return dx * dx + dy * dy <= r * r; } static void -xps_draw_radial_gradient(xps_context *ctx, xps_item *root, int spread) +xps_draw_radial_gradient(xps_context *ctx, fz_matrix ctm, + struct stop *stops, int count, + xps_item *root, int spread) { fz_rect bbox; float x0, y0, r0; @@ -322,8 +326,7 @@ xps_draw_radial_gradient(xps_context *ctx, xps_item *root, int spread) yrad = atof(radius_y_att); /* scale the ctm to make ellipses */ -// gs_gsave(ctx->pgs); -// gs_scale(ctx->pgs, 1.0, yrad / xrad); + ctm = fz_concat(fz_scale(1.0, yrad / xrad), ctm); invscale = xrad / yrad; y0 = y0 * invscale; @@ -335,13 +338,15 @@ xps_draw_radial_gradient(xps_context *ctx, xps_item *root, int spread) dx = x1 - x0; dy = y1 - y0; + xps_draw_one_radial_gradient(ctx, ctm, stops, count, 0, x0, y0, r0, x1, y1, r1); + +#if 0 xps_bounds_in_user_space(ctx, &bbox); if (spread == SPREAD_PAD) { if (!point_inside_circle(x0, y0, x1, y1, r1)) { -#if 0 float in[1]; float out[4]; float fary[10]; @@ -376,18 +381,19 @@ xps_draw_radial_gradient(xps_context *ctx, xps_item *root, int spread) */ // reverse = xps_reverse_function(ctx, func, fary, vary); - - xps_draw_one_radial_gradient(ctx, reverse, 1, x1, y1, r1, x0, y0, r0); -#endif +// xps_draw_one_radial_gradient(ctx, reverse, 1, x1, y1, r1, x0, y0, r0); + xps_draw_one_radial_gradient(ctx, ctm, stops, count, + 1, x1, y1, r1, x0, y0, r0); } else { - xps_draw_one_radial_gradient(ctx, 1, x0, y0, r0, x1, y1, r1); + xps_draw_one_radial_gradient(ctx, ctm, stops, count, + 1, x0, y0, r0, x1, y1, r1); } } else { - for (i = 0; i < 100; i++) + for (i = 0; i < 2; i++) { /* Draw current circle */ @@ -395,12 +401,13 @@ xps_draw_radial_gradient(xps_context *ctx, xps_item *root, int spread) printf("xps: we should reverse gradient here too\n"); if (spread == SPREAD_REFLECT && (i & 1)) - xps_draw_one_radial_gradient(ctx, 0, x1, y1, r1, x0, y0, r0); + xps_draw_one_radial_gradient(ctx, ctm, stops, count, + 0, x1, y1, r1, x0, y0, r0); else - xps_draw_one_radial_gradient(ctx, 0, x0, y0, r0, x1, y1, r1); + xps_draw_one_radial_gradient(ctx, ctm, stops, count, + 0, x0, y0, r0, x1, y1, r1); /* Check if circle encompassed the entire bounding box (break loop if we do) */ - done = 1; if (!point_inside_circle(bbox.x0, bbox.y0, x1, y1, r1)) done = 0; if (!point_inside_circle(bbox.x0, bbox.y1, x1, y1, r1)) done = 0; @@ -420,6 +427,7 @@ xps_draw_radial_gradient(xps_context *ctx, xps_item *root, int spread) y1 += dy; } } +#endif } /* @@ -428,7 +436,9 @@ xps_draw_radial_gradient(xps_context *ctx, xps_item *root, int spread) */ static void -xps_draw_linear_gradient(xps_context *ctx, xps_item *root, int spread) +xps_draw_linear_gradient(xps_context *ctx, fz_matrix ctm, + struct stop *stops, int count, + xps_item *root, int spread) { fz_rect bbox; float x0, y0, x1, y1; @@ -451,11 +461,14 @@ xps_draw_linear_gradient(xps_context *ctx, xps_item *root, int spread) dx = x1 - x0; dy = y1 - y0; + xps_draw_one_linear_gradient(ctx, ctm, stops, count, 0, x0, y0, x1, y1); + +#if 0 xps_bounds_in_user_space(ctx, &bbox); if (spread == SPREAD_PAD) { - xps_draw_one_linear_gradient(ctx, 1, x0, y0, x1, y1); + xps_draw_one_linear_gradient(ctx, ctm, stops, count, 1, x0, y0, x1, y1); } else { @@ -488,15 +501,16 @@ xps_draw_linear_gradient(xps_context *ctx, xps_item *root, int spread) for (i = i0; i < i1; i++) { if (spread == SPREAD_REFLECT && (i & 1)) - xps_draw_one_linear_gradient(ctx, 0, + xps_draw_one_linear_gradient(ctx, ctm, stops, count, 0, x1 + dx * i, y1 + dy * i, x0 + dx * i, y0 + dy * i); else - xps_draw_one_linear_gradient(ctx, 0, + xps_draw_one_linear_gradient(ctx, ctm, stops, count, 0, x0 + dx * i, y0 + dy * i, x1 + dx * i, y1 + dy * i); } } +#endif } /* @@ -507,7 +521,7 @@ xps_draw_linear_gradient(xps_context *ctx, xps_item *root, int spread) static void xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root, - void (*draw)(xps_context *, xps_item *, int)) + void (*draw)(xps_context *, fz_matrix, struct stop *, int, xps_item *, int)) { xps_item *node; @@ -527,8 +541,6 @@ xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect bbox; - int has_opacity = 0; - opacity_att = xps_att(root, "Opacity"); interpolation_att = xps_att(root, "ColorInterpolationMode"); spread_att = xps_att(root, "SpreadMethod"); @@ -575,101 +587,35 @@ xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, } stop_count = xps_parse_gradient_stops(ctx, base_uri, stop_tag, stop_list, MAX_STOPS); - if (stop_count == 0) { + if (stop_count == 0) + { fz_warn("no gradient stops found"); return; } -/* - color_func = xps_create_gradient_stop_function(ctx, stop_list, stop_count, 0); - if (!color_func) - return fz_rethrow(-1, "cannot create color gradient function"); - - opacity_func = xps_create_gradient_stop_function(ctx, stop_list, stop_count, 1); - if (!opacity_func) - return fz_rethrow(-1, "cannot create opacity gradient function"); -*/ - - has_opacity = xps_gradient_has_transparent_colors(stop_list, stop_count); - xps_bounds_in_user_space(ctx, &bbox); -#if 0 - code = xps_begin_opacity(ctx, base_uri, dict, opacity_att, NULL); - if (code) - { - gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot create transparency group"); - } - - if (ctx->opacity_only) - { - code = draw(ctx, root, spread_method, opacity_func); - if (code) - { - gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot draw gradient opacity"); - } - } - else - { - if (has_opacity) - { - gs_transparency_mask_params params; - gs_transparency_group_params tgp; - - gs_trans_mask_params_init(¶ms, TRANSPARENCY_MASK_Luminosity); - gs_begin_transparency_mask(ctx->pgs, ¶ms, &bbox, 0); - code = draw(ctx, root, spread_method, opacity_func); - if (code) - { - gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); - gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot draw gradient opacity"); - } - gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity); + xps_begin_opacity(ctx, ctm, base_uri, dict, opacity_att, NULL); - gs_trans_group_params_init(&tgp); - gs_begin_transparency_group(ctx->pgs, &tgp, &bbox); - code = draw(ctx, root, spread_method, color_func); - if (code) - { - gs_end_transparency_group(ctx->pgs); - gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot draw gradient color"); - } - gs_end_transparency_group(ctx->pgs); - } - else - { - code = draw(ctx, root, spread_method, color_func); - if (code) - { - gs_grestore(ctx->pgs); - return fz_rethrow(code, "cannot draw gradient color"); - } - } - } -#endif + draw(ctx, ctm, stop_list, stop_count, root, spread_method); xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); - -// gs_grestore(ctx->pgs); - -// xps_free_gradient_stop_function(ctx, opacity_func); -// xps_free_gradient_stop_function(ctx, color_func); } void xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root) { + xps_clip(ctx, ctm); xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_linear_gradient); + ctx->dev->popclip(ctx->dev->user); } void xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root) { + xps_clip(ctx, ctm); xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_radial_gradient); + ctx->dev->popclip(ctx->dev->user); } diff --git a/xps/xpsopacity.c b/xps/xpsopacity.c index 19cb4ce3..6bd640b3 100644 --- a/xps/xpsopacity.c +++ b/xps/xpsopacity.c @@ -4,6 +4,7 @@ void xps_bounds_in_user_space(xps_context *ctx, fz_rect *ubox) { + *ubox = fz_infiniterect; // *evil grin* #if 0 gx_clip_path *clip_path; fz_rect dbox; -- cgit v1.2.3 From af6d16923fab4597e364c891fee2deb89998bdde Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Thu, 31 Mar 2011 15:04:30 +0200 Subject: xps: Fix bugs in TIFF reader. WhiteIsBlack was flipped for fax images. re-multiplying alpha with CMYK images needs special care because of subtractive colors. --- fitz/res_pixmap.c | 30 ++++++++++++++++++++++++------ xps/xpstiff.c | 10 ++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/fitz/res_pixmap.c b/fitz/res_pixmap.c index b37b7a3e..93fdb307 100644 --- a/fitz/res_pixmap.c +++ b/fitz/res_pixmap.c @@ -103,14 +103,32 @@ fz_premultiplypixmap(fz_pixmap *pix) unsigned char *s = pix->samples; unsigned char a; int k, x, y; - for (y = 0; y < pix->h; y++) + + /* special case for CMYK (subtractive colors) */ + if (pix->n == 5) + { + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + a = s[pix->n - 1]; + for (k = 0; k < pix->n - 1; k++) + s[k] = 255 - fz_mul255(255 - s[k], a); + s += pix->n; + } + } + } + else { - for (x = 0; x < pix->w; x++) + for (y = 0; y < pix->h; y++) { - a = s[pix->n - 1]; - for (k = 0; k < pix->n - 1; k++) - s[k] = fz_mul255(s[k], a); - s += pix->n; + for (x = 0; x < pix->w; x++) + { + a = s[pix->n - 1]; + for (k = 0; k < pix->n - 1; k++) + s[k] = fz_mul255(s[k], a); + s += pix->n; + } } } } diff --git a/xps/xpstiff.c b/xps/xpstiff.c index 4c9494f2..889635a8 100644 --- a/xps/xpstiff.c +++ b/xps/xpstiff.c @@ -187,7 +187,7 @@ xps_decode_tiff_fax(xps_context *ctx, xps_tiff *tiff, int comp, fz_stream *chain columns = fz_newint(tiff->imagewidth); rows = fz_newint(tiff->imagelength); - blackis1 = fz_newbool(tiff->photometric == 0); + blackis1 = fz_newbool(tiff->photometric != 0); k = fz_newint(comp == 4 ? -1 : 0); encodedbytealign = fz_newbool(comp == 2); @@ -324,8 +324,6 @@ xps_expand_tiff_colormap(xps_context *ctx, xps_tiff *tiff) stride = tiff->imagewidth * (tiff->samplesperpixel + 2); samples = fz_malloc(stride * tiff->imagelength); - if (!samples) - return fz_throw("out of memory: samples"); for (y = 0; y < tiff->imagelength; y++) { @@ -353,10 +351,10 @@ xps_expand_tiff_colormap(xps_context *ctx, xps_tiff *tiff) } } + tiff->samplesperpixel += 2; tiff->bitspersample = 8; tiff->stride = stride; tiff->samples = samples; - return fz_okay; } @@ -841,8 +839,8 @@ xps_decode_tiff(xps_image **imagep, xps_context *ctx, byte *buf, int len) fz_unpacktile(pixmap, tiff.samples, tiff.samplesperpixel, tiff.bitspersample, tiff.stride, 0); - /* Non-pre-multiplied transparency */ - if (tiff.extrasamples == 2) + /* We should only do this on non-pre-multiplied images, but files in the wild are bad */ + if (tiff.extrasamples /* == 2 */) fz_premultiplypixmap(pixmap); image = fz_malloc(sizeof(xps_image)); -- cgit v1.2.3 From e1101a5de25079fa42ac03e5abea48b0f382ec4e Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 1 Apr 2011 00:55:04 +0200 Subject: xps: Remove dependency on libpng. --- Makefile | 2 +- xps/xpspng.c | 627 ++++++++++++++++++++++++++++++++++++++++++++++++---------- xps/xpstiff.c | 25 ++- 3 files changed, 535 insertions(+), 119 deletions(-) diff --git a/Makefile b/Makefile index 4dfb0e96..23e0a281 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ endif # directory exists. LIBS := -lfreetype -ljbig2dec -lopenjpeg -ljpeg -lz -lm -XPSLIBS := -lexpat -lpng +XPSLIBS := -lexpat include Makerules include Makethird diff --git a/xps/xpspng.c b/xps/xpspng.c index 6af62e63..f59be56d 100644 --- a/xps/xpspng.c +++ b/xps/xpspng.c @@ -1,162 +1,579 @@ #include "fitz.h" #include "muxps.h" -#include +#include -struct xps_png_io_s +struct info { - byte *ptr; - byte *lim; + int width, height, depth, n; + int interlace, indexed; + int size; + unsigned char *samples; + unsigned char palette[256*4]; + int transparency; + int trns[3]; + int xres, yres; }; +static inline int getint(unsigned char *p) +{ + return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +} + +static inline int +getcomp(unsigned char *line, int x, int bpc) +{ + switch (bpc) + { + case 1: return (line[x >> 3] >> ( 7 - (x & 7) ) ) & 1; + case 2: return (line[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3; + case 4: return (line[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15; + case 8: return line[x]; + case 16: return line[x << 1] << 8 | line[(x << 1) + 1]; + } + return 0; +} + +static inline void +putcomp(unsigned char *line, int x, int bpc, int value) +{ + int maxval = (1 << bpc) - 1; + + switch (bpc) + { + case 1: line[x >> 3] &= ~(maxval << (7 - (x & 7))); break; + case 2: line[x >> 2] &= ~(maxval << ((3 - (x & 3)) << 1)); break; + case 4: line[x >> 1] &= ~(maxval << ((1 - (x & 1)) << 2)); break; + } + + switch (bpc) + { + case 1: line[x >> 3] |= value << (7 - (x & 7)); break; + case 2: line[x >> 2] |= value << ((3 - (x & 3)) << 1); break; + case 4: line[x >> 1] |= value << ((1 - (x & 1)) << 2); break; + case 8: line[x] = value; break; + case 16: line[x << 1] = value >> 8; line[(x << 1) + 1] = value & 0xFF; break; + } +} + +static const unsigned char png_signature[8] = +{ + 137, 80, 78, 71, 13, 10, 26, 10 +}; + +static void *zalloc(void *opaque, unsigned int items, unsigned int size) +{ + return fz_calloc(items, size); +} + +static void zfree(void *opaque, void *address) +{ + fz_free(address); +} + +static inline int paeth(int a, int b, int c) +{ + /* The definitions of ac and bc are correct, not a typo. */ + int ac = b - c, bc = a - c, abcc = ac + bc; + int pa = (ac < 0 ? -ac : ac); + int pb = (bc < 0 ? -bc : bc); + int pc = (abcc < 0 ? -abcc : abcc); + return pa <= pb && pa <= pc ? a : pb <= pc ? b : c; +} + static void -xps_png_read(png_structp png, png_bytep data, png_size_t length) +png_predict(unsigned char *samples, int width, int height, int n, int depth) { - struct xps_png_io_s *io = png_get_io_ptr(png); - if (io->ptr + length > io->lim) - png_error(png, "Read Error"); - memcpy(data, io->ptr, length); - io->ptr += length; + int stride = (width * n * depth + 7) / 8; + int bpp = (n * depth + 7) / 8; + int i, row; + + for (row = 0; row < height; row ++) + { + unsigned char *src = samples + (stride + 1) * row; + unsigned char *dst = samples + stride * row; + + unsigned char *a = dst; + unsigned char *b = dst - stride; + unsigned char *c = dst - stride; + + switch (*src++) + { + default: + case 0: /* None */ + for (i = 0; i < stride; i++) + *dst++ = *src++; + break; + + case 1: /* Sub */ + for (i = 0; i < bpp; i++) + *dst++ = *src++; + for (i = bpp; i < stride; i++) + *dst++ = *src++ + *a++; + break; + + case 2: /* Up */ + if (row == 0) + for (i = 0; i < stride; i++) + *dst++ = *src++; + else + for (i = 0; i < stride; i++) + *dst++ = *src++ + *b++; + break; + + case 3: /* Average */ + if (row == 0) + { + for (i = 0; i < bpp; i++) + *dst++ = *src++; + for (i = bpp; i < stride; i++) + *dst++ = *src++ + (*a++ >> 1); + } + else + { + for (i = 0; i < bpp; i++) + *dst++ = *src++ + (*b++ >> 1); + for (i = bpp; i < stride; i++) + *dst++ = *src++ + ((*b++ + *a++) >> 1); + } + break; + + case 4: /* Paeth */ + if (row == 0) + { + for (i = 0; i < bpp; i++) + *dst++ = *src++ + paeth(0, 0, 0); + for (i = bpp; i < stride; i++) + *dst++ = *src++ + paeth(*a++, 0, 0); + } + else + { + for (i = 0; i < bpp; i++) + *dst++ = *src++ + paeth(0, *b++, 0); + for (i = bpp; i < stride; i++) + *dst++ = *src++ + paeth(*a++, *b++, *c++); + } + break; + } + } } -static png_voidp -xps_png_malloc(png_structp png, png_size_t size) +static const int adam7_ix[7] = { 0, 4, 0, 2, 0, 1, 0 }; +static const int adam7_dx[7] = { 8, 8, 4, 4, 2, 2, 1 }; +static const int adam7_iy[7] = { 0, 0, 4, 0, 2, 0, 1 }; +static const int adam7_dy[7] = { 8, 8, 8, 4, 4, 2, 2 }; + +static void +png_deinterlace_passes(struct info *info, int *w, int *h, int *ofs) { - return fz_malloc(size); + int p, bpp = info->depth * info->n; + ofs[0] = 0; + for (p = 0; p < 7; p++) + { + w[p] = (info->width + adam7_dx[p] - adam7_ix[p] - 1) / adam7_dx[p]; + h[p] = (info->height + adam7_dy[p] - adam7_iy[p] - 1) / adam7_dy[p]; + if (w[p] == 0) h[p] = 0; + if (h[p] == 0) w[p] = 0; + if (w[p] && h[p]) + ofs[p + 1] = ofs[p] + h[p] * (1 + (w[p] * bpp + 7) / 8); + else + ofs[p + 1] = ofs[p]; + } } static void -xps_png_free(png_structp png, png_voidp ptr) +png_deinterlace(struct info *info, int *passw, int *passh, int *passofs) { - fz_free(ptr); + int n = info->n; + int depth = info->depth; + int stride = (info->width * n * depth + 7) / 8; + unsigned char *output; + int p, x, y, k; + + output = fz_calloc(info->height, stride); + + for (p = 0; p < 7; p++) + { + unsigned char *sp = info->samples + passofs[p]; + int w = passw[p]; + int h = passh[p]; + + png_predict(sp, w, h, n, depth); + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + int outx = x * adam7_dx[p] + adam7_ix[p]; + int outy = y * adam7_dy[p] + adam7_iy[p]; + unsigned char *dp = output + outy * stride; + for (k = 0; k < n; k++) + { + int v = getcomp(sp, x * n + k, depth); + putcomp(dp, outx * n + k, depth, v); + } + } + sp += (w * depth * n + 7) / 8; + } + } + + fz_free(info->samples); + info->samples = output; } -int -xps_decode_png(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen) +static int +png_read_ihdr(struct info *info, unsigned char *p, int size) { - png_structp png; - png_infop info; - struct xps_png_io_s io; - int width, height, stride, premul; - int npasses; - int pass; - int y; + int color, compression, filter; + + if (size != 13) + return fz_throw("IHDR chunk is the wrong size"); + + info->width = getint(p + 0); + info->height = getint(p + 4); + info->depth = p[8]; - fz_pixmap *pixmap = NULL; - xps_image *image = NULL; + color = p[9]; + compression = p[10]; + filter = p[11]; + info->interlace = p[12]; - /* - * Set up PNG structs and input source - */ + if (info->width <= 0) + return fz_throw("image width must be > 0"); + if (info->height <= 0) + return fz_throw("image height must be > 0"); - io.ptr = rbuf; - io.lim = rbuf + rlen; + if (info->depth != 1 && info->depth != 2 && info->depth != 4 && + info->depth != 8 && info->depth != 16) + return fz_throw("image bit depth must be one of 1, 2, 4, 8, 16"); + if (color == 2 && info->depth < 8) + return fz_throw("illegal bit depth for truecolor"); + if (color == 3 && info->depth > 8) + return fz_throw("illegal bit depth for indexed"); + if (color == 4 && info->depth < 8) + return fz_throw("illegal bit depth for grayscale with alpha"); + if (color == 6 && info->depth < 8) + return fz_throw("illegal bit depth for truecolor with alpha"); - png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, - NULL, NULL, NULL, - ctx, xps_png_malloc, xps_png_free); - if (!png) - return fz_throw("png_create_read_struct"); + info->indexed = 0; + if (color == 0) /* gray */ + info->n = 1; + else if (color == 2) /* rgb */ + info->n = 3; + else if (color == 4) /* gray alpha */ + info->n = 2; + else if (color == 6) /* rgb alpha */ + info->n = 4; + else if (color == 3) /* indexed */ + { + info->indexed = 1; + info->n = 1; + } + else + return fz_throw("unknown color type"); - info = png_create_info_struct(png); - if (!info) - return fz_throw("png_create_info_struct"); + if (compression != 0) + return fz_throw("unknown compression method"); + if (filter != 0) + return fz_throw("unknown filter method"); + if (info->interlace != 0 && info->interlace != 1) + return fz_throw("interlace method not supported"); + + return fz_okay; +} - png_set_read_fn(png, &io, xps_png_read); - png_set_crc_action(png, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE); +static int +png_read_plte(struct info *info, unsigned char *p, int size) +{ + int n = size / 3; + int i; - /* - * Jump to here on errors. - */ + if (n > 256 || n > (1 << info->depth)) + return fz_throw("too many samples in palette"); - if (setjmp(png_jmpbuf(png))) + for (i = 0; i < n; i++) { - if (pixmap) - fz_droppixmap(pixmap); - if (image) - fz_free(image); - png_destroy_read_struct(&png, &info, NULL); - return fz_throw("cannot read png image"); + info->palette[i * 4] = p[i * 3]; + info->palette[i * 4 + 1] = p[i * 3 + 1]; + info->palette[i * 4 + 2] = p[i * 3 + 2]; } - /* - * Read PNG header - */ + return fz_okay; +} + +static int +png_read_trns(struct info *info, unsigned char *p, int size) +{ + int i; - png_read_info(png, info); + info->transparency = 1; - if (png_get_interlace_type(png, info) == PNG_INTERLACE_ADAM7) - npasses = png_set_interlace_handling(png); + if (info->indexed) + { + if (size > 256 || size > (1 << info->depth)) + return fz_throw("too many samples in transparency table"); + for (i = 0; i < size; i++) + info->palette[i * 4 + 3] = p[i]; + } else - npasses = 1; + { + if (size != info->n * 2) + return fz_throw("tRNS chunk is the wrong size"); + for (i = 0; i < info->n; i++) + info->trns[i] = (p[i * 2] << 8 | p[i * 2 + 1]) & ((1 << info->depth) - 1); + } - png_set_expand(png); - png_set_packing(png); - png_set_strip_16(png); + return fz_okay; +} + +static int +png_read_idat(struct info *info, unsigned char *p, int size, z_stream *stm) +{ + int code; + + stm->next_in = p; + stm->avail_in = size; - premul = 0; - if (png_get_color_type(png, info) == PNG_COLOR_TYPE_GRAY) - png_set_add_alpha(png, 0xff, PNG_FILLER_AFTER); - else if (png_get_color_type(png, info) == PNG_COLOR_TYPE_RGB) - png_set_add_alpha(png, 0xff, PNG_FILLER_AFTER); + code = inflate(stm, Z_SYNC_FLUSH); + if (code != Z_OK && code != Z_STREAM_END) + return fz_throw("zlib error: %s", stm->msg); + if (stm->avail_in != 0) + { + if (stm->avail_out == 0) + return fz_throw("ran out of output before input"); + return fz_throw("inflate did not consume buffer (%d remaining)", stm->avail_in); + } + + return fz_okay; +} + +static int +png_read_phys(struct info *info, unsigned char *p, int size) +{ + if (size != 9) + return fz_throw("pHYs chunk is the wrong size"); + if (p[8] == 1) + { + info->xres = getint(p) * 254 / 10000; + info->yres = getint(p + 4) * 254 / 10000; + } + return fz_okay; +} + +static int +png_read_image(struct info *info, unsigned char *p, int total) +{ + int passw[7], passh[7], passofs[8]; + int code, size; + z_stream stm; + + memset(info, 0, sizeof (struct info)); + memset(info->palette, 255, sizeof(info->palette)); + info->xres = 96; + info->yres = 96; + + /* Read signature */ + + if (total < 8 + 12 || memcmp(p, png_signature, 8)) + return fz_throw("not a png image (wrong signature)"); + + p += 8; + total -= 8; + + /* Read IHDR chunk (must come first) */ + + size = getint(p); + + if (size + 12 > total) + return fz_throw("premature end of data in png image"); + + if (!memcmp(p + 4, "IHDR", 4)) + { + code = png_read_ihdr(info, p + 8, size); + if (code) + return fz_rethrow(code, "cannot read png header"); + } else - premul = 1; + return fz_throw("png file must start with IHDR chunk"); - png_read_update_info(png, info); + p += size + 12; + total -= size + 12; - width = png_get_image_width(png, info); - height = png_get_image_height(png, info); - stride = png_get_rowbytes(png, info); + /* Prepare output buffer */ - switch (png_get_color_type(png, info)) + if (!info->interlace) + { + info->size = info->height * (1 + (info->width * info->n * info->depth + 7) / 8); + } + else { - case PNG_COLOR_TYPE_GRAY_ALPHA: - pixmap = fz_newpixmap(fz_devicegray, 0, 0, width, height); - break; - case PNG_COLOR_TYPE_RGB_ALPHA: - pixmap = fz_newpixmap(fz_devicergb, 0, 0, width, height); - break; - default: - return fz_throw("cannot handle this png color type"); + png_deinterlace_passes(info, passw, passh, passofs); + info->size = passofs[7]; } - image = fz_malloc(sizeof(xps_image)); - image->pixmap = pixmap; - image->xres = 96; - image->yres = 96; + info->samples = fz_malloc(info->size); + + stm.zalloc = zalloc; + stm.zfree = zfree; + stm.opaque = NULL; + + stm.next_out = info->samples; + stm.avail_out = info->size; - /* - * Extract DPI, default to 96 dpi - */ + code = inflateInit(&stm); + if (code != Z_OK) + return fz_throw("zlib error: %s", stm.msg); - if (info->valid & PNG_INFO_pHYs) + /* Read remaining chunks until IEND */ + + while (total > 8) { - png_uint_32 xres, yres; - int unit; - png_get_pHYs(png, info, &xres, &yres, &unit); - if (unit == PNG_RESOLUTION_METER) + size = getint(p); + + if (size + 12 > total) + return fz_throw("premature end of data in png image"); + + if (!memcmp(p + 4, "PLTE", 4)) + { + code = png_read_plte(info, p + 8, size); + if (code) + return fz_rethrow(code, "cannot read png palette"); + } + + if (!memcmp(p + 4, "tRNS", 4)) + { + code = png_read_trns(info, p + 8, size); + if (code) + return fz_rethrow(code, "cannot read png transparency"); + } + + if (!memcmp(p + 4, "pHYs", 4)) + { + code = png_read_phys(info, p + 8, size); + if (code) + return fz_rethrow(code, "cannot read png resolution"); + } + + if (!memcmp(p + 4, "IDAT", 4)) { - image->xres = xres * 0.0254 + 0.5; - image->yres = yres * 0.0254 + 0.5; + code = png_read_idat(info, p + 8, size, &stm); + if (code) + return fz_rethrow(code, "cannot read png image data"); } + + if (!memcmp(p + 4, "IEND", 4)) + break; + + p += size + 12; + total -= size + 12; } - /* - * Read rows, filling transformed output into image buffer. - */ + code = inflateEnd(&stm); + if (code != Z_OK) + return fz_throw("zlib error: %s", stm.msg); - for (pass = 0; pass < npasses; pass++) - for (y = 0; y < height; y++) - png_read_row(png, pixmap->samples + (y * stride), NULL); + /* Apply prediction filter and deinterlacing */ - if (premul) + if (!info->interlace) + png_predict(info->samples, info->width, info->height, info->n, info->depth); + else + png_deinterlace(info, passw, passh, passofs); + + return fz_okay; +} + +static fz_pixmap * +png_expand_palette(struct info *info, fz_pixmap *src) +{ + fz_pixmap *dst = fz_newpixmap(fz_devicergb, 0, 0, src->w, src->h); + unsigned char *sp = src->samples; + unsigned char *dp = dst->samples; + int x, y; + + for (y = 0; y < info->height; y++) + { + for (x = 0; x < info->width; x++) + { + int v = *sp << 2; + *dp++ = info->palette[v]; + *dp++ = info->palette[v + 1]; + *dp++ = info->palette[v + 2]; + *dp++ = info->palette[v + 3]; + sp += 2; + } + } + + fz_droppixmap(src); + return dst; +} + +static void +png_mask_transparency(struct info *info, fz_pixmap *dst) +{ + int stride = (info->width * info->n * info->depth + 7) / 8; + int depth = info->depth; + int n = info->n; + int x, y, k, t; + + for (y = 0; y < info->height; y++) + { + unsigned char *sp = info->samples + y * stride; + unsigned char *dp = dst->samples + y * dst->w * dst->n; + for (x = 0; x < info->width; x++) + { + t = 1; + for (k = 0; k < n; k++) + if (getcomp(sp, x * n + k, depth) != info->trns[k]) + t = 0; + if (t) + dp[x * dst->n + dst->n - 1] = 0; + } + } +} + +int +xps_decode_png(xps_image **imagep, xps_context *ctx, byte *p, int total) +{ + fz_pixmap *pixmap; + fz_colorspace *colorspace; + xps_image *image; + struct info png; + int code; + int stride; + + code = png_read_image(&png, p, total); + if (code) + return fz_rethrow(code, "cannot read png image"); + + if (png.n == 3 || png.n == 4) + colorspace = fz_devicergb; + else + colorspace = fz_devicegray; + + stride = (png.width * png.n * png.depth + 7) / 8; + + pixmap = fz_newpixmap(colorspace, 0, 0, png.width, png.height); + fz_unpacktile(pixmap, png.samples, png.n, png.depth, stride, png.indexed); + + if (png.indexed) + { + pixmap = png_expand_palette(&png, pixmap); + } + else if (png.transparency) + { + png_mask_transparency(&png, pixmap); + } + + if (png.transparency || png.n == 2 || png.n == 4) + { fz_premultiplypixmap(pixmap); + } - /* - * Clean up memory. - */ + fz_free(png.samples); - png_destroy_read_struct(&png, &info, NULL); + image = fz_malloc(sizeof(xps_image)); + image->pixmap = pixmap; + image->xres = png.xres; + image->yres = png.yres; *imagep = image; return fz_okay; diff --git a/xps/xpstiff.c b/xps/xpstiff.c index 889635a8..4c977ab1 100644 --- a/xps/xpstiff.c +++ b/xps/xpstiff.c @@ -187,7 +187,7 @@ xps_decode_tiff_fax(xps_context *ctx, xps_tiff *tiff, int comp, fz_stream *chain columns = fz_newint(tiff->imagewidth); rows = fz_newint(tiff->imagelength); - blackis1 = fz_newbool(tiff->photometric != 0); + blackis1 = fz_newbool(tiff->photometric == 0); k = fz_newint(comp == 4 ? -1 : 0); encodedbytealign = fz_newbool(comp == 2); @@ -230,11 +230,11 @@ getcomp(byte *line, int x, int bpc) { switch (bpc) { - case 1: return line[x / 8] >> (7 - (x % 8)) & 0x01; - case 2: return line[x / 4] >> ((3 - (x % 4)) * 2) & 0x03; - case 4: return line[x / 2] >> ((1 - (x % 2)) * 4) & 0x0f; + case 1: return (line[x >> 3] >> ( 7 - (x & 7) ) ) & 1; + case 2: return (line[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3; + case 4: return (line[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15; case 8: return line[x]; - case 16: return ((line[x * 2 + 0]) << 8) | (line[x * 2 + 1]); + case 16: return line[x << 1] << 8 | line[(x << 1) + 1]; } return 0; } @@ -244,21 +244,20 @@ putcomp(byte *line, int x, int bpc, int value) { int maxval = (1 << bpc) - 1; - // clear bits first switch (bpc) { - case 1: line[x / 8] &= ~(maxval << (7 - (x % 8))); break; - case 2: line[x / 4] &= ~(maxval << ((3 - (x % 4)) * 2)); break; - case 4: line[x / 2] &= ~(maxval << ((1 - (x % 2)) * 4)); break; + case 1: line[x >> 3] &= ~(maxval << (7 - (x & 7))); break; + case 2: line[x >> 2] &= ~(maxval << ((3 - (x & 3)) << 1)); break; + case 4: line[x >> 1] &= ~(maxval << ((1 - (x & 1)) << 2)); break; } switch (bpc) { - case 1: line[x / 8] |= value << (7 - (x % 8)); break; - case 2: line[x / 4] |= value << ((3 - (x % 4)) * 2); break; - case 4: line[x / 2] |= value << ((1 - (x % 2)) * 4); break; + case 1: line[x >> 3] |= value << (7 - (x & 7)); break; + case 2: line[x >> 2] |= value << ((3 - (x & 3)) << 1); break; + case 4: line[x >> 1] |= value << ((1 - (x & 1)) << 2); break; case 8: line[x] = value; break; - case 16: line[x * 2 + 0] = value >> 8; line[x * 2 + 1] = value & 0xFF; break; + case 16: line[x << 1] = value >> 8; line[(x << 1) + 1] = value & 0xFF; break; } } -- cgit v1.2.3 From 9c9674b3081bfd433460be934e2a792390474367 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 1 Apr 2011 02:09:01 +0200 Subject: xps: fix clip/popclip nesting --- xps/xpscolor.c | 26 -------------------------- xps/xpscommon.c | 5 ++--- xps/xpsglyphs.c | 7 +++++++ xps/xpsgradient.c | 10 ++-------- xps/xpspath.c | 23 +++++++---------------- xps/xpstile.c | 4 ---- 6 files changed, 18 insertions(+), 57 deletions(-) diff --git a/xps/xpscolor.c b/xps/xpscolor.c index bb2f225f..b0678603 100644 --- a/xps/xpscolor.c +++ b/xps/xpscolor.c @@ -199,29 +199,3 @@ xps_read_icc_colorspace(xps_context *ctx, char *base_uri, char *profilename) return NULL; #endif } - -void -xps_parse_solid_color_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node) -{ - char *opacity_att; - char *color_att; - fz_colorspace *colorspace; - float samples[32]; - - color_att = xps_att(node, "Color"); - opacity_att = xps_att(node, "Opacity"); - - colorspace = fz_devicergb; - samples[0] = 1.0; - samples[1] = 0.0; - samples[2] = 0.0; - samples[3] = 0.0; - - if (color_att) - xps_parse_color(ctx, base_uri, color_att, &colorspace, samples); - if (opacity_att) - samples[0] = atof(opacity_att); - - xps_set_color(ctx, colorspace, samples); - xps_fill(ctx, ctm); -} diff --git a/xps/xpscommon.c b/xps/xpscommon.c index 2d46caa4..d338ffaa 100644 --- a/xps/xpscommon.c +++ b/xps/xpscommon.c @@ -4,9 +4,8 @@ void xps_parse_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node) { - if (!strcmp(xps_tag(node), "SolidColorBrush")) - xps_parse_solid_color_brush(ctx, ctm, base_uri, dict, node); - else if (!strcmp(xps_tag(node), "ImageBrush")) + /* SolidColorBrushes are handled in a special case and will never show up here */ + if (!strcmp(xps_tag(node), "ImageBrush")) xps_parse_image_brush(ctx, ctm, base_uri, dict, node); else if (!strcmp(xps_tag(node), "VisualBrush")) xps_parse_visual_brush(ctx, ctm, base_uri, dict, node); diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index d30f016b..372cf222 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -497,7 +497,14 @@ xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, xps_parse_glyphs_imp(ctx, ctm, font, font_size, atof(origin_x_att), atof(origin_y_att), is_sideways, bidi_level, indices_att, unicode_att, 1); + + ctx->dev->cliptext(ctx->dev->user, ctx->text, ctm, 0); + fz_freetext(ctx->text); + ctx->text = nil; + xps_parse_brush(ctx, ctm, fill_uri, dict, fill_tag); + + ctx->dev->popclip(ctx->dev->user); } xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); diff --git a/xps/xpsgradient.c b/xps/xpsgradient.c index 5104ef1f..c1b45e93 100644 --- a/xps/xpsgradient.c +++ b/xps/xpsgradient.c @@ -338,7 +338,7 @@ xps_draw_radial_gradient(xps_context *ctx, fz_matrix ctm, dx = x1 - x0; dy = y1 - y0; - xps_draw_one_radial_gradient(ctx, ctm, stops, count, 0, x0, y0, r0, x1, y1, r1); + xps_draw_one_radial_gradient(ctx, ctm, stops, count, 1, x0, y0, r0, x1, y1, r1); #if 0 xps_bounds_in_user_space(ctx, &bbox); @@ -461,7 +461,7 @@ xps_draw_linear_gradient(xps_context *ctx, fz_matrix ctm, dx = x1 - x0; dy = y1 - y0; - xps_draw_one_linear_gradient(ctx, ctm, stops, count, 0, x0, y0, x1, y1); + xps_draw_one_linear_gradient(ctx, ctm, stops, count, 1, x0, y0, x1, y1); #if 0 xps_bounds_in_user_space(ctx, &bbox); @@ -572,8 +572,6 @@ xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, spread_method = SPREAD_REPEAT; } - xps_clip(ctx, ctm); - transform = fz_identity; if (transform_att) xps_parse_render_transform(ctx, transform_att, &transform); @@ -606,16 +604,12 @@ void xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root) { - xps_clip(ctx, ctm); xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_linear_gradient); - ctx->dev->popclip(ctx->dev->user); } void xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root) { - xps_clip(ctx, ctm); xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_radial_gradient); - ctx->dev->popclip(ctx->dev->user); } diff --git a/xps/xpspath.c b/xps/xpspath.c index 86d37197..d442698d 100644 --- a/xps/xpspath.c +++ b/xps/xpspath.c @@ -38,22 +38,9 @@ fz_currentpoint(fz_path *path) void xps_clip(xps_context *ctx, fz_matrix ctm) { - if (ctx->path) - { - ctx->dev->clippath(ctx->dev->user, ctx->path, ctx->fill_rule == 0, ctm); - fz_freepath(ctx->path); - ctx->path = NULL; - } - else if (ctx->text) - { - ctx->dev->cliptext(ctx->dev->user, ctx->text, ctm, 0); - fz_freetext(ctx->text); - ctx->text = nil; - } - else - { - fz_warn("clip not a path nor text"); - } + ctx->dev->clippath(ctx->dev->user, ctx->path, ctx->fill_rule == 0, ctm); + fz_freepath(ctx->path); + ctx->path = NULL; } void @@ -984,7 +971,11 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di if (data_tag) xps_parse_path_geometry(ctx, dict, data_tag, 0); + xps_clip(ctx, ctm); + xps_parse_brush(ctx, ctm, fill_uri, dict, fill_tag); + + ctx->dev->popclip(ctx->dev->user); } if (stroke_att) diff --git a/xps/xpstile.c b/xps/xpstile.c index db6e6655..baa274e6 100644 --- a/xps/xpstile.c +++ b/xps/xpstile.c @@ -148,8 +148,6 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, tile_mode = TILE_FLIP_X_Y; } - xps_clip(ctx, ctm); - xps_begin_opacity(ctx, ctm, base_uri, dict, opacity_att, NULL); ctm = fz_concat(transform, ctm); @@ -180,6 +178,4 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, } xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); - - ctx->dev->popclip(ctx->dev->user); } -- cgit v1.2.3 From 7c6ae0b110d0a29558305878482023ad7e8a66dc Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 1 Apr 2011 02:24:08 +0200 Subject: xps: Respect PathGeometry.Transform attribute. --- fitz/fitz.h | 2 ++ fitz/res_path.c | 36 ++++++++++++++++++++++++++++++++++++ xps/xpspath.c | 11 +++-------- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/fitz/fitz.h b/fitz/fitz.h index 5d3ba1c1..5cff40b8 100644 --- a/fitz/fitz.h +++ b/fitz/fitz.h @@ -830,6 +830,8 @@ void fz_curvetoy(fz_path*, float, float, float, float); void fz_closepath(fz_path*); void fz_freepath(fz_path *path); +void fz_transformpath(fz_path *path, fz_matrix transform); + fz_path *fz_clonepath(fz_path *old); fz_rect fz_boundpath(fz_path *path, fz_strokestate *stroke, fz_matrix ctm); diff --git a/fitz/res_path.c b/fitz/res_path.c index ebfb3cdc..b4ac415a 100644 --- a/fitz/res_path.c +++ b/fitz/res_path.c @@ -170,6 +170,42 @@ fz_boundpath(fz_path *path, fz_strokestate *stroke, fz_matrix ctm) return r; } +void +fz_transformpath(fz_path *path, fz_matrix ctm) +{ + fz_point p; + int k, i = 0; + + while (i < path->len) + { + switch (path->els[i++].k) + { + case FZ_CURVETO: + for (k = 0; k < 3; k++) + { + p.x = path->els[i].v; + p.y = path->els[i+1].v; + p = fz_transformpoint(ctm, p); + path->els[i].v = p.x; + path->els[i+1].v = p.y; + i += 2; + } + break; + case FZ_MOVETO: + case FZ_LINETO: + p.x = path->els[i].v; + p.y = path->els[i+1].v; + p = fz_transformpoint(ctm, p); + path->els[i].v = p.x; + path->els[i+1].v = p.y; + i += 2; + break; + case FZ_CLOSEPATH: + break; + } + } +} + void fz_debugpath(fz_path *path, int indent) { diff --git a/xps/xpspath.c b/xps/xpspath.c index d442698d..dd0d2ef3 100644 --- a/xps/xpspath.c +++ b/xps/xpspath.c @@ -741,25 +741,20 @@ xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, in xps_parse_render_transform(ctx, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); - // TODO: apply matrix - if (transform_att || transform_tag) - fz_warn("ignoring PathGeometry.Transform attribute"); if (figures_att) - { xps_parse_abbreviated_geometry(ctx, figures_att); - } - if (figures_tag) - { xps_parse_path_figure(ctx, figures_tag, stroking); - } for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "PathFigure")) xps_parse_path_figure(ctx, node, stroking); } + + if (transform_att || transform_tag) + fz_transformpath(ctx->path, transform); } static int -- cgit v1.2.3 From 5a18d4874159ba759863a3dc29ef11fcd23924f9 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 1 Apr 2011 03:50:43 +0200 Subject: xps: Use opacity masks, and draw gradients with opacity. --- draw/meshdraw.c | 3 ++- fitz/fitz.h | 2 +- mupdf/pdf_shade.c | 2 ++ xps/muxps.h | 10 ---------- xps/xpscolor.c | 18 ++++-------------- xps/xpsopacity.c | 20 ++++---------------- xps/xpszip.c | 5 ----- 7 files changed, 13 insertions(+), 47 deletions(-) diff --git a/draw/meshdraw.c b/draw/meshdraw.c index 80b80378..71c0bdb7 100644 --- a/draw/meshdraw.c +++ b/draw/meshdraw.c @@ -540,6 +540,7 @@ fz_paintshade(fz_shade *shade, fz_matrix ctm, fz_pixmap *dest, fz_bbox bbox) fz_convertcolor(shade->cs, shade->function[i], dest->colorspace, color); for (k = 0; k < dest->colorspace->n; k++) clut[i][k] = color[k] * 255; + clut[i][k] = shade->function[i][shade->cs->n] * 255; } conv = fz_newpixmapwithrect(dest->colorspace, bbox); temp = fz_newpixmapwithrect(fz_devicegray, bbox); @@ -565,7 +566,7 @@ fz_paintshade(fz_shade *shade, fz_matrix ctm, fz_pixmap *dest, fz_bbox bbox) while (len--) { int v = *s++; - int a = *s++; + int a = fz_mul255(*s++, clut[v][conv->n - 1]); for (k = 0; k < conv->n - 1; k++) *d++ = fz_mul255(clut[v][k], a); *d++ = a; diff --git a/fitz/fitz.h b/fitz/fitz.h index 5cff40b8..f8bef4c3 100644 --- a/fitz/fitz.h +++ b/fitz/fitz.h @@ -900,7 +900,7 @@ struct fz_shade_s float background[FZ_MAXCOLORS]; int usefunction; - float function[256][FZ_MAXCOLORS]; + float function[256][FZ_MAXCOLORS + 1]; int type; /* linear, radial, mesh */ int extend[2]; diff --git a/mupdf/pdf_shade.c b/mupdf/pdf_shade.c index 1242c6c8..9c4d8d09 100644 --- a/mupdf/pdf_shade.c +++ b/mupdf/pdf_shade.c @@ -332,6 +332,7 @@ pdf_samplecompositeshadefunction(fz_shade *shade, pdf_function *func, float t0, { t = t0 + (i / 255.0f) * (t1 - t0); pdf_evalfunction(func, &t, 1, shade->function[i], shade->cs->n); + shade->function[i][shade->cs->n] = 1; } } @@ -346,6 +347,7 @@ pdf_samplecomponentshadefunction(fz_shade *shade, int funcs, pdf_function **func t = t0 + (i / 255.0f) * (t1 - t0); for (k = 0; k < funcs; k++) pdf_evalfunction(func[k], &t, 1, &shade->function[i][k], 1); + shade->function[i][k] = 1; } } diff --git a/xps/muxps.h b/xps/muxps.h index 1b137a59..8150996f 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -250,16 +250,6 @@ struct xps_context_s xps_hash_table *font_table; xps_hash_table *colorspace_table; - /* Global toggle for transparency */ - int use_transparency; - - /* Hack to workaround ghostscript's lack of understanding - * the pdf 1.4 specification of Alpha only transparency groups. - * We have to force all colors to be grayscale whenever we are computing - * opacity masks. - */ - int opacity_only; - /* The fill_rule is set by path parsing. * It is used by clip/fill functions. * 1=nonzero, 0=evenodd diff --git a/xps/xpscolor.c b/xps/xpscolor.c index b0678603..1858c687 100644 --- a/xps/xpscolor.c +++ b/xps/xpscolor.c @@ -7,20 +7,10 @@ void xps_set_color(xps_context *ctx, fz_colorspace *colorspace, float *samples) { int i; - - if (ctx->opacity_only) - { - ctx->colorspace = fz_devicegray; - ctx->color[0] = samples[0]; - ctx->alpha = 1.0; - } - else - { - ctx->colorspace = colorspace; - for (i = 0; i < colorspace->n; i++) - ctx->color[i] = samples[i + 1]; - ctx->alpha = samples[0]; - } + ctx->colorspace = colorspace; + for (i = 0; i < colorspace->n; i++) + ctx->color[i] = samples[i + 1]; + ctx->alpha = samples[0]; } static int unhex(int chr) diff --git a/xps/xpsopacity.c b/xps/xpsopacity.c index 6bd640b3..047e213d 100644 --- a/xps/xpsopacity.c +++ b/xps/xpsopacity.c @@ -28,9 +28,6 @@ xps_begin_opacity(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource { fz_rect bbox; float opacity; - int save; - -return; if (!opacity_att && !opacity_mask_tag) return; @@ -38,23 +35,13 @@ return; opacity = 1.0; if (opacity_att) opacity = atof(opacity_att); -// gs_setopacityalpha(ctx->pgs, opacity); xps_bounds_in_user_space(ctx, &bbox); + ctx->dev->beginmask(ctx->dev->user, bbox, 0, fz_devicegray, &opacity); if (opacity_mask_tag) - { - /* opacity-only mode: use alpha value as gray color to create luminosity mask */ - save = ctx->opacity_only; - ctx->opacity_only = 1; - - // begin mask xps_parse_brush(ctx, ctm, base_uri, dict, opacity_mask_tag); - - ctx->opacity_only = save; - } - - // begin group + ctx->dev->endmask(ctx->dev->user); } void @@ -63,5 +50,6 @@ xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, { if (!opacity_att && !opacity_mask_tag) return; - // end mask+group + + ctx->dev->popclip(ctx->dev->user); } diff --git a/xps/xpszip.c b/xps/xpszip.c index 5528f0f7..069c83ea 100644 --- a/xps/xpszip.c +++ b/xps/xpszip.c @@ -498,11 +498,6 @@ xps_new_context(void) ctx->start_part = NULL; - ctx->use_transparency = 1; - if (getenv("XPS_DISABLE_TRANSPARENCY")) - ctx->use_transparency = 0; - - ctx->opacity_only = 0; ctx->fill_rule = 0; return ctx; -- cgit v1.2.3 From 324d663baa86f9355f7a336b60f706ce31d4f9ca Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 1 Apr 2011 14:21:43 +0200 Subject: xps: Use Opacity attribute for setting constant alpha. --- xps/muxps.h | 4 ++++ xps/xpscolor.c | 2 +- xps/xpsopacity.c | 37 ++++++++++++++++++++++++++++++++++--- xps/xpspage.c | 3 +++ 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/xps/muxps.h b/xps/muxps.h index 8150996f..f135b875 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -256,6 +256,10 @@ struct xps_context_s */ int fill_rule; + /* Opacity attribute stack */ + float opacity[64]; + int opacity_top; + /* Current path being accumulated */ fz_path *path; fz_text *text; /* ... or text, for clipping brushes */ diff --git a/xps/xpscolor.c b/xps/xpscolor.c index 1858c687..37dee5fe 100644 --- a/xps/xpscolor.c +++ b/xps/xpscolor.c @@ -10,7 +10,7 @@ xps_set_color(xps_context *ctx, fz_colorspace *colorspace, float *samples) ctx->colorspace = colorspace; for (i = 0; i < colorspace->n; i++) ctx->color[i] = samples[i + 1]; - ctx->alpha = samples[0]; + ctx->alpha = samples[0] * ctx->opacity[ctx->opacity_top]; } static int unhex(int chr) diff --git a/xps/xpsopacity.c b/xps/xpsopacity.c index 047e213d..51a356e3 100644 --- a/xps/xpsopacity.c +++ b/xps/xpsopacity.c @@ -36,12 +36,36 @@ xps_begin_opacity(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource if (opacity_att) opacity = atof(opacity_att); + if (opacity_mask_tag && !strcmp(xps_tag(opacity_mask_tag), "SolidColorBrush")) + { + char *scb_opacity_att = xps_att(opacity_mask_tag, "Opacity"); + char *scb_color_att = xps_att(opacity_mask_tag, "Color"); + if (scb_opacity_att) + opacity = opacity * atof(scb_opacity_att); + if (scb_color_att) + { + fz_colorspace *colorspace; + float samples[32]; + xps_parse_color(ctx, base_uri, scb_color_att, &colorspace, samples); + opacity = opacity * samples[0]; + } + opacity_mask_tag = NULL; + } + xps_bounds_in_user_space(ctx, &bbox); - ctx->dev->beginmask(ctx->dev->user, bbox, 0, fz_devicegray, &opacity); + if (ctx->opacity_top + 1 < nelem(ctx->opacity)) + { + ctx->opacity[ctx->opacity_top + 1] = ctx->opacity[ctx->opacity_top] * opacity; + ctx->opacity_top++; + } + if (opacity_mask_tag) + { + ctx->dev->beginmask(ctx->dev->user, bbox, 0, NULL, NULL); xps_parse_brush(ctx, ctm, base_uri, dict, opacity_mask_tag); - ctx->dev->endmask(ctx->dev->user); + ctx->dev->endmask(ctx->dev->user); + } } void @@ -51,5 +75,12 @@ xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, if (!opacity_att && !opacity_mask_tag) return; - ctx->dev->popclip(ctx->dev->user); + if (ctx->opacity_top > 0) + ctx->opacity_top--; + + if (opacity_mask_tag) + { + if (strcmp(xps_tag(opacity_mask_tag), "SolidColorBrush")) + ctx->dev->popclip(ctx->dev->user); + } } diff --git a/xps/xpspage.c b/xps/xpspage.c index 32439f19..39731ef2 100644 --- a/xps/xpspage.c +++ b/xps/xpspage.c @@ -101,6 +101,9 @@ xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page) dict = NULL; + ctx->opacity_top = 0; + ctx->opacity[0] = 1; + for (node = xps_down(page->root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "FixedPage.Resources") && xps_down(node)) -- cgit v1.2.3 From 706c332d78120675f26164c93f59f8f16b3a885e Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 1 Apr 2011 14:51:57 +0200 Subject: xps: Repeat enough tiles to cover the shape being filled. --- xps/muxps.h | 10 ++-- xps/xpscommon.c | 6 +- xps/xpsglyphs.c | 6 +- xps/xpsgradient.c | 161 +----------------------------------------------------- xps/xpsimage.c | 5 +- xps/xpsopacity.c | 29 ++-------- xps/xpspath.c | 14 ++--- xps/xpstile.c | 39 ++++++++----- xps/xpsvisual.c | 6 +- 9 files changed, 58 insertions(+), 218 deletions(-) diff --git a/xps/muxps.h b/xps/muxps.h index f135b875..9154316b 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -189,12 +189,12 @@ void xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resou void xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); void xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); void xps_parse_solid_color_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); void xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); void xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root, void(*func)(xps_context*, fz_matrix, char*, xps_resource*, xps_item*, void*), void *user); +void xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *root, void(*func)(xps_context*, fz_matrix, char*, xps_resource*, xps_item*, void*), void *user); void xps_parse_matrix_transform(xps_context *ctx, xps_item *root, fz_matrix *matrix); void xps_parse_render_transform(xps_context *ctx, char *text, fz_matrix *matrix); @@ -205,12 +205,10 @@ void xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *roo void xps_begin_opacity(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, char *opacity_att, xps_item *opacity_mask_tag); void xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, char *opacity_att, xps_item *opacity_mask_tag); -void xps_parse_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); void xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); -void xps_fill(xps_context *ctx, fz_matrix ctm); void xps_clip(xps_context *ctx, fz_matrix ctm); -void xps_bounds_in_user_space(xps_context *ctx, fz_rect *user); int xps_element_has_transparency(xps_context *ctx, char *base_uri, xps_item *node); int xps_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri, xps_item *node); diff --git a/xps/xpscommon.c b/xps/xpscommon.c index d338ffaa..3213f3e9 100644 --- a/xps/xpscommon.c +++ b/xps/xpscommon.c @@ -2,13 +2,13 @@ #include "muxps.h" void -xps_parse_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node) +xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node) { /* SolidColorBrushes are handled in a special case and will never show up here */ if (!strcmp(xps_tag(node), "ImageBrush")) - xps_parse_image_brush(ctx, ctm, base_uri, dict, node); + xps_parse_image_brush(ctx, ctm, area, base_uri, dict, node); else if (!strcmp(xps_tag(node), "VisualBrush")) - xps_parse_visual_brush(ctx, ctm, base_uri, dict, node); + xps_parse_visual_brush(ctx, ctm, area, base_uri, dict, node); else if (!strcmp(xps_tag(node), "LinearGradientBrush")) xps_parse_linear_gradient_brush(ctx, ctm, base_uri, dict, node); else if (!strcmp(xps_tag(node), "RadialGradientBrush")) diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index 372cf222..05b12ed3 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -328,6 +328,8 @@ xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, int is_sideways = 0; int bidi_level = 0; + fz_rect area; + /* * Extract attributes and extended attributes. */ @@ -498,11 +500,13 @@ xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, atof(origin_x_att), atof(origin_y_att), is_sideways, bidi_level, indices_att, unicode_att, 1); + area = fz_boundtext(ctx->text, ctm); + ctx->dev->cliptext(ctx->dev->user, ctx->text, ctm, 0); fz_freetext(ctx->text); ctx->text = nil; - xps_parse_brush(ctx, ctm, fill_uri, dict, fill_tag); + xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag); ctx->dev->popclip(ctx->dev->user); } diff --git a/xps/xpsgradient.c b/xps/xpsgradient.c index c1b45e93..340528bb 100644 --- a/xps/xpsgradient.c +++ b/xps/xpsgradient.c @@ -301,15 +301,11 @@ xps_draw_radial_gradient(xps_context *ctx, fz_matrix ctm, struct stop *stops, int count, xps_item *root, int spread) { - fz_rect bbox; float x0, y0, r0; float x1, y1, r1; float xrad = 1; float yrad = 1; float invscale; - float dx, dy; - int i; - int done; char *center_att = xps_att(root, "Center"); char *origin_att = xps_att(root, "GradientOrigin"); @@ -335,99 +331,7 @@ xps_draw_radial_gradient(xps_context *ctx, fz_matrix ctm, r0 = 0.0; r1 = xrad; - dx = x1 - x0; - dy = y1 - y0; - xps_draw_one_radial_gradient(ctx, ctm, stops, count, 1, x0, y0, r0, x1, y1, r1); - -#if 0 - xps_bounds_in_user_space(ctx, &bbox); - - if (spread == SPREAD_PAD) - { - if (!point_inside_circle(x0, y0, x1, y1, r1)) - { - float in[1]; - float out[4]; - float fary[10]; - void *vary[1]; - - /* PDF shadings with extend doesn't work the same way as XPS - * gradients when the radial shading is a cone. In this case - * we fill the background ourselves. - */ - in[0] = 1.0; - out[0] = 1.0; - out[1] = 0.0; - out[2] = 0.0; - out[3] = 0.0; - if (ctx->opacity_only) - gs_function_evaluate(func, in, out); - else - gs_function_evaluate(func, in, out + 1); - - xps_set_color(ctx, ctx->srgb, out); - - gs_moveto(ctx->pgs, bbox.p.x, bbox.p.y); - gs_lineto(ctx->pgs, bbox.q.x, bbox.p.y); - gs_lineto(ctx->pgs, bbox.q.x, bbox.q.y); - gs_lineto(ctx->pgs, bbox.p.x, bbox.q.y); - gs_closepath(ctx->pgs); - gs_fill(ctx->pgs); - - /* We also have to reverse the direction so the bigger circle - * comes first or the graphical results do not match. We also - * have to reverse the direction of the function to compensate. - */ - -// reverse = xps_reverse_function(ctx, func, fary, vary); -// xps_draw_one_radial_gradient(ctx, reverse, 1, x1, y1, r1, x0, y0, r0); - xps_draw_one_radial_gradient(ctx, ctm, stops, count, - 1, x1, y1, r1, x0, y0, r0); - } - else - { - xps_draw_one_radial_gradient(ctx, ctm, stops, count, - 1, x0, y0, r0, x1, y1, r1); - } - } - else - { - for (i = 0; i < 2; i++) - { - /* Draw current circle */ - - if (!point_inside_circle(x0, y0, x1, y1, r1)) - printf("xps: we should reverse gradient here too\n"); - - if (spread == SPREAD_REFLECT && (i & 1)) - xps_draw_one_radial_gradient(ctx, ctm, stops, count, - 0, x1, y1, r1, x0, y0, r0); - else - xps_draw_one_radial_gradient(ctx, ctm, stops, count, - 0, x0, y0, r0, x1, y1, r1); - - /* Check if circle encompassed the entire bounding box (break loop if we do) */ - done = 1; - if (!point_inside_circle(bbox.x0, bbox.y0, x1, y1, r1)) done = 0; - if (!point_inside_circle(bbox.x0, bbox.y1, x1, y1, r1)) done = 0; - if (!point_inside_circle(bbox.x1, bbox.y1, x1, y1, r1)) done = 0; - if (!point_inside_circle(bbox.x1, bbox.y0, x1, y1, r1)) done = 0; - if (done) - break; - - /* Prepare next circle */ - - r0 = r1; - r1 += xrad; - - x0 += dx; - y0 += dy; - x1 += dx; - y1 += dy; - } - } -#endif } /* @@ -440,77 +344,20 @@ xps_draw_linear_gradient(xps_context *ctx, fz_matrix ctm, struct stop *stops, int count, xps_item *root, int spread) { - fz_rect bbox; float x0, y0, x1, y1; - float dx, dy; - int i; char *start_point_att = xps_att(root, "StartPoint"); char *end_point_att = xps_att(root, "EndPoint"); - x0 = 0; - y0 = 0; - x1 = 0; - y1 = 1; + x0 = y0 = 0; + x1 = y1 = 1; if (start_point_att) sscanf(start_point_att, "%g,%g", &x0, &y0); if (end_point_att) sscanf(end_point_att, "%g,%g", &x1, &y1); - dx = x1 - x0; - dy = y1 - y0; - xps_draw_one_linear_gradient(ctx, ctm, stops, count, 1, x0, y0, x1, y1); - -#if 0 - xps_bounds_in_user_space(ctx, &bbox); - - if (spread == SPREAD_PAD) - { - xps_draw_one_linear_gradient(ctx, ctm, stops, count, 1, x0, y0, x1, y1); - } - else - { - float len; - float a, b; - float dist[4]; - float d0, d1; - int i0, i1; - - len = sqrt(dx * dx + dy * dy); - a = dx / len; - b = dy / len; - - dist[0] = a * (bbox.x0 - x0) + b * (bbox.y0 - y0); - dist[1] = a * (bbox.x0 - x0) + b * (bbox.y1 - y0); - dist[2] = a * (bbox.x1 - x0) + b * (bbox.y1 - y0); - dist[3] = a * (bbox.x1 - x0) + b * (bbox.y0 - y0); - - d0 = dist[0]; - d1 = dist[0]; - for (i = 1; i < 4; i++) - { - if (dist[i] < d0) d0 = dist[i]; - if (dist[i] > d1) d1 = dist[i]; - } - - i0 = floor(d0 / len); - i1 = ceil(d1 / len); - - for (i = i0; i < i1; i++) - { - if (spread == SPREAD_REFLECT && (i & 1)) - xps_draw_one_linear_gradient(ctx, ctm, stops, count, 0, - x1 + dx * i, y1 + dy * i, - x0 + dx * i, y0 + dy * i); - else - xps_draw_one_linear_gradient(ctx, ctm, stops, count, 0, - x0 + dx * i, y0 + dy * i, - x1 + dx * i, y1 + dy * i); - } - } -#endif } /* @@ -539,8 +386,6 @@ xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_matrix transform; int spread_method; - fz_rect bbox; - opacity_att = xps_att(root, "Opacity"); interpolation_att = xps_att(root, "ColorInterpolationMode"); spread_att = xps_att(root, "SpreadMethod"); @@ -591,8 +436,6 @@ xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, return; } - xps_bounds_in_user_space(ctx, &bbox); - xps_begin_opacity(ctx, ctm, base_uri, dict, opacity_att, NULL); draw(ctx, ctm, stop_list, stop_count, root, spread_method); diff --git a/xps/xpsimage.c b/xps/xpsimage.c index 8fc2f7a1..102051cb 100644 --- a/xps/xpsimage.c +++ b/xps/xpsimage.c @@ -105,7 +105,8 @@ xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xps_item *roo } void -xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root) +xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, + char *base_uri, xps_resource *dict, xps_item *root) { xps_part *part; xps_image *image; @@ -123,7 +124,7 @@ xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resou return; } - xps_parse_tiling_brush(ctx, ctm, base_uri, dict, root, xps_paint_image_brush, image); + xps_parse_tiling_brush(ctx, ctm, area, base_uri, dict, root, xps_paint_image_brush, image); xps_free_image(ctx, image); xps_free_part(ctx, part); diff --git a/xps/xpsopacity.c b/xps/xpsopacity.c index 51a356e3..db7bc400 100644 --- a/xps/xpsopacity.c +++ b/xps/xpsopacity.c @@ -1,32 +1,11 @@ #include "fitz.h" #include "muxps.h" -void -xps_bounds_in_user_space(xps_context *ctx, fz_rect *ubox) -{ - *ubox = fz_infiniterect; // *evil grin* -#if 0 - gx_clip_path *clip_path; - fz_rect dbox; - int code; - - code = gx_effective_clip_path(ctx->pgs, &clip_path); - if (code < 0) - fz_warn("gx_effective_clip_path failed"); - - dbox.p.x = fixed2float(clip_path->outer_box.p.x); - dbox.p.y = fixed2float(clip_path->outer_box.p.y); - dbox.q.x = fixed2float(clip_path->outer_box.q.x); - dbox.q.y = fixed2float(clip_path->outer_box.q.y); - gs_bbox_transform_inverse(&dbox, &ctm_only(ctx->pgs), ubox); -#endif -} - void xps_begin_opacity(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, char *opacity_att, xps_item *opacity_mask_tag) { - fz_rect bbox; + fz_rect area; float opacity; if (!opacity_att && !opacity_mask_tag) @@ -52,7 +31,7 @@ xps_begin_opacity(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource opacity_mask_tag = NULL; } - xps_bounds_in_user_space(ctx, &bbox); + area = fz_infiniterect; /* FIXME */ if (ctx->opacity_top + 1 < nelem(ctx->opacity)) { @@ -62,8 +41,8 @@ xps_begin_opacity(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource if (opacity_mask_tag) { - ctx->dev->beginmask(ctx->dev->user, bbox, 0, NULL, NULL); - xps_parse_brush(ctx, ctm, base_uri, dict, opacity_mask_tag); + ctx->dev->beginmask(ctx->dev->user, area, 0, NULL, NULL); + xps_parse_brush(ctx, ctm, area, base_uri, dict, opacity_mask_tag); ctx->dev->endmask(ctx->dev->user); } } diff --git a/xps/xpspath.c b/xps/xpspath.c index dd0d2ef3..14de9147 100644 --- a/xps/xpspath.c +++ b/xps/xpspath.c @@ -815,6 +815,7 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di fz_matrix transform; float samples[32]; fz_colorspace *colorspace; + fz_rect area; ctx->fill_rule = 0; @@ -966,10 +967,10 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di if (data_tag) xps_parse_path_geometry(ctx, dict, data_tag, 0); - xps_clip(ctx, ctm); - - xps_parse_brush(ctx, ctm, fill_uri, dict, fill_tag); + area = fz_boundpath(ctx->path, NULL, ctm); + xps_clip(ctx, ctm); + xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag); ctx->dev->popclip(ctx->dev->user); } @@ -997,11 +998,10 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di if (data_tag) xps_parse_path_geometry(ctx, dict, data_tag, 1); - ctx->fill_rule = 1; /* over-ride fill rule when converting outline to stroked */ - xps_clipstroke(ctx, ctm, &stroke); - - xps_parse_brush(ctx, ctm, stroke_uri, dict, stroke_tag); + area = fz_boundpath(ctx->path, &stroke, ctm); + xps_clipstroke(ctx, ctm, &stroke); + xps_parse_brush(ctx, ctm, area, stroke_uri, dict, stroke_tag); ctx->dev->popclip(ctx->dev->user); } diff --git a/xps/xpstile.c b/xps/xpstile.c index baa274e6..59cf2f09 100644 --- a/xps/xpstile.c +++ b/xps/xpstile.c @@ -63,7 +63,7 @@ xps_paint_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect viewbox, int til } void -xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, +xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *root, void (*func)(xps_context*, fz_matrix, char*, xps_resource*, xps_item*, void*), void *user) { @@ -83,7 +83,8 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_matrix transform; fz_rect viewbox; fz_rect viewport; - float scalex, scaley; + float xstep, ystep; + float xscale, yscale; int tile_mode; opacity_att = xps_att(root, "Opacity"); @@ -130,8 +131,11 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, if (fabs(viewbox.x1 - viewbox.x0) < 0.01) return; if (fabs(viewbox.y1 - viewbox.y0) < 0.01) return; - scalex = (viewport.x1 - viewport.x0) / (viewbox.x1 - viewbox.x0); - scaley = (viewport.y1 - viewport.y0) / (viewbox.y1 - viewbox.y0); + xstep = viewbox.x1 - viewbox.x0; + ystep = viewbox.y1 - viewbox.y0; + + xscale = (viewport.x1 - viewport.x0) / xstep; + yscale = (viewport.y1 - viewport.y0) / ystep; tile_mode = TILE_NONE; if (tile_mode_att) @@ -148,26 +152,35 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, tile_mode = TILE_FLIP_X_Y; } + if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y) + xstep *= 2; + if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y) + ystep *= 2; + xps_begin_opacity(ctx, ctm, base_uri, dict, opacity_att, NULL); ctm = fz_concat(transform, ctm); ctm = fz_concat(fz_translate(viewport.x0, viewport.y0), ctm); - ctm = fz_concat(fz_scale(scalex, scaley), ctm); + ctm = fz_concat(fz_scale(xscale, yscale), ctm); ctm = fz_concat(fz_translate(-viewbox.x0, -viewbox.y0), ctm); - if (tile_mode != TILE_NONE) + if (tile_mode != TILE_NONE && !fz_isinfiniterect(area)) { - float w = viewbox.x1 - viewbox.x0; - float h = viewbox.y1 - viewbox.y0; - fz_matrix ttm; + fz_matrix invctm = fz_invertmatrix(ctm); + fz_rect bbox = fz_transformrect(invctm, area); + int x0 = floorf(bbox.x0 / xstep); + int y0 = floorf(bbox.y0 / ystep); + int x1 = ceilf(bbox.x1 / xstep); + int y1 = ceilf(bbox.y1 / ystep); int x, y; - /* TODO: loop in visible area */ - for (y = 0; y < 2; y++) + printf("repeating tile %d x %d times\n", x1-x0, y1-y0); + + for (y = y0; y < y1; y++) { - for (x = 0; x < 2; x++) + for (x = x0; x < x1; x++) { - ttm = fz_concat(fz_translate(w*x, h*y), ctm); + fz_matrix ttm = fz_concat(fz_translate(xstep * x, ystep * y), ctm); xps_paint_tiling_brush(ctx, ttm, viewbox, tile_mode, &c); } } diff --git a/xps/xpsvisual.c b/xps/xpsvisual.c index 82d37691..ad633708 100644 --- a/xps/xpsvisual.c +++ b/xps/xpsvisual.c @@ -11,7 +11,8 @@ xps_paint_visual_brush(xps_context *ctx, fz_matrix ctm, } void -xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root) +xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, + char *base_uri, xps_resource *dict, xps_item *root) { xps_item *node; @@ -32,6 +33,7 @@ xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_reso if (visual_tag) { - xps_parse_tiling_brush(ctx, ctm, visual_uri, dict, root, xps_paint_visual_brush, visual_tag); + xps_parse_tiling_brush(ctx, ctm, area, + visual_uri, dict, root, xps_paint_visual_brush, visual_tag); } } -- cgit v1.2.3 From ed88bc126023a80536ba437a75b250ba1a95eafb Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 1 Apr 2011 15:23:08 +0200 Subject: xps: Remove 'path' and 'text' state objects from context. --- xps/muxps.h | 16 +--- xps/xpsglyphs.c | 53 ++++------- xps/xpspage.c | 9 +- xps/xpspath.c | 288 ++++++++++++++++++++++++++------------------------------ xps/xpstile.c | 15 +-- xps/xpszip.c | 2 - 6 files changed, 167 insertions(+), 216 deletions(-) diff --git a/xps/muxps.h b/xps/muxps.h index 9154316b..0bb03584 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -199,8 +199,8 @@ void xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char void xps_parse_matrix_transform(xps_context *ctx, xps_item *root, fz_matrix *matrix); void xps_parse_render_transform(xps_context *ctx, char *text, fz_matrix *matrix); void xps_parse_rectangle(xps_context *ctx, char *text, fz_rect *rect); -void xps_parse_abbreviated_geometry(xps_context *ctx, char *geom); -void xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, int stroking); +fz_path *xps_parse_abbreviated_geometry(xps_context *ctx, char *geom, int *fill_rule); +fz_path *xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, int stroking, int *fill_rule); void xps_begin_opacity(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, char *opacity_att, xps_item *opacity_mask_tag); void xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, char *opacity_att, xps_item *opacity_mask_tag); @@ -208,7 +208,7 @@ void xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, char void xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); void xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); -void xps_clip(xps_context *ctx, fz_matrix ctm); +void xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xps_item *clip_tag); int xps_element_has_transparency(xps_context *ctx, char *base_uri, xps_item *node); int xps_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri, xps_item *node); @@ -248,20 +248,10 @@ struct xps_context_s xps_hash_table *font_table; xps_hash_table *colorspace_table; - /* The fill_rule is set by path parsing. - * It is used by clip/fill functions. - * 1=nonzero, 0=evenodd - */ - int fill_rule; - /* Opacity attribute stack */ float opacity[64]; int opacity_top; - /* Current path being accumulated */ - fz_path *path; - fz_text *text; /* ... or text, for clipping brushes */ - /* Current color */ fz_colorspace *colorspace; float color[8]; diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index 05b12ed3..f857542c 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -171,12 +171,13 @@ xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs) * Parse unicode and indices strings and encode glyphs. * Calculate metrics for positioning. */ -static void +static fz_text * xps_parse_glyphs_imp(xps_context *ctx, fz_matrix ctm, fz_font *font, float size, float originx, float originy, int is_sideways, int bidi_level, - char *indices, char *unicode, int is_charpath) + char *indices, char *unicode) { xps_glyph_metrics mtx; + fz_text *text; fz_matrix tm; float e, f; float x = originx; @@ -186,10 +187,7 @@ xps_parse_glyphs_imp(xps_context *ctx, fz_matrix ctm, fz_font *font, float size, int un = 0; if (!unicode && !indices) - { fz_warn("glyphs element with neither characters nor indices"); - return; - } if (us) { @@ -203,7 +201,7 @@ xps_parse_glyphs_imp(xps_context *ctx, fz_matrix ctm, fz_font *font, float size, else tm = fz_scale(size, -size); - ctx->text = fz_newtext(font, tm, is_sideways); + text = fz_newtext(font, tm, is_sideways); while ((us && un > 0) || (is && *is)) { @@ -277,11 +275,13 @@ xps_parse_glyphs_imp(xps_context *ctx, fz_matrix ctm, fz_font *font, float size, f = y - v_offset; } - fz_addtext(ctx->text, glyph_index, char_code, e, f); + fz_addtext(text, glyph_index, char_code, e, f); x += advance * 0.01 * size; } } + + return text; } void @@ -328,6 +328,7 @@ xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, int is_sideways = 0; int bidi_level = 0; + fz_text *text; fz_rect area; /* @@ -445,17 +446,16 @@ xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, } if (clip_att || clip_tag) - { - ctx->path = fz_newpath(); - if (clip_att) - xps_parse_abbreviated_geometry(ctx, clip_att); - if (clip_tag) - xps_parse_path_geometry(ctx, dict, clip_tag, 0); - xps_clip(ctx, ctm); - } + xps_clip(ctx, ctm, dict, clip_att, clip_tag); font_size = atof(font_size_att); + text = xps_parse_glyphs_imp(ctx, ctm, font, font_size, + atof(origin_x_att), atof(origin_y_att), + is_sideways, bidi_level, indices_att, unicode_att); + + area = fz_boundtext(text, ctm); + xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); /* @@ -479,15 +479,8 @@ xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, samples[0] = atof(fill_opacity_att); xps_set_color(ctx, colorspace, samples); - xps_parse_glyphs_imp(ctx, ctm, font, font_size, - atof(origin_x_att), atof(origin_y_att), - is_sideways, bidi_level, - indices_att, unicode_att, 0); - - ctx->dev->filltext(ctx->dev->user, ctx->text, ctm, + ctx->dev->filltext(ctx->dev->user, text, ctm, ctx->colorspace, ctx->color, ctx->alpha); - fz_freetext(ctx->text); - ctx->text = nil; } /* @@ -496,23 +489,15 @@ xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, if (fill_tag) { - xps_parse_glyphs_imp(ctx, ctm, font, font_size, - atof(origin_x_att), atof(origin_y_att), - is_sideways, bidi_level, indices_att, unicode_att, 1); - - area = fz_boundtext(ctx->text, ctm); - - ctx->dev->cliptext(ctx->dev->user, ctx->text, ctm, 0); - fz_freetext(ctx->text); - ctx->text = nil; - + ctx->dev->cliptext(ctx->dev->user, text, ctm, 0); xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag); - ctx->dev->popclip(ctx->dev->user); } xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + fz_freetext(text); + if (clip_att || clip_tag) ctx->dev->popclip(ctx->dev->user); } diff --git a/xps/xpspage.c b/xps/xpspage.c index 39731ef2..0188eb62 100644 --- a/xps/xpspage.c +++ b/xps/xpspage.c @@ -60,14 +60,7 @@ xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource * ctm = fz_concat(transform, ctm); if (clip_att || clip_tag) - { - ctx->path = fz_newpath(); - if (clip_att) - xps_parse_abbreviated_geometry(ctx, clip_att); - if (clip_tag) - xps_parse_path_geometry(ctx, dict, clip_tag, 0); - xps_clip(ctx, ctm); - } + xps_clip(ctx, ctm, dict, clip_att, clip_tag); xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); diff --git a/xps/xpspath.c b/xps/xpspath.c index 14de9147..d5063a87 100644 --- a/xps/xpspath.c +++ b/xps/xpspath.c @@ -36,37 +36,19 @@ fz_currentpoint(fz_path *path) } void -xps_clip(xps_context *ctx, fz_matrix ctm) +xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xps_item *clip_tag) { - ctx->dev->clippath(ctx->dev->user, ctx->path, ctx->fill_rule == 0, ctm); - fz_freepath(ctx->path); - ctx->path = NULL; -} - -void -xps_fill(xps_context *ctx, fz_matrix ctm) -{ - ctx->dev->fillpath(ctx->dev->user, ctx->path, ctx->fill_rule == 0, ctm, - ctx->colorspace, ctx->color, ctx->alpha); - fz_freepath(ctx->path); - ctx->path = NULL; -} + fz_path *path; + int fill_rule = 0; -static void -xps_stroke(xps_context *ctx, fz_matrix ctm, fz_strokestate *stroke) -{ - ctx->dev->strokepath(ctx->dev->user, ctx->path, stroke, ctm, - ctx->colorspace, ctx->color, ctx->alpha); - fz_freepath(ctx->path); - ctx->path = NULL; -} - -static void -xps_clipstroke(xps_context *ctx, fz_matrix ctm, fz_strokestate *stroke) -{ - ctx->dev->clipstrokepath(ctx->dev->user, ctx->path, stroke, ctm); - fz_freepath(ctx->path); - ctx->path = NULL; + if (clip_att) + path = xps_parse_abbreviated_geometry(ctx, clip_att, &fill_rule); + else if (clip_tag) + path = xps_parse_path_geometry(ctx, dict, clip_tag, 0, &fill_rule); + else + path = fz_newpath(); + ctx->dev->clippath(ctx->dev->user, path, fill_rule, ctm); + fz_freepath(path); } /* Draw an arc segment transformed by the matrix, we approximate with straight @@ -75,7 +57,7 @@ xps_clipstroke(xps_context *ctx, fz_matrix ctm, fz_strokestate *stroke) * without transforming the line width. */ static inline void -xps_draw_arc_segment(xps_context *ctx, fz_matrix mtx, float th0, float th1, int iscw) +xps_draw_arc_segment(fz_path *path, fz_matrix mtx, float th0, float th1, int iscw) { float t, d; fz_point p; @@ -90,18 +72,18 @@ xps_draw_arc_segment(xps_context *ctx, fz_matrix mtx, float th0, float th1, int p.x = cos(th0); p.y = sin(th0); p = fz_transformpoint(mtx, p); - fz_lineto(ctx->path, p.x, p.y); + fz_lineto(path, p.x, p.y); for (t = th0; t < th1; t += d) { p.x = cos(t); p.y = sin(t); p = fz_transformpoint(mtx, p); - fz_lineto(ctx->path, p.x, p.y); + fz_lineto(path, p.x, p.y); } p.x = cos(th1); p.y = sin(th1); p = fz_transformpoint(mtx, p); - fz_lineto(ctx->path, p.x, p.y); + fz_lineto(path, p.x, p.y); } else { @@ -109,18 +91,18 @@ xps_draw_arc_segment(xps_context *ctx, fz_matrix mtx, float th0, float th1, int p.x = cos(th0); p.y = sin(th0); p = fz_transformpoint(mtx, p); - fz_lineto(ctx->path, p.x, p.y); + fz_lineto(path, p.x, p.y); for (t = th0; t > th1; t -= d) { p.x = cos(t); p.y = sin(t); p = fz_transformpoint(mtx, p); - fz_lineto(ctx->path, p.x, p.y); + fz_lineto(path, p.x, p.y); } p.x = cos(th1); p.y = sin(th1); p = fz_transformpoint(mtx, p); - fz_lineto(ctx->path, p.x, p.y); + fz_lineto(path, p.x, p.y); } } @@ -141,7 +123,7 @@ angle_between(const fz_point u, const fz_point v) } static void -xps_draw_arc(xps_context *ctx, +xps_draw_arc(fz_path *path, float size_x, float size_y, float rotation_angle, int is_large_arc, int is_clockwise, float point_x, float point_y) @@ -157,7 +139,7 @@ xps_draw_arc(xps_context *ctx, double sign; double th1, dth; - pt = fz_currentpoint(ctx->path); + pt = fz_currentpoint(path); x1 = pt.x; y1 = pt.y; x2 = point_x; @@ -181,7 +163,7 @@ xps_draw_arc(xps_context *ctx, ry = fabsf(ry); if (rx < 0.001 || ry < 0.001) { - fz_lineto(ctx->path, x2, y2); + fz_lineto(path, x2, y2); return; } @@ -241,9 +223,9 @@ xps_draw_arc(xps_context *ctx, mtx = fz_concat(fz_translate(cx, cy), mtx); mtx = fz_concat(fz_rotate(rotation_angle), mtx); mtx = fz_concat(fz_scale(rx, ry), mtx); - xps_draw_arc_segment(ctx, mtx, th1, th1 + dth, is_clockwise); + xps_draw_arc_segment(path, mtx, th1, th1 + dth, is_clockwise); - fz_lineto(ctx->path, point_x, point_y); + fz_lineto(path, point_x, point_y); } /* @@ -252,9 +234,10 @@ xps_draw_arc(xps_context *ctx, * build up a path. */ -void -xps_parse_abbreviated_geometry(xps_context *ctx, char *geom) +fz_path * +xps_parse_abbreviated_geometry(xps_context *ctx, char *geom, int *fill_rule) { + fz_path *path; char **args; char **pargs; char *s = geom; @@ -265,6 +248,8 @@ xps_parse_abbreviated_geometry(xps_context *ctx, char *geom) float smooth_x, smooth_y; /* saved cubic bezier control point for smooth curves */ int reset_smooth; + path = fz_newpath(); + args = fz_calloc(strlen(geom) + 1, sizeof(char*)); pargs = args; @@ -317,49 +302,49 @@ xps_parse_abbreviated_geometry(xps_context *ctx, char *geom) switch (cmd) { case 'F': - ctx->fill_rule = atoi(args[i]); + *fill_rule = atoi(args[i]); i ++; break; case 'M': - fz_moveto(ctx->path, atof(args[i]), atof(args[i+1])); + fz_moveto(path, atof(args[i]), atof(args[i+1])); i += 2; break; case 'm': - pt = fz_currentpoint(ctx->path); - fz_moveto(ctx->path, pt.x + atof(args[i]), pt.y + atof(args[i+1])); + pt = fz_currentpoint(path); + fz_moveto(path, pt.x + atof(args[i]), pt.y + atof(args[i+1])); i += 2; break; case 'L': - fz_lineto(ctx->path, atof(args[i]), atof(args[i+1])); + fz_lineto(path, atof(args[i]), atof(args[i+1])); i += 2; break; case 'l': - pt = fz_currentpoint(ctx->path); - fz_lineto(ctx->path, pt.x + atof(args[i]), pt.y + atof(args[i+1])); + pt = fz_currentpoint(path); + fz_lineto(path, pt.x + atof(args[i]), pt.y + atof(args[i+1])); i += 2; break; case 'H': - pt = fz_currentpoint(ctx->path); - fz_lineto(ctx->path, atof(args[i]), pt.y); + pt = fz_currentpoint(path); + fz_lineto(path, atof(args[i]), pt.y); i += 1; break; case 'h': - pt = fz_currentpoint(ctx->path); - fz_lineto(ctx->path, pt.x + atof(args[i]), pt.y); + pt = fz_currentpoint(path); + fz_lineto(path, pt.x + atof(args[i]), pt.y); i += 1; break; case 'V': - pt = fz_currentpoint(ctx->path); - fz_lineto(ctx->path, pt.x, atof(args[i])); + pt = fz_currentpoint(path); + fz_lineto(path, pt.x, atof(args[i])); i += 1; break; case 'v': - pt = fz_currentpoint(ctx->path); - fz_lineto(ctx->path, pt.x, pt.y + atof(args[i])); + pt = fz_currentpoint(path); + fz_lineto(path, pt.x, pt.y + atof(args[i])); i += 1; break; @@ -370,7 +355,7 @@ xps_parse_abbreviated_geometry(xps_context *ctx, char *geom) y2 = atof(args[i+3]); x3 = atof(args[i+4]); y3 = atof(args[i+5]); - fz_curveto(ctx->path, x1, y1, x2, y2, x3, y3); + fz_curveto(path, x1, y1, x2, y2, x3, y3); i += 6; reset_smooth = 0; smooth_x = x3 - x2; @@ -378,14 +363,14 @@ xps_parse_abbreviated_geometry(xps_context *ctx, char *geom) break; case 'c': - pt = fz_currentpoint(ctx->path); + pt = fz_currentpoint(path); x1 = atof(args[i+0]) + pt.x; y1 = atof(args[i+1]) + pt.y; x2 = atof(args[i+2]) + pt.x; y2 = atof(args[i+3]) + pt.y; x3 = atof(args[i+4]) + pt.x; y3 = atof(args[i+5]) + pt.y; - fz_curveto(ctx->path, x1, y1, x2, y2, x3, y3); + fz_curveto(path, x1, y1, x2, y2, x3, y3); i += 6; reset_smooth = 0; smooth_x = x3 - x2; @@ -393,12 +378,12 @@ xps_parse_abbreviated_geometry(xps_context *ctx, char *geom) break; case 'S': - pt = fz_currentpoint(ctx->path); + pt = fz_currentpoint(path); x1 = atof(args[i+0]); y1 = atof(args[i+1]); x2 = atof(args[i+2]); y2 = atof(args[i+3]); - fz_curveto(ctx->path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); + fz_curveto(path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); i += 4; reset_smooth = 0; smooth_x = x2 - x1; @@ -406,12 +391,12 @@ xps_parse_abbreviated_geometry(xps_context *ctx, char *geom) break; case 's': - pt = fz_currentpoint(ctx->path); + pt = fz_currentpoint(path); x1 = atof(args[i+0]) + pt.x; y1 = atof(args[i+1]) + pt.y; x2 = atof(args[i+2]) + pt.x; y2 = atof(args[i+3]) + pt.y; - fz_curveto(ctx->path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); + fz_curveto(path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); i += 4; reset_smooth = 0; smooth_x = x2 - x1; @@ -419,49 +404,49 @@ xps_parse_abbreviated_geometry(xps_context *ctx, char *geom) break; case 'Q': - pt = fz_currentpoint(ctx->path); + pt = fz_currentpoint(path); x1 = atof(args[i+0]); y1 = atof(args[i+1]); x2 = atof(args[i+2]); y2 = atof(args[i+3]); - fz_curveto(ctx->path, - (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, - (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, - x2, y2); + fz_curveto(path, + (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, + (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, + x2, y2); i += 4; break; case 'q': - pt = fz_currentpoint(ctx->path); + pt = fz_currentpoint(path); x1 = atof(args[i+0]) + pt.x; y1 = atof(args[i+1]) + pt.y; x2 = atof(args[i+2]) + pt.x; y2 = atof(args[i+3]) + pt.y; - fz_curveto(ctx->path, - (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, - (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, - x2, y2); + fz_curveto(path, + (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, + (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, + x2, y2); i += 4; break; case 'A': - xps_draw_arc(ctx, - atof(args[i+0]), atof(args[i+1]), atof(args[i+2]), - atoi(args[i+3]), atoi(args[i+4]), - atof(args[i+5]), atof(args[i+6])); + xps_draw_arc(path, + atof(args[i+0]), atof(args[i+1]), atof(args[i+2]), + atoi(args[i+3]), atoi(args[i+4]), + atof(args[i+5]), atof(args[i+6])); i += 7; break; case 'a': - pt = fz_currentpoint(ctx->path); - xps_draw_arc(ctx, - atof(args[i+0]), atof(args[i+1]), atof(args[i+2]), - atoi(args[i+3]), atoi(args[i+4]), - atof(args[i+5]) + pt.x, atof(args[i+6]) + pt.y); + pt = fz_currentpoint(path); + xps_draw_arc(path, + atof(args[i+0]), atof(args[i+1]), atof(args[i+2]), + atoi(args[i+3]), atoi(args[i+4]), + atof(args[i+5]) + pt.x, atof(args[i+6]) + pt.y); i += 7; break; case 'Z': case 'z': - fz_closepath(ctx->path); + fz_closepath(path); break; default: @@ -473,10 +458,11 @@ xps_parse_abbreviated_geometry(xps_context *ctx, char *geom) } fz_free(args); + return path; } static void -xps_parse_arc_segment(xps_context *ctx, xps_item *root, int stroking, int *skipped_stroke) +xps_parse_arc_segment(fz_path *path, xps_item *root, int stroking, int *skipped_stroke) { /* ArcSegment pretty much follows the SVG algorithm for converting an * arc in endpoint representation to an arc in centerpoint @@ -516,15 +502,15 @@ xps_parse_arc_segment(xps_context *ctx, xps_item *root, int stroking, int *skipp if (stroking && !is_stroked) { - fz_moveto(ctx->path, point_x, point_y); + fz_moveto(path, point_x, point_y); return; } - xps_draw_arc(ctx, size_x, size_y, rotation_angle, is_large_arc, is_clockwise, point_x, point_y); + xps_draw_arc(path, size_x, size_y, rotation_angle, is_large_arc, is_clockwise, point_x, point_y); } static void -xps_parse_poly_quadratic_bezier_segment(xps_context *ctx, xps_item *root, int stroking, int *skipped_stroke) +xps_parse_poly_quadratic_bezier_segment(fz_path *path, xps_item *root, int stroking, int *skipped_stroke) { char *points_att = xps_att(root, "Points"); char *is_stroked_att = xps_att(root, "IsStroked"); @@ -558,12 +544,12 @@ xps_parse_poly_quadratic_bezier_segment(xps_context *ctx, xps_item *root, int st { if (stroking && !is_stroked) { - fz_moveto(ctx->path, x[1], y[1]); + fz_moveto(path, x[1], y[1]); } else { - pt = fz_currentpoint(ctx->path); - fz_curveto(ctx->path, + pt = fz_currentpoint(path); + fz_curveto(path, (pt.x + 2 * x[0]) / 3, (pt.y + 2 * y[0]) / 3, (x[1] + 2 * x[0]) / 3, (y[1] + 2 * y[0]) / 3, x[1], y[1]); @@ -574,7 +560,7 @@ xps_parse_poly_quadratic_bezier_segment(xps_context *ctx, xps_item *root, int st } static void -xps_parse_poly_bezier_segment(xps_context *ctx, xps_item *root, int stroking, int *skipped_stroke) +xps_parse_poly_bezier_segment(fz_path *path, xps_item *root, int stroking, int *skipped_stroke) { char *points_att = xps_att(root, "Points"); char *is_stroked_att = xps_att(root, "IsStroked"); @@ -606,16 +592,16 @@ xps_parse_poly_bezier_segment(xps_context *ctx, xps_item *root, int stroking, in if (n == 3) { if (stroking && !is_stroked) - fz_moveto(ctx->path, x[2], y[2]); + fz_moveto(path, x[2], y[2]); else - fz_curveto(ctx->path, x[0], y[0], x[1], y[1], x[2], y[2]); + fz_curveto(path, x[0], y[0], x[1], y[1], x[2], y[2]); n = 0; } } } static void -xps_parse_poly_line_segment(xps_context *ctx, xps_item *root, int stroking, int *skipped_stroke) +xps_parse_poly_line_segment(fz_path *path, xps_item *root, int stroking, int *skipped_stroke) { char *points_att = xps_att(root, "Points"); char *is_stroked_att = xps_att(root, "IsStroked"); @@ -641,15 +627,15 @@ xps_parse_poly_line_segment(xps_context *ctx, xps_item *root, int stroking, int while (*s == ' ') s++; sscanf(s, "%g,%g", &x, &y); if (stroking && !is_stroked) - fz_moveto(ctx->path, x, y); + fz_moveto(path, x, y); else - fz_lineto(ctx->path, x, y); + fz_lineto(path, x, y); while (*s != ' ' && *s != 0) s++; } } static void -xps_parse_path_figure(xps_context *ctx, xps_item *root, int stroking) +xps_parse_path_figure(fz_path *path, xps_item *root, int stroking) { xps_item *node; @@ -678,31 +664,31 @@ xps_parse_path_figure(xps_context *ctx, xps_item *root, int stroking) if (!stroking && !is_filled) /* not filled, when filling */ return; - fz_moveto(ctx->path, start_x, start_y); + fz_moveto(path, start_x, start_y); for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "ArcSegment")) - xps_parse_arc_segment(ctx, node, stroking, &skipped_stroke); + xps_parse_arc_segment(path, node, stroking, &skipped_stroke); if (!strcmp(xps_tag(node), "PolyBezierSegment")) - xps_parse_poly_bezier_segment(ctx, node, stroking, &skipped_stroke); + xps_parse_poly_bezier_segment(path, node, stroking, &skipped_stroke); if (!strcmp(xps_tag(node), "PolyLineSegment")) - xps_parse_poly_line_segment(ctx, node, stroking, &skipped_stroke); + xps_parse_poly_line_segment(path, node, stroking, &skipped_stroke); if (!strcmp(xps_tag(node), "PolyQuadraticBezierSegment")) - xps_parse_poly_quadratic_bezier_segment(ctx, node, stroking, &skipped_stroke); + xps_parse_poly_quadratic_bezier_segment(path, node, stroking, &skipped_stroke); } if (is_closed) { if (stroking && skipped_stroke) - fz_lineto(ctx->path, start_x, start_y); /* we've skipped using fz_moveto... */ + fz_lineto(path, start_x, start_y); /* we've skipped using fz_moveto... */ else - fz_closepath(ctx->path); /* no skipped segments, safe to closepath properly */ + fz_closepath(path); /* no skipped segments, safe to closepath properly */ } } -void -xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, int stroking) +fz_path * +xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, int stroking, int *fill_rule) { xps_item *node; @@ -714,6 +700,7 @@ xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, in xps_item *figures_tag = NULL; /* only used by resource */ fz_matrix transform; + fz_path *path; figures_att = xps_att(root, "Figures"); fill_rule_att = xps_att(root, "FillRule"); @@ -731,9 +718,9 @@ xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, in if (fill_rule_att) { if (!strcmp(fill_rule_att, "NonZero")) - ctx->fill_rule = 1; + *fill_rule = 1; if (!strcmp(fill_rule_att, "EvenOdd")) - ctx->fill_rule = 0; + *fill_rule = 0; } transform = fz_identity; @@ -743,18 +730,23 @@ xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, in xps_parse_matrix_transform(ctx, transform_tag, &transform); if (figures_att) - xps_parse_abbreviated_geometry(ctx, figures_att); + path = xps_parse_abbreviated_geometry(ctx, figures_att, fill_rule); + else + path = fz_newpath(); + if (figures_tag) - xps_parse_path_figure(ctx, figures_tag, stroking); + xps_parse_path_figure(path, figures_tag, stroking); for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "PathFigure")) - xps_parse_path_figure(ctx, node, stroking); + xps_parse_path_figure(path, node, stroking); } if (transform_att || transform_tag) - fz_transformpath(ctx->path, transform); + fz_transformpath(path, transform); + + return path; } static int @@ -815,9 +807,9 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di fz_matrix transform; float samples[32]; fz_colorspace *colorspace; + fz_path *path; fz_rect area; - - ctx->fill_rule = 0; + int fill_rule; /* * Extract attributes and extended attributes. @@ -871,6 +863,9 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di * Act on the information we have gathered: */ + if (!data_att && !data_tag) + return; + if (fill_tag && !strcmp(xps_tag(fill_tag), "SolidColorBrush")) { fill_opacity_att = xps_att(fill_tag, "Opacity"); @@ -932,14 +927,18 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di ctm = fz_concat(transform, ctm); if (clip_att || clip_tag) - { - ctx->path = fz_newpath(); - if (clip_att) - xps_parse_abbreviated_geometry(ctx, clip_att); - if (clip_tag) - xps_parse_path_geometry(ctx, dict, clip_tag, 0); - xps_clip(ctx, ctm); - } + xps_clip(ctx, ctm, dict, clip_att, clip_tag); + + fill_rule = 0; + if (data_att) + path = xps_parse_abbreviated_geometry(ctx, data_att, &fill_rule); + else if (data_tag) + path = xps_parse_path_geometry(ctx, dict, data_tag, 0, &fill_rule); + + if (stroke_att || stroke_tag) + area = fz_boundpath(path, &stroke, ctm); + else + area = fz_boundpath(path, NULL, ctm); xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); @@ -950,26 +949,15 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di samples[0] = atof(fill_opacity_att); xps_set_color(ctx, colorspace, samples); - ctx->path = fz_newpath(); - if (data_att) - xps_parse_abbreviated_geometry(ctx, data_att); - if (data_tag) - xps_parse_path_geometry(ctx, dict, data_tag, 0); - - xps_fill(ctx, ctm); + ctx->dev->fillpath(ctx->dev->user, path, fill_rule == 0, ctm, + ctx->colorspace, ctx->color, ctx->alpha); } if (fill_tag) { - ctx->path = fz_newpath(); - if (data_att) - xps_parse_abbreviated_geometry(ctx, data_att); - if (data_tag) - xps_parse_path_geometry(ctx, dict, data_tag, 0); + area = fz_boundpath(path, NULL, ctm); - area = fz_boundpath(ctx->path, NULL, ctm); - - xps_clip(ctx, ctm); + ctx->dev->clippath(ctx->dev->user, path, fill_rule == 0, ctm); xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag); ctx->dev->popclip(ctx->dev->user); } @@ -981,32 +969,28 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di samples[0] = atof(stroke_opacity_att); xps_set_color(ctx, colorspace, samples); - ctx->path = fz_newpath(); - if (data_att) - xps_parse_abbreviated_geometry(ctx, data_att); - if (data_tag) - xps_parse_path_geometry(ctx, dict, data_tag, 1); - - xps_stroke(ctx, ctm, &stroke); + ctx->dev->strokepath(ctx->dev->user, path, &stroke, ctm, + ctx->colorspace, ctx->color, ctx->alpha); } if (stroke_tag) { - ctx->path = fz_newpath(); + path = fz_newpath(); if (data_att) - xps_parse_abbreviated_geometry(ctx, data_att); + xps_parse_abbreviated_geometry(ctx, data_att, &fill_rule); if (data_tag) - xps_parse_path_geometry(ctx, dict, data_tag, 1); + xps_parse_path_geometry(ctx, dict, data_tag, 1, &fill_rule); - area = fz_boundpath(ctx->path, &stroke, ctm); - - xps_clipstroke(ctx, ctm, &stroke); + ctx->dev->clipstrokepath(ctx->dev->user, path, &stroke, ctm); xps_parse_brush(ctx, ctm, area, stroke_uri, dict, stroke_tag); ctx->dev->popclip(ctx->dev->user); } xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + fz_freepath(path); + path = NULL; + if (clip_att || clip_tag) ctx->dev->popclip(ctx->dev->user); } diff --git a/xps/xpstile.c b/xps/xpstile.c index 59cf2f09..dedb0a3d 100644 --- a/xps/xpstile.c +++ b/xps/xpstile.c @@ -20,13 +20,14 @@ struct closure static void xps_paint_tiling_brush_clipped(xps_context *ctx, fz_matrix ctm, fz_rect viewbox, struct closure *c) { - ctx->path = fz_newpath(); - fz_moveto(ctx->path, viewbox.x0, viewbox.y0); - fz_lineto(ctx->path, viewbox.x0, viewbox.y1); - fz_lineto(ctx->path, viewbox.x1, viewbox.y1); - fz_lineto(ctx->path, viewbox.x1, viewbox.y0); - fz_closepath(ctx->path); - xps_clip(ctx, ctm); + fz_path *path = fz_newpath(); + fz_moveto(path, viewbox.x0, viewbox.y0); + fz_lineto(path, viewbox.x0, viewbox.y1); + fz_lineto(path, viewbox.x1, viewbox.y1); + fz_lineto(path, viewbox.x1, viewbox.y0); + fz_closepath(path); + + ctx->dev->clippath(ctx->dev->user, path, 0, ctm); c->func(ctx, ctm, c->base_uri, c->dict, c->root, c->user); diff --git a/xps/xpszip.c b/xps/xpszip.c index 069c83ea..0646b89f 100644 --- a/xps/xpszip.c +++ b/xps/xpszip.c @@ -498,8 +498,6 @@ xps_new_context(void) ctx->start_part = NULL; - ctx->fill_rule = 0; - return ctx; } -- cgit v1.2.3 From 6192f4c4fdec838fd5ef86a17517d615d7ff16ca Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 1 Apr 2011 15:29:02 +0200 Subject: xps: Pass bounding box to opacity mask brush. --- xps/muxps.h | 6 +++--- xps/xpscommon.c | 4 ++-- xps/xpsglyphs.c | 2 +- xps/xpsgradient.c | 14 ++++++++------ xps/xpsopacity.c | 10 ++++------ xps/xpspage.c | 2 +- xps/xpspath.c | 2 +- xps/xpstile.c | 4 +--- 8 files changed, 21 insertions(+), 23 deletions(-) diff --git a/xps/muxps.h b/xps/muxps.h index 0bb03584..51c0323e 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -191,8 +191,8 @@ void xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resou void xps_parse_solid_color_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); void xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); void xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); void xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *root, void(*func)(xps_context*, fz_matrix, char*, xps_resource*, xps_item*, void*), void *user); @@ -202,7 +202,7 @@ void xps_parse_rectangle(xps_context *ctx, char *text, fz_rect *rect); fz_path *xps_parse_abbreviated_geometry(xps_context *ctx, char *geom, int *fill_rule); fz_path *xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, int stroking, int *fill_rule); -void xps_begin_opacity(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, char *opacity_att, xps_item *opacity_mask_tag); +void xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, char *opacity_att, xps_item *opacity_mask_tag); void xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, char *opacity_att, xps_item *opacity_mask_tag); void xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); diff --git a/xps/xpscommon.c b/xps/xpscommon.c index 3213f3e9..063435c5 100644 --- a/xps/xpscommon.c +++ b/xps/xpscommon.c @@ -10,9 +10,9 @@ xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, x else if (!strcmp(xps_tag(node), "VisualBrush")) xps_parse_visual_brush(ctx, ctm, area, base_uri, dict, node); else if (!strcmp(xps_tag(node), "LinearGradientBrush")) - xps_parse_linear_gradient_brush(ctx, ctm, base_uri, dict, node); + xps_parse_linear_gradient_brush(ctx, ctm, area, base_uri, dict, node); else if (!strcmp(xps_tag(node), "RadialGradientBrush")) - xps_parse_radial_gradient_brush(ctx, ctm, base_uri, dict, node); + xps_parse_radial_gradient_brush(ctx, ctm, area, base_uri, dict, node); else fz_warn("unknown brush tag: %s", xps_tag(node)); } diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index f857542c..f62cc928 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -456,7 +456,7 @@ xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, area = fz_boundtext(text, ctm); - xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + xps_begin_opacity(ctx, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); /* * If it's a solid color brush fill/stroke do a simple fill diff --git a/xps/xpsgradient.c b/xps/xpsgradient.c index 340528bb..180224f8 100644 --- a/xps/xpsgradient.c +++ b/xps/xpsgradient.c @@ -366,7 +366,7 @@ xps_draw_linear_gradient(xps_context *ctx, fz_matrix ctm, */ static void -xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, +xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *root, void (*draw)(xps_context *, fz_matrix, struct stop *, int, xps_item *, int)) { @@ -436,7 +436,9 @@ xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, return; } - xps_begin_opacity(ctx, ctm, base_uri, dict, opacity_att, NULL); + area = fz_transformrect(ctm, area); + + xps_begin_opacity(ctx, ctm, area, base_uri, dict, opacity_att, NULL); draw(ctx, ctm, stop_list, stop_count, root, spread_method); @@ -444,15 +446,15 @@ xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, } void -xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, +xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *root) { - xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_linear_gradient); + xps_parse_gradient_brush(ctx, ctm, area, base_uri, dict, root, xps_draw_linear_gradient); } void -xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, +xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *root) { - xps_parse_gradient_brush(ctx, ctm, base_uri, dict, root, xps_draw_radial_gradient); + xps_parse_gradient_brush(ctx, ctm, area, base_uri, dict, root, xps_draw_radial_gradient); } diff --git a/xps/xpsopacity.c b/xps/xpsopacity.c index db7bc400..e5b0f787 100644 --- a/xps/xpsopacity.c +++ b/xps/xpsopacity.c @@ -2,10 +2,10 @@ #include "muxps.h" void -xps_begin_opacity(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, - char *opacity_att, xps_item *opacity_mask_tag) +xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, + char *base_uri, xps_resource *dict, + char *opacity_att, xps_item *opacity_mask_tag) { - fz_rect area; float opacity; if (!opacity_att && !opacity_mask_tag) @@ -31,8 +31,6 @@ xps_begin_opacity(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource opacity_mask_tag = NULL; } - area = fz_infiniterect; /* FIXME */ - if (ctx->opacity_top + 1 < nelem(ctx->opacity)) { ctx->opacity[ctx->opacity_top + 1] = ctx->opacity[ctx->opacity_top] * opacity; @@ -49,7 +47,7 @@ xps_begin_opacity(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource void xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, - char *opacity_att, xps_item *opacity_mask_tag) + char *opacity_att, xps_item *opacity_mask_tag) { if (!opacity_att && !opacity_mask_tag) return; diff --git a/xps/xpspage.c b/xps/xpspage.c index 0188eb62..1854b1fc 100644 --- a/xps/xpspage.c +++ b/xps/xpspage.c @@ -62,7 +62,7 @@ xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource * if (clip_att || clip_tag) xps_clip(ctx, ctm, dict, clip_att, clip_tag); - xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + xps_begin_opacity(ctx, ctm, fz_infiniterect, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); for (node = xps_down(root); node; node = xps_next(node)) { diff --git a/xps/xpspath.c b/xps/xpspath.c index d5063a87..250ab022 100644 --- a/xps/xpspath.c +++ b/xps/xpspath.c @@ -940,7 +940,7 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di else area = fz_boundpath(path, NULL, ctm); - xps_begin_opacity(ctx, ctm, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + xps_begin_opacity(ctx, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); if (fill_att) { diff --git a/xps/xpstile.c b/xps/xpstile.c index dedb0a3d..1b96a7d3 100644 --- a/xps/xpstile.c +++ b/xps/xpstile.c @@ -158,7 +158,7 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y) ystep *= 2; - xps_begin_opacity(ctx, ctm, base_uri, dict, opacity_att, NULL); + xps_begin_opacity(ctx, ctm, area, base_uri, dict, opacity_att, NULL); ctm = fz_concat(transform, ctm); ctm = fz_concat(fz_translate(viewport.x0, viewport.y0), ctm); @@ -175,8 +175,6 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, int y1 = ceilf(bbox.y1 / ystep); int x, y; - printf("repeating tile %d x %d times\n", x1-x0, y1-y0); - for (y = y0; y < y1; y++) { for (x = x0; x < x1; x++) -- cgit v1.2.3 From d2250034a7e629455b9bfbf735473ea39638a701 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 1 Apr 2011 16:02:09 +0200 Subject: Use a function pointer for resolveindirect. This removes a static dependency between fitz and mupdf. Fitz should now be link time independent of mupdf again. --- fitz/fitz.h | 6 +++--- fitz/obj_simple.c | 32 ++++++++------------------------ mupdf/pdf_xref.c | 28 ++++++++++++++++++++++++++++ xps/xpstop.c | 6 ------ 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/fitz/fitz.h b/fitz/fitz.h index f8bef4c3..d4ab8ff4 100644 --- a/fitz/fitz.h +++ b/fitz/fitz.h @@ -406,13 +406,15 @@ struct fz_obj_s } u; }; +extern fz_obj* (*fz_resolveindirect)(fz_obj*); + fz_obj *fz_newnull(void); fz_obj *fz_newbool(int b); fz_obj *fz_newint(int i); fz_obj *fz_newreal(float f); fz_obj *fz_newname(char *str); fz_obj *fz_newstring(char *str, int len); -fz_obj *fz_newindirect(int num, int gen, struct pdf_xref_s *xref); +fz_obj *fz_newindirect(int num, int gen, void *xref); fz_obj *fz_newarray(int initialcap); fz_obj *fz_newdict(int initialcap); @@ -435,8 +437,6 @@ int fz_isindirect(fz_obj *obj); int fz_objcmp(fz_obj *a, fz_obj *b); -fz_obj *fz_resolveindirect(fz_obj *obj); - /* silent failure, no error reporting */ int fz_tobool(fz_obj *obj); int fz_toint(fz_obj *obj); diff --git a/fitz/obj_simple.c b/fitz/obj_simple.c index f279bd55..b629974e 100644 --- a/fitz/obj_simple.c +++ b/fitz/obj_simple.c @@ -1,9 +1,15 @@ #include "fitz.h" -#include "mupdf.h" /* for pdf_loadobject */ extern void fz_freearray(fz_obj *array); extern void fz_freedict(fz_obj *dict); +static fz_obj *fz_resolve_indirect_null(fz_obj *ref) +{ + return ref; +} + +fz_obj* (*fz_resolveindirect)(fz_obj*) = fz_resolve_indirect_null; + fz_obj * fz_newnull(void) { @@ -66,7 +72,7 @@ fz_newname(char *str) } fz_obj * -fz_newindirect(int num, int gen, pdf_xref *xref) +fz_newindirect(int num, int gen, void *xref) { fz_obj *o = fz_malloc(sizeof(fz_obj)); o->refs = 1; @@ -219,28 +225,6 @@ int fz_togen(fz_obj *obj) return 0; } -fz_obj *fz_resolveindirect(fz_obj *ref) -{ - if (fz_isindirect(ref)) - { - pdf_xref *xref = ref->u.r.xref; - int num = fz_tonum(ref); - int gen = fz_togen(ref); - if (xref) - { - fz_error error = pdf_cacheobject(xref, num, gen); - if (error) - { - fz_catch(error, "cannot load object (%d %d R) into cache", num, gen); - return ref; - } - if (xref->table[num].obj) - return xref->table[num].obj; - } - } - return ref; -} - int fz_objcmp(fz_obj *a, fz_obj *b) { diff --git a/mupdf/pdf_xref.c b/mupdf/pdf_xref.c index 7174cfc5..4ec23a30 100644 --- a/mupdf/pdf_xref.c +++ b/mupdf/pdf_xref.c @@ -1,6 +1,8 @@ #include "fitz.h" #include "mupdf.h" +static fz_obj *pdf_resolveindirect(fz_obj *ref); + static inline int iswhite(int ch) { return @@ -535,6 +537,9 @@ pdf_openxrefwithstream(pdf_xref **xrefp, fz_stream *file, char *password) fz_obj *dict, *obj; int i, repaired = 0; + /* install pdf specific callback */ + fz_resolveindirect = pdf_resolveindirect; + xref = fz_malloc(sizeof(pdf_xref)); memset(xref, 0, sizeof(pdf_xref)); @@ -893,6 +898,29 @@ pdf_loadobject(fz_obj **objp, pdf_xref *xref, int num, int gen) return fz_okay; } +static fz_obj * +pdf_resolveindirect(fz_obj *ref) +{ + if (fz_isindirect(ref)) + { + pdf_xref *xref = ref->u.r.xref; + int num = fz_tonum(ref); + int gen = fz_togen(ref); + if (xref) + { + fz_error error = pdf_cacheobject(xref, num, gen); + if (error) + { + fz_catch(error, "cannot load object (%d %d R) into cache", num, gen); + return ref; + } + if (xref->table[num].obj) + return xref->table[num].obj; + } + } + return ref; +} + /* Replace numbered object -- for use by pdfclean and similar tools */ void pdf_updateobject(pdf_xref *xref, int num, int gen, fz_obj *newobj) diff --git a/xps/xpstop.c b/xps/xpstop.c index 2ae1d585..921d677a 100644 --- a/xps/xpstop.c +++ b/xps/xpstop.c @@ -21,12 +21,6 @@ fz_colorspace *colorspace; fz_glyphcache *glyphcache; char *filename; -/* stub function for automatic resolution of indirect objects in obj_simple.c */ -int pdf_cacheobject(struct pdf_xref_s *xref, int num, int gen) -{ - return fz_throw("not a pdf"); -} - struct { int count, total; int min, max; -- cgit v1.2.3 From a6de4b76903f590c776c112e02e2f2ce0a363010 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 1 Apr 2011 16:44:30 +0200 Subject: apps: Add XPS support to mupdf viewer. --- Makefile | 8 +-- apps/pdfapp.c | 176 +++++++++++++++++++++++++++++++++++++++---------------- apps/pdfapp.h | 8 ++- apps/x11_main.c | 1 + mupdf/mupdf.h | 2 - mupdf/pdf_page.c | 6 -- xps/muxps.h | 1 + xps/xpszip.c | 8 +++ 8 files changed, 147 insertions(+), 63 deletions(-) diff --git a/Makefile b/Makefile index 23e0a281..23ce82d1 100644 --- a/Makefile +++ b/Makefile @@ -397,8 +397,8 @@ X11VIEW_OBJ=$(X11VIEW_SRC:apps/%.c=$(OBJDIR)/%.o) X11VIEW_EXE=$(OBJDIR)/mupdf $(X11VIEW_OBJ): $(MUPDF_HDR) $(PDFAPP_HDR) -$(X11VIEW_EXE): $(X11VIEW_OBJ) $(MUPDF_LIB) $(THIRD_LIBS) - $(LD_CMD) $(X11LIBS) +$(X11VIEW_EXE): $(X11VIEW_OBJ) $(MUPDF_LIB) $(MUXPS_LIB) $(THIRD_LIBS) + $(LD_CMD) $(XPSLIBS) $(X11LIBS) WINVIEW_SRC=apps/win_main.c apps/pdfapp.c WINVIEW_RES=apps/win_res.rc @@ -409,8 +409,8 @@ $(OBJDIR)/%.o: apps/%.rc $(WINDRES) -i $< -o $@ --include-dir=apps $(WINVIEW_OBJ): $(MUPDF_HDR) $(PDFAPP_HDR) -$(WINVIEW_EXE): $(WINVIEW_OBJ) $(MUPDF_LIB) $(THIRD_LIBS) - $(LD_CMD) $(W32LIBS) +$(WINVIEW_EXE): $(WINVIEW_OBJ) $(MUPDF_LIB) $(MUXPS_LIB) $(THIRD_LIBS) + $(LD_CMD) $(XPSLIBS) $(W32LIBS) # # Default rules diff --git a/apps/pdfapp.c b/apps/pdfapp.c index 2fca8d0c..aec9ad62 100644 --- a/apps/pdfapp.c +++ b/apps/pdfapp.c @@ -1,5 +1,6 @@ -#include -#include +#include "fitz.h" +#include "mupdf.h" +#include "muxps.h" #include "pdfapp.h" #include /* for tolower() */ @@ -95,15 +96,13 @@ void pdfapp_invert(pdfapp_t *app, fz_bbox rect) } } -void pdfapp_open(pdfapp_t *app, char *filename, int fd) +static void pdfapp_open_pdf(pdfapp_t *app, char *filename, int fd) { fz_error error; + fz_stream *file; + char *password = ""; fz_obj *obj; fz_obj *info; - char *password = ""; - fz_stream *file; - - app->cache = fz_newglyphcache(); /* * Open PDF and load xref table @@ -161,6 +160,32 @@ void pdfapp_open(pdfapp_t *app, char *filename, int fd) pdfapp_error(app, fz_rethrow(error, "cannot load page tree")); app->pagecount = pdf_getpagecount(app->xref); +} + +static void pdfapp_open_xps(pdfapp_t *app, char *filename, int fd) +{ + fz_error error; + + close(fd); // TODO: fix this for windows + + app->xps = xps_new_context(); + error = xps_open_file(app->xps, filename); + if (error) + pdfapp_error(app, fz_rethrow(error, "cannot open document: '%s'", filename)); + + app->doctitle = filename; + + app->pagecount = xps_count_pages(app->xps); +} + +void pdfapp_open(pdfapp_t *app, char *filename, int fd) +{ + if (strstr(filename, ".xps") || strstr(filename, ".XPS")) + pdfapp_open_xps(app, filename, fd); + else + pdfapp_open_pdf(app, filename, fd); + + app->cache = fz_newglyphcache(); app->shrinkwrap = 1; if (app->pageno < 1) @@ -184,10 +209,6 @@ void pdfapp_close(pdfapp_t *app) fz_freeglyphcache(app->cache); app->cache = nil; - if (app->page) - pdf_freepage(app->page); - app->page = nil; - if (app->image) fz_droppixmap(app->image); app->image = nil; @@ -206,6 +227,12 @@ void pdfapp_close(pdfapp_t *app) app->xref = nil; } + if (app->xps) + { + xps_free_context(app->xps); + app->xps = NULL; + } + fz_flushwarnings(); } @@ -213,9 +240,12 @@ static fz_matrix pdfapp_viewctm(pdfapp_t *app) { fz_matrix ctm; ctm = fz_identity; - ctm = fz_concat(ctm, fz_translate(0, -app->page->mediabox.y1)); - ctm = fz_concat(ctm, fz_scale(app->resolution/72.0f, -app->resolution/72.0f)); - ctm = fz_concat(ctm, fz_rotate(app->rotate + app->page->rotate)); + ctm = fz_concat(ctm, fz_translate(0, -app->page_bbox.y1)); + if (app->xref) + ctm = fz_concat(ctm, fz_scale(app->resolution/72.0f, -app->resolution/72.0f)); + else + ctm = fz_concat(ctm, fz_scale(app->resolution/72.0f, app->resolution/72.0f)); + ctm = fz_concat(ctm, fz_rotate(app->rotate + app->page_rotate)); return ctm; } @@ -243,51 +273,97 @@ static void pdfapp_panview(pdfapp_t *app, int newx, int newy) app->pany = newy; } +static void pdfapp_loadpage_pdf(pdfapp_t *app) +{ + pdf_page *page; + fz_error error; + fz_device *mdev; + fz_obj *obj; + + obj = pdf_getpageobject(app->xref, app->pageno); + error = pdf_loadpage(&page, app->xref, obj); + if (error) + pdfapp_error(app, error); + + app->page_bbox = page->mediabox; + app->page_rotate = page->rotate; + app->page_links = page->links; + page->links = NULL; + + /* Create display list */ + app->page_list = fz_newdisplaylist(); + mdev = fz_newlistdevice(app->page_list); + error = pdf_runpage(app->xref, page, mdev, fz_identity); + if (error) + { + error = fz_rethrow(error, "cannot draw page %d in '%s'", app->pageno, app->doctitle); + pdfapp_error(app, error); + } + fz_freedevice(mdev); + + pdf_freepage(page); + + pdf_agestore(app->xref->store, 3); +} + +static void pdfapp_loadpage_xps(pdfapp_t *app) +{ + xps_page *page; + fz_device *mdev; + + page = xps_load_page(app->xps, app->pageno - 1); + if (!page) + pdfapp_error(app, fz_throw("cannot load page %d in file '%s'", app->pageno, app->doctitle)); + + app->page_bbox = fz_transformrect(fz_scale(page->width, page->height), fz_unitrect); + app->page_rotate = 0; + app->page_links = NULL; + + /* Create display list */ + app->page_list = fz_newdisplaylist(); + mdev = fz_newlistdevice(app->page_list); + app->xps->dev = mdev; + xps_parse_fixed_page(app->xps, fz_identity, page); + app->xps->dev = nil; + fz_freedevice(mdev); + + xps_free_page(app->xps, page); +} + static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repaint) { char buf[256]; - fz_error error; - fz_device *idev, *tdev, *mdev; + fz_device *idev; + fz_device *tdev; fz_colorspace *colorspace; fz_matrix ctm; fz_bbox bbox; - fz_obj *obj; wincursor(app, WAIT); if (loadpage) { - if (app->page) - pdf_freepage(app->page); - app->page = nil; - - obj = pdf_getpageobject(app->xref, app->pageno); - error = pdf_loadpage(&app->page, app->xref, obj); - if (error) - pdfapp_error(app, error); - - /* Create display list */ - app->page->list = fz_newdisplaylist(); - mdev = fz_newlistdevice(app->page->list); - error = pdf_runpage(app->xref, app->page, mdev, fz_identity); - if (error) - { - error = fz_rethrow(error, "cannot draw page %d in '%s'", app->pageno, app->doctitle); - pdfapp_error(app, error); - } - fz_freedevice(mdev); + if (app->page_list) + fz_freedisplaylist(app->page_list); + if (app->page_text) + fz_freetextspan(app->page_text); + if (app->page_links) + pdf_freelink(app->page_links); + + if (app->xref) + pdfapp_loadpage_pdf(app); + if (app->xps) + pdfapp_loadpage_xps(app); /* Zero search hit position */ app->hit = -1; app->hitlen = 0; /* Extract text */ - app->page->text = fz_newtextspan(); - tdev = fz_newtextdevice(app->page->text); - fz_executedisplaylist(app->page->list, tdev, fz_identity); + app->page_text = fz_newtextspan(); + tdev = fz_newtextdevice(app->page_text); + fz_executedisplaylist(app->page_list, tdev, fz_identity); fz_freedevice(tdev); - - pdf_agestore(app->xref->store, 3); } if (drawpage) @@ -297,7 +373,7 @@ static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repai wintitle(app, buf); ctm = pdfapp_viewctm(app); - bbox = fz_roundrect(fz_transformrect(ctm, app->page->mediabox)); + bbox = fz_roundrect(fz_transformrect(ctm, app->page_bbox)); /* Draw */ if (app->image) @@ -313,7 +389,7 @@ static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repai app->image = fz_newpixmapwithrect(colorspace, bbox); fz_clearpixmapwithcolor(app->image, 255); idev = fz_newdrawdevice(app->cache, app->image); - fz_executedisplaylist(app->page->list, idev, ctm); + fz_executedisplaylist(app->page_list, idev, ctm); fz_freedevice(idev); } @@ -402,7 +478,7 @@ void pdfapp_inverthit(pdfapp_t *app) for (i = app->hit; i < app->hit + app->hitlen; i++) { - bbox = bboxcharat(app->page->text, i); + bbox = bboxcharat(app->page_text, i); if (fz_isemptyrect(bbox)) { if (!fz_isemptyrect(hitbox)) @@ -492,7 +568,7 @@ static void pdfapp_searchforward(pdfapp_t *app) do { - len = textlen(app->page->text); + len = textlen(app->page_text); if (app->hit >= 0) test = app->hit + strlen(app->search); @@ -501,7 +577,7 @@ static void pdfapp_searchforward(pdfapp_t *app) while (test < len) { - matchlen = match(app->search, app->page->text, test); + matchlen = match(app->search, app->page_text, test); if (matchlen) { app->hit = test; @@ -540,7 +616,7 @@ static void pdfapp_searchbackward(pdfapp_t *app) do { - len = textlen(app->page->text); + len = textlen(app->page_text); if (app->hit >= 0) test = app->hit - 1; @@ -549,7 +625,7 @@ static void pdfapp_searchbackward(pdfapp_t *app) while (test >= 0) { - matchlen = match(app->search, app->page->text, test); + matchlen = match(app->search, app->page_text, test); if (matchlen) { app->hit = test; @@ -881,7 +957,7 @@ void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int sta p = fz_transformpoint(ctm, p); - for (link = app->page->links; link; link = link->next) + for (link = app->page_links; link; link = link->next) { if (p.x >= link->rect.x0 && p.x <= link->rect.x1) if (p.y >= link->rect.y0 && p.y <= link->rect.y1) @@ -1005,7 +1081,7 @@ void pdfapp_oncopy(pdfapp_t *app, unsigned short *ucsbuf, int ucslen) ctm = pdfapp_viewctm(app); p = 0; - for (span = app->page->text; span; span = span->next) + for (span = app->page_text; span; span = span->next) { seen = 0; diff --git a/apps/pdfapp.h b/apps/pdfapp.h index 3771485e..dda637db 100644 --- a/apps/pdfapp.h +++ b/apps/pdfapp.h @@ -31,6 +31,8 @@ struct pdfapp_s char *doctitle; pdf_xref *xref; pdf_outline *outline; + xps_context *xps; + int pagecount; fz_glyphcache *cache; @@ -42,7 +44,11 @@ struct pdfapp_s /* current page params */ int pageno; - pdf_page *page; + fz_rect page_bbox; + float page_rotate; + fz_displaylist *page_list; + fz_textspan *page_text; + pdf_link *page_links; /* snapback history */ int hist[256]; diff --git a/apps/x11_main.c b/apps/x11_main.c index 73c8c568..1e08368e 100644 --- a/apps/x11_main.c +++ b/apps/x11_main.c @@ -1,5 +1,6 @@ #include "fitz.h" #include "mupdf.h" +#include "muxps.h" #include "pdfapp.h" #include diff --git a/mupdf/mupdf.h b/mupdf/mupdf.h index 28fa20c3..a9a2a2ea 100644 --- a/mupdf/mupdf.h +++ b/mupdf/mupdf.h @@ -515,8 +515,6 @@ struct pdf_page_s int transparency; fz_obj *resources; fz_buffer *contents; - fz_displaylist *list; - fz_textspan *text; pdf_link *links; pdf_annot *annots; }; diff --git a/mupdf/pdf_page.c b/mupdf/pdf_page.c index e09cafb0..869f7a5c 100644 --- a/mupdf/pdf_page.c +++ b/mupdf/pdf_page.c @@ -168,8 +168,6 @@ pdf_loadpage(pdf_page **pagep, pdf_xref *xref, fz_obj *dict) page->resources = nil; page->contents = nil; page->transparency = 0; - page->list = nil; - page->text = nil; page->links = nil; page->annots = nil; @@ -240,10 +238,6 @@ pdf_freepage(pdf_page *page) fz_dropobj(page->resources); if (page->contents) fz_dropbuffer(page->contents); - if (page->list) - fz_freedisplaylist(page->list); - if (page->text) - fz_freetextspan(page->text); if (page->links) pdf_freelink(page->links); if (page->annots) diff --git a/xps/muxps.h b/xps/muxps.h index 51c0323e..d0478e7b 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -265,6 +265,7 @@ int xps_read_and_process_page_part(xps_context *ctx, fz_matrix ctm, char *name); int xps_open_file(xps_context *ctx, char *filename); int xps_count_pages(xps_context *ctx); xps_page *xps_load_page(xps_context *ctx, int number); +void xps_free_page(xps_context *ctx, xps_page *page); xps_context *xps_new_context(void); int xps_free_context(xps_context *ctx); diff --git a/xps/xpszip.c b/xps/xpszip.c index 0646b89f..4905ea37 100644 --- a/xps/xpszip.c +++ b/xps/xpszip.c @@ -484,6 +484,14 @@ xps_load_page(xps_context *ctx, int number) return nil; } +void +xps_free_page(xps_context *ctx, xps_page *page) +{ + if (page->root) + xps_free_item(ctx, page->root); + page->root = NULL; +} + xps_context * xps_new_context(void) { -- cgit v1.2.3 From 9e43fd5ace67a6c8493c222c8a71ba7bcca87325 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 1 Apr 2011 16:58:41 +0200 Subject: xps: Fix trivial bug. --- xps/xpspath.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/xps/xpspath.c b/xps/xpspath.c index 250ab022..ded566ca 100644 --- a/xps/xpspath.c +++ b/xps/xpspath.c @@ -975,12 +975,6 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di if (stroke_tag) { - path = fz_newpath(); - if (data_att) - xps_parse_abbreviated_geometry(ctx, data_att, &fill_rule); - if (data_tag) - xps_parse_path_geometry(ctx, dict, data_tag, 1, &fill_rule); - ctx->dev->clipstrokepath(ctx->dev->user, path, &stroke, ctm); xps_parse_brush(ctx, ctm, area, stroke_uri, dict, stroke_tag); ctx->dev->popclip(ctx->dev->user); -- cgit v1.2.3 From e603c180e7d70f64cf50302ecf6d8937172ed121 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 3 Apr 2011 03:44:12 +0200 Subject: xps: Remove dependency on expat. --- Makefile | 7 +- Makethird | 21 -- apps/pdfapp.c | 2 +- xps/muxps.h | 80 ++++---- xps/xpsanalyze.c | 112 +++++------ xps/xpscommon.c | 26 +-- xps/xpsdoc.c | 120 ++++------- xps/xpsglyphs.c | 68 +++---- xps/xpsgradient.c | 68 +++---- xps/xpsimage.c | 8 +- xps/xpsopacity.c | 12 +- xps/xpspage.c | 63 +++--- xps/xpspath.c | 166 +++++++-------- xps/xpsresource.c | 32 +-- xps/xpstile.c | 36 ++-- xps/xpsvisual.c | 18 +- xps/xpsxml.c | 588 +++++++++++++++++++++++++++++------------------------- xps/xpszip.c | 8 +- 18 files changed, 717 insertions(+), 718 deletions(-) diff --git a/Makefile b/Makefile index 23ce82d1..11fb41c6 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,6 @@ endif # directory exists. LIBS := -lfreetype -ljbig2dec -lopenjpeg -ljpeg -lz -lm -XPSLIBS := -lexpat include Makerules include Makethird @@ -388,7 +387,7 @@ XPSDRAW_OBJ=$(XPSDRAW_SRC:xps/%.c=$(OBJDIR)/%.o) XPSDRAW_EXE=$(OBJDIR)/xpsdraw $(XPSDRAW_OBJ): $(MUXPS_HDR) $(XPSDRAW_EXE): $(XPSDRAW_OBJ) $(MUXPS_LIB) $(THIRD_LIBS) - $(LD_CMD) $(XPSLIBS) + $(LD_CMD) PDFAPP_HDR = apps/pdfapp.h @@ -398,7 +397,7 @@ X11VIEW_EXE=$(OBJDIR)/mupdf $(X11VIEW_OBJ): $(MUPDF_HDR) $(PDFAPP_HDR) $(X11VIEW_EXE): $(X11VIEW_OBJ) $(MUPDF_LIB) $(MUXPS_LIB) $(THIRD_LIBS) - $(LD_CMD) $(XPSLIBS) $(X11LIBS) + $(LD_CMD) $(X11LIBS) WINVIEW_SRC=apps/win_main.c apps/pdfapp.c WINVIEW_RES=apps/win_res.rc @@ -410,7 +409,7 @@ $(OBJDIR)/%.o: apps/%.rc $(WINVIEW_OBJ): $(MUPDF_HDR) $(PDFAPP_HDR) $(WINVIEW_EXE): $(WINVIEW_OBJ) $(MUPDF_LIB) $(MUXPS_LIB) $(THIRD_LIBS) - $(LD_CMD) $(XPSLIBS) $(W32LIBS) + $(LD_CMD) $(W32LIBS) # # Default rules diff --git a/Makethird b/Makethird index bef43aea..d74f1176 100644 --- a/Makethird +++ b/Makethird @@ -9,27 +9,6 @@ openjpeg_dir := $(wildcard thirdparty/openjpeg*/libopenjpeg) freetype_dir := $(wildcard thirdparty/freetype*) jpeg_dir := $(wildcard thirdparty/jpeg*) zlib_dir := $(wildcard thirdparty/zlib*) -expat_dir := $(wildcard thirdparty/expat*/lib) - -ifneq "$(expat_dir)" "" - -THIRD_LIBS += $(EXPAT_LIB) -THIRD_INCS += -I$(expat_dir) -LIBS := $(filter-out -lexpat, $(LIBS)) -XPSLIBS := $(filter-out -lexpat, $(XPSLIBS)) - -EXPAT_SRC=$(addprefix $(expat_dir)/, \ - xmlparse.c \ - xmlrole.c \ - xmltok.c ) -EXPAT_OBJ=$(EXPAT_SRC:$(expat_dir)/%.c=$(OBJDIR)/%.o) -EXPAT_LIB=$(OBJDIR)/libexpat.a -$(EXPAT_LIB): $(EXPAT_OBJ) - $(AR_CMD) -$(OBJDIR)/%.o: $(expat_dir)/%.c - $(CC_CMD) -DHAVE_MEMMOVE - -endif ifneq "$(jbig2dec_dir)" "" diff --git a/apps/pdfapp.c b/apps/pdfapp.c index aec9ad62..332f3bb5 100644 --- a/apps/pdfapp.c +++ b/apps/pdfapp.c @@ -313,7 +313,7 @@ static void pdfapp_loadpage_xps(pdfapp_t *app) page = xps_load_page(app->xps, app->pageno - 1); if (!page) - pdfapp_error(app, fz_throw("cannot load page %d in file '%s'", app->pageno, app->doctitle)); + pdfapp_error(app, fz_rethrow(-1, "cannot load page %d in file '%s'", app->pageno, app->doctitle)); app->page_bbox = fz_transformrect(fz_scale(page->width, page->height), fz_unitrect); app->page_rotate = 0; diff --git a/xps/muxps.h b/xps/muxps.h index d0478e7b..0455ee68 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -48,6 +48,20 @@ void xps_hash_free(xps_context *ctx, xps_hash_table *table, void (*free_value)(xps_context *ctx, void *)); void xps_hash_debug(xps_hash_table *table); +/* + * XML document model + */ + +typedef struct element xml_element; + +xml_element *xml_parse_document(byte *buf, int len); +xml_element *xml_next(xml_element *item); +xml_element *xml_down(xml_element *item); +char *xml_tag(xml_element *item); +char *xml_att(xml_element *item, const char *att); +void xml_free_element(xml_element *item); +void xml_print_element(xml_element *item, int level); + /* * Container parts. */ @@ -84,7 +98,7 @@ struct xps_page_s char *name; int width; int height; - struct xps_item_s *root; + struct element *root; xps_page *next; }; @@ -143,20 +157,6 @@ fz_colorspace *xps_read_icc_colorspace(xps_context *ctx, char *base_uri, char *p void xps_parse_color(xps_context *ctx, char *base_uri, char *hexstring, fz_colorspace **csp, float *samples); void xps_set_color(xps_context *ctx, fz_colorspace *colorspace, float *samples); -/* - * XML document model - */ - -typedef struct xps_item_s xps_item; - -xps_item * xps_parse_xml(xps_context *ctx, byte *buf, int len); -xps_item * xps_next(xps_item *item); -xps_item * xps_down(xps_item *item); -char * xps_tag(xps_item *item); -char * xps_att(xps_item *item, const char *att); -void xps_free_item(xps_context *ctx, xps_item *item); -void xps_debug_item(xps_item *item, int level); - /* * Resource dictionaries. */ @@ -167,15 +167,15 @@ struct xps_resource_s { char *name; char *base_uri; /* only used in the head nodes */ - xps_item *base_xml; /* only used in the head nodes, to free the xml document */ - xps_item *data; + xml_element *base_xml; /* only used in the head nodes, to free the xml document */ + xml_element *data; xps_resource *next; xps_resource *parent; /* up to the previous dict in the stack */ }; -int xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, xps_item *root); +int xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, xml_element *root); void xps_free_resource_dictionary(xps_context *ctx, xps_resource *dict); -void xps_resolve_resource_reference(xps_context *ctx, xps_resource *dict, char **attp, xps_item **tagp, char **urip); +void xps_resolve_resource_reference(xps_context *ctx, xps_resource *dict, char **attp, xml_element **tagp, char **urip); void xps_debug_resource_dictionary(xps_resource *dict); @@ -185,34 +185,34 @@ void xps_debug_resource_dictionary(xps_resource *dict); int xps_load_fixed_page(xps_context *ctx, xps_page *page); void xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page); -void xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_solid_color_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); - -void xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *root, void(*func)(xps_context*, fz_matrix, char*, xps_resource*, xps_item*, void*), void *user); - -void xps_parse_matrix_transform(xps_context *ctx, xps_item *root, fz_matrix *matrix); +void xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node); +void xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node); +void xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node); +void xps_parse_solid_color_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node); +void xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node); +void xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node); +void xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node); +void xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node); + +void xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *root, void(*func)(xps_context*, fz_matrix, char*, xps_resource*, xml_element*, void*), void *user); + +void xps_parse_matrix_transform(xps_context *ctx, xml_element *root, fz_matrix *matrix); void xps_parse_render_transform(xps_context *ctx, char *text, fz_matrix *matrix); void xps_parse_rectangle(xps_context *ctx, char *text, fz_rect *rect); fz_path *xps_parse_abbreviated_geometry(xps_context *ctx, char *geom, int *fill_rule); -fz_path *xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, int stroking, int *fill_rule); +fz_path *xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xml_element *root, int stroking, int *fill_rule); -void xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, char *opacity_att, xps_item *opacity_mask_tag); -void xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, char *opacity_att, xps_item *opacity_mask_tag); +void xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, char *opacity_att, xml_element *opacity_mask_tag); +void xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, char *opacity_att, xml_element *opacity_mask_tag); -void xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node); -void xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node); +void xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node); +void xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node); -void xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xps_item *clip_tag); +void xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xml_element *clip_tag); -int xps_element_has_transparency(xps_context *ctx, char *base_uri, xps_item *node); -int xps_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri, xps_item *node); -int xps_image_brush_has_transparency(xps_context *ctx, char *base_uri, xps_item *root); +int xps_element_has_transparency(xps_context *ctx, char *base_uri, xml_element *node); +int xps_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri, xml_element *node); +int xps_image_brush_has_transparency(xps_context *ctx, char *base_uri, xml_element *root); /* * The interpreter context. diff --git a/xps/xpsanalyze.c b/xps/xpsanalyze.c index 9cd29840..86dd4626 100644 --- a/xps/xpsanalyze.c +++ b/xps/xpsanalyze.c @@ -14,16 +14,16 @@ xps_remote_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri } int -xps_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) +xps_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri, xml_element *root) { char *source; - xps_item *node; + xml_element *node; - source = xps_att(root, "Source"); + source = xml_att(root, "Source"); if (source) return xps_remote_resource_dictionary_has_transparency(ctx, base_uri, source); - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { // TODO: ... all kinds of stuff can be here, brushes, elements, whatnot } @@ -32,18 +32,18 @@ xps_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri, xps_i } static int -xps_gradient_stops_have_transparency(xps_context *ctx, char *base_uri, xps_item *root) +xps_gradient_stops_have_transparency(xps_context *ctx, char *base_uri, xml_element *root) { - xps_item *node; + xml_element *node; fz_colorspace *colorspace; char *color_att; float samples[32]; - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "GradientStop")) + if (!strcmp(xml_tag(node), "GradientStop")) { - color_att = xps_att(node, "Color"); + color_att = xml_att(node, "Color"); if (color_att) { xps_parse_color(ctx, base_uri, color_att, &colorspace, samples); @@ -60,12 +60,12 @@ xps_gradient_stops_have_transparency(xps_context *ctx, char *base_uri, xps_item } static int -xps_gradient_brush_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) +xps_gradient_brush_has_transparency(xps_context *ctx, char *base_uri, xml_element *root) { - xps_item *node; + xml_element *node; char *opacity_att; - opacity_att = xps_att(root, "Opacity"); + opacity_att = xml_att(root, "Opacity"); if (opacity_att) { if (atof(opacity_att) < 1.0) @@ -75,14 +75,14 @@ xps_gradient_brush_has_transparency(xps_context *ctx, char *base_uri, xps_item * } } - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "RadialGradientBrush.GradientStops")) + if (!strcmp(xml_tag(node), "RadialGradientBrush.GradientStops")) { if (xps_gradient_stops_have_transparency(ctx, base_uri, node)) return 1; } - if (!strcmp(xps_tag(node), "LinearGradientBrush.GradientStops")) + if (!strcmp(xml_tag(node), "LinearGradientBrush.GradientStops")) { if (xps_gradient_stops_have_transparency(ctx, base_uri, node)) return 1; @@ -93,18 +93,18 @@ xps_gradient_brush_has_transparency(xps_context *ctx, char *base_uri, xps_item * } static int -xps_brush_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) +xps_brush_has_transparency(xps_context *ctx, char *base_uri, xml_element *root) { char *opacity_att; char *color_att; - xps_item *node; + xml_element *node; fz_colorspace *colorspace; float samples[32]; - if (!strcmp(xps_tag(root), "SolidColorBrush")) + if (!strcmp(xml_tag(root), "SolidColorBrush")) { - opacity_att = xps_att(root, "Opacity"); + opacity_att = xml_att(root, "Opacity"); if (opacity_att) { if (atof(opacity_att) < 1.0) @@ -114,7 +114,7 @@ xps_brush_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) } } - color_att = xps_att(root, "Color"); + color_att = xml_att(root, "Color"); if (color_att) { xps_parse_color(ctx, base_uri, color_att, &colorspace, samples); @@ -126,9 +126,9 @@ xps_brush_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) } } - if (!strcmp(xps_tag(root), "VisualBrush")) + if (!strcmp(xml_tag(root), "VisualBrush")) { - char *opacity_att = xps_att(root, "Opacity"); + char *opacity_att = xml_att(root, "Opacity"); if (opacity_att) { if (atof(opacity_att) < 1.0) @@ -138,29 +138,29 @@ xps_brush_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) } } - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "VisualBrush.Visual")) + if (!strcmp(xml_tag(node), "VisualBrush.Visual")) { - if (xps_element_has_transparency(ctx, base_uri, xps_down(node))) + if (xps_element_has_transparency(ctx, base_uri, xml_down(node))) return 1; } } } - if (!strcmp(xps_tag(root), "ImageBrush")) + if (!strcmp(xml_tag(root), "ImageBrush")) { if (xps_image_brush_has_transparency(ctx, base_uri, root)) return 1; } - if (!strcmp(xps_tag(root), "LinearGradientBrush")) + if (!strcmp(xml_tag(root), "LinearGradientBrush")) { if (xps_gradient_brush_has_transparency(ctx, base_uri, root)) return 1; } - if (!strcmp(xps_tag(root), "RadialGradientBrush")) + if (!strcmp(xml_tag(root), "RadialGradientBrush")) { if (xps_gradient_brush_has_transparency(ctx, base_uri, root)) return 1; @@ -170,27 +170,27 @@ xps_brush_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) } static int -xps_path_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) +xps_path_has_transparency(xps_context *ctx, char *base_uri, xml_element *root) { - xps_item *node; + xml_element *node; - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "Path.OpacityMask")) + if (!strcmp(xml_tag(node), "Path.OpacityMask")) { //dputs("page has transparency: Path.OpacityMask\n"); return 1; } - if (!strcmp(xps_tag(node), "Path.Stroke")) + if (!strcmp(xml_tag(node), "Path.Stroke")) { - if (xps_brush_has_transparency(ctx, base_uri, xps_down(node))) + if (xps_brush_has_transparency(ctx, base_uri, xml_down(node))) return 1; } - if (!strcmp(xps_tag(node), "Path.Fill")) + if (!strcmp(xml_tag(node), "Path.Fill")) { - if (xps_brush_has_transparency(ctx, base_uri, xps_down(node))) + if (xps_brush_has_transparency(ctx, base_uri, xml_down(node))) return 1; } } @@ -199,21 +199,21 @@ xps_path_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) } static int -xps_glyphs_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) +xps_glyphs_has_transparency(xps_context *ctx, char *base_uri, xml_element *root) { - xps_item *node; + xml_element *node; - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "Glyphs.OpacityMask")) + if (!strcmp(xml_tag(node), "Glyphs.OpacityMask")) { //dputs("page has transparency: Glyphs.OpacityMask\n"); return 1; } - if (!strcmp(xps_tag(node), "Glyphs.Fill")) + if (!strcmp(xml_tag(node), "Glyphs.Fill")) { - if (xps_brush_has_transparency(ctx, base_uri, xps_down(node))) + if (xps_brush_has_transparency(ctx, base_uri, xml_down(node))) return 1; } } @@ -222,19 +222,19 @@ xps_glyphs_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) } static int -xps_canvas_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) +xps_canvas_has_transparency(xps_context *ctx, char *base_uri, xml_element *root) { - xps_item *node; + xml_element *node; - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "Canvas.Resources")) + if (!strcmp(xml_tag(node), "Canvas.Resources")) { - if (xps_resource_dictionary_has_transparency(ctx, base_uri, xps_down(node))) + if (xps_resource_dictionary_has_transparency(ctx, base_uri, xml_down(node))) return 1; } - if (!strcmp(xps_tag(node), "Canvas.OpacityMask")) + if (!strcmp(xml_tag(node), "Canvas.OpacityMask")) { //dputs("page has transparency: Canvas.OpacityMask\n"); return 1; @@ -248,7 +248,7 @@ xps_canvas_has_transparency(xps_context *ctx, char *base_uri, xps_item *root) } int -xps_element_has_transparency(xps_context *ctx, char *base_uri, xps_item *node) +xps_element_has_transparency(xps_context *ctx, char *base_uri, xml_element *node) { char *opacity_att; char *stroke_att; @@ -257,7 +257,7 @@ xps_element_has_transparency(xps_context *ctx, char *base_uri, xps_item *node) fz_colorspace *colorspace; float samples[32]; - stroke_att = xps_att(node, "Stroke"); + stroke_att = xml_att(node, "Stroke"); if (stroke_att) { xps_parse_color(ctx, base_uri, stroke_att, &colorspace, samples); @@ -268,7 +268,7 @@ xps_element_has_transparency(xps_context *ctx, char *base_uri, xps_item *node) } } - fill_att = xps_att(node, "Fill"); + fill_att = xml_att(node, "Fill"); if (fill_att) { xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples); @@ -279,7 +279,7 @@ xps_element_has_transparency(xps_context *ctx, char *base_uri, xps_item *node) } } - opacity_att = xps_att(node, "Opacity"); + opacity_att = xml_att(node, "Opacity"); if (opacity_att) { if (atof(opacity_att) < 1.0) @@ -289,19 +289,19 @@ xps_element_has_transparency(xps_context *ctx, char *base_uri, xps_item *node) } } - if (xps_att(node, "OpacityMask")) + if (xml_att(node, "OpacityMask")) { //dputs("page has transparency: OpacityMask\n"); return 1; } - if (!strcmp(xps_tag(node), "Path")) + if (!strcmp(xml_tag(node), "Path")) if (xps_path_has_transparency(ctx, base_uri, node)) return 1; - if (!strcmp(xps_tag(node), "Glyphs")) + if (!strcmp(xml_tag(node), "Glyphs")) if (xps_glyphs_has_transparency(ctx, base_uri, node)) return 1; - if (!strcmp(xps_tag(node), "Canvas")) + if (!strcmp(xml_tag(node), "Canvas")) if (xps_canvas_has_transparency(ctx, base_uri, node)) return 1; diff --git a/xps/xpscommon.c b/xps/xpscommon.c index 063435c5..77aec58c 100644 --- a/xps/xpscommon.c +++ b/xps/xpscommon.c @@ -2,29 +2,29 @@ #include "muxps.h" void -xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xps_item *node) +xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node) { /* SolidColorBrushes are handled in a special case and will never show up here */ - if (!strcmp(xps_tag(node), "ImageBrush")) + if (!strcmp(xml_tag(node), "ImageBrush")) xps_parse_image_brush(ctx, ctm, area, base_uri, dict, node); - else if (!strcmp(xps_tag(node), "VisualBrush")) + else if (!strcmp(xml_tag(node), "VisualBrush")) xps_parse_visual_brush(ctx, ctm, area, base_uri, dict, node); - else if (!strcmp(xps_tag(node), "LinearGradientBrush")) + else if (!strcmp(xml_tag(node), "LinearGradientBrush")) xps_parse_linear_gradient_brush(ctx, ctm, area, base_uri, dict, node); - else if (!strcmp(xps_tag(node), "RadialGradientBrush")) + else if (!strcmp(xml_tag(node), "RadialGradientBrush")) xps_parse_radial_gradient_brush(ctx, ctm, area, base_uri, dict, node); else - fz_warn("unknown brush tag: %s", xps_tag(node)); + fz_warn("unknown brush tag: %s", xml_tag(node)); } void -xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *node) +xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node) { - if (!strcmp(xps_tag(node), "Path")) + if (!strcmp(xml_tag(node), "Path")) xps_parse_path(ctx, ctm, base_uri, dict, node); - if (!strcmp(xps_tag(node), "Glyphs")) + if (!strcmp(xml_tag(node), "Glyphs")) xps_parse_glyphs(ctx, ctm, base_uri, dict, node); - if (!strcmp(xps_tag(node), "Canvas")) + if (!strcmp(xml_tag(node), "Canvas")) xps_parse_canvas(ctx, ctm, base_uri, dict, node); /* skip unknown tags (like Foo.Resources and similar) */ } @@ -55,15 +55,15 @@ xps_parse_render_transform(xps_context *ctx, char *transform, fz_matrix *matrix) } void -xps_parse_matrix_transform(xps_context *ctx, xps_item *root, fz_matrix *matrix) +xps_parse_matrix_transform(xps_context *ctx, xml_element *root, fz_matrix *matrix) { char *transform; *matrix = fz_identity; - if (!strcmp(xps_tag(root), "MatrixTransform")) + if (!strcmp(xml_tag(root), "MatrixTransform")) { - transform = xps_att(root, "Matrix"); + transform = xml_att(root, "Matrix"); if (transform) xps_parse_render_transform(ctx, transform, matrix); } diff --git a/xps/xpsdoc.c b/xps/xpsdoc.c index a37ed0e9..94fb32d7 100644 --- a/xps/xpsdoc.c +++ b/xps/xpsdoc.c @@ -1,8 +1,6 @@ #include "fitz.h" #include "muxps.h" -#include - xps_part * xps_new_part(xps_context *ctx, char *name, int size) { @@ -11,7 +9,8 @@ xps_new_part(xps_context *ctx, char *name, int size) part = fz_malloc(sizeof(xps_part)); part->name = fz_strdup(name); part->size = size; - part->data = fz_malloc(size); + part->data = fz_malloc(size + 1); + part->data[size] = 0; /* null-terminate for xml parser */ return part; } @@ -137,87 +136,63 @@ xps_free_fixed_pages(xps_context *ctx) } /* - * Parse the fixed document sequence structure and _rels/.rels to find the - * start part. We hook up unique expat handlers for this, since we don't need - * the full document model. + * Parse the fixed document sequence structure and _rels/.rels to find the start part. */ static void -xps_parse_metadata_imp(void *zp, char *name, char **atts) +xps_parse_metadata_imp(xps_context *ctx, xml_element *item) { - xps_context *ctx = zp; - int i; - - if (!strcmp(name, "Relationship")) - { - char tgtbuf[1024]; - char *target = NULL; - char *type = NULL; - - for (i = 0; atts[i]; i += 2) - { - if (!strcmp(atts[i], "Target")) - target = atts[i + 1]; - if (!strcmp(atts[i], "Type")) - type = atts[i + 1]; - } - - if (target && type) - { - xps_absolute_path(tgtbuf, ctx->base_uri, target, sizeof tgtbuf); - if (!strcmp(type, REL_START_PART)) - ctx->start_part = fz_strdup(tgtbuf); - } - } - - if (!strcmp(name, "DocumentReference")) + while (item) { - char *source = NULL; - char srcbuf[1024]; + xps_parse_metadata_imp(ctx, xml_down(item)); - for (i = 0; atts[i]; i += 2) + if (!strcmp(xml_tag(item), "Relationship")) { - if (!strcmp(atts[i], "Source")) - source = atts[i + 1]; + char *target = xml_att(item, "Target"); + char *type = xml_att(item, "Type"); + if (target && type) + { + char tgtbuf[1024]; + xps_absolute_path(tgtbuf, ctx->base_uri, target, sizeof tgtbuf); + if (!strcmp(type, REL_START_PART)) + ctx->start_part = fz_strdup(tgtbuf); + } } - if (source) + if (!strcmp(xml_tag(item), "DocumentReference")) { - xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf); - xps_add_fixed_document(ctx, srcbuf); + char *source = xml_att(item, "Source"); + if (source) + { + char srcbuf[1024]; + xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf); + xps_add_fixed_document(ctx, srcbuf); + } } - } - - if (!strcmp(name, "PageContent")) - { - char *source = NULL; - char srcbuf[1024]; - int width = 0; - int height = 0; - for (i = 0; atts[i]; i += 2) + if (!strcmp(xml_tag(item), "PageContent")) { - if (!strcmp(atts[i], "Source")) - source = atts[i + 1]; - if (!strcmp(atts[i], "Width")) - width = atoi(atts[i + 1]); - if (!strcmp(atts[i], "Height")) - height = atoi(atts[i + 1]); + char *source = xml_att(item, "Source"); + char *width_att = xml_att(item, "Width"); + char *height_att = xml_att(item, "Height"); + int width = width_att ? atoi(width_att) : 0; + int height = height_att ? atoi(height_att) : 0; + if (source) + { + char srcbuf[1024]; + xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf); + xps_add_fixed_page(ctx, srcbuf, width, height); + } } - if (source) - { - xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf); - xps_add_fixed_page(ctx, srcbuf, width, height); - } + item = xml_next(item); } } int xps_parse_metadata(xps_context *ctx, xps_part *part) { - XML_Parser xp; - int code; + xml_element *root; char buf[1024]; char *s; @@ -238,23 +213,16 @@ xps_parse_metadata(xps_context *ctx, xps_part *part) ctx->base_uri = buf; ctx->part_uri = part->name; - xp = XML_ParserCreate(NULL); - if (!xp) - return fz_throw("cannot create XML parser"); + root = xml_parse_document(part->data, part->size); + if (!root) + return fz_rethrow(-1, "cannot parse metadata part '%s'", part->name); - XML_SetUserData(xp, ctx); - XML_SetParamEntityParsing(xp, XML_PARAM_ENTITY_PARSING_NEVER); - XML_SetStartElementHandler(xp, (XML_StartElementHandler)xps_parse_metadata_imp); + xps_parse_metadata_imp(ctx, root); - code = XML_Parse(xp, (char*)part->data, part->size, 1); - - XML_ParserFree(xp); + xml_free_element(root); ctx->base_uri = NULL; ctx->part_uri = NULL; - if (code == 0) - return fz_throw("cannot parse XML in part: %s", part->name); - - return 0; + return fz_okay; } diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index f62cc928..1936534b 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -286,9 +286,9 @@ xps_parse_glyphs_imp(xps_context *ctx, fz_matrix ctm, fz_font *font, float size, void xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, - char *base_uri, xps_resource *dict, xps_item *root) + char *base_uri, xps_resource *dict, xml_element *root) { - xps_item *node; + xml_element *node; int code; char *fill_uri; @@ -310,10 +310,10 @@ xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, char *opacity_att; char *opacity_mask_att; - xps_item *transform_tag = NULL; - xps_item *clip_tag = NULL; - xps_item *fill_tag = NULL; - xps_item *opacity_mask_tag = NULL; + xml_element *transform_tag = NULL; + xml_element *clip_tag = NULL; + xml_element *fill_tag = NULL; + xml_element *opacity_mask_tag = NULL; char *fill_opacity_att = NULL; @@ -335,32 +335,32 @@ xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, * Extract attributes and extended attributes. */ - bidi_level_att = xps_att(root, "BidiLevel"); - caret_stops_att = xps_att(root, "CaretStops"); - fill_att = xps_att(root, "Fill"); - font_size_att = xps_att(root, "FontRenderingEmSize"); - font_uri_att = xps_att(root, "FontUri"); - origin_x_att = xps_att(root, "OriginX"); - origin_y_att = xps_att(root, "OriginY"); - is_sideways_att = xps_att(root, "IsSideways"); - indices_att = xps_att(root, "Indices"); - unicode_att = xps_att(root, "UnicodeString"); - style_att = xps_att(root, "StyleSimulations"); - transform_att = xps_att(root, "RenderTransform"); - clip_att = xps_att(root, "Clip"); - opacity_att = xps_att(root, "Opacity"); - opacity_mask_att = xps_att(root, "OpacityMask"); - - for (node = xps_down(root); node; node = xps_next(node)) + bidi_level_att = xml_att(root, "BidiLevel"); + caret_stops_att = xml_att(root, "CaretStops"); + fill_att = xml_att(root, "Fill"); + font_size_att = xml_att(root, "FontRenderingEmSize"); + font_uri_att = xml_att(root, "FontUri"); + origin_x_att = xml_att(root, "OriginX"); + origin_y_att = xml_att(root, "OriginY"); + is_sideways_att = xml_att(root, "IsSideways"); + indices_att = xml_att(root, "Indices"); + unicode_att = xml_att(root, "UnicodeString"); + style_att = xml_att(root, "StyleSimulations"); + transform_att = xml_att(root, "RenderTransform"); + clip_att = xml_att(root, "Clip"); + opacity_att = xml_att(root, "Opacity"); + opacity_mask_att = xml_att(root, "OpacityMask"); + + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "Glyphs.RenderTransform")) - transform_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Glyphs.OpacityMask")) - opacity_mask_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Glyphs.Clip")) - clip_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Glyphs.Fill")) - fill_tag = xps_down(node); + if (!strcmp(xml_tag(node), "Glyphs.RenderTransform")) + transform_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Glyphs.OpacityMask")) + opacity_mask_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Glyphs.Clip")) + clip_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Glyphs.Fill")) + fill_tag = xml_down(node); } fill_uri = base_uri; @@ -462,10 +462,10 @@ xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, * If it's a solid color brush fill/stroke do a simple fill */ - if (fill_tag && !strcmp(xps_tag(fill_tag), "SolidColorBrush")) + if (fill_tag && !strcmp(xml_tag(fill_tag), "SolidColorBrush")) { - fill_opacity_att = xps_att(fill_tag, "Opacity"); - fill_att = xps_att(fill_tag, "Color"); + fill_opacity_att = xml_att(fill_tag, "Opacity"); + fill_att = xml_att(fill_tag, "Color"); fill_tag = NULL; } diff --git a/xps/xpsgradient.c b/xps/xpsgradient.c index 180224f8..8c877a0b 100644 --- a/xps/xpsgradient.c +++ b/xps/xpsgradient.c @@ -35,7 +35,7 @@ static inline float lerp(float a, float b, float x) } static int -xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xps_item *node, +xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xml_element *node, struct stop *stops, int maxcount) { fz_colorspace *colorspace; @@ -51,10 +51,10 @@ xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xps_item *node, count = 0; while (node && count < maxcount) { - if (!strcmp(xps_tag(node), "GradientStop")) + if (!strcmp(xml_tag(node), "GradientStop")) { - char *offset = xps_att(node, "Offset"); - char *color = xps_att(node, "Color"); + char *offset = xml_att(node, "Offset"); + char *color = xml_att(node, "Color"); if (offset && color) { stops[count].offset = atof(offset); @@ -71,7 +71,7 @@ xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xps_item *node, count ++; } } - node = xps_next(node); + node = xml_next(node); } if (count == 0) @@ -299,7 +299,7 @@ static inline float point_inside_circle(float px, float py, float x, float y, fl static void xps_draw_radial_gradient(xps_context *ctx, fz_matrix ctm, struct stop *stops, int count, - xps_item *root, int spread) + xml_element *root, int spread) { float x0, y0, r0; float x1, y1, r1; @@ -307,10 +307,10 @@ xps_draw_radial_gradient(xps_context *ctx, fz_matrix ctm, float yrad = 1; float invscale; - char *center_att = xps_att(root, "Center"); - char *origin_att = xps_att(root, "GradientOrigin"); - char *radius_x_att = xps_att(root, "RadiusX"); - char *radius_y_att = xps_att(root, "RadiusY"); + char *center_att = xml_att(root, "Center"); + char *origin_att = xml_att(root, "GradientOrigin"); + char *radius_x_att = xml_att(root, "RadiusX"); + char *radius_y_att = xml_att(root, "RadiusY"); if (origin_att) sscanf(origin_att, "%g,%g", &x0, &y0); @@ -342,12 +342,12 @@ xps_draw_radial_gradient(xps_context *ctx, fz_matrix ctm, static void xps_draw_linear_gradient(xps_context *ctx, fz_matrix ctm, struct stop *stops, int count, - xps_item *root, int spread) + xml_element *root, int spread) { float x0, y0, x1, y1; - char *start_point_att = xps_att(root, "StartPoint"); - char *end_point_att = xps_att(root, "EndPoint"); + char *start_point_att = xml_att(root, "StartPoint"); + char *end_point_att = xml_att(root, "EndPoint"); x0 = y0 = 0; x1 = y1 = 1; @@ -367,10 +367,10 @@ xps_draw_linear_gradient(xps_context *ctx, fz_matrix ctm, static void xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, xps_item *root, - void (*draw)(xps_context *, fz_matrix, struct stop *, int, xps_item *, int)) + char *base_uri, xps_resource *dict, xml_element *root, + void (*draw)(xps_context *, fz_matrix, struct stop *, int, xml_element *, int)) { - xps_item *node; + xml_element *node; char *opacity_att; char *interpolation_att; @@ -378,30 +378,30 @@ xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *mapping_att; char *transform_att; - xps_item *transform_tag = NULL; - xps_item *stop_tag = NULL; + xml_element *transform_tag = NULL; + xml_element *stop_tag = NULL; struct stop stop_list[MAX_STOPS]; int stop_count; fz_matrix transform; int spread_method; - opacity_att = xps_att(root, "Opacity"); - interpolation_att = xps_att(root, "ColorInterpolationMode"); - spread_att = xps_att(root, "SpreadMethod"); - mapping_att = xps_att(root, "MappingMode"); - transform_att = xps_att(root, "Transform"); + opacity_att = xml_att(root, "Opacity"); + interpolation_att = xml_att(root, "ColorInterpolationMode"); + spread_att = xml_att(root, "SpreadMethod"); + mapping_att = xml_att(root, "MappingMode"); + transform_att = xml_att(root, "Transform"); - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "LinearGradientBrush.Transform")) - transform_tag = xps_down(node); - if (!strcmp(xps_tag(node), "RadialGradientBrush.Transform")) - transform_tag = xps_down(node); - if (!strcmp(xps_tag(node), "LinearGradientBrush.GradientStops")) - stop_tag = xps_down(node); - if (!strcmp(xps_tag(node), "RadialGradientBrush.GradientStops")) - stop_tag = xps_down(node); + if (!strcmp(xml_tag(node), "LinearGradientBrush.Transform")) + transform_tag = xml_down(node); + if (!strcmp(xml_tag(node), "RadialGradientBrush.Transform")) + transform_tag = xml_down(node); + if (!strcmp(xml_tag(node), "LinearGradientBrush.GradientStops")) + stop_tag = xml_down(node); + if (!strcmp(xml_tag(node), "RadialGradientBrush.GradientStops")) + stop_tag = xml_down(node); } xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); @@ -447,14 +447,14 @@ xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, void xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, xps_item *root) + char *base_uri, xps_resource *dict, xml_element *root) { xps_parse_gradient_brush(ctx, ctm, area, base_uri, dict, root, xps_draw_linear_gradient); } void xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, xps_item *root) + char *base_uri, xps_resource *dict, xml_element *root) { xps_parse_gradient_brush(ctx, ctm, area, base_uri, dict, root, xps_draw_radial_gradient); } diff --git a/xps/xpsimage.c b/xps/xpsimage.c index 102051cb..c9bf64fd 100644 --- a/xps/xpsimage.c +++ b/xps/xpsimage.c @@ -42,7 +42,7 @@ xps_decode_image(xps_image **imagep, xps_context *ctx, xps_part *part) } static void -xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root, void *vimage) +xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root, void *vimage) { xps_image *image = vimage; fz_pixmap *pixmap = image->pixmap; @@ -55,7 +55,7 @@ xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resou } static xps_part * -xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xps_item *root) +xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xml_element *root) { char *image_source_att; char buf[1024]; @@ -64,7 +64,7 @@ xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xps_item *roo char *profile_name; char *p; - image_source_att = xps_att(root, "ImageSource"); + image_source_att = xml_att(root, "ImageSource"); if (!image_source_att) return NULL; @@ -106,7 +106,7 @@ xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xps_item *roo void xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, xps_item *root) + char *base_uri, xps_resource *dict, xml_element *root) { xps_part *part; xps_image *image; diff --git a/xps/xpsopacity.c b/xps/xpsopacity.c index e5b0f787..75064452 100644 --- a/xps/xpsopacity.c +++ b/xps/xpsopacity.c @@ -4,7 +4,7 @@ void xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, - char *opacity_att, xps_item *opacity_mask_tag) + char *opacity_att, xml_element *opacity_mask_tag) { float opacity; @@ -15,10 +15,10 @@ xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, if (opacity_att) opacity = atof(opacity_att); - if (opacity_mask_tag && !strcmp(xps_tag(opacity_mask_tag), "SolidColorBrush")) + if (opacity_mask_tag && !strcmp(xml_tag(opacity_mask_tag), "SolidColorBrush")) { - char *scb_opacity_att = xps_att(opacity_mask_tag, "Opacity"); - char *scb_color_att = xps_att(opacity_mask_tag, "Color"); + char *scb_opacity_att = xml_att(opacity_mask_tag, "Opacity"); + char *scb_color_att = xml_att(opacity_mask_tag, "Color"); if (scb_opacity_att) opacity = opacity * atof(scb_opacity_att); if (scb_color_att) @@ -47,7 +47,7 @@ xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, void xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, - char *opacity_att, xps_item *opacity_mask_tag) + char *opacity_att, xml_element *opacity_mask_tag) { if (!opacity_att && !opacity_mask_tag) return; @@ -57,7 +57,7 @@ xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, if (opacity_mask_tag) { - if (strcmp(xps_tag(opacity_mask_tag), "SolidColorBrush")) + if (strcmp(xml_tag(opacity_mask_tag), "SolidColorBrush")) ctx->dev->popclip(ctx->dev->user); } } diff --git a/xps/xpspage.c b/xps/xpspage.c index 1854b1fc..7659a1a7 100644 --- a/xps/xpspage.c +++ b/xps/xpspage.c @@ -2,10 +2,10 @@ #include "muxps.h" void -xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root) +xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root) { xps_resource *new_dict = NULL; - xps_item *node; + xml_element *node; char *opacity_mask_uri; int code; @@ -14,22 +14,22 @@ xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource * char *opacity_att; char *opacity_mask_att; - xps_item *transform_tag = NULL; - xps_item *clip_tag = NULL; - xps_item *opacity_mask_tag = NULL; + xml_element *transform_tag = NULL; + xml_element *clip_tag = NULL; + xml_element *opacity_mask_tag = NULL; fz_matrix transform; - transform_att = xps_att(root, "RenderTransform"); - clip_att = xps_att(root, "Clip"); - opacity_att = xps_att(root, "Opacity"); - opacity_mask_att = xps_att(root, "OpacityMask"); + transform_att = xml_att(root, "RenderTransform"); + clip_att = xml_att(root, "Clip"); + opacity_att = xml_att(root, "Opacity"); + opacity_mask_att = xml_att(root, "OpacityMask"); - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "Canvas.Resources") && xps_down(node)) + if (!strcmp(xml_tag(node), "Canvas.Resources") && xml_down(node)) { - code = xps_parse_resource_dictionary(ctx, &new_dict, base_uri, xps_down(node)); + code = xps_parse_resource_dictionary(ctx, &new_dict, base_uri, xml_down(node)); if (code) fz_catch(code, "cannot load Canvas.Resources"); else @@ -39,12 +39,12 @@ xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource * } } - if (!strcmp(xps_tag(node), "Canvas.RenderTransform")) - transform_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Canvas.Clip")) - clip_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Canvas.OpacityMask")) - opacity_mask_tag = xps_down(node); + if (!strcmp(xml_tag(node), "Canvas.RenderTransform")) + transform_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Canvas.Clip")) + clip_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Canvas.OpacityMask")) + opacity_mask_tag = xml_down(node); } opacity_mask_uri = base_uri; @@ -64,7 +64,7 @@ xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource * xps_begin_opacity(ctx, ctm, fz_infiniterect, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { xps_parse_element(ctx, ctm, base_uri, dict, node); } @@ -81,7 +81,7 @@ xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource * void xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page) { - xps_item *node; + xml_element *node; xps_resource *dict; char base_uri[1024]; char *s; @@ -97,11 +97,14 @@ xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page) ctx->opacity_top = 0; ctx->opacity[0] = 1; - for (node = xps_down(page->root); node; node = xps_next(node)) + if (!page->root) + return; + + for (node = xml_down(page->root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "FixedPage.Resources") && xps_down(node)) + if (!strcmp(xml_tag(node), "FixedPage.Resources") && xml_down(node)) { - code = xps_parse_resource_dictionary(ctx, &dict, base_uri, xps_down(node)); + code = xps_parse_resource_dictionary(ctx, &dict, base_uri, xml_down(node)); if (code) fz_catch(code, "cannot load FixedPage.Resources"); } @@ -118,7 +121,7 @@ int xps_load_fixed_page(xps_context *ctx, xps_page *page) { xps_part *part; - xps_item *root; + xml_element *root; char *width_att; char *height_att; @@ -126,20 +129,20 @@ xps_load_fixed_page(xps_context *ctx, xps_page *page) if (!part) return fz_rethrow(-1, "cannot read zip part '%s'", page->name); - root = xps_parse_xml(ctx, part->data, part->size); + root = xml_parse_document(part->data, part->size); if (!root) - return fz_rethrow(-1, "cannot parse xml"); + return fz_rethrow(-1, "cannot parse xml part '%s'", page->name); xps_free_part(ctx, part); - if (strcmp(xps_tag(root), "FixedPage")) - return fz_throw("expected FixedPage element (found %s)", xps_tag(root)); + if (strcmp(xml_tag(root), "FixedPage")) + return fz_throw("expected FixedPage element (found %s)", xml_tag(root)); - width_att = xps_att(root, "Width"); + width_att = xml_att(root, "Width"); if (!width_att) return fz_throw("FixedPage missing required attribute: Width"); - height_att = xps_att(root, "Height"); + height_att = xml_att(root, "Height"); if (!height_att) return fz_throw("FixedPage missing required attribute: Height"); diff --git a/xps/xpspath.c b/xps/xpspath.c index ded566ca..00e57bee 100644 --- a/xps/xpspath.c +++ b/xps/xpspath.c @@ -36,7 +36,7 @@ fz_currentpoint(fz_path *path) } void -xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xps_item *clip_tag) +xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xml_element *clip_tag) { fz_path *path; int fill_rule = 0; @@ -462,7 +462,7 @@ xps_parse_abbreviated_geometry(xps_context *ctx, char *geom, int *fill_rule) } static void -xps_parse_arc_segment(fz_path *path, xps_item *root, int stroking, int *skipped_stroke) +xps_parse_arc_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke) { /* ArcSegment pretty much follows the SVG algorithm for converting an * arc in endpoint representation to an arc in centerpoint @@ -475,12 +475,12 @@ xps_parse_arc_segment(fz_path *path, xps_item *root, int stroking, int *skipped_ float size_x, size_y; int is_stroked; - char *point_att = xps_att(root, "Point"); - char *size_att = xps_att(root, "Size"); - char *rotation_angle_att = xps_att(root, "RotationAngle"); - char *is_large_arc_att = xps_att(root, "IsLargeArc"); - char *sweep_direction_att = xps_att(root, "SweepDirection"); - char *is_stroked_att = xps_att(root, "IsStroked"); + char *point_att = xml_att(root, "Point"); + char *size_att = xml_att(root, "Size"); + char *rotation_angle_att = xml_att(root, "RotationAngle"); + char *is_large_arc_att = xml_att(root, "IsLargeArc"); + char *sweep_direction_att = xml_att(root, "SweepDirection"); + char *is_stroked_att = xml_att(root, "IsStroked"); if (!point_att || !size_att || !rotation_angle_att || !is_large_arc_att || !sweep_direction_att) { @@ -510,10 +510,10 @@ xps_parse_arc_segment(fz_path *path, xps_item *root, int stroking, int *skipped_ } static void -xps_parse_poly_quadratic_bezier_segment(fz_path *path, xps_item *root, int stroking, int *skipped_stroke) +xps_parse_poly_quadratic_bezier_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke) { - char *points_att = xps_att(root, "Points"); - char *is_stroked_att = xps_att(root, "IsStroked"); + char *points_att = xml_att(root, "Points"); + char *is_stroked_att = xml_att(root, "IsStroked"); float x[2], y[2]; int is_stroked; fz_point pt; @@ -560,10 +560,10 @@ xps_parse_poly_quadratic_bezier_segment(fz_path *path, xps_item *root, int strok } static void -xps_parse_poly_bezier_segment(fz_path *path, xps_item *root, int stroking, int *skipped_stroke) +xps_parse_poly_bezier_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke) { - char *points_att = xps_att(root, "Points"); - char *is_stroked_att = xps_att(root, "IsStroked"); + char *points_att = xml_att(root, "Points"); + char *is_stroked_att = xml_att(root, "IsStroked"); float x[3], y[3]; int is_stroked; char *s; @@ -601,10 +601,10 @@ xps_parse_poly_bezier_segment(fz_path *path, xps_item *root, int stroking, int * } static void -xps_parse_poly_line_segment(fz_path *path, xps_item *root, int stroking, int *skipped_stroke) +xps_parse_poly_line_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke) { - char *points_att = xps_att(root, "Points"); - char *is_stroked_att = xps_att(root, "IsStroked"); + char *points_att = xml_att(root, "Points"); + char *is_stroked_att = xml_att(root, "IsStroked"); int is_stroked; float x, y; char *s; @@ -635,9 +635,9 @@ xps_parse_poly_line_segment(fz_path *path, xps_item *root, int stroking, int *sk } static void -xps_parse_path_figure(fz_path *path, xps_item *root, int stroking) +xps_parse_path_figure(fz_path *path, xml_element *root, int stroking) { - xps_item *node; + xml_element *node; char *is_closed_att; char *start_point_att; @@ -650,9 +650,9 @@ xps_parse_path_figure(fz_path *path, xps_item *root, int stroking) int skipped_stroke = 0; - is_closed_att = xps_att(root, "IsClosed"); - start_point_att = xps_att(root, "StartPoint"); - is_filled_att = xps_att(root, "IsFilled"); + is_closed_att = xml_att(root, "IsClosed"); + start_point_att = xml_att(root, "StartPoint"); + is_filled_att = xml_att(root, "IsFilled"); if (is_closed_att) is_closed = !strcmp(is_closed_att, "true"); @@ -666,15 +666,15 @@ xps_parse_path_figure(fz_path *path, xps_item *root, int stroking) fz_moveto(path, start_x, start_y); - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "ArcSegment")) + if (!strcmp(xml_tag(node), "ArcSegment")) xps_parse_arc_segment(path, node, stroking, &skipped_stroke); - if (!strcmp(xps_tag(node), "PolyBezierSegment")) + if (!strcmp(xml_tag(node), "PolyBezierSegment")) xps_parse_poly_bezier_segment(path, node, stroking, &skipped_stroke); - if (!strcmp(xps_tag(node), "PolyLineSegment")) + if (!strcmp(xml_tag(node), "PolyLineSegment")) xps_parse_poly_line_segment(path, node, stroking, &skipped_stroke); - if (!strcmp(xps_tag(node), "PolyQuadraticBezierSegment")) + if (!strcmp(xml_tag(node), "PolyQuadraticBezierSegment")) xps_parse_poly_quadratic_bezier_segment(path, node, stroking, &skipped_stroke); } @@ -688,28 +688,28 @@ xps_parse_path_figure(fz_path *path, xps_item *root, int stroking) } fz_path * -xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, int stroking, int *fill_rule) +xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xml_element *root, int stroking, int *fill_rule) { - xps_item *node; + xml_element *node; char *figures_att; char *fill_rule_att; char *transform_att; - xps_item *transform_tag = NULL; - xps_item *figures_tag = NULL; /* only used by resource */ + xml_element *transform_tag = NULL; + xml_element *figures_tag = NULL; /* only used by resource */ fz_matrix transform; fz_path *path; - figures_att = xps_att(root, "Figures"); - fill_rule_att = xps_att(root, "FillRule"); - transform_att = xps_att(root, "Transform"); + figures_att = xml_att(root, "Figures"); + fill_rule_att = xml_att(root, "FillRule"); + transform_att = xml_att(root, "Transform"); - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "PathGeometry.Transform")) - transform_tag = xps_down(node); + if (!strcmp(xml_tag(node), "PathGeometry.Transform")) + transform_tag = xml_down(node); } xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); @@ -737,9 +737,9 @@ xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xps_item *root, in if (figures_tag) xps_parse_path_figure(path, figures_tag, stroking); - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "PathFigure")) + if (!strcmp(xml_tag(node), "PathFigure")) xps_parse_path_figure(path, node, stroking); } @@ -768,9 +768,9 @@ xps_parse_line_cap(char *attr) */ void -xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xps_item *root) +xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root) { - xps_item *node; + xml_element *node; char *fill_uri; char *stroke_uri; @@ -784,12 +784,12 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di char *opacity_att; char *opacity_mask_att; - xps_item *transform_tag = NULL; - xps_item *clip_tag = NULL; - xps_item *data_tag = NULL; - xps_item *fill_tag = NULL; - xps_item *stroke_tag = NULL; - xps_item *opacity_mask_tag = NULL; + xml_element *transform_tag = NULL; + xml_element *clip_tag = NULL; + xml_element *data_tag = NULL; + xml_element *fill_tag = NULL; + xml_element *stroke_tag = NULL; + xml_element *opacity_mask_tag = NULL; char *fill_opacity_att = NULL; char *stroke_opacity_att = NULL; @@ -815,37 +815,37 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di * Extract attributes and extended attributes. */ - transform_att = xps_att(root, "RenderTransform"); - clip_att = xps_att(root, "Clip"); - data_att = xps_att(root, "Data"); - fill_att = xps_att(root, "Fill"); - stroke_att = xps_att(root, "Stroke"); - opacity_att = xps_att(root, "Opacity"); - opacity_mask_att = xps_att(root, "OpacityMask"); - - stroke_dash_array_att = xps_att(root, "StrokeDashArray"); - stroke_dash_cap_att = xps_att(root, "StrokeDashCap"); - stroke_dash_offset_att = xps_att(root, "StrokeDashOffset"); - stroke_end_line_cap_att = xps_att(root, "StrokeEndLineCap"); - stroke_start_line_cap_att = xps_att(root, "StrokeStartLineCap"); - stroke_line_join_att = xps_att(root, "StrokeLineJoin"); - stroke_miter_limit_att = xps_att(root, "StrokeMiterLimit"); - stroke_thickness_att = xps_att(root, "StrokeThickness"); - - for (node = xps_down(root); node; node = xps_next(node)) + transform_att = xml_att(root, "RenderTransform"); + clip_att = xml_att(root, "Clip"); + data_att = xml_att(root, "Data"); + fill_att = xml_att(root, "Fill"); + stroke_att = xml_att(root, "Stroke"); + opacity_att = xml_att(root, "Opacity"); + opacity_mask_att = xml_att(root, "OpacityMask"); + + stroke_dash_array_att = xml_att(root, "StrokeDashArray"); + stroke_dash_cap_att = xml_att(root, "StrokeDashCap"); + stroke_dash_offset_att = xml_att(root, "StrokeDashOffset"); + stroke_end_line_cap_att = xml_att(root, "StrokeEndLineCap"); + stroke_start_line_cap_att = xml_att(root, "StrokeStartLineCap"); + stroke_line_join_att = xml_att(root, "StrokeLineJoin"); + stroke_miter_limit_att = xml_att(root, "StrokeMiterLimit"); + stroke_thickness_att = xml_att(root, "StrokeThickness"); + + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "Path.RenderTransform")) - transform_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Path.OpacityMask")) - opacity_mask_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Path.Clip")) - clip_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Path.Fill")) - fill_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Path.Stroke")) - stroke_tag = xps_down(node); - if (!strcmp(xps_tag(node), "Path.Data")) - data_tag = xps_down(node); + if (!strcmp(xml_tag(node), "Path.RenderTransform")) + transform_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Path.OpacityMask")) + opacity_mask_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Path.Clip")) + clip_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Path.Fill")) + fill_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Path.Stroke")) + stroke_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Path.Data")) + data_tag = xml_down(node); } fill_uri = base_uri; @@ -866,17 +866,17 @@ xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *di if (!data_att && !data_tag) return; - if (fill_tag && !strcmp(xps_tag(fill_tag), "SolidColorBrush")) + if (fill_tag && !strcmp(xml_tag(fill_tag), "SolidColorBrush")) { - fill_opacity_att = xps_att(fill_tag, "Opacity"); - fill_att = xps_att(fill_tag, "Color"); + fill_opacity_att = xml_att(fill_tag, "Opacity"); + fill_att = xml_att(fill_tag, "Color"); fill_tag = NULL; } - if (stroke_tag && !strcmp(xps_tag(stroke_tag), "SolidColorBrush")) + if (stroke_tag && !strcmp(xml_tag(stroke_tag), "SolidColorBrush")) { - stroke_opacity_att = xps_att(stroke_tag, "Opacity"); - stroke_att = xps_att(stroke_tag, "Color"); + stroke_opacity_att = xml_att(stroke_tag, "Opacity"); + stroke_att = xml_att(stroke_tag, "Color"); stroke_tag = NULL; } diff --git a/xps/xpsresource.c b/xps/xpsresource.c index 28c172bf..59dc3f9d 100644 --- a/xps/xpsresource.c +++ b/xps/xpsresource.c @@ -1,7 +1,7 @@ #include "fitz.h" #include "muxps.h" -static xps_item * +static xml_element * xps_find_resource(xps_context *ctx, xps_resource *dict, char *name, char **urip) { xps_resource *head, *node; @@ -20,7 +20,7 @@ xps_find_resource(xps_context *ctx, xps_resource *dict, char *name, char **urip) return NULL; } -static xps_item * +static xml_element * xps_parse_resource_reference(xps_context *ctx, xps_resource *dict, char *att, char **urip) { char name[1024]; @@ -39,11 +39,11 @@ xps_parse_resource_reference(xps_context *ctx, xps_resource *dict, char *att, ch void xps_resolve_resource_reference(xps_context *ctx, xps_resource *dict, - char **attp, xps_item **tagp, char **urip) + char **attp, xml_element **tagp, char **urip) { if (*attp) { - xps_item *rsrc = xps_parse_resource_reference(ctx, dict, *attp, urip); + xml_element *rsrc = xps_parse_resource_reference(ctx, dict, *attp, urip); if (rsrc) { *attp = NULL; @@ -59,7 +59,7 @@ xps_parse_remote_resource_dictionary(xps_context *ctx, xps_resource **dictp, cha char part_uri[1024]; xps_resource *dict; xps_part *part; - xps_item *xml; + xml_element *xml; char *s; int code; @@ -71,18 +71,18 @@ xps_parse_remote_resource_dictionary(xps_context *ctx, xps_resource **dictp, cha return fz_throw("cannot find remote resource part '%s'", part_name); } - xml = xps_parse_xml(ctx, part->data, part->size); + xml = xml_parse_document(part->data, part->size); if (!xml) { xps_free_part(ctx, part); return fz_rethrow(-1, "cannot parse xml"); } - if (strcmp(xps_tag(xml), "ResourceDictionary")) + if (strcmp(xml_tag(xml), "ResourceDictionary")) { - xps_free_item(ctx, xml); + xml_free_element(xml); xps_free_part(ctx, part); - return fz_throw("expected ResourceDictionary element (found %s)", xps_tag(xml)); + return fz_throw("expected ResourceDictionary element (found %s)", xml_tag(xml)); } fz_strlcpy(part_uri, part_name, sizeof part_uri); @@ -93,7 +93,7 @@ xps_parse_remote_resource_dictionary(xps_context *ctx, xps_resource **dictp, cha code = xps_parse_resource_dictionary(ctx, &dict, part_uri, xml); if (code) { - xps_free_item(ctx, xml); + xml_free_element(xml); xps_free_part(ctx, part); return fz_rethrow(code, "cannot parse remote resource dictionary: %s", part_uri); } @@ -107,16 +107,16 @@ xps_parse_remote_resource_dictionary(xps_context *ctx, xps_resource **dictp, cha } int -xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, xps_item *root) +xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, xml_element *root) { xps_resource *head; xps_resource *entry; - xps_item *node; + xml_element *node; char *source; char *key; int code; - source = xps_att(root, "Source"); + source = xml_att(root, "Source"); if (source) { code = xps_parse_remote_resource_dictionary(ctx, dictp, base_uri, source); @@ -127,10 +127,10 @@ xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base head = NULL; - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { /* Usually "x:Key"; we have already processed and stripped namespace */ - key = xps_att(node, "Key"); + key = xml_att(node, "Key"); if (key) { entry = fz_malloc(sizeof(xps_resource)); @@ -161,7 +161,7 @@ xps_free_resource_dictionary(xps_context *ctx, xps_resource *dict) { next = dict->next; if (dict->base_xml) - xps_free_item(ctx, dict->base_xml); + xml_free_element(dict->base_xml); if (dict->base_uri) fz_free(dict->base_uri); fz_free(dict); diff --git a/xps/xpstile.c b/xps/xpstile.c index 1b96a7d3..ecc0af3f 100644 --- a/xps/xpstile.c +++ b/xps/xpstile.c @@ -12,9 +12,9 @@ struct closure { char *base_uri; xps_resource *dict; - xps_item *root; + xml_element *root; void *user; - void (*func)(xps_context*, fz_matrix, char*, xps_resource*, xps_item*, void*); + void (*func)(xps_context*, fz_matrix, char*, xps_resource*, xml_element*, void*); }; static void @@ -65,10 +65,10 @@ xps_paint_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect viewbox, int til void xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, xps_item *root, - void (*func)(xps_context*, fz_matrix, char*, xps_resource*, xps_item*, void*), void *user) + char *base_uri, xps_resource *dict, xml_element *root, + void (*func)(xps_context*, fz_matrix, char*, xps_resource*, xml_element*, void*), void *user) { - xps_item *node; + xml_element *node; struct closure c; char *opacity_att; @@ -79,7 +79,7 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *viewbox_units_att; char *viewport_units_att; - xps_item *transform_tag = NULL; + xml_element *transform_tag = NULL; fz_matrix transform; fz_rect viewbox; @@ -88,13 +88,13 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, float xscale, yscale; int tile_mode; - opacity_att = xps_att(root, "Opacity"); - transform_att = xps_att(root, "Transform"); - viewbox_att = xps_att(root, "Viewbox"); - viewport_att = xps_att(root, "Viewport"); - tile_mode_att = xps_att(root, "TileMode"); - viewbox_units_att = xps_att(root, "ViewboxUnits"); - viewport_units_att = xps_att(root, "ViewportUnits"); + opacity_att = xml_att(root, "Opacity"); + transform_att = xml_att(root, "Transform"); + viewbox_att = xml_att(root, "Viewbox"); + viewport_att = xml_att(root, "Viewport"); + tile_mode_att = xml_att(root, "TileMode"); + viewbox_units_att = xml_att(root, "ViewboxUnits"); + viewport_units_att = xml_att(root, "ViewportUnits"); c.base_uri = base_uri; c.dict = dict; @@ -102,12 +102,12 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, c.user = user; c.func = func; - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "ImageBrush.Transform")) - transform_tag = xps_down(node); - if (!strcmp(xps_tag(node), "VisualBrush.Transform")) - transform_tag = xps_down(node); + if (!strcmp(xml_tag(node), "ImageBrush.Transform")) + transform_tag = xml_down(node); + if (!strcmp(xml_tag(node), "VisualBrush.Transform")) + transform_tag = xml_down(node); } xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); diff --git a/xps/xpsvisual.c b/xps/xpsvisual.c index ad633708..ac7de23c 100644 --- a/xps/xpsvisual.c +++ b/xps/xpsvisual.c @@ -5,27 +5,27 @@ enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y }; static void xps_paint_visual_brush(xps_context *ctx, fz_matrix ctm, - char *base_uri, xps_resource *dict, xps_item *root, void *visual_tag) + char *base_uri, xps_resource *dict, xml_element *root, void *visual_tag) { - xps_parse_element(ctx, ctm, base_uri, dict, (xps_item *)visual_tag); + xps_parse_element(ctx, ctm, base_uri, dict, (xml_element *)visual_tag); } void xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, xps_item *root) + char *base_uri, xps_resource *dict, xml_element *root) { - xps_item *node; + xml_element *node; char *visual_uri; char *visual_att; - xps_item *visual_tag = NULL; + xml_element *visual_tag = NULL; - visual_att = xps_att(root, "Visual"); + visual_att = xml_att(root, "Visual"); - for (node = xps_down(root); node; node = xps_next(node)) + for (node = xml_down(root); node; node = xml_next(node)) { - if (!strcmp(xps_tag(node), "VisualBrush.Visual")) - visual_tag = xps_down(node); + if (!strcmp(xml_tag(node), "VisualBrush.Visual")) + visual_tag = xml_down(node); } visual_uri = base_uri; diff --git a/xps/xpsxml.c b/xps/xpsxml.c index c6f41123..397b68ec 100644 --- a/xps/xpsxml.c +++ b/xps/xpsxml.c @@ -1,339 +1,387 @@ -/* Simple XML document object model on top of Expat. */ - #include "fitz.h" #include "muxps.h" -#include - -#define XMLBUFLEN 4096 - -#define NS_XPS "http://schemas.microsoft.com/xps/2005/06" -#define NS_MC "http://schemas.openxmlformats.org/markup-compatibility/2006" - -typedef struct xps_parser_s xps_parser; +struct attribute +{ + char name[32]; + char *value; + struct attribute *next; +}; -struct xps_parser_s +struct element { - xps_context *ctx; - xps_item *root; - xps_item *head; - char *error; - int compat; - char *base; /* base of relative URIs */ + char name[32]; + struct attribute *atts; + struct element *up, *down, *next; }; -struct xps_item_s +struct parser { - char *name; - char **atts; - xps_item *up; - xps_item *down; - xps_item *next; + struct element *head; }; -static char * -skip_namespace(char *s) +static inline void indent(int n) { - char *p = strchr(s, ' '); - if (p) - return p + 1; - return s; + while (n--) putchar(' '); } -static void -on_open_tag(void *zp, char *ns_name, char **atts) +void xml_print_element(struct element *item, int level) { - xps_parser *parser = zp; - xps_item *item; - xps_item *tail; - int namelen; - int attslen; - int textlen; - char *name, *p; - int i; - - if (parser->error) - return; - - /* check namespace */ - - name = NULL; - - p = strstr(ns_name, NS_XPS); - if (p == ns_name) - { - name = strchr(ns_name, ' ') + 1; + while (item) { + struct attribute *att; + indent(level); + printf("<%s", item->name); + for (att = item->atts; att; att = att->next) + printf(" %s=\"%s\"", att->name, att->value); + if (item->down) { + printf(">\n"); + xml_print_element(item->down, level + 1); + indent(level); + printf("\n", item->name); + } + else { + printf("/>\n"); + } + item = item->next; } +} - p = strstr(ns_name, NS_MC); - if (p == ns_name) - { - name = strchr(ns_name, ' ') + 1; - parser->compat = 1; - } +struct element *xml_next(struct element *item) +{ + return item->next; +} - if (!name) - { - fz_warn("unknown namespace: %s", ns_name); - name = ns_name; - } +struct element *xml_down(struct element *item) +{ + return item->down; +} - /* count size to alloc */ +char *xml_tag(struct element *item) +{ + return item->name; +} - namelen = strlen(name) + 1; /* zero terminated */ - attslen = sizeof(char*); /* with space for sentinel */ - textlen = 0; - for (i = 0; atts[i]; i++) - { - attslen += sizeof(char*); - if ((i & 1) == 0) - textlen += strlen(skip_namespace(atts[i])) + 1; - else - textlen += strlen(atts[i]) + 1; - } +char *xml_att(struct element *item, const char *name) +{ + struct attribute *att; + for (att = item->atts; att; att = att->next) + if (!strcmp(att->name, name)) + return att->value; + return NULL; +} - item = fz_malloc(sizeof(xps_item) + attslen + namelen + textlen); - if (!item) - { - parser->error = "out of memory"; +static void xml_free_attribute(struct attribute *att) +{ + while (att) { + struct attribute *next = att->next; + if (att->value) + fz_free(att->value); + fz_free(att); + att = next; } +} - /* copy strings to new memory */ - - item->atts = (char**) (((char*)item) + sizeof(xps_item)); - item->name = ((char*)item) + sizeof(xps_item) + attslen; - p = ((char*)item) + sizeof(xps_item) + attslen + namelen; +void xml_free_element(struct element *item) +{ + while (item) { + struct element *next = item->next; + if (item->atts) + xml_free_attribute(item->atts); + if (item->down) + xml_free_element(item->down); + fz_free(item); + item = next; + } +} - strcpy(item->name, name); - for (i = 0; atts[i]; i++) - { - item->atts[i] = p; - if ((i & 1) == 0) - strcpy(item->atts[i], skip_namespace(atts[i])); +static int xml_parse_entity(int *c, char *a) +{ + char *b; + if (a[1] == '#') { + if (a[2] == 'x') + *c = strtol(a + 3, &b, 16); else - strcpy(item->atts[i], atts[i]); - p += strlen(p) + 1; + *c = strtol(a + 2, &b, 10); + if (*b == ';') + return b - a; } - - item->atts[i] = 0; - - /* link item into tree */ - - item->up = parser->head; - item->down = NULL; - item->next = NULL; - - if (!parser->head) - { - parser->root = item; - parser->head = item; - return; + else if (a[1] == 'l' && a[2] == 't' && a[3] == ';') { + *c = '<'; + return 4; } - - if (!parser->head->down) - { - parser->head->down = item; - parser->head = item; - return; + else if (a[1] == 'g' && a[2] == 't' && a[3] == ';') { + *c = '>'; + return 4; } - - tail = parser->head->down; - while (tail->next) - tail = tail->next; - tail->next = item; - parser->head = item; + else if (a[1] == 'a' && a[2] == 'm' && a[3] == 'p' && a[4] == ';') { + *c = '&'; + return 5; + } + else if (a[1] == 'a' && a[2] == 'p' && a[3] == 'o' && a[4] == 's' && a[5] == ';') { + *c = '\''; + return 6; + } + else if (a[1] == 'q' && a[2] == 'u' && a[3] == 'o' && a[4] == 't' && a[5] == ';') { + *c = '"'; + return 6; + } + *c = *a++; + return 1; } -static void -on_close_tag(void *zp, char *name) +static void xml_emit_open_tag(struct parser *parser, char *a, char *b) { - xps_parser *parser = zp; + struct element *head, *tail; - if (parser->error) - return; + head = fz_malloc(sizeof(struct element)); + if (b - a > sizeof(head->name)) + b = a + sizeof(head->name); + memcpy(head->name, a, b - a); + head->name[b - a] = 0; - if (parser->head) - parser->head = parser->head->up; -} + head->atts = NULL; + head->up = parser->head; + head->down = NULL; + head->next = NULL; -static inline int -is_xml_space(int c) -{ - return c == ' ' || c == '\t' || c == '\r' || c == '\n'; -} - -static void -on_text(void *zp, char *buf, int len) -{ - xps_parser *parser = zp; - char *atts[3]; - int i; - - if (parser->error) - return; - - for (i = 0; i < len; i++) - { - if (!is_xml_space(buf[i])) - { - char *tmp = fz_malloc(len + 1); - if (!tmp) - { - parser->error = "out of memory"; - return; - } - - atts[0] = ""; - atts[1] = tmp; - atts[2] = NULL; - - memcpy(tmp, buf, len); - tmp[len] = 0; - on_open_tag(zp, "", atts); - on_close_tag(zp, ""); - fz_free(tmp); - return; - } + if (!parser->head->down) { + parser->head->down = head; + } + else { + tail = parser->head->down; + while (tail->next) + tail = tail->next; + tail->next = head; } + + parser->head = head; } -static xps_item * -xps_process_compatibility(xps_context *ctx, xps_item *root) +static void xml_emit_att_name(struct parser *parser, char *a, char *b) { - fz_warn("XPS document uses markup compatibility tags"); - return root; + struct element *head = parser->head; + struct attribute *att; + + att = fz_malloc(sizeof(struct attribute)); + if (b - a > sizeof(att->name)) + b = a + sizeof(att->name); + memcpy(att->name, a, b - a); + att->name[b - a] = 0; + att->value = NULL; + att->next = head->atts; + head->atts = att; } -xps_item * -xps_parse_xml(xps_context *ctx, byte *buf, int len) +static void xml_emit_att_value(struct parser *parser, char *a, char *b) { - xps_parser parser; - XML_Parser xp; - int code; - - parser.ctx = ctx; - parser.root = NULL; - parser.head = NULL; - parser.error = NULL; - parser.compat = 0; - - xp = XML_ParserCreateNS(NULL, ' '); - if (!xp) - { - fz_throw("xml error: cannot create expat parser"); - return NULL; - } - - XML_SetUserData(xp, &parser); - XML_SetParamEntityParsing(xp, XML_PARAM_ENTITY_PARSING_NEVER); - XML_SetStartElementHandler(xp, (XML_StartElementHandler)on_open_tag); - XML_SetEndElementHandler(xp, (XML_EndElementHandler)on_close_tag); - XML_SetCharacterDataHandler(xp, (XML_CharacterDataHandler)on_text); - - code = XML_Parse(xp, (char*)buf, len, 1); - if (code == 0) - { - if (parser.root) - xps_free_item(ctx, parser.root); - XML_ParserFree(xp); - fz_throw("xml error: %s", XML_ErrorString(XML_GetErrorCode(xp))); - return NULL; + struct element *head = parser->head; + struct attribute *att = head->atts; + char *s; + int c; + + /* entities are all longer than UTFmax so runetochar is safe */ + s = att->value = fz_malloc(b - a + 1); + while (a < b) { + if (*a == '&') { + a += xml_parse_entity(&c, a); + s += runetochar(s, &c); + } + else { + *s++ = *a++; + } } - - XML_ParserFree(xp); - - if (parser.compat) - xps_process_compatibility(ctx, parser.root); - - return parser.root; + *s = 0; } -xps_item * -xps_next(xps_item *item) +static void xml_emit_close_tag(struct parser *parser) { - return item->next; + if (parser->head->up) + parser->head = parser->head->up; } -xps_item * -xps_down(xps_item *item) +static inline int isname(int c) { - return item->down; + return c == '.' || c == '-' || c == '_' || c == ':' || + (c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z'); } -char * -xps_tag(xps_item *item) +static inline int iswhite(int c) { - return item->name; + return c == ' ' || c == '\r' || c == '\n' || c == '\t'; } -char * -xps_att(xps_item *item, const char *att) +static char *xml_parse_document_imp(struct parser *x, char *p) { - int i; - for (i = 0; item->atts[i]; i += 2) - if (!strcmp(item->atts[i], att)) - return item->atts[i + 1]; + char *mark; + int quote; + +parse_text: + mark = p; + while (*p && *p != '<') ++p; + if (*p == '<') { ++p; goto parse_element; } return NULL; -} -void -xps_free_item(xps_context *ctx, xps_item *item) -{ - xps_item *next; - while (item) - { - next = item->next; - if (item->down) - xps_free_item(ctx, item->down); - fz_free(item); - item = next; +parse_element: + if (*p == '/') { ++p; goto parse_closing_element; } + if (*p == '!') { ++p; goto parse_comment; } + if (*p == '?') { ++p; goto parse_processing_instruction; } + while (iswhite(*p)) ++p; + if (isname(*p)) + goto parse_element_name; + return "syntax error in element"; + +parse_comment: + if (*p == '[') goto parse_cdata; + if (*p++ != '-') return "syntax error in comment (') { + p += 3; + goto parse_text; + } + ++p; + } + return "end of data in comment"; + +parse_cdata: + if (p[1] != 'C' || p[2] != 'D' || p[3] != 'A' || p[4] != 'T' || p[5] != 'A' || p[6] != '[') + return "syntax error in CDATA section"; + p += 7; + mark = p; + while (*p) { + if (p[0] == ']' && p[1] == ']' && p[2] == '>') { + p += 3; + goto parse_text; + } + ++p; + } + return "end of data in CDATA section"; + +parse_processing_instruction: + while (*p) { + if (p[0] == '?' && p[1] == '>') { + p += 2; + goto parse_text; + } + ++p; + } + return "end of data in processing instruction"; + +parse_closing_element: + while (iswhite(*p)) ++p; + mark = p; + while (isname(*p)) ++p; + while (iswhite(*p)) ++p; + if (*p != '>') + return "syntax error in closing element"; + xml_emit_close_tag(x); + ++p; + goto parse_text; + +parse_element_name: + mark = p; + while (isname(*p)) ++p; + xml_emit_open_tag(x, mark, p); + if (*p == '>') { ++p; goto parse_text; } + if (p[0] == '/' && p[1] == '>') { + xml_emit_close_tag(x); + p += 2; + goto parse_text; + } + if (iswhite(*p)) + goto parse_attributes; + return "syntax error after element name"; + +parse_attributes: + while (iswhite(*p)) ++p; + if (isname(*p)) + goto parse_attribute_name; + if (*p == '>') { ++p; goto parse_text; } + if (p[0] == '/' && p[1] == '>') { + xml_emit_close_tag(x); + p += 2; + goto parse_text; } + return "syntax error in attributes"; + +parse_attribute_name: + mark = p; + while (isname(*p)) ++p; + xml_emit_att_name(x, mark, p); + while (iswhite(*p)) ++p; + if (*p == '=') { ++p; goto parse_attribute_value; } + return "syntax error after attribute name"; + +parse_attribute_value: + while (iswhite(*p)) ++p; + quote = *p++; + if (quote != '"' && quote != '\'') + return "missing quote character"; + mark = p; + while (*p && *p != quote) ++p; + if (*p == quote) { + xml_emit_att_value(x, mark, p++); + goto parse_attributes; + } + return "end of data in attribute value"; } -static void indent(int n) +static char *convert_to_utf8(unsigned char *s, int n) { - while (n--) - printf(" "); + unsigned char *e = s + n; + char *dst, *d; + int c; + + if (s[0] == 0xFE && s[1] == 0xFF) { + dst = d = fz_malloc(n * 2); + while (s + 1 < e) { + c = s[0] << 8 | s[1]; + d += runetochar(d, &c); + s += 2; + } + *d = 0; + return dst; + } + + if (s[0] == 0xFF && s[1] == 0xFE) { + dst = d = fz_malloc(n * 2); + while (s + 1 < e) { + c = s[0] | s[1] << 8; + d += runetochar(d, &c); + s += 2; + } + *d = 0; + return dst; + } + + return (char*)s; } -static void -xps_debug_item_imp(xps_item *item, int level, int loop) +struct element * +xml_parse_document(unsigned char *s, int n) { - int i; + struct parser parser; + struct element root; + char *p, *error; - while (item) - { - indent(level); + /* s is already null-terminated (see xps_new_part) */ - if (strlen(item->name) == 0) - printf("%s\n", item->atts[1]); - else - { - printf("<%s", item->name); - - for (i = 0; item->atts[i]; i += 2) - printf(" %s=\"%s\"", item->atts[i], item->atts[i+1]); - - if (item->down) - { - printf(">\n"); - xps_debug_item_imp(item->down, level + 1, 1); - indent(level); - printf("\n", item->name); - } - else - printf(" />\n"); - } + memset(&root, 0, sizeof(root)); + parser.head = &root; - item = item->next; + p = convert_to_utf8(s, n); - if (!loop) - return; + error = xml_parse_document_imp(&parser, p); + if (error) { + fz_throw(error); + return NULL; } -} -void -xps_debug_item(xps_item *item, int level) -{ - xps_debug_item_imp(item, level, 0); + if (p != (char*)s) + fz_free(p); + + return root.down; } diff --git a/xps/xpszip.c b/xps/xpszip.c index 4905ea37..ad708bcb 100644 --- a/xps/xpszip.c +++ b/xps/xpszip.c @@ -474,8 +474,10 @@ xps_load_page(xps_context *ctx, int number) if (!page->root) { code = xps_load_fixed_page(ctx, page); - if (code) - fz_catch(code, "ignoring errors on page"); + if (code) { + fz_rethrow(code, "cannot load page %d", number + 1); + return NULL; + } } return page; } @@ -488,7 +490,7 @@ void xps_free_page(xps_context *ctx, xps_page *page) { if (page->root) - xps_free_item(ctx, page->root); + xml_free_element(page->root); page->root = NULL; } -- cgit v1.2.3 From c17177480e7fcfc821844e8af5e651048c85ac96 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 3 Apr 2011 14:51:35 +0200 Subject: xps: Rearrange files, part one. --- Makefile | 7 -- xps/muxps.h | 3 - xps/xpsanalyze.c | 309 ------------------------------------------------------- xps/xpscolor.c | 191 ---------------------------------- xps/xpscommon.c | 187 +++++++++++++++++++++++++++++++++ xps/xpsfont.c | 54 ---------- xps/xpsglyphs.c | 70 +++++++++++-- xps/xpsimage.c | 4 +- xps/xpsjxr.c | 10 -- xps/xpsmem.c | 7 +- xps/xpsopacity.c | 63 ------------ xps/xpstile.c | 35 +++++++ xps/xpsutf.c | 55 ---------- xps/xpsvisual.c | 39 ------- 14 files changed, 289 insertions(+), 745 deletions(-) delete mode 100644 xps/xpsanalyze.c delete mode 100644 xps/xpscolor.c delete mode 100644 xps/xpsfont.c delete mode 100644 xps/xpsjxr.c delete mode 100644 xps/xpsopacity.c delete mode 100644 xps/xpsutf.c delete mode 100644 xps/xpsvisual.c diff --git a/Makefile b/Makefile index 11fb41c6..96ebd0f2 100644 --- a/Makefile +++ b/Makefile @@ -172,27 +172,20 @@ $(MUPDF_OBJ): $(MUPDF_HDR) MUXPS_HDR := $(FITZ_HDR) xps/muxps.h MUXPS_SRC := \ - xps/xpsanalyze.c \ - xps/xpscolor.c \ xps/xpscommon.c \ xps/xpsdoc.c \ - xps/xpsfont.c \ xps/xpsglyphs.c \ xps/xpsgradient.c \ xps/xpshash.c \ - xps/xpsjxr.c \ xps/xpsimage.c \ xps/xpsjpeg.c \ xps/xpsmem.c \ - xps/xpsopacity.c \ xps/xpspage.c \ xps/xpspath.c \ xps/xpspng.c \ xps/xpsresource.c \ xps/xpstiff.c \ xps/xpstile.c \ - xps/xpsutf.c \ - xps/xpsvisual.c \ xps/xpsxml.c \ xps/xpszip.c MUXPS_OBJ := $(MUXPS_SRC:xps/%.c=$(OBJDIR)/%.o) diff --git a/xps/muxps.h b/xps/muxps.h index 0455ee68..e4595dbe 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -32,8 +32,6 @@ typedef struct xps_context_s xps_context; int xps_strcasecmp(char *a, char *b); void xps_absolute_path(char *output, char *base_uri, char *path, int output_size); -int xps_utf8_to_ucs(int *p, const char *s, int n); - /* * Generic hashtable. */ @@ -153,7 +151,6 @@ void xps_debug_path(xps_context *ctx); * Colorspaces and colors. */ -fz_colorspace *xps_read_icc_colorspace(xps_context *ctx, char *base_uri, char *profile); void xps_parse_color(xps_context *ctx, char *base_uri, char *hexstring, fz_colorspace **csp, float *samples); void xps_set_color(xps_context *ctx, fz_colorspace *colorspace, float *samples); diff --git a/xps/xpsanalyze.c b/xps/xpsanalyze.c deleted file mode 100644 index 86dd4626..00000000 --- a/xps/xpsanalyze.c +++ /dev/null @@ -1,309 +0,0 @@ -/* XPS interpreter - analyze page checking for transparency. - * This is a stripped down parser that looks for alpha values < 1.0 in - * any part of the page. - */ - -#include "fitz.h" -#include "muxps.h" - -static int -xps_remote_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri, char *source_att) -{ - //dputs("page has transparency: uses a remote resource; not parsed; being conservative\n"); - return 1; -} - -int -xps_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri, xml_element *root) -{ - char *source; - xml_element *node; - - source = xml_att(root, "Source"); - if (source) - return xps_remote_resource_dictionary_has_transparency(ctx, base_uri, source); - - for (node = xml_down(root); node; node = xml_next(node)) - { - // TODO: ... all kinds of stuff can be here, brushes, elements, whatnot - } - - return 1; -} - -static int -xps_gradient_stops_have_transparency(xps_context *ctx, char *base_uri, xml_element *root) -{ - xml_element *node; - fz_colorspace *colorspace; - char *color_att; - float samples[32]; - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "GradientStop")) - { - color_att = xml_att(node, "Color"); - if (color_att) - { - xps_parse_color(ctx, base_uri, color_att, &colorspace, samples); - if (samples[0] < 1.0) - { - //dputs("page has transparency: GradientStop has alpha\n"); - return 1; - } - } - } - } - - return 0; -} - -static int -xps_gradient_brush_has_transparency(xps_context *ctx, char *base_uri, xml_element *root) -{ - xml_element *node; - char *opacity_att; - - opacity_att = xml_att(root, "Opacity"); - if (opacity_att) - { - if (atof(opacity_att) < 1.0) - { - //dputs("page has transparency: GradientBrush Opacity\n"); - return 1; - } - } - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "RadialGradientBrush.GradientStops")) - { - if (xps_gradient_stops_have_transparency(ctx, base_uri, node)) - return 1; - } - if (!strcmp(xml_tag(node), "LinearGradientBrush.GradientStops")) - { - if (xps_gradient_stops_have_transparency(ctx, base_uri, node)) - return 1; - } - } - - return 0; -} - -static int -xps_brush_has_transparency(xps_context *ctx, char *base_uri, xml_element *root) -{ - char *opacity_att; - char *color_att; - xml_element *node; - - fz_colorspace *colorspace; - float samples[32]; - - if (!strcmp(xml_tag(root), "SolidColorBrush")) - { - opacity_att = xml_att(root, "Opacity"); - if (opacity_att) - { - if (atof(opacity_att) < 1.0) - { - //dputs("page has transparency: SolidColorBrush Opacity\n"); - return 1; - } - } - - color_att = xml_att(root, "Color"); - if (color_att) - { - xps_parse_color(ctx, base_uri, color_att, &colorspace, samples); - if (samples[0] < 1.0) - { - //dputs("page has transparency: SolidColorBrush Color has alpha\n"); - return 1; - } - } - } - - if (!strcmp(xml_tag(root), "VisualBrush")) - { - char *opacity_att = xml_att(root, "Opacity"); - if (opacity_att) - { - if (atof(opacity_att) < 1.0) - { - //dputs("page has transparency: VisualBrush Opacity\n"); - return 1; - } - } - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "VisualBrush.Visual")) - { - if (xps_element_has_transparency(ctx, base_uri, xml_down(node))) - return 1; - } - } - } - - if (!strcmp(xml_tag(root), "ImageBrush")) - { - if (xps_image_brush_has_transparency(ctx, base_uri, root)) - return 1; - } - - if (!strcmp(xml_tag(root), "LinearGradientBrush")) - { - if (xps_gradient_brush_has_transparency(ctx, base_uri, root)) - return 1; - } - - if (!strcmp(xml_tag(root), "RadialGradientBrush")) - { - if (xps_gradient_brush_has_transparency(ctx, base_uri, root)) - return 1; - } - - return 0; -} - -static int -xps_path_has_transparency(xps_context *ctx, char *base_uri, xml_element *root) -{ - xml_element *node; - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "Path.OpacityMask")) - { - //dputs("page has transparency: Path.OpacityMask\n"); - return 1; - } - - if (!strcmp(xml_tag(node), "Path.Stroke")) - { - if (xps_brush_has_transparency(ctx, base_uri, xml_down(node))) - return 1; - } - - if (!strcmp(xml_tag(node), "Path.Fill")) - { - if (xps_brush_has_transparency(ctx, base_uri, xml_down(node))) - return 1; - } - } - - return 0; -} - -static int -xps_glyphs_has_transparency(xps_context *ctx, char *base_uri, xml_element *root) -{ - xml_element *node; - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "Glyphs.OpacityMask")) - { - //dputs("page has transparency: Glyphs.OpacityMask\n"); - return 1; - } - - if (!strcmp(xml_tag(node), "Glyphs.Fill")) - { - if (xps_brush_has_transparency(ctx, base_uri, xml_down(node))) - return 1; - } - } - - return 0; -} - -static int -xps_canvas_has_transparency(xps_context *ctx, char *base_uri, xml_element *root) -{ - xml_element *node; - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "Canvas.Resources")) - { - if (xps_resource_dictionary_has_transparency(ctx, base_uri, xml_down(node))) - return 1; - } - - if (!strcmp(xml_tag(node), "Canvas.OpacityMask")) - { - //dputs("page has transparency: Canvas.OpacityMask\n"); - return 1; - } - - if (xps_element_has_transparency(ctx, base_uri, node)) - return 1; - } - - return 0; -} - -int -xps_element_has_transparency(xps_context *ctx, char *base_uri, xml_element *node) -{ - char *opacity_att; - char *stroke_att; - char *fill_att; - - fz_colorspace *colorspace; - float samples[32]; - - stroke_att = xml_att(node, "Stroke"); - if (stroke_att) - { - xps_parse_color(ctx, base_uri, stroke_att, &colorspace, samples); - if (samples[0] < 1.0) - { - //dprintf1("page has transparency: Stroke alpha=%g\n", samples[0]); - return 1; - } - } - - fill_att = xml_att(node, "Fill"); - if (fill_att) - { - xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples); - if (samples[0] < 1.0) - { - //dprintf1("page has transparency: Fill alpha=%g\n", samples[0]); - return 1; - } - } - - opacity_att = xml_att(node, "Opacity"); - if (opacity_att) - { - if (atof(opacity_att) < 1.0) - { - //dprintf1("page has transparency: Opacity=%g\n", atof(opacity_att)); - return 1; - } - } - - if (xml_att(node, "OpacityMask")) - { - //dputs("page has transparency: OpacityMask\n"); - return 1; - } - - if (!strcmp(xml_tag(node), "Path")) - if (xps_path_has_transparency(ctx, base_uri, node)) - return 1; - if (!strcmp(xml_tag(node), "Glyphs")) - if (xps_glyphs_has_transparency(ctx, base_uri, node)) - return 1; - if (!strcmp(xml_tag(node), "Canvas")) - if (xps_canvas_has_transparency(ctx, base_uri, node)) - return 1; - - return 0; -} diff --git a/xps/xpscolor.c b/xps/xpscolor.c deleted file mode 100644 index 37dee5fe..00000000 --- a/xps/xpscolor.c +++ /dev/null @@ -1,191 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -#include /* for toupper() */ - -void -xps_set_color(xps_context *ctx, fz_colorspace *colorspace, float *samples) -{ - int i; - ctx->colorspace = colorspace; - for (i = 0; i < colorspace->n; i++) - ctx->color[i] = samples[i + 1]; - ctx->alpha = samples[0] * ctx->opacity[ctx->opacity_top]; -} - -static int unhex(int chr) -{ - const char *hextable = "0123456789ABCDEF"; - return strchr(hextable, (toupper(chr))) - hextable; -} - -static int count_commas(char *s) -{ - int n = 0; - while (*s) - { - if (*s == ',') - n ++; - s ++; - } - return n; -} - -void -xps_parse_color(xps_context *ctx, char *base_uri, char *string, - fz_colorspace **csp, float *samples) -{ - char *p; - int i, n; - char buf[1024]; - char *profile; - - *csp = fz_devicergb; - - samples[0] = 1.0; - samples[1] = 0.0; - samples[2] = 0.0; - samples[3] = 0.0; - - if (string[0] == '#') - { - if (strlen(string) == 9) - { - samples[0] = unhex(string[1]) * 16 + unhex(string[2]); - samples[1] = unhex(string[3]) * 16 + unhex(string[4]); - samples[2] = unhex(string[5]) * 16 + unhex(string[6]); - samples[3] = unhex(string[7]) * 16 + unhex(string[8]); - } - else - { - samples[0] = 255.0; - samples[1] = unhex(string[1]) * 16 + unhex(string[2]); - samples[2] = unhex(string[3]) * 16 + unhex(string[4]); - samples[3] = unhex(string[5]) * 16 + unhex(string[6]); - } - - samples[0] /= 255.0; - samples[1] /= 255.0; - samples[2] /= 255.0; - samples[3] /= 255.0; - } - - else if (string[0] == 's' && string[1] == 'c' && string[2] == '#') - { - if (count_commas(string) == 2) - sscanf(string, "sc#%g,%g,%g", samples + 1, samples + 2, samples + 3); - if (count_commas(string) == 3) - sscanf(string, "sc#%g,%g,%g,%g", samples, samples + 1, samples + 2, samples + 3); - } - - else if (strstr(string, "ContextColor ") == string) - { - /* Crack the string for profile name and sample values */ - strcpy(buf, string); - - profile = strchr(buf, ' '); - if (!profile) - { - fz_warn("cannot find icc profile uri in '%s'", string); - return; - } - - *profile++ = 0; - p = strchr(profile, ' '); - if (!p) - { - fz_warn("cannot find component values in '%s'", profile); - return; - } - - *p++ = 0; - n = count_commas(p) + 1; - i = 0; - while (i < n) - { - samples[i++] = atof(p); - p = strchr(p, ','); - if (!p) - break; - p ++; - if (*p == ' ') - p ++; - } - while (i < n) - { - samples[i++] = 0.0; - } - - *csp = xps_read_icc_colorspace(ctx, base_uri, profile); - if (!*csp) - { - /* Default fallbacks if the ICC stuff fails */ - switch (n) - { - case 2: *csp = fz_devicegray; break; /* alpha + tint */ - case 4: *csp = fz_devicergb; break; /* alpha + RGB */ - case 5: *csp = fz_devicecmyk; break; /* alpha + CMYK */ - default: *csp = fz_devicegray; break; - } - } - } -} - -fz_colorspace * -xps_read_icc_colorspace(xps_context *ctx, char *base_uri, char *profilename) -{ -#if 0 - fz_colorspace *space; - xps_part *part; - char partname[1024]; - - /* Find ICC colorspace part */ - xps_absolute_path(partname, base_uri, profilename, sizeof partname); - - /* See if we cached the profile */ - space = xps_hash_lookup(ctx->colorspace_table, partname); - if (!space) - { - part = xps_read_part(ctx, partname); - - /* Problem finding profile. Don't fail, just use default */ - if (!part) { - fz_warn("cannot find icc profile part: %s", partname); - return NULL; - } - - /* Create the profile */ - profile = gsicc_profile_new(NULL, ctx->memory, NULL, 0); - - /* Set buffer */ - profile->buffer = part->data; - profile->buffer_size = part->size; - - /* Parse */ - gsicc_init_profile_info(profile); - - /* Problem with profile. Don't fail, just use the default */ - if (profile->profile_handle == NULL) - { - gsicc_profile_reference(profile, -1); - fz_warn("there was a problem with the profile: %s", partname); - return NULL; - } - - /* Create a new colorspace and associate with the profile */ - gs_cspace_build_ICC(&space, NULL, ctx->memory); - space->cmm_icc_profile_data = profile; - - /* Steal the buffer data before freeing the part */ - part->data = NULL; - xps_free_part(ctx, part); - - /* Add colorspace to xps color cache. */ - xps_hash_insert(ctx, ctx->colorspace_table, fz_strdup(partname), space); - } - - return space; -#else - return NULL; -#endif -} diff --git a/xps/xpscommon.c b/xps/xpscommon.c index 77aec58c..7e489ef7 100644 --- a/xps/xpscommon.c +++ b/xps/xpscommon.c @@ -1,6 +1,14 @@ #include "fitz.h" #include "muxps.h" +static inline int unhex(int a) +{ + if (a >= 'A' && a <= 'F') return a - 'A' + 0xA; + if (a >= 'a' && a <= 'f') return a - 'a' + 0xA; + if (a >= '0' && a <= '9') return a - '0'; + return 0; +} + void xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node) { @@ -29,6 +37,67 @@ xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource /* skip unknown tags (like Foo.Resources and similar) */ } +void +xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, + char *base_uri, xps_resource *dict, + char *opacity_att, xml_element *opacity_mask_tag) +{ + float opacity; + + if (!opacity_att && !opacity_mask_tag) + return; + + opacity = 1.0; + if (opacity_att) + opacity = atof(opacity_att); + + if (opacity_mask_tag && !strcmp(xml_tag(opacity_mask_tag), "SolidColorBrush")) + { + char *scb_opacity_att = xml_att(opacity_mask_tag, "Opacity"); + char *scb_color_att = xml_att(opacity_mask_tag, "Color"); + if (scb_opacity_att) + opacity = opacity * atof(scb_opacity_att); + if (scb_color_att) + { + fz_colorspace *colorspace; + float samples[32]; + xps_parse_color(ctx, base_uri, scb_color_att, &colorspace, samples); + opacity = opacity * samples[0]; + } + opacity_mask_tag = NULL; + } + + if (ctx->opacity_top + 1 < nelem(ctx->opacity)) + { + ctx->opacity[ctx->opacity_top + 1] = ctx->opacity[ctx->opacity_top] * opacity; + ctx->opacity_top++; + } + + if (opacity_mask_tag) + { + ctx->dev->beginmask(ctx->dev->user, area, 0, NULL, NULL); + xps_parse_brush(ctx, ctm, area, base_uri, dict, opacity_mask_tag); + ctx->dev->endmask(ctx->dev->user); + } +} + +void +xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, + char *opacity_att, xml_element *opacity_mask_tag) +{ + if (!opacity_att && !opacity_mask_tag) + return; + + if (ctx->opacity_top > 0) + ctx->opacity_top--; + + if (opacity_mask_tag) + { + if (strcmp(xml_tag(opacity_mask_tag), "SolidColorBrush")) + ctx->dev->popclip(ctx->dev->user); + } +} + void xps_parse_render_transform(xps_context *ctx, char *transform, fz_matrix *matrix) { @@ -93,3 +162,121 @@ xps_parse_rectangle(xps_context *ctx, char *text, fz_rect *rect) rect->x1 = args[0] + args[2]; rect->y1 = args[1] + args[3]; } + +static int count_commas(char *s) +{ + int n = 0; + while (*s) + { + if (*s == ',') + n ++; + s ++; + } + return n; +} + +void +xps_parse_color(xps_context *ctx, char *base_uri, char *string, + fz_colorspace **csp, float *samples) +{ + char *p; + int i, n; + char buf[1024]; + char *profile; + + *csp = fz_devicergb; + + samples[0] = 1.0; + samples[1] = 0.0; + samples[2] = 0.0; + samples[3] = 0.0; + + if (string[0] == '#') + { + if (strlen(string) == 9) + { + samples[0] = unhex(string[1]) * 16 + unhex(string[2]); + samples[1] = unhex(string[3]) * 16 + unhex(string[4]); + samples[2] = unhex(string[5]) * 16 + unhex(string[6]); + samples[3] = unhex(string[7]) * 16 + unhex(string[8]); + } + else + { + samples[0] = 255.0; + samples[1] = unhex(string[1]) * 16 + unhex(string[2]); + samples[2] = unhex(string[3]) * 16 + unhex(string[4]); + samples[3] = unhex(string[5]) * 16 + unhex(string[6]); + } + + samples[0] /= 255.0; + samples[1] /= 255.0; + samples[2] /= 255.0; + samples[3] /= 255.0; + } + + else if (string[0] == 's' && string[1] == 'c' && string[2] == '#') + { + if (count_commas(string) == 2) + sscanf(string, "sc#%g,%g,%g", samples + 1, samples + 2, samples + 3); + if (count_commas(string) == 3) + sscanf(string, "sc#%g,%g,%g,%g", samples, samples + 1, samples + 2, samples + 3); + } + + else if (strstr(string, "ContextColor ") == string) + { + /* Crack the string for profile name and sample values */ + strcpy(buf, string); + + profile = strchr(buf, ' '); + if (!profile) + { + fz_warn("cannot find icc profile uri in '%s'", string); + return; + } + + *profile++ = 0; + p = strchr(profile, ' '); + if (!p) + { + fz_warn("cannot find component values in '%s'", profile); + return; + } + + *p++ = 0; + n = count_commas(p) + 1; + i = 0; + while (i < n) + { + samples[i++] = atof(p); + p = strchr(p, ','); + if (!p) + break; + p ++; + if (*p == ' ') + p ++; + } + while (i < n) + { + samples[i++] = 0.0; + } + + /* TODO: load ICC profile */ + switch (n) + { + case 2: *csp = fz_devicegray; break; + case 4: *csp = fz_devicergb; break; + case 5: *csp = fz_devicecmyk; break; + default: *csp = fz_devicegray; break; + } + } +} + +void +xps_set_color(xps_context *ctx, fz_colorspace *colorspace, float *samples) +{ + int i; + ctx->colorspace = colorspace; + for (i = 0; i < colorspace->n; i++) + ctx->color[i] = samples[i + 1]; + ctx->alpha = samples[0] * ctx->opacity[ctx->opacity_top]; +} diff --git a/xps/xpsfont.c b/xps/xpsfont.c deleted file mode 100644 index a76cc10e..00000000 --- a/xps/xpsfont.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -#include -#include FT_FREETYPE_H -#include FT_ADVANCES_H - -int -xps_count_font_encodings(fz_font *font) -{ - FT_Face face = font->ftface; - return face->num_charmaps; -} - -void -xps_identify_font_encoding(fz_font *font, int idx, int *pid, int *eid) -{ - FT_Face face = font->ftface; - *pid = face->charmaps[idx]->platform_id; - *eid = face->charmaps[idx]->encoding_id; -} - -void -xps_select_font_encoding(fz_font *font, int idx) -{ - FT_Face face = font->ftface; - FT_Set_Charmap(face, face->charmaps[idx]); -} - -int -xps_encode_font_char(fz_font *font, int code) -{ - FT_Face face = font->ftface; - int gid = FT_Get_Char_Index(face, code); - if (gid == 0 && face->charmap->platform_id == 3 && face->charmap->encoding_id == 0) - gid = FT_Get_Char_Index(face, 0xF000 | code); - return gid; -} - -void -xps_measure_font_glyph(xps_context *ctx, fz_font *font, int gid, xps_glyph_metrics *mtx) -{ - int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM; - FT_Face face = font->ftface; - FT_Fixed hadv, vadv; - - FT_Set_Char_Size(face, 64, 64, 72, 72); - FT_Get_Advance(face, gid, mask, &hadv); - FT_Get_Advance(face, gid, mask | FT_LOAD_VERTICAL_LAYOUT, &vadv); - - mtx->hadv = hadv / 65536.0f; - mtx->vadv = vadv / 65536.0f; - mtx->vorg = face->ascender / (float) face->units_per_EM; -} diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c index 1936534b..91c7dbcd 100644 --- a/xps/xpsglyphs.c +++ b/xps/xpsglyphs.c @@ -1,13 +1,69 @@ #include "fitz.h" #include "muxps.h" -#include /* for tolower() */ +#include +#include FT_FREETYPE_H +#include FT_ADVANCES_H -static inline int unhex(int i) +static inline int ishex(int a) { - if (isdigit(i)) - return i - '0'; - return tolower(i) - 'a' + 10; + return (a >= 'A' && a <= 'F') || (a >= 'a' && a <= 'f') || (a >= '0' && a <= '9'); +} + +static inline int unhex(int a) +{ + if (a >= 'A' && a <= 'F') return a - 'A' + 0xA; + if (a >= 'a' && a <= 'f') return a - 'a' + 0xA; + if (a >= '0' && a <= '9') return a - '0'; + return 0; +} + +int +xps_count_font_encodings(fz_font *font) +{ + FT_Face face = font->ftface; + return face->num_charmaps; +} + +void +xps_identify_font_encoding(fz_font *font, int idx, int *pid, int *eid) +{ + FT_Face face = font->ftface; + *pid = face->charmaps[idx]->platform_id; + *eid = face->charmaps[idx]->encoding_id; +} + +void +xps_select_font_encoding(fz_font *font, int idx) +{ + FT_Face face = font->ftface; + FT_Set_Charmap(face, face->charmaps[idx]); +} + +int +xps_encode_font_char(fz_font *font, int code) +{ + FT_Face face = font->ftface; + int gid = FT_Get_Char_Index(face, code); + if (gid == 0 && face->charmap->platform_id == 3 && face->charmap->encoding_id == 0) + gid = FT_Get_Char_Index(face, 0xF000 | code); + return gid; +} + +void +xps_measure_font_glyph(xps_context *ctx, fz_font *font, int gid, xps_glyph_metrics *mtx) +{ + int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM; + FT_Face face = font->ftface; + FT_Fixed hadv, vadv; + + FT_Set_Char_Size(face, 64, 64, 72, 72); + FT_Get_Advance(face, gid, mask, &hadv); + FT_Get_Advance(face, gid, mask | FT_LOAD_VERTICAL_LAYOUT, &vadv); + + mtx->hadv = hadv / 65536.0f; + mtx->vadv = vadv / 65536.0f; + mtx->vorg = face->ascender / (float) face->units_per_EM; } /* @@ -28,7 +84,7 @@ xps_deobfuscate_font_resource(xps_context *ctx, xps_part *part) for (i = 0; i < 32 && *p; p++) { - if (isxdigit(*p)) + if (ishex(*p)) buf[i++] = *p; } buf[i] = 0; @@ -225,7 +281,7 @@ xps_parse_glyphs_imp(xps_context *ctx, fz_matrix ctm, fz_font *font, float size, { if (us && un > 0) { - int t = xps_utf8_to_ucs(&char_code, us, un); + int t = chartorune(&char_code, us); us += t; un -= t; } } diff --git a/xps/xpsimage.c b/xps/xpsimage.c index c9bf64fd..ced58bb5 100644 --- a/xps/xpsimage.c +++ b/xps/xpsimage.c @@ -25,9 +25,7 @@ xps_decode_image(xps_image **imagep, xps_context *ctx, xps_part *part) } else if (memcmp(buf, "II", 2) == 0 && buf[2] == 0xBC) { - error = xps_decode_jpegxr(imagep, ctx, buf, len); - if (error) - return fz_rethrow(error, "cannot decode JPEG-XR image"); + return fz_throw("JPEG-XR codec is not available"); } else if (memcmp(buf, "MM", 2) == 0 || memcmp(buf, "II", 2) == 0) { diff --git a/xps/xpsjxr.c b/xps/xpsjxr.c deleted file mode 100644 index 9499bdec..00000000 --- a/xps/xpsjxr.c +++ /dev/null @@ -1,10 +0,0 @@ -/* JPEG-XR (formerly HD-Photo (formerly Windows Media Photo)) support */ - -#include "fitz.h" -#include "muxps.h" - -int -xps_decode_jpegxr(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen) -{ - return fz_throw("JPEG-XR codec is not available"); -} diff --git a/xps/xpsmem.c b/xps/xpsmem.c index a85b2117..76175d68 100644 --- a/xps/xpsmem.c +++ b/xps/xpsmem.c @@ -1,8 +1,7 @@ #include "fitz.h" #include "muxps.h" -static inline int -xps_tolower(int c) +static inline int tolower(int c) { if (c >= 'A' && c <= 'Z') return c + 32; @@ -12,13 +11,13 @@ xps_tolower(int c) int xps_strcasecmp(char *a, char *b) { - while (xps_tolower(*a) == xps_tolower(*b)) + while (tolower(*a) == tolower(*b)) { if (*a++ == 0) return 0; b++; } - return xps_tolower(*a) - xps_tolower(*b); + return tolower(*a) - tolower(*b); } #define SEP(x) ((x)=='/' || (x) == 0) diff --git a/xps/xpsopacity.c b/xps/xpsopacity.c deleted file mode 100644 index 75064452..00000000 --- a/xps/xpsopacity.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -void -xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, - char *opacity_att, xml_element *opacity_mask_tag) -{ - float opacity; - - if (!opacity_att && !opacity_mask_tag) - return; - - opacity = 1.0; - if (opacity_att) - opacity = atof(opacity_att); - - if (opacity_mask_tag && !strcmp(xml_tag(opacity_mask_tag), "SolidColorBrush")) - { - char *scb_opacity_att = xml_att(opacity_mask_tag, "Opacity"); - char *scb_color_att = xml_att(opacity_mask_tag, "Color"); - if (scb_opacity_att) - opacity = opacity * atof(scb_opacity_att); - if (scb_color_att) - { - fz_colorspace *colorspace; - float samples[32]; - xps_parse_color(ctx, base_uri, scb_color_att, &colorspace, samples); - opacity = opacity * samples[0]; - } - opacity_mask_tag = NULL; - } - - if (ctx->opacity_top + 1 < nelem(ctx->opacity)) - { - ctx->opacity[ctx->opacity_top + 1] = ctx->opacity[ctx->opacity_top] * opacity; - ctx->opacity_top++; - } - - if (opacity_mask_tag) - { - ctx->dev->beginmask(ctx->dev->user, area, 0, NULL, NULL); - xps_parse_brush(ctx, ctm, area, base_uri, dict, opacity_mask_tag); - ctx->dev->endmask(ctx->dev->user); - } -} - -void -xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, - char *opacity_att, xml_element *opacity_mask_tag) -{ - if (!opacity_att && !opacity_mask_tag) - return; - - if (ctx->opacity_top > 0) - ctx->opacity_top--; - - if (opacity_mask_tag) - { - if (strcmp(xml_tag(opacity_mask_tag), "SolidColorBrush")) - ctx->dev->popclip(ctx->dev->user); - } -} diff --git a/xps/xpstile.c b/xps/xpstile.c index ecc0af3f..82ec543f 100644 --- a/xps/xpstile.c +++ b/xps/xpstile.c @@ -191,3 +191,38 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); } + +static void +xps_paint_visual_brush(xps_context *ctx, fz_matrix ctm, + char *base_uri, xps_resource *dict, xml_element *root, void *visual_tag) +{ + xps_parse_element(ctx, ctm, base_uri, dict, (xml_element *)visual_tag); +} + +void +xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, + char *base_uri, xps_resource *dict, xml_element *root) +{ + xml_element *node; + + char *visual_uri; + char *visual_att; + xml_element *visual_tag = NULL; + + visual_att = xml_att(root, "Visual"); + + for (node = xml_down(root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "VisualBrush.Visual")) + visual_tag = xml_down(node); + } + + visual_uri = base_uri; + xps_resolve_resource_reference(ctx, dict, &visual_att, &visual_tag, &visual_uri); + + if (visual_tag) + { + xps_parse_tiling_brush(ctx, ctm, area, + visual_uri, dict, root, xps_paint_visual_brush, visual_tag); + } +} diff --git a/xps/xpsutf.c b/xps/xpsutf.c deleted file mode 100644 index d5c1974e..00000000 --- a/xps/xpsutf.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -/* - * http://tools.ietf.org/html/rfc3629 - */ - -int -xps_utf8_to_ucs(int *p, const char *ss, int n) -{ - unsigned char *s = (unsigned char *)ss; - - if (s == NULL) - goto bad; - - if ((s[0] & 0x80) == 0) - { - *p = (s[0] & 0x7f); - return 1; - } - - if ((s[0] & 0xe0) == 0xc0) - { - if (n < 2 || s[1] < 0x80) - goto bad; - *p = (s[0] & 0x1f) << 6; - *p |= (s[1] & 0x3f); - return 2; - } - - if ((s[0] & 0xf0) == 0xe0) - { - if (n < 3 || s[1] < 0x80 || s[2] < 0x80) - goto bad; - *p = (s[0] & 0x0f) << 12; - *p |= (s[1] & 0x3f) << 6; - *p |= (s[2] & 0x3f); - return 3; - } - - if ((s[0] & 0xf8) == 0xf0) - { - if (n < 4 || s[1] < 0x80 || s[2] < 0x80 || s[3] < 0x80) - goto bad; - *p = (s[0] & 0x07) << 18; - *p |= (s[1] & 0x3f) << 12; - *p |= (s[2] & 0x3f) << 6; - *p |= (s[3] & 0x3f); - return 4; - } - -bad: - *p = 0x80; - return 1; -} diff --git a/xps/xpsvisual.c b/xps/xpsvisual.c deleted file mode 100644 index ac7de23c..00000000 --- a/xps/xpsvisual.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y }; - -static void -xps_paint_visual_brush(xps_context *ctx, fz_matrix ctm, - char *base_uri, xps_resource *dict, xml_element *root, void *visual_tag) -{ - xps_parse_element(ctx, ctm, base_uri, dict, (xml_element *)visual_tag); -} - -void -xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, xml_element *root) -{ - xml_element *node; - - char *visual_uri; - char *visual_att; - xml_element *visual_tag = NULL; - - visual_att = xml_att(root, "Visual"); - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "VisualBrush.Visual")) - visual_tag = xml_down(node); - } - - visual_uri = base_uri; - xps_resolve_resource_reference(ctx, dict, &visual_att, &visual_tag, &visual_uri); - - if (visual_tag) - { - xps_parse_tiling_brush(ctx, ctm, area, - visual_uri, dict, root, xps_paint_visual_brush, visual_tag); - } -} -- cgit v1.2.3 From f2eeb7778b262dffe2fc3bc81b9c942b07c12a25 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 3 Apr 2011 14:58:13 +0200 Subject: xps: Rearrange files, part two. --- Makefile | 36 +- apps/xpsdraw.c | 367 ++++++++++++++++++++ xps/xps_common.c | 282 +++++++++++++++ xps/xps_doc.c | 228 ++++++++++++ xps/xps_glyphs.c | 559 ++++++++++++++++++++++++++++++ xps/xps_gradient.c | 460 +++++++++++++++++++++++++ xps/xps_hash.c | 190 ++++++++++ xps/xps_image.c | 137 ++++++++ xps/xps_jpeg.c | 134 ++++++++ xps/xps_page.c | 154 +++++++++ xps/xps_path.c | 990 +++++++++++++++++++++++++++++++++++++++++++++++++++++ xps/xps_png.c | 580 +++++++++++++++++++++++++++++++ xps/xps_resource.c | 188 ++++++++++ xps/xps_tiff.c | 859 ++++++++++++++++++++++++++++++++++++++++++++++ xps/xps_tile.c | 228 ++++++++++++ xps/xps_util.c | 94 +++++ xps/xps_xml.c | 387 +++++++++++++++++++++ xps/xps_zip.c | 545 +++++++++++++++++++++++++++++ xps/xpscommon.c | 282 --------------- xps/xpsdoc.c | 228 ------------ xps/xpsglyphs.c | 559 ------------------------------ xps/xpsgradient.c | 460 ------------------------- xps/xpshash.c | 190 ---------- xps/xpsimage.c | 137 -------- xps/xpsjpeg.c | 134 -------- xps/xpsmem.c | 94 ----- xps/xpspage.c | 154 --------- xps/xpspath.c | 990 ----------------------------------------------------- xps/xpspng.c | 580 ------------------------------- xps/xpsresource.c | 188 ---------- xps/xpstiff.c | 859 ---------------------------------------------- xps/xpstile.c | 228 ------------ xps/xpstop.c | 367 -------------------- xps/xpsxml.c | 387 --------------------- xps/xpszip.c | 545 ----------------------------- 35 files changed, 6400 insertions(+), 6400 deletions(-) create mode 100644 apps/xpsdraw.c create mode 100644 xps/xps_common.c create mode 100644 xps/xps_doc.c create mode 100644 xps/xps_glyphs.c create mode 100644 xps/xps_gradient.c create mode 100644 xps/xps_hash.c create mode 100644 xps/xps_image.c create mode 100644 xps/xps_jpeg.c create mode 100644 xps/xps_page.c create mode 100644 xps/xps_path.c create mode 100644 xps/xps_png.c create mode 100644 xps/xps_resource.c create mode 100644 xps/xps_tiff.c create mode 100644 xps/xps_tile.c create mode 100644 xps/xps_util.c create mode 100644 xps/xps_xml.c create mode 100644 xps/xps_zip.c delete mode 100644 xps/xpscommon.c delete mode 100644 xps/xpsdoc.c delete mode 100644 xps/xpsglyphs.c delete mode 100644 xps/xpsgradient.c delete mode 100644 xps/xpshash.c delete mode 100644 xps/xpsimage.c delete mode 100644 xps/xpsjpeg.c delete mode 100644 xps/xpsmem.c delete mode 100644 xps/xpspage.c delete mode 100644 xps/xpspath.c delete mode 100644 xps/xpspng.c delete mode 100644 xps/xpsresource.c delete mode 100644 xps/xpstiff.c delete mode 100644 xps/xpstile.c delete mode 100644 xps/xpstop.c delete mode 100644 xps/xpsxml.c delete mode 100644 xps/xpszip.c diff --git a/Makefile b/Makefile index 96ebd0f2..fcfbad39 100644 --- a/Makefile +++ b/Makefile @@ -172,22 +172,22 @@ $(MUPDF_OBJ): $(MUPDF_HDR) MUXPS_HDR := $(FITZ_HDR) xps/muxps.h MUXPS_SRC := \ - xps/xpscommon.c \ - xps/xpsdoc.c \ - xps/xpsglyphs.c \ - xps/xpsgradient.c \ - xps/xpshash.c \ - xps/xpsimage.c \ - xps/xpsjpeg.c \ - xps/xpsmem.c \ - xps/xpspage.c \ - xps/xpspath.c \ - xps/xpspng.c \ - xps/xpsresource.c \ - xps/xpstiff.c \ - xps/xpstile.c \ - xps/xpsxml.c \ - xps/xpszip.c + xps/xps_common.c \ + xps/xps_doc.c \ + xps/xps_glyphs.c \ + xps/xps_gradient.c \ + xps/xps_hash.c \ + xps/xps_image.c \ + xps/xps_jpeg.c \ + xps/xps_page.c \ + xps/xps_path.c \ + xps/xps_png.c \ + xps/xps_resource.c \ + xps/xps_tiff.c \ + xps/xps_tile.c \ + xps/xps_util.c \ + xps/xps_xml.c \ + xps/xps_zip.c MUXPS_OBJ := $(MUXPS_SRC:xps/%.c=$(OBJDIR)/%.o) $(MUXPS_OBJ): $(MUXPS_HDR) @@ -375,8 +375,8 @@ $(PDFINFO_OBJ): $(MUPDF_HDR) $(PDFINFO_EXE): $(PDFINFO_OBJ) $(MUPDF_LIB) $(THIRD_LIBS) $(LD_CMD) -XPSDRAW_SRC=xps/xpstop.c -XPSDRAW_OBJ=$(XPSDRAW_SRC:xps/%.c=$(OBJDIR)/%.o) +XPSDRAW_SRC=apps/xpsdraw.c +XPSDRAW_OBJ=$(XPSDRAW_SRC:apps/%.c=$(OBJDIR)/%.o) XPSDRAW_EXE=$(OBJDIR)/xpsdraw $(XPSDRAW_OBJ): $(MUXPS_HDR) $(XPSDRAW_EXE): $(XPSDRAW_OBJ) $(MUXPS_LIB) $(THIRD_LIBS) diff --git a/apps/xpsdraw.c b/apps/xpsdraw.c new file mode 100644 index 00000000..921d677a --- /dev/null +++ b/apps/xpsdraw.c @@ -0,0 +1,367 @@ +#include "fitz.h" +#include "muxps.h" + +#ifdef _MSC_VER +#include +#else +#include +#endif + +char *output = NULL; +float resolution = 72; + +int showxml = 0; +int showtext = 0; +int showtime = 0; +int showmd5 = 0; +int savealpha = 0; +int uselist = 1; + +fz_colorspace *colorspace; +fz_glyphcache *glyphcache; +char *filename; + +struct { + int count, total; + int min, max; + int minpage, maxpage; +} timing; + +static void die(fz_error error) +{ + fz_catch(error, "aborting"); + exit(1); +} + +static void usage(void) +{ + fprintf(stderr, + "usage: xpsdraw [options] input.xps [pages]\n" + "\t-o -\toutput filename (%%d for page number)\n" + "\t\tsupported formats: pgm, ppm, pam, png\n" + "\t-r -\tresolution in dpi (default: 72)\n" + "\t-a\tsave alpha channel (only pam and png)\n" + "\t-g\trender in grayscale\n" + "\t-m\tshow timing information\n" + "\t-t\tshow text (-tt for xml)\n" + "\t-x\tshow display list\n" + "\t-d\tdisable use of display list\n" + "\t-5\tshow md5 checksums\n" + "\tpages\tcomma separated list of ranges\n"); + exit(1); +} + +static int gettime(void) +{ + static struct timeval first; + static int once = 1; + struct timeval now; + if (once) + { + gettimeofday(&first, NULL); + once = 0; + } + gettimeofday(&now, NULL); + return (now.tv_sec - first.tv_sec) * 1000 + (now.tv_usec - first.tv_usec) / 1000; +} + +static int isrange(char *s) +{ + while (*s) + { + if ((*s < '0' || *s > '9') && *s != '-' && *s != ',') + return 0; + s++; + } + return 1; +} + +static void +xps_run_page(xps_context *ctx, xps_page *page, fz_device *dev, fz_matrix ctm) +{ + ctx->dev = dev; + xps_parse_fixed_page(ctx, ctm, page); + ctx->dev = nil; +} + +static void drawpage(xps_context *ctx, int pagenum) +{ + xps_page *page; + fz_displaylist *list; + fz_device *dev; + int start; + + if (showtime) + { + start = gettime(); + } + + page = xps_load_page(ctx, pagenum - 1); + if (!page) + die(fz_throw("cannot load page %d in file '%s'", pagenum, filename)); + + list = nil; + + if (uselist) + { + list = fz_newdisplaylist(); + dev = fz_newlistdevice(list); + xps_run_page(ctx, page, dev, fz_identity); + fz_freedevice(dev); + } + + if (showxml) + { + dev = fz_newtracedevice(); + printf("\n", pagenum); + if (list) + fz_executedisplaylist(list, dev, fz_identity); + else + xps_run_page(ctx, page, dev, fz_identity); + printf("\n"); + fz_freedevice(dev); + } + + if (showtext) + { + fz_textspan *text = fz_newtextspan(); + dev = fz_newtextdevice(text); + if (list) + fz_executedisplaylist(list, dev, fz_identity); + else + xps_run_page(ctx, page, dev, fz_identity); + fz_freedevice(dev); + printf("[Page %d]\n", pagenum); + if (showtext > 1) + fz_debugtextspanxml(text); + else + fz_debugtextspan(text); + printf("\n"); + fz_freetextspan(text); + } + + if (showmd5 || showtime) + printf("page %s %d", filename, pagenum); + + if (output || showmd5 || showtime) + { + float zoom; + fz_matrix ctm; + fz_rect rect; + fz_bbox bbox; + fz_pixmap *pix; + + rect.x0 = rect.y0 = 0; + rect.x1 = page->width; + rect.y1 = page->height; + + zoom = resolution / 72; + ctm = fz_translate(0, -page->height); + ctm = fz_concat(ctm, fz_scale(zoom, zoom)); + bbox = fz_roundrect(fz_transformrect(ctm, rect)); + + /* TODO: banded rendering and multi-page ppm */ + + pix = fz_newpixmapwithrect(colorspace, bbox); + + if (savealpha) + fz_clearpixmap(pix); + else + fz_clearpixmapwithcolor(pix, 255); + + dev = fz_newdrawdevice(glyphcache, pix); + if (list) + fz_executedisplaylist(list, dev, ctm); + else + xps_run_page(ctx, page, dev, ctm); + fz_freedevice(dev); + + if (output) + { + char buf[512]; + sprintf(buf, output, pagenum); + if (strstr(output, ".pgm") || strstr(output, ".ppm") || strstr(output, ".pnm")) + fz_writepnm(pix, buf); + else if (strstr(output, ".pam")) + fz_writepam(pix, buf, savealpha); + else if (strstr(output, ".png")) + fz_writepng(pix, buf, savealpha); + } + + if (showmd5) + { + fz_md5 md5; + unsigned char digest[16]; + int i; + + fz_md5init(&md5); + fz_md5update(&md5, pix->samples, pix->w * pix->h * pix->n); + fz_md5final(&md5, digest); + + printf(" "); + for (i = 0; i < 16; i++) + printf("%02x", digest[i]); + } + + fz_droppixmap(pix); + } + + if (list) + fz_freedisplaylist(list); + + if (showtime) + { + int end = gettime(); + int diff = end - start; + + if (diff < timing.min) + { + timing.min = diff; + timing.minpage = pagenum; + } + if (diff > timing.max) + { + timing.max = diff; + timing.maxpage = pagenum; + } + timing.total += diff; + timing.count ++; + + printf(" %dms", diff); + } + + if (showmd5 || showtime) + printf("\n"); +} + + +static void drawrange(xps_context *ctx, char *range) +{ + int page, spage, epage; + char *spec, *dash; + + spec = fz_strsep(&range, ","); + while (spec) + { + dash = strchr(spec, '-'); + + if (dash == spec) + spage = epage = xps_count_pages(ctx); + else + spage = epage = atoi(spec); + + if (dash) + { + if (strlen(dash) > 1) + epage = atoi(dash + 1); + else + epage = xps_count_pages(ctx); + } + + spage = CLAMP(spage, 1, xps_count_pages(ctx)); + epage = CLAMP(epage, 1, xps_count_pages(ctx)); + + if (spage < epage) + for (page = spage; page <= epage; page++) + drawpage(ctx, page); + else + for (page = spage; page >= epage; page--) + drawpage(ctx, page); + + spec = fz_strsep(&range, ","); + } +} + +int main(int argc, char **argv) +{ + int grayscale = 0; + int accelerate = 1; + xps_context *ctx; + int code; + int c; + + while ((c = fz_getopt(argc, argv, "o:p:r:Aadgmtx5")) != -1) + { + switch (c) + { + case 'o': output = fz_optarg; break; + case 'r': resolution = atof(fz_optarg); break; + case 'A': accelerate = 0; break; + case 'a': savealpha = 1; break; + case 'm': showtime++; break; + case 't': showtext++; break; + case 'x': showxml++; break; + case '5': showmd5++; break; + case 'g': grayscale++; break; + case 'd': uselist = 0; break; + default: usage(); break; + } + } + + if (fz_optind == argc) + usage(); + + if (!showtext && !showxml && !showtime && !showmd5 && !output) + { + printf("nothing to do\n"); + exit(0); + } + + if (accelerate) + fz_accelerate(); + + glyphcache = fz_newglyphcache(); + + colorspace = fz_devicergb; + if (grayscale) + colorspace = fz_devicegray; + if (output && strstr(output, ".pgm")) + colorspace = fz_devicegray; + if (output && strstr(output, ".ppm")) + colorspace = fz_devicergb; + + timing.count = 0; + timing.total = 0; + timing.min = 1 << 30; + timing.max = 0; + timing.minpage = 0; + timing.maxpage = 0; + + if (showxml) + printf("\n"); + + while (fz_optind < argc) + { + filename = argv[fz_optind++]; + + ctx = xps_new_context(); + code = xps_open_file(ctx, filename); + if (code) + die(fz_rethrow(code, "cannot open document: %s", filename)); + + if (showxml) + printf("\n", filename); + + if (fz_optind == argc || !isrange(argv[fz_optind])) + drawrange(ctx, "1-"); + if (fz_optind < argc && isrange(argv[fz_optind])) + drawrange(ctx, argv[fz_optind++]); + + if (showxml) + printf("\n"); + + xps_free_context(ctx); + } + + if (showtime) + { + printf("total %dms / %d pages for an average of %dms\n", + timing.total, timing.count, timing.total / timing.count); + printf("fastest page %d: %dms\n", timing.minpage, timing.min); + printf("slowest page %d: %dms\n", timing.maxpage, timing.max); + } + + fz_freeglyphcache(glyphcache); + + return 0; +} diff --git a/xps/xps_common.c b/xps/xps_common.c new file mode 100644 index 00000000..7e489ef7 --- /dev/null +++ b/xps/xps_common.c @@ -0,0 +1,282 @@ +#include "fitz.h" +#include "muxps.h" + +static inline int unhex(int a) +{ + if (a >= 'A' && a <= 'F') return a - 'A' + 0xA; + if (a >= 'a' && a <= 'f') return a - 'a' + 0xA; + if (a >= '0' && a <= '9') return a - '0'; + return 0; +} + +void +xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node) +{ + /* SolidColorBrushes are handled in a special case and will never show up here */ + if (!strcmp(xml_tag(node), "ImageBrush")) + xps_parse_image_brush(ctx, ctm, area, base_uri, dict, node); + else if (!strcmp(xml_tag(node), "VisualBrush")) + xps_parse_visual_brush(ctx, ctm, area, base_uri, dict, node); + else if (!strcmp(xml_tag(node), "LinearGradientBrush")) + xps_parse_linear_gradient_brush(ctx, ctm, area, base_uri, dict, node); + else if (!strcmp(xml_tag(node), "RadialGradientBrush")) + xps_parse_radial_gradient_brush(ctx, ctm, area, base_uri, dict, node); + else + fz_warn("unknown brush tag: %s", xml_tag(node)); +} + +void +xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node) +{ + if (!strcmp(xml_tag(node), "Path")) + xps_parse_path(ctx, ctm, base_uri, dict, node); + if (!strcmp(xml_tag(node), "Glyphs")) + xps_parse_glyphs(ctx, ctm, base_uri, dict, node); + if (!strcmp(xml_tag(node), "Canvas")) + xps_parse_canvas(ctx, ctm, base_uri, dict, node); + /* skip unknown tags (like Foo.Resources and similar) */ +} + +void +xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, + char *base_uri, xps_resource *dict, + char *opacity_att, xml_element *opacity_mask_tag) +{ + float opacity; + + if (!opacity_att && !opacity_mask_tag) + return; + + opacity = 1.0; + if (opacity_att) + opacity = atof(opacity_att); + + if (opacity_mask_tag && !strcmp(xml_tag(opacity_mask_tag), "SolidColorBrush")) + { + char *scb_opacity_att = xml_att(opacity_mask_tag, "Opacity"); + char *scb_color_att = xml_att(opacity_mask_tag, "Color"); + if (scb_opacity_att) + opacity = opacity * atof(scb_opacity_att); + if (scb_color_att) + { + fz_colorspace *colorspace; + float samples[32]; + xps_parse_color(ctx, base_uri, scb_color_att, &colorspace, samples); + opacity = opacity * samples[0]; + } + opacity_mask_tag = NULL; + } + + if (ctx->opacity_top + 1 < nelem(ctx->opacity)) + { + ctx->opacity[ctx->opacity_top + 1] = ctx->opacity[ctx->opacity_top] * opacity; + ctx->opacity_top++; + } + + if (opacity_mask_tag) + { + ctx->dev->beginmask(ctx->dev->user, area, 0, NULL, NULL); + xps_parse_brush(ctx, ctm, area, base_uri, dict, opacity_mask_tag); + ctx->dev->endmask(ctx->dev->user); + } +} + +void +xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, + char *opacity_att, xml_element *opacity_mask_tag) +{ + if (!opacity_att && !opacity_mask_tag) + return; + + if (ctx->opacity_top > 0) + ctx->opacity_top--; + + if (opacity_mask_tag) + { + if (strcmp(xml_tag(opacity_mask_tag), "SolidColorBrush")) + ctx->dev->popclip(ctx->dev->user); + } +} + +void +xps_parse_render_transform(xps_context *ctx, char *transform, fz_matrix *matrix) +{ + float args[6]; + char *s = transform; + int i; + + args[0] = 1.0; args[1] = 0.0; + args[2] = 0.0; args[3] = 1.0; + args[4] = 0.0; args[5] = 0.0; + + for (i = 0; i < 6 && *s; i++) + { + args[i] = atof(s); + while (*s && *s != ',') + s++; + if (*s == ',') + s++; + } + + matrix->a = args[0]; matrix->b = args[1]; + matrix->c = args[2]; matrix->d = args[3]; + matrix->e = args[4]; matrix->f = args[5]; +} + +void +xps_parse_matrix_transform(xps_context *ctx, xml_element *root, fz_matrix *matrix) +{ + char *transform; + + *matrix = fz_identity; + + if (!strcmp(xml_tag(root), "MatrixTransform")) + { + transform = xml_att(root, "Matrix"); + if (transform) + xps_parse_render_transform(ctx, transform, matrix); + } +} + +void +xps_parse_rectangle(xps_context *ctx, char *text, fz_rect *rect) +{ + float args[4]; + char *s = text; + int i; + + args[0] = 0.0; args[1] = 0.0; + args[2] = 1.0; args[3] = 1.0; + + for (i = 0; i < 4 && *s; i++) + { + args[i] = atof(s); + while (*s && *s != ',') + s++; + if (*s == ',') + s++; + } + + rect->x0 = args[0]; + rect->y0 = args[1]; + rect->x1 = args[0] + args[2]; + rect->y1 = args[1] + args[3]; +} + +static int count_commas(char *s) +{ + int n = 0; + while (*s) + { + if (*s == ',') + n ++; + s ++; + } + return n; +} + +void +xps_parse_color(xps_context *ctx, char *base_uri, char *string, + fz_colorspace **csp, float *samples) +{ + char *p; + int i, n; + char buf[1024]; + char *profile; + + *csp = fz_devicergb; + + samples[0] = 1.0; + samples[1] = 0.0; + samples[2] = 0.0; + samples[3] = 0.0; + + if (string[0] == '#') + { + if (strlen(string) == 9) + { + samples[0] = unhex(string[1]) * 16 + unhex(string[2]); + samples[1] = unhex(string[3]) * 16 + unhex(string[4]); + samples[2] = unhex(string[5]) * 16 + unhex(string[6]); + samples[3] = unhex(string[7]) * 16 + unhex(string[8]); + } + else + { + samples[0] = 255.0; + samples[1] = unhex(string[1]) * 16 + unhex(string[2]); + samples[2] = unhex(string[3]) * 16 + unhex(string[4]); + samples[3] = unhex(string[5]) * 16 + unhex(string[6]); + } + + samples[0] /= 255.0; + samples[1] /= 255.0; + samples[2] /= 255.0; + samples[3] /= 255.0; + } + + else if (string[0] == 's' && string[1] == 'c' && string[2] == '#') + { + if (count_commas(string) == 2) + sscanf(string, "sc#%g,%g,%g", samples + 1, samples + 2, samples + 3); + if (count_commas(string) == 3) + sscanf(string, "sc#%g,%g,%g,%g", samples, samples + 1, samples + 2, samples + 3); + } + + else if (strstr(string, "ContextColor ") == string) + { + /* Crack the string for profile name and sample values */ + strcpy(buf, string); + + profile = strchr(buf, ' '); + if (!profile) + { + fz_warn("cannot find icc profile uri in '%s'", string); + return; + } + + *profile++ = 0; + p = strchr(profile, ' '); + if (!p) + { + fz_warn("cannot find component values in '%s'", profile); + return; + } + + *p++ = 0; + n = count_commas(p) + 1; + i = 0; + while (i < n) + { + samples[i++] = atof(p); + p = strchr(p, ','); + if (!p) + break; + p ++; + if (*p == ' ') + p ++; + } + while (i < n) + { + samples[i++] = 0.0; + } + + /* TODO: load ICC profile */ + switch (n) + { + case 2: *csp = fz_devicegray; break; + case 4: *csp = fz_devicergb; break; + case 5: *csp = fz_devicecmyk; break; + default: *csp = fz_devicegray; break; + } + } +} + +void +xps_set_color(xps_context *ctx, fz_colorspace *colorspace, float *samples) +{ + int i; + ctx->colorspace = colorspace; + for (i = 0; i < colorspace->n; i++) + ctx->color[i] = samples[i + 1]; + ctx->alpha = samples[0] * ctx->opacity[ctx->opacity_top]; +} diff --git a/xps/xps_doc.c b/xps/xps_doc.c new file mode 100644 index 00000000..94fb32d7 --- /dev/null +++ b/xps/xps_doc.c @@ -0,0 +1,228 @@ +#include "fitz.h" +#include "muxps.h" + +xps_part * +xps_new_part(xps_context *ctx, char *name, int size) +{ + xps_part *part; + + part = fz_malloc(sizeof(xps_part)); + part->name = fz_strdup(name); + part->size = size; + part->data = fz_malloc(size + 1); + part->data[size] = 0; /* null-terminate for xml parser */ + + return part; +} + +void +xps_free_part(xps_context *ctx, xps_part *part) +{ + fz_free(part->name); + fz_free(part->data); + fz_free(part); +} + +/* + * The FixedDocumentSequence and FixedDocument parts determine + * which parts correspond to actual pages, and the page order. + */ + +void +xps_debug_fixdocseq(xps_context *ctx) +{ + xps_document *fixdoc = ctx->first_fixdoc; + xps_page *page = ctx->first_page; + + if (ctx->start_part) + printf("start part %s\n", ctx->start_part); + + while (fixdoc) + { + printf("fixdoc %s\n", fixdoc->name); + fixdoc = fixdoc->next; + } + + while (page) + { + printf("page %s w=%d h=%d\n", page->name, page->width, page->height); + page = page->next; + } +} + +static void +xps_add_fixed_document(xps_context *ctx, char *name) +{ + xps_document *fixdoc; + + /* Check for duplicates first */ + for (fixdoc = ctx->first_fixdoc; fixdoc; fixdoc = fixdoc->next) + if (!strcmp(fixdoc->name, name)) + return; + + fixdoc = fz_malloc(sizeof(xps_document)); + fixdoc->name = fz_strdup(name); + fixdoc->next = NULL; + + if (!ctx->first_fixdoc) + { + ctx->first_fixdoc = fixdoc; + ctx->last_fixdoc = fixdoc; + } + else + { + ctx->last_fixdoc->next = fixdoc; + ctx->last_fixdoc = fixdoc; + } +} + +void +xps_free_fixed_documents(xps_context *ctx) +{ + xps_document *node = ctx->first_fixdoc; + while (node) + { + xps_document *next = node->next; + fz_free(node->name); + fz_free(node); + node = next; + } + ctx->first_fixdoc = NULL; + ctx->last_fixdoc = NULL; +} + +static void +xps_add_fixed_page(xps_context *ctx, char *name, int width, int height) +{ + xps_page *page; + + /* Check for duplicates first */ + for (page = ctx->first_page; page; page = page->next) + if (!strcmp(page->name, name)) + return; + + page = fz_malloc(sizeof(xps_page)); + page->name = fz_strdup(name); + page->width = width; + page->height = height; + page->root = NULL; + page->next = NULL; + + if (!ctx->first_page) + { + ctx->first_page = page; + ctx->last_page = page; + } + else + { + ctx->last_page->next = page; + ctx->last_page = page; + } +} + +void +xps_free_fixed_pages(xps_context *ctx) +{ + xps_page *node = ctx->first_page; + while (node) + { + xps_page *next = node->next; + fz_free(node->name); + fz_free(node); + node = next; + } + ctx->first_page = NULL; + ctx->last_page = NULL; +} + +/* + * Parse the fixed document sequence structure and _rels/.rels to find the start part. + */ + +static void +xps_parse_metadata_imp(xps_context *ctx, xml_element *item) +{ + while (item) + { + xps_parse_metadata_imp(ctx, xml_down(item)); + + if (!strcmp(xml_tag(item), "Relationship")) + { + char *target = xml_att(item, "Target"); + char *type = xml_att(item, "Type"); + if (target && type) + { + char tgtbuf[1024]; + xps_absolute_path(tgtbuf, ctx->base_uri, target, sizeof tgtbuf); + if (!strcmp(type, REL_START_PART)) + ctx->start_part = fz_strdup(tgtbuf); + } + } + + if (!strcmp(xml_tag(item), "DocumentReference")) + { + char *source = xml_att(item, "Source"); + if (source) + { + char srcbuf[1024]; + xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf); + xps_add_fixed_document(ctx, srcbuf); + } + } + + if (!strcmp(xml_tag(item), "PageContent")) + { + char *source = xml_att(item, "Source"); + char *width_att = xml_att(item, "Width"); + char *height_att = xml_att(item, "Height"); + int width = width_att ? atoi(width_att) : 0; + int height = height_att ? atoi(height_att) : 0; + if (source) + { + char srcbuf[1024]; + xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf); + xps_add_fixed_page(ctx, srcbuf, width, height); + } + } + + item = xml_next(item); + } +} + +int +xps_parse_metadata(xps_context *ctx, xps_part *part) +{ + xml_element *root; + char buf[1024]; + char *s; + + /* Save directory name part */ + fz_strlcpy(buf, part->name, sizeof buf); + s = strrchr(buf, '/'); + if (s) + s[0] = 0; + + /* _rels parts are voodoo: their URI references are from + * the part they are associated with, not the actual _rels + * part being parsed. + */ + s = strstr(buf, "/_rels"); + if (s) + *s = 0; + + ctx->base_uri = buf; + ctx->part_uri = part->name; + + root = xml_parse_document(part->data, part->size); + if (!root) + return fz_rethrow(-1, "cannot parse metadata part '%s'", part->name); + + xps_parse_metadata_imp(ctx, root); + + xml_free_element(root); + + ctx->base_uri = NULL; + ctx->part_uri = NULL; + + return fz_okay; +} diff --git a/xps/xps_glyphs.c b/xps/xps_glyphs.c new file mode 100644 index 00000000..91c7dbcd --- /dev/null +++ b/xps/xps_glyphs.c @@ -0,0 +1,559 @@ +#include "fitz.h" +#include "muxps.h" + +#include +#include FT_FREETYPE_H +#include FT_ADVANCES_H + +static inline int ishex(int a) +{ + return (a >= 'A' && a <= 'F') || (a >= 'a' && a <= 'f') || (a >= '0' && a <= '9'); +} + +static inline int unhex(int a) +{ + if (a >= 'A' && a <= 'F') return a - 'A' + 0xA; + if (a >= 'a' && a <= 'f') return a - 'a' + 0xA; + if (a >= '0' && a <= '9') return a - '0'; + return 0; +} + +int +xps_count_font_encodings(fz_font *font) +{ + FT_Face face = font->ftface; + return face->num_charmaps; +} + +void +xps_identify_font_encoding(fz_font *font, int idx, int *pid, int *eid) +{ + FT_Face face = font->ftface; + *pid = face->charmaps[idx]->platform_id; + *eid = face->charmaps[idx]->encoding_id; +} + +void +xps_select_font_encoding(fz_font *font, int idx) +{ + FT_Face face = font->ftface; + FT_Set_Charmap(face, face->charmaps[idx]); +} + +int +xps_encode_font_char(fz_font *font, int code) +{ + FT_Face face = font->ftface; + int gid = FT_Get_Char_Index(face, code); + if (gid == 0 && face->charmap->platform_id == 3 && face->charmap->encoding_id == 0) + gid = FT_Get_Char_Index(face, 0xF000 | code); + return gid; +} + +void +xps_measure_font_glyph(xps_context *ctx, fz_font *font, int gid, xps_glyph_metrics *mtx) +{ + int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM; + FT_Face face = font->ftface; + FT_Fixed hadv, vadv; + + FT_Set_Char_Size(face, 64, 64, 72, 72); + FT_Get_Advance(face, gid, mask, &hadv); + FT_Get_Advance(face, gid, mask | FT_LOAD_VERTICAL_LAYOUT, &vadv); + + mtx->hadv = hadv / 65536.0f; + mtx->vadv = vadv / 65536.0f; + mtx->vorg = face->ascender / (float) face->units_per_EM; +} + +/* + * Some fonts in XPS are obfuscated by XOR:ing the first 32 bytes of the + * data with the GUID in the fontname. + */ +static void +xps_deobfuscate_font_resource(xps_context *ctx, xps_part *part) +{ + byte buf[33]; + byte key[16]; + char *p; + int i; + + p = strrchr(part->name, '/'); + if (!p) + p = part->name; + + for (i = 0; i < 32 && *p; p++) + { + if (ishex(*p)) + buf[i++] = *p; + } + buf[i] = 0; + + if (i != 32) + { + fz_warn("cannot extract GUID from obfuscated font part name"); + return; + } + + for (i = 0; i < 16; i++) + key[i] = unhex(buf[i*2+0]) * 16 + unhex(buf[i*2+1]); + + for (i = 0; i < 16; i++) + { + part->data[i] ^= key[15-i]; + part->data[i+16] ^= key[15-i]; + } +} + +static void +xps_select_best_font_encoding(fz_font *font) +{ + static struct { int pid, eid; } xps_cmap_list[] = + { + { 3, 10 }, /* Unicode with surrogates */ + { 3, 1 }, /* Unicode without surrogates */ + { 3, 5 }, /* Wansung */ + { 3, 4 }, /* Big5 */ + { 3, 3 }, /* Prc */ + { 3, 2 }, /* ShiftJis */ + { 3, 0 }, /* Symbol */ + // { 0, * }, -- Unicode (deprecated) + { 1, 0 }, + { -1, -1 }, + }; + + int i, k, n, pid, eid; + + n = xps_count_font_encodings(font); + for (k = 0; xps_cmap_list[k].pid != -1; k++) + { + for (i = 0; i < n; i++) + { + xps_identify_font_encoding(font, i, &pid, &eid); + if (pid == xps_cmap_list[k].pid && eid == xps_cmap_list[k].eid) + { + xps_select_font_encoding(font, i); + return; + } + } + } + + fz_warn("cannot find a suitable cmap"); +} + +/* + * Parse and draw an XPS element. + * + * Indices syntax: + + GlyphIndices = GlyphMapping ( ";" GlyphMapping ) + GlyphMapping = ( [ClusterMapping] GlyphIndex ) [GlyphMetrics] + ClusterMapping = "(" ClusterCodeUnitCount [":" ClusterGlyphCount] ")" + ClusterCodeUnitCount = * DIGIT + ClusterGlyphCount = * DIGIT + GlyphIndex = * DIGIT + GlyphMetrics = "," AdvanceWidth ["," uOffset ["," vOffset]] + AdvanceWidth = ["+"] RealNum + uOffset = ["+" | "-"] RealNum + vOffset = ["+" | "-"] RealNum + RealNum = ((DIGIT ["." DIGIT]) | ("." DIGIT)) [Exponent] + Exponent = ( ("E"|"e") ("+"|"-") DIGIT ) + + */ + +static char * +xps_parse_digits(char *s, int *digit) +{ + *digit = 0; + while (*s >= '0' && *s <= '9') + { + *digit = *digit * 10 + (*s - '0'); + s ++; + } + return s; +} + +static inline int is_real_num_char(int c) +{ + return (c >= '0' && c <= '9') || c == 'e' || c == 'E' || c == '+' || c == '-' || c == '.'; +} + +static char * +xps_parse_real_num(char *s, float *number) +{ + char buf[64]; + char *p = buf; + while (is_real_num_char(*s)) + *p++ = *s++; + *p = 0; + if (buf[0]) + *number = atof(buf); + return s; +} + +static char * +xps_parse_cluster_mapping(char *s, int *code_count, int *glyph_count) +{ + if (*s == '(') + s = xps_parse_digits(s + 1, code_count); + if (*s == ':') + s = xps_parse_digits(s + 1, glyph_count); + if (*s == ')') + s ++; + return s; +} + +static char * +xps_parse_glyph_index(char *s, int *glyph_index) +{ + if (*s >= '0' && *s <= '9') + s = xps_parse_digits(s, glyph_index); + return s; +} + +static char * +xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs) +{ + if (*s == ',') + s = xps_parse_real_num(s + 1, advance); + if (*s == ',') + s = xps_parse_real_num(s + 1, uofs); + if (*s == ',') + s = xps_parse_real_num(s + 1, vofs); + return s; +} + +/* + * Parse unicode and indices strings and encode glyphs. + * Calculate metrics for positioning. + */ +static fz_text * +xps_parse_glyphs_imp(xps_context *ctx, fz_matrix ctm, fz_font *font, float size, + float originx, float originy, int is_sideways, int bidi_level, + char *indices, char *unicode) +{ + xps_glyph_metrics mtx; + fz_text *text; + fz_matrix tm; + float e, f; + float x = originx; + float y = originy; + char *us = unicode; + char *is = indices; + int un = 0; + + if (!unicode && !indices) + fz_warn("glyphs element with neither characters nor indices"); + + if (us) + { + if (us[0] == '{' && us[1] == '}') + us = us + 2; + un = strlen(us); + } + + if (is_sideways) + tm = fz_concat(fz_scale(-size, size), fz_rotate(90)); + else + tm = fz_scale(size, -size); + + text = fz_newtext(font, tm, is_sideways); + + while ((us && un > 0) || (is && *is)) + { + int char_code = '?'; + int code_count = 1; + int glyph_count = 1; + + if (is && *is) + { + is = xps_parse_cluster_mapping(is, &code_count, &glyph_count); + } + + if (code_count < 1) + code_count = 1; + if (glyph_count < 1) + glyph_count = 1; + + /* TODO: add code chars with cluster mappings for proper text extraction */ + + while (code_count--) + { + if (us && un > 0) + { + int t = chartorune(&char_code, us); + us += t; un -= t; + } + } + + while (glyph_count--) + { + int glyph_index = -1; + float u_offset = 0.0; + float v_offset = 0.0; + float advance; + + if (is && *is) + is = xps_parse_glyph_index(is, &glyph_index); + + if (glyph_index == -1) + glyph_index = xps_encode_font_char(font, char_code); + + xps_measure_font_glyph(ctx, font, glyph_index, &mtx); + if (is_sideways) + advance = mtx.vadv * 100.0; + else if (bidi_level & 1) + advance = -mtx.hadv * 100.0; + else + advance = mtx.hadv * 100.0; + + if (is && *is) + { + is = xps_parse_glyph_metrics(is, &advance, &u_offset, &v_offset); + if (*is == ';') + is ++; + } + + if (bidi_level & 1) + u_offset = -mtx.hadv * 100 - u_offset; + + u_offset = u_offset * 0.01 * size; + v_offset = v_offset * 0.01 * size; + + if (is_sideways) + { + e = x + u_offset + (mtx.vorg * size); + f = y - v_offset + (mtx.hadv * 0.5 * size); + } + else + { + e = x + u_offset; + f = y - v_offset; + } + + fz_addtext(text, glyph_index, char_code, e, f); + + x += advance * 0.01 * size; + } + } + + return text; +} + +void +xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, + char *base_uri, xps_resource *dict, xml_element *root) +{ + xml_element *node; + int code; + + char *fill_uri; + char *opacity_mask_uri; + + char *bidi_level_att; + char *caret_stops_att; + char *fill_att; + char *font_size_att; + char *font_uri_att; + char *origin_x_att; + char *origin_y_att; + char *is_sideways_att; + char *indices_att; + char *unicode_att; + char *style_att; + char *transform_att; + char *clip_att; + char *opacity_att; + char *opacity_mask_att; + + xml_element *transform_tag = NULL; + xml_element *clip_tag = NULL; + xml_element *fill_tag = NULL; + xml_element *opacity_mask_tag = NULL; + + char *fill_opacity_att = NULL; + + xps_part *part; + fz_font *font; + + char partname[1024]; + char *subfont; + + float font_size = 10.0; + int subfontid = 0; + int is_sideways = 0; + int bidi_level = 0; + + fz_text *text; + fz_rect area; + + /* + * Extract attributes and extended attributes. + */ + + bidi_level_att = xml_att(root, "BidiLevel"); + caret_stops_att = xml_att(root, "CaretStops"); + fill_att = xml_att(root, "Fill"); + font_size_att = xml_att(root, "FontRenderingEmSize"); + font_uri_att = xml_att(root, "FontUri"); + origin_x_att = xml_att(root, "OriginX"); + origin_y_att = xml_att(root, "OriginY"); + is_sideways_att = xml_att(root, "IsSideways"); + indices_att = xml_att(root, "Indices"); + unicode_att = xml_att(root, "UnicodeString"); + style_att = xml_att(root, "StyleSimulations"); + transform_att = xml_att(root, "RenderTransform"); + clip_att = xml_att(root, "Clip"); + opacity_att = xml_att(root, "Opacity"); + opacity_mask_att = xml_att(root, "OpacityMask"); + + for (node = xml_down(root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "Glyphs.RenderTransform")) + transform_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Glyphs.OpacityMask")) + opacity_mask_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Glyphs.Clip")) + clip_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Glyphs.Fill")) + fill_tag = xml_down(node); + } + + fill_uri = base_uri; + opacity_mask_uri = base_uri; + + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &fill_att, &fill_tag, &fill_uri); + xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); + + /* + * Check that we have all the necessary information. + */ + + if (!font_size_att || !font_uri_att || !origin_x_att || !origin_y_att) { + fz_warn("missing attributes in glyphs element"); + return; + } + + if (!indices_att && !unicode_att) + return; /* nothing to draw */ + + if (is_sideways_att) + is_sideways = !strcmp(is_sideways_att, "true"); + + if (bidi_level_att) + bidi_level = atoi(bidi_level_att); + + /* + * Find and load the font resource + */ + + xps_absolute_path(partname, base_uri, font_uri_att, sizeof partname); + subfont = strrchr(partname, '#'); + if (subfont) + { + subfontid = atoi(subfont + 1); + *subfont = 0; + } + + font = xps_hash_lookup(ctx->font_table, partname); + if (!font) + { + part = xps_read_part(ctx, partname); + if (!part) { + fz_warn("cannot find font resource part '%s'", partname); + return; + } + + /* deobfuscate if necessary */ + if (strstr(part->name, ".odttf")) + xps_deobfuscate_font_resource(ctx, part); + if (strstr(part->name, ".ODTTF")) + xps_deobfuscate_font_resource(ctx, part); + + code = fz_newfontfrombuffer(&font, part->data, part->size, subfontid); + if (code) { + fz_catch(code, "cannot load font resource '%s'", partname); + xps_free_part(ctx, part); + return; + } + + xps_select_best_font_encoding(font); + + xps_hash_insert(ctx, ctx->font_table, part->name, font); + + /* NOTE: we kept part->name in the hashtable and part->data in the font */ + fz_free(part); + } + + /* + * Set up graphics state. + */ + + if (transform_att || transform_tag) + { + fz_matrix transform; + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + ctm = fz_concat(transform, ctm); + } + + if (clip_att || clip_tag) + xps_clip(ctx, ctm, dict, clip_att, clip_tag); + + font_size = atof(font_size_att); + + text = xps_parse_glyphs_imp(ctx, ctm, font, font_size, + atof(origin_x_att), atof(origin_y_att), + is_sideways, bidi_level, indices_att, unicode_att); + + area = fz_boundtext(text, ctm); + + xps_begin_opacity(ctx, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + + /* + * If it's a solid color brush fill/stroke do a simple fill + */ + + if (fill_tag && !strcmp(xml_tag(fill_tag), "SolidColorBrush")) + { + fill_opacity_att = xml_att(fill_tag, "Opacity"); + fill_att = xml_att(fill_tag, "Color"); + fill_tag = NULL; + } + + if (fill_att) + { + float samples[32]; + fz_colorspace *colorspace; + + xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples); + if (fill_opacity_att) + samples[0] = atof(fill_opacity_att); + xps_set_color(ctx, colorspace, samples); + + ctx->dev->filltext(ctx->dev->user, text, ctm, + ctx->colorspace, ctx->color, ctx->alpha); + } + + /* + * If it's a visual brush or image, use the charpath as a clip mask to paint brush + */ + + if (fill_tag) + { + ctx->dev->cliptext(ctx->dev->user, text, ctm, 0); + xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag); + ctx->dev->popclip(ctx->dev->user); + } + + xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + + fz_freetext(text); + + if (clip_att || clip_tag) + ctx->dev->popclip(ctx->dev->user); +} diff --git a/xps/xps_gradient.c b/xps/xps_gradient.c new file mode 100644 index 00000000..8c877a0b --- /dev/null +++ b/xps/xps_gradient.c @@ -0,0 +1,460 @@ +#include "fitz.h" +#include "muxps.h" + +#define MAX_STOPS 256 + +enum { SPREAD_PAD, SPREAD_REPEAT, SPREAD_REFLECT }; + +/* + * Parse a list of GradientStop elements. + * Fill the offset and color arrays, and + * return the number of stops parsed. + */ + +struct stop +{ + float offset; + float r, g, b, a; +}; + +static int cmp_stop(const void *a, const void *b) +{ + const struct stop *astop = a; + const struct stop *bstop = b; + float diff = astop->offset - bstop->offset; + if (diff < 0) + return -1; + if (diff > 0) + return 1; + return 0; +} + +static inline float lerp(float a, float b, float x) +{ + return a + (b - a) * x; +} + +static int +xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xml_element *node, + struct stop *stops, int maxcount) +{ + fz_colorspace *colorspace; + float sample[8]; + float rgb[3]; + int before, after; + int count; + int i; + + /* We may have to insert 2 extra stops when postprocessing */ + maxcount -= 2; + + count = 0; + while (node && count < maxcount) + { + if (!strcmp(xml_tag(node), "GradientStop")) + { + char *offset = xml_att(node, "Offset"); + char *color = xml_att(node, "Color"); + if (offset && color) + { + stops[count].offset = atof(offset); + + xps_parse_color(ctx, base_uri, color, &colorspace, sample); + + fz_convertcolor(colorspace, sample + 1, fz_devicergb, rgb); + + stops[count].r = rgb[0]; + stops[count].g = rgb[1]; + stops[count].b = rgb[2]; + stops[count].a = sample[0]; + + count ++; + } + } + node = xml_next(node); + } + + if (count == 0) + { + fz_warn("gradient brush has no gradient stops"); + stops[0].offset = 0; + stops[0].r = 0; + stops[0].g = 0; + stops[0].b = 0; + stops[0].a = 1; + stops[1].offset = 1; + stops[1].r = 1; + stops[1].g = 1; + stops[1].b = 1; + stops[1].a = 1; + return 2; + } + + if (count == maxcount) + fz_warn("gradient brush exceeded maximum number of gradient stops"); + + /* Postprocess to make sure the range of offsets is 0.0 to 1.0 */ + + qsort(stops, count, sizeof(struct stop), cmp_stop); + + before = -1; + after = -1; + + for (i = 0; i < count; i++) + { + if (stops[i].offset < 0) + before = i; + if (stops[i].offset > 1) + { + after = i; + break; + } + } + + /* Remove all stops < 0 except the largest one */ + if (before > 0) + { + memmove(stops, stops + before, (count - before) * sizeof(struct stop)); + count -= before; + } + + /* Remove all stops > 1 except the smallest one */ + if (after >= 0) + count = after + 1; + + /* Expand single stop to 0 .. 1 */ + if (count == 1) + { + stops[1] = stops[0]; + stops[0].offset = 0; + stops[1].offset = 1; + return 2; + } + + /* First stop < 0 -- interpolate value to 0 */ + if (stops[0].offset < 0) + { + float d = -stops[0].offset / (stops[1].offset - stops[0].offset); + stops[0].offset = 0; + stops[0].r = lerp(stops[0].r, stops[1].r, d); + stops[0].g = lerp(stops[0].g, stops[1].g, d); + stops[0].b = lerp(stops[0].b, stops[1].b, d); + stops[0].a = lerp(stops[0].a, stops[1].a, d); + } + + /* Last stop > 1 -- interpolate value to 1 */ + if (stops[count-1].offset > 1) + { + float d = (1 - stops[count-2].offset) / (stops[count-1].offset - stops[count-2].offset); + stops[count-1].offset = 1; + stops[0].r = lerp(stops[count-2].r, stops[count-1].r, d); + stops[0].g = lerp(stops[count-2].g, stops[count-1].g, d); + stops[0].b = lerp(stops[count-2].b, stops[count-1].b, d); + stops[0].a = lerp(stops[count-2].a, stops[count-1].a, d); + } + + /* First stop > 0 -- insert a duplicate at 0 */ + if (stops[0].offset > 0) + { + memmove(stops + 1, stops, count * sizeof(struct stop)); + stops[0] = stops[1]; + stops[0].offset = 0; + count++; + } + + /* Last stop < 1 -- insert a duplicate at 1 */ + if (stops[count-1].offset < 1) + { + stops[count] = stops[count-1]; + stops[count].offset = 1; + count++; + } + + return count; +} + +static void +xps_sample_gradient_stops(fz_shade *shade, struct stop *stops, int count) +{ + float offset, d; + int i, k; + + k = 0; + for (i = 0; i < 256; i++) + { + offset = i / 255.0f; + while (k + 1 < count && offset > stops[k+1].offset) + k++; + + d = (offset - stops[k].offset) / (stops[k+1].offset - stops[k].offset); + + shade->function[i][0] = lerp(stops[k].r, stops[k+1].r, d); + shade->function[i][1] = lerp(stops[k].g, stops[k+1].g, d); + shade->function[i][2] = lerp(stops[k].b, stops[k+1].b, d); + shade->function[i][3] = lerp(stops[k].a, stops[k+1].a, d); + } +} + +/* + * Radial gradients map more or less to Radial shadings. + * The inner circle is always a point. + * The outer circle is actually an ellipse, + * mess with the transform to squash the circle into the right aspect. + */ + +static void +xps_draw_one_radial_gradient(xps_context *ctx, fz_matrix ctm, + struct stop *stops, int count, + int extend, + float x0, float y0, float r0, + float x1, float y1, float r1) +{ + fz_shade *shade; + + /* TODO: this (and the stuff in pdf_shade) should move to res_shade.c */ + shade = fz_malloc(sizeof(fz_shade)); + shade->refs = 1; + shade->cs = fz_devicergb; + shade->bbox = fz_infiniterect; + shade->matrix = fz_identity; + shade->usebackground = 0; + shade->usefunction = 1; + shade->type = FZ_RADIAL; + shade->extend[0] = extend; + shade->extend[1] = extend; + + xps_sample_gradient_stops(shade, stops, count); + + shade->meshlen = 6; + shade->meshcap = 6; + shade->mesh = fz_calloc(shade->meshcap, sizeof(float)); + shade->mesh[0] = x0; + shade->mesh[1] = y0; + shade->mesh[2] = r0; + shade->mesh[3] = x1; + shade->mesh[4] = y1; + shade->mesh[5] = r1; + + ctx->dev->fillshade(ctx->dev->user, shade, ctm, 1); + + fz_dropshade(shade); +} + +/* + * Linear gradients map to Axial shadings. + */ + +static void +xps_draw_one_linear_gradient(xps_context *ctx, fz_matrix ctm, + struct stop *stops, int count, + int extend, + float x0, float y0, float x1, float y1) +{ + fz_shade *shade; + + /* TODO: this (and the stuff in pdf_shade) should move to res_shade.c */ + shade = fz_malloc(sizeof(fz_shade)); + shade->refs = 1; + shade->cs = fz_devicergb; + shade->bbox = fz_infiniterect; + shade->matrix = fz_identity; + shade->usebackground = 0; + shade->usefunction = 1; + shade->type = FZ_LINEAR; + shade->extend[0] = extend; + shade->extend[1] = extend; + + xps_sample_gradient_stops(shade, stops, count); + + shade->meshlen = 6; + shade->meshcap = 6; + shade->mesh = fz_calloc(shade->meshcap, sizeof(float)); + shade->mesh[0] = x0; + shade->mesh[1] = y0; + shade->mesh[2] = 0; + shade->mesh[3] = x1; + shade->mesh[4] = y1; + shade->mesh[5] = 0; + + ctx->dev->fillshade(ctx->dev->user, shade, ctm, 1); + + fz_dropshade(shade); +} + +/* + * We need to loop and create many shading objects to account + * for the Repeat and Reflect SpreadMethods. + * I'm not smart enough to calculate this analytically + * so we iterate and check each object until we + * reach a reasonable limit for infinite cases. + */ + +static inline float point_inside_circle(float px, float py, float x, float y, float r) +{ + float dx = px - x; + float dy = py - y; + return dx * dx + dy * dy <= r * r; +} + +static void +xps_draw_radial_gradient(xps_context *ctx, fz_matrix ctm, + struct stop *stops, int count, + xml_element *root, int spread) +{ + float x0, y0, r0; + float x1, y1, r1; + float xrad = 1; + float yrad = 1; + float invscale; + + char *center_att = xml_att(root, "Center"); + char *origin_att = xml_att(root, "GradientOrigin"); + char *radius_x_att = xml_att(root, "RadiusX"); + char *radius_y_att = xml_att(root, "RadiusY"); + + if (origin_att) + sscanf(origin_att, "%g,%g", &x0, &y0); + if (center_att) + sscanf(center_att, "%g,%g", &x1, &y1); + if (radius_x_att) + xrad = atof(radius_x_att); + if (radius_y_att) + yrad = atof(radius_y_att); + + /* scale the ctm to make ellipses */ + ctm = fz_concat(fz_scale(1.0, yrad / xrad), ctm); + + invscale = xrad / yrad; + y0 = y0 * invscale; + y1 = y1 * invscale; + + r0 = 0.0; + r1 = xrad; + + xps_draw_one_radial_gradient(ctx, ctm, stops, count, 1, x0, y0, r0, x1, y1, r1); +} + +/* + * Calculate how many iterations are needed to cover + * the bounding box. + */ + +static void +xps_draw_linear_gradient(xps_context *ctx, fz_matrix ctm, + struct stop *stops, int count, + xml_element *root, int spread) +{ + float x0, y0, x1, y1; + + char *start_point_att = xml_att(root, "StartPoint"); + char *end_point_att = xml_att(root, "EndPoint"); + + x0 = y0 = 0; + x1 = y1 = 1; + + if (start_point_att) + sscanf(start_point_att, "%g,%g", &x0, &y0); + if (end_point_att) + sscanf(end_point_att, "%g,%g", &x1, &y1); + + xps_draw_one_linear_gradient(ctx, ctm, stops, count, 1, x0, y0, x1, y1); +} + +/* + * Parse XML tag and attributes for a gradient brush, create color/opacity + * function objects and call gradient drawing primitives. + */ + +static void +xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, + char *base_uri, xps_resource *dict, xml_element *root, + void (*draw)(xps_context *, fz_matrix, struct stop *, int, xml_element *, int)) +{ + xml_element *node; + + char *opacity_att; + char *interpolation_att; + char *spread_att; + char *mapping_att; + char *transform_att; + + xml_element *transform_tag = NULL; + xml_element *stop_tag = NULL; + + struct stop stop_list[MAX_STOPS]; + int stop_count; + fz_matrix transform; + int spread_method; + + opacity_att = xml_att(root, "Opacity"); + interpolation_att = xml_att(root, "ColorInterpolationMode"); + spread_att = xml_att(root, "SpreadMethod"); + mapping_att = xml_att(root, "MappingMode"); + transform_att = xml_att(root, "Transform"); + + for (node = xml_down(root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "LinearGradientBrush.Transform")) + transform_tag = xml_down(node); + if (!strcmp(xml_tag(node), "RadialGradientBrush.Transform")) + transform_tag = xml_down(node); + if (!strcmp(xml_tag(node), "LinearGradientBrush.GradientStops")) + stop_tag = xml_down(node); + if (!strcmp(xml_tag(node), "RadialGradientBrush.GradientStops")) + stop_tag = xml_down(node); + } + + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + + spread_method = SPREAD_PAD; + if (spread_att) + { + if (!strcmp(spread_att, "Pad")) + spread_method = SPREAD_PAD; + if (!strcmp(spread_att, "Reflect")) + spread_method = SPREAD_REFLECT; + if (!strcmp(spread_att, "Repeat")) + spread_method = SPREAD_REPEAT; + } + + transform = fz_identity; + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + ctm = fz_concat(transform, ctm); + + if (!stop_tag) { + fz_warn("missing gradient stops tag"); + return; + } + + stop_count = xps_parse_gradient_stops(ctx, base_uri, stop_tag, stop_list, MAX_STOPS); + if (stop_count == 0) + { + fz_warn("no gradient stops found"); + return; + } + + area = fz_transformrect(ctm, area); + + xps_begin_opacity(ctx, ctm, area, base_uri, dict, opacity_att, NULL); + + draw(ctx, ctm, stop_list, stop_count, root, spread_method); + + xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); +} + +void +xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, + char *base_uri, xps_resource *dict, xml_element *root) +{ + xps_parse_gradient_brush(ctx, ctm, area, base_uri, dict, root, xps_draw_linear_gradient); +} + +void +xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, + char *base_uri, xps_resource *dict, xml_element *root) +{ + xps_parse_gradient_brush(ctx, ctm, area, base_uri, dict, root, xps_draw_radial_gradient); +} diff --git a/xps/xps_hash.c b/xps/xps_hash.c new file mode 100644 index 00000000..71e97097 --- /dev/null +++ b/xps/xps_hash.c @@ -0,0 +1,190 @@ +/* Linear probe hash table. + * + * Simple hashtable with open adressing linear probe. + * Does not manage memory of key/value pointers. + * Does not support deleting entries. + */ + +#include "fitz.h" +#include "muxps.h" + +static const unsigned primes[] = +{ + 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, + 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, 0 +}; + +typedef struct xps_hash_entry_s xps_hash_entry; + +struct xps_hash_entry_s +{ + char *key; + void *value; +}; + +struct xps_hash_table_s +{ + void *ctx; + unsigned int size; + unsigned int load; + xps_hash_entry *entries; +}; + +static inline int +xps_tolower(int c) +{ + if (c >= 'A' && c <= 'Z') + return c + 32; + return c; +} + +static unsigned int +xps_hash(char *s) +{ + unsigned int h = 0; + while (*s) + h = xps_tolower(*s++) + (h << 6) + (h << 16) - h; + return h; +} + +xps_hash_table * +xps_hash_new(xps_context *ctx) +{ + xps_hash_table *table; + + table = fz_malloc(sizeof(xps_hash_table)); + table->size = primes[0]; + table->load = 0; + + table->entries = fz_calloc(table->size, sizeof(xps_hash_entry)); + memset(table->entries, 0, table->size * sizeof(xps_hash_entry)); + + return table; +} + +static int +xps_hash_double(xps_context *ctx, xps_hash_table *table) +{ + xps_hash_entry *old_entries; + xps_hash_entry *new_entries; + unsigned int old_size = table->size; + unsigned int new_size = table->size * 2; + int i; + + for (i = 0; primes[i] != 0; i++) + { + if (primes[i] > old_size) + { + new_size = primes[i]; + break; + } + } + + old_entries = table->entries; + new_entries = fz_calloc(new_size, sizeof(xps_hash_entry)); + + table->size = new_size; + table->entries = new_entries; + table->load = 0; + + memset(table->entries, 0, table->size * sizeof(xps_hash_entry)); + + for (i = 0; i < old_size; i++) + if (old_entries[i].value) + xps_hash_insert(ctx, table, old_entries[i].key, old_entries[i].value); + + fz_free(old_entries); + + return 0; +} + +void +xps_hash_free(xps_context *ctx, xps_hash_table *table, + void (*free_key)(xps_context *ctx, void *), + void (*free_value)(xps_context *ctx, void *)) +{ + int i; + + for (i = 0; i < table->size; i++) + { + if (table->entries[i].key && free_key) + free_key(ctx, table->entries[i].key); + if (table->entries[i].value && free_value) + free_value(ctx, table->entries[i].value); + } + + fz_free(table->entries); + fz_free(table); +} + +void * +xps_hash_lookup(xps_hash_table *table, char *key) +{ + xps_hash_entry *entries = table->entries; + unsigned int size = table->size; + unsigned int pos = xps_hash(key) % size; + + while (1) + { + if (!entries[pos].value) + return NULL; + + if (xps_strcasecmp(key, entries[pos].key) == 0) + return entries[pos].value; + + pos = (pos + 1) % size; + } +} + +int +xps_hash_insert(xps_context *ctx, xps_hash_table *table, char *key, void *value) +{ + xps_hash_entry *entries; + unsigned int size, pos; + + /* Grow the table at 80% load */ + if (table->load > table->size * 8 / 10) + { + if (xps_hash_double(ctx, table) < 0) + return fz_rethrow(-1, "cannot grow hash table"); + } + + entries = table->entries; + size = table->size; + pos = xps_hash(key) % size; + + while (1) + { + if (!entries[pos].value) + { + entries[pos].key = key; + entries[pos].value = value; + table->load ++; + return 0; + } + + if (xps_strcasecmp(key, entries[pos].key) == 0) + { + return 0; + } + + pos = (pos + 1) % size; + } +} + +void +xps_hash_debug(xps_hash_table *table) +{ + int i; + + printf("hash table load %d / %d\n", table->load, table->size); + + for (i = 0; i < table->size; i++) + { + if (!table->entries[i].value) + printf("table % 4d: empty\n", i); + else + printf("table % 4d: key=%s value=%p\n", i, + table->entries[i].key, table->entries[i].value); + } +} diff --git a/xps/xps_image.c b/xps/xps_image.c new file mode 100644 index 00000000..ced58bb5 --- /dev/null +++ b/xps/xps_image.c @@ -0,0 +1,137 @@ +#include "fitz.h" +#include "muxps.h" + +static int +xps_decode_image(xps_image **imagep, xps_context *ctx, xps_part *part) +{ + byte *buf = part->data; + int len = part->size; + int error; + + if (len < 8) + return fz_throw("unknown image file format"); + + if (buf[0] == 0xff && buf[1] == 0xd8) + { + error = xps_decode_jpeg(imagep, ctx, buf, len); + if (error) + return fz_rethrow(error, "cannot decode jpeg image"); + } + else if (memcmp(buf, "\211PNG\r\n\032\n", 8) == 0) + { + error = xps_decode_png(imagep, ctx, buf, len); + if (error) + return fz_rethrow(error, "cannot decode png image"); + } + else if (memcmp(buf, "II", 2) == 0 && buf[2] == 0xBC) + { + return fz_throw("JPEG-XR codec is not available"); + } + else if (memcmp(buf, "MM", 2) == 0 || memcmp(buf, "II", 2) == 0) + { + error = xps_decode_tiff(imagep, ctx, buf, len); + if (error) + return fz_rethrow(error, "cannot decode TIFF image"); + } + else + return fz_throw("unknown image file format"); + + return fz_okay; +} + +static void +xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root, void *vimage) +{ + xps_image *image = vimage; + fz_pixmap *pixmap = image->pixmap; + float xs = pixmap->w * 96.0 / image->xres; + float ys = pixmap->h * 96.0 / image->yres; + fz_matrix im = fz_scale(xs, -ys); + im.f = ys; + ctm = fz_concat(im, ctm); + ctx->dev->fillimage(ctx->dev->user, pixmap, ctm, 1.0); +} + +static xps_part * +xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xml_element *root) +{ + char *image_source_att; + char buf[1024]; + char partname[1024]; + char *image_name; + char *profile_name; + char *p; + + image_source_att = xml_att(root, "ImageSource"); + if (!image_source_att) + return NULL; + + /* "{ColorConvertedBitmap /Resources/Image.tiff /Resources/Profile.icc}" */ + if (strstr(image_source_att, "{ColorConvertedBitmap") == image_source_att) + { + image_name = NULL; + profile_name = NULL; + + fz_strlcpy(buf, image_source_att, sizeof buf); + p = strchr(buf, ' '); + if (p) + { + image_name = p + 1; + p = strchr(p + 1, ' '); + if (p) + { + *p = 0; + profile_name = p + 1; + p = strchr(p + 1, '}'); + if (p) + *p = 0; + } + } + } + else + { + image_name = image_source_att; + profile_name = NULL; + } + + if (!image_name) + return NULL; + + xps_absolute_path(partname, base_uri, image_name, sizeof partname); + + return xps_read_part(ctx, partname); +} + +void +xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, + char *base_uri, xps_resource *dict, xml_element *root) +{ + xps_part *part; + xps_image *image; + int code; + + part = xps_find_image_brush_source_part(ctx, base_uri, root); + if (!part) { + fz_warn("cannot find image source"); + return; + } + + code = xps_decode_image(&image, ctx, part); + if (code < 0) { + fz_catch(-1, "cannot decode image resource"); + return; + } + + xps_parse_tiling_brush(ctx, ctm, area, base_uri, dict, root, xps_paint_image_brush, image); + + xps_free_image(ctx, image); + xps_free_part(ctx, part); +} + +void +xps_free_image(xps_context *ctx, xps_image *image) +{ + if (image->pixmap) + fz_droppixmap(image->pixmap); + fz_free(image); +} diff --git a/xps/xps_jpeg.c b/xps/xps_jpeg.c new file mode 100644 index 00000000..4dc2a7e4 --- /dev/null +++ b/xps/xps_jpeg.c @@ -0,0 +1,134 @@ +#include "fitz.h" +#include "muxps.h" + +#include +#include + +struct jpeg_error_mgr_jmp +{ + struct jpeg_error_mgr super; + jmp_buf env; + char msg[JMSG_LENGTH_MAX]; +}; + +static void error_exit(j_common_ptr cinfo) +{ + struct jpeg_error_mgr_jmp *err = (struct jpeg_error_mgr_jmp *)cinfo->err; + cinfo->err->format_message(cinfo, err->msg); + longjmp(err->env, 1); +} + +static void init_source(j_decompress_ptr cinfo) +{ + /* nothing to do */ +} + +static void term_source(j_decompress_ptr cinfo) +{ + /* nothing to do */ +} + +static boolean fill_input_buffer(j_decompress_ptr cinfo) +{ + static unsigned char eoi[2] = { 0xFF, JPEG_EOI }; + struct jpeg_source_mgr *src = cinfo->src; + src->next_input_byte = eoi; + src->bytes_in_buffer = 2; + return 1; +} + +static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + struct jpeg_source_mgr *src = cinfo->src; + if (num_bytes > 0) + { + src->next_input_byte += num_bytes; + src->bytes_in_buffer -= num_bytes; + } +} + +int +xps_decode_jpeg(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr_jmp err; + struct jpeg_source_mgr src; + unsigned char *row[1], *sp, *dp; + fz_colorspace *colorspace; + int x, k; + + xps_image *image = NULL; + + if (setjmp(err.env)) + { + if (image) + xps_free_image(ctx, image); + return fz_throw("jpeg error: %s", err.msg); + } + + cinfo.err = jpeg_std_error(&err.super); + err.super.error_exit = error_exit; + + jpeg_create_decompress(&cinfo); + + cinfo.src = &src; + src.init_source = init_source; + src.fill_input_buffer = fill_input_buffer; + src.skip_input_data = skip_input_data; + src.resync_to_restart = jpeg_resync_to_restart; + src.term_source = term_source; + src.next_input_byte = rbuf; + src.bytes_in_buffer = rlen; + + jpeg_read_header(&cinfo, 1); + + jpeg_start_decompress(&cinfo); + + if (cinfo.output_components == 1) + colorspace = fz_devicegray; + else if (cinfo.output_components == 3) + colorspace = fz_devicergb; + else if (cinfo.output_components == 4) + colorspace = fz_devicecmyk; + else + return fz_throw("bad number of components in jpeg: %d", cinfo.output_components); + + image = fz_malloc(sizeof(xps_image)); + image->pixmap = fz_newpixmap(colorspace, 0, 0, cinfo.output_width, cinfo.output_height); + image->xres = 96; + image->yres = 96; + + if (cinfo.density_unit == 1) + { + image->xres = cinfo.X_density; + image->yres = cinfo.Y_density; + } + else if (cinfo.density_unit == 2) + { + image->xres = cinfo.X_density * 2.54; + image->yres = cinfo.Y_density * 2.54; + } + + fz_clearpixmap(image->pixmap); + + row[0] = fz_malloc(cinfo.output_components * cinfo.output_width); + dp = image->pixmap->samples; + while (cinfo.output_scanline < cinfo.output_height) + { + jpeg_read_scanlines(&cinfo, row, 1); + sp = row[0]; + for (x = 0; x < cinfo.output_width; x++) + { + for (k = 0; k < cinfo.output_components; k++) + *dp++ = *sp++; + *dp++ = 255; + } + } + fz_free(row[0]); + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + + *imagep = image; + return fz_okay; +} diff --git a/xps/xps_page.c b/xps/xps_page.c new file mode 100644 index 00000000..7659a1a7 --- /dev/null +++ b/xps/xps_page.c @@ -0,0 +1,154 @@ +#include "fitz.h" +#include "muxps.h" + +void +xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root) +{ + xps_resource *new_dict = NULL; + xml_element *node; + char *opacity_mask_uri; + int code; + + char *transform_att; + char *clip_att; + char *opacity_att; + char *opacity_mask_att; + + xml_element *transform_tag = NULL; + xml_element *clip_tag = NULL; + xml_element *opacity_mask_tag = NULL; + + fz_matrix transform; + + transform_att = xml_att(root, "RenderTransform"); + clip_att = xml_att(root, "Clip"); + opacity_att = xml_att(root, "Opacity"); + opacity_mask_att = xml_att(root, "OpacityMask"); + + for (node = xml_down(root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "Canvas.Resources") && xml_down(node)) + { + code = xps_parse_resource_dictionary(ctx, &new_dict, base_uri, xml_down(node)); + if (code) + fz_catch(code, "cannot load Canvas.Resources"); + else + { + new_dict->parent = dict; + dict = new_dict; + } + } + + if (!strcmp(xml_tag(node), "Canvas.RenderTransform")) + transform_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Canvas.Clip")) + clip_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Canvas.OpacityMask")) + opacity_mask_tag = xml_down(node); + } + + opacity_mask_uri = base_uri; + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); + + transform = fz_identity; + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + ctm = fz_concat(transform, ctm); + + if (clip_att || clip_tag) + xps_clip(ctx, ctm, dict, clip_att, clip_tag); + + xps_begin_opacity(ctx, ctm, fz_infiniterect, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + + for (node = xml_down(root); node; node = xml_next(node)) + { + xps_parse_element(ctx, ctm, base_uri, dict, node); + } + + xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + + if (clip_att || clip_tag) + ctx->dev->popclip(ctx->dev->user); + + if (new_dict) + xps_free_resource_dictionary(ctx, new_dict); +} + +void +xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page) +{ + xml_element *node; + xps_resource *dict; + char base_uri[1024]; + char *s; + int code; + + fz_strlcpy(base_uri, page->name, sizeof base_uri); + s = strrchr(base_uri, '/'); + if (s) + s[1] = 0; + + dict = NULL; + + ctx->opacity_top = 0; + ctx->opacity[0] = 1; + + if (!page->root) + return; + + for (node = xml_down(page->root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "FixedPage.Resources") && xml_down(node)) + { + code = xps_parse_resource_dictionary(ctx, &dict, base_uri, xml_down(node)); + if (code) + fz_catch(code, "cannot load FixedPage.Resources"); + } + xps_parse_element(ctx, ctm, base_uri, dict, node); + } + + if (dict) + { + xps_free_resource_dictionary(ctx, dict); + } +} + +int +xps_load_fixed_page(xps_context *ctx, xps_page *page) +{ + xps_part *part; + xml_element *root; + char *width_att; + char *height_att; + + part = xps_read_part(ctx, page->name); + if (!part) + return fz_rethrow(-1, "cannot read zip part '%s'", page->name); + + root = xml_parse_document(part->data, part->size); + if (!root) + return fz_rethrow(-1, "cannot parse xml part '%s'", page->name); + + xps_free_part(ctx, part); + + if (strcmp(xml_tag(root), "FixedPage")) + return fz_throw("expected FixedPage element (found %s)", xml_tag(root)); + + width_att = xml_att(root, "Width"); + if (!width_att) + return fz_throw("FixedPage missing required attribute: Width"); + + height_att = xml_att(root, "Height"); + if (!height_att) + return fz_throw("FixedPage missing required attribute: Height"); + + page->width = atoi(width_att); + page->height = atoi(height_att); + page->root = root; + + return 0; +} diff --git a/xps/xps_path.c b/xps/xps_path.c new file mode 100644 index 00000000..00e57bee --- /dev/null +++ b/xps/xps_path.c @@ -0,0 +1,990 @@ +#include "fitz.h" +#include "muxps.h" + +static fz_point +fz_currentpoint(fz_path *path) +{ + fz_point c, m; + int i; + + c.x = c.y = m.x = m.y = 0; + i = 0; + + while (i < path->len) + { + switch (path->els[i++].k) + { + case FZ_MOVETO: + m.x = c.x = path->els[i++].v; + m.y = c.y = path->els[i++].v; + break; + case FZ_LINETO: + c.x = path->els[i++].v; + c.y = path->els[i++].v; + break; + case FZ_CURVETO: + i += 4; + c.x = path->els[i++].v; + c.y = path->els[i++].v; + break; + case FZ_CLOSEPATH: + c = m; + } + } + + return c; +} + +void +xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xml_element *clip_tag) +{ + fz_path *path; + int fill_rule = 0; + + if (clip_att) + path = xps_parse_abbreviated_geometry(ctx, clip_att, &fill_rule); + else if (clip_tag) + path = xps_parse_path_geometry(ctx, dict, clip_tag, 0, &fill_rule); + else + path = fz_newpath(); + ctx->dev->clippath(ctx->dev->user, path, fill_rule, ctm); + fz_freepath(path); +} + +/* Draw an arc segment transformed by the matrix, we approximate with straight + * line segments. We cannot use the fz_arc function because they only draw + * circular arcs, we need to transform the line to make them elliptical but + * without transforming the line width. + */ +static inline void +xps_draw_arc_segment(fz_path *path, fz_matrix mtx, float th0, float th1, int iscw) +{ + float t, d; + fz_point p; + + while (th1 < th0) + th1 += M_PI * 2.0; + + d = 1 * (M_PI / 180.0); /* 1-degree precision */ + + if (iscw) + { + p.x = cos(th0); + p.y = sin(th0); + p = fz_transformpoint(mtx, p); + fz_lineto(path, p.x, p.y); + for (t = th0; t < th1; t += d) + { + p.x = cos(t); + p.y = sin(t); + p = fz_transformpoint(mtx, p); + fz_lineto(path, p.x, p.y); + } + p.x = cos(th1); + p.y = sin(th1); + p = fz_transformpoint(mtx, p); + fz_lineto(path, p.x, p.y); + } + else + { + th0 += M_PI * 2; + p.x = cos(th0); + p.y = sin(th0); + p = fz_transformpoint(mtx, p); + fz_lineto(path, p.x, p.y); + for (t = th0; t > th1; t -= d) + { + p.x = cos(t); + p.y = sin(t); + p = fz_transformpoint(mtx, p); + fz_lineto(path, p.x, p.y); + } + p.x = cos(th1); + p.y = sin(th1); + p = fz_transformpoint(mtx, p); + fz_lineto(path, p.x, p.y); + } +} + +/* Given two vectors find the angle between them. */ +static inline double +angle_between(const fz_point u, const fz_point v) +{ + double det = u.x * v.y - u.y * v.x; + double sign = (det < 0 ? -1.0 : 1.0); + double magu = u.x * u.x + u.y * u.y; + double magv = v.x * v.x + v.y * v.y; + double udotv = u.x * v.x + u.y * v.y; + double t = udotv / (magu * magv); + /* guard against rounding errors when near |1| (where acos will return NaN) */ + if (t < -1.0) t = -1.0; + if (t > 1.0) t = 1.0; + return sign * acos(t); +} + +static void +xps_draw_arc(fz_path *path, + float size_x, float size_y, float rotation_angle, + int is_large_arc, int is_clockwise, + float point_x, float point_y) +{ + fz_matrix rotmat, revmat; + fz_matrix mtx; + fz_point pt; + double rx, ry; + double x1, y1, x2, y2; + double x1t, y1t; + double cxt, cyt, cx, cy; + double t1, t2, t3; + double sign; + double th1, dth; + + pt = fz_currentpoint(path); + x1 = pt.x; + y1 = pt.y; + x2 = point_x; + y2 = point_y; + rx = size_x; + ry = size_y; + + if (is_clockwise != is_large_arc) + sign = 1; + else + sign = -1; + + rotmat = fz_rotate(rotation_angle); + revmat = fz_rotate(-rotation_angle); + + /* http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes */ + /* Conversion from endpoint to center parameterization */ + + /* F.6.6.1 -- ensure radii are positive and non-zero */ + rx = fabsf(rx); + ry = fabsf(ry); + if (rx < 0.001 || ry < 0.001) + { + fz_lineto(path, x2, y2); + return; + } + + /* F.6.5.1 */ + pt.x = (x1 - x2) / 2; + pt.y = (y1 - y2) / 2; + pt = fz_transformvector(revmat, pt); + x1t = pt.x; + y1t = pt.y; + + /* F.6.6.2 -- ensure radii are large enough */ + t1 = (x1t * x1t) / (rx * rx) + (y1t * y1t) / (ry * ry); + if (t1 > 1.0) + { + rx = rx * sqrtf(t1); + ry = ry * sqrtf(t1); + } + + /* F.6.5.2 */ + t1 = (rx * rx * ry * ry) - (rx * rx * y1t * y1t) - (ry * ry * x1t * x1t); + t2 = (rx * rx * y1t * y1t) + (ry * ry * x1t * x1t); + t3 = t1 / t2; + /* guard against rounding errors; sqrt of negative numbers is bad for your health */ + if (t3 < 0.0) t3 = 0.0; + t3 = sqrtf(t3); + + cxt = sign * t3 * (rx * y1t) / ry; + cyt = sign * t3 * -(ry * x1t) / rx; + + /* F.6.5.3 */ + pt.x = cxt; + pt.y = cyt; + pt = fz_transformvector(rotmat, pt); + cx = pt.x + (x1 + x2) / 2; + cy = pt.y + (y1 + y2) / 2; + + /* F.6.5.4 */ + { + fz_point coord1, coord2, coord3, coord4; + coord1.x = 1; + coord1.y = 0; + coord2.x = (x1t - cxt) / rx; + coord2.y = (y1t - cyt) / ry; + coord3.x = (x1t - cxt) / rx; + coord3.y = (y1t - cyt) / ry; + coord4.x = (-x1t - cxt) / rx; + coord4.y = (-y1t - cyt) / ry; + th1 = angle_between(coord1, coord2); + dth = angle_between(coord3, coord4); + if (dth < 0 && !is_clockwise) + dth += ((M_PI / 180.0) * 360); + if (dth > 0 && is_clockwise) + dth -= ((M_PI / 180.0) * 360); + } + + mtx = fz_identity; + mtx = fz_concat(fz_translate(cx, cy), mtx); + mtx = fz_concat(fz_rotate(rotation_angle), mtx); + mtx = fz_concat(fz_scale(rx, ry), mtx); + xps_draw_arc_segment(path, mtx, th1, th1 + dth, is_clockwise); + + fz_lineto(path, point_x, point_y); +} + +/* + * Parse an abbreviated geometry string, and call + * ghostscript moveto/lineto/curveto functions to + * build up a path. + */ + +fz_path * +xps_parse_abbreviated_geometry(xps_context *ctx, char *geom, int *fill_rule) +{ + fz_path *path; + char **args; + char **pargs; + char *s = geom; + fz_point pt; + int i, n; + int cmd, old; + float x1, y1, x2, y2, x3, y3; + float smooth_x, smooth_y; /* saved cubic bezier control point for smooth curves */ + int reset_smooth; + + path = fz_newpath(); + + args = fz_calloc(strlen(geom) + 1, sizeof(char*)); + pargs = args; + + while (*s) + { + if ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')) + { + *pargs++ = s++; + } + else if ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E') + { + *pargs++ = s; + while ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E') + s ++; + } + else + { + s++; + } + } + + pargs[0] = s; + pargs[1] = 0; + + n = pargs - args; + i = 0; + + old = 0; + + reset_smooth = 1; + smooth_x = 0.0; + smooth_y = 0.0; + + while (i < n) + { + cmd = args[i][0]; + if (cmd == '+' || cmd == '.' || cmd == '-' || (cmd >= '0' && cmd <= '9')) + cmd = old; /* it's a number, repeat old command */ + else + i ++; + + if (reset_smooth) + { + smooth_x = 0.0; + smooth_y = 0.0; + } + + reset_smooth = 1; + + switch (cmd) + { + case 'F': + *fill_rule = atoi(args[i]); + i ++; + break; + + case 'M': + fz_moveto(path, atof(args[i]), atof(args[i+1])); + i += 2; + break; + case 'm': + pt = fz_currentpoint(path); + fz_moveto(path, pt.x + atof(args[i]), pt.y + atof(args[i+1])); + i += 2; + break; + + case 'L': + fz_lineto(path, atof(args[i]), atof(args[i+1])); + i += 2; + break; + case 'l': + pt = fz_currentpoint(path); + fz_lineto(path, pt.x + atof(args[i]), pt.y + atof(args[i+1])); + i += 2; + break; + + case 'H': + pt = fz_currentpoint(path); + fz_lineto(path, atof(args[i]), pt.y); + i += 1; + break; + case 'h': + pt = fz_currentpoint(path); + fz_lineto(path, pt.x + atof(args[i]), pt.y); + i += 1; + break; + + case 'V': + pt = fz_currentpoint(path); + fz_lineto(path, pt.x, atof(args[i])); + i += 1; + break; + case 'v': + pt = fz_currentpoint(path); + fz_lineto(path, pt.x, pt.y + atof(args[i])); + i += 1; + break; + + case 'C': + x1 = atof(args[i+0]); + y1 = atof(args[i+1]); + x2 = atof(args[i+2]); + y2 = atof(args[i+3]); + x3 = atof(args[i+4]); + y3 = atof(args[i+5]); + fz_curveto(path, x1, y1, x2, y2, x3, y3); + i += 6; + reset_smooth = 0; + smooth_x = x3 - x2; + smooth_y = y3 - y2; + break; + + case 'c': + pt = fz_currentpoint(path); + x1 = atof(args[i+0]) + pt.x; + y1 = atof(args[i+1]) + pt.y; + x2 = atof(args[i+2]) + pt.x; + y2 = atof(args[i+3]) + pt.y; + x3 = atof(args[i+4]) + pt.x; + y3 = atof(args[i+5]) + pt.y; + fz_curveto(path, x1, y1, x2, y2, x3, y3); + i += 6; + reset_smooth = 0; + smooth_x = x3 - x2; + smooth_y = y3 - y2; + break; + + case 'S': + pt = fz_currentpoint(path); + x1 = atof(args[i+0]); + y1 = atof(args[i+1]); + x2 = atof(args[i+2]); + y2 = atof(args[i+3]); + fz_curveto(path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); + i += 4; + reset_smooth = 0; + smooth_x = x2 - x1; + smooth_y = y2 - y1; + break; + + case 's': + pt = fz_currentpoint(path); + x1 = atof(args[i+0]) + pt.x; + y1 = atof(args[i+1]) + pt.y; + x2 = atof(args[i+2]) + pt.x; + y2 = atof(args[i+3]) + pt.y; + fz_curveto(path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); + i += 4; + reset_smooth = 0; + smooth_x = x2 - x1; + smooth_y = y2 - y1; + break; + + case 'Q': + pt = fz_currentpoint(path); + x1 = atof(args[i+0]); + y1 = atof(args[i+1]); + x2 = atof(args[i+2]); + y2 = atof(args[i+3]); + fz_curveto(path, + (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, + (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, + x2, y2); + i += 4; + break; + case 'q': + pt = fz_currentpoint(path); + x1 = atof(args[i+0]) + pt.x; + y1 = atof(args[i+1]) + pt.y; + x2 = atof(args[i+2]) + pt.x; + y2 = atof(args[i+3]) + pt.y; + fz_curveto(path, + (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, + (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, + x2, y2); + i += 4; + break; + + case 'A': + xps_draw_arc(path, + atof(args[i+0]), atof(args[i+1]), atof(args[i+2]), + atoi(args[i+3]), atoi(args[i+4]), + atof(args[i+5]), atof(args[i+6])); + i += 7; + break; + case 'a': + pt = fz_currentpoint(path); + xps_draw_arc(path, + atof(args[i+0]), atof(args[i+1]), atof(args[i+2]), + atoi(args[i+3]), atoi(args[i+4]), + atof(args[i+5]) + pt.x, atof(args[i+6]) + pt.y); + i += 7; + break; + + case 'Z': + case 'z': + fz_closepath(path); + break; + + default: + /* eek */ + break; + } + + old = cmd; + } + + fz_free(args); + return path; +} + +static void +xps_parse_arc_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke) +{ + /* ArcSegment pretty much follows the SVG algorithm for converting an + * arc in endpoint representation to an arc in centerpoint + * representation. Once in centerpoint it can be given to the + * graphics library in the form of a postscript arc. */ + + float rotation_angle; + int is_large_arc, is_clockwise; + float point_x, point_y; + float size_x, size_y; + int is_stroked; + + char *point_att = xml_att(root, "Point"); + char *size_att = xml_att(root, "Size"); + char *rotation_angle_att = xml_att(root, "RotationAngle"); + char *is_large_arc_att = xml_att(root, "IsLargeArc"); + char *sweep_direction_att = xml_att(root, "SweepDirection"); + char *is_stroked_att = xml_att(root, "IsStroked"); + + if (!point_att || !size_att || !rotation_angle_att || !is_large_arc_att || !sweep_direction_att) + { + fz_warn("ArcSegment element is missing attributes"); + return; + } + + is_stroked = 1; + if (is_stroked_att && !strcmp(is_stroked_att, "false")) + is_stroked = 0; + if (!is_stroked) + *skipped_stroke = 1; + + sscanf(point_att, "%g,%g", &point_x, &point_y); + sscanf(size_att, "%g,%g", &size_x, &size_y); + rotation_angle = atof(rotation_angle_att); + is_large_arc = !strcmp(is_large_arc_att, "true"); + is_clockwise = !strcmp(sweep_direction_att, "Clockwise"); + + if (stroking && !is_stroked) + { + fz_moveto(path, point_x, point_y); + return; + } + + xps_draw_arc(path, size_x, size_y, rotation_angle, is_large_arc, is_clockwise, point_x, point_y); +} + +static void +xps_parse_poly_quadratic_bezier_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke) +{ + char *points_att = xml_att(root, "Points"); + char *is_stroked_att = xml_att(root, "IsStroked"); + float x[2], y[2]; + int is_stroked; + fz_point pt; + char *s; + int n; + + if (!points_att) + { + fz_warn("PolyQuadraticBezierSegment element has no points"); + return; + } + + is_stroked = 1; + if (is_stroked_att && !strcmp(is_stroked_att, "false")) + is_stroked = 0; + if (!is_stroked) + *skipped_stroke = 1; + + s = points_att; + n = 0; + while (*s != 0) + { + while (*s == ' ') s++; + sscanf(s, "%g,%g", &x[n], &y[n]); + while (*s != ' ' && *s != 0) s++; + n ++; + if (n == 2) + { + if (stroking && !is_stroked) + { + fz_moveto(path, x[1], y[1]); + } + else + { + pt = fz_currentpoint(path); + fz_curveto(path, + (pt.x + 2 * x[0]) / 3, (pt.y + 2 * y[0]) / 3, + (x[1] + 2 * x[0]) / 3, (y[1] + 2 * y[0]) / 3, + x[1], y[1]); + } + n = 0; + } + } +} + +static void +xps_parse_poly_bezier_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke) +{ + char *points_att = xml_att(root, "Points"); + char *is_stroked_att = xml_att(root, "IsStroked"); + float x[3], y[3]; + int is_stroked; + char *s; + int n; + + if (!points_att) + { + fz_warn("PolyBezierSegment element has no points"); + return; + } + + is_stroked = 1; + if (is_stroked_att && !strcmp(is_stroked_att, "false")) + is_stroked = 0; + if (!is_stroked) + *skipped_stroke = 1; + + s = points_att; + n = 0; + while (*s != 0) + { + while (*s == ' ') s++; + sscanf(s, "%g,%g", &x[n], &y[n]); + while (*s != ' ' && *s != 0) s++; + n ++; + if (n == 3) + { + if (stroking && !is_stroked) + fz_moveto(path, x[2], y[2]); + else + fz_curveto(path, x[0], y[0], x[1], y[1], x[2], y[2]); + n = 0; + } + } +} + +static void +xps_parse_poly_line_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke) +{ + char *points_att = xml_att(root, "Points"); + char *is_stroked_att = xml_att(root, "IsStroked"); + int is_stroked; + float x, y; + char *s; + + if (!points_att) + { + fz_warn("PolyLineSegment element has no points"); + return; + } + + is_stroked = 1; + if (is_stroked_att && !strcmp(is_stroked_att, "false")) + is_stroked = 0; + if (!is_stroked) + *skipped_stroke = 1; + + s = points_att; + while (*s != 0) + { + while (*s == ' ') s++; + sscanf(s, "%g,%g", &x, &y); + if (stroking && !is_stroked) + fz_moveto(path, x, y); + else + fz_lineto(path, x, y); + while (*s != ' ' && *s != 0) s++; + } +} + +static void +xps_parse_path_figure(fz_path *path, xml_element *root, int stroking) +{ + xml_element *node; + + char *is_closed_att; + char *start_point_att; + char *is_filled_att; + + int is_closed = 0; + int is_filled = 1; + float start_x = 0.0; + float start_y = 0.0; + + int skipped_stroke = 0; + + is_closed_att = xml_att(root, "IsClosed"); + start_point_att = xml_att(root, "StartPoint"); + is_filled_att = xml_att(root, "IsFilled"); + + if (is_closed_att) + is_closed = !strcmp(is_closed_att, "true"); + if (is_filled_att) + is_filled = !strcmp(is_filled_att, "true"); + if (start_point_att) + sscanf(start_point_att, "%g,%g", &start_x, &start_y); + + if (!stroking && !is_filled) /* not filled, when filling */ + return; + + fz_moveto(path, start_x, start_y); + + for (node = xml_down(root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "ArcSegment")) + xps_parse_arc_segment(path, node, stroking, &skipped_stroke); + if (!strcmp(xml_tag(node), "PolyBezierSegment")) + xps_parse_poly_bezier_segment(path, node, stroking, &skipped_stroke); + if (!strcmp(xml_tag(node), "PolyLineSegment")) + xps_parse_poly_line_segment(path, node, stroking, &skipped_stroke); + if (!strcmp(xml_tag(node), "PolyQuadraticBezierSegment")) + xps_parse_poly_quadratic_bezier_segment(path, node, stroking, &skipped_stroke); + } + + if (is_closed) + { + if (stroking && skipped_stroke) + fz_lineto(path, start_x, start_y); /* we've skipped using fz_moveto... */ + else + fz_closepath(path); /* no skipped segments, safe to closepath properly */ + } +} + +fz_path * +xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xml_element *root, int stroking, int *fill_rule) +{ + xml_element *node; + + char *figures_att; + char *fill_rule_att; + char *transform_att; + + xml_element *transform_tag = NULL; + xml_element *figures_tag = NULL; /* only used by resource */ + + fz_matrix transform; + fz_path *path; + + figures_att = xml_att(root, "Figures"); + fill_rule_att = xml_att(root, "FillRule"); + transform_att = xml_att(root, "Transform"); + + for (node = xml_down(root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "PathGeometry.Transform")) + transform_tag = xml_down(node); + } + + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &figures_att, &figures_tag, NULL); + + if (fill_rule_att) + { + if (!strcmp(fill_rule_att, "NonZero")) + *fill_rule = 1; + if (!strcmp(fill_rule_att, "EvenOdd")) + *fill_rule = 0; + } + + transform = fz_identity; + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + + if (figures_att) + path = xps_parse_abbreviated_geometry(ctx, figures_att, fill_rule); + else + path = fz_newpath(); + + if (figures_tag) + xps_parse_path_figure(path, figures_tag, stroking); + + for (node = xml_down(root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "PathFigure")) + xps_parse_path_figure(path, node, stroking); + } + + if (transform_att || transform_tag) + fz_transformpath(path, transform); + + return path; +} + +static int +xps_parse_line_cap(char *attr) +{ + if (attr) + { + if (!strcmp(attr, "Flat")) return 0; + if (!strcmp(attr, "Round")) return 1; + if (!strcmp(attr, "Square")) return 2; + if (!strcmp(attr, "Triangle")) return 3; /* FIXME add triangle caps */ + } + return 0; +} + +/* + * Parse an XPS element, and call relevant ghostscript + * functions for drawing and/or clipping the child elements. + */ + +void +xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root) +{ + xml_element *node; + + char *fill_uri; + char *stroke_uri; + char *opacity_mask_uri; + + char *transform_att; + char *clip_att; + char *data_att; + char *fill_att; + char *stroke_att; + char *opacity_att; + char *opacity_mask_att; + + xml_element *transform_tag = NULL; + xml_element *clip_tag = NULL; + xml_element *data_tag = NULL; + xml_element *fill_tag = NULL; + xml_element *stroke_tag = NULL; + xml_element *opacity_mask_tag = NULL; + + char *fill_opacity_att = NULL; + char *stroke_opacity_att = NULL; + + char *stroke_dash_array_att; + char *stroke_dash_cap_att; + char *stroke_dash_offset_att; + char *stroke_end_line_cap_att; + char *stroke_start_line_cap_att; + char *stroke_line_join_att; + char *stroke_miter_limit_att; + char *stroke_thickness_att; + + fz_strokestate stroke; + fz_matrix transform; + float samples[32]; + fz_colorspace *colorspace; + fz_path *path; + fz_rect area; + int fill_rule; + + /* + * Extract attributes and extended attributes. + */ + + transform_att = xml_att(root, "RenderTransform"); + clip_att = xml_att(root, "Clip"); + data_att = xml_att(root, "Data"); + fill_att = xml_att(root, "Fill"); + stroke_att = xml_att(root, "Stroke"); + opacity_att = xml_att(root, "Opacity"); + opacity_mask_att = xml_att(root, "OpacityMask"); + + stroke_dash_array_att = xml_att(root, "StrokeDashArray"); + stroke_dash_cap_att = xml_att(root, "StrokeDashCap"); + stroke_dash_offset_att = xml_att(root, "StrokeDashOffset"); + stroke_end_line_cap_att = xml_att(root, "StrokeEndLineCap"); + stroke_start_line_cap_att = xml_att(root, "StrokeStartLineCap"); + stroke_line_join_att = xml_att(root, "StrokeLineJoin"); + stroke_miter_limit_att = xml_att(root, "StrokeMiterLimit"); + stroke_thickness_att = xml_att(root, "StrokeThickness"); + + for (node = xml_down(root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "Path.RenderTransform")) + transform_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Path.OpacityMask")) + opacity_mask_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Path.Clip")) + clip_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Path.Fill")) + fill_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Path.Stroke")) + stroke_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Path.Data")) + data_tag = xml_down(node); + } + + fill_uri = base_uri; + stroke_uri = base_uri; + opacity_mask_uri = base_uri; + + xps_resolve_resource_reference(ctx, dict, &data_att, &data_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &fill_att, &fill_tag, &fill_uri); + xps_resolve_resource_reference(ctx, dict, &stroke_att, &stroke_tag, &stroke_uri); + xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); + + /* + * Act on the information we have gathered: + */ + + if (!data_att && !data_tag) + return; + + if (fill_tag && !strcmp(xml_tag(fill_tag), "SolidColorBrush")) + { + fill_opacity_att = xml_att(fill_tag, "Opacity"); + fill_att = xml_att(fill_tag, "Color"); + fill_tag = NULL; + } + + if (stroke_tag && !strcmp(xml_tag(stroke_tag), "SolidColorBrush")) + { + stroke_opacity_att = xml_att(stroke_tag, "Opacity"); + stroke_att = xml_att(stroke_tag, "Color"); + stroke_tag = NULL; + } + + stroke.linecap = xps_parse_line_cap(stroke_start_line_cap_att); +// fz_setlineendcap(ctx->pgs, xps_parse_line_cap(stroke_end_line_cap_att)); +// fz_setlinedashcap(ctx->pgs, xps_parse_line_cap(stroke_dash_cap_att)); + + stroke.linejoin = 0; + if (stroke_line_join_att) + { + if (!strcmp(stroke_line_join_att, "Miter")) stroke.linejoin = 0; + if (!strcmp(stroke_line_join_att, "Round")) stroke.linejoin = 1; + if (!strcmp(stroke_line_join_att, "Bevel")) stroke.linejoin = 2; + } + + stroke.miterlimit = 10.0; + if (stroke_miter_limit_att) + stroke.miterlimit = atof(stroke_miter_limit_att); + + stroke.linewidth = 1.0; + if (stroke_thickness_att) + stroke.linewidth = atof(stroke_thickness_att); + + stroke.dashphase = 0; + stroke.dashlen = 0; + if (stroke_dash_array_att) + { + char *s = stroke_dash_array_att; + + if (stroke_dash_offset_att) + stroke.dashphase = atof(stroke_dash_offset_att) * stroke.linewidth; + + while (*s && stroke.dashlen < nelem(stroke.dashlist)) + { + while (*s == ' ') + s++; + stroke.dashlist[stroke.dashlen++] = atof(s) * stroke.linewidth; + while (*s && *s != ' ') + s++; + } + } + + transform = fz_identity; + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + ctm = fz_concat(transform, ctm); + + if (clip_att || clip_tag) + xps_clip(ctx, ctm, dict, clip_att, clip_tag); + + fill_rule = 0; + if (data_att) + path = xps_parse_abbreviated_geometry(ctx, data_att, &fill_rule); + else if (data_tag) + path = xps_parse_path_geometry(ctx, dict, data_tag, 0, &fill_rule); + + if (stroke_att || stroke_tag) + area = fz_boundpath(path, &stroke, ctm); + else + area = fz_boundpath(path, NULL, ctm); + + xps_begin_opacity(ctx, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + + if (fill_att) + { + xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples); + if (fill_opacity_att) + samples[0] = atof(fill_opacity_att); + xps_set_color(ctx, colorspace, samples); + + ctx->dev->fillpath(ctx->dev->user, path, fill_rule == 0, ctm, + ctx->colorspace, ctx->color, ctx->alpha); + } + + if (fill_tag) + { + area = fz_boundpath(path, NULL, ctm); + + ctx->dev->clippath(ctx->dev->user, path, fill_rule == 0, ctm); + xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag); + ctx->dev->popclip(ctx->dev->user); + } + + if (stroke_att) + { + xps_parse_color(ctx, base_uri, stroke_att, &colorspace, samples); + if (stroke_opacity_att) + samples[0] = atof(stroke_opacity_att); + xps_set_color(ctx, colorspace, samples); + + ctx->dev->strokepath(ctx->dev->user, path, &stroke, ctm, + ctx->colorspace, ctx->color, ctx->alpha); + } + + if (stroke_tag) + { + ctx->dev->clipstrokepath(ctx->dev->user, path, &stroke, ctm); + xps_parse_brush(ctx, ctm, area, stroke_uri, dict, stroke_tag); + ctx->dev->popclip(ctx->dev->user); + } + + xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + + fz_freepath(path); + path = NULL; + + if (clip_att || clip_tag) + ctx->dev->popclip(ctx->dev->user); +} diff --git a/xps/xps_png.c b/xps/xps_png.c new file mode 100644 index 00000000..f59be56d --- /dev/null +++ b/xps/xps_png.c @@ -0,0 +1,580 @@ +#include "fitz.h" +#include "muxps.h" + +#include + +struct info +{ + int width, height, depth, n; + int interlace, indexed; + int size; + unsigned char *samples; + unsigned char palette[256*4]; + int transparency; + int trns[3]; + int xres, yres; +}; + +static inline int getint(unsigned char *p) +{ + return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +} + +static inline int +getcomp(unsigned char *line, int x, int bpc) +{ + switch (bpc) + { + case 1: return (line[x >> 3] >> ( 7 - (x & 7) ) ) & 1; + case 2: return (line[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3; + case 4: return (line[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15; + case 8: return line[x]; + case 16: return line[x << 1] << 8 | line[(x << 1) + 1]; + } + return 0; +} + +static inline void +putcomp(unsigned char *line, int x, int bpc, int value) +{ + int maxval = (1 << bpc) - 1; + + switch (bpc) + { + case 1: line[x >> 3] &= ~(maxval << (7 - (x & 7))); break; + case 2: line[x >> 2] &= ~(maxval << ((3 - (x & 3)) << 1)); break; + case 4: line[x >> 1] &= ~(maxval << ((1 - (x & 1)) << 2)); break; + } + + switch (bpc) + { + case 1: line[x >> 3] |= value << (7 - (x & 7)); break; + case 2: line[x >> 2] |= value << ((3 - (x & 3)) << 1); break; + case 4: line[x >> 1] |= value << ((1 - (x & 1)) << 2); break; + case 8: line[x] = value; break; + case 16: line[x << 1] = value >> 8; line[(x << 1) + 1] = value & 0xFF; break; + } +} + +static const unsigned char png_signature[8] = +{ + 137, 80, 78, 71, 13, 10, 26, 10 +}; + +static void *zalloc(void *opaque, unsigned int items, unsigned int size) +{ + return fz_calloc(items, size); +} + +static void zfree(void *opaque, void *address) +{ + fz_free(address); +} + +static inline int paeth(int a, int b, int c) +{ + /* The definitions of ac and bc are correct, not a typo. */ + int ac = b - c, bc = a - c, abcc = ac + bc; + int pa = (ac < 0 ? -ac : ac); + int pb = (bc < 0 ? -bc : bc); + int pc = (abcc < 0 ? -abcc : abcc); + return pa <= pb && pa <= pc ? a : pb <= pc ? b : c; +} + +static void +png_predict(unsigned char *samples, int width, int height, int n, int depth) +{ + int stride = (width * n * depth + 7) / 8; + int bpp = (n * depth + 7) / 8; + int i, row; + + for (row = 0; row < height; row ++) + { + unsigned char *src = samples + (stride + 1) * row; + unsigned char *dst = samples + stride * row; + + unsigned char *a = dst; + unsigned char *b = dst - stride; + unsigned char *c = dst - stride; + + switch (*src++) + { + default: + case 0: /* None */ + for (i = 0; i < stride; i++) + *dst++ = *src++; + break; + + case 1: /* Sub */ + for (i = 0; i < bpp; i++) + *dst++ = *src++; + for (i = bpp; i < stride; i++) + *dst++ = *src++ + *a++; + break; + + case 2: /* Up */ + if (row == 0) + for (i = 0; i < stride; i++) + *dst++ = *src++; + else + for (i = 0; i < stride; i++) + *dst++ = *src++ + *b++; + break; + + case 3: /* Average */ + if (row == 0) + { + for (i = 0; i < bpp; i++) + *dst++ = *src++; + for (i = bpp; i < stride; i++) + *dst++ = *src++ + (*a++ >> 1); + } + else + { + for (i = 0; i < bpp; i++) + *dst++ = *src++ + (*b++ >> 1); + for (i = bpp; i < stride; i++) + *dst++ = *src++ + ((*b++ + *a++) >> 1); + } + break; + + case 4: /* Paeth */ + if (row == 0) + { + for (i = 0; i < bpp; i++) + *dst++ = *src++ + paeth(0, 0, 0); + for (i = bpp; i < stride; i++) + *dst++ = *src++ + paeth(*a++, 0, 0); + } + else + { + for (i = 0; i < bpp; i++) + *dst++ = *src++ + paeth(0, *b++, 0); + for (i = bpp; i < stride; i++) + *dst++ = *src++ + paeth(*a++, *b++, *c++); + } + break; + } + } +} + +static const int adam7_ix[7] = { 0, 4, 0, 2, 0, 1, 0 }; +static const int adam7_dx[7] = { 8, 8, 4, 4, 2, 2, 1 }; +static const int adam7_iy[7] = { 0, 0, 4, 0, 2, 0, 1 }; +static const int adam7_dy[7] = { 8, 8, 8, 4, 4, 2, 2 }; + +static void +png_deinterlace_passes(struct info *info, int *w, int *h, int *ofs) +{ + int p, bpp = info->depth * info->n; + ofs[0] = 0; + for (p = 0; p < 7; p++) + { + w[p] = (info->width + adam7_dx[p] - adam7_ix[p] - 1) / adam7_dx[p]; + h[p] = (info->height + adam7_dy[p] - adam7_iy[p] - 1) / adam7_dy[p]; + if (w[p] == 0) h[p] = 0; + if (h[p] == 0) w[p] = 0; + if (w[p] && h[p]) + ofs[p + 1] = ofs[p] + h[p] * (1 + (w[p] * bpp + 7) / 8); + else + ofs[p + 1] = ofs[p]; + } +} + +static void +png_deinterlace(struct info *info, int *passw, int *passh, int *passofs) +{ + int n = info->n; + int depth = info->depth; + int stride = (info->width * n * depth + 7) / 8; + unsigned char *output; + int p, x, y, k; + + output = fz_calloc(info->height, stride); + + for (p = 0; p < 7; p++) + { + unsigned char *sp = info->samples + passofs[p]; + int w = passw[p]; + int h = passh[p]; + + png_predict(sp, w, h, n, depth); + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + int outx = x * adam7_dx[p] + adam7_ix[p]; + int outy = y * adam7_dy[p] + adam7_iy[p]; + unsigned char *dp = output + outy * stride; + for (k = 0; k < n; k++) + { + int v = getcomp(sp, x * n + k, depth); + putcomp(dp, outx * n + k, depth, v); + } + } + sp += (w * depth * n + 7) / 8; + } + } + + fz_free(info->samples); + info->samples = output; +} + +static int +png_read_ihdr(struct info *info, unsigned char *p, int size) +{ + int color, compression, filter; + + if (size != 13) + return fz_throw("IHDR chunk is the wrong size"); + + info->width = getint(p + 0); + info->height = getint(p + 4); + info->depth = p[8]; + + color = p[9]; + compression = p[10]; + filter = p[11]; + info->interlace = p[12]; + + if (info->width <= 0) + return fz_throw("image width must be > 0"); + if (info->height <= 0) + return fz_throw("image height must be > 0"); + + if (info->depth != 1 && info->depth != 2 && info->depth != 4 && + info->depth != 8 && info->depth != 16) + return fz_throw("image bit depth must be one of 1, 2, 4, 8, 16"); + if (color == 2 && info->depth < 8) + return fz_throw("illegal bit depth for truecolor"); + if (color == 3 && info->depth > 8) + return fz_throw("illegal bit depth for indexed"); + if (color == 4 && info->depth < 8) + return fz_throw("illegal bit depth for grayscale with alpha"); + if (color == 6 && info->depth < 8) + return fz_throw("illegal bit depth for truecolor with alpha"); + + info->indexed = 0; + if (color == 0) /* gray */ + info->n = 1; + else if (color == 2) /* rgb */ + info->n = 3; + else if (color == 4) /* gray alpha */ + info->n = 2; + else if (color == 6) /* rgb alpha */ + info->n = 4; + else if (color == 3) /* indexed */ + { + info->indexed = 1; + info->n = 1; + } + else + return fz_throw("unknown color type"); + + if (compression != 0) + return fz_throw("unknown compression method"); + if (filter != 0) + return fz_throw("unknown filter method"); + if (info->interlace != 0 && info->interlace != 1) + return fz_throw("interlace method not supported"); + + return fz_okay; +} + +static int +png_read_plte(struct info *info, unsigned char *p, int size) +{ + int n = size / 3; + int i; + + if (n > 256 || n > (1 << info->depth)) + return fz_throw("too many samples in palette"); + + for (i = 0; i < n; i++) + { + info->palette[i * 4] = p[i * 3]; + info->palette[i * 4 + 1] = p[i * 3 + 1]; + info->palette[i * 4 + 2] = p[i * 3 + 2]; + } + + return fz_okay; +} + +static int +png_read_trns(struct info *info, unsigned char *p, int size) +{ + int i; + + info->transparency = 1; + + if (info->indexed) + { + if (size > 256 || size > (1 << info->depth)) + return fz_throw("too many samples in transparency table"); + for (i = 0; i < size; i++) + info->palette[i * 4 + 3] = p[i]; + } + else + { + if (size != info->n * 2) + return fz_throw("tRNS chunk is the wrong size"); + for (i = 0; i < info->n; i++) + info->trns[i] = (p[i * 2] << 8 | p[i * 2 + 1]) & ((1 << info->depth) - 1); + } + + return fz_okay; +} + +static int +png_read_idat(struct info *info, unsigned char *p, int size, z_stream *stm) +{ + int code; + + stm->next_in = p; + stm->avail_in = size; + + code = inflate(stm, Z_SYNC_FLUSH); + if (code != Z_OK && code != Z_STREAM_END) + return fz_throw("zlib error: %s", stm->msg); + if (stm->avail_in != 0) + { + if (stm->avail_out == 0) + return fz_throw("ran out of output before input"); + return fz_throw("inflate did not consume buffer (%d remaining)", stm->avail_in); + } + + return fz_okay; +} + +static int +png_read_phys(struct info *info, unsigned char *p, int size) +{ + if (size != 9) + return fz_throw("pHYs chunk is the wrong size"); + if (p[8] == 1) + { + info->xres = getint(p) * 254 / 10000; + info->yres = getint(p + 4) * 254 / 10000; + } + return fz_okay; +} + +static int +png_read_image(struct info *info, unsigned char *p, int total) +{ + int passw[7], passh[7], passofs[8]; + int code, size; + z_stream stm; + + memset(info, 0, sizeof (struct info)); + memset(info->palette, 255, sizeof(info->palette)); + info->xres = 96; + info->yres = 96; + + /* Read signature */ + + if (total < 8 + 12 || memcmp(p, png_signature, 8)) + return fz_throw("not a png image (wrong signature)"); + + p += 8; + total -= 8; + + /* Read IHDR chunk (must come first) */ + + size = getint(p); + + if (size + 12 > total) + return fz_throw("premature end of data in png image"); + + if (!memcmp(p + 4, "IHDR", 4)) + { + code = png_read_ihdr(info, p + 8, size); + if (code) + return fz_rethrow(code, "cannot read png header"); + } + else + return fz_throw("png file must start with IHDR chunk"); + + p += size + 12; + total -= size + 12; + + /* Prepare output buffer */ + + if (!info->interlace) + { + info->size = info->height * (1 + (info->width * info->n * info->depth + 7) / 8); + } + else + { + png_deinterlace_passes(info, passw, passh, passofs); + info->size = passofs[7]; + } + + info->samples = fz_malloc(info->size); + + stm.zalloc = zalloc; + stm.zfree = zfree; + stm.opaque = NULL; + + stm.next_out = info->samples; + stm.avail_out = info->size; + + code = inflateInit(&stm); + if (code != Z_OK) + return fz_throw("zlib error: %s", stm.msg); + + /* Read remaining chunks until IEND */ + + while (total > 8) + { + size = getint(p); + + if (size + 12 > total) + return fz_throw("premature end of data in png image"); + + if (!memcmp(p + 4, "PLTE", 4)) + { + code = png_read_plte(info, p + 8, size); + if (code) + return fz_rethrow(code, "cannot read png palette"); + } + + if (!memcmp(p + 4, "tRNS", 4)) + { + code = png_read_trns(info, p + 8, size); + if (code) + return fz_rethrow(code, "cannot read png transparency"); + } + + if (!memcmp(p + 4, "pHYs", 4)) + { + code = png_read_phys(info, p + 8, size); + if (code) + return fz_rethrow(code, "cannot read png resolution"); + } + + if (!memcmp(p + 4, "IDAT", 4)) + { + code = png_read_idat(info, p + 8, size, &stm); + if (code) + return fz_rethrow(code, "cannot read png image data"); + } + + if (!memcmp(p + 4, "IEND", 4)) + break; + + p += size + 12; + total -= size + 12; + } + + code = inflateEnd(&stm); + if (code != Z_OK) + return fz_throw("zlib error: %s", stm.msg); + + /* Apply prediction filter and deinterlacing */ + + if (!info->interlace) + png_predict(info->samples, info->width, info->height, info->n, info->depth); + else + png_deinterlace(info, passw, passh, passofs); + + return fz_okay; +} + +static fz_pixmap * +png_expand_palette(struct info *info, fz_pixmap *src) +{ + fz_pixmap *dst = fz_newpixmap(fz_devicergb, 0, 0, src->w, src->h); + unsigned char *sp = src->samples; + unsigned char *dp = dst->samples; + int x, y; + + for (y = 0; y < info->height; y++) + { + for (x = 0; x < info->width; x++) + { + int v = *sp << 2; + *dp++ = info->palette[v]; + *dp++ = info->palette[v + 1]; + *dp++ = info->palette[v + 2]; + *dp++ = info->palette[v + 3]; + sp += 2; + } + } + + fz_droppixmap(src); + return dst; +} + +static void +png_mask_transparency(struct info *info, fz_pixmap *dst) +{ + int stride = (info->width * info->n * info->depth + 7) / 8; + int depth = info->depth; + int n = info->n; + int x, y, k, t; + + for (y = 0; y < info->height; y++) + { + unsigned char *sp = info->samples + y * stride; + unsigned char *dp = dst->samples + y * dst->w * dst->n; + for (x = 0; x < info->width; x++) + { + t = 1; + for (k = 0; k < n; k++) + if (getcomp(sp, x * n + k, depth) != info->trns[k]) + t = 0; + if (t) + dp[x * dst->n + dst->n - 1] = 0; + } + } +} + +int +xps_decode_png(xps_image **imagep, xps_context *ctx, byte *p, int total) +{ + fz_pixmap *pixmap; + fz_colorspace *colorspace; + xps_image *image; + struct info png; + int code; + int stride; + + code = png_read_image(&png, p, total); + if (code) + return fz_rethrow(code, "cannot read png image"); + + if (png.n == 3 || png.n == 4) + colorspace = fz_devicergb; + else + colorspace = fz_devicegray; + + stride = (png.width * png.n * png.depth + 7) / 8; + + pixmap = fz_newpixmap(colorspace, 0, 0, png.width, png.height); + fz_unpacktile(pixmap, png.samples, png.n, png.depth, stride, png.indexed); + + if (png.indexed) + { + pixmap = png_expand_palette(&png, pixmap); + } + else if (png.transparency) + { + png_mask_transparency(&png, pixmap); + } + + if (png.transparency || png.n == 2 || png.n == 4) + { + fz_premultiplypixmap(pixmap); + } + + fz_free(png.samples); + + image = fz_malloc(sizeof(xps_image)); + image->pixmap = pixmap; + image->xres = png.xres; + image->yres = png.yres; + + *imagep = image; + return fz_okay; +} diff --git a/xps/xps_resource.c b/xps/xps_resource.c new file mode 100644 index 00000000..59dc3f9d --- /dev/null +++ b/xps/xps_resource.c @@ -0,0 +1,188 @@ +#include "fitz.h" +#include "muxps.h" + +static xml_element * +xps_find_resource(xps_context *ctx, xps_resource *dict, char *name, char **urip) +{ + xps_resource *head, *node; + for (head = dict; head; head = head->parent) + { + for (node = head; node; node = node->next) + { + if (!strcmp(node->name, name)) + { + if (urip && head->base_uri) + *urip = head->base_uri; + return node->data; + } + } + } + return NULL; +} + +static xml_element * +xps_parse_resource_reference(xps_context *ctx, xps_resource *dict, char *att, char **urip) +{ + char name[1024]; + char *s; + + if (strstr(att, "{StaticResource ") != att) + return NULL; + + fz_strlcpy(name, att + 16, sizeof name); + s = strrchr(name, '}'); + if (s) + *s = 0; + + return xps_find_resource(ctx, dict, name, urip); +} + +void +xps_resolve_resource_reference(xps_context *ctx, xps_resource *dict, + char **attp, xml_element **tagp, char **urip) +{ + if (*attp) + { + xml_element *rsrc = xps_parse_resource_reference(ctx, dict, *attp, urip); + if (rsrc) + { + *attp = NULL; + *tagp = rsrc; + } + } +} + +static int +xps_parse_remote_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, char *source_att) +{ + char part_name[1024]; + char part_uri[1024]; + xps_resource *dict; + xps_part *part; + xml_element *xml; + char *s; + int code; + + /* External resource dictionaries MUST NOT reference other resource dictionaries */ + xps_absolute_path(part_name, base_uri, source_att, sizeof part_name); + part = xps_read_part(ctx, part_name); + if (!part) + { + return fz_throw("cannot find remote resource part '%s'", part_name); + } + + xml = xml_parse_document(part->data, part->size); + if (!xml) + { + xps_free_part(ctx, part); + return fz_rethrow(-1, "cannot parse xml"); + } + + if (strcmp(xml_tag(xml), "ResourceDictionary")) + { + xml_free_element(xml); + xps_free_part(ctx, part); + return fz_throw("expected ResourceDictionary element (found %s)", xml_tag(xml)); + } + + fz_strlcpy(part_uri, part_name, sizeof part_uri); + s = strrchr(part_uri, '/'); + if (s) + s[1] = 0; + + code = xps_parse_resource_dictionary(ctx, &dict, part_uri, xml); + if (code) + { + xml_free_element(xml); + xps_free_part(ctx, part); + return fz_rethrow(code, "cannot parse remote resource dictionary: %s", part_uri); + } + + dict->base_xml = xml; /* pass on ownership */ + + xps_free_part(ctx, part); + + *dictp = dict; + return fz_okay; +} + +int +xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, xml_element *root) +{ + xps_resource *head; + xps_resource *entry; + xml_element *node; + char *source; + char *key; + int code; + + source = xml_att(root, "Source"); + if (source) + { + code = xps_parse_remote_resource_dictionary(ctx, dictp, base_uri, source); + if (code) + return fz_rethrow(code, "cannot parse remote resource dictionary"); + return fz_okay; + } + + head = NULL; + + for (node = xml_down(root); node; node = xml_next(node)) + { + /* Usually "x:Key"; we have already processed and stripped namespace */ + key = xml_att(node, "Key"); + if (key) + { + entry = fz_malloc(sizeof(xps_resource)); + entry->name = key; + entry->base_uri = NULL; + entry->base_xml = NULL; + entry->data = node; + entry->next = head; + entry->parent = NULL; + head = entry; + } + } + + if (head) + { + head->base_uri = fz_strdup(base_uri); + } + + *dictp = head; + return fz_okay; +} + +void +xps_free_resource_dictionary(xps_context *ctx, xps_resource *dict) +{ + xps_resource *next; + while (dict) + { + next = dict->next; + if (dict->base_xml) + xml_free_element(dict->base_xml); + if (dict->base_uri) + fz_free(dict->base_uri); + fz_free(dict); + dict = next; + } +} + +void +xps_debug_resource_dictionary(xps_resource *dict) +{ + while (dict) + { + if (dict->base_uri) + printf("URI = '%s'\n", dict->base_uri); + printf("KEY = '%s' VAL = %p\n", dict->name, dict->data); + if (dict->parent) + { + printf("PARENT = {\n"); + xps_debug_resource_dictionary(dict->parent); + printf("}\n"); + } + dict = dict->next; + } +} diff --git a/xps/xps_tiff.c b/xps/xps_tiff.c new file mode 100644 index 00000000..4c977ab1 --- /dev/null +++ b/xps/xps_tiff.c @@ -0,0 +1,859 @@ +#include "fitz.h" +#include "muxps.h" + +/* + * TIFF image loader. Should be enough to support TIFF files in XPS. + * Baseline TIFF 6.0 plus CMYK, LZW, Flate and JPEG support. + * Limited bit depths (1,2,4,8). + * Limited planar configurations (1=chunky). + * No tiles (easy fix if necessary). + * TODO: RGBPal images + */ + +typedef struct xps_tiff xps_tiff; + +struct xps_tiff +{ + /* "file" */ + byte *bp, *rp, *ep; + + /* byte order */ + unsigned order; + + /* where we can find the strips of image data */ + unsigned rowsperstrip; + unsigned *stripoffsets; + unsigned *stripbytecounts; + + /* colormap */ + unsigned *colormap; + + /* assorted tags */ + unsigned subfiletype; + unsigned photometric; + unsigned compression; + unsigned imagewidth; + unsigned imagelength; + unsigned samplesperpixel; + unsigned bitspersample; + unsigned planar; + unsigned extrasamples; + unsigned xresolution; + unsigned yresolution; + unsigned resolutionunit; + unsigned fillorder; + unsigned g3opts; + unsigned g4opts; + unsigned predictor; + + unsigned ycbcrsubsamp[2]; + + byte *jpegtables; /* point into "file" buffer */ + unsigned jpegtableslen; + + byte *profile; + int profilesize; + + /* decoded data */ + fz_colorspace *colorspace; + byte *samples; + int stride; +}; + +enum +{ + TII = 0x4949, /* 'II' */ + TMM = 0x4d4d, /* 'MM' */ + TBYTE = 1, + TASCII = 2, + TSHORT = 3, + TLONG = 4, + TRATIONAL = 5 +}; + +#define NewSubfileType 254 +#define ImageWidth 256 +#define ImageLength 257 +#define BitsPerSample 258 +#define Compression 259 +#define PhotometricInterpretation 262 +#define FillOrder 266 +#define StripOffsets 273 +#define SamplesPerPixel 277 +#define RowsPerStrip 278 +#define StripByteCounts 279 +#define XResolution 282 +#define YResolution 283 +#define PlanarConfiguration 284 +#define T4Options 292 +#define T6Options 293 +#define ResolutionUnit 296 +#define Predictor 317 +#define ColorMap 320 +#define TileWidth 322 +#define TileLength 323 +#define TileOffsets 324 +#define TileByteCounts 325 +#define ExtraSamples 338 +#define JPEGTables 347 +#define YCbCrSubSampling 520 +#define ICCProfile 34675 + +static const byte bitrev[256] = +{ + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + +static int +xps_decode_tiff_uncompressed(xps_context *ctx, xps_tiff *tiff, fz_stream *stm, byte *wp, int wlen) +{ + int n = fz_read(stm, wp, wlen); + fz_close(stm); + if (n < 0) + return fz_rethrow(n, "cannot read uncompressed strip"); + return fz_okay; +} + +static int +xps_decode_tiff_packbits(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) +{ + fz_stream *stm = fz_openrld(chain); + int n = fz_read(stm, wp, wlen); + fz_close(stm); + if (n < 0) + return fz_rethrow(n, "cannot read packbits strip"); + return fz_okay; +} + +static int +xps_decode_tiff_lzw(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) +{ + fz_stream *stm = fz_openlzwd(chain, NULL); + int n = fz_read(stm, wp, wlen); + fz_close(stm); + if (n < 0) + return fz_rethrow(n, "cannot read lzw strip"); + return fz_okay; +} +static int +xps_decode_tiff_flate(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) +{ + fz_stream *stm = fz_openflated(chain); + int n = fz_read(stm, wp, wlen); + fz_close(stm); + if (n < 0) + return fz_rethrow(n, "cannot read flate strip"); + return fz_okay; +} + +static int +xps_decode_tiff_fax(xps_context *ctx, xps_tiff *tiff, int comp, fz_stream *chain, byte *wp, int wlen) +{ + fz_stream *stm; + fz_obj *params; + fz_obj *columns, *rows, *blackis1, *k, *encodedbytealign; + int n; + + columns = fz_newint(tiff->imagewidth); + rows = fz_newint(tiff->imagelength); + blackis1 = fz_newbool(tiff->photometric == 0); + k = fz_newint(comp == 4 ? -1 : 0); + encodedbytealign = fz_newbool(comp == 2); + + params = fz_newdict(5); + fz_dictputs(params, "Columns", columns); + fz_dictputs(params, "Rows", rows); + fz_dictputs(params, "BlackIs1", blackis1); + fz_dictputs(params, "K", k); + fz_dictputs(params, "EncodedByteAlign", encodedbytealign); + + fz_dropobj(columns); + fz_dropobj(rows); + fz_dropobj(blackis1); + fz_dropobj(k); + fz_dropobj(encodedbytealign); + + stm = fz_openfaxd(chain, params); + n = fz_read(stm, wp, wlen); + fz_close(stm); + fz_dropobj(params); + + if (n < 0) + return fz_rethrow(n, "cannot read fax strip"); + return fz_okay; +} + +static int +xps_decode_tiff_jpeg(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) +{ + fz_stream *stm = fz_opendctd(chain, NULL); + int n = fz_read(stm, wp, wlen); + fz_close(stm); + if (n < 0) + return fz_rethrow(n, "cannot read jpeg strip"); + return fz_okay; +} + +static inline int +getcomp(byte *line, int x, int bpc) +{ + switch (bpc) + { + case 1: return (line[x >> 3] >> ( 7 - (x & 7) ) ) & 1; + case 2: return (line[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3; + case 4: return (line[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15; + case 8: return line[x]; + case 16: return line[x << 1] << 8 | line[(x << 1) + 1]; + } + return 0; +} + +static inline void +putcomp(byte *line, int x, int bpc, int value) +{ + int maxval = (1 << bpc) - 1; + + switch (bpc) + { + case 1: line[x >> 3] &= ~(maxval << (7 - (x & 7))); break; + case 2: line[x >> 2] &= ~(maxval << ((3 - (x & 3)) << 1)); break; + case 4: line[x >> 1] &= ~(maxval << ((1 - (x & 1)) << 2)); break; + } + + switch (bpc) + { + case 1: line[x >> 3] |= value << (7 - (x & 7)); break; + case 2: line[x >> 2] |= value << ((3 - (x & 3)) << 1); break; + case 4: line[x >> 1] |= value << ((1 - (x & 1)) << 2); break; + case 8: line[x] = value; break; + case 16: line[x << 1] = value >> 8; line[(x << 1) + 1] = value & 0xFF; break; + } +} + +static void +xps_unpredict_tiff(byte *line, int width, int comps, int bits) +{ + byte left[32]; + int i, k, v; + + for (k = 0; k < comps; k++) + left[k] = 0; + + for (i = 0; i < width; i++) + { + for (k = 0; k < comps; k++) + { + v = getcomp(line, i * comps + k, bits); + v = v + left[k]; + v = v % (1 << bits); + putcomp(line, i * comps + k, bits, v); + left[k] = v; + } + } +} + +static void +xps_invert_tiff(byte *line, int width, int comps, int bits, int alpha) +{ + int i, k, v; + int m = (1 << bits) - 1; + + for (i = 0; i < width; i++) + { + for (k = 0; k < comps; k++) + { + v = getcomp(line, i * comps + k, bits); + if (!alpha || k < comps - 1) + v = m - v; + putcomp(line, i * comps + k, bits, v); + } + } +} + +static int +xps_expand_tiff_colormap(xps_context *ctx, xps_tiff *tiff) +{ + int maxval = 1 << tiff->bitspersample; + byte *samples; + byte *src, *dst; + int stride; + int x, y; + + /* colormap has first all red, then all green, then all blue values */ + /* colormap values are 0..65535, bits is 4 or 8 */ + /* image can be with or without extrasamples: comps is 1 or 2 */ + + if (tiff->samplesperpixel != 1 && tiff->samplesperpixel != 2) + return fz_throw("invalid number of samples for RGBPal"); + + if (tiff->bitspersample != 4 && tiff->bitspersample != 8) + return fz_throw("invalid number of bits for RGBPal"); + + stride = tiff->imagewidth * (tiff->samplesperpixel + 2); + + samples = fz_malloc(stride * tiff->imagelength); + + for (y = 0; y < tiff->imagelength; y++) + { + src = tiff->samples + (tiff->stride * y); + dst = samples + (stride * y); + + for (x = 0; x < tiff->imagewidth; x++) + { + if (tiff->extrasamples) + { + int c = getcomp(src, x * 2, tiff->bitspersample); + int a = getcomp(src, x * 2 + 1, tiff->bitspersample); + *dst++ = tiff->colormap[c + 0] >> 8; + *dst++ = tiff->colormap[c + maxval] >> 8; + *dst++ = tiff->colormap[c + maxval * 2] >> 8; + *dst++ = a << (8 - tiff->bitspersample); + } + else + { + int c = getcomp(src, x, tiff->bitspersample); + *dst++ = tiff->colormap[c + 0] >> 8; + *dst++ = tiff->colormap[c + maxval] >> 8; + *dst++ = tiff->colormap[c + maxval * 2] >> 8; + } + } + } + + tiff->samplesperpixel += 2; + tiff->bitspersample = 8; + tiff->stride = stride; + tiff->samples = samples; + return fz_okay; +} + +static int +xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff) +{ + fz_buffer buf; + fz_stream *stm; + int error; + + /* switch on compression to create a filter */ + /* feed each strip to the filter */ + /* read out the data and pack the samples into an xps_image */ + + /* type 32773 / packbits -- nothing special (same row-padding as PDF) */ + /* type 2 / ccitt rle -- no EOL, no RTC, rows are byte-aligned */ + /* type 3 and 4 / g3 and g4 -- each strip starts new section */ + /* type 5 / lzw -- each strip is handled separately */ + + byte *wp; + unsigned row; + unsigned strip; + unsigned i; + + if (!tiff->rowsperstrip || !tiff->stripoffsets || !tiff->rowsperstrip) + return fz_throw("no image data in tiff; maybe it is tiled"); + + if (tiff->planar != 1) + return fz_throw("image data is not in chunky format"); + + tiff->stride = (tiff->imagewidth * tiff->samplesperpixel * tiff->bitspersample + 7) / 8; + + switch (tiff->photometric) + { + case 0: /* WhiteIsZero -- inverted */ + tiff->colorspace = fz_devicegray; + break; + case 1: /* BlackIsZero */ + tiff->colorspace = fz_devicegray; + break; + case 2: /* RGB */ + tiff->colorspace = fz_devicergb; + break; + case 3: /* RGBPal */ + tiff->colorspace = fz_devicergb; + break; + case 5: /* CMYK */ + tiff->colorspace = fz_devicecmyk; + break; + case 6: /* YCbCr */ + /* it's probably a jpeg ... we let jpeg convert to rgb */ + tiff->colorspace = fz_devicergb; + break; + default: + return fz_throw("unknown photometric: %d", tiff->photometric); + } + + switch (tiff->resolutionunit) + { + case 2: + /* no unit conversion needed */ + break; + case 3: + tiff->xresolution *= 2.54; + tiff->yresolution *= 2.54; + break; + default: + tiff->xresolution = 96; + tiff->yresolution = 96; + break; + } + + /* Note xres and yres could be 0 even if unit was set. If so default to 96dpi. */ + if (tiff->xresolution == 0 || tiff->yresolution == 0) + { + tiff->xresolution = 96; + tiff->yresolution = 96; + } + + tiff->samples = fz_calloc(tiff->imagelength, tiff->stride); + memset(tiff->samples, 0x55, tiff->imagelength * tiff->stride); + wp = tiff->samples; + + strip = 0; + for (row = 0; row < tiff->imagelength; row += tiff->rowsperstrip) + { + unsigned offset = tiff->stripoffsets[strip]; + unsigned rlen = tiff->stripbytecounts[strip]; + unsigned wlen = tiff->stride * tiff->rowsperstrip; + byte *rp = tiff->bp + offset; + + if (wp + wlen > tiff->samples + tiff->stride * tiff->imagelength) + wlen = tiff->samples + tiff->stride * tiff->imagelength - wp; + + if (rp + rlen > tiff->ep) + return fz_throw("strip extends beyond the end of the file"); + + /* the bits are in un-natural order */ + if (tiff->fillorder == 2) + for (i = 0; i < rlen; i++) + rp[i] = bitrev[rp[i]]; + + /* create a fz_buffer on the stack */ + buf.refs = 2; + buf.data = rp; + buf.len = rlen; + buf.cap = rlen; + + /* the strip decoders will close this */ + stm = fz_openbuffer(&buf); + + switch (tiff->compression) + { + case 1: + error = xps_decode_tiff_uncompressed(ctx, tiff, stm, wp, wlen); + break; + case 2: + error = xps_decode_tiff_fax(ctx, tiff, 2, stm, wp, wlen); + break; + case 3: + error = xps_decode_tiff_fax(ctx, tiff, 3, stm, wp, wlen); + break; + case 4: + error = xps_decode_tiff_fax(ctx, tiff, 4, stm, wp, wlen); + break; + case 5: + error = xps_decode_tiff_lzw(ctx, tiff, stm, wp, wlen); + break; + case 6: + error = fz_throw("deprecated JPEG in TIFF compression not supported"); + break; + case 7: + error = xps_decode_tiff_jpeg(ctx, tiff, stm, wp, wlen); + break; + case 8: + error = xps_decode_tiff_flate(ctx, tiff, stm, wp, wlen); + break; + case 32773: + error = xps_decode_tiff_packbits(ctx, tiff, stm, wp, wlen); + break; + default: + error = fz_throw("unknown TIFF compression: %d", tiff->compression); + } + + if (error) + return fz_rethrow(error, "cannot decode strip %d", row / tiff->rowsperstrip); + + /* scramble the bits back into original order */ + if (tiff->fillorder == 2) + for (i = 0; i < rlen; i++) + rp[i] = bitrev[rp[i]]; + + wp += tiff->stride * tiff->rowsperstrip; + strip ++; + } + + /* Predictor (only for LZW and Flate) */ + if ((tiff->compression == 5 || tiff->compression == 8) && tiff->predictor == 2) + { + byte *p = tiff->samples; + for (i = 0; i < tiff->imagelength; i++) + { + xps_unpredict_tiff(p, tiff->imagewidth, tiff->samplesperpixel, tiff->bitspersample); + p += tiff->stride; + } + } + + /* RGBPal */ + if (tiff->photometric == 3 && tiff->colormap) + { + error = xps_expand_tiff_colormap(ctx, tiff); + if (error) + return fz_rethrow(error, "cannot expand colormap"); + } + + /* WhiteIsZero .. invert */ + if (tiff->photometric == 0) + { + byte *p = tiff->samples; + for (i = 0; i < tiff->imagelength; i++) + { + xps_invert_tiff(p, tiff->imagewidth, tiff->samplesperpixel, tiff->bitspersample, tiff->extrasamples); + p += tiff->stride; + } + } + + return fz_okay; +} + +static inline int readbyte(xps_tiff *tiff) +{ + if (tiff->rp < tiff->ep) + return *tiff->rp++; + return EOF; +} + +static inline unsigned readshort(xps_tiff *tiff) +{ + unsigned a = readbyte(tiff); + unsigned b = readbyte(tiff); + if (tiff->order == TII) + return (b << 8) | a; + return (a << 8) | b; +} + +static inline unsigned readlong(xps_tiff *tiff) +{ + unsigned a = readbyte(tiff); + unsigned b = readbyte(tiff); + unsigned c = readbyte(tiff); + unsigned d = readbyte(tiff); + if (tiff->order == TII) + return (d << 24) | (c << 16) | (b << 8) | a; + return (a << 24) | (b << 16) | (c << 8) | d; +} + +static void +xps_read_tiff_bytes(unsigned char *p, xps_tiff *tiff, unsigned ofs, unsigned n) +{ + tiff->rp = tiff->bp + ofs; + if (tiff->rp > tiff->ep) + tiff->rp = tiff->bp; + + while (n--) + *p++ = readbyte(tiff); +} + +static void +xps_read_tiff_tag_value(unsigned *p, xps_tiff *tiff, unsigned type, unsigned ofs, unsigned n) +{ + tiff->rp = tiff->bp + ofs; + if (tiff->rp > tiff->ep) + tiff->rp = tiff->bp; + + while (n--) + { + switch (type) + { + case TRATIONAL: + *p = readlong(tiff); + *p = *p / readlong(tiff); + p ++; + break; + case TBYTE: *p++ = readbyte(tiff); break; + case TSHORT: *p++ = readshort(tiff); break; + case TLONG: *p++ = readlong(tiff); break; + default: *p++ = 0; break; + } + } +} + +static int +xps_read_tiff_tag(xps_context *ctx, xps_tiff *tiff, unsigned offset) +{ + unsigned tag; + unsigned type; + unsigned count; + unsigned value; + + tiff->rp = tiff->bp + offset; + + tag = readshort(tiff); + type = readshort(tiff); + count = readlong(tiff); + + if ((type == TBYTE && count <= 4) || + (type == TSHORT && count <= 2) || + (type == TLONG && count <= 1)) + value = tiff->rp - tiff->bp; + else + value = readlong(tiff); + + switch (tag) + { + case NewSubfileType: + xps_read_tiff_tag_value(&tiff->subfiletype, tiff, type, value, 1); + break; + case ImageWidth: + xps_read_tiff_tag_value(&tiff->imagewidth, tiff, type, value, 1); + break; + case ImageLength: + xps_read_tiff_tag_value(&tiff->imagelength, tiff, type, value, 1); + break; + case BitsPerSample: + xps_read_tiff_tag_value(&tiff->bitspersample, tiff, type, value, 1); + break; + case Compression: + xps_read_tiff_tag_value(&tiff->compression, tiff, type, value, 1); + break; + case PhotometricInterpretation: + xps_read_tiff_tag_value(&tiff->photometric, tiff, type, value, 1); + break; + case FillOrder: + xps_read_tiff_tag_value(&tiff->fillorder, tiff, type, value, 1); + break; + case SamplesPerPixel: + xps_read_tiff_tag_value(&tiff->samplesperpixel, tiff, type, value, 1); + break; + case RowsPerStrip: + xps_read_tiff_tag_value(&tiff->rowsperstrip, tiff, type, value, 1); + break; + case XResolution: + xps_read_tiff_tag_value(&tiff->xresolution, tiff, type, value, 1); + break; + case YResolution: + xps_read_tiff_tag_value(&tiff->yresolution, tiff, type, value, 1); + break; + case PlanarConfiguration: + xps_read_tiff_tag_value(&tiff->planar, tiff, type, value, 1); + break; + case T4Options: + xps_read_tiff_tag_value(&tiff->g3opts, tiff, type, value, 1); + break; + case T6Options: + xps_read_tiff_tag_value(&tiff->g4opts, tiff, type, value, 1); + break; + case Predictor: + xps_read_tiff_tag_value(&tiff->predictor, tiff, type, value, 1); + break; + case ResolutionUnit: + xps_read_tiff_tag_value(&tiff->resolutionunit, tiff, type, value, 1); + break; + case YCbCrSubSampling: + xps_read_tiff_tag_value(tiff->ycbcrsubsamp, tiff, type, value, 2); + break; + case ExtraSamples: + xps_read_tiff_tag_value(&tiff->extrasamples, tiff, type, value, 1); + break; + + case ICCProfile: + tiff->profile = fz_malloc(count); + /* ICC profile data type is set to UNDEFINED. + * TBYTE reading not correct in xps_read_tiff_tag_value */ + xps_read_tiff_bytes(tiff->profile, tiff, value, count); + tiff->profilesize = count; + break; + + case JPEGTables: + fz_warn("jpeg tables in tiff not implemented"); + tiff->jpegtables = tiff->bp + value; + tiff->jpegtableslen = count; + break; + + case StripOffsets: + tiff->stripoffsets = fz_calloc(count, sizeof(unsigned)); + xps_read_tiff_tag_value(tiff->stripoffsets, tiff, type, value, count); + break; + + case StripByteCounts: + tiff->stripbytecounts = fz_calloc(count, sizeof(unsigned)); + xps_read_tiff_tag_value(tiff->stripbytecounts, tiff, type, value, count); + break; + + case ColorMap: + tiff->colormap = fz_calloc(count, sizeof(unsigned)); + xps_read_tiff_tag_value(tiff->colormap, tiff, type, value, count); + break; + + case TileWidth: + case TileLength: + case TileOffsets: + case TileByteCounts: + return fz_throw("tiled tiffs not supported"); + + default: + /* printf("unknown tag: %d t=%d n=%d\n", tag, type, count); */ + break; + } + + return fz_okay; +} + +static void +xps_swap_byte_order(byte *buf, int n) +{ + int i, t; + for (i = 0; i < n; i++) + { + t = buf[i * 2 + 0]; + buf[i * 2 + 0] = buf[i * 2 + 1]; + buf[i * 2 + 1] = t; + } +} + +static int +xps_decode_tiff_header(xps_context *ctx, xps_tiff *tiff, byte *buf, int len) +{ + unsigned version; + unsigned offset; + unsigned count; + unsigned i; + int error; + + memset(tiff, 0, sizeof(xps_tiff)); + + tiff->bp = buf; + tiff->rp = buf; + tiff->ep = buf + len; + + /* tag defaults, where applicable */ + tiff->bitspersample = 1; + tiff->compression = 1; + tiff->samplesperpixel = 1; + tiff->resolutionunit = 2; + tiff->rowsperstrip = 0xFFFFFFFF; + tiff->fillorder = 1; + tiff->planar = 1; + tiff->subfiletype = 0; + tiff->predictor = 1; + tiff->ycbcrsubsamp[0] = 2; + tiff->ycbcrsubsamp[1] = 2; + + /* + * Read IFH + */ + + /* get byte order marker */ + tiff->order = TII; + tiff->order = readshort(tiff); + if (tiff->order != TII && tiff->order != TMM) + return fz_throw("not a TIFF file, wrong magic marker"); + + /* check version */ + version = readshort(tiff); + if (version != 42) + return fz_throw("not a TIFF file, wrong version marker"); + + /* get offset of IFD */ + offset = readlong(tiff); + + /* + * Read IFD + */ + + tiff->rp = tiff->bp + offset; + + count = readshort(tiff); + + offset += 2; + for (i = 0; i < count; i++) + { + error = xps_read_tiff_tag(ctx, tiff, offset); + if (error) + return fz_rethrow(error, "cannot read TIFF header tag"); + offset += 12; + } + + return fz_okay; +} + +int +xps_decode_tiff(xps_image **imagep, xps_context *ctx, byte *buf, int len) +{ + int error; + fz_pixmap *pixmap; + xps_image *image; + xps_tiff tiff; + + error = xps_decode_tiff_header(ctx, &tiff, buf, len); + if (error) + return fz_rethrow(error, "cannot decode tiff header"); + + /* Decode the image strips */ + + if (tiff.rowsperstrip > tiff.imagelength) + tiff.rowsperstrip = tiff.imagelength; + + error = xps_decode_tiff_strips(ctx, &tiff); + if (error) + return fz_rethrow(error, "cannot decode image data"); + + /* Byte swap 16-bit images to big endian if necessary */ + if (tiff.bitspersample == 16) + { + if (tiff.order == TII) + xps_swap_byte_order(tiff.samples, tiff.imagewidth * tiff.imagelength * tiff.samplesperpixel); + } + + /* Expand into fz_pixmap struct */ + + pixmap = fz_newpixmap(tiff.colorspace, 0, 0, tiff.imagewidth, tiff.imagelength); + + fz_unpacktile(pixmap, tiff.samples, tiff.samplesperpixel, tiff.bitspersample, tiff.stride, 0); + + /* We should only do this on non-pre-multiplied images, but files in the wild are bad */ + if (tiff.extrasamples /* == 2 */) + fz_premultiplypixmap(pixmap); + + image = fz_malloc(sizeof(xps_image)); + image->pixmap = pixmap; + image->xres = tiff.xresolution; + image->yres = tiff.yresolution; + + /* Clean up scratch memory */ + + if (tiff.colormap) fz_free(tiff.colormap); + if (tiff.stripoffsets) fz_free(tiff.stripoffsets); + if (tiff.stripbytecounts) fz_free(tiff.stripbytecounts); + if (tiff.samples) fz_free(tiff.samples); + + *imagep = image; + return fz_okay; +} diff --git a/xps/xps_tile.c b/xps/xps_tile.c new file mode 100644 index 00000000..82ec543f --- /dev/null +++ b/xps/xps_tile.c @@ -0,0 +1,228 @@ +#include "fitz.h" +#include "muxps.h" + +/* + * Parse a tiling brush (visual and image brushes at this time) common + * properties. Use the callback to draw the individual tiles. + */ + +enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y }; + +struct closure +{ + char *base_uri; + xps_resource *dict; + xml_element *root; + void *user; + void (*func)(xps_context*, fz_matrix, char*, xps_resource*, xml_element*, void*); +}; + +static void +xps_paint_tiling_brush_clipped(xps_context *ctx, fz_matrix ctm, fz_rect viewbox, struct closure *c) +{ + fz_path *path = fz_newpath(); + fz_moveto(path, viewbox.x0, viewbox.y0); + fz_lineto(path, viewbox.x0, viewbox.y1); + fz_lineto(path, viewbox.x1, viewbox.y1); + fz_lineto(path, viewbox.x1, viewbox.y0); + fz_closepath(path); + + ctx->dev->clippath(ctx->dev->user, path, 0, ctm); + + c->func(ctx, ctm, c->base_uri, c->dict, c->root, c->user); + + ctx->dev->popclip(ctx->dev->user); +} + +static void +xps_paint_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect viewbox, int tile_mode, struct closure *c) +{ + fz_matrix ttm; + + xps_paint_tiling_brush_clipped(ctx, ctm, viewbox, c); + + if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y) + { + ttm = fz_concat(fz_translate(viewbox.x1 * 2, 0), ctm); + ttm = fz_concat(fz_scale(-1, 1), ttm); + xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c); + } + + if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y) + { + ttm = fz_concat(fz_translate(0, viewbox.y1 * 2), ctm); + ttm = fz_concat(fz_scale(1, -1), ttm); + xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c); + } + + if (tile_mode == TILE_FLIP_X_Y) + { + ttm = fz_concat(fz_translate(viewbox.x1 * 2, viewbox.y1 * 2), ctm); + ttm = fz_concat(fz_scale(-1, -1), ttm); + xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c); + } +} + +void +xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, + char *base_uri, xps_resource *dict, xml_element *root, + void (*func)(xps_context*, fz_matrix, char*, xps_resource*, xml_element*, void*), void *user) +{ + xml_element *node; + struct closure c; + + char *opacity_att; + char *transform_att; + char *viewbox_att; + char *viewport_att; + char *tile_mode_att; + char *viewbox_units_att; + char *viewport_units_att; + + xml_element *transform_tag = NULL; + + fz_matrix transform; + fz_rect viewbox; + fz_rect viewport; + float xstep, ystep; + float xscale, yscale; + int tile_mode; + + opacity_att = xml_att(root, "Opacity"); + transform_att = xml_att(root, "Transform"); + viewbox_att = xml_att(root, "Viewbox"); + viewport_att = xml_att(root, "Viewport"); + tile_mode_att = xml_att(root, "TileMode"); + viewbox_units_att = xml_att(root, "ViewboxUnits"); + viewport_units_att = xml_att(root, "ViewportUnits"); + + c.base_uri = base_uri; + c.dict = dict; + c.root = root; + c.user = user; + c.func = func; + + for (node = xml_down(root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "ImageBrush.Transform")) + transform_tag = xml_down(node); + if (!strcmp(xml_tag(node), "VisualBrush.Transform")) + transform_tag = xml_down(node); + } + + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + + transform = fz_identity; + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + + viewbox = fz_unitrect; + if (viewbox_att) + xps_parse_rectangle(ctx, viewbox_att, &viewbox); + + viewport = fz_unitrect; + if (viewport_att) + xps_parse_rectangle(ctx, viewport_att, &viewport); + + /* some sanity checks on the viewport/viewbox size */ + if (fabs(viewport.x1 - viewport.x0) < 0.01) return; + if (fabs(viewport.y1 - viewport.y0) < 0.01) return; + if (fabs(viewbox.x1 - viewbox.x0) < 0.01) return; + if (fabs(viewbox.y1 - viewbox.y0) < 0.01) return; + + xstep = viewbox.x1 - viewbox.x0; + ystep = viewbox.y1 - viewbox.y0; + + xscale = (viewport.x1 - viewport.x0) / xstep; + yscale = (viewport.y1 - viewport.y0) / ystep; + + tile_mode = TILE_NONE; + if (tile_mode_att) + { + if (!strcmp(tile_mode_att, "None")) + tile_mode = TILE_NONE; + if (!strcmp(tile_mode_att, "Tile")) + tile_mode = TILE_TILE; + if (!strcmp(tile_mode_att, "FlipX")) + tile_mode = TILE_FLIP_X; + if (!strcmp(tile_mode_att, "FlipY")) + tile_mode = TILE_FLIP_Y; + if (!strcmp(tile_mode_att, "FlipXY")) + tile_mode = TILE_FLIP_X_Y; + } + + if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y) + xstep *= 2; + if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y) + ystep *= 2; + + xps_begin_opacity(ctx, ctm, area, base_uri, dict, opacity_att, NULL); + + ctm = fz_concat(transform, ctm); + ctm = fz_concat(fz_translate(viewport.x0, viewport.y0), ctm); + ctm = fz_concat(fz_scale(xscale, yscale), ctm); + ctm = fz_concat(fz_translate(-viewbox.x0, -viewbox.y0), ctm); + + if (tile_mode != TILE_NONE && !fz_isinfiniterect(area)) + { + fz_matrix invctm = fz_invertmatrix(ctm); + fz_rect bbox = fz_transformrect(invctm, area); + int x0 = floorf(bbox.x0 / xstep); + int y0 = floorf(bbox.y0 / ystep); + int x1 = ceilf(bbox.x1 / xstep); + int y1 = ceilf(bbox.y1 / ystep); + int x, y; + + for (y = y0; y < y1; y++) + { + for (x = x0; x < x1; x++) + { + fz_matrix ttm = fz_concat(fz_translate(xstep * x, ystep * y), ctm); + xps_paint_tiling_brush(ctx, ttm, viewbox, tile_mode, &c); + } + } + } + else + { + xps_paint_tiling_brush(ctx, ctm, viewbox, tile_mode, &c); + } + + xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); +} + +static void +xps_paint_visual_brush(xps_context *ctx, fz_matrix ctm, + char *base_uri, xps_resource *dict, xml_element *root, void *visual_tag) +{ + xps_parse_element(ctx, ctm, base_uri, dict, (xml_element *)visual_tag); +} + +void +xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, + char *base_uri, xps_resource *dict, xml_element *root) +{ + xml_element *node; + + char *visual_uri; + char *visual_att; + xml_element *visual_tag = NULL; + + visual_att = xml_att(root, "Visual"); + + for (node = xml_down(root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "VisualBrush.Visual")) + visual_tag = xml_down(node); + } + + visual_uri = base_uri; + xps_resolve_resource_reference(ctx, dict, &visual_att, &visual_tag, &visual_uri); + + if (visual_tag) + { + xps_parse_tiling_brush(ctx, ctm, area, + visual_uri, dict, root, xps_paint_visual_brush, visual_tag); + } +} diff --git a/xps/xps_util.c b/xps/xps_util.c new file mode 100644 index 00000000..76175d68 --- /dev/null +++ b/xps/xps_util.c @@ -0,0 +1,94 @@ +#include "fitz.h" +#include "muxps.h" + +static inline int tolower(int c) +{ + if (c >= 'A' && c <= 'Z') + return c + 32; + return c; +} + +int +xps_strcasecmp(char *a, char *b) +{ + while (tolower(*a) == tolower(*b)) + { + if (*a++ == 0) + return 0; + b++; + } + return tolower(*a) - tolower(*b); +} + +#define SEP(x) ((x)=='/' || (x) == 0) + +static char * +xps_clean_path(char *name) +{ + char *p, *q, *dotdot; + int rooted; + + rooted = name[0] == '/'; + + /* + * invariants: + * p points at beginning of path element we're considering. + * q points just past the last path element we wrote (no slash). + * dotdot points just past the point where .. cannot backtrack + * any further (no slash). + */ + p = q = dotdot = name + rooted; + while (*p) + { + if(p[0] == '/') /* null element */ + p++; + else if (p[0] == '.' && SEP(p[1])) + p += 1; /* don't count the separator in case it is nul */ + else if (p[0] == '.' && p[1] == '.' && SEP(p[2])) + { + p += 2; + if (q > dotdot) /* can backtrack */ + { + while(--q > dotdot && *q != '/') + ; + } + else if (!rooted) /* /.. is / but ./../ is .. */ + { + if (q != name) + *q++ = '/'; + *q++ = '.'; + *q++ = '.'; + dotdot = q; + } + } + else /* real path element */ + { + if (q != name+rooted) + *q++ = '/'; + while ((*q = *p) != '/' && *q != 0) + p++, q++; + } + } + + if (q == name) /* empty string is really "." */ + *q++ = '.'; + *q = '\0'; + + return name; +} + +void +xps_absolute_path(char *output, char *base_uri, char *path, int output_size) +{ + if (path[0] == '/') + { + fz_strlcpy(output, path, output_size); + } + else + { + fz_strlcpy(output, base_uri, output_size); + fz_strlcat(output, "/", output_size); + fz_strlcat(output, path, output_size); + } + xps_clean_path(output); +} diff --git a/xps/xps_xml.c b/xps/xps_xml.c new file mode 100644 index 00000000..397b68ec --- /dev/null +++ b/xps/xps_xml.c @@ -0,0 +1,387 @@ +#include "fitz.h" +#include "muxps.h" + +struct attribute +{ + char name[32]; + char *value; + struct attribute *next; +}; + +struct element +{ + char name[32]; + struct attribute *atts; + struct element *up, *down, *next; +}; + +struct parser +{ + struct element *head; +}; + +static inline void indent(int n) +{ + while (n--) putchar(' '); +} + +void xml_print_element(struct element *item, int level) +{ + while (item) { + struct attribute *att; + indent(level); + printf("<%s", item->name); + for (att = item->atts; att; att = att->next) + printf(" %s=\"%s\"", att->name, att->value); + if (item->down) { + printf(">\n"); + xml_print_element(item->down, level + 1); + indent(level); + printf("\n", item->name); + } + else { + printf("/>\n"); + } + item = item->next; + } +} + +struct element *xml_next(struct element *item) +{ + return item->next; +} + +struct element *xml_down(struct element *item) +{ + return item->down; +} + +char *xml_tag(struct element *item) +{ + return item->name; +} + +char *xml_att(struct element *item, const char *name) +{ + struct attribute *att; + for (att = item->atts; att; att = att->next) + if (!strcmp(att->name, name)) + return att->value; + return NULL; +} + +static void xml_free_attribute(struct attribute *att) +{ + while (att) { + struct attribute *next = att->next; + if (att->value) + fz_free(att->value); + fz_free(att); + att = next; + } +} + +void xml_free_element(struct element *item) +{ + while (item) { + struct element *next = item->next; + if (item->atts) + xml_free_attribute(item->atts); + if (item->down) + xml_free_element(item->down); + fz_free(item); + item = next; + } +} + +static int xml_parse_entity(int *c, char *a) +{ + char *b; + if (a[1] == '#') { + if (a[2] == 'x') + *c = strtol(a + 3, &b, 16); + else + *c = strtol(a + 2, &b, 10); + if (*b == ';') + return b - a; + } + else if (a[1] == 'l' && a[2] == 't' && a[3] == ';') { + *c = '<'; + return 4; + } + else if (a[1] == 'g' && a[2] == 't' && a[3] == ';') { + *c = '>'; + return 4; + } + else if (a[1] == 'a' && a[2] == 'm' && a[3] == 'p' && a[4] == ';') { + *c = '&'; + return 5; + } + else if (a[1] == 'a' && a[2] == 'p' && a[3] == 'o' && a[4] == 's' && a[5] == ';') { + *c = '\''; + return 6; + } + else if (a[1] == 'q' && a[2] == 'u' && a[3] == 'o' && a[4] == 't' && a[5] == ';') { + *c = '"'; + return 6; + } + *c = *a++; + return 1; +} + +static void xml_emit_open_tag(struct parser *parser, char *a, char *b) +{ + struct element *head, *tail; + + head = fz_malloc(sizeof(struct element)); + if (b - a > sizeof(head->name)) + b = a + sizeof(head->name); + memcpy(head->name, a, b - a); + head->name[b - a] = 0; + + head->atts = NULL; + head->up = parser->head; + head->down = NULL; + head->next = NULL; + + if (!parser->head->down) { + parser->head->down = head; + } + else { + tail = parser->head->down; + while (tail->next) + tail = tail->next; + tail->next = head; + } + + parser->head = head; +} + +static void xml_emit_att_name(struct parser *parser, char *a, char *b) +{ + struct element *head = parser->head; + struct attribute *att; + + att = fz_malloc(sizeof(struct attribute)); + if (b - a > sizeof(att->name)) + b = a + sizeof(att->name); + memcpy(att->name, a, b - a); + att->name[b - a] = 0; + att->value = NULL; + att->next = head->atts; + head->atts = att; +} + +static void xml_emit_att_value(struct parser *parser, char *a, char *b) +{ + struct element *head = parser->head; + struct attribute *att = head->atts; + char *s; + int c; + + /* entities are all longer than UTFmax so runetochar is safe */ + s = att->value = fz_malloc(b - a + 1); + while (a < b) { + if (*a == '&') { + a += xml_parse_entity(&c, a); + s += runetochar(s, &c); + } + else { + *s++ = *a++; + } + } + *s = 0; +} + +static void xml_emit_close_tag(struct parser *parser) +{ + if (parser->head->up) + parser->head = parser->head->up; +} + +static inline int isname(int c) +{ + return c == '.' || c == '-' || c == '_' || c == ':' || + (c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z'); +} + +static inline int iswhite(int c) +{ + return c == ' ' || c == '\r' || c == '\n' || c == '\t'; +} + +static char *xml_parse_document_imp(struct parser *x, char *p) +{ + char *mark; + int quote; + +parse_text: + mark = p; + while (*p && *p != '<') ++p; + if (*p == '<') { ++p; goto parse_element; } + return NULL; + +parse_element: + if (*p == '/') { ++p; goto parse_closing_element; } + if (*p == '!') { ++p; goto parse_comment; } + if (*p == '?') { ++p; goto parse_processing_instruction; } + while (iswhite(*p)) ++p; + if (isname(*p)) + goto parse_element_name; + return "syntax error in element"; + +parse_comment: + if (*p == '[') goto parse_cdata; + if (*p++ != '-') return "syntax error in comment (') { + p += 3; + goto parse_text; + } + ++p; + } + return "end of data in comment"; + +parse_cdata: + if (p[1] != 'C' || p[2] != 'D' || p[3] != 'A' || p[4] != 'T' || p[5] != 'A' || p[6] != '[') + return "syntax error in CDATA section"; + p += 7; + mark = p; + while (*p) { + if (p[0] == ']' && p[1] == ']' && p[2] == '>') { + p += 3; + goto parse_text; + } + ++p; + } + return "end of data in CDATA section"; + +parse_processing_instruction: + while (*p) { + if (p[0] == '?' && p[1] == '>') { + p += 2; + goto parse_text; + } + ++p; + } + return "end of data in processing instruction"; + +parse_closing_element: + while (iswhite(*p)) ++p; + mark = p; + while (isname(*p)) ++p; + while (iswhite(*p)) ++p; + if (*p != '>') + return "syntax error in closing element"; + xml_emit_close_tag(x); + ++p; + goto parse_text; + +parse_element_name: + mark = p; + while (isname(*p)) ++p; + xml_emit_open_tag(x, mark, p); + if (*p == '>') { ++p; goto parse_text; } + if (p[0] == '/' && p[1] == '>') { + xml_emit_close_tag(x); + p += 2; + goto parse_text; + } + if (iswhite(*p)) + goto parse_attributes; + return "syntax error after element name"; + +parse_attributes: + while (iswhite(*p)) ++p; + if (isname(*p)) + goto parse_attribute_name; + if (*p == '>') { ++p; goto parse_text; } + if (p[0] == '/' && p[1] == '>') { + xml_emit_close_tag(x); + p += 2; + goto parse_text; + } + return "syntax error in attributes"; + +parse_attribute_name: + mark = p; + while (isname(*p)) ++p; + xml_emit_att_name(x, mark, p); + while (iswhite(*p)) ++p; + if (*p == '=') { ++p; goto parse_attribute_value; } + return "syntax error after attribute name"; + +parse_attribute_value: + while (iswhite(*p)) ++p; + quote = *p++; + if (quote != '"' && quote != '\'') + return "missing quote character"; + mark = p; + while (*p && *p != quote) ++p; + if (*p == quote) { + xml_emit_att_value(x, mark, p++); + goto parse_attributes; + } + return "end of data in attribute value"; +} + +static char *convert_to_utf8(unsigned char *s, int n) +{ + unsigned char *e = s + n; + char *dst, *d; + int c; + + if (s[0] == 0xFE && s[1] == 0xFF) { + dst = d = fz_malloc(n * 2); + while (s + 1 < e) { + c = s[0] << 8 | s[1]; + d += runetochar(d, &c); + s += 2; + } + *d = 0; + return dst; + } + + if (s[0] == 0xFF && s[1] == 0xFE) { + dst = d = fz_malloc(n * 2); + while (s + 1 < e) { + c = s[0] | s[1] << 8; + d += runetochar(d, &c); + s += 2; + } + *d = 0; + return dst; + } + + return (char*)s; +} + +struct element * +xml_parse_document(unsigned char *s, int n) +{ + struct parser parser; + struct element root; + char *p, *error; + + /* s is already null-terminated (see xps_new_part) */ + + memset(&root, 0, sizeof(root)); + parser.head = &root; + + p = convert_to_utf8(s, n); + + error = xml_parse_document_imp(&parser, p); + if (error) { + fz_throw(error); + return NULL; + } + + if (p != (char*)s) + fz_free(p); + + return root.down; +} diff --git a/xps/xps_zip.c b/xps/xps_zip.c new file mode 100644 index 00000000..ad708bcb --- /dev/null +++ b/xps/xps_zip.c @@ -0,0 +1,545 @@ +#include "fitz.h" +#include "muxps.h" + +#include + +static inline int getshort(FILE *file) +{ + int a = getc(file); + int b = getc(file); + return a | (b << 8); +} + +static inline int getlong(FILE *file) +{ + int a = getc(file); + int b = getc(file); + int c = getc(file); + int d = getc(file); + return a | (b << 8) | (c << 16) | (d << 24); +} + +static void * +xps_zip_alloc_items(xps_context *ctx, int items, int size) +{ + return fz_calloc(items, size); +} + +static void +xps_zip_free(xps_context *ctx, void *ptr) +{ + fz_free(ptr); +} + +static int +xps_compare_entries(const void *a0, const void *b0) +{ + xps_entry *a = (xps_entry*) a0; + xps_entry *b = (xps_entry*) b0; + return xps_strcasecmp(a->name, b->name); +} + +static xps_entry * +xps_find_zip_entry(xps_context *ctx, char *name) +{ + int l = 0; + int r = ctx->zip_count - 1; + while (l <= r) + { + int m = (l + r) >> 1; + int c = xps_strcasecmp(name, ctx->zip_table[m].name); + if (c < 0) + r = m - 1; + else if (c > 0) + l = m + 1; + else + return &ctx->zip_table[m]; + } + return NULL; +} + +/* + * Inflate the data in a zip entry. + */ + +static int +xps_read_zip_entry(xps_context *ctx, xps_entry *ent, unsigned char *outbuf) +{ + z_stream stream; + unsigned char *inbuf; + int sig; + int version, general, method; + int namelength, extralength; + int code; + + fseek(ctx->file, ent->offset, 0); + + sig = getlong(ctx->file); + if (sig != ZIP_LOCAL_FILE_SIG) + return fz_throw("wrong zip local file signature (0x%x)", sig); + + version = getshort(ctx->file); + general = getshort(ctx->file); + method = getshort(ctx->file); + (void) getshort(ctx->file); /* file time */ + (void) getshort(ctx->file); /* file date */ + (void) getlong(ctx->file); /* crc-32 */ + (void) getlong(ctx->file); /* csize */ + (void) getlong(ctx->file); /* usize */ + namelength = getshort(ctx->file); + extralength = getshort(ctx->file); + + fseek(ctx->file, namelength + extralength, 1); + + if (method == 0) + { + fread(outbuf, 1, ent->usize, ctx->file); + } + else if (method == 8) + { + inbuf = fz_malloc(ent->csize); + + fread(inbuf, 1, ent->csize, ctx->file); + + memset(&stream, 0, sizeof(z_stream)); + stream.zalloc = (alloc_func) xps_zip_alloc_items; + stream.zfree = (free_func) xps_zip_free; + stream.opaque = ctx; + stream.next_in = inbuf; + stream.avail_in = ent->csize; + stream.next_out = outbuf; + stream.avail_out = ent->usize; + + code = inflateInit2(&stream, -15); + if (code != Z_OK) + return fz_throw("zlib inflateInit2 error: %s", stream.msg); + code = inflate(&stream, Z_FINISH); + if (code != Z_STREAM_END) + { + inflateEnd(&stream); + return fz_throw("zlib inflate error: %s", stream.msg); + } + code = inflateEnd(&stream); + if (code != Z_OK) + return fz_throw("zlib inflateEnd error: %s", stream.msg); + + fz_free(inbuf); + } + else + { + return fz_throw("unknown compression method (%d)", method); + } + + return fz_okay; +} + +/* + * Read the central directory in a zip file. + */ + +static int +xps_read_zip_dir(xps_context *ctx, int start_offset) +{ + int sig; + int offset, count; + int namesize, metasize, commentsize; + int i; + + fseek(ctx->file, start_offset, 0); + + sig = getlong(ctx->file); + if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG) + return fz_throw("wrong zip end of central directory signature (0x%x)", sig); + + (void) getshort(ctx->file); /* this disk */ + (void) getshort(ctx->file); /* start disk */ + (void) getshort(ctx->file); /* entries in this disk */ + count = getshort(ctx->file); /* entries in central directory disk */ + (void) getlong(ctx->file); /* size of central directory */ + offset = getlong(ctx->file); /* offset to central directory */ + + ctx->zip_count = count; + ctx->zip_table = fz_calloc(count, sizeof(xps_entry)); + memset(ctx->zip_table, 0, sizeof(xps_entry) * count); + + fseek(ctx->file, offset, 0); + + for (i = 0; i < count; i++) + { + sig = getlong(ctx->file); + if (sig != ZIP_CENTRAL_DIRECTORY_SIG) + return fz_throw("wrong zip central directory signature (0x%x)", sig); + + (void) getshort(ctx->file); /* version made by */ + (void) getshort(ctx->file); /* version to extract */ + (void) getshort(ctx->file); /* general */ + (void) getshort(ctx->file); /* method */ + (void) getshort(ctx->file); /* last mod file time */ + (void) getshort(ctx->file); /* last mod file date */ + (void) getlong(ctx->file); /* crc-32 */ + ctx->zip_table[i].csize = getlong(ctx->file); + ctx->zip_table[i].usize = getlong(ctx->file); + namesize = getshort(ctx->file); + metasize = getshort(ctx->file); + commentsize = getshort(ctx->file); + (void) getshort(ctx->file); /* disk number start */ + (void) getshort(ctx->file); /* int file atts */ + (void) getlong(ctx->file); /* ext file atts */ + ctx->zip_table[i].offset = getlong(ctx->file); + + ctx->zip_table[i].name = fz_malloc(namesize + 1); + fread(ctx->zip_table[i].name, 1, namesize, ctx->file); + ctx->zip_table[i].name[namesize] = 0; + + fseek(ctx->file, metasize, 1); + fseek(ctx->file, commentsize, 1); + } + + qsort(ctx->zip_table, count, sizeof(xps_entry), xps_compare_entries); + + return fz_okay; +} + +static int +xps_find_and_read_zip_dir(xps_context *ctx) +{ + int filesize, back, maxback; + int i, n; + char buf[512]; + + fseek(ctx->file, 0, SEEK_END); + filesize = ftell(ctx->file); + + maxback = MIN(filesize, 0xFFFF + sizeof buf); + back = MIN(maxback, sizeof buf); + + while (back < maxback) + { + fseek(ctx->file, filesize - back, 0); + + n = fread(buf, 1, sizeof buf, ctx->file); + if (n < 0) + return fz_throw("cannot read end of central directory"); + + for (i = n - 4; i > 0; i--) + if (!memcmp(buf + i, "PK\5\6", 4)) + return xps_read_zip_dir(ctx, filesize - back + i); + + back += sizeof buf - 4; + } + + return fz_throw("cannot find end of central directory"); +} + +/* + * Read and interleave split parts from a ZIP file. + */ + +static xps_part * +xps_read_zip_part(xps_context *ctx, char *partname) +{ + char buf[2048]; + xps_entry *ent; + xps_part *part; + int count, size, offset, i; + char *name; + + name = partname; + if (name[0] == '/') + name ++; + + /* All in one piece */ + ent = xps_find_zip_entry(ctx, name); + if (ent) + { + part = xps_new_part(ctx, partname, ent->usize); + xps_read_zip_entry(ctx, ent, part->data); + return part; + } + + /* Count the number of pieces and their total size */ + count = 0; + size = 0; + while (1) + { + sprintf(buf, "%s/[%d].piece", name, count); + ent = xps_find_zip_entry(ctx, buf); + if (!ent) + { + sprintf(buf, "%s/[%d].last.piece", name, count); + ent = xps_find_zip_entry(ctx, buf); + } + if (!ent) + break; + count ++; + size += ent->usize; + } + + /* Inflate the pieces */ + if (count) + { + part = xps_new_part(ctx, partname, size); + offset = 0; + for (i = 0; i < count; i++) + { + if (i < count - 1) + sprintf(buf, "%s/[%d].piece", name, i); + else + sprintf(buf, "%s/[%d].last.piece", name, i); + ent = xps_find_zip_entry(ctx, buf); + xps_read_zip_entry(ctx, ent, part->data + offset); + offset += ent->usize; + } + return part; + } + + return NULL; +} + +/* + * Read and interleave split parts from files in the directory. + */ + +static xps_part * +xps_read_dir_part(xps_context *ctx, char *name) +{ + char buf[2048]; + xps_part *part; + FILE *file; + int count, size, offset, i, n; + + fz_strlcpy(buf, ctx->directory, sizeof buf); + fz_strlcat(buf, name, sizeof buf); + + /* All in one piece */ + file = fopen(buf, "rb"); + if (file) + { + fseek(file, 0, SEEK_END); + size = ftell(file); + fseek(file, 0, SEEK_SET); + part = xps_new_part(ctx, name, size); + fread(part->data, 1, size, file); + fclose(file); + return part; + } + + /* Count the number of pieces and their total size */ + count = 0; + size = 0; + while (1) + { + sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, count); + file = fopen(buf, "rb"); + if (!file) + { + sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, count); + file = fopen(buf, "rb"); + } + if (!file) + break; + count ++; + fseek(file, 0, SEEK_END); + size += ftell(file); + fclose(file); + } + + /* Inflate the pieces */ + if (count) + { + part = xps_new_part(ctx, name, size); + offset = 0; + for (i = 0; i < count; i++) + { + if (i < count - 1) + sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, i); + else + sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, i); + file = fopen(buf, "rb"); + n = fread(part->data + offset, 1, size - offset, file); + offset += n; + fclose(file); + } + return part; + } + + return NULL; +} + +xps_part * +xps_read_part(xps_context *ctx, char *partname) +{ + if (ctx->directory) + return xps_read_dir_part(ctx, partname); + return xps_read_zip_part(ctx, partname); +} + +/* + * Read and process the XPS document. + */ + +static int +xps_read_and_process_metadata_part(xps_context *ctx, char *name) +{ + xps_part *part; + int code; + + part = xps_read_part(ctx, name); + if (!part) + return fz_rethrow(-1, "cannot read zip part '%s'", name); + + code = xps_parse_metadata(ctx, part); + if (code) + return fz_rethrow(code, "cannot process metadata part '%s'", name); + + xps_free_part(ctx, part); + + return fz_okay; +} + +/* + * Called by xpstop.c + */ + +int +xps_open_file(xps_context *ctx, char *filename) +{ + char buf[2048]; + xps_document *doc; + int code; + char *p; + + ctx->file = fopen(filename, "rb"); + if (!ctx->file) + return fz_throw("cannot open file: '%s'", filename); + + if (strstr(filename, "/_rels/.rels") || strstr(filename, "\\_rels\\.rels")) + { + fz_strlcpy(buf, filename, sizeof buf); + p = strstr(buf, "/_rels/.rels"); + if (!p) + p = strstr(buf, "\\_rels\\.rels"); + *p = 0; + ctx->directory = fz_strdup(buf); + } + else + { + code = xps_find_and_read_zip_dir(ctx); + if (code < 0) + return fz_rethrow(code, "cannot read zip central directory"); + } + + code = xps_read_and_process_metadata_part(ctx, "/_rels/.rels"); + if (code) + return fz_rethrow(code, "cannot process root relationship part"); + + if (!ctx->start_part) + return fz_throw("cannot find fixed document sequence start part"); + + code = xps_read_and_process_metadata_part(ctx, ctx->start_part); + if (code) + return fz_rethrow(code, "cannot process FixedDocumentSequence part"); + + for (doc = ctx->first_fixdoc; doc; doc = doc->next) + { + code = xps_read_and_process_metadata_part(ctx, doc->name); + if (code) + return fz_rethrow(code, "cannot process FixedDocument part"); + } + + return fz_okay; +} + +int +xps_count_pages(xps_context *ctx) +{ + xps_page *page; + int n = 0; + for (page = ctx->first_page; page; page = page->next) + n ++; + return n; +} + +xps_page * +xps_load_page(xps_context *ctx, int number) +{ + xps_page *page; + int code; + int n = 0; + + for (page = ctx->first_page; page; page = page->next) + { + if (n == number) + { + if (!page->root) + { + code = xps_load_fixed_page(ctx, page); + if (code) { + fz_rethrow(code, "cannot load page %d", number + 1); + return NULL; + } + } + return page; + } + n ++; + } + return nil; +} + +void +xps_free_page(xps_context *ctx, xps_page *page) +{ + if (page->root) + xml_free_element(page->root); + page->root = NULL; +} + +xps_context * +xps_new_context(void) +{ + xps_context *ctx; + + ctx = fz_malloc(sizeof(xps_context)); + + memset(ctx, 0, sizeof(xps_context)); + + ctx->font_table = xps_hash_new(ctx); + ctx->colorspace_table = xps_hash_new(ctx); + + ctx->start_part = NULL; + + return ctx; +} + +static void xps_free_key_func(xps_context *ctx, void *ptr) +{ + fz_free(ptr); +} + +static void xps_free_font_func(xps_context *ctx, void *ptr) +{ + fz_dropfont(ptr); +} + +/* Wrap up interp instance after a "job" */ +int +xps_free_context(xps_context *ctx) +{ + int i; + + if (ctx->file) + fclose(ctx->file); + + for (i = 0; i < ctx->zip_count; i++) + fz_free(ctx->zip_table[i].name); + fz_free(ctx->zip_table); + + /* TODO: free resources too */ + xps_hash_free(ctx, ctx->font_table, xps_free_key_func, xps_free_font_func); + xps_hash_free(ctx, ctx->colorspace_table, xps_free_key_func, NULL); + + xps_free_fixed_pages(ctx); + xps_free_fixed_documents(ctx); + + return 0; +} diff --git a/xps/xpscommon.c b/xps/xpscommon.c deleted file mode 100644 index 7e489ef7..00000000 --- a/xps/xpscommon.c +++ /dev/null @@ -1,282 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -static inline int unhex(int a) -{ - if (a >= 'A' && a <= 'F') return a - 'A' + 0xA; - if (a >= 'a' && a <= 'f') return a - 'a' + 0xA; - if (a >= '0' && a <= '9') return a - '0'; - return 0; -} - -void -xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node) -{ - /* SolidColorBrushes are handled in a special case and will never show up here */ - if (!strcmp(xml_tag(node), "ImageBrush")) - xps_parse_image_brush(ctx, ctm, area, base_uri, dict, node); - else if (!strcmp(xml_tag(node), "VisualBrush")) - xps_parse_visual_brush(ctx, ctm, area, base_uri, dict, node); - else if (!strcmp(xml_tag(node), "LinearGradientBrush")) - xps_parse_linear_gradient_brush(ctx, ctm, area, base_uri, dict, node); - else if (!strcmp(xml_tag(node), "RadialGradientBrush")) - xps_parse_radial_gradient_brush(ctx, ctm, area, base_uri, dict, node); - else - fz_warn("unknown brush tag: %s", xml_tag(node)); -} - -void -xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node) -{ - if (!strcmp(xml_tag(node), "Path")) - xps_parse_path(ctx, ctm, base_uri, dict, node); - if (!strcmp(xml_tag(node), "Glyphs")) - xps_parse_glyphs(ctx, ctm, base_uri, dict, node); - if (!strcmp(xml_tag(node), "Canvas")) - xps_parse_canvas(ctx, ctm, base_uri, dict, node); - /* skip unknown tags (like Foo.Resources and similar) */ -} - -void -xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, - char *opacity_att, xml_element *opacity_mask_tag) -{ - float opacity; - - if (!opacity_att && !opacity_mask_tag) - return; - - opacity = 1.0; - if (opacity_att) - opacity = atof(opacity_att); - - if (opacity_mask_tag && !strcmp(xml_tag(opacity_mask_tag), "SolidColorBrush")) - { - char *scb_opacity_att = xml_att(opacity_mask_tag, "Opacity"); - char *scb_color_att = xml_att(opacity_mask_tag, "Color"); - if (scb_opacity_att) - opacity = opacity * atof(scb_opacity_att); - if (scb_color_att) - { - fz_colorspace *colorspace; - float samples[32]; - xps_parse_color(ctx, base_uri, scb_color_att, &colorspace, samples); - opacity = opacity * samples[0]; - } - opacity_mask_tag = NULL; - } - - if (ctx->opacity_top + 1 < nelem(ctx->opacity)) - { - ctx->opacity[ctx->opacity_top + 1] = ctx->opacity[ctx->opacity_top] * opacity; - ctx->opacity_top++; - } - - if (opacity_mask_tag) - { - ctx->dev->beginmask(ctx->dev->user, area, 0, NULL, NULL); - xps_parse_brush(ctx, ctm, area, base_uri, dict, opacity_mask_tag); - ctx->dev->endmask(ctx->dev->user); - } -} - -void -xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, - char *opacity_att, xml_element *opacity_mask_tag) -{ - if (!opacity_att && !opacity_mask_tag) - return; - - if (ctx->opacity_top > 0) - ctx->opacity_top--; - - if (opacity_mask_tag) - { - if (strcmp(xml_tag(opacity_mask_tag), "SolidColorBrush")) - ctx->dev->popclip(ctx->dev->user); - } -} - -void -xps_parse_render_transform(xps_context *ctx, char *transform, fz_matrix *matrix) -{ - float args[6]; - char *s = transform; - int i; - - args[0] = 1.0; args[1] = 0.0; - args[2] = 0.0; args[3] = 1.0; - args[4] = 0.0; args[5] = 0.0; - - for (i = 0; i < 6 && *s; i++) - { - args[i] = atof(s); - while (*s && *s != ',') - s++; - if (*s == ',') - s++; - } - - matrix->a = args[0]; matrix->b = args[1]; - matrix->c = args[2]; matrix->d = args[3]; - matrix->e = args[4]; matrix->f = args[5]; -} - -void -xps_parse_matrix_transform(xps_context *ctx, xml_element *root, fz_matrix *matrix) -{ - char *transform; - - *matrix = fz_identity; - - if (!strcmp(xml_tag(root), "MatrixTransform")) - { - transform = xml_att(root, "Matrix"); - if (transform) - xps_parse_render_transform(ctx, transform, matrix); - } -} - -void -xps_parse_rectangle(xps_context *ctx, char *text, fz_rect *rect) -{ - float args[4]; - char *s = text; - int i; - - args[0] = 0.0; args[1] = 0.0; - args[2] = 1.0; args[3] = 1.0; - - for (i = 0; i < 4 && *s; i++) - { - args[i] = atof(s); - while (*s && *s != ',') - s++; - if (*s == ',') - s++; - } - - rect->x0 = args[0]; - rect->y0 = args[1]; - rect->x1 = args[0] + args[2]; - rect->y1 = args[1] + args[3]; -} - -static int count_commas(char *s) -{ - int n = 0; - while (*s) - { - if (*s == ',') - n ++; - s ++; - } - return n; -} - -void -xps_parse_color(xps_context *ctx, char *base_uri, char *string, - fz_colorspace **csp, float *samples) -{ - char *p; - int i, n; - char buf[1024]; - char *profile; - - *csp = fz_devicergb; - - samples[0] = 1.0; - samples[1] = 0.0; - samples[2] = 0.0; - samples[3] = 0.0; - - if (string[0] == '#') - { - if (strlen(string) == 9) - { - samples[0] = unhex(string[1]) * 16 + unhex(string[2]); - samples[1] = unhex(string[3]) * 16 + unhex(string[4]); - samples[2] = unhex(string[5]) * 16 + unhex(string[6]); - samples[3] = unhex(string[7]) * 16 + unhex(string[8]); - } - else - { - samples[0] = 255.0; - samples[1] = unhex(string[1]) * 16 + unhex(string[2]); - samples[2] = unhex(string[3]) * 16 + unhex(string[4]); - samples[3] = unhex(string[5]) * 16 + unhex(string[6]); - } - - samples[0] /= 255.0; - samples[1] /= 255.0; - samples[2] /= 255.0; - samples[3] /= 255.0; - } - - else if (string[0] == 's' && string[1] == 'c' && string[2] == '#') - { - if (count_commas(string) == 2) - sscanf(string, "sc#%g,%g,%g", samples + 1, samples + 2, samples + 3); - if (count_commas(string) == 3) - sscanf(string, "sc#%g,%g,%g,%g", samples, samples + 1, samples + 2, samples + 3); - } - - else if (strstr(string, "ContextColor ") == string) - { - /* Crack the string for profile name and sample values */ - strcpy(buf, string); - - profile = strchr(buf, ' '); - if (!profile) - { - fz_warn("cannot find icc profile uri in '%s'", string); - return; - } - - *profile++ = 0; - p = strchr(profile, ' '); - if (!p) - { - fz_warn("cannot find component values in '%s'", profile); - return; - } - - *p++ = 0; - n = count_commas(p) + 1; - i = 0; - while (i < n) - { - samples[i++] = atof(p); - p = strchr(p, ','); - if (!p) - break; - p ++; - if (*p == ' ') - p ++; - } - while (i < n) - { - samples[i++] = 0.0; - } - - /* TODO: load ICC profile */ - switch (n) - { - case 2: *csp = fz_devicegray; break; - case 4: *csp = fz_devicergb; break; - case 5: *csp = fz_devicecmyk; break; - default: *csp = fz_devicegray; break; - } - } -} - -void -xps_set_color(xps_context *ctx, fz_colorspace *colorspace, float *samples) -{ - int i; - ctx->colorspace = colorspace; - for (i = 0; i < colorspace->n; i++) - ctx->color[i] = samples[i + 1]; - ctx->alpha = samples[0] * ctx->opacity[ctx->opacity_top]; -} diff --git a/xps/xpsdoc.c b/xps/xpsdoc.c deleted file mode 100644 index 94fb32d7..00000000 --- a/xps/xpsdoc.c +++ /dev/null @@ -1,228 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -xps_part * -xps_new_part(xps_context *ctx, char *name, int size) -{ - xps_part *part; - - part = fz_malloc(sizeof(xps_part)); - part->name = fz_strdup(name); - part->size = size; - part->data = fz_malloc(size + 1); - part->data[size] = 0; /* null-terminate for xml parser */ - - return part; -} - -void -xps_free_part(xps_context *ctx, xps_part *part) -{ - fz_free(part->name); - fz_free(part->data); - fz_free(part); -} - -/* - * The FixedDocumentSequence and FixedDocument parts determine - * which parts correspond to actual pages, and the page order. - */ - -void -xps_debug_fixdocseq(xps_context *ctx) -{ - xps_document *fixdoc = ctx->first_fixdoc; - xps_page *page = ctx->first_page; - - if (ctx->start_part) - printf("start part %s\n", ctx->start_part); - - while (fixdoc) - { - printf("fixdoc %s\n", fixdoc->name); - fixdoc = fixdoc->next; - } - - while (page) - { - printf("page %s w=%d h=%d\n", page->name, page->width, page->height); - page = page->next; - } -} - -static void -xps_add_fixed_document(xps_context *ctx, char *name) -{ - xps_document *fixdoc; - - /* Check for duplicates first */ - for (fixdoc = ctx->first_fixdoc; fixdoc; fixdoc = fixdoc->next) - if (!strcmp(fixdoc->name, name)) - return; - - fixdoc = fz_malloc(sizeof(xps_document)); - fixdoc->name = fz_strdup(name); - fixdoc->next = NULL; - - if (!ctx->first_fixdoc) - { - ctx->first_fixdoc = fixdoc; - ctx->last_fixdoc = fixdoc; - } - else - { - ctx->last_fixdoc->next = fixdoc; - ctx->last_fixdoc = fixdoc; - } -} - -void -xps_free_fixed_documents(xps_context *ctx) -{ - xps_document *node = ctx->first_fixdoc; - while (node) - { - xps_document *next = node->next; - fz_free(node->name); - fz_free(node); - node = next; - } - ctx->first_fixdoc = NULL; - ctx->last_fixdoc = NULL; -} - -static void -xps_add_fixed_page(xps_context *ctx, char *name, int width, int height) -{ - xps_page *page; - - /* Check for duplicates first */ - for (page = ctx->first_page; page; page = page->next) - if (!strcmp(page->name, name)) - return; - - page = fz_malloc(sizeof(xps_page)); - page->name = fz_strdup(name); - page->width = width; - page->height = height; - page->root = NULL; - page->next = NULL; - - if (!ctx->first_page) - { - ctx->first_page = page; - ctx->last_page = page; - } - else - { - ctx->last_page->next = page; - ctx->last_page = page; - } -} - -void -xps_free_fixed_pages(xps_context *ctx) -{ - xps_page *node = ctx->first_page; - while (node) - { - xps_page *next = node->next; - fz_free(node->name); - fz_free(node); - node = next; - } - ctx->first_page = NULL; - ctx->last_page = NULL; -} - -/* - * Parse the fixed document sequence structure and _rels/.rels to find the start part. - */ - -static void -xps_parse_metadata_imp(xps_context *ctx, xml_element *item) -{ - while (item) - { - xps_parse_metadata_imp(ctx, xml_down(item)); - - if (!strcmp(xml_tag(item), "Relationship")) - { - char *target = xml_att(item, "Target"); - char *type = xml_att(item, "Type"); - if (target && type) - { - char tgtbuf[1024]; - xps_absolute_path(tgtbuf, ctx->base_uri, target, sizeof tgtbuf); - if (!strcmp(type, REL_START_PART)) - ctx->start_part = fz_strdup(tgtbuf); - } - } - - if (!strcmp(xml_tag(item), "DocumentReference")) - { - char *source = xml_att(item, "Source"); - if (source) - { - char srcbuf[1024]; - xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf); - xps_add_fixed_document(ctx, srcbuf); - } - } - - if (!strcmp(xml_tag(item), "PageContent")) - { - char *source = xml_att(item, "Source"); - char *width_att = xml_att(item, "Width"); - char *height_att = xml_att(item, "Height"); - int width = width_att ? atoi(width_att) : 0; - int height = height_att ? atoi(height_att) : 0; - if (source) - { - char srcbuf[1024]; - xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf); - xps_add_fixed_page(ctx, srcbuf, width, height); - } - } - - item = xml_next(item); - } -} - -int -xps_parse_metadata(xps_context *ctx, xps_part *part) -{ - xml_element *root; - char buf[1024]; - char *s; - - /* Save directory name part */ - fz_strlcpy(buf, part->name, sizeof buf); - s = strrchr(buf, '/'); - if (s) - s[0] = 0; - - /* _rels parts are voodoo: their URI references are from - * the part they are associated with, not the actual _rels - * part being parsed. - */ - s = strstr(buf, "/_rels"); - if (s) - *s = 0; - - ctx->base_uri = buf; - ctx->part_uri = part->name; - - root = xml_parse_document(part->data, part->size); - if (!root) - return fz_rethrow(-1, "cannot parse metadata part '%s'", part->name); - - xps_parse_metadata_imp(ctx, root); - - xml_free_element(root); - - ctx->base_uri = NULL; - ctx->part_uri = NULL; - - return fz_okay; -} diff --git a/xps/xpsglyphs.c b/xps/xpsglyphs.c deleted file mode 100644 index 91c7dbcd..00000000 --- a/xps/xpsglyphs.c +++ /dev/null @@ -1,559 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -#include -#include FT_FREETYPE_H -#include FT_ADVANCES_H - -static inline int ishex(int a) -{ - return (a >= 'A' && a <= 'F') || (a >= 'a' && a <= 'f') || (a >= '0' && a <= '9'); -} - -static inline int unhex(int a) -{ - if (a >= 'A' && a <= 'F') return a - 'A' + 0xA; - if (a >= 'a' && a <= 'f') return a - 'a' + 0xA; - if (a >= '0' && a <= '9') return a - '0'; - return 0; -} - -int -xps_count_font_encodings(fz_font *font) -{ - FT_Face face = font->ftface; - return face->num_charmaps; -} - -void -xps_identify_font_encoding(fz_font *font, int idx, int *pid, int *eid) -{ - FT_Face face = font->ftface; - *pid = face->charmaps[idx]->platform_id; - *eid = face->charmaps[idx]->encoding_id; -} - -void -xps_select_font_encoding(fz_font *font, int idx) -{ - FT_Face face = font->ftface; - FT_Set_Charmap(face, face->charmaps[idx]); -} - -int -xps_encode_font_char(fz_font *font, int code) -{ - FT_Face face = font->ftface; - int gid = FT_Get_Char_Index(face, code); - if (gid == 0 && face->charmap->platform_id == 3 && face->charmap->encoding_id == 0) - gid = FT_Get_Char_Index(face, 0xF000 | code); - return gid; -} - -void -xps_measure_font_glyph(xps_context *ctx, fz_font *font, int gid, xps_glyph_metrics *mtx) -{ - int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM; - FT_Face face = font->ftface; - FT_Fixed hadv, vadv; - - FT_Set_Char_Size(face, 64, 64, 72, 72); - FT_Get_Advance(face, gid, mask, &hadv); - FT_Get_Advance(face, gid, mask | FT_LOAD_VERTICAL_LAYOUT, &vadv); - - mtx->hadv = hadv / 65536.0f; - mtx->vadv = vadv / 65536.0f; - mtx->vorg = face->ascender / (float) face->units_per_EM; -} - -/* - * Some fonts in XPS are obfuscated by XOR:ing the first 32 bytes of the - * data with the GUID in the fontname. - */ -static void -xps_deobfuscate_font_resource(xps_context *ctx, xps_part *part) -{ - byte buf[33]; - byte key[16]; - char *p; - int i; - - p = strrchr(part->name, '/'); - if (!p) - p = part->name; - - for (i = 0; i < 32 && *p; p++) - { - if (ishex(*p)) - buf[i++] = *p; - } - buf[i] = 0; - - if (i != 32) - { - fz_warn("cannot extract GUID from obfuscated font part name"); - return; - } - - for (i = 0; i < 16; i++) - key[i] = unhex(buf[i*2+0]) * 16 + unhex(buf[i*2+1]); - - for (i = 0; i < 16; i++) - { - part->data[i] ^= key[15-i]; - part->data[i+16] ^= key[15-i]; - } -} - -static void -xps_select_best_font_encoding(fz_font *font) -{ - static struct { int pid, eid; } xps_cmap_list[] = - { - { 3, 10 }, /* Unicode with surrogates */ - { 3, 1 }, /* Unicode without surrogates */ - { 3, 5 }, /* Wansung */ - { 3, 4 }, /* Big5 */ - { 3, 3 }, /* Prc */ - { 3, 2 }, /* ShiftJis */ - { 3, 0 }, /* Symbol */ - // { 0, * }, -- Unicode (deprecated) - { 1, 0 }, - { -1, -1 }, - }; - - int i, k, n, pid, eid; - - n = xps_count_font_encodings(font); - for (k = 0; xps_cmap_list[k].pid != -1; k++) - { - for (i = 0; i < n; i++) - { - xps_identify_font_encoding(font, i, &pid, &eid); - if (pid == xps_cmap_list[k].pid && eid == xps_cmap_list[k].eid) - { - xps_select_font_encoding(font, i); - return; - } - } - } - - fz_warn("cannot find a suitable cmap"); -} - -/* - * Parse and draw an XPS element. - * - * Indices syntax: - - GlyphIndices = GlyphMapping ( ";" GlyphMapping ) - GlyphMapping = ( [ClusterMapping] GlyphIndex ) [GlyphMetrics] - ClusterMapping = "(" ClusterCodeUnitCount [":" ClusterGlyphCount] ")" - ClusterCodeUnitCount = * DIGIT - ClusterGlyphCount = * DIGIT - GlyphIndex = * DIGIT - GlyphMetrics = "," AdvanceWidth ["," uOffset ["," vOffset]] - AdvanceWidth = ["+"] RealNum - uOffset = ["+" | "-"] RealNum - vOffset = ["+" | "-"] RealNum - RealNum = ((DIGIT ["." DIGIT]) | ("." DIGIT)) [Exponent] - Exponent = ( ("E"|"e") ("+"|"-") DIGIT ) - - */ - -static char * -xps_parse_digits(char *s, int *digit) -{ - *digit = 0; - while (*s >= '0' && *s <= '9') - { - *digit = *digit * 10 + (*s - '0'); - s ++; - } - return s; -} - -static inline int is_real_num_char(int c) -{ - return (c >= '0' && c <= '9') || c == 'e' || c == 'E' || c == '+' || c == '-' || c == '.'; -} - -static char * -xps_parse_real_num(char *s, float *number) -{ - char buf[64]; - char *p = buf; - while (is_real_num_char(*s)) - *p++ = *s++; - *p = 0; - if (buf[0]) - *number = atof(buf); - return s; -} - -static char * -xps_parse_cluster_mapping(char *s, int *code_count, int *glyph_count) -{ - if (*s == '(') - s = xps_parse_digits(s + 1, code_count); - if (*s == ':') - s = xps_parse_digits(s + 1, glyph_count); - if (*s == ')') - s ++; - return s; -} - -static char * -xps_parse_glyph_index(char *s, int *glyph_index) -{ - if (*s >= '0' && *s <= '9') - s = xps_parse_digits(s, glyph_index); - return s; -} - -static char * -xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs) -{ - if (*s == ',') - s = xps_parse_real_num(s + 1, advance); - if (*s == ',') - s = xps_parse_real_num(s + 1, uofs); - if (*s == ',') - s = xps_parse_real_num(s + 1, vofs); - return s; -} - -/* - * Parse unicode and indices strings and encode glyphs. - * Calculate metrics for positioning. - */ -static fz_text * -xps_parse_glyphs_imp(xps_context *ctx, fz_matrix ctm, fz_font *font, float size, - float originx, float originy, int is_sideways, int bidi_level, - char *indices, char *unicode) -{ - xps_glyph_metrics mtx; - fz_text *text; - fz_matrix tm; - float e, f; - float x = originx; - float y = originy; - char *us = unicode; - char *is = indices; - int un = 0; - - if (!unicode && !indices) - fz_warn("glyphs element with neither characters nor indices"); - - if (us) - { - if (us[0] == '{' && us[1] == '}') - us = us + 2; - un = strlen(us); - } - - if (is_sideways) - tm = fz_concat(fz_scale(-size, size), fz_rotate(90)); - else - tm = fz_scale(size, -size); - - text = fz_newtext(font, tm, is_sideways); - - while ((us && un > 0) || (is && *is)) - { - int char_code = '?'; - int code_count = 1; - int glyph_count = 1; - - if (is && *is) - { - is = xps_parse_cluster_mapping(is, &code_count, &glyph_count); - } - - if (code_count < 1) - code_count = 1; - if (glyph_count < 1) - glyph_count = 1; - - /* TODO: add code chars with cluster mappings for proper text extraction */ - - while (code_count--) - { - if (us && un > 0) - { - int t = chartorune(&char_code, us); - us += t; un -= t; - } - } - - while (glyph_count--) - { - int glyph_index = -1; - float u_offset = 0.0; - float v_offset = 0.0; - float advance; - - if (is && *is) - is = xps_parse_glyph_index(is, &glyph_index); - - if (glyph_index == -1) - glyph_index = xps_encode_font_char(font, char_code); - - xps_measure_font_glyph(ctx, font, glyph_index, &mtx); - if (is_sideways) - advance = mtx.vadv * 100.0; - else if (bidi_level & 1) - advance = -mtx.hadv * 100.0; - else - advance = mtx.hadv * 100.0; - - if (is && *is) - { - is = xps_parse_glyph_metrics(is, &advance, &u_offset, &v_offset); - if (*is == ';') - is ++; - } - - if (bidi_level & 1) - u_offset = -mtx.hadv * 100 - u_offset; - - u_offset = u_offset * 0.01 * size; - v_offset = v_offset * 0.01 * size; - - if (is_sideways) - { - e = x + u_offset + (mtx.vorg * size); - f = y - v_offset + (mtx.hadv * 0.5 * size); - } - else - { - e = x + u_offset; - f = y - v_offset; - } - - fz_addtext(text, glyph_index, char_code, e, f); - - x += advance * 0.01 * size; - } - } - - return text; -} - -void -xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, - char *base_uri, xps_resource *dict, xml_element *root) -{ - xml_element *node; - int code; - - char *fill_uri; - char *opacity_mask_uri; - - char *bidi_level_att; - char *caret_stops_att; - char *fill_att; - char *font_size_att; - char *font_uri_att; - char *origin_x_att; - char *origin_y_att; - char *is_sideways_att; - char *indices_att; - char *unicode_att; - char *style_att; - char *transform_att; - char *clip_att; - char *opacity_att; - char *opacity_mask_att; - - xml_element *transform_tag = NULL; - xml_element *clip_tag = NULL; - xml_element *fill_tag = NULL; - xml_element *opacity_mask_tag = NULL; - - char *fill_opacity_att = NULL; - - xps_part *part; - fz_font *font; - - char partname[1024]; - char *subfont; - - float font_size = 10.0; - int subfontid = 0; - int is_sideways = 0; - int bidi_level = 0; - - fz_text *text; - fz_rect area; - - /* - * Extract attributes and extended attributes. - */ - - bidi_level_att = xml_att(root, "BidiLevel"); - caret_stops_att = xml_att(root, "CaretStops"); - fill_att = xml_att(root, "Fill"); - font_size_att = xml_att(root, "FontRenderingEmSize"); - font_uri_att = xml_att(root, "FontUri"); - origin_x_att = xml_att(root, "OriginX"); - origin_y_att = xml_att(root, "OriginY"); - is_sideways_att = xml_att(root, "IsSideways"); - indices_att = xml_att(root, "Indices"); - unicode_att = xml_att(root, "UnicodeString"); - style_att = xml_att(root, "StyleSimulations"); - transform_att = xml_att(root, "RenderTransform"); - clip_att = xml_att(root, "Clip"); - opacity_att = xml_att(root, "Opacity"); - opacity_mask_att = xml_att(root, "OpacityMask"); - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "Glyphs.RenderTransform")) - transform_tag = xml_down(node); - if (!strcmp(xml_tag(node), "Glyphs.OpacityMask")) - opacity_mask_tag = xml_down(node); - if (!strcmp(xml_tag(node), "Glyphs.Clip")) - clip_tag = xml_down(node); - if (!strcmp(xml_tag(node), "Glyphs.Fill")) - fill_tag = xml_down(node); - } - - fill_uri = base_uri; - opacity_mask_uri = base_uri; - - xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); - xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); - xps_resolve_resource_reference(ctx, dict, &fill_att, &fill_tag, &fill_uri); - xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); - - /* - * Check that we have all the necessary information. - */ - - if (!font_size_att || !font_uri_att || !origin_x_att || !origin_y_att) { - fz_warn("missing attributes in glyphs element"); - return; - } - - if (!indices_att && !unicode_att) - return; /* nothing to draw */ - - if (is_sideways_att) - is_sideways = !strcmp(is_sideways_att, "true"); - - if (bidi_level_att) - bidi_level = atoi(bidi_level_att); - - /* - * Find and load the font resource - */ - - xps_absolute_path(partname, base_uri, font_uri_att, sizeof partname); - subfont = strrchr(partname, '#'); - if (subfont) - { - subfontid = atoi(subfont + 1); - *subfont = 0; - } - - font = xps_hash_lookup(ctx->font_table, partname); - if (!font) - { - part = xps_read_part(ctx, partname); - if (!part) { - fz_warn("cannot find font resource part '%s'", partname); - return; - } - - /* deobfuscate if necessary */ - if (strstr(part->name, ".odttf")) - xps_deobfuscate_font_resource(ctx, part); - if (strstr(part->name, ".ODTTF")) - xps_deobfuscate_font_resource(ctx, part); - - code = fz_newfontfrombuffer(&font, part->data, part->size, subfontid); - if (code) { - fz_catch(code, "cannot load font resource '%s'", partname); - xps_free_part(ctx, part); - return; - } - - xps_select_best_font_encoding(font); - - xps_hash_insert(ctx, ctx->font_table, part->name, font); - - /* NOTE: we kept part->name in the hashtable and part->data in the font */ - fz_free(part); - } - - /* - * Set up graphics state. - */ - - if (transform_att || transform_tag) - { - fz_matrix transform; - if (transform_att) - xps_parse_render_transform(ctx, transform_att, &transform); - if (transform_tag) - xps_parse_matrix_transform(ctx, transform_tag, &transform); - ctm = fz_concat(transform, ctm); - } - - if (clip_att || clip_tag) - xps_clip(ctx, ctm, dict, clip_att, clip_tag); - - font_size = atof(font_size_att); - - text = xps_parse_glyphs_imp(ctx, ctm, font, font_size, - atof(origin_x_att), atof(origin_y_att), - is_sideways, bidi_level, indices_att, unicode_att); - - area = fz_boundtext(text, ctm); - - xps_begin_opacity(ctx, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - - /* - * If it's a solid color brush fill/stroke do a simple fill - */ - - if (fill_tag && !strcmp(xml_tag(fill_tag), "SolidColorBrush")) - { - fill_opacity_att = xml_att(fill_tag, "Opacity"); - fill_att = xml_att(fill_tag, "Color"); - fill_tag = NULL; - } - - if (fill_att) - { - float samples[32]; - fz_colorspace *colorspace; - - xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples); - if (fill_opacity_att) - samples[0] = atof(fill_opacity_att); - xps_set_color(ctx, colorspace, samples); - - ctx->dev->filltext(ctx->dev->user, text, ctm, - ctx->colorspace, ctx->color, ctx->alpha); - } - - /* - * If it's a visual brush or image, use the charpath as a clip mask to paint brush - */ - - if (fill_tag) - { - ctx->dev->cliptext(ctx->dev->user, text, ctm, 0); - xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag); - ctx->dev->popclip(ctx->dev->user); - } - - xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - - fz_freetext(text); - - if (clip_att || clip_tag) - ctx->dev->popclip(ctx->dev->user); -} diff --git a/xps/xpsgradient.c b/xps/xpsgradient.c deleted file mode 100644 index 8c877a0b..00000000 --- a/xps/xpsgradient.c +++ /dev/null @@ -1,460 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -#define MAX_STOPS 256 - -enum { SPREAD_PAD, SPREAD_REPEAT, SPREAD_REFLECT }; - -/* - * Parse a list of GradientStop elements. - * Fill the offset and color arrays, and - * return the number of stops parsed. - */ - -struct stop -{ - float offset; - float r, g, b, a; -}; - -static int cmp_stop(const void *a, const void *b) -{ - const struct stop *astop = a; - const struct stop *bstop = b; - float diff = astop->offset - bstop->offset; - if (diff < 0) - return -1; - if (diff > 0) - return 1; - return 0; -} - -static inline float lerp(float a, float b, float x) -{ - return a + (b - a) * x; -} - -static int -xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xml_element *node, - struct stop *stops, int maxcount) -{ - fz_colorspace *colorspace; - float sample[8]; - float rgb[3]; - int before, after; - int count; - int i; - - /* We may have to insert 2 extra stops when postprocessing */ - maxcount -= 2; - - count = 0; - while (node && count < maxcount) - { - if (!strcmp(xml_tag(node), "GradientStop")) - { - char *offset = xml_att(node, "Offset"); - char *color = xml_att(node, "Color"); - if (offset && color) - { - stops[count].offset = atof(offset); - - xps_parse_color(ctx, base_uri, color, &colorspace, sample); - - fz_convertcolor(colorspace, sample + 1, fz_devicergb, rgb); - - stops[count].r = rgb[0]; - stops[count].g = rgb[1]; - stops[count].b = rgb[2]; - stops[count].a = sample[0]; - - count ++; - } - } - node = xml_next(node); - } - - if (count == 0) - { - fz_warn("gradient brush has no gradient stops"); - stops[0].offset = 0; - stops[0].r = 0; - stops[0].g = 0; - stops[0].b = 0; - stops[0].a = 1; - stops[1].offset = 1; - stops[1].r = 1; - stops[1].g = 1; - stops[1].b = 1; - stops[1].a = 1; - return 2; - } - - if (count == maxcount) - fz_warn("gradient brush exceeded maximum number of gradient stops"); - - /* Postprocess to make sure the range of offsets is 0.0 to 1.0 */ - - qsort(stops, count, sizeof(struct stop), cmp_stop); - - before = -1; - after = -1; - - for (i = 0; i < count; i++) - { - if (stops[i].offset < 0) - before = i; - if (stops[i].offset > 1) - { - after = i; - break; - } - } - - /* Remove all stops < 0 except the largest one */ - if (before > 0) - { - memmove(stops, stops + before, (count - before) * sizeof(struct stop)); - count -= before; - } - - /* Remove all stops > 1 except the smallest one */ - if (after >= 0) - count = after + 1; - - /* Expand single stop to 0 .. 1 */ - if (count == 1) - { - stops[1] = stops[0]; - stops[0].offset = 0; - stops[1].offset = 1; - return 2; - } - - /* First stop < 0 -- interpolate value to 0 */ - if (stops[0].offset < 0) - { - float d = -stops[0].offset / (stops[1].offset - stops[0].offset); - stops[0].offset = 0; - stops[0].r = lerp(stops[0].r, stops[1].r, d); - stops[0].g = lerp(stops[0].g, stops[1].g, d); - stops[0].b = lerp(stops[0].b, stops[1].b, d); - stops[0].a = lerp(stops[0].a, stops[1].a, d); - } - - /* Last stop > 1 -- interpolate value to 1 */ - if (stops[count-1].offset > 1) - { - float d = (1 - stops[count-2].offset) / (stops[count-1].offset - stops[count-2].offset); - stops[count-1].offset = 1; - stops[0].r = lerp(stops[count-2].r, stops[count-1].r, d); - stops[0].g = lerp(stops[count-2].g, stops[count-1].g, d); - stops[0].b = lerp(stops[count-2].b, stops[count-1].b, d); - stops[0].a = lerp(stops[count-2].a, stops[count-1].a, d); - } - - /* First stop > 0 -- insert a duplicate at 0 */ - if (stops[0].offset > 0) - { - memmove(stops + 1, stops, count * sizeof(struct stop)); - stops[0] = stops[1]; - stops[0].offset = 0; - count++; - } - - /* Last stop < 1 -- insert a duplicate at 1 */ - if (stops[count-1].offset < 1) - { - stops[count] = stops[count-1]; - stops[count].offset = 1; - count++; - } - - return count; -} - -static void -xps_sample_gradient_stops(fz_shade *shade, struct stop *stops, int count) -{ - float offset, d; - int i, k; - - k = 0; - for (i = 0; i < 256; i++) - { - offset = i / 255.0f; - while (k + 1 < count && offset > stops[k+1].offset) - k++; - - d = (offset - stops[k].offset) / (stops[k+1].offset - stops[k].offset); - - shade->function[i][0] = lerp(stops[k].r, stops[k+1].r, d); - shade->function[i][1] = lerp(stops[k].g, stops[k+1].g, d); - shade->function[i][2] = lerp(stops[k].b, stops[k+1].b, d); - shade->function[i][3] = lerp(stops[k].a, stops[k+1].a, d); - } -} - -/* - * Radial gradients map more or less to Radial shadings. - * The inner circle is always a point. - * The outer circle is actually an ellipse, - * mess with the transform to squash the circle into the right aspect. - */ - -static void -xps_draw_one_radial_gradient(xps_context *ctx, fz_matrix ctm, - struct stop *stops, int count, - int extend, - float x0, float y0, float r0, - float x1, float y1, float r1) -{ - fz_shade *shade; - - /* TODO: this (and the stuff in pdf_shade) should move to res_shade.c */ - shade = fz_malloc(sizeof(fz_shade)); - shade->refs = 1; - shade->cs = fz_devicergb; - shade->bbox = fz_infiniterect; - shade->matrix = fz_identity; - shade->usebackground = 0; - shade->usefunction = 1; - shade->type = FZ_RADIAL; - shade->extend[0] = extend; - shade->extend[1] = extend; - - xps_sample_gradient_stops(shade, stops, count); - - shade->meshlen = 6; - shade->meshcap = 6; - shade->mesh = fz_calloc(shade->meshcap, sizeof(float)); - shade->mesh[0] = x0; - shade->mesh[1] = y0; - shade->mesh[2] = r0; - shade->mesh[3] = x1; - shade->mesh[4] = y1; - shade->mesh[5] = r1; - - ctx->dev->fillshade(ctx->dev->user, shade, ctm, 1); - - fz_dropshade(shade); -} - -/* - * Linear gradients map to Axial shadings. - */ - -static void -xps_draw_one_linear_gradient(xps_context *ctx, fz_matrix ctm, - struct stop *stops, int count, - int extend, - float x0, float y0, float x1, float y1) -{ - fz_shade *shade; - - /* TODO: this (and the stuff in pdf_shade) should move to res_shade.c */ - shade = fz_malloc(sizeof(fz_shade)); - shade->refs = 1; - shade->cs = fz_devicergb; - shade->bbox = fz_infiniterect; - shade->matrix = fz_identity; - shade->usebackground = 0; - shade->usefunction = 1; - shade->type = FZ_LINEAR; - shade->extend[0] = extend; - shade->extend[1] = extend; - - xps_sample_gradient_stops(shade, stops, count); - - shade->meshlen = 6; - shade->meshcap = 6; - shade->mesh = fz_calloc(shade->meshcap, sizeof(float)); - shade->mesh[0] = x0; - shade->mesh[1] = y0; - shade->mesh[2] = 0; - shade->mesh[3] = x1; - shade->mesh[4] = y1; - shade->mesh[5] = 0; - - ctx->dev->fillshade(ctx->dev->user, shade, ctm, 1); - - fz_dropshade(shade); -} - -/* - * We need to loop and create many shading objects to account - * for the Repeat and Reflect SpreadMethods. - * I'm not smart enough to calculate this analytically - * so we iterate and check each object until we - * reach a reasonable limit for infinite cases. - */ - -static inline float point_inside_circle(float px, float py, float x, float y, float r) -{ - float dx = px - x; - float dy = py - y; - return dx * dx + dy * dy <= r * r; -} - -static void -xps_draw_radial_gradient(xps_context *ctx, fz_matrix ctm, - struct stop *stops, int count, - xml_element *root, int spread) -{ - float x0, y0, r0; - float x1, y1, r1; - float xrad = 1; - float yrad = 1; - float invscale; - - char *center_att = xml_att(root, "Center"); - char *origin_att = xml_att(root, "GradientOrigin"); - char *radius_x_att = xml_att(root, "RadiusX"); - char *radius_y_att = xml_att(root, "RadiusY"); - - if (origin_att) - sscanf(origin_att, "%g,%g", &x0, &y0); - if (center_att) - sscanf(center_att, "%g,%g", &x1, &y1); - if (radius_x_att) - xrad = atof(radius_x_att); - if (radius_y_att) - yrad = atof(radius_y_att); - - /* scale the ctm to make ellipses */ - ctm = fz_concat(fz_scale(1.0, yrad / xrad), ctm); - - invscale = xrad / yrad; - y0 = y0 * invscale; - y1 = y1 * invscale; - - r0 = 0.0; - r1 = xrad; - - xps_draw_one_radial_gradient(ctx, ctm, stops, count, 1, x0, y0, r0, x1, y1, r1); -} - -/* - * Calculate how many iterations are needed to cover - * the bounding box. - */ - -static void -xps_draw_linear_gradient(xps_context *ctx, fz_matrix ctm, - struct stop *stops, int count, - xml_element *root, int spread) -{ - float x0, y0, x1, y1; - - char *start_point_att = xml_att(root, "StartPoint"); - char *end_point_att = xml_att(root, "EndPoint"); - - x0 = y0 = 0; - x1 = y1 = 1; - - if (start_point_att) - sscanf(start_point_att, "%g,%g", &x0, &y0); - if (end_point_att) - sscanf(end_point_att, "%g,%g", &x1, &y1); - - xps_draw_one_linear_gradient(ctx, ctm, stops, count, 1, x0, y0, x1, y1); -} - -/* - * Parse XML tag and attributes for a gradient brush, create color/opacity - * function objects and call gradient drawing primitives. - */ - -static void -xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, xml_element *root, - void (*draw)(xps_context *, fz_matrix, struct stop *, int, xml_element *, int)) -{ - xml_element *node; - - char *opacity_att; - char *interpolation_att; - char *spread_att; - char *mapping_att; - char *transform_att; - - xml_element *transform_tag = NULL; - xml_element *stop_tag = NULL; - - struct stop stop_list[MAX_STOPS]; - int stop_count; - fz_matrix transform; - int spread_method; - - opacity_att = xml_att(root, "Opacity"); - interpolation_att = xml_att(root, "ColorInterpolationMode"); - spread_att = xml_att(root, "SpreadMethod"); - mapping_att = xml_att(root, "MappingMode"); - transform_att = xml_att(root, "Transform"); - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "LinearGradientBrush.Transform")) - transform_tag = xml_down(node); - if (!strcmp(xml_tag(node), "RadialGradientBrush.Transform")) - transform_tag = xml_down(node); - if (!strcmp(xml_tag(node), "LinearGradientBrush.GradientStops")) - stop_tag = xml_down(node); - if (!strcmp(xml_tag(node), "RadialGradientBrush.GradientStops")) - stop_tag = xml_down(node); - } - - xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); - - spread_method = SPREAD_PAD; - if (spread_att) - { - if (!strcmp(spread_att, "Pad")) - spread_method = SPREAD_PAD; - if (!strcmp(spread_att, "Reflect")) - spread_method = SPREAD_REFLECT; - if (!strcmp(spread_att, "Repeat")) - spread_method = SPREAD_REPEAT; - } - - transform = fz_identity; - if (transform_att) - xps_parse_render_transform(ctx, transform_att, &transform); - if (transform_tag) - xps_parse_matrix_transform(ctx, transform_tag, &transform); - ctm = fz_concat(transform, ctm); - - if (!stop_tag) { - fz_warn("missing gradient stops tag"); - return; - } - - stop_count = xps_parse_gradient_stops(ctx, base_uri, stop_tag, stop_list, MAX_STOPS); - if (stop_count == 0) - { - fz_warn("no gradient stops found"); - return; - } - - area = fz_transformrect(ctm, area); - - xps_begin_opacity(ctx, ctm, area, base_uri, dict, opacity_att, NULL); - - draw(ctx, ctm, stop_list, stop_count, root, spread_method); - - xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); -} - -void -xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, xml_element *root) -{ - xps_parse_gradient_brush(ctx, ctm, area, base_uri, dict, root, xps_draw_linear_gradient); -} - -void -xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, xml_element *root) -{ - xps_parse_gradient_brush(ctx, ctm, area, base_uri, dict, root, xps_draw_radial_gradient); -} diff --git a/xps/xpshash.c b/xps/xpshash.c deleted file mode 100644 index 71e97097..00000000 --- a/xps/xpshash.c +++ /dev/null @@ -1,190 +0,0 @@ -/* Linear probe hash table. - * - * Simple hashtable with open adressing linear probe. - * Does not manage memory of key/value pointers. - * Does not support deleting entries. - */ - -#include "fitz.h" -#include "muxps.h" - -static const unsigned primes[] = -{ - 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, - 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, 0 -}; - -typedef struct xps_hash_entry_s xps_hash_entry; - -struct xps_hash_entry_s -{ - char *key; - void *value; -}; - -struct xps_hash_table_s -{ - void *ctx; - unsigned int size; - unsigned int load; - xps_hash_entry *entries; -}; - -static inline int -xps_tolower(int c) -{ - if (c >= 'A' && c <= 'Z') - return c + 32; - return c; -} - -static unsigned int -xps_hash(char *s) -{ - unsigned int h = 0; - while (*s) - h = xps_tolower(*s++) + (h << 6) + (h << 16) - h; - return h; -} - -xps_hash_table * -xps_hash_new(xps_context *ctx) -{ - xps_hash_table *table; - - table = fz_malloc(sizeof(xps_hash_table)); - table->size = primes[0]; - table->load = 0; - - table->entries = fz_calloc(table->size, sizeof(xps_hash_entry)); - memset(table->entries, 0, table->size * sizeof(xps_hash_entry)); - - return table; -} - -static int -xps_hash_double(xps_context *ctx, xps_hash_table *table) -{ - xps_hash_entry *old_entries; - xps_hash_entry *new_entries; - unsigned int old_size = table->size; - unsigned int new_size = table->size * 2; - int i; - - for (i = 0; primes[i] != 0; i++) - { - if (primes[i] > old_size) - { - new_size = primes[i]; - break; - } - } - - old_entries = table->entries; - new_entries = fz_calloc(new_size, sizeof(xps_hash_entry)); - - table->size = new_size; - table->entries = new_entries; - table->load = 0; - - memset(table->entries, 0, table->size * sizeof(xps_hash_entry)); - - for (i = 0; i < old_size; i++) - if (old_entries[i].value) - xps_hash_insert(ctx, table, old_entries[i].key, old_entries[i].value); - - fz_free(old_entries); - - return 0; -} - -void -xps_hash_free(xps_context *ctx, xps_hash_table *table, - void (*free_key)(xps_context *ctx, void *), - void (*free_value)(xps_context *ctx, void *)) -{ - int i; - - for (i = 0; i < table->size; i++) - { - if (table->entries[i].key && free_key) - free_key(ctx, table->entries[i].key); - if (table->entries[i].value && free_value) - free_value(ctx, table->entries[i].value); - } - - fz_free(table->entries); - fz_free(table); -} - -void * -xps_hash_lookup(xps_hash_table *table, char *key) -{ - xps_hash_entry *entries = table->entries; - unsigned int size = table->size; - unsigned int pos = xps_hash(key) % size; - - while (1) - { - if (!entries[pos].value) - return NULL; - - if (xps_strcasecmp(key, entries[pos].key) == 0) - return entries[pos].value; - - pos = (pos + 1) % size; - } -} - -int -xps_hash_insert(xps_context *ctx, xps_hash_table *table, char *key, void *value) -{ - xps_hash_entry *entries; - unsigned int size, pos; - - /* Grow the table at 80% load */ - if (table->load > table->size * 8 / 10) - { - if (xps_hash_double(ctx, table) < 0) - return fz_rethrow(-1, "cannot grow hash table"); - } - - entries = table->entries; - size = table->size; - pos = xps_hash(key) % size; - - while (1) - { - if (!entries[pos].value) - { - entries[pos].key = key; - entries[pos].value = value; - table->load ++; - return 0; - } - - if (xps_strcasecmp(key, entries[pos].key) == 0) - { - return 0; - } - - pos = (pos + 1) % size; - } -} - -void -xps_hash_debug(xps_hash_table *table) -{ - int i; - - printf("hash table load %d / %d\n", table->load, table->size); - - for (i = 0; i < table->size; i++) - { - if (!table->entries[i].value) - printf("table % 4d: empty\n", i); - else - printf("table % 4d: key=%s value=%p\n", i, - table->entries[i].key, table->entries[i].value); - } -} diff --git a/xps/xpsimage.c b/xps/xpsimage.c deleted file mode 100644 index ced58bb5..00000000 --- a/xps/xpsimage.c +++ /dev/null @@ -1,137 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -static int -xps_decode_image(xps_image **imagep, xps_context *ctx, xps_part *part) -{ - byte *buf = part->data; - int len = part->size; - int error; - - if (len < 8) - return fz_throw("unknown image file format"); - - if (buf[0] == 0xff && buf[1] == 0xd8) - { - error = xps_decode_jpeg(imagep, ctx, buf, len); - if (error) - return fz_rethrow(error, "cannot decode jpeg image"); - } - else if (memcmp(buf, "\211PNG\r\n\032\n", 8) == 0) - { - error = xps_decode_png(imagep, ctx, buf, len); - if (error) - return fz_rethrow(error, "cannot decode png image"); - } - else if (memcmp(buf, "II", 2) == 0 && buf[2] == 0xBC) - { - return fz_throw("JPEG-XR codec is not available"); - } - else if (memcmp(buf, "MM", 2) == 0 || memcmp(buf, "II", 2) == 0) - { - error = xps_decode_tiff(imagep, ctx, buf, len); - if (error) - return fz_rethrow(error, "cannot decode TIFF image"); - } - else - return fz_throw("unknown image file format"); - - return fz_okay; -} - -static void -xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root, void *vimage) -{ - xps_image *image = vimage; - fz_pixmap *pixmap = image->pixmap; - float xs = pixmap->w * 96.0 / image->xres; - float ys = pixmap->h * 96.0 / image->yres; - fz_matrix im = fz_scale(xs, -ys); - im.f = ys; - ctm = fz_concat(im, ctm); - ctx->dev->fillimage(ctx->dev->user, pixmap, ctm, 1.0); -} - -static xps_part * -xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xml_element *root) -{ - char *image_source_att; - char buf[1024]; - char partname[1024]; - char *image_name; - char *profile_name; - char *p; - - image_source_att = xml_att(root, "ImageSource"); - if (!image_source_att) - return NULL; - - /* "{ColorConvertedBitmap /Resources/Image.tiff /Resources/Profile.icc}" */ - if (strstr(image_source_att, "{ColorConvertedBitmap") == image_source_att) - { - image_name = NULL; - profile_name = NULL; - - fz_strlcpy(buf, image_source_att, sizeof buf); - p = strchr(buf, ' '); - if (p) - { - image_name = p + 1; - p = strchr(p + 1, ' '); - if (p) - { - *p = 0; - profile_name = p + 1; - p = strchr(p + 1, '}'); - if (p) - *p = 0; - } - } - } - else - { - image_name = image_source_att; - profile_name = NULL; - } - - if (!image_name) - return NULL; - - xps_absolute_path(partname, base_uri, image_name, sizeof partname); - - return xps_read_part(ctx, partname); -} - -void -xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, xml_element *root) -{ - xps_part *part; - xps_image *image; - int code; - - part = xps_find_image_brush_source_part(ctx, base_uri, root); - if (!part) { - fz_warn("cannot find image source"); - return; - } - - code = xps_decode_image(&image, ctx, part); - if (code < 0) { - fz_catch(-1, "cannot decode image resource"); - return; - } - - xps_parse_tiling_brush(ctx, ctm, area, base_uri, dict, root, xps_paint_image_brush, image); - - xps_free_image(ctx, image); - xps_free_part(ctx, part); -} - -void -xps_free_image(xps_context *ctx, xps_image *image) -{ - if (image->pixmap) - fz_droppixmap(image->pixmap); - fz_free(image); -} diff --git a/xps/xpsjpeg.c b/xps/xpsjpeg.c deleted file mode 100644 index 4dc2a7e4..00000000 --- a/xps/xpsjpeg.c +++ /dev/null @@ -1,134 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -#include -#include - -struct jpeg_error_mgr_jmp -{ - struct jpeg_error_mgr super; - jmp_buf env; - char msg[JMSG_LENGTH_MAX]; -}; - -static void error_exit(j_common_ptr cinfo) -{ - struct jpeg_error_mgr_jmp *err = (struct jpeg_error_mgr_jmp *)cinfo->err; - cinfo->err->format_message(cinfo, err->msg); - longjmp(err->env, 1); -} - -static void init_source(j_decompress_ptr cinfo) -{ - /* nothing to do */ -} - -static void term_source(j_decompress_ptr cinfo) -{ - /* nothing to do */ -} - -static boolean fill_input_buffer(j_decompress_ptr cinfo) -{ - static unsigned char eoi[2] = { 0xFF, JPEG_EOI }; - struct jpeg_source_mgr *src = cinfo->src; - src->next_input_byte = eoi; - src->bytes_in_buffer = 2; - return 1; -} - -static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) -{ - struct jpeg_source_mgr *src = cinfo->src; - if (num_bytes > 0) - { - src->next_input_byte += num_bytes; - src->bytes_in_buffer -= num_bytes; - } -} - -int -xps_decode_jpeg(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen) -{ - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr_jmp err; - struct jpeg_source_mgr src; - unsigned char *row[1], *sp, *dp; - fz_colorspace *colorspace; - int x, k; - - xps_image *image = NULL; - - if (setjmp(err.env)) - { - if (image) - xps_free_image(ctx, image); - return fz_throw("jpeg error: %s", err.msg); - } - - cinfo.err = jpeg_std_error(&err.super); - err.super.error_exit = error_exit; - - jpeg_create_decompress(&cinfo); - - cinfo.src = &src; - src.init_source = init_source; - src.fill_input_buffer = fill_input_buffer; - src.skip_input_data = skip_input_data; - src.resync_to_restart = jpeg_resync_to_restart; - src.term_source = term_source; - src.next_input_byte = rbuf; - src.bytes_in_buffer = rlen; - - jpeg_read_header(&cinfo, 1); - - jpeg_start_decompress(&cinfo); - - if (cinfo.output_components == 1) - colorspace = fz_devicegray; - else if (cinfo.output_components == 3) - colorspace = fz_devicergb; - else if (cinfo.output_components == 4) - colorspace = fz_devicecmyk; - else - return fz_throw("bad number of components in jpeg: %d", cinfo.output_components); - - image = fz_malloc(sizeof(xps_image)); - image->pixmap = fz_newpixmap(colorspace, 0, 0, cinfo.output_width, cinfo.output_height); - image->xres = 96; - image->yres = 96; - - if (cinfo.density_unit == 1) - { - image->xres = cinfo.X_density; - image->yres = cinfo.Y_density; - } - else if (cinfo.density_unit == 2) - { - image->xres = cinfo.X_density * 2.54; - image->yres = cinfo.Y_density * 2.54; - } - - fz_clearpixmap(image->pixmap); - - row[0] = fz_malloc(cinfo.output_components * cinfo.output_width); - dp = image->pixmap->samples; - while (cinfo.output_scanline < cinfo.output_height) - { - jpeg_read_scanlines(&cinfo, row, 1); - sp = row[0]; - for (x = 0; x < cinfo.output_width; x++) - { - for (k = 0; k < cinfo.output_components; k++) - *dp++ = *sp++; - *dp++ = 255; - } - } - fz_free(row[0]); - - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - - *imagep = image; - return fz_okay; -} diff --git a/xps/xpsmem.c b/xps/xpsmem.c deleted file mode 100644 index 76175d68..00000000 --- a/xps/xpsmem.c +++ /dev/null @@ -1,94 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -static inline int tolower(int c) -{ - if (c >= 'A' && c <= 'Z') - return c + 32; - return c; -} - -int -xps_strcasecmp(char *a, char *b) -{ - while (tolower(*a) == tolower(*b)) - { - if (*a++ == 0) - return 0; - b++; - } - return tolower(*a) - tolower(*b); -} - -#define SEP(x) ((x)=='/' || (x) == 0) - -static char * -xps_clean_path(char *name) -{ - char *p, *q, *dotdot; - int rooted; - - rooted = name[0] == '/'; - - /* - * invariants: - * p points at beginning of path element we're considering. - * q points just past the last path element we wrote (no slash). - * dotdot points just past the point where .. cannot backtrack - * any further (no slash). - */ - p = q = dotdot = name + rooted; - while (*p) - { - if(p[0] == '/') /* null element */ - p++; - else if (p[0] == '.' && SEP(p[1])) - p += 1; /* don't count the separator in case it is nul */ - else if (p[0] == '.' && p[1] == '.' && SEP(p[2])) - { - p += 2; - if (q > dotdot) /* can backtrack */ - { - while(--q > dotdot && *q != '/') - ; - } - else if (!rooted) /* /.. is / but ./../ is .. */ - { - if (q != name) - *q++ = '/'; - *q++ = '.'; - *q++ = '.'; - dotdot = q; - } - } - else /* real path element */ - { - if (q != name+rooted) - *q++ = '/'; - while ((*q = *p) != '/' && *q != 0) - p++, q++; - } - } - - if (q == name) /* empty string is really "." */ - *q++ = '.'; - *q = '\0'; - - return name; -} - -void -xps_absolute_path(char *output, char *base_uri, char *path, int output_size) -{ - if (path[0] == '/') - { - fz_strlcpy(output, path, output_size); - } - else - { - fz_strlcpy(output, base_uri, output_size); - fz_strlcat(output, "/", output_size); - fz_strlcat(output, path, output_size); - } - xps_clean_path(output); -} diff --git a/xps/xpspage.c b/xps/xpspage.c deleted file mode 100644 index 7659a1a7..00000000 --- a/xps/xpspage.c +++ /dev/null @@ -1,154 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -void -xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root) -{ - xps_resource *new_dict = NULL; - xml_element *node; - char *opacity_mask_uri; - int code; - - char *transform_att; - char *clip_att; - char *opacity_att; - char *opacity_mask_att; - - xml_element *transform_tag = NULL; - xml_element *clip_tag = NULL; - xml_element *opacity_mask_tag = NULL; - - fz_matrix transform; - - transform_att = xml_att(root, "RenderTransform"); - clip_att = xml_att(root, "Clip"); - opacity_att = xml_att(root, "Opacity"); - opacity_mask_att = xml_att(root, "OpacityMask"); - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "Canvas.Resources") && xml_down(node)) - { - code = xps_parse_resource_dictionary(ctx, &new_dict, base_uri, xml_down(node)); - if (code) - fz_catch(code, "cannot load Canvas.Resources"); - else - { - new_dict->parent = dict; - dict = new_dict; - } - } - - if (!strcmp(xml_tag(node), "Canvas.RenderTransform")) - transform_tag = xml_down(node); - if (!strcmp(xml_tag(node), "Canvas.Clip")) - clip_tag = xml_down(node); - if (!strcmp(xml_tag(node), "Canvas.OpacityMask")) - opacity_mask_tag = xml_down(node); - } - - opacity_mask_uri = base_uri; - xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); - xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); - xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); - - transform = fz_identity; - if (transform_att) - xps_parse_render_transform(ctx, transform_att, &transform); - if (transform_tag) - xps_parse_matrix_transform(ctx, transform_tag, &transform); - ctm = fz_concat(transform, ctm); - - if (clip_att || clip_tag) - xps_clip(ctx, ctm, dict, clip_att, clip_tag); - - xps_begin_opacity(ctx, ctm, fz_infiniterect, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - - for (node = xml_down(root); node; node = xml_next(node)) - { - xps_parse_element(ctx, ctm, base_uri, dict, node); - } - - xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - - if (clip_att || clip_tag) - ctx->dev->popclip(ctx->dev->user); - - if (new_dict) - xps_free_resource_dictionary(ctx, new_dict); -} - -void -xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page) -{ - xml_element *node; - xps_resource *dict; - char base_uri[1024]; - char *s; - int code; - - fz_strlcpy(base_uri, page->name, sizeof base_uri); - s = strrchr(base_uri, '/'); - if (s) - s[1] = 0; - - dict = NULL; - - ctx->opacity_top = 0; - ctx->opacity[0] = 1; - - if (!page->root) - return; - - for (node = xml_down(page->root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "FixedPage.Resources") && xml_down(node)) - { - code = xps_parse_resource_dictionary(ctx, &dict, base_uri, xml_down(node)); - if (code) - fz_catch(code, "cannot load FixedPage.Resources"); - } - xps_parse_element(ctx, ctm, base_uri, dict, node); - } - - if (dict) - { - xps_free_resource_dictionary(ctx, dict); - } -} - -int -xps_load_fixed_page(xps_context *ctx, xps_page *page) -{ - xps_part *part; - xml_element *root; - char *width_att; - char *height_att; - - part = xps_read_part(ctx, page->name); - if (!part) - return fz_rethrow(-1, "cannot read zip part '%s'", page->name); - - root = xml_parse_document(part->data, part->size); - if (!root) - return fz_rethrow(-1, "cannot parse xml part '%s'", page->name); - - xps_free_part(ctx, part); - - if (strcmp(xml_tag(root), "FixedPage")) - return fz_throw("expected FixedPage element (found %s)", xml_tag(root)); - - width_att = xml_att(root, "Width"); - if (!width_att) - return fz_throw("FixedPage missing required attribute: Width"); - - height_att = xml_att(root, "Height"); - if (!height_att) - return fz_throw("FixedPage missing required attribute: Height"); - - page->width = atoi(width_att); - page->height = atoi(height_att); - page->root = root; - - return 0; -} diff --git a/xps/xpspath.c b/xps/xpspath.c deleted file mode 100644 index 00e57bee..00000000 --- a/xps/xpspath.c +++ /dev/null @@ -1,990 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -static fz_point -fz_currentpoint(fz_path *path) -{ - fz_point c, m; - int i; - - c.x = c.y = m.x = m.y = 0; - i = 0; - - while (i < path->len) - { - switch (path->els[i++].k) - { - case FZ_MOVETO: - m.x = c.x = path->els[i++].v; - m.y = c.y = path->els[i++].v; - break; - case FZ_LINETO: - c.x = path->els[i++].v; - c.y = path->els[i++].v; - break; - case FZ_CURVETO: - i += 4; - c.x = path->els[i++].v; - c.y = path->els[i++].v; - break; - case FZ_CLOSEPATH: - c = m; - } - } - - return c; -} - -void -xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xml_element *clip_tag) -{ - fz_path *path; - int fill_rule = 0; - - if (clip_att) - path = xps_parse_abbreviated_geometry(ctx, clip_att, &fill_rule); - else if (clip_tag) - path = xps_parse_path_geometry(ctx, dict, clip_tag, 0, &fill_rule); - else - path = fz_newpath(); - ctx->dev->clippath(ctx->dev->user, path, fill_rule, ctm); - fz_freepath(path); -} - -/* Draw an arc segment transformed by the matrix, we approximate with straight - * line segments. We cannot use the fz_arc function because they only draw - * circular arcs, we need to transform the line to make them elliptical but - * without transforming the line width. - */ -static inline void -xps_draw_arc_segment(fz_path *path, fz_matrix mtx, float th0, float th1, int iscw) -{ - float t, d; - fz_point p; - - while (th1 < th0) - th1 += M_PI * 2.0; - - d = 1 * (M_PI / 180.0); /* 1-degree precision */ - - if (iscw) - { - p.x = cos(th0); - p.y = sin(th0); - p = fz_transformpoint(mtx, p); - fz_lineto(path, p.x, p.y); - for (t = th0; t < th1; t += d) - { - p.x = cos(t); - p.y = sin(t); - p = fz_transformpoint(mtx, p); - fz_lineto(path, p.x, p.y); - } - p.x = cos(th1); - p.y = sin(th1); - p = fz_transformpoint(mtx, p); - fz_lineto(path, p.x, p.y); - } - else - { - th0 += M_PI * 2; - p.x = cos(th0); - p.y = sin(th0); - p = fz_transformpoint(mtx, p); - fz_lineto(path, p.x, p.y); - for (t = th0; t > th1; t -= d) - { - p.x = cos(t); - p.y = sin(t); - p = fz_transformpoint(mtx, p); - fz_lineto(path, p.x, p.y); - } - p.x = cos(th1); - p.y = sin(th1); - p = fz_transformpoint(mtx, p); - fz_lineto(path, p.x, p.y); - } -} - -/* Given two vectors find the angle between them. */ -static inline double -angle_between(const fz_point u, const fz_point v) -{ - double det = u.x * v.y - u.y * v.x; - double sign = (det < 0 ? -1.0 : 1.0); - double magu = u.x * u.x + u.y * u.y; - double magv = v.x * v.x + v.y * v.y; - double udotv = u.x * v.x + u.y * v.y; - double t = udotv / (magu * magv); - /* guard against rounding errors when near |1| (where acos will return NaN) */ - if (t < -1.0) t = -1.0; - if (t > 1.0) t = 1.0; - return sign * acos(t); -} - -static void -xps_draw_arc(fz_path *path, - float size_x, float size_y, float rotation_angle, - int is_large_arc, int is_clockwise, - float point_x, float point_y) -{ - fz_matrix rotmat, revmat; - fz_matrix mtx; - fz_point pt; - double rx, ry; - double x1, y1, x2, y2; - double x1t, y1t; - double cxt, cyt, cx, cy; - double t1, t2, t3; - double sign; - double th1, dth; - - pt = fz_currentpoint(path); - x1 = pt.x; - y1 = pt.y; - x2 = point_x; - y2 = point_y; - rx = size_x; - ry = size_y; - - if (is_clockwise != is_large_arc) - sign = 1; - else - sign = -1; - - rotmat = fz_rotate(rotation_angle); - revmat = fz_rotate(-rotation_angle); - - /* http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes */ - /* Conversion from endpoint to center parameterization */ - - /* F.6.6.1 -- ensure radii are positive and non-zero */ - rx = fabsf(rx); - ry = fabsf(ry); - if (rx < 0.001 || ry < 0.001) - { - fz_lineto(path, x2, y2); - return; - } - - /* F.6.5.1 */ - pt.x = (x1 - x2) / 2; - pt.y = (y1 - y2) / 2; - pt = fz_transformvector(revmat, pt); - x1t = pt.x; - y1t = pt.y; - - /* F.6.6.2 -- ensure radii are large enough */ - t1 = (x1t * x1t) / (rx * rx) + (y1t * y1t) / (ry * ry); - if (t1 > 1.0) - { - rx = rx * sqrtf(t1); - ry = ry * sqrtf(t1); - } - - /* F.6.5.2 */ - t1 = (rx * rx * ry * ry) - (rx * rx * y1t * y1t) - (ry * ry * x1t * x1t); - t2 = (rx * rx * y1t * y1t) + (ry * ry * x1t * x1t); - t3 = t1 / t2; - /* guard against rounding errors; sqrt of negative numbers is bad for your health */ - if (t3 < 0.0) t3 = 0.0; - t3 = sqrtf(t3); - - cxt = sign * t3 * (rx * y1t) / ry; - cyt = sign * t3 * -(ry * x1t) / rx; - - /* F.6.5.3 */ - pt.x = cxt; - pt.y = cyt; - pt = fz_transformvector(rotmat, pt); - cx = pt.x + (x1 + x2) / 2; - cy = pt.y + (y1 + y2) / 2; - - /* F.6.5.4 */ - { - fz_point coord1, coord2, coord3, coord4; - coord1.x = 1; - coord1.y = 0; - coord2.x = (x1t - cxt) / rx; - coord2.y = (y1t - cyt) / ry; - coord3.x = (x1t - cxt) / rx; - coord3.y = (y1t - cyt) / ry; - coord4.x = (-x1t - cxt) / rx; - coord4.y = (-y1t - cyt) / ry; - th1 = angle_between(coord1, coord2); - dth = angle_between(coord3, coord4); - if (dth < 0 && !is_clockwise) - dth += ((M_PI / 180.0) * 360); - if (dth > 0 && is_clockwise) - dth -= ((M_PI / 180.0) * 360); - } - - mtx = fz_identity; - mtx = fz_concat(fz_translate(cx, cy), mtx); - mtx = fz_concat(fz_rotate(rotation_angle), mtx); - mtx = fz_concat(fz_scale(rx, ry), mtx); - xps_draw_arc_segment(path, mtx, th1, th1 + dth, is_clockwise); - - fz_lineto(path, point_x, point_y); -} - -/* - * Parse an abbreviated geometry string, and call - * ghostscript moveto/lineto/curveto functions to - * build up a path. - */ - -fz_path * -xps_parse_abbreviated_geometry(xps_context *ctx, char *geom, int *fill_rule) -{ - fz_path *path; - char **args; - char **pargs; - char *s = geom; - fz_point pt; - int i, n; - int cmd, old; - float x1, y1, x2, y2, x3, y3; - float smooth_x, smooth_y; /* saved cubic bezier control point for smooth curves */ - int reset_smooth; - - path = fz_newpath(); - - args = fz_calloc(strlen(geom) + 1, sizeof(char*)); - pargs = args; - - while (*s) - { - if ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')) - { - *pargs++ = s++; - } - else if ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E') - { - *pargs++ = s; - while ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E') - s ++; - } - else - { - s++; - } - } - - pargs[0] = s; - pargs[1] = 0; - - n = pargs - args; - i = 0; - - old = 0; - - reset_smooth = 1; - smooth_x = 0.0; - smooth_y = 0.0; - - while (i < n) - { - cmd = args[i][0]; - if (cmd == '+' || cmd == '.' || cmd == '-' || (cmd >= '0' && cmd <= '9')) - cmd = old; /* it's a number, repeat old command */ - else - i ++; - - if (reset_smooth) - { - smooth_x = 0.0; - smooth_y = 0.0; - } - - reset_smooth = 1; - - switch (cmd) - { - case 'F': - *fill_rule = atoi(args[i]); - i ++; - break; - - case 'M': - fz_moveto(path, atof(args[i]), atof(args[i+1])); - i += 2; - break; - case 'm': - pt = fz_currentpoint(path); - fz_moveto(path, pt.x + atof(args[i]), pt.y + atof(args[i+1])); - i += 2; - break; - - case 'L': - fz_lineto(path, atof(args[i]), atof(args[i+1])); - i += 2; - break; - case 'l': - pt = fz_currentpoint(path); - fz_lineto(path, pt.x + atof(args[i]), pt.y + atof(args[i+1])); - i += 2; - break; - - case 'H': - pt = fz_currentpoint(path); - fz_lineto(path, atof(args[i]), pt.y); - i += 1; - break; - case 'h': - pt = fz_currentpoint(path); - fz_lineto(path, pt.x + atof(args[i]), pt.y); - i += 1; - break; - - case 'V': - pt = fz_currentpoint(path); - fz_lineto(path, pt.x, atof(args[i])); - i += 1; - break; - case 'v': - pt = fz_currentpoint(path); - fz_lineto(path, pt.x, pt.y + atof(args[i])); - i += 1; - break; - - case 'C': - x1 = atof(args[i+0]); - y1 = atof(args[i+1]); - x2 = atof(args[i+2]); - y2 = atof(args[i+3]); - x3 = atof(args[i+4]); - y3 = atof(args[i+5]); - fz_curveto(path, x1, y1, x2, y2, x3, y3); - i += 6; - reset_smooth = 0; - smooth_x = x3 - x2; - smooth_y = y3 - y2; - break; - - case 'c': - pt = fz_currentpoint(path); - x1 = atof(args[i+0]) + pt.x; - y1 = atof(args[i+1]) + pt.y; - x2 = atof(args[i+2]) + pt.x; - y2 = atof(args[i+3]) + pt.y; - x3 = atof(args[i+4]) + pt.x; - y3 = atof(args[i+5]) + pt.y; - fz_curveto(path, x1, y1, x2, y2, x3, y3); - i += 6; - reset_smooth = 0; - smooth_x = x3 - x2; - smooth_y = y3 - y2; - break; - - case 'S': - pt = fz_currentpoint(path); - x1 = atof(args[i+0]); - y1 = atof(args[i+1]); - x2 = atof(args[i+2]); - y2 = atof(args[i+3]); - fz_curveto(path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); - i += 4; - reset_smooth = 0; - smooth_x = x2 - x1; - smooth_y = y2 - y1; - break; - - case 's': - pt = fz_currentpoint(path); - x1 = atof(args[i+0]) + pt.x; - y1 = atof(args[i+1]) + pt.y; - x2 = atof(args[i+2]) + pt.x; - y2 = atof(args[i+3]) + pt.y; - fz_curveto(path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); - i += 4; - reset_smooth = 0; - smooth_x = x2 - x1; - smooth_y = y2 - y1; - break; - - case 'Q': - pt = fz_currentpoint(path); - x1 = atof(args[i+0]); - y1 = atof(args[i+1]); - x2 = atof(args[i+2]); - y2 = atof(args[i+3]); - fz_curveto(path, - (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, - (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, - x2, y2); - i += 4; - break; - case 'q': - pt = fz_currentpoint(path); - x1 = atof(args[i+0]) + pt.x; - y1 = atof(args[i+1]) + pt.y; - x2 = atof(args[i+2]) + pt.x; - y2 = atof(args[i+3]) + pt.y; - fz_curveto(path, - (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, - (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, - x2, y2); - i += 4; - break; - - case 'A': - xps_draw_arc(path, - atof(args[i+0]), atof(args[i+1]), atof(args[i+2]), - atoi(args[i+3]), atoi(args[i+4]), - atof(args[i+5]), atof(args[i+6])); - i += 7; - break; - case 'a': - pt = fz_currentpoint(path); - xps_draw_arc(path, - atof(args[i+0]), atof(args[i+1]), atof(args[i+2]), - atoi(args[i+3]), atoi(args[i+4]), - atof(args[i+5]) + pt.x, atof(args[i+6]) + pt.y); - i += 7; - break; - - case 'Z': - case 'z': - fz_closepath(path); - break; - - default: - /* eek */ - break; - } - - old = cmd; - } - - fz_free(args); - return path; -} - -static void -xps_parse_arc_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke) -{ - /* ArcSegment pretty much follows the SVG algorithm for converting an - * arc in endpoint representation to an arc in centerpoint - * representation. Once in centerpoint it can be given to the - * graphics library in the form of a postscript arc. */ - - float rotation_angle; - int is_large_arc, is_clockwise; - float point_x, point_y; - float size_x, size_y; - int is_stroked; - - char *point_att = xml_att(root, "Point"); - char *size_att = xml_att(root, "Size"); - char *rotation_angle_att = xml_att(root, "RotationAngle"); - char *is_large_arc_att = xml_att(root, "IsLargeArc"); - char *sweep_direction_att = xml_att(root, "SweepDirection"); - char *is_stroked_att = xml_att(root, "IsStroked"); - - if (!point_att || !size_att || !rotation_angle_att || !is_large_arc_att || !sweep_direction_att) - { - fz_warn("ArcSegment element is missing attributes"); - return; - } - - is_stroked = 1; - if (is_stroked_att && !strcmp(is_stroked_att, "false")) - is_stroked = 0; - if (!is_stroked) - *skipped_stroke = 1; - - sscanf(point_att, "%g,%g", &point_x, &point_y); - sscanf(size_att, "%g,%g", &size_x, &size_y); - rotation_angle = atof(rotation_angle_att); - is_large_arc = !strcmp(is_large_arc_att, "true"); - is_clockwise = !strcmp(sweep_direction_att, "Clockwise"); - - if (stroking && !is_stroked) - { - fz_moveto(path, point_x, point_y); - return; - } - - xps_draw_arc(path, size_x, size_y, rotation_angle, is_large_arc, is_clockwise, point_x, point_y); -} - -static void -xps_parse_poly_quadratic_bezier_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke) -{ - char *points_att = xml_att(root, "Points"); - char *is_stroked_att = xml_att(root, "IsStroked"); - float x[2], y[2]; - int is_stroked; - fz_point pt; - char *s; - int n; - - if (!points_att) - { - fz_warn("PolyQuadraticBezierSegment element has no points"); - return; - } - - is_stroked = 1; - if (is_stroked_att && !strcmp(is_stroked_att, "false")) - is_stroked = 0; - if (!is_stroked) - *skipped_stroke = 1; - - s = points_att; - n = 0; - while (*s != 0) - { - while (*s == ' ') s++; - sscanf(s, "%g,%g", &x[n], &y[n]); - while (*s != ' ' && *s != 0) s++; - n ++; - if (n == 2) - { - if (stroking && !is_stroked) - { - fz_moveto(path, x[1], y[1]); - } - else - { - pt = fz_currentpoint(path); - fz_curveto(path, - (pt.x + 2 * x[0]) / 3, (pt.y + 2 * y[0]) / 3, - (x[1] + 2 * x[0]) / 3, (y[1] + 2 * y[0]) / 3, - x[1], y[1]); - } - n = 0; - } - } -} - -static void -xps_parse_poly_bezier_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke) -{ - char *points_att = xml_att(root, "Points"); - char *is_stroked_att = xml_att(root, "IsStroked"); - float x[3], y[3]; - int is_stroked; - char *s; - int n; - - if (!points_att) - { - fz_warn("PolyBezierSegment element has no points"); - return; - } - - is_stroked = 1; - if (is_stroked_att && !strcmp(is_stroked_att, "false")) - is_stroked = 0; - if (!is_stroked) - *skipped_stroke = 1; - - s = points_att; - n = 0; - while (*s != 0) - { - while (*s == ' ') s++; - sscanf(s, "%g,%g", &x[n], &y[n]); - while (*s != ' ' && *s != 0) s++; - n ++; - if (n == 3) - { - if (stroking && !is_stroked) - fz_moveto(path, x[2], y[2]); - else - fz_curveto(path, x[0], y[0], x[1], y[1], x[2], y[2]); - n = 0; - } - } -} - -static void -xps_parse_poly_line_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke) -{ - char *points_att = xml_att(root, "Points"); - char *is_stroked_att = xml_att(root, "IsStroked"); - int is_stroked; - float x, y; - char *s; - - if (!points_att) - { - fz_warn("PolyLineSegment element has no points"); - return; - } - - is_stroked = 1; - if (is_stroked_att && !strcmp(is_stroked_att, "false")) - is_stroked = 0; - if (!is_stroked) - *skipped_stroke = 1; - - s = points_att; - while (*s != 0) - { - while (*s == ' ') s++; - sscanf(s, "%g,%g", &x, &y); - if (stroking && !is_stroked) - fz_moveto(path, x, y); - else - fz_lineto(path, x, y); - while (*s != ' ' && *s != 0) s++; - } -} - -static void -xps_parse_path_figure(fz_path *path, xml_element *root, int stroking) -{ - xml_element *node; - - char *is_closed_att; - char *start_point_att; - char *is_filled_att; - - int is_closed = 0; - int is_filled = 1; - float start_x = 0.0; - float start_y = 0.0; - - int skipped_stroke = 0; - - is_closed_att = xml_att(root, "IsClosed"); - start_point_att = xml_att(root, "StartPoint"); - is_filled_att = xml_att(root, "IsFilled"); - - if (is_closed_att) - is_closed = !strcmp(is_closed_att, "true"); - if (is_filled_att) - is_filled = !strcmp(is_filled_att, "true"); - if (start_point_att) - sscanf(start_point_att, "%g,%g", &start_x, &start_y); - - if (!stroking && !is_filled) /* not filled, when filling */ - return; - - fz_moveto(path, start_x, start_y); - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "ArcSegment")) - xps_parse_arc_segment(path, node, stroking, &skipped_stroke); - if (!strcmp(xml_tag(node), "PolyBezierSegment")) - xps_parse_poly_bezier_segment(path, node, stroking, &skipped_stroke); - if (!strcmp(xml_tag(node), "PolyLineSegment")) - xps_parse_poly_line_segment(path, node, stroking, &skipped_stroke); - if (!strcmp(xml_tag(node), "PolyQuadraticBezierSegment")) - xps_parse_poly_quadratic_bezier_segment(path, node, stroking, &skipped_stroke); - } - - if (is_closed) - { - if (stroking && skipped_stroke) - fz_lineto(path, start_x, start_y); /* we've skipped using fz_moveto... */ - else - fz_closepath(path); /* no skipped segments, safe to closepath properly */ - } -} - -fz_path * -xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xml_element *root, int stroking, int *fill_rule) -{ - xml_element *node; - - char *figures_att; - char *fill_rule_att; - char *transform_att; - - xml_element *transform_tag = NULL; - xml_element *figures_tag = NULL; /* only used by resource */ - - fz_matrix transform; - fz_path *path; - - figures_att = xml_att(root, "Figures"); - fill_rule_att = xml_att(root, "FillRule"); - transform_att = xml_att(root, "Transform"); - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "PathGeometry.Transform")) - transform_tag = xml_down(node); - } - - xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); - xps_resolve_resource_reference(ctx, dict, &figures_att, &figures_tag, NULL); - - if (fill_rule_att) - { - if (!strcmp(fill_rule_att, "NonZero")) - *fill_rule = 1; - if (!strcmp(fill_rule_att, "EvenOdd")) - *fill_rule = 0; - } - - transform = fz_identity; - if (transform_att) - xps_parse_render_transform(ctx, transform_att, &transform); - if (transform_tag) - xps_parse_matrix_transform(ctx, transform_tag, &transform); - - if (figures_att) - path = xps_parse_abbreviated_geometry(ctx, figures_att, fill_rule); - else - path = fz_newpath(); - - if (figures_tag) - xps_parse_path_figure(path, figures_tag, stroking); - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "PathFigure")) - xps_parse_path_figure(path, node, stroking); - } - - if (transform_att || transform_tag) - fz_transformpath(path, transform); - - return path; -} - -static int -xps_parse_line_cap(char *attr) -{ - if (attr) - { - if (!strcmp(attr, "Flat")) return 0; - if (!strcmp(attr, "Round")) return 1; - if (!strcmp(attr, "Square")) return 2; - if (!strcmp(attr, "Triangle")) return 3; /* FIXME add triangle caps */ - } - return 0; -} - -/* - * Parse an XPS element, and call relevant ghostscript - * functions for drawing and/or clipping the child elements. - */ - -void -xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root) -{ - xml_element *node; - - char *fill_uri; - char *stroke_uri; - char *opacity_mask_uri; - - char *transform_att; - char *clip_att; - char *data_att; - char *fill_att; - char *stroke_att; - char *opacity_att; - char *opacity_mask_att; - - xml_element *transform_tag = NULL; - xml_element *clip_tag = NULL; - xml_element *data_tag = NULL; - xml_element *fill_tag = NULL; - xml_element *stroke_tag = NULL; - xml_element *opacity_mask_tag = NULL; - - char *fill_opacity_att = NULL; - char *stroke_opacity_att = NULL; - - char *stroke_dash_array_att; - char *stroke_dash_cap_att; - char *stroke_dash_offset_att; - char *stroke_end_line_cap_att; - char *stroke_start_line_cap_att; - char *stroke_line_join_att; - char *stroke_miter_limit_att; - char *stroke_thickness_att; - - fz_strokestate stroke; - fz_matrix transform; - float samples[32]; - fz_colorspace *colorspace; - fz_path *path; - fz_rect area; - int fill_rule; - - /* - * Extract attributes and extended attributes. - */ - - transform_att = xml_att(root, "RenderTransform"); - clip_att = xml_att(root, "Clip"); - data_att = xml_att(root, "Data"); - fill_att = xml_att(root, "Fill"); - stroke_att = xml_att(root, "Stroke"); - opacity_att = xml_att(root, "Opacity"); - opacity_mask_att = xml_att(root, "OpacityMask"); - - stroke_dash_array_att = xml_att(root, "StrokeDashArray"); - stroke_dash_cap_att = xml_att(root, "StrokeDashCap"); - stroke_dash_offset_att = xml_att(root, "StrokeDashOffset"); - stroke_end_line_cap_att = xml_att(root, "StrokeEndLineCap"); - stroke_start_line_cap_att = xml_att(root, "StrokeStartLineCap"); - stroke_line_join_att = xml_att(root, "StrokeLineJoin"); - stroke_miter_limit_att = xml_att(root, "StrokeMiterLimit"); - stroke_thickness_att = xml_att(root, "StrokeThickness"); - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "Path.RenderTransform")) - transform_tag = xml_down(node); - if (!strcmp(xml_tag(node), "Path.OpacityMask")) - opacity_mask_tag = xml_down(node); - if (!strcmp(xml_tag(node), "Path.Clip")) - clip_tag = xml_down(node); - if (!strcmp(xml_tag(node), "Path.Fill")) - fill_tag = xml_down(node); - if (!strcmp(xml_tag(node), "Path.Stroke")) - stroke_tag = xml_down(node); - if (!strcmp(xml_tag(node), "Path.Data")) - data_tag = xml_down(node); - } - - fill_uri = base_uri; - stroke_uri = base_uri; - opacity_mask_uri = base_uri; - - xps_resolve_resource_reference(ctx, dict, &data_att, &data_tag, NULL); - xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); - xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); - xps_resolve_resource_reference(ctx, dict, &fill_att, &fill_tag, &fill_uri); - xps_resolve_resource_reference(ctx, dict, &stroke_att, &stroke_tag, &stroke_uri); - xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); - - /* - * Act on the information we have gathered: - */ - - if (!data_att && !data_tag) - return; - - if (fill_tag && !strcmp(xml_tag(fill_tag), "SolidColorBrush")) - { - fill_opacity_att = xml_att(fill_tag, "Opacity"); - fill_att = xml_att(fill_tag, "Color"); - fill_tag = NULL; - } - - if (stroke_tag && !strcmp(xml_tag(stroke_tag), "SolidColorBrush")) - { - stroke_opacity_att = xml_att(stroke_tag, "Opacity"); - stroke_att = xml_att(stroke_tag, "Color"); - stroke_tag = NULL; - } - - stroke.linecap = xps_parse_line_cap(stroke_start_line_cap_att); -// fz_setlineendcap(ctx->pgs, xps_parse_line_cap(stroke_end_line_cap_att)); -// fz_setlinedashcap(ctx->pgs, xps_parse_line_cap(stroke_dash_cap_att)); - - stroke.linejoin = 0; - if (stroke_line_join_att) - { - if (!strcmp(stroke_line_join_att, "Miter")) stroke.linejoin = 0; - if (!strcmp(stroke_line_join_att, "Round")) stroke.linejoin = 1; - if (!strcmp(stroke_line_join_att, "Bevel")) stroke.linejoin = 2; - } - - stroke.miterlimit = 10.0; - if (stroke_miter_limit_att) - stroke.miterlimit = atof(stroke_miter_limit_att); - - stroke.linewidth = 1.0; - if (stroke_thickness_att) - stroke.linewidth = atof(stroke_thickness_att); - - stroke.dashphase = 0; - stroke.dashlen = 0; - if (stroke_dash_array_att) - { - char *s = stroke_dash_array_att; - - if (stroke_dash_offset_att) - stroke.dashphase = atof(stroke_dash_offset_att) * stroke.linewidth; - - while (*s && stroke.dashlen < nelem(stroke.dashlist)) - { - while (*s == ' ') - s++; - stroke.dashlist[stroke.dashlen++] = atof(s) * stroke.linewidth; - while (*s && *s != ' ') - s++; - } - } - - transform = fz_identity; - if (transform_att) - xps_parse_render_transform(ctx, transform_att, &transform); - if (transform_tag) - xps_parse_matrix_transform(ctx, transform_tag, &transform); - ctm = fz_concat(transform, ctm); - - if (clip_att || clip_tag) - xps_clip(ctx, ctm, dict, clip_att, clip_tag); - - fill_rule = 0; - if (data_att) - path = xps_parse_abbreviated_geometry(ctx, data_att, &fill_rule); - else if (data_tag) - path = xps_parse_path_geometry(ctx, dict, data_tag, 0, &fill_rule); - - if (stroke_att || stroke_tag) - area = fz_boundpath(path, &stroke, ctm); - else - area = fz_boundpath(path, NULL, ctm); - - xps_begin_opacity(ctx, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - - if (fill_att) - { - xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples); - if (fill_opacity_att) - samples[0] = atof(fill_opacity_att); - xps_set_color(ctx, colorspace, samples); - - ctx->dev->fillpath(ctx->dev->user, path, fill_rule == 0, ctm, - ctx->colorspace, ctx->color, ctx->alpha); - } - - if (fill_tag) - { - area = fz_boundpath(path, NULL, ctm); - - ctx->dev->clippath(ctx->dev->user, path, fill_rule == 0, ctm); - xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag); - ctx->dev->popclip(ctx->dev->user); - } - - if (stroke_att) - { - xps_parse_color(ctx, base_uri, stroke_att, &colorspace, samples); - if (stroke_opacity_att) - samples[0] = atof(stroke_opacity_att); - xps_set_color(ctx, colorspace, samples); - - ctx->dev->strokepath(ctx->dev->user, path, &stroke, ctm, - ctx->colorspace, ctx->color, ctx->alpha); - } - - if (stroke_tag) - { - ctx->dev->clipstrokepath(ctx->dev->user, path, &stroke, ctm); - xps_parse_brush(ctx, ctm, area, stroke_uri, dict, stroke_tag); - ctx->dev->popclip(ctx->dev->user); - } - - xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - - fz_freepath(path); - path = NULL; - - if (clip_att || clip_tag) - ctx->dev->popclip(ctx->dev->user); -} diff --git a/xps/xpspng.c b/xps/xpspng.c deleted file mode 100644 index f59be56d..00000000 --- a/xps/xpspng.c +++ /dev/null @@ -1,580 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -#include - -struct info -{ - int width, height, depth, n; - int interlace, indexed; - int size; - unsigned char *samples; - unsigned char palette[256*4]; - int transparency; - int trns[3]; - int xres, yres; -}; - -static inline int getint(unsigned char *p) -{ - return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; -} - -static inline int -getcomp(unsigned char *line, int x, int bpc) -{ - switch (bpc) - { - case 1: return (line[x >> 3] >> ( 7 - (x & 7) ) ) & 1; - case 2: return (line[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3; - case 4: return (line[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15; - case 8: return line[x]; - case 16: return line[x << 1] << 8 | line[(x << 1) + 1]; - } - return 0; -} - -static inline void -putcomp(unsigned char *line, int x, int bpc, int value) -{ - int maxval = (1 << bpc) - 1; - - switch (bpc) - { - case 1: line[x >> 3] &= ~(maxval << (7 - (x & 7))); break; - case 2: line[x >> 2] &= ~(maxval << ((3 - (x & 3)) << 1)); break; - case 4: line[x >> 1] &= ~(maxval << ((1 - (x & 1)) << 2)); break; - } - - switch (bpc) - { - case 1: line[x >> 3] |= value << (7 - (x & 7)); break; - case 2: line[x >> 2] |= value << ((3 - (x & 3)) << 1); break; - case 4: line[x >> 1] |= value << ((1 - (x & 1)) << 2); break; - case 8: line[x] = value; break; - case 16: line[x << 1] = value >> 8; line[(x << 1) + 1] = value & 0xFF; break; - } -} - -static const unsigned char png_signature[8] = -{ - 137, 80, 78, 71, 13, 10, 26, 10 -}; - -static void *zalloc(void *opaque, unsigned int items, unsigned int size) -{ - return fz_calloc(items, size); -} - -static void zfree(void *opaque, void *address) -{ - fz_free(address); -} - -static inline int paeth(int a, int b, int c) -{ - /* The definitions of ac and bc are correct, not a typo. */ - int ac = b - c, bc = a - c, abcc = ac + bc; - int pa = (ac < 0 ? -ac : ac); - int pb = (bc < 0 ? -bc : bc); - int pc = (abcc < 0 ? -abcc : abcc); - return pa <= pb && pa <= pc ? a : pb <= pc ? b : c; -} - -static void -png_predict(unsigned char *samples, int width, int height, int n, int depth) -{ - int stride = (width * n * depth + 7) / 8; - int bpp = (n * depth + 7) / 8; - int i, row; - - for (row = 0; row < height; row ++) - { - unsigned char *src = samples + (stride + 1) * row; - unsigned char *dst = samples + stride * row; - - unsigned char *a = dst; - unsigned char *b = dst - stride; - unsigned char *c = dst - stride; - - switch (*src++) - { - default: - case 0: /* None */ - for (i = 0; i < stride; i++) - *dst++ = *src++; - break; - - case 1: /* Sub */ - for (i = 0; i < bpp; i++) - *dst++ = *src++; - for (i = bpp; i < stride; i++) - *dst++ = *src++ + *a++; - break; - - case 2: /* Up */ - if (row == 0) - for (i = 0; i < stride; i++) - *dst++ = *src++; - else - for (i = 0; i < stride; i++) - *dst++ = *src++ + *b++; - break; - - case 3: /* Average */ - if (row == 0) - { - for (i = 0; i < bpp; i++) - *dst++ = *src++; - for (i = bpp; i < stride; i++) - *dst++ = *src++ + (*a++ >> 1); - } - else - { - for (i = 0; i < bpp; i++) - *dst++ = *src++ + (*b++ >> 1); - for (i = bpp; i < stride; i++) - *dst++ = *src++ + ((*b++ + *a++) >> 1); - } - break; - - case 4: /* Paeth */ - if (row == 0) - { - for (i = 0; i < bpp; i++) - *dst++ = *src++ + paeth(0, 0, 0); - for (i = bpp; i < stride; i++) - *dst++ = *src++ + paeth(*a++, 0, 0); - } - else - { - for (i = 0; i < bpp; i++) - *dst++ = *src++ + paeth(0, *b++, 0); - for (i = bpp; i < stride; i++) - *dst++ = *src++ + paeth(*a++, *b++, *c++); - } - break; - } - } -} - -static const int adam7_ix[7] = { 0, 4, 0, 2, 0, 1, 0 }; -static const int adam7_dx[7] = { 8, 8, 4, 4, 2, 2, 1 }; -static const int adam7_iy[7] = { 0, 0, 4, 0, 2, 0, 1 }; -static const int adam7_dy[7] = { 8, 8, 8, 4, 4, 2, 2 }; - -static void -png_deinterlace_passes(struct info *info, int *w, int *h, int *ofs) -{ - int p, bpp = info->depth * info->n; - ofs[0] = 0; - for (p = 0; p < 7; p++) - { - w[p] = (info->width + adam7_dx[p] - adam7_ix[p] - 1) / adam7_dx[p]; - h[p] = (info->height + adam7_dy[p] - adam7_iy[p] - 1) / adam7_dy[p]; - if (w[p] == 0) h[p] = 0; - if (h[p] == 0) w[p] = 0; - if (w[p] && h[p]) - ofs[p + 1] = ofs[p] + h[p] * (1 + (w[p] * bpp + 7) / 8); - else - ofs[p + 1] = ofs[p]; - } -} - -static void -png_deinterlace(struct info *info, int *passw, int *passh, int *passofs) -{ - int n = info->n; - int depth = info->depth; - int stride = (info->width * n * depth + 7) / 8; - unsigned char *output; - int p, x, y, k; - - output = fz_calloc(info->height, stride); - - for (p = 0; p < 7; p++) - { - unsigned char *sp = info->samples + passofs[p]; - int w = passw[p]; - int h = passh[p]; - - png_predict(sp, w, h, n, depth); - for (y = 0; y < h; y++) - { - for (x = 0; x < w; x++) - { - int outx = x * adam7_dx[p] + adam7_ix[p]; - int outy = y * adam7_dy[p] + adam7_iy[p]; - unsigned char *dp = output + outy * stride; - for (k = 0; k < n; k++) - { - int v = getcomp(sp, x * n + k, depth); - putcomp(dp, outx * n + k, depth, v); - } - } - sp += (w * depth * n + 7) / 8; - } - } - - fz_free(info->samples); - info->samples = output; -} - -static int -png_read_ihdr(struct info *info, unsigned char *p, int size) -{ - int color, compression, filter; - - if (size != 13) - return fz_throw("IHDR chunk is the wrong size"); - - info->width = getint(p + 0); - info->height = getint(p + 4); - info->depth = p[8]; - - color = p[9]; - compression = p[10]; - filter = p[11]; - info->interlace = p[12]; - - if (info->width <= 0) - return fz_throw("image width must be > 0"); - if (info->height <= 0) - return fz_throw("image height must be > 0"); - - if (info->depth != 1 && info->depth != 2 && info->depth != 4 && - info->depth != 8 && info->depth != 16) - return fz_throw("image bit depth must be one of 1, 2, 4, 8, 16"); - if (color == 2 && info->depth < 8) - return fz_throw("illegal bit depth for truecolor"); - if (color == 3 && info->depth > 8) - return fz_throw("illegal bit depth for indexed"); - if (color == 4 && info->depth < 8) - return fz_throw("illegal bit depth for grayscale with alpha"); - if (color == 6 && info->depth < 8) - return fz_throw("illegal bit depth for truecolor with alpha"); - - info->indexed = 0; - if (color == 0) /* gray */ - info->n = 1; - else if (color == 2) /* rgb */ - info->n = 3; - else if (color == 4) /* gray alpha */ - info->n = 2; - else if (color == 6) /* rgb alpha */ - info->n = 4; - else if (color == 3) /* indexed */ - { - info->indexed = 1; - info->n = 1; - } - else - return fz_throw("unknown color type"); - - if (compression != 0) - return fz_throw("unknown compression method"); - if (filter != 0) - return fz_throw("unknown filter method"); - if (info->interlace != 0 && info->interlace != 1) - return fz_throw("interlace method not supported"); - - return fz_okay; -} - -static int -png_read_plte(struct info *info, unsigned char *p, int size) -{ - int n = size / 3; - int i; - - if (n > 256 || n > (1 << info->depth)) - return fz_throw("too many samples in palette"); - - for (i = 0; i < n; i++) - { - info->palette[i * 4] = p[i * 3]; - info->palette[i * 4 + 1] = p[i * 3 + 1]; - info->palette[i * 4 + 2] = p[i * 3 + 2]; - } - - return fz_okay; -} - -static int -png_read_trns(struct info *info, unsigned char *p, int size) -{ - int i; - - info->transparency = 1; - - if (info->indexed) - { - if (size > 256 || size > (1 << info->depth)) - return fz_throw("too many samples in transparency table"); - for (i = 0; i < size; i++) - info->palette[i * 4 + 3] = p[i]; - } - else - { - if (size != info->n * 2) - return fz_throw("tRNS chunk is the wrong size"); - for (i = 0; i < info->n; i++) - info->trns[i] = (p[i * 2] << 8 | p[i * 2 + 1]) & ((1 << info->depth) - 1); - } - - return fz_okay; -} - -static int -png_read_idat(struct info *info, unsigned char *p, int size, z_stream *stm) -{ - int code; - - stm->next_in = p; - stm->avail_in = size; - - code = inflate(stm, Z_SYNC_FLUSH); - if (code != Z_OK && code != Z_STREAM_END) - return fz_throw("zlib error: %s", stm->msg); - if (stm->avail_in != 0) - { - if (stm->avail_out == 0) - return fz_throw("ran out of output before input"); - return fz_throw("inflate did not consume buffer (%d remaining)", stm->avail_in); - } - - return fz_okay; -} - -static int -png_read_phys(struct info *info, unsigned char *p, int size) -{ - if (size != 9) - return fz_throw("pHYs chunk is the wrong size"); - if (p[8] == 1) - { - info->xres = getint(p) * 254 / 10000; - info->yres = getint(p + 4) * 254 / 10000; - } - return fz_okay; -} - -static int -png_read_image(struct info *info, unsigned char *p, int total) -{ - int passw[7], passh[7], passofs[8]; - int code, size; - z_stream stm; - - memset(info, 0, sizeof (struct info)); - memset(info->palette, 255, sizeof(info->palette)); - info->xres = 96; - info->yres = 96; - - /* Read signature */ - - if (total < 8 + 12 || memcmp(p, png_signature, 8)) - return fz_throw("not a png image (wrong signature)"); - - p += 8; - total -= 8; - - /* Read IHDR chunk (must come first) */ - - size = getint(p); - - if (size + 12 > total) - return fz_throw("premature end of data in png image"); - - if (!memcmp(p + 4, "IHDR", 4)) - { - code = png_read_ihdr(info, p + 8, size); - if (code) - return fz_rethrow(code, "cannot read png header"); - } - else - return fz_throw("png file must start with IHDR chunk"); - - p += size + 12; - total -= size + 12; - - /* Prepare output buffer */ - - if (!info->interlace) - { - info->size = info->height * (1 + (info->width * info->n * info->depth + 7) / 8); - } - else - { - png_deinterlace_passes(info, passw, passh, passofs); - info->size = passofs[7]; - } - - info->samples = fz_malloc(info->size); - - stm.zalloc = zalloc; - stm.zfree = zfree; - stm.opaque = NULL; - - stm.next_out = info->samples; - stm.avail_out = info->size; - - code = inflateInit(&stm); - if (code != Z_OK) - return fz_throw("zlib error: %s", stm.msg); - - /* Read remaining chunks until IEND */ - - while (total > 8) - { - size = getint(p); - - if (size + 12 > total) - return fz_throw("premature end of data in png image"); - - if (!memcmp(p + 4, "PLTE", 4)) - { - code = png_read_plte(info, p + 8, size); - if (code) - return fz_rethrow(code, "cannot read png palette"); - } - - if (!memcmp(p + 4, "tRNS", 4)) - { - code = png_read_trns(info, p + 8, size); - if (code) - return fz_rethrow(code, "cannot read png transparency"); - } - - if (!memcmp(p + 4, "pHYs", 4)) - { - code = png_read_phys(info, p + 8, size); - if (code) - return fz_rethrow(code, "cannot read png resolution"); - } - - if (!memcmp(p + 4, "IDAT", 4)) - { - code = png_read_idat(info, p + 8, size, &stm); - if (code) - return fz_rethrow(code, "cannot read png image data"); - } - - if (!memcmp(p + 4, "IEND", 4)) - break; - - p += size + 12; - total -= size + 12; - } - - code = inflateEnd(&stm); - if (code != Z_OK) - return fz_throw("zlib error: %s", stm.msg); - - /* Apply prediction filter and deinterlacing */ - - if (!info->interlace) - png_predict(info->samples, info->width, info->height, info->n, info->depth); - else - png_deinterlace(info, passw, passh, passofs); - - return fz_okay; -} - -static fz_pixmap * -png_expand_palette(struct info *info, fz_pixmap *src) -{ - fz_pixmap *dst = fz_newpixmap(fz_devicergb, 0, 0, src->w, src->h); - unsigned char *sp = src->samples; - unsigned char *dp = dst->samples; - int x, y; - - for (y = 0; y < info->height; y++) - { - for (x = 0; x < info->width; x++) - { - int v = *sp << 2; - *dp++ = info->palette[v]; - *dp++ = info->palette[v + 1]; - *dp++ = info->palette[v + 2]; - *dp++ = info->palette[v + 3]; - sp += 2; - } - } - - fz_droppixmap(src); - return dst; -} - -static void -png_mask_transparency(struct info *info, fz_pixmap *dst) -{ - int stride = (info->width * info->n * info->depth + 7) / 8; - int depth = info->depth; - int n = info->n; - int x, y, k, t; - - for (y = 0; y < info->height; y++) - { - unsigned char *sp = info->samples + y * stride; - unsigned char *dp = dst->samples + y * dst->w * dst->n; - for (x = 0; x < info->width; x++) - { - t = 1; - for (k = 0; k < n; k++) - if (getcomp(sp, x * n + k, depth) != info->trns[k]) - t = 0; - if (t) - dp[x * dst->n + dst->n - 1] = 0; - } - } -} - -int -xps_decode_png(xps_image **imagep, xps_context *ctx, byte *p, int total) -{ - fz_pixmap *pixmap; - fz_colorspace *colorspace; - xps_image *image; - struct info png; - int code; - int stride; - - code = png_read_image(&png, p, total); - if (code) - return fz_rethrow(code, "cannot read png image"); - - if (png.n == 3 || png.n == 4) - colorspace = fz_devicergb; - else - colorspace = fz_devicegray; - - stride = (png.width * png.n * png.depth + 7) / 8; - - pixmap = fz_newpixmap(colorspace, 0, 0, png.width, png.height); - fz_unpacktile(pixmap, png.samples, png.n, png.depth, stride, png.indexed); - - if (png.indexed) - { - pixmap = png_expand_palette(&png, pixmap); - } - else if (png.transparency) - { - png_mask_transparency(&png, pixmap); - } - - if (png.transparency || png.n == 2 || png.n == 4) - { - fz_premultiplypixmap(pixmap); - } - - fz_free(png.samples); - - image = fz_malloc(sizeof(xps_image)); - image->pixmap = pixmap; - image->xres = png.xres; - image->yres = png.yres; - - *imagep = image; - return fz_okay; -} diff --git a/xps/xpsresource.c b/xps/xpsresource.c deleted file mode 100644 index 59dc3f9d..00000000 --- a/xps/xpsresource.c +++ /dev/null @@ -1,188 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -static xml_element * -xps_find_resource(xps_context *ctx, xps_resource *dict, char *name, char **urip) -{ - xps_resource *head, *node; - for (head = dict; head; head = head->parent) - { - for (node = head; node; node = node->next) - { - if (!strcmp(node->name, name)) - { - if (urip && head->base_uri) - *urip = head->base_uri; - return node->data; - } - } - } - return NULL; -} - -static xml_element * -xps_parse_resource_reference(xps_context *ctx, xps_resource *dict, char *att, char **urip) -{ - char name[1024]; - char *s; - - if (strstr(att, "{StaticResource ") != att) - return NULL; - - fz_strlcpy(name, att + 16, sizeof name); - s = strrchr(name, '}'); - if (s) - *s = 0; - - return xps_find_resource(ctx, dict, name, urip); -} - -void -xps_resolve_resource_reference(xps_context *ctx, xps_resource *dict, - char **attp, xml_element **tagp, char **urip) -{ - if (*attp) - { - xml_element *rsrc = xps_parse_resource_reference(ctx, dict, *attp, urip); - if (rsrc) - { - *attp = NULL; - *tagp = rsrc; - } - } -} - -static int -xps_parse_remote_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, char *source_att) -{ - char part_name[1024]; - char part_uri[1024]; - xps_resource *dict; - xps_part *part; - xml_element *xml; - char *s; - int code; - - /* External resource dictionaries MUST NOT reference other resource dictionaries */ - xps_absolute_path(part_name, base_uri, source_att, sizeof part_name); - part = xps_read_part(ctx, part_name); - if (!part) - { - return fz_throw("cannot find remote resource part '%s'", part_name); - } - - xml = xml_parse_document(part->data, part->size); - if (!xml) - { - xps_free_part(ctx, part); - return fz_rethrow(-1, "cannot parse xml"); - } - - if (strcmp(xml_tag(xml), "ResourceDictionary")) - { - xml_free_element(xml); - xps_free_part(ctx, part); - return fz_throw("expected ResourceDictionary element (found %s)", xml_tag(xml)); - } - - fz_strlcpy(part_uri, part_name, sizeof part_uri); - s = strrchr(part_uri, '/'); - if (s) - s[1] = 0; - - code = xps_parse_resource_dictionary(ctx, &dict, part_uri, xml); - if (code) - { - xml_free_element(xml); - xps_free_part(ctx, part); - return fz_rethrow(code, "cannot parse remote resource dictionary: %s", part_uri); - } - - dict->base_xml = xml; /* pass on ownership */ - - xps_free_part(ctx, part); - - *dictp = dict; - return fz_okay; -} - -int -xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, xml_element *root) -{ - xps_resource *head; - xps_resource *entry; - xml_element *node; - char *source; - char *key; - int code; - - source = xml_att(root, "Source"); - if (source) - { - code = xps_parse_remote_resource_dictionary(ctx, dictp, base_uri, source); - if (code) - return fz_rethrow(code, "cannot parse remote resource dictionary"); - return fz_okay; - } - - head = NULL; - - for (node = xml_down(root); node; node = xml_next(node)) - { - /* Usually "x:Key"; we have already processed and stripped namespace */ - key = xml_att(node, "Key"); - if (key) - { - entry = fz_malloc(sizeof(xps_resource)); - entry->name = key; - entry->base_uri = NULL; - entry->base_xml = NULL; - entry->data = node; - entry->next = head; - entry->parent = NULL; - head = entry; - } - } - - if (head) - { - head->base_uri = fz_strdup(base_uri); - } - - *dictp = head; - return fz_okay; -} - -void -xps_free_resource_dictionary(xps_context *ctx, xps_resource *dict) -{ - xps_resource *next; - while (dict) - { - next = dict->next; - if (dict->base_xml) - xml_free_element(dict->base_xml); - if (dict->base_uri) - fz_free(dict->base_uri); - fz_free(dict); - dict = next; - } -} - -void -xps_debug_resource_dictionary(xps_resource *dict) -{ - while (dict) - { - if (dict->base_uri) - printf("URI = '%s'\n", dict->base_uri); - printf("KEY = '%s' VAL = %p\n", dict->name, dict->data); - if (dict->parent) - { - printf("PARENT = {\n"); - xps_debug_resource_dictionary(dict->parent); - printf("}\n"); - } - dict = dict->next; - } -} diff --git a/xps/xpstiff.c b/xps/xpstiff.c deleted file mode 100644 index 4c977ab1..00000000 --- a/xps/xpstiff.c +++ /dev/null @@ -1,859 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -/* - * TIFF image loader. Should be enough to support TIFF files in XPS. - * Baseline TIFF 6.0 plus CMYK, LZW, Flate and JPEG support. - * Limited bit depths (1,2,4,8). - * Limited planar configurations (1=chunky). - * No tiles (easy fix if necessary). - * TODO: RGBPal images - */ - -typedef struct xps_tiff xps_tiff; - -struct xps_tiff -{ - /* "file" */ - byte *bp, *rp, *ep; - - /* byte order */ - unsigned order; - - /* where we can find the strips of image data */ - unsigned rowsperstrip; - unsigned *stripoffsets; - unsigned *stripbytecounts; - - /* colormap */ - unsigned *colormap; - - /* assorted tags */ - unsigned subfiletype; - unsigned photometric; - unsigned compression; - unsigned imagewidth; - unsigned imagelength; - unsigned samplesperpixel; - unsigned bitspersample; - unsigned planar; - unsigned extrasamples; - unsigned xresolution; - unsigned yresolution; - unsigned resolutionunit; - unsigned fillorder; - unsigned g3opts; - unsigned g4opts; - unsigned predictor; - - unsigned ycbcrsubsamp[2]; - - byte *jpegtables; /* point into "file" buffer */ - unsigned jpegtableslen; - - byte *profile; - int profilesize; - - /* decoded data */ - fz_colorspace *colorspace; - byte *samples; - int stride; -}; - -enum -{ - TII = 0x4949, /* 'II' */ - TMM = 0x4d4d, /* 'MM' */ - TBYTE = 1, - TASCII = 2, - TSHORT = 3, - TLONG = 4, - TRATIONAL = 5 -}; - -#define NewSubfileType 254 -#define ImageWidth 256 -#define ImageLength 257 -#define BitsPerSample 258 -#define Compression 259 -#define PhotometricInterpretation 262 -#define FillOrder 266 -#define StripOffsets 273 -#define SamplesPerPixel 277 -#define RowsPerStrip 278 -#define StripByteCounts 279 -#define XResolution 282 -#define YResolution 283 -#define PlanarConfiguration 284 -#define T4Options 292 -#define T6Options 293 -#define ResolutionUnit 296 -#define Predictor 317 -#define ColorMap 320 -#define TileWidth 322 -#define TileLength 323 -#define TileOffsets 324 -#define TileByteCounts 325 -#define ExtraSamples 338 -#define JPEGTables 347 -#define YCbCrSubSampling 520 -#define ICCProfile 34675 - -static const byte bitrev[256] = -{ - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, - 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, - 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, - 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, - 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, - 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, - 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, - 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, - 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, - 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, - 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, - 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, - 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, - 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, - 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, - 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, - 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, - 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, - 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, - 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff -}; - -static int -xps_decode_tiff_uncompressed(xps_context *ctx, xps_tiff *tiff, fz_stream *stm, byte *wp, int wlen) -{ - int n = fz_read(stm, wp, wlen); - fz_close(stm); - if (n < 0) - return fz_rethrow(n, "cannot read uncompressed strip"); - return fz_okay; -} - -static int -xps_decode_tiff_packbits(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) -{ - fz_stream *stm = fz_openrld(chain); - int n = fz_read(stm, wp, wlen); - fz_close(stm); - if (n < 0) - return fz_rethrow(n, "cannot read packbits strip"); - return fz_okay; -} - -static int -xps_decode_tiff_lzw(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) -{ - fz_stream *stm = fz_openlzwd(chain, NULL); - int n = fz_read(stm, wp, wlen); - fz_close(stm); - if (n < 0) - return fz_rethrow(n, "cannot read lzw strip"); - return fz_okay; -} -static int -xps_decode_tiff_flate(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) -{ - fz_stream *stm = fz_openflated(chain); - int n = fz_read(stm, wp, wlen); - fz_close(stm); - if (n < 0) - return fz_rethrow(n, "cannot read flate strip"); - return fz_okay; -} - -static int -xps_decode_tiff_fax(xps_context *ctx, xps_tiff *tiff, int comp, fz_stream *chain, byte *wp, int wlen) -{ - fz_stream *stm; - fz_obj *params; - fz_obj *columns, *rows, *blackis1, *k, *encodedbytealign; - int n; - - columns = fz_newint(tiff->imagewidth); - rows = fz_newint(tiff->imagelength); - blackis1 = fz_newbool(tiff->photometric == 0); - k = fz_newint(comp == 4 ? -1 : 0); - encodedbytealign = fz_newbool(comp == 2); - - params = fz_newdict(5); - fz_dictputs(params, "Columns", columns); - fz_dictputs(params, "Rows", rows); - fz_dictputs(params, "BlackIs1", blackis1); - fz_dictputs(params, "K", k); - fz_dictputs(params, "EncodedByteAlign", encodedbytealign); - - fz_dropobj(columns); - fz_dropobj(rows); - fz_dropobj(blackis1); - fz_dropobj(k); - fz_dropobj(encodedbytealign); - - stm = fz_openfaxd(chain, params); - n = fz_read(stm, wp, wlen); - fz_close(stm); - fz_dropobj(params); - - if (n < 0) - return fz_rethrow(n, "cannot read fax strip"); - return fz_okay; -} - -static int -xps_decode_tiff_jpeg(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) -{ - fz_stream *stm = fz_opendctd(chain, NULL); - int n = fz_read(stm, wp, wlen); - fz_close(stm); - if (n < 0) - return fz_rethrow(n, "cannot read jpeg strip"); - return fz_okay; -} - -static inline int -getcomp(byte *line, int x, int bpc) -{ - switch (bpc) - { - case 1: return (line[x >> 3] >> ( 7 - (x & 7) ) ) & 1; - case 2: return (line[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3; - case 4: return (line[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15; - case 8: return line[x]; - case 16: return line[x << 1] << 8 | line[(x << 1) + 1]; - } - return 0; -} - -static inline void -putcomp(byte *line, int x, int bpc, int value) -{ - int maxval = (1 << bpc) - 1; - - switch (bpc) - { - case 1: line[x >> 3] &= ~(maxval << (7 - (x & 7))); break; - case 2: line[x >> 2] &= ~(maxval << ((3 - (x & 3)) << 1)); break; - case 4: line[x >> 1] &= ~(maxval << ((1 - (x & 1)) << 2)); break; - } - - switch (bpc) - { - case 1: line[x >> 3] |= value << (7 - (x & 7)); break; - case 2: line[x >> 2] |= value << ((3 - (x & 3)) << 1); break; - case 4: line[x >> 1] |= value << ((1 - (x & 1)) << 2); break; - case 8: line[x] = value; break; - case 16: line[x << 1] = value >> 8; line[(x << 1) + 1] = value & 0xFF; break; - } -} - -static void -xps_unpredict_tiff(byte *line, int width, int comps, int bits) -{ - byte left[32]; - int i, k, v; - - for (k = 0; k < comps; k++) - left[k] = 0; - - for (i = 0; i < width; i++) - { - for (k = 0; k < comps; k++) - { - v = getcomp(line, i * comps + k, bits); - v = v + left[k]; - v = v % (1 << bits); - putcomp(line, i * comps + k, bits, v); - left[k] = v; - } - } -} - -static void -xps_invert_tiff(byte *line, int width, int comps, int bits, int alpha) -{ - int i, k, v; - int m = (1 << bits) - 1; - - for (i = 0; i < width; i++) - { - for (k = 0; k < comps; k++) - { - v = getcomp(line, i * comps + k, bits); - if (!alpha || k < comps - 1) - v = m - v; - putcomp(line, i * comps + k, bits, v); - } - } -} - -static int -xps_expand_tiff_colormap(xps_context *ctx, xps_tiff *tiff) -{ - int maxval = 1 << tiff->bitspersample; - byte *samples; - byte *src, *dst; - int stride; - int x, y; - - /* colormap has first all red, then all green, then all blue values */ - /* colormap values are 0..65535, bits is 4 or 8 */ - /* image can be with or without extrasamples: comps is 1 or 2 */ - - if (tiff->samplesperpixel != 1 && tiff->samplesperpixel != 2) - return fz_throw("invalid number of samples for RGBPal"); - - if (tiff->bitspersample != 4 && tiff->bitspersample != 8) - return fz_throw("invalid number of bits for RGBPal"); - - stride = tiff->imagewidth * (tiff->samplesperpixel + 2); - - samples = fz_malloc(stride * tiff->imagelength); - - for (y = 0; y < tiff->imagelength; y++) - { - src = tiff->samples + (tiff->stride * y); - dst = samples + (stride * y); - - for (x = 0; x < tiff->imagewidth; x++) - { - if (tiff->extrasamples) - { - int c = getcomp(src, x * 2, tiff->bitspersample); - int a = getcomp(src, x * 2 + 1, tiff->bitspersample); - *dst++ = tiff->colormap[c + 0] >> 8; - *dst++ = tiff->colormap[c + maxval] >> 8; - *dst++ = tiff->colormap[c + maxval * 2] >> 8; - *dst++ = a << (8 - tiff->bitspersample); - } - else - { - int c = getcomp(src, x, tiff->bitspersample); - *dst++ = tiff->colormap[c + 0] >> 8; - *dst++ = tiff->colormap[c + maxval] >> 8; - *dst++ = tiff->colormap[c + maxval * 2] >> 8; - } - } - } - - tiff->samplesperpixel += 2; - tiff->bitspersample = 8; - tiff->stride = stride; - tiff->samples = samples; - return fz_okay; -} - -static int -xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff) -{ - fz_buffer buf; - fz_stream *stm; - int error; - - /* switch on compression to create a filter */ - /* feed each strip to the filter */ - /* read out the data and pack the samples into an xps_image */ - - /* type 32773 / packbits -- nothing special (same row-padding as PDF) */ - /* type 2 / ccitt rle -- no EOL, no RTC, rows are byte-aligned */ - /* type 3 and 4 / g3 and g4 -- each strip starts new section */ - /* type 5 / lzw -- each strip is handled separately */ - - byte *wp; - unsigned row; - unsigned strip; - unsigned i; - - if (!tiff->rowsperstrip || !tiff->stripoffsets || !tiff->rowsperstrip) - return fz_throw("no image data in tiff; maybe it is tiled"); - - if (tiff->planar != 1) - return fz_throw("image data is not in chunky format"); - - tiff->stride = (tiff->imagewidth * tiff->samplesperpixel * tiff->bitspersample + 7) / 8; - - switch (tiff->photometric) - { - case 0: /* WhiteIsZero -- inverted */ - tiff->colorspace = fz_devicegray; - break; - case 1: /* BlackIsZero */ - tiff->colorspace = fz_devicegray; - break; - case 2: /* RGB */ - tiff->colorspace = fz_devicergb; - break; - case 3: /* RGBPal */ - tiff->colorspace = fz_devicergb; - break; - case 5: /* CMYK */ - tiff->colorspace = fz_devicecmyk; - break; - case 6: /* YCbCr */ - /* it's probably a jpeg ... we let jpeg convert to rgb */ - tiff->colorspace = fz_devicergb; - break; - default: - return fz_throw("unknown photometric: %d", tiff->photometric); - } - - switch (tiff->resolutionunit) - { - case 2: - /* no unit conversion needed */ - break; - case 3: - tiff->xresolution *= 2.54; - tiff->yresolution *= 2.54; - break; - default: - tiff->xresolution = 96; - tiff->yresolution = 96; - break; - } - - /* Note xres and yres could be 0 even if unit was set. If so default to 96dpi. */ - if (tiff->xresolution == 0 || tiff->yresolution == 0) - { - tiff->xresolution = 96; - tiff->yresolution = 96; - } - - tiff->samples = fz_calloc(tiff->imagelength, tiff->stride); - memset(tiff->samples, 0x55, tiff->imagelength * tiff->stride); - wp = tiff->samples; - - strip = 0; - for (row = 0; row < tiff->imagelength; row += tiff->rowsperstrip) - { - unsigned offset = tiff->stripoffsets[strip]; - unsigned rlen = tiff->stripbytecounts[strip]; - unsigned wlen = tiff->stride * tiff->rowsperstrip; - byte *rp = tiff->bp + offset; - - if (wp + wlen > tiff->samples + tiff->stride * tiff->imagelength) - wlen = tiff->samples + tiff->stride * tiff->imagelength - wp; - - if (rp + rlen > tiff->ep) - return fz_throw("strip extends beyond the end of the file"); - - /* the bits are in un-natural order */ - if (tiff->fillorder == 2) - for (i = 0; i < rlen; i++) - rp[i] = bitrev[rp[i]]; - - /* create a fz_buffer on the stack */ - buf.refs = 2; - buf.data = rp; - buf.len = rlen; - buf.cap = rlen; - - /* the strip decoders will close this */ - stm = fz_openbuffer(&buf); - - switch (tiff->compression) - { - case 1: - error = xps_decode_tiff_uncompressed(ctx, tiff, stm, wp, wlen); - break; - case 2: - error = xps_decode_tiff_fax(ctx, tiff, 2, stm, wp, wlen); - break; - case 3: - error = xps_decode_tiff_fax(ctx, tiff, 3, stm, wp, wlen); - break; - case 4: - error = xps_decode_tiff_fax(ctx, tiff, 4, stm, wp, wlen); - break; - case 5: - error = xps_decode_tiff_lzw(ctx, tiff, stm, wp, wlen); - break; - case 6: - error = fz_throw("deprecated JPEG in TIFF compression not supported"); - break; - case 7: - error = xps_decode_tiff_jpeg(ctx, tiff, stm, wp, wlen); - break; - case 8: - error = xps_decode_tiff_flate(ctx, tiff, stm, wp, wlen); - break; - case 32773: - error = xps_decode_tiff_packbits(ctx, tiff, stm, wp, wlen); - break; - default: - error = fz_throw("unknown TIFF compression: %d", tiff->compression); - } - - if (error) - return fz_rethrow(error, "cannot decode strip %d", row / tiff->rowsperstrip); - - /* scramble the bits back into original order */ - if (tiff->fillorder == 2) - for (i = 0; i < rlen; i++) - rp[i] = bitrev[rp[i]]; - - wp += tiff->stride * tiff->rowsperstrip; - strip ++; - } - - /* Predictor (only for LZW and Flate) */ - if ((tiff->compression == 5 || tiff->compression == 8) && tiff->predictor == 2) - { - byte *p = tiff->samples; - for (i = 0; i < tiff->imagelength; i++) - { - xps_unpredict_tiff(p, tiff->imagewidth, tiff->samplesperpixel, tiff->bitspersample); - p += tiff->stride; - } - } - - /* RGBPal */ - if (tiff->photometric == 3 && tiff->colormap) - { - error = xps_expand_tiff_colormap(ctx, tiff); - if (error) - return fz_rethrow(error, "cannot expand colormap"); - } - - /* WhiteIsZero .. invert */ - if (tiff->photometric == 0) - { - byte *p = tiff->samples; - for (i = 0; i < tiff->imagelength; i++) - { - xps_invert_tiff(p, tiff->imagewidth, tiff->samplesperpixel, tiff->bitspersample, tiff->extrasamples); - p += tiff->stride; - } - } - - return fz_okay; -} - -static inline int readbyte(xps_tiff *tiff) -{ - if (tiff->rp < tiff->ep) - return *tiff->rp++; - return EOF; -} - -static inline unsigned readshort(xps_tiff *tiff) -{ - unsigned a = readbyte(tiff); - unsigned b = readbyte(tiff); - if (tiff->order == TII) - return (b << 8) | a; - return (a << 8) | b; -} - -static inline unsigned readlong(xps_tiff *tiff) -{ - unsigned a = readbyte(tiff); - unsigned b = readbyte(tiff); - unsigned c = readbyte(tiff); - unsigned d = readbyte(tiff); - if (tiff->order == TII) - return (d << 24) | (c << 16) | (b << 8) | a; - return (a << 24) | (b << 16) | (c << 8) | d; -} - -static void -xps_read_tiff_bytes(unsigned char *p, xps_tiff *tiff, unsigned ofs, unsigned n) -{ - tiff->rp = tiff->bp + ofs; - if (tiff->rp > tiff->ep) - tiff->rp = tiff->bp; - - while (n--) - *p++ = readbyte(tiff); -} - -static void -xps_read_tiff_tag_value(unsigned *p, xps_tiff *tiff, unsigned type, unsigned ofs, unsigned n) -{ - tiff->rp = tiff->bp + ofs; - if (tiff->rp > tiff->ep) - tiff->rp = tiff->bp; - - while (n--) - { - switch (type) - { - case TRATIONAL: - *p = readlong(tiff); - *p = *p / readlong(tiff); - p ++; - break; - case TBYTE: *p++ = readbyte(tiff); break; - case TSHORT: *p++ = readshort(tiff); break; - case TLONG: *p++ = readlong(tiff); break; - default: *p++ = 0; break; - } - } -} - -static int -xps_read_tiff_tag(xps_context *ctx, xps_tiff *tiff, unsigned offset) -{ - unsigned tag; - unsigned type; - unsigned count; - unsigned value; - - tiff->rp = tiff->bp + offset; - - tag = readshort(tiff); - type = readshort(tiff); - count = readlong(tiff); - - if ((type == TBYTE && count <= 4) || - (type == TSHORT && count <= 2) || - (type == TLONG && count <= 1)) - value = tiff->rp - tiff->bp; - else - value = readlong(tiff); - - switch (tag) - { - case NewSubfileType: - xps_read_tiff_tag_value(&tiff->subfiletype, tiff, type, value, 1); - break; - case ImageWidth: - xps_read_tiff_tag_value(&tiff->imagewidth, tiff, type, value, 1); - break; - case ImageLength: - xps_read_tiff_tag_value(&tiff->imagelength, tiff, type, value, 1); - break; - case BitsPerSample: - xps_read_tiff_tag_value(&tiff->bitspersample, tiff, type, value, 1); - break; - case Compression: - xps_read_tiff_tag_value(&tiff->compression, tiff, type, value, 1); - break; - case PhotometricInterpretation: - xps_read_tiff_tag_value(&tiff->photometric, tiff, type, value, 1); - break; - case FillOrder: - xps_read_tiff_tag_value(&tiff->fillorder, tiff, type, value, 1); - break; - case SamplesPerPixel: - xps_read_tiff_tag_value(&tiff->samplesperpixel, tiff, type, value, 1); - break; - case RowsPerStrip: - xps_read_tiff_tag_value(&tiff->rowsperstrip, tiff, type, value, 1); - break; - case XResolution: - xps_read_tiff_tag_value(&tiff->xresolution, tiff, type, value, 1); - break; - case YResolution: - xps_read_tiff_tag_value(&tiff->yresolution, tiff, type, value, 1); - break; - case PlanarConfiguration: - xps_read_tiff_tag_value(&tiff->planar, tiff, type, value, 1); - break; - case T4Options: - xps_read_tiff_tag_value(&tiff->g3opts, tiff, type, value, 1); - break; - case T6Options: - xps_read_tiff_tag_value(&tiff->g4opts, tiff, type, value, 1); - break; - case Predictor: - xps_read_tiff_tag_value(&tiff->predictor, tiff, type, value, 1); - break; - case ResolutionUnit: - xps_read_tiff_tag_value(&tiff->resolutionunit, tiff, type, value, 1); - break; - case YCbCrSubSampling: - xps_read_tiff_tag_value(tiff->ycbcrsubsamp, tiff, type, value, 2); - break; - case ExtraSamples: - xps_read_tiff_tag_value(&tiff->extrasamples, tiff, type, value, 1); - break; - - case ICCProfile: - tiff->profile = fz_malloc(count); - /* ICC profile data type is set to UNDEFINED. - * TBYTE reading not correct in xps_read_tiff_tag_value */ - xps_read_tiff_bytes(tiff->profile, tiff, value, count); - tiff->profilesize = count; - break; - - case JPEGTables: - fz_warn("jpeg tables in tiff not implemented"); - tiff->jpegtables = tiff->bp + value; - tiff->jpegtableslen = count; - break; - - case StripOffsets: - tiff->stripoffsets = fz_calloc(count, sizeof(unsigned)); - xps_read_tiff_tag_value(tiff->stripoffsets, tiff, type, value, count); - break; - - case StripByteCounts: - tiff->stripbytecounts = fz_calloc(count, sizeof(unsigned)); - xps_read_tiff_tag_value(tiff->stripbytecounts, tiff, type, value, count); - break; - - case ColorMap: - tiff->colormap = fz_calloc(count, sizeof(unsigned)); - xps_read_tiff_tag_value(tiff->colormap, tiff, type, value, count); - break; - - case TileWidth: - case TileLength: - case TileOffsets: - case TileByteCounts: - return fz_throw("tiled tiffs not supported"); - - default: - /* printf("unknown tag: %d t=%d n=%d\n", tag, type, count); */ - break; - } - - return fz_okay; -} - -static void -xps_swap_byte_order(byte *buf, int n) -{ - int i, t; - for (i = 0; i < n; i++) - { - t = buf[i * 2 + 0]; - buf[i * 2 + 0] = buf[i * 2 + 1]; - buf[i * 2 + 1] = t; - } -} - -static int -xps_decode_tiff_header(xps_context *ctx, xps_tiff *tiff, byte *buf, int len) -{ - unsigned version; - unsigned offset; - unsigned count; - unsigned i; - int error; - - memset(tiff, 0, sizeof(xps_tiff)); - - tiff->bp = buf; - tiff->rp = buf; - tiff->ep = buf + len; - - /* tag defaults, where applicable */ - tiff->bitspersample = 1; - tiff->compression = 1; - tiff->samplesperpixel = 1; - tiff->resolutionunit = 2; - tiff->rowsperstrip = 0xFFFFFFFF; - tiff->fillorder = 1; - tiff->planar = 1; - tiff->subfiletype = 0; - tiff->predictor = 1; - tiff->ycbcrsubsamp[0] = 2; - tiff->ycbcrsubsamp[1] = 2; - - /* - * Read IFH - */ - - /* get byte order marker */ - tiff->order = TII; - tiff->order = readshort(tiff); - if (tiff->order != TII && tiff->order != TMM) - return fz_throw("not a TIFF file, wrong magic marker"); - - /* check version */ - version = readshort(tiff); - if (version != 42) - return fz_throw("not a TIFF file, wrong version marker"); - - /* get offset of IFD */ - offset = readlong(tiff); - - /* - * Read IFD - */ - - tiff->rp = tiff->bp + offset; - - count = readshort(tiff); - - offset += 2; - for (i = 0; i < count; i++) - { - error = xps_read_tiff_tag(ctx, tiff, offset); - if (error) - return fz_rethrow(error, "cannot read TIFF header tag"); - offset += 12; - } - - return fz_okay; -} - -int -xps_decode_tiff(xps_image **imagep, xps_context *ctx, byte *buf, int len) -{ - int error; - fz_pixmap *pixmap; - xps_image *image; - xps_tiff tiff; - - error = xps_decode_tiff_header(ctx, &tiff, buf, len); - if (error) - return fz_rethrow(error, "cannot decode tiff header"); - - /* Decode the image strips */ - - if (tiff.rowsperstrip > tiff.imagelength) - tiff.rowsperstrip = tiff.imagelength; - - error = xps_decode_tiff_strips(ctx, &tiff); - if (error) - return fz_rethrow(error, "cannot decode image data"); - - /* Byte swap 16-bit images to big endian if necessary */ - if (tiff.bitspersample == 16) - { - if (tiff.order == TII) - xps_swap_byte_order(tiff.samples, tiff.imagewidth * tiff.imagelength * tiff.samplesperpixel); - } - - /* Expand into fz_pixmap struct */ - - pixmap = fz_newpixmap(tiff.colorspace, 0, 0, tiff.imagewidth, tiff.imagelength); - - fz_unpacktile(pixmap, tiff.samples, tiff.samplesperpixel, tiff.bitspersample, tiff.stride, 0); - - /* We should only do this on non-pre-multiplied images, but files in the wild are bad */ - if (tiff.extrasamples /* == 2 */) - fz_premultiplypixmap(pixmap); - - image = fz_malloc(sizeof(xps_image)); - image->pixmap = pixmap; - image->xres = tiff.xresolution; - image->yres = tiff.yresolution; - - /* Clean up scratch memory */ - - if (tiff.colormap) fz_free(tiff.colormap); - if (tiff.stripoffsets) fz_free(tiff.stripoffsets); - if (tiff.stripbytecounts) fz_free(tiff.stripbytecounts); - if (tiff.samples) fz_free(tiff.samples); - - *imagep = image; - return fz_okay; -} diff --git a/xps/xpstile.c b/xps/xpstile.c deleted file mode 100644 index 82ec543f..00000000 --- a/xps/xpstile.c +++ /dev/null @@ -1,228 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -/* - * Parse a tiling brush (visual and image brushes at this time) common - * properties. Use the callback to draw the individual tiles. - */ - -enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y }; - -struct closure -{ - char *base_uri; - xps_resource *dict; - xml_element *root; - void *user; - void (*func)(xps_context*, fz_matrix, char*, xps_resource*, xml_element*, void*); -}; - -static void -xps_paint_tiling_brush_clipped(xps_context *ctx, fz_matrix ctm, fz_rect viewbox, struct closure *c) -{ - fz_path *path = fz_newpath(); - fz_moveto(path, viewbox.x0, viewbox.y0); - fz_lineto(path, viewbox.x0, viewbox.y1); - fz_lineto(path, viewbox.x1, viewbox.y1); - fz_lineto(path, viewbox.x1, viewbox.y0); - fz_closepath(path); - - ctx->dev->clippath(ctx->dev->user, path, 0, ctm); - - c->func(ctx, ctm, c->base_uri, c->dict, c->root, c->user); - - ctx->dev->popclip(ctx->dev->user); -} - -static void -xps_paint_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect viewbox, int tile_mode, struct closure *c) -{ - fz_matrix ttm; - - xps_paint_tiling_brush_clipped(ctx, ctm, viewbox, c); - - if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y) - { - ttm = fz_concat(fz_translate(viewbox.x1 * 2, 0), ctm); - ttm = fz_concat(fz_scale(-1, 1), ttm); - xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c); - } - - if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y) - { - ttm = fz_concat(fz_translate(0, viewbox.y1 * 2), ctm); - ttm = fz_concat(fz_scale(1, -1), ttm); - xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c); - } - - if (tile_mode == TILE_FLIP_X_Y) - { - ttm = fz_concat(fz_translate(viewbox.x1 * 2, viewbox.y1 * 2), ctm); - ttm = fz_concat(fz_scale(-1, -1), ttm); - xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c); - } -} - -void -xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, xml_element *root, - void (*func)(xps_context*, fz_matrix, char*, xps_resource*, xml_element*, void*), void *user) -{ - xml_element *node; - struct closure c; - - char *opacity_att; - char *transform_att; - char *viewbox_att; - char *viewport_att; - char *tile_mode_att; - char *viewbox_units_att; - char *viewport_units_att; - - xml_element *transform_tag = NULL; - - fz_matrix transform; - fz_rect viewbox; - fz_rect viewport; - float xstep, ystep; - float xscale, yscale; - int tile_mode; - - opacity_att = xml_att(root, "Opacity"); - transform_att = xml_att(root, "Transform"); - viewbox_att = xml_att(root, "Viewbox"); - viewport_att = xml_att(root, "Viewport"); - tile_mode_att = xml_att(root, "TileMode"); - viewbox_units_att = xml_att(root, "ViewboxUnits"); - viewport_units_att = xml_att(root, "ViewportUnits"); - - c.base_uri = base_uri; - c.dict = dict; - c.root = root; - c.user = user; - c.func = func; - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "ImageBrush.Transform")) - transform_tag = xml_down(node); - if (!strcmp(xml_tag(node), "VisualBrush.Transform")) - transform_tag = xml_down(node); - } - - xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); - - transform = fz_identity; - if (transform_att) - xps_parse_render_transform(ctx, transform_att, &transform); - if (transform_tag) - xps_parse_matrix_transform(ctx, transform_tag, &transform); - - viewbox = fz_unitrect; - if (viewbox_att) - xps_parse_rectangle(ctx, viewbox_att, &viewbox); - - viewport = fz_unitrect; - if (viewport_att) - xps_parse_rectangle(ctx, viewport_att, &viewport); - - /* some sanity checks on the viewport/viewbox size */ - if (fabs(viewport.x1 - viewport.x0) < 0.01) return; - if (fabs(viewport.y1 - viewport.y0) < 0.01) return; - if (fabs(viewbox.x1 - viewbox.x0) < 0.01) return; - if (fabs(viewbox.y1 - viewbox.y0) < 0.01) return; - - xstep = viewbox.x1 - viewbox.x0; - ystep = viewbox.y1 - viewbox.y0; - - xscale = (viewport.x1 - viewport.x0) / xstep; - yscale = (viewport.y1 - viewport.y0) / ystep; - - tile_mode = TILE_NONE; - if (tile_mode_att) - { - if (!strcmp(tile_mode_att, "None")) - tile_mode = TILE_NONE; - if (!strcmp(tile_mode_att, "Tile")) - tile_mode = TILE_TILE; - if (!strcmp(tile_mode_att, "FlipX")) - tile_mode = TILE_FLIP_X; - if (!strcmp(tile_mode_att, "FlipY")) - tile_mode = TILE_FLIP_Y; - if (!strcmp(tile_mode_att, "FlipXY")) - tile_mode = TILE_FLIP_X_Y; - } - - if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y) - xstep *= 2; - if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y) - ystep *= 2; - - xps_begin_opacity(ctx, ctm, area, base_uri, dict, opacity_att, NULL); - - ctm = fz_concat(transform, ctm); - ctm = fz_concat(fz_translate(viewport.x0, viewport.y0), ctm); - ctm = fz_concat(fz_scale(xscale, yscale), ctm); - ctm = fz_concat(fz_translate(-viewbox.x0, -viewbox.y0), ctm); - - if (tile_mode != TILE_NONE && !fz_isinfiniterect(area)) - { - fz_matrix invctm = fz_invertmatrix(ctm); - fz_rect bbox = fz_transformrect(invctm, area); - int x0 = floorf(bbox.x0 / xstep); - int y0 = floorf(bbox.y0 / ystep); - int x1 = ceilf(bbox.x1 / xstep); - int y1 = ceilf(bbox.y1 / ystep); - int x, y; - - for (y = y0; y < y1; y++) - { - for (x = x0; x < x1; x++) - { - fz_matrix ttm = fz_concat(fz_translate(xstep * x, ystep * y), ctm); - xps_paint_tiling_brush(ctx, ttm, viewbox, tile_mode, &c); - } - } - } - else - { - xps_paint_tiling_brush(ctx, ctm, viewbox, tile_mode, &c); - } - - xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); -} - -static void -xps_paint_visual_brush(xps_context *ctx, fz_matrix ctm, - char *base_uri, xps_resource *dict, xml_element *root, void *visual_tag) -{ - xps_parse_element(ctx, ctm, base_uri, dict, (xml_element *)visual_tag); -} - -void -xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, - char *base_uri, xps_resource *dict, xml_element *root) -{ - xml_element *node; - - char *visual_uri; - char *visual_att; - xml_element *visual_tag = NULL; - - visual_att = xml_att(root, "Visual"); - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "VisualBrush.Visual")) - visual_tag = xml_down(node); - } - - visual_uri = base_uri; - xps_resolve_resource_reference(ctx, dict, &visual_att, &visual_tag, &visual_uri); - - if (visual_tag) - { - xps_parse_tiling_brush(ctx, ctm, area, - visual_uri, dict, root, xps_paint_visual_brush, visual_tag); - } -} diff --git a/xps/xpstop.c b/xps/xpstop.c deleted file mode 100644 index 921d677a..00000000 --- a/xps/xpstop.c +++ /dev/null @@ -1,367 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -#ifdef _MSC_VER -#include -#else -#include -#endif - -char *output = NULL; -float resolution = 72; - -int showxml = 0; -int showtext = 0; -int showtime = 0; -int showmd5 = 0; -int savealpha = 0; -int uselist = 1; - -fz_colorspace *colorspace; -fz_glyphcache *glyphcache; -char *filename; - -struct { - int count, total; - int min, max; - int minpage, maxpage; -} timing; - -static void die(fz_error error) -{ - fz_catch(error, "aborting"); - exit(1); -} - -static void usage(void) -{ - fprintf(stderr, - "usage: xpsdraw [options] input.xps [pages]\n" - "\t-o -\toutput filename (%%d for page number)\n" - "\t\tsupported formats: pgm, ppm, pam, png\n" - "\t-r -\tresolution in dpi (default: 72)\n" - "\t-a\tsave alpha channel (only pam and png)\n" - "\t-g\trender in grayscale\n" - "\t-m\tshow timing information\n" - "\t-t\tshow text (-tt for xml)\n" - "\t-x\tshow display list\n" - "\t-d\tdisable use of display list\n" - "\t-5\tshow md5 checksums\n" - "\tpages\tcomma separated list of ranges\n"); - exit(1); -} - -static int gettime(void) -{ - static struct timeval first; - static int once = 1; - struct timeval now; - if (once) - { - gettimeofday(&first, NULL); - once = 0; - } - gettimeofday(&now, NULL); - return (now.tv_sec - first.tv_sec) * 1000 + (now.tv_usec - first.tv_usec) / 1000; -} - -static int isrange(char *s) -{ - while (*s) - { - if ((*s < '0' || *s > '9') && *s != '-' && *s != ',') - return 0; - s++; - } - return 1; -} - -static void -xps_run_page(xps_context *ctx, xps_page *page, fz_device *dev, fz_matrix ctm) -{ - ctx->dev = dev; - xps_parse_fixed_page(ctx, ctm, page); - ctx->dev = nil; -} - -static void drawpage(xps_context *ctx, int pagenum) -{ - xps_page *page; - fz_displaylist *list; - fz_device *dev; - int start; - - if (showtime) - { - start = gettime(); - } - - page = xps_load_page(ctx, pagenum - 1); - if (!page) - die(fz_throw("cannot load page %d in file '%s'", pagenum, filename)); - - list = nil; - - if (uselist) - { - list = fz_newdisplaylist(); - dev = fz_newlistdevice(list); - xps_run_page(ctx, page, dev, fz_identity); - fz_freedevice(dev); - } - - if (showxml) - { - dev = fz_newtracedevice(); - printf("\n", pagenum); - if (list) - fz_executedisplaylist(list, dev, fz_identity); - else - xps_run_page(ctx, page, dev, fz_identity); - printf("\n"); - fz_freedevice(dev); - } - - if (showtext) - { - fz_textspan *text = fz_newtextspan(); - dev = fz_newtextdevice(text); - if (list) - fz_executedisplaylist(list, dev, fz_identity); - else - xps_run_page(ctx, page, dev, fz_identity); - fz_freedevice(dev); - printf("[Page %d]\n", pagenum); - if (showtext > 1) - fz_debugtextspanxml(text); - else - fz_debugtextspan(text); - printf("\n"); - fz_freetextspan(text); - } - - if (showmd5 || showtime) - printf("page %s %d", filename, pagenum); - - if (output || showmd5 || showtime) - { - float zoom; - fz_matrix ctm; - fz_rect rect; - fz_bbox bbox; - fz_pixmap *pix; - - rect.x0 = rect.y0 = 0; - rect.x1 = page->width; - rect.y1 = page->height; - - zoom = resolution / 72; - ctm = fz_translate(0, -page->height); - ctm = fz_concat(ctm, fz_scale(zoom, zoom)); - bbox = fz_roundrect(fz_transformrect(ctm, rect)); - - /* TODO: banded rendering and multi-page ppm */ - - pix = fz_newpixmapwithrect(colorspace, bbox); - - if (savealpha) - fz_clearpixmap(pix); - else - fz_clearpixmapwithcolor(pix, 255); - - dev = fz_newdrawdevice(glyphcache, pix); - if (list) - fz_executedisplaylist(list, dev, ctm); - else - xps_run_page(ctx, page, dev, ctm); - fz_freedevice(dev); - - if (output) - { - char buf[512]; - sprintf(buf, output, pagenum); - if (strstr(output, ".pgm") || strstr(output, ".ppm") || strstr(output, ".pnm")) - fz_writepnm(pix, buf); - else if (strstr(output, ".pam")) - fz_writepam(pix, buf, savealpha); - else if (strstr(output, ".png")) - fz_writepng(pix, buf, savealpha); - } - - if (showmd5) - { - fz_md5 md5; - unsigned char digest[16]; - int i; - - fz_md5init(&md5); - fz_md5update(&md5, pix->samples, pix->w * pix->h * pix->n); - fz_md5final(&md5, digest); - - printf(" "); - for (i = 0; i < 16; i++) - printf("%02x", digest[i]); - } - - fz_droppixmap(pix); - } - - if (list) - fz_freedisplaylist(list); - - if (showtime) - { - int end = gettime(); - int diff = end - start; - - if (diff < timing.min) - { - timing.min = diff; - timing.minpage = pagenum; - } - if (diff > timing.max) - { - timing.max = diff; - timing.maxpage = pagenum; - } - timing.total += diff; - timing.count ++; - - printf(" %dms", diff); - } - - if (showmd5 || showtime) - printf("\n"); -} - - -static void drawrange(xps_context *ctx, char *range) -{ - int page, spage, epage; - char *spec, *dash; - - spec = fz_strsep(&range, ","); - while (spec) - { - dash = strchr(spec, '-'); - - if (dash == spec) - spage = epage = xps_count_pages(ctx); - else - spage = epage = atoi(spec); - - if (dash) - { - if (strlen(dash) > 1) - epage = atoi(dash + 1); - else - epage = xps_count_pages(ctx); - } - - spage = CLAMP(spage, 1, xps_count_pages(ctx)); - epage = CLAMP(epage, 1, xps_count_pages(ctx)); - - if (spage < epage) - for (page = spage; page <= epage; page++) - drawpage(ctx, page); - else - for (page = spage; page >= epage; page--) - drawpage(ctx, page); - - spec = fz_strsep(&range, ","); - } -} - -int main(int argc, char **argv) -{ - int grayscale = 0; - int accelerate = 1; - xps_context *ctx; - int code; - int c; - - while ((c = fz_getopt(argc, argv, "o:p:r:Aadgmtx5")) != -1) - { - switch (c) - { - case 'o': output = fz_optarg; break; - case 'r': resolution = atof(fz_optarg); break; - case 'A': accelerate = 0; break; - case 'a': savealpha = 1; break; - case 'm': showtime++; break; - case 't': showtext++; break; - case 'x': showxml++; break; - case '5': showmd5++; break; - case 'g': grayscale++; break; - case 'd': uselist = 0; break; - default: usage(); break; - } - } - - if (fz_optind == argc) - usage(); - - if (!showtext && !showxml && !showtime && !showmd5 && !output) - { - printf("nothing to do\n"); - exit(0); - } - - if (accelerate) - fz_accelerate(); - - glyphcache = fz_newglyphcache(); - - colorspace = fz_devicergb; - if (grayscale) - colorspace = fz_devicegray; - if (output && strstr(output, ".pgm")) - colorspace = fz_devicegray; - if (output && strstr(output, ".ppm")) - colorspace = fz_devicergb; - - timing.count = 0; - timing.total = 0; - timing.min = 1 << 30; - timing.max = 0; - timing.minpage = 0; - timing.maxpage = 0; - - if (showxml) - printf("\n"); - - while (fz_optind < argc) - { - filename = argv[fz_optind++]; - - ctx = xps_new_context(); - code = xps_open_file(ctx, filename); - if (code) - die(fz_rethrow(code, "cannot open document: %s", filename)); - - if (showxml) - printf("\n", filename); - - if (fz_optind == argc || !isrange(argv[fz_optind])) - drawrange(ctx, "1-"); - if (fz_optind < argc && isrange(argv[fz_optind])) - drawrange(ctx, argv[fz_optind++]); - - if (showxml) - printf("\n"); - - xps_free_context(ctx); - } - - if (showtime) - { - printf("total %dms / %d pages for an average of %dms\n", - timing.total, timing.count, timing.total / timing.count); - printf("fastest page %d: %dms\n", timing.minpage, timing.min); - printf("slowest page %d: %dms\n", timing.maxpage, timing.max); - } - - fz_freeglyphcache(glyphcache); - - return 0; -} diff --git a/xps/xpsxml.c b/xps/xpsxml.c deleted file mode 100644 index 397b68ec..00000000 --- a/xps/xpsxml.c +++ /dev/null @@ -1,387 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -struct attribute -{ - char name[32]; - char *value; - struct attribute *next; -}; - -struct element -{ - char name[32]; - struct attribute *atts; - struct element *up, *down, *next; -}; - -struct parser -{ - struct element *head; -}; - -static inline void indent(int n) -{ - while (n--) putchar(' '); -} - -void xml_print_element(struct element *item, int level) -{ - while (item) { - struct attribute *att; - indent(level); - printf("<%s", item->name); - for (att = item->atts; att; att = att->next) - printf(" %s=\"%s\"", att->name, att->value); - if (item->down) { - printf(">\n"); - xml_print_element(item->down, level + 1); - indent(level); - printf("\n", item->name); - } - else { - printf("/>\n"); - } - item = item->next; - } -} - -struct element *xml_next(struct element *item) -{ - return item->next; -} - -struct element *xml_down(struct element *item) -{ - return item->down; -} - -char *xml_tag(struct element *item) -{ - return item->name; -} - -char *xml_att(struct element *item, const char *name) -{ - struct attribute *att; - for (att = item->atts; att; att = att->next) - if (!strcmp(att->name, name)) - return att->value; - return NULL; -} - -static void xml_free_attribute(struct attribute *att) -{ - while (att) { - struct attribute *next = att->next; - if (att->value) - fz_free(att->value); - fz_free(att); - att = next; - } -} - -void xml_free_element(struct element *item) -{ - while (item) { - struct element *next = item->next; - if (item->atts) - xml_free_attribute(item->atts); - if (item->down) - xml_free_element(item->down); - fz_free(item); - item = next; - } -} - -static int xml_parse_entity(int *c, char *a) -{ - char *b; - if (a[1] == '#') { - if (a[2] == 'x') - *c = strtol(a + 3, &b, 16); - else - *c = strtol(a + 2, &b, 10); - if (*b == ';') - return b - a; - } - else if (a[1] == 'l' && a[2] == 't' && a[3] == ';') { - *c = '<'; - return 4; - } - else if (a[1] == 'g' && a[2] == 't' && a[3] == ';') { - *c = '>'; - return 4; - } - else if (a[1] == 'a' && a[2] == 'm' && a[3] == 'p' && a[4] == ';') { - *c = '&'; - return 5; - } - else if (a[1] == 'a' && a[2] == 'p' && a[3] == 'o' && a[4] == 's' && a[5] == ';') { - *c = '\''; - return 6; - } - else if (a[1] == 'q' && a[2] == 'u' && a[3] == 'o' && a[4] == 't' && a[5] == ';') { - *c = '"'; - return 6; - } - *c = *a++; - return 1; -} - -static void xml_emit_open_tag(struct parser *parser, char *a, char *b) -{ - struct element *head, *tail; - - head = fz_malloc(sizeof(struct element)); - if (b - a > sizeof(head->name)) - b = a + sizeof(head->name); - memcpy(head->name, a, b - a); - head->name[b - a] = 0; - - head->atts = NULL; - head->up = parser->head; - head->down = NULL; - head->next = NULL; - - if (!parser->head->down) { - parser->head->down = head; - } - else { - tail = parser->head->down; - while (tail->next) - tail = tail->next; - tail->next = head; - } - - parser->head = head; -} - -static void xml_emit_att_name(struct parser *parser, char *a, char *b) -{ - struct element *head = parser->head; - struct attribute *att; - - att = fz_malloc(sizeof(struct attribute)); - if (b - a > sizeof(att->name)) - b = a + sizeof(att->name); - memcpy(att->name, a, b - a); - att->name[b - a] = 0; - att->value = NULL; - att->next = head->atts; - head->atts = att; -} - -static void xml_emit_att_value(struct parser *parser, char *a, char *b) -{ - struct element *head = parser->head; - struct attribute *att = head->atts; - char *s; - int c; - - /* entities are all longer than UTFmax so runetochar is safe */ - s = att->value = fz_malloc(b - a + 1); - while (a < b) { - if (*a == '&') { - a += xml_parse_entity(&c, a); - s += runetochar(s, &c); - } - else { - *s++ = *a++; - } - } - *s = 0; -} - -static void xml_emit_close_tag(struct parser *parser) -{ - if (parser->head->up) - parser->head = parser->head->up; -} - -static inline int isname(int c) -{ - return c == '.' || c == '-' || c == '_' || c == ':' || - (c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z'); -} - -static inline int iswhite(int c) -{ - return c == ' ' || c == '\r' || c == '\n' || c == '\t'; -} - -static char *xml_parse_document_imp(struct parser *x, char *p) -{ - char *mark; - int quote; - -parse_text: - mark = p; - while (*p && *p != '<') ++p; - if (*p == '<') { ++p; goto parse_element; } - return NULL; - -parse_element: - if (*p == '/') { ++p; goto parse_closing_element; } - if (*p == '!') { ++p; goto parse_comment; } - if (*p == '?') { ++p; goto parse_processing_instruction; } - while (iswhite(*p)) ++p; - if (isname(*p)) - goto parse_element_name; - return "syntax error in element"; - -parse_comment: - if (*p == '[') goto parse_cdata; - if (*p++ != '-') return "syntax error in comment (') { - p += 3; - goto parse_text; - } - ++p; - } - return "end of data in comment"; - -parse_cdata: - if (p[1] != 'C' || p[2] != 'D' || p[3] != 'A' || p[4] != 'T' || p[5] != 'A' || p[6] != '[') - return "syntax error in CDATA section"; - p += 7; - mark = p; - while (*p) { - if (p[0] == ']' && p[1] == ']' && p[2] == '>') { - p += 3; - goto parse_text; - } - ++p; - } - return "end of data in CDATA section"; - -parse_processing_instruction: - while (*p) { - if (p[0] == '?' && p[1] == '>') { - p += 2; - goto parse_text; - } - ++p; - } - return "end of data in processing instruction"; - -parse_closing_element: - while (iswhite(*p)) ++p; - mark = p; - while (isname(*p)) ++p; - while (iswhite(*p)) ++p; - if (*p != '>') - return "syntax error in closing element"; - xml_emit_close_tag(x); - ++p; - goto parse_text; - -parse_element_name: - mark = p; - while (isname(*p)) ++p; - xml_emit_open_tag(x, mark, p); - if (*p == '>') { ++p; goto parse_text; } - if (p[0] == '/' && p[1] == '>') { - xml_emit_close_tag(x); - p += 2; - goto parse_text; - } - if (iswhite(*p)) - goto parse_attributes; - return "syntax error after element name"; - -parse_attributes: - while (iswhite(*p)) ++p; - if (isname(*p)) - goto parse_attribute_name; - if (*p == '>') { ++p; goto parse_text; } - if (p[0] == '/' && p[1] == '>') { - xml_emit_close_tag(x); - p += 2; - goto parse_text; - } - return "syntax error in attributes"; - -parse_attribute_name: - mark = p; - while (isname(*p)) ++p; - xml_emit_att_name(x, mark, p); - while (iswhite(*p)) ++p; - if (*p == '=') { ++p; goto parse_attribute_value; } - return "syntax error after attribute name"; - -parse_attribute_value: - while (iswhite(*p)) ++p; - quote = *p++; - if (quote != '"' && quote != '\'') - return "missing quote character"; - mark = p; - while (*p && *p != quote) ++p; - if (*p == quote) { - xml_emit_att_value(x, mark, p++); - goto parse_attributes; - } - return "end of data in attribute value"; -} - -static char *convert_to_utf8(unsigned char *s, int n) -{ - unsigned char *e = s + n; - char *dst, *d; - int c; - - if (s[0] == 0xFE && s[1] == 0xFF) { - dst = d = fz_malloc(n * 2); - while (s + 1 < e) { - c = s[0] << 8 | s[1]; - d += runetochar(d, &c); - s += 2; - } - *d = 0; - return dst; - } - - if (s[0] == 0xFF && s[1] == 0xFE) { - dst = d = fz_malloc(n * 2); - while (s + 1 < e) { - c = s[0] | s[1] << 8; - d += runetochar(d, &c); - s += 2; - } - *d = 0; - return dst; - } - - return (char*)s; -} - -struct element * -xml_parse_document(unsigned char *s, int n) -{ - struct parser parser; - struct element root; - char *p, *error; - - /* s is already null-terminated (see xps_new_part) */ - - memset(&root, 0, sizeof(root)); - parser.head = &root; - - p = convert_to_utf8(s, n); - - error = xml_parse_document_imp(&parser, p); - if (error) { - fz_throw(error); - return NULL; - } - - if (p != (char*)s) - fz_free(p); - - return root.down; -} diff --git a/xps/xpszip.c b/xps/xpszip.c deleted file mode 100644 index ad708bcb..00000000 --- a/xps/xpszip.c +++ /dev/null @@ -1,545 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -#include - -static inline int getshort(FILE *file) -{ - int a = getc(file); - int b = getc(file); - return a | (b << 8); -} - -static inline int getlong(FILE *file) -{ - int a = getc(file); - int b = getc(file); - int c = getc(file); - int d = getc(file); - return a | (b << 8) | (c << 16) | (d << 24); -} - -static void * -xps_zip_alloc_items(xps_context *ctx, int items, int size) -{ - return fz_calloc(items, size); -} - -static void -xps_zip_free(xps_context *ctx, void *ptr) -{ - fz_free(ptr); -} - -static int -xps_compare_entries(const void *a0, const void *b0) -{ - xps_entry *a = (xps_entry*) a0; - xps_entry *b = (xps_entry*) b0; - return xps_strcasecmp(a->name, b->name); -} - -static xps_entry * -xps_find_zip_entry(xps_context *ctx, char *name) -{ - int l = 0; - int r = ctx->zip_count - 1; - while (l <= r) - { - int m = (l + r) >> 1; - int c = xps_strcasecmp(name, ctx->zip_table[m].name); - if (c < 0) - r = m - 1; - else if (c > 0) - l = m + 1; - else - return &ctx->zip_table[m]; - } - return NULL; -} - -/* - * Inflate the data in a zip entry. - */ - -static int -xps_read_zip_entry(xps_context *ctx, xps_entry *ent, unsigned char *outbuf) -{ - z_stream stream; - unsigned char *inbuf; - int sig; - int version, general, method; - int namelength, extralength; - int code; - - fseek(ctx->file, ent->offset, 0); - - sig = getlong(ctx->file); - if (sig != ZIP_LOCAL_FILE_SIG) - return fz_throw("wrong zip local file signature (0x%x)", sig); - - version = getshort(ctx->file); - general = getshort(ctx->file); - method = getshort(ctx->file); - (void) getshort(ctx->file); /* file time */ - (void) getshort(ctx->file); /* file date */ - (void) getlong(ctx->file); /* crc-32 */ - (void) getlong(ctx->file); /* csize */ - (void) getlong(ctx->file); /* usize */ - namelength = getshort(ctx->file); - extralength = getshort(ctx->file); - - fseek(ctx->file, namelength + extralength, 1); - - if (method == 0) - { - fread(outbuf, 1, ent->usize, ctx->file); - } - else if (method == 8) - { - inbuf = fz_malloc(ent->csize); - - fread(inbuf, 1, ent->csize, ctx->file); - - memset(&stream, 0, sizeof(z_stream)); - stream.zalloc = (alloc_func) xps_zip_alloc_items; - stream.zfree = (free_func) xps_zip_free; - stream.opaque = ctx; - stream.next_in = inbuf; - stream.avail_in = ent->csize; - stream.next_out = outbuf; - stream.avail_out = ent->usize; - - code = inflateInit2(&stream, -15); - if (code != Z_OK) - return fz_throw("zlib inflateInit2 error: %s", stream.msg); - code = inflate(&stream, Z_FINISH); - if (code != Z_STREAM_END) - { - inflateEnd(&stream); - return fz_throw("zlib inflate error: %s", stream.msg); - } - code = inflateEnd(&stream); - if (code != Z_OK) - return fz_throw("zlib inflateEnd error: %s", stream.msg); - - fz_free(inbuf); - } - else - { - return fz_throw("unknown compression method (%d)", method); - } - - return fz_okay; -} - -/* - * Read the central directory in a zip file. - */ - -static int -xps_read_zip_dir(xps_context *ctx, int start_offset) -{ - int sig; - int offset, count; - int namesize, metasize, commentsize; - int i; - - fseek(ctx->file, start_offset, 0); - - sig = getlong(ctx->file); - if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG) - return fz_throw("wrong zip end of central directory signature (0x%x)", sig); - - (void) getshort(ctx->file); /* this disk */ - (void) getshort(ctx->file); /* start disk */ - (void) getshort(ctx->file); /* entries in this disk */ - count = getshort(ctx->file); /* entries in central directory disk */ - (void) getlong(ctx->file); /* size of central directory */ - offset = getlong(ctx->file); /* offset to central directory */ - - ctx->zip_count = count; - ctx->zip_table = fz_calloc(count, sizeof(xps_entry)); - memset(ctx->zip_table, 0, sizeof(xps_entry) * count); - - fseek(ctx->file, offset, 0); - - for (i = 0; i < count; i++) - { - sig = getlong(ctx->file); - if (sig != ZIP_CENTRAL_DIRECTORY_SIG) - return fz_throw("wrong zip central directory signature (0x%x)", sig); - - (void) getshort(ctx->file); /* version made by */ - (void) getshort(ctx->file); /* version to extract */ - (void) getshort(ctx->file); /* general */ - (void) getshort(ctx->file); /* method */ - (void) getshort(ctx->file); /* last mod file time */ - (void) getshort(ctx->file); /* last mod file date */ - (void) getlong(ctx->file); /* crc-32 */ - ctx->zip_table[i].csize = getlong(ctx->file); - ctx->zip_table[i].usize = getlong(ctx->file); - namesize = getshort(ctx->file); - metasize = getshort(ctx->file); - commentsize = getshort(ctx->file); - (void) getshort(ctx->file); /* disk number start */ - (void) getshort(ctx->file); /* int file atts */ - (void) getlong(ctx->file); /* ext file atts */ - ctx->zip_table[i].offset = getlong(ctx->file); - - ctx->zip_table[i].name = fz_malloc(namesize + 1); - fread(ctx->zip_table[i].name, 1, namesize, ctx->file); - ctx->zip_table[i].name[namesize] = 0; - - fseek(ctx->file, metasize, 1); - fseek(ctx->file, commentsize, 1); - } - - qsort(ctx->zip_table, count, sizeof(xps_entry), xps_compare_entries); - - return fz_okay; -} - -static int -xps_find_and_read_zip_dir(xps_context *ctx) -{ - int filesize, back, maxback; - int i, n; - char buf[512]; - - fseek(ctx->file, 0, SEEK_END); - filesize = ftell(ctx->file); - - maxback = MIN(filesize, 0xFFFF + sizeof buf); - back = MIN(maxback, sizeof buf); - - while (back < maxback) - { - fseek(ctx->file, filesize - back, 0); - - n = fread(buf, 1, sizeof buf, ctx->file); - if (n < 0) - return fz_throw("cannot read end of central directory"); - - for (i = n - 4; i > 0; i--) - if (!memcmp(buf + i, "PK\5\6", 4)) - return xps_read_zip_dir(ctx, filesize - back + i); - - back += sizeof buf - 4; - } - - return fz_throw("cannot find end of central directory"); -} - -/* - * Read and interleave split parts from a ZIP file. - */ - -static xps_part * -xps_read_zip_part(xps_context *ctx, char *partname) -{ - char buf[2048]; - xps_entry *ent; - xps_part *part; - int count, size, offset, i; - char *name; - - name = partname; - if (name[0] == '/') - name ++; - - /* All in one piece */ - ent = xps_find_zip_entry(ctx, name); - if (ent) - { - part = xps_new_part(ctx, partname, ent->usize); - xps_read_zip_entry(ctx, ent, part->data); - return part; - } - - /* Count the number of pieces and their total size */ - count = 0; - size = 0; - while (1) - { - sprintf(buf, "%s/[%d].piece", name, count); - ent = xps_find_zip_entry(ctx, buf); - if (!ent) - { - sprintf(buf, "%s/[%d].last.piece", name, count); - ent = xps_find_zip_entry(ctx, buf); - } - if (!ent) - break; - count ++; - size += ent->usize; - } - - /* Inflate the pieces */ - if (count) - { - part = xps_new_part(ctx, partname, size); - offset = 0; - for (i = 0; i < count; i++) - { - if (i < count - 1) - sprintf(buf, "%s/[%d].piece", name, i); - else - sprintf(buf, "%s/[%d].last.piece", name, i); - ent = xps_find_zip_entry(ctx, buf); - xps_read_zip_entry(ctx, ent, part->data + offset); - offset += ent->usize; - } - return part; - } - - return NULL; -} - -/* - * Read and interleave split parts from files in the directory. - */ - -static xps_part * -xps_read_dir_part(xps_context *ctx, char *name) -{ - char buf[2048]; - xps_part *part; - FILE *file; - int count, size, offset, i, n; - - fz_strlcpy(buf, ctx->directory, sizeof buf); - fz_strlcat(buf, name, sizeof buf); - - /* All in one piece */ - file = fopen(buf, "rb"); - if (file) - { - fseek(file, 0, SEEK_END); - size = ftell(file); - fseek(file, 0, SEEK_SET); - part = xps_new_part(ctx, name, size); - fread(part->data, 1, size, file); - fclose(file); - return part; - } - - /* Count the number of pieces and their total size */ - count = 0; - size = 0; - while (1) - { - sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, count); - file = fopen(buf, "rb"); - if (!file) - { - sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, count); - file = fopen(buf, "rb"); - } - if (!file) - break; - count ++; - fseek(file, 0, SEEK_END); - size += ftell(file); - fclose(file); - } - - /* Inflate the pieces */ - if (count) - { - part = xps_new_part(ctx, name, size); - offset = 0; - for (i = 0; i < count; i++) - { - if (i < count - 1) - sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, i); - else - sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, i); - file = fopen(buf, "rb"); - n = fread(part->data + offset, 1, size - offset, file); - offset += n; - fclose(file); - } - return part; - } - - return NULL; -} - -xps_part * -xps_read_part(xps_context *ctx, char *partname) -{ - if (ctx->directory) - return xps_read_dir_part(ctx, partname); - return xps_read_zip_part(ctx, partname); -} - -/* - * Read and process the XPS document. - */ - -static int -xps_read_and_process_metadata_part(xps_context *ctx, char *name) -{ - xps_part *part; - int code; - - part = xps_read_part(ctx, name); - if (!part) - return fz_rethrow(-1, "cannot read zip part '%s'", name); - - code = xps_parse_metadata(ctx, part); - if (code) - return fz_rethrow(code, "cannot process metadata part '%s'", name); - - xps_free_part(ctx, part); - - return fz_okay; -} - -/* - * Called by xpstop.c - */ - -int -xps_open_file(xps_context *ctx, char *filename) -{ - char buf[2048]; - xps_document *doc; - int code; - char *p; - - ctx->file = fopen(filename, "rb"); - if (!ctx->file) - return fz_throw("cannot open file: '%s'", filename); - - if (strstr(filename, "/_rels/.rels") || strstr(filename, "\\_rels\\.rels")) - { - fz_strlcpy(buf, filename, sizeof buf); - p = strstr(buf, "/_rels/.rels"); - if (!p) - p = strstr(buf, "\\_rels\\.rels"); - *p = 0; - ctx->directory = fz_strdup(buf); - } - else - { - code = xps_find_and_read_zip_dir(ctx); - if (code < 0) - return fz_rethrow(code, "cannot read zip central directory"); - } - - code = xps_read_and_process_metadata_part(ctx, "/_rels/.rels"); - if (code) - return fz_rethrow(code, "cannot process root relationship part"); - - if (!ctx->start_part) - return fz_throw("cannot find fixed document sequence start part"); - - code = xps_read_and_process_metadata_part(ctx, ctx->start_part); - if (code) - return fz_rethrow(code, "cannot process FixedDocumentSequence part"); - - for (doc = ctx->first_fixdoc; doc; doc = doc->next) - { - code = xps_read_and_process_metadata_part(ctx, doc->name); - if (code) - return fz_rethrow(code, "cannot process FixedDocument part"); - } - - return fz_okay; -} - -int -xps_count_pages(xps_context *ctx) -{ - xps_page *page; - int n = 0; - for (page = ctx->first_page; page; page = page->next) - n ++; - return n; -} - -xps_page * -xps_load_page(xps_context *ctx, int number) -{ - xps_page *page; - int code; - int n = 0; - - for (page = ctx->first_page; page; page = page->next) - { - if (n == number) - { - if (!page->root) - { - code = xps_load_fixed_page(ctx, page); - if (code) { - fz_rethrow(code, "cannot load page %d", number + 1); - return NULL; - } - } - return page; - } - n ++; - } - return nil; -} - -void -xps_free_page(xps_context *ctx, xps_page *page) -{ - if (page->root) - xml_free_element(page->root); - page->root = NULL; -} - -xps_context * -xps_new_context(void) -{ - xps_context *ctx; - - ctx = fz_malloc(sizeof(xps_context)); - - memset(ctx, 0, sizeof(xps_context)); - - ctx->font_table = xps_hash_new(ctx); - ctx->colorspace_table = xps_hash_new(ctx); - - ctx->start_part = NULL; - - return ctx; -} - -static void xps_free_key_func(xps_context *ctx, void *ptr) -{ - fz_free(ptr); -} - -static void xps_free_font_func(xps_context *ctx, void *ptr) -{ - fz_dropfont(ptr); -} - -/* Wrap up interp instance after a "job" */ -int -xps_free_context(xps_context *ctx) -{ - int i; - - if (ctx->file) - fclose(ctx->file); - - for (i = 0; i < ctx->zip_count; i++) - fz_free(ctx->zip_table[i].name); - fz_free(ctx->zip_table); - - /* TODO: free resources too */ - xps_hash_free(ctx, ctx->font_table, xps_free_key_func, xps_free_font_func); - xps_hash_free(ctx, ctx->colorspace_table, xps_free_key_func, NULL); - - xps_free_fixed_pages(ctx); - xps_free_fixed_documents(ctx); - - return 0; -} -- cgit v1.2.3 From 8e0110dcba95b991fc397a8c1b70c97953b1b8d5 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 3 Apr 2011 15:58:56 +0200 Subject: xps: Rearrange files, part three. --- Makefile | 1 - xps/muxps.h | 26 +++----- xps/xps_doc.c | 191 +++++++++++++++++++++++++++++++++++++++++++++------------ xps/xps_page.c | 154 ---------------------------------------------- xps/xps_path.c | 34 +++++----- xps/xps_tile.c | 116 +++++++++++++++++++++++++++++++++++ xps/xps_zip.c | 122 ++++++++---------------------------- 7 files changed, 318 insertions(+), 326 deletions(-) delete mode 100644 xps/xps_page.c diff --git a/Makefile b/Makefile index fcfbad39..da793520 100644 --- a/Makefile +++ b/Makefile @@ -179,7 +179,6 @@ MUXPS_SRC := \ xps/xps_hash.c \ xps/xps_image.c \ xps/xps_jpeg.c \ - xps/xps_page.c \ xps/xps_path.c \ xps/xps_png.c \ xps/xps_resource.c \ diff --git a/xps/muxps.h b/xps/muxps.h index e4595dbe..371b5983 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -96,14 +96,17 @@ struct xps_page_s char *name; int width; int height; - struct element *root; + xml_element *root; xps_page *next; }; -int xps_parse_metadata(xps_context *ctx, xps_part *part); -void xps_free_fixed_pages(xps_context *ctx); -void xps_free_fixed_documents(xps_context *ctx); -void xps_debug_fixdocseq(xps_context *ctx); +int xps_read_page_list(xps_context *ctx); +void xps_debug_page_list(xps_context *ctx); +void xps_free_page_list(xps_context *ctx); + +int xps_count_pages(xps_context *ctx); +xps_page *xps_load_page(xps_context *ctx, int number); +void xps_free_page(xps_context *ctx, xps_page *page); /* * Images. @@ -180,7 +183,6 @@ void xps_debug_resource_dictionary(xps_resource *dict); * Fixed page/graphics parsing. */ -int xps_load_fixed_page(xps_context *ctx, xps_page *page); void xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page); void xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node); void xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node); @@ -196,8 +198,6 @@ void xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char void xps_parse_matrix_transform(xps_context *ctx, xml_element *root, fz_matrix *matrix); void xps_parse_render_transform(xps_context *ctx, char *text, fz_matrix *matrix); void xps_parse_rectangle(xps_context *ctx, char *text, fz_rect *rect); -fz_path *xps_parse_abbreviated_geometry(xps_context *ctx, char *geom, int *fill_rule); -fz_path *xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xml_element *root, int stroking, int *fill_rule); void xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, char *opacity_att, xml_element *opacity_mask_tag); void xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, char *opacity_att, xml_element *opacity_mask_tag); @@ -207,10 +207,6 @@ void xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_reso void xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xml_element *clip_tag); -int xps_element_has_transparency(xps_context *ctx, char *base_uri, xml_element *node); -int xps_resource_dictionary_has_transparency(xps_context *ctx, char *base_uri, xml_element *node); -int xps_image_brush_has_transparency(xps_context *ctx, char *base_uri, xml_element *root); - /* * The interpreter context. */ @@ -258,12 +254,8 @@ struct xps_context_s fz_device *dev; }; -int xps_read_and_process_page_part(xps_context *ctx, fz_matrix ctm, char *name); -int xps_open_file(xps_context *ctx, char *filename); -int xps_count_pages(xps_context *ctx); -xps_page *xps_load_page(xps_context *ctx, int number); -void xps_free_page(xps_context *ctx, xps_page *page); xps_context *xps_new_context(void); +int xps_open_file(xps_context *ctx, char *filename); int xps_free_context(xps_context *ctx); #endif diff --git a/xps/xps_doc.c b/xps/xps_doc.c index 94fb32d7..c9a4eec5 100644 --- a/xps/xps_doc.c +++ b/xps/xps_doc.c @@ -1,35 +1,13 @@ #include "fitz.h" #include "muxps.h" -xps_part * -xps_new_part(xps_context *ctx, char *name, int size) -{ - xps_part *part; - - part = fz_malloc(sizeof(xps_part)); - part->name = fz_strdup(name); - part->size = size; - part->data = fz_malloc(size + 1); - part->data[size] = 0; /* null-terminate for xml parser */ - - return part; -} - -void -xps_free_part(xps_context *ctx, xps_part *part) -{ - fz_free(part->name); - fz_free(part->data); - fz_free(part); -} - /* * The FixedDocumentSequence and FixedDocument parts determine * which parts correspond to actual pages, and the page order. */ void -xps_debug_fixdocseq(xps_context *ctx) +xps_debug_page_list(xps_context *ctx) { xps_document *fixdoc = ctx->first_fixdoc; xps_page *page = ctx->first_page; @@ -76,21 +54,6 @@ xps_add_fixed_document(xps_context *ctx, char *name) } } -void -xps_free_fixed_documents(xps_context *ctx) -{ - xps_document *node = ctx->first_fixdoc; - while (node) - { - xps_document *next = node->next; - fz_free(node->name); - fz_free(node); - node = next; - } - ctx->first_fixdoc = NULL; - ctx->last_fixdoc = NULL; -} - static void xps_add_fixed_page(xps_context *ctx, char *name, int width, int height) { @@ -120,7 +83,7 @@ xps_add_fixed_page(xps_context *ctx, char *name, int width, int height) } } -void +static void xps_free_fixed_pages(xps_context *ctx) { xps_page *node = ctx->first_page; @@ -135,6 +98,28 @@ xps_free_fixed_pages(xps_context *ctx) ctx->last_page = NULL; } +static void +xps_free_fixed_documents(xps_context *ctx) +{ + xps_document *node = ctx->first_fixdoc; + while (node) + { + xps_document *next = node->next; + fz_free(node->name); + fz_free(node); + node = next; + } + ctx->first_fixdoc = NULL; + ctx->last_fixdoc = NULL; +} + +void +xps_free_page_list(xps_context *ctx) +{ + xps_free_fixed_documents(ctx); + xps_free_fixed_pages(ctx); +} + /* * Parse the fixed document sequence structure and _rels/.rels to find the start part. */ @@ -189,7 +174,7 @@ xps_parse_metadata_imp(xps_context *ctx, xml_element *item) } } -int +static int xps_parse_metadata(xps_context *ctx, xps_part *part) { xml_element *root; @@ -226,3 +211,129 @@ xps_parse_metadata(xps_context *ctx, xps_part *part) return fz_okay; } + +static int +xps_read_and_process_metadata_part(xps_context *ctx, char *name) +{ + xps_part *part; + int code; + + part = xps_read_part(ctx, name); + if (!part) + return fz_rethrow(-1, "cannot read zip part '%s'", name); + + code = xps_parse_metadata(ctx, part); + if (code) + return fz_rethrow(code, "cannot process metadata part '%s'", name); + + xps_free_part(ctx, part); + + return fz_okay; +} + +int +xps_read_page_list(xps_context *ctx) +{ + xps_document *doc; + int code; + + code = xps_read_and_process_metadata_part(ctx, "/_rels/.rels"); + if (code) + return fz_rethrow(code, "cannot process root relationship part"); + + if (!ctx->start_part) + return fz_throw("cannot find fixed document sequence start part"); + + code = xps_read_and_process_metadata_part(ctx, ctx->start_part); + if (code) + return fz_rethrow(code, "cannot process FixedDocumentSequence part"); + + for (doc = ctx->first_fixdoc; doc; doc = doc->next) + { + code = xps_read_and_process_metadata_part(ctx, doc->name); + if (code) + return fz_rethrow(code, "cannot process FixedDocument part"); + } + + return fz_okay; +} + +int +xps_count_pages(xps_context *ctx) +{ + xps_page *page; + int n = 0; + for (page = ctx->first_page; page; page = page->next) + n ++; + return n; +} + +static int +xps_load_fixed_page(xps_context *ctx, xps_page *page) +{ + xps_part *part; + xml_element *root; + char *width_att; + char *height_att; + + part = xps_read_part(ctx, page->name); + if (!part) + return fz_rethrow(-1, "cannot read zip part '%s'", page->name); + + root = xml_parse_document(part->data, part->size); + if (!root) + return fz_rethrow(-1, "cannot parse xml part '%s'", page->name); + + xps_free_part(ctx, part); + + if (strcmp(xml_tag(root), "FixedPage")) + return fz_throw("expected FixedPage element (found %s)", xml_tag(root)); + + width_att = xml_att(root, "Width"); + if (!width_att) + return fz_throw("FixedPage missing required attribute: Width"); + + height_att = xml_att(root, "Height"); + if (!height_att) + return fz_throw("FixedPage missing required attribute: Height"); + + page->width = atoi(width_att); + page->height = atoi(height_att); + page->root = root; + + return 0; +} + +xps_page * +xps_load_page(xps_context *ctx, int number) +{ + xps_page *page; + int code; + int n = 0; + + for (page = ctx->first_page; page; page = page->next) + { + if (n == number) + { + if (!page->root) + { + code = xps_load_fixed_page(ctx, page); + if (code) { + fz_rethrow(code, "cannot load page %d", number + 1); + return NULL; + } + } + return page; + } + n ++; + } + return nil; +} + +void +xps_free_page(xps_context *ctx, xps_page *page) +{ + if (page->root) + xml_free_element(page->root); + page->root = NULL; +} diff --git a/xps/xps_page.c b/xps/xps_page.c deleted file mode 100644 index 7659a1a7..00000000 --- a/xps/xps_page.c +++ /dev/null @@ -1,154 +0,0 @@ -#include "fitz.h" -#include "muxps.h" - -void -xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root) -{ - xps_resource *new_dict = NULL; - xml_element *node; - char *opacity_mask_uri; - int code; - - char *transform_att; - char *clip_att; - char *opacity_att; - char *opacity_mask_att; - - xml_element *transform_tag = NULL; - xml_element *clip_tag = NULL; - xml_element *opacity_mask_tag = NULL; - - fz_matrix transform; - - transform_att = xml_att(root, "RenderTransform"); - clip_att = xml_att(root, "Clip"); - opacity_att = xml_att(root, "Opacity"); - opacity_mask_att = xml_att(root, "OpacityMask"); - - for (node = xml_down(root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "Canvas.Resources") && xml_down(node)) - { - code = xps_parse_resource_dictionary(ctx, &new_dict, base_uri, xml_down(node)); - if (code) - fz_catch(code, "cannot load Canvas.Resources"); - else - { - new_dict->parent = dict; - dict = new_dict; - } - } - - if (!strcmp(xml_tag(node), "Canvas.RenderTransform")) - transform_tag = xml_down(node); - if (!strcmp(xml_tag(node), "Canvas.Clip")) - clip_tag = xml_down(node); - if (!strcmp(xml_tag(node), "Canvas.OpacityMask")) - opacity_mask_tag = xml_down(node); - } - - opacity_mask_uri = base_uri; - xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); - xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); - xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); - - transform = fz_identity; - if (transform_att) - xps_parse_render_transform(ctx, transform_att, &transform); - if (transform_tag) - xps_parse_matrix_transform(ctx, transform_tag, &transform); - ctm = fz_concat(transform, ctm); - - if (clip_att || clip_tag) - xps_clip(ctx, ctm, dict, clip_att, clip_tag); - - xps_begin_opacity(ctx, ctm, fz_infiniterect, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - - for (node = xml_down(root); node; node = xml_next(node)) - { - xps_parse_element(ctx, ctm, base_uri, dict, node); - } - - xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); - - if (clip_att || clip_tag) - ctx->dev->popclip(ctx->dev->user); - - if (new_dict) - xps_free_resource_dictionary(ctx, new_dict); -} - -void -xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page) -{ - xml_element *node; - xps_resource *dict; - char base_uri[1024]; - char *s; - int code; - - fz_strlcpy(base_uri, page->name, sizeof base_uri); - s = strrchr(base_uri, '/'); - if (s) - s[1] = 0; - - dict = NULL; - - ctx->opacity_top = 0; - ctx->opacity[0] = 1; - - if (!page->root) - return; - - for (node = xml_down(page->root); node; node = xml_next(node)) - { - if (!strcmp(xml_tag(node), "FixedPage.Resources") && xml_down(node)) - { - code = xps_parse_resource_dictionary(ctx, &dict, base_uri, xml_down(node)); - if (code) - fz_catch(code, "cannot load FixedPage.Resources"); - } - xps_parse_element(ctx, ctm, base_uri, dict, node); - } - - if (dict) - { - xps_free_resource_dictionary(ctx, dict); - } -} - -int -xps_load_fixed_page(xps_context *ctx, xps_page *page) -{ - xps_part *part; - xml_element *root; - char *width_att; - char *height_att; - - part = xps_read_part(ctx, page->name); - if (!part) - return fz_rethrow(-1, "cannot read zip part '%s'", page->name); - - root = xml_parse_document(part->data, part->size); - if (!root) - return fz_rethrow(-1, "cannot parse xml part '%s'", page->name); - - xps_free_part(ctx, part); - - if (strcmp(xml_tag(root), "FixedPage")) - return fz_throw("expected FixedPage element (found %s)", xml_tag(root)); - - width_att = xml_att(root, "Width"); - if (!width_att) - return fz_throw("FixedPage missing required attribute: Width"); - - height_att = xml_att(root, "Height"); - if (!height_att) - return fz_throw("FixedPage missing required attribute: Height"); - - page->width = atoi(width_att); - page->height = atoi(height_att); - page->root = root; - - return 0; -} diff --git a/xps/xps_path.c b/xps/xps_path.c index 00e57bee..5674eac2 100644 --- a/xps/xps_path.c +++ b/xps/xps_path.c @@ -35,22 +35,6 @@ fz_currentpoint(fz_path *path) return c; } -void -xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xml_element *clip_tag) -{ - fz_path *path; - int fill_rule = 0; - - if (clip_att) - path = xps_parse_abbreviated_geometry(ctx, clip_att, &fill_rule); - else if (clip_tag) - path = xps_parse_path_geometry(ctx, dict, clip_tag, 0, &fill_rule); - else - path = fz_newpath(); - ctx->dev->clippath(ctx->dev->user, path, fill_rule, ctm); - fz_freepath(path); -} - /* Draw an arc segment transformed by the matrix, we approximate with straight * line segments. We cannot use the fz_arc function because they only draw * circular arcs, we need to transform the line to make them elliptical but @@ -234,7 +218,7 @@ xps_draw_arc(fz_path *path, * build up a path. */ -fz_path * +static fz_path * xps_parse_abbreviated_geometry(xps_context *ctx, char *geom, int *fill_rule) { fz_path *path; @@ -762,6 +746,22 @@ xps_parse_line_cap(char *attr) return 0; } +void +xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xml_element *clip_tag) +{ + fz_path *path; + int fill_rule = 0; + + if (clip_att) + path = xps_parse_abbreviated_geometry(ctx, clip_att, &fill_rule); + else if (clip_tag) + path = xps_parse_path_geometry(ctx, dict, clip_tag, 0, &fill_rule); + else + path = fz_newpath(); + ctx->dev->clippath(ctx->dev->user, path, fill_rule, ctm); + fz_freepath(path); +} + /* * Parse an XPS element, and call relevant ghostscript * functions for drawing and/or clipping the child elements. diff --git a/xps/xps_tile.c b/xps/xps_tile.c index 82ec543f..353f769a 100644 --- a/xps/xps_tile.c +++ b/xps/xps_tile.c @@ -226,3 +226,119 @@ xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, visual_uri, dict, root, xps_paint_visual_brush, visual_tag); } } + +void +xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root) +{ + xps_resource *new_dict = NULL; + xml_element *node; + char *opacity_mask_uri; + int code; + + char *transform_att; + char *clip_att; + char *opacity_att; + char *opacity_mask_att; + + xml_element *transform_tag = NULL; + xml_element *clip_tag = NULL; + xml_element *opacity_mask_tag = NULL; + + fz_matrix transform; + + transform_att = xml_att(root, "RenderTransform"); + clip_att = xml_att(root, "Clip"); + opacity_att = xml_att(root, "Opacity"); + opacity_mask_att = xml_att(root, "OpacityMask"); + + for (node = xml_down(root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "Canvas.Resources") && xml_down(node)) + { + code = xps_parse_resource_dictionary(ctx, &new_dict, base_uri, xml_down(node)); + if (code) + fz_catch(code, "cannot load Canvas.Resources"); + else + { + new_dict->parent = dict; + dict = new_dict; + } + } + + if (!strcmp(xml_tag(node), "Canvas.RenderTransform")) + transform_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Canvas.Clip")) + clip_tag = xml_down(node); + if (!strcmp(xml_tag(node), "Canvas.OpacityMask")) + opacity_mask_tag = xml_down(node); + } + + opacity_mask_uri = base_uri; + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL); + xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); + + transform = fz_identity; + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + ctm = fz_concat(transform, ctm); + + if (clip_att || clip_tag) + xps_clip(ctx, ctm, dict, clip_att, clip_tag); + + xps_begin_opacity(ctx, ctm, fz_infiniterect, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + + for (node = xml_down(root); node; node = xml_next(node)) + { + xps_parse_element(ctx, ctm, base_uri, dict, node); + } + + xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + + if (clip_att || clip_tag) + ctx->dev->popclip(ctx->dev->user); + + if (new_dict) + xps_free_resource_dictionary(ctx, new_dict); +} + +void +xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page) +{ + xml_element *node; + xps_resource *dict; + char base_uri[1024]; + char *s; + int code; + + fz_strlcpy(base_uri, page->name, sizeof base_uri); + s = strrchr(base_uri, '/'); + if (s) + s[1] = 0; + + dict = NULL; + + ctx->opacity_top = 0; + ctx->opacity[0] = 1; + + if (!page->root) + return; + + for (node = xml_down(page->root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "FixedPage.Resources") && xml_down(node)) + { + code = xps_parse_resource_dictionary(ctx, &dict, base_uri, xml_down(node)); + if (code) + fz_catch(code, "cannot load FixedPage.Resources"); + } + xps_parse_element(ctx, ctm, base_uri, dict, node); + } + + if (dict) + { + xps_free_resource_dictionary(ctx, dict); + } +} diff --git a/xps/xps_zip.c b/xps/xps_zip.c index ad708bcb..89509cb3 100644 --- a/xps/xps_zip.c +++ b/xps/xps_zip.c @@ -3,6 +3,28 @@ #include +xps_part * +xps_new_part(xps_context *ctx, char *name, int size) +{ + xps_part *part; + + part = fz_malloc(sizeof(xps_part)); + part->name = fz_strdup(name); + part->size = size; + part->data = fz_malloc(size + 1); + part->data[size] = 0; /* null-terminate for xml parser */ + + return part; +} + +void +xps_free_part(xps_context *ctx, xps_part *part) +{ + fz_free(part->name); + fz_free(part->data); + fz_free(part); +} + static inline int getshort(FILE *file) { int a = getc(file); @@ -58,10 +80,6 @@ xps_find_zip_entry(xps_context *ctx, char *name) return NULL; } -/* - * Inflate the data in a zip entry. - */ - static int xps_read_zip_entry(xps_context *ctx, xps_entry *ent, unsigned char *outbuf) { @@ -234,7 +252,6 @@ xps_find_and_read_zip_dir(xps_context *ctx) /* * Read and interleave split parts from a ZIP file. */ - static xps_part * xps_read_zip_part(xps_context *ctx, char *partname) { @@ -299,7 +316,6 @@ xps_read_zip_part(xps_context *ctx, char *partname) /* * Read and interleave split parts from files in the directory. */ - static xps_part * xps_read_dir_part(xps_context *ctx, char *name) { @@ -374,38 +390,10 @@ xps_read_part(xps_context *ctx, char *partname) return xps_read_zip_part(ctx, partname); } -/* - * Read and process the XPS document. - */ - -static int -xps_read_and_process_metadata_part(xps_context *ctx, char *name) -{ - xps_part *part; - int code; - - part = xps_read_part(ctx, name); - if (!part) - return fz_rethrow(-1, "cannot read zip part '%s'", name); - - code = xps_parse_metadata(ctx, part); - if (code) - return fz_rethrow(code, "cannot process metadata part '%s'", name); - - xps_free_part(ctx, part); - - return fz_okay; -} - -/* - * Called by xpstop.c - */ - int xps_open_file(xps_context *ctx, char *filename) { char buf[2048]; - xps_document *doc; int code; char *p; @@ -429,71 +417,13 @@ xps_open_file(xps_context *ctx, char *filename) return fz_rethrow(code, "cannot read zip central directory"); } - code = xps_read_and_process_metadata_part(ctx, "/_rels/.rels"); - if (code) - return fz_rethrow(code, "cannot process root relationship part"); - - if (!ctx->start_part) - return fz_throw("cannot find fixed document sequence start part"); - - code = xps_read_and_process_metadata_part(ctx, ctx->start_part); + code = xps_read_page_list(ctx); if (code) - return fz_rethrow(code, "cannot process FixedDocumentSequence part"); - - for (doc = ctx->first_fixdoc; doc; doc = doc->next) - { - code = xps_read_and_process_metadata_part(ctx, doc->name); - if (code) - return fz_rethrow(code, "cannot process FixedDocument part"); - } + return fz_rethrow(code, "cannot read page list"); return fz_okay; } -int -xps_count_pages(xps_context *ctx) -{ - xps_page *page; - int n = 0; - for (page = ctx->first_page; page; page = page->next) - n ++; - return n; -} - -xps_page * -xps_load_page(xps_context *ctx, int number) -{ - xps_page *page; - int code; - int n = 0; - - for (page = ctx->first_page; page; page = page->next) - { - if (n == number) - { - if (!page->root) - { - code = xps_load_fixed_page(ctx, page); - if (code) { - fz_rethrow(code, "cannot load page %d", number + 1); - return NULL; - } - } - return page; - } - n ++; - } - return nil; -} - -void -xps_free_page(xps_context *ctx, xps_page *page) -{ - if (page->root) - xml_free_element(page->root); - page->root = NULL; -} - xps_context * xps_new_context(void) { @@ -521,7 +451,6 @@ static void xps_free_font_func(xps_context *ctx, void *ptr) fz_dropfont(ptr); } -/* Wrap up interp instance after a "job" */ int xps_free_context(xps_context *ctx) { @@ -538,8 +467,7 @@ xps_free_context(xps_context *ctx) xps_hash_free(ctx, ctx->font_table, xps_free_key_func, xps_free_font_func); xps_hash_free(ctx, ctx->colorspace_table, xps_free_key_func, NULL); - xps_free_fixed_pages(ctx); - xps_free_fixed_documents(ctx); + xps_free_page_list(ctx); return 0; } -- cgit v1.2.3 From 6af0abc477539365a19a4797babfd567da4d375f Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 3 Apr 2011 15:26:44 +0200 Subject: xps: Remove xps_context from hash table implementation. --- xps/muxps.h | 10 +++++----- xps/xps_glyphs.c | 2 +- xps/xps_hash.c | 20 ++++++++++---------- xps/xps_zip.c | 13 ++++++------- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/xps/muxps.h b/xps/muxps.h index 371b5983..f3f2cdf4 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -38,12 +38,12 @@ void xps_absolute_path(char *output, char *base_uri, char *path, int output_size typedef struct xps_hash_table_s xps_hash_table; -xps_hash_table *xps_hash_new(xps_context *ctx); +xps_hash_table *xps_hash_new(void); void *xps_hash_lookup(xps_hash_table *table, char *key); -int xps_hash_insert(xps_context *ctx, xps_hash_table *table, char *key, void *value); -void xps_hash_free(xps_context *ctx, xps_hash_table *table, - void (*free_key)(xps_context *ctx, void *), - void (*free_value)(xps_context *ctx, void *)); +int xps_hash_insert(xps_hash_table *table, char *key, void *value); +void xps_hash_free(xps_hash_table *table, + void (*free_key)(void *), + void (*free_value)(void *)); void xps_hash_debug(xps_hash_table *table); /* diff --git a/xps/xps_glyphs.c b/xps/xps_glyphs.c index 91c7dbcd..9d0d9fca 100644 --- a/xps/xps_glyphs.c +++ b/xps/xps_glyphs.c @@ -481,7 +481,7 @@ xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, xps_select_best_font_encoding(font); - xps_hash_insert(ctx, ctx->font_table, part->name, font); + xps_hash_insert(ctx->font_table, part->name, font); /* NOTE: we kept part->name in the hashtable and part->data in the font */ fz_free(part); diff --git a/xps/xps_hash.c b/xps/xps_hash.c index 71e97097..f2a7d48a 100644 --- a/xps/xps_hash.c +++ b/xps/xps_hash.c @@ -48,7 +48,7 @@ xps_hash(char *s) } xps_hash_table * -xps_hash_new(xps_context *ctx) +xps_hash_new(void) { xps_hash_table *table; @@ -63,7 +63,7 @@ xps_hash_new(xps_context *ctx) } static int -xps_hash_double(xps_context *ctx, xps_hash_table *table) +xps_hash_double(xps_hash_table *table) { xps_hash_entry *old_entries; xps_hash_entry *new_entries; @@ -91,7 +91,7 @@ xps_hash_double(xps_context *ctx, xps_hash_table *table) for (i = 0; i < old_size; i++) if (old_entries[i].value) - xps_hash_insert(ctx, table, old_entries[i].key, old_entries[i].value); + xps_hash_insert(table, old_entries[i].key, old_entries[i].value); fz_free(old_entries); @@ -99,18 +99,18 @@ xps_hash_double(xps_context *ctx, xps_hash_table *table) } void -xps_hash_free(xps_context *ctx, xps_hash_table *table, - void (*free_key)(xps_context *ctx, void *), - void (*free_value)(xps_context *ctx, void *)) +xps_hash_free(xps_hash_table *table, + void (*free_key)(void *), + void (*free_value)(void *)) { int i; for (i = 0; i < table->size; i++) { if (table->entries[i].key && free_key) - free_key(ctx, table->entries[i].key); + free_key(table->entries[i].key); if (table->entries[i].value && free_value) - free_value(ctx, table->entries[i].value); + free_value(table->entries[i].value); } fz_free(table->entries); @@ -137,7 +137,7 @@ xps_hash_lookup(xps_hash_table *table, char *key) } int -xps_hash_insert(xps_context *ctx, xps_hash_table *table, char *key, void *value) +xps_hash_insert(xps_hash_table *table, char *key, void *value) { xps_hash_entry *entries; unsigned int size, pos; @@ -145,7 +145,7 @@ xps_hash_insert(xps_context *ctx, xps_hash_table *table, char *key, void *value) /* Grow the table at 80% load */ if (table->load > table->size * 8 / 10) { - if (xps_hash_double(ctx, table) < 0) + if (xps_hash_double(table) < 0) return fz_rethrow(-1, "cannot grow hash table"); } diff --git a/xps/xps_zip.c b/xps/xps_zip.c index 89509cb3..d353ec11 100644 --- a/xps/xps_zip.c +++ b/xps/xps_zip.c @@ -433,20 +433,20 @@ xps_new_context(void) memset(ctx, 0, sizeof(xps_context)); - ctx->font_table = xps_hash_new(ctx); - ctx->colorspace_table = xps_hash_new(ctx); + ctx->font_table = xps_hash_new(); + ctx->colorspace_table = xps_hash_new(); ctx->start_part = NULL; return ctx; } -static void xps_free_key_func(xps_context *ctx, void *ptr) +static void xps_free_key_func(void *ptr) { fz_free(ptr); } -static void xps_free_font_func(xps_context *ctx, void *ptr) +static void xps_free_font_func(void *ptr) { fz_dropfont(ptr); } @@ -463,9 +463,8 @@ xps_free_context(xps_context *ctx) fz_free(ctx->zip_table[i].name); fz_free(ctx->zip_table); - /* TODO: free resources too */ - xps_hash_free(ctx, ctx->font_table, xps_free_key_func, xps_free_font_func); - xps_hash_free(ctx, ctx->colorspace_table, xps_free_key_func, NULL); + xps_hash_free(ctx->font_table, xps_free_key_func, xps_free_font_func); + xps_hash_free(ctx->colorspace_table, xps_free_key_func, NULL); xps_free_page_list(ctx); -- cgit v1.2.3 From 8975aec496710bb9e35a4a1cb635ee0d4eaa60cc Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 3 Apr 2011 15:17:00 +0200 Subject: xps: Use fz_pixmap directly instead of wrapping it in xps_image. --- fitz/fitz.h | 1 + fitz/res_pixmap.c | 2 ++ xps/muxps.h | 20 +++----------- xps/xps_image.c | 35 +++++++++--------------- xps/xps_jpeg.c | 15 +++++------ xps/xps_png.c | 32 +++++++++------------- xps/xps_tiff.c | 80 +++++++++++++++++++++++++------------------------------ 7 files changed, 75 insertions(+), 110 deletions(-) diff --git a/fitz/fitz.h b/fitz/fitz.h index d4ab8ff4..39542c0d 100644 --- a/fitz/fitz.h +++ b/fitz/fitz.h @@ -673,6 +673,7 @@ struct fz_pixmap_s int x, y, w, h, n; fz_pixmap *mask; /* explicit soft/image mask */ int interpolate; + int xres, yres; fz_colorspace *colorspace; unsigned char *samples; int freesamples; diff --git a/fitz/res_pixmap.c b/fitz/res_pixmap.c index 93fdb307..9f336ab1 100644 --- a/fitz/res_pixmap.c +++ b/fitz/res_pixmap.c @@ -13,6 +13,8 @@ fz_newpixmapwithdata(fz_colorspace *colorspace, int x, int y, int w, int h, unsi pix->h = h; pix->mask = nil; pix->interpolate = 1; + pix->xres = 96; + pix->yres = 96; pix->colorspace = nil; pix->n = 1; diff --git a/xps/muxps.h b/xps/muxps.h index f3f2cdf4..067d4780 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -112,23 +112,9 @@ void xps_free_page(xps_context *ctx, xps_page *page); * Images. */ -typedef struct xps_image xps_image; - -/* type for the information derived directly from the raster file format */ - -struct xps_image -{ - fz_pixmap *pixmap; - int xres; - int yres; -}; - -int xps_decode_jpeg(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen); -int xps_decode_png(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen); -int xps_decode_tiff(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen); -int xps_decode_jpegxr(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen); - -void xps_free_image(xps_context *ctx, xps_image *image); +int xps_decode_jpeg(fz_pixmap **imagep, byte *rbuf, int rlen); +int xps_decode_png(fz_pixmap **imagep, byte *rbuf, int rlen); +int xps_decode_tiff(fz_pixmap **imagep, byte *rbuf, int rlen); /* * Fonts. diff --git a/xps/xps_image.c b/xps/xps_image.c index ced58bb5..f4d02f78 100644 --- a/xps/xps_image.c +++ b/xps/xps_image.c @@ -2,10 +2,8 @@ #include "muxps.h" static int -xps_decode_image(xps_image **imagep, xps_context *ctx, xps_part *part) +xps_decode_image(fz_pixmap **imagep, byte *buf, int len) { - byte *buf = part->data; - int len = part->size; int error; if (len < 8) @@ -13,13 +11,13 @@ xps_decode_image(xps_image **imagep, xps_context *ctx, xps_part *part) if (buf[0] == 0xff && buf[1] == 0xd8) { - error = xps_decode_jpeg(imagep, ctx, buf, len); + error = xps_decode_jpeg(imagep, buf, len); if (error) return fz_rethrow(error, "cannot decode jpeg image"); } else if (memcmp(buf, "\211PNG\r\n\032\n", 8) == 0) { - error = xps_decode_png(imagep, ctx, buf, len); + error = xps_decode_png(imagep, buf, len); if (error) return fz_rethrow(error, "cannot decode png image"); } @@ -29,7 +27,7 @@ xps_decode_image(xps_image **imagep, xps_context *ctx, xps_part *part) } else if (memcmp(buf, "MM", 2) == 0 || memcmp(buf, "II", 2) == 0) { - error = xps_decode_tiff(imagep, ctx, buf, len); + error = xps_decode_tiff(imagep, buf, len); if (error) return fz_rethrow(error, "cannot decode TIFF image"); } @@ -40,12 +38,12 @@ xps_decode_image(xps_image **imagep, xps_context *ctx, xps_part *part) } static void -xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root, void *vimage) +xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, + xml_element *root, void *vimage) { - xps_image *image = vimage; - fz_pixmap *pixmap = image->pixmap; - float xs = pixmap->w * 96.0 / image->xres; - float ys = pixmap->h * 96.0 / image->yres; + fz_pixmap *pixmap = vimage; + float xs = pixmap->w * 96.0 / pixmap->xres; + float ys = pixmap->h * 96.0 / pixmap->yres; fz_matrix im = fz_scale(xs, -ys); im.f = ys; ctm = fz_concat(im, ctm); @@ -107,7 +105,7 @@ xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *root) { xps_part *part; - xps_image *image; + fz_pixmap *image; int code; part = xps_find_image_brush_source_part(ctx, base_uri, root); @@ -116,22 +114,15 @@ xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, return; } - code = xps_decode_image(&image, ctx, part); + code = xps_decode_image(&image, part->data, part->size); if (code < 0) { + xps_free_part(ctx, part); fz_catch(-1, "cannot decode image resource"); return; } xps_parse_tiling_brush(ctx, ctm, area, base_uri, dict, root, xps_paint_image_brush, image); - xps_free_image(ctx, image); + fz_droppixmap(image); xps_free_part(ctx, part); } - -void -xps_free_image(xps_context *ctx, xps_image *image) -{ - if (image->pixmap) - fz_droppixmap(image->pixmap); - fz_free(image); -} diff --git a/xps/xps_jpeg.c b/xps/xps_jpeg.c index 4dc2a7e4..a8ea40f5 100644 --- a/xps/xps_jpeg.c +++ b/xps/xps_jpeg.c @@ -48,7 +48,7 @@ static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) } int -xps_decode_jpeg(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen) +xps_decode_jpeg(fz_pixmap **imagep, byte *rbuf, int rlen) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr_jmp err; @@ -57,12 +57,12 @@ xps_decode_jpeg(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen) fz_colorspace *colorspace; int x, k; - xps_image *image = NULL; + fz_pixmap *image = NULL; if (setjmp(err.env)) { if (image) - xps_free_image(ctx, image); + fz_droppixmap(image); return fz_throw("jpeg error: %s", err.msg); } @@ -93,10 +93,7 @@ xps_decode_jpeg(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen) else return fz_throw("bad number of components in jpeg: %d", cinfo.output_components); - image = fz_malloc(sizeof(xps_image)); - image->pixmap = fz_newpixmap(colorspace, 0, 0, cinfo.output_width, cinfo.output_height); - image->xres = 96; - image->yres = 96; + image = fz_newpixmap(colorspace, 0, 0, cinfo.output_width, cinfo.output_height); if (cinfo.density_unit == 1) { @@ -109,10 +106,10 @@ xps_decode_jpeg(xps_image **imagep, xps_context *ctx, byte *rbuf, int rlen) image->yres = cinfo.Y_density * 2.54; } - fz_clearpixmap(image->pixmap); + fz_clearpixmap(image); row[0] = fz_malloc(cinfo.output_components * cinfo.output_width); - dp = image->pixmap->samples; + dp = image->samples; while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, row, 1); diff --git a/xps/xps_png.c b/xps/xps_png.c index f59be56d..829bde90 100644 --- a/xps/xps_png.c +++ b/xps/xps_png.c @@ -489,6 +489,9 @@ png_expand_palette(struct info *info, fz_pixmap *src) unsigned char *dp = dst->samples; int x, y; + dst->xres = src->xres; + dst->yres = src->yres; + for (y = 0; y < info->height; y++) { for (x = 0; x < info->width; x++) @@ -531,11 +534,10 @@ png_mask_transparency(struct info *info, fz_pixmap *dst) } int -xps_decode_png(xps_image **imagep, xps_context *ctx, byte *p, int total) +xps_decode_png(fz_pixmap **imagep, byte *p, int total) { - fz_pixmap *pixmap; + fz_pixmap *image; fz_colorspace *colorspace; - xps_image *image; struct info png; int code; int stride; @@ -551,30 +553,22 @@ xps_decode_png(xps_image **imagep, xps_context *ctx, byte *p, int total) stride = (png.width * png.n * png.depth + 7) / 8; - pixmap = fz_newpixmap(colorspace, 0, 0, png.width, png.height); - fz_unpacktile(pixmap, png.samples, png.n, png.depth, stride, png.indexed); + image = fz_newpixmap(colorspace, 0, 0, png.width, png.height); + image->xres = png.xres; + image->yres = png.yres; + + fz_unpacktile(image, png.samples, png.n, png.depth, stride, png.indexed); if (png.indexed) - { - pixmap = png_expand_palette(&png, pixmap); - } + image = png_expand_palette(&png, image); else if (png.transparency) - { - png_mask_transparency(&png, pixmap); - } + png_mask_transparency(&png, image); if (png.transparency || png.n == 2 || png.n == 4) - { - fz_premultiplypixmap(pixmap); - } + fz_premultiplypixmap(image); fz_free(png.samples); - image = fz_malloc(sizeof(xps_image)); - image->pixmap = pixmap; - image->xres = png.xres; - image->yres = png.yres; - *imagep = image; return fz_okay; } diff --git a/xps/xps_tiff.c b/xps/xps_tiff.c index 4c977ab1..d70d5f87 100644 --- a/xps/xps_tiff.c +++ b/xps/xps_tiff.c @@ -10,9 +10,7 @@ * TODO: RGBPal images */ -typedef struct xps_tiff xps_tiff; - -struct xps_tiff +struct tiff { /* "file" */ byte *bp, *rp, *ep; @@ -136,7 +134,7 @@ static const byte bitrev[256] = }; static int -xps_decode_tiff_uncompressed(xps_context *ctx, xps_tiff *tiff, fz_stream *stm, byte *wp, int wlen) +xps_decode_tiff_uncompressed(struct tiff *tiff, fz_stream *stm, byte *wp, int wlen) { int n = fz_read(stm, wp, wlen); fz_close(stm); @@ -146,7 +144,7 @@ xps_decode_tiff_uncompressed(xps_context *ctx, xps_tiff *tiff, fz_stream *stm, b } static int -xps_decode_tiff_packbits(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) +xps_decode_tiff_packbits(struct tiff *tiff, fz_stream *chain, byte *wp, int wlen) { fz_stream *stm = fz_openrld(chain); int n = fz_read(stm, wp, wlen); @@ -157,7 +155,7 @@ xps_decode_tiff_packbits(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byt } static int -xps_decode_tiff_lzw(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) +xps_decode_tiff_lzw(struct tiff *tiff, fz_stream *chain, byte *wp, int wlen) { fz_stream *stm = fz_openlzwd(chain, NULL); int n = fz_read(stm, wp, wlen); @@ -167,7 +165,7 @@ xps_decode_tiff_lzw(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp return fz_okay; } static int -xps_decode_tiff_flate(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) +xps_decode_tiff_flate(struct tiff *tiff, fz_stream *chain, byte *wp, int wlen) { fz_stream *stm = fz_openflated(chain); int n = fz_read(stm, wp, wlen); @@ -178,7 +176,7 @@ xps_decode_tiff_flate(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte * } static int -xps_decode_tiff_fax(xps_context *ctx, xps_tiff *tiff, int comp, fz_stream *chain, byte *wp, int wlen) +xps_decode_tiff_fax(struct tiff *tiff, int comp, fz_stream *chain, byte *wp, int wlen) { fz_stream *stm; fz_obj *params; @@ -215,7 +213,7 @@ xps_decode_tiff_fax(xps_context *ctx, xps_tiff *tiff, int comp, fz_stream *chain } static int -xps_decode_tiff_jpeg(xps_context *ctx, xps_tiff *tiff, fz_stream *chain, byte *wp, int wlen) +xps_decode_tiff_jpeg(struct tiff *tiff, fz_stream *chain, byte *wp, int wlen) { fz_stream *stm = fz_opendctd(chain, NULL); int n = fz_read(stm, wp, wlen); @@ -302,7 +300,7 @@ xps_invert_tiff(byte *line, int width, int comps, int bits, int alpha) } static int -xps_expand_tiff_colormap(xps_context *ctx, xps_tiff *tiff) +xps_expand_tiff_colormap(struct tiff *tiff) { int maxval = 1 << tiff->bitspersample; byte *samples; @@ -358,7 +356,7 @@ xps_expand_tiff_colormap(xps_context *ctx, xps_tiff *tiff) } static int -xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff) +xps_decode_tiff_strips(struct tiff *tiff) { fz_buffer buf; fz_stream *stm; @@ -468,31 +466,31 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff) switch (tiff->compression) { case 1: - error = xps_decode_tiff_uncompressed(ctx, tiff, stm, wp, wlen); + error = xps_decode_tiff_uncompressed(tiff, stm, wp, wlen); break; case 2: - error = xps_decode_tiff_fax(ctx, tiff, 2, stm, wp, wlen); + error = xps_decode_tiff_fax(tiff, 2, stm, wp, wlen); break; case 3: - error = xps_decode_tiff_fax(ctx, tiff, 3, stm, wp, wlen); + error = xps_decode_tiff_fax(tiff, 3, stm, wp, wlen); break; case 4: - error = xps_decode_tiff_fax(ctx, tiff, 4, stm, wp, wlen); + error = xps_decode_tiff_fax(tiff, 4, stm, wp, wlen); break; case 5: - error = xps_decode_tiff_lzw(ctx, tiff, stm, wp, wlen); + error = xps_decode_tiff_lzw(tiff, stm, wp, wlen); break; case 6: error = fz_throw("deprecated JPEG in TIFF compression not supported"); break; case 7: - error = xps_decode_tiff_jpeg(ctx, tiff, stm, wp, wlen); + error = xps_decode_tiff_jpeg(tiff, stm, wp, wlen); break; case 8: - error = xps_decode_tiff_flate(ctx, tiff, stm, wp, wlen); + error = xps_decode_tiff_flate(tiff, stm, wp, wlen); break; case 32773: - error = xps_decode_tiff_packbits(ctx, tiff, stm, wp, wlen); + error = xps_decode_tiff_packbits(tiff, stm, wp, wlen); break; default: error = fz_throw("unknown TIFF compression: %d", tiff->compression); @@ -524,7 +522,7 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff) /* RGBPal */ if (tiff->photometric == 3 && tiff->colormap) { - error = xps_expand_tiff_colormap(ctx, tiff); + error = xps_expand_tiff_colormap(tiff); if (error) return fz_rethrow(error, "cannot expand colormap"); } @@ -543,14 +541,14 @@ xps_decode_tiff_strips(xps_context *ctx, xps_tiff *tiff) return fz_okay; } -static inline int readbyte(xps_tiff *tiff) +static inline int readbyte(struct tiff *tiff) { if (tiff->rp < tiff->ep) return *tiff->rp++; return EOF; } -static inline unsigned readshort(xps_tiff *tiff) +static inline unsigned readshort(struct tiff *tiff) { unsigned a = readbyte(tiff); unsigned b = readbyte(tiff); @@ -559,7 +557,7 @@ static inline unsigned readshort(xps_tiff *tiff) return (a << 8) | b; } -static inline unsigned readlong(xps_tiff *tiff) +static inline unsigned readlong(struct tiff *tiff) { unsigned a = readbyte(tiff); unsigned b = readbyte(tiff); @@ -571,7 +569,7 @@ static inline unsigned readlong(xps_tiff *tiff) } static void -xps_read_tiff_bytes(unsigned char *p, xps_tiff *tiff, unsigned ofs, unsigned n) +xps_read_tiff_bytes(unsigned char *p, struct tiff *tiff, unsigned ofs, unsigned n) { tiff->rp = tiff->bp + ofs; if (tiff->rp > tiff->ep) @@ -582,7 +580,7 @@ xps_read_tiff_bytes(unsigned char *p, xps_tiff *tiff, unsigned ofs, unsigned n) } static void -xps_read_tiff_tag_value(unsigned *p, xps_tiff *tiff, unsigned type, unsigned ofs, unsigned n) +xps_read_tiff_tag_value(unsigned *p, struct tiff *tiff, unsigned type, unsigned ofs, unsigned n) { tiff->rp = tiff->bp + ofs; if (tiff->rp > tiff->ep) @@ -606,7 +604,7 @@ xps_read_tiff_tag_value(unsigned *p, xps_tiff *tiff, unsigned type, unsigned ofs } static int -xps_read_tiff_tag(xps_context *ctx, xps_tiff *tiff, unsigned offset) +xps_read_tiff_tag(struct tiff *tiff, unsigned offset) { unsigned tag; unsigned type; @@ -739,7 +737,7 @@ xps_swap_byte_order(byte *buf, int n) } static int -xps_decode_tiff_header(xps_context *ctx, xps_tiff *tiff, byte *buf, int len) +xps_decode_tiff_header(struct tiff *tiff, byte *buf, int len) { unsigned version; unsigned offset; @@ -747,7 +745,7 @@ xps_decode_tiff_header(xps_context *ctx, xps_tiff *tiff, byte *buf, int len) unsigned i; int error; - memset(tiff, 0, sizeof(xps_tiff)); + memset(tiff, 0, sizeof(struct tiff)); tiff->bp = buf; tiff->rp = buf; @@ -795,7 +793,7 @@ xps_decode_tiff_header(xps_context *ctx, xps_tiff *tiff, byte *buf, int len) offset += 2; for (i = 0; i < count; i++) { - error = xps_read_tiff_tag(ctx, tiff, offset); + error = xps_read_tiff_tag(tiff, offset); if (error) return fz_rethrow(error, "cannot read TIFF header tag"); offset += 12; @@ -805,14 +803,13 @@ xps_decode_tiff_header(xps_context *ctx, xps_tiff *tiff, byte *buf, int len) } int -xps_decode_tiff(xps_image **imagep, xps_context *ctx, byte *buf, int len) +xps_decode_tiff(fz_pixmap **imagep, byte *buf, int len) { int error; - fz_pixmap *pixmap; - xps_image *image; - xps_tiff tiff; + fz_pixmap *image; + struct tiff tiff; - error = xps_decode_tiff_header(ctx, &tiff, buf, len); + error = xps_decode_tiff_header(&tiff, buf, len); if (error) return fz_rethrow(error, "cannot decode tiff header"); @@ -821,7 +818,7 @@ xps_decode_tiff(xps_image **imagep, xps_context *ctx, byte *buf, int len) if (tiff.rowsperstrip > tiff.imagelength) tiff.rowsperstrip = tiff.imagelength; - error = xps_decode_tiff_strips(ctx, &tiff); + error = xps_decode_tiff_strips(&tiff); if (error) return fz_rethrow(error, "cannot decode image data"); @@ -834,18 +831,15 @@ xps_decode_tiff(xps_image **imagep, xps_context *ctx, byte *buf, int len) /* Expand into fz_pixmap struct */ - pixmap = fz_newpixmap(tiff.colorspace, 0, 0, tiff.imagewidth, tiff.imagelength); + image = fz_newpixmap(tiff.colorspace, 0, 0, tiff.imagewidth, tiff.imagelength); + image->xres = tiff.xresolution; + image->yres = tiff.yresolution; - fz_unpacktile(pixmap, tiff.samples, tiff.samplesperpixel, tiff.bitspersample, tiff.stride, 0); + fz_unpacktile(image, tiff.samples, tiff.samplesperpixel, tiff.bitspersample, tiff.stride, 0); /* We should only do this on non-pre-multiplied images, but files in the wild are bad */ if (tiff.extrasamples /* == 2 */) - fz_premultiplypixmap(pixmap); - - image = fz_malloc(sizeof(xps_image)); - image->pixmap = pixmap; - image->xres = tiff.xresolution; - image->yres = tiff.yresolution; + fz_premultiplypixmap(image); /* Clean up scratch memory */ -- cgit v1.2.3 From 2c267e58e55cf0ee9fd99d8d402fdfc732c294cc Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 3 Apr 2011 16:14:06 +0200 Subject: xps: Fix some issues with the XML parser. The new XML parser doesn't strip namespace prefixes, so we need to look for "x:Key" rather than just "Key". Also increase the max tag name length to avoid truncating tag names like "LinearGradientBrush.GradientStops". --- xps/xps_resource.c | 3 +-- xps/xps_xml.c | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/xps/xps_resource.c b/xps/xps_resource.c index 59dc3f9d..7cbfe91f 100644 --- a/xps/xps_resource.c +++ b/xps/xps_resource.c @@ -129,8 +129,7 @@ xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base for (node = xml_down(root); node; node = xml_next(node)) { - /* Usually "x:Key"; we have already processed and stripped namespace */ - key = xml_att(node, "Key"); + key = xml_att(node, "x:Key"); if (key) { entry = fz_malloc(sizeof(xps_resource)); diff --git a/xps/xps_xml.c b/xps/xps_xml.c index 397b68ec..6373b6c6 100644 --- a/xps/xps_xml.c +++ b/xps/xps_xml.c @@ -3,14 +3,14 @@ struct attribute { - char name[32]; + char name[40]; char *value; struct attribute *next; }; struct element { - char name[32]; + char name[40]; struct attribute *atts; struct element *up, *down, *next; }; -- cgit v1.2.3 From 119335460c2b50e169de7f3043095fa339bd00a4 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 3 Apr 2011 16:54:37 +0200 Subject: xps: Propagate bounding box through canvas and tiles. --- xps/muxps.h | 6 +++--- xps/xps_common.c | 4 ++-- xps/xps_gradient.c | 2 -- xps/xps_image.c | 2 +- xps/xps_tile.c | 25 ++++++++++++++----------- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/xps/muxps.h b/xps/muxps.h index 067d4780..8a29d674 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -170,7 +170,7 @@ void xps_debug_resource_dictionary(xps_resource *dict); */ void xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page); -void xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node); +void xps_parse_canvas(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node); void xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node); void xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node); void xps_parse_solid_color_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node); @@ -179,7 +179,7 @@ void xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char void xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node); void xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node); -void xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *root, void(*func)(xps_context*, fz_matrix, char*, xps_resource*, xml_element*, void*), void *user); +void xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *root, void(*func)(xps_context*, fz_matrix, fz_rect, char*, xps_resource*, xml_element*, void*), void *user); void xps_parse_matrix_transform(xps_context *ctx, xml_element *root, fz_matrix *matrix); void xps_parse_render_transform(xps_context *ctx, char *text, fz_matrix *matrix); @@ -189,7 +189,7 @@ void xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base void xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, char *opacity_att, xml_element *opacity_mask_tag); void xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node); -void xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node); +void xps_parse_element(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node); void xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xml_element *clip_tag); diff --git a/xps/xps_common.c b/xps/xps_common.c index 7e489ef7..65f06e60 100644 --- a/xps/xps_common.c +++ b/xps/xps_common.c @@ -26,14 +26,14 @@ xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, x } void -xps_parse_element(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node) +xps_parse_element(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node) { if (!strcmp(xml_tag(node), "Path")) xps_parse_path(ctx, ctm, base_uri, dict, node); if (!strcmp(xml_tag(node), "Glyphs")) xps_parse_glyphs(ctx, ctm, base_uri, dict, node); if (!strcmp(xml_tag(node), "Canvas")) - xps_parse_canvas(ctx, ctm, base_uri, dict, node); + xps_parse_canvas(ctx, ctm, area, base_uri, dict, node); /* skip unknown tags (like Foo.Resources and similar) */ } diff --git a/xps/xps_gradient.c b/xps/xps_gradient.c index 8c877a0b..3342112c 100644 --- a/xps/xps_gradient.c +++ b/xps/xps_gradient.c @@ -436,8 +436,6 @@ xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, return; } - area = fz_transformrect(ctm, area); - xps_begin_opacity(ctx, ctm, area, base_uri, dict, opacity_att, NULL); draw(ctx, ctm, stop_list, stop_count, root, spread_method); diff --git a/xps/xps_image.c b/xps/xps_image.c index f4d02f78..a5d54fea 100644 --- a/xps/xps_image.c +++ b/xps/xps_image.c @@ -38,7 +38,7 @@ xps_decode_image(fz_pixmap **imagep, byte *buf, int len) } static void -xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, +xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *root, void *vimage) { fz_pixmap *pixmap = vimage; diff --git a/xps/xps_tile.c b/xps/xps_tile.c index 353f769a..59dd181b 100644 --- a/xps/xps_tile.c +++ b/xps/xps_tile.c @@ -14,7 +14,7 @@ struct closure xps_resource *dict; xml_element *root; void *user; - void (*func)(xps_context*, fz_matrix, char*, xps_resource*, xml_element*, void*); + void (*func)(xps_context*, fz_matrix, fz_rect, char*, xps_resource*, xml_element*, void*); }; static void @@ -29,7 +29,7 @@ xps_paint_tiling_brush_clipped(xps_context *ctx, fz_matrix ctm, fz_rect viewbox, ctx->dev->clippath(ctx->dev->user, path, 0, ctm); - c->func(ctx, ctm, c->base_uri, c->dict, c->root, c->user); + c->func(ctx, ctm, viewbox, c->base_uri, c->dict, c->root, c->user); ctx->dev->popclip(ctx->dev->user); } @@ -66,7 +66,7 @@ xps_paint_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect viewbox, int til void xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *root, - void (*func)(xps_context*, fz_matrix, char*, xps_resource*, xml_element*, void*), void *user) + void (*func)(xps_context*, fz_matrix, fz_rect, char*, xps_resource*, xml_element*, void*), void *user) { xml_element *node; struct closure c; @@ -117,6 +117,7 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, xps_parse_render_transform(ctx, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); + ctm = fz_concat(transform, ctm); viewbox = fz_unitrect; if (viewbox_att) @@ -160,12 +161,11 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, xps_begin_opacity(ctx, ctm, area, base_uri, dict, opacity_att, NULL); - ctm = fz_concat(transform, ctm); ctm = fz_concat(fz_translate(viewport.x0, viewport.y0), ctm); ctm = fz_concat(fz_scale(xscale, yscale), ctm); ctm = fz_concat(fz_translate(-viewbox.x0, -viewbox.y0), ctm); - if (tile_mode != TILE_NONE && !fz_isinfiniterect(area)) + if (tile_mode != TILE_NONE) { fz_matrix invctm = fz_invertmatrix(ctm); fz_rect bbox = fz_transformrect(invctm, area); @@ -193,10 +193,10 @@ xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, } static void -xps_paint_visual_brush(xps_context *ctx, fz_matrix ctm, +xps_paint_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *root, void *visual_tag) { - xps_parse_element(ctx, ctm, base_uri, dict, (xml_element *)visual_tag); + xps_parse_element(ctx, ctm, area, base_uri, dict, (xml_element *)visual_tag); } void @@ -228,7 +228,7 @@ xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, } void -xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root) +xps_parse_canvas(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *root) { xps_resource *new_dict = NULL; xml_element *node; @@ -288,11 +288,11 @@ xps_parse_canvas(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource * if (clip_att || clip_tag) xps_clip(ctx, ctm, dict, clip_att, clip_tag); - xps_begin_opacity(ctx, ctm, fz_infiniterect, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); + xps_begin_opacity(ctx, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); for (node = xml_down(root); node; node = xml_next(node)) { - xps_parse_element(ctx, ctm, base_uri, dict, node); + xps_parse_element(ctx, ctm, area, base_uri, dict, node); } xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); @@ -310,6 +310,7 @@ xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page) xml_element *node; xps_resource *dict; char base_uri[1024]; + fz_rect area; char *s; int code; @@ -326,6 +327,8 @@ xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page) if (!page->root) return; + area = fz_transformrect(fz_scale(page->width, page->height), fz_unitrect); + for (node = xml_down(page->root); node; node = xml_next(node)) { if (!strcmp(xml_tag(node), "FixedPage.Resources") && xml_down(node)) @@ -334,7 +337,7 @@ xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page) if (code) fz_catch(code, "cannot load FixedPage.Resources"); } - xps_parse_element(ctx, ctm, base_uri, dict, node); + xps_parse_element(ctx, ctm, area, base_uri, dict, node); } if (dict) -- cgit v1.2.3 From cb1f58e1e204eab0995b57d1a973714f024e70ad Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 3 Apr 2011 17:17:09 +0200 Subject: xps: Update win32 project files. --- apps/win_main.c | 15 +++++++++--- win32/libmupdf.vcproj | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ win32/mupdf.vcproj | 4 ++-- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/apps/win_main.c b/apps/win_main.c index e217dfbb..ae62b62b 100644 --- a/apps/win_main.c +++ b/apps/win_main.c @@ -1,5 +1,6 @@ -#include -#include +#include "fitz.h" +#include "mupdf.h" +#include "muxps.h" #include "pdfapp.h" #ifndef UNICODE @@ -90,7 +91,7 @@ int winfilename(wchar_t *buf, int len) ofn.nMaxFile = len; ofn.lpstrInitialDir = NULL; ofn.lpstrTitle = L"MuPDF: Open PDF file"; - ofn.lpstrFilter = L"PDF Files (*.pdf)\0*.pdf\0All Files\0*\0\0"; + ofn.lpstrFilter = L"Documents (*.pdf;*.xps)\0*.xps;*.pdf\0PDF Files (*.pdf)\0*.pdf\0XPS Files (*.xps)\0*.xps\0All Files\0*\0\0"; ofn.Flags = OFN_FILEMUSTEXIST|OFN_HIDEREADONLY; return GetOpenFileNameW(&ofn); } @@ -157,6 +158,14 @@ dloginfoproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) SetDlgItemTextW(hwnd, 0x10, wbuf); + if (!xref) + { + SetDlgItemTextA(hwnd, 0x11, "XPS"); + SetDlgItemTextA(hwnd, 0x12, "None"); + SetDlgItemTextA(hwnd, 0x13, "n/a"); + return TRUE; + } + sprintf(buf, "PDF %d.%d", xref->version / 10, xref->version % 10); SetDlgItemTextA(hwnd, 0x11, buf); diff --git a/win32/libmupdf.vcproj b/win32/libmupdf.vcproj index 8ff67a52..7fd5634f 100644 --- a/win32/libmupdf.vcproj +++ b/win32/libmupdf.vcproj @@ -520,6 +520,70 @@ > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/win32/mupdf.vcproj b/win32/mupdf.vcproj index 684093e9..509ef4df 100644 --- a/win32/mupdf.vcproj +++ b/win32/mupdf.vcproj @@ -40,7 +40,7 @@ Date: Sun, 3 Apr 2011 17:49:32 +0200 Subject: xps: Fix bugs uncovered by QualityLogicMinBar tests. --- fitz/res_font.c | 28 ++++++++++++++++++---------- xps/xps_image.c | 2 +- xps/xps_path.c | 2 +- xps/xps_xml.c | 2 +- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/fitz/res_font.c b/fitz/res_font.c index 1df8b2e6..44046451 100644 --- a/fitz/res_font.c +++ b/fitz/res_font.c @@ -176,6 +176,7 @@ fz_finalizefreetype(void) fz_error fz_newfontfromfile(fz_font **fontp, char *path, int index) { + FT_Face face; fz_error error; fz_font *font; int fterr; @@ -184,14 +185,16 @@ fz_newfontfromfile(fz_font **fontp, char *path, int index) if (error) return fz_rethrow(error, "cannot init freetype library"); - font = fz_newfont(); - - fterr = FT_New_Face(fz_ftlib, path, index, (FT_Face*)&font->ftface); + fterr = FT_New_Face(fz_ftlib, path, index, &face); if (fterr) - { - fz_free(font); return fz_throw("freetype: cannot load font: %s", ft_errorstring(fterr)); - } + + font = fz_newfont(); + font->ftface = face; + font->bbox.x0 = face->bbox.xMin * 1000 / face->units_per_EM; + font->bbox.y0 = face->bbox.yMin * 1000 / face->units_per_EM; + font->bbox.x1 = face->bbox.xMax * 1000 / face->units_per_EM; + font->bbox.y1 = face->bbox.yMax * 1000 / face->units_per_EM; *fontp = font; return fz_okay; @@ -200,6 +203,7 @@ fz_newfontfromfile(fz_font **fontp, char *path, int index) fz_error fz_newfontfrombuffer(fz_font **fontp, unsigned char *data, int len, int index) { + FT_Face face; fz_error error; fz_font *font; int fterr; @@ -210,12 +214,16 @@ fz_newfontfrombuffer(fz_font **fontp, unsigned char *data, int len, int index) font = fz_newfont(); - fterr = FT_New_Memory_Face(fz_ftlib, data, len, index, (FT_Face*)&font->ftface); + fterr = FT_New_Memory_Face(fz_ftlib, data, len, index, &face); if (fterr) - { - fz_free(font); return fz_throw("freetype: cannot load font: %s", ft_errorstring(fterr)); - } + + font = fz_newfont(); + font->ftface = face; + font->bbox.x0 = face->bbox.xMin * 1000 / face->units_per_EM; + font->bbox.y0 = face->bbox.yMin * 1000 / face->units_per_EM; + font->bbox.x1 = face->bbox.xMax * 1000 / face->units_per_EM; + font->bbox.y1 = face->bbox.yMax * 1000 / face->units_per_EM; *fontp = font; return fz_okay; diff --git a/xps/xps_image.c b/xps/xps_image.c index a5d54fea..f4b75a1b 100644 --- a/xps/xps_image.c +++ b/xps/xps_image.c @@ -47,7 +47,7 @@ xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_ fz_matrix im = fz_scale(xs, -ys); im.f = ys; ctm = fz_concat(im, ctm); - ctx->dev->fillimage(ctx->dev->user, pixmap, ctm, 1.0); + ctx->dev->fillimage(ctx->dev->user, pixmap, ctm, ctx->opacity[ctx->opacity_top]); } static xps_part * diff --git a/xps/xps_path.c b/xps/xps_path.c index 5674eac2..28b34894 100644 --- a/xps/xps_path.c +++ b/xps/xps_path.c @@ -758,7 +758,7 @@ xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xm path = xps_parse_path_geometry(ctx, dict, clip_tag, 0, &fill_rule); else path = fz_newpath(); - ctx->dev->clippath(ctx->dev->user, path, fill_rule, ctm); + ctx->dev->clippath(ctx->dev->user, path, fill_rule == 0, ctm); fz_freepath(path); } diff --git a/xps/xps_xml.c b/xps/xps_xml.c index 6373b6c6..8448ecef 100644 --- a/xps/xps_xml.c +++ b/xps/xps_xml.c @@ -103,7 +103,7 @@ static int xml_parse_entity(int *c, char *a) else *c = strtol(a + 2, &b, 10); if (*b == ';') - return b - a; + return b - a + 1; } else if (a[1] == 'l' && a[2] == 't' && a[3] == ';') { *c = '<'; -- cgit v1.2.3 From bc5be11061e7687a80bcda6edc9bf3c136529111 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 3 Apr 2011 19:50:47 +0200 Subject: xps: XPS uses 96 dpi as default where PDF uses 72. --- apps/pdfapp.c | 7 +++++-- apps/xpsdraw.c | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/pdfapp.c b/apps/pdfapp.c index 332f3bb5..0faff3b3 100644 --- a/apps/pdfapp.c +++ b/apps/pdfapp.c @@ -244,7 +244,7 @@ static fz_matrix pdfapp_viewctm(pdfapp_t *app) if (app->xref) ctm = fz_concat(ctm, fz_scale(app->resolution/72.0f, -app->resolution/72.0f)); else - ctm = fz_concat(ctm, fz_scale(app->resolution/72.0f, app->resolution/72.0f)); + ctm = fz_concat(ctm, fz_scale(app->resolution/96.0f, app->resolution/96.0f)); ctm = fz_concat(ctm, fz_rotate(app->rotate + app->page_rotate)); return ctm; } @@ -315,7 +315,10 @@ static void pdfapp_loadpage_xps(pdfapp_t *app) if (!page) pdfapp_error(app, fz_rethrow(-1, "cannot load page %d in file '%s'", app->pageno, app->doctitle)); - app->page_bbox = fz_transformrect(fz_scale(page->width, page->height), fz_unitrect); + app->page_bbox.x0 = 0; + app->page_bbox.y0 = 0; + app->page_bbox.x1 = page->width; + app->page_bbox.y1 = page->height; app->page_rotate = 0; app->page_links = NULL; diff --git a/apps/xpsdraw.c b/apps/xpsdraw.c index 921d677a..a9050a1c 100644 --- a/apps/xpsdraw.c +++ b/apps/xpsdraw.c @@ -155,7 +155,7 @@ static void drawpage(xps_context *ctx, int pagenum) rect.x1 = page->width; rect.y1 = page->height; - zoom = resolution / 72; + zoom = resolution / 96; ctm = fz_translate(0, -page->height); ctm = fz_concat(ctm, fz_scale(zoom, zoom)); bbox = fz_roundrect(fz_transformrect(ctm, rect)); -- cgit v1.2.3