Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 4679 → Rev 4680

/contrib/media/updf/xps/muxps.h
0,0 → 1,233
#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;
 
#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.
*/
 
int xps_strcasecmp(char *a, char *b);
void xps_absolute_path(char *output, char *base_uri, char *path, int output_size);
 
/*
* 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.
*/
 
typedef struct xps_part_s xps_part;
 
struct xps_part_s
{
char *name;
int size;
int cap;
byte *data;
};
 
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;
typedef struct xps_page_s xps_page;
 
struct xps_document_s
{
char *name;
xps_document *next;
};
 
struct xps_page_s
{
char *name;
int width;
int height;
xml_element *root;
xps_page *next;
};
 
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);
int xps_load_page(xps_page **page, xps_context *ctx, int number);
void xps_free_page(xps_context *ctx, xps_page *page);
 
/*
* Images, fonts, and colorspaces.
*/
 
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);
 
typedef struct xps_font_cache_s xps_font_cache;
 
struct xps_font_cache_s
{
char *name;
fz_font *font;
xps_font_cache *next;
};
 
typedef struct xps_glyph_metrics_s xps_glyph_metrics;
 
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 *ctx, fz_font *font, int gid, xps_glyph_metrics *mtx);
 
void xps_debug_path(xps_context *ctx);
 
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);
 
/*
* Resource dictionaries.
*/
 
typedef struct xps_resource_s xps_resource;
 
struct xps_resource_s
{
char *name;
char *base_uri; /* only used in the head nodes */
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, 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, xml_element **tagp, char **urip);
 
void xps_debug_resource_dictionary(xps_resource *dict);
 
/*
* Fixed page/graphics parsing.
*/
 
void xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page);
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);
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, 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);
void xps_parse_rectangle(xps_context *ctx, char *text, fz_rect *rect);
 
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, 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);
 
/*
* The interpreter context.
*/
 
typedef struct xps_entry_s xps_entry;
 
struct xps_entry_s
{
char *name;
int offset;
int csize;
int usize;
};
 
struct xps_context_s
{
char *directory;
fz_stream *file;
int zip_count;
xps_entry *zip_table;
 
char *start_part; /* fixed document sequence */
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 resources */
xps_font_cache *font_table;
 
/* Opacity attribute stack */
float opacity[64];
int opacity_top;
 
/* Current color */
fz_colorspace *colorspace;
float color[8];
float alpha;
 
/* Current device */
fz_device *dev;
};
 
int xps_open_file(xps_context **ctxp, char *filename);
int xps_open_stream(xps_context **ctxp, fz_stream *file);
void xps_free_context(xps_context *ctx);
 
#endif
/contrib/media/updf/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, 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, area, 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;
if (opacity_att)
opacity = fz_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 * fz_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)
{
fz_begin_mask(ctx->dev, area, 0, NULL, NULL);
xps_parse_brush(ctx, ctm, area, base_uri, dict, opacity_mask_tag);
fz_end_mask(ctx->dev);
}
}
 
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"))
fz_pop_clip(ctx->dev);
}
}
 
void
xps_parse_render_transform(xps_context *ctx, char *transform, fz_matrix *matrix)
{
float args[6];
char *s = transform;
int i;
 
args[0] = 1; args[1] = 0;
args[2] = 0; args[3] = 1;
args[4] = 0; args[5] = 0;
 
for (i = 0; i < 6 && *s; i++)
{
args[i] = fz_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; args[1] = 0;
args[2] = 1; args[3] = 1;
 
for (i = 0; i < 4 && *s; i++)
{
args[i] = fz_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_device_rgb;
 
samples[0] = 1;
samples[1] = 0;
samples[2] = 0;
samples[3] = 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;
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;
samples[1] /= 255;
samples[2] /= 255;
samples[3] /= 255;
}
 
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 */
fz_strlcpy(buf, string, sizeof buf);
 
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++] = fz_atof(p);
p = strchr(p, ',');
if (!p)
break;
p ++;
if (*p == ' ')
p ++;
}
while (i < n)
{
samples[i++] = 0;
}
 
/* TODO: load ICC profile */
switch (n)
{
case 2: *csp = fz_device_gray; break;
case 4: *csp = fz_device_rgb; break;
case 5: *csp = fz_device_cmyk; break;
default: *csp = fz_device_gray; 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];
}
/contrib/media/updf/xps/xps_doc.c
0,0 → 1,341
#include "fitz.h"
#include "muxps.h"
 
/*
* The FixedDocumentSequence and FixedDocument parts determine
* which parts correspond to actual pages, and the page order.
*/
 
void
xps_debug_page_list(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;
}
}
 
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;
}
}
 
static void
xps_free_fixed_pages(xps_context *ctx)
{
xps_page *page = ctx->first_page;
while (page)
{
xps_page *next = page->next;
xps_free_page(ctx, page);
fz_free(page->name);
fz_free(page);
page = next;
}
ctx->first_page = NULL;
ctx->last_page = NULL;
}
 
static void
xps_free_fixed_documents(xps_context *ctx)
{
xps_document *doc = ctx->first_fixdoc;
while (doc)
{
xps_document *next = doc->next;
fz_free(doc->name);
fz_free(doc);
doc = 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.
*/
 
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);
}
}
 
static 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;
}
 
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;
}
 
int
xps_load_page(xps_page **pagep, 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)
return fz_rethrow(code, "cannot load page %d", number + 1);
}
*pagep = page;
return fz_okay;
}
n ++;
}
 
return fz_throw("cannot find page %d", number + 1);
}
 
void
xps_free_page(xps_context *ctx, xps_page *page)
{
/* only free the XML contents */
if (page->root)
xml_free_element(page->root);
page->root = NULL;
}
/contrib/media/updf/xps/xps_glyphs.c
0,0 → 1,584
#include "fitz.h"
#include "muxps.h"
 
#include <ft2build.h>
#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->ft_face;
return face->num_charmaps;
}
 
void
xps_identify_font_encoding(fz_font *font, int idx, int *pid, int *eid)
{
FT_Face face = font->ft_face;
*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->ft_face;
FT_Set_Charmap(face, face->charmaps[idx]);
}
 
int
xps_encode_font_char(fz_font *font, int code)
{
FT_Face face = font->ft_face;
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->ft_face;
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;
}
 
static fz_font *
xps_lookup_font(xps_context *ctx, char *name)
{
xps_font_cache *cache;
for (cache = ctx->font_table; cache; cache = cache->next)
if (!xps_strcasecmp(cache->name, name))
return fz_keep_font(cache->font);
return NULL;
}
 
static void
xps_insert_font(xps_context *ctx, char *name, fz_font *font)
{
xps_font_cache *cache = fz_malloc(sizeof(xps_font_cache));
cache->name = fz_strdup(name);
cache->font = fz_keep_font(font);
cache->next = ctx->font_table;
ctx->font_table = cache;
}
 
/*
* 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 <Glyphs> 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 = fz_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_new_text(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 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;
float v_offset = 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;
else if (bidi_level & 1)
advance = -mtx.hadv * 100;
else
advance = mtx.hadv * 100;
 
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.01f * size;
v_offset = v_offset * 0.01f * size;
 
if (is_sideways)
{
e = x + u_offset + (mtx.vorg * size);
f = y - v_offset + (mtx.hadv * 0.5f * size);
}
else
{
e = x + u_offset;
f = y - v_offset;
}
 
fz_add_text(text, glyph_index, char_code, e, f);
 
x += advance * 0.01f * 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;
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_lookup_font(ctx, 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_new_font_from_memory(&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_insert_font(ctx, part->name, font);
 
/* NOTE: we keep part->data in the font */
font->ft_data = part->data;
font->ft_size = part->size;
fz_free(part->name);
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 = fz_atof(font_size_att);
 
text = xps_parse_glyphs_imp(ctx, ctm, font, font_size,
fz_atof(origin_x_att), fz_atof(origin_y_att),
is_sideways, bidi_level, indices_att, unicode_att);
 
area = fz_bound_text(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] = fz_atof(fill_opacity_att);
xps_set_color(ctx, colorspace, samples);
 
fz_fill_text(ctx->dev, text, ctm,
ctx->colorspace, ctx->color, ctx->alpha);
}
 
/* If it's a complex brush, use the charpath as a clip mask */
 
if (fill_tag)
{
fz_clip_text(ctx->dev, text, ctm, 0);
xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag);
fz_pop_clip(ctx->dev);
}
 
xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
 
fz_free_text(text);
 
if (clip_att || clip_tag)
fz_pop_clip(ctx->dev);
 
fz_drop_font(font);
}
/contrib/media/updf/xps/xps_gradient.c
0,0 → 1,458
#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 = fz_atof(offset);
 
xps_parse_color(ctx, base_uri, color, &colorspace, sample);
 
fz_convert_color(colorspace, sample + 1, fz_device_rgb, 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->colorspace = fz_device_rgb;
shade->bbox = fz_infinite_rect;
shade->matrix = fz_identity;
shade->use_background = 0;
shade->use_function = 1;
shade->type = FZ_RADIAL;
shade->extend[0] = extend;
shade->extend[1] = extend;
 
xps_sample_gradient_stops(shade, stops, count);
 
shade->mesh_len = 6;
shade->mesh_cap = 6;
shade->mesh = fz_calloc(shade->mesh_cap, 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;
 
fz_fill_shade(ctx->dev, shade, ctm, 1);
 
fz_drop_shade(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->colorspace = fz_device_rgb;
shade->bbox = fz_infinite_rect;
shade->matrix = fz_identity;
shade->use_background = 0;
shade->use_function = 1;
shade->type = FZ_LINEAR;
shade->extend[0] = extend;
shade->extend[1] = extend;
 
xps_sample_gradient_stops(shade, stops, count);
 
shade->mesh_len = 6;
shade->mesh_cap = 6;
shade->mesh = fz_calloc(shade->mesh_cap, 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;
 
fz_fill_shade(ctx->dev, shade, ctm, 1);
 
fz_drop_shade(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 = fz_atof(radius_x_att);
if (radius_y_att)
yrad = fz_atof(radius_y_att);
 
/* scale the ctm to make ellipses */
ctm = fz_concat(fz_scale(1, yrad / xrad), ctm);
 
invscale = xrad / yrad;
y0 = y0 * invscale;
y1 = y1 * invscale;
 
r0 = 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;
}
 
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);
}
/contrib/media/updf/xps/xps_image.c
0,0 → 1,128
#include "fitz.h"
#include "muxps.h"
 
static int
xps_decode_image(fz_pixmap **imagep, byte *buf, int len)
{
int error;
 
if (len < 8)
return fz_throw("unknown image file format");
 
if (buf[0] == 0xff && buf[1] == 0xd8)
{
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, 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, 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, fz_rect area, char *base_uri, xps_resource *dict,
xml_element *root, void *vimage)
{
fz_pixmap *pixmap = vimage;
float xs = pixmap->w * 96 / pixmap->xres;
float ys = pixmap->h * 96 / pixmap->yres;
fz_matrix im = fz_scale(xs, -ys);
im.f = ys;
ctm = fz_concat(im, ctm);
fz_fill_image(ctx->dev, pixmap, ctm, ctx->opacity[ctx->opacity_top]);
}
 
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;
fz_pixmap *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, 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);
 
fz_drop_pixmap(image);
xps_free_part(ctx, part);
}
/contrib/media/updf/xps/xps_jpeg.c
0,0 → 1,138
#include "fitz.h"
#include "muxps.h"
 
#include <jpeglib.h>
#include <setjmp.h>
 
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(fz_pixmap **imagep, 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;
unsigned int x;
int k;
 
fz_pixmap *image = NULL;
 
if (setjmp(err.env))
{
if (image)
fz_drop_pixmap(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_device_gray;
else if (cinfo.output_components == 3)
colorspace = fz_device_rgb;
else if (cinfo.output_components == 4)
colorspace = fz_device_cmyk;
else
return fz_throw("bad number of components in jpeg: %d", cinfo.output_components);
 
image = fz_new_pixmap_with_limit(colorspace, cinfo.output_width, cinfo.output_height);
if (!image)
{
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return fz_throw("out of memory");
}
 
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 * 254 / 100;
image->yres = cinfo.Y_density * 254 / 100;
}
 
fz_clear_pixmap(image);
 
row[0] = fz_malloc(cinfo.output_components * cinfo.output_width);
dp = image->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;
}
/contrib/media/updf/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->items[i++].k)
{
case FZ_MOVETO:
m.x = c.x = path->items[i++].v;
m.y = c.y = path->items[i++].v;
break;
case FZ_LINETO:
c.x = path->items[i++].v;
c.y = path->items[i++].v;
break;
case FZ_CURVETO:
i += 4;
c.x = path->items[i++].v;
c.y = path->items[i++].v;
break;
case FZ_CLOSE_PATH:
c = m;
}
}
 
return c;
}
 
/* 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 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 += (float)M_PI * 2;
 
d = (float)M_PI / 180; /* 1-degree precision */
 
if (iscw)
{
p.x = cosf(th0);
p.y = sinf(th0);
p = fz_transform_point(mtx, p);
fz_lineto(path, p.x, p.y);
for (t = th0; t < th1; t += d)
{
p.x = cosf(t);
p.y = sinf(t);
p = fz_transform_point(mtx, p);
fz_lineto(path, p.x, p.y);
}
p.x = cosf(th1);
p.y = sinf(th1);
p = fz_transform_point(mtx, p);
fz_lineto(path, p.x, p.y);
}
else
{
th0 += (float)M_PI * 2;
p.x = cosf(th0);
p.y = sinf(th0);
p = fz_transform_point(mtx, p);
fz_lineto(path, p.x, p.y);
for (t = th0; t > th1; t -= d)
{
p.x = cosf(t);
p.y = sinf(t);
p = fz_transform_point(mtx, p);
fz_lineto(path, p.x, p.y);
}
p.x = cosf(th1);
p.y = sinf(th1);
p = fz_transform_point(mtx, p);
fz_lineto(path, p.x, p.y);
}
}
 
/* Given two vectors find the angle between them. */
static float
angle_between(const fz_point u, const fz_point v)
{
float det = u.x * v.y - u.y * v.x;
float sign = (det < 0 ? -1 : 1);
float magu = u.x * u.x + u.y * u.y;
float magv = v.x * v.x + v.y * v.y;
float udotv = u.x * v.x + u.y * v.y;
float t = udotv / (magu * magv);
/* guard against rounding errors when near |1| (where acos will return NaN) */
if (t < -1) t = -1;
if (t > 1) t = 1;
return sign * acosf(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;
float rx, ry;
float x1, y1, x2, y2;
float x1t, y1t;
float cxt, cyt, cx, cy;
float t1, t2, t3;
float sign;
float 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.001f || ry < 0.001f)
{
fz_lineto(path, x2, y2);
return;
}
 
/* F.6.5.1 */
pt.x = (x1 - x2) / 2;
pt.y = (y1 - y2) / 2;
pt = fz_transform_vector(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)
{
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) t3 = 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_transform_vector(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 += (((float)M_PI / 180) * 360);
if (dth > 0 && is_clockwise)
dth -= (((float)M_PI / 180) * 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.
*/
 
static 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_new_path();
 
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;
smooth_y = 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;
smooth_y = 0;
}
 
reset_smooth = 1;
 
switch (cmd)
{
case 'F':
*fill_rule = atoi(args[i]);
i ++;
break;
 
case 'M':
fz_moveto(path, fz_atof(args[i]), fz_atof(args[i+1]));
i += 2;
break;
case 'm':
pt = fz_currentpoint(path);
fz_moveto(path, pt.x + fz_atof(args[i]), pt.y + fz_atof(args[i+1]));
i += 2;
break;
 
case 'L':
fz_lineto(path, fz_atof(args[i]), fz_atof(args[i+1]));
i += 2;
break;
case 'l':
pt = fz_currentpoint(path);
fz_lineto(path, pt.x + fz_atof(args[i]), pt.y + fz_atof(args[i+1]));
i += 2;
break;
 
case 'H':
pt = fz_currentpoint(path);
fz_lineto(path, fz_atof(args[i]), pt.y);
i += 1;
break;
case 'h':
pt = fz_currentpoint(path);
fz_lineto(path, pt.x + fz_atof(args[i]), pt.y);
i += 1;
break;
 
case 'V':
pt = fz_currentpoint(path);
fz_lineto(path, pt.x, fz_atof(args[i]));
i += 1;
break;
case 'v':
pt = fz_currentpoint(path);
fz_lineto(path, pt.x, pt.y + fz_atof(args[i]));
i += 1;
break;
 
case 'C':
x1 = fz_atof(args[i+0]);
y1 = fz_atof(args[i+1]);
x2 = fz_atof(args[i+2]);
y2 = fz_atof(args[i+3]);
x3 = fz_atof(args[i+4]);
y3 = fz_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 = fz_atof(args[i+0]) + pt.x;
y1 = fz_atof(args[i+1]) + pt.y;
x2 = fz_atof(args[i+2]) + pt.x;
y2 = fz_atof(args[i+3]) + pt.y;
x3 = fz_atof(args[i+4]) + pt.x;
y3 = fz_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 = fz_atof(args[i+0]);
y1 = fz_atof(args[i+1]);
x2 = fz_atof(args[i+2]);
y2 = fz_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 = fz_atof(args[i+0]) + pt.x;
y1 = fz_atof(args[i+1]) + pt.y;
x2 = fz_atof(args[i+2]) + pt.x;
y2 = fz_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 = fz_atof(args[i+0]);
y1 = fz_atof(args[i+1]);
x2 = fz_atof(args[i+2]);
y2 = fz_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 = fz_atof(args[i+0]) + pt.x;
y1 = fz_atof(args[i+1]) + pt.y;
x2 = fz_atof(args[i+2]) + pt.x;
y2 = fz_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,
fz_atof(args[i+0]), fz_atof(args[i+1]), fz_atof(args[i+2]),
atoi(args[i+3]), atoi(args[i+4]),
fz_atof(args[i+5]), fz_atof(args[i+6]));
i += 7;
break;
case 'a':
pt = fz_currentpoint(path);
xps_draw_arc(path,
fz_atof(args[i+0]), fz_atof(args[i+1]), fz_atof(args[i+2]),
atoi(args[i+3]), atoi(args[i+4]),
fz_atof(args[i+5]) + pt.x, fz_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 = fz_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;
float start_y = 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_new_path();
 
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_transform_path(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;
}
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_new_path();
fz_clip_path(ctx->dev, path, NULL, fill_rule == 0, ctm);
fz_free_path(path);
}
 
/*
* Parse an XPS <Path> 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_stroke_state 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.start_cap = xps_parse_line_cap(stroke_start_line_cap_att);
stroke.dash_cap = xps_parse_line_cap(stroke_dash_cap_att);
stroke.end_cap = xps_parse_line_cap(stroke_end_line_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;
if (stroke_miter_limit_att)
stroke.miterlimit = fz_atof(stroke_miter_limit_att);
 
stroke.linewidth = 1;
if (stroke_thickness_att)
stroke.linewidth = fz_atof(stroke_thickness_att);
 
stroke.dash_phase = 0;
stroke.dash_len = 0;
if (stroke_dash_array_att)
{
char *s = stroke_dash_array_att;
 
if (stroke_dash_offset_att)
stroke.dash_phase = fz_atof(stroke_dash_offset_att) * stroke.linewidth;
 
while (*s && stroke.dash_len < nelem(stroke.dash_list))
{
while (*s == ' ')
s++;
stroke.dash_list[stroke.dash_len++] = fz_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_bound_path(path, &stroke, ctm);
else
area = fz_bound_path(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] = fz_atof(fill_opacity_att);
xps_set_color(ctx, colorspace, samples);
 
fz_fill_path(ctx->dev, path, fill_rule == 0, ctm,
ctx->colorspace, ctx->color, ctx->alpha);
}
 
if (fill_tag)
{
area = fz_bound_path(path, NULL, ctm);
 
fz_clip_path(ctx->dev, path, NULL, fill_rule == 0, ctm);
xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag);
fz_pop_clip(ctx->dev);
}
 
if (stroke_att)
{
xps_parse_color(ctx, base_uri, stroke_att, &colorspace, samples);
if (stroke_opacity_att)
samples[0] = fz_atof(stroke_opacity_att);
xps_set_color(ctx, colorspace, samples);
 
fz_stroke_path(ctx->dev, path, &stroke, ctm,
ctx->colorspace, ctx->color, ctx->alpha);
}
 
if (stroke_tag)
{
fz_clip_stroke_path(ctx->dev, path, NULL, &stroke, ctm);
xps_parse_brush(ctx, ctm, area, stroke_uri, dict, stroke_tag);
fz_pop_clip(ctx->dev);
}
 
xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
 
fz_free_path(path);
path = NULL;
 
if (clip_att || clip_tag)
fz_pop_clip(ctx->dev);
}
/contrib/media/updf/xps/xps_png.c
0,0 → 1,578
#include "fitz.h"
#include "muxps.h"
 
#include <zlib.h>
 
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_new_pixmap(fz_device_rgb, src->w, src->h);
unsigned char *sp = src->samples;
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++)
{
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_drop_pixmap(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(fz_pixmap **imagep, byte *p, int total)
{
fz_pixmap *image;
fz_colorspace *colorspace;
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_device_rgb;
else
colorspace = fz_device_gray;
 
stride = (png.width * png.n * png.depth + 7) / 8;
 
image = fz_new_pixmap_with_limit(colorspace, png.width, png.height);
if (!image)
{
fz_free(png.samples);
return fz_throw("out of memory");
}
 
image->xres = png.xres;
image->yres = png.yres;
 
fz_unpack_tile(image, png.samples, png.n, png.depth, stride, png.indexed);
 
if (png.indexed)
image = png_expand_palette(&png, image);
else if (png.transparency)
png_mask_transparency(&png, image);
 
if (png.transparency || png.n == 2 || png.n == 4)
fz_premultiply_pixmap(image);
 
fz_free(png.samples);
 
*imagep = image;
return fz_okay;
}
/contrib/media/updf/xps/xps_resource.c
0,0 → 1,187
#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))
{
key = xml_att(node, "x: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);
else
return fz_throw("empty resource dictionary");
 
*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;
}
}
/contrib/media/updf/xps/xps_tiff.c
0,0 → 1,865
#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
*/
 
struct 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(struct 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(struct tiff *tiff, fz_stream *chain, byte *wp, int wlen)
{
fz_stream *stm = fz_open_rld(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(struct tiff *tiff, fz_stream *chain, byte *wp, int wlen)
{
fz_stream *stm = fz_open_lzwd(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(struct tiff *tiff, fz_stream *chain, byte *wp, int wlen)
{
fz_stream *stm = fz_open_flated(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(struct tiff *tiff, int comp, fz_stream *chain, byte *wp, int wlen)
{
fz_stream *stm;
fz_obj *params;
fz_obj *columns, *rows, *black_is_1, *k, *encoded_byte_align;
int n;
 
columns = fz_new_int(tiff->imagewidth);
rows = fz_new_int(tiff->imagelength);
black_is_1 = fz_new_bool(tiff->photometric == 0);
k = fz_new_int(comp == 4 ? -1 : 0);
encoded_byte_align = fz_new_bool(comp == 2);
 
params = fz_new_dict(5);
fz_dict_puts(params, "Columns", columns);
fz_dict_puts(params, "Rows", rows);
fz_dict_puts(params, "BlackIs1", black_is_1);
fz_dict_puts(params, "K", k);
fz_dict_puts(params, "EncodedByteAlign", encoded_byte_align);
 
fz_drop_obj(columns);
fz_drop_obj(rows);
fz_drop_obj(black_is_1);
fz_drop_obj(k);
fz_drop_obj(encoded_byte_align);
 
stm = fz_open_faxd(chain, params);
n = fz_read(stm, wp, wlen);
fz_close(stm);
fz_drop_obj(params);
 
if (n < 0)
return fz_rethrow(n, "cannot read fax strip");
return fz_okay;
}
 
static int
xps_decode_tiff_jpeg(struct tiff *tiff, fz_stream *chain, byte *wp, int wlen)
{
fz_stream *stm = fz_open_dctd(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(struct tiff *tiff)
{
int maxval = 1 << tiff->bitspersample;
byte *samples;
byte *src, *dst;
unsigned int x, y;
unsigned int stride;
 
/* 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(struct tiff *tiff)
{
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_device_gray;
break;
case 1: /* BlackIsZero */
tiff->colorspace = fz_device_gray;
break;
case 2: /* RGB */
tiff->colorspace = fz_device_rgb;
break;
case 3: /* RGBPal */
tiff->colorspace = fz_device_rgb;
break;
case 5: /* CMYK */
tiff->colorspace = fz_device_cmyk;
break;
case 6: /* YCbCr */
/* it's probably a jpeg ... we let jpeg convert to rgb */
tiff->colorspace = fz_device_rgb;
break;
default:
return fz_throw("unknown photometric: %d", tiff->photometric);
}
 
switch (tiff->resolutionunit)
{
case 2:
/* no unit conversion needed */
break;
case 3:
tiff->xresolution = tiff->xresolution * 254 / 100;
tiff->yresolution = tiff->yresolution * 254 / 100;
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]];
 
/* the strip decoders will close this */
stm = fz_open_memory(rp, rlen);
 
switch (tiff->compression)
{
case 1:
error = xps_decode_tiff_uncompressed(tiff, stm, wp, wlen);
break;
case 2:
error = xps_decode_tiff_fax(tiff, 2, stm, wp, wlen);
break;
case 3:
error = xps_decode_tiff_fax(tiff, 3, stm, wp, wlen);
break;
case 4:
error = xps_decode_tiff_fax(tiff, 4, stm, wp, wlen);
break;
case 5:
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(tiff, stm, wp, wlen);
break;
case 8:
error = xps_decode_tiff_flate(tiff, stm, wp, wlen);
break;
case 32773:
error = xps_decode_tiff_packbits(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(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(struct tiff *tiff)
{
if (tiff->rp < tiff->ep)
return *tiff->rp++;
return EOF;
}
 
static inline unsigned readshort(struct 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(struct 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, struct 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, struct 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(struct 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(struct tiff *tiff, byte *buf, int len)
{
unsigned version;
unsigned offset;
unsigned count;
unsigned i;
int error;
 
memset(tiff, 0, sizeof(struct 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(tiff, offset);
if (error)
return fz_rethrow(error, "cannot read TIFF header tag");
offset += 12;
}
 
return fz_okay;
}
 
int
xps_decode_tiff(fz_pixmap **imagep, byte *buf, int len)
{
int error;
fz_pixmap *image;
struct tiff tiff;
 
error = xps_decode_tiff_header(&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(&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 */
 
image = fz_new_pixmap_with_limit(tiff.colorspace, tiff.imagewidth, tiff.imagelength);
if (!image)
{
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);
return fz_throw("out of memory");
}
 
image->xres = tiff.xresolution;
image->yres = tiff.yresolution;
 
fz_unpack_tile(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 */)
{
/* CMYK is a subtractive colorspace, we want additive for premul alpha */
if (image->n == 5)
{
fz_pixmap *rgb = fz_new_pixmap(fz_device_rgb, image->w, image->h);
fz_convert_pixmap(image, rgb);
rgb->xres = image->xres;
rgb->yres = image->yres;
fz_drop_pixmap(image);
image = rgb;
}
fz_premultiply_pixmap(image);
}
 
/* 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;
}
/contrib/media/updf/xps/xps_tile.c
0,0 → 1,365
#include "fitz.h"
#include "muxps.h"
 
#define TILE
 
/*
* 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, fz_rect, 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_new_path();
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);
fz_clip_path(ctx->dev, path, NULL, 0, ctm);
fz_free_path(path);
c->func(ctx, ctm, viewbox, c->base_uri, c->dict, c->root, c->user);
fz_pop_clip(ctx->dev);
}
 
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, fz_rect, 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);
ctm = fz_concat(transform, ctm);
 
viewbox = fz_unit_rect;
if (viewbox_att)
xps_parse_rectangle(ctx, viewbox_att, &viewbox);
 
viewport = fz_unit_rect;
if (viewport_att)
xps_parse_rectangle(ctx, viewport_att, &viewport);
 
/* some sanity checks on the viewport/viewbox size */
if (fabsf(viewport.x1 - viewport.x0) < 0.01f) return;
if (fabsf(viewport.y1 - viewport.y0) < 0.01f) return;
if (fabsf(viewbox.x1 - viewbox.x0) < 0.01f) return;
if (fabsf(viewbox.y1 - viewbox.y0) < 0.01f) 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(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)
{
int x0, y0, x1, y1;
fz_matrix invctm = fz_invert_matrix(ctm);
area = fz_transform_rect(invctm, area);
x0 = floorf(area.x0 / xstep);
y0 = floorf(area.y0 / ystep);
x1 = ceilf(area.x1 / xstep);
y1 = ceilf(area.y1 / ystep);
 
#ifdef TILE
{
int n = (x1 - x0) * (y1 - y0);
fz_rect bigview = viewbox;
bigview.x1 = bigview.x0 + xstep;
bigview.y1 = bigview.y0 + ystep;
if (n > 1)
fz_begin_tile(ctx->dev, area, bigview, xstep, ystep, ctm);
if (n > 0)
xps_paint_tiling_brush(ctx, ctm, viewbox, tile_mode, &c);
if (n > 1)
fz_end_tile(ctx->dev);
}
#else
{
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);
}
}
}
#endif
}
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, fz_rect area,
char *base_uri, xps_resource *dict, xml_element *root, void *visual_tag)
{
xps_parse_element(ctx, ctm, area, 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);
}
}
 
void
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;
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, 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, area, base_uri, dict, node);
}
 
xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
 
if (clip_att || clip_tag)
fz_pop_clip(ctx->dev);
 
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];
fz_rect area;
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;
 
area = fz_transform_rect(fz_scale(page->width, page->height), fz_unit_rect);
 
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, area, base_uri, dict, node);
}
 
if (dict)
{
xps_free_resource_dictionary(ctx, dict);
}
}
/contrib/media/updf/xps/xps_util.c
0,0 → 1,94
#include "fitz.h"
#include "muxps.h"
 
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);
}
 
#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);
}
/contrib/media/updf/xps/xps_xml.c
0,0 → 1,387
#include "fitz.h"
#include "muxps.h"
 
struct attribute
{
char name[40];
char *value;
struct attribute *next;
};
 
struct element
{
char name[40];
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("</%s>\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 + 1;
}
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 (<! not followed by --)";
if (*p++ != '-') return "syntax error in comment (<!- not followed by -)";
mark = p;
while (*p) {
if (p[0] == '-' && p[1] == '-' && p[2] == '>') {
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;
}
/contrib/media/updf/xps/xps_zip.c
0,0 → 1,501
#include "fitz.h"
#include "muxps.h"
 
#include <zlib.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);
}
 
static inline int getshort(fz_stream *file)
{
int a = fz_read_byte(file);
int b = fz_read_byte(file);
return a | b << 8;
}
 
static inline int getlong(fz_stream *file)
{
int a = fz_read_byte(file);
int b = fz_read_byte(file);
int c = fz_read_byte(file);
int d = fz_read_byte(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;
}
 
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;
 
fz_seek(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);
 
fz_seek(ctx->file, namelength + extralength, 1);
 
if (method == 0)
{
fz_read(ctx->file, outbuf, ent->usize);
}
else if (method == 8)
{
inbuf = fz_malloc(ent->csize);
 
fz_read(ctx->file, inbuf, ent->csize);
 
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;
 
fz_seek(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);
 
fz_seek(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);
fz_read(ctx->file, (unsigned char*)ctx->zip_table[i].name, namesize);
ctx->zip_table[i].name[namesize] = 0;
 
fz_seek(ctx->file, metasize, 1);
fz_seek(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)
{
unsigned char buf[512];
int file_size, back, maxback;
int i, n;
 
fz_seek(ctx->file, 0, SEEK_END);
file_size = fz_tell(ctx->file);
 
maxback = MIN(file_size, 0xFFFF + sizeof buf);
back = MIN(maxback, sizeof buf);
 
while (back < maxback)
{
fz_seek(ctx->file, file_size - back, 0);
 
n = fz_read(ctx->file, buf, sizeof buf);
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, file_size - 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);
}
 
static int
xps_open_directory(xps_context **ctxp, char *directory)
{
xps_context *ctx;
int code;
 
ctx = fz_malloc(sizeof(xps_context));
memset(ctx, 0, sizeof(xps_context));
 
ctx->directory = fz_strdup(directory);
 
code = xps_read_page_list(ctx);
if (code)
{
xps_free_context(ctx);
return fz_rethrow(code, "cannot read page list");
}
 
*ctxp = ctx;
return fz_okay;
}
 
int
xps_open_stream(xps_context **ctxp, fz_stream *file)
{
xps_context *ctx;
int code;
 
ctx = fz_malloc(sizeof(xps_context));
memset(ctx, 0, sizeof(xps_context));
 
ctx->file = fz_keep_stream(file);
 
code = xps_find_and_read_zip_dir(ctx);
if (code < 0)
{
xps_free_context(ctx);
return fz_rethrow(code, "cannot read zip central directory");
}
 
code = xps_read_page_list(ctx);
if (code)
{
xps_free_context(ctx);
return fz_rethrow(code, "cannot read page list");
}
 
*ctxp = ctx;
return fz_okay;
}
 
int
xps_open_file(xps_context **ctxp, char *filename)
{
char buf[2048];
fz_stream *file;
char *p;
int code;
 
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;
return xps_open_directory(ctxp, buf);
}
 
file = fz_open_file(filename);
if (!file)
return fz_throw("cannot open file '%s': %s", filename, strerror(errno));
 
code = xps_open_stream(ctxp, file);
fz_close(file);
if (code)
return fz_rethrow(code, "cannot load document '%s'", filename);
return fz_okay;
}
 
void
xps_free_context(xps_context *ctx)
{
xps_font_cache *font, *next;
int i;
 
if (ctx->file)
fz_close(ctx->file);
 
for (i = 0; i < ctx->zip_count; i++)
fz_free(ctx->zip_table[i].name);
fz_free(ctx->zip_table);
 
font = ctx->font_table;
while (font)
{
next = font->next;
fz_drop_font(font->font);
fz_free(font->name);
fz_free(font);
font = next;
}
 
xps_free_page_list(ctx);
 
fz_free(ctx->start_part);
fz_free(ctx->directory);
fz_free(ctx);
}