/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); |
} |