diff options
Diffstat (limited to 'source/fitz')
71 files changed, 39672 insertions, 0 deletions
diff --git a/source/fitz/bbox-device.c b/source/fitz/bbox-device.c new file mode 100644 index 00000000..9cb2a27e --- /dev/null +++ b/source/fitz/bbox-device.c @@ -0,0 +1,231 @@ +#include "mupdf/fitz.h" + +#define STACK_SIZE 96 + +typedef struct fz_bbox_data_s +{ + fz_rect *result; + int top; + fz_rect stack[STACK_SIZE]; + /* mask content and tiles are ignored */ + int ignore; +} fz_bbox_data; + +static void +fz_bbox_add_rect(fz_device *dev, const fz_rect *rect, int clip) +{ + fz_bbox_data *data = dev->user; + fz_rect r = *rect; + + if (0 < data->top && data->top <= STACK_SIZE) + { + fz_intersect_rect(&r, &data->stack[data->top-1]); + } + if (!clip && data->top <= STACK_SIZE && !data->ignore) + { + fz_union_rect(data->result, &r); + } + if (clip && ++data->top <= STACK_SIZE) + { + data->stack[data->top-1] = r; + } +} + +static void +fz_bbox_fill_path(fz_device *dev, fz_path *path, int even_odd, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_rect r; + fz_bbox_add_rect(dev, fz_bound_path(dev->ctx, path, NULL, ctm, &r), 0); +} + +static void +fz_bbox_stroke_path(fz_device *dev, fz_path *path, fz_stroke_state *stroke, + const fz_matrix *ctm, fz_colorspace *colorspace, float *color, float alpha) +{ + fz_rect r; + fz_bbox_add_rect(dev, fz_bound_path(dev->ctx, path, stroke, ctm, &r), 0); +} + +static void +fz_bbox_fill_text(fz_device *dev, fz_text *text, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_rect r; + fz_bbox_add_rect(dev, fz_bound_text(dev->ctx, text, NULL, ctm, &r), 0); +} + +static void +fz_bbox_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, + const fz_matrix *ctm, fz_colorspace *colorspace, float *color, float alpha) +{ + fz_rect r; + fz_bbox_add_rect(dev, fz_bound_text(dev->ctx, text, stroke, ctm, &r), 0); +} + +static void +fz_bbox_fill_shade(fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) +{ + fz_rect r; + fz_bbox_add_rect(dev, fz_bound_shade(dev->ctx, shade, ctm, &r), 0); +} + +static void +fz_bbox_fill_image(fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) +{ + fz_rect r = fz_unit_rect; + fz_bbox_add_rect(dev, fz_transform_rect(&r, ctm), 0); +} + +static void +fz_bbox_fill_image_mask(fz_device *dev, fz_image *image, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_rect r = fz_unit_rect; + fz_bbox_add_rect(dev, fz_transform_rect(&r, ctm), 0); +} + +static void +fz_bbox_clip_path(fz_device *dev, fz_path *path, const fz_rect *rect, int even_odd, const fz_matrix *ctm) +{ + fz_rect r; + fz_bbox_add_rect(dev, fz_bound_path(dev->ctx, path, NULL, ctm, &r), 1); +} + +static void +fz_bbox_clip_stroke_path(fz_device *dev, fz_path *path, const fz_rect *rect, fz_stroke_state *stroke, const fz_matrix *ctm) +{ + fz_rect r; + fz_bbox_add_rect(dev, fz_bound_path(dev->ctx, path, stroke, ctm, &r), 1); +} + +static void +fz_bbox_clip_text(fz_device *dev, fz_text *text, const fz_matrix *ctm, int accumulate) +{ + fz_rect r = fz_infinite_rect; + if (accumulate) + fz_bbox_add_rect(dev, &r, accumulate != 2); + else + fz_bbox_add_rect(dev, fz_bound_text(dev->ctx, text, NULL, ctm, &r), 1); +} + +static void +fz_bbox_clip_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm) +{ + fz_rect r; + fz_bbox_add_rect(dev, fz_bound_text(dev->ctx, text, stroke, ctm, &r), 1); +} + +static void +fz_bbox_clip_image_mask(fz_device *dev, fz_image *image, const fz_rect *rect, const fz_matrix *ctm) +{ + fz_rect r = *rect; + fz_bbox_add_rect(dev, fz_transform_rect(&r, ctm), 1); +} + +static void +fz_bbox_pop_clip(fz_device *dev) +{ + fz_bbox_data *data = dev->user; + if (data->top > 0) + data->top--; + else + fz_warn(dev->ctx, "unexpected pop clip"); +} + +static void +fz_bbox_begin_mask(fz_device *dev, const fz_rect *rect, int luminosity, fz_colorspace *colorspace, float *color) +{ + fz_bbox_data *data = dev->user; + fz_bbox_add_rect(dev, rect, 1); + data->ignore++; +} + +static void +fz_bbox_end_mask(fz_device *dev) +{ + fz_bbox_data *data = dev->user; + assert(data->ignore > 0); + data->ignore--; +} + +static void +fz_bbox_begin_group(fz_device *dev, const fz_rect *rect, int isolated, int knockout, int blendmode, float alpha) +{ + fz_bbox_add_rect(dev, rect, 1); +} + +static void +fz_bbox_end_group(fz_device *dev) +{ + fz_bbox_pop_clip(dev); +} + +static int +fz_bbox_begin_tile(fz_device *dev, const fz_rect *area, const fz_rect *view, float xstep, float ystep, const fz_matrix *ctm, int id) +{ + fz_bbox_data *data = dev->user; + fz_rect r = *area; + fz_bbox_add_rect(dev, fz_transform_rect(&r, ctm), 0); + data->ignore++; + return 0; +} + +static void +fz_bbox_end_tile(fz_device *dev) +{ + fz_bbox_data *data = dev->user; + assert(data->ignore > 0); + data->ignore--; +} + +static void +fz_bbox_free_user(fz_device *dev) +{ + fz_bbox_data *data = dev->user; + if (data->top > 0) + fz_warn(dev->ctx, "items left on stack in bbox device: %d", data->top); + fz_free(dev->ctx, dev->user); +} + +fz_device * +fz_new_bbox_device(fz_context *ctx, fz_rect *result) +{ + fz_device *dev; + + fz_bbox_data *user = fz_malloc_struct(ctx, fz_bbox_data); + user->result = result; + user->top = 0; + user->ignore = 0; + dev = fz_new_device(ctx, user); + dev->free_user = fz_bbox_free_user; + + dev->fill_path = fz_bbox_fill_path; + dev->stroke_path = fz_bbox_stroke_path; + dev->clip_path = fz_bbox_clip_path; + dev->clip_stroke_path = fz_bbox_clip_stroke_path; + + dev->fill_text = fz_bbox_fill_text; + dev->stroke_text = fz_bbox_stroke_text; + dev->clip_text = fz_bbox_clip_text; + dev->clip_stroke_text = fz_bbox_clip_stroke_text; + + dev->fill_shade = fz_bbox_fill_shade; + dev->fill_image = fz_bbox_fill_image; + dev->fill_image_mask = fz_bbox_fill_image_mask; + dev->clip_image_mask = fz_bbox_clip_image_mask; + + dev->pop_clip = fz_bbox_pop_clip; + + dev->begin_mask = fz_bbox_begin_mask; + dev->end_mask = fz_bbox_end_mask; + dev->begin_group = fz_bbox_begin_group; + dev->end_group = fz_bbox_end_group; + + dev->begin_tile = fz_bbox_begin_tile; + dev->end_tile = fz_bbox_end_tile; + + *result = fz_empty_rect; + + return dev; +} diff --git a/source/fitz/bitmap.c b/source/fitz/bitmap.c new file mode 100644 index 00000000..6357f7d7 --- /dev/null +++ b/source/fitz/bitmap.c @@ -0,0 +1,123 @@ +#include "mupdf/fitz.h" + +fz_bitmap * +fz_new_bitmap(fz_context *ctx, int w, int h, int n, int xres, int yres) +{ + fz_bitmap *bit; + + bit = fz_malloc_struct(ctx, fz_bitmap); + bit->refs = 1; + bit->w = w; + bit->h = h; + bit->n = n; + bit->xres = xres; + bit->yres = yres; + /* Span is 32 bit aligned. We may want to make this 64 bit if we + * use SSE2 etc. */ + bit->stride = ((n * w + 31) & ~31) >> 3; + + bit->samples = fz_malloc_array(ctx, h, bit->stride); + + return bit; +} + +fz_bitmap * +fz_keep_bitmap(fz_context *ctx, fz_bitmap *bit) +{ + if (bit) + bit->refs++; + return bit; +} + +void +fz_drop_bitmap(fz_context *ctx, fz_bitmap *bit) +{ + if (bit && --bit->refs == 0) + { + fz_free(ctx, bit->samples); + fz_free(ctx, bit); + } +} + +void +fz_clear_bitmap(fz_context *ctx, fz_bitmap *bit) +{ + memset(bit->samples, 0, bit->stride * bit->h); +} + +/* + * Write bitmap to PBM file + */ + +void +fz_write_pbm(fz_context *ctx, fz_bitmap *bitmap, char *filename) +{ + FILE *fp; + unsigned char *p; + int h, bytestride; + + fp = fopen(filename, "wb"); + if (!fp) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", filename, strerror(errno)); + + assert(bitmap->n == 1); + + fprintf(fp, "P4\n%d %d\n", bitmap->w, bitmap->h); + + p = bitmap->samples; + + h = bitmap->h; + bytestride = (bitmap->w + 7) >> 3; + while (h--) + { + fwrite(p, 1, bytestride, fp); + p += bitmap->stride; + } + + fclose(fp); +} + +fz_colorspace *fz_pixmap_colorspace(fz_context *ctx, fz_pixmap *pix) +{ + if (!pix) + return NULL; + return pix->colorspace; +} + +int fz_pixmap_components(fz_context *ctx, fz_pixmap *pix) +{ + if (!pix) + return 0; + return pix->n; +} + +unsigned char *fz_pixmap_samples(fz_context *ctx, fz_pixmap *pix) +{ + if (!pix) + return NULL; + return pix->samples; +} + +void fz_bitmap_details(fz_bitmap *bit, int *w, int *h, int *n, int *stride) +{ + if (!bit) + { + if (w) + *w = 0; + if (h) + *h = 0; + if (n) + *n = 0; + if (stride) + *stride = 0; + return; + } + if (w) + *w = bit->w; + if (h) + *h = bit->h; + if (n) + *n = bit->n; + if (stride) + *stride = bit->stride; +} diff --git a/source/fitz/buffer.c b/source/fitz/buffer.c new file mode 100644 index 00000000..388e0461 --- /dev/null +++ b/source/fitz/buffer.c @@ -0,0 +1,390 @@ +#include "mupdf/fitz.h" + +fz_buffer * +fz_new_buffer(fz_context *ctx, int size) +{ + fz_buffer *b; + + size = size > 1 ? size : 16; + + b = fz_malloc_struct(ctx, fz_buffer); + b->refs = 1; + fz_try(ctx) + { + b->data = fz_malloc(ctx, size); + } + fz_catch(ctx) + { + fz_free(ctx, b); + fz_rethrow(ctx); + } + b->cap = size; + b->len = 0; + b->unused_bits = 0; + + return b; +} + +fz_buffer * +fz_new_buffer_from_data(fz_context *ctx, unsigned char *data, int size) +{ + fz_buffer *b; + + b = fz_malloc_struct(ctx, fz_buffer); + b->refs = 1; + b->data = data; + b->cap = size; + b->len = size; + b->unused_bits = 0; + + return b; +} + +fz_buffer * +fz_keep_buffer(fz_context *ctx, fz_buffer *buf) +{ + if (buf) + { + if (buf->refs == 1 && buf->cap > buf->len+1) + fz_resize_buffer(ctx, buf, buf->len); + buf->refs ++; + } + + return buf; +} + +void +fz_drop_buffer(fz_context *ctx, fz_buffer *buf) +{ + if (!buf) + return; + if (--buf->refs == 0) + { + fz_free(ctx, buf->data); + fz_free(ctx, buf); + } +} + +void +fz_resize_buffer(fz_context *ctx, fz_buffer *buf, int size) +{ + buf->data = fz_resize_array(ctx, buf->data, size, 1); + buf->cap = size; + if (buf->len > buf->cap) + buf->len = buf->cap; +} + +void +fz_grow_buffer(fz_context *ctx, fz_buffer *buf) +{ + int newsize = (buf->cap * 3) / 2; + if (newsize == 0) + newsize = 256; + fz_resize_buffer(ctx, buf, newsize); +} + +static void +fz_ensure_buffer(fz_context *ctx, fz_buffer *buf, int min) +{ + int newsize = buf->cap; + while (newsize < min) + { + newsize = (newsize * 3) / 2; + } + fz_resize_buffer(ctx, buf, newsize); +} + +void +fz_trim_buffer(fz_context *ctx, fz_buffer *buf) +{ + if (buf->cap > buf->len+1) + fz_resize_buffer(ctx, buf, buf->len); +} + +int +fz_buffer_storage(fz_context *ctx, fz_buffer *buf, unsigned char **datap) +{ + if (datap) + *datap = (buf ? buf->data : NULL); + return (buf ? buf->len : 0); +} + +void +fz_buffer_cat(fz_context *ctx, fz_buffer *buf, fz_buffer *extra) +{ + if (buf->cap - buf->len < extra->len) + { + buf->data = fz_resize_array(ctx, buf->data, buf->len + extra->len, 1); + buf->cap = buf->len + extra->len; + } + + memcpy(buf->data + buf->len, extra->data, extra->len); + buf->len += extra->len; +} + +void fz_write_buffer(fz_context *ctx, fz_buffer *buf, const void *data, int len) +{ + if (buf->len + len > buf->cap) + fz_ensure_buffer(ctx, buf, buf->len + len); + memcpy(buf->data + buf->len, data, len); + buf->len += len; + buf->unused_bits = 0; +} + +void fz_write_buffer_byte(fz_context *ctx, fz_buffer *buf, int val) +{ + if (buf->len > buf->cap) + fz_grow_buffer(ctx, buf); + buf->data[buf->len++] = val; + buf->unused_bits = 0; +} + +void fz_write_buffer_rune(fz_context *ctx, fz_buffer *buf, int c) +{ + char data[10]; + int len = fz_runetochar(data, c); + if (buf->len + len > buf->cap) + fz_ensure_buffer(ctx, buf, buf->len + len); + memcpy(buf->data + buf->len, data, len); + buf->len += len; + buf->unused_bits = 0; +} + +void fz_write_buffer_bits(fz_context *ctx, fz_buffer *buf, int val, int bits) +{ + int shift; + + /* Throughout this code, the invariant is that we need to write the + * bottom 'bits' bits of 'val' into the stream. On entry we assume + * that val & ((1<<bits)-1) == val, but we do not rely on this after + * having written the first partial byte. */ + + if (bits == 0) + return; + + /* buf->len always covers all the bits in the buffer, including + * any unused ones in the last byte, which will always be 0. + * buf->unused_bits = the number of unused bits in the last byte. + */ + + /* Find the amount we need to shift val up by so that it will be in + * the correct position to be inserted into any existing data byte. */ + shift = (buf->unused_bits - bits); + + /* Extend the buffer as required before we start; that way we never + * fail part way during writing. If shift < 0, then we'll need -shift + * more bits. */ + if (shift < 0) + { + int extra = (7-shift)>>3; /* Round up to bytes */ + fz_ensure_buffer(ctx, buf, buf->len + extra); + } + + /* Write any bits that will fit into the existing byte */ + if (buf->unused_bits) + { + buf->data[buf->len-1] |= (shift >= 0 ? (((unsigned int)val)<<shift) : (((unsigned int)val)>>-shift)); + if (shift >= 0) + { + /* If we were shifting up, we're done. */ + buf->unused_bits -= bits; + return; + } + /* The number of bits left to write is the number that didn't + * fit in this first byte. */ + bits = -shift; + } + + /* Write any whole bytes */ + while (bits >= 8) + { + bits -= 8; + buf->data[buf->len++] = val>>bits; + } + + /* Write trailing bits (with 0's in unused bits) */ + if (bits > 0) + { + bits = 8-bits; + buf->data[buf->len++] = val<<bits; + } + buf->unused_bits = bits; +} + +void fz_write_buffer_pad(fz_context *ctx, fz_buffer *buf) +{ + buf->unused_bits = 0; +} + +int +fz_buffer_printf(fz_context *ctx, fz_buffer *buffer, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + + ret = fz_buffer_vprintf(ctx, buffer, fmt, args); + + va_end(args); + + return ret; +} + +int +fz_buffer_vprintf(fz_context *ctx, fz_buffer *buffer, const char *fmt, va_list old_args) +{ + int len; + + do + { + int slack = buffer->cap - buffer->len; + + if (slack > 0) + { + va_list args; +#ifdef _MSC_VER /* Microsoft Visual C */ + args = old_args; +#else + va_copy(args, old_args); +#endif + len = vsnprintf((char *)buffer->data + buffer->len, slack, fmt, args); +#ifndef _MSC_VER + va_end(args); +#endif + /* len = number of chars written, not including the terminating + * NULL, so len+1 > slack means "truncated". MSVC differs here + * and returns -1 for truncated. */ + if (len >= 0 && len+1 <= slack) + break; + } + /* Grow the buffer and retry */ + fz_grow_buffer(ctx, buffer); + } + while (1); + + buffer->len += len; + + return len; +} + +void +fz_buffer_cat_pdf_string(fz_context *ctx, fz_buffer *buffer, const char *text) +{ + int len = 2; + const char *s = text; + char *d; + char c; + + while ((c = *s++) != 0) + { + switch (c) + { + case '\n': + case '\r': + case '\t': + case '\b': + case '\f': + case '(': + case ')': + case '\\': + len++; + break; + } + len++; + } + + while(buffer->cap - buffer->len < len) + fz_grow_buffer(ctx, buffer); + + s = text; + d = (char *)buffer->data + buffer->len; + *d++ = '('; + while ((c = *s++) != 0) + { + switch (c) + { + case '\n': + *d++ = '\\'; + *d++ = 'n'; + break; + case '\r': + *d++ = '\\'; + *d++ = 'r'; + break; + case '\t': + *d++ = '\\'; + *d++ = 't'; + break; + case '\b': + *d++ = '\\'; + *d++ = 'b'; + break; + case '\f': + *d++ = '\\'; + *d++ = 'f'; + break; + case '(': + *d++ = '\\'; + *d++ = '('; + break; + case ')': + *d++ = '\\'; + *d++ = ')'; + break; + case '\\': + *d++ = '\\'; + *d++ = '\\'; + break; + default: + *d++ = c; + } + } + *d++ = ')'; + buffer->len += len; +} + +#ifdef TEST_BUFFER_WRITE + +#define TEST_LEN 1024 + +void +fz_test_buffer_write(fz_context *ctx) +{ + fz_buffer *master = fz_new_buffer(ctx, TEST_LEN); + fz_buffer *copy = fz_new_buffer(ctx, TEST_LEN); + fz_stream *stm; + int i, j, k; + + /* Make us a dummy buffer */ + for (i = 0; i < TEST_LEN; i++) + { + master->data[i] = rand(); + } + master->len = TEST_LEN; + + /* Now copy that buffer several times, checking it for validity */ + stm = fz_open_buffer(ctx, master); + for (i = 0; i < 256; i++) + { + memset(copy->data, i, TEST_LEN); + copy->len = 0; + j = TEST_LEN * 8; + do + { + k = (rand() & 31)+1; + if (k > j) + k = j; + fz_write_buffer_bits(ctx, copy, fz_read_bits(stm, k), k); + j -= k; + } + while (j); + + if (memcmp(copy->data, master->data, TEST_LEN) != 0) + fprintf(stderr, "Copied buffer is different!\n"); + fz_seek(stm, 0, 0); + } + fz_close(stm); + fz_drop_buffer(ctx, master); + fz_drop_buffer(ctx, copy); +} +#endif diff --git a/source/fitz/colorspace.c b/source/fitz/colorspace.c new file mode 100644 index 00000000..07da5a95 --- /dev/null +++ b/source/fitz/colorspace.c @@ -0,0 +1,1277 @@ +#include "mupdf/fitz.h" + +#define SLOWCMYK + +void +fz_free_colorspace_imp(fz_context *ctx, fz_storable *cs_) +{ + fz_colorspace *cs = (fz_colorspace *)cs_; + + if (cs->free_data && cs->data) + cs->free_data(ctx, cs); + fz_free(ctx, cs); +} + +fz_colorspace * +fz_new_colorspace(fz_context *ctx, char *name, int n) +{ + fz_colorspace *cs = fz_malloc(ctx, sizeof(fz_colorspace)); + FZ_INIT_STORABLE(cs, 1, fz_free_colorspace_imp); + cs->size = sizeof(fz_colorspace); + fz_strlcpy(cs->name, name, sizeof cs->name); + cs->n = n; + cs->to_rgb = NULL; + cs->from_rgb = NULL; + cs->free_data = NULL; + cs->data = NULL; + return cs; +} + +fz_colorspace * +fz_keep_colorspace(fz_context *ctx, fz_colorspace *cs) +{ + return (fz_colorspace *)fz_keep_storable(ctx, &cs->storable); +} + +void +fz_drop_colorspace(fz_context *ctx, fz_colorspace *cs) +{ + fz_drop_storable(ctx, &cs->storable); +} + +/* Device colorspace definitions */ + +static void gray_to_rgb(fz_context *ctx, fz_colorspace *cs, float *gray, float *rgb) +{ + rgb[0] = gray[0]; + rgb[1] = gray[0]; + rgb[2] = gray[0]; +} + +static void rgb_to_gray(fz_context *ctx, fz_colorspace *cs, float *rgb, float *gray) +{ + float r = rgb[0]; + float g = rgb[1]; + float b = rgb[2]; + gray[0] = r * 0.3f + g * 0.59f + b * 0.11f; +} + +static void rgb_to_rgb(fz_context *ctx, fz_colorspace *cs, float *rgb, float *xyz) +{ + xyz[0] = rgb[0]; + xyz[1] = rgb[1]; + xyz[2] = rgb[2]; +} + +static void bgr_to_rgb(fz_context *ctx, fz_colorspace *cs, float *bgr, float *rgb) +{ + rgb[0] = bgr[2]; + rgb[1] = bgr[1]; + rgb[2] = bgr[0]; +} + +static void rgb_to_bgr(fz_context *ctx, fz_colorspace *cs, float *rgb, float *bgr) +{ + bgr[0] = rgb[2]; + bgr[1] = rgb[1]; + bgr[2] = rgb[0]; +} + +static void cmyk_to_rgb(fz_context *ctx, fz_colorspace *cs, float *cmyk, float *rgb) +{ +#ifdef SLOWCMYK /* from poppler */ + float c = cmyk[0], m = cmyk[1], y = cmyk[2], k = cmyk[3]; + float r, g, b, x; + float cm = c * m; + float c1m = m - cm; + float cm1 = c - cm; + float c1m1 = 1 - m - cm1; + float c1m1y = c1m1 * y; + float c1m1y1 = c1m1 - c1m1y; + float c1my = c1m * y; + float c1my1 = c1m - c1my; + float cm1y = cm1 * y; + float cm1y1 = cm1 - cm1y; + float cmy = cm * y; + float cmy1 = cm - cmy; + + /* this is a matrix multiplication, unrolled for performance */ + x = c1m1y1 * k; /* 0 0 0 1 */ + r = g = b = c1m1y1 - x; /* 0 0 0 0 */ + r += 0.1373 * x; + g += 0.1216 * x; + b += 0.1255 * x; + + x = c1m1y * k; /* 0 0 1 1 */ + r += 0.1098 * x; + g += 0.1020 * x; + x = c1m1y - x; /* 0 0 1 0 */ + r += x; + g += 0.9490 * x; + + x = c1my1 * k; /* 0 1 0 1 */ + r += 0.1412 * x; + x = c1my1 - x; /* 0 1 0 0 */ + r += 0.9255 * x; + b += 0.5490 * x; + + x = c1my * k; /* 0 1 1 1 */ + r += 0.1333 * x; + x = c1my - x; /* 0 1 1 0 */ + r += 0.9294 * x; + g += 0.1098 * x; + b += 0.1412 * x; + + x = cm1y1 * k; /* 1 0 0 1 */ + g += 0.0588 * x; + b += 0.1412 * x; + x = cm1y1 - x; /* 1 0 0 0 */ + g += 0.6784 * x; + b += 0.9373 * x; + + x = cm1y * k; /* 1 0 1 1 */ + g += 0.0745 * x; + x = cm1y - x; /* 1 0 1 0 */ + g += 0.6510 * x; + b += 0.3137 * x; + + x = cmy1 * k; /* 1 1 0 1 */ + b += 0.0078 * x; + x = cmy1 - x; /* 1 1 0 0 */ + r += 0.1804 * x; + g += 0.1922 * x; + b += 0.5725 * x; + + x = cmy * (1-k); /* 1 1 1 0 */ + r += 0.2118 * x; + g += 0.2119 * x; + b += 0.2235 * x; + rgb[0] = fz_clamp(r, 0, 1); + rgb[1] = fz_clamp(g, 0, 1); + rgb[2] = fz_clamp(b, 0, 1); +#else + rgb[0] = 1 - fz_min(1, cmyk[0] + cmyk[3]); + rgb[1] = 1 - fz_min(1, cmyk[1] + cmyk[3]); + rgb[2] = 1 - fz_min(1, cmyk[2] + cmyk[3]); +#endif +} + +static void rgb_to_cmyk(fz_context *ctx, fz_colorspace *cs, float *rgb, float *cmyk) +{ + float c, m, y, k; + c = 1 - rgb[0]; + m = 1 - rgb[1]; + y = 1 - rgb[2]; + k = fz_min(c, fz_min(m, y)); + cmyk[0] = c - k; + cmyk[1] = m - k; + cmyk[2] = y - k; + cmyk[3] = k; +} + +static fz_colorspace k_default_gray = { {-1, fz_free_colorspace_imp}, 0, "DeviceGray", 1, gray_to_rgb, rgb_to_gray }; +static fz_colorspace k_default_rgb = { {-1, fz_free_colorspace_imp}, 0, "DeviceRGB", 3, rgb_to_rgb, rgb_to_rgb }; +static fz_colorspace k_default_bgr = { {-1, fz_free_colorspace_imp}, 0, "DeviceRGB", 3, bgr_to_rgb, rgb_to_bgr }; +static fz_colorspace k_default_cmyk = { {-1, fz_free_colorspace_imp}, 0, "DeviceCMYK", 4, cmyk_to_rgb, rgb_to_cmyk }; + +static fz_colorspace *fz_default_gray = &k_default_gray; +static fz_colorspace *fz_default_rgb = &k_default_rgb; +static fz_colorspace *fz_default_bgr = &k_default_bgr; +static fz_colorspace *fz_default_cmyk = &k_default_cmyk; + +struct fz_colorspace_context_s +{ + int ctx_refs; + fz_colorspace *gray, *rgb, *bgr, *cmyk; +}; + +void fz_new_colorspace_context(fz_context *ctx) +{ + ctx->colorspace = fz_malloc_struct(ctx, fz_colorspace_context); + ctx->colorspace->ctx_refs = 1; + ctx->colorspace->gray = fz_default_gray; + ctx->colorspace->rgb = fz_default_rgb; + ctx->colorspace->bgr = fz_default_bgr; + ctx->colorspace->cmyk = fz_default_cmyk; +} + +fz_colorspace_context * +fz_keep_colorspace_context(fz_context *ctx) +{ + if (!ctx || !ctx->colorspace) + return NULL; + fz_lock(ctx, FZ_LOCK_ALLOC); + ctx->colorspace->ctx_refs++; + fz_unlock(ctx, FZ_LOCK_ALLOC); + return ctx->colorspace; +} + +void fz_drop_colorspace_context(fz_context *ctx) +{ + int drop; + if (!ctx || !ctx->colorspace) + return; + fz_lock(ctx, FZ_LOCK_ALLOC); + drop = --ctx->colorspace->ctx_refs; + fz_unlock(ctx, FZ_LOCK_ALLOC); + if (drop == 0) + fz_free(ctx, ctx->colorspace); +} + +fz_colorspace * +fz_device_gray(fz_context *ctx) +{ + return ctx->colorspace->gray; +} + +fz_colorspace * +fz_device_rgb(fz_context *ctx) +{ + return ctx->colorspace->rgb; +} + +fz_colorspace * +fz_device_bgr(fz_context *ctx) +{ + return ctx->colorspace->bgr; +} + +fz_colorspace * +fz_device_cmyk(fz_context *ctx) +{ + return ctx->colorspace->cmyk; +} + +fz_colorspace * +fz_lookup_device_colorspace(fz_context *ctx, char *name) +{ + if (!strcmp(name, "DeviceGray")) + return fz_device_gray(ctx); + if (!strcmp(name, "DeviceRGB")) + return fz_device_rgb(ctx); + if (!strcmp(name, "DeviceBGR")) + return fz_device_bgr(ctx); + if (!strcmp(name, "DeviceCMYK")) + return fz_device_cmyk(ctx); + assert(!"unknown device colorspace"); + return NULL; +} + +void +fz_set_device_gray(fz_context *ctx, fz_colorspace *cs) +{ + fz_drop_colorspace(ctx, ctx->colorspace->gray); + ctx->colorspace->gray = fz_keep_colorspace(ctx, cs); +} + +void +fz_set_device_rgb(fz_context *ctx, fz_colorspace *cs) +{ + fz_drop_colorspace(ctx, ctx->colorspace->rgb); + ctx->colorspace->rgb = fz_keep_colorspace(ctx, cs); +} + +void +fz_set_device_bgr(fz_context *ctx, fz_colorspace *cs) +{ + fz_drop_colorspace(ctx, ctx->colorspace->bgr); + ctx->colorspace->bgr = fz_keep_colorspace(ctx, cs); +} + +void +fz_set_device_cmyk(fz_context *ctx, fz_colorspace *cs) +{ + fz_drop_colorspace(ctx, ctx->colorspace->cmyk); + ctx->colorspace->cmyk = fz_keep_colorspace(ctx, cs); +} + +int +fz_colorspace_is_indexed(fz_colorspace *cs) +{ + return (cs && !strcmp(cs->name, "Indexed")); +} + +/* Fast pixmap color conversions */ + +static void fast_gray_to_rgb(fz_pixmap *dst, fz_pixmap *src) +{ + unsigned char *s = src->samples; + unsigned char *d = dst->samples; + int n = src->w * src->h; + while (n--) + { + d[0] = s[0]; + d[1] = s[0]; + d[2] = s[0]; + d[3] = s[1]; + s += 2; + d += 4; + } +} + +static void fast_gray_to_cmyk(fz_pixmap *dst, fz_pixmap *src) +{ + unsigned char *s = src->samples; + unsigned char *d = dst->samples; + int n = src->w * src->h; + while (n--) + { + d[0] = 0; + d[1] = 0; + d[2] = 0; + d[3] = s[0]; + d[4] = s[1]; + s += 2; + d += 5; + } +} + +static void fast_rgb_to_gray(fz_pixmap *dst, fz_pixmap *src) +{ + unsigned char *s = src->samples; + unsigned char *d = dst->samples; + int n = src->w * src->h; + while (n--) + { + d[0] = ((s[0]+1) * 77 + (s[1]+1) * 150 + (s[2]+1) * 28) >> 8; + d[1] = s[3]; + s += 4; + d += 2; + } +} + +static void fast_bgr_to_gray(fz_pixmap *dst, fz_pixmap *src) +{ + unsigned char *s = src->samples; + unsigned char *d = dst->samples; + int n = src->w * src->h; + while (n--) + { + d[0] = ((s[0]+1) * 28 + (s[1]+1) * 150 + (s[2]+1) * 77) >> 8; + d[1] = s[3]; + s += 4; + d += 2; + } +} + +static void fast_rgb_to_cmyk(fz_pixmap *dst, fz_pixmap *src) +{ + unsigned char *s = src->samples; + unsigned char *d = dst->samples; + int n = src->w * src->h; + while (n--) + { + unsigned char c = 255 - s[0]; + unsigned char m = 255 - s[1]; + unsigned char y = 255 - s[2]; + unsigned char k = (unsigned char)fz_mini(c, fz_mini(m, y)); + d[0] = c - k; + d[1] = m - k; + d[2] = y - k; + d[3] = k; + d[4] = s[3]; + s += 4; + d += 5; + } +} + +static void fast_bgr_to_cmyk(fz_pixmap *dst, fz_pixmap *src) +{ + unsigned char *s = src->samples; + unsigned char *d = dst->samples; + int n = src->w * src->h; + while (n--) + { + unsigned char c = 255 - s[2]; + unsigned char m = 255 - s[1]; + unsigned char y = 255 - s[0]; + unsigned char k = (unsigned char)fz_mini(c, fz_mini(m, y)); + d[0] = c - k; + d[1] = m - k; + d[2] = y - k; + d[3] = k; + d[4] = s[3]; + s += 4; + d += 5; + } +} + +static void fast_cmyk_to_gray(fz_pixmap *dst, fz_pixmap *src) +{ + unsigned char *s = src->samples; + unsigned char *d = dst->samples; + int n = src->w * src->h; + while (n--) + { + unsigned char c = fz_mul255(s[0], 77); + unsigned char m = fz_mul255(s[1], 150); + unsigned char y = fz_mul255(s[2], 28); + d[0] = 255 - (unsigned char)fz_mini(c + m + y + s[3], 255); + d[1] = s[4]; + s += 5; + d += 2; + } +} + +#ifdef ARCH_ARM +static void +fast_cmyk_to_rgb_ARM(unsigned char *dst, unsigned char *src, int n) +__attribute__((naked)); + +static void +fast_cmyk_to_rgb_ARM(unsigned char *dst, unsigned char *src, int n) +{ + asm volatile( + ENTER_ARM + "stmfd r13!,{r4-r11,r14} \n" + "@ r0 = dst \n" + "@ r1 = src \n" + "@ r2 = n \n" + "mov r12, #0 @ r12= CMYK = 0 \n" + "b 2f @ enter loop \n" + "1: @ White or Black \n" + "@ Cunning trick: On entry r11 = 0 if black, r11 = FF if white \n" + "ldrb r7, [r1],#1 @ r8 = s[4] \n" + "strb r11,[r0],#1 @ d[0] = r \n" + "strb r11,[r0],#1 @ d[1] = g \n" + "strb r11,[r0],#1 @ d[2] = b \n" + "strb r7, [r0],#1 @ d[3] = s[4] \n" + "subs r2, r2, #1 @ r2 = n-- \n" + "beq 9f \n" + "2: @ Main loop starts here \n" + "ldrb r3, [r1], #4 @ r3 = c \n" + "ldrb r6, [r1, #-1] @ r6 = k \n" + "ldrb r5, [r1, #-2] @ r5 = y \n" + "ldrb r4, [r1, #-3] @ r4 = m \n" + "eors r11,r6, #0xFF @ if (k == 255) \n" + "beq 1b @ goto black \n" + "orr r7, r3, r4, LSL #8 \n" + "orr r14,r5, r6, LSL #8 \n" + "orrs r7, r7, r14,LSL #16 @ r7 = cmyk \n" + "beq 1b @ if (cmyk == 0) white \n" + "@ At this point, we have to decode a new pixel \n" + "@ r0 = dst r1 = src r2 = n r7 = cmyk \n" + "3: @ unmatched \n" + "stmfd r13!,{r0-r1,r7} @ stash regs for space \n" + "add r3, r3, r3, LSR #7 @ r3 = c += c>>7 \n" + "add r4, r4, r4, LSR #7 @ r4 = m += m>>7 \n" + "add r5, r5, r5, LSR #7 @ r5 = y += y>>7 \n" + "add r6, r6, r6, LSR #7 @ r6 = k += k>>7 \n" + "mov r5, r5, LSR #1 @ sacrifice 1 bit of Y \n" + "mul r8, r3, r4 @ r8 = cm = c * m \n" + "rsb r9, r8, r4, LSL #8 @ r9 = c1m = (m<<8) - cm \n" + "rsb r3, r8, r3, LSL #8 @ r3 = cm1 = (c<<8) - cm \n" + "rsb r4, r4, #0x100 @ r4 = 256-m \n" + "rsb r4, r3, r4, LSL #8 @ r4 = c1m1 =((256-m)<<8)-cm1 \n" + "mul r7, r4, r5 @ r7 = c1m1y = c1m1 * y \n" + "rsb r4, r7, r4, LSL #7 @ r4 = c1m1y1 = (c1m1<<7)-c1m1y \n" + "mul r10,r9, r5 @ r10= c1my = c1m * y \n" + "rsb r9, r10,r9, LSL #7 @ r9 = c1my1 = (c1m<<7) - c1my \n" + "mul r11,r3, r5 @ r11= cm1y = cm1 * y \n" + "rsb r3, r11,r3, LSL #7 @ r3 = cm1y1 = (cm1<<7) - cm1y \n" + "mul r5, r8, r5 @ r5 = cmy = cm * y \n" + "rsb r8, r5, r8, LSL #7 @ r8 = cmy1 = (cm<<7) - cmy \n" + "@ Register recap: \n" + "@ r3 = cm1y1 \n" + "@ r4 = c1m1y1 \n" + "@ r5 = cmy \n" + "@ r6 = k \n" + "@ r7 = c1m1y \n" + "@ r8 = cmy1 \n" + "@ r9 = c1my1 \n" + "@ r10= c1my \n" + "@ r11= cm1y \n" + "@ The actual matrix multiplication \n" + "mul r14,r4, r6 @ r14= x1 = c1m1y1 * k \n" + "rsb r4, r14,r4, LSL #8 @ r4 = x0 = (c1m1y1<<8) - x1 \n" + "add r4, r4, r14,LSR #8-5 @ r4 = b = x0 + 32*(x1>>8) \n" + "sub r1, r4, r14,LSR #8 @ r1 = g = x0 + 31*(x1>>8) \n" + "add r0, r1, r14,LSR #8-2 @ r0 = r = x0 + 35*(x1>>8) \n" + " \n" + "mul r14,r7, r6 @ r14= x1 = c1m1y * k \n" + "rsb r7, r14,r7, LSL #8 @ r7 = x0 = (c1m1y<<8) - x1 \n" + "add r0, r0, r7 @ r0 = r += x0 \n" + "add r1, r1, r7 @ r1 = g += (x0>>8 * 256) \n" + "sub r1, r1, r7, LSR #8-3 @ 248 \n" + "sub r1, r1, r7, LSR #8-2 @ 244 \n" + "sub r1, r1, r7, LSR #8 @ 243 \n" + "sub r7, r14,r14,LSR #3 @ r7 = 28*(x1>>5) \n" + "add r0, r0, r7, LSR #8-5 @ r0 = r += 28 * x1 \n" + "sub r7, r7, r14,LSR #4 @ r7 = 26*(x1>>5) \n" + "add r1, r1, r7, LSR #8-5 @ r1 = g += 26 * x1 \n" + " \n" + "mul r14,r9, r6 @ r14= x1 = c1my1 * k \n" + "sub r9, r9, r14,LSR #8 @ r9 = x0>>8 = c1my1 - (x1>>8) \n" + "add r0, r0, r14,LSR #8-5 @ r0 = r += (x1>>8)*32 \n" + "add r0, r0, r14,LSR #8-2 @ r0 = r += (x1>>8)*36 \n" + "mov r14,#237 @ r14= 237 \n" + "mla r0,r14,r9,r0 @ r14= r += x0*237 \n" + "mov r14,#141 @ r14= 141 \n" + "mla r4,r14,r9,r4 @ r14= b += x0*141 \n" + " \n" + "mul r14,r10,r6 @ r14= x1 = c1my * k \n" + "sub r10,r10,r14,LSR #8 @ r10= x0>>8 = c1my - (x1>>8) \n" + "add r0, r0, r14,LSR #8-5 @ r0 = r += 32 * x1 \n" + "add r0, r0, r14,LSR #8-1 @ r0 = r += 34 * x1 \n" + "mov r14,#238 @ r14= 238 \n" + "mla r0,r14,r10,r0 @ r0 = r += 238 * x0 \n" + "mov r14,#28 @ r14= 28 \n" + "mla r1,r14,r10,r1 @ r1 = g += 28 * x0 \n" + "mov r14,#36 @ r14= 36 \n" + "mla r4,r14,r10,r4 @ r4 = b += 36 * x0 \n" + " \n" + "mul r14,r3, r6 @ r14= x1 = cm1y1 * k \n" + "sub r3, r3, r14,LSR #8 @ r3 = x1>>8 = cm1y1 - (x1>>8) \n" + "add r1, r1, r14,LSR #8-4 @ r1 = g += 16*x1 \n" + "sub r1, r1, r14,LSR #8 @ 15*x1 \n" + "add r4, r4, r14,LSR #8-5 @ r4 = b += 32*x1 \n" + "add r4, r4, r14,LSR #8-2 @ 36*x1 \n" + "mov r14,#174 @ r14= 174 \n" + "mla r1, r14,r3, r1 @ r1 = g += 174 * x0 \n" + "mov r14,#240 @ r14= 240 \n" + "mla r4, r14,r3, r4 @ r4 = b += 240 * x0 \n" + " \n" + "mul r14,r11,r6 @ r14= x1 = cm1y * k \n" + "sub r11,r11,r14,LSR #8 @ r11= x0>>8 = cm1y - (x1>>8) \n" + "add r1, r1, r14,LSR #8-4 @ r1 = g += x1 * 16 \n" + "add r1, r1, r14,LSR #8 @ x1 * 17 \n" + "add r1, r1, r14,LSR #8-1 @ x1 * 19 \n" + "mov r14,#167 @ r14 = 167 \n" + "mla r1, r14,r11,r1 @ r1 = g += 167 * x0 \n" + "mov r14,#80 @ r14 = 80 \n" + "mla r4, r14,r11,r4 @ r4 = b += 80 * x0 \n" + " \n" + "mul r14,r8, r6 @ r14= x1 = cmy1 * k \n" + "sub r8, r8, r14,LSR #8 @ r8 = x0>>8 = cmy1 - (x1>>8) \n" + "add r4, r4, r14,LSR #8-1 @ r4 = b += x1 * 2 \n" + "mov r14,#46 @ r14=46 \n" + "mla r0, r14,r8, r0 @ r0 = r += 46 * x0 \n" + "mov r14,#49 @ r14=49 \n" + "mla r1, r14,r8, r1 @ r1 = g += 49 * x0 \n" + "mov r14,#147 @ r14=147 \n" + "mla r4, r14,r8, r4 @ r4 = b += 147 * x0 \n" + " \n" + "rsb r6, r6, #256 @ r6 = k = 256-k \n" + "mul r14,r5, r6 @ r14= x0 = cmy * (256-k) \n" + "mov r11,#54 @ r11= 54 \n" + "mov r14,r14,LSR #8 @ r14= (x0>>8) \n" + "mov r8,#57 @ r8 = 57 \n" + "mla r0,r14,r11,r0 @ r0 = r += 54*x0 \n" + "mla r1,r14,r11,r1 @ r1 = g += 54*x0 \n" + "mla r4,r14,r8, r4 @ r4 = b += 57*x0 \n" + " \n" + "sub r8, r0, r0, LSR #8 @ r8 = r -= (r>>8) \n" + "sub r9, r1, r1, LSR #8 @ r9 = g -= (r>>8) \n" + "sub r10,r4, r4, LSR #8 @ r10= b -= (r>>8) \n" + "ldmfd r13!,{r0-r1,r12} \n" + "mov r8, r8, LSR #23 @ r8 = r>>23 \n" + "mov r9, r9, LSR #23 @ r9 = g>>23 \n" + "mov r10,r10,LSR #23 @ r10= b>>23 \n" + "ldrb r14,[r1],#1 @ r8 = s[4] \n" + "strb r8, [r0],#1 @ d[0] = r \n" + "strb r9, [r0],#1 @ d[1] = g \n" + "strb r10,[r0],#1 @ d[2] = b \n" + "strb r14,[r0],#1 @ d[3] = s[4] \n" + "subs r2, r2, #1 @ r2 = n-- \n" + "beq 9f \n" + "@ At this point, we've just decoded a pixel \n" + "@ r0 = dst r1 = src r2 = n r8 = r r9 = g r10= b r12= CMYK \n" + "4: \n" + "ldrb r3, [r1], #4 @ r3 = c \n" + "ldrb r6, [r1, #-1] @ r6 = k \n" + "ldrb r5, [r1, #-2] @ r5 = y \n" + "ldrb r4, [r1, #-3] @ r4 = m \n" + "eors r11,r6, #0xFF @ if (k == 255) \n" + "beq 1b @ goto black \n" + "orr r7, r3, r4, LSL #8 \n" + "orr r14,r5, r6, LSL #8 \n" + "orrs r7, r7, r14,LSL #16 @ r7 = cmyk \n" + "beq 1b @ if (cmyk == 0) white \n" + "cmp r7, r12 @ if (cmyk != CMYK) \n" + "bne 3b @ not the same, loop \n" + "@ If we get here, we just matched a pixel we have just decoded \n" + "ldrb r3, [r1],#1 @ r8 = s[4] \n" + "strb r8, [r0],#1 @ d[0] = r \n" + "strb r9, [r0],#1 @ d[1] = g \n" + "strb r10,[r0],#1 @ d[2] = b \n" + "strb r3, [r0],#1 @ d[3] = s[4] \n" + "subs r2, r2, #1 @ r2 = n-- \n" + "bne 4b \n" + "9: \n" + "ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n" + ENTER_THUMB + ); +} +#endif + +static void fast_cmyk_to_rgb(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +{ + unsigned char *s = src->samples; + unsigned char *d = dst->samples; + int n = src->w * src->h; +#ifdef ARCH_ARM + fast_cmyk_to_rgb_ARM(d, s, n); +#else + unsigned int C,M,Y,K,r,g,b; + + C = 0; + M = 0; + Y = 0; + K = 0; + r = 255; + g = 255; + b = 255; + + while (n--) + { +#ifdef SLOWCMYK + unsigned int c = s[0]; + unsigned int m = s[1]; + unsigned int y = s[2]; + unsigned int k = s[3]; + unsigned int cm, c1m, cm1, c1m1, c1m1y, c1m1y1, c1my, c1my1, cm1y, cm1y1, cmy, cmy1; + unsigned int x0, x1; + + if (c == C && m == M && y == Y && k == K) + { + /* Nothing to do */ + } + else if (k == 0 && c == 0 && m == 0 && y == 0) + { + r = g = b = 255; + } + else if (k == 255) + { + r = g = b = 0; + } + else + { + c += c>>7; + m += m>>7; + y += y>>7; + k += k>>7; + y >>= 1; /* Ditch 1 bit of Y to avoid overflow */ + cm = c * m; + c1m = (m<<8) - cm; + cm1 = (c<<8) - cm; + c1m1 = ((256 - m)<<8) - cm1; + c1m1y = c1m1 * y; + c1m1y1 = (c1m1<<7) - c1m1y; + c1my = c1m * y; + c1my1 = (c1m<<7) - c1my; + cm1y = cm1 * y; + cm1y1 = (cm1<<7) - cm1y; + cmy = cm * y; + cmy1 = (cm<<7) - cmy; + + /* this is a matrix multiplication, unrolled for performance */ + x1 = c1m1y1 * k; /* 0 0 0 1 */ + x0 = (c1m1y1<<8) - x1; /* 0 0 0 0 */ + x1 = x1>>8; /* From 23 fractional bits to 15 */ + r = g = b = x0; + r += 35 * x1; /* 0.1373 */ + g += 31 * x1; /* 0.1216 */ + b += 32 * x1; /* 0.1255 */ + + x1 = c1m1y * k; /* 0 0 1 1 */ + x0 = (c1m1y<<8) - x1; /* 0 0 1 0 */ + x1 >>= 8; /* From 23 fractional bits to 15 */ + r += 28 * x1; /* 0.1098 */ + g += 26 * x1; /* 0.1020 */ + r += x0; + x0 >>= 8; /* From 23 fractional bits to 15 */ + g += 243 * x0; /* 0.9490 */ + + x1 = c1my1 * k; /* 0 1 0 1 */ + x0 = (c1my1<<8) - x1; /* 0 1 0 0 */ + x1 >>= 8; /* From 23 fractional bits to 15 */ + x0 >>= 8; /* From 23 fractional bits to 15 */ + r += 36 * x1; /* 0.1412 */ + r += 237 * x0; /* 0.9255 */ + b += 141 * x0; /* 0.5490 */ + + x1 = c1my * k; /* 0 1 1 1 */ + x0 = (c1my<<8) - x1; /* 0 1 1 0 */ + x1 >>= 8; /* From 23 fractional bits to 15 */ + x0 >>= 8; /* From 23 fractional bits to 15 */ + r += 34 * x1; /* 0.1333 */ + r += 238 * x0; /* 0.9294 */ + g += 28 * x0; /* 0.1098 */ + b += 36 * x0; /* 0.1412 */ + + x1 = cm1y1 * k; /* 1 0 0 1 */ + x0 = (cm1y1<<8) - x1; /* 1 0 0 0 */ + x1 >>= 8; /* From 23 fractional bits to 15 */ + x0 >>= 8; /* From 23 fractional bits to 15 */ + g += 15 * x1; /* 0.0588 */ + b += 36 * x1; /* 0.1412 */ + g += 174 * x0; /* 0.6784 */ + b += 240 * x0; /* 0.9373 */ + + x1 = cm1y * k; /* 1 0 1 1 */ + x0 = (cm1y<<8) - x1; /* 1 0 1 0 */ + x1 >>= 8; /* From 23 fractional bits to 15 */ + x0 >>= 8; /* From 23 fractional bits to 15 */ + g += 19 * x1; /* 0.0745 */ + g += 167 * x0; /* 0.6510 */ + b += 80 * x0; /* 0.3137 */ + + x1 = cmy1 * k; /* 1 1 0 1 */ + x0 = (cmy1<<8) - x1; /* 1 1 0 0 */ + x1 >>= 8; /* From 23 fractional bits to 15 */ + x0 >>= 8; /* From 23 fractional bits to 15 */ + b += 2 * x1; /* 0.0078 */ + r += 46 * x0; /* 0.1804 */ + g += 49 * x0; /* 0.1922 */ + b += 147 * x0; /* 0.5725 */ + + x0 = cmy * (256-k); /* 1 1 1 0 */ + x0 >>= 8; /* From 23 fractional bits to 15 */ + r += 54 * x0; /* 0.2118 */ + g += 54 * x0; /* 0.2119 */ + b += 57 * x0; /* 0.2235 */ + + r -= (r>>8); + g -= (g>>8); + b -= (b>>8); + r = r>>23; + g = g>>23; + b = b>>23; + C = c; + M = m; + Y = y; + K = k; + } + d[0] = r; + d[1] = g; + d[2] = b; +#else + d[0] = 255 - (unsigned char)fz_mini(s[0] + s[3], 255); + d[1] = 255 - (unsigned char)fz_mini(s[1] + s[3], 255); + d[2] = 255 - (unsigned char)fz_mini(s[2] + s[3], 255); +#endif + d[3] = s[4]; + s += 5; + d += 4; + } +#endif +} + +static void fast_cmyk_to_bgr(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +{ + unsigned char *s = src->samples; + unsigned char *d = dst->samples; + int n = src->w * src->h; + while (n--) + { +#ifdef SLOWCMYK + float cmyk[4], rgb[3]; + cmyk[0] = s[0] / 255.0f; + cmyk[1] = s[1] / 255.0f; + cmyk[2] = s[2] / 255.0f; + cmyk[3] = s[3] / 255.0f; + cmyk_to_rgb(ctx, NULL, cmyk, rgb); + d[0] = rgb[2] * 255; + d[1] = rgb[1] * 255; + d[2] = rgb[0] * 255; +#else + d[0] = 255 - (unsigned char)fz_mini(s[2] + s[3], 255); + d[1] = 255 - (unsigned char)fz_mini(s[1] + s[3], 255); + d[2] = 255 - (unsigned char)fz_mini(s[0] + s[3], 255); +#endif + d[3] = s[4]; + s += 5; + d += 4; + } +} + +static void fast_rgb_to_bgr(fz_pixmap *dst, fz_pixmap *src) +{ + unsigned char *s = src->samples; + unsigned char *d = dst->samples; + int n = src->w * src->h; + while (n--) + { + d[0] = s[2]; + d[1] = s[1]; + d[2] = s[0]; + d[3] = s[3]; + s += 4; + d += 4; + } +} + +static void +fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +{ + float srcv[FZ_MAX_COLORS]; + float dstv[FZ_MAX_COLORS]; + int srcn, dstn; + int k, i; + unsigned int xy; + + fz_colorspace *ss = src->colorspace; + fz_colorspace *ds = dst->colorspace; + + unsigned char *s = src->samples; + unsigned char *d = dst->samples; + + assert(src->w == dst->w && src->h == dst->h); + assert(src->n == ss->n + 1); + assert(dst->n == ds->n + 1); + + srcn = ss->n; + dstn = ds->n; + + xy = (unsigned int)(src->w * src->h); + + /* Special case for Lab colorspace (scaling of components to float) */ + if (!strcmp(ss->name, "Lab") && srcn == 3) + { + fz_color_converter cc; + + fz_lookup_color_converter(&cc, ctx, ds, ss); + for (; xy > 0; xy--) + { + srcv[0] = *s++ / 255.0f * 100; + srcv[1] = *s++ - 128; + srcv[2] = *s++ - 128; + + cc.convert(&cc, dstv, srcv); + + for (k = 0; k < dstn; k++) + *d++ = dstv[k] * 255; + + *d++ = *s++; + } + } + + /* Brute-force for small images */ + else if (xy < 256) + { + fz_color_converter cc; + + fz_lookup_color_converter(&cc, ctx, ds, ss); + for (; xy > 0; xy--) + { + for (k = 0; k < srcn; k++) + srcv[k] = *s++ / 255.0f; + + cc.convert(&cc, dstv, srcv); + + for (k = 0; k < dstn; k++) + *d++ = dstv[k] * 255; + + *d++ = *s++; + } + } + + /* 1-d lookup table for separation and similar colorspaces */ + else if (srcn == 1) + { + unsigned char lookup[FZ_MAX_COLORS * 256]; + fz_color_converter cc; + + fz_lookup_color_converter(&cc, ctx, ds, ss); + for (i = 0; i < 256; i++) + { + srcv[0] = i / 255.0f; + cc.convert(&cc, dstv, srcv); + for (k = 0; k < dstn; k++) + lookup[i * dstn + k] = dstv[k] * 255; + } + + for (; xy > 0; xy--) + { + i = *s++; + for (k = 0; k < dstn; k++) + *d++ = lookup[i * dstn + k]; + *d++ = *s++; + } + } + + /* Memoize colors using a hash table for the general case */ + else + { + fz_hash_table *lookup; + unsigned char *color; + unsigned char dummy = s[0] ^ 255; + unsigned char *sold = &dummy; + fz_color_converter cc; + + fz_lookup_color_converter(&cc, ctx, ds, ss); + lookup = fz_new_hash_table(ctx, 509, srcn, -1); + + for (; xy > 0; xy--) + { + if (*s == *sold && memcmp(sold,s,srcn) == 0) + { + sold = s; + memcpy(d, d-dstn-1, dstn); + d += dstn; + s += srcn; + *d++ = *s++; + } + else + { + sold = s; + color = fz_hash_find(ctx, lookup, s); + if (color) + { + memcpy(d, color, dstn); + s += srcn; + d += dstn; + *d++ = *s++; + } + else + { + for (k = 0; k < srcn; k++) + srcv[k] = *s++ / 255.0f; + cc.convert(&cc, dstv, srcv); + for (k = 0; k < dstn; k++) + *d++ = dstv[k] * 255; + + fz_hash_insert(ctx, lookup, s - srcn, d - dstn); + + *d++ = *s++; + } + } + } + + fz_free_hash(ctx, lookup); + } +} + +void +fz_convert_pixmap(fz_context *ctx, fz_pixmap *dp, fz_pixmap *sp) +{ + fz_colorspace *ss = sp->colorspace; + fz_colorspace *ds = dp->colorspace; + + assert(ss && ds); + + dp->interpolate = sp->interpolate; + + if (ss == fz_default_gray) + { + if (ds == fz_default_rgb) fast_gray_to_rgb(dp, sp); + else if (ds == fz_default_bgr) fast_gray_to_rgb(dp, sp); /* bgr == rgb here */ + else if (ds == fz_default_cmyk) fast_gray_to_cmyk(dp, sp); + else fz_std_conv_pixmap(ctx, dp, sp); + } + + else if (ss == fz_default_rgb) + { + if (ds == fz_default_gray) fast_rgb_to_gray(dp, sp); + else if (ds == fz_default_bgr) fast_rgb_to_bgr(dp, sp); + else if (ds == fz_default_cmyk) fast_rgb_to_cmyk(dp, sp); + else fz_std_conv_pixmap(ctx, dp, sp); + } + + else if (ss == fz_default_bgr) + { + if (ds == fz_default_gray) fast_bgr_to_gray(dp, sp); + else if (ds == fz_default_rgb) fast_rgb_to_bgr(dp, sp); /* bgr = rgb here */ + else if (ds == fz_default_cmyk) fast_bgr_to_cmyk(sp, dp); + else fz_std_conv_pixmap(ctx, dp, sp); + } + + else if (ss == fz_default_cmyk) + { + if (ds == fz_default_gray) fast_cmyk_to_gray(dp, sp); + else if (ds == fz_default_bgr) fast_cmyk_to_bgr(ctx, dp, sp); + else if (ds == fz_default_rgb) fast_cmyk_to_rgb(ctx, dp, sp); + else fz_std_conv_pixmap(ctx, dp, sp); + } + + else fz_std_conv_pixmap(ctx, dp, sp); +} + +/* Convert a single color */ + +static void +std_conv_color(fz_color_converter *cc, float *dstv, float *srcv) +{ + float rgb[3]; + int i; + fz_colorspace *srcs = cc->ss; + fz_colorspace *dsts = cc->ds; + fz_context *ctx = cc->ctx; + + if (srcs != dsts) + { + assert(srcs->to_rgb && dsts->from_rgb); + srcs->to_rgb(ctx, srcs, srcv, rgb); + dsts->from_rgb(ctx, dsts, rgb, dstv); + for (i = 0; i < dsts->n; i++) + dstv[i] = fz_clamp(dstv[i], 0, 1); + } + else + { + for (i = 0; i < srcs->n; i++) + dstv[i] = srcv[i]; + } +} + +static void +g2rgb(fz_color_converter *cc, float *dv, float *sv) +{ + dv[0] = sv[0]; + dv[1] = sv[0]; + dv[2] = sv[0]; +} + +static void +g2cmyk(fz_color_converter *cc, float *dv, float *sv) +{ + dv[0] = 0; + dv[1] = 0; + dv[2] = 0; + dv[3] = sv[0]; +} + +static void +rgb2g(fz_color_converter *cc, float *dv, float *sv) +{ + dv[0] = sv[0] * 0.3f + sv[1] * 0.59f + sv[2] * 0.11f; +} + +static void +rgb2bgr(fz_color_converter *cc, float *dv, float *sv) +{ + dv[0] = sv[2]; + dv[1] = sv[1]; + dv[2] = sv[0]; +} + +static void +rgb2cmyk(fz_color_converter *cc, float *dv, float *sv) +{ + float c = 1 - sv[0]; + float m = 1 - sv[1]; + float y = 1 - sv[2]; + float k = fz_min(c, fz_min(m, y)); + dv[0] = c - k; + dv[1] = m - k; + dv[2] = y - k; + dv[3] = k; +} + +static void +bgr2g(fz_color_converter *cc, float *dv, float *sv) +{ + dv[0] = sv[0] * 0.11f + sv[1] * 0.59f + sv[2] * 0.3f; +} + +static void +bgr2cmyk(fz_color_converter *cc, float *dv, float *sv) +{ + float c = 1 - sv[2]; + float m = 1 - sv[1]; + float y = 1 - sv[0]; + float k = fz_min(c, fz_min(m, y)); + dv[0] = c - k; + dv[1] = m - k; + dv[2] = y - k; + dv[3] = k; +} + +static void +cmyk2g(fz_color_converter *cc, float *dv, float *sv) +{ + float c = sv[0] * 0.3f; + float m = sv[1] * 0.59f; + float y = sv[2] * 0.11f; + dv[0] = 1 - fz_min(c + m + y + sv[3], 1); +} + +static void +cmyk2rgb(fz_color_converter *cc, float *dv, float *sv) +{ +#ifdef SLOWCMYK + cmyk_to_rgb(cc->ctx, NULL, sv, dv); +#else + dv[0] = 1 - fz_min(sv[0] + sv[3], 1); + dv[1] = 1 - fz_min(sv[1] + sv[3], 1); + dv[2] = 1 - fz_min(sv[2] + sv[3], 1); +#endif +} + +static void +cmyk2bgr(fz_color_converter *cc, float *dv, float *sv) +{ +#ifdef SLOWCMYK + float rgb[3]; + cmyk_to_rgb(cc->ctx, NULL, sv, rgb); + dv[0] = rgb[2]; + dv[1] = rgb[1]; + dv[2] = rgb[0]; +#else + dv[0] = 1 - fz_min(sv[2] + sv[3], 1); + dv[1] = 1 - fz_min(sv[1] + sv[3], 1); + dv[2] = 1 - fz_min(sv[0] + sv[3], 1); +#endif +} + +void fz_lookup_color_converter(fz_color_converter *cc, fz_context *ctx, fz_colorspace *ds, fz_colorspace *ss) +{ + cc->ctx = ctx; + cc->ds = ds; + cc->ss = ss; + if (ss == fz_default_gray) + { + if ((ds == fz_default_rgb) || (ds == fz_default_bgr)) + cc->convert = g2rgb; + else if (ds == fz_default_cmyk) + cc->convert = g2cmyk; + else + cc->convert = std_conv_color; + } + + else if (ss == fz_default_rgb) + { + if (ds == fz_default_gray) + cc->convert = rgb2g; + else if (ds == fz_default_bgr) + cc->convert = rgb2bgr; + else if (ds == fz_default_cmyk) + cc->convert = rgb2cmyk; + else + cc->convert = std_conv_color; + } + + else if (ss == fz_default_bgr) + { + if (ds == fz_default_gray) + cc->convert = bgr2g; + else if (ds == fz_default_rgb) + cc->convert = rgb2bgr; + else if (ds == fz_default_cmyk) + cc->convert = bgr2cmyk; + else + cc->convert = std_conv_color; + } + + else if (ss == fz_default_cmyk) + { + if (ds == fz_default_gray) + cc->convert = cmyk2g; + else if (ds == fz_default_rgb) + cc->convert = cmyk2rgb; + else if (ds == fz_default_bgr) + cc->convert = cmyk2bgr; + else + cc->convert = std_conv_color; + } + + else + cc->convert = std_conv_color; +} + +void +fz_convert_color(fz_context *ctx, fz_colorspace *ds, float *dv, fz_colorspace *ss, float *sv) +{ + fz_color_converter cc; + + fz_lookup_color_converter(&cc, ctx, ds, ss); + cc.convert(&cc, dv, sv); +} + +/* Indexed */ + +struct indexed +{ + fz_colorspace *base; + int high; + unsigned char *lookup; +}; + +static void +indexed_to_rgb(fz_context *ctx, fz_colorspace *cs, float *color, float *rgb) +{ + struct indexed *idx = cs->data; + float alt[FZ_MAX_COLORS]; + int i, k; + i = color[0] * 255; + i = fz_clampi(i, 0, idx->high); + for (k = 0; k < idx->base->n; k++) + alt[k] = idx->lookup[i * idx->base->n + k] / 255.0f; + idx->base->to_rgb(ctx, idx->base, alt, rgb); +} + +static void +free_indexed(fz_context *ctx, fz_colorspace *cs) +{ + struct indexed *idx = cs->data; + if (idx->base) + fz_drop_colorspace(ctx, idx->base); + fz_free(ctx, idx->lookup); + fz_free(ctx, idx); +} + +fz_colorspace * +fz_new_indexed_colorspace(fz_context *ctx, fz_colorspace *base, int high, unsigned char *lookup) +{ + fz_colorspace *cs; + struct indexed *idx; + + idx = fz_malloc_struct(ctx, struct indexed); + idx->lookup = lookup; + idx->base = base; + idx->high = high; + + fz_try(ctx) + { + cs = fz_new_colorspace(ctx, "Indexed", 1); + cs->to_rgb = indexed_to_rgb; + cs->free_data = free_indexed; + cs->data = idx; + cs->size += sizeof(*idx) + (base->n * (idx->high + 1)) + base->size; + } + fz_catch(ctx) + { + fz_free(ctx, idx); + fz_rethrow_message(ctx, "failed to create indexed colorspace"); + } + return cs; +} + +fz_pixmap * +fz_expand_indexed_pixmap(fz_context *ctx, fz_pixmap *src) +{ + struct indexed *idx; + fz_pixmap *dst; + unsigned char *s, *d; + int y, x, k, n, high; + unsigned char *lookup; + fz_irect bbox; + + assert(src->colorspace->to_rgb == indexed_to_rgb); + assert(src->n == 2); + + idx = src->colorspace->data; + high = idx->high; + lookup = idx->lookup; + n = idx->base->n; + + dst = fz_new_pixmap_with_bbox(ctx, idx->base, fz_pixmap_bbox(ctx, src, &bbox)); + s = src->samples; + d = dst->samples; + + for (y = 0; y < src->h; y++) + { + for (x = 0; x < src->w; x++) + { + int v = *s++; + int a = *s++; + v = fz_mini(v, high); + for (k = 0; k < n; k++) + *d++ = fz_mul255(lookup[v * n + k], a); + *d++ = a; + } + } + + dst->interpolate = src->interpolate; + + return dst; +} diff --git a/source/fitz/compressed-buffer.c b/source/fitz/compressed-buffer.c new file mode 100644 index 00000000..acdf2747 --- /dev/null +++ b/source/fitz/compressed-buffer.c @@ -0,0 +1,75 @@ +#include "mupdf/fitz.h" + +/* This code needs to be kept out of stm_buffer.c to avoid it being + * pulled into cmapdump.c */ + +void +fz_free_compressed_buffer(fz_context *ctx, fz_compressed_buffer *buf) +{ + if (!buf) + return; + + fz_drop_buffer(ctx, buf->buffer); + fz_free(ctx, buf); +} + +fz_stream * +fz_open_image_decomp_stream(fz_context *ctx, fz_compressed_buffer *buffer, int *l2factor) +{ + fz_stream *chain = fz_open_buffer(ctx, buffer->buffer); + fz_compression_params *params = &buffer->params; + + switch (params->type) + { + case FZ_IMAGE_FAX: + *l2factor = 0; + return fz_open_faxd(chain, + params->u.fax.k, + params->u.fax.end_of_line, + params->u.fax.encoded_byte_align, + params->u.fax.columns, + params->u.fax.rows, + params->u.fax.end_of_block, + params->u.fax.black_is_1); + case FZ_IMAGE_JPEG: + if (*l2factor > 3) + *l2factor = 3; + return fz_open_resized_dctd(chain, params->u.jpeg.color_transform, *l2factor); + case FZ_IMAGE_RLD: + *l2factor = 0; + return fz_open_rld(chain); + case FZ_IMAGE_FLATE: + *l2factor = 0; + chain = fz_open_flated(chain); + if (params->u.flate.predictor > 1) + chain = fz_open_predict(chain, params->u.flate.predictor, params->u.flate.columns, params->u.flate.colors, params->u.flate.bpc); + return chain; + case FZ_IMAGE_LZW: + *l2factor = 0; + chain = fz_open_lzwd(chain, params->u.lzw.early_change); + if (params->u.lzw.predictor > 1) + chain = fz_open_predict(chain, params->u.lzw.predictor, params->u.lzw.columns, params->u.lzw.colors, params->u.lzw.bpc); + return chain; + default: + *l2factor = 0; + break; + } + + return chain; +} + +fz_stream * +fz_open_compressed_buffer(fz_context *ctx, fz_compressed_buffer *buffer) +{ + int l2factor = 0; + + return fz_open_image_decomp_stream(ctx, buffer, &l2factor); +} + +unsigned int +fz_compressed_buffer_size(fz_compressed_buffer *buffer) +{ + if (!buffer || !buffer->buffer) + return 0; + return (unsigned int)buffer->buffer->cap; +} diff --git a/source/fitz/context.c b/source/fitz/context.c new file mode 100644 index 00000000..c65377a8 --- /dev/null +++ b/source/fitz/context.c @@ -0,0 +1,210 @@ +#include "mupdf/fitz.h" + +struct fz_id_context_s +{ + int refs; + int id; +}; + +static void +fz_drop_id_context(fz_context *ctx) +{ + int refs; + fz_id_context *id = ctx->id; + + if (id == NULL) + return; + fz_lock(ctx, FZ_LOCK_ALLOC); + refs = --id->refs; + fz_unlock(ctx, FZ_LOCK_ALLOC); + if (refs == 0) + fz_free(ctx, id); +} + +static void +fz_new_id_context(fz_context *ctx) +{ + ctx->id = fz_malloc_struct(ctx, fz_id_context); + ctx->id->refs = 1; + ctx->id->id = 0; +} + +static fz_id_context * +fz_keep_id_context(fz_context *ctx) +{ + fz_id_context *id = ctx->id; + + if (id == NULL) + return NULL; + fz_lock(ctx, FZ_LOCK_ALLOC); + ++id->refs; + fz_unlock(ctx, FZ_LOCK_ALLOC); + return id; +} + +void +fz_free_context(fz_context *ctx) +{ + if (!ctx) + return; + + /* Other finalisation calls go here (in reverse order) */ + fz_drop_glyph_cache_context(ctx); + fz_drop_store_context(ctx); + fz_free_aa_context(ctx); + fz_drop_colorspace_context(ctx); + fz_drop_font_context(ctx); + fz_drop_id_context(ctx); + + if (ctx->warn) + { + fz_flush_warnings(ctx); + fz_free(ctx, ctx->warn); + } + + if (ctx->error) + { + assert(ctx->error->top == -1); + fz_free(ctx, ctx->error); + } + + /* Free the context itself */ + ctx->alloc->free(ctx->alloc->user, ctx); +} + +/* Allocate new context structure, and initialise allocator, and sections + * that aren't shared between contexts. + */ +static fz_context * +new_context_phase1(fz_alloc_context *alloc, fz_locks_context *locks) +{ + fz_context *ctx; + + ctx = alloc->malloc(alloc->user, sizeof(fz_context)); + if (!ctx) + return NULL; + memset(ctx, 0, sizeof *ctx); + ctx->alloc = alloc; + ctx->locks = locks; + + ctx->glyph_cache = NULL; + + ctx->error = fz_malloc_no_throw(ctx, sizeof(fz_error_context)); + if (!ctx->error) + goto cleanup; + ctx->error->top = -1; + ctx->error->errcode = FZ_ERROR_NONE; + ctx->error->message[0] = 0; + + ctx->warn = fz_malloc_no_throw(ctx, sizeof(fz_warn_context)); + if (!ctx->warn) + goto cleanup; + ctx->warn->message[0] = 0; + ctx->warn->count = 0; + + /* New initialisation calls for context entries go here */ + fz_try(ctx) + { + fz_new_aa_context(ctx); + } + fz_catch(ctx) + { + goto cleanup; + } + + return ctx; + +cleanup: + fprintf(stderr, "cannot create context (phase 1)\n"); + fz_free_context(ctx); + return NULL; +} + +fz_context * +fz_new_context(fz_alloc_context *alloc, fz_locks_context *locks, unsigned int max_store) +{ + fz_context *ctx; + + if (!alloc) + alloc = &fz_alloc_default; + + if (!locks) + locks = &fz_locks_default; + + ctx = new_context_phase1(alloc, locks); + if (!ctx) + return NULL; + + /* Now initialise sections that are shared */ + fz_try(ctx) + { + fz_new_store_context(ctx, max_store); + fz_new_glyph_cache_context(ctx); + fz_new_colorspace_context(ctx); + fz_new_font_context(ctx); + fz_new_id_context(ctx); + } + fz_catch(ctx) + { + fprintf(stderr, "cannot create context (phase 2)\n"); + fz_free_context(ctx); + return NULL; + } + return ctx; +} + +fz_context * +fz_clone_context(fz_context *ctx) +{ + /* We cannot safely clone the context without having locking/ + * unlocking functions. */ + if (ctx == NULL || ctx->locks == &fz_locks_default) + return NULL; + return fz_clone_context_internal(ctx); +} + +fz_context * +fz_clone_context_internal(fz_context *ctx) +{ + fz_context *new_ctx; + + if (ctx == NULL || ctx->alloc == NULL) + return NULL; + + new_ctx = new_context_phase1(ctx->alloc, ctx->locks); + if (!new_ctx) + return NULL; + + /* Inherit AA defaults from old context. */ + fz_copy_aa_context(new_ctx, ctx); + + /* Keep thread lock checking happy by copying pointers first and locking under new context */ + new_ctx->store = ctx->store; + new_ctx->store = fz_keep_store_context(new_ctx); + new_ctx->glyph_cache = ctx->glyph_cache; + new_ctx->glyph_cache = fz_keep_glyph_cache(new_ctx); + new_ctx->colorspace = ctx->colorspace; + new_ctx->colorspace = fz_keep_colorspace_context(new_ctx); + new_ctx->font = ctx->font; + new_ctx->font = fz_keep_font_context(new_ctx); + new_ctx->id = ctx->id; + new_ctx->id = fz_keep_id_context(new_ctx); + + return new_ctx; +} + +int +fz_gen_id(fz_context *ctx) +{ + int id; + fz_lock(ctx, FZ_LOCK_ALLOC); + /* We'll never wrap around in normal use, but if we *do*, then avoid + * 0. */ + do + { + id = ++ctx->id->id; + } + while (id == 0); + fz_unlock(ctx, FZ_LOCK_ALLOC); + return id; +} diff --git a/source/fitz/crypt-aes.c b/source/fitz/crypt-aes.c new file mode 100644 index 00000000..6ce14903 --- /dev/null +++ b/source/fitz/crypt-aes.c @@ -0,0 +1,569 @@ +/* + * FIPS-197 compliant AES implementation + * + * Copyright (C) 2006-2007 Christophe Devine + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code _must_ retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form may or may not reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of XySSL nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * The AES block cipher was designed by Vincent Rijmen and Joan Daemen. + * + * http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf + * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + */ + +#include "mupdf/fitz.h" + +#define aes_context fz_aes + +/* AES block cipher implementation from XYSSL */ + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_ULONG_LE +#define GET_ULONG_LE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i)] ) \ + | ( (unsigned long) (b)[(i) + 1] << 8 ) \ + | ( (unsigned long) (b)[(i) + 2] << 16 ) \ + | ( (unsigned long) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_ULONG_LE +#define PUT_ULONG_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +/* + * Forward S-box & tables + */ +static unsigned char FSb[256]; +static unsigned long FT0[256]; +static unsigned long FT1[256]; +static unsigned long FT2[256]; +static unsigned long FT3[256]; + +/* + * Reverse S-box & tables + */ +static unsigned char RSb[256]; +static unsigned long RT0[256]; +static unsigned long RT1[256]; +static unsigned long RT2[256]; +static unsigned long RT3[256]; + +/* + * Round constants + */ +static unsigned long RCON[10]; + +/* + * Tables generation code + */ +#define ROTL8(x) ( ( x << 8 ) & 0xFFFFFFFF ) | ( x >> 24 ) +#define XTIME(x) ( ( x << 1 ) ^ ( ( x & 0x80 ) ? 0x1B : 0x00 ) ) +#define MUL(x,y) ( ( x && y ) ? pow[(log[x]+log[y]) % 255] : 0 ) + +static int aes_init_done = 0; + +static void aes_gen_tables( void ) +{ + int i, x, y, z; + int pow[256]; + int log[256]; + + /* + * compute pow and log tables over GF(2^8) + */ + for( i = 0, x = 1; i < 256; i++ ) + { + pow[i] = x; + log[x] = i; + x = ( x ^ XTIME( x ) ) & 0xFF; + } + + /* + * calculate the round constants + */ + for( i = 0, x = 1; i < 10; i++ ) + { + RCON[i] = (unsigned long) x; + x = XTIME( x ) & 0xFF; + } + + /* + * generate the forward and reverse S-boxes + */ + FSb[0x00] = 0x63; + RSb[0x63] = 0x00; + + for( i = 1; i < 256; i++ ) + { + x = pow[255 - log[i]]; + + y = x; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y ^ 0x63; + + FSb[i] = (unsigned char) x; + RSb[x] = (unsigned char) i; + } + + /* + * generate the forward and reverse tables + */ + for( i = 0; i < 256; i++ ) + { + x = FSb[i]; + y = XTIME( x ) & 0xFF; + z = ( y ^ x ) & 0xFF; + + FT0[i] = ( (unsigned long) y ) ^ + ( (unsigned long) x << 8 ) ^ + ( (unsigned long) x << 16 ) ^ + ( (unsigned long) z << 24 ); + + FT1[i] = ROTL8( FT0[i] ); + FT2[i] = ROTL8( FT1[i] ); + FT3[i] = ROTL8( FT2[i] ); + + x = RSb[i]; + + RT0[i] = ( (unsigned long) MUL( 0x0E, x ) ) ^ + ( (unsigned long) MUL( 0x09, x ) << 8 ) ^ + ( (unsigned long) MUL( 0x0D, x ) << 16 ) ^ + ( (unsigned long) MUL( 0x0B, x ) << 24 ); + + RT1[i] = ROTL8( RT0[i] ); + RT2[i] = ROTL8( RT1[i] ); + RT3[i] = ROTL8( RT2[i] ); + } +} + +/* + * AES key schedule (encryption) + */ +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, int keysize ) +{ + int i; + unsigned long *RK; + +#if !defined(XYSSL_AES_ROM_TABLES) + if( aes_init_done == 0 ) + { + aes_gen_tables(); + aes_init_done = 1; + } +#endif + + switch( keysize ) + { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default : return 1; + } + +#if defined(PADLOCK_ALIGN16) + ctx->rk = RK = PADLOCK_ALIGN16( ctx->buf ); +#else + ctx->rk = RK = ctx->buf; +#endif + + for( i = 0; i < (keysize >> 5); i++ ) + { + GET_ULONG_LE( RK[i], key, i << 2 ); + } + + switch( ctx->nr ) + { + case 10: + + for( i = 0; i < 10; i++, RK += 4 ) + { + RK[4] = RK[0] ^ RCON[i] ^ + ( FSb[ ( RK[3] >> 8 ) & 0xFF ] ) ^ + ( FSb[ ( RK[3] >> 16 ) & 0xFF ] << 8 ) ^ + ( FSb[ ( RK[3] >> 24 ) & 0xFF ] << 16 ) ^ + ( FSb[ ( RK[3] ) & 0xFF ] << 24 ); + + RK[5] = RK[1] ^ RK[4]; + RK[6] = RK[2] ^ RK[5]; + RK[7] = RK[3] ^ RK[6]; + } + break; + + case 12: + + for( i = 0; i < 8; i++, RK += 6 ) + { + RK[6] = RK[0] ^ RCON[i] ^ + ( FSb[ ( RK[5] >> 8 ) & 0xFF ] ) ^ + ( FSb[ ( RK[5] >> 16 ) & 0xFF ] << 8 ) ^ + ( FSb[ ( RK[5] >> 24 ) & 0xFF ] << 16 ) ^ + ( FSb[ ( RK[5] ) & 0xFF ] << 24 ); + + RK[7] = RK[1] ^ RK[6]; + RK[8] = RK[2] ^ RK[7]; + RK[9] = RK[3] ^ RK[8]; + RK[10] = RK[4] ^ RK[9]; + RK[11] = RK[5] ^ RK[10]; + } + break; + + case 14: + + for( i = 0; i < 7; i++, RK += 8 ) + { + RK[8] = RK[0] ^ RCON[i] ^ + ( FSb[ ( RK[7] >> 8 ) & 0xFF ] ) ^ + ( FSb[ ( RK[7] >> 16 ) & 0xFF ] << 8 ) ^ + ( FSb[ ( RK[7] >> 24 ) & 0xFF ] << 16 ) ^ + ( FSb[ ( RK[7] ) & 0xFF ] << 24 ); + + RK[9] = RK[1] ^ RK[8]; + RK[10] = RK[2] ^ RK[9]; + RK[11] = RK[3] ^ RK[10]; + + RK[12] = RK[4] ^ + ( FSb[ ( RK[11] ) & 0xFF ] ) ^ + ( FSb[ ( RK[11] >> 8 ) & 0xFF ] << 8 ) ^ + ( FSb[ ( RK[11] >> 16 ) & 0xFF ] << 16 ) ^ + ( FSb[ ( RK[11] >> 24 ) & 0xFF ] << 24 ); + + RK[13] = RK[5] ^ RK[12]; + RK[14] = RK[6] ^ RK[13]; + RK[15] = RK[7] ^ RK[14]; + } + break; + + default: + + break; + } + return 0; +} + +/* + * AES key schedule (decryption) + */ +int aes_setkey_dec(aes_context *ctx, const unsigned char *key, int keysize) +{ + int i, j; + aes_context cty; + unsigned long *RK; + unsigned long *SK; + + switch( keysize ) + { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default: return 1; + } + +#if defined(PADLOCK_ALIGN16) + ctx->rk = RK = PADLOCK_ALIGN16( ctx->buf ); +#else + ctx->rk = RK = ctx->buf; +#endif + + i = aes_setkey_enc( &cty, key, keysize ); + if (i) + return i; + SK = cty.rk + cty.nr * 4; + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + for( i = ctx->nr - 1, SK -= 8; i > 0; i--, SK -= 8 ) + { + for( j = 0; j < 4; j++, SK++ ) + { + *RK++ = RT0[ FSb[ ( *SK ) & 0xFF ] ] ^ + RT1[ FSb[ ( *SK >> 8 ) & 0xFF ] ] ^ + RT2[ FSb[ ( *SK >> 16 ) & 0xFF ] ] ^ + RT3[ FSb[ ( *SK >> 24 ) & 0xFF ] ]; + } + } + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + memset( &cty, 0, sizeof( aes_context ) ); + return 0; +} + +#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + X0 = *RK++ ^ FT0[ ( Y0 ) & 0xFF ] ^ \ + FT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y3 >> 24 ) & 0xFF ]; \ + \ + X1 = *RK++ ^ FT0[ ( Y1 ) & 0xFF ] ^ \ + FT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y0 >> 24 ) & 0xFF ]; \ + \ + X2 = *RK++ ^ FT0[ ( Y2 ) & 0xFF ] ^ \ + FT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y1 >> 24 ) & 0xFF ]; \ + \ + X3 = *RK++ ^ FT0[ ( Y3 ) & 0xFF ] ^ \ + FT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y2 >> 24 ) & 0xFF ]; \ +} + +#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + X0 = *RK++ ^ RT0[ ( Y0 ) & 0xFF ] ^ \ + RT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y1 >> 24 ) & 0xFF ]; \ + \ + X1 = *RK++ ^ RT0[ ( Y1 ) & 0xFF ] ^ \ + RT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y2 >> 24 ) & 0xFF ]; \ + \ + X2 = *RK++ ^ RT0[ ( Y2 ) & 0xFF ] ^ \ + RT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y3 >> 24 ) & 0xFF ]; \ + \ + X3 = *RK++ ^ RT0[ ( Y3 ) & 0xFF ] ^ \ + RT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y0 >> 24 ) & 0xFF ]; \ +} + +/* + * AES-ECB block encryption/decryption + */ +void aes_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ) +{ + int i; + unsigned long *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; + +#if defined(XYSSL_PADLOCK_C) && defined(XYSSL_HAVE_X86) + if( padlock_supports( PADLOCK_ACE ) ) + { + if( padlock_xcryptecb( ctx, mode, input, output ) == 0 ) + return; + } +#endif + + RK = ctx->rk; + + GET_ULONG_LE( X0, input, 0 ); X0 ^= *RK++; + GET_ULONG_LE( X1, input, 4 ); X1 ^= *RK++; + GET_ULONG_LE( X2, input, 8 ); X2 ^= *RK++; + GET_ULONG_LE( X3, input, 12 ); X3 ^= *RK++; + + if( mode == AES_DECRYPT ) + { + for( i = (ctx->nr >> 1) - 1; i > 0; i-- ) + { + AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); + } + + AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + + X0 = *RK++ ^ ( RSb[ ( Y0 ) & 0xFF ] ) ^ + ( RSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ + ( RSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ + ( RSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); + + X1 = *RK++ ^ ( RSb[ ( Y1 ) & 0xFF ] ) ^ + ( RSb[ ( Y0 >>8 ) & 0xFF ] << 8 ) ^ + ( RSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ + ( RSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); + + X2 = *RK++ ^ ( RSb[ ( Y2 ) & 0xFF ] ) ^ + ( RSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ + ( RSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ + ( RSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); + + X3 = *RK++ ^ ( RSb[ ( Y3 ) & 0xFF ] ) ^ + ( RSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ + ( RSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ + ( RSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); + } + else /* AES_ENCRYPT */ + { + for( i = (ctx->nr >> 1) - 1; i > 0; i-- ) + { + AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); + } + + AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + + X0 = *RK++ ^ ( FSb[ ( Y0 ) & 0xFF ] ) ^ + ( FSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ + ( FSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ + ( FSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); + + X1 = *RK++ ^ ( FSb[ ( Y1 ) & 0xFF ] ) ^ + ( FSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ + ( FSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ + ( FSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); + + X2 = *RK++ ^ ( FSb[ ( Y2 ) & 0xFF ] ) ^ + ( FSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ + ( FSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ + ( FSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); + + X3 = *RK++ ^ ( FSb[ ( Y3 ) & 0xFF ] ) ^ + ( FSb[ ( Y0 >> 8 ) & 0xFF ] << 8 ) ^ + ( FSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ + ( FSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); + } + + PUT_ULONG_LE( X0, output, 0 ); + PUT_ULONG_LE( X1, output, 4 ); + PUT_ULONG_LE( X2, output, 8 ); + PUT_ULONG_LE( X3, output, 12 ); +} + +/* + * AES-CBC buffer encryption/decryption + */ +void aes_crypt_cbc( aes_context *ctx, + int mode, + int length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int i; + unsigned char temp[16]; + +#if defined(XYSSL_PADLOCK_C) && defined(XYSSL_HAVE_X86) + if( padlock_supports( PADLOCK_ACE ) ) + { + if( padlock_xcryptcbc( ctx, mode, length, iv, input, output ) == 0 ) + return; + } +#endif + + if( mode == AES_DECRYPT ) + { + while( length > 0 ) + { + memcpy( temp, input, 16 ); + aes_crypt_ecb( ctx, mode, input, output ); + + for( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( output[i] ^ iv[i] ); + + memcpy( iv, temp, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } + else + { + while( length > 0 ) + { + for( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( input[i] ^ iv[i] ); + + aes_crypt_ecb( ctx, mode, output, output ); + memcpy( iv, output, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } +} + +/* + * AES-CFB buffer encryption/decryption + */ +void aes_crypt_cfb( aes_context *ctx, + int mode, + int length, + int *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int c, n = *iv_off; + + if( mode == AES_DECRYPT ) + { + while( length-- ) + { + if( n == 0 ) + aes_crypt_ecb( ctx, AES_ENCRYPT, iv, iv ); + + c = *input++; + *output++ = (unsigned char)( c ^ iv[n] ); + iv[n] = (unsigned char) c; + + n = (n + 1) & 0x0F; + } + } + else + { + while( length-- ) + { + if( n == 0 ) + aes_crypt_ecb( ctx, AES_ENCRYPT, iv, iv ); + + iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ ); + + n = (n + 1) & 0x0F; + } + } + + *iv_off = n; +} diff --git a/source/fitz/crypt-arc4.c b/source/fitz/crypt-arc4.c new file mode 100644 index 00000000..9c54fbae --- /dev/null +++ b/source/fitz/crypt-arc4.c @@ -0,0 +1,98 @@ +/* This code illustrates a sample implementation + * of the Arcfour algorithm + * Copyright (c) April 29, 1997 Kalle Kaukonen. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that this copyright + * notice and disclaimer are retained. + * + * THIS SOFTWARE IS PROVIDED BY KALLE KAUKONEN AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KALLE + * KAUKONEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "mupdf/fitz.h" + +void +fz_arc4_init(fz_arc4 *arc4, const unsigned char *key, unsigned keylen) +{ + unsigned int t, u; + unsigned int keyindex; + unsigned int stateindex; + unsigned char *state; + unsigned int counter; + + state = arc4->state; + + arc4->x = 0; + arc4->y = 0; + + for (counter = 0; counter < 256; counter++) + { + state[counter] = counter; + } + + keyindex = 0; + stateindex = 0; + + for (counter = 0; counter < 256; counter++) + { + t = state[counter]; + stateindex = (stateindex + key[keyindex] + t) & 0xff; + u = state[stateindex]; + + state[stateindex] = t; + state[counter] = u; + + if (++keyindex >= keylen) + { + keyindex = 0; + } + } +} + +static unsigned char +fz_arc4_next(fz_arc4 *arc4) +{ + unsigned int x; + unsigned int y; + unsigned int sx, sy; + unsigned char *state; + + state = arc4->state; + + x = (arc4->x + 1) & 0xff; + sx = state[x]; + y = (sx + arc4->y) & 0xff; + sy = state[y]; + + arc4->x = x; + arc4->y = y; + + state[y] = sx; + state[x] = sy; + + return state[(sx + sy) & 0xff]; +} + +void +fz_arc4_encrypt(fz_arc4 *arc4, unsigned char *dest, const unsigned char *src, unsigned len) +{ + unsigned int i; + for (i = 0; i < len; i++) + { + unsigned char x; + x = fz_arc4_next(arc4); + dest[i] = src[i] ^ x; + } +} diff --git a/source/fitz/crypt-md5.c b/source/fitz/crypt-md5.c new file mode 100644 index 00000000..7490c0bc --- /dev/null +++ b/source/fitz/crypt-md5.c @@ -0,0 +1,272 @@ +/* +MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + +Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. +All rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. +*/ + +#include "mupdf/fitz.h" + +/* Constants for MD5Transform routine */ +enum +{ + S11 = 7, S12 = 12, S13 = 17, S14 = 22, + S21 = 5, S22 = 9, S23 = 14, S24 = 20, + S31 = 4, S32 = 11, S33 = 16, S34 = 23, + S41 = 6, S42 = 10, S43 = 15, S44 = 21 +}; + +static void encode(unsigned char *, const unsigned int *, const unsigned); +static void decode(unsigned int *, const unsigned char *, const unsigned); +static void transform(unsigned int state[4], const unsigned char block[64]); + +static unsigned char padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE rotates x left n bits */ +#define ROTATE(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + * Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (unsigned int)(ac); \ + (a) = ROTATE ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (unsigned int)(ac); \ + (a) = ROTATE ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (unsigned int)(ac); \ + (a) = ROTATE ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (unsigned int)(ac); \ + (a) = ROTATE ((a), (s)); \ + (a) += (b); \ + } + +static void encode(unsigned char *output, const unsigned int *input, const unsigned len) +{ + unsigned i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +static void decode(unsigned int *output, const unsigned char *input, const unsigned len) +{ + unsigned i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + output[i] = ((unsigned int)input[j]) | + (((unsigned int)input[j+1]) << 8) | + (((unsigned int)input[j+2]) << 16) | + (((unsigned int)input[j+3]) << 24); + } +} + +static void transform(unsigned int state[4], const unsigned char block[64]) +{ + unsigned int a = state[0]; + unsigned int b = state[1]; + unsigned int c = state[2]; + unsigned int d = state[3]; + unsigned int x[16]; + + decode(x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x02441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x04881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information */ + memset(x, 0, sizeof (x)); +} + +/* MD5 initialization. Begins an MD5 operation, writing a new context. */ +void fz_md5_init(fz_md5 *context) +{ + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest operation, + * processing another message block, and updating the context. + */ +void fz_md5_update(fz_md5 *context, const unsigned char *input, unsigned inlen) +{ + unsigned i, index, partlen; + + /* Compute number of bytes mod 64 */ + index = (unsigned)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + context->count[0] += (unsigned int) inlen << 3; + if (context->count[0] < (unsigned int) inlen << 3) + context->count[1] ++; + context->count[1] += (unsigned int) inlen >> 29; + + partlen = 64 - index; + + /* Transform as many times as possible. */ + if (inlen >= partlen) + { + memcpy(context->buffer + index, input, partlen); + transform(context->state, context->buffer); + + for (i = partlen; i + 63 < inlen; i += 64) + transform(context->state, input + i); + + index = 0; + } + else + { + i = 0; + } + + /* Buffer remaining input */ + memcpy(context->buffer + index, input + i, inlen - i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + * the message digest and zeroizing the context. + */ +void fz_md5_final(fz_md5 *context, unsigned char digest[16]) +{ + unsigned char bits[8]; + unsigned index, padlen; + + /* Save number of bits */ + encode(bits, context->count, 8); + + /* Pad out to 56 mod 64 */ + index = (unsigned)((context->count[0] >> 3) & 0x3f); + padlen = index < 56 ? 56 - index : 120 - index; + fz_md5_update(context, padding, padlen); + + /* Append length (before padding) */ + fz_md5_update(context, bits, 8); + + /* Store state in digest */ + encode(digest, context->state, 16); + + /* Zeroize sensitive information */ + memset(context, 0, sizeof(fz_md5)); +} diff --git a/source/fitz/crypt-sha2.c b/source/fitz/crypt-sha2.c new file mode 100644 index 00000000..ffedfc95 --- /dev/null +++ b/source/fitz/crypt-sha2.c @@ -0,0 +1,393 @@ +/* +This code is based on the code found from 7-Zip, which has a modified +version of the SHA-256 found from Crypto++ <http://www.cryptopp.com/>. +The code was modified a little to fit into liblzma and fitz. + +This file has been put into the public domain. +You can do whatever you want with this file. + +SHA-384 and SHA-512 were also taken from Crypto++ and adapted for fitz. +*/ + +#include "mupdf/fitz.h" + +static inline int isbigendian(void) +{ + static const int one = 1; + return *(char*)&one == 0; +} + +static inline unsigned int bswap32(unsigned int num) +{ + if (!isbigendian()) + { + return ( (((num) << 24)) + | (((num) << 8) & 0x00FF0000) + | (((num) >> 8) & 0x0000FF00) + | (((num) >> 24)) ); + } + return num; +} + +static inline uint64_t bswap64(uint64_t num) +{ + if (!isbigendian()) + { + return ( (((num) << 56)) + | (((num) << 40) & 0x00FF000000000000ULL) + | (((num) << 24) & 0x0000FF0000000000ULL) + | (((num) << 8) & 0x000000FF00000000ULL) + | (((num) >> 8) & 0x00000000FF000000ULL) + | (((num) >> 24) & 0x0000000000FF0000ULL) + | (((num) >> 40) & 0x000000000000FF00ULL) + | (((num) >> 56)) ); + } + return num; +} + +/* At least on x86, GCC is able to optimize this to a rotate instruction. */ +#define rotr(num, amount) ((num) >> (amount) | (num) << (8 * sizeof(num) - (amount))) + +#define blk0(i) (W[i] = data[i]) +#define blk2(i) (W[i & 15] += s1(W[(i - 2) & 15]) + W[(i - 7) & 15] \ + + s0(W[(i - 15) & 15])) + +#define Ch(x, y, z) (z ^ (x & (y ^ z))) +#define Maj(x, y, z) ((x & y) | (z & (x | y))) + +#define a(i) T[(0 - i) & 7] +#define b(i) T[(1 - i) & 7] +#define c(i) T[(2 - i) & 7] +#define d(i) T[(3 - i) & 7] +#define e(i) T[(4 - i) & 7] +#define f(i) T[(5 - i) & 7] +#define g(i) T[(6 - i) & 7] +#define h(i) T[(7 - i) & 7] + +#define R(i) \ + h(i) += S1(e(i)) + Ch(e(i), f(i), g(i)) + K[i + j] \ + + (j ? blk2(i) : blk0(i)); \ + d(i) += h(i); \ + h(i) += S0(a(i)) + Maj(a(i), b(i), c(i)) + +/* For SHA256 */ + +#define S0(x) (rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22)) +#define S1(x) (rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25)) +#define s0(x) (rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3)) +#define s1(x) (rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10)) + +static const unsigned int SHA256_K[64] = { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2, +}; + +static void +transform256(unsigned int state[8], const unsigned int data_xe[16]) +{ + const unsigned int *K = SHA256_K; + unsigned int data[16]; + unsigned int W[16]; + unsigned int T[8]; + unsigned int j; + + /* ensure big-endian integers */ + for (j = 0; j < 16; j++) + data[j] = bswap32(data_xe[j]); + + /* Copy state[] to working vars. */ + memcpy(T, state, sizeof(T)); + + /* 64 operations, partially loop unrolled */ + for (j = 0; j < 64; j += 16) { + R( 0); R( 1); R( 2); R( 3); + R( 4); R( 5); R( 6); R( 7); + R( 8); R( 9); R(10); R(11); + R(12); R(13); R(14); R(15); + } + + /* Add the working vars back into state[]. */ + state[0] += a(0); + state[1] += b(0); + state[2] += c(0); + state[3] += d(0); + state[4] += e(0); + state[5] += f(0); + state[6] += g(0); + state[7] += h(0); +} + +#undef S0 +#undef S1 +#undef s0 +#undef s1 + +void fz_sha256_init(fz_sha256 *context) +{ + context->count[0] = context->count[1] = 0; + + context->state[0] = 0x6A09E667; + context->state[1] = 0xBB67AE85; + context->state[2] = 0x3C6EF372; + context->state[3] = 0xA54FF53A; + context->state[4] = 0x510E527F; + context->state[5] = 0x9B05688C; + context->state[6] = 0x1F83D9AB; + context->state[7] = 0x5BE0CD19; +} + +void fz_sha256_update(fz_sha256 *context, const unsigned char *input, unsigned int inlen) +{ + /* Copy the input data into a properly aligned temporary buffer. + * This way we can be called with arbitrarily sized buffers + * (no need to be multiple of 64 bytes), and the code works also + * on architectures that don't allow unaligned memory access. */ + while (inlen > 0) + { + const unsigned int copy_start = context->count[0] & 0x3F; + unsigned int copy_size = 64 - copy_start; + if (copy_size > inlen) + copy_size = inlen; + + memcpy(context->buffer.u8 + copy_start, input, copy_size); + + input += copy_size; + inlen -= copy_size; + context->count[0] += copy_size; + /* carry overflow from low to high */ + if (context->count[0] < copy_size) + context->count[1]++; + + if ((context->count[0] & 0x3F) == 0) + transform256(context->state, context->buffer.u32); + } +} + +void fz_sha256_final(fz_sha256 *context, unsigned char digest[32]) +{ + /* Add padding as described in RFC 3174 (it describes SHA-1 but + * the same padding style is used for SHA-256 too). */ + unsigned int j = context->count[0] & 0x3F; + context->buffer.u8[j++] = 0x80; + + while (j != 56) + { + if (j == 64) + { + transform256(context->state, context->buffer.u32); + j = 0; + } + context->buffer.u8[j++] = 0x00; + } + + /* Convert the message size from bytes to bits. */ + context->count[1] = (context->count[1] << 3) + (context->count[0] >> 29); + context->count[0] = context->count[0] << 3; + + context->buffer.u32[14] = bswap32(context->count[1]); + context->buffer.u32[15] = bswap32(context->count[0]); + transform256(context->state, context->buffer.u32); + + for (j = 0; j < 8; j++) + ((unsigned int *)digest)[j] = bswap32(context->state[j]); + memset(context, 0, sizeof(fz_sha256)); +} + +/* For SHA512 */ + +#define S0(x) (rotr(x, 28) ^ rotr(x, 34) ^ rotr(x, 39)) +#define S1(x) (rotr(x, 14) ^ rotr(x, 18) ^ rotr(x, 41)) +#define s0(x) (rotr(x, 1) ^ rotr(x, 8) ^ (x >> 7)) +#define s1(x) (rotr(x, 19) ^ rotr(x, 61) ^ (x >> 6)) + +static const uint64_t SHA512_K[80] = { + 0x428A2F98D728AE22ULL, 0x7137449123EF65CDULL, + 0xB5C0FBCFEC4D3B2FULL, 0xE9B5DBA58189DBBCULL, + 0x3956C25BF348B538ULL, 0x59F111F1B605D019ULL, + 0x923F82A4AF194F9BULL, 0xAB1C5ED5DA6D8118ULL, + 0xD807AA98A3030242ULL, 0x12835B0145706FBEULL, + 0x243185BE4EE4B28CULL, 0x550C7DC3D5FFB4E2ULL, + 0x72BE5D74F27B896FULL, 0x80DEB1FE3B1696B1ULL, + 0x9BDC06A725C71235ULL, 0xC19BF174CF692694ULL, + 0xE49B69C19EF14AD2ULL, 0xEFBE4786384F25E3ULL, + 0x0FC19DC68B8CD5B5ULL, 0x240CA1CC77AC9C65ULL, + 0x2DE92C6F592B0275ULL, 0x4A7484AA6EA6E483ULL, + 0x5CB0A9DCBD41FBD4ULL, 0x76F988DA831153B5ULL, + 0x983E5152EE66DFABULL, 0xA831C66D2DB43210ULL, + 0xB00327C898FB213FULL, 0xBF597FC7BEEF0EE4ULL, + 0xC6E00BF33DA88FC2ULL, 0xD5A79147930AA725ULL, + 0x06CA6351E003826FULL, 0x142929670A0E6E70ULL, + 0x27B70A8546D22FFCULL, 0x2E1B21385C26C926ULL, + 0x4D2C6DFC5AC42AEDULL, 0x53380D139D95B3DFULL, + 0x650A73548BAF63DEULL, 0x766A0ABB3C77B2A8ULL, + 0x81C2C92E47EDAEE6ULL, 0x92722C851482353BULL, + 0xA2BFE8A14CF10364ULL, 0xA81A664BBC423001ULL, + 0xC24B8B70D0F89791ULL, 0xC76C51A30654BE30ULL, + 0xD192E819D6EF5218ULL, 0xD69906245565A910ULL, + 0xF40E35855771202AULL, 0x106AA07032BBD1B8ULL, + 0x19A4C116B8D2D0C8ULL, 0x1E376C085141AB53ULL, + 0x2748774CDF8EEB99ULL, 0x34B0BCB5E19B48A8ULL, + 0x391C0CB3C5C95A63ULL, 0x4ED8AA4AE3418ACBULL, + 0x5B9CCA4F7763E373ULL, 0x682E6FF3D6B2B8A3ULL, + 0x748F82EE5DEFB2FCULL, 0x78A5636F43172F60ULL, + 0x84C87814A1F0AB72ULL, 0x8CC702081A6439ECULL, + 0x90BEFFFA23631E28ULL, 0xA4506CEBDE82BDE9ULL, + 0xBEF9A3F7B2C67915ULL, 0xC67178F2E372532BULL, + 0xCA273ECEEA26619CULL, 0xD186B8C721C0C207ULL, + 0xEADA7DD6CDE0EB1EULL, 0xF57D4F7FEE6ED178ULL, + 0x06F067AA72176FBAULL, 0x0A637DC5A2C898A6ULL, + 0x113F9804BEF90DAEULL, 0x1B710B35131C471BULL, + 0x28DB77F523047D84ULL, 0x32CAAB7B40C72493ULL, + 0x3C9EBE0A15C9BEBCULL, 0x431D67C49C100D4CULL, + 0x4CC5D4BECB3E42B6ULL, 0x597F299CFC657E2AULL, + 0x5FCB6FAB3AD6FAECULL, 0x6C44198C4A475817ULL, +}; + +static void +transform512(uint64_t state[8], const uint64_t data_xe[16]) +{ + const uint64_t *K = SHA512_K; + uint64_t data[16]; + uint64_t W[16]; + uint64_t T[8]; + unsigned int j; + + /* ensure big-endian integers */ + for (j = 0; j < 16; j++) + data[j] = bswap64(data_xe[j]); + + /* Copy state[] to working vars. */ + memcpy(T, state, sizeof(T)); + + /* 80 operations, partially loop unrolled */ + for (j = 0; j < 80; j+= 16) { + R( 0); R( 1); R( 2); R( 3); + R( 4); R( 5); R( 6); R( 7); + R( 8); R( 9); R(10); R(11); + R(12); R(13); R(14); R(15); + } + + /* Add the working vars back into state[]. */ + state[0] += a(0); + state[1] += b(0); + state[2] += c(0); + state[3] += d(0); + state[4] += e(0); + state[5] += f(0); + state[6] += g(0); + state[7] += h(0); +} + +#undef S0 +#undef S1 +#undef s0 +#undef s1 + +void fz_sha512_init(fz_sha512 *context) +{ + context->count[0] = context->count[1] = 0; + + context->state[0] = 0x6A09E667F3BCC908ull; + context->state[1] = 0xBB67AE8584CAA73Bull; + context->state[2] = 0x3C6EF372FE94F82Bull; + context->state[3] = 0xA54FF53A5F1D36F1ull; + context->state[4] = 0x510E527FADE682D1ull; + context->state[5] = 0x9B05688C2B3E6C1Full; + context->state[6] = 0x1F83D9ABFB41BD6Bull; + context->state[7] = 0x5BE0CD19137E2179ull; +} + +void fz_sha512_update(fz_sha512 *context, const unsigned char *input, unsigned int inlen) +{ + /* Copy the input data into a properly aligned temporary buffer. + * This way we can be called with arbitrarily sized buffers + * (no need to be multiple of 128 bytes), and the code works also + * on architectures that don't allow unaligned memory access. */ + while (inlen > 0) + { + const unsigned int copy_start = context->count[0] & 0x7F; + unsigned int copy_size = 128 - copy_start; + if (copy_size > inlen) + copy_size = inlen; + + memcpy(context->buffer.u8 + copy_start, input, copy_size); + + input += copy_size; + inlen -= copy_size; + context->count[0] += copy_size; + /* carry overflow from low to high */ + if (context->count[0] < copy_size) + context->count[1]++; + + if ((context->count[0] & 0x7F) == 0) + transform512(context->state, context->buffer.u64); + } +} + +void fz_sha512_final(fz_sha512 *context, unsigned char digest[64]) +{ + /* Add padding as described in RFC 3174 (it describes SHA-1 but + * the same padding style is used for SHA-512 too). */ + unsigned int j = context->count[0] & 0x7F; + context->buffer.u8[j++] = 0x80; + + while (j != 112) + { + if (j == 128) + { + transform512(context->state, context->buffer.u64); + j = 0; + } + context->buffer.u8[j++] = 0x00; + } + + /* Convert the message size from bytes to bits. */ + context->count[1] = (context->count[1] << 3) + (context->count[0] >> 29); + context->count[0] = context->count[0] << 3; + + context->buffer.u64[14] = bswap64(context->count[1]); + context->buffer.u64[15] = bswap64(context->count[0]); + transform512(context->state, context->buffer.u64); + + for (j = 0; j < 8; j++) + ((uint64_t *)digest)[j] = bswap64(context->state[j]); + memset(context, 0, sizeof(fz_sha512)); +} + +void fz_sha384_init(fz_sha384 *context) +{ + context->count[0] = context->count[1] = 0; + + context->state[0] = 0xCBBB9D5DC1059ED8ull; + context->state[1] = 0x629A292A367CD507ull; + context->state[2] = 0x9159015A3070DD17ull; + context->state[3] = 0x152FECD8F70E5939ull; + context->state[4] = 0x67332667FFC00B31ull; + context->state[5] = 0x8EB44A8768581511ull; + context->state[6] = 0xDB0C2E0D64F98FA7ull; + context->state[7] = 0x47B5481DBEFA4FA4ull; +} + +void fz_sha384_update(fz_sha384 *context, const unsigned char *input, unsigned int inlen) +{ + fz_sha512_update(context, input, inlen); +} + +void fz_sha384_final(fz_sha384 *context, unsigned char digest[64]) +{ + fz_sha512_final(context, digest); +} diff --git a/source/fitz/device.c b/source/fitz/device.c new file mode 100644 index 00000000..175b00db --- /dev/null +++ b/source/fitz/device.c @@ -0,0 +1,388 @@ +#include "mupdf/fitz.h" + +fz_device * +fz_new_device(fz_context *ctx, void *user) +{ + fz_device *dev = fz_malloc_struct(ctx, fz_device); + dev->hints = 0; + dev->flags = 0; + dev->user = user; + dev->ctx = ctx; + dev->error_depth = 0; + return dev; +} + +void +fz_free_device(fz_device *dev) +{ + if (dev == NULL) + return; + if (dev->free_user) + dev->free_user(dev); + fz_free(dev->ctx, dev); +} + +void +fz_enable_device_hints(fz_device *dev, int hints) +{ + dev->hints |= hints; +} + +void +fz_disable_device_hints(fz_device *dev, int hints) +{ + dev->hints &= ~hints; +} + +void +fz_begin_page(fz_device *dev, const fz_rect *rect, const fz_matrix *ctm) +{ + if (dev->begin_page) + dev->begin_page(dev, rect, ctm); +} + +void +fz_end_page(fz_device *dev) +{ + if (dev->end_page) + dev->end_page(dev); +} + +void +fz_fill_path(fz_device *dev, fz_path *path, int even_odd, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + if (dev->error_depth) + return; + if (dev->fill_path) + dev->fill_path(dev, path, even_odd, ctm, colorspace, color, alpha); +} + +void +fz_stroke_path(fz_device *dev, fz_path *path, fz_stroke_state *stroke, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + if (dev->error_depth) + return; + if (dev->stroke_path) + dev->stroke_path(dev, path, stroke, ctm, colorspace, color, alpha); +} + +void +fz_clip_path(fz_device *dev, fz_path *path, const fz_rect *rect, int even_odd, const fz_matrix *ctm) +{ + fz_context *ctx = dev->ctx; + + if (dev->error_depth) + { + dev->error_depth++; + return; + } + + fz_try(ctx) + { + if (dev->clip_path) + dev->clip_path(dev, path, rect, even_odd, ctm); + } + fz_catch(ctx) + { + dev->error_depth = 1; + strcpy(dev->errmess, fz_caught_message(ctx)); + /* Error swallowed */ + } +} + +void +fz_clip_stroke_path(fz_device *dev, fz_path *path, const fz_rect *rect, fz_stroke_state *stroke, const fz_matrix *ctm) +{ + fz_context *ctx = dev->ctx; + + if (dev->error_depth) + { + dev->error_depth++; + return; + } + + fz_try(ctx) + { + if (dev->clip_stroke_path) + dev->clip_stroke_path(dev, path, rect, stroke, ctm); + } + fz_catch(ctx) + { + dev->error_depth = 1; + strcpy(dev->errmess, fz_caught_message(ctx)); + /* Error swallowed */ + } +} + +void +fz_fill_text(fz_device *dev, fz_text *text, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + if (dev->error_depth) + return; + if (dev->fill_text) + dev->fill_text(dev, text, ctm, colorspace, color, alpha); +} + +void +fz_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + if (dev->error_depth) + return; + if (dev->stroke_text) + dev->stroke_text(dev, text, stroke, ctm, colorspace, color, alpha); +} + +void +fz_clip_text(fz_device *dev, fz_text *text, const fz_matrix *ctm, int accumulate) +{ + fz_context *ctx = dev->ctx; + + if (dev->error_depth) + { + if (accumulate == 0 || accumulate == 1) + dev->error_depth++; + return; + } + + fz_try(ctx) + { + if (dev->clip_text) + dev->clip_text(dev, text, ctm, accumulate); + } + fz_catch(ctx) + { + if (accumulate == 2) + fz_rethrow(ctx); + dev->error_depth = 1; + strcpy(dev->errmess, fz_caught_message(ctx)); + /* Error swallowed */ + } +} + +void +fz_clip_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm) +{ + fz_context *ctx = dev->ctx; + + if (dev->error_depth) + { + dev->error_depth++; + return; + } + + fz_try(ctx) + { + if (dev->clip_stroke_text) + dev->clip_stroke_text(dev, text, stroke, ctm); + } + fz_catch(ctx) + { + dev->error_depth = 1; + strcpy(dev->errmess, fz_caught_message(ctx)); + /* Error swallowed */ + } +} + +void +fz_ignore_text(fz_device *dev, fz_text *text, const fz_matrix *ctm) +{ + if (dev->error_depth) + return; + if (dev->ignore_text) + dev->ignore_text(dev, text, ctm); +} + +void +fz_pop_clip(fz_device *dev) +{ + if (dev->error_depth) + { + dev->error_depth--; + if (dev->error_depth == 0) + fz_throw(dev->ctx, FZ_ERROR_GENERIC, "%s", dev->errmess); + return; + } + if (dev->pop_clip) + dev->pop_clip(dev); +} + +void +fz_fill_shade(fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) +{ + if (dev->error_depth) + return; + if (dev->fill_shade) + dev->fill_shade(dev, shade, ctm, alpha); +} + +void +fz_fill_image(fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) +{ + if (dev->error_depth) + return; + if (dev->fill_image) + dev->fill_image(dev, image, ctm, alpha); +} + +void +fz_fill_image_mask(fz_device *dev, fz_image *image, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + if (dev->error_depth) + return; + if (dev->fill_image_mask) + dev->fill_image_mask(dev, image, ctm, colorspace, color, alpha); +} + +void +fz_clip_image_mask(fz_device *dev, fz_image *image, const fz_rect *rect, const fz_matrix *ctm) +{ + fz_context *ctx = dev->ctx; + + if (dev->error_depth) + { + dev->error_depth++; + return; + } + + fz_try(ctx) + { + if (dev->clip_image_mask) + dev->clip_image_mask(dev, image, rect, ctm); + } + fz_catch(ctx) + { + dev->error_depth = 1; + strcpy(dev->errmess, fz_caught_message(ctx)); + /* Error swallowed */ + } +} + +void +fz_begin_mask(fz_device *dev, const fz_rect *area, int luminosity, fz_colorspace *colorspace, float *bc) +{ + fz_context *ctx = dev->ctx; + + if (dev->error_depth) + { + dev->error_depth++; + return; + } + + fz_try(ctx) + { + if (dev->begin_mask) + dev->begin_mask(dev, area, luminosity, colorspace, bc); + } + fz_catch(ctx) + { + dev->error_depth = 1; + strcpy(dev->errmess, fz_caught_message(ctx)); + /* Error swallowed */ + } +} + +void +fz_end_mask(fz_device *dev) +{ + if (dev->error_depth) + { + /* Converts from mask to clip, so no change in stack depth */ + return; + } + if (dev->end_mask) + dev->end_mask(dev); +} + +void +fz_begin_group(fz_device *dev, const fz_rect *area, int isolated, int knockout, int blendmode, float alpha) +{ + fz_context *ctx = dev->ctx; + + if (dev->error_depth) + { + dev->error_depth++; + return; + } + + fz_try(ctx) + { + if (dev->begin_group) + dev->begin_group(dev, area, isolated, knockout, blendmode, alpha); + } + fz_catch(ctx) + { + dev->error_depth = 1; + strcpy(dev->errmess, fz_caught_message(ctx)); + /* Error swallowed */ + } +} + +void +fz_end_group(fz_device *dev) +{ + if (dev->error_depth) + { + dev->error_depth--; + if (dev->error_depth == 0) + fz_throw(dev->ctx, FZ_ERROR_GENERIC, "%s", dev->errmess); + return; + } + if (dev->end_group) + dev->end_group(dev); +} + +void +fz_begin_tile(fz_device *dev, const fz_rect *area, const fz_rect *view, float xstep, float ystep, const fz_matrix *ctm) +{ + (void)fz_begin_tile_id(dev, area, view, xstep, ystep, ctm, 0); +} + +int +fz_begin_tile_id(fz_device *dev, const fz_rect *area, const fz_rect *view, float xstep, float ystep, const fz_matrix *ctm, int id) +{ + fz_context *ctx = dev->ctx; + int ret = 0; + + if (dev->error_depth) + { + dev->error_depth++; + return 0; + } + + if (xstep < 0) + xstep = -xstep; + if (ystep < 0) + ystep = -ystep; + + fz_try(ctx) + { + if (dev->begin_tile) + ret = dev->begin_tile(dev, area, view, xstep, ystep, ctm, id); + } + fz_catch(ctx) + { + dev->error_depth = 1; + strcpy(dev->errmess, fz_caught_message(ctx)); + /* Error swallowed */ + } + return ret; +} + +void +fz_end_tile(fz_device *dev) +{ + if (dev->error_depth) + { + dev->error_depth--; + if (dev->error_depth == 0) + fz_throw(dev->ctx, FZ_ERROR_GENERIC, "%s", dev->errmess); + return; + } + if (dev->end_tile) + dev->end_tile(dev); +} diff --git a/source/fitz/document.c b/source/fitz/document.c new file mode 100644 index 00000000..8adbf816 --- /dev/null +++ b/source/fitz/document.c @@ -0,0 +1,274 @@ +#include "mupdf/fitz.h" + +/* Yuck! Promiscuous we are. */ +extern struct pdf_document *pdf_open_document(fz_context *ctx, const char *filename); +extern struct xps_document *xps_open_document(fz_context *ctx, const char *filename); +extern struct cbz_document *cbz_open_document(fz_context *ctx, const char *filename); +extern struct image_document *image_open_document(fz_context *ctx, const char *filename); + +extern struct pdf_document *pdf_open_document_with_stream(fz_context *ctx, fz_stream *file); +extern struct xps_document *xps_open_document_with_stream(fz_context *ctx, fz_stream *file); +extern struct cbz_document *cbz_open_document_with_stream(fz_context *ctx, fz_stream *file); +extern struct image_document *image_open_document_with_stream(fz_context *ctx, fz_stream *file); + +extern int pdf_js_supported(void); + +static inline int fz_tolower(int c) +{ + if (c >= 'A' && c <= 'Z') + return c + 32; + return c; +} + +static inline int fz_strcasecmp(const char *a, const char *b) +{ + while (fz_tolower(*a) == fz_tolower(*b)) + { + if (*a++ == 0) + return 0; + b++; + } + return fz_tolower(*a) - fz_tolower(*b); +} + +fz_document * +fz_open_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stream) +{ + char *ext = strrchr(magic, '.'); + + if (ext) + { + if (!fz_strcasecmp(ext, ".xps") || !fz_strcasecmp(ext, ".rels") || !fz_strcasecmp(ext, ".oxps")) + return (fz_document*) xps_open_document_with_stream(ctx, stream); + if (!fz_strcasecmp(ext, ".cbz") || !fz_strcasecmp(ext, ".zip")) + return (fz_document*) cbz_open_document_with_stream(ctx, stream); + if (!fz_strcasecmp(ext, ".pdf")) + return (fz_document*) pdf_open_document_with_stream(ctx, stream); + if (!fz_strcasecmp(ext, ".png") || !fz_strcasecmp(ext, ".jpg") || + !fz_strcasecmp(ext, ".jpeg") || !fz_strcasecmp(ext, ".jfif") || + !fz_strcasecmp(ext, ".jfif-tbnl") || !fz_strcasecmp(ext, ".jpe") || + !fz_strcasecmp(ext, ".tif") || !fz_strcasecmp(ext, ".tiff")) + return (fz_document*) image_open_document_with_stream(ctx, stream); + } + + if (!strcmp(magic, "cbz") || !strcmp(magic, "application/x-cbz")) + return (fz_document*) cbz_open_document_with_stream(ctx, stream); + if (!strcmp(magic, "xps") || !strcmp(magic, "oxps") || !strcmp(magic, "application/vnd.ms-xpsdocument")) + return (fz_document*) xps_open_document_with_stream(ctx, stream); + if (!strcmp(magic, "pdf") || !strcmp(magic, "application/pdf")) + return (fz_document*) pdf_open_document_with_stream(ctx, stream); + if (!strcmp(magic, "png") || !strcmp(magic, "image/png") || + !strcmp(magic, "jpg") || !strcmp(magic, "image/jpeg") || + !strcmp(magic, "jpeg") || !strcmp(magic, "image/pjpeg") || + !strcmp(magic, "jpe") || !strcmp(magic, "jfif") || + !strcmp(magic, "tif") || !strcmp(magic, "image/tiff") || + !strcmp(magic, "tiff") || !strcmp(magic, "image/x-tiff")) + return (fz_document*) image_open_document_with_stream(ctx, stream); + + /* last guess: pdf */ + return (fz_document*) pdf_open_document_with_stream(ctx, stream); +} + +fz_document * +fz_open_document(fz_context *ctx, const char *filename) +{ + char *ext = strrchr(filename, '.'); + + if (ext) + { + if (!fz_strcasecmp(ext, ".xps") || !fz_strcasecmp(ext, ".rels") || !fz_strcasecmp(ext, ".oxps")) + return (fz_document*) xps_open_document(ctx, filename); + if (!fz_strcasecmp(ext, ".cbz") || !fz_strcasecmp(ext, ".zip")) + return (fz_document*) cbz_open_document(ctx, filename); + if (!fz_strcasecmp(ext, ".pdf")) + return (fz_document*) pdf_open_document(ctx, filename); + if (!fz_strcasecmp(ext, ".png") || !fz_strcasecmp(ext, ".jpg") || + !fz_strcasecmp(ext, ".jpeg") || !fz_strcasecmp(ext, ".jpe") || + !fz_strcasecmp(ext, ".jfif") || !fz_strcasecmp(ext, ".jfif-tbnl") || + !fz_strcasecmp(ext, ".tif") || !fz_strcasecmp(ext, ".tiff")) + return (fz_document*) image_open_document(ctx, filename); + } + + /* last guess: pdf */ + return (fz_document*) pdf_open_document(ctx, filename); +} + +void +fz_close_document(fz_document *doc) +{ + if (doc && doc->close) + doc->close(doc); +} + +int +fz_needs_password(fz_document *doc) +{ + if (doc && doc->needs_password) + return doc->needs_password(doc); + return 0; +} + +int +fz_authenticate_password(fz_document *doc, char *password) +{ + if (doc && doc->authenticate_password) + return doc->authenticate_password(doc, password); + return 1; +} + +fz_outline * +fz_load_outline(fz_document *doc) +{ + if (doc && doc->load_outline) + return doc->load_outline(doc); + return NULL; +} + +int +fz_count_pages(fz_document *doc) +{ + if (doc && doc->count_pages) + return doc->count_pages(doc); + return 0; +} + +fz_page * +fz_load_page(fz_document *doc, int number) +{ + if (doc && doc->load_page) + return doc->load_page(doc, number); + return NULL; +} + +fz_link * +fz_load_links(fz_document *doc, fz_page *page) +{ + if (doc && doc->load_links && page) + return doc->load_links(doc, page); + return NULL; +} + +fz_rect * +fz_bound_page(fz_document *doc, fz_page *page, fz_rect *r) +{ + if (doc && doc->bound_page && page && r) + return doc->bound_page(doc, page, r); + if (r) + *r = fz_empty_rect; + return r; +} + +fz_annot * +fz_first_annot(fz_document *doc, fz_page *page) +{ + if (doc && doc->first_annot && page) + return doc->first_annot(doc, page); + return NULL; +} + +fz_annot * +fz_next_annot(fz_document *doc, fz_annot *annot) +{ + if (doc && doc->next_annot && annot) + return doc->next_annot(doc, annot); + return NULL; +} + +fz_rect * +fz_bound_annot(fz_document *doc, fz_annot *annot, fz_rect *rect) +{ + if (doc && doc->bound_annot && annot && rect) + return doc->bound_annot(doc, annot, rect); + if (rect) + *rect = fz_empty_rect; + return rect; +} + +void +fz_run_page_contents(fz_document *doc, fz_page *page, fz_device *dev, const fz_matrix *transform, fz_cookie *cookie) +{ + if (doc && doc->run_page_contents && page) + doc->run_page_contents(doc, page, dev, transform, cookie); +} + +void +fz_run_annot(fz_document *doc, fz_page *page, fz_annot *annot, fz_device *dev, const fz_matrix *transform, fz_cookie *cookie) +{ + if (doc && doc->run_annot && page && annot) + doc->run_annot(doc, page, annot, dev, transform, cookie); +} + +void +fz_run_page(fz_document *doc, fz_page *page, fz_device *dev, const fz_matrix *transform, fz_cookie *cookie) +{ + fz_annot *annot; + fz_rect mediabox; + + fz_bound_page(doc, page, &mediabox); + fz_begin_page(dev, &mediabox, transform); + + fz_run_page_contents(doc, page, dev, transform, cookie); + + if (cookie && cookie->progress_max != -1) + { + int count = 1; + for (annot = fz_first_annot(doc, page); annot; annot = fz_next_annot(doc, annot)) + count++; + cookie->progress_max += count; + } + + for (annot = fz_first_annot(doc, page); annot; annot = fz_next_annot(doc, annot)) + { + /* Check the cookie for aborting */ + if (cookie) + { + if (cookie->abort) + break; + cookie->progress++; + } + + fz_run_annot(doc, page, annot, dev, transform, cookie); + } + + fz_end_page(dev); +} + +void +fz_free_page(fz_document *doc, fz_page *page) +{ + if (doc && doc->free_page && page) + doc->free_page(doc, page); +} + +int +fz_meta(fz_document *doc, int key, void *ptr, int size) +{ + if (doc && doc->meta) + return doc->meta(doc, key, ptr, size); + return FZ_META_UNKNOWN_KEY; +} + +fz_transition * +fz_page_presentation(fz_document *doc, fz_page *page, float *duration) +{ + float dummy; + if (duration) + *duration = 0; + else + duration = &dummy; + if (doc && doc->page_presentation && page) + return doc->page_presentation(doc, page, duration); + return NULL; +} + +int fz_javascript_supported(void) +{ + return pdf_js_supported(); +} + +void +fz_write_document(fz_document *doc, char *filename, fz_write_options *opts) +{ + if (doc && doc->write) + doc->write(doc, filename, opts); +} diff --git a/source/fitz/draw-affine.c b/source/fitz/draw-affine.c new file mode 100644 index 00000000..a8ebfa02 --- /dev/null +++ b/source/fitz/draw-affine.c @@ -0,0 +1,755 @@ +#include "mupdf/fitz.h" +#include "draw-imp.h" + +typedef unsigned char byte; + +static inline float roundup(float x) +{ + return (x < 0) ? floorf(x) : ceilf(x); +} + +static inline int lerp(int a, int b, int t) +{ + return a + (((b - a) * t) >> 16); +} + +static inline int bilerp(int a, int b, int c, int d, int u, int v) +{ + return lerp(lerp(a, b, u), lerp(c, d, u), v); +} + +static inline byte *sample_nearest(byte *s, int w, int h, int n, int u, int v) +{ + if (u < 0) u = 0; + if (v < 0) v = 0; + if (u >= w) u = w - 1; + if (v >= h) v = h - 1; + return s + (v * w + u) * n; +} + +/* Blend premultiplied source image in constant alpha over destination */ + +static inline void +fz_paint_affine_alpha_N_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *hp) +{ + int k; + int n1 = n-1; + + while (w--) + { + int ui = u >> 16; + int vi = v >> 16; + if (ui >= 0 && ui < sw && vi >= 0 && vi < sh) + { + int uf = u & 0xffff; + int vf = v & 0xffff; + byte *a = sample_nearest(sp, sw, sh, n, ui, vi); + byte *b = sample_nearest(sp, sw, sh, n, ui+1, vi); + byte *c = sample_nearest(sp, sw, sh, n, ui, vi+1); + byte *d = sample_nearest(sp, sw, sh, n, ui+1, vi+1); + int xa = bilerp(a[n1], b[n1], c[n1], d[n1], uf, vf); + int t; + xa = fz_mul255(xa, alpha); + t = 255 - xa; + for (k = 0; k < n1; k++) + { + int x = bilerp(a[k], b[k], c[k], d[k], uf, vf); + dp[k] = fz_mul255(x, alpha) + fz_mul255(dp[k], t); + } + dp[n1] = xa + fz_mul255(dp[n1], t); + if (hp) + hp[0] = xa + fz_mul255(hp[0], t); + } + dp += n; + if (hp) + hp++; + u += fa; + v += fb; + } +} + +/* Special case code for gray -> rgb */ +static inline void +fz_paint_affine_alpha_g2rgb_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int alpha, byte *hp) +{ + while (w--) + { + int ui = u >> 16; + int vi = v >> 16; + if (ui >= 0 && ui < sw && vi >= 0 && vi < sh) + { + int uf = u & 0xffff; + int vf = v & 0xffff; + byte *a = sample_nearest(sp, sw, sh, 2, ui, vi); + byte *b = sample_nearest(sp, sw, sh, 2, ui+1, vi); + byte *c = sample_nearest(sp, sw, sh, 2, ui, vi+1); + byte *d = sample_nearest(sp, sw, sh, 2, ui+1, vi+1); + int y = bilerp(a[1], b[1], c[1], d[1], uf, vf); + int x = bilerp(a[0], b[0], c[0], d[0], uf, vf); + int t; + x = fz_mul255(x, alpha); + y = fz_mul255(y, alpha); + t = 255 - y; + dp[0] = x + fz_mul255(dp[0], t); + dp[1] = x + fz_mul255(dp[1], t); + dp[2] = x + fz_mul255(dp[2], t); + dp[3] = y + fz_mul255(dp[3], t); + if (hp) + hp[0] = y + fz_mul255(hp[0], t); + } + dp += 4; + if (hp) + hp++; + u += fa; + v += fb; + } +} + +static inline void +fz_paint_affine_alpha_N_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *hp) +{ + int k; + int n1 = n-1; + + while (w--) + { + int ui = u >> 16; + int vi = v >> 16; + if (ui >= 0 && ui < sw && vi >= 0 && vi < sh) + { + byte *sample = sp + ((vi * sw + ui) * n); + int a = fz_mul255(sample[n-1], alpha); + int t = 255 - a; + for (k = 0; k < n1; k++) + dp[k] = fz_mul255(sample[k], alpha) + fz_mul255(dp[k], t); + dp[n1] = a + fz_mul255(dp[n1], t); + if (hp) + hp[0] = a + fz_mul255(hp[0], t); + } + dp += n; + if (hp) + hp++; + u += fa; + v += fb; + } +} + +static inline void +fz_paint_affine_alpha_g2rgb_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int alpha, byte *hp) +{ + while (w--) + { + int ui = u >> 16; + int vi = v >> 16; + if (ui >= 0 && ui < sw && vi >= 0 && vi < sh) + { + byte *sample = sp + ((vi * sw + ui) * 2); + int x = fz_mul255(sample[0], alpha); + int a = fz_mul255(sample[1], alpha); + int t = 255 - a; + dp[0] = x + fz_mul255(dp[0], t); + dp[1] = x + fz_mul255(dp[1], t); + dp[2] = x + fz_mul255(dp[2], t); + dp[3] = a + fz_mul255(dp[3], t); + if (hp) + hp[0] = a + fz_mul255(hp[0], t); + } + dp += 4; + if (hp) + hp++; + u += fa; + v += fb; + } +} + +/* Blend premultiplied source image over destination */ + +static inline void +fz_paint_affine_N_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, byte *hp) +{ + int k; + int n1 = n-1; + + while (w--) + { + int ui = u >> 16; + int vi = v >> 16; + if (ui >= 0 && ui < sw && vi >= 0 && vi < sh) + { + int uf = u & 0xffff; + int vf = v & 0xffff; + byte *a = sample_nearest(sp, sw, sh, n, ui, vi); + byte *b = sample_nearest(sp, sw, sh, n, ui+1, vi); + byte *c = sample_nearest(sp, sw, sh, n, ui, vi+1); + byte *d = sample_nearest(sp, sw, sh, n, ui+1, vi+1); + int y = bilerp(a[n1], b[n1], c[n1], d[n1], uf, vf); + int t = 255 - y; + for (k = 0; k < n1; k++) + { + int x = bilerp(a[k], b[k], c[k], d[k], uf, vf); + dp[k] = x + fz_mul255(dp[k], t); + } + dp[n1] = y + fz_mul255(dp[n1], t); + if (hp) + hp[0] = y + fz_mul255(hp[0], t); + } + dp += n; + if (hp) + hp++; + u += fa; + v += fb; + } +} + +static inline void +fz_paint_affine_solid_g2rgb_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, byte *hp) +{ + while (w--) + { + int ui = u >> 16; + int vi = v >> 16; + if (ui >= 0 && ui < sw && vi >= 0 && vi < sh) + { + int uf = u & 0xffff; + int vf = v & 0xffff; + byte *a = sample_nearest(sp, sw, sh, 2, ui, vi); + byte *b = sample_nearest(sp, sw, sh, 2, ui+1, vi); + byte *c = sample_nearest(sp, sw, sh, 2, ui, vi+1); + byte *d = sample_nearest(sp, sw, sh, 2, ui+1, vi+1); + int y = bilerp(a[1], b[1], c[1], d[1], uf, vf); + int t = 255 - y; + int x = bilerp(a[0], b[0], c[0], d[0], uf, vf); + dp[0] = x + fz_mul255(dp[0], t); + dp[1] = x + fz_mul255(dp[1], t); + dp[2] = x + fz_mul255(dp[2], t); + dp[3] = y + fz_mul255(dp[3], t); + if (hp) + hp[0] = y + fz_mul255(hp[0], t); + } + dp += 4; + if (hp) + hp++; + u += fa; + v += fb; + } +} + +static inline void +fz_paint_affine_N_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, byte *hp) +{ + int k; + int n1 = n-1; + + while (w--) + { + int ui = u >> 16; + int vi = v >> 16; + if (ui >= 0 && ui < sw && vi >= 0 && vi < sh) + { + byte *sample = sp + ((vi * sw + ui) * n); + int a = sample[n1]; + int t = 255 - a; + for (k = 0; k < n1; k++) + dp[k] = sample[k] + fz_mul255(dp[k], t); + dp[n1] = a + fz_mul255(dp[n1], t); + if (hp) + hp[0] = a + fz_mul255(hp[0], t); + } + dp += n; + if (hp) + hp++; + u += fa; + v += fb; + } +} + +static inline void +fz_paint_affine_solid_g2rgb_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, byte *hp) +{ + while (w--) + { + int ui = u >> 16; + int vi = v >> 16; + if (ui >= 0 && ui < sw && vi >= 0 && vi < sh) + { + byte *sample = sp + ((vi * sw + ui) * 2); + int x = sample[0]; + int a = sample[1]; + int t = 255 - a; + dp[0] = x + fz_mul255(dp[0], t); + dp[1] = x + fz_mul255(dp[1], t); + dp[2] = x + fz_mul255(dp[2], t); + dp[3] = a + fz_mul255(dp[3], t); + if (hp) + hp[0] = a + fz_mul255(hp[0], t); + } + dp += 4; + if (hp) + hp++; + u += fa; + v += fb; + } +} + +/* Blend non-premultiplied color in source image mask over destination */ + +static inline void +fz_paint_affine_color_N_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, byte *color, byte *hp) +{ + int n1 = n - 1; + int sa = color[n1]; + int k; + + while (w--) + { + int ui = u >> 16; + int vi = v >> 16; + if (ui >= 0 && ui < sw && vi >= 0 && vi < sh) + { + int uf = u & 0xffff; + int vf = v & 0xffff; + byte *a = sample_nearest(sp, sw, sh, 1, ui, vi); + byte *b = sample_nearest(sp, sw, sh, 1, ui+1, vi); + byte *c = sample_nearest(sp, sw, sh, 1, ui, vi+1); + byte *d = sample_nearest(sp, sw, sh, 1, ui+1, vi+1); + int ma = bilerp(a[0], b[0], c[0], d[0], uf, vf); + int masa = FZ_COMBINE(FZ_EXPAND(ma), sa); + for (k = 0; k < n1; k++) + dp[k] = FZ_BLEND(color[k], dp[k], masa); + dp[n1] = FZ_BLEND(255, dp[n1], masa); + if (hp) + hp[0] = FZ_BLEND(255, hp[0], masa); + } + dp += n; + if (hp) + hp++; + u += fa; + v += fb; + } +} + +static inline void +fz_paint_affine_color_N_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, byte *color, byte *hp) +{ + int n1 = n-1; + int sa = color[n1]; + int k; + + while (w--) + { + int ui = u >> 16; + int vi = v >> 16; + if (ui >= 0 && ui < sw && vi >= 0 && vi < sh) + { + int ma = sp[vi * sw + ui]; + int masa = FZ_COMBINE(FZ_EXPAND(ma), sa); + for (k = 0; k < n1; k++) + dp[k] = FZ_BLEND(color[k], dp[k], masa); + dp[n1] = FZ_BLEND(255, dp[n1], masa); + if (hp) + hp[0] = FZ_BLEND(255, hp[0], masa); + } + dp += n; + if (hp) + hp++; + u += fa; + v += fb; + } +} + +static void +fz_paint_affine_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *color/*unused*/, byte *hp) +{ + if (alpha == 255) + { + switch (n) + { + case 1: fz_paint_affine_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 1, hp); break; + case 2: fz_paint_affine_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 2, hp); break; + case 4: fz_paint_affine_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 4, hp); break; + default: fz_paint_affine_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, n, hp); break; + } + } + else if (alpha > 0) + { + switch (n) + { + case 1: fz_paint_affine_alpha_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 1, alpha, hp); break; + case 2: fz_paint_affine_alpha_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 2, alpha, hp); break; + case 4: fz_paint_affine_alpha_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 4, alpha, hp); break; + default: fz_paint_affine_alpha_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, n, alpha, hp); break; + } + } +} + +static void +fz_paint_affine_g2rgb_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *color/*unused*/, byte *hp) +{ + if (alpha == 255) + { + fz_paint_affine_solid_g2rgb_lerp(dp, sp, sw, sh, u, v, fa, fb, w, hp); + } + else if (alpha > 0) + { + fz_paint_affine_alpha_g2rgb_lerp(dp, sp, sw, sh, u, v, fa, fb, w, alpha, hp); + } +} + +static void +fz_paint_affine_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *color/*unused */, byte *hp) +{ + if (alpha == 255) + { + switch (n) + { + case 1: fz_paint_affine_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 1, hp); break; + case 2: fz_paint_affine_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 2, hp); break; + case 4: fz_paint_affine_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 4, hp); break; + default: fz_paint_affine_N_near(dp, sp, sw, sh, u, v, fa, fb, w, n, hp); break; + } + } + else if (alpha > 0) + { + switch (n) + { + case 1: fz_paint_affine_alpha_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 1, alpha, hp); break; + case 2: fz_paint_affine_alpha_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 2, alpha, hp); break; + case 4: fz_paint_affine_alpha_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 4, alpha, hp); break; + default: fz_paint_affine_alpha_N_near(dp, sp, sw, sh, u, v, fa, fb, w, n, alpha, hp); break; + } + } +} + +static void +fz_paint_affine_g2rgb_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *color/*unused*/, byte *hp) +{ + if (alpha == 255) + { + fz_paint_affine_solid_g2rgb_near(dp, sp, sw, sh, u, v, fa, fb, w, hp); + } + else if (alpha > 0) + { + fz_paint_affine_alpha_g2rgb_near(dp, sp, sw, sh, u, v, fa, fb, w, alpha, hp); + } +} + +static void +fz_paint_affine_color_lerp(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha/*unused*/, byte *color, byte *hp) +{ + switch (n) + { + case 2: fz_paint_affine_color_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 2, color, hp); break; + case 4: fz_paint_affine_color_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, 4, color, hp); break; + default: fz_paint_affine_color_N_lerp(dp, sp, sw, sh, u, v, fa, fb, w, n, color, hp); break; + } +} + +static void +fz_paint_affine_color_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha/*unused*/, byte *color, byte *hp) +{ + switch (n) + { + case 2: fz_paint_affine_color_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 2, color, hp); break; + case 4: fz_paint_affine_color_N_near(dp, sp, sw, sh, u, v, fa, fb, w, 4, color, hp); break; + default: fz_paint_affine_color_N_near(dp, sp, sw, sh, u, v, fa, fb, w, n, color, hp); break; + } +} + +/* RJW: The following code was originally written to be sensitive to + * FLT_EPSILON. Given the way the 'minimum representable difference' + * between 2 floats changes size as we scale, we now pick a larger + * value to ensure idempotency even with rounding problems. The + * value we pick is still far smaller than would ever show up with + * antialiasing. + */ +#define MY_EPSILON 0.001 + +void +fz_gridfit_matrix(fz_matrix *m) +{ + if (fabsf(m->b) < FLT_EPSILON && fabsf(m->c) < FLT_EPSILON) + { + if (m->a > 0) + { + float f; + /* Adjust left hand side onto pixel boundary */ + f = (float)(int)(m->e); + if (f - m->e > MY_EPSILON) + f -= 1.0; /* Ensure it moves left */ + m->a += m->e - f; /* width gets wider as f <= m.e */ + m->e = f; + /* Adjust right hand side onto pixel boundary */ + f = (float)(int)(m->a); + if (m->a - f > MY_EPSILON) + f += 1.0; /* Ensure it moves right */ + m->a = f; + } + else if (m->a < 0) + { + float f; + /* Adjust right hand side onto pixel boundary */ + f = (float)(int)(m->e); + if (m->e - f > MY_EPSILON) + f += 1.0; /* Ensure it moves right */ + m->a += m->e - f; /* width gets wider (more -ve) */ + m->e = f; + /* Adjust left hand side onto pixel boundary */ + f = (float)(int)(m->a); + if (f - m->a > MY_EPSILON) + f -= 1.0; /* Ensure it moves left */ + m->a = f; + } + if (m->d > 0) + { + float f; + /* Adjust top onto pixel boundary */ + f = (float)(int)(m->f); + if (f - m->f > MY_EPSILON) + f -= 1.0; /* Ensure it moves upwards */ + m->d += m->f - f; /* width gets wider as f <= m.f */ + m->f = f; + /* Adjust bottom onto pixel boundary */ + f = (float)(int)(m->d); + if (m->d - f > MY_EPSILON) + f += 1.0; /* Ensure it moves down */ + m->d = f; + } + else if (m->d < 0) + { + float f; + /* Adjust bottom onto pixel boundary */ + f = (float)(int)(m->f); + if (m->f - f > MY_EPSILON) + f += 1.0; /* Ensure it moves down */ + m->d += m->f - f; /* width gets wider (more -ve) */ + m->f = f; + /* Adjust top onto pixel boundary */ + f = (float)(int)(m->d); + if (f - m->d > MY_EPSILON) + f -= 1.0; /* Ensure it moves up */ + m->d = f; + } + } + else if (fabsf(m->a) < FLT_EPSILON && fabsf(m->d) < FLT_EPSILON) + { + if (m->b > 0) + { + float f; + /* Adjust left hand side onto pixel boundary */ + f = (float)(int)(m->f); + if (f - m->f > MY_EPSILON) + f -= 1.0; /* Ensure it moves left */ + m->b += m->f - f; /* width gets wider as f <= m.f */ + m->f = f; + /* Adjust right hand side onto pixel boundary */ + f = (float)(int)(m->b); + if (m->b - f > MY_EPSILON) + f += 1.0; /* Ensure it moves right */ + m->b = f; + } + else if (m->b < 0) + { + float f; + /* Adjust right hand side onto pixel boundary */ + f = (float)(int)(m->f); + if (m->f - f > MY_EPSILON) + f += 1.0; /* Ensure it moves right */ + m->b += m->f - f; /* width gets wider (more -ve) */ + m->f = f; + /* Adjust left hand side onto pixel boundary */ + f = (float)(int)(m->b); + if (f - m->b > MY_EPSILON) + f -= 1.0; /* Ensure it moves left */ + m->b = f; + } + if (m->c > 0) + { + float f; + /* Adjust top onto pixel boundary */ + f = (float)(int)(m->e); + if (f - m->e > MY_EPSILON) + f -= 1.0; /* Ensure it moves upwards */ + m->c += m->e - f; /* width gets wider as f <= m.e */ + m->e = f; + /* Adjust bottom onto pixel boundary */ + f = (float)(int)(m->c); + if (m->c - f > MY_EPSILON) + f += 1.0; /* Ensure it moves down */ + m->c = f; + } + else if (m->c < 0) + { + float f; + /* Adjust bottom onto pixel boundary */ + f = (float)(int)(m->e); + if (m->e - f > MY_EPSILON) + f += 1.0; /* Ensure it moves down */ + m->c += m->e - f; /* width gets wider (more -ve) */ + m->e = f; + /* Adjust top onto pixel boundary */ + f = (float)(int)(m->c); + if (f - m->c > MY_EPSILON) + f -= 1.0; /* Ensure it moves up */ + m->c = f; + } + } +} + +/* Draw an image with an affine transform on destination */ + +static void +fz_paint_image_imp(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, byte *color, int alpha) +{ + byte *dp, *sp, *hp; + int u, v, fa, fb, fc, fd; + int x, y, w, h; + int sw, sh, n, hw; + fz_irect bbox; + int dolerp; + void (*paintfn)(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *color, byte *hp); + fz_matrix local_ctm = *ctm; + fz_rect rect; + int is_rectilinear; + + /* grid fit the image */ + fz_gridfit_matrix(&local_ctm); + + /* turn on interpolation for upscaled and non-rectilinear transforms */ + dolerp = 0; + is_rectilinear = fz_is_rectilinear(&local_ctm); + if (!is_rectilinear) + dolerp = 1; + if (sqrtf(local_ctm.a * local_ctm.a + local_ctm.b * local_ctm.b) > img->w) + dolerp = 1; + if (sqrtf(local_ctm.c * local_ctm.c + local_ctm.d * local_ctm.d) > img->h) + dolerp = 1; + + /* except when we shouldn't, at large magnifications */ + if (!img->interpolate) + { + if (sqrtf(local_ctm.a * local_ctm.a + local_ctm.b * local_ctm.b) > img->w * 2) + dolerp = 0; + if (sqrtf(local_ctm.c * local_ctm.c + local_ctm.d * local_ctm.d) > img->h * 2) + dolerp = 0; + } + + rect = fz_unit_rect; + fz_irect_from_rect(&bbox, fz_transform_rect(&rect, &local_ctm)); + fz_intersect_irect(&bbox, scissor); + + x = bbox.x0; + if (shape && shape->x > x) + x = shape->x; + y = bbox.y0; + if (shape && shape->y > y) + y = shape->y; + w = bbox.x1; + if (shape && shape->x + shape->w < w) + w = shape->x + shape->w; + w -= x; + h = bbox.y1; + if (shape && shape->y + shape->h < h) + h = shape->y + shape->h; + h -= y; + if (w < 0 || h < 0) + return; + + /* map from screen space (x,y) to image space (u,v) */ + fz_pre_scale(&local_ctm, 1.0f / img->w, 1.0f / img->h); + fz_invert_matrix(&local_ctm, &local_ctm); + + fa = (int)(local_ctm.a *= 65536.0f); + fb = (int)(local_ctm.b *= 65536.0f); + fc = (int)(local_ctm.c *= 65536.0f); + fd = (int)(local_ctm.d *= 65536.0f); + local_ctm.e *= 65536.0f; + local_ctm.f *= 65536.0f; + + /* Calculate initial texture positions. Do a half step to start. */ + /* Bug 693021: Keep calculation in float for as long as possible to + * avoid overflow. */ + u = (int)((local_ctm.a * x) + (local_ctm.c * y) + local_ctm.e + ((local_ctm.a + local_ctm.c) * .5f)); + v = (int)((local_ctm.b * x) + (local_ctm.d * y) + local_ctm.f + ((local_ctm.b + local_ctm.d) * .5f)); + + /* RJW: The following is voodoo. No idea why it works, but it gives + * the best match between scaled/unscaled/interpolated/non-interpolated + * that we have found. */ + if (dolerp) { + u -= 32768; + v -= 32768; + if (is_rectilinear) + { + if (u < 0) + u = 0; + if (v < 0) + v = 0; + } + } + + dp = dst->samples + (unsigned int)(((y - dst->y) * dst->w + (x - dst->x)) * dst->n); + n = dst->n; + sp = img->samples; + sw = img->w; + sh = img->h; + if (shape) + { + hw = shape->w; + hp = shape->samples + (unsigned int)(((y - shape->y) * hw) + x - shape->x); + } + else + { + hw = 0; + hp = NULL; + } + + /* TODO: if (fb == 0 && fa == 1) call fz_paint_span */ + + if (dst->n == 4 && img->n == 2) + { + assert(!color); + if (dolerp) + paintfn = fz_paint_affine_g2rgb_lerp; + else + paintfn = fz_paint_affine_g2rgb_near; + } + else + { + if (dolerp) + { + if (color) + paintfn = fz_paint_affine_color_lerp; + else + paintfn = fz_paint_affine_lerp; + } + else + { + if (color) + paintfn = fz_paint_affine_color_near; + else + paintfn = fz_paint_affine_near; + } + } + + while (h--) + { + paintfn(dp, sp, sw, sh, u, v, fa, fb, w, n, alpha, color, hp); + dp += dst->w * n; + hp += hw; + u += fc; + v += fd; + } +} + +void +fz_paint_image_with_color(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, byte *color) +{ + assert(img->n == 1); + fz_paint_image_imp(dst, scissor, shape, img, ctm, color, 255); +} + +void +fz_paint_image(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, int alpha) +{ + assert(dst->n == img->n || (dst->n == 4 && img->n == 2)); + fz_paint_image_imp(dst, scissor, shape, img, ctm, NULL, alpha); +} diff --git a/source/fitz/draw-blend.c b/source/fitz/draw-blend.c new file mode 100644 index 00000000..62666fc5 --- /dev/null +++ b/source/fitz/draw-blend.c @@ -0,0 +1,636 @@ +#include "mupdf/fitz.h" +#include "draw-imp.h" + +/* PDF 1.4 blend modes. These are slow. */ + +typedef unsigned char byte; + +static const char *fz_blendmode_names[] = +{ + "Normal", + "Multiply", + "Screen", + "Overlay", + "Darken", + "Lighten", + "ColorDodge", + "ColorBurn", + "HardLight", + "SoftLight", + "Difference", + "Exclusion", + "Hue", + "Saturation", + "Color", + "Luminosity", +}; + +int fz_lookup_blendmode(char *name) +{ + int i; + for (i = 0; i < nelem(fz_blendmode_names); i++) + if (!strcmp(name, fz_blendmode_names[i])) + return i; + return FZ_BLEND_NORMAL; +} + +char *fz_blendmode_name(int blendmode) +{ + if (blendmode >= 0 && blendmode < nelem(fz_blendmode_names)) + return (char*)fz_blendmode_names[blendmode]; + return "Normal"; +} + +/* Separable blend modes */ + +static inline int fz_screen_byte(int b, int s) +{ + return b + s - fz_mul255(b, s); +} + +static inline int fz_hard_light_byte(int b, int s) +{ + int s2 = s << 1; + if (s <= 127) + return fz_mul255(b, s2); + else + return fz_screen_byte(b, s2 - 255); +} + +static inline int fz_overlay_byte(int b, int s) +{ + return fz_hard_light_byte(s, b); /* note swapped order */ +} + +static inline int fz_darken_byte(int b, int s) +{ + return fz_mini(b, s); +} + +static inline int fz_lighten_byte(int b, int s) +{ + return fz_maxi(b, s); +} + +static inline int fz_color_dodge_byte(int b, int s) +{ + s = 255 - s; + if (b == 0) + return 0; + else if (b >= s) + return 255; + else + return (0x1fe * b + s) / (s << 1); +} + +static inline int fz_color_burn_byte(int b, int s) +{ + b = 255 - b; + if (b == 0) + return 255; + else if (b >= s) + return 0; + else + return 0xff - (0x1fe * b + s) / (s << 1); +} + +static inline int fz_soft_light_byte(int b, int s) +{ + /* review this */ + if (s < 128) { + return b - fz_mul255(fz_mul255((255 - (s<<1)), b), 255 - b); + } + else { + int dbd; + if (b < 64) + dbd = fz_mul255(fz_mul255((b << 4) - 12, b) + 4, b); + else + dbd = (int)sqrtf(255.0f * b); + return b + fz_mul255(((s<<1) - 255), (dbd - b)); + } +} + +static inline int fz_difference_byte(int b, int s) +{ + return fz_absi(b - s); +} + +static inline int fz_exclusion_byte(int b, int s) +{ + return b + s - (fz_mul255(b, s)<<1); +} + +/* Non-separable blend modes */ + +static void +fz_luminosity_rgb(unsigned char *rd, unsigned char *gd, unsigned char *bd, int rb, int gb, int bb, int rs, int gs, int bs) +{ + int delta, scale; + int r, g, b, y; + + /* 0.3, 0.59, 0.11 in fixed point */ + delta = ((rs - rb) * 77 + (gs - gb) * 151 + (bs - bb) * 28 + 0x80) >> 8; + r = rb + delta; + g = gb + delta; + b = bb + delta; + + if ((r | g | b) & 0x100) + { + y = (rs * 77 + gs * 151 + bs * 28 + 0x80) >> 8; + if (delta > 0) + { + int max; + max = fz_maxi(r, fz_maxi(g, b)); + scale = (max == y ? 0 : ((255 - y) << 16) / (max - y)); + } + else + { + int min; + min = fz_mini(r, fz_mini(g, b)); + scale = (y == min ? 0 : (y << 16) / (y - min)); + } + r = y + (((r - y) * scale + 0x8000) >> 16); + g = y + (((g - y) * scale + 0x8000) >> 16); + b = y + (((b - y) * scale + 0x8000) >> 16); + } + + *rd = fz_clampi(r, 0, 255); + *gd = fz_clampi(g, 0, 255); + *bd = fz_clampi(b, 0, 255); +} + +static void +fz_saturation_rgb(unsigned char *rd, unsigned char *gd, unsigned char *bd, int rb, int gb, int bb, int rs, int gs, int bs) +{ + int minb, maxb; + int mins, maxs; + int y; + int scale; + int r, g, b; + + minb = fz_mini(rb, fz_mini(gb, bb)); + maxb = fz_maxi(rb, fz_maxi(gb, bb)); + if (minb == maxb) + { + /* backdrop has zero saturation, avoid divide by 0 */ + gb = fz_clampi(gb, 0, 255); + *rd = gb; + *gd = gb; + *bd = gb; + return; + } + + mins = fz_mini(rs, fz_mini(gs, bs)); + maxs = fz_maxi(rs, fz_maxi(gs, bs)); + + scale = ((maxs - mins) << 16) / (maxb - minb); + y = (rb * 77 + gb * 151 + bb * 28 + 0x80) >> 8; + r = y + ((((rb - y) * scale) + 0x8000) >> 16); + g = y + ((((gb - y) * scale) + 0x8000) >> 16); + b = y + ((((bb - y) * scale) + 0x8000) >> 16); + + if ((r | g | b) & 0x100) + { + int scalemin, scalemax; + int min, max; + + min = fz_mini(r, fz_mini(g, b)); + max = fz_maxi(r, fz_maxi(g, b)); + + if (min < 0) + scalemin = (y << 16) / (y - min); + else + scalemin = 0x10000; + + if (max > 255) + scalemax = ((255 - y) << 16) / (max - y); + else + scalemax = 0x10000; + + scale = fz_mini(scalemin, scalemax); + r = y + (((r - y) * scale + 0x8000) >> 16); + g = y + (((g - y) * scale + 0x8000) >> 16); + b = y + (((b - y) * scale + 0x8000) >> 16); + } + + *rd = fz_clampi(r, 0, 255); + *gd = fz_clampi(g, 0, 255); + *bd = fz_clampi(b, 0, 255); +} + +static void +fz_color_rgb(unsigned char *rr, unsigned char *rg, unsigned char *rb, int br, int bg, int bb, int sr, int sg, int sb) +{ + fz_luminosity_rgb(rr, rg, rb, sr, sg, sb, br, bg, bb); +} + +static void +fz_hue_rgb(unsigned char *rr, unsigned char *rg, unsigned char *rb, int br, int bg, int bb, int sr, int sg, int sb) +{ + unsigned char tr, tg, tb; + fz_luminosity_rgb(&tr, &tg, &tb, sr, sg, sb, br, bg, bb); + fz_saturation_rgb(rr, rg, rb, tr, tg, tb, br, bg, bb); +} + +void +fz_blend_pixel(unsigned char dp[3], unsigned char bp[3], unsigned char sp[3], int blendmode) +{ + int k; + /* non-separable blend modes */ + switch (blendmode) + { + case FZ_BLEND_HUE: fz_hue_rgb(&dp[0], &dp[1], &dp[2], bp[0], bp[1], bp[2], sp[0], sp[1], sp[2]); return; + case FZ_BLEND_SATURATION: fz_saturation_rgb(&dp[0], &dp[1], &dp[2], bp[0], bp[1], bp[2], sp[0], sp[1], sp[2]); return; + case FZ_BLEND_COLOR: fz_color_rgb(&dp[0], &dp[1], &dp[2], bp[0], bp[1], bp[2], sp[0], sp[1], sp[2]); return; + case FZ_BLEND_LUMINOSITY: fz_luminosity_rgb(&dp[0], &dp[1], &dp[2], bp[0], bp[1], bp[2], sp[0], sp[1], sp[2]); return; + } + /* separable blend modes */ + for (k = 0; k < 3; k++) + { + switch (blendmode) + { + default: + case FZ_BLEND_NORMAL: dp[k] = sp[k]; break; + case FZ_BLEND_MULTIPLY: dp[k] = fz_mul255(bp[k], sp[k]); break; + case FZ_BLEND_SCREEN: dp[k] = fz_screen_byte(bp[k], sp[k]); break; + case FZ_BLEND_OVERLAY: dp[k] = fz_overlay_byte(bp[k], sp[k]); break; + case FZ_BLEND_DARKEN: dp[k] = fz_darken_byte(bp[k], sp[k]); break; + case FZ_BLEND_LIGHTEN: dp[k] = fz_lighten_byte(bp[k], sp[k]); break; + case FZ_BLEND_COLOR_DODGE: dp[k] = fz_color_dodge_byte(bp[k], sp[k]); break; + case FZ_BLEND_COLOR_BURN: dp[k] = fz_color_burn_byte(bp[k], sp[k]); break; + case FZ_BLEND_HARD_LIGHT: dp[k] = fz_hard_light_byte(bp[k], sp[k]); break; + case FZ_BLEND_SOFT_LIGHT: dp[k] = fz_soft_light_byte(bp[k], sp[k]); break; + case FZ_BLEND_DIFFERENCE: dp[k] = fz_difference_byte(bp[k], sp[k]); break; + case FZ_BLEND_EXCLUSION: dp[k] = fz_exclusion_byte(bp[k], sp[k]); break; + } + } +} + +/* Blending loops */ + +void +fz_blend_separable(byte * restrict bp, byte * restrict sp, int n, int w, int blendmode) +{ + int k; + int n1 = n - 1; + while (w--) + { + int sa = sp[n1]; + int ba = bp[n1]; + int saba = fz_mul255(sa, ba); + + /* ugh, division to get non-premul components */ + int invsa = sa ? 255 * 256 / sa : 0; + int invba = ba ? 255 * 256 / ba : 0; + + for (k = 0; k < n1; k++) + { + int sc = (sp[k] * invsa) >> 8; + int bc = (bp[k] * invba) >> 8; + int rc; + + switch (blendmode) + { + default: + case FZ_BLEND_NORMAL: rc = sc; break; + case FZ_BLEND_MULTIPLY: rc = fz_mul255(bc, sc); break; + case FZ_BLEND_SCREEN: rc = fz_screen_byte(bc, sc); break; + case FZ_BLEND_OVERLAY: rc = fz_overlay_byte(bc, sc); break; + case FZ_BLEND_DARKEN: rc = fz_darken_byte(bc, sc); break; + case FZ_BLEND_LIGHTEN: rc = fz_lighten_byte(bc, sc); break; + case FZ_BLEND_COLOR_DODGE: rc = fz_color_dodge_byte(bc, sc); break; + case FZ_BLEND_COLOR_BURN: rc = fz_color_burn_byte(bc, sc); break; + case FZ_BLEND_HARD_LIGHT: rc = fz_hard_light_byte(bc, sc); break; + case FZ_BLEND_SOFT_LIGHT: rc = fz_soft_light_byte(bc, sc); break; + case FZ_BLEND_DIFFERENCE: rc = fz_difference_byte(bc, sc); break; + case FZ_BLEND_EXCLUSION: rc = fz_exclusion_byte(bc, sc); break; + } + + bp[k] = fz_mul255(255 - sa, bp[k]) + fz_mul255(255 - ba, sp[k]) + fz_mul255(saba, rc); + } + + bp[k] = ba + sa - saba; + + sp += n; + bp += n; + } +} + +void +fz_blend_nonseparable(byte * restrict bp, byte * restrict sp, int w, int blendmode) +{ + while (w--) + { + unsigned char rr, rg, rb; + + int sa = sp[3]; + int ba = bp[3]; + int saba = fz_mul255(sa, ba); + + /* ugh, division to get non-premul components */ + int invsa = sa ? 255 * 256 / sa : 0; + int invba = ba ? 255 * 256 / ba : 0; + + int sr = (sp[0] * invsa) >> 8; + int sg = (sp[1] * invsa) >> 8; + int sb = (sp[2] * invsa) >> 8; + + int br = (bp[0] * invba) >> 8; + int bg = (bp[1] * invba) >> 8; + int bb = (bp[2] * invba) >> 8; + + switch (blendmode) + { + default: + case FZ_BLEND_HUE: + fz_hue_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + case FZ_BLEND_SATURATION: + fz_saturation_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + case FZ_BLEND_COLOR: + fz_color_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + case FZ_BLEND_LUMINOSITY: + fz_luminosity_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + } + + bp[0] = fz_mul255(255 - sa, bp[0]) + fz_mul255(255 - ba, sp[0]) + fz_mul255(saba, rr); + bp[1] = fz_mul255(255 - sa, bp[1]) + fz_mul255(255 - ba, sp[1]) + fz_mul255(saba, rg); + bp[2] = fz_mul255(255 - sa, bp[2]) + fz_mul255(255 - ba, sp[2]) + fz_mul255(saba, rb); + bp[3] = ba + sa - saba; + + sp += 4; + bp += 4; + } +} + +static void +fz_blend_separable_nonisolated(byte * restrict bp, byte * restrict sp, int n, int w, int blendmode, byte * restrict hp, int alpha) +{ + int k; + int n1 = n - 1; + + if (alpha == 255 && blendmode == 0) + { + /* In this case, the uncompositing and the recompositing + * cancel one another out, and it's just a simple copy. */ + /* FIXME: Maybe we can avoid using the shape plane entirely + * and just copy? */ + while (w--) + { + int ha = fz_mul255(*hp++, alpha); /* ha = shape_alpha */ + /* If ha == 0 then leave everything unchanged */ + if (ha != 0) + { + for (k = 0; k < n; k++) + { + bp[k] = sp[k]; + } + } + + sp += n; + bp += n; + } + return; + } + while (w--) + { + int ha = *hp++; + int haa = fz_mul255(ha, alpha); /* ha = shape_alpha */ + /* If haa == 0 then leave everything unchanged */ + while (haa != 0) /* Use while, so we can break out */ + { + int sa, ba, bahaa, ra, invsa, invba, invha, invra; + sa = sp[n1]; + if (sa == 0) + break; /* No change! */ + invsa = sa ? 255 * 256 / sa : 0; + ba = bp[n1]; + if (ba == 0) + { + /* Just copy pixels (allowing for change in + * premultiplied alphas) */ + for (k = 0; k < n1; k++) + { + bp[k] = fz_mul255((sp[k] * invsa) >> 8, haa); + } + bp[n1] = haa; + break; + } + bahaa = fz_mul255(ba, haa); + + /* ugh, division to get non-premul components */ + invba = ba ? 255 * 256 / ba : 0; + + /* Calculate result_alpha - a combination of the + * background alpha, and 'shape' */ + ra = bp[n1] = ba - bahaa + haa; + if (ra == 0) + break; + /* Because we are a non-isolated group, we need to + * 'uncomposite' before we blend (recomposite). + * We assume that normal blending has been done inside + * the group, so: rc = (1-ha).bc + ha.sc + * A bit of rearrangement, and that gives us that: + * sc = (rc - bc)/ha + bc + * Now, the result of the blend (rc) was stored in src, so + * we actually want to calculate: + * sc = (sc-bc)/ha + bc + */ + invha = ha ? 255 * 256 / ha : 0; + invra = ra ? 255 * 256 / ra : 0; + + /* sa = the final alpha to blend with - this + * is calculated from the shape + alpha, + * divided by ra. */ + sa = (haa*invra + 128)>>8; + if (sa < 0) sa = 0; + if (sa > 255) sa = 255; + + for (k = 0; k < n1; k++) + { + /* Read pixels (and convert to non-premultiplied form) */ + int sc = (sp[k] * invsa + 128) >> 8; + int bc = (bp[k] * invba + 128) >> 8; + int rc; + + /* Uncomposite (see above) */ + sc = (((sc-bc) * invha + 128)>>8) + bc; + if (sc < 0) sc = 0; + if (sc > 255) sc = 255; + + switch (blendmode) + { + default: + case FZ_BLEND_NORMAL: rc = sc; break; + case FZ_BLEND_MULTIPLY: rc = fz_mul255(bc, sc); break; + case FZ_BLEND_SCREEN: rc = fz_screen_byte(bc, sc); break; + case FZ_BLEND_OVERLAY: rc = fz_overlay_byte(bc, sc); break; + case FZ_BLEND_DARKEN: rc = fz_darken_byte(bc, sc); break; + case FZ_BLEND_LIGHTEN: rc = fz_lighten_byte(bc, sc); break; + case FZ_BLEND_COLOR_DODGE: rc = fz_color_dodge_byte(bc, sc); break; + case FZ_BLEND_COLOR_BURN: rc = fz_color_burn_byte(bc, sc); break; + case FZ_BLEND_HARD_LIGHT: rc = fz_hard_light_byte(bc, sc); break; + case FZ_BLEND_SOFT_LIGHT: rc = fz_soft_light_byte(bc, sc); break; + case FZ_BLEND_DIFFERENCE: rc = fz_difference_byte(bc, sc); break; + case FZ_BLEND_EXCLUSION: rc = fz_exclusion_byte(bc, sc); break; + } + /* Composition formula, as given in pdf_reference17.pdf: + * rc = ( 1 - (ha/ra)) * bc + (ha/ra) * ((1-ba)*sc + ba * rc) + */ + rc = bc + fz_mul255(sa, fz_mul255(255 - ba, sc) + fz_mul255(ba, rc) - bc); + if (rc < 0) rc = 0; + if (rc > 255) rc = 255; + bp[k] = fz_mul255(rc, ra); + } + break; + } + + sp += n; + bp += n; + } +} + +static void +fz_blend_nonseparable_nonisolated(byte * restrict bp, byte * restrict sp, int w, int blendmode, byte * restrict hp, int alpha) +{ + while (w--) + { + int ha = *hp++; + int haa = fz_mul255(ha, alpha); + if (haa != 0) + { + int sa = sp[3]; + int ba = bp[3]; + int baha = fz_mul255(ba, haa); + + /* Calculate result_alpha */ + int ra = bp[3] = ba - baha + haa; + if (ra != 0) + { + /* Because we are a non-isolated group, we + * need to 'uncomposite' before we blend + * (recomposite). We assume that normal + * blending has been done inside the group, + * so: ra.rc = (1-ha).bc + ha.sc + * A bit of rearrangement, and that gives us + * that: sc = (ra.rc - bc)/ha + bc + * Now, the result of the blend was stored in + * src, so: */ + int invha = ha ? 255 * 256 / ha : 0; + + unsigned char rr, rg, rb; + + /* ugh, division to get non-premul components */ + int invsa = sa ? 255 * 256 / sa : 0; + int invba = ba ? 255 * 256 / ba : 0; + + int sr = (sp[0] * invsa) >> 8; + int sg = (sp[1] * invsa) >> 8; + int sb = (sp[2] * invsa) >> 8; + + int br = (bp[0] * invba) >> 8; + int bg = (bp[1] * invba) >> 8; + int bb = (bp[2] * invba) >> 8; + + /* Uncomposite */ + sr = (((sr-br)*invha)>>8) + br; + sg = (((sg-bg)*invha)>>8) + bg; + sb = (((sb-bb)*invha)>>8) + bb; + + switch (blendmode) + { + default: + case FZ_BLEND_HUE: + fz_hue_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + case FZ_BLEND_SATURATION: + fz_saturation_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + case FZ_BLEND_COLOR: + fz_color_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + case FZ_BLEND_LUMINOSITY: + fz_luminosity_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + } + + rr = fz_mul255(255 - haa, bp[0]) + fz_mul255(fz_mul255(255 - ba, sr), haa) + fz_mul255(baha, rr); + rg = fz_mul255(255 - haa, bp[1]) + fz_mul255(fz_mul255(255 - ba, sg), haa) + fz_mul255(baha, rg); + rb = fz_mul255(255 - haa, bp[2]) + fz_mul255(fz_mul255(255 - ba, sb), haa) + fz_mul255(baha, rb); + bp[0] = fz_mul255(ra, rr); + bp[1] = fz_mul255(ra, rg); + bp[2] = fz_mul255(ra, rb); + } + } + + sp += 4; + bp += 4; + } +} + +void +fz_blend_pixmap(fz_pixmap *dst, fz_pixmap *src, int alpha, int blendmode, int isolated, fz_pixmap *shape) +{ + unsigned char *sp, *dp; + fz_irect bbox; + fz_irect bbox2; + int x, y, w, h, n; + + /* TODO: fix this hack! */ + if (isolated && alpha < 255) + { + sp = src->samples; + n = src->w * src->h * src->n; + while (n--) + { + *sp = fz_mul255(*sp, alpha); + sp++; + } + } + + fz_pixmap_bbox_no_ctx(dst, &bbox); + fz_pixmap_bbox_no_ctx(src, &bbox2); + fz_intersect_irect(&bbox, &bbox2); + + x = bbox.x0; + y = bbox.y0; + w = bbox.x1 - bbox.x0; + h = bbox.y1 - bbox.y0; + + n = src->n; + sp = src->samples + (unsigned int)(((y - src->y) * src->w + (x - src->x)) * n); + dp = dst->samples + (unsigned int)(((y - dst->y) * dst->w + (x - dst->x)) * n); + + assert(src->n == dst->n); + + if (!isolated) + { + unsigned char *hp = shape->samples + (unsigned int)((y - shape->y) * shape->w + (x - shape->x)); + + while (h--) + { + if (n == 4 && blendmode >= FZ_BLEND_HUE) + fz_blend_nonseparable_nonisolated(dp, sp, w, blendmode, hp, alpha); + else + fz_blend_separable_nonisolated(dp, sp, n, w, blendmode, hp, alpha); + sp += src->w * n; + dp += dst->w * n; + hp += shape->w; + } + } + else + { + while (h--) + { + if (n == 4 && blendmode >= FZ_BLEND_HUE) + fz_blend_nonseparable(dp, sp, w, blendmode); + else + fz_blend_separable(dp, sp, n, w, blendmode); + sp += src->w * n; + dp += dst->w * n; + } + } +} diff --git a/source/fitz/draw-device.c b/source/fitz/draw-device.c new file mode 100644 index 00000000..e993daf7 --- /dev/null +++ b/source/fitz/draw-device.c @@ -0,0 +1,2126 @@ +#include "mupdf/fitz.h" +#include "draw-imp.h" + +#define QUANT(x,a) (((int)((x) * (a))) / (a)) +#define HSUBPIX 5.0 +#define VSUBPIX 5.0 + +#define STACK_SIZE 96 + +/* Enable the following to attempt to support knockout and/or isolated + * blending groups. */ +#define ATTEMPT_KNOCKOUT_AND_ISOLATED + +/* Enable the following to help debug group blending. */ +#undef DUMP_GROUP_BLENDS + +typedef struct fz_draw_device_s fz_draw_device; + +enum { + FZ_DRAWDEV_FLAGS_TYPE3 = 1, +}; + +typedef struct fz_draw_state_s fz_draw_state; + +struct fz_draw_state_s { + fz_irect scissor; + fz_pixmap *dest; + fz_pixmap *mask; + fz_pixmap *shape; + int blendmode; + int luminosity; + int id; + float alpha; + fz_matrix ctm; + float xstep, ystep; + fz_irect area; +}; + +struct fz_draw_device_s +{ + fz_gel *gel; + fz_context *ctx; + int flags; + int top; + fz_scale_cache *cache_x; + fz_scale_cache *cache_y; + fz_draw_state *stack; + int stack_max; + fz_draw_state init_stack[STACK_SIZE]; +}; + +#ifdef DUMP_GROUP_BLENDS +static int group_dump_count = 0; + +static void fz_dump_blend(fz_context *ctx, fz_pixmap *pix, const char *s) +{ + char name[80]; + + if (!pix) + return; + + sprintf(name, "dump%02d.png", group_dump_count); + if (s) + printf("%s%02d", s, group_dump_count); + group_dump_count++; + + fz_write_png(ctx, pix, name, (pix->n > 1)); +} + +static void dump_spaces(int x, const char *s) +{ + int i; + for (i = 0; i < x; i++) + printf(" "); + printf("%s", s); +} + +#endif + +static void fz_grow_stack(fz_draw_device *dev) +{ + int max = dev->stack_max * 2; + fz_draw_state *stack; + + if (dev->stack == &dev->init_stack[0]) + { + stack = fz_malloc(dev->ctx, sizeof(*stack) * max); + memcpy(stack, dev->stack, sizeof(*stack) * dev->stack_max); + } + else + { + stack = fz_resize_array(dev->ctx, dev->stack, max, sizeof(*stack)); + } + dev->stack = stack; + dev->stack_max = max; +} + +/* 'Push' the stack. Returns a pointer to the current state, with state[1] + * already having been initialised to contain the same thing. Simply + * change any contents of state[1] that you want to and continue. */ +static fz_draw_state * +push_stack(fz_draw_device *dev) +{ + fz_draw_state *state; + + if (dev->top == dev->stack_max-1) + fz_grow_stack(dev); + state = &dev->stack[dev->top]; + dev->top++; + memcpy(&state[1], state, sizeof(*state)); + return state; +} + +static void emergency_pop_stack(fz_draw_device *dev, fz_draw_state *state) +{ + fz_context *ctx = dev->ctx; + + if (state[1].mask != state[0].mask) + fz_drop_pixmap(ctx, state[1].mask); + if (state[1].dest != state[0].dest) + fz_drop_pixmap(ctx, state[1].dest); + if (state[1].shape != state[0].shape) + fz_drop_pixmap(ctx, state[1].shape); + dev->top--; + fz_rethrow(ctx); +} + +static fz_draw_state * +fz_knockout_begin(fz_draw_device *dev) +{ + fz_context *ctx = dev->ctx; + fz_irect bbox; + fz_pixmap *dest, *shape; + fz_draw_state *state = &dev->stack[dev->top]; + int isolated = state->blendmode & FZ_BLEND_ISOLATED; + + if ((state->blendmode & FZ_BLEND_KNOCKOUT) == 0) + return state; + + state = push_stack(dev); + + fz_pixmap_bbox(dev->ctx, state->dest, &bbox); + fz_intersect_irect(&bbox, &state->scissor); + dest = fz_new_pixmap_with_bbox(dev->ctx, state->dest->colorspace, &bbox); + + if (isolated) + { + fz_clear_pixmap(ctx, dest); + } + else + { + /* Find the last but one destination to copy */ + int i = dev->top-1; /* i = the one on entry (i.e. the last one) */ + fz_pixmap *prev = state->dest; + while (i > 0) + { + prev = dev->stack[--i].dest; + if (prev != state->dest) + break; + } + if (prev) + fz_copy_pixmap_rect(ctx, dest, prev, &bbox); + else + fz_clear_pixmap(ctx, dest); + } + + if (state->blendmode == 0 && isolated) + { + /* We can render direct to any existing shape plane. If there + * isn't one, we don't need to make one. */ + shape = state->shape; + } + else + { + shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, shape); + } +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Knockout begin\n"); +#endif + state[1].scissor = bbox; + state[1].dest = dest; + state[1].shape = shape; + state[1].blendmode &= ~FZ_BLEND_MODEMASK; + + return &state[1]; +} + +static void fz_knockout_end(fz_draw_device *dev) +{ + fz_draw_state *state; + int blendmode; + int isolated; + fz_context *ctx = dev->ctx; + + if (dev->top == 0) + { + fz_warn(ctx, "unexpected knockout end"); + return; + } + state = &dev->stack[--dev->top]; + if ((state[0].blendmode & FZ_BLEND_KNOCKOUT) == 0) + return; + + blendmode = state->blendmode & FZ_BLEND_MODEMASK; + isolated = state->blendmode & FZ_BLEND_ISOLATED; + +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top, ""); + fz_dump_blend(dev->ctx, state[1].dest, "Knockout end: blending "); + if (state[1].shape) + fz_dump_blend(dev->ctx, state[1].shape, "/"); + fz_dump_blend(dev->ctx, state[0].dest, " onto "); + if (state[0].shape) + fz_dump_blend(dev->ctx, state[0].shape, "/"); + if (blendmode != 0) + printf(" (blend %d)", blendmode); + if (isolated != 0) + printf(" (isolated)"); + printf(" (knockout)"); +#endif + if ((blendmode == 0) && (state[0].shape == state[1].shape)) + fz_paint_pixmap(state[0].dest, state[1].dest, 255); + else + fz_blend_pixmap(state[0].dest, state[1].dest, 255, blendmode, isolated, state[1].shape); + + fz_drop_pixmap(dev->ctx, state[1].dest); + if (state[0].shape != state[1].shape) + { + if (state[0].shape) + fz_paint_pixmap(state[0].shape, state[1].shape, 255); + fz_drop_pixmap(dev->ctx, state[1].shape); + } +#ifdef DUMP_GROUP_BLENDS + fz_dump_blend(dev->ctx, state[0].dest, " to get "); + if (state[0].shape) + fz_dump_blend(dev->ctx, state[0].shape, "/"); + printf("\n"); +#endif +} + +static void +fz_draw_fill_path(fz_device *devp, fz_path *path, int even_odd, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_draw_device *dev = devp->user; + float expansion = fz_matrix_expansion(ctm); + float flatness = 0.3f / expansion; + unsigned char colorbv[FZ_MAX_COLORS + 1]; + float colorfv[FZ_MAX_COLORS]; + fz_irect bbox; + int i; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; + + if (model == NULL) + model = fz_device_gray(dev->ctx); + + fz_reset_gel(dev->gel, &state->scissor); + fz_flatten_fill_path(dev->gel, path, ctm, flatness); + fz_sort_gel(dev->gel); + + fz_intersect_irect(fz_bound_gel(dev->gel, &bbox), &state->scissor); + + if (fz_is_empty_irect(&bbox)) + return; + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); + + fz_convert_color(dev->ctx, model, colorfv, colorspace, color); + for (i = 0; i < model->n; i++) + colorbv[i] = colorfv[i] * 255; + colorbv[i] = alpha * 255; + + fz_scan_convert(dev->gel, even_odd, &bbox, state->dest, colorbv); + if (state->shape) + { + fz_reset_gel(dev->gel, &state->scissor); + fz_flatten_fill_path(dev->gel, path, ctm, flatness); + fz_sort_gel(dev->gel); + + colorbv[0] = alpha * 255; + fz_scan_convert(dev->gel, even_odd, &bbox, state->shape, colorbv); + } + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); +} + +static void +fz_draw_stroke_path(fz_device *devp, fz_path *path, fz_stroke_state *stroke, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_draw_device *dev = devp->user; + float expansion = fz_matrix_expansion(ctm); + float flatness = 0.3f / expansion; + float linewidth = stroke->linewidth; + unsigned char colorbv[FZ_MAX_COLORS + 1]; + float colorfv[FZ_MAX_COLORS]; + fz_irect bbox; + int i; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; + + if (model == NULL) + model = fz_device_gray(dev->ctx); + + if (linewidth * expansion < 0.1f) + linewidth = 1 / expansion; + + fz_reset_gel(dev->gel, &state->scissor); + if (stroke->dash_len > 0) + fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth); + else + fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth); + fz_sort_gel(dev->gel); + + fz_intersect_irect(fz_bound_gel(dev->gel, &bbox), &state->scissor); + + if (fz_is_empty_irect(&bbox)) + return; + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); + + fz_convert_color(dev->ctx, model, colorfv, colorspace, color); + for (i = 0; i < model->n; i++) + colorbv[i] = colorfv[i] * 255; + colorbv[i] = alpha * 255; + + fz_scan_convert(dev->gel, 0, &bbox, state->dest, colorbv); + if (state->shape) + { + fz_reset_gel(dev->gel, &state->scissor); + if (stroke->dash_len > 0) + fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth); + else + fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth); + fz_sort_gel(dev->gel); + + colorbv[0] = 255; + fz_scan_convert(dev->gel, 0, &bbox, state->shape, colorbv); + } + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); +} + +static void +fz_draw_clip_path(fz_device *devp, fz_path *path, const fz_rect *rect, int even_odd, const fz_matrix *ctm) +{ + fz_draw_device *dev = devp->user; + float expansion = fz_matrix_expansion(ctm); + float flatness = 0.3f / expansion; + fz_irect bbox; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model; + fz_context *ctx = dev->ctx; + + fz_reset_gel(dev->gel, &state->scissor); + fz_flatten_fill_path(dev->gel, path, ctm, flatness); + fz_sort_gel(dev->gel); + + state = push_stack(dev); + model = state->dest->colorspace; + + fz_intersect_irect(fz_bound_gel(dev->gel, &bbox), &state->scissor); + if (rect) + { + fz_irect bbox2; + fz_intersect_irect(&bbox, fz_irect_from_rect(&bbox2, rect)); + } + + if (fz_is_empty_irect(&bbox) || fz_is_rect_gel(dev->gel)) + { + state[1].scissor = bbox; + state[1].mask = NULL; +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Clip (rectangular) begin\n"); +#endif + return; + } + + fz_try(ctx) + { + state[1].mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, state[1].mask); + state[1].dest = fz_new_pixmap_with_bbox(dev->ctx, model, &bbox); + fz_clear_pixmap(dev->ctx, state[1].dest); + if (state[1].shape) + { + state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, state[1].shape); + } + + fz_scan_convert(dev->gel, even_odd, &bbox, state[1].mask, NULL); + + state[1].blendmode |= FZ_BLEND_ISOLATED; + state[1].scissor = bbox; +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Clip (non-rectangular) begin\n"); +#endif + } + fz_catch(ctx) + { + emergency_pop_stack(dev, state); + } +} + +static void +fz_draw_clip_stroke_path(fz_device *devp, fz_path *path, const fz_rect *rect, fz_stroke_state *stroke, const fz_matrix *ctm) +{ + fz_draw_device *dev = devp->user; + float expansion = fz_matrix_expansion(ctm); + float flatness = 0.3f / expansion; + float linewidth = stroke->linewidth; + fz_irect bbox; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model; + fz_context *ctx = dev->ctx; + + if (linewidth * expansion < 0.1f) + linewidth = 1 / expansion; + + fz_reset_gel(dev->gel, &state->scissor); + if (stroke->dash_len > 0) + fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth); + else + fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth); + fz_sort_gel(dev->gel); + + state = push_stack(dev); + model = state->dest->colorspace; + + fz_intersect_irect(fz_bound_gel(dev->gel, &bbox), &state->scissor); + if (rect) + { + fz_irect bbox2; + fz_intersect_irect(&bbox, fz_irect_from_rect(&bbox2, rect)); + } + + fz_try(ctx) + { + state[1].mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, state[1].mask); + state[1].dest = fz_new_pixmap_with_bbox(dev->ctx, model, &bbox); + fz_clear_pixmap(dev->ctx, state[1].dest); + if (state->shape) + { + state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, state[1].shape); + } + + if (!fz_is_empty_irect(&bbox)) + fz_scan_convert(dev->gel, 0, &bbox, state[1].mask, NULL); + + state[1].blendmode |= FZ_BLEND_ISOLATED; + state[1].scissor = bbox; +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Clip (stroke) begin\n"); +#endif + } + fz_catch(ctx) + { + emergency_pop_stack(dev, state); + } +} + +static void +draw_glyph(unsigned char *colorbv, fz_pixmap *dst, fz_pixmap *msk, + int xorig, int yorig, const fz_irect *scissor) +{ + unsigned char *dp, *mp; + fz_irect bbox; + int x, y, w, h; + + fz_pixmap_bbox_no_ctx(msk, &bbox); + fz_translate_irect(&bbox, xorig, yorig); + fz_intersect_irect(&bbox, scissor); /* scissor < dst */ + x = bbox.x0; + y = bbox.y0; + w = bbox.x1 - bbox.x0; + h = bbox.y1 - bbox.y0; + + mp = msk->samples + (unsigned int)((y - msk->y - yorig) * msk->w + (x - msk->x - xorig)); + dp = dst->samples + (unsigned int)(((y - dst->y) * dst->w + (x - dst->x)) * dst->n); + + assert(msk->n == 1); + + while (h--) + { + if (dst->colorspace) + fz_paint_span_with_color(dp, mp, dst->n, w, colorbv); + else + fz_paint_span(dp, mp, 1, w, 255); + dp += dst->w * dst->n; + mp += msk->w; + } +} + +static void +fz_draw_fill_text(fz_device *devp, fz_text *text, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_draw_device *dev = devp->user; + unsigned char colorbv[FZ_MAX_COLORS + 1]; + unsigned char shapebv; + float colorfv[FZ_MAX_COLORS]; + fz_matrix tm, trm, trunc_trm; + fz_pixmap *glyph; + int i, x, y, gid; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; + fz_irect scissor; + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); + + fz_convert_color(dev->ctx, model, colorfv, colorspace, color); + for (i = 0; i < model->n; i++) + colorbv[i] = colorfv[i] * 255; + colorbv[i] = alpha * 255; + shapebv = 255; + + tm = text->trm; + + for (i = 0; i < text->len; i++) + { + gid = text->items[i].gid; + if (gid < 0) + continue; + + tm.e = text->items[i].x; + tm.f = text->items[i].y; + fz_concat(&trm, &tm, ctm); + x = floorf(trm.e); + y = floorf(trm.f); + + trunc_trm = trm; + trunc_trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX); + trunc_trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX); + + scissor.x0 = state->scissor.x0 - x; + scissor.y0 = state->scissor.y0 - y; + scissor.x1 = state->scissor.x1 - x; + scissor.y1 = state->scissor.y1 - y; + + glyph = fz_render_glyph(dev->ctx, text->font, gid, &trunc_trm, model, scissor); + if (glyph) + { + if (glyph->n == 1) + { + draw_glyph(colorbv, state->dest, glyph, x, y, &state->scissor); + if (state->shape) + draw_glyph(&shapebv, state->shape, glyph, x, y, &state->scissor); + } + else + { + fz_matrix mat = {glyph->w, 0.0, 0.0, glyph->h, x + glyph->x, y + glyph->y}; + fz_paint_image(state->dest, &state->scissor, state->shape, glyph, &mat, alpha * 255); + } + fz_drop_pixmap(dev->ctx, glyph); + } + else + { + fz_path *path = fz_outline_glyph(dev->ctx, text->font, gid, &trm); + if (path) + { + fz_draw_fill_path(devp, path, 0, &fz_identity, colorspace, color, alpha); + fz_free_path(dev->ctx, path); + } + else + { + fz_warn(dev->ctx, "cannot render glyph"); + } + } + } + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); +} + +static void +fz_draw_stroke_text(fz_device *devp, fz_text *text, fz_stroke_state *stroke, + const fz_matrix *ctm, fz_colorspace *colorspace, + float *color, float alpha) +{ + fz_draw_device *dev = devp->user; + unsigned char colorbv[FZ_MAX_COLORS + 1]; + float colorfv[FZ_MAX_COLORS]; + fz_matrix tm, trm, trunc_trm; + fz_pixmap *glyph; + int i, x, y, gid; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; + fz_irect scissor; + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); + + fz_convert_color(dev->ctx, model, colorfv, colorspace, color); + for (i = 0; i < model->n; i++) + colorbv[i] = colorfv[i] * 255; + colorbv[i] = alpha * 255; + + tm = text->trm; + + for (i = 0; i < text->len; i++) + { + gid = text->items[i].gid; + if (gid < 0) + continue; + + tm.e = text->items[i].x; + tm.f = text->items[i].y; + fz_concat(&trm, &tm, ctm); + x = floorf(trm.e); + y = floorf(trm.f); + + trunc_trm = trm; + trunc_trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX); + trunc_trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX); + + scissor.x0 = state->scissor.x0 - x; + scissor.y0 = state->scissor.y0 - y; + scissor.x1 = state->scissor.x1 - x; + scissor.y1 = state->scissor.y1 - y; + + glyph = fz_render_stroked_glyph(dev->ctx, text->font, gid, &trunc_trm, ctm, stroke, scissor); + if (glyph) + { + draw_glyph(colorbv, state->dest, glyph, x, y, &state->scissor); + if (state->shape) + draw_glyph(colorbv, state->shape, glyph, x, y, &state->scissor); + fz_drop_pixmap(dev->ctx, glyph); + } + else + { + fz_path *path = fz_outline_glyph(dev->ctx, text->font, gid, &trm); + if (path) + { + fz_draw_stroke_path(devp, path, stroke, &fz_identity, colorspace, color, alpha); + fz_free_path(dev->ctx, path); + } + else + { + fz_warn(dev->ctx, "cannot render glyph"); + } + } + } + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); +} + +static void +fz_draw_clip_text(fz_device *devp, fz_text *text, const fz_matrix *ctm, int accumulate) +{ + fz_draw_device *dev = devp->user; + fz_context *ctx = dev->ctx; + fz_irect bbox; + fz_pixmap *mask, *dest, *shape; + fz_matrix tm, trm, trunc_trm; + fz_pixmap *glyph; + int i, x, y, gid; + fz_draw_state *state; + fz_colorspace *model; + + /* If accumulate == 0 then this text object is guaranteed complete */ + /* If accumulate == 1 then this text object is the first (or only) in a sequence */ + /* If accumulate == 2 then this text object is a continuation */ + + state = push_stack(dev); + model = state->dest->colorspace; + + if (accumulate == 0) + { + /* make the mask the exact size needed */ + fz_rect rect; + + fz_irect_from_rect(&bbox, fz_bound_text(dev->ctx, text, NULL, ctm, &rect)); + fz_intersect_irect(&bbox, &state->scissor); + } + else + { + /* be conservative about the size of the mask needed */ + bbox = state->scissor; + } + + fz_try(ctx) + { + if (accumulate == 0 || accumulate == 1) + { + mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, mask); + dest = fz_new_pixmap_with_bbox(dev->ctx, model, &bbox); + fz_clear_pixmap(dev->ctx, dest); + if (state->shape) + { + shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, shape); + } + else + shape = NULL; + + state[1].blendmode |= FZ_BLEND_ISOLATED; + state[1].scissor = bbox; + state[1].dest = dest; + state[1].mask = mask; + state[1].shape = shape; +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Clip (text) begin\n"); +#endif + } + else + { + mask = state->mask; + dev->top--; + } + + if (!fz_is_empty_irect(&bbox) && mask) + { + tm = text->trm; + + for (i = 0; i < text->len; i++) + { + fz_irect scissor; + + gid = text->items[i].gid; + if (gid < 0) + continue; + + tm.e = text->items[i].x; + tm.f = text->items[i].y; + fz_concat(&trm, &tm, ctm); + x = floorf(trm.e); + y = floorf(trm.f); + + trunc_trm = trm; + trunc_trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX); + trunc_trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX); + scissor.x0 = bbox.x0 - x; + scissor.y0 = bbox.y0 - y; + scissor.x1 = bbox.x1 - x; + scissor.y1 = bbox.y1 - y; + + glyph = fz_render_glyph(dev->ctx, text->font, gid, &trunc_trm, model, scissor); + if (glyph) + { + draw_glyph(NULL, mask, glyph, x, y, &bbox); + if (state[1].shape) + draw_glyph(NULL, state[1].shape, glyph, x, y, &bbox); + fz_drop_pixmap(dev->ctx, glyph); + } + else + { + fz_path *path = fz_outline_glyph(dev->ctx, text->font, gid, &trm); + if (path) + { + fz_pixmap *old_dest; + float white = 1; + + state = &dev->stack[dev->top]; + old_dest = state[0].dest; + state[0].dest = state[0].mask; + state[0].mask = NULL; + fz_try(ctx) + { + fz_draw_fill_path(devp, path, 0, &fz_identity, fz_device_gray(ctx), &white, 1); + } + fz_always(ctx) + { + state[0].mask = state[0].dest; + state[0].dest = old_dest; + fz_free_path(dev->ctx, path); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } + } + else + { + fz_warn(dev->ctx, "cannot render glyph for clipping"); + } + } + } + } + } + fz_catch(ctx) + { + if (accumulate == 0 || accumulate == 1) + emergency_pop_stack(dev, state); + fz_rethrow(ctx); + } +} + +static void +fz_draw_clip_stroke_text(fz_device *devp, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm) +{ + fz_draw_device *dev = devp->user; + fz_context *ctx = dev->ctx; + fz_irect bbox; + fz_pixmap *mask, *dest, *shape; + fz_matrix tm, trm, trunc_trm; + fz_pixmap *glyph; + int i, x, y, gid; + fz_draw_state *state = push_stack(dev); + fz_colorspace *model = state->dest->colorspace; + fz_rect rect; + + /* make the mask the exact size needed */ + fz_irect_from_rect(&bbox, fz_bound_text(dev->ctx, text, stroke, ctm, &rect)); + fz_intersect_irect(&bbox, &state->scissor); + + fz_try(ctx) + { + state[1].mask = mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, mask); + state[1].dest = dest = fz_new_pixmap_with_bbox(dev->ctx, model, &bbox); + fz_clear_pixmap(dev->ctx, dest); + if (state->shape) + { + state[1].shape = shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, shape); + } + else + shape = state->shape; + + state[1].blendmode |= FZ_BLEND_ISOLATED; + state[1].scissor = bbox; +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Clip (stroke text) begin\n"); +#endif + + if (!fz_is_empty_irect(&bbox)) + { + tm = text->trm; + + for (i = 0; i < text->len; i++) + { + fz_irect scissor; + gid = text->items[i].gid; + if (gid < 0) + continue; + + tm.e = text->items[i].x; + tm.f = text->items[i].y; + fz_concat(&trm, &tm, ctm); + x = floorf(trm.e); + y = floorf(trm.f); + + trunc_trm = trm; + trunc_trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX); + trunc_trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX); + + scissor.x0 = bbox.x0 - x; + scissor.y0 = bbox.y0 - y; + scissor.x1 = bbox.x1 - x; + scissor.y1 = bbox.y1 - y; + + glyph = fz_render_stroked_glyph(dev->ctx, text->font, gid, &trunc_trm, ctm, stroke, scissor); + if (glyph) + { + draw_glyph(NULL, mask, glyph, x, y, &bbox); + if (shape) + draw_glyph(NULL, shape, glyph, x, y, &bbox); + fz_drop_pixmap(dev->ctx, glyph); + } + else + { + fz_path *path = fz_outline_glyph(dev->ctx, text->font, gid, &trm); + if (path) + { + fz_pixmap *old_dest; + float white = 1; + + state = &dev->stack[dev->top]; + old_dest = state[0].dest; + state[0].dest = state[0].mask; + state[0].mask = NULL; + fz_try(ctx) + { + fz_draw_stroke_path(devp, path, stroke, &fz_identity, fz_device_gray(ctx), &white, 1); + } + fz_always(ctx) + { + state[0].mask = state[0].dest; + state[0].dest = old_dest; + fz_free_path(dev->ctx, path); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } + } + else + { + fz_warn(dev->ctx, "cannot render glyph for stroked clipping"); + } + } + } + } + } + fz_catch(ctx) + { + emergency_pop_stack(dev, state); + } +} + +static void +fz_draw_ignore_text(fz_device *dev, fz_text *text, const fz_matrix *ctm) +{ +} + +static void +fz_draw_fill_shade(fz_device *devp, fz_shade *shade, const fz_matrix *ctm, float alpha) +{ + fz_draw_device *dev = devp->user; + fz_rect bounds; + fz_irect bbox, scissor; + fz_pixmap *dest, *shape; + float colorfv[FZ_MAX_COLORS]; + unsigned char colorbv[FZ_MAX_COLORS + 1]; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; + + fz_bound_shade(dev->ctx, shade, ctm, &bounds); + scissor = state->scissor; + fz_intersect_irect(fz_irect_from_rect(&bbox, &bounds), &scissor); + + if (fz_is_empty_irect(&bbox)) + return; + + if (!model) + { + fz_warn(dev->ctx, "cannot render shading directly to an alpha mask"); + return; + } + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); + + dest = state->dest; + shape = state->shape; + + if (alpha < 1) + { + dest = fz_new_pixmap_with_bbox(dev->ctx, state->dest->colorspace, &bbox); + fz_clear_pixmap(dev->ctx, dest); + if (shape) + { + shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, shape); + } + } + + if (shade->use_background) + { + unsigned char *s; + int x, y, n, i; + fz_convert_color(dev->ctx, model, colorfv, shade->colorspace, shade->background); + for (i = 0; i < model->n; i++) + colorbv[i] = colorfv[i] * 255; + colorbv[i] = 255; + + n = dest->n; + for (y = scissor.y0; y < scissor.y1; y++) + { + s = dest->samples + (unsigned int)(((scissor.x0 - dest->x) + (y - dest->y) * dest->w) * dest->n); + for (x = scissor.x0; x < scissor.x1; x++) + { + for (i = 0; i < n; i++) + *s++ = colorbv[i]; + } + } + if (shape) + { + for (y = scissor.y0; y < scissor.y1; y++) + { + s = shape->samples + (unsigned int)((scissor.x0 - shape->x) + (y - shape->y) * shape->w); + for (x = scissor.x0; x < scissor.x1; x++) + { + *s++ = 255; + } + } + } + } + + fz_paint_shade(dev->ctx, shade, ctm, dest, &bbox); + if (shape) + fz_clear_pixmap_rect_with_value(dev->ctx, shape, 255, &bbox); + + if (alpha < 1) + { + fz_paint_pixmap(state->dest, dest, alpha * 255); + fz_drop_pixmap(dev->ctx, dest); + if (shape) + { + fz_paint_pixmap(state->shape, shape, alpha * 255); + fz_drop_pixmap(dev->ctx, shape); + } + } + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); +} + +static fz_pixmap * +fz_transform_pixmap(fz_draw_device *dev, fz_pixmap *image, fz_matrix *ctm, int x, int y, int dx, int dy, int gridfit, const fz_irect *clip) +{ + fz_pixmap *scaled; + fz_context *ctx = dev->ctx; + + if (ctm->a != 0 && ctm->b == 0 && ctm->c == 0 && ctm->d != 0) + { + /* Unrotated or X-flip or Y-flip or XY-flip */ + fz_matrix m = *ctm; + if (gridfit) + fz_gridfit_matrix(&m); + scaled = fz_scale_pixmap_cached(ctx, image, m.e, m.f, m.a, m.d, clip, dev->cache_x, dev->cache_y); + if (!scaled) + return NULL; + ctm->a = scaled->w; + ctm->d = scaled->h; + ctm->e = scaled->x; + ctm->f = scaled->y; + return scaled; + } + + if (ctm->a == 0 && ctm->b != 0 && ctm->c != 0 && ctm->d == 0) + { + /* Other orthogonal flip/rotation cases */ + fz_matrix m = *ctm; + fz_irect rclip; + if (gridfit) + fz_gridfit_matrix(&m); + if (clip) + { + rclip.x0 = clip->y0; + rclip.y0 = clip->x0; + rclip.x1 = clip->y1; + rclip.y1 = clip->x1; + } + scaled = fz_scale_pixmap_cached(ctx, image, m.f, m.e, m.b, m.c, (clip ? &rclip : NULL), dev->cache_x, dev->cache_y); + if (!scaled) + return NULL; + ctm->b = scaled->w; + ctm->c = scaled->h; + ctm->f = scaled->x; + ctm->e = scaled->y; + return scaled; + } + + /* Downscale, non rectilinear case */ + if (dx > 0 && dy > 0) + { + scaled = fz_scale_pixmap_cached(ctx, image, 0, 0, (float)dx, (float)dy, NULL, dev->cache_x, dev->cache_y); + return scaled; + } + + return NULL; +} + +static void +fz_draw_fill_image(fz_device *devp, fz_image *image, const fz_matrix *ctm, float alpha) +{ + fz_draw_device *dev = devp->user; + fz_pixmap *converted = NULL; + fz_pixmap *scaled = NULL; + fz_pixmap *pixmap; + fz_pixmap *orig_pixmap; + int after; + int dx, dy; + fz_context *ctx = dev->ctx; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; + fz_irect clip; + fz_matrix local_ctm = *ctm; + + fz_intersect_irect(fz_pixmap_bbox(ctx, state->dest, &clip), &state->scissor); + + fz_var(scaled); + + if (!model) + { + fz_warn(dev->ctx, "cannot render image directly to an alpha mask"); + return; + } + + if (image->w == 0 || image->h == 0) + return; + + dx = sqrtf(local_ctm.a * local_ctm.a + local_ctm.b * local_ctm.b); + dy = sqrtf(local_ctm.c * local_ctm.c + local_ctm.d * local_ctm.d); + + pixmap = fz_image_to_pixmap(ctx, image, dx, dy); + orig_pixmap = pixmap; + + /* convert images with more components (cmyk->rgb) before scaling */ + /* convert images with fewer components (gray->rgb after scaling */ + /* convert images with expensive colorspace transforms after scaling */ + + fz_try(ctx) + { + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); + + after = 0; + if (pixmap->colorspace == fz_device_gray(ctx)) + after = 1; + + if (pixmap->colorspace != model && !after) + { + fz_irect bbox; + fz_pixmap_bbox(ctx, pixmap, &bbox); + converted = fz_new_pixmap_with_bbox(ctx, model, &bbox); + fz_convert_pixmap(ctx, converted, pixmap); + pixmap = converted; + } + + if (dx < pixmap->w && dy < pixmap->h) + { + int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); + scaled = fz_transform_pixmap(dev, pixmap, &local_ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); + if (!scaled) + { + if (dx < 1) + dx = 1; + if (dy < 1) + dy = 1; + scaled = fz_scale_pixmap_cached(ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL, dev->cache_x, dev->cache_y); + } + if (scaled) + pixmap = scaled; + } + + if (pixmap->colorspace != model) + { + if ((pixmap->colorspace == fz_device_gray(ctx) && model == fz_device_rgb(ctx)) || + (pixmap->colorspace == fz_device_gray(ctx) && model == fz_device_bgr(ctx))) + { + /* We have special case rendering code for gray -> rgb/bgr */ + } + else + { + fz_irect bbox; + fz_pixmap_bbox(ctx, pixmap, &bbox); + converted = fz_new_pixmap_with_bbox(ctx, model, &bbox); + fz_convert_pixmap(ctx, converted, pixmap); + pixmap = converted; + } + } + + fz_paint_image(state->dest, &state->scissor, state->shape, pixmap, &local_ctm, alpha * 255); + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); + } + fz_always(ctx) + { + fz_drop_pixmap(ctx, scaled); + fz_drop_pixmap(ctx, converted); + fz_drop_pixmap(ctx, orig_pixmap); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +static void +fz_draw_fill_image_mask(fz_device *devp, fz_image *image, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_draw_device *dev = devp->user; + unsigned char colorbv[FZ_MAX_COLORS + 1]; + float colorfv[FZ_MAX_COLORS]; + fz_pixmap *scaled = NULL; + fz_pixmap *pixmap; + fz_pixmap *orig_pixmap; + int dx, dy; + int i; + fz_context *ctx = dev->ctx; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; + fz_irect clip; + fz_matrix local_ctm = *ctm; + + fz_pixmap_bbox(ctx, state->dest, &clip); + fz_intersect_irect(&clip, &state->scissor); + + if (image->w == 0 || image->h == 0) + return; + + dx = sqrtf(local_ctm.a * local_ctm.a + local_ctm.b * local_ctm.b); + dy = sqrtf(local_ctm.c * local_ctm.c + local_ctm.d * local_ctm.d); + pixmap = fz_image_to_pixmap(ctx, image, dx, dy); + orig_pixmap = pixmap; + + fz_try(ctx) + { + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); + + if (dx < pixmap->w && dy < pixmap->h) + { + int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); + scaled = fz_transform_pixmap(dev, pixmap, &local_ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); + if (!scaled) + { + if (dx < 1) + dx = 1; + if (dy < 1) + dy = 1; + scaled = fz_scale_pixmap_cached(dev->ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL, dev->cache_x, dev->cache_y); + } + if (scaled) + pixmap = scaled; + } + + fz_convert_color(dev->ctx, model, colorfv, colorspace, color); + for (i = 0; i < model->n; i++) + colorbv[i] = colorfv[i] * 255; + colorbv[i] = alpha * 255; + + fz_paint_image_with_color(state->dest, &state->scissor, state->shape, pixmap, &local_ctm, colorbv); + + if (scaled) + fz_drop_pixmap(dev->ctx, scaled); + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); + } + fz_always(ctx) + { + fz_drop_pixmap(dev->ctx, orig_pixmap); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +static void +fz_draw_clip_image_mask(fz_device *devp, fz_image *image, const fz_rect *rect, const fz_matrix *ctm) +{ + fz_draw_device *dev = devp->user; + fz_context *ctx = dev->ctx; + fz_irect bbox; + fz_pixmap *mask = NULL; + fz_pixmap *dest = NULL; + fz_pixmap *shape = NULL; + fz_pixmap *scaled = NULL; + fz_pixmap *pixmap = NULL; + fz_pixmap *orig_pixmap = NULL; + int dx, dy; + fz_draw_state *state = push_stack(dev); + fz_colorspace *model = state->dest->colorspace; + fz_irect clip; + fz_matrix local_ctm = *ctm; + fz_rect urect; + + fz_pixmap_bbox(ctx, state->dest, &clip); + fz_intersect_irect(&clip, &state->scissor); + + fz_var(mask); + fz_var(dest); + fz_var(shape); + fz_var(pixmap); + fz_var(orig_pixmap); + + if (image->w == 0 || image->h == 0) + { +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Clip (image mask) (empty) begin\n"); +#endif + state[1].scissor = fz_empty_irect; + state[1].mask = NULL; + return; + } + +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Clip (image mask) begin\n"); +#endif + + urect = fz_unit_rect; + fz_irect_from_rect(&bbox, fz_transform_rect(&urect, &local_ctm)); + fz_intersect_irect(&bbox, &state->scissor); + if (rect) + { + fz_irect bbox2; + fz_intersect_irect(&bbox, fz_irect_from_rect(&bbox2, rect)); + } + + dx = sqrtf(local_ctm.a * local_ctm.a + local_ctm.b * local_ctm.b); + dy = sqrtf(local_ctm.c * local_ctm.c + local_ctm.d * local_ctm.d); + + fz_try(ctx) + { + pixmap = fz_image_to_pixmap(ctx, image, dx, dy); + orig_pixmap = pixmap; + + state[1].mask = mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, mask); + + state[1].dest = dest = fz_new_pixmap_with_bbox(dev->ctx, model, &bbox); + fz_clear_pixmap(dev->ctx, dest); + if (state->shape) + { + state[1].shape = shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, shape); + } + + state[1].blendmode |= FZ_BLEND_ISOLATED; + state[1].scissor = bbox; + + if (dx < pixmap->w && dy < pixmap->h) + { + int gridfit = !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); + scaled = fz_transform_pixmap(dev, pixmap, &local_ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); + if (!scaled) + { + if (dx < 1) + dx = 1; + if (dy < 1) + dy = 1; + scaled = fz_scale_pixmap_cached(dev->ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL, dev->cache_x, dev->cache_y); + } + if (scaled) + pixmap = scaled; + } + fz_paint_image(mask, &bbox, state->shape, pixmap, &local_ctm, 255); + } + fz_always(ctx) + { + fz_drop_pixmap(ctx, scaled); + fz_drop_pixmap(ctx, orig_pixmap); + } + fz_catch(ctx) + { + emergency_pop_stack(dev, state); + } +} + +static void +fz_draw_pop_clip(fz_device *devp) +{ + fz_draw_device *dev = devp->user; + fz_context *ctx = dev->ctx; + fz_draw_state *state; + + if (dev->top == 0) + { + fz_warn(ctx, "Unexpected pop clip"); + return; + } + state = &dev->stack[--dev->top]; + + /* We can get here with state[1].mask == NULL if the clipping actually + * resolved to a rectangle earlier. + */ + if (state[1].mask) + { +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top, ""); + fz_dump_blend(dev->ctx, state[1].dest, "Clipping "); + if (state[1].shape) + fz_dump_blend(dev->ctx, state[1].shape, "/"); + fz_dump_blend(dev->ctx, state[0].dest, " onto "); + if (state[0].shape) + fz_dump_blend(dev->ctx, state[0].shape, "/"); + fz_dump_blend(dev->ctx, state[1].mask, " with "); +#endif + fz_paint_pixmap_with_mask(state[0].dest, state[1].dest, state[1].mask); + if (state[0].shape != state[1].shape) + { + fz_paint_pixmap_with_mask(state[0].shape, state[1].shape, state[1].mask); + fz_drop_pixmap(dev->ctx, state[1].shape); + } + fz_drop_pixmap(dev->ctx, state[1].mask); + fz_drop_pixmap(dev->ctx, state[1].dest); +#ifdef DUMP_GROUP_BLENDS + fz_dump_blend(dev->ctx, state[0].dest, " to get "); + if (state[0].shape) + fz_dump_blend(dev->ctx, state[0].shape, "/"); + printf("\n"); +#endif + } + else + { +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top, "Clip end\n"); +#endif + } +} + +static void +fz_draw_begin_mask(fz_device *devp, const fz_rect *rect, int luminosity, fz_colorspace *colorspace, float *colorfv) +{ + fz_draw_device *dev = devp->user; + fz_pixmap *dest; + fz_irect bbox; + fz_draw_state *state = push_stack(dev); + fz_pixmap *shape = state->shape; + fz_context *ctx = dev->ctx; + + fz_intersect_irect(fz_irect_from_rect(&bbox, rect), &state->scissor); + + fz_try(ctx) + { + state[1].dest = dest = fz_new_pixmap_with_bbox(dev->ctx, fz_device_gray(ctx), &bbox); + if (state->shape) + { + /* FIXME: If we ever want to support AIS true, then + * we probably want to create a shape pixmap here, + * using: shape = fz_new_pixmap_with_bbox(NULL, bbox); + * then, in the end_mask code, we create the mask + * from this rather than dest. + */ + state[1].shape = shape = NULL; + } + + if (luminosity) + { + float bc; + if (!colorspace) + colorspace = fz_device_gray(ctx); + fz_convert_color(dev->ctx, fz_device_gray(ctx), &bc, colorspace, colorfv); + fz_clear_pixmap_with_value(dev->ctx, dest, bc * 255); + if (shape) + fz_clear_pixmap_with_value(dev->ctx, shape, 255); + } + else + { + fz_clear_pixmap(dev->ctx, dest); + if (shape) + fz_clear_pixmap(dev->ctx, shape); + } + +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Mask begin\n"); +#endif + state[1].scissor = bbox; + state[1].luminosity = luminosity; + } + fz_catch(ctx) + { + emergency_pop_stack(dev, state); + } +} + +static void +fz_draw_end_mask(fz_device *devp) +{ + fz_draw_device *dev = devp->user; + fz_pixmap *temp, *dest; + fz_irect bbox; + int luminosity; + fz_context *ctx = dev->ctx; + fz_draw_state *state; + + if (dev->top == 0) + { + fz_warn(ctx, "Unexpected draw_end_mask"); + return; + } + state = &dev->stack[dev->top-1]; + /* pop soft mask buffer */ + luminosity = state[1].luminosity; + +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Mask -> Clip\n"); +#endif + /* convert to alpha mask */ + temp = fz_alpha_from_gray(dev->ctx, state[1].dest, luminosity); + if (state[1].dest != state[0].dest) + fz_drop_pixmap(dev->ctx, state[1].dest); + state[1].dest = NULL; + if (state[1].shape != state[0].shape) + fz_drop_pixmap(dev->ctx, state[1].shape); + state[1].shape = NULL; + if (state[1].mask != state[0].mask) + fz_drop_pixmap(dev->ctx, state[1].mask); + state[1].mask = NULL; + + /* create new dest scratch buffer */ + fz_pixmap_bbox(ctx, temp, &bbox); + dest = fz_new_pixmap_with_bbox(dev->ctx, state->dest->colorspace, &bbox); + fz_clear_pixmap(dev->ctx, dest); + + /* push soft mask as clip mask */ + state[1].mask = temp; + state[1].dest = dest; + state[1].blendmode |= FZ_BLEND_ISOLATED; + /* If we have a shape, then it'll need to be masked with the + * clip mask when we pop. So create a new shape now. */ + if (state[0].shape) + { + state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, state[1].shape); + } + state[1].scissor = bbox; +} + +static void +fz_draw_begin_group(fz_device *devp, const fz_rect *rect, int isolated, int knockout, int blendmode, float alpha) +{ + fz_draw_device *dev = devp->user; + fz_irect bbox; + fz_pixmap *dest, *shape; + fz_context *ctx = dev->ctx; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_begin(dev); + + state = push_stack(dev); + fz_intersect_irect(fz_irect_from_rect(&bbox, rect), &state->scissor); + + fz_try(ctx) + { + state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, model, &bbox); + +#ifndef ATTEMPT_KNOCKOUT_AND_ISOLATED + knockout = 0; + isolated = 1; +#endif + + if (isolated) + { + fz_clear_pixmap(dev->ctx, dest); + } + else + { + fz_copy_pixmap_rect(dev->ctx, dest, state[0].dest, &bbox); + } + + if (blendmode == 0 && alpha == 1.0 && isolated) + { + /* We can render direct to any existing shape plane. + * If there isn't one, we don't need to make one. */ + state[1].shape =shape = state[0].shape; + } + else + { + state[1].shape = shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox); + fz_clear_pixmap(dev->ctx, shape); + } + + state[1].alpha = alpha; +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Group begin\n"); +#endif + + state[1].scissor = bbox; + state[1].blendmode = blendmode | (isolated ? FZ_BLEND_ISOLATED : 0) | (knockout ? FZ_BLEND_KNOCKOUT : 0); + } + fz_catch(ctx) + { + emergency_pop_stack(dev, state); + } +} + +static void +fz_draw_end_group(fz_device *devp) +{ + fz_draw_device *dev = devp->user; + int blendmode; + int isolated; + float alpha; + fz_context *ctx = dev->ctx; + fz_draw_state *state; + + if (dev->top == 0) + { + fz_warn(ctx, "Unexpected end_group"); + return; + } + + state = &dev->stack[--dev->top]; + alpha = state[1].alpha; + blendmode = state[1].blendmode & FZ_BLEND_MODEMASK; + isolated = state[1].blendmode & FZ_BLEND_ISOLATED; +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top, ""); + fz_dump_blend(dev->ctx, state[1].dest, "Group end: blending "); + if (state[1].shape) + fz_dump_blend(dev->ctx, state[1].shape, "/"); + fz_dump_blend(dev->ctx, state[0].dest, " onto "); + if (state[0].shape) + fz_dump_blend(dev->ctx, state[0].shape, "/"); + if (alpha != 1.0f) + printf(" (alpha %g)", alpha); + if (blendmode != 0) + printf(" (blend %d)", blendmode); + if (isolated != 0) + printf(" (isolated)"); + if (state[1].blendmode & FZ_BLEND_KNOCKOUT) + printf(" (knockout)"); +#endif + if ((blendmode == 0) && (state[0].shape == state[1].shape)) + fz_paint_pixmap(state[0].dest, state[1].dest, alpha * 255); + else + fz_blend_pixmap(state[0].dest, state[1].dest, alpha * 255, blendmode, isolated, state[1].shape); + + fz_drop_pixmap(dev->ctx, state[1].dest); + if (state[0].shape != state[1].shape) + { + if (state[0].shape) + fz_paint_pixmap(state[0].shape, state[1].shape, alpha * 255); + fz_drop_pixmap(dev->ctx, state[1].shape); + } +#ifdef DUMP_GROUP_BLENDS + fz_dump_blend(dev->ctx, state[0].dest, " to get "); + if (state[0].shape) + fz_dump_blend(dev->ctx, state[0].shape, "/"); + printf("\n"); +#endif + + if (state[0].blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); +} + +typedef struct +{ + int refs; + float ctm[4]; + int id; +} tile_key; + +typedef struct +{ + fz_storable storable; + fz_pixmap *dest; + fz_pixmap *shape; +} tile_record; + +static int +fz_make_hash_tile_key(fz_store_hash *hash, void *key_) +{ + tile_key *key = (tile_key *)key_; + + hash->u.im.id = key->id; + hash->u.im.m[0] = key->ctm[0]; + hash->u.im.m[1] = key->ctm[1]; + hash->u.im.m[2] = key->ctm[2]; + hash->u.im.m[3] = key->ctm[3]; + return 1; +} + +static void * +fz_keep_tile_key(fz_context *ctx, void *key_) +{ + tile_key *key = (tile_key *)key_; + + fz_lock(ctx, FZ_LOCK_ALLOC); + key->refs++; + fz_unlock(ctx, FZ_LOCK_ALLOC); + + return (void *)key; +} + +static void +fz_drop_tile_key(fz_context *ctx, void *key_) +{ + tile_key *key = (tile_key *)key_; + int drop; + + fz_lock(ctx, FZ_LOCK_ALLOC); + drop = --key->refs; + fz_unlock(ctx, FZ_LOCK_ALLOC); + if (drop == 0) + { + fz_free(ctx, key); + } +} + +static int +fz_cmp_tile_key(void *k0_, void *k1_) +{ + tile_key *k0 = (tile_key *)k0_; + tile_key *k1 = (tile_key *)k1_; + + return k0->id == k1->id && k0->ctm[0] == k1->ctm[0] && k0->ctm[1] == k1->ctm[1] && k0->ctm[2] == k1->ctm[2] && k0->ctm[3] == k1->ctm[3]; +} + +#ifndef NDEBUG +static void +fz_debug_tile(FILE *out, void *key_) +{ + tile_key *key = (tile_key *)key_; + + fprintf(out, "(tile id=%x, ctm=%g %g %g %g) ", key->id, key->ctm[0], key->ctm[1], key->ctm[2], key->ctm[3]); +} +#endif + +static fz_store_type fz_tile_store_type = +{ + fz_make_hash_tile_key, + fz_keep_tile_key, + fz_drop_tile_key, + fz_cmp_tile_key, +#ifndef NDEBUG + fz_debug_tile +#endif +}; + +static void +fz_free_tile_record_imp(fz_context *ctx, fz_storable *storable) +{ + tile_record *tr = (tile_record *)(void *)storable; + + if (tr == NULL) + return; + fz_drop_pixmap(ctx, tr->dest); + fz_drop_pixmap(ctx, tr->shape); + fz_free(ctx, tr); +} + +static void +fz_drop_tile_record(fz_context *ctx, tile_record *tile) +{ + fz_drop_storable(ctx, &tile->storable); +} + +static tile_record * +fz_new_tile_record(fz_context *ctx, fz_pixmap *dest, fz_pixmap *shape) +{ + tile_record *tile = fz_malloc_struct(ctx, tile_record); + FZ_INIT_STORABLE(tile, 1, fz_free_tile_record_imp); + tile->dest = fz_keep_pixmap(ctx, dest); + tile->shape = fz_keep_pixmap(ctx, shape); + return tile; +} + +unsigned int +fz_tile_size(fz_context *ctx, tile_record *tile) +{ + if (!tile) + return 0; + return sizeof(*tile) + fz_pixmap_size(ctx, tile->dest) + fz_pixmap_size(ctx, tile->shape); +} + +static int +fz_draw_begin_tile(fz_device *devp, const fz_rect *area, const fz_rect *view, float xstep, float ystep, const fz_matrix *ctm, int id) +{ + fz_draw_device *dev = devp->user; + fz_pixmap *dest = NULL; + fz_pixmap *shape; + fz_irect bbox; + fz_context *ctx = dev->ctx; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; + fz_rect local_view = *view; + + /* area, view, xstep, ystep are in pattern space */ + /* ctm maps from pattern space to device space */ + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_begin(dev); + + state = push_stack(dev); + fz_irect_from_rect(&bbox, fz_transform_rect(&local_view, ctm)); + /* We should never have a bbox that entirely covers our destination. + * If we do, then the check for only 1 tile being visible above has + * failed. Actually, this *can* fail due to the round_rect, at extreme + * resolutions, so disable this assert. + * assert(bbox.x0 > state->dest->x || bbox.x1 < state->dest->x + state->dest->w || + * bbox.y0 > state->dest->y || bbox.y1 < state->dest->y + state->dest->h); + */ + + /* Check to see if we have one cached */ + if (id) + { + tile_key tk; + tile_record *tile; + tk.ctm[0] = ctm->a; + tk.ctm[1] = ctm->b; + tk.ctm[2] = ctm->c; + tk.ctm[3] = ctm->d; + tk.id = id; + + tile = fz_find_item(ctx, fz_free_tile_record_imp, &tk, &fz_tile_store_type); + if (tile) + { + state[1].dest = fz_keep_pixmap(ctx, tile->dest); + state[1].shape = fz_keep_pixmap(ctx, tile->shape); + state[1].blendmode |= FZ_BLEND_ISOLATED; + state[1].xstep = xstep; + state[1].ystep = ystep; + state[1].id = id; + fz_irect_from_rect(&state[1].area, area); + state[1].ctm = *ctm; +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Tile begin (cached)\n"); +#endif + + state[1].scissor = bbox; + fz_drop_tile_record(ctx, tile); + return 1; + } + } + + fz_try(ctx) + { + state[1].dest = dest = fz_new_pixmap_with_bbox(dev->ctx, model, &bbox); + fz_clear_pixmap(ctx, dest); + shape = state[0].shape; + if (shape) + { + state[1].shape = shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, &bbox); + fz_clear_pixmap(ctx, shape); + } + state[1].blendmode |= FZ_BLEND_ISOLATED; + state[1].xstep = xstep; + state[1].ystep = ystep; + state[1].id = id; + fz_irect_from_rect(&state[1].area, area); + state[1].ctm = *ctm; +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Tile begin\n"); +#endif + + state[1].scissor = bbox; + } + fz_catch(ctx) + { + emergency_pop_stack(dev, state); + } + + return 0; +} + +static void +fz_draw_end_tile(fz_device *devp) +{ + fz_draw_device *dev = devp->user; + float xstep, ystep; + fz_matrix ttm, ctm, shapectm; + fz_irect area, scissor; + fz_rect scissor_tmp; + int x0, y0, x1, y1, x, y; + fz_context *ctx = dev->ctx; + fz_draw_state *state; + tile_record *tile; + tile_key *key; + + if (dev->top == 0) + { + fz_warn(ctx, "Unexpected end_tile"); + return; + } + + state = &dev->stack[--dev->top]; + xstep = state[1].xstep; + ystep = state[1].ystep; + area = state[1].area; + ctm = state[1].ctm; + + /* Fudge the scissor bbox a little to allow for inaccuracies in the + * matrix inversion. */ + fz_rect_from_irect(&scissor_tmp, &state[0].scissor); + fz_transform_rect(fz_expand_rect(&scissor_tmp, 1), fz_invert_matrix(&ttm, &ctm)); + fz_intersect_irect(&area, fz_irect_from_rect(&scissor, &scissor_tmp)); + + /* FIXME: area is a bbox, so FP not appropriate here */ + /* In PDF files xstep/ystep can be smaller than view (the area of a + * single tile) (see fts_15_1506.pdf for an example). This means that + * we have to bias the left hand/bottom edge calculations by the + * difference between the step and the width/height of the tile. */ + /* state[0].scissor = view, transformed by ctm */ + x0 = floorf((area.x0 + xstep - state[0].scissor.x1 + state[0].scissor.x0) / xstep); + y0 = floorf((area.y0 + ystep - state[0].scissor.y1 + state[0].scissor.y0) / ystep); + x1 = ceilf(area.x1 / xstep); + y1 = ceilf(area.y1 / ystep); + + ctm.e = state[1].dest->x; + ctm.f = state[1].dest->y; + if (state[1].shape) + { + shapectm = ctm; + shapectm.e = state[1].shape->x; + shapectm.f = state[1].shape->y; + } + +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top, ""); + fz_dump_blend(dev->ctx, state[1].dest, "Tiling "); + if (state[1].shape) + fz_dump_blend(dev->ctx, state[1].shape, "/"); + fz_dump_blend(dev->ctx, state[0].dest, " onto "); + if (state[0].shape) + fz_dump_blend(dev->ctx, state[0].shape, "/"); +#endif + + for (y = y0; y < y1; y++) + { + for (x = x0; x < x1; x++) + { + ttm = ctm; + fz_pre_translate(&ttm, x * xstep, y * ystep); + state[1].dest->x = ttm.e; + state[1].dest->y = ttm.f; + if (state[1].dest->x > 0 && state[1].dest->x + state[1].dest->w < 0) + continue; + if (state[1].dest->y > 0 && state[1].dest->y + state[1].dest->h < 0) + continue; + fz_paint_pixmap_with_bbox(state[0].dest, state[1].dest, 255, state[0].scissor); + if (state[1].shape) + { + ttm = shapectm; + fz_pre_translate(&ttm, x * xstep, y * ystep); + state[1].shape->x = ttm.e; + state[1].shape->y = ttm.f; + fz_paint_pixmap_with_bbox(state[0].shape, state[1].shape, 255, state[0].scissor); + } + } + } + + state[1].dest->x = ctm.e; + state[1].dest->y = ctm.f; + if (state[1].shape) + { + state[1].shape->x = shapectm.e; + state[1].shape->y = shapectm.f; + } + + /* Now we try to cache the tiles. Any failure here will just result + * in us not caching. */ + tile = NULL; + key = NULL; + fz_var(tile); + fz_var(key); + fz_try(ctx) + { + tile_record *existing_tile; + + tile = fz_new_tile_record(ctx, state[1].dest, state[1].shape); + + key = fz_malloc_struct(ctx, tile_key); + key->refs = 1; + key->id = state[1].id; + key->ctm[0] = ctm.a; + key->ctm[1] = ctm.b; + key->ctm[2] = ctm.c; + key->ctm[3] = ctm.d; + existing_tile = fz_store_item(ctx, key, tile, fz_tile_size(ctx, tile), &fz_tile_store_type); + if (existing_tile) + { + /* We already have a tile. This will either have been + * produced by a racing thread, or there is already + * an entry for this one in the store. */ + fz_drop_tile_record(ctx, tile); + tile = existing_tile; + } + } + fz_always(ctx) + { + fz_drop_tile_key(ctx, key); + fz_drop_tile_record(ctx, tile); + } + fz_catch(ctx) + { + /* Do nothing */ + } + + fz_drop_pixmap(dev->ctx, state[1].dest); + fz_drop_pixmap(dev->ctx, state[1].shape); +#ifdef DUMP_GROUP_BLENDS + fz_dump_blend(dev->ctx, state[0].dest, " to get "); + if (state[0].shape) + fz_dump_blend(dev->ctx, state[0].shape, "/"); + printf("\n"); +#endif + + if (state->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); +} + +static void +fz_draw_free_user(fz_device *devp) +{ + fz_draw_device *dev = devp->user; + fz_context *ctx = dev->ctx; + /* pop and free the stacks */ + if (dev->top > 0) + fz_warn(ctx, "items left on stack in draw device: %d", dev->top+1); + + while(dev->top-- > 0) + { + fz_draw_state *state = &dev->stack[dev->top]; + if (state[1].mask != state[0].mask) + fz_drop_pixmap(ctx, state[1].mask); + if (state[1].dest != state[0].dest) + fz_drop_pixmap(ctx, state[1].dest); + if (state[1].shape != state[0].shape) + fz_drop_pixmap(ctx, state[1].shape); + } + /* We never free the dest/mask/shape at level 0, as: + * 1) dest is passed in and ownership remains with the caller. + * 2) shape and mask are NULL at level 0. + */ + if (dev->stack != &dev->init_stack[0]) + fz_free(ctx, dev->stack); + fz_free_scale_cache(ctx, dev->cache_x); + fz_free_scale_cache(ctx, dev->cache_y); + fz_free_gel(dev->gel); + fz_free(ctx, dev); +} + +fz_device * +fz_new_draw_device(fz_context *ctx, fz_pixmap *dest) +{ + fz_device *dev = NULL; + fz_draw_device *ddev = fz_malloc_struct(ctx, fz_draw_device); + + fz_var(dev); + fz_try(ctx) + { + ddev->gel = fz_new_gel(ctx); + ddev->flags = 0; + ddev->ctx = ctx; + ddev->top = 0; + ddev->cache_x = fz_new_scale_cache(ctx); + ddev->cache_y = fz_new_scale_cache(ctx); + ddev->stack = &ddev->init_stack[0]; + ddev->stack_max = STACK_SIZE; + ddev->stack[0].dest = dest; + ddev->stack[0].shape = NULL; + ddev->stack[0].mask = NULL; + ddev->stack[0].blendmode = 0; + ddev->stack[0].scissor.x0 = dest->x; + ddev->stack[0].scissor.y0 = dest->y; + ddev->stack[0].scissor.x1 = dest->x + dest->w; + ddev->stack[0].scissor.y1 = dest->y + dest->h; + + dev = fz_new_device(ctx, ddev); + } + fz_catch(ctx) + { + fz_free_scale_cache(ctx, ddev->cache_x); + fz_free_scale_cache(ctx, ddev->cache_y); + fz_free_gel(ddev->gel); + fz_free(ctx, ddev); + fz_rethrow(ctx); + } + dev->free_user = fz_draw_free_user; + + dev->fill_path = fz_draw_fill_path; + dev->stroke_path = fz_draw_stroke_path; + dev->clip_path = fz_draw_clip_path; + dev->clip_stroke_path = fz_draw_clip_stroke_path; + + dev->fill_text = fz_draw_fill_text; + dev->stroke_text = fz_draw_stroke_text; + dev->clip_text = fz_draw_clip_text; + dev->clip_stroke_text = fz_draw_clip_stroke_text; + dev->ignore_text = fz_draw_ignore_text; + + dev->fill_image_mask = fz_draw_fill_image_mask; + dev->clip_image_mask = fz_draw_clip_image_mask; + dev->fill_image = fz_draw_fill_image; + dev->fill_shade = fz_draw_fill_shade; + + dev->pop_clip = fz_draw_pop_clip; + + dev->begin_mask = fz_draw_begin_mask; + dev->end_mask = fz_draw_end_mask; + dev->begin_group = fz_draw_begin_group; + dev->end_group = fz_draw_end_group; + + dev->begin_tile = fz_draw_begin_tile; + dev->end_tile = fz_draw_end_tile; + + return dev; +} + +fz_device * +fz_new_draw_device_with_bbox(fz_context *ctx, fz_pixmap *dest, const fz_irect *clip) +{ + fz_device *dev = fz_new_draw_device(ctx, dest); + fz_draw_device *ddev = dev->user; + + if (clip->x0 > ddev->stack[0].scissor.x0) + ddev->stack[0].scissor.x0 = clip->x0; + if (clip->x1 < ddev->stack[0].scissor.x1) + ddev->stack[0].scissor.x1 = clip->x1; + if (clip->y0 > ddev->stack[0].scissor.y0) + ddev->stack[0].scissor.y0 = clip->y0; + if (clip->y1 < ddev->stack[0].scissor.y1) + ddev->stack[0].scissor.y1 = clip->y1; + return dev; +} + +fz_device * +fz_new_draw_device_type3(fz_context *ctx, fz_pixmap *dest) +{ + fz_device *dev = fz_new_draw_device(ctx, dest); + fz_draw_device *ddev = dev->user; + ddev->flags |= FZ_DRAWDEV_FLAGS_TYPE3; + return dev; +} + +fz_irect * +fz_bound_path_accurate(fz_context *ctx, fz_irect *bbox, const fz_irect *scissor, fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth) +{ + fz_gel *gel = fz_new_gel(ctx); + + fz_reset_gel(gel, scissor); + if (stroke) + { + if (stroke->dash_len > 0) + fz_flatten_dash_path(gel, path, stroke, ctm, flatness, linewidth); + else + fz_flatten_stroke_path(gel, path, stroke, ctm, flatness, linewidth); + } + else + fz_flatten_fill_path(gel, path, ctm, flatness); + fz_bound_gel(gel, bbox); + fz_free_gel(gel); + + return bbox; +} diff --git a/source/fitz/draw-edge.c b/source/fitz/draw-edge.c new file mode 100644 index 00000000..5f2f45d3 --- /dev/null +++ b/source/fitz/draw-edge.c @@ -0,0 +1,972 @@ +#include "mupdf/fitz.h" +#include "draw-imp.h" + +#define BBOX_MIN -(1<<20) +#define BBOX_MAX (1<<20) + +/* divide and floor towards -inf */ +static inline int fz_idiv(int a, int b) +{ + return a < 0 ? (a - b + 1) / b : a / b; +} + +/* If AA_BITS is defined, then we assume constant N bits of antialiasing. We + * will attempt to provide at least that number of bits of accuracy in the + * antialiasing (to a maximum of 8). If it is defined to be 0 then no + * antialiasing is done. If it is undefined to we will leave the antialiasing + * accuracy as a run time choice. + */ +struct fz_aa_context_s +{ + int hscale; + int vscale; + int scale; + int bits; +}; + +void fz_new_aa_context(fz_context *ctx) +{ +#ifndef AA_BITS + ctx->aa = fz_malloc_struct(ctx, fz_aa_context); + ctx->aa->hscale = 17; + ctx->aa->vscale = 15; + ctx->aa->scale = 256; + ctx->aa->bits = 8; + +#define fz_aa_hscale ((ctxaa)->hscale) +#define fz_aa_vscale ((ctxaa)->vscale) +#define fz_aa_scale ((ctxaa)->scale) +#define fz_aa_bits ((ctxaa)->bits) +#define AA_SCALE(x) ((x * fz_aa_scale) >> 8) + +#endif +} + +void fz_copy_aa_context(fz_context *dst, fz_context *src) +{ + if (dst && dst->aa && src && src->aa) + memcpy(dst->aa, src->aa, sizeof(*src->aa)); +} + +void fz_free_aa_context(fz_context *ctx) +{ +#ifndef AA_BITS + fz_free(ctx, ctx->aa); + ctx->aa = NULL; +#endif +} + +#ifdef AA_BITS + +#if AA_BITS > 6 +#define AA_SCALE(x) (x) +#define fz_aa_hscale 17 +#define fz_aa_vscale 15 +#define fz_aa_bits 8 + +#elif AA_BITS > 4 +#define AA_SCALE(x) ((x * 255) >> 6) +#define fz_aa_hscale 8 +#define fz_aa_vscale 8 +#define fz_aa_bits 6 + +#elif AA_BITS > 2 +#define AA_SCALE(x) (x * 17) +#define fz_aa_hscale 5 +#define fz_aa_vscale 3 +#define fz_aa_bits 4 + +#elif AA_BITS > 0 +#define AA_SCALE(x) ((x * 255) >> 2) +#define fz_aa_hscale 2 +#define fz_aa_vscale 2 +#define fz_aa_bits 2 + +#else +#define AA_SCALE(x) (x * 255) +#define fz_aa_hscale 1 +#define fz_aa_vscale 1 +#define fz_aa_bits 0 + +#endif +#endif + +int +fz_aa_level(fz_context *ctx) +{ + fz_aa_context *ctxaa = ctx->aa; + return fz_aa_bits; +} + +void +fz_set_aa_level(fz_context *ctx, int level) +{ + fz_aa_context *ctxaa = ctx->aa; +#ifdef AA_BITS + fz_warn(ctx, "anti-aliasing was compiled with a fixed precision of %d bits", fz_aa_bits); +#else + if (level > 6) + { + fz_aa_hscale = 17; + fz_aa_vscale = 15; + fz_aa_bits = 8; + } + else if (level > 4) + { + fz_aa_hscale = 8; + fz_aa_vscale = 8; + fz_aa_bits = 6; + } + else if (level > 2) + { + fz_aa_hscale = 5; + fz_aa_vscale = 3; + fz_aa_bits = 4; + } + else if (level > 0) + { + fz_aa_hscale = 2; + fz_aa_vscale = 2; + fz_aa_bits = 2; + } + else + { + fz_aa_hscale = 1; + fz_aa_vscale = 1; + fz_aa_bits = 0; + } + fz_aa_scale = 0xFF00 / (fz_aa_hscale * fz_aa_vscale); +#endif +} + +/* + * Global Edge List -- list of straight path segments for scan conversion + * + * Stepping along the edges is with Bresenham's line algorithm. + * + * See Mike Abrash -- Graphics Programming Black Book (notably chapter 40) + */ + +typedef struct fz_edge_s fz_edge; + +struct fz_edge_s +{ + int x, e, h, y; + int adj_up, adj_down; + int xmove; + int xdir, ydir; /* -1 or +1 */ +}; + +struct fz_gel_s +{ + fz_rect clip; + fz_rect bbox; + int cap, len; + fz_edge *edges; + int acap, alen; + fz_edge **active; + fz_context *ctx; +}; + +fz_gel * +fz_new_gel(fz_context *ctx) +{ + fz_gel *gel; + + gel = fz_malloc_struct(ctx, fz_gel); + fz_try(ctx) + { + gel->edges = NULL; + gel->ctx = ctx; + gel->cap = 512; + gel->len = 0; + gel->edges = fz_malloc_array(ctx, gel->cap, sizeof(fz_edge)); + + gel->clip.x0 = gel->clip.y0 = BBOX_MAX; + gel->clip.x1 = gel->clip.y1 = BBOX_MIN; + + gel->bbox.x0 = gel->bbox.y0 = BBOX_MAX; + gel->bbox.x1 = gel->bbox.y1 = BBOX_MIN; + + gel->acap = 64; + gel->alen = 0; + gel->active = fz_malloc_array(ctx, gel->acap, sizeof(fz_edge*)); + } + fz_catch(ctx) + { + if (gel) + fz_free(ctx, gel->edges); + fz_free(ctx, gel); + fz_rethrow(ctx); + } + + return gel; +} + +void +fz_reset_gel(fz_gel *gel, const fz_irect *clip) +{ + fz_aa_context *ctxaa = gel->ctx->aa; + + if (fz_is_infinite_irect(clip)) + { + gel->clip.x0 = gel->clip.y0 = BBOX_MAX; + gel->clip.x1 = gel->clip.y1 = BBOX_MIN; + } + else { + gel->clip.x0 = clip->x0 * fz_aa_hscale; + gel->clip.x1 = clip->x1 * fz_aa_hscale; + gel->clip.y0 = clip->y0 * fz_aa_vscale; + gel->clip.y1 = clip->y1 * fz_aa_vscale; + } + + gel->bbox.x0 = gel->bbox.y0 = BBOX_MAX; + gel->bbox.x1 = gel->bbox.y1 = BBOX_MIN; + + gel->len = 0; +} + +void +fz_free_gel(fz_gel *gel) +{ + if (gel == NULL) + return; + fz_free(gel->ctx, gel->active); + fz_free(gel->ctx, gel->edges); + fz_free(gel->ctx, gel); +} + +fz_irect * +fz_bound_gel(const fz_gel *gel, fz_irect *bbox) +{ + fz_aa_context *ctxaa = gel->ctx->aa; + if (gel->len == 0) + { + *bbox = fz_empty_irect; + } + else + { + bbox->x0 = fz_idiv(gel->bbox.x0, fz_aa_hscale); + bbox->y0 = fz_idiv(gel->bbox.y0, fz_aa_vscale); + bbox->x1 = fz_idiv(gel->bbox.x1, fz_aa_hscale) + 1; + bbox->y1 = fz_idiv(gel->bbox.y1, fz_aa_vscale) + 1; + } + return bbox; +} + +enum { INSIDE, OUTSIDE, LEAVE, ENTER }; + +#define clip_lerp_y(v,m,x0,y0,x1,y1,t) clip_lerp_x(v,m,y0,x0,y1,x1,t) + +static int +clip_lerp_x(int val, int m, int x0, int y0, int x1, int y1, int *out) +{ + int v0out = m ? x0 > val : x0 < val; + int v1out = m ? x1 > val : x1 < val; + + if (v0out + v1out == 0) + return INSIDE; + + if (v0out + v1out == 2) + return OUTSIDE; + + if (v1out) + { + *out = y0 + (int)(((float)(y1 - y0)) * (val - x0) / (x1 - x0)); + return LEAVE; + } + + else + { + *out = y1 + (int)(((float)(y0 - y1)) * (val - x1) / (x0 - x1)); + return ENTER; + } +} + +static void +fz_insert_gel_raw(fz_gel *gel, int x0, int y0, int x1, int y1) +{ + fz_edge *edge; + int dx, dy; + int winding; + int width; + int tmp; + + if (y0 == y1) + return; + + if (y0 > y1) { + winding = -1; + tmp = x0; x0 = x1; x1 = tmp; + tmp = y0; y0 = y1; y1 = tmp; + } + else + winding = 1; + + if (x0 < gel->bbox.x0) gel->bbox.x0 = x0; + if (x0 > gel->bbox.x1) gel->bbox.x1 = x0; + if (x1 < gel->bbox.x0) gel->bbox.x0 = x1; + if (x1 > gel->bbox.x1) gel->bbox.x1 = x1; + + if (y0 < gel->bbox.y0) gel->bbox.y0 = y0; + if (y1 > gel->bbox.y1) gel->bbox.y1 = y1; + + if (gel->len + 1 == gel->cap) { + int new_cap = gel->cap + 512; + gel->edges = fz_resize_array(gel->ctx, gel->edges, new_cap, sizeof(fz_edge)); + gel->cap = new_cap; + } + + edge = &gel->edges[gel->len++]; + + dy = y1 - y0; + dx = x1 - x0; + width = fz_absi(dx); + + edge->xdir = dx > 0 ? 1 : -1; + edge->ydir = winding; + edge->x = x0; + edge->y = y0; + edge->h = dy; + edge->adj_down = dy; + + /* initial error term going l->r and r->l */ + if (dx >= 0) + edge->e = 0; + else + edge->e = -dy + 1; + + /* y-major edge */ + if (dy >= width) { + edge->xmove = 0; + edge->adj_up = width; + } + + /* x-major edge */ + else { + edge->xmove = (width / dy) * edge->xdir; + edge->adj_up = width % dy; + } +} + +void +fz_insert_gel(fz_gel *gel, float fx0, float fy0, float fx1, float fy1) +{ + int x0, y0, x1, y1; + int d, v; + fz_aa_context *ctxaa = gel->ctx->aa; + + fx0 = floorf(fx0 * fz_aa_hscale); + fx1 = floorf(fx1 * fz_aa_hscale); + fy0 = floorf(fy0 * fz_aa_vscale); + fy1 = floorf(fy1 * fz_aa_vscale); + + /* Call fz_clamp so that clamping is done in the float domain, THEN + * cast down to an int. Calling fz_clampi causes problems due to the + * implicit cast down from float to int of the first argument + * over/underflowing and flipping sign at extreme values. */ + x0 = (int)fz_clamp(fx0, BBOX_MIN * fz_aa_hscale, BBOX_MAX * fz_aa_hscale); + y0 = (int)fz_clamp(fy0, BBOX_MIN * fz_aa_vscale, BBOX_MAX * fz_aa_vscale); + x1 = (int)fz_clamp(fx1, BBOX_MIN * fz_aa_hscale, BBOX_MAX * fz_aa_hscale); + y1 = (int)fz_clamp(fy1, BBOX_MIN * fz_aa_vscale, BBOX_MAX * fz_aa_vscale); + + d = clip_lerp_y(gel->clip.y0, 0, x0, y0, x1, y1, &v); + if (d == OUTSIDE) return; + if (d == LEAVE) { y1 = gel->clip.y0; x1 = v; } + if (d == ENTER) { y0 = gel->clip.y0; x0 = v; } + + d = clip_lerp_y(gel->clip.y1, 1, x0, y0, x1, y1, &v); + if (d == OUTSIDE) return; + if (d == LEAVE) { y1 = gel->clip.y1; x1 = v; } + if (d == ENTER) { y0 = gel->clip.y1; x0 = v; } + + d = clip_lerp_x(gel->clip.x0, 0, x0, y0, x1, y1, &v); + if (d == OUTSIDE) { + x0 = x1 = gel->clip.x0; + } + if (d == LEAVE) { + fz_insert_gel_raw(gel, gel->clip.x0, v, gel->clip.x0, y1); + x1 = gel->clip.x0; + y1 = v; + } + if (d == ENTER) { + fz_insert_gel_raw(gel, gel->clip.x0, y0, gel->clip.x0, v); + x0 = gel->clip.x0; + y0 = v; + } + + d = clip_lerp_x(gel->clip.x1, 1, x0, y0, x1, y1, &v); + if (d == OUTSIDE) { + x0 = x1 = gel->clip.x1; + } + if (d == LEAVE) { + fz_insert_gel_raw(gel, gel->clip.x1, v, gel->clip.x1, y1); + x1 = gel->clip.x1; + y1 = v; + } + if (d == ENTER) { + fz_insert_gel_raw(gel, gel->clip.x1, y0, gel->clip.x1, v); + x0 = gel->clip.x1; + y0 = v; + } + + fz_insert_gel_raw(gel, x0, y0, x1, y1); +} + +void +fz_sort_gel(fz_gel *gel) +{ + fz_edge *a = gel->edges; + int n = gel->len; + + int h, i, k; + fz_edge t; + + h = 1; + if (n < 14) { + h = 1; + } + else { + while (h < n) + h = 3 * h + 1; + h /= 3; + h /= 3; + } + + while (h > 0) + { + for (i = 0; i < n; i++) { + t = a[i]; + k = i - h; + /* TODO: sort on y major, x minor */ + while (k >= 0 && a[k].y > t.y) { + a[k + h] = a[k]; + k -= h; + } + a[k + h] = t; + } + + h /= 3; + } +} + +int +fz_is_rect_gel(fz_gel *gel) +{ + /* a rectangular path is converted into two vertical edges of identical height */ + if (gel->len == 2) + { + fz_edge *a = gel->edges + 0; + fz_edge *b = gel->edges + 1; + return a->y == b->y && a->h == b->h && + a->xmove == 0 && a->adj_up == 0 && + b->xmove == 0 && b->adj_up == 0; + } + return 0; +} + +/* + * Active Edge List -- keep track of active edges while sweeping + */ + +static void +sort_active(fz_edge **a, int n) +{ + int h, i, k; + fz_edge *t; + + h = 1; + if (n < 14) { + h = 1; + } + else { + while (h < n) + h = 3 * h + 1; + h /= 3; + h /= 3; + } + + while (h > 0) + { + for (i = 0; i < n; i++) { + t = a[i]; + k = i - h; + while (k >= 0 && a[k]->x > t->x) { + a[k + h] = a[k]; + k -= h; + } + a[k + h] = t; + } + + h /= 3; + } +} + +static int +insert_active(fz_gel *gel, int y, int *e_) +{ + int h_min = INT_MAX; + int e = *e_; + + /* insert edges that start here */ + if (e < gel->len && gel->edges[e].y == y) + { + do { + if (gel->alen + 1 == gel->acap) { + int newcap = gel->acap + 64; + fz_edge **newactive = fz_resize_array(gel->ctx, gel->active, newcap, sizeof(fz_edge*)); + gel->active = newactive; + gel->acap = newcap; + } + gel->active[gel->alen++] = &gel->edges[e++]; + } while (e < gel->len && gel->edges[e].y == y); + *e_ = e; + } + + if (e < gel->len) + h_min = gel->edges[e].y - y; + + for (e=0; e < gel->alen; e++) + { + if (gel->active[e]->xmove != 0 || gel->active[e]->adj_up != 0) + { + h_min = 1; + break; + } + if (gel->active[e]->h < h_min) + { + h_min = gel->active[e]->h; + if (h_min == 1) + break; + } + } + + /* shell-sort the edges by increasing x */ + sort_active(gel->active, gel->alen); + + return h_min; +} + +static void +advance_active(fz_gel *gel, int inc) +{ + fz_edge *edge; + int i = 0; + + while (i < gel->alen) + { + edge = gel->active[i]; + + edge->h -= inc; + + /* terminator! */ + if (edge->h == 0) { + gel->active[i] = gel->active[--gel->alen]; + } + + else { + edge->x += edge->xmove; + edge->e += edge->adj_up; + if (edge->e > 0) { + edge->x += edge->xdir; + edge->e -= edge->adj_down; + } + i ++; + } + } +} + +/* + * Anti-aliased scan conversion. + */ + +static inline void add_span_aa(fz_aa_context *ctxaa, int *list, int x0, int x1, int xofs, int h) +{ + int x0pix, x0sub; + int x1pix, x1sub; + + if (x0 == x1) + return; + + /* x between 0 and width of bbox */ + x0 -= xofs; + x1 -= xofs; + + /* The cast to unsigned below helps the compiler produce faster + * code on ARMs as the multiply by reciprocal trick it uses does not + * need to correct for signedness. */ + x0pix = ((unsigned int)x0) / fz_aa_hscale; + x0sub = ((unsigned int)x0) % fz_aa_hscale; + x1pix = ((unsigned int)x1) / fz_aa_hscale; + x1sub = ((unsigned int)x1) % fz_aa_hscale; + + if (x0pix == x1pix) + { + list[x0pix] += h*(x1sub - x0sub); + list[x0pix+1] += h*(x0sub - x1sub); + } + + else + { + list[x0pix] += h*(fz_aa_hscale - x0sub); + list[x0pix+1] += h*x0sub; + list[x1pix] += h*(x1sub - fz_aa_hscale); + list[x1pix+1] += h*-x1sub; + } +} + +static inline void non_zero_winding_aa(fz_gel *gel, int *list, int xofs, int h) +{ + int winding = 0; + int x = 0; + int i; + fz_aa_context *ctxaa = gel->ctx->aa; + + for (i = 0; i < gel->alen; i++) + { + if (!winding && (winding + gel->active[i]->ydir)) + x = gel->active[i]->x; + if (winding && !(winding + gel->active[i]->ydir)) + add_span_aa(ctxaa, list, x, gel->active[i]->x, xofs, h); + winding += gel->active[i]->ydir; + } +} + +static inline void even_odd_aa(fz_gel *gel, int *list, int xofs, int h) +{ + int even = 0; + int x = 0; + int i; + fz_aa_context *ctxaa = gel->ctx->aa; + + for (i = 0; i < gel->alen; i++) + { + if (!even) + x = gel->active[i]->x; + else + add_span_aa(ctxaa, list, x, gel->active[i]->x, xofs, h); + even = !even; + } +} + +static inline void undelta_aa(fz_aa_context *ctxaa, unsigned char * restrict out, int * restrict in, int n) +{ + int d = 0; + while (n--) + { + d += *in++; + *out++ = AA_SCALE(d); + } +} + +static inline void blit_aa(fz_pixmap *dst, int x, int y, + unsigned char *mp, int w, unsigned char *color) +{ + unsigned char *dp; + dp = dst->samples + (unsigned int)(( (y - dst->y) * dst->w + (x - dst->x) ) * dst->n); + if (color) + fz_paint_span_with_color(dp, mp, dst->n, w, color); + else + fz_paint_span(dp, mp, 1, w, 255); +} + +static void +fz_scan_convert_aa(fz_gel *gel, int eofill, const fz_irect *clip, + fz_pixmap *dst, unsigned char *color) +{ + unsigned char *alphas; + int *deltas; + int y, e; + int yd, yc; + fz_context *ctx = gel->ctx; + fz_aa_context *ctxaa = ctx->aa; + int height, h0, rh; + + int xmin = fz_idiv(gel->bbox.x0, fz_aa_hscale); + int xmax = fz_idiv(gel->bbox.x1, fz_aa_hscale) + 1; + + int xofs = xmin * fz_aa_hscale; + + int skipx = clip->x0 - xmin; + int clipn = clip->x1 - clip->x0; + + if (gel->len == 0) + return; + + assert(clip->x0 >= xmin); + assert(clip->x1 <= xmax); + + alphas = fz_malloc_no_throw(ctx, xmax - xmin + 1); + deltas = fz_malloc_no_throw(ctx, (xmax - xmin + 1) * sizeof(int)); + if (alphas == NULL || deltas == NULL) + { + fz_free(ctx, alphas); + fz_free(ctx, deltas); + fz_throw(ctx, FZ_ERROR_GENERIC, "scan conversion failed (malloc failure)"); + } + memset(deltas, 0, (xmax - xmin + 1) * sizeof(int)); + gel->alen = 0; + + /* The theory here is that we have a list of the edges (gel) of length + * gel->len. We have an initially empty list of 'active' edges (of + * length gel->alen). As we increase y, we move any edge that is + * active at this point into the active list. We know that any edge + * before index 'e' is either active, or has been retired. + * Once the length of the active list is 0, and e has reached gel->len + * we know we are finished. + * + * As we move through the list, we group fz_aa_vscale 'sub scanlines' + * into single scanlines, and we blit them. + */ + + e = 0; + y = gel->edges[0].y; + yd = fz_idiv(y, fz_aa_vscale); + + /* Quickly skip to the start of the clip region */ + while (yd < clip->y0 && (gel->alen > 0 || e < gel->len)) + { + /* rh = remaining height = number of subscanlines left to be + * inserted into the current scanline, which will be plotted + * at yd. */ + rh = (yd+1)*fz_aa_vscale - y; + + /* height = The number of subscanlines with identical edge + * positions (i.e. 1 if we have any non vertical edges). */ + height = insert_active(gel, y, &e); + h0 = height; + if (h0 >= rh) + { + /* We have enough subscanlines to skip to the next + * scanline. */ + h0 -= rh; + yd++; + } + /* Skip any whole scanlines we can */ + while (yd < clip->y0 && h0 >= fz_aa_vscale) + { + h0 -= fz_aa_vscale; + yd++; + } + /* If we haven't hit the start of the clip region, then we + * have less than a scanline left. */ + if (yd < clip->y0) + { + h0 = 0; + } + height -= h0; + advance_active(gel, height); + + y += height; + } + + /* Now do the active lines */ + while (gel->alen > 0 || e < gel->len) + { + yc = fz_idiv(y, fz_aa_vscale); /* yc = current scanline */ + /* rh = remaining height = number of subscanlines left to be + * inserted into the current scanline, which will be plotted + * at yd. */ + rh = (yc+1)*fz_aa_vscale - y; + if (yc != yd) + { + undelta_aa(ctxaa, alphas, deltas, skipx + clipn); + blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color); + memset(deltas, 0, (skipx + clipn) * sizeof(int)); + } + yd = yc; + if (yd >= clip->y1) + break; + + /* height = The number of subscanlines with identical edge + * positions (i.e. 1 if we have any non vertical edges). */ + height = insert_active(gel, y, &e); + h0 = height; + if (h0 > rh) + { + if (rh < fz_aa_vscale) + { + /* We have to finish a scanline off, and we + * have more sub scanlines than will fit into + * it. */ + if (eofill) + even_odd_aa(gel, deltas, xofs, rh); + else + non_zero_winding_aa(gel, deltas, xofs, rh); + undelta_aa(ctxaa, alphas, deltas, skipx + clipn); + blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color); + memset(deltas, 0, (skipx + clipn) * sizeof(int)); + yd++; + if (yd >= clip->y1) + break; + h0 -= rh; + } + if (h0 > fz_aa_vscale) + { + /* Calculate the deltas for any completely full + * scanlines. */ + h0 -= fz_aa_vscale; + if (eofill) + even_odd_aa(gel, deltas, xofs, fz_aa_vscale); + else + non_zero_winding_aa(gel, deltas, xofs, fz_aa_vscale); + undelta_aa(ctxaa, alphas, deltas, skipx + clipn); + do + { + /* Do any successive whole scanlines - no need + * to recalculate deltas here. */ + blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color); + yd++; + if (yd >= clip->y1) + goto clip_ended; + h0 -= fz_aa_vscale; + } + while (h0 > 0); + /* If we have exactly one full scanline left + * to go, then the deltas/alphas are set up + * already. */ + if (h0 == 0) + goto advance; + memset(deltas, 0, (skipx + clipn) * sizeof(int)); + h0 += fz_aa_vscale; + } + } + if (eofill) + even_odd_aa(gel, deltas, xofs, h0); + else + non_zero_winding_aa(gel, deltas, xofs, h0); +advance: + advance_active(gel, height); + + y += height; + } + + if (yd < clip->y1) + { + undelta_aa(ctxaa, alphas, deltas, skipx + clipn); + blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color); + } +clip_ended: + fz_free(ctx, deltas); + fz_free(ctx, alphas); +} + +/* + * Sharp (not anti-aliased) scan conversion + */ + +static inline void blit_sharp(int x0, int x1, int y, + const fz_irect *clip, fz_pixmap *dst, unsigned char *color) +{ + unsigned char *dp; + x0 = fz_clampi(x0, dst->x, dst->x + dst->w); + x1 = fz_clampi(x1, dst->x, dst->x + dst->w); + if (x0 < x1) + { + dp = dst->samples + (unsigned int)(( (y - dst->y) * dst->w + (x0 - dst->x) ) * dst->n); + if (color) + fz_paint_solid_color(dp, dst->n, x1 - x0, color); + else + fz_paint_solid_alpha(dp, x1 - x0, 255); + } +} + +static inline void non_zero_winding_sharp(fz_gel *gel, int y, + const fz_irect *clip, fz_pixmap *dst, unsigned char *color) +{ + int winding = 0; + int x = 0; + int i; + for (i = 0; i < gel->alen; i++) + { + if (!winding && (winding + gel->active[i]->ydir)) + x = gel->active[i]->x; + if (winding && !(winding + gel->active[i]->ydir)) + blit_sharp(x, gel->active[i]->x, y, clip, dst, color); + winding += gel->active[i]->ydir; + } +} + +static inline void even_odd_sharp(fz_gel *gel, int y, + const fz_irect *clip, fz_pixmap *dst, unsigned char *color) +{ + int even = 0; + int x = 0; + int i; + for (i = 0; i < gel->alen; i++) + { + if (!even) + x = gel->active[i]->x; + else + blit_sharp(x, gel->active[i]->x, y, clip, dst, color); + even = !even; + } +} + +static void +fz_scan_convert_sharp(fz_gel *gel, int eofill, const fz_irect *clip, + fz_pixmap *dst, unsigned char *color) +{ + int e = 0; + int y = gel->edges[0].y; + int height; + + gel->alen = 0; + + /* Skip any lines before the clip region */ + if (y < clip->y0) + { + while (gel->alen > 0 || e < gel->len) + { + height = insert_active(gel, y, &e); + y += height; + if (y >= clip->y0) + { + height -= y - clip->y0; + y = clip->y0; + break; + } + } + } + + /* Now process as lines within the clip region */ + while (gel->alen > 0 || e < gel->len) + { + height = insert_active(gel, y, &e); + + if (gel->alen == 0) + y += height; + else + { + int h; + if (height >= clip->y1 - y) + height = clip->y1 - y; + + h = height; + while (h--) + { + if (eofill) + even_odd_sharp(gel, y, clip, dst, color); + else + non_zero_winding_sharp(gel, y, clip, dst, color); + y++; + } + } + if (y >= clip->y1) + break; + + advance_active(gel, height); + } +} + +void +fz_scan_convert(fz_gel *gel, int eofill, const fz_irect *clip, + fz_pixmap *dst, unsigned char *color) +{ + fz_aa_context *ctxaa = gel->ctx->aa; + + if (fz_aa_bits > 0) + fz_scan_convert_aa(gel, eofill, clip, dst, color); + else + fz_scan_convert_sharp(gel, eofill, clip, dst, color); +} diff --git a/source/fitz/draw-glyph.c b/source/fitz/draw-glyph.c new file mode 100644 index 00000000..b0a52949 --- /dev/null +++ b/source/fitz/draw-glyph.c @@ -0,0 +1,241 @@ +#include "mupdf/fitz.h" +#include "draw-imp.h" + +#define MAX_GLYPH_SIZE 256 +#define MAX_CACHE_SIZE (1024*1024) + +typedef struct fz_glyph_key_s fz_glyph_key; + +struct fz_glyph_cache_s +{ + int refs; + fz_hash_table *hash; + int total; +}; + +struct fz_glyph_key_s +{ + fz_font *font; + int a, b; + int c, d; + unsigned short gid; + unsigned char e, f; + int aa; +}; + +void +fz_new_glyph_cache_context(fz_context *ctx) +{ + fz_glyph_cache *cache; + + cache = fz_malloc_struct(ctx, fz_glyph_cache); + fz_try(ctx) + { + cache->hash = fz_new_hash_table(ctx, 509, sizeof(fz_glyph_key), FZ_LOCK_GLYPHCACHE); + } + fz_catch(ctx) + { + fz_free(ctx, cache); + fz_rethrow(ctx); + } + cache->total = 0; + cache->refs = 1; + + ctx->glyph_cache = cache; +} + +/* The glyph cache lock is always held when this function is called. */ +static void +fz_evict_glyph_cache(fz_context *ctx) +{ + fz_glyph_cache *cache = ctx->glyph_cache; + fz_glyph_key *key; + fz_pixmap *pixmap; + int i; + + for (i = 0; i < fz_hash_len(ctx, cache->hash); i++) + { + key = fz_hash_get_key(ctx, cache->hash, i); + if (key->font) + fz_drop_font(ctx, key->font); + pixmap = fz_hash_get_val(ctx, cache->hash, i); + if (pixmap) + fz_drop_pixmap(ctx, pixmap); + } + + cache->total = 0; + + fz_empty_hash(ctx, cache->hash); +} + +void +fz_drop_glyph_cache_context(fz_context *ctx) +{ + if (!ctx->glyph_cache) + return; + + fz_lock(ctx, FZ_LOCK_GLYPHCACHE); + ctx->glyph_cache->refs--; + if (ctx->glyph_cache->refs == 0) + { + fz_evict_glyph_cache(ctx); + fz_free_hash(ctx, ctx->glyph_cache->hash); + fz_free(ctx, ctx->glyph_cache); + ctx->glyph_cache = NULL; + } + fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); +} + +fz_glyph_cache * +fz_keep_glyph_cache(fz_context *ctx) +{ + fz_lock(ctx, FZ_LOCK_GLYPHCACHE); + ctx->glyph_cache->refs++; + fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); + return ctx->glyph_cache; +} + +fz_pixmap * +fz_render_stroked_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, const fz_matrix *ctm, fz_stroke_state *stroke, fz_irect scissor) +{ + if (font->ft_face) + { + if (stroke->dash_len > 0) + return NULL; + return fz_render_ft_stroked_glyph(ctx, font, gid, trm, ctm, stroke); + } + return fz_render_glyph(ctx, font, gid, trm, NULL, scissor); +} + +/* + Render a glyph and return a bitmap. + If the glyph is too large to fit the cache we have two choices: + 1) Return NULL so the caller can draw the glyph using an outline. + Only supported for freetype fonts. + 2) Render a clipped glyph by using the scissor rectangle. + Only supported for type 3 fonts. + This must not be inserted into the cache. + */ +fz_pixmap * +fz_render_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *ctm, fz_colorspace *model, fz_irect scissor) +{ + fz_glyph_cache *cache; + fz_glyph_key key; + fz_pixmap *val; + float size = fz_matrix_expansion(ctm); + int do_cache, locked, caching; + fz_matrix local_ctm = *ctm; + + fz_var(locked); + fz_var(caching); + + if (size <= MAX_GLYPH_SIZE) + { + scissor = fz_infinite_irect; + do_cache = 1; + } + else + { + if (font->ft_face) + return NULL; + do_cache = 0; + } + + cache = ctx->glyph_cache; + + memset(&key, 0, sizeof key); + key.font = font; + key.gid = gid; + key.a = local_ctm.a * 65536; + key.b = local_ctm.b * 65536; + key.c = local_ctm.c * 65536; + key.d = local_ctm.d * 65536; + key.e = (local_ctm.e - floorf(local_ctm.e)) * 256; + key.f = (local_ctm.f - floorf(local_ctm.f)) * 256; + key.aa = fz_aa_level(ctx); + + local_ctm.e = floorf(local_ctm.e) + key.e / 256.0f; + local_ctm.f = floorf(local_ctm.f) + key.f / 256.0f; + + fz_lock(ctx, FZ_LOCK_GLYPHCACHE); + val = fz_hash_find(ctx, cache->hash, &key); + if (val) + { + fz_keep_pixmap(ctx, val); + fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); + return val; + } + + locked = 1; + caching = 0; + + fz_try(ctx) + { + if (font->ft_face) + { + val = fz_render_ft_glyph(ctx, font, gid, &local_ctm, key.aa); + } + else if (font->t3procs) + { + /* We drop the glyphcache here, and execute the t3 + * glyph code. The danger here is that some other + * thread will come along, and want the same glyph + * too. If it does, we may both end up rendering + * pixmaps. We cope with this later on, by ensuring + * that only one gets inserted into the cache. If + * we insert ours to find one already there, we + * abandon ours, and use the one there already. + */ + fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); + locked = 0; + val = fz_render_t3_glyph(ctx, font, gid, &local_ctm, model, scissor); + fz_lock(ctx, FZ_LOCK_GLYPHCACHE); + locked = 1; + } + else + { + fz_warn(ctx, "assert: uninitialized font structure"); + val = NULL; + } + if (val && do_cache) + { + if (val->w < MAX_GLYPH_SIZE && val->h < MAX_GLYPH_SIZE) + { + fz_pixmap *pix; + + /* If we throw an exception whilst caching, + * just ignore the exception and carry on. */ + caching = 1; + if (cache->total + val->w * val->h > MAX_CACHE_SIZE) + fz_evict_glyph_cache(ctx); + + pix = fz_hash_insert(ctx, cache->hash, &key, val); + if (pix) + { + fz_drop_pixmap(ctx, val); + val = pix; + } + else + { + fz_keep_font(ctx, key.font); + cache->total += val->w * val->h; + } + val = fz_keep_pixmap(ctx, val); + } + } + } + fz_always(ctx) + { + if (locked) + fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); + } + fz_catch(ctx) + { + if (caching) + fz_warn(ctx, "cannot encache glyph; continuing"); + else + fz_rethrow(ctx); + } + + return val; +} diff --git a/source/fitz/draw-imp.h b/source/fitz/draw-imp.h new file mode 100644 index 00000000..ca3f2c81 --- /dev/null +++ b/source/fitz/draw-imp.h @@ -0,0 +1,46 @@ +#ifndef MUPDF_DRAW_IMP_H +#define MUPDF_DRAW_IMP_H + +/* + * Scan converter + */ + +typedef struct fz_gel_s fz_gel; + +fz_gel *fz_new_gel(fz_context *ctx); +void fz_insert_gel(fz_gel *gel, float x0, float y0, float x1, float y1); +void fz_reset_gel(fz_gel *gel, const fz_irect *clip); +void fz_sort_gel(fz_gel *gel); +fz_irect *fz_bound_gel(const fz_gel *gel, fz_irect *bbox); +void fz_free_gel(fz_gel *gel); +int fz_is_rect_gel(fz_gel *gel); + +void fz_scan_convert(fz_gel *gel, int eofill, const fz_irect *clip, fz_pixmap *pix, unsigned char *colorbv); + +void fz_flatten_fill_path(fz_gel *gel, fz_path *path, const fz_matrix *ctm, float flatness); +void fz_flatten_stroke_path(fz_gel *gel, fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth); +void fz_flatten_dash_path(fz_gel *gel, fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth); + +fz_irect *fz_bound_path_accurate(fz_context *ctx, fz_irect *bbox, const fz_irect *scissor, fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth); + +/* + * Plotting functions. + */ + +void fz_paint_solid_alpha(unsigned char * restrict dp, int w, int alpha); +void fz_paint_solid_color(unsigned char * restrict dp, int n, int w, unsigned char *color); + +void fz_paint_span(unsigned char * restrict dp, unsigned char * restrict sp, int n, int w, int alpha); +void fz_paint_span_with_color(unsigned char * restrict dp, unsigned char * restrict mp, int n, int w, unsigned char *color); + +void fz_paint_image(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, int alpha); +void fz_paint_image_with_color(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, unsigned char *colorbv); + +void fz_paint_pixmap(fz_pixmap *dst, fz_pixmap *src, int alpha); +void fz_paint_pixmap_with_mask(fz_pixmap *dst, fz_pixmap *src, fz_pixmap *msk); +void fz_paint_pixmap_with_bbox(fz_pixmap *dst, fz_pixmap *src, int alpha, fz_irect bbox); + +void fz_blend_pixmap(fz_pixmap *dst, fz_pixmap *src, int alpha, int blendmode, int isolated, fz_pixmap *shape); +void fz_blend_pixel(unsigned char dp[3], unsigned char bp[3], unsigned char sp[3], int blendmode); + +#endif diff --git a/source/fitz/draw-mesh.c b/source/fitz/draw-mesh.c new file mode 100644 index 00000000..c0ad45d2 --- /dev/null +++ b/source/fitz/draw-mesh.c @@ -0,0 +1,273 @@ +#include "mupdf/fitz.h" +#include "draw-imp.h" + +enum { MAXN = 2 + FZ_MAX_COLORS }; + +static void paint_scan(fz_pixmap *restrict pix, int y, int fx0, int fx1, int cx0, int cx1, const int *restrict v0, const int *restrict v1, int n) +{ + unsigned char *p; + int c[MAXN], dc[MAXN]; + int k, w; + float div, mul; + int x0, x1; + + /* Ensure that fx0 is left edge, and fx1 is right */ + if (fx0 > fx1) + { + const int *v; + int t = fx0; fx0 = fx1; fx1 = t; + v = v0; v0 = v1; v1 = v; + } + else if (fx0 == fx1) + return; + + /* Clip fx0, fx1 to range */ + if (fx0 >= cx1) + return; + if (fx1 <= cx0) + return; + x0 = (fx0 > cx0 ? fx0 : cx0); + x1 = (fx1 < cx1 ? fx1 : cx1); + + w = x1 - x0; + if (w == 0) + return; + + div = 1.0f / (fx1 - fx0); + mul = (x0 - fx0); + for (k = 0; k < n; k++) + { + dc[k] = (v1[k] - v0[k]) * div; + c[k] = v0[k] + dc[k] * mul; + } + + p = pix->samples + ((x0 - pix->x) + (y - pix->y) * pix->w) * pix->n; + while (w--) + { + for (k = 0; k < n; k++) + { + *p++ = c[k]>>16; + c[k] += dc[k]; + } + *p++ = 255; + } +} + +typedef struct edge_data_s edge_data; + +struct edge_data_s +{ + float x; + float dx; + int v[2*MAXN]; +}; + +static inline void prepare_edge(const float *restrict vtop, const float *restrict vbot, edge_data *restrict edge, float y, int n) +{ + float r = 1.0f / (vbot[1] - vtop[1]); + float t = (y - vtop[1]) * r; + float diff = vbot[0] - vtop[0]; + int i; + + edge->x = vtop[0] + diff * t; + edge->dx = diff * r; + + for (i = 0; i < n; i++) + { + diff = vbot[i+2] - vtop[i+2]; + edge->v[i] = (int)(65536.0f * (vtop[i+2] + diff * t)); + edge->v[i+MAXN] = (int)(65536.0f * diff * r); + } +} + +static inline void step_edge(edge_data *edge, int n) +{ + int i; + + edge->x += edge->dx; + + for (i = 0; i < n; i++) + { + edge->v[i] += edge->v[i + MAXN]; + } +} + +static void +fz_paint_triangle(fz_pixmap *pix, float v[3][MAXN], int n, const fz_irect *bbox) +{ + edge_data e0, e1; + int top, mid, bot; + float y, y1; + int minx, maxx; + + top = bot = 0; + if (v[1][1] < v[0][1]) top = 1; else bot = 1; + if (v[2][1] < v[top][1]) top = 2; + else if (v[2][1] > v[bot][1]) bot = 2; + if (v[top][1] == v[bot][1]) return; + + /* Test if the triangle is completely outside the scissor rect */ + if (v[bot][1] < bbox->y0) return; + if (v[top][1] > bbox->y1) return; + + /* Magic! Ensure that mid/top/bot are all different */ + mid = 3^top^bot; + + assert(top != bot && top != mid && mid != bot); + + minx = fz_maxi(bbox->x0, pix->x); + maxx = fz_mini(bbox->x1, pix->x + pix->w); + + y = ceilf(fz_max(bbox->y0, v[top][1])); + y1 = ceilf(fz_min(bbox->y1, v[mid][1])); + + n -= 2; + prepare_edge(v[top], v[bot], &e0, y, n); + if (y < y1) + { + prepare_edge(v[top], v[mid], &e1, y, n); + + do + { + paint_scan(pix, y, (int)e0.x, (int)e1.x, minx, maxx, &e0.v[0], &e1.v[0], n); + step_edge(&e0, n); + step_edge(&e1, n); + y ++; + } + while (y < y1); + } + + y1 = ceilf(fz_min(bbox->y1, v[bot][1])); + if (y < y1) + { + prepare_edge(v[mid], v[bot], &e1, y, n); + + do + { + paint_scan(pix, y, (int)e0.x, (int)e1.x, minx, maxx, &e0.v[0], &e1.v[0], n); + y ++; + if (y >= y1) + break; + step_edge(&e0, n); + step_edge(&e1, n); + } + while (1); + } +} + +struct paint_tri_data +{ + fz_context *ctx; + fz_shade *shade; + fz_pixmap *dest; + const fz_irect *bbox; +}; + +static void +do_paint_tri(void *arg, fz_vertex *av, fz_vertex *bv, fz_vertex *cv) +{ + struct paint_tri_data *ptd = (struct paint_tri_data *)arg; + int i, k; + fz_vertex *vertices[3]; + fz_vertex *v; + float *ltri; + fz_context *ctx; + fz_shade *shade; + fz_pixmap *dest; + float local[3][MAXN]; + + vertices[0] = av; + vertices[1] = bv; + vertices[2] = cv; + + dest = ptd->dest; + ctx = ptd->ctx; + shade = ptd->shade; + for (k = 0; k < 3; k++) + { + v = vertices[k]; + ltri = &local[k][0]; + ltri[0] = v->p.x; + ltri[1] = v->p.y; + if (shade->use_function) + ltri[2] = v->c[0] * 255; + else + { + fz_convert_color(ctx, dest->colorspace, <ri[2], shade->colorspace, v->c); + for (i = 0; i < dest->colorspace->n; i++) + ltri[i + 2] *= 255; + } + } + fz_paint_triangle(dest, local, 2 + dest->colorspace->n, ptd->bbox); +} + +void +fz_paint_shade(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_pixmap *dest, const fz_irect *bbox) +{ + unsigned char clut[256][FZ_MAX_COLORS]; + fz_pixmap *temp = NULL; + fz_pixmap *conv = NULL; + float color[FZ_MAX_COLORS]; + struct paint_tri_data ptd; + int i, k; + fz_matrix local_ctm; + + fz_var(temp); + fz_var(conv); + + fz_try(ctx) + { + fz_concat(&local_ctm, &shade->matrix, ctm); + + if (shade->use_function) + { + fz_color_converter cc; + fz_lookup_color_converter(&cc, ctx, dest->colorspace, shade->colorspace); + for (i = 0; i < 256; i++) + { + cc.convert(&cc, color, shade->function[i]); + for (k = 0; k < dest->colorspace->n; k++) + clut[i][k] = color[k] * 255; + clut[i][k] = shade->function[i][shade->colorspace->n] * 255; + } + conv = fz_new_pixmap_with_bbox(ctx, dest->colorspace, bbox); + temp = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx), bbox); + fz_clear_pixmap(ctx, temp); + } + else + { + temp = dest; + } + + ptd.ctx = ctx; + ptd.dest = temp; + ptd.shade = shade; + ptd.bbox = bbox; + + fz_process_mesh(ctx, shade, &local_ctm, &do_paint_tri, &ptd); + + if (shade->use_function) + { + unsigned char *s = temp->samples; + unsigned char *d = conv->samples; + int len = temp->w * temp->h; + while (len--) + { + int v = *s++; + int a = fz_mul255(*s++, clut[v][conv->n - 1]); + for (k = 0; k < conv->n - 1; k++) + *d++ = fz_mul255(clut[v][k], a); + *d++ = a; + } + fz_paint_pixmap(dest, conv, 255); + fz_drop_pixmap(ctx, conv); + fz_drop_pixmap(ctx, temp); + } + } + fz_catch(ctx) + { + fz_drop_pixmap(ctx, conv); + fz_drop_pixmap(ctx, temp); + fz_rethrow(ctx); + } +} diff --git a/source/fitz/draw-paint.c b/source/fitz/draw-paint.c new file mode 100644 index 00000000..e7cfbdb4 --- /dev/null +++ b/source/fitz/draw-paint.c @@ -0,0 +1,479 @@ +#include "mupdf/fitz.h" +#include "draw-imp.h" + +/* + +The functions in this file implement various flavours of Porter-Duff blending. + +We take the following as definitions: + + Cx = Color (from plane x) + ax = Alpha (from plane x) + cx = Cx.ax = Premultiplied color (from plane x) + +The general PorterDuff blending equation is: + + Blend Z = X op Y cz = Fx.cx + Fy. cy where Fx and Fy depend on op + +The two operations we use in this file are: '(X in Y) over Z' and +'S over Z'. The definitions of the 'over' and 'in' operations are as +follows: + + For S over Z, Fs = 1, Fz = 1-as + For X in Y, Fx = ay, Fy = 0 + +We have 2 choices; we can either work with premultiplied data, or non +premultiplied data. Our + +First the premultiplied case: + + Let S = (X in Y) + Let R = (X in Y) over Z = S over Z + + cs = cx.Fx + cy.Fy (where Fx = ay, Fy = 0) + = cx.ay + as = ax.Fx + ay.Fy + = ax.ay + + cr = cs.Fs + cz.Fz (where Fs = 1, Fz = 1-as) + = cs + cz.(1-as) + = cx.ay + cz.(1-ax.ay) + ar = as.Fs + az.Fz + = as + az.(1-as) + = ax.ay + az.(1-ax.ay) + +This has various nice properties, like not needing any divisions, and +being symmetric in color and alpha, so this is what we use. Because we +went through the pain of deriving the non premultiplied forms, we list +them here too, though they are not used. + +Non Pre-multiplied case: + + Cs.as = Fx.Cx.ax + Fy.Cy.ay (where Fx = ay, Fy = 0) + = Cx.ay.ax + Cs = (Cx.ay.ax)/(ay.ax) + = Cx + Cr.ar = Fs.Cs.as + Fz.Cz.az (where Fs = 1, Fz = 1-as) + = Cs.as + (1-as).Cz.az + = Cx.ax.ay + Cz.az.(1-ax.ay) + Cr = (Cx.ax.ay + Cz.az.(1-ax.ay))/(ax.ay + az.(1-ax-ay)) + +Much more complex, it seems. However, if we could restrict ourselves to +the case where we were always plotting onto an opaque background (i.e. +az = 1), then: + + Cr = Cx.(ax.ay) + Cz.(1-ax.ay) + = (Cx-Cz)*(1-ax.ay) + Cz (a single MLA operation) + ar = 1 + +Sadly, this is not true in the general case, so we abandon this effort +and stick to using the premultiplied form. + +*/ + +typedef unsigned char byte; + +/* These are used by the non-aa scan converter */ + +void +fz_paint_solid_alpha(byte * restrict dp, int w, int alpha) +{ + int t = FZ_EXPAND(255 - alpha); + while (w--) + { + *dp = alpha + FZ_COMBINE(*dp, t); + dp ++; + } +} + +void +fz_paint_solid_color(byte * restrict dp, int n, int w, byte *color) +{ + int n1 = n - 1; + int sa = FZ_EXPAND(color[n1]); + int k; + while (w--) + { + int ma = FZ_COMBINE(FZ_EXPAND(255), sa); + for (k = 0; k < n1; k++) + dp[k] = FZ_BLEND(color[k], dp[k], ma); + dp[k] = FZ_BLEND(255, dp[k], ma); + dp += n; + } +} + +/* Blend a non-premultiplied color in mask over destination */ + +static inline void +fz_paint_span_with_color_2(byte * restrict dp, byte * restrict mp, int w, byte *color) +{ + int sa = FZ_EXPAND(color[1]); + int g = color[0]; + while (w--) + { + int ma = *mp++; + ma = FZ_COMBINE(FZ_EXPAND(ma), sa); + dp[0] = FZ_BLEND(g, dp[0], ma); + dp[1] = FZ_BLEND(255, dp[1], ma); + dp += 2; + } +} + +static inline void +fz_paint_span_with_color_4(byte * restrict dp, byte * restrict mp, int w, byte *color) +{ + int sa = FZ_EXPAND(color[3]); + int r = color[0]; + int g = color[1]; + int b = color[2]; + while (w--) + { + int ma = *mp++; + ma = FZ_COMBINE(FZ_EXPAND(ma), sa); + dp[0] = FZ_BLEND(r, dp[0], ma); + dp[1] = FZ_BLEND(g, dp[1], ma); + dp[2] = FZ_BLEND(b, dp[2], ma); + dp[3] = FZ_BLEND(255, dp[3], ma); + dp += 4; + } +} + +static inline void +fz_paint_span_with_color_N(byte * restrict dp, byte * restrict mp, int n, int w, byte *color) +{ + int n1 = n - 1; + int sa = FZ_EXPAND(color[n1]); + int k; + while (w--) + { + int ma = *mp++; + ma = FZ_COMBINE(FZ_EXPAND(ma), sa); + for (k = 0; k < n1; k++) + dp[k] = FZ_BLEND(color[k], dp[k], ma); + dp[k] = FZ_BLEND(255, dp[k], ma); + dp += n; + } +} + +void +fz_paint_span_with_color(byte * restrict dp, byte * restrict mp, int n, int w, byte *color) +{ + switch (n) + { + case 2: fz_paint_span_with_color_2(dp, mp, w, color); break; + case 4: fz_paint_span_with_color_4(dp, mp, w, color); break; + default: fz_paint_span_with_color_N(dp, mp, n, w, color); break; + } +} + +/* Blend source in mask over destination */ + +static inline void +fz_paint_span_with_mask_2(byte * restrict dp, byte * restrict sp, byte * restrict mp, int w) +{ + while (w--) + { + int masa; + int ma = *mp++; + ma = FZ_EXPAND(ma); + masa = FZ_COMBINE(sp[1], ma); + masa = 255 - masa; + masa = FZ_EXPAND(masa); + *dp = FZ_COMBINE2(*sp, ma, *dp, masa); + sp++; dp++; + *dp = FZ_COMBINE2(*sp, ma, *dp, masa); + sp++; dp++; + } +} + +static inline void +fz_paint_span_with_mask_4(byte * restrict dp, byte * restrict sp, byte * restrict mp, int w) +{ + while (w--) + { + int masa; + int ma = *mp++; + ma = FZ_EXPAND(ma); + masa = FZ_COMBINE(sp[3], ma); + masa = 255 - masa; + masa = FZ_EXPAND(masa); + *dp = FZ_COMBINE2(*sp, ma, *dp, masa); + sp++; dp++; + *dp = FZ_COMBINE2(*sp, ma, *dp, masa); + sp++; dp++; + *dp = FZ_COMBINE2(*sp, ma, *dp, masa); + sp++; dp++; + *dp = FZ_COMBINE2(*sp, ma, *dp, masa); + sp++; dp++; + } +} + +static inline void +fz_paint_span_with_mask_N(byte * restrict dp, byte * restrict sp, byte * restrict mp, int n, int w) +{ + while (w--) + { + int k = n; + int masa; + int ma = *mp++; + ma = FZ_EXPAND(ma); + masa = FZ_COMBINE(sp[n-1], ma); + masa = 255-masa; + masa = FZ_EXPAND(masa); + while (k--) + { + *dp = FZ_COMBINE2(*sp, ma, *dp, masa); + sp++; dp++; + } + } +} + +static void +fz_paint_span_with_mask(byte * restrict dp, byte * restrict sp, byte * restrict mp, int n, int w) +{ + switch (n) + { + case 2: fz_paint_span_with_mask_2(dp, sp, mp, w); break; + case 4: fz_paint_span_with_mask_4(dp, sp, mp, w); break; + default: fz_paint_span_with_mask_N(dp, sp, mp, n, w); break; + } +} + +/* Blend source in constant alpha over destination */ + +static inline void +fz_paint_span_2_with_alpha(byte * restrict dp, byte * restrict sp, int w, int alpha) +{ + alpha = FZ_EXPAND(alpha); + while (w--) + { + int masa = FZ_COMBINE(sp[1], alpha); + *dp = FZ_BLEND(*sp, *dp, masa); + dp++; sp++; + *dp = FZ_BLEND(*sp, *dp, masa); + dp++; sp++; + } +} + +static inline void +fz_paint_span_4_with_alpha(byte * restrict dp, byte * restrict sp, int w, int alpha) +{ + alpha = FZ_EXPAND(alpha); + while (w--) + { + int masa = FZ_COMBINE(sp[3], alpha); + *dp = FZ_BLEND(*sp, *dp, masa); + sp++; dp++; + *dp = FZ_BLEND(*sp, *dp, masa); + sp++; dp++; + *dp = FZ_BLEND(*sp, *dp, masa); + sp++; dp++; + *dp = FZ_BLEND(*sp, *dp, masa); + sp++; dp++; + } +} + +static inline void +fz_paint_span_N_with_alpha(byte * restrict dp, byte * restrict sp, int n, int w, int alpha) +{ + alpha = FZ_EXPAND(alpha); + while (w--) + { + int masa = FZ_COMBINE(sp[n-1], alpha); + int k = n; + while (k--) + { + *dp = FZ_BLEND(*sp++, *dp, masa); + dp++; + } + } +} + +/* Blend source over destination */ + +static inline void +fz_paint_span_1(byte * restrict dp, byte * restrict sp, int w) +{ + while (w--) + { + int t = FZ_EXPAND(255 - sp[0]); + *dp = *sp++ + FZ_COMBINE(*dp, t); + dp ++; + } +} + +static inline void +fz_paint_span_2(byte * restrict dp, byte * restrict sp, int w) +{ + while (w--) + { + int t = FZ_EXPAND(255 - sp[1]); + *dp = *sp++ + FZ_COMBINE(*dp, t); + dp++; + *dp = *sp++ + FZ_COMBINE(*dp, t); + dp++; + } +} + +static inline void +fz_paint_span_4(byte * restrict dp, byte * restrict sp, int w) +{ + while (w--) + { + int t = FZ_EXPAND(255 - sp[3]); + *dp = *sp++ + FZ_COMBINE(*dp, t); + dp++; + *dp = *sp++ + FZ_COMBINE(*dp, t); + dp++; + *dp = *sp++ + FZ_COMBINE(*dp, t); + dp++; + *dp = *sp++ + FZ_COMBINE(*dp, t); + dp++; + } +} + +static inline void +fz_paint_span_N(byte * restrict dp, byte * restrict sp, int n, int w) +{ + while (w--) + { + int k = n; + int t = FZ_EXPAND(255 - sp[n-1]); + while (k--) + { + *dp = *sp++ + FZ_COMBINE(*dp, t); + dp++; + } + } +} + +void +fz_paint_span(byte * restrict dp, byte * restrict sp, int n, int w, int alpha) +{ + if (alpha == 255) + { + switch (n) + { + case 1: fz_paint_span_1(dp, sp, w); break; + case 2: fz_paint_span_2(dp, sp, w); break; + case 4: fz_paint_span_4(dp, sp, w); break; + default: fz_paint_span_N(dp, sp, n, w); break; + } + } + else if (alpha > 0) + { + switch (n) + { + case 2: fz_paint_span_2_with_alpha(dp, sp, w, alpha); break; + case 4: fz_paint_span_4_with_alpha(dp, sp, w, alpha); break; + default: fz_paint_span_N_with_alpha(dp, sp, n, w, alpha); break; + } + } +} + +/* + * Pixmap blending functions + */ + +void +fz_paint_pixmap_with_bbox(fz_pixmap *dst, fz_pixmap *src, int alpha, fz_irect bbox) +{ + unsigned char *sp, *dp; + int x, y, w, h, n; + fz_irect bbox2; + + assert(dst->n == src->n); + + fz_pixmap_bbox_no_ctx(dst, &bbox2); + fz_intersect_irect(&bbox, &bbox2); + fz_pixmap_bbox_no_ctx(src, &bbox2); + fz_intersect_irect(&bbox, &bbox2); + + x = bbox.x0; + y = bbox.y0; + w = bbox.x1 - bbox.x0; + h = bbox.y1 - bbox.y0; + if ((w | h) == 0) + return; + + n = src->n; + sp = src->samples + (unsigned int)(((y - src->y) * src->w + (x - src->x)) * src->n); + dp = dst->samples + (unsigned int)(((y - dst->y) * dst->w + (x - dst->x)) * dst->n); + + while (h--) + { + fz_paint_span(dp, sp, n, w, alpha); + sp += src->w * n; + dp += dst->w * n; + } +} + +void +fz_paint_pixmap(fz_pixmap *dst, fz_pixmap *src, int alpha) +{ + unsigned char *sp, *dp; + fz_irect bbox; + fz_irect bbox2; + int x, y, w, h, n; + + assert(dst->n == src->n); + + fz_pixmap_bbox_no_ctx(dst, &bbox); + fz_pixmap_bbox_no_ctx(src, &bbox2); + fz_intersect_irect(&bbox, &bbox2); + + x = bbox.x0; + y = bbox.y0; + w = bbox.x1 - bbox.x0; + h = bbox.y1 - bbox.y0; + if ((w | h) == 0) + return; + + n = src->n; + sp = src->samples + (unsigned int)(((y - src->y) * src->w + (x - src->x)) * src->n); + dp = dst->samples + (unsigned int)(((y - dst->y) * dst->w + (x - dst->x)) * dst->n); + + while (h--) + { + fz_paint_span(dp, sp, n, w, alpha); + sp += src->w * n; + dp += dst->w * n; + } +} + +void +fz_paint_pixmap_with_mask(fz_pixmap *dst, fz_pixmap *src, fz_pixmap *msk) +{ + unsigned char *sp, *dp, *mp; + fz_irect bbox, bbox2; + int x, y, w, h, n; + + assert(dst->n == src->n); + assert(msk->n == 1); + + fz_pixmap_bbox_no_ctx(dst, &bbox); + fz_pixmap_bbox_no_ctx(src, &bbox2); + fz_intersect_irect(&bbox, &bbox2); + fz_pixmap_bbox_no_ctx(msk, &bbox2); + fz_intersect_irect(&bbox, &bbox2); + + x = bbox.x0; + y = bbox.y0; + w = bbox.x1 - bbox.x0; + h = bbox.y1 - bbox.y0; + if ((w | h) == 0) + return; + + n = src->n; + sp = src->samples + (unsigned int)(((y - src->y) * src->w + (x - src->x)) * src->n); + mp = msk->samples + (unsigned int)(((y - msk->y) * msk->w + (x - msk->x)) * msk->n); + dp = dst->samples + (unsigned int)(((y - dst->y) * dst->w + (x - dst->x)) * dst->n); + + while (h--) + { + fz_paint_span_with_mask(dp, sp, mp, n, w); + sp += src->w * n; + dp += dst->w * n; + mp += msk->w; + } +} diff --git a/source/fitz/draw-path.c b/source/fitz/draw-path.c new file mode 100644 index 00000000..31b038b1 --- /dev/null +++ b/source/fitz/draw-path.c @@ -0,0 +1,831 @@ +#include "mupdf/fitz.h" +#include "draw-imp.h" + +#define MAX_DEPTH 8 + +static void +line(fz_gel *gel, const fz_matrix *ctm, float x0, float y0, float x1, float y1) +{ + float tx0 = ctm->a * x0 + ctm->c * y0 + ctm->e; + float ty0 = ctm->b * x0 + ctm->d * y0 + ctm->f; + float tx1 = ctm->a * x1 + ctm->c * y1 + ctm->e; + float ty1 = ctm->b * x1 + ctm->d * y1 + ctm->f; + fz_insert_gel(gel, tx0, ty0, tx1, ty1); +} + +static void +bezier(fz_gel *gel, const fz_matrix *ctm, float flatness, + float xa, float ya, + float xb, float yb, + float xc, float yc, + float xd, float yd, int depth) +{ + float dmax; + float xab, yab; + float xbc, ybc; + float xcd, ycd; + float xabc, yabc; + float xbcd, ybcd; + float xabcd, yabcd; + + /* termination check */ + dmax = fz_abs(xa - xb); + dmax = fz_max(dmax, fz_abs(ya - yb)); + dmax = fz_max(dmax, fz_abs(xd - xc)); + dmax = fz_max(dmax, fz_abs(yd - yc)); + if (dmax < flatness || depth >= MAX_DEPTH) + { + line(gel, ctm, xa, ya, xd, yd); + return; + } + + xab = xa + xb; + yab = ya + yb; + xbc = xb + xc; + ybc = yb + yc; + xcd = xc + xd; + ycd = yc + yd; + + xabc = xab + xbc; + yabc = yab + ybc; + xbcd = xbc + xcd; + ybcd = ybc + ycd; + + xabcd = xabc + xbcd; + yabcd = yabc + ybcd; + + xab *= 0.5f; yab *= 0.5f; + xbc *= 0.5f; ybc *= 0.5f; + xcd *= 0.5f; ycd *= 0.5f; + + xabc *= 0.25f; yabc *= 0.25f; + xbcd *= 0.25f; ybcd *= 0.25f; + + xabcd *= 0.125f; yabcd *= 0.125f; + + bezier(gel, ctm, flatness, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1); + bezier(gel, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1); +} + +void +fz_flatten_fill_path(fz_gel *gel, fz_path *path, const fz_matrix *ctm, float flatness) +{ + float x1, y1, x2, y2, x3, y3; + float cx = 0; + float cy = 0; + float bx = 0; + float by = 0; + int i = 0; + + while (i < path->len) + { + switch (path->items[i++].k) + { + case FZ_MOVETO: + /* implicit closepath before moveto */ + if (cx != bx || cy != by) + line(gel, ctm, cx, cy, bx, by); + x1 = path->items[i++].v; + y1 = path->items[i++].v; + cx = bx = x1; + cy = by = y1; + break; + + case FZ_LINETO: + x1 = path->items[i++].v; + y1 = path->items[i++].v; + line(gel, ctm, cx, cy, x1, y1); + cx = x1; + cy = y1; + break; + + case FZ_CURVETO: + x1 = path->items[i++].v; + y1 = path->items[i++].v; + x2 = path->items[i++].v; + y2 = path->items[i++].v; + x3 = path->items[i++].v; + y3 = path->items[i++].v; + bezier(gel, ctm, flatness, cx, cy, x1, y1, x2, y2, x3, y3, 0); + cx = x3; + cy = y3; + break; + + case FZ_CLOSE_PATH: + line(gel, ctm, cx, cy, bx, by); + cx = bx; + cy = by; + break; + } + } + + if (cx != bx || cy != by) + line(gel, ctm, cx, cy, bx, by); +} + +struct sctx +{ + fz_gel *gel; + const fz_matrix *ctm; + float flatness; + + int linejoin; + float linewidth; + float miterlimit; + fz_point beg[2]; + fz_point seg[2]; + int sn, bn; + int dot; + int from_bezier; + + const float *dash_list; + float dash_phase; + int dash_len; + int toggle, cap; + int offset; + float phase; + fz_point cur; +}; + +static void +fz_add_line(struct sctx *s, float x0, float y0, float x1, float y1) +{ + float tx0 = s->ctm->a * x0 + s->ctm->c * y0 + s->ctm->e; + float ty0 = s->ctm->b * x0 + s->ctm->d * y0 + s->ctm->f; + float tx1 = s->ctm->a * x1 + s->ctm->c * y1 + s->ctm->e; + float ty1 = s->ctm->b * x1 + s->ctm->d * y1 + s->ctm->f; + fz_insert_gel(s->gel, tx0, ty0, tx1, ty1); +} + +static void +fz_add_arc(struct sctx *s, + float xc, float yc, + float x0, float y0, + float x1, float y1) +{ + float th0, th1, r; + float theta; + float ox, oy, nx, ny; + int n, i; + + r = fabsf(s->linewidth); + theta = 2 * (float)M_SQRT2 * sqrtf(s->flatness / r); + th0 = atan2f(y0, x0); + th1 = atan2f(y1, x1); + + if (r > 0) + { + if (th0 < th1) + th0 += (float)M_PI * 2; + n = ceilf((th0 - th1) / theta); + } + else + { + if (th1 < th0) + th1 += (float)M_PI * 2; + n = ceilf((th1 - th0) / theta); + } + + ox = x0; + oy = y0; + for (i = 1; i < n; i++) + { + theta = th0 + (th1 - th0) * i / n; + nx = cosf(theta) * r; + ny = sinf(theta) * r; + fz_add_line(s, xc + ox, yc + oy, xc + nx, yc + ny); + ox = nx; + oy = ny; + } + + fz_add_line(s, xc + ox, yc + oy, xc + x1, yc + y1); +} + +static void +fz_add_line_stroke(struct sctx *s, fz_point a, fz_point b) +{ + float dx = b.x - a.x; + float dy = b.y - a.y; + float scale = s->linewidth / sqrtf(dx * dx + dy * dy); + float dlx = dy * scale; + float dly = -dx * scale; + fz_add_line(s, a.x - dlx, a.y - dly, b.x - dlx, b.y - dly); + fz_add_line(s, b.x + dlx, b.y + dly, a.x + dlx, a.y + dly); +} + +static void +fz_add_line_join(struct sctx *s, fz_point a, fz_point b, fz_point c, int join_under) +{ + float miterlimit = s->miterlimit; + float linewidth = s->linewidth; + fz_linejoin linejoin = s->linejoin; + float dx0, dy0; + float dx1, dy1; + float dlx0, dly0; + float dlx1, dly1; + float dmx, dmy; + float dmr2; + float scale; + float cross; + float len0, len1; + + dx0 = b.x - a.x; + dy0 = b.y - a.y; + + dx1 = c.x - b.x; + dy1 = c.y - b.y; + + cross = dx1 * dy0 - dx0 * dy1; + /* Ensure that cross >= 0 */ + if (cross < 0) + { + float tmp; + tmp = dx1; dx1 = -dx0; dx0 = -tmp; + tmp = dy1; dy1 = -dy0; dy0 = -tmp; + cross = -cross; + } + + len0 = dx0 * dx0 + dy0 * dy0; + if (len0 < FLT_EPSILON) + linejoin = FZ_LINEJOIN_BEVEL; + len1 = dx1 * dx1 + dy1 * dy1; + if (len1 < FLT_EPSILON) + linejoin = FZ_LINEJOIN_BEVEL; + + scale = linewidth / sqrtf(len0); + dlx0 = dy0 * scale; + dly0 = -dx0 * scale; + + scale = linewidth / sqrtf(len1); + dlx1 = dy1 * scale; + dly1 = -dx1 * scale; + + dmx = (dlx0 + dlx1) * 0.5f; + dmy = (dly0 + dly1) * 0.5f; + dmr2 = dmx * dmx + dmy * dmy; + + if (cross * cross < FLT_EPSILON && dx0 * dx1 + dy0 * dy1 >= 0) + linejoin = FZ_LINEJOIN_BEVEL; + + if (join_under) + { + fz_add_line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); + } + else + { + fz_add_line(s, b.x + dlx1, b.y + dly1, b.x, b.y); + fz_add_line(s, b.x, b.y, b.x + dlx0, b.y + dly0); + } + + /* XPS miter joins are clipped at miterlength, rather than simply + * being converted to bevelled joins. */ + if (linejoin == FZ_LINEJOIN_MITER_XPS) + { + if (cross == 0) + linejoin = FZ_LINEJOIN_BEVEL; + else if (dmr2 * miterlimit * miterlimit >= linewidth * linewidth) + linejoin = FZ_LINEJOIN_MITER; + else + { + float k, t0x, t0y, t1x, t1y; + scale = linewidth * linewidth / dmr2; + dmx *= scale; + dmy *= scale; + k = (scale - linewidth * miterlimit / sqrtf(dmr2)) / (scale - 1); + t0x = b.x - dmx + k * (dmx - dlx0); + t0y = b.y - dmy + k * (dmy - dly0); + t1x = b.x - dmx + k * (dmx - dlx1); + t1y = b.y - dmy + k * (dmy - dly1); + + fz_add_line(s, b.x - dlx0, b.y - dly0, t0x, t0y); + fz_add_line(s, t0x, t0y, t1x, t1y); + fz_add_line(s, t1x, t1y, b.x - dlx1, b.y - dly1); + } + } + else if (linejoin == FZ_LINEJOIN_MITER) + if (dmr2 * miterlimit * miterlimit < linewidth * linewidth) + linejoin = FZ_LINEJOIN_BEVEL; + + if (linejoin == FZ_LINEJOIN_MITER) + { + scale = linewidth * linewidth / dmr2; + dmx *= scale; + dmy *= scale; + + fz_add_line(s, b.x - dlx0, b.y - dly0, b.x - dmx, b.y - dmy); + fz_add_line(s, b.x - dmx, b.y - dmy, b.x - dlx1, b.y - dly1); + } + + if (linejoin == FZ_LINEJOIN_BEVEL) + { + fz_add_line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); + } + + if (linejoin == FZ_LINEJOIN_ROUND) + { + fz_add_arc(s, b.x, b.y, -dlx0, -dly0, -dlx1, -dly1); + } +} + +static void +fz_add_line_cap(struct sctx *s, fz_point a, fz_point b, fz_linecap linecap) +{ + float flatness = s->flatness; + float linewidth = s->linewidth; + + float dx = b.x - a.x; + float dy = b.y - a.y; + + float scale = linewidth / sqrtf(dx * dx + dy * dy); + float dlx = dy * scale; + float dly = -dx * scale; + + if (linecap == FZ_LINECAP_BUTT) + fz_add_line(s, b.x - dlx, b.y - dly, b.x + dlx, b.y + dly); + + if (linecap == FZ_LINECAP_ROUND) + { + int i; + int n = ceilf((float)M_PI / (2.0f * (float)M_SQRT2 * sqrtf(flatness / linewidth))); + float ox = b.x - dlx; + float oy = b.y - dly; + for (i = 1; i < n; i++) + { + float theta = (float)M_PI * i / n; + float cth = cosf(theta); + float sth = sinf(theta); + float nx = b.x - dlx * cth - dly * sth; + float ny = b.y - dly * cth + dlx * sth; + fz_add_line(s, ox, oy, nx, ny); + ox = nx; + oy = ny; + } + fz_add_line(s, ox, oy, b.x + dlx, b.y + dly); + } + + if (linecap == FZ_LINECAP_SQUARE) + { + fz_add_line(s, b.x - dlx, b.y - dly, + b.x - dlx - dly, b.y - dly + dlx); + fz_add_line(s, b.x - dlx - dly, b.y - dly + dlx, + b.x + dlx - dly, b.y + dly + dlx); + fz_add_line(s, b.x + dlx - dly, b.y + dly + dlx, + b.x + dlx, b.y + dly); + } + + if (linecap == FZ_LINECAP_TRIANGLE) + { + float mx = -dly; + float my = dlx; + fz_add_line(s, b.x - dlx, b.y - dly, b.x + mx, b.y + my); + fz_add_line(s, b.x + mx, b.y + my, b.x + dlx, b.y + dly); + } +} + +static void +fz_add_line_dot(struct sctx *s, fz_point a) +{ + float flatness = s->flatness; + float linewidth = s->linewidth; + int n = ceilf((float)M_PI / ((float)M_SQRT2 * sqrtf(flatness / linewidth))); + float ox = a.x - linewidth; + float oy = a.y; + int i; + + for (i = 1; i < n; i++) + { + float theta = (float)M_PI * 2 * i / n; + float cth = cosf(theta); + float sth = sinf(theta); + float nx = a.x - cth * linewidth; + float ny = a.y + sth * linewidth; + fz_add_line(s, ox, oy, nx, ny); + ox = nx; + oy = ny; + } + + fz_add_line(s, ox, oy, a.x - linewidth, a.y); +} + +static void +fz_stroke_flush(struct sctx *s, fz_linecap start_cap, fz_linecap end_cap) +{ + if (s->sn == 2) + { + fz_add_line_cap(s, s->beg[1], s->beg[0], start_cap); + fz_add_line_cap(s, s->seg[0], s->seg[1], end_cap); + } + else if (s->dot) + { + fz_add_line_dot(s, s->beg[0]); + } +} + +static void +fz_stroke_moveto(struct sctx *s, fz_point cur) +{ + s->seg[0] = cur; + s->beg[0] = cur; + s->sn = 1; + s->bn = 1; + s->dot = 0; + s->from_bezier = 0; +} + +static void +fz_stroke_lineto(struct sctx *s, fz_point cur, int from_bezier) +{ + float dx = cur.x - s->seg[s->sn-1].x; + float dy = cur.y - s->seg[s->sn-1].y; + + if (dx * dx + dy * dy < FLT_EPSILON) + { + if (s->cap == FZ_LINECAP_ROUND || s->dash_list) + s->dot = 1; + return; + } + + fz_add_line_stroke(s, s->seg[s->sn-1], cur); + + if (s->sn == 2) + { + fz_add_line_join(s, s->seg[0], s->seg[1], cur, s->from_bezier & from_bezier); + s->seg[0] = s->seg[1]; + s->seg[1] = cur; + } + s->from_bezier = from_bezier; + + if (s->sn == 1) + s->seg[s->sn++] = cur; + if (s->bn == 1) + s->beg[s->bn++] = cur; +} + +static void +fz_stroke_closepath(struct sctx *s) +{ + if (s->sn == 2) + { + fz_stroke_lineto(s, s->beg[0], 0); + if (s->seg[1].x == s->beg[0].x && s->seg[1].y == s->beg[0].y) + fz_add_line_join(s, s->seg[0], s->beg[0], s->beg[1], 0); + else + fz_add_line_join(s, s->seg[1], s->beg[0], s->beg[1], 0); + } + else if (s->dot) + { + fz_add_line_dot(s, s->beg[0]); + } + + s->seg[0] = s->beg[0]; + s->bn = 1; + s->sn = 1; + s->dot = 0; + s->from_bezier = 0; +} + +static void +fz_stroke_bezier(struct sctx *s, + float xa, float ya, + float xb, float yb, + float xc, float yc, + float xd, float yd, int depth) +{ + float dmax; + float xab, yab; + float xbc, ybc; + float xcd, ycd; + float xabc, yabc; + float xbcd, ybcd; + float xabcd, yabcd; + + /* termination check */ + dmax = fz_abs(xa - xb); + dmax = fz_max(dmax, fz_abs(ya - yb)); + dmax = fz_max(dmax, fz_abs(xd - xc)); + dmax = fz_max(dmax, fz_abs(yd - yc)); + if (dmax < s->flatness || depth >= MAX_DEPTH) + { + fz_point p; + p.x = xd; + p.y = yd; + fz_stroke_lineto(s, p, 1); + return; + } + + xab = xa + xb; + yab = ya + yb; + xbc = xb + xc; + ybc = yb + yc; + xcd = xc + xd; + ycd = yc + yd; + + xabc = xab + xbc; + yabc = yab + ybc; + xbcd = xbc + xcd; + ybcd = ybc + ycd; + + xabcd = xabc + xbcd; + yabcd = yabc + ybcd; + + xab *= 0.5f; yab *= 0.5f; + xbc *= 0.5f; ybc *= 0.5f; + xcd *= 0.5f; ycd *= 0.5f; + + xabc *= 0.25f; yabc *= 0.25f; + xbcd *= 0.25f; ybcd *= 0.25f; + + xabcd *= 0.125f; yabcd *= 0.125f; + + fz_stroke_bezier(s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1); + fz_stroke_bezier(s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1); +} + +void +fz_flatten_stroke_path(fz_gel *gel, fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth) +{ + struct sctx s; + fz_point p0, p1, p2, p3; + int i; + + s.gel = gel; + s.ctm = ctm; + s.flatness = flatness; + + s.linejoin = stroke->linejoin; + s.linewidth = linewidth * 0.5f; /* hairlines use a different value from the path value */ + s.miterlimit = stroke->miterlimit; + s.sn = 0; + s.bn = 0; + s.dot = 0; + + s.dash_list = NULL; + s.dash_phase = 0; + s.dash_len = 0; + s.toggle = 0; + s.offset = 0; + s.phase = 0; + + s.cap = stroke->start_cap; + + i = 0; + + if (path->len > 0 && path->items[0].k != FZ_MOVETO) + return; + + p0.x = p0.y = 0; + + while (i < path->len) + { + switch (path->items[i++].k) + { + case FZ_MOVETO: + p1.x = path->items[i++].v; + p1.y = path->items[i++].v; + fz_stroke_flush(&s, stroke->start_cap, stroke->end_cap); + fz_stroke_moveto(&s, p1); + p0 = p1; + break; + + case FZ_LINETO: + p1.x = path->items[i++].v; + p1.y = path->items[i++].v; + fz_stroke_lineto(&s, p1, 0); + p0 = p1; + break; + + case FZ_CURVETO: + p1.x = path->items[i++].v; + p1.y = path->items[i++].v; + p2.x = path->items[i++].v; + p2.y = path->items[i++].v; + p3.x = path->items[i++].v; + p3.y = path->items[i++].v; + fz_stroke_bezier(&s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, 0); + p0 = p3; + break; + + case FZ_CLOSE_PATH: + fz_stroke_closepath(&s); + break; + } + } + + fz_stroke_flush(&s, stroke->start_cap, stroke->end_cap); +} + +static void +fz_dash_moveto(struct sctx *s, fz_point a, fz_linecap start_cap, fz_linecap end_cap) +{ + s->toggle = 1; + s->offset = 0; + s->phase = s->dash_phase; + + while (s->phase >= s->dash_list[s->offset]) + { + s->toggle = !s->toggle; + s->phase -= s->dash_list[s->offset]; + s->offset ++; + if (s->offset == s->dash_len) + s->offset = 0; + } + + s->cur = a; + + if (s->toggle) + { + fz_stroke_flush(s, s->cap, end_cap); + s->cap = start_cap; + fz_stroke_moveto(s, a); + } +} + +static void +fz_dash_lineto(struct sctx *s, fz_point b, int dash_cap, int from_bezier) +{ + float dx, dy; + float total, used, ratio; + fz_point a; + fz_point m; + + a = s->cur; + dx = b.x - a.x; + dy = b.y - a.y; + total = sqrtf(dx * dx + dy * dy); + used = 0; + + while (total - used > s->dash_list[s->offset] - s->phase) + { + used += s->dash_list[s->offset] - s->phase; + ratio = used / total; + m.x = a.x + ratio * dx; + m.y = a.y + ratio * dy; + + if (s->toggle) + { + fz_stroke_lineto(s, m, from_bezier); + } + else + { + fz_stroke_flush(s, s->cap, dash_cap); + s->cap = dash_cap; + fz_stroke_moveto(s, m); + } + + s->toggle = !s->toggle; + s->phase = 0; + s->offset ++; + if (s->offset == s->dash_len) + s->offset = 0; + } + + s->phase += total - used; + + s->cur = b; + + if (s->toggle) + { + fz_stroke_lineto(s, b, from_bezier); + } +} + +static void +fz_dash_bezier(struct sctx *s, + float xa, float ya, + float xb, float yb, + float xc, float yc, + float xd, float yd, int depth, + int dash_cap) +{ + float dmax; + float xab, yab; + float xbc, ybc; + float xcd, ycd; + float xabc, yabc; + float xbcd, ybcd; + float xabcd, yabcd; + + /* termination check */ + dmax = fz_abs(xa - xb); + dmax = fz_max(dmax, fz_abs(ya - yb)); + dmax = fz_max(dmax, fz_abs(xd - xc)); + dmax = fz_max(dmax, fz_abs(yd - yc)); + if (dmax < s->flatness || depth >= MAX_DEPTH) + { + fz_point p; + p.x = xd; + p.y = yd; + fz_dash_lineto(s, p, dash_cap, 1); + return; + } + + xab = xa + xb; + yab = ya + yb; + xbc = xb + xc; + ybc = yb + yc; + xcd = xc + xd; + ycd = yc + yd; + + xabc = xab + xbc; + yabc = yab + ybc; + xbcd = xbc + xcd; + ybcd = ybc + ycd; + + xabcd = xabc + xbcd; + yabcd = yabc + ybcd; + + xab *= 0.5f; yab *= 0.5f; + xbc *= 0.5f; ybc *= 0.5f; + xcd *= 0.5f; ycd *= 0.5f; + + xabc *= 0.25f; yabc *= 0.25f; + xbcd *= 0.25f; ybcd *= 0.25f; + + xabcd *= 0.125f; yabcd *= 0.125f; + + fz_dash_bezier(s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1, dash_cap); + fz_dash_bezier(s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1, dash_cap); +} + +void +fz_flatten_dash_path(fz_gel *gel, fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth) +{ + struct sctx s; + fz_point p0, p1, p2, p3, beg; + float phase_len, max_expand; + int i; + + s.gel = gel; + s.ctm = ctm; + s.flatness = flatness; + + s.linejoin = stroke->linejoin; + s.linewidth = linewidth * 0.5f; + s.miterlimit = stroke->miterlimit; + s.sn = 0; + s.bn = 0; + s.dot = 0; + + s.dash_list = stroke->dash_list; + s.dash_phase = stroke->dash_phase; + s.dash_len = stroke->dash_len; + s.toggle = 0; + s.offset = 0; + s.phase = 0; + + s.cap = stroke->start_cap; + + if (path->len > 0 && path->items[0].k != FZ_MOVETO) + return; + + phase_len = 0; + for (i = 0; i < stroke->dash_len; i++) + phase_len += stroke->dash_list[i]; + max_expand = fz_matrix_max_expansion(ctm); + if (phase_len < 0.01f || phase_len * max_expand < 0.5f) + { + fz_flatten_stroke_path(gel, path, stroke, ctm, flatness, linewidth); + return; + } + + p0.x = p0.y = 0; + i = 0; + + while (i < path->len) + { + switch (path->items[i++].k) + { + case FZ_MOVETO: + p1.x = path->items[i++].v; + p1.y = path->items[i++].v; + fz_dash_moveto(&s, p1, stroke->start_cap, stroke->end_cap); + beg = p0 = p1; + break; + + case FZ_LINETO: + p1.x = path->items[i++].v; + p1.y = path->items[i++].v; + fz_dash_lineto(&s, p1, stroke->dash_cap, 0); + p0 = p1; + break; + + case FZ_CURVETO: + p1.x = path->items[i++].v; + p1.y = path->items[i++].v; + p2.x = path->items[i++].v; + p2.y = path->items[i++].v; + p3.x = path->items[i++].v; + p3.y = path->items[i++].v; + fz_dash_bezier(&s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, 0, stroke->dash_cap); + p0 = p3; + break; + + case FZ_CLOSE_PATH: + fz_dash_lineto(&s, beg, stroke->dash_cap, 0); + p0 = p1 = beg; + break; + } + } + + fz_stroke_flush(&s, s.cap, stroke->end_cap); +} diff --git a/source/fitz/draw-scale-simple.c b/source/fitz/draw-scale-simple.c new file mode 100644 index 00000000..08dedf0b --- /dev/null +++ b/source/fitz/draw-scale-simple.c @@ -0,0 +1,1509 @@ +/* +This code does smooth scaling of a pixmap. + +This function returns a new pixmap representing the area starting at (0,0) +given by taking the source pixmap src, scaling it to width w, and height h, +and then positioning it at (frac(x),frac(y)). + +This is a cut-down version of draw_scale.c that only copes with filters +that return values strictly in the 0..1 range, and uses bytes for +intermediate results rather than ints. +*/ + +#include "mupdf/fitz.h" +#include "draw-imp.h" + +/* Do we special case handling of single pixel high/wide images? The + * 'purest' handling is given by not special casing them, but certain + * files that use such images 'stack' them to give full images. Not + * special casing them results in then being fainter and giving noticeable + * rounding errors. + */ +#define SINGLE_PIXEL_SPECIALS + +#ifdef DEBUG_SCALING +#ifdef WIN32 +#include <windows.h> +static void debug_print(const char *fmt, ...) +{ + va_list args; + char text[256]; + va_start(args, fmt); + vsprintf(text, fmt, args); + va_end(args); + OutputDebugStringA(text); + printf(text); +} +#else +static void debug_print(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} +#endif +#endif +#ifdef DEBUG_SCALING +#define DBUG(A) debug_print A +#else +#define DBUG(A) do {} while(0==1) +#endif + +/* +Consider a row of source samples, src, of width src_w, positioned at x, +scaled to width dst_w. + +src[i] is centred at: x + (i + 0.5)*dst_w/src_w + +Therefore the distance between the centre of the jth output pixel and +the centre of the ith source sample is: + +dist[j,i] = j + 0.5 - (x + (i + 0.5)*dst_w/src_w) + +When scaling up, therefore: + +dst[j] = SUM(filter(dist[j,i]) * src[i]) + (for all ints i) + +This can be simplified by noticing that filters are only non zero within +a given filter width (henceforth called W). So: + +dst[j] = SUM(filter(dist[j,i]) * src[i]) + (for ints i, s.t. (j*src_w/dst_w)-W < i < (j*src_w/dst_w)+W) + +When scaling down, each filtered source sample is stretched to be wider +to avoid aliasing issues. This effectively reduces the distance between +centres. + +dst[j] = SUM(filter(dist[j,i] * F) * F * src[i]) + (where F = dst_w/src_w) + (for ints i, s.t. (j-W)/F < i < (j+W)/F) + +*/ + +typedef struct fz_scale_filter_s fz_scale_filter; + +struct fz_scale_filter_s +{ + int width; + float (*fn)(fz_scale_filter *, float); +}; + +/* Image scale filters */ + +static float +triangle(fz_scale_filter *filter, float f) +{ + if (f >= 1) + return 0; + return 1-f; +} + +static float +box(fz_scale_filter *filter, float f) +{ + if (f >= 0.5f) + return 0; + return 1; +} + +static float +simple(fz_scale_filter *filter, float x) +{ + if (x >= 1) + return 0; + return 1 + (2*x - 3)*x*x; +} + +fz_scale_filter fz_scale_filter_box = { 1, box }; +fz_scale_filter fz_scale_filter_triangle = { 1, triangle }; +fz_scale_filter fz_scale_filter_simple = { 1, simple }; + +/* +We build ourselves a set of tables to contain the precalculated weights +for a given set of scale settings. + +The first dst_w entries in index are the index into index of the +sets of weight for each destination pixel. + +Each of the sets of weights is a set of values consisting of: + the minimum source pixel index used for this destination pixel + the number of weights used for this destination pixel + the weights themselves + +So to calculate dst[i] we do the following: + + weights = &index[index[i]]; + min = *weights++; + len = *weights++; + dst[i] = 0; + while (--len > 0) + dst[i] += src[min++] * *weights++ + +in addition, we guarantee that at the end of this process weights will now +point to the weights value for dst pixel i+1. + +In the simplest version of this algorithm, we would scale the whole image +horizontally first into a temporary buffer, then scale that temporary +buffer again vertically to give us our result. Using such a simple +algorithm would mean that could use the same style of weights for both +horizontal and vertical scaling. + +Unfortunately, this would also require a large temporary buffer, +particularly in the case where we are scaling up. + +We therefore modify the algorithm as follows; we scale scanlines from the +source image horizontally into a temporary buffer, until we have all the +contributors for a given output scanline. We then produce that output +scanline from the temporary buffer. In this way we restrict the height +of the temporary buffer to a small fraction of the final size. + +Unfortunately, this means that the pseudo code for recombining a +scanline of fully scaled pixels is as follows: + + weights = &index[index[y]]; + min = *weights++; + len = *weights++; + for (x=0 to dst_w) + min2 = min + len2 = len + weights2 = weights + dst[x] = 0; + while (--len2 > 0) + dst[x] += temp[x][(min2++) % tmp_buf_height] * *weights2++ + +i.e. it requires a % operation for every source pixel - this is typically +expensive. + +To avoid this, we alter the order in which vertical weights are stored, +so that they are ordered in the same order as the temporary buffer lines +would appear. This simplifies the algorithm to: + + weights = &index[index[y]]; + min = *weights++; + len = *weights++; + for (x=0 to dst_w) + min2 = 0 + len2 = len + weights2 = weights + dst[x] = 0; + while (--len2 > 0) + dst[x] += temp[i][min2++] * *weights2++ + +This means that len may be larger than it needs to be (due to the +possible inclusion of a zero weight row or two), but in practise this +is only an increase of 1 or 2 at worst. + +We implement this by generating the weights as normal (but ensuring we +leave enough space) and then reordering afterwards. + +*/ + +typedef struct fz_weights_s fz_weights; + +/* This structure is accessed from ARM code - bear this in mind before + * altering it! */ +struct fz_weights_s +{ + int flip; /* true if outputting reversed */ + int count; /* number of output pixels we have records for in this table */ + int max_len; /* Maximum number of weights for any one output pixel */ + int n; /* number of components (src->n) */ + int new_line; /* True if no weights for the current output pixel */ + int patch_l; /* How many output pixels we skip over */ + int index[1]; +}; + +struct fz_scale_cache_s +{ + int src_w; + float x; + float dst_w; + fz_scale_filter *filter; + int vertical; + int dst_w_int; + int patch_l; + int patch_r; + int n; + int flip; + fz_weights *weights; +}; + +static fz_weights * +new_weights(fz_context *ctx, fz_scale_filter *filter, int src_w, float dst_w, int patch_w, int n, int flip, int patch_l) +{ + int max_len; + fz_weights *weights; + + if (src_w > dst_w) + { + /* Scaling down, so there will be a maximum of + * 2*filterwidth*src_w/dst_w src pixels + * contributing to each dst pixel. */ + max_len = (int)ceilf((2 * filter->width * src_w)/dst_w); + if (max_len > src_w) + max_len = src_w; + } + else + { + /* Scaling up, so there will be a maximum of + * 2*filterwidth src pixels contributing to each dst pixel. + */ + max_len = 2 * filter->width; + } + /* We need the size of the struct, + * plus patch_w*sizeof(int) for the index + * plus (2+max_len)*sizeof(int) for the weights + * plus room for an extra set of weights for reordering. + */ + weights = fz_malloc(ctx, sizeof(*weights)+(max_len+3)*(patch_w+1)*sizeof(int)); + if (!weights) + return NULL; + weights->count = -1; + weights->max_len = max_len; + weights->index[0] = patch_w; + weights->n = n; + weights->patch_l = patch_l; + weights->flip = flip; + return weights; +} + +/* j is destination pixel in the patch_l..patch_l+patch_w range */ +static void +init_weights(fz_weights *weights, int j) +{ + int index; + + j -= weights->patch_l; + assert(weights->count == j-1); + weights->count++; + weights->new_line = 1; + if (j == 0) + index = weights->index[0]; + else + { + index = weights->index[j-1]; + index += 2 + weights->index[index+1]; + } + weights->index[j] = index; /* row pointer */ + weights->index[index] = 0; /* min */ + weights->index[index+1] = 0; /* len */ +} + +static void +add_weight(fz_weights *weights, int j, int i, fz_scale_filter *filter, + float x, float F, float G, int src_w, float dst_w) +{ + float dist = j - x + 0.5f - ((i + 0.5f)*dst_w/src_w); + float f; + int min, len, index, weight; + + dist *= G; + if (dist < 0) + dist = -dist; + f = filter->fn(filter, dist)*F; + weight = (int)(256*f+0.5f); + + /* Ensure i is in range */ + if (i < 0 || i >= src_w) + return; + if (weight == 0) + { + /* We add a fudge factor here to allow for extreme downscales + * where all the weights round to 0. Ensure that at least one + * (arbitrarily the first one) is non zero. */ + if (weights->new_line && f > 0) + weight = 1; + else + return; + } + + DBUG(("add_weight[%d][%d] = %d(%g) dist=%g\n",j,i,weight,f,dist)); + + /* Move j from patch_l...patch_l+patch_w range to 0..patch_w range */ + j -= weights->patch_l; + if (weights->new_line) + { + /* New line */ + weights->new_line = 0; + index = weights->index[j]; /* row pointer */ + weights->index[index] = i; /* min */ + weights->index[index+1] = 0; /* len */ + } + index = weights->index[j]; + min = weights->index[index++]; + len = weights->index[index++]; + while (i < min) + { + /* This only happens in rare cases, but we need to insert + * one earlier. In exceedingly rare cases we may need to + * insert more than one earlier. */ + int k; + + for (k = len; k > 0; k--) + { + weights->index[index+k] = weights->index[index+k-1]; + } + weights->index[index] = 0; + min--; + len++; + weights->index[index-2] = min; + weights->index[index-1] = len; + } + if (i-min >= len) + { + /* The usual case */ + while (i-min >= ++len) + { + weights->index[index+len-1] = 0; + } + assert(len-1 == i-min); + weights->index[index+i-min] = weight; + weights->index[index-1] = len; + assert(len <= weights->max_len); + } + else + { + /* Infrequent case */ + weights->index[index+i-min] += weight; + } +} + +static void +reorder_weights(fz_weights *weights, int j, int src_w) +{ + int idx = weights->index[j - weights->patch_l]; + int min = weights->index[idx++]; + int len = weights->index[idx++]; + int max = weights->max_len; + int tmp = idx+max; + int i, off; + + /* Copy into the temporary area */ + memcpy(&weights->index[tmp], &weights->index[idx], sizeof(int)*len); + + /* Pad out if required */ + assert(len <= max); + assert(min+len <= src_w); + off = 0; + if (len < max) + { + memset(&weights->index[tmp+len], 0, sizeof(int)*(max-len)); + len = max; + if (min + len > src_w) + { + off = min + len - src_w; + min = src_w - len; + weights->index[idx-2] = min; + } + weights->index[idx-1] = len; + } + + /* Copy back into the proper places */ + for (i = 0; i < len; i++) + { + weights->index[idx+((min+i+off) % max)] = weights->index[tmp+i]; + } +} + +/* Due to rounding and edge effects, the sums for the weights sometimes don't + * add up to 256. This causes visible rendering effects. Therefore, we take + * pains to ensure that they 1) never exceed 256, and 2) add up to exactly + * 256 for all pixels that are completely covered. See bug #691629. */ +static void +check_weights(fz_weights *weights, int j, int w, float x, float wf) +{ + int idx, len; + int sum = 0; + int max = -256; + int maxidx = 0; + int i; + + idx = weights->index[j - weights->patch_l]; + idx++; /* min */ + len = weights->index[idx++]; + + for(i=0; i < len; i++) + { + int v = weights->index[idx++]; + sum += v; + if (v > max) + { + max = v; + maxidx = idx; + } + } + /* If we aren't the first or last pixel, OR if the sum is too big + * then adjust it. */ + if (((j != 0) && (j != w-1)) || (sum > 256)) + weights->index[maxidx-1] += 256-sum; + /* Otherwise, if we are the first pixel, and it's fully covered, then + * adjust it. */ + else if ((j == 0) && (x < 0.0001F) && (sum != 256)) + weights->index[maxidx-1] += 256-sum; + /* Finally, if we are the last pixel, and it's fully covered, then + * adjust it. */ + else if ((j == w-1) && ((float)w-wf < 0.0001F) && (sum != 256)) + weights->index[maxidx-1] += 256-sum; + DBUG(("total weight %d = %d\n", j, sum)); +} + +static fz_weights * +make_weights(fz_context *ctx, int src_w, float x, float dst_w, fz_scale_filter *filter, int vertical, int dst_w_int, int patch_l, int patch_r, int n, int flip, fz_scale_cache *cache) +{ + fz_weights *weights; + float F, G; + float window; + int j; + + if (cache) + { + if (cache->src_w == src_w && cache->x == x && cache->dst_w == dst_w && + cache->filter == filter && cache->vertical == vertical && + cache->dst_w_int == dst_w_int && + cache->patch_l == patch_l && cache->patch_r == patch_r && + cache->n == n && cache->flip == flip) + { + return cache->weights; + } + cache->src_w = src_w; + cache->x = x; + cache->dst_w = dst_w; + cache->filter = filter; + cache->vertical = vertical; + cache->dst_w_int = dst_w_int; + cache->patch_l = patch_l; + cache->patch_r = patch_r; + cache->n = n; + cache->flip = flip; + fz_free(ctx, cache->weights); + cache->weights = NULL; + } + + if (dst_w < src_w) + { + /* Scaling down */ + F = dst_w / src_w; + G = 1; + } + else + { + /* Scaling up */ + F = 1; + G = src_w / dst_w; + } + window = filter->width / F; + DBUG(("make_weights src_w=%d x=%g dst_w=%g patch_l=%d patch_r=%d F=%g window=%g\n", src_w, x, dst_w, patch_l, patch_r, F, window)); + weights = new_weights(ctx, filter, src_w, dst_w, patch_r-patch_l, n, flip, patch_l); + if (!weights) + return NULL; + for (j = patch_l; j < patch_r; j++) + { + /* find the position of the centre of dst[j] in src space */ + float centre = (j - x + 0.5f)*src_w/dst_w - 0.5f; + int l, r; + l = ceilf(centre - window); + r = floorf(centre + window); + DBUG(("%d: centre=%g l=%d r=%d\n", j, centre, l, r)); + init_weights(weights, j); + for (; l <= r; l++) + { + add_weight(weights, j, l, filter, x, F, G, src_w, dst_w); + } + check_weights(weights, j, dst_w_int, x, dst_w); + if (vertical) + { + reorder_weights(weights, j, src_w); + } + } + weights->count++; /* weights->count = dst_w_int now */ + if (cache) + { + cache->weights = weights; + } + return weights; +} + +static void +scale_row_to_temp(unsigned char *dst, unsigned char *src, fz_weights *weights) +{ + int *contrib = &weights->index[weights->index[0]]; + int len, i, j, n; + unsigned char *min; + int tmp[FZ_MAX_COLORS]; + int *t = tmp; + + n = weights->n; + for (j = 0; j < n; j++) + tmp[j] = 128; + if (weights->flip) + { + dst += (weights->count-1)*n; + for (i=weights->count; i > 0; i--) + { + min = &src[n * *contrib++]; + len = *contrib++; + while (len-- > 0) + { + for (j = n; j > 0; j--) + *t++ += *min++ * *contrib; + t -= n; + contrib++; + } + for (j = n; j > 0; j--) + { + *dst++ = (unsigned char)(*t>>8); + *t++ = 128; + } + t -= n; + dst -= n*2; + } + } + else + { + for (i=weights->count; i > 0; i--) + { + min = &src[n * *contrib++]; + len = *contrib++; + while (len-- > 0) + { + for (j = n; j > 0; j--) + *t++ += *min++ * *contrib; + t -= n; + contrib++; + } + for (j = n; j > 0; j--) + { + *dst++ = (unsigned char)(*t>>8); + *t++ = 128; + } + t -= n; + } + } +} + +#ifdef ARCH_ARM + +static void +scale_row_to_temp1(unsigned char *dst, unsigned char *src, fz_weights *weights) +__attribute__((naked)); + +static void +scale_row_to_temp2(unsigned char *dst, unsigned char *src, fz_weights *weights) +__attribute__((naked)); + +static void +scale_row_to_temp4(unsigned char *dst, unsigned char *src, fz_weights *weights) +__attribute__((naked)); + +static void +scale_row_from_temp(unsigned char *dst, unsigned char *src, fz_weights *weights, int width, int row) +__attribute__((naked)); + +static void +scale_row_to_temp1(unsigned char *dst, unsigned char *src, fz_weights *weights) +{ + asm volatile( + ENTER_ARM + "stmfd r13!,{r4-r7,r9,r14} \n" + "@ r0 = dst \n" + "@ r1 = src \n" + "@ r2 = weights \n" + "ldr r12,[r2],#4 @ r12= flip \n" + "ldr r3, [r2],#20 @ r3 = count r2 = &index\n" + "ldr r4, [r2] @ r4 = index[0] \n" + "cmp r12,#0 @ if (flip) \n" + "beq 5f @ { \n" + "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n" + "add r0, r0, r3 @ dst += count \n" + "1: \n" + "ldr r4, [r2], #4 @ r4 = *contrib++ \n" + "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n" + "mov r5, #128 @ r5 = a = 128 \n" + "add r4, r1, r4 @ r4 = min = &src[r4] \n" + "subs r9, r9, #1 @ len-- \n" + "blt 3f @ while (len >= 0) \n" + "2: @ { \n" + "ldrgt r6, [r2], #4 @ r6 = *contrib++ \n" + "ldrgtb r7, [r4], #1 @ r7 = *min++ \n" + "ldr r12,[r2], #4 @ r12 = *contrib++ \n" + "ldrb r14,[r4], #1 @ r14 = *min++ \n" + "mlagt r5, r6, r7, r5 @ g += r6 * r7 \n" + "subs r9, r9, #2 @ r9 = len -= 2 \n" + "mla r5, r12,r14,r5 @ g += r14 * r12 \n" + "bge 2b @ } \n" + "3: \n" + "mov r5, r5, lsr #8 @ g >>= 8 \n" + "strb r5,[r0, #-1]! @ *--dst=a \n" + "subs r3, r3, #1 @ i-- \n" + "bgt 1b @ \n" + "ldmfd r13!,{r4-r7,r9,PC} @ pop, return to thumb \n" + "5:" + "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n" + "6:" + "ldr r4, [r2], #4 @ r4 = *contrib++ \n" + "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n" + "mov r5, #128 @ r5 = a = 128 \n" + "add r4, r1, r4 @ r4 = min = &src[r4] \n" + "subs r9, r9, #1 @ len-- \n" + "blt 9f @ while (len > 0) \n" + "7: @ { \n" + "ldrgt r6, [r2], #4 @ r6 = *contrib++ \n" + "ldrgtb r7, [r4], #1 @ r7 = *min++ \n" + "ldr r12,[r2], #4 @ r12 = *contrib++ \n" + "ldrb r14,[r4], #1 @ r14 = *min++ \n" + "mlagt r5, r6,r7,r5 @ a += r6 * r7 \n" + "subs r9, r9, #2 @ r9 = len -= 2 \n" + "mla r5, r12,r14,r5 @ a += r14 * r12 \n" + "bge 7b @ } \n" + "9: \n" + "mov r5, r5, LSR #8 @ a >>= 8 \n" + "strb r5, [r0], #1 @ *dst++=a \n" + "subs r3, r3, #1 @ i-- \n" + "bgt 6b @ \n" + "ldmfd r13!,{r4-r7,r9,PC} @ pop, return to thumb \n" + ENTER_THUMB + ); +} + +static void +scale_row_to_temp2(unsigned char *dst, unsigned char *src, fz_weights *weights) +{ + asm volatile( + ENTER_ARM + "stmfd r13!,{r4-r6,r9-r11,r14} \n" + "@ r0 = dst \n" + "@ r1 = src \n" + "@ r2 = weights \n" + "ldr r12,[r2],#4 @ r12= flip \n" + "ldr r3, [r2],#20 @ r3 = count r2 = &index\n" + "ldr r4, [r2] @ r4 = index[0] \n" + "cmp r12,#0 @ if (flip) \n" + "beq 4f @ { \n" + "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n" + "add r0, r0, r3, LSL #1 @ dst += 2*count \n" + "1: \n" + "ldr r4, [r2], #4 @ r4 = *contrib++ \n" + "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n" + "mov r5, #128 @ r5 = g = 128 \n" + "mov r6, #128 @ r6 = a = 128 \n" + "add r4, r1, r4, LSL #1 @ r4 = min = &src[2*r4] \n" + "cmp r9, #0 @ while (len-- > 0) \n" + "beq 3f @ { \n" + "2: \n" + "ldr r14,[r2], #4 @ r14 = *contrib++ \n" + "ldrb r11,[r4], #1 @ r11 = *min++ \n" + "ldrb r12,[r4], #1 @ r12 = *min++ \n" + "subs r9, r9, #1 @ r9 = len-- \n" + "mla r5, r14,r11,r5 @ g += r11 * r14 \n" + "mla r6, r14,r12,r6 @ a += r12 * r14 \n" + "bgt 2b @ } \n" + "3: \n" + "mov r5, r5, lsr #8 @ g >>= 8 \n" + "mov r6, r6, lsr #8 @ a >>= 8 \n" + "strb r5, [r0, #-2]! @ *--dst=a \n" + "strb r6, [r0, #1] @ *--dst=g \n" + "subs r3, r3, #1 @ i-- \n" + "bgt 1b @ \n" + "ldmfd r13!,{r4-r6,r9-r11,PC} @ pop, return to thumb \n" + "4:" + "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n" + "5:" + "ldr r4, [r2], #4 @ r4 = *contrib++ \n" + "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n" + "mov r5, #128 @ r5 = g = 128 \n" + "mov r6, #128 @ r6 = a = 128 \n" + "add r4, r1, r4, LSL #1 @ r4 = min = &src[2*r4] \n" + "cmp r9, #0 @ while (len-- > 0) \n" + "beq 7f @ { \n" + "6: \n" + "ldr r14,[r2], #4 @ r10 = *contrib++ \n" + "ldrb r11,[r4], #1 @ r11 = *min++ \n" + "ldrb r12,[r4], #1 @ r12 = *min++ \n" + "subs r9, r9, #1 @ r9 = len-- \n" + "mla r5, r14,r11,r5 @ g += r11 * r14 \n" + "mla r6, r14,r12,r6 @ a += r12 * r14 \n" + "bgt 6b @ } \n" + "7: \n" + "mov r5, r5, lsr #8 @ g >>= 8 \n" + "mov r6, r6, lsr #8 @ a >>= 8 \n" + "strb r5, [r0], #1 @ *dst++=g \n" + "strb r6, [r0], #1 @ *dst++=a \n" + "subs r3, r3, #1 @ i-- \n" + "bgt 5b @ \n" + "ldmfd r13!,{r4-r6,r9-r11,PC} @ pop, return to thumb \n" + ENTER_THUMB + ); +} + +static void +scale_row_to_temp4(unsigned char *dst, unsigned char *src, fz_weights *weights) +{ + asm volatile( + ENTER_ARM + "stmfd r13!,{r4-r11,r14} \n" + "@ r0 = dst \n" + "@ r1 = src \n" + "@ r2 = weights \n" + "ldr r12,[r2],#4 @ r12= flip \n" + "ldr r3, [r2],#20 @ r3 = count r2 = &index\n" + "ldr r4, [r2] @ r4 = index[0] \n" + "ldr r5,=0x00800080 @ r5 = rounding \n" + "ldr r6,=0x00FF00FF @ r7 = 0x00FF00FF \n" + "cmp r12,#0 @ if (flip) \n" + "beq 4f @ { \n" + "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n" + "add r0, r0, r3, LSL #2 @ dst += 4*count \n" + "1: \n" + "ldr r4, [r2], #4 @ r4 = *contrib++ \n" + "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n" + "mov r7, r5 @ r7 = b = rounding \n" + "mov r8, r5 @ r8 = a = rounding \n" + "add r4, r1, r4, LSL #2 @ r4 = min = &src[4*r4] \n" + "cmp r9, #0 @ while (len-- > 0) \n" + "beq 3f @ { \n" + "2: \n" + "ldr r11,[r4], #4 @ r11 = *min++ \n" + "ldr r10,[r2], #4 @ r10 = *contrib++ \n" + "subs r9, r9, #1 @ r9 = len-- \n" + "and r12,r6, r11 @ r12 = __22__00 \n" + "and r11,r6, r11,LSR #8 @ r11 = __33__11 \n" + "mla r7, r10,r12,r7 @ b += r14 * r10 \n" + "mla r8, r10,r11,r8 @ a += r11 * r10 \n" + "bgt 2b @ } \n" + "3: \n" + "and r7, r6, r7, lsr #8 @ r7 = __22__00 \n" + "bic r8, r8, r6 @ r8 = 33__11__ \n" + "orr r7, r7, r8 @ r7 = 33221100 \n" + "str r7, [r0, #-4]! @ *--dst=r \n" + "subs r3, r3, #1 @ i-- \n" + "bgt 1b @ \n" + "ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n" + "4: \n" + "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n" + "5: \n" + "ldr r4, [r2], #4 @ r4 = *contrib++ \n" + "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n" + "mov r7, r5 @ r7 = b = rounding \n" + "mov r8, r5 @ r8 = a = rounding \n" + "add r4, r1, r4, LSL #2 @ r4 = min = &src[4*r4] \n" + "cmp r9, #0 @ while (len-- > 0) \n" + "beq 7f @ { \n" + "6: \n" + "ldr r11,[r4], #4 @ r11 = *min++ \n" + "ldr r10,[r2], #4 @ r10 = *contrib++ \n" + "subs r9, r9, #1 @ r9 = len-- \n" + "and r12,r6, r11 @ r12 = __22__00 \n" + "and r11,r6, r11,LSR #8 @ r11 = __33__11 \n" + "mla r7, r10,r12,r7 @ b += r14 * r10 \n" + "mla r8, r10,r11,r8 @ a += r11 * r10 \n" + "bgt 6b @ } \n" + "7: \n" + "and r7, r6, r7, lsr #8 @ r7 = __22__00 \n" + "bic r8, r8, r6 @ r8 = 33__11__ \n" + "orr r7, r7, r8 @ r7 = 33221100 \n" + "str r7, [r0], #4 @ *dst++=r \n" + "subs r3, r3, #1 @ i-- \n" + "bgt 5b @ \n" + "ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n" + ENTER_THUMB + ); +} + +static void +scale_row_from_temp(unsigned char *dst, unsigned char *src, fz_weights *weights, int width, int row) +{ + asm volatile( + ENTER_ARM + "ldr r12,[r13] @ r12= row \n" + "add r2, r2, #24 @ r2 = weights->index \n" + "stmfd r13!,{r4-r11,r14} \n" + "@ r0 = dst \n" + "@ r1 = src \n" + "@ r2 = &weights->index[0] \n" + "@ r3 = width \n" + "@ r12= row \n" + "ldr r4, [r2, r12, LSL #2] @ r4 = index[row] \n" + "add r2, r2, #4 @ r2 = &index[1] \n" + "subs r6, r3, #4 @ r6 = x = width-4 \n" + "ldr r14,[r2, r4, LSL #2]! @ r2 = contrib = index[index[row]+1]\n" + " @ r14= len = *contrib \n" + "blt 4f @ while (x >= 0) { \n" +#ifndef ARCH_ARM_CAN_LOAD_UNALIGNED + "tst r3, #3 @ if ((r3 & 3) \n" + "tsteq r1, #3 @ || (r1 & 3)) \n" + "bne 4f @ can't do fast code \n" +#endif + "ldr r9, =0x00FF00FF @ r9 = 0x00FF00FF \n" + "1: \n" + "ldr r7, =0x00800080 @ r5 = val0 = round \n" + "stmfd r13!,{r1,r2,r7} @ stash r1,r2,r5 \n" + " @ r1 = min = src \n" + " @ r2 = contrib2-4 \n" + "movs r8, r14 @ r8 = len2 = len \n" + "mov r5, r7 @ r7 = val1 = round \n" + "ble 3f @ while (len2-- > 0) { \n" + "2: \n" + "ldr r12,[r1], r3 @ r12 = *min r5 = min += width\n" + "ldr r10,[r2, #4]! @ r10 = *contrib2++ \n" + "subs r8, r8, #1 @ len2-- \n" + "and r11,r9, r12 @ r11= __22__00 \n" + "and r12,r9, r12,LSR #8 @ r12= __33__11 \n" + "mla r5, r10,r11,r5 @ r5 = val0 += r11 * r10\n" + "mla r7, r10,r12,r7 @ r7 = val1 += r12 * r10\n" + "bgt 2b @ } \n" + "and r5, r9, r5, LSR #8 @ r5 = __22__00 \n" + "and r7, r7, r9, LSL #8 @ r7 = 33__11__ \n" + "orr r5, r5, r7 @ r5 = 33221100 \n" + "3: \n" + "ldmfd r13!,{r1,r2,r7} @ restore r1,r2,r7 \n" + "subs r6, r6, #4 @ x-- \n" + "add r1, r1, #4 @ src++ \n" + "str r5, [r0], #4 @ *dst++ = val \n" + "bge 1b @ \n" + "4: @ } (Less than 4 to go) \n" + "adds r6, r6, #4 @ r6 = x += 4 \n" + "beq 8f @ if (x == 0) done \n" + "5: \n" + "mov r5, r1 @ r5 = min = src \n" + "mov r7, #128 @ r7 = val = 128 \n" + "movs r8, r14 @ r8 = len2 = len \n" + "add r9, r2, #4 @ r9 = contrib2 \n" + "ble 7f @ while (len2-- > 0) { \n" + "6: \n" + "ldr r10,[r9], #4 @ r10 = *contrib2++ \n" + "ldrb r12,[r5], r3 @ r12 = *min r5 = min += width\n" + "subs r8, r8, #1 @ len2-- \n" + "@ stall r12 \n" + "mla r7, r10,r12,r7 @ val += r12 * r10 \n" + "bgt 6b @ } \n" + "7: \n" + "mov r7, r7, asr #8 @ r7 = val >>= 8 \n" + "subs r6, r6, #1 @ x-- \n" + "add r1, r1, #1 @ src++ \n" + "strb r7, [r0], #1 @ *dst++ = val \n" + "bgt 5b @ \n" + "8: \n" + "ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n" + ".ltorg \n" + ENTER_THUMB + ); +} +#else + +static void +scale_row_to_temp1(unsigned char *dst, unsigned char *src, fz_weights *weights) +{ + int *contrib = &weights->index[weights->index[0]]; + int len, i; + unsigned char *min; + + assert(weights->n == 1); + if (weights->flip) + { + dst += weights->count; + for (i=weights->count; i > 0; i--) + { + int val = 128; + min = &src[*contrib++]; + len = *contrib++; + while (len-- > 0) + { + val += *min++ * *contrib++; + } + *--dst = (unsigned char)(val>>8); + } + } + else + { + for (i=weights->count; i > 0; i--) + { + int val = 128; + min = &src[*contrib++]; + len = *contrib++; + while (len-- > 0) + { + val += *min++ * *contrib++; + } + *dst++ = (unsigned char)(val>>8); + } + } +} + +static void +scale_row_to_temp2(unsigned char *dst, unsigned char *src, fz_weights *weights) +{ + int *contrib = &weights->index[weights->index[0]]; + int len, i; + unsigned char *min; + + assert(weights->n == 2); + if (weights->flip) + { + dst += 2*weights->count; + for (i=weights->count; i > 0; i--) + { + int c1 = 128; + int c2 = 128; + min = &src[2 * *contrib++]; + len = *contrib++; + while (len-- > 0) + { + c1 += *min++ * *contrib; + c2 += *min++ * *contrib++; + } + *--dst = (unsigned char)(c2>>8); + *--dst = (unsigned char)(c1>>8); + } + } + else + { + for (i=weights->count; i > 0; i--) + { + int c1 = 128; + int c2 = 128; + min = &src[2 * *contrib++]; + len = *contrib++; + while (len-- > 0) + { + c1 += *min++ * *contrib; + c2 += *min++ * *contrib++; + } + *dst++ = (unsigned char)(c1>>8); + *dst++ = (unsigned char)(c2>>8); + } + } +} + +static void +scale_row_to_temp4(unsigned char *dst, unsigned char *src, fz_weights *weights) +{ + int *contrib = &weights->index[weights->index[0]]; + int len, i; + unsigned char *min; + + assert(weights->n == 4); + if (weights->flip) + { + dst += 4*weights->count; + for (i=weights->count; i > 0; i--) + { + int r = 128; + int g = 128; + int b = 128; + int a = 128; + min = &src[4 * *contrib++]; + len = *contrib++; + while (len-- > 0) + { + r += *min++ * *contrib; + g += *min++ * *contrib; + b += *min++ * *contrib; + a += *min++ * *contrib++; + } + *--dst = (unsigned char)(a>>8); + *--dst = (unsigned char)(b>>8); + *--dst = (unsigned char)(g>>8); + *--dst = (unsigned char)(r>>8); + } + } + else + { + for (i=weights->count; i > 0; i--) + { + int r = 128; + int g = 128; + int b = 128; + int a = 128; + min = &src[4 * *contrib++]; + len = *contrib++; + while (len-- > 0) + { + r += *min++ * *contrib; + g += *min++ * *contrib; + b += *min++ * *contrib; + a += *min++ * *contrib++; + } + *dst++ = (unsigned char)(r>>8); + *dst++ = (unsigned char)(g>>8); + *dst++ = (unsigned char)(b>>8); + *dst++ = (unsigned char)(a>>8); + } + } +} + +static void +scale_row_from_temp(unsigned char *dst, unsigned char *src, fz_weights *weights, int width, int row) +{ + int *contrib = &weights->index[weights->index[row]]; + int len, x; + + contrib++; /* Skip min */ + len = *contrib++; + for (x=width; x > 0; x--) + { + unsigned char *min = src; + int val = 128; + int len2 = len; + int *contrib2 = contrib; + + while (len2-- > 0) + { + val += *min * *contrib2++; + min += width; + } + *dst++ = (unsigned char)(val>>8); + src++; + } +} +#endif + +#ifdef SINGLE_PIXEL_SPECIALS +static void +duplicate_single_pixel(unsigned char *dst, unsigned char *src, int n, int w, int h) +{ + int i; + + for (i = n; i > 0; i--) + *dst++ = *src++; + for (i = (w*h-1)*n; i > 0; i--) + { + *dst = dst[-n]; + dst++; + } +} + +static void +scale_single_row(unsigned char *dst, unsigned char *src, fz_weights *weights, int src_w, int h) +{ + int *contrib = &weights->index[weights->index[0]]; + int min, len, i, j, n; + int tmp[FZ_MAX_COLORS]; + + n = weights->n; + /* Scale a single row */ + for (j = 0; j < n; j++) + tmp[j] = 128; + if (weights->flip) + { + dst += (weights->count-1)*n; + for (i=weights->count; i > 0; i--) + { + min = *contrib++; + len = *contrib++; + min *= n; + while (len-- > 0) + { + for (j = 0; j < n; j++) + tmp[j] += src[min++] * *contrib; + contrib++; + } + for (j = 0; j < n; j++) + { + *dst++ = (unsigned char)(tmp[j]>>8); + tmp[j] = 128; + } + dst -= 2*n; + } + dst += n * (weights->count+1); + } + else + { + for (i=weights->count; i > 0; i--) + { + min = *contrib++; + len = *contrib++; + min *= n; + while (len-- > 0) + { + for (j = 0; j < n; j++) + tmp[j] += src[min++] * *contrib; + contrib++; + } + for (j = 0; j < n; j++) + { + *dst++ = (unsigned char)(tmp[j]>>8); + tmp[j] = 128; + } + } + } + /* And then duplicate it h times */ + n *= weights->count; + while (--h > 0) + { + memcpy(dst, dst-n, n); + dst += n; + } +} + +static void +scale_single_col(unsigned char *dst, unsigned char *src, fz_weights *weights, int src_w, int n, int w, int flip_y) +{ + int *contrib = &weights->index[weights->index[0]]; + int min, len, i, j; + int tmp[FZ_MAX_COLORS]; + + for (j = 0; j < n; j++) + tmp[j] = 128; + if (flip_y) + { + src_w = (src_w-1)*n; + w = (w-1)*n; + for (i=weights->count; i > 0; i--) + { + /* Scale the next pixel in the column */ + min = *contrib++; + len = *contrib++; + min = src_w-min*n; + while (len-- > 0) + { + for (j = 0; j < n; j++) + tmp[j] += src[src_w-min+j] * *contrib; + contrib++; + } + for (j = 0; j < n; j++) + { + *dst++ = (unsigned char)(tmp[j]>>8); + tmp[j] = 128; + } + /* And then duplicate it across the row */ + for (j = w; j > 0; j--) + { + *dst = dst[-n]; + dst++; + } + } + } + else + { + w = (w-1)*n; + for (i=weights->count; i > 0; i--) + { + /* Scale the next pixel in the column */ + min = *contrib++; + len = *contrib++; + min *= n; + while (len-- > 0) + { + for (j = 0; j < n; j++) + tmp[j] += src[min++] * *contrib; + contrib++; + } + for (j = 0; j < n; j++) + { + *dst++ = (unsigned char)(tmp[j]>>8); + tmp[j] = 128; + } + /* And then duplicate it across the row */ + for (j = w; j > 0; j--) + { + *dst = dst[-n]; + dst++; + } + } + } +} +#endif /* SINGLE_PIXEL_SPECIALS */ + +fz_pixmap * +fz_scale_pixmap(fz_context *ctx, fz_pixmap *src, float x, float y, float w, float h, fz_irect *clip) +{ + return fz_scale_pixmap_cached(ctx, src, x, y, w, h, clip, NULL, NULL); +} + +fz_pixmap * +fz_scale_pixmap_cached(fz_context *ctx, fz_pixmap *src, float x, float y, float w, float h, const fz_irect *clip, fz_scale_cache *cache_x, fz_scale_cache *cache_y) +{ + fz_scale_filter *filter = &fz_scale_filter_simple; + fz_weights *contrib_rows = NULL; + fz_weights *contrib_cols = NULL; + fz_pixmap *output = NULL; + unsigned char *temp = NULL; + int max_row, temp_span, temp_rows, row; + int dst_w_int, dst_h_int, dst_x_int, dst_y_int; + int flip_x, flip_y; + fz_rect patch; + + fz_var(contrib_cols); + fz_var(contrib_rows); + + DBUG(("Scale: (%d,%d) to (%g,%g) at (%g,%g)\n",src->w,src->h,w,h,x,y)); + + /* Avoid extreme scales where overflows become problematic. */ + if (w > (1<<24) || h > (1<<24) || w < -(1<<24) || h < -(1<<24)) + return NULL; + + /* Clamp small ranges of w and h */ + if (w <= -1) + { + } + else if (w < 0) + { + w = -1; + } + else if (w < 1) + { + w = 1; + } + if (h <= -1) + { + } + else if (h < 0) + { + h = -1; + } + else if (h < 1) + { + h = 1; + } + + /* Find the destination bbox, width/height, and sub pixel offset, + * allowing for whether we're flipping or not. */ + /* The (x,y) position given describes where the top left corner + * of the source image should be mapped to (i.e. where (0,0) in image + * space ends up). Also there are differences in the way we scale + * horizontally and vertically. When scaling rows horizontally, we + * always read forwards through the source, and store either forwards + * or in reverse as required. When scaling vertically, we always store + * out forwards, but may feed source rows in in a different order. + * + * Consider the image rectangle 'r' to which the image is mapped, + * and the (possibly) larger rectangle 'R', given by expanding 'r' to + * complete pixels. + * + * x can either be r.xmin-R.xmin or R.xmax-r.xmax depending on whether + * the image is x flipped or not. Whatever happens 0 <= x < 1. + * y is always R.ymax - r.ymax. + */ + /* dst_x_int is calculated to be the left of the scaled image, and + * x (the sub pixel offset) is the distance in from either the left + * or right pixel expanded edge. */ + flip_x = (w < 0); + if (flip_x) + { + float tmp; + w = -w; + dst_x_int = floorf(x-w); + tmp = ceilf(x); + dst_w_int = (int)tmp; + x = tmp - x; + dst_w_int -= dst_x_int; + } + else + { + dst_x_int = floorf(x); + x -= (float)dst_x_int; + dst_w_int = (int)ceilf(x + w); + } + /* dst_y_int is calculated to be the top of the scaled image, and + * y (the sub pixel offset) is the distance in from either the top + * or bottom pixel expanded edge. + */ + flip_y = (h < 0); + if (flip_y) + { + float tmp; + h = -h; + dst_y_int = floorf(y-h); + tmp = ceilf(y); + dst_h_int = (int)tmp; + y = tmp - y; + dst_h_int -= dst_y_int; + } + else + { + dst_y_int = floorf(y); + y -= (float)dst_y_int; + dst_h_int = (int)ceilf(y + h); + } + + DBUG(("Result image: (%d,%d) at (%d,%d) (subpix=%g,%g)\n", dst_w_int, dst_h_int, dst_x_int, dst_y_int, x, y)); + + /* Step 0: Calculate the patch */ + patch.x0 = 0; + patch.y0 = 0; + patch.x1 = dst_w_int; + patch.y1 = dst_h_int; + if (clip) + { + if (flip_x) + { + if (dst_x_int + dst_w_int > clip->x1) + patch.x0 = dst_x_int + dst_w_int - clip->x1; + if (clip->x0 > dst_x_int) + { + patch.x1 = dst_w_int - (clip->x0 - dst_x_int); + dst_x_int = clip->x0; + } + } + else + { + if (dst_x_int + dst_w_int > clip->x1) + patch.x1 = clip->x1 - dst_x_int; + if (clip->x0 > dst_x_int) + { + patch.x0 = clip->x0 - dst_x_int; + dst_x_int += patch.x0; + } + } + + if (flip_y) + { + if (dst_y_int + dst_h_int > clip->y1) + patch.y1 = clip->y1 - dst_y_int; + if (clip->y0 > dst_y_int) + { + patch.y0 = clip->y0 - dst_y_int; + dst_y_int = clip->y0; + } + } + else + { + if (dst_y_int + dst_h_int > clip->y1) + patch.y1 = clip->y1 - dst_y_int; + if (clip->y0 > dst_y_int) + { + patch.y0 = clip->y0 - dst_y_int; + dst_y_int += patch.y0; + } + } + } + if (patch.x0 >= patch.x1 || patch.y0 >= patch.y1) + return NULL; + + fz_try(ctx) + { + /* Step 1: Calculate the weights for columns and rows */ +#ifdef SINGLE_PIXEL_SPECIALS + if (src->w == 1) + contrib_cols = NULL; + else +#endif /* SINGLE_PIXEL_SPECIALS */ + contrib_cols = make_weights(ctx, src->w, x, w, filter, 0, dst_w_int, patch.x0, patch.x1, src->n, flip_x, cache_x); +#ifdef SINGLE_PIXEL_SPECIALS + if (src->h == 1) + contrib_rows = NULL; + else +#endif /* SINGLE_PIXEL_SPECIALS */ + contrib_rows = make_weights(ctx, src->h, y, h, filter, 1, dst_h_int, patch.y0, patch.y1, src->n, flip_y, cache_y); + + output = fz_new_pixmap(ctx, src->colorspace, patch.x1 - patch.x0, patch.y1 - patch.y0); + } + fz_catch(ctx) + { + if (!cache_x) + fz_free(ctx, contrib_cols); + if (!cache_y) + fz_free(ctx, contrib_rows); + fz_rethrow(ctx); + } + output->x = dst_x_int; + output->y = dst_y_int; + + /* Step 2: Apply the weights */ +#ifdef SINGLE_PIXEL_SPECIALS + if (!contrib_rows) + { + /* Only 1 source pixel high. */ + if (!contrib_cols) + { + /* Only 1 pixel in the entire image! */ + duplicate_single_pixel(output->samples, src->samples, src->n, patch.x1-patch.x0, patch.y1-patch.y0); + } + else + { + /* Scale the row once, then copy it. */ + scale_single_row(output->samples, src->samples, contrib_cols, src->w, patch.y1-patch.y0); + } + } + else if (!contrib_cols) + { + /* Only 1 source pixel wide. Scale the col and duplicate. */ + scale_single_col(output->samples, src->samples, contrib_rows, src->h, src->n, patch.x1-patch.x0, flip_y); + } + else +#endif /* SINGLE_PIXEL_SPECIALS */ + { + void (*row_scale)(unsigned char *dst, unsigned char *src, fz_weights *weights); + + temp_span = contrib_cols->count * src->n; + temp_rows = contrib_rows->max_len; + if (temp_span <= 0 || temp_rows > INT_MAX / temp_span) + goto cleanup; + fz_try(ctx) + { + temp = fz_calloc(ctx, temp_span*temp_rows, sizeof(unsigned char)); + } + fz_catch(ctx) + { + fz_drop_pixmap(ctx, output); + if (!cache_x) + fz_free(ctx, contrib_cols); + if (!cache_y) + fz_free(ctx, contrib_rows); + fz_rethrow(ctx); + } + switch (src->n) + { + default: + row_scale = scale_row_to_temp; + break; + case 1: /* Image mask case */ + row_scale = scale_row_to_temp1; + break; + case 2: /* Greyscale with alpha case */ + row_scale = scale_row_to_temp2; + break; + case 4: /* RGBA */ + row_scale = scale_row_to_temp4; + break; + } + max_row = contrib_rows->index[contrib_rows->index[0]]; + for (row = 0; row < contrib_rows->count; row++) + { + /* + Which source rows do we need to have scaled into the + temporary buffer in order to be able to do the final + scale? + */ + int row_index = contrib_rows->index[row]; + int row_min = contrib_rows->index[row_index++]; + int row_len = contrib_rows->index[row_index++]; + while (max_row < row_min+row_len) + { + /* Scale another row */ + assert(max_row < src->h); + DBUG(("scaling row %d to temp\n", max_row)); + (*row_scale)(&temp[temp_span*(max_row % temp_rows)], &src->samples[(flip_y ? (src->h-1-max_row): max_row)*src->w*src->n], contrib_cols); + max_row++; + } + + DBUG(("scaling row %d from temp\n", row)); + scale_row_from_temp(&output->samples[row*output->w*output->n], temp, contrib_rows, temp_span, row); + } + fz_free(ctx, temp); + } + +cleanup: + if (!cache_y) + fz_free(ctx, contrib_rows); + if (!cache_x) + fz_free(ctx, contrib_cols); + return output; +} + +void +fz_free_scale_cache(fz_context *ctx, fz_scale_cache *sc) +{ + if (!sc) + return; + fz_free(ctx, sc->weights); + fz_free(ctx, sc); +} + +fz_scale_cache * +fz_new_scale_cache(fz_context *ctx) +{ + return fz_malloc_struct(ctx, fz_scale_cache); +} diff --git a/source/fitz/draw-scale.c b/source/fitz/draw-scale.c new file mode 100644 index 00000000..553d7830 --- /dev/null +++ b/source/fitz/draw-scale.c @@ -0,0 +1,1541 @@ +/* +This code does smooth scaling of a pixmap. + +This function returns a new pixmap representing the area starting at (0,0) +given by taking the source pixmap src, scaling it to width w, and height h, +and then positioning it at (frac(x),frac(y)). +*/ + +#include "mupdf/fitz.h" +#include "draw-imp.h" + +/* Do we special case handling of single pixel high/wide images? The + * 'purest' handling is given by not special casing them, but certain + * files that use such images 'stack' them to give full images. Not + * special casing them results in then being fainter and giving noticeable + * rounding errors. + */ +#define SINGLE_PIXEL_SPECIALS + +/* If we're compiling as thumb code, then we need to tell the compiler + * to enter and exit ARM mode around our assembly sections. If we move + * the ARM functions to a separate file and arrange for it to be compiled + * without thumb mode, we can save some time on entry. + */ +#ifdef ARCH_ARM +#ifdef ARCH_THUMB +#define ENTER_ARM ".balign 4\nmov r12,pc\nbx r12\n0:.arm\n" +#define ENTER_THUMB "9:.thumb\n" +#else +#define ENTER_ARM +#define ENTER_THUMB +#endif +#endif + +#ifdef DEBUG_SCALING +#ifdef WIN32 +#include <windows.h> +static void debug_print(const char *fmt, ...) +{ + va_list args; + char text[256]; + va_start(args, fmt); + vsprintf(text, fmt, args); + va_end(args); + OutputDebugStringA(text); + printf(text); +} +#else +static void debug_print(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} +#endif +#endif +#ifdef DEBUG_SCALING +#define DBUG(A) debug_print A +#else +#define DBUG(A) do {} while(0==1) +#endif + +/* +Consider a row of source samples, src, of width src_w, positioned at x, +scaled to width dst_w. + +src[i] is centred at: x + (i + 0.5)*dst_w/src_w + +Therefore the distance between the centre of the jth output pixel and +the centre of the ith source sample is: + +dist[j,i] = j + 0.5 - (x + (i + 0.5)*dst_w/src_w) + +When scaling up, therefore: + +dst[j] = SUM(filter(dist[j,i]) * src[i]) + (for all ints i) + +This can be simplified by noticing that filters are only non zero within +a given filter width (henceforth called W). So: + +dst[j] = SUM(filter(dist[j,i]) * src[i]) + (for ints i, s.t. (j*src_w/dst_w)-W < i < (j*src_w/dst_w)+W) + +When scaling down, each filtered source sample is stretched to be wider +to avoid aliasing issues. This effectively reduces the distance between +centres. + +dst[j] = SUM(filter(dist[j,i] * F) * F * src[i]) + (where F = dst_w/src_w) + (for ints i, s.t. (j-W)/F < i < (j+W)/F) + +*/ + +typedef struct fz_scale_filter_s fz_scale_filter; + +struct fz_scale_filter_s +{ + int width; + float (*fn)(fz_scale_filter *, float); +}; + +/* Image scale filters */ + +static float +triangle(fz_scale_filter *filter, float f) +{ + if (f >= 1) + return 0; + return 1-f; +} + +static float +box(fz_scale_filter *filter, float f) +{ + if (f >= 0.5f) + return 0; + return 1; +} + +static float +simple(fz_scale_filter *filter, float x) +{ + if (x >= 1) + return 0; + return 1 + (2*x - 3)*x*x; +} + +static float +lanczos2(fz_scale_filter *filter, float x) +{ + if (x >= 2) + return 0; + return sinf(M_PI*x) * sinf(M_PI*x/2) / (M_PI*x) / (M_PI*x/2); +} + +static float +lanczos3(fz_scale_filter *filter, float f) +{ + if (f >= 3) + return 0; + return sinf(M_PI*f) * sinf(M_PI*f/3) / (M_PI*f) / (M_PI*f/3); +} + +/* +The Mitchell family of filters is defined: + + f(x) = 1 { (12-9B-6C)x^3 + (-18+12B+6C)x^2 + (6-2B) for x < 1 + - { + 6 { (-B-6C)x^3+(6B+30C)x^2+(-12B-48C)x+(8B+24C) for 1<=x<=2 + +The 'best' ones lie along the line B+2C = 1. +The literature suggests that B=1/3, C=1/3 is best. + + f(x) = 1 { (12-3-2)x^3 - (-18+4+2)x^2 + (16/3) for x < 1 + - { + 6 { (-7/3)x^3 + 12x^2 - 20x + (32/3) for 1<=x<=2 + + f(x) = 1 { 21x^3 - 36x^2 + 16 for x < 1 + - { + 18{ -7x^3 + 36x^2 - 60x + 32 for 1<=x<=2 +*/ + +static float +mitchell(fz_scale_filter *filter, float x) +{ + if (x >= 2) + return 0; + if (x >= 1) + return (32 + x*(-60 + x*(36 - 7*x)))/18; + return (16 + x*x*(-36 + 21*x))/18; +} + +fz_scale_filter fz_scale_filter_box = { 1, box }; +fz_scale_filter fz_scale_filter_triangle = { 1, triangle }; +fz_scale_filter fz_scale_filter_simple = { 1, simple }; +fz_scale_filter fz_scale_filter_lanczos2 = { 2, lanczos2 }; +fz_scale_filter fz_scale_filter_lanczos3 = { 3, lanczos3 }; +fz_scale_filter fz_scale_filter_mitchell = { 2, mitchell }; + +/* +We build ourselves a set of tables to contain the precalculated weights +for a given set of scale settings. + +The first dst_w entries in index are the index into index of the +sets of weight for each destination pixel. + +Each of the sets of weights is a set of values consisting of: + the minimum source pixel index used for this destination pixel + the number of weights used for this destination pixel + the weights themselves + +So to calculate dst[i] we do the following: + + weights = &index[index[i]]; + min = *weights++; + len = *weights++; + dst[i] = 0; + while (--len > 0) + dst[i] += src[min++] * *weights++ + +in addition, we guarantee that at the end of this process weights will now +point to the weights value for dst pixel i+1. + +In the simplest version of this algorithm, we would scale the whole image +horizontally first into a temporary buffer, then scale that temporary +buffer again vertically to give us our result. Using such a simple +algorithm would mean that could use the same style of weights for both +horizontal and vertical scaling. + +Unfortunately, this would also require a large temporary buffer, +particularly in the case where we are scaling up. + +We therefore modify the algorithm as follows; we scale scanlines from the +source image horizontally into a temporary buffer, until we have all the +contributors for a given output scanline. We then produce that output +scanline from the temporary buffer. In this way we restrict the height +of the temporary buffer to a small fraction of the final size. + +Unfortunately, this means that the pseudo code for recombining a +scanline of fully scaled pixels is as follows: + + weights = &index[index[y]]; + min = *weights++; + len = *weights++; + for (x=0 to dst_w) + min2 = min + len2 = len + weights2 = weights + dst[x] = 0; + while (--len2 > 0) + dst[x] += temp[x][(min2++) % tmp_buf_height] * *weights2++ + +i.e. it requires a % operation for every source pixel - this is typically +expensive. + +To avoid this, we alter the order in which vertical weights are stored, +so that they are ordered in the same order as the temporary buffer lines +would appear. This simplifies the algorithm to: + + weights = &index[index[y]]; + min = *weights++; + len = *weights++; + for (x=0 to dst_w) + min2 = 0 + len2 = len + weights2 = weights + dst[x] = 0; + while (--len2 > 0) + dst[x] += temp[i][min2++] * *weights2++ + +This means that len may be larger than it needs to be (due to the +possible inclusion of a zero weight row or two), but in practise this +is only an increase of 1 or 2 at worst. + +We implement this by generating the weights as normal (but ensuring we +leave enough space) and then reordering afterwards. + +*/ + +typedef struct fz_weights_s fz_weights; + +/* This structure is accessed from ARM code - bear this in mind before + * altering it! */ +struct fz_weights_s +{ + int flip; /* true if outputting reversed */ + int count; /* number of output pixels we have records for in this table */ + int max_len; /* Maximum number of weights for any one output pixel */ + int n; /* number of components (src->n) */ + int new_line; /* True if no weights for the current output pixel */ + int patch_l; /* How many output pixels we skip over */ + int index[1]; +}; + +struct fz_scale_cache_s +{ + int src_w; + float x; + float dst_w; + fz_scale_filter *filter; + int vertical; + int dst_w_int; + int patch_l; + int patch_r; + int n; + int flip; + fz_weights *weights; +}; + +static fz_weights * +new_weights(fz_context *ctx, fz_scale_filter *filter, int src_w, float dst_w, int patch_w, int n, int flip, int patch_l) +{ + int max_len; + fz_weights *weights; + + if (src_w > dst_w) + { + /* Scaling down, so there will be a maximum of + * 2*filterwidth*src_w/dst_w src pixels + * contributing to each dst pixel. */ + max_len = (int)ceilf((2 * filter->width * src_w)/dst_w); + if (max_len > src_w) + max_len = src_w; + } + else + { + /* Scaling up, so there will be a maximum of + * 2*filterwidth src pixels contributing to each dst pixel. + */ + max_len = 2 * filter->width; + } + /* We need the size of the struct, + * plus patch_w*sizeof(int) for the index + * plus (2+max_len)*sizeof(int) for the weights + * plus room for an extra set of weights for reordering. + */ + weights = fz_malloc(ctx, sizeof(*weights)+(max_len+3)*(patch_w+1)*sizeof(int)); + if (!weights) + return NULL; + weights->count = -1; + weights->max_len = max_len; + weights->index[0] = patch_w; + weights->n = n; + weights->patch_l = patch_l; + weights->flip = flip; + return weights; +} + +/* j is destination pixel in the patch_l..patch_l+patch_w range */ +static void +init_weights(fz_weights *weights, int j) +{ + int index; + + j -= weights->patch_l; + assert(weights->count == j-1); + weights->count++; + weights->new_line = 1; + if (j == 0) + index = weights->index[0]; + else + { + index = weights->index[j-1]; + index += 2 + weights->index[index+1]; + } + weights->index[j] = index; /* row pointer */ + weights->index[index] = 0; /* min */ + weights->index[index+1] = 0; /* len */ +} + +static void +add_weight(fz_weights *weights, int j, int i, fz_scale_filter *filter, + float x, float F, float G, int src_w, float dst_w) +{ + float dist = j - x + 0.5f - ((i + 0.5f)*dst_w/src_w); + float f; + int min, len, index, weight; + + dist *= G; + if (dist < 0) + dist = -dist; + f = filter->fn(filter, dist)*F; + weight = (int)(256*f+0.5f); + + /* Ensure i is in range */ + if (i < 0 || i >= src_w) + return; + if (weight == 0) + { + /* We add a fudge factor here to allow for extreme downscales + * where all the weights round to 0. Ensure that at least one + * (arbitrarily the first one) is non zero. */ + if (weights->new_line && f > 0) + weight = 1; + else + return; + } + + DBUG(("add_weight[%d][%d] = %d(%g) dist=%g\n",j,i,weight,f,dist)); + + /* Move j from patch_l...patch_l+patch_w range to 0..patch_w range */ + j -= weights->patch_l; + if (weights->new_line) + { + /* New line */ + weights->new_line = 0; + index = weights->index[j]; /* row pointer */ + weights->index[index] = i; /* min */ + weights->index[index+1] = 0; /* len */ + } + index = weights->index[j]; + min = weights->index[index++]; + len = weights->index[index++]; + while (i < min) + { + /* This only happens in rare cases, but we need to insert + * one earlier. In exceedingly rare cases we may need to + * insert more than one earlier. */ + int k; + + for (k = len; k > 0; k--) + { + weights->index[index+k] = weights->index[index+k-1]; + } + weights->index[index] = 0; + min--; + len++; + weights->index[index-2] = min; + weights->index[index-1] = len; + } + if (i-min >= len) + { + /* The usual case */ + while (i-min >= ++len) + { + weights->index[index+len-1] = 0; + } + assert(len-1 == i-min); + weights->index[index+i-min] = weight; + weights->index[index-1] = len; + assert(len <= weights->max_len); + } + else + { + /* Infrequent case */ + weights->index[index+i-min] += weight; + } +} + +static void +reorder_weights(fz_weights *weights, int j, int src_w) +{ + int idx = weights->index[j - weights->patch_l]; + int min = weights->index[idx++]; + int len = weights->index[idx++]; + int max = weights->max_len; + int tmp = idx+max; + int i, off; + + /* Copy into the temporary area */ + memcpy(&weights->index[tmp], &weights->index[idx], sizeof(int)*len); + + /* Pad out if required */ + assert(len <= max); + assert(min+len <= src_w); + off = 0; + if (len < max) + { + memset(&weights->index[tmp+len], 0, sizeof(int)*(max-len)); + len = max; + if (min + len > src_w) + { + off = min + len - src_w; + min = src_w - len; + weights->index[idx-2] = min; + } + weights->index[idx-1] = len; + } + + /* Copy back into the proper places */ + for (i = 0; i < len; i++) + { + weights->index[idx+((min+i+off) % max)] = weights->index[tmp+i]; + } +} + +/* Due to rounding and edge effects, the sums for the weights sometimes don't + * add up to 256. This causes visible rendering effects. Therefore, we take + * pains to ensure that they 1) never exceed 256, and 2) add up to exactly + * 256 for all pixels that are completely covered. See bug #691629. */ +static void +check_weights(fz_weights *weights, int j, int w, float x, float wf) +{ + int idx, len; + int sum = 0; + int max = -256; + int maxidx = 0; + int i; + + idx = weights->index[j - weights->patch_l]; + idx++; /* min */ + len = weights->index[idx++]; + + for(i=0; i < len; i++) + { + int v = weights->index[idx++]; + sum += v; + if (v > max) + { + max = v; + maxidx = idx; + } + } + /* If we aren't the first or last pixel, OR if the sum is too big + * then adjust it. */ + if (((j != 0) && (j != w-1)) || (sum > 256)) + weights->index[maxidx-1] += 256-sum; + /* Otherwise, if we are the first pixel, and it's fully covered, then + * adjust it. */ + else if ((j == 0) && (x < 0.0001F) && (sum != 256)) + weights->index[maxidx-1] += 256-sum; + /* Finally, if we are the last pixel, and it's fully covered, then + * adjust it. */ + else if ((j == w-1) && ((float)w-wf < 0.0001F) && (sum != 256)) + weights->index[maxidx-1] += 256-sum; + DBUG(("total weight %d = %d\n", j, sum)); +} + +static fz_weights * +make_weights(fz_context *ctx, int src_w, float x, float dst_w, fz_scale_filter *filter, int vertical, int dst_w_int, int patch_l, int patch_r, int n, int flip, fz_scale_cache *cache) +{ + fz_weights *weights; + float F, G; + float window; + int j; + + if (cache) + { + if (cache->src_w == src_w && cache->x == x && cache->dst_w == dst_w && + cache->filter == filter && cache->vertical == vertical && + cache->dst_w_int == dst_w_int && + cache->patch_l == patch_l && cache->patch_r == patch_r && + cache->n == n && cache->flip == flip) + { + return cache->weights; + } + cache->src_w = src_w; + cache->x = x; + cache->dst_w = dst_w; + cache->filter = filter; + cache->vertical = vertical; + cache->dst_w_int = dst_w_int; + cache->patch_l = patch_l; + cache->patch_r = patch_r; + cache->n = n; + cache->flip = flip; + fz_free(ctx, cache->weights); + cache->weights = NULL; + } + + if (dst_w < src_w) + { + /* Scaling down */ + F = dst_w / src_w; + G = 1; + } + else + { + /* Scaling up */ + F = 1; + G = src_w / dst_w; + } + window = filter->width / F; + DBUG(("make_weights src_w=%d x=%g dst_w=%g patch_l=%d patch_r=%d F=%g window=%g\n", src_w, x, dst_w, patch_l, patch_r, F, window)); + weights = new_weights(ctx, filter, src_w, dst_w, patch_r-patch_l, n, flip, patch_l); + if (!weights) + return NULL; + for (j = patch_l; j < patch_r; j++) + { + /* find the position of the centre of dst[j] in src space */ + float centre = (j - x + 0.5f)*src_w/dst_w - 0.5f; + int l, r; + l = ceilf(centre - window); + r = floorf(centre + window); + DBUG(("%d: centre=%g l=%d r=%d\n", j, centre, l, r)); + init_weights(weights, j); + for (; l <= r; l++) + { + add_weight(weights, j, l, filter, x, F, G, src_w, dst_w); + } + check_weights(weights, j, dst_w_int, x, dst_w); + if (vertical) + { + reorder_weights(weights, j, src_w); + } + } + weights->count++; /* weights->count = dst_w_int now */ + if (cache) + { + cache->weights = weights; + } + return weights; +} + +static void +scale_row_to_temp(int *dst, unsigned char *src, fz_weights *weights) +{ + int *contrib = &weights->index[weights->index[0]]; + int len, i, j, n; + unsigned char *min; + + n = weights->n; + if (weights->flip) + { + dst += (weights->count-1)*n; + for (i=weights->count; i > 0; i--) + { + min = &src[n * *contrib++]; + len = *contrib++; + for (j = 0; j < n; j++) + dst[j] = 0; + while (len-- > 0) + { + for (j = n; j > 0; j--) + *dst++ += *min++ * *contrib; + dst -= n; + contrib++; + } + dst -= n; + } + } + else + { + for (i=weights->count; i > 0; i--) + { + min = &src[n * *contrib++]; + len = *contrib++; + for (j = 0; j < n; j++) + dst[j] = 0; + while (len-- > 0) + { + for (j = n; j > 0; j--) + *dst++ += *min++ * *contrib; + dst -= n; + contrib++; + } + dst += n; + } + } +} + +#ifdef ARCH_ARM + +static void +scale_row_to_temp1(int *dst, unsigned char *src, fz_weights *weights) +__attribute__((naked)); + +static void +scale_row_to_temp2(int *dst, unsigned char *src, fz_weights *weights) +__attribute__((naked)); + +static void +scale_row_to_temp4(int *dst, unsigned char *src, fz_weights *weights) +__attribute__((naked)); + +static void +scale_row_from_temp(unsigned char *dst, int *src, fz_weights *weights, int width, int row) +__attribute__((naked)); + +static void +scale_row_to_temp1(int *dst, unsigned char *src, fz_weights *weights) +{ + /* possible optimisation in here; unroll inner loops to avoid stall. */ + asm volatile( + ENTER_ARM + "stmfd r13!,{r4-r5,r9,r14} \n" + "@ r0 = dst \n" + "@ r1 = src \n" + "@ r2 = weights \n" + "ldr r12,[r2],#4 @ r12= flip \n" + "ldr r3, [r2],#20 @ r3 = count r2 = &index\n" + "ldr r4, [r2] @ r4 = index[0] \n" + "cmp r12,#0 @ if (flip) \n" + "beq 4f @ { \n" + "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n" + "add r0, r0, r3, LSL #2 @ dst += count \n" + "1: \n" + "ldr r4, [r2], #4 @ r4 = *contrib++ \n" + "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n" + "mov r5, #0 @ r5 = a = 0 \n" + "add r4, r1, r4 @ r4 = min = &src[r4] \n" + "cmp r9, #0 @ while (len-- > 0) \n" + "beq 3f @ { \n" + "2: \n" + "ldr r12,[r2], #4 @ r12 = *contrib++ \n" + "ldrb r14,[r4], #1 @ r14 = *min++ \n" + "subs r9, r9, #1 @ r9 = len-- \n" + "@stall on r14 \n" + "mla r5, r12,r14,r5 @ g += r14 * r12 \n" + "bgt 2b @ } \n" + "3: \n" + "str r5,[r0, #-4]! @ *--dst=a \n" + "subs r3, r3, #1 @ i-- \n" + "bgt 1b @ \n" + "ldmfd r13!,{r4-r5,r9,PC} @ pop, return to thumb \n" + "4:" + "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n" + "5:" + "ldr r4, [r2], #4 @ r4 = *contrib++ \n" + "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n" + "mov r5, #0 @ r5 = a = 0 \n" + "add r4, r1, r4 @ r4 = min = &src[r4] \n" + "cmp r9, #0 @ while (len-- > 0) \n" + "beq 7f @ { \n" + "6: \n" + "ldr r12,[r2], #4 @ r12 = *contrib++ \n" + "ldrb r14,[r4], #1 @ r14 = *min++ \n" + "subs r9, r9, #1 @ r9 = len-- \n" + "@stall on r14 \n" + "mla r5, r12,r14,r5 @ a += r14 * r12 \n" + "bgt 6b @ } \n" + "7: \n" + "str r5, [r0], #4 @ *dst++=a \n" + "subs r3, r3, #1 @ i-- \n" + "bgt 5b @ \n" + "ldmfd r13!,{r4-r5,r9,PC} @ pop, return to thumb \n" + ENTER_THUMB + ); +} + +static void +scale_row_to_temp2(int *dst, unsigned char *src, fz_weights *weights) +{ + asm volatile( + ENTER_ARM + "stmfd r13!,{r4-r6,r9-r11,r14} \n" + "@ r0 = dst \n" + "@ r1 = src \n" + "@ r2 = weights \n" + "ldr r12,[r2],#4 @ r12= flip \n" + "ldr r3, [r2],#20 @ r3 = count r2 = &index\n" + "ldr r4, [r2] @ r4 = index[0] \n" + "cmp r12,#0 @ if (flip) \n" + "beq 4f @ { \n" + "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n" + "add r0, r0, r3, LSL #3 @ dst += 2*count \n" + "1: \n" + "ldr r4, [r2], #4 @ r4 = *contrib++ \n" + "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n" + "mov r5, #0 @ r5 = g = 0 \n" + "mov r6, #0 @ r6 = a = 0 \n" + "add r4, r1, r4, LSL #1 @ r4 = min = &src[2*r4] \n" + "cmp r9, #0 @ while (len-- > 0) \n" + "beq 3f @ { \n" + "2: \n" + "ldr r14,[r2], #4 @ r14 = *contrib++ \n" + "ldrb r11,[r4], #1 @ r11 = *min++ \n" + "ldrb r12,[r4], #1 @ r12 = *min++ \n" + "subs r9, r9, #1 @ r9 = len-- \n" + "mla r5, r14,r11,r5 @ g += r11 * r14 \n" + "mla r6, r14,r12,r6 @ a += r12 * r14 \n" + "bgt 2b @ } \n" + "3: \n" + "stmdb r0!,{r5,r6} @ *--dst=a;*--dst=g; \n" + "subs r3, r3, #1 @ i-- \n" + "bgt 1b @ \n" + "ldmfd r13!,{r4-r6,r9-r11,PC} @ pop, return to thumb \n" + "4:" + "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n" + "5:" + "ldr r4, [r2], #4 @ r4 = *contrib++ \n" + "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n" + "mov r5, #0 @ r5 = g = 0 \n" + "mov r6, #0 @ r6 = a = 0 \n" + "add r4, r1, r4, LSL #1 @ r4 = min = &src[2*r4] \n" + "cmp r9, #0 @ while (len-- > 0) \n" + "beq 7f @ { \n" + "6: \n" + "ldr r14,[r2], #4 @ r10 = *contrib++ \n" + "ldrb r11,[r4], #1 @ r11 = *min++ \n" + "ldrb r12,[r4], #1 @ r12 = *min++ \n" + "subs r9, r9, #1 @ r9 = len-- \n" + "mla r5, r14,r11,r5 @ g += r11 * r14 \n" + "mla r6, r14,r12,r6 @ a += r12 * r14 \n" + "bgt 6b @ } \n" + "7: \n" + "stmia r0!,{r5,r6} @ *dst++=r;*dst++=g; \n" + "subs r3, r3, #1 @ i-- \n" + "bgt 5b @ \n" + "ldmfd r13!,{r4-r6,r9-r11,PC} @ pop, return to thumb \n" + ENTER_THUMB + ); +} + +static void +scale_row_to_temp4(int *dst, unsigned char *src, fz_weights *weights) +{ + asm volatile( + ENTER_ARM + "stmfd r13!,{r4-r11,r14} \n" + "@ r0 = dst \n" + "@ r1 = src \n" + "@ r2 = weights \n" + "ldr r12,[r2],#4 @ r12= flip \n" + "ldr r3, [r2],#20 @ r3 = count r2 = &index\n" + "ldr r4, [r2] @ r4 = index[0] \n" + "cmp r12,#0 @ if (flip) \n" + "beq 4f @ { \n" + "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n" + "add r0, r0, r3, LSL #4 @ dst += 4*count \n" + "1: \n" + "ldr r4, [r2], #4 @ r4 = *contrib++ \n" + "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n" + "mov r5, #0 @ r5 = r = 0 \n" + "mov r6, #0 @ r6 = g = 0 \n" + "mov r7, #0 @ r7 = b = 0 \n" + "mov r8, #0 @ r8 = a = 0 \n" + "add r4, r1, r4, LSL #2 @ r4 = min = &src[4*r4] \n" + "cmp r9, #0 @ while (len-- > 0) \n" + "beq 3f @ { \n" + "2: \n" + "ldr r10,[r2], #4 @ r10 = *contrib++ \n" + "ldrb r11,[r4], #1 @ r11 = *min++ \n" + "ldrb r12,[r4], #1 @ r12 = *min++ \n" + "ldrb r14,[r4], #1 @ r14 = *min++ \n" + "mla r5, r10,r11,r5 @ r += r11 * r10 \n" + "ldrb r11,[r4], #1 @ r11 = *min++ \n" + "mla r6, r10,r12,r6 @ g += r12 * r10 \n" + "mla r7, r10,r14,r7 @ b += r14 * r10 \n" + "mla r8, r10,r11,r8 @ a += r11 * r10 \n" + "subs r9, r9, #1 @ r9 = len-- \n" + "bgt 2b @ } \n" + "3: \n" + "stmdb r0!,{r5,r6,r7,r8} @ *--dst=a;*--dst=b; \n" + " @ *--dst=g;*--dst=r; \n" + "subs r3, r3, #1 @ i-- \n" + "bgt 1b @ \n" + "ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n" + "4:" + "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n" + "5:" + "ldr r4, [r2], #4 @ r4 = *contrib++ \n" + "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n" + "mov r5, #0 @ r5 = r = 0 \n" + "mov r6, #0 @ r6 = g = 0 \n" + "mov r7, #0 @ r7 = b = 0 \n" + "mov r8, #0 @ r8 = a = 0 \n" + "add r4, r1, r4, LSL #2 @ r4 = min = &src[4*r4] \n" + "cmp r9, #0 @ while (len-- > 0) \n" + "beq 7f @ { \n" + "6: \n" + "ldr r10,[r2], #4 @ r10 = *contrib++ \n" + "ldrb r11,[r4], #1 @ r11 = *min++ \n" + "ldrb r12,[r4], #1 @ r12 = *min++ \n" + "ldrb r14,[r4], #1 @ r14 = *min++ \n" + "mla r5, r10,r11,r5 @ r += r11 * r10 \n" + "ldrb r11,[r4], #1 @ r11 = *min++ \n" + "mla r6, r10,r12,r6 @ g += r12 * r10 \n" + "mla r7, r10,r14,r7 @ b += r14 * r10 \n" + "mla r8, r10,r11,r8 @ a += r11 * r10 \n" + "subs r9, r9, #1 @ r9 = len-- \n" + "bgt 6b @ } \n" + "7: \n" + "stmia r0!,{r5,r6,r7,r8} @ *dst++=r;*dst++=g; \n" + " @ *dst++=b;*dst++=a; \n" + "subs r3, r3, #1 @ i-- \n" + "bgt 5b @ \n" + "ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n" + ENTER_THUMB + ); +} + +static void +scale_row_from_temp(unsigned char *dst, int *src, fz_weights *weights, int width, int row) +{ + asm volatile( + ENTER_ARM + "ldr r12,[r13] @ r12= row \n" + "add r2, r2, #24 @ r2 = weights->index \n" + "stmfd r13!,{r4-r11,r14} \n" + "@ r0 = dst \n" + "@ r1 = src \n" + "@ r2 = &weights->index[0] \n" + "@ r3 = width \n" + "@ r12= row \n" + "ldr r4, [r2, r12, LSL #2] @ r4 = index[row] \n" + "add r2, r2, #4 @ r2 = &index[1] \n" + "mov r6, r3 @ r6 = x = width \n" + "ldr r14,[r2, r4, LSL #2]! @ r2 = contrib = index[index[row]+1]\n" + " @ r14= len = *contrib \n" + "1: \n" + "mov r5, r1 @ r5 = min = src \n" + "mov r7, #1<<15 @ r7 = val = 1<<15 \n" + "movs r8, r14 @ r8 = len2 = len \n" + "add r9, r2, #4 @ r9 = contrib2 \n" + "ble 3f @ while (len2-- > 0) { \n" + "2: \n" + "ldr r10,[r9], #4 @ r10 = *contrib2++ \n" + "ldr r12,[r5], r3, LSL #2 @ r12 = *min r5 = min += width\n" + "subs r8, r8, #1 @ len2-- \n" + "@ stall r12 \n" + "mla r7, r10,r12,r7 @ val += r12 * r10 \n" + "bgt 2b @ } \n" + "3: \n" + "movs r7, r7, asr #16 @ r7 = val >>= 16 \n" + "movlt r7, #0 @ if (r7 < 0) r7 = 0 \n" + "cmp r7, #255 @ if (r7 > 255) \n" + "add r1, r1, #4 @ src++ \n" + "movgt r7, #255 @ r7 = 255 \n" + "subs r6, r6, #1 @ x-- \n" + "strb r7, [r0], #1 @ *dst++ = val \n" + "bgt 1b @ \n" + "ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n" + ENTER_THUMB + ); +} + +#else + +static void +scale_row_to_temp1(int *dst, unsigned char *src, fz_weights *weights) +{ + int *contrib = &weights->index[weights->index[0]]; + int len, i; + unsigned char *min; + + assert(weights->n == 1); + if (weights->flip) + { + dst += weights->count; + for (i=weights->count; i > 0; i--) + { + int val = 0; + min = &src[*contrib++]; + len = *contrib++; + while (len-- > 0) + { + val += *min++ * *contrib++; + } + *--dst = val; + } + } + else + { + for (i=weights->count; i > 0; i--) + { + int val = 0; + min = &src[*contrib++]; + len = *contrib++; + while (len-- > 0) + { + val += *min++ * *contrib++; + } + *dst++ = val; + } + } +} + +static void +scale_row_to_temp2(int *dst, unsigned char *src, fz_weights *weights) +{ + int *contrib = &weights->index[weights->index[0]]; + int len, i; + unsigned char *min; + + assert(weights->n == 2); + if (weights->flip) + { + dst += 2*weights->count; + for (i=weights->count; i > 0; i--) + { + int c1 = 0; + int c2 = 0; + min = &src[2 * *contrib++]; + len = *contrib++; + while (len-- > 0) + { + c1 += *min++ * *contrib; + c2 += *min++ * *contrib++; + } + *--dst = c2; + *--dst = c1; + } + } + else + { + for (i=weights->count; i > 0; i--) + { + int c1 = 0; + int c2 = 0; + min = &src[2 * *contrib++]; + len = *contrib++; + while (len-- > 0) + { + c1 += *min++ * *contrib; + c2 += *min++ * *contrib++; + } + *dst++ = c1; + *dst++ = c2; + } + } +} + +static void +scale_row_to_temp4(int *dst, unsigned char *src, fz_weights *weights) +{ + int *contrib = &weights->index[weights->index[0]]; + int len, i; + unsigned char *min; + + assert(weights->n == 4); + if (weights->flip) + { + dst += 4*weights->count; + for (i=weights->count; i > 0; i--) + { + int r = 0; + int g = 0; + int b = 0; + int a = 0; + min = &src[4 * *contrib++]; + len = *contrib++; + while (len-- > 0) + { + r += *min++ * *contrib; + g += *min++ * *contrib; + b += *min++ * *contrib; + a += *min++ * *contrib++; + } + *--dst = a; + *--dst = b; + *--dst = g; + *--dst = r; + } + } + else + { + for (i=weights->count; i > 0; i--) + { + int r = 0; + int g = 0; + int b = 0; + int a = 0; + min = &src[4 * *contrib++]; + len = *contrib++; + while (len-- > 0) + { + r += *min++ * *contrib; + g += *min++ * *contrib; + b += *min++ * *contrib; + a += *min++ * *contrib++; + } + *dst++ = r; + *dst++ = g; + *dst++ = b; + *dst++ = a; + } + } +} + +static void +scale_row_from_temp(unsigned char *dst, int *src, fz_weights *weights, int width, int row) +{ + int *contrib = &weights->index[weights->index[row]]; + int len, x; + + contrib++; /* Skip min */ + len = *contrib++; + for (x=width; x > 0; x--) + { + int *min = src; + int val = 0; + int len2 = len; + int *contrib2 = contrib; + + while (len2-- > 0) + { + val += *min * *contrib2++; + min += width; + } + val = (val+(1<<15))>>16; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + *dst++ = val; + src++; + } +} +#endif + +#ifdef SINGLE_PIXEL_SPECIALS +static void +duplicate_single_pixel(unsigned char *dst, unsigned char *src, int n, int w, int h) +{ + int i; + + for (i = n; i > 0; i--) + *dst++ = *src++; + for (i = (w*h-1)*n; i > 0; i--) + { + *dst = dst[-n]; + dst++; + } +} + +static void +scale_single_row(unsigned char *dst, unsigned char *src, fz_weights *weights, int src_w, int h) +{ + int *contrib = &weights->index[weights->index[0]]; + int min, len, i, j, val, n; + int tmp[FZ_MAX_COLORS]; + + n = weights->n; + /* Scale a single row */ + if (weights->flip) + { + dst += (weights->count-1)*n; + for (i=weights->count; i > 0; i--) + { + min = *contrib++; + len = *contrib++; + min *= n; + for (j = 0; j < n; j++) + tmp[j] = 0; + while (len-- > 0) + { + for (j = 0; j < n; j++) + tmp[j] += src[min++] * *contrib; + contrib++; + } + for (j = 0; j < n; j++) + { + val = (tmp[j]+(1<<7))>>8; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + *dst++ = val; + } + dst -= 2*n; + } + dst += n * (weights->count+1); + } + else + { + for (i=weights->count; i > 0; i--) + { + min = *contrib++; + len = *contrib++; + min *= n; + for (j = 0; j < n; j++) + tmp[j] = 0; + while (len-- > 0) + { + for (j = 0; j < n; j++) + tmp[j] += src[min++] * *contrib; + contrib++; + } + for (j = 0; j < n; j++) + { + val = (tmp[j]+(1<<7))>>8; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + *dst++ = val; + } + } + } + /* And then duplicate it h times */ + n *= weights->count; + while (--h > 0) + { + memcpy(dst, dst-n, n); + dst += n; + } +} + +static void +scale_single_col(unsigned char *dst, unsigned char *src, fz_weights *weights, int src_w, int n, int w, int flip_y) +{ + int *contrib = &weights->index[weights->index[0]]; + int min, len, i, j, val; + int tmp[FZ_MAX_COLORS]; + + if (flip_y) + { + src_w = (src_w-1)*n; + w = (w-1)*n; + for (i=weights->count; i > 0; i--) + { + /* Scale the next pixel in the column */ + min = *contrib++; + len = *contrib++; + min = src_w-min*n; + for (j = 0; j < n; j++) + tmp[j] = 0; + while (len-- > 0) + { + for (j = 0; j < n; j++) + tmp[j] += src[src_w-min+j] * *contrib; + contrib++; + } + for (j = 0; j < n; j++) + { + val = (tmp[j]+(1<<7))>>8; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + *dst++ = val; + } + /* And then duplicate it across the row */ + for (j = w; j > 0; j--) + { + *dst = dst[-n]; + dst++; + } + } + } + else + { + w = (w-1)*n; + for (i=weights->count; i > 0; i--) + { + /* Scale the next pixel in the column */ + min = *contrib++; + len = *contrib++; + min *= n; + for (j = 0; j < n; j++) + tmp[j] = 0; + while (len-- > 0) + { + for (j = 0; j < n; j++) + tmp[j] += src[min++] * *contrib; + contrib++; + } + for (j = 0; j < n; j++) + { + val = (tmp[j]+(1<<7))>>8; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + *dst++ = val; + } + /* And then duplicate it across the row */ + for (j = w; j > 0; j--) + { + *dst = dst[-n]; + dst++; + } + } + } +} +#endif /* SINGLE_PIXEL_SPECIALS */ + +fz_pixmap * +fz_scale_pixmap(fz_context *ctx, fz_pixmap *src, float x, float y, float w, float h, fz_irect *clip) +{ + return fz_scale_pixmap_cached(ctx, src, x, y, w, h, clip, NULL, NULL); +} + +fz_pixmap * +fz_scale_pixmap_cached(fz_context *ctx, fz_pixmap *src, float x, float y, float w, float h, const fz_irect *clip, fz_scale_cache *cache_x, fz_scale_cache *cache_y) +{ + fz_scale_filter *filter = &fz_scale_filter_simple; + fz_weights *contrib_rows = NULL; + fz_weights *contrib_cols = NULL; + fz_pixmap *output = NULL; + int *temp = NULL; + int max_row, temp_span, temp_rows, row; + int dst_w_int, dst_h_int, dst_x_int, dst_y_int; + int flip_x, flip_y; + fz_rect patch; + + fz_var(contrib_cols); + fz_var(contrib_rows); + + DBUG(("Scale: (%d,%d) to (%g,%g) at (%g,%g)\n",src->w,src->h,w,h,x,y)); + + /* Avoid extreme scales where overflows become problematic. */ + if (w > (1<<24) || h > (1<<24) || w < -(1<<24) || h < -(1<<24)) + return NULL; + + /* Clamp small ranges of w and h */ + if (w <= -1) + { + } + else if (w < 0) + { + w = -1; + } + else if (w < 1) + { + w = 1; + } + if (h <= -1) + { + } + else if (h < 0) + { + h = -1; + } + else if (h < 1) + { + h = 1; + } + + /* Find the destination bbox, width/height, and sub pixel offset, + * allowing for whether we're flipping or not. */ + /* The (x,y) position given describes where the top left corner + * of the source image should be mapped to (i.e. where (0,0) in image + * space ends up). Also there are differences in the way we scale + * horizontally and vertically. When scaling rows horizontally, we + * always read forwards through the source, and store either forwards + * or in reverse as required. When scaling vertically, we always store + * out forwards, but may feed source rows in in a different order. + * + * Consider the image rectangle 'r' to which the image is mapped, + * and the (possibly) larger rectangle 'R', given by expanding 'r' to + * complete pixels. + * + * x can either be r.xmin-R.xmin or R.xmax-r.xmax depending on whether + * the image is x flipped or not. Whatever happens 0 <= x < 1. + * y is always R.ymax - r.ymax. + */ + /* dst_x_int is calculated to be the left of the scaled image, and + * x (the sub pixel offset) is the distance in from either the left + * or right pixel expanded edge. */ + flip_x = (w < 0); + if (flip_x) + { + float tmp; + w = -w; + dst_x_int = floorf(x-w); + tmp = ceilf(x); + dst_w_int = (int)tmp; + x = tmp - x; + dst_w_int -= dst_x_int; + } + else + { + dst_x_int = floorf(x); + x -= (float)dst_x_int; + dst_w_int = (int)ceilf(x + w); + } + /* dst_y_int is calculated to be the top of the scaled image, and + * y (the sub pixel offset) is the distance in from either the top + * or bottom pixel expanded edge. + */ + flip_y = (h < 0); + if (flip_y) + { + float tmp; + h = -h; + dst_y_int = floorf(y-h); + tmp = ceilf(y); + dst_h_int = (int)tmp; + y = tmp - y; + dst_h_int -= dst_y_int; + } + else + { + dst_y_int = floorf(y); + y -= (float)dst_y_int; + dst_h_int = (int)ceilf(y + h); + } + + DBUG(("Result image: (%d,%d) at (%d,%d) (subpix=%g,%g)\n", dst_w_int, dst_h_int, dst_x_int, dst_y_int, x, y)); + + /* Step 0: Calculate the patch */ + patch.x0 = 0; + patch.y0 = 0; + patch.x1 = dst_w_int; + patch.y1 = dst_h_int; + if (clip) + { + if (flip_x) + { + if (dst_x_int + dst_w_int > clip->x1) + patch.x0 = dst_x_int + dst_w_int - clip->x1; + if (clip->x0 > dst_x_int) + { + patch.x1 = dst_w_int - (clip->x0 - dst_x_int); + dst_x_int = clip->x0; + } + } + else + { + if (dst_x_int + dst_w_int > clip->x1) + patch.x1 = clip->x1 - dst_x_int; + if (clip->x0 > dst_x_int) + { + patch.x0 = clip->x0 - dst_x_int; + dst_x_int += patch.x0; + } + } + + if (flip_y) + { + if (dst_y_int + dst_h_int > clip->y1) + patch.y1 = clip->y1 - dst_y_int; + if (clip->y0 > dst_y_int) + { + patch.y0 = clip->y0 - dst_y_int; + dst_y_int = clip->y0; + } + } + else + { + if (dst_y_int + dst_h_int > clip->y1) + patch.y1 = clip->y1 - dst_y_int; + if (clip->y0 > dst_y_int) + { + patch.y0 = clip->y0 - dst_y_int; + dst_y_int += patch.y0; + } + } + } + if (patch.x0 >= patch.x1 || patch.y0 >= patch.y1) + return NULL; + + fz_try(ctx) + { + /* Step 1: Calculate the weights for columns and rows */ +#ifdef SINGLE_PIXEL_SPECIALS + if (src->w == 1) + contrib_cols = NULL; + else +#endif /* SINGLE_PIXEL_SPECIALS */ + contrib_cols = make_weights(ctx, src->w, x, w, filter, 0, dst_w_int, patch.x0, patch.x1, src->n, flip_x, cache_x); +#ifdef SINGLE_PIXEL_SPECIALS + if (src->h == 1) + contrib_rows = NULL; + else +#endif /* SINGLE_PIXEL_SPECIALS */ + contrib_rows = make_weights(ctx, src->h, y, h, filter, 1, dst_h_int, patch.y0, patch.y1, src->n, flip_y, cache_y); + + output = fz_new_pixmap(ctx, src->colorspace, patch.x1 - patch.x0, patch.y1 - patch.y0); + } + fz_catch(ctx) + { + if (!cache_x) + fz_free(ctx, contrib_cols); + if (!cache_y) + fz_free(ctx, contrib_rows); + fz_rethrow(ctx); + } + output->x = dst_x_int; + output->y = dst_y_int; + + /* Step 2: Apply the weights */ +#ifdef SINGLE_PIXEL_SPECIALS + if (!contrib_rows) + { + /* Only 1 source pixel high. */ + if (!contrib_cols) + { + /* Only 1 pixel in the entire image! */ + duplicate_single_pixel(output->samples, src->samples, src->n, patch.x1-patch.x0, patch.y1-patch.y0); + } + else + { + /* Scale the row once, then copy it. */ + scale_single_row(output->samples, src->samples, contrib_cols, src->w, patch.y1-patch.y0); + } + } + else if (!contrib_cols) + { + /* Only 1 source pixel wide. Scale the col and duplicate. */ + scale_single_col(output->samples, src->samples, contrib_rows, src->h, src->n, patch.x1-patch.x0, flip_y); + } + else +#endif /* SINGLE_PIXEL_SPECIALS */ + { + void (*row_scale)(int *dst, unsigned char *src, fz_weights *weights); + + temp_span = contrib_cols->count * src->n; + temp_rows = contrib_rows->max_len; + if (temp_span <= 0 || temp_rows > INT_MAX / temp_span) + goto cleanup; + fz_try(ctx) + { + temp = fz_calloc(ctx, temp_span*temp_rows, sizeof(int)); + } + fz_catch(ctx) + { + fz_drop_pixmap(ctx, output); + if (!cache_x) + fz_free(ctx, contrib_cols); + if (!cache_y) + fz_free(ctx, contrib_rows); + fz_rethrow(ctx); + } + switch (src->n) + { + default: + row_scale = scale_row_to_temp; + break; + case 1: /* Image mask case */ + row_scale = scale_row_to_temp1; + break; + case 2: /* Greyscale with alpha case */ + row_scale = scale_row_to_temp2; + break; + case 4: /* RGBA */ + row_scale = scale_row_to_temp4; + break; + } + max_row = contrib_rows->index[contrib_rows->index[0]]; + for (row = 0; row < contrib_rows->count; row++) + { + /* + Which source rows do we need to have scaled into the + temporary buffer in order to be able to do the final + scale? + */ + int row_index = contrib_rows->index[row]; + int row_min = contrib_rows->index[row_index++]; + int row_len = contrib_rows->index[row_index++]; + while (max_row < row_min+row_len) + { + /* Scale another row */ + assert(max_row < src->h); + DBUG(("scaling row %d to temp\n", max_row)); + (*row_scale)(&temp[temp_span*(max_row % temp_rows)], &src->samples[(flip_y ? (src->h-1-max_row): max_row)*src->w*src->n], contrib_cols); + max_row++; + } + + DBUG(("scaling row %d from temp\n", row)); + scale_row_from_temp(&output->samples[row*output->w*output->n], temp, contrib_rows, temp_span, row); + } + fz_free(ctx, temp); + } + +cleanup: + if (!cache_y) + fz_free(ctx, contrib_rows); + if (!cache_x) + fz_free(ctx, contrib_cols); + return output; +} + +void +fz_free_scale_cache(fz_context *ctx, fz_scale_cache *sc) +{ + if (!sc) + return; + fz_free(ctx, sc->weights); + fz_free(ctx, sc); +} + +fz_scale_cache * +fz_new_scale_cache(fz_context *ctx) +{ + return fz_malloc_struct(ctx, fz_scale_cache); +} diff --git a/source/fitz/draw-unpack.c b/source/fitz/draw-unpack.c new file mode 100644 index 00000000..d862ea2a --- /dev/null +++ b/source/fitz/draw-unpack.c @@ -0,0 +1,242 @@ +#include "mupdf/fitz.h" +#include "draw-imp.h" + +/* Unpack image samples and optionally pad pixels with opaque alpha */ + +#define get1(buf,x) ((buf[x >> 3] >> ( 7 - (x & 7) ) ) & 1 ) +#define get2(buf,x) ((buf[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3 ) +#define get4(buf,x) ((buf[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15 ) +#define get8(buf,x) (buf[x]) +#define get16(buf,x) (buf[x << 1]) + +static unsigned char get1_tab_1[256][8]; +static unsigned char get1_tab_1p[256][16]; +static unsigned char get1_tab_255[256][8]; +static unsigned char get1_tab_255p[256][16]; + +static void +init_get1_tables(void) +{ + static int once = 0; + unsigned char bits[1]; + int i, k, x; + + /* TODO: mutex lock here */ + + if (once) + return; + + for (i = 0; i < 256; i++) + { + bits[0] = i; + for (k = 0; k < 8; k++) + { + x = get1(bits, k); + + get1_tab_1[i][k] = x; + get1_tab_1p[i][k * 2] = x; + get1_tab_1p[i][k * 2 + 1] = 255; + + get1_tab_255[i][k] = x * 255; + get1_tab_255p[i][k * 2] = x * 255; + get1_tab_255p[i][k * 2 + 1] = 255; + } + } + + once = 1; +} + +void +fz_unpack_tile(fz_pixmap *dst, unsigned char * restrict src, int n, int depth, int stride, int scale) +{ + int pad, x, y, k; + int w = dst->w; + + pad = 0; + if (dst->n > n) + pad = 255; + + if (depth == 1) + init_get1_tables(); + + if (scale == 0) + { + switch (depth) + { + case 1: scale = 255; break; + case 2: scale = 85; break; + case 4: scale = 17; break; + } + } + + for (y = 0; y < dst->h; y++) + { + unsigned char *sp = src + (unsigned int)(y * stride); + unsigned char *dp = dst->samples + (unsigned int)(y * dst->w * dst->n); + + /* Specialized loops */ + + if (n == 1 && depth == 1 && scale == 1 && !pad) + { + int w3 = w >> 3; + for (x = 0; x < w3; x++) + { + memcpy(dp, get1_tab_1[*sp++], 8); + dp += 8; + } + x = x << 3; + if (x < w) + memcpy(dp, get1_tab_1[*sp], w - x); + } + + else if (n == 1 && depth == 1 && scale == 255 && !pad) + { + int w3 = w >> 3; + for (x = 0; x < w3; x++) + { + memcpy(dp, get1_tab_255[*sp++], 8); + dp += 8; + } + x = x << 3; + if (x < w) + memcpy(dp, get1_tab_255[*sp], w - x); + } + + else if (n == 1 && depth == 1 && scale == 1 && pad) + { + int w3 = w >> 3; + for (x = 0; x < w3; x++) + { + memcpy(dp, get1_tab_1p[*sp++], 16); + dp += 16; + } + x = x << 3; + if (x < w) + memcpy(dp, get1_tab_1p[*sp], (w - x) << 1); + } + + else if (n == 1 && depth == 1 && scale == 255 && pad) + { + int w3 = w >> 3; + for (x = 0; x < w3; x++) + { + memcpy(dp, get1_tab_255p[*sp++], 16); + dp += 16; + } + x = x << 3; + if (x < w) + memcpy(dp, get1_tab_255p[*sp], (w - x) << 1); + } + + else if (depth == 8 && !pad) + { + int len = w * n; + while (len--) + *dp++ = *sp++; + } + + else if (depth == 8 && pad) + { + for (x = 0; x < w; x++) + { + for (k = 0; k < n; k++) + *dp++ = *sp++; + *dp++ = 255; + } + } + + else + { + int b = 0; + for (x = 0; x < w; x++) + { + for (k = 0; k < n; k++) + { + switch (depth) + { + case 1: *dp++ = get1(sp, b) * scale; break; + case 2: *dp++ = get2(sp, b) * scale; break; + case 4: *dp++ = get4(sp, b) * scale; break; + case 8: *dp++ = get8(sp, b); break; + case 16: *dp++ = get16(sp, b); break; + } + b++; + } + if (pad) + *dp++ = 255; + } + } + } +} + +/* Apply decode array */ + +void +fz_decode_indexed_tile(fz_pixmap *pix, float *decode, int maxval) +{ + int add[FZ_MAX_COLORS]; + int mul[FZ_MAX_COLORS]; + unsigned char *p = pix->samples; + int len = pix->w * pix->h; + int n = pix->n - 1; + int needed; + int k; + + needed = 0; + for (k = 0; k < n; k++) + { + int min = decode[k * 2] * 256; + int max = decode[k * 2 + 1] * 256; + add[k] = min; + mul[k] = (max - min) / maxval; + needed |= min != 0 || max != maxval * 256; + } + + if (!needed) + return; + + while (len--) + { + for (k = 0; k < n; k++) + { + int value = (add[k] + (((p[k] << 8) * mul[k]) >> 8)) >> 8; + p[k] = fz_clampi(value, 0, 255); + } + p += n + 1; + } +} + +void +fz_decode_tile(fz_pixmap *pix, float *decode) +{ + int add[FZ_MAX_COLORS]; + int mul[FZ_MAX_COLORS]; + unsigned char *p = pix->samples; + int len = pix->w * pix->h; + int n = fz_maxi(1, pix->n - 1); + int needed; + int k; + + needed = 0; + for (k = 0; k < n; k++) + { + int min = decode[k * 2] * 255; + int max = decode[k * 2 + 1] * 255; + add[k] = min; + mul[k] = max - min; + needed |= min != 0 || max != 255; + } + + if (!needed) + return; + + while (len--) + { + for (k = 0; k < n; k++) + { + int value = add[k] + fz_mul255(p[k], mul[k]); + p[k] = fz_clampi(value, 0, 255); + } + p += pix->n; + } +} diff --git a/source/fitz/error.c b/source/fitz/error.c new file mode 100644 index 00000000..50b3c5aa --- /dev/null +++ b/source/fitz/error.c @@ -0,0 +1,155 @@ +#include "mupdf/fitz.h" + +/* Warning context */ + +void fz_var_imp(void *var) +{ + UNUSED(var); /* Do nothing */ +} + +void fz_flush_warnings(fz_context *ctx) +{ + if (ctx->warn->count > 1) + { + fprintf(stderr, "warning: ... repeated %d times ...\n", ctx->warn->count); + LOGE("warning: ... repeated %d times ...\n", ctx->warn->count); + } + ctx->warn->message[0] = 0; + ctx->warn->count = 0; +} + +void fz_warn(fz_context *ctx, const char *fmt, ...) +{ + va_list ap; + char buf[sizeof ctx->warn->message]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof buf, fmt, ap); + va_end(ap); + + if (!strcmp(buf, ctx->warn->message)) + { + ctx->warn->count++; + } + else + { + fz_flush_warnings(ctx); + fprintf(stderr, "warning: %s\n", buf); + LOGE("warning: %s\n", buf); + fz_strlcpy(ctx->warn->message, buf, sizeof ctx->warn->message); + ctx->warn->count = 1; + } +} + +/* Error context */ + +/* When we first setjmp, code is set to 0. Whenever we throw, we add 2 to + * this code. Whenever we enter the always block, we add 1. + * + * fz_push_try sets code to 0. + * If (fz_throw called within fz_try) + * fz_throw makes code = 2. + * If (no always block present) + * enter catch region with code = 2. OK. + * else + * fz_always entered as code < 3; Makes code = 3; + * if (fz_throw called within fz_always) + * fz_throw makes code = 5 + * fz_always is not reentered. + * catch region entered with code = 5. OK. + * else + * catch region entered with code = 3. OK + * else + * if (no always block present) + * catch region not entered as code = 0. OK. + * else + * fz_always entered as code < 3. makes code = 1 + * if (fz_throw called within fz_always) + * fz_throw makes code = 3; + * fz_always NOT entered as code >= 3 + * catch region entered with code = 3. OK. + * else + * catch region entered with code = 1. + */ + +static void throw(fz_error_context *ex) FZ_NORETURN; + +static void throw(fz_error_context *ex) +{ + if (ex->top >= 0) { + fz_longjmp(ex->stack[ex->top].buffer, ex->stack[ex->top].code + 2); + } else { + fprintf(stderr, "uncaught exception: %s\n", ex->message); + LOGE("uncaught exception: %s\n", ex->message); + exit(EXIT_FAILURE); + } +} + +int fz_push_try(fz_error_context *ex) +{ + assert(ex); + ex->top++; + /* Normal case, get out of here quick */ + if (ex->top < nelem(ex->stack)-1) + return 1; /* We exit here, and the setjmp sets the code to 0 */ + /* We reserve the top slot on the exception stack purely to cope with + * the case when we overflow. If we DO hit this, then we 'throw' + * immediately - returning 0 stops the setjmp happening and takes us + * direct to the always/catch clauses. */ + assert(ex->top == nelem(ex->stack)-1); + strcpy(ex->message, "exception stack overflow!"); + ex->stack[ex->top].code = 2; + fprintf(stderr, "error: %s\n", ex->message); + LOGE("error: %s\n", ex->message); + return 0; +} + +int fz_caught(fz_context *ctx) +{ + assert(ctx && ctx->error && ctx->error->errcode >= FZ_ERROR_NONE); + return ctx->error->errcode; +} + +const char *fz_caught_message(fz_context *ctx) +{ + assert(ctx && ctx->error && ctx->error->errcode >= FZ_ERROR_NONE); + return ctx->error->message; +} + +void fz_throw(fz_context *ctx, int code, const char *fmt, ...) +{ + va_list args; + ctx->error->errcode = code; + va_start(args, fmt); + vsnprintf(ctx->error->message, sizeof ctx->error->message, fmt, args); + va_end(args); + + fz_flush_warnings(ctx); + fprintf(stderr, "error: %s\n", ctx->error->message); + LOGE("error: %s\n", ctx->error->message); + + throw(ctx->error); +} + +void fz_rethrow(fz_context *ctx) +{ + assert(ctx && ctx->error && ctx->error->errcode >= FZ_ERROR_NONE); + throw(ctx->error); +} + +void fz_rethrow_message(fz_context *ctx, const char *fmt, ...) +{ + va_list args; + + assert(ctx && ctx->error && ctx->error->errcode >= FZ_ERROR_NONE); + + va_start(args, fmt); + vsnprintf(ctx->error->message, sizeof ctx->error->message, fmt, args); + va_end(args); + + fz_flush_warnings(ctx); + fprintf(stderr, "error: %s\n", ctx->error->message); + LOGE("error: %s\n", ctx->error->message); + + throw(ctx->error); +} diff --git a/source/fitz/filter-basic.c b/source/fitz/filter-basic.c new file mode 100644 index 00000000..3968d193 --- /dev/null +++ b/source/fitz/filter-basic.c @@ -0,0 +1,662 @@ +#include "mupdf/fitz.h" + +/* Pretend we have a filter that just copies data forever */ + +fz_stream * +fz_open_copy(fz_stream *chain) +{ + return fz_keep_stream(chain); +} + +/* Null filter copies a specified amount of data */ + +struct null_filter +{ + fz_stream *chain; + int remain; + int pos; +}; + +static int +read_null(fz_stream *stm, unsigned char *buf, int len) +{ + struct null_filter *state = stm->state; + int amount = fz_mini(len, state->remain); + int n; + + fz_seek(state->chain, state->pos, 0); + n = fz_read(state->chain, buf, amount); + state->remain -= n; + state->pos += n; + return n; +} + +static void +close_null(fz_context *ctx, void *state_) +{ + struct null_filter *state = (struct null_filter *)state_; + fz_stream *chain = state->chain; + fz_free(ctx, state); + fz_close(chain); +} + +fz_stream * +fz_open_null(fz_stream *chain, int len, int offset) +{ + struct null_filter *state; + fz_context *ctx = chain->ctx; + + if (len < 0) + len = 0; + fz_try(ctx) + { + state = fz_malloc_struct(ctx, struct null_filter); + state->chain = chain; + state->remain = len; + state->pos = offset; + } + fz_catch(ctx) + { + fz_close(chain); + fz_rethrow(ctx); + } + + return fz_new_stream(ctx, state, read_null, close_null); +} + +/* Concat filter concatenates several streams into one */ + +struct concat_filter +{ + int max; + int count; + int current; + int pad; /* 1 if we should add whitespace padding between streams */ + int ws; /* 1 if we should send a whitespace padding byte next */ + fz_stream *chain[1]; +}; + +static int +read_concat(fz_stream *stm, unsigned char *buf, int len) +{ + struct concat_filter *state = (struct concat_filter *)stm->state; + int n; + int read = 0; + + if (len <= 0) + return 0; + + while (state->current != state->count && len > 0) + { + /* If we need to send a whitespace char, do that */ + if (state->ws) + { + *buf++ = 32; + read++; + len--; + state->ws = 0; + continue; + } + /* Otherwise, read as much data as will fit in the buffer */ + n = fz_read(state->chain[state->current], buf, len); + read += n; + buf += n; + len -= n; + /* If we didn't read any, then we must have hit the end of + * our buffer space. Move to the next stream, and remember to + * pad. */ + if (n == 0) + { + fz_close(state->chain[state->current]); + state->current++; + state->ws = state->pad; + } + } + + return read; +} + +static void +close_concat(fz_context *ctx, void *state_) +{ + struct concat_filter *state = (struct concat_filter *)state_; + int i; + + for (i = state->current; i < state->count; i++) + { + fz_close(state->chain[i]); + } + fz_free(ctx, state); +} + +fz_stream * +fz_open_concat(fz_context *ctx, int len, int pad) +{ + struct concat_filter *state; + + state = fz_calloc(ctx, 1, sizeof(struct concat_filter) + (len-1)*sizeof(fz_stream *)); + state->max = len; + state->count = 0; + state->current = 0; + state->pad = pad; + state->ws = 0; /* We never send padding byte at the start */ + + return fz_new_stream(ctx, state, read_concat, close_concat); +} + +void +fz_concat_push(fz_stream *concat, fz_stream *chain) +{ + struct concat_filter *state = (struct concat_filter *)concat->state; + + if (state->count == state->max) + fz_throw(concat->ctx, FZ_ERROR_GENERIC, "Concat filter size exceeded"); + + state->chain[state->count++] = chain; +} + +/* ASCII Hex Decode */ + +typedef struct fz_ahxd_s fz_ahxd; + +struct fz_ahxd_s +{ + fz_stream *chain; + int eod; +}; + +static inline int iswhite(int a) +{ + switch (a) { + case '\n': case '\r': case '\t': case ' ': + case '\0': case '\f': case '\b': case 0177: + return 1; + } + return 0; +} + +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; +} + +static int +read_ahxd(fz_stream *stm, unsigned char *buf, int len) +{ + fz_ahxd *state = stm->state; + unsigned char *p = buf; + unsigned char *ep = buf + len; + int a, b, c, odd; + + odd = 0; + + while (p < ep) + { + if (state->eod) + return p - buf; + + c = fz_read_byte(state->chain); + if (c < 0) + return p - buf; + + if (ishex(c)) + { + if (!odd) + { + a = unhex(c); + odd = 1; + } + else + { + b = unhex(c); + *p++ = (a << 4) | b; + odd = 0; + } + } + else if (c == '>') + { + if (odd) + *p++ = (a << 4); + state->eod = 1; + } + else if (!iswhite(c)) + { + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "bad data in ahxd: '%c'", c); + } + } + + return p - buf; +} + +static void +close_ahxd(fz_context *ctx, void *state_) +{ + fz_ahxd *state = (fz_ahxd *)state_; + fz_stream *chain = state->chain; + fz_free(ctx, state); + fz_close(chain); +} + +fz_stream * +fz_open_ahxd(fz_stream *chain) +{ + fz_ahxd *state; + fz_context *ctx = chain->ctx; + + fz_try(ctx) + { + state = fz_malloc_struct(ctx, fz_ahxd); + state->chain = chain; + state->eod = 0; + } + fz_catch(ctx) + { + fz_close(chain); + fz_rethrow(ctx); + } + + return fz_new_stream(ctx, state, read_ahxd, close_ahxd); +} + +/* ASCII 85 Decode */ + +typedef struct fz_a85d_s fz_a85d; + +struct fz_a85d_s +{ + fz_stream *chain; + unsigned char bp[4]; + unsigned char *rp, *wp; + int eod; +}; + +static int +read_a85d(fz_stream *stm, unsigned char *buf, int len) +{ + fz_a85d *state = stm->state; + unsigned char *p = buf; + unsigned char *ep = buf + len; + int count = 0; + int word = 0; + int c; + + while (state->rp < state->wp && p < ep) + *p++ = *state->rp++; + + while (p < ep) + { + if (state->eod) + return p - buf; + + c = fz_read_byte(state->chain); + if (c < 0) + return p - buf; + + if (c >= '!' && c <= 'u') + { + if (count == 4) + { + word = word * 85 + (c - '!'); + + state->bp[0] = (word >> 24) & 0xff; + state->bp[1] = (word >> 16) & 0xff; + state->bp[2] = (word >> 8) & 0xff; + state->bp[3] = (word) & 0xff; + state->rp = state->bp; + state->wp = state->bp + 4; + + word = 0; + count = 0; + } + else + { + word = word * 85 + (c - '!'); + count ++; + } + } + + else if (c == 'z' && count == 0) + { + state->bp[0] = 0; + state->bp[1] = 0; + state->bp[2] = 0; + state->bp[3] = 0; + state->rp = state->bp; + state->wp = state->bp + 4; + } + + else if (c == '~') + { + c = fz_read_byte(state->chain); + if (c != '>') + fz_warn(stm->ctx, "bad eod marker in a85d"); + + switch (count) { + case 0: + break; + case 1: + /* Specifically illegal in the spec, but adobe + * and gs both cope. See normal_87.pdf for a + * case where this matters. */ + fz_warn(stm->ctx, "partial final byte in a85d"); + break; + case 2: + word = word * (85 * 85 * 85) + 0xffffff; + state->bp[0] = word >> 24; + state->rp = state->bp; + state->wp = state->bp + 1; + break; + case 3: + word = word * (85 * 85) + 0xffff; + state->bp[0] = word >> 24; + state->bp[1] = word >> 16; + state->rp = state->bp; + state->wp = state->bp + 2; + break; + case 4: + word = word * 85 + 0xff; + state->bp[0] = word >> 24; + state->bp[1] = word >> 16; + state->bp[2] = word >> 8; + state->rp = state->bp; + state->wp = state->bp + 3; + break; + } + state->eod = 1; + } + + else if (!iswhite(c)) + { + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "bad data in a85d: '%c'", c); + } + + while (state->rp < state->wp && p < ep) + *p++ = *state->rp++; + } + + return p - buf; +} + +static void +close_a85d(fz_context *ctx, void *state_) +{ + fz_a85d *state = (fz_a85d *)state_; + fz_stream *chain = state->chain; + + fz_free(ctx, state); + fz_close(chain); +} + +fz_stream * +fz_open_a85d(fz_stream *chain) +{ + fz_a85d *state; + fz_context *ctx = chain->ctx; + + fz_try(ctx) + { + state = fz_malloc_struct(ctx, fz_a85d); + state->chain = chain; + state->rp = state->bp; + state->wp = state->bp; + state->eod = 0; + } + fz_catch(ctx) + { + fz_close(chain); + fz_rethrow(ctx); + } + + return fz_new_stream(ctx, state, read_a85d, close_a85d); +} + +/* Run Length Decode */ + +typedef struct fz_rld_s fz_rld; + +struct fz_rld_s +{ + fz_stream *chain; + int run, n, c; +}; + +static int +read_rld(fz_stream *stm, unsigned char *buf, int len) +{ + fz_rld *state = stm->state; + unsigned char *p = buf; + unsigned char *ep = buf + len; + + while (p < ep) + { + if (state->run == 128) + return p - buf; + + if (state->n == 0) + { + state->run = fz_read_byte(state->chain); + if (state->run < 0) + state->run = 128; + if (state->run < 128) + state->n = state->run + 1; + if (state->run > 128) + { + state->n = 257 - state->run; + state->c = fz_read_byte(state->chain); + if (state->c < 0) + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "premature end of data in run length decode"); + } + } + + if (state->run < 128) + { + while (p < ep && state->n) + { + int c = fz_read_byte(state->chain); + if (c < 0) + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "premature end of data in run length decode"); + *p++ = c; + state->n--; + } + } + + if (state->run > 128) + { + while (p < ep && state->n) + { + *p++ = state->c; + state->n--; + } + } + } + + return p - buf; +} + +static void +close_rld(fz_context *ctx, void *state_) +{ + fz_rld *state = (fz_rld *)state_; + fz_stream *chain = state->chain; + + fz_free(ctx, state); + fz_close(chain); +} + +fz_stream * +fz_open_rld(fz_stream *chain) +{ + fz_rld *state; + fz_context *ctx = chain->ctx; + + fz_try(ctx) + { + state = fz_malloc_struct(ctx, fz_rld); + state->chain = chain; + state->run = 0; + state->n = 0; + state->c = 0; + } + fz_catch(ctx) + { + fz_close(chain); + fz_rethrow(ctx); + } + + return fz_new_stream(ctx, state, read_rld, close_rld); +} + +/* RC4 Filter */ + +typedef struct fz_arc4c_s fz_arc4c; + +struct fz_arc4c_s +{ + fz_stream *chain; + fz_arc4 arc4; +}; + +static int +read_arc4(fz_stream *stm, unsigned char *buf, int len) +{ + fz_arc4c *state = stm->state; + int n = fz_read(state->chain, buf, len); + fz_arc4_encrypt(&state->arc4, buf, buf, n); + return n; +} + +static void +close_arc4(fz_context *ctx, void *state_) +{ + fz_arc4c *state = (fz_arc4c *)state_; + fz_stream *chain = state->chain; + + fz_free(ctx, state); + fz_close(chain); +} + +fz_stream * +fz_open_arc4(fz_stream *chain, unsigned char *key, unsigned keylen) +{ + fz_arc4c *state; + fz_context *ctx = chain->ctx; + + fz_try(ctx) + { + state = fz_malloc_struct(ctx, fz_arc4c); + state->chain = chain; + fz_arc4_init(&state->arc4, key, keylen); + } + fz_catch(ctx) + { + fz_close(chain); + fz_rethrow(ctx); + } + + return fz_new_stream(ctx, state, read_arc4, close_arc4); +} + +/* AES Filter */ + +typedef struct fz_aesd_s fz_aesd; + +struct fz_aesd_s +{ + fz_stream *chain; + fz_aes aes; + unsigned char iv[16]; + int ivcount; + unsigned char bp[16]; + unsigned char *rp, *wp; +}; + +static int +read_aesd(fz_stream *stm, unsigned char *buf, int len) +{ + fz_aesd *state = stm->state; + unsigned char *p = buf; + unsigned char *ep = buf + len; + + while (state->ivcount < 16) + { + int c = fz_read_byte(state->chain); + if (c < 0) + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "premature end in aes filter"); + state->iv[state->ivcount++] = c; + } + + while (state->rp < state->wp && p < ep) + *p++ = *state->rp++; + + while (p < ep) + { + int n = fz_read(state->chain, state->bp, 16); + if (n == 0) + return p - buf; + else if (n < 16) + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "partial block in aes filter"); + + aes_crypt_cbc(&state->aes, AES_DECRYPT, 16, state->iv, state->bp, state->bp); + state->rp = state->bp; + state->wp = state->bp + 16; + + /* strip padding at end of file */ + if (fz_is_eof(state->chain)) + { + int pad = state->bp[15]; + if (pad < 1 || pad > 16) + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "aes padding out of range: %d", pad); + state->wp -= pad; + } + + while (state->rp < state->wp && p < ep) + *p++ = *state->rp++; + } + + return p - buf; +} + +static void +close_aesd(fz_context *ctx, void *state_) +{ + fz_aesd *state = (fz_aesd *)state_; + fz_stream *chain = state->chain; + + fz_free(ctx, state); + fz_close(chain); +} + +fz_stream * +fz_open_aesd(fz_stream *chain, unsigned char *key, unsigned keylen) +{ + fz_aesd *state; + fz_context *ctx = chain->ctx; + + fz_try(ctx) + { + state = fz_malloc_struct(ctx, fz_aesd); + state->chain = chain; + if (aes_setkey_dec(&state->aes, key, keylen * 8)) + fz_throw(ctx, FZ_ERROR_GENERIC, "AES key init failed (keylen=%d)", keylen * 8); + state->ivcount = 0; + state->rp = state->bp; + state->wp = state->bp; + } + fz_catch(ctx) + { + fz_close(chain); + fz_rethrow(ctx); + } + + return fz_new_stream(ctx, state, read_aesd, close_aesd); +} diff --git a/source/fitz/filter-dct.c b/source/fitz/filter-dct.c new file mode 100644 index 00000000..1a55e584 --- /dev/null +++ b/source/fitz/filter-dct.c @@ -0,0 +1,256 @@ +#include "mupdf/fitz.h" + +#include <jpeglib.h> +#include <setjmp.h> + +typedef struct fz_dctd_s fz_dctd; + +struct fz_dctd_s +{ + fz_stream *chain; + fz_context *ctx; + int color_transform; + int init; + int stride; + int l2factor; + unsigned char *scanline; + unsigned char *rp, *wp; + struct jpeg_decompress_struct cinfo; + struct jpeg_source_mgr srcmgr; + struct jpeg_error_mgr errmgr; + jmp_buf jb; + char msg[JMSG_LENGTH_MAX]; +}; + +static void error_exit(j_common_ptr cinfo) +{ + fz_dctd *state = cinfo->client_data; + cinfo->err->format_message(cinfo, state->msg); + longjmp(state->jb, 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) +{ + struct jpeg_source_mgr *src = cinfo->src; + fz_dctd *state = cinfo->client_data; + fz_stream *chain = state->chain; + fz_context *ctx = chain->ctx; + + chain->rp = chain->wp; + fz_try(ctx) + { + fz_fill_buffer(chain); + } + fz_catch(ctx) + { + /* FIXME: TRYLATER */ + return 0; + } + src->next_input_byte = chain->rp; + src->bytes_in_buffer = chain->wp - chain->rp; + + if (src->bytes_in_buffer == 0) + { + static unsigned char eoi[2] = { 0xFF, JPEG_EOI }; + fz_warn(state->ctx, "premature end of file in jpeg"); + 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) + { + while ((size_t)num_bytes > src->bytes_in_buffer) + { + num_bytes -= src->bytes_in_buffer; + (void) src->fill_input_buffer(cinfo); + } + src->next_input_byte += num_bytes; + src->bytes_in_buffer -= num_bytes; + } +} + +static int +read_dctd(fz_stream *stm, unsigned char *buf, int len) +{ + fz_dctd *state = stm->state; + j_decompress_ptr cinfo = &state->cinfo; + unsigned char *p = buf; + unsigned char *ep = buf + len; + + if (setjmp(state->jb)) + { + if (cinfo->src) + state->chain->rp = state->chain->wp - cinfo->src->bytes_in_buffer; + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "jpeg error: %s", state->msg); + } + + if (!state->init) + { + int c; + cinfo->client_data = state; + cinfo->err = &state->errmgr; + jpeg_std_error(cinfo->err); + cinfo->err->error_exit = error_exit; + jpeg_create_decompress(cinfo); + state->init = 1; + + /* Skip over any stray returns at the start of the stream */ + while ((c = fz_peek_byte(state->chain)) == '\n' || c == '\r') + (void)fz_read_byte(state->chain); + + cinfo->src = &state->srcmgr; + cinfo->src->init_source = init_source; + cinfo->src->fill_input_buffer = fill_input_buffer; + cinfo->src->skip_input_data = skip_input_data; + cinfo->src->resync_to_restart = jpeg_resync_to_restart; + cinfo->src->term_source = term_source; + cinfo->src->next_input_byte = state->chain->rp; + cinfo->src->bytes_in_buffer = state->chain->wp - state->chain->rp; + + jpeg_read_header(cinfo, 1); + + /* speed up jpeg decoding a bit */ + cinfo->dct_method = JDCT_FASTEST; + cinfo->do_fancy_upsampling = FALSE; + + /* default value if ColorTransform is not set */ + if (state->color_transform == -1) + { + if (state->cinfo.num_components == 3) + state->color_transform = 1; + else + state->color_transform = 0; + } + + if (cinfo->saw_Adobe_marker) + state->color_transform = cinfo->Adobe_transform; + + /* Guess the input colorspace, and set output colorspace accordingly */ + switch (cinfo->num_components) + { + case 3: + if (state->color_transform) + cinfo->jpeg_color_space = JCS_YCbCr; + else + cinfo->jpeg_color_space = JCS_RGB; + break; + case 4: + if (state->color_transform) + cinfo->jpeg_color_space = JCS_YCCK; + else + cinfo->jpeg_color_space = JCS_CMYK; + break; + } + + cinfo->scale_num = 8/(1<<state->l2factor); + cinfo->scale_denom = 8; + + jpeg_start_decompress(cinfo); + + state->stride = cinfo->output_width * cinfo->output_components; + state->scanline = fz_malloc(state->ctx, state->stride); + state->rp = state->scanline; + state->wp = state->scanline; + } + + while (state->rp < state->wp && p < ep) + *p++ = *state->rp++; + + while (p < ep) + { + if (cinfo->output_scanline == cinfo->output_height) + break; + + if (p + state->stride <= ep) + { + jpeg_read_scanlines(cinfo, &p, 1); + p += state->stride; + } + else + { + jpeg_read_scanlines(cinfo, &state->scanline, 1); + state->rp = state->scanline; + state->wp = state->scanline + state->stride; + } + + while (state->rp < state->wp && p < ep) + *p++ = *state->rp++; + } + + return p - buf; +} + +static void +close_dctd(fz_context *ctx, void *state_) +{ + fz_dctd *state = (fz_dctd *)state_; + + if (setjmp(state->jb)) + { + fz_warn(ctx, "jpeg error: %s", state->msg); + goto skip; + } + + if (state->init) + jpeg_finish_decompress(&state->cinfo); + +skip: + if (state->cinfo.src) + state->chain->rp = state->chain->wp - state->cinfo.src->bytes_in_buffer; + if (state->init) + jpeg_destroy_decompress(&state->cinfo); + + fz_free(ctx, state->scanline); + fz_close(state->chain); + fz_free(ctx, state); +} + +/* Default: color_transform = -1 (unset) */ +fz_stream * +fz_open_dctd(fz_stream *chain, int color_transform) +{ + return fz_open_resized_dctd(chain, color_transform, 0); +} + +fz_stream * +fz_open_resized_dctd(fz_stream *chain, int color_transform, int l2factor) +{ + fz_context *ctx = chain->ctx; + fz_dctd *state = NULL; + + fz_var(state); + + fz_try(ctx) + { + state = fz_malloc_struct(chain->ctx, fz_dctd); + state->ctx = ctx; + state->chain = chain; + state->color_transform = color_transform; + state->init = 0; + state->l2factor = l2factor; + } + fz_catch(ctx) + { + fz_free(ctx, state); + fz_close(chain); + fz_rethrow(ctx); + } + + return fz_new_stream(ctx, state, read_dctd, close_dctd); +} diff --git a/source/fitz/filter-fax.c b/source/fitz/filter-fax.c new file mode 100644 index 00000000..8ac98f42 --- /dev/null +++ b/source/fitz/filter-fax.c @@ -0,0 +1,776 @@ +#include "mupdf/fitz.h" + +/* Fax G3/G4 decoder */ + +/* TODO: uncompressed */ + +/* +<raph> the first 2^(initialbits) entries map bit patterns to decodes +<raph> let's say initial_bits is 8 for the sake of example +<raph> and that the code is 1001 +<raph> that means that entries 0x90 .. 0x9f have the entry { val, 4 } +<raph> because those are all the bytes that start with the code +<raph> and the 4 is the length of the code +... if (n_bits > initial_bits) ... +<raph> anyway, in that case, it basically points to a mini table +<raph> the n_bits is the maximum length of all codes beginning with that byte +<raph> so 2^(n_bits - initial_bits) is the size of the mini-table +<raph> peter came up with this, and it makes sense +*/ + +typedef struct cfd_node_s cfd_node; + +struct cfd_node_s +{ + short val; + short nbits; +}; + +enum +{ + cfd_white_initial_bits = 8, + cfd_black_initial_bits = 7, + cfd_2d_initial_bits = 7, + cfd_uncompressed_initial_bits = 6 /* must be 6 */ +}; + +/* non-run codes in tables */ +enum +{ + ERROR = -1, + ZEROS = -2, /* EOL follows, possibly with more padding first */ + UNCOMPRESSED = -3 +}; + +/* semantic codes for cf_2d_decode */ +enum +{ + P = -4, + H = -5, + VR3 = 0, + VR2 = 1, + VR1 = 2, + V0 = 3, + VL1 = 4, + VL2 = 5, + VL3 = 6 +}; + +/* White decoding table. */ +static const cfd_node cf_white_decode[] = { + {256,12},{272,12},{29,8},{30,8},{45,8},{46,8},{22,7},{22,7}, + {23,7},{23,7},{47,8},{48,8},{13,6},{13,6},{13,6},{13,6},{20,7}, + {20,7},{33,8},{34,8},{35,8},{36,8},{37,8},{38,8},{19,7},{19,7}, + {31,8},{32,8},{1,6},{1,6},{1,6},{1,6},{12,6},{12,6},{12,6},{12,6}, + {53,8},{54,8},{26,7},{26,7},{39,8},{40,8},{41,8},{42,8},{43,8}, + {44,8},{21,7},{21,7},{28,7},{28,7},{61,8},{62,8},{63,8},{0,8}, + {320,8},{384,8},{10,5},{10,5},{10,5},{10,5},{10,5},{10,5},{10,5}, + {10,5},{11,5},{11,5},{11,5},{11,5},{11,5},{11,5},{11,5},{11,5}, + {27,7},{27,7},{59,8},{60,8},{288,9},{290,9},{18,7},{18,7},{24,7}, + {24,7},{49,8},{50,8},{51,8},{52,8},{25,7},{25,7},{55,8},{56,8}, + {57,8},{58,8},{192,6},{192,6},{192,6},{192,6},{1664,6},{1664,6}, + {1664,6},{1664,6},{448,8},{512,8},{292,9},{640,8},{576,8},{294,9}, + {296,9},{298,9},{300,9},{302,9},{256,7},{256,7},{2,4},{2,4},{2,4}, + {2,4},{2,4},{2,4},{2,4},{2,4},{2,4},{2,4},{2,4},{2,4},{2,4},{2,4}, + {2,4},{2,4},{3,4},{3,4},{3,4},{3,4},{3,4},{3,4},{3,4},{3,4},{3,4}, + {3,4},{3,4},{3,4},{3,4},{3,4},{3,4},{3,4},{128,5},{128,5},{128,5}, + {128,5},{128,5},{128,5},{128,5},{128,5},{8,5},{8,5},{8,5},{8,5}, + {8,5},{8,5},{8,5},{8,5},{9,5},{9,5},{9,5},{9,5},{9,5},{9,5},{9,5}, + {9,5},{16,6},{16,6},{16,6},{16,6},{17,6},{17,6},{17,6},{17,6}, + {4,4},{4,4},{4,4},{4,4},{4,4},{4,4},{4,4},{4,4},{4,4},{4,4},{4,4}, + {4,4},{4,4},{4,4},{4,4},{4,4},{5,4},{5,4},{5,4},{5,4},{5,4},{5,4}, + {5,4},{5,4},{5,4},{5,4},{5,4},{5,4},{5,4},{5,4},{5,4},{5,4}, + {14,6},{14,6},{14,6},{14,6},{15,6},{15,6},{15,6},{15,6},{64,5}, + {64,5},{64,5},{64,5},{64,5},{64,5},{64,5},{64,5},{6,4},{6,4}, + {6,4},{6,4},{6,4},{6,4},{6,4},{6,4},{6,4},{6,4},{6,4},{6,4},{6,4}, + {6,4},{6,4},{6,4},{7,4},{7,4},{7,4},{7,4},{7,4},{7,4},{7,4},{7,4}, + {7,4},{7,4},{7,4},{7,4},{7,4},{7,4},{7,4},{7,4},{-2,3},{-2,3}, + {-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0}, + {-1,0},{-1,0},{-1,0},{-1,0},{-3,4},{1792,3},{1792,3},{1984,4}, + {2048,4},{2112,4},{2176,4},{2240,4},{2304,4},{1856,3},{1856,3}, + {1920,3},{1920,3},{2368,4},{2432,4},{2496,4},{2560,4},{1472,1}, + {1536,1},{1600,1},{1728,1},{704,1},{768,1},{832,1},{896,1}, + {960,1},{1024,1},{1088,1},{1152,1},{1216,1},{1280,1},{1344,1}, + {1408,1} +}; + +/* Black decoding table. */ +static const cfd_node cf_black_decode[] = { + {128,12},{160,13},{224,12},{256,12},{10,7},{11,7},{288,12},{12,7}, + {9,6},{9,6},{8,6},{8,6},{7,5},{7,5},{7,5},{7,5},{6,4},{6,4},{6,4}, + {6,4},{6,4},{6,4},{6,4},{6,4},{5,4},{5,4},{5,4},{5,4},{5,4},{5,4}, + {5,4},{5,4},{1,3},{1,3},{1,3},{1,3},{1,3},{1,3},{1,3},{1,3},{1,3}, + {1,3},{1,3},{1,3},{1,3},{1,3},{1,3},{1,3},{4,3},{4,3},{4,3},{4,3}, + {4,3},{4,3},{4,3},{4,3},{4,3},{4,3},{4,3},{4,3},{4,3},{4,3},{4,3}, + {4,3},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2}, + {3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2}, + {3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2},{3,2}, + {2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2}, + {2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2}, + {2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2},{2,2}, + {-2,4},{-2,4},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0}, + {-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-3,5},{1792,4}, + {1792,4},{1984,5},{2048,5},{2112,5},{2176,5},{2240,5},{2304,5}, + {1856,4},{1856,4},{1920,4},{1920,4},{2368,5},{2432,5},{2496,5}, + {2560,5},{18,3},{18,3},{18,3},{18,3},{18,3},{18,3},{18,3},{18,3}, + {52,5},{52,5},{640,6},{704,6},{768,6},{832,6},{55,5},{55,5}, + {56,5},{56,5},{1280,6},{1344,6},{1408,6},{1472,6},{59,5},{59,5}, + {60,5},{60,5},{1536,6},{1600,6},{24,4},{24,4},{24,4},{24,4}, + {25,4},{25,4},{25,4},{25,4},{1664,6},{1728,6},{320,5},{320,5}, + {384,5},{384,5},{448,5},{448,5},{512,6},{576,6},{53,5},{53,5}, + {54,5},{54,5},{896,6},{960,6},{1024,6},{1088,6},{1152,6},{1216,6}, + {64,3},{64,3},{64,3},{64,3},{64,3},{64,3},{64,3},{64,3},{13,1}, + {13,1},{13,1},{13,1},{13,1},{13,1},{13,1},{13,1},{13,1},{13,1}, + {13,1},{13,1},{13,1},{13,1},{13,1},{13,1},{23,4},{23,4},{50,5}, + {51,5},{44,5},{45,5},{46,5},{47,5},{57,5},{58,5},{61,5},{256,5}, + {16,3},{16,3},{16,3},{16,3},{17,3},{17,3},{17,3},{17,3},{48,5}, + {49,5},{62,5},{63,5},{30,5},{31,5},{32,5},{33,5},{40,5},{41,5}, + {22,4},{22,4},{14,1},{14,1},{14,1},{14,1},{14,1},{14,1},{14,1}, + {14,1},{14,1},{14,1},{14,1},{14,1},{14,1},{14,1},{14,1},{14,1}, + {15,2},{15,2},{15,2},{15,2},{15,2},{15,2},{15,2},{15,2},{128,5}, + {192,5},{26,5},{27,5},{28,5},{29,5},{19,4},{19,4},{20,4},{20,4}, + {34,5},{35,5},{36,5},{37,5},{38,5},{39,5},{21,4},{21,4},{42,5}, + {43,5},{0,3},{0,3},{0,3},{0,3} +}; + +/* 2-D decoding table. */ +static const cfd_node cf_2d_decode[] = { + {128,11},{144,10},{6,7},{0,7},{5,6},{5,6},{1,6},{1,6},{-4,4}, + {-4,4},{-4,4},{-4,4},{-4,4},{-4,4},{-4,4},{-4,4},{-5,3},{-5,3}, + {-5,3},{-5,3},{-5,3},{-5,3},{-5,3},{-5,3},{-5,3},{-5,3},{-5,3}, + {-5,3},{-5,3},{-5,3},{-5,3},{-5,3},{4,3},{4,3},{4,3},{4,3},{4,3}, + {4,3},{4,3},{4,3},{4,3},{4,3},{4,3},{4,3},{4,3},{4,3},{4,3},{4,3}, + {2,3},{2,3},{2,3},{2,3},{2,3},{2,3},{2,3},{2,3},{2,3},{2,3},{2,3}, + {2,3},{2,3},{2,3},{2,3},{2,3},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1}, + {3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1}, + {3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1}, + {3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1}, + {3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1}, + {3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1},{3,1}, + {3,1},{3,1},{3,1},{-2,4},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0}, + {-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0}, + {-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-1,0},{-3,3} +}; + +/* Uncompressed decoding table. */ +static const cfd_node cf_uncompressed_decode[] = { + {64,12},{5,6},{4,5},{4,5},{3,4},{3,4},{3,4},{3,4},{2,3},{2,3}, + {2,3},{2,3},{2,3},{2,3},{2,3},{2,3},{1,2},{1,2},{1,2},{1,2},{1,2}, + {1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2}, + {0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1}, + {0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1}, + {0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1}, + {-1,0},{-1,0},{8,6},{9,6},{6,5},{6,5},{7,5},{7,5},{4,4},{4,4}, + {4,4},{4,4},{5,4},{5,4},{5,4},{5,4},{2,3},{2,3},{2,3},{2,3},{2,3}, + {2,3},{2,3},{2,3},{3,3},{3,3},{3,3},{3,3},{3,3},{3,3},{3,3},{3,3}, + {0,2},{0,2},{0,2},{0,2},{0,2},{0,2},{0,2},{0,2},{0,2},{0,2},{0,2}, + {0,2},{0,2},{0,2},{0,2},{0,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2}, + {1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2} +}; + +/* bit magic */ + +static inline int getbit(const unsigned char *buf, int x) +{ + return ( buf[x >> 3] >> ( 7 - (x & 7) ) ) & 1; +} + +static const unsigned char mask[8] = { + 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0 +}; + +static const unsigned char clz[256] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static inline int +find_changing(const unsigned char *line, int x, int w) +{ + int a, b, m, W; + + if (!line) + return w; + + /* We assume w > 0, -1 <= x < w */ + if (x < 0) + { + x = 0; + m = 0xFF; + } + else + { + /* Mask out the bits we've already used (including the one + * we started from) */ + m = mask[x & 7]; + } + /* We have 'w' pixels (bits) in line. The last pixel that can be + * safely accessed is the (w-1)th bit of line. + * By taking W = w>>3, we know that the first W bytes of line are + * full, with w&7 stray bits following. */ + W = w>>3; + x >>= 3; + a = line[x]; /* Safe as x < w => x <= w-1 => x>>3 <= (w-1)>>3 */ + b = a ^ (a>>1); + b &= m; + if (x >= W) + { + /* Within the last byte already */ + x = (x<<3) + clz[b]; + if (x > w) + x = w; + return x; + } + while (b == 0) + { + if (++x >= W) + goto nearend; + b = a & 1; + a = line[x]; + b = (b<<7) ^ a ^ (a>>1); + } + return (x<<3) + clz[b]; +nearend: + /* We have less than a byte to go. If no stray bits, exit now. */ + if ((x<<3) == w) + return w; + b = a&1; + a = line[x]; + b = (b<<7) ^ a ^ (a>>1); + x = (x<<3) + clz[b]; + if (x > w) + x = w; + return x; +} + +static inline int +find_changing_color(const unsigned char *line, int x, int w, int color) +{ + if (!line || x >= w) + return w; + + x = find_changing(line, (x > 0 || !color) ? x : -1, w); + + if (x < w && getbit(line, x) != color) + x = find_changing(line, x, w); + + return x; +} + +static const unsigned char lm[8] = { + 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 +}; + +static const unsigned char rm[8] = { + 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE +}; + +static inline void setbits(unsigned char *line, int x0, int x1) +{ + int a0, a1, b0, b1, a; + + if (x1 <= x0) + return; + + a0 = x0 >> 3; + a1 = x1 >> 3; + + b0 = x0 & 7; + b1 = x1 & 7; + + if (a0 == a1) + { + if (b1) + line[a0] |= lm[b0] & rm[b1]; + } + else + { + line[a0] |= lm[b0]; + for (a = a0 + 1; a < a1; a++) + line[a] = 0xFF; + if (b1) + line[a1] |= rm[b1]; + } +} + +typedef struct fz_faxd_s fz_faxd; + +enum +{ + STATE_NORMAL, /* neutral state, waiting for any code */ + STATE_MAKEUP, /* got a 1d makeup code, waiting for terminating code */ + STATE_EOL, /* at eol, needs output buffer space */ + STATE_H1, STATE_H2, /* in H part 1 and 2 (both makeup and terminating codes) */ + STATE_DONE /* all done */ +}; + +struct fz_faxd_s +{ + fz_context *ctx; + fz_stream *chain; + + int k; + int end_of_line; + int encoded_byte_align; + int columns; + int rows; + int end_of_block; + int black_is_1; + + int stride; + int ridx; + + int bidx; + unsigned int word; + + int stage; + + int a, c, dim, eolc; + unsigned char *ref; + unsigned char *dst; + unsigned char *rp, *wp; +}; + +static inline void eat_bits(fz_faxd *fax, int nbits) +{ + fax->word <<= nbits; + fax->bidx += nbits; +} + +static int +fill_bits(fz_faxd *fax) +{ + while (fax->bidx >= 8) + { + int c = fz_read_byte(fax->chain); + if (c == EOF) + return EOF; + fax->bidx -= 8; + fax->word |= c << fax->bidx; + } + return 0; +} + +static int +get_code(fz_faxd *fax, const cfd_node *table, int initialbits) +{ + unsigned int word = fax->word; + int tidx = word >> (32 - initialbits); + int val = table[tidx].val; + int nbits = table[tidx].nbits; + + if (nbits > initialbits) + { + int mask = (1 << (32 - initialbits)) - 1; + tidx = val + ((word & mask) >> (32 - nbits)); + val = table[tidx].val; + nbits = initialbits + table[tidx].nbits; + } + + eat_bits(fax, nbits); + + return val; +} + +/* decode one 1d code */ +static void +dec1d(fz_context *ctx, fz_faxd *fax) +{ + int code; + + if (fax->a == -1) + fax->a = 0; + + if (fax->c) + code = get_code(fax, cf_black_decode, cfd_black_initial_bits); + else + code = get_code(fax, cf_white_decode, cfd_white_initial_bits); + + if (code == UNCOMPRESSED) + fz_throw(ctx, FZ_ERROR_GENERIC, "uncompressed data in faxd"); + + if (code < 0) + fz_throw(ctx, FZ_ERROR_GENERIC, "negative code in 1d faxd"); + + if (fax->a + code > fax->columns) + fz_throw(ctx, FZ_ERROR_GENERIC, "overflow in 1d faxd"); + + if (fax->c) + setbits(fax->dst, fax->a, fax->a + code); + + fax->a += code; + + if (code < 64) + { + fax->c = !fax->c; + fax->stage = STATE_NORMAL; + } + else + fax->stage = STATE_MAKEUP; +} + +/* decode one 2d code */ +static void +dec2d(fz_context *ctx, fz_faxd *fax) +{ + int code, b1, b2; + + if (fax->stage == STATE_H1 || fax->stage == STATE_H2) + { + if (fax->a == -1) + fax->a = 0; + + if (fax->c) + code = get_code(fax, cf_black_decode, cfd_black_initial_bits); + else + code = get_code(fax, cf_white_decode, cfd_white_initial_bits); + + if (code == UNCOMPRESSED) + fz_throw(ctx, FZ_ERROR_GENERIC, "uncompressed data in faxd"); + + if (code < 0) + fz_throw(ctx, FZ_ERROR_GENERIC, "negative code in 2d faxd"); + + if (fax->a + code > fax->columns) + fz_throw(ctx, FZ_ERROR_GENERIC, "overflow in 2d faxd"); + + if (fax->c) + setbits(fax->dst, fax->a, fax->a + code); + + fax->a += code; + + if (code < 64) + { + fax->c = !fax->c; + if (fax->stage == STATE_H1) + fax->stage = STATE_H2; + else if (fax->stage == STATE_H2) + fax->stage = STATE_NORMAL; + } + + return; + } + + code = get_code(fax, cf_2d_decode, cfd_2d_initial_bits); + + switch (code) + { + case H: + fax->stage = STATE_H1; + break; + + case P: + b1 = find_changing_color(fax->ref, fax->a, fax->columns, !fax->c); + if (b1 >= fax->columns) + b2 = fax->columns; + else + b2 = find_changing(fax->ref, b1, fax->columns); + if (fax->c) setbits(fax->dst, fax->a, b2); + fax->a = b2; + break; + + case V0: + b1 = find_changing_color(fax->ref, fax->a, fax->columns, !fax->c); + if (fax->c) setbits(fax->dst, fax->a, b1); + fax->a = b1; + fax->c = !fax->c; + break; + + case VR1: + b1 = 1 + find_changing_color(fax->ref, fax->a, fax->columns, !fax->c); + if (b1 >= fax->columns) b1 = fax->columns; + if (fax->c) setbits(fax->dst, fax->a, b1); + fax->a = b1; + fax->c = !fax->c; + break; + + case VR2: + b1 = 2 + find_changing_color(fax->ref, fax->a, fax->columns, !fax->c); + if (b1 >= fax->columns) b1 = fax->columns; + if (fax->c) setbits(fax->dst, fax->a, b1); + fax->a = b1; + fax->c = !fax->c; + break; + + case VR3: + b1 = 3 + find_changing_color(fax->ref, fax->a, fax->columns, !fax->c); + if (b1 >= fax->columns) b1 = fax->columns; + if (fax->c) setbits(fax->dst, fax->a, b1); + fax->a = b1; + fax->c = !fax->c; + break; + + case VL1: + b1 = -1 + find_changing_color(fax->ref, fax->a, fax->columns, !fax->c); + if (b1 < 0) b1 = 0; + if (fax->c) setbits(fax->dst, fax->a, b1); + fax->a = b1; + fax->c = !fax->c; + break; + + case VL2: + b1 = -2 + find_changing_color(fax->ref, fax->a, fax->columns, !fax->c); + if (b1 < 0) b1 = 0; + if (fax->c) setbits(fax->dst, fax->a, b1); + fax->a = b1; + fax->c = !fax->c; + break; + + case VL3: + b1 = -3 + find_changing_color(fax->ref, fax->a, fax->columns, !fax->c); + if (b1 < 0) b1 = 0; + if (fax->c) setbits(fax->dst, fax->a, b1); + fax->a = b1; + fax->c = !fax->c; + break; + + case UNCOMPRESSED: + fz_throw(ctx, FZ_ERROR_GENERIC, "uncompressed data in faxd"); + + case ERROR: + fz_throw(ctx, FZ_ERROR_GENERIC, "invalid code in 2d faxd"); + + default: + fz_throw(ctx, FZ_ERROR_GENERIC, "invalid code in 2d faxd (%d)", code); + } +} + +static int +read_faxd(fz_stream *stm, unsigned char *buf, int len) +{ + fz_faxd *fax = stm->state; + unsigned char *p = buf; + unsigned char *ep = buf + len; + unsigned char *tmp; + + if (fax->stage == STATE_DONE) + return 0; + + if (fax->stage == STATE_EOL) + goto eol; + +loop: + + if (fill_bits(fax)) + { + if (fax->bidx > 31) + { + if (fax->a > 0) + goto eol; + goto rtc; + } + } + + if ((fax->word >> (32 - 12)) == 0) + { + eat_bits(fax, 1); + goto loop; + } + + if ((fax->word >> (32 - 12)) == 1) + { + eat_bits(fax, 12); + fax->eolc ++; + + if (fax->k > 0) + { + if (fax->a == -1) + fax->a = 0; + if ((fax->word >> (32 - 1)) == 1) + fax->dim = 1; + else + fax->dim = 2; + eat_bits(fax, 1); + } + } + else if (fax->k > 0 && fax->a == -1) + { + fax->a = 0; + if ((fax->word >> (32 - 1)) == 1) + fax->dim = 1; + else + fax->dim = 2; + eat_bits(fax, 1); + } + else if (fax->dim == 1) + { + fax->eolc = 0; + dec1d(stm->ctx, fax); + } + else if (fax->dim == 2) + { + fax->eolc = 0; + dec2d(stm->ctx, fax); + } + + /* no eol check after makeup codes nor in the middle of an H code */ + if (fax->stage == STATE_MAKEUP || fax->stage == STATE_H1 || fax->stage == STATE_H2) + goto loop; + + /* check for eol conditions */ + if (fax->eolc || fax->a >= fax->columns) + { + if (fax->a > 0) + goto eol; + if (fax->eolc == (fax->k < 0 ? 2 : 6)) + goto rtc; + } + + goto loop; + +eol: + fax->stage = STATE_EOL; + + if (fax->black_is_1) + { + while (fax->rp < fax->wp && p < ep) + *p++ = *fax->rp++; + } + else + { + while (fax->rp < fax->wp && p < ep) + *p++ = *fax->rp++ ^ 0xff; + } + + if (fax->rp < fax->wp) + return p - buf; + + tmp = fax->ref; + fax->ref = fax->dst; + fax->dst = tmp; + memset(fax->dst, 0, fax->stride); + + fax->rp = fax->dst; + fax->wp = fax->dst + fax->stride; + + fax->stage = STATE_NORMAL; + fax->c = 0; + fax->a = -1; + fax->ridx ++; + + if (!fax->end_of_block && fax->rows) + { + if (fax->ridx >= fax->rows) + goto rtc; + } + + /* we have not read dim from eol, make a guess */ + if (fax->k > 0 && !fax->eolc && fax->a == -1) + { + if (fax->ridx % fax->k == 0) + fax->dim = 1; + else + fax->dim = 2; + } + + /* if end_of_line & encoded_byte_align, EOLs are *not* optional */ + if (fax->encoded_byte_align) + { + if (fax->end_of_line) + eat_bits(fax, (12 - fax->bidx) & 7); + else + eat_bits(fax, (8 - fax->bidx) & 7); + } + + /* no more space in output, don't decode the next row yet */ + if (p == buf + len) + return p - buf; + + goto loop; + +rtc: + fax->stage = STATE_DONE; + return p - buf; +} + +static void +close_faxd(fz_context *ctx, void *state_) +{ + fz_faxd *fax = (fz_faxd *)state_; + int i; + + /* if we read any extra bytes, try to put them back */ + i = (32 - fax->bidx) / 8; + while (i--) + fz_unread_byte(fax->chain); + + fz_close(fax->chain); + fz_free(ctx, fax->ref); + fz_free(ctx, fax->dst); + fz_free(ctx, fax); +} + +/* Default: columns = 1728, end_of_block = 1, the rest = 0 */ +fz_stream * +fz_open_faxd(fz_stream *chain, + int k, int end_of_line, int encoded_byte_align, + int columns, int rows, int end_of_block, int black_is_1) +{ + fz_context *ctx = chain->ctx; + fz_faxd *fax = NULL; + + fz_var(fax); + + fz_try(ctx) + { + fax = fz_malloc_struct(ctx, fz_faxd); + fax->chain = chain; + + fax->ref = NULL; + fax->dst = NULL; + + fax->k = k; + fax->end_of_line = end_of_line; + fax->encoded_byte_align = encoded_byte_align; + fax->columns = columns; + fax->rows = rows; + fax->end_of_block = end_of_block; + fax->black_is_1 = black_is_1; + + fax->stride = ((fax->columns - 1) >> 3) + 1; + fax->ridx = 0; + fax->bidx = 32; + fax->word = 0; + + fax->stage = STATE_NORMAL; + fax->a = -1; + fax->c = 0; + fax->dim = fax->k < 0 ? 2 : 1; + fax->eolc = 0; + + fax->ref = fz_malloc(ctx, fax->stride); + fax->dst = fz_malloc(ctx, fax->stride); + fax->rp = fax->dst; + fax->wp = fax->dst + fax->stride; + + memset(fax->ref, 0, fax->stride); + memset(fax->dst, 0, fax->stride); + } + fz_catch(ctx) + { + if (fax) + { + fz_free(ctx, fax->dst); + fz_free(ctx, fax->ref); + } + fz_free(ctx, fax); + fz_close(chain); + fz_rethrow(ctx); + } + + return fz_new_stream(ctx, fax, read_faxd, close_faxd); +} diff --git a/source/fitz/filter-flate.c b/source/fitz/filter-flate.c new file mode 100644 index 00000000..73451d59 --- /dev/null +++ b/source/fitz/filter-flate.c @@ -0,0 +1,117 @@ +#include "mupdf/fitz.h" + +#include <zlib.h> + +typedef struct fz_flate_s fz_flate; + +struct fz_flate_s +{ + fz_stream *chain; + z_stream z; +}; + +static void *zalloc(void *opaque, unsigned int items, unsigned int size) +{ + return fz_malloc_array_no_throw(opaque, items, size); +} + +static void zfree(void *opaque, void *ptr) +{ + fz_free(opaque, ptr); +} + +static int +read_flated(fz_stream *stm, unsigned char *outbuf, int outlen) +{ + fz_flate *state = stm->state; + fz_stream *chain = state->chain; + z_streamp zp = &state->z; + int code; + + zp->next_out = outbuf; + zp->avail_out = outlen; + + while (zp->avail_out > 0) + { + if (chain->rp == chain->wp) + fz_fill_buffer(chain); + + zp->next_in = chain->rp; + zp->avail_in = chain->wp - chain->rp; + + code = inflate(zp, Z_SYNC_FLUSH); + + chain->rp = chain->wp - zp->avail_in; + + if (code == Z_STREAM_END) + { + return outlen - zp->avail_out; + } + else if (code == Z_BUF_ERROR) + { + fz_warn(stm->ctx, "premature end of data in flate filter"); + return outlen - zp->avail_out; + } + else if (code == Z_DATA_ERROR && zp->avail_in == 0) + { + fz_warn(stm->ctx, "ignoring zlib error: %s", zp->msg); + return outlen - zp->avail_out; + } + else if (code != Z_OK) + { + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "zlib error: %s", zp->msg); + } + } + + return outlen - zp->avail_out; +} + +static void +close_flated(fz_context *ctx, void *state_) +{ + fz_flate *state = (fz_flate *)state_; + int code; + + code = inflateEnd(&state->z); + if (code != Z_OK) + fz_warn(ctx, "zlib error: inflateEnd: %s", state->z.msg); + + fz_close(state->chain); + fz_free(ctx, state); +} + +fz_stream * +fz_open_flated(fz_stream *chain) +{ + fz_flate *state = NULL; + int code = Z_OK; + fz_context *ctx = chain->ctx; + + fz_var(code); + fz_var(state); + + fz_try(ctx) + { + state = fz_malloc_struct(ctx, fz_flate); + state->chain = chain; + + state->z.zalloc = zalloc; + state->z.zfree = zfree; + state->z.opaque = ctx; + state->z.next_in = NULL; + state->z.avail_in = 0; + + code = inflateInit(&state->z); + if (code != Z_OK) + fz_throw(ctx, FZ_ERROR_GENERIC, "zlib error: inflateInit: %s", state->z.msg); + } + fz_catch(ctx) + { + if (state && code == Z_OK) + inflateEnd(&state->z); + fz_free(ctx, state); + fz_close(chain); + fz_rethrow(ctx); + } + return fz_new_stream(ctx, state, read_flated, close_flated); +} diff --git a/source/fitz/filter-jbig2.c b/source/fitz/filter-jbig2.c new file mode 100644 index 00000000..12eb8c3b --- /dev/null +++ b/source/fitz/filter-jbig2.c @@ -0,0 +1,108 @@ +#include "mupdf/fitz.h" + +#include <jbig2.h> + +typedef struct fz_jbig2d_s fz_jbig2d; + +struct fz_jbig2d_s +{ + fz_stream *chain; + Jbig2Ctx *ctx; + Jbig2GlobalCtx *gctx; + Jbig2Image *page; + int idx; +}; + +static void +close_jbig2d(fz_context *ctx, void *state_) +{ + fz_jbig2d *state = (fz_jbig2d *)state_; + if (state->page) + jbig2_release_page(state->ctx, state->page); + if (state->gctx) + jbig2_global_ctx_free(state->gctx); + jbig2_ctx_free(state->ctx); + fz_close(state->chain); + fz_free(ctx, state); +} + +static int +read_jbig2d(fz_stream *stm, unsigned char *buf, int len) +{ + fz_jbig2d *state = stm->state; + unsigned char tmp[4096]; + unsigned char *p = buf; + unsigned char *ep = buf + len; + unsigned char *s; + int x, w, n; + + if (!state->page) + { + while (1) + { + n = fz_read(state->chain, tmp, sizeof tmp); + if (n == 0) + break; + jbig2_data_in(state->ctx, tmp, n); + } + + jbig2_complete_page(state->ctx); + + state->page = jbig2_page_out(state->ctx); + if (!state->page) + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "jbig2_page_out failed"); + } + + s = state->page->data; + w = state->page->height * state->page->stride; + x = state->idx; + while (p < ep && x < w) + *p++ = s[x++] ^ 0xff; + state->idx = x; + + return p - buf; +} + +fz_stream * +fz_open_jbig2d(fz_stream *chain, fz_buffer *globals) +{ + fz_jbig2d *state = NULL; + fz_context *ctx = chain->ctx; + + fz_var(state); + + fz_try(ctx) + { + state = fz_malloc_struct(chain->ctx, fz_jbig2d); + state->ctx = NULL; + state->gctx = NULL; + state->chain = chain; + state->ctx = jbig2_ctx_new(NULL, JBIG2_OPTIONS_EMBEDDED, NULL, NULL, NULL); + state->page = NULL; + state->idx = 0; + + if (globals) + { + jbig2_data_in(state->ctx, globals->data, globals->len); + state->gctx = jbig2_make_global_ctx(state->ctx); + state->ctx = jbig2_ctx_new(NULL, JBIG2_OPTIONS_EMBEDDED, state->gctx, NULL, NULL); + } + } + fz_catch(ctx) + { + if (state) + { + if (state->gctx) + jbig2_global_ctx_free(state->gctx); + if (state->ctx) + jbig2_ctx_free(state->ctx); + } + fz_drop_buffer(ctx, globals); + fz_free(ctx, state); + fz_close(chain); + fz_rethrow(ctx); + } + fz_drop_buffer(ctx, globals); + + return fz_new_stream(ctx, state, read_jbig2d, close_jbig2d); +} diff --git a/source/fitz/filter-lzw.c b/source/fitz/filter-lzw.c new file mode 100644 index 00000000..73909ca1 --- /dev/null +++ b/source/fitz/filter-lzw.c @@ -0,0 +1,224 @@ +#include "mupdf/fitz.h" + +/* TODO: error checking */ + +enum +{ + MIN_BITS = 9, + MAX_BITS = 12, + NUM_CODES = (1 << MAX_BITS), + LZW_CLEAR = 256, + LZW_EOD = 257, + LZW_FIRST = 258, + MAX_LENGTH = 4097 +}; + +typedef struct lzw_code_s lzw_code; + +struct lzw_code_s +{ + int prev; /* prev code (in string) */ + unsigned short length; /* string len, including this token */ + unsigned char value; /* data value */ + unsigned char first_char; /* first token of string */ +}; + +typedef struct fz_lzwd_s fz_lzwd; + +struct fz_lzwd_s +{ + fz_stream *chain; + int eod; + + int early_change; + + int code_bits; /* num bits/code */ + int code; /* current code */ + int old_code; /* previously recognized code */ + int next_code; /* next free entry */ + + lzw_code table[NUM_CODES]; + + unsigned char bp[MAX_LENGTH]; + unsigned char *rp, *wp; +}; + +static int +read_lzwd(fz_stream *stm, unsigned char *buf, int len) +{ + fz_lzwd *lzw = stm->state; + lzw_code *table = lzw->table; + unsigned char *p = buf; + unsigned char *ep = buf + len; + unsigned char *s; + int codelen; + + int code_bits = lzw->code_bits; + int code = lzw->code; + int old_code = lzw->old_code; + int next_code = lzw->next_code; + + while (lzw->rp < lzw->wp && p < ep) + *p++ = *lzw->rp++; + + while (p < ep) + { + if (lzw->eod) + return 0; + + code = fz_read_bits(lzw->chain, code_bits); + + if (fz_is_eof_bits(lzw->chain)) + { + lzw->eod = 1; + break; + } + + if (code == LZW_EOD) + { + lzw->eod = 1; + break; + } + + if (next_code >= NUM_CODES && code != LZW_CLEAR) + { + fz_warn(stm->ctx, "missing clear code in lzw decode"); + code = LZW_CLEAR; + } + + if (code == LZW_CLEAR) + { + code_bits = MIN_BITS; + next_code = LZW_FIRST; + old_code = -1; + continue; + } + + /* if stream starts without a clear code, old_code is undefined... */ + if (old_code == -1) + { + old_code = code; + } + else if (code > next_code || next_code >= NUM_CODES) + { + fz_warn(stm->ctx, "out of range code encountered in lzw decode"); + } + else + { + /* add new entry to the code table */ + table[next_code].prev = old_code; + table[next_code].first_char = table[old_code].first_char; + table[next_code].length = table[old_code].length + 1; + if (code < next_code) + table[next_code].value = table[code].first_char; + else if (code == next_code) + table[next_code].value = table[next_code].first_char; + else + fz_warn(stm->ctx, "out of range code encountered in lzw decode"); + + next_code ++; + + if (next_code > (1 << code_bits) - lzw->early_change - 1) + { + code_bits ++; + if (code_bits > MAX_BITS) + code_bits = MAX_BITS; + } + + old_code = code; + } + + /* code maps to a string, copy to output (in reverse...) */ + if (code > 255) + { + codelen = table[code].length; + lzw->rp = lzw->bp; + lzw->wp = lzw->bp + codelen; + + assert(codelen < MAX_LENGTH); + + s = lzw->wp; + do { + *(--s) = table[code].value; + code = table[code].prev; + } while (code >= 0 && s > lzw->bp); + } + + /* ... or just a single character */ + else + { + lzw->bp[0] = code; + lzw->rp = lzw->bp; + lzw->wp = lzw->bp + 1; + } + + /* copy to output */ + while (lzw->rp < lzw->wp && p < ep) + *p++ = *lzw->rp++; + } + + lzw->code_bits = code_bits; + lzw->code = code; + lzw->old_code = old_code; + lzw->next_code = next_code; + + return p - buf; +} + +static void +close_lzwd(fz_context *ctx, void *state_) +{ + fz_lzwd *lzw = (fz_lzwd *)state_; + fz_close(lzw->chain); + fz_free(ctx, lzw); +} + +/* Default: early_change = 1 */ +fz_stream * +fz_open_lzwd(fz_stream *chain, int early_change) +{ + fz_context *ctx = chain->ctx; + fz_lzwd *lzw = NULL; + int i; + + fz_var(lzw); + + fz_try(ctx) + { + lzw = fz_malloc_struct(ctx, fz_lzwd); + lzw->chain = chain; + lzw->eod = 0; + lzw->early_change = early_change; + + for (i = 0; i < 256; i++) + { + lzw->table[i].value = i; + lzw->table[i].first_char = i; + lzw->table[i].length = 1; + lzw->table[i].prev = -1; + } + + for (i = 256; i < NUM_CODES; i++) + { + lzw->table[i].value = 0; + lzw->table[i].first_char = 0; + lzw->table[i].length = 0; + lzw->table[i].prev = -1; + } + + lzw->code_bits = MIN_BITS; + lzw->code = -1; + lzw->next_code = LZW_FIRST; + lzw->old_code = -1; + lzw->rp = lzw->bp; + lzw->wp = lzw->bp; + } + fz_catch(ctx) + { + fz_free(ctx, lzw); + fz_close(chain); + fz_rethrow(ctx); + } + + return fz_new_stream(ctx, lzw, read_lzwd, close_lzwd); +} diff --git a/source/fitz/filter-predict.c b/source/fitz/filter-predict.c new file mode 100644 index 00000000..c8de290a --- /dev/null +++ b/source/fitz/filter-predict.c @@ -0,0 +1,256 @@ +#include "mupdf/fitz.h" + +/* TODO: check if this works with 16bpp images */ + +enum { MAXC = 32 }; + +typedef struct fz_predict_s fz_predict; + +struct fz_predict_s +{ + fz_stream *chain; + + int predictor; + int columns; + int colors; + int bpc; + + int stride; + int bpp; + unsigned char *in; + unsigned char *out; + unsigned char *ref; + unsigned char *rp, *wp; +}; + +static inline int getcomponent(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 putcomponent(unsigned char *buf, int x, int bpc, int value) +{ + switch (bpc) + { + case 1: buf[x >> 3] |= value << (7 - (x & 7)); break; + case 2: buf[x >> 2] |= value << ((3 - (x & 3)) << 1); break; + case 4: buf[x >> 1] |= value << ((1 - (x & 1)) << 2); break; + case 8: buf[x] = value; break; + case 16: buf[x<<1] = value>>8; buf[(x<<1)+1] = value; break; + } +} + +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 = fz_absi(ac); + int pb = fz_absi(bc); + int pc = fz_absi(abcc); + return pa <= pb && pa <= pc ? a : pb <= pc ? b : c; +} + +static void +fz_predict_tiff(fz_predict *state, unsigned char *out, unsigned char *in, int len) +{ + int left[MAXC]; + int i, k; + const int mask = (1 << state->bpc)-1; + + for (k = 0; k < state->colors; k++) + left[k] = 0; + memset(out, 0, state->stride); + + for (i = 0; i < state->columns; i++) + { + for (k = 0; k < state->colors; k++) + { + int a = getcomponent(in, i * state->colors + k, state->bpc); + int b = a + left[k]; + int c = b & mask; + putcomponent(out, i * state->colors + k, state->bpc, c); + left[k] = c; + } + } +} + +static void +fz_predict_png(fz_predict *state, unsigned char *out, unsigned char *in, int len, int predictor) +{ + int bpp = state->bpp; + int i; + unsigned char *ref = state->ref; + + switch (predictor) + { + case 0: + memcpy(out, in, len); + break; + case 1: + for (i = bpp; i > 0; i--) + { + *out++ = *in++; + } + for (i = len - bpp; i > 0; i--) + { + *out = *in++ + out[-bpp]; + out++; + } + break; + case 2: + for (i = bpp; i > 0; i--) + { + *out++ = *in++ + *ref++; + } + for (i = len - bpp; i > 0; i--) + { + *out++ = *in++ + *ref++; + } + break; + case 3: + for (i = bpp; i > 0; i--) + { + *out++ = *in++ + (*ref++) / 2; + } + for (i = len - bpp; i > 0; i--) + { + *out = *in++ + (out[-bpp] + *ref++) / 2; + out++; + } + break; + case 4: + for (i = bpp; i > 0; i--) + { + *out++ = *in++ + paeth(0, *ref++, 0); + } + for (i = len - bpp; i > 0; i --) + { + *out = *in++ + paeth(out[-bpp], *ref, ref[-bpp]); + ref++; + out++; + } + break; + } +} + +static int +read_predict(fz_stream *stm, unsigned char *buf, int len) +{ + fz_predict *state = stm->state; + unsigned char *p = buf; + unsigned char *ep = buf + len; + int ispng = state->predictor >= 10; + int n; + + while (state->rp < state->wp && p < ep) + *p++ = *state->rp++; + + while (p < ep) + { + n = fz_read(state->chain, state->in, state->stride + ispng); + if (n == 0) + return p - buf; + + if (state->predictor == 1) + memcpy(state->out, state->in, n); + else if (state->predictor == 2) + fz_predict_tiff(state, state->out, state->in, n); + else + { + fz_predict_png(state, state->out, state->in + 1, n - 1, state->in[0]); + memcpy(state->ref, state->out, state->stride); + } + + state->rp = state->out; + state->wp = state->out + n - ispng; + + while (state->rp < state->wp && p < ep) + *p++ = *state->rp++; + } + + return p - buf; +} + +static void +close_predict(fz_context *ctx, void *state_) +{ + fz_predict *state = (fz_predict *)state_; + fz_close(state->chain); + fz_free(ctx, state->in); + fz_free(ctx, state->out); + fz_free(ctx, state->ref); + fz_free(ctx, state); +} + +/* Default values: predictor = 1, columns = 1, colors = 1, bpc = 8 */ +fz_stream * +fz_open_predict(fz_stream *chain, int predictor, int columns, int colors, int bpc) +{ + fz_context *ctx = chain->ctx; + fz_predict *state = NULL; + + fz_var(state); + + if (predictor < 1) + predictor = 1; + if (columns < 1) + columns = 1; + if (colors < 1) + colors = 1; + if (bpc < 1) + bpc = 8; + + fz_try(ctx) + { + state = fz_malloc_struct(ctx, fz_predict); + state->in = NULL; + state->out = NULL; + state->chain = chain; + + state->predictor = predictor; + state->columns = columns; + state->colors = colors; + state->bpc = bpc; + + if (state->predictor != 1 && state->predictor != 2 && + state->predictor != 10 && state->predictor != 11 && + state->predictor != 12 && state->predictor != 13 && + state->predictor != 14 && state->predictor != 15) + { + fz_warn(ctx, "invalid predictor: %d", state->predictor); + state->predictor = 1; + } + + state->stride = (state->bpc * state->colors * state->columns + 7) / 8; + state->bpp = (state->bpc * state->colors + 7) / 8; + + state->in = fz_malloc(ctx, state->stride + 1); + state->out = fz_malloc(ctx, state->stride); + state->ref = fz_malloc(ctx, state->stride); + state->rp = state->out; + state->wp = state->out; + + memset(state->ref, 0, state->stride); + } + fz_catch(ctx) + { + if (state) + { + fz_free(ctx, state->in); + fz_free(ctx, state->out); + } + fz_free(ctx, state); + fz_close(chain); + fz_rethrow(ctx); + } + + return fz_new_stream(ctx, state, read_predict, close_predict); +} diff --git a/source/fitz/font.c b/source/fitz/font.c new file mode 100644 index 00000000..e8613f84 --- /dev/null +++ b/source/fitz/font.c @@ -0,0 +1,1094 @@ +#include "mupdf/fitz.h" + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_STROKER_H + +#define MAX_BBOX_TABLE_SIZE 4096 + +/* 20 degrees */ +#define SHEAR 0.36397f + +static void fz_drop_freetype(fz_context *ctx); + +static fz_font * +fz_new_font(fz_context *ctx, char *name, int use_glyph_bbox, int glyph_count) +{ + fz_font *font; + int i; + + font = fz_malloc_struct(ctx, fz_font); + font->refs = 1; + + if (name) + fz_strlcpy(font->name, name, sizeof font->name); + else + fz_strlcpy(font->name, "(null)", sizeof font->name); + + font->ft_face = NULL; + font->ft_substitute = 0; + font->ft_bold = 0; + font->ft_italic = 0; + font->ft_hint = 0; + + font->ft_file = NULL; + font->ft_data = NULL; + font->ft_size = 0; + + font->t3matrix = fz_identity; + font->t3resources = NULL; + font->t3procs = NULL; + font->t3lists = NULL; + font->t3widths = NULL; + font->t3flags = NULL; + font->t3doc = NULL; + font->t3run = NULL; + + font->bbox.x0 = 0; + font->bbox.y0 = 0; + font->bbox.x1 = 1; + font->bbox.y1 = 1; + + font->use_glyph_bbox = use_glyph_bbox; + if (use_glyph_bbox && glyph_count <= MAX_BBOX_TABLE_SIZE) + { + font->bbox_count = glyph_count; + font->bbox_table = fz_malloc_array(ctx, glyph_count, sizeof(fz_rect)); + for (i = 0; i < glyph_count; i++) + font->bbox_table[i] = fz_infinite_rect; + } + else + { + if (use_glyph_bbox) + fz_warn(ctx, "not building glyph bbox table for font '%s' with %d glyphs", font->name, glyph_count); + font->bbox_count = 0; + font->bbox_table = NULL; + } + + font->width_count = 0; + font->width_table = NULL; + + return font; +} + +fz_font * +fz_keep_font(fz_context *ctx, fz_font *font) +{ + if (!font) + return NULL; + fz_lock(ctx, FZ_LOCK_ALLOC); + font->refs ++; + fz_unlock(ctx, FZ_LOCK_ALLOC); + return font; +} + +void +fz_drop_font(fz_context *ctx, fz_font *font) +{ + int fterr; + int i, drop; + + fz_lock(ctx, FZ_LOCK_ALLOC); + drop = (font && --font->refs == 0); + fz_unlock(ctx, FZ_LOCK_ALLOC); + if (!drop) + return; + + if (font->t3procs) + { + if (font->t3resources) + font->t3freeres(font->t3doc, font->t3resources); + for (i = 0; i < 256; i++) + { + if (font->t3procs[i]) + fz_drop_buffer(ctx, font->t3procs[i]); + if (font->t3lists[i]) + fz_drop_display_list(ctx, font->t3lists[i]); + } + fz_free(ctx, font->t3procs); + fz_free(ctx, font->t3lists); + fz_free(ctx, font->t3widths); + fz_free(ctx, font->t3flags); + } + + if (font->ft_face) + { + fz_lock(ctx, FZ_LOCK_FREETYPE); + fterr = FT_Done_Face((FT_Face)font->ft_face); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + if (fterr) + fz_warn(ctx, "freetype finalizing face: %s", ft_error_string(fterr)); + fz_drop_freetype(ctx); + } + + fz_free(ctx, font->ft_file); + fz_free(ctx, font->ft_data); + fz_free(ctx, font->bbox_table); + fz_free(ctx, font->width_table); + fz_free(ctx, font); +} + +void +fz_set_font_bbox(fz_context *ctx, fz_font *font, float xmin, float ymin, float xmax, float ymax) +{ + if (xmin >= xmax || ymin >= ymax) + { + /* Invalid bbox supplied. It would be prohibitively slow to + * measure the true one, so make one up. */ + font->bbox.x0 = -1; + font->bbox.y0 = -1; + font->bbox.x1 = 2; + font->bbox.y1 = 2; + } + else + { + font->bbox.x0 = xmin; + font->bbox.y0 = ymin; + font->bbox.x1 = xmax; + font->bbox.y1 = ymax; + } +} + +/* + * Freetype hooks + */ + +struct fz_font_context_s { + int ctx_refs; + FT_Library ftlib; + int ftlib_refs; +}; + +#undef __FTERRORS_H__ +#define FT_ERRORDEF(e, v, s) { (e), (s) }, +#define FT_ERROR_START_LIST +#define FT_ERROR_END_LIST { 0, NULL } + +struct ft_error +{ + int err; + char *str; +}; + +void fz_new_font_context(fz_context *ctx) +{ + ctx->font = fz_malloc_struct(ctx, fz_font_context); + ctx->font->ctx_refs = 1; + ctx->font->ftlib = NULL; + ctx->font->ftlib_refs = 0; +} + +fz_font_context * +fz_keep_font_context(fz_context *ctx) +{ + if (!ctx || !ctx->font) + return NULL; + fz_lock(ctx, FZ_LOCK_ALLOC); + ctx->font->ctx_refs++; + fz_unlock(ctx, FZ_LOCK_ALLOC); + return ctx->font; +} + +void fz_drop_font_context(fz_context *ctx) +{ + int drop; + if (!ctx || !ctx->font) + return; + fz_lock(ctx, FZ_LOCK_ALLOC); + drop = --ctx->font->ctx_refs; + fz_unlock(ctx, FZ_LOCK_ALLOC); + if (drop == 0) + fz_free(ctx, ctx->font); +} + +static const struct ft_error ft_errors[] = +{ +#include FT_ERRORS_H +}; + +char *ft_error_string(int err) +{ + const struct ft_error *e; + + for (e = ft_errors; e->str; e++) + if (e->err == err) + return e->str; + + return "Unknown error"; +} + +static void +fz_keep_freetype(fz_context *ctx) +{ + int fterr; + int maj, min, pat; + fz_font_context *fct = ctx->font; + + fz_lock(ctx, FZ_LOCK_FREETYPE); + if (fct->ftlib) + { + fct->ftlib_refs++; + fz_unlock(ctx, FZ_LOCK_FREETYPE); + return; + } + + fterr = FT_Init_FreeType(&fct->ftlib); + if (fterr) + { + char *mess = ft_error_string(fterr); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot init freetype: %s", mess); + } + + FT_Library_Version(fct->ftlib, &maj, &min, &pat); + if (maj == 2 && min == 1 && pat < 7) + { + fterr = FT_Done_FreeType(fct->ftlib); + if (fterr) + fz_warn(ctx, "freetype finalizing: %s", ft_error_string(fterr)); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + fz_throw(ctx, FZ_ERROR_GENERIC, "freetype version too old: %d.%d.%d", maj, min, pat); + } + + fct->ftlib_refs++; + fz_unlock(ctx, FZ_LOCK_FREETYPE); +} + +static void +fz_drop_freetype(fz_context *ctx) +{ + int fterr; + fz_font_context *fct = ctx->font; + + fz_lock(ctx, FZ_LOCK_FREETYPE); + if (--fct->ftlib_refs == 0) + { + fterr = FT_Done_FreeType(fct->ftlib); + if (fterr) + fz_warn(ctx, "freetype finalizing: %s", ft_error_string(fterr)); + fct->ftlib = NULL; + } + fz_unlock(ctx, FZ_LOCK_FREETYPE); +} + +fz_font * +fz_new_font_from_file(fz_context *ctx, char *name, char *path, int index, int use_glyph_bbox) +{ + FT_Face face; + fz_font *font; + int fterr; + + fz_keep_freetype(ctx); + + fz_lock(ctx, FZ_LOCK_FREETYPE); + fterr = FT_New_Face(ctx->font->ftlib, path, index, &face); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + if (fterr) + { + fz_drop_freetype(ctx); + fz_throw(ctx, FZ_ERROR_GENERIC, "freetype: cannot load font: %s", ft_error_string(fterr)); + } + + if (!name) + name = face->family_name; + + font = fz_new_font(ctx, name, use_glyph_bbox, face->num_glyphs); + font->ft_face = face; + fz_set_font_bbox(ctx, font, + (float) face->bbox.xMin / face->units_per_EM, + (float) face->bbox.yMin / face->units_per_EM, + (float) face->bbox.xMax / face->units_per_EM, + (float) face->bbox.yMax / face->units_per_EM); + + return font; +} + +fz_font * +fz_new_font_from_memory(fz_context *ctx, char *name, unsigned char *data, int len, int index, int use_glyph_bbox) +{ + FT_Face face; + fz_font *font; + int fterr; + + fz_keep_freetype(ctx); + + fz_lock(ctx, FZ_LOCK_FREETYPE); + fterr = FT_New_Memory_Face(ctx->font->ftlib, data, len, index, &face); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + if (fterr) + { + fz_drop_freetype(ctx); + fz_throw(ctx, FZ_ERROR_GENERIC, "freetype: cannot load font: %s", ft_error_string(fterr)); + } + + if (!name) + name = face->family_name; + + font = fz_new_font(ctx, name, use_glyph_bbox, face->num_glyphs); + font->ft_face = face; + fz_set_font_bbox(ctx, font, + (float) face->bbox.xMin / face->units_per_EM, + (float) face->bbox.yMin / face->units_per_EM, + (float) face->bbox.xMax / face->units_per_EM, + (float) face->bbox.yMax / face->units_per_EM); + + return font; +} + +static fz_matrix * +fz_adjust_ft_glyph_width(fz_context *ctx, fz_font *font, int gid, fz_matrix *trm) +{ + /* Fudge the font matrix to stretch the glyph if we've substituted the font. */ + if (font->ft_substitute && font->width_table && gid < font->width_count) + { + FT_Error fterr; + int subw; + int realw; + float scale; + + fz_lock(ctx, FZ_LOCK_FREETYPE); + /* TODO: use FT_Get_Advance */ + fterr = FT_Set_Char_Size(font->ft_face, 1000, 1000, 72, 72); + if (fterr) + fz_warn(ctx, "freetype setting character size: %s", ft_error_string(fterr)); + + fterr = FT_Load_Glyph(font->ft_face, gid, + FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM); + if (fterr) + fz_warn(ctx, "freetype failed to load glyph: %s", ft_error_string(fterr)); + + realw = ((FT_Face)font->ft_face)->glyph->metrics.horiAdvance; + fz_unlock(ctx, FZ_LOCK_FREETYPE); + subw = font->width_table[gid]; + if (realw) + scale = (float) subw / realw; + else + scale = 1; + + fz_pre_scale(trm, scale, 1); + } + + return trm; +} + +static fz_pixmap * +fz_copy_ft_bitmap(fz_context *ctx, int left, int top, FT_Bitmap *bitmap) +{ + fz_pixmap *pixmap; + int y; + + pixmap = fz_new_pixmap(ctx, NULL, bitmap->width, bitmap->rows); + pixmap->x = left; + pixmap->y = top - bitmap->rows; + + if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) + { + for (y = 0; y < pixmap->h; y++) + { + unsigned char *out = pixmap->samples + (unsigned int)(y * pixmap->w); + unsigned char *in = bitmap->buffer + (unsigned int)((pixmap->h - y - 1) * bitmap->pitch); + unsigned char bit = 0x80; + int w = pixmap->w; + while (w--) + { + *out++ = (*in & bit) ? 255 : 0; + bit >>= 1; + if (bit == 0) + { + bit = 0x80; + in++; + } + } + } + } + else + { + for (y = 0; y < pixmap->h; y++) + { + memcpy(pixmap->samples + (unsigned int)(y * pixmap->w), + bitmap->buffer + (unsigned int)((pixmap->h - y - 1) * bitmap->pitch), + pixmap->w); + } + } + + return pixmap; +} + +/* The glyph cache lock is always taken when this is called. */ +fz_pixmap * +fz_render_ft_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, int aa) +{ + FT_Face face = font->ft_face; + FT_Matrix m; + FT_Vector v; + FT_Error fterr; + fz_pixmap *result; + fz_matrix local_trm = *trm; + + float strength = fz_matrix_expansion(trm) * 0.02f; + + fz_adjust_ft_glyph_width(ctx, font, gid, &local_trm); + + if (font->ft_italic) + fz_pre_shear(&local_trm, SHEAR, 0); + + /* + Freetype mutilates complex glyphs if they are loaded + with FT_Set_Char_Size 1.0. it rounds the coordinates + before applying transformation. to get more precision in + freetype, we shift part of the scale in the matrix + into FT_Set_Char_Size instead + */ + + m.xx = local_trm.a * 64; /* should be 65536 */ + m.yx = local_trm.b * 64; + m.xy = local_trm.c * 64; + m.yy = local_trm.d * 64; + v.x = local_trm.e * 64; + v.y = local_trm.f * 64; + + fz_lock(ctx, FZ_LOCK_FREETYPE); + fterr = FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */ + if (fterr) + fz_warn(ctx, "freetype setting character size: %s", ft_error_string(fterr)); + FT_Set_Transform(face, &m, &v); + + if (aa == 0) + { + /* enable grid fitting for non-antialiased rendering */ + float scale = fz_matrix_expansion(&local_trm); + m.xx = local_trm.a * 65536 / scale; + m.xy = local_trm.b * 65536 / scale; + m.yx = local_trm.c * 65536 / scale; + m.yy = local_trm.d * 65536 / scale; + v.x = 0; + v.y = 0; + + fterr = FT_Set_Char_Size(face, 64 * scale, 64 * scale, 72, 72); + if (fterr) + fz_warn(ctx, "freetype setting character size: %s", ft_error_string(fterr)); + FT_Set_Transform(face, &m, &v); + fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_TARGET_MONO); + if (fterr) { + fz_warn(ctx, "freetype load hinted glyph (gid %d): %s", gid, ft_error_string(fterr)); + goto retry_unhinted; + } + } + else if (font->ft_hint) + { + /* + Enable hinting, but keep the huge char size so that + it is hinted for a character. This will in effect nullify + the effect of grid fitting. This form of hinting should + only be used for DynaLab and similar tricky TrueType fonts, + so that we get the correct outline shape. + */ + fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP); + if (fterr) { + fz_warn(ctx, "freetype load hinted glyph (gid %d): %s", gid, ft_error_string(fterr)); + goto retry_unhinted; + } + } + else + { +retry_unhinted: + fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING); + if (fterr) + { + fz_warn(ctx, "freetype load glyph (gid %d): %s", gid, ft_error_string(fterr)); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + return NULL; + } + } + + if (font->ft_bold) + { + FT_Outline_Embolden(&face->glyph->outline, strength * 64); + FT_Outline_Translate(&face->glyph->outline, -strength * 32, -strength * 32); + } + + fterr = FT_Render_Glyph(face->glyph, fz_aa_level(ctx) > 0 ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); + if (fterr) + { + fz_warn(ctx, "freetype render glyph (gid %d): %s", gid, ft_error_string(fterr)); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + return NULL; + } + + fz_try(ctx) + { + result = fz_copy_ft_bitmap(ctx, face->glyph->bitmap_left, face->glyph->bitmap_top, &face->glyph->bitmap); + } + fz_always(ctx) + { + fz_unlock(ctx, FZ_LOCK_FREETYPE); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } + + return result; +} + +fz_pixmap * +fz_render_ft_stroked_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, const fz_matrix *ctm, fz_stroke_state *state) +{ + FT_Face face = font->ft_face; + float expansion = fz_matrix_expansion(ctm); + int linewidth = state->linewidth * expansion * 64 / 2; + FT_Matrix m; + FT_Vector v; + FT_Error fterr; + FT_Stroker stroker; + FT_Glyph glyph; + FT_BitmapGlyph bitmap; + fz_pixmap *pixmap; + FT_Stroker_LineJoin line_join; + fz_matrix local_trm = *trm; + + fz_adjust_ft_glyph_width(ctx, font, gid, &local_trm); + + if (font->ft_italic) + fz_pre_shear(&local_trm, SHEAR, 0); + + m.xx = local_trm.a * 64; /* should be 65536 */ + m.yx = local_trm.b * 64; + m.xy = local_trm.c * 64; + m.yy = local_trm.d * 64; + v.x = local_trm.e * 64; + v.y = local_trm.f * 64; + + fz_lock(ctx, FZ_LOCK_FREETYPE); + fterr = FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */ + if (fterr) + { + fz_warn(ctx, "FT_Set_Char_Size: %s", ft_error_string(fterr)); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + return NULL; + } + + FT_Set_Transform(face, &m, &v); + + fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING); + if (fterr) + { + fz_warn(ctx, "FT_Load_Glyph(gid %d): %s", gid, ft_error_string(fterr)); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + return NULL; + } + + fterr = FT_Stroker_New(ctx->font->ftlib, &stroker); + if (fterr) + { + fz_warn(ctx, "FT_Stroker_New: %s", ft_error_string(fterr)); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + return NULL; + } + +#if FREETYPE_MAJOR * 10000 + FREETYPE_MINOR * 100 + FREETYPE_PATCH > 20405 + /* New freetype */ + line_join = + state->linejoin == FZ_LINEJOIN_MITER ? FT_STROKER_LINEJOIN_MITER_FIXED : + state->linejoin == FZ_LINEJOIN_ROUND ? FT_STROKER_LINEJOIN_ROUND : + state->linejoin == FZ_LINEJOIN_BEVEL ? FT_STROKER_LINEJOIN_BEVEL : + FT_STROKER_LINEJOIN_MITER_VARIABLE; +#else + /* Old freetype */ + line_join = + state->linejoin == FZ_LINEJOIN_MITER ? FT_STROKER_LINEJOIN_MITER : + state->linejoin == FZ_LINEJOIN_ROUND ? FT_STROKER_LINEJOIN_ROUND : + state->linejoin == FZ_LINEJOIN_BEVEL ? FT_STROKER_LINEJOIN_BEVEL : + FT_STROKER_LINEJOIN_MITER; +#endif + FT_Stroker_Set(stroker, linewidth, (FT_Stroker_LineCap)state->start_cap, line_join, state->miterlimit * 65536); + + fterr = FT_Get_Glyph(face->glyph, &glyph); + if (fterr) + { + fz_warn(ctx, "FT_Get_Glyph: %s", ft_error_string(fterr)); + FT_Stroker_Done(stroker); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + return NULL; + } + + fterr = FT_Glyph_Stroke(&glyph, stroker, 1); + if (fterr) + { + fz_warn(ctx, "FT_Glyph_Stroke: %s", ft_error_string(fterr)); + FT_Done_Glyph(glyph); + FT_Stroker_Done(stroker); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + return NULL; + } + + FT_Stroker_Done(stroker); + + fterr = FT_Glyph_To_Bitmap(&glyph, fz_aa_level(ctx) > 0 ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1); + if (fterr) + { + fz_warn(ctx, "FT_Glyph_To_Bitmap: %s", ft_error_string(fterr)); + FT_Done_Glyph(glyph); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + return NULL; + } + + bitmap = (FT_BitmapGlyph)glyph; + fz_try(ctx) + { + pixmap = fz_copy_ft_bitmap(ctx, bitmap->left, bitmap->top, &bitmap->bitmap); + } + fz_always(ctx) + { + FT_Done_Glyph(glyph); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } + + return pixmap; +} + +static fz_rect * +fz_bound_ft_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, fz_rect *bounds) +{ + FT_Face face = font->ft_face; + FT_Error fterr; + FT_BBox cbox; + FT_Matrix m; + FT_Vector v; + + // TODO: refactor loading into fz_load_ft_glyph + // TODO: cache results + + float strength = fz_matrix_expansion(trm) * 0.02f; + fz_matrix local_trm = *trm; + + fz_adjust_ft_glyph_width(ctx, font, gid, &local_trm); + + if (font->ft_italic) + fz_pre_shear(&local_trm, SHEAR, 0); + + m.xx = local_trm.a * 64; /* should be 65536 */ + m.yx = local_trm.b * 64; + m.xy = local_trm.c * 64; + m.yy = local_trm.d * 64; + v.x = local_trm.e * 64; + v.y = local_trm.f * 64; + + fz_lock(ctx, FZ_LOCK_FREETYPE); + fterr = FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */ + if (fterr) + fz_warn(ctx, "freetype setting character size: %s", ft_error_string(fterr)); + FT_Set_Transform(face, &m, &v); + + fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING); + if (fterr) + { + fz_warn(ctx, "freetype load glyph (gid %d): %s", gid, ft_error_string(fterr)); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + bounds->x0 = bounds->x1 = local_trm.e; + bounds->y0 = bounds->y1 = local_trm.f; + return bounds; + } + + if (font->ft_bold) + { + FT_Outline_Embolden(&face->glyph->outline, strength * 64); + FT_Outline_Translate(&face->glyph->outline, -strength * 32, -strength * 32); + } + + FT_Outline_Get_CBox(&face->glyph->outline, &cbox); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + bounds->x0 = cbox.xMin / 64.0f; + bounds->y0 = cbox.yMin / 64.0f; + bounds->x1 = cbox.xMax / 64.0f; + bounds->y1 = cbox.yMax / 64.0f; + + if (fz_is_empty_rect(bounds)) + { + bounds->x0 = bounds->x1 = local_trm.e; + bounds->y0 = bounds->y1 = local_trm.f; + } + + return bounds; +} + +/* Turn FT_Outline into a fz_path */ + +struct closure { + fz_context *ctx; + fz_path *path; + float x, y; +}; + +static int move_to(const FT_Vector *p, void *cc) +{ + fz_context *ctx = ((struct closure *)cc)->ctx; + fz_path *path = ((struct closure *)cc)->path; + float tx = ((struct closure *)cc)->x; + float ty = ((struct closure *)cc)->y; + fz_moveto(ctx, path, tx + p->x / 64.0f, ty + p->y / 64.0f); + return 0; +} + +static int line_to(const FT_Vector *p, void *cc) +{ + fz_context *ctx = ((struct closure *)cc)->ctx; + fz_path *path = ((struct closure *)cc)->path; + float tx = ((struct closure *)cc)->x; + float ty = ((struct closure *)cc)->y; + fz_lineto(ctx, path, tx + p->x / 64.0f, ty + p->y / 64.0f); + return 0; +} + +static int conic_to(const FT_Vector *c, const FT_Vector *p, void *cc) +{ + fz_context *ctx = ((struct closure *)cc)->ctx; + fz_path *path = ((struct closure *)cc)->path; + float tx = ((struct closure *)cc)->x; + float ty = ((struct closure *)cc)->y; + fz_point s, c1, c2; + float cx = tx + c->x / 64.0f, cy = ty + c->y / 64.0f; + float px = tx + p->x / 64.0f, py = ty + p->y / 64.0f; + s = fz_currentpoint(ctx, path); + c1.x = (s.x + cx * 2) / 3; + c1.y = (s.y + cy * 2) / 3; + c2.x = (px + cx * 2) / 3; + c2.y = (py + cy * 2) / 3; + fz_curveto(ctx, path, c1.x, c1.y, c2.x, c2.y, px, py); + return 0; +} + +static int cubic_to(const FT_Vector *c1, const FT_Vector *c2, const FT_Vector *p, void *cc) +{ + fz_context *ctx = ((struct closure *)cc)->ctx; + fz_path *path = ((struct closure *)cc)->path; + float tx = ((struct closure *)cc)->x; + float ty = ((struct closure *)cc)->y; + fz_curveto(ctx, path, + tx + c1->x/64.0f, ty + c1->y/64.0f, + tx + c2->x/64.0f, ty + c2->y/64.0f, + tx + p->x/64.0f, ty + p->y/64.0f); + return 0; +} + +static const FT_Outline_Funcs outline_funcs = { + move_to, line_to, conic_to, cubic_to, 0, 0 +}; + +fz_path * +fz_outline_ft_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm) +{ + struct closure cc; + FT_Face face = font->ft_face; + FT_Matrix m; + FT_Vector v; + int fterr; + fz_matrix local_trm = *trm; + + float strength = fz_matrix_expansion(trm) * 0.02f; + + fz_adjust_ft_glyph_width(ctx, font, gid, &local_trm); + + if (font->ft_italic) + fz_pre_shear(&local_trm, SHEAR, 0); + + m.xx = local_trm.a * 64; /* should be 65536 */ + m.yx = local_trm.b * 64; + m.xy = local_trm.c * 64; + m.yy = local_trm.d * 64; + v.x = 0; + v.y = 0; + + fz_lock(ctx, FZ_LOCK_FREETYPE); + + fterr = FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */ + if (fterr) + fz_warn(ctx, "freetype setting character size: %s", ft_error_string(fterr)); + FT_Set_Transform(face, &m, &v); + + fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING); + if (fterr) + { + fz_warn(ctx, "freetype load glyph (gid %d): %s", gid, ft_error_string(fterr)); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + return NULL; + } + + if (font->ft_bold) + { + FT_Outline_Embolden(&face->glyph->outline, strength * 64); + FT_Outline_Translate(&face->glyph->outline, -strength * 32, -strength * 32); + } + + fz_try(ctx) + { + cc.ctx = ctx; + cc.path = fz_new_path(ctx); + cc.x = local_trm.e; + cc.y = local_trm.f; + fz_moveto(ctx, cc.path, cc.x, cc.y); + FT_Outline_Decompose(&face->glyph->outline, &outline_funcs, &cc); + fz_closepath(ctx, cc.path); + } + fz_always(ctx) + { + fz_unlock(ctx, FZ_LOCK_FREETYPE); + } + fz_catch(ctx) + { + fz_warn(ctx, "freetype cannot decompose outline"); + fz_free(ctx, cc.path); + return NULL; + } + + return cc.path; +} + +/* + * Type 3 fonts... + */ + +fz_font * +fz_new_type3_font(fz_context *ctx, char *name, const fz_matrix *matrix) +{ + fz_font *font; + int i; + + font = fz_new_font(ctx, name, 1, 256); + font->t3procs = fz_malloc_array(ctx, 256, sizeof(fz_buffer*)); + font->t3lists = fz_malloc_array(ctx, 256, sizeof(fz_display_list*)); + font->t3widths = fz_malloc_array(ctx, 256, sizeof(float)); + font->t3flags = fz_malloc_array(ctx, 256, sizeof(char)); + + font->t3matrix = *matrix; + for (i = 0; i < 256; i++) + { + font->t3procs[i] = NULL; + font->t3lists[i] = NULL; + font->t3widths[i] = 0; + font->t3flags[i] = 0; + } + + return font; +} + +void +fz_prepare_t3_glyph(fz_context *ctx, fz_font *font, int gid, int nested_depth) +{ + fz_buffer *contents; + fz_device *dev; + + contents = font->t3procs[gid]; + if (!contents) + return; + + /* We've not already loaded this one! */ + assert(font->t3lists[gid] == NULL); + + font->t3lists[gid] = fz_new_display_list(ctx); + + dev = fz_new_list_device(ctx, font->t3lists[gid]); + dev->flags = FZ_DEVFLAG_FILLCOLOR_UNDEFINED | + FZ_DEVFLAG_STROKECOLOR_UNDEFINED | + FZ_DEVFLAG_STARTCAP_UNDEFINED | + FZ_DEVFLAG_DASHCAP_UNDEFINED | + FZ_DEVFLAG_ENDCAP_UNDEFINED | + FZ_DEVFLAG_LINEJOIN_UNDEFINED | + FZ_DEVFLAG_MITERLIMIT_UNDEFINED | + FZ_DEVFLAG_LINEWIDTH_UNDEFINED; + font->t3run(font->t3doc, font->t3resources, contents, dev, &fz_identity, NULL, 0); + font->t3flags[gid] = dev->flags; + fz_free_device(dev); +} + +static fz_rect * +fz_bound_t3_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, fz_rect *bounds) +{ + fz_display_list *list; + fz_matrix ctm; + fz_device *dev; + + list = font->t3lists[gid]; + if (!list) + { + *bounds = fz_empty_rect; + return fz_transform_rect(bounds, trm); + } + + fz_concat(&ctm, &font->t3matrix, trm); + dev = fz_new_bbox_device(ctx, bounds); + fz_try(ctx) + { + fz_run_display_list(list, dev, &ctm, &fz_infinite_rect, NULL); + } + fz_always(ctx) + { + fz_free_device(dev); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } + + return bounds; +} + +fz_pixmap * +fz_render_t3_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, fz_colorspace *model, fz_irect scissor) +{ + fz_display_list *list; + fz_matrix ctm; + fz_rect bounds; + fz_irect bbox; + fz_device *dev; + fz_pixmap *glyph; + fz_pixmap *result; + + if (gid < 0 || gid > 255) + return NULL; + + list = font->t3lists[gid]; + if (!list) + return NULL; + + if (font->t3flags[gid] & FZ_DEVFLAG_MASK) + { + if (font->t3flags[gid] & FZ_DEVFLAG_COLOR) + fz_warn(ctx, "type3 glyph claims to be both masked and colored"); + model = NULL; + } + else if (font->t3flags[gid] & FZ_DEVFLAG_COLOR) + { + if (!model) + fz_warn(ctx, "colored type3 glyph wanted in masked context"); + } + else + { + fz_warn(ctx, "type3 glyph doesn't specify masked or colored"); + model = NULL; /* Treat as masked */ + } + + fz_expand_rect(fz_bound_glyph(ctx, font, gid, trm, &bounds), 1); + fz_irect_from_rect(&bbox, &bounds); + fz_intersect_irect(&bbox, &scissor); + + glyph = fz_new_pixmap_with_bbox(ctx, model ? model : fz_device_gray(ctx), &bbox); + fz_clear_pixmap(ctx, glyph); + + fz_concat(&ctm, &font->t3matrix, trm); + dev = fz_new_draw_device_type3(ctx, glyph); + fz_run_display_list(list, dev, &ctm, &fz_infinite_rect, NULL); + fz_free_device(dev); + + if (!model) + { + result = fz_alpha_from_gray(ctx, glyph, 0); + fz_drop_pixmap(ctx, glyph); + } + else + result = glyph; + + return result; +} + +void +fz_render_t3_glyph_direct(fz_context *ctx, fz_device *dev, fz_font *font, int gid, const fz_matrix *trm, void *gstate, int nested_depth) +{ + fz_matrix ctm; + void *contents; + + if (gid < 0 || gid > 255) + return; + + contents = font->t3procs[gid]; + if (!contents) + return; + + if (font->t3flags[gid] & FZ_DEVFLAG_MASK) + { + if (font->t3flags[gid] & FZ_DEVFLAG_COLOR) + fz_warn(ctx, "type3 glyph claims to be both masked and colored"); + } + else if (font->t3flags[gid] & FZ_DEVFLAG_COLOR) + { + } + else + { + fz_warn(ctx, "type3 glyph doesn't specify masked or colored"); + } + + fz_concat(&ctm, &font->t3matrix, trm); + font->t3run(font->t3doc, font->t3resources, contents, dev, &ctm, gstate, nested_depth); +} + +#ifndef NDEBUG +void +fz_print_font(fz_context *ctx, FILE *out, fz_font *font) +{ + fprintf(out, "font '%s' {\n", font->name); + + if (font->ft_face) + { + fprintf(out, "\tfreetype face %p\n", font->ft_face); + if (font->ft_substitute) + fprintf(out, "\tsubstitute font\n"); + } + + if (font->t3procs) + { + fprintf(out, "\ttype3 matrix [%g %g %g %g]\n", + font->t3matrix.a, font->t3matrix.b, + font->t3matrix.c, font->t3matrix.d); + + fprintf(out, "\ttype3 bbox [%g %g %g %g]\n", + font->bbox.x0, font->bbox.y0, + font->bbox.x1, font->bbox.y1); + } + + fprintf(out, "}\n"); +} +#endif + +fz_rect * +fz_bound_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, fz_rect *rect) +{ + if (font->bbox_table && gid < font->bbox_count) + { + if (fz_is_infinite_rect(&font->bbox_table[gid])) + { + if (font->ft_face) + fz_bound_ft_glyph(ctx, font, gid, &fz_identity, &font->bbox_table[gid]); + else if (font->t3lists) + fz_bound_t3_glyph(ctx, font, gid, &fz_identity, &font->bbox_table[gid]); + else + font->bbox_table[gid] = fz_empty_rect; + } + *rect = font->bbox_table[gid]; + } + else + { + /* fall back to font bbox */ + *rect = font->bbox; + } + + return fz_transform_rect(rect, trm); +} + +fz_path * +fz_outline_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *ctm) +{ + if (!font->ft_face) + return NULL; + return fz_outline_ft_glyph(ctx, font, gid, ctm); +} + +int fz_glyph_cacheable(fz_context *ctx, fz_font *font, int gid) +{ + if (!font->t3procs || !font->t3flags || gid < 0 || gid >= font->bbox_count) + return 1; + return (font->t3flags[gid] & FZ_DEVFLAG_UNCACHEABLE) == 0; +} diff --git a/source/fitz/function.c b/source/fitz/function.c new file mode 100644 index 00000000..b5ba4815 --- /dev/null +++ b/source/fitz/function.c @@ -0,0 +1,48 @@ +#include "mupdf/fitz.h" + +void +fz_eval_function(fz_context *ctx, fz_function *func, float *in_, int inlen, float *out_, int outlen) +{ + float fakein[FZ_FN_MAXM]; + float fakeout[FZ_FN_MAXN]; + float *in = in_; + float *out = out_; + + if (inlen < func->m) + { + in = fakein; + memset(in, 0, sizeof(float) * func->m); + memcpy(in, in_, sizeof(float) * inlen); + } + + if (outlen < func->n) + { + out = fakeout; + memset(out, 0, sizeof(float) * func->n); + } + else + memset(out, 0, sizeof(float) * outlen); + + func->evaluate(ctx, func, in, out); + + if (outlen < func->n) + memcpy(out_, out, sizeof(float) * outlen); +} + +fz_function * +fz_keep_function(fz_context *ctx, fz_function *func) +{ + return (fz_function *)fz_keep_storable(ctx, &func->storable); +} + +void +fz_drop_function(fz_context *ctx, fz_function *func) +{ + fz_drop_storable(ctx, &func->storable); +} + +unsigned int +fz_function_size(fz_function *func) +{ + return (func ? func->size : 0); +} diff --git a/source/fitz/geometry.c b/source/fitz/geometry.c new file mode 100644 index 00000000..81450246 --- /dev/null +++ b/source/fitz/geometry.c @@ -0,0 +1,483 @@ +#include "mupdf/fitz.h" + +#define MAX4(a,b,c,d) fz_max(fz_max(a,b), fz_max(c,d)) +#define MIN4(a,b,c,d) fz_min(fz_min(a,b), fz_min(c,d)) + +/* A useful macro to add with overflow detection and clamping. + + We want to do "b = a + x", but to allow for overflow. Consider the + top bits, and the cases in which overflow occurs: + + overflow a x b ~a^x a^b (~a^x)&(a^b) + no 0 0 0 1 0 0 + yes 0 0 1 1 1 1 + no 0 1 0 0 0 0 + no 0 1 1 0 1 0 + no 1 0 0 0 1 0 + no 1 0 1 0 0 0 + yes 1 1 0 1 1 1 + no 1 1 1 1 0 0 +*/ +#define ADD_WITH_SAT(b,a,x) \ + ((b) = (a) + (x), (b) = (((~(a)^(x))&((a)^(b))) < 0 ? ((x) < 0 ? INT_MIN : INT_MAX) : (b))) + +/* Matrices, points and affine transformations */ + +const fz_matrix fz_identity = { 1, 0, 0, 1, 0, 0 }; + +fz_matrix * +fz_concat(fz_matrix *dst, const fz_matrix *one, const fz_matrix *two) +{ + fz_matrix dst2; + dst2.a = one->a * two->a + one->b * two->c; + dst2.b = one->a * two->b + one->b * two->d; + dst2.c = one->c * two->a + one->d * two->c; + dst2.d = one->c * two->b + one->d * two->d; + dst2.e = one->e * two->a + one->f * two->c + two->e; + dst2.f = one->e * two->b + one->f * two->d + two->f; + *dst = dst2; + return dst; +} + +fz_matrix * +fz_scale(fz_matrix *m, float sx, float sy) +{ + m->a = sx; m->b = 0; + m->c = 0; m->d = sy; + m->e = 0; m->f = 0; + return m; +} + +fz_matrix * +fz_pre_scale(fz_matrix *mat, float sx, float sy) +{ + mat->a *= sx; + mat->b *= sx; + mat->c *= sy; + mat->d *= sy; + return mat; +} + +fz_matrix * +fz_shear(fz_matrix *mat, float h, float v) +{ + mat->a = 1; mat->b = v; + mat->c = h; mat->d = 1; + mat->e = 0; mat->f = 0; + return mat; +} + +fz_matrix * +fz_pre_shear(fz_matrix *mat, float h, float v) +{ + float a = mat->a; + float b = mat->b; + mat->a += v * mat->c; + mat->b += v * mat->d; + mat->c += h * a; + mat->d += h * b; + return mat; +} + +fz_matrix * +fz_rotate(fz_matrix *m, float theta) +{ + float s; + float c; + + while (theta < 0) + theta += 360; + while (theta >= 360) + theta -= 360; + + if (fabsf(0 - theta) < FLT_EPSILON) + { + s = 0; + c = 1; + } + else if (fabsf(90.0f - theta) < FLT_EPSILON) + { + s = 1; + c = 0; + } + else if (fabsf(180.0f - theta) < FLT_EPSILON) + { + s = 0; + c = -1; + } + else if (fabsf(270.0f - theta) < FLT_EPSILON) + { + s = -1; + c = 0; + } + else + { + s = sinf(theta * (float)M_PI / 180); + c = cosf(theta * (float)M_PI / 180); + } + + m->a = c; m->b = s; + m->c = -s; m->d = c; + m->e = 0; m->f = 0; + return m; +} + +fz_matrix * +fz_pre_rotate(fz_matrix *m, float theta) +{ + while (theta < 0) + theta += 360; + while (theta >= 360) + theta -= 360; + + if (fabsf(0 - theta) < FLT_EPSILON) + { + /* Nothing to do */ + } + else if (fabsf(90.0f - theta) < FLT_EPSILON) + { + float a = m->a; + float b = m->b; + m->a = m->c; + m->b = m->d; + m->c = -a; + m->d = -b; + } + else if (fabsf(180.0f - theta) < FLT_EPSILON) + { + m->a = -m->a; + m->b = -m->b; + m->c = -m->c; + m->d = -m->d; + } + else if (fabsf(270.0f - theta) < FLT_EPSILON) + { + float a = m->a; + float b = m->b; + m->a = -m->c; + m->b = -m->d; + m->c = a; + m->d = b; + } + else + { + float s = sinf(theta * (float)M_PI / 180); + float c = cosf(theta * (float)M_PI / 180); + float a = m->a; + float b = m->b; + m->a = c * a + s * m->c; + m->b = c * b + s * m->d; + m->c =-s * a + c * m->c; + m->d =-s * b + c * m->d; + } + + return m; +} + +fz_matrix * +fz_translate(fz_matrix *m, float tx, float ty) +{ + m->a = 1; m->b = 0; + m->c = 0; m->d = 1; + m->e = tx; m->f = ty; + return m; +} + +fz_matrix * +fz_pre_translate(fz_matrix *mat, float tx, float ty) +{ + mat->e += tx * mat->a + ty * mat->c; + mat->f += tx * mat->b + ty * mat->d; + return mat; +} + +fz_matrix * +fz_invert_matrix(fz_matrix *dst, const fz_matrix *src) +{ + /* Be careful to cope with dst == src */ + float a = src->a; + float det = a * src->d - src->b * src->c; + if (det < -FLT_EPSILON || det > FLT_EPSILON) + { + float rdet = 1 / det; + dst->a = src->d * rdet; + dst->b = -src->b * rdet; + dst->c = -src->c * rdet; + dst->d = a * rdet; + a = -src->e * dst->a - src->f * dst->c; + dst->f = -src->e * dst->b - src->f * dst->d; + dst->e = a; + } + else + *dst = *src; + return dst; +} + +int +fz_is_rectilinear(const fz_matrix *m) +{ + return (fabsf(m->b) < FLT_EPSILON && fabsf(m->c) < FLT_EPSILON) || + (fabsf(m->a) < FLT_EPSILON && fabsf(m->d) < FLT_EPSILON); +} + +float +fz_matrix_expansion(const fz_matrix *m) +{ + return sqrtf(fabsf(m->a * m->d - m->b * m->c)); +} + +float +fz_matrix_max_expansion(const fz_matrix *m) +{ + float max = fabsf(m->a); + float x = fabsf(m->b); + if (max < x) + max = x; + x = fabsf(m->c); + if (max < x) + max = x; + x = fabsf(m->d); + if (max < x) + max = x; + return max; +} + +fz_point * +fz_transform_point(fz_point *restrict p, const fz_matrix *restrict m) +{ + float x = p->x; + p->x = x * m->a + p->y * m->c + m->e; + p->y = x * m->b + p->y * m->d + m->f; + return p; +} + +fz_point * +fz_transform_vector(fz_point *restrict p, const fz_matrix *restrict m) +{ + float x = p->x; + p->x = x * m->a + p->y * m->c; + p->y = x * m->b + p->y * m->d; + return p; +} + +void +fz_normalize_vector(fz_point *p) +{ + float len = p->x * p->x + p->y * p->y; + if (len != 0) + { + len = sqrtf(len); + p->x /= len; + p->y /= len; + } +} + +/* Rectangles and bounding boxes */ + +/* biggest and smallest integers that a float can represent perfectly (i.e. 24 bits) */ +#define MAX_SAFE_INT 16777216 +#define MIN_SAFE_INT -16777216 + +const fz_rect fz_infinite_rect = { 1, 1, -1, -1 }; +const fz_rect fz_empty_rect = { 0, 0, 0, 0 }; +const fz_rect fz_unit_rect = { 0, 0, 1, 1 }; + +const fz_irect fz_infinite_irect = { 1, 1, -1, -1 }; +const fz_irect fz_empty_irect = { 0, 0, 0, 0 }; +const fz_irect fz_unit_bbox = { 0, 0, 1, 1 }; + +fz_irect * +fz_irect_from_rect(fz_irect *restrict b, const fz_rect *restrict r) +{ + b->x0 = fz_clamp(floorf(r->x0), MIN_SAFE_INT, MAX_SAFE_INT); + b->y0 = fz_clamp(floorf(r->y0), MIN_SAFE_INT, MAX_SAFE_INT); + b->x1 = fz_clamp(ceilf(r->x1), MIN_SAFE_INT, MAX_SAFE_INT); + b->y1 = fz_clamp(ceilf(r->y1), MIN_SAFE_INT, MAX_SAFE_INT); + return b; +} + +fz_rect * +fz_rect_from_irect(fz_rect *restrict r, const fz_irect *restrict a) +{ + r->x0 = a->x0; + r->y0 = a->y0; + r->x1 = a->x1; + r->y1 = a->y1; + return r; +} + +fz_irect * +fz_round_rect(fz_irect * restrict b, const fz_rect *restrict r) +{ + int i; + + i = floorf(r->x0 + 0.001); + b->x0 = fz_clamp(i, MIN_SAFE_INT, MAX_SAFE_INT); + i = floorf(r->y0 + 0.001); + b->y0 = fz_clamp(i, MIN_SAFE_INT, MAX_SAFE_INT); + i = ceilf(r->x1 - 0.001); + b->x1 = fz_clamp(i, MIN_SAFE_INT, MAX_SAFE_INT); + i = ceilf(r->y1 - 0.001); + b->y1 = fz_clamp(i, MIN_SAFE_INT, MAX_SAFE_INT); + + return b; +} + +fz_rect * +fz_intersect_rect(fz_rect *restrict a, const fz_rect *restrict b) +{ + /* Check for empty box before infinite box */ + if (fz_is_empty_rect(a)) return a; + if (fz_is_empty_rect(b)) { + *a = fz_empty_rect; + return a; + } + if (fz_is_infinite_rect(b)) return a; + if (fz_is_infinite_rect(a)) { + *a = *b; + return a; + } + if (a->x0 < b->x0) + a->x0 = b->x0; + if (a->y0 < b->y0) + a->y0 = b->y0; + if (a->x1 > b->x1) + a->x1 = b->x1; + if (a->y1 > b->y1) + a->y1 = b->y1; + if (a->x1 < a->x0 || a->y1 < a->y0) + *a = fz_empty_rect; + return a; +} + +fz_irect * +fz_intersect_irect(fz_irect *restrict a, const fz_irect *restrict b) +{ + /* Check for empty box before infinite box */ + if (fz_is_empty_irect(a)) return a; + if (fz_is_empty_irect(b)) + { + *a = fz_empty_irect; + return a; + } + if (fz_is_infinite_irect(b)) return a; + if (fz_is_infinite_irect(a)) + { + *a = *b; + return a; + } + if (a->x0 < b->x0) + a->x0 = b->x0; + if (a->y0 < b->y0) + a->y0 = b->y0; + if (a->x1 > b->x1) + a->x1 = b->x1; + if (a->y1 > b->y1) + a->y1 = b->y1; + if (a->x1 < a->x0 || a->y1 < a->y0) + *a = fz_empty_irect; + return a; +} + +fz_rect * +fz_union_rect(fz_rect *restrict a, const fz_rect *restrict b) +{ + /* Check for empty box before infinite box */ + if (fz_is_empty_rect(b)) return a; + if (fz_is_empty_rect(a)) { + *a = *b; + return a; + } + if (fz_is_infinite_rect(a)) return a; + if (fz_is_infinite_rect(b)) { + *a = *b; + return a; + } + if (a->x0 > b->x0) + a->x0 = b->x0; + if (a->y0 > b->y0) + a->y0 = b->y0; + if (a->x1 < b->x1) + a->x1 = b->x1; + if (a->y1 < b->y1) + a->y1 = b->y1; + return a; +} + +fz_irect * +fz_translate_irect(fz_irect *a, int xoff, int yoff) +{ + int t; + + if (fz_is_empty_irect(a)) return a; + if (fz_is_infinite_irect(a)) return a; + a->x0 = ADD_WITH_SAT(t, a->x0, xoff); + a->y0 = ADD_WITH_SAT(t, a->y0, yoff); + a->x1 = ADD_WITH_SAT(t, a->x1, xoff); + a->y1 = ADD_WITH_SAT(t, a->y1, yoff); + return a; +} + +fz_rect * +fz_transform_rect(fz_rect *restrict r, const fz_matrix *restrict m) +{ + fz_point s, t, u, v; + + if (fz_is_infinite_rect(r)) + return r; + + if (fabsf(m->b) < FLT_EPSILON && fabsf(m->c) < FLT_EPSILON) + { + if (m->a < 0) + { + float f = r->x0; + r->x0 = r->x1; + r->x1 = f; + } + if (m->d < 0) + { + float f = r->y0; + r->y0 = r->y1; + r->y1 = f; + } + fz_transform_point(fz_rect_min(r), m); + fz_transform_point(fz_rect_max(r), m); + return r; + } + + s.x = r->x0; s.y = r->y0; + t.x = r->x0; t.y = r->y1; + u.x = r->x1; u.y = r->y1; + v.x = r->x1; v.y = r->y0; + fz_transform_point(&s, m); + fz_transform_point(&t, m); + fz_transform_point(&u, m); + fz_transform_point(&v, m); + r->x0 = MIN4(s.x, t.x, u.x, v.x); + r->y0 = MIN4(s.y, t.y, u.y, v.y); + r->x1 = MAX4(s.x, t.x, u.x, v.x); + r->y1 = MAX4(s.y, t.y, u.y, v.y); + return r; +} + +fz_rect * +fz_expand_rect(fz_rect *a, float expand) +{ + if (fz_is_empty_rect(a)) return a; + if (fz_is_infinite_rect(a)) return a; + a->x0 -= expand; + a->y0 -= expand; + a->x1 += expand; + a->y1 += expand; + return a; +} + +fz_rect *fz_include_point_in_rect(fz_rect *r, const fz_point *p) +{ + if (p->x < r->x0) r->x0 = p->x; + if (p->x > r->x1) r->x1 = p->x; + if (p->y < r->y0) r->y0 = p->y; + if (p->y > r->y1) r->y1 = p->y; + + return r; +} diff --git a/source/fitz/getopt.c b/source/fitz/getopt.c new file mode 100644 index 00000000..2a6e5ac4 --- /dev/null +++ b/source/fitz/getopt.c @@ -0,0 +1,66 @@ +/* + * This is a version of the public domain getopt implementation by + * Henry Spencer originally posted to net.sources. + * + * This file is in the public domain. + */ + +#include <stdio.h> +#include <string.h> + +#define getopt fz_getopt +#define optarg fz_optarg +#define optind fz_optind + +char *optarg; /* Global argument pointer. */ +int optind = 0; /* Global argv index. */ + +static char *scan = NULL; /* Private scan pointer. */ + +int +getopt(int argc, char *argv[], char *optstring) +{ + char c; + char *place; + + optarg = NULL; + + if (!scan || *scan == '\0') { + if (optind == 0) + optind++; + + if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') + return EOF; + if (argv[optind][1] == '-' && argv[optind][2] == '\0') { + optind++; + return EOF; + } + + scan = argv[optind]+1; + optind++; + } + + c = *scan++; + place = strchr(optstring, c); + + if (!place || c == ':') { + fprintf(stderr, "%s: unknown option -%c\n", argv[0], c); + return '?'; + } + + place++; + if (*place == ':') { + if (*scan != '\0') { + optarg = scan; + scan = NULL; + } else if( optind < argc ) { + optarg = argv[optind]; + optind++; + } else { + fprintf(stderr, "%s: option requires argument -%c\n", argv[0], c); + return ':'; + } + } + + return c; +} diff --git a/source/fitz/halftone.c b/source/fitz/halftone.c new file mode 100644 index 00000000..15aebead --- /dev/null +++ b/source/fitz/halftone.c @@ -0,0 +1,202 @@ +#include "mupdf/fitz.h" + +fz_halftone * +fz_new_halftone(fz_context *ctx, int comps) +{ + fz_halftone *ht; + int i; + + ht = fz_malloc(ctx, sizeof(fz_halftone) + (comps-1)*sizeof(fz_pixmap *)); + ht->refs = 1; + ht->n = comps; + for (i = 0; i < comps; i++) + ht->comp[i] = NULL; + + return ht; +} + +fz_halftone * +fz_keep_halftone(fz_context *ctx, fz_halftone *ht) +{ + if (ht) + ht->refs++; + return ht; +} + +void +fz_drop_halftone(fz_context *ctx, fz_halftone *ht) +{ + int i; + + if (!ht || --ht->refs != 0) + return; + for (i = 0; i < ht->n; i++) + fz_drop_pixmap(ctx, ht->comp[i]); + fz_free(ctx, ht); +} + +/* Default mono halftone, lifted from Ghostscript. */ +/* The 0x00 entry has been changed to 0x01 to avoid problems with white + * pixels appearing in the output; as we use < 0 should not appear in the + * array. I think that gs scales this slighly and hence never actually uses + * the raw values here. */ +static unsigned char mono_ht[] = +{ + 0x0E, 0x8E, 0x2E, 0xAE, 0x06, 0x86, 0x26, 0xA6, 0x0C, 0x8C, 0x2C, 0xAC, 0x04, 0x84, 0x24, 0xA4, + 0xCE, 0x4E, 0xEE, 0x6E, 0xC6, 0x46, 0xE6, 0x66, 0xCC, 0x4C, 0xEC, 0x6C, 0xC4, 0x44, 0xE4, 0x64, + 0x3E, 0xBE, 0x1E, 0x9E, 0x36, 0xB6, 0x16, 0x96, 0x3C, 0xBC, 0x1C, 0x9C, 0x34, 0xB4, 0x14, 0x94, + 0xFE, 0x7E, 0xDE, 0x5E, 0xF6, 0x76, 0xD6, 0x56, 0xFC, 0x7C, 0xDC, 0x5C, 0xF4, 0x74, 0xD4, 0x54, + 0x01, 0x81, 0x21, 0xA1, 0x09, 0x89, 0x29, 0xA9, 0x03, 0x83, 0x23, 0xA3, 0x0B, 0x8B, 0x2B, 0xAB, + 0xC1, 0x41, 0xE1, 0x61, 0xC9, 0x49, 0xE9, 0x69, 0xC3, 0x43, 0xE3, 0x63, 0xCB, 0x4B, 0xEB, 0x6B, + 0x31, 0xB1, 0x11, 0x91, 0x39, 0xB9, 0x19, 0x99, 0x33, 0xB3, 0x13, 0x93, 0x3B, 0xBB, 0x1B, 0x9B, + 0xF1, 0x71, 0xD1, 0x51, 0xF9, 0x79, 0xD9, 0x59, 0xF3, 0x73, 0xD3, 0x53, 0xFB, 0x7B, 0xDB, 0x5B, + 0x0D, 0x8D, 0x2D, 0xAD, 0x05, 0x85, 0x25, 0xA5, 0x0F, 0x8F, 0x2F, 0xAF, 0x07, 0x87, 0x27, 0xA7, + 0xCD, 0x4D, 0xED, 0x6D, 0xC5, 0x45, 0xE5, 0x65, 0xCF, 0x4F, 0xEF, 0x6F, 0xC7, 0x47, 0xE7, 0x67, + 0x3D, 0xBD, 0x1D, 0x9D, 0x35, 0xB5, 0x15, 0x95, 0x3F, 0xBF, 0x1F, 0x9F, 0x37, 0xB7, 0x17, 0x97, + 0xFD, 0x7D, 0xDD, 0x5D, 0xF5, 0x75, 0xD5, 0x55, 0xFF, 0x7F, 0xDF, 0x5F, 0xF7, 0x77, 0xD7, 0x57, + 0x02, 0x82, 0x22, 0xA2, 0x0A, 0x8A, 0x2A, 0xAA, 0x01 /*0x00*/, 0x80, 0x20, 0xA0, 0x08, 0x88, 0x28, 0xA8, + 0xC2, 0x42, 0xE2, 0x62, 0xCA, 0x4A, 0xEA, 0x6A, 0xC0, 0x40, 0xE0, 0x60, 0xC8, 0x48, 0xE8, 0x68, + 0x32, 0xB2, 0x12, 0x92, 0x3A, 0xBA, 0x1A, 0x9A, 0x30, 0xB0, 0x10, 0x90, 0x38, 0xB8, 0x18, 0x98, + 0xF2, 0x72, 0xD2, 0x52, 0xFA, 0x7A, 0xDA, 0x5A, 0xF0, 0x70, 0xD0, 0x50, 0xF8, 0x78, 0xD8, 0x58 +}; + +fz_halftone *fz_default_halftone(fz_context *ctx, int num_comps) +{ + fz_halftone *ht = fz_new_halftone(ctx, num_comps); + assert(num_comps == 1); /* Only support 1 component for now */ + ht->comp[0] = fz_new_pixmap_with_data(ctx, NULL, 16, 16, mono_ht); + return ht; +} + +/* Finally, code to actually perform halftoning. */ +static void make_ht_line(unsigned char *buf, fz_halftone *ht, int x, int y, int w) +{ + /* FIXME: There is a potential optimisation here; in the case where + * the LCM of the halftone tile widths is smaller than w, we could + * form just one 'LCM' run, then copy it repeatedly. + */ + int k, n; + n = ht->n; + for (k = 0; k < n; k++) + { + fz_pixmap *tile = ht->comp[k]; + unsigned char *b = buf++; + unsigned char *t; + unsigned char *tbase; + int px = x + tile->x; + int py = y + tile->y; + int tw = tile->w; + int th = tile->h; + int w2 = w; + int len; + px = px % tw; + if (px < 0) + px += tw; + py = py % th; + if (py < 0) + py += th; + + assert(tile->n == 1); + + /* Left hand section; from x to tile width */ + tbase = tile->samples + (unsigned int)(py * tw); + t = tbase + px; + len = tw - px; + if (len > w2) + len = w2; + w2 -= len; + while (len--) + { + *b = *t++; + b += n; + } + + /* Centre section - complete copies */ + w2 -= tw; + while (w2 >= 0) + { + len = tw; + t = tbase; + while (len--) + { + *b = *t++; + b += n; + } + w2 -= tw; + } + w2 += tw; + + /* Right hand section - stragglers */ + t = tbase; + while (w2--) + { + *b = *t++; + b += n; + } + } +} + +/* Inner mono thresholding code */ +static void do_threshold_1(unsigned char *ht_line, unsigned char *pixmap, unsigned char *out, int w) +{ + int bit = 0x80; + int h = 0; + + do + { + if (*pixmap < *ht_line++) + h |= bit; + pixmap += 2; /* Skip the alpha */ + bit >>= 1; + if (bit == 0) + { + *out++ = h; + h = 0; + bit = 0x80; + } + + } + while (--w); + if (bit != 0x80) + *out++ = h; +} + +fz_bitmap *fz_halftone_pixmap(fz_context *ctx, fz_pixmap *pix, fz_halftone *ht) +{ + fz_bitmap *out; + unsigned char *ht_line, *o, *p; + int w, h, x, y, n, pstride, ostride; + fz_halftone *ht_orig = ht; + + if (!pix) + return NULL; + + assert(pix->n == 2); /* Mono + Alpha */ + + n = pix->n-1; /* Remove alpha */ + if (ht == NULL) + { + ht = fz_default_halftone(ctx, n); + } + ht_line = fz_malloc(ctx, pix->w * n); + out = fz_new_bitmap(ctx, pix->w, pix->h, n, pix->xres, pix->yres); + o = out->samples; + p = pix->samples; + + h = pix->h; + x = pix->x; + y = pix->y; + w = pix->w; + ostride = out->stride; + pstride = pix->w * pix->n; + while (h--) + { + make_ht_line(ht_line, ht, x, y++, w); + do_threshold_1(ht_line, p, o, w); + o += ostride; + p += pstride; + } + if (!ht_orig) + fz_drop_halftone(ctx, ht); + return out; +} diff --git a/source/fitz/hash.c b/source/fitz/hash.c new file mode 100644 index 00000000..624cc305 --- /dev/null +++ b/source/fitz/hash.c @@ -0,0 +1,357 @@ +#include "mupdf/fitz.h" + +/* +Simple hashtable with open addressing linear probe. +Unlike text book examples, removing entries works +correctly in this implementation, so it wont start +exhibiting bad behaviour if entries are inserted +and removed frequently. +*/ + +enum { MAX_KEY_LEN = 48 }; +typedef struct fz_hash_entry_s fz_hash_entry; + +struct fz_hash_entry_s +{ + unsigned char key[MAX_KEY_LEN]; + void *val; +}; + +struct fz_hash_table_s +{ + int keylen; + int size; + int load; + int lock; /* -1 or the lock used to protect this hash table */ + fz_hash_entry *ents; +}; + +static unsigned hash(unsigned char *s, int len) +{ + unsigned val = 0; + int i; + for (i = 0; i < len; i++) + { + val += s[i]; + val += (val << 10); + val ^= (val >> 6); + } + val += (val << 3); + val ^= (val >> 11); + val += (val << 15); + return val; +} + +fz_hash_table * +fz_new_hash_table(fz_context *ctx, int initialsize, int keylen, int lock) +{ + fz_hash_table *table; + + assert(keylen <= MAX_KEY_LEN); + + table = fz_malloc_struct(ctx, fz_hash_table); + table->keylen = keylen; + table->size = initialsize; + table->load = 0; + table->lock = lock; + fz_try(ctx) + { + table->ents = fz_malloc_array(ctx, table->size, sizeof(fz_hash_entry)); + memset(table->ents, 0, sizeof(fz_hash_entry) * table->size); + } + fz_catch(ctx) + { + fz_free(ctx, table); + fz_rethrow(ctx); + } + + return table; +} + +void +fz_empty_hash(fz_context *ctx, fz_hash_table *table) +{ + table->load = 0; + memset(table->ents, 0, sizeof(fz_hash_entry) * table->size); +} + +int +fz_hash_len(fz_context *ctx, fz_hash_table *table) +{ + return table->size; +} + +void * +fz_hash_get_key(fz_context *ctx, fz_hash_table *table, int idx) +{ + return table->ents[idx].key; +} + +void * +fz_hash_get_val(fz_context *ctx, fz_hash_table *table, int idx) +{ + return table->ents[idx].val; +} + +void +fz_free_hash(fz_context *ctx, fz_hash_table *table) +{ + fz_free(ctx, table->ents); + fz_free(ctx, table); +} + +static void * +do_hash_insert(fz_context *ctx, fz_hash_table *table, void *key, void *val, unsigned *pos_ptr) +{ + fz_hash_entry *ents; + unsigned size; + unsigned pos; + + ents = table->ents; + size = table->size; + pos = hash(key, table->keylen) % size; + + if (table->lock >= 0) + fz_assert_lock_held(ctx, table->lock); + + while (1) + { + if (!ents[pos].val) + { + memcpy(ents[pos].key, key, table->keylen); + ents[pos].val = val; + table->load ++; + if (pos_ptr) + *pos_ptr = pos; + return NULL; + } + + if (memcmp(key, ents[pos].key, table->keylen) == 0) + { + /* This is legal, but should happen rarely in the non + * pos_ptr case. */ + if (pos_ptr) + *pos_ptr = pos; + else + fz_warn(ctx, "assert: overwrite hash slot"); + return ents[pos].val; + } + + pos = (pos + 1) % size; + } +} + +/* Entered with the lock taken, held throughout and at exit, UNLESS the lock + * is the alloc lock in which case it may be momentarily dropped. */ +static void +fz_resize_hash(fz_context *ctx, fz_hash_table *table, int newsize) +{ + fz_hash_entry *oldents = table->ents; + fz_hash_entry *newents; + int oldsize = table->size; + int oldload = table->load; + int i; + + if (newsize < oldload * 8 / 10) + { + fz_warn(ctx, "assert: resize hash too small"); + return; + } + + if (table->lock == FZ_LOCK_ALLOC) + fz_unlock(ctx, FZ_LOCK_ALLOC); + newents = fz_malloc_array_no_throw(ctx, newsize, sizeof(fz_hash_entry)); + if (table->lock == FZ_LOCK_ALLOC) + fz_lock(ctx, FZ_LOCK_ALLOC); + if (table->lock >= 0) + { + if (table->size >= newsize) + { + /* Someone else fixed it before we could lock! */ + if (table->lock == FZ_LOCK_ALLOC) + fz_unlock(ctx, table->lock); + fz_free(ctx, newents); + if (table->lock == FZ_LOCK_ALLOC) + fz_lock(ctx, table->lock); + return; + } + } + if (newents == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "hash table resize failed; out of memory (%d entries)", newsize); + table->ents = newents; + memset(table->ents, 0, sizeof(fz_hash_entry) * newsize); + table->size = newsize; + table->load = 0; + + for (i = 0; i < oldsize; i++) + { + if (oldents[i].val) + { + do_hash_insert(ctx, table, oldents[i].key, oldents[i].val, NULL); + } + } + + if (table->lock == FZ_LOCK_ALLOC) + fz_unlock(ctx, FZ_LOCK_ALLOC); + fz_free(ctx, oldents); + if (table->lock == FZ_LOCK_ALLOC) + fz_lock(ctx, FZ_LOCK_ALLOC); +} + +void * +fz_hash_find(fz_context *ctx, fz_hash_table *table, void *key) +{ + fz_hash_entry *ents = table->ents; + unsigned size = table->size; + unsigned pos = hash(key, table->keylen) % size; + + if (table->lock >= 0) + fz_assert_lock_held(ctx, table->lock); + + while (1) + { + if (!ents[pos].val) + return NULL; + + if (memcmp(key, ents[pos].key, table->keylen) == 0) + return ents[pos].val; + + pos = (pos + 1) % size; + } +} + +void * +fz_hash_insert(fz_context *ctx, fz_hash_table *table, void *key, void *val) +{ + if (table->load > table->size * 8 / 10) + { + fz_resize_hash(ctx, table, table->size * 2); + } + + return do_hash_insert(ctx, table, key, val, NULL); +} + +void * +fz_hash_insert_with_pos(fz_context *ctx, fz_hash_table *table, void *key, void *val, unsigned *pos) +{ + if (table->load > table->size * 8 / 10) + { + fz_resize_hash(ctx, table, table->size * 2); + } + + return do_hash_insert(ctx, table, key, val, pos); +} + +static void +do_removal(fz_context *ctx, fz_hash_table *table, void *key, unsigned hole) +{ + fz_hash_entry *ents = table->ents; + unsigned size = table->size; + unsigned look, code; + + if (table->lock >= 0) + fz_assert_lock_held(ctx, table->lock); + + ents[hole].val = NULL; + + look = hole + 1; + if (look == size) + look = 0; + + while (ents[look].val) + { + code = hash(ents[look].key, table->keylen) % size; + if ((code <= hole && hole < look) || + (look < code && code <= hole) || + (hole < look && look < code)) + { + ents[hole] = ents[look]; + ents[look].val = NULL; + hole = look; + } + + look++; + if (look == size) + look = 0; + } + + table->load --; +} + +void +fz_hash_remove(fz_context *ctx, fz_hash_table *table, void *key) +{ + fz_hash_entry *ents = table->ents; + unsigned size = table->size; + unsigned pos = hash(key, table->keylen) % size; + + if (table->lock >= 0) + fz_assert_lock_held(ctx, table->lock); + + while (1) + { + if (!ents[pos].val) + { + fz_warn(ctx, "assert: remove non-existent hash entry"); + return; + } + + if (memcmp(key, ents[pos].key, table->keylen) == 0) + { + do_removal(ctx, table, key, pos); + return; + } + + pos++; + if (pos == size) + pos = 0; + } +} + +void +fz_hash_remove_fast(fz_context *ctx, fz_hash_table *table, void *key, unsigned pos) +{ + fz_hash_entry *ents = table->ents; + + if (ents[pos].val == NULL || memcmp(key, ents[pos].key, table->keylen) != 0) + { + /* The value isn't there, or the key didn't match! The table + * must have been rebuilt (or the contents moved) in the + * meantime. Do the removal the slow way. */ + fz_hash_remove(ctx, table, key); + } + else + do_removal(ctx, table, key, pos); +} + +#ifndef NDEBUG +void +fz_print_hash(fz_context *ctx, FILE *out, fz_hash_table *table) +{ + fz_print_hash_details(ctx, out, table, NULL); +} + +void +fz_print_hash_details(fz_context *ctx, FILE *out, fz_hash_table *table, void (*details)(FILE *,void*)) +{ + int i, k; + + fprintf(out, "cache load %d / %d\n", table->load, table->size); + + for (i = 0; i < table->size; i++) + { + if (!table->ents[i].val) + fprintf(out, "table % 4d: empty\n", i); + else + { + fprintf(out, "table % 4d: key=", i); + for (k = 0; k < MAX_KEY_LEN; k++) + fprintf(out, "%02x", ((char*)table->ents[i].key)[k]); + if (details) + details(out, table->ents[i].val); + else + fprintf(out, " val=$%p\n", table->ents[i].val); + } + } +} +#endif diff --git a/source/fitz/image.c b/source/fitz/image.c new file mode 100644 index 00000000..73b4bc28 --- /dev/null +++ b/source/fitz/image.c @@ -0,0 +1,493 @@ +#include "mupdf/fitz.h" + +fz_pixmap * +fz_image_to_pixmap(fz_context *ctx, fz_image *image, int w, int h) +{ + if (image == NULL) + return NULL; + return image->get_pixmap(ctx, image, w, h); +} + +fz_image * +fz_keep_image(fz_context *ctx, fz_image *image) +{ + return (fz_image *)fz_keep_storable(ctx, &image->storable); +} + +void +fz_drop_image(fz_context *ctx, fz_image *image) +{ + fz_drop_storable(ctx, &image->storable); +} + +typedef struct fz_image_key_s fz_image_key; + +struct fz_image_key_s { + int refs; + fz_image *image; + int l2factor; +}; + +static int +fz_make_hash_image_key(fz_store_hash *hash, void *key_) +{ + fz_image_key *key = (fz_image_key *)key_; + + hash->u.pi.ptr = key->image; + hash->u.pi.i = key->l2factor; + return 1; +} + +static void * +fz_keep_image_key(fz_context *ctx, void *key_) +{ + fz_image_key *key = (fz_image_key *)key_; + + fz_lock(ctx, FZ_LOCK_ALLOC); + key->refs++; + fz_unlock(ctx, FZ_LOCK_ALLOC); + + return (void *)key; +} + +static void +fz_drop_image_key(fz_context *ctx, void *key_) +{ + fz_image_key *key = (fz_image_key *)key_; + int drop; + + if (key == NULL) + return; + fz_lock(ctx, FZ_LOCK_ALLOC); + drop = --key->refs; + fz_unlock(ctx, FZ_LOCK_ALLOC); + if (drop == 0) + { + fz_drop_image(ctx, key->image); + fz_free(ctx, key); + } +} + +static int +fz_cmp_image_key(void *k0_, void *k1_) +{ + fz_image_key *k0 = (fz_image_key *)k0_; + fz_image_key *k1 = (fz_image_key *)k1_; + + return k0->image == k1->image && k0->l2factor == k1->l2factor; +} + +#ifndef NDEBUG +static void +fz_debug_image(FILE *out, void *key_) +{ + fz_image_key *key = (fz_image_key *)key_; + + fprintf(out, "(image %d x %d sf=%d) ", key->image->w, key->image->h, key->l2factor); +} +#endif + +static fz_store_type fz_image_store_type = +{ + fz_make_hash_image_key, + fz_keep_image_key, + fz_drop_image_key, + fz_cmp_image_key, +#ifndef NDEBUG + fz_debug_image +#endif +}; + +static void +fz_mask_color_key(fz_pixmap *pix, int n, int *colorkey) +{ + unsigned char *p = pix->samples; + int len = pix->w * pix->h; + int k, t; + while (len--) + { + t = 1; + for (k = 0; k < n; k++) + if (p[k] < colorkey[k * 2] || p[k] > colorkey[k * 2 + 1]) + t = 0; + if (t) + for (k = 0; k < pix->n; k++) + p[k] = 0; + p += pix->n; + } +} + +fz_pixmap * +fz_decomp_image_from_stream(fz_context *ctx, fz_stream *stm, fz_image *image, int in_line, int indexed, int l2factor, int native_l2factor) +{ + fz_pixmap *tile = NULL; + int stride, len, i; + unsigned char *samples = NULL; + int f = 1<<native_l2factor; + int w = (image->w + f-1) >> native_l2factor; + int h = (image->h + f-1) >> native_l2factor; + + fz_var(tile); + fz_var(samples); + + fz_try(ctx) + { + tile = fz_new_pixmap(ctx, image->colorspace, w, h); + tile->interpolate = image->interpolate; + + stride = (w * image->n * image->bpc + 7) / 8; + + samples = fz_malloc_array(ctx, h, stride); + + len = fz_read(stm, samples, h * stride); + if (len < 0) + { + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot read image data"); + } + + /* Make sure we read the EOF marker (for inline images only) */ + if (in_line) + { + unsigned char tbuf[512]; + fz_try(ctx) + { + int tlen = fz_read(stm, tbuf, sizeof tbuf); + if (tlen > 0) + fz_warn(ctx, "ignoring garbage at end of image"); + } + fz_catch(ctx) + { + /* FIXME: TryLater? */ + fz_warn(ctx, "ignoring error at end of image"); + } + } + + /* Pad truncated images */ + if (len < stride * h) + { + fz_warn(ctx, "padding truncated image"); + memset(samples + len, 0, stride * h - len); + } + + /* Invert 1-bit image masks */ + if (image->imagemask) + { + /* 0=opaque and 1=transparent so we need to invert */ + unsigned char *p = samples; + len = h * stride; + for (i = 0; i < len; i++) + p[i] = ~p[i]; + } + + fz_unpack_tile(tile, samples, image->n, image->bpc, stride, indexed); + + fz_free(ctx, samples); + samples = NULL; + + if (image->usecolorkey) + fz_mask_color_key(tile, image->n, image->colorkey); + + if (indexed) + { + fz_pixmap *conv; + fz_decode_indexed_tile(tile, image->decode, (1 << image->bpc) - 1); + conv = fz_expand_indexed_pixmap(ctx, tile); + fz_drop_pixmap(ctx, tile); + tile = conv; + } + else + { + fz_decode_tile(tile, image->decode); + } + } + fz_always(ctx) + { + fz_close(stm); + } + fz_catch(ctx) + { + if (tile) + fz_drop_pixmap(ctx, tile); + fz_free(ctx, samples); + + fz_rethrow(ctx); + } + + /* Now apply any extra subsampling required */ + if (l2factor - native_l2factor > 0) + { + if (l2factor - native_l2factor > 8) + l2factor = native_l2factor + 8; + fz_subsample_pixmap(ctx, tile, l2factor - native_l2factor); + } + + return tile; +} + +void +fz_free_image(fz_context *ctx, fz_storable *image_) +{ + fz_image *image = (fz_image *)image_; + + if (image == NULL) + return; + fz_drop_pixmap(ctx, image->tile); + fz_free_compressed_buffer(ctx, image->buffer); + fz_drop_colorspace(ctx, image->colorspace); + fz_drop_image(ctx, image->mask); + fz_free(ctx, image); +} + +fz_pixmap * +fz_image_get_pixmap(fz_context *ctx, fz_image *image, int w, int h) +{ + fz_pixmap *tile; + fz_stream *stm; + int l2factor; + fz_image_key key; + int native_l2factor; + int indexed; + fz_image_key *keyp; + + /* Check for 'simple' images which are just pixmaps */ + if (image->buffer == NULL) + { + tile = image->tile; + if (!tile) + return NULL; + return fz_keep_pixmap(ctx, tile); /* That's all we can give you! */ + } + + /* Ensure our expectations for tile size are reasonable */ + if (w > image->w) + w = image->w; + if (h > image->h) + h = image->h; + + /* What is our ideal factor? */ + if (w == 0 || h == 0) + l2factor = 0; + else + for (l2factor=0; image->w>>(l2factor+1) >= w && image->h>>(l2factor+1) >= h && l2factor < 8; l2factor++); + + /* Can we find any suitable tiles in the cache? */ + key.refs = 1; + key.image = image; + key.l2factor = l2factor; + do + { + tile = fz_find_item(ctx, fz_free_pixmap_imp, &key, &fz_image_store_type); + if (tile) + return tile; + key.l2factor--; + } + while (key.l2factor >= 0); + + /* We need to make a new one. */ + /* First check for ones that we can't decode using streams */ + switch (image->buffer->params.type) + { + case FZ_IMAGE_PNG: + tile = fz_load_png(ctx, image->buffer->buffer->data, image->buffer->buffer->len); + break; + case FZ_IMAGE_TIFF: + tile = fz_load_tiff(ctx, image->buffer->buffer->data, image->buffer->buffer->len); + break; + default: + native_l2factor = l2factor; + stm = fz_open_image_decomp_stream(ctx, image->buffer, &native_l2factor); + + indexed = fz_colorspace_is_indexed(image->colorspace); + tile = fz_decomp_image_from_stream(ctx, stm, image, 0, indexed, l2factor, native_l2factor); + break; + } + + /* Now we try to cache the pixmap. Any failure here will just result + * in us not caching. */ + fz_var(keyp); + fz_try(ctx) + { + fz_pixmap *existing_tile; + + keyp = fz_malloc_struct(ctx, fz_image_key); + keyp->refs = 1; + keyp->image = fz_keep_image(ctx, image); + keyp->l2factor = l2factor; + existing_tile = fz_store_item(ctx, keyp, tile, fz_pixmap_size(ctx, tile), &fz_image_store_type); + if (existing_tile) + { + /* We already have a tile. This must have been produced by a + * racing thread. We'll throw away ours and use that one. */ + fz_drop_pixmap(ctx, tile); + tile = existing_tile; + } + } + fz_always(ctx) + { + fz_drop_image_key(ctx, keyp); + } + fz_catch(ctx) + { + /* Do nothing */ + } + + return tile; +} + +fz_image * +fz_new_image_from_pixmap(fz_context *ctx, fz_pixmap *pixmap, fz_image *mask) +{ + fz_image *image; + + assert(mask == NULL || mask->mask == NULL); + + fz_try(ctx) + { + image = fz_malloc_struct(ctx, fz_image); + FZ_INIT_STORABLE(image, 1, fz_free_image); + image->w = pixmap->w; + image->h = pixmap->h; + image->n = pixmap->n; + image->colorspace = pixmap->colorspace; + image->bpc = 8; + image->buffer = NULL; + image->get_pixmap = fz_image_get_pixmap; + image->xres = pixmap->xres; + image->yres = pixmap->yres; + image->tile = pixmap; + image->mask = mask; + } + fz_catch(ctx) + { + fz_drop_image(ctx, mask); + fz_rethrow(ctx); + } + return image; +} + +fz_image * +fz_new_image(fz_context *ctx, int w, int h, int bpc, fz_colorspace *colorspace, + int xres, int yres, int interpolate, int imagemask, float *decode, + int *colorkey, fz_compressed_buffer *buffer, fz_image *mask) +{ + fz_image *image; + + assert(mask == NULL || mask->mask == NULL); + + fz_try(ctx) + { + image = fz_malloc_struct(ctx, fz_image); + FZ_INIT_STORABLE(image, 1, fz_free_image); + image->get_pixmap = fz_image_get_pixmap; + image->w = w; + image->h = h; + image->xres = xres; + image->yres = yres; + image->bpc = bpc; + image->n = (colorspace ? colorspace->n : 1); + image->colorspace = colorspace; + image->interpolate = interpolate; + image->imagemask = imagemask; + image->usecolorkey = (colorkey != NULL); + if (colorkey) + memcpy(image->colorkey, colorkey, sizeof(int)*image->n*2); + if (decode) + memcpy(image->decode, decode, sizeof(float)*image->n*2); + else + { + float maxval = fz_colorspace_is_indexed(colorspace) ? (1 << bpc) - 1 : 1; + int i; + for (i = 0; i < image->n; i++) + { + image->decode[2*i] = 0; + image->decode[2*i+1] = maxval; + } + } + image->mask = mask; + image->buffer = buffer; + } + fz_catch(ctx) + { + fz_free_compressed_buffer(ctx, buffer); + fz_rethrow(ctx); + } + + return image; +} + +fz_image * +fz_new_image_from_data(fz_context *ctx, unsigned char *data, int len) +{ + fz_buffer *buffer = NULL; + fz_image *image; + + fz_var(buffer); + fz_var(data); + + fz_try(ctx) + { + buffer = fz_new_buffer_from_data(ctx, data, len); + data = NULL; + image = fz_new_image_from_buffer(ctx, buffer); + } + fz_always(ctx) + { + fz_drop_buffer(ctx, buffer); + } + fz_catch(ctx) + { + fz_free(ctx, data); + fz_rethrow(ctx); + } + + return image; +} + +fz_image * +fz_new_image_from_buffer(fz_context *ctx, fz_buffer *buffer) +{ + fz_compressed_buffer *bc = NULL; + int w, h, xres, yres; + fz_colorspace *cspace; + int len = buffer->len; + unsigned char *buf = buffer->data; + + fz_var(bc); + + fz_try(ctx) + { + if (len < 8) + fz_throw(ctx, FZ_ERROR_GENERIC, "unknown image file format"); + + bc = fz_malloc_struct(ctx, fz_compressed_buffer); + bc->buffer = fz_keep_buffer(ctx, buffer); + + if (buf[0] == 0xff && buf[1] == 0xd8) + { + bc->params.type = FZ_IMAGE_JPEG; + bc->params.u.jpeg.color_transform = -1; + fz_load_jpeg_info(ctx, buf, len, &w, &h, &xres, &yres, &cspace); + } + else if (memcmp(buf, "\211PNG\r\n\032\n", 8) == 0) + { + bc->params.type = FZ_IMAGE_PNG; + fz_load_png_info(ctx, buf, len, &w, &h, &xres, &yres, &cspace); + } + else if (memcmp(buf, "II", 2) == 0 && buf[2] == 0xBC) + fz_throw(ctx, FZ_ERROR_GENERIC, "JPEG-XR codec is not available"); + else if (memcmp(buf, "MM", 2) == 0 || memcmp(buf, "II", 2) == 0) + { + bc->params.type = FZ_IMAGE_TIFF; + fz_load_tiff_info(ctx, buf, len, &w, &h, &xres, &yres, &cspace); + } + else + fz_throw(ctx, FZ_ERROR_GENERIC, "unknown image file format"); + } + fz_catch(ctx) + { + fz_free_compressed_buffer(ctx, bc); + fz_rethrow(ctx); + } + + return fz_new_image(ctx, w, h, 8, cspace, xres, yres, 0, 0, NULL, NULL, bc, NULL); +} diff --git a/source/fitz/link.c b/source/fitz/link.c new file mode 100644 index 00000000..30dca222 --- /dev/null +++ b/source/fitz/link.c @@ -0,0 +1,65 @@ +#include "mupdf/fitz.h" + +void +fz_free_link_dest(fz_context *ctx, fz_link_dest *dest) +{ + switch (dest->kind) + { + case FZ_LINK_NONE: + case FZ_LINK_GOTO: + break; + case FZ_LINK_URI: + fz_free(ctx, dest->ld.uri.uri); + break; + case FZ_LINK_LAUNCH: + fz_free(ctx, dest->ld.launch.file_spec); + break; + case FZ_LINK_NAMED: + fz_free(ctx, dest->ld.named.named); + break; + case FZ_LINK_GOTOR: + fz_free(ctx, dest->ld.gotor.file_spec); + break; + } +} + +fz_link * +fz_new_link(fz_context *ctx, const fz_rect *bbox, fz_link_dest dest) +{ + fz_link *link; + + fz_try(ctx) + { + link = fz_malloc_struct(ctx, fz_link); + link->refs = 1; + } + fz_catch(ctx) + { + fz_free_link_dest(ctx, &dest); + fz_rethrow(ctx); + } + link->dest = dest; + link->rect = *bbox; + link->next = NULL; + return link; +} + +fz_link * +fz_keep_link(fz_context *ctx, fz_link *link) +{ + if (link) + link->refs++; + return link; +} + +void +fz_drop_link(fz_context *ctx, fz_link *link) +{ + while (link && --link->refs == 0) + { + fz_link *next = link->next; + fz_free_link_dest(ctx, &link->dest); + fz_free(ctx, link); + link = next; + } +} diff --git a/source/fitz/list-device.c b/source/fitz/list-device.c new file mode 100644 index 00000000..6ffe165f --- /dev/null +++ b/source/fitz/list-device.c @@ -0,0 +1,851 @@ +#include "mupdf/fitz.h" + +typedef struct fz_display_node_s fz_display_node; + +#define STACK_SIZE 96 + +typedef enum fz_display_command_e +{ + FZ_CMD_BEGIN_PAGE, + FZ_CMD_END_PAGE, + FZ_CMD_FILL_PATH, + FZ_CMD_STROKE_PATH, + FZ_CMD_CLIP_PATH, + FZ_CMD_CLIP_STROKE_PATH, + FZ_CMD_FILL_TEXT, + FZ_CMD_STROKE_TEXT, + FZ_CMD_CLIP_TEXT, + FZ_CMD_CLIP_STROKE_TEXT, + FZ_CMD_IGNORE_TEXT, + FZ_CMD_FILL_SHADE, + FZ_CMD_FILL_IMAGE, + FZ_CMD_FILL_IMAGE_MASK, + FZ_CMD_CLIP_IMAGE_MASK, + FZ_CMD_POP_CLIP, + FZ_CMD_BEGIN_MASK, + FZ_CMD_END_MASK, + FZ_CMD_BEGIN_GROUP, + FZ_CMD_END_GROUP, + FZ_CMD_BEGIN_TILE, + FZ_CMD_END_TILE +} fz_display_command; + +struct fz_display_node_s +{ + fz_display_command cmd; + fz_display_node *next; + fz_rect rect; + union { + fz_path *path; + fz_text *text; + fz_shade *shade; + fz_image *image; + int blendmode; + } item; + fz_stroke_state *stroke; + int flag; /* even_odd, accumulate, isolated/knockout... */ + fz_matrix ctm; + fz_colorspace *colorspace; + float alpha; + float color[FZ_MAX_COLORS]; +}; + +struct fz_display_list_s +{ + fz_storable storable; + fz_display_node *first; + fz_display_node *last; + int len; + + int top; + struct { + fz_rect *update; + fz_rect rect; + } stack[STACK_SIZE]; + int tiled; +}; + +enum { ISOLATED = 1, KNOCKOUT = 2 }; + +static fz_display_node * +fz_new_display_node(fz_context *ctx, fz_display_command cmd, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_display_node *node; + int i; + + node = fz_malloc_struct(ctx, fz_display_node); + node->cmd = cmd; + node->next = NULL; + node->rect = fz_empty_rect; + node->item.path = NULL; + node->stroke = NULL; + node->flag = (cmd == FZ_CMD_BEGIN_TILE ? fz_gen_id(ctx) : 0); + node->ctm = *ctm; + if (colorspace) + { + node->colorspace = fz_keep_colorspace(ctx, colorspace); + if (color) + { + for (i = 0; i < node->colorspace->n; i++) + node->color[i] = color[i]; + } + } + else + { + node->colorspace = NULL; + } + node->alpha = alpha; + + return node; +} + +static void +fz_append_display_node(fz_display_list *list, fz_display_node *node) +{ + switch (node->cmd) + { + case FZ_CMD_CLIP_PATH: + case FZ_CMD_CLIP_STROKE_PATH: + case FZ_CMD_CLIP_IMAGE_MASK: + if (list->top < STACK_SIZE) + { + list->stack[list->top].update = &node->rect; + list->stack[list->top].rect = fz_empty_rect; + } + list->top++; + break; + case FZ_CMD_END_MASK: + case FZ_CMD_CLIP_TEXT: + case FZ_CMD_CLIP_STROKE_TEXT: + if (list->top < STACK_SIZE) + { + list->stack[list->top].update = NULL; + list->stack[list->top].rect = fz_empty_rect; + } + list->top++; + break; + case FZ_CMD_BEGIN_TILE: + list->tiled++; + if (list->top > 0 && list->top <= STACK_SIZE) + { + list->stack[list->top-1].rect = fz_infinite_rect; + } + break; + case FZ_CMD_END_TILE: + list->tiled--; + break; + case FZ_CMD_END_GROUP: + break; + case FZ_CMD_POP_CLIP: + if (list->top > STACK_SIZE) + { + list->top--; + node->rect = fz_infinite_rect; + } + else if (list->top > 0) + { + fz_rect *update; + list->top--; + update = list->stack[list->top].update; + if (list->tiled == 0) + { + if (update) + { + fz_intersect_rect(update, &list->stack[list->top].rect); + node->rect = *update; + } + else + node->rect = list->stack[list->top].rect; + } + else + node->rect = fz_infinite_rect; + } + /* fallthrough */ + default: + if (list->top > 0 && list->tiled == 0 && list->top <= STACK_SIZE) + fz_union_rect(&list->stack[list->top-1].rect, &node->rect); + break; + } + if (!list->first) + { + list->first = node; + list->last = node; + } + else + { + list->last->next = node; + list->last = node; + } + list->len++; +} + +static void +fz_free_display_node(fz_context *ctx, fz_display_node *node) +{ + switch (node->cmd) + { + case FZ_CMD_FILL_PATH: + case FZ_CMD_STROKE_PATH: + case FZ_CMD_CLIP_PATH: + case FZ_CMD_CLIP_STROKE_PATH: + fz_free_path(ctx, node->item.path); + break; + case FZ_CMD_FILL_TEXT: + case FZ_CMD_STROKE_TEXT: + case FZ_CMD_CLIP_TEXT: + case FZ_CMD_CLIP_STROKE_TEXT: + case FZ_CMD_IGNORE_TEXT: + fz_free_text(ctx, node->item.text); + break; + case FZ_CMD_FILL_SHADE: + fz_drop_shade(ctx, node->item.shade); + break; + case FZ_CMD_FILL_IMAGE: + case FZ_CMD_FILL_IMAGE_MASK: + case FZ_CMD_CLIP_IMAGE_MASK: + fz_drop_image(ctx, node->item.image); + break; + case FZ_CMD_POP_CLIP: + case FZ_CMD_BEGIN_MASK: + case FZ_CMD_END_MASK: + case FZ_CMD_BEGIN_GROUP: + case FZ_CMD_END_GROUP: + case FZ_CMD_BEGIN_TILE: + case FZ_CMD_END_TILE: + case FZ_CMD_BEGIN_PAGE: + case FZ_CMD_END_PAGE: + break; + } + if (node->stroke) + fz_drop_stroke_state(ctx, node->stroke); + if (node->colorspace) + fz_drop_colorspace(ctx, node->colorspace); + fz_free(ctx, node); +} + +static void +fz_list_begin_page(fz_device *dev, const fz_rect *mediabox, const fz_matrix *ctm) +{ + fz_context *ctx = dev->ctx; + fz_display_node *node = fz_new_display_node(ctx, FZ_CMD_BEGIN_PAGE, ctm, NULL, NULL, 0); + node->rect = *mediabox; + fz_transform_rect(&node->rect, ctm); + fz_append_display_node(dev->user, node); +} + +static void +fz_list_end_page(fz_device *dev) +{ + fz_context *ctx = dev->ctx; + fz_display_node *node = fz_new_display_node(ctx, FZ_CMD_END_PAGE, &fz_identity, NULL, NULL, 0); + fz_append_display_node(dev->user, node); +} + +static void +fz_list_fill_path(fz_device *dev, fz_path *path, int even_odd, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_display_node *node; + fz_context *ctx = dev->ctx; + node = fz_new_display_node(ctx, FZ_CMD_FILL_PATH, ctm, colorspace, color, alpha); + fz_try(ctx) + { + fz_bound_path(dev->ctx, path, NULL, ctm, &node->rect); + node->item.path = fz_clone_path(dev->ctx, path); + node->flag = even_odd; + } + fz_catch(ctx) + { + fz_free_display_node(ctx, node); + fz_rethrow(ctx); + } + fz_append_display_node(dev->user, node); +} + +static void +fz_list_stroke_path(fz_device *dev, fz_path *path, fz_stroke_state *stroke, + const fz_matrix *ctm, fz_colorspace *colorspace, float *color, float alpha) +{ + fz_display_node *node; + fz_context *ctx = dev->ctx; + node = fz_new_display_node(ctx, FZ_CMD_STROKE_PATH, ctm, colorspace, color, alpha); + fz_try(ctx) + { + fz_bound_path(dev->ctx, path, stroke, ctm, &node->rect); + node->item.path = fz_clone_path(dev->ctx, path); + node->stroke = fz_keep_stroke_state(dev->ctx, stroke); + } + fz_catch(ctx) + { + fz_free_display_node(ctx, node); + fz_rethrow(ctx); + } + fz_append_display_node(dev->user, node); +} + +static void +fz_list_clip_path(fz_device *dev, fz_path *path, const fz_rect *rect, int even_odd, const fz_matrix *ctm) +{ + fz_display_node *node; + fz_context *ctx = dev->ctx; + node = fz_new_display_node(ctx, FZ_CMD_CLIP_PATH, ctm, NULL, NULL, 0); + fz_try(ctx) + { + fz_bound_path(dev->ctx, path, NULL, ctm, &node->rect); + if (rect) + fz_intersect_rect(&node->rect, rect); + node->item.path = fz_clone_path(dev->ctx, path); + node->flag = even_odd; + } + fz_catch(ctx) + { + fz_free_display_node(ctx, node); + fz_rethrow(ctx); + } + fz_append_display_node(dev->user, node); +} + +static void +fz_list_clip_stroke_path(fz_device *dev, fz_path *path, const fz_rect *rect, fz_stroke_state *stroke, const fz_matrix *ctm) +{ + fz_display_node *node; + fz_context *ctx = dev->ctx; + node = fz_new_display_node(ctx, FZ_CMD_CLIP_STROKE_PATH, ctm, NULL, NULL, 0); + fz_try(ctx) + { + fz_bound_path(dev->ctx, path, stroke, ctm, &node->rect); + if (rect) + fz_intersect_rect(&node->rect, rect); + node->item.path = fz_clone_path(dev->ctx, path); + node->stroke = fz_keep_stroke_state(dev->ctx, stroke); + } + fz_catch(ctx) + { + fz_free_display_node(ctx, node); + fz_rethrow(ctx); + } + fz_append_display_node(dev->user, node); +} + +static void +fz_list_fill_text(fz_device *dev, fz_text *text, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_display_node *node; + fz_context *ctx = dev->ctx; + node = fz_new_display_node(ctx, FZ_CMD_FILL_TEXT, ctm, colorspace, color, alpha); + fz_try(ctx) + { + fz_bound_text(dev->ctx, text, NULL, ctm, &node->rect); + node->item.text = fz_clone_text(dev->ctx, text); + } + fz_catch(ctx) + { + fz_free_display_node(ctx, node); + fz_rethrow(ctx); + } + fz_append_display_node(dev->user, node); +} + +static void +fz_list_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_display_node *node; + fz_context *ctx = dev->ctx; + node = fz_new_display_node(ctx, FZ_CMD_STROKE_TEXT, ctm, colorspace, color, alpha); + node->item.text = NULL; + fz_try(ctx) + { + fz_bound_text(dev->ctx, text, stroke, ctm, &node->rect); + node->item.text = fz_clone_text(dev->ctx, text); + node->stroke = fz_keep_stroke_state(dev->ctx, stroke); + } + fz_catch(ctx) + { + fz_free_display_node(ctx, node); + fz_rethrow(ctx); + } + fz_append_display_node(dev->user, node); +} + +static void +fz_list_clip_text(fz_device *dev, fz_text *text, const fz_matrix *ctm, int accumulate) +{ + fz_display_node *node; + fz_context *ctx = dev->ctx; + node = fz_new_display_node(ctx, FZ_CMD_CLIP_TEXT, ctm, NULL, NULL, 0); + fz_try(ctx) + { + fz_bound_text(dev->ctx, text, NULL, ctm, &node->rect); + node->item.text = fz_clone_text(dev->ctx, text); + node->flag = accumulate; + /* when accumulating, be conservative about culling */ + if (accumulate) + node->rect = fz_infinite_rect; + } + fz_catch(ctx) + { + fz_free_display_node(ctx, node); + fz_rethrow(ctx); + } + fz_append_display_node(dev->user, node); +} + +static void +fz_list_clip_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm) +{ + fz_display_node *node; + fz_context *ctx = dev->ctx; + node = fz_new_display_node(ctx, FZ_CMD_CLIP_STROKE_TEXT, ctm, NULL, NULL, 0); + fz_try(ctx) + { + fz_bound_text(dev->ctx, text, stroke, ctm, &node->rect); + node->item.text = fz_clone_text(dev->ctx, text); + node->stroke = fz_keep_stroke_state(dev->ctx, stroke); + } + fz_catch(ctx) + { + fz_free_display_node(ctx, node); + fz_rethrow(ctx); + } + fz_append_display_node(dev->user, node); +} + +static void +fz_list_ignore_text(fz_device *dev, fz_text *text, const fz_matrix *ctm) +{ + fz_display_node *node; + fz_context *ctx = dev->ctx; + node = fz_new_display_node(ctx, FZ_CMD_IGNORE_TEXT, ctm, NULL, NULL, 0); + fz_try(ctx) + { + fz_bound_text(dev->ctx, text, NULL, ctm, &node->rect); + node->item.text = fz_clone_text(dev->ctx, text); + } + fz_catch(ctx) + { + fz_free_display_node(ctx, node); + fz_rethrow(ctx); + } + fz_append_display_node(dev->user, node); +} + +static void +fz_list_pop_clip(fz_device *dev) +{ + fz_display_node *node; + node = fz_new_display_node(dev->ctx, FZ_CMD_POP_CLIP, &fz_identity, NULL, NULL, 0); + fz_append_display_node(dev->user, node); +} + +static void +fz_list_fill_shade(fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) +{ + fz_display_node *node; + fz_context *ctx = dev->ctx; + node = fz_new_display_node(ctx, FZ_CMD_FILL_SHADE, ctm, NULL, NULL, alpha); + fz_bound_shade(ctx, shade, ctm, &node->rect); + node->item.shade = fz_keep_shade(ctx, shade); + fz_append_display_node(dev->user, node); +} + +static void +fz_list_fill_image(fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) +{ + fz_display_node *node; + node = fz_new_display_node(dev->ctx, FZ_CMD_FILL_IMAGE, ctm, NULL, NULL, alpha); + node->rect = fz_unit_rect; + fz_transform_rect(&node->rect, ctm); + node->item.image = fz_keep_image(dev->ctx, image); + fz_append_display_node(dev->user, node); +} + +static void +fz_list_fill_image_mask(fz_device *dev, fz_image *image, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_display_node *node; + node = fz_new_display_node(dev->ctx, FZ_CMD_FILL_IMAGE_MASK, ctm, colorspace, color, alpha); + node->rect = fz_unit_rect; + fz_transform_rect(&node->rect, ctm); + node->item.image = fz_keep_image(dev->ctx, image); + fz_append_display_node(dev->user, node); +} + +static void +fz_list_clip_image_mask(fz_device *dev, fz_image *image, const fz_rect *rect, const fz_matrix *ctm) +{ + fz_display_node *node; + node = fz_new_display_node(dev->ctx, FZ_CMD_CLIP_IMAGE_MASK, ctm, NULL, NULL, 0); + node->rect = fz_unit_rect; + fz_transform_rect(&node->rect, ctm); + if (rect) + fz_intersect_rect(&node->rect, rect); + node->item.image = fz_keep_image(dev->ctx, image); + fz_append_display_node(dev->user, node); +} + +static void +fz_list_begin_mask(fz_device *dev, const fz_rect *rect, int luminosity, fz_colorspace *colorspace, float *color) +{ + fz_display_node *node; + node = fz_new_display_node(dev->ctx, FZ_CMD_BEGIN_MASK, &fz_identity, colorspace, color, 0); + node->rect = *rect; + node->flag = luminosity; + fz_append_display_node(dev->user, node); +} + +static void +fz_list_end_mask(fz_device *dev) +{ + fz_display_node *node; + node = fz_new_display_node(dev->ctx, FZ_CMD_END_MASK, &fz_identity, NULL, NULL, 0); + fz_append_display_node(dev->user, node); +} + +static void +fz_list_begin_group(fz_device *dev, const fz_rect *rect, int isolated, int knockout, int blendmode, float alpha) +{ + fz_display_node *node; + node = fz_new_display_node(dev->ctx, FZ_CMD_BEGIN_GROUP, &fz_identity, NULL, NULL, alpha); + node->rect = *rect; + node->item.blendmode = blendmode; + node->flag |= isolated ? ISOLATED : 0; + node->flag |= knockout ? KNOCKOUT : 0; + fz_append_display_node(dev->user, node); +} + +static void +fz_list_end_group(fz_device *dev) +{ + fz_display_node *node; + node = fz_new_display_node(dev->ctx, FZ_CMD_END_GROUP, &fz_identity, NULL, NULL, 0); + fz_append_display_node(dev->user, node); +} + +static int +fz_list_begin_tile(fz_device *dev, const fz_rect *area, const fz_rect *view, float xstep, float ystep, const fz_matrix *ctm, int id) +{ + /* We ignore id here, as we will pass on our own id */ + fz_display_node *node; + node = fz_new_display_node(dev->ctx, FZ_CMD_BEGIN_TILE, ctm, NULL, NULL, 0); + node->rect = *area; + node->color[0] = xstep; + node->color[1] = ystep; + node->color[2] = view->x0; + node->color[3] = view->y0; + node->color[4] = view->x1; + node->color[5] = view->y1; + fz_append_display_node(dev->user, node); + return 0; +} + +static void +fz_list_end_tile(fz_device *dev) +{ + fz_display_node *node; + node = fz_new_display_node(dev->ctx, FZ_CMD_END_TILE, &fz_identity, NULL, NULL, 0); + fz_append_display_node(dev->user, node); +} + +fz_device * +fz_new_list_device(fz_context *ctx, fz_display_list *list) +{ + fz_device *dev = fz_new_device(ctx, list); + + dev->begin_page = fz_list_begin_page; + dev->end_page = fz_list_end_page; + + dev->fill_path = fz_list_fill_path; + dev->stroke_path = fz_list_stroke_path; + dev->clip_path = fz_list_clip_path; + dev->clip_stroke_path = fz_list_clip_stroke_path; + + dev->fill_text = fz_list_fill_text; + dev->stroke_text = fz_list_stroke_text; + dev->clip_text = fz_list_clip_text; + dev->clip_stroke_text = fz_list_clip_stroke_text; + dev->ignore_text = fz_list_ignore_text; + + dev->fill_shade = fz_list_fill_shade; + dev->fill_image = fz_list_fill_image; + dev->fill_image_mask = fz_list_fill_image_mask; + dev->clip_image_mask = fz_list_clip_image_mask; + + dev->pop_clip = fz_list_pop_clip; + + dev->begin_mask = fz_list_begin_mask; + dev->end_mask = fz_list_end_mask; + dev->begin_group = fz_list_begin_group; + dev->end_group = fz_list_end_group; + + dev->begin_tile = fz_list_begin_tile; + dev->end_tile = fz_list_end_tile; + + return dev; +} + +static void +fz_free_display_list(fz_context *ctx, fz_storable *list_) +{ + fz_display_list *list = (fz_display_list *)list_; + fz_display_node *node; + + if (list == NULL) + return; + node = list->first; + while (node) + { + fz_display_node *next = node->next; + fz_free_display_node(ctx, node); + node = next; + } + fz_free(ctx, list); +} + +fz_display_list * +fz_new_display_list(fz_context *ctx) +{ + fz_display_list *list = fz_malloc_struct(ctx, fz_display_list); + FZ_INIT_STORABLE(list, 1, fz_free_display_list); + list->first = NULL; + list->last = NULL; + list->len = 0; + list->top = 0; + list->tiled = 0; + return list; +} + +fz_display_list * +fz_keep_display_list(fz_context *ctx, fz_display_list *list) +{ + return (fz_display_list *)fz_keep_storable(ctx, &list->storable); +} + +void +fz_drop_display_list(fz_context *ctx, fz_display_list *list) +{ + fz_drop_storable(ctx, &list->storable); +} + +static fz_display_node * +skip_to_end_tile(fz_display_node *node, int *progress) +{ + fz_display_node *next; + int depth = 1; + + /* Skip through until we find the matching end_tile. Note that + * (somewhat nastily) we return the PREVIOUS node to this to help + * the calling routine. */ + do + { + next = node->next; + if (next == NULL) + break; + if (next->cmd == FZ_CMD_BEGIN_TILE) + depth++; + else if (next->cmd == FZ_CMD_END_TILE) + { + depth--; + if (depth == 0) + return node; + } + (*progress)++; + node = next; + } + while (1); + + return NULL; +} + +void +fz_run_display_list(fz_display_list *list, fz_device *dev, const fz_matrix *top_ctm, const fz_rect *scissor, fz_cookie *cookie) +{ + fz_display_node *node; + fz_matrix ctm; + int clipped = 0; + int tiled = 0; + int progress = 0; + fz_context *ctx = dev->ctx; + + if (!scissor) + scissor = &fz_infinite_rect; + + if (cookie) + { + cookie->progress_max = list->len; + cookie->progress = 0; + } + + for (node = list->first; node; node = node->next) + { + int empty; + + fz_rect node_rect = node->rect; + fz_transform_rect(&node_rect, top_ctm); + + /* Check the cookie for aborting */ + if (cookie) + { + if (cookie->abort) + break; + cookie->progress = progress++; + } + + /* cull objects to draw using a quick visibility test */ + + if (tiled || + node->cmd == FZ_CMD_BEGIN_TILE || node->cmd == FZ_CMD_END_TILE || + node->cmd == FZ_CMD_BEGIN_PAGE || node->cmd == FZ_CMD_END_PAGE) + { + empty = 0; + } + else + { + fz_rect rect = node_rect; + fz_intersect_rect(&rect, scissor); + empty = fz_is_empty_rect(&rect); + } + + if (clipped || empty) + { + switch (node->cmd) + { + case FZ_CMD_CLIP_PATH: + case FZ_CMD_CLIP_STROKE_PATH: + case FZ_CMD_CLIP_STROKE_TEXT: + case FZ_CMD_CLIP_IMAGE_MASK: + case FZ_CMD_BEGIN_MASK: + case FZ_CMD_BEGIN_GROUP: + clipped++; + continue; + case FZ_CMD_CLIP_TEXT: + /* Accumulated text has no extra pops */ + if (node->flag != 2) + clipped++; + continue; + case FZ_CMD_POP_CLIP: + case FZ_CMD_END_GROUP: + if (!clipped) + goto visible; + clipped--; + continue; + case FZ_CMD_END_MASK: + if (!clipped) + goto visible; + continue; + default: + continue; + } + } + +visible: + fz_concat(&ctm, &node->ctm, top_ctm); + + + fz_try(ctx) + { + switch (node->cmd) + { + case FZ_CMD_BEGIN_PAGE: + fz_begin_page(dev, &node_rect, &ctm); + break; + case FZ_CMD_END_PAGE: + fz_end_page(dev); + break; + case FZ_CMD_FILL_PATH: + fz_fill_path(dev, node->item.path, node->flag, &ctm, + node->colorspace, node->color, node->alpha); + break; + case FZ_CMD_STROKE_PATH: + fz_stroke_path(dev, node->item.path, node->stroke, &ctm, + node->colorspace, node->color, node->alpha); + break; + case FZ_CMD_CLIP_PATH: + fz_clip_path(dev, node->item.path, &node_rect, node->flag, &ctm); + break; + case FZ_CMD_CLIP_STROKE_PATH: + fz_clip_stroke_path(dev, node->item.path, &node_rect, node->stroke, &ctm); + break; + case FZ_CMD_FILL_TEXT: + fz_fill_text(dev, node->item.text, &ctm, + node->colorspace, node->color, node->alpha); + break; + case FZ_CMD_STROKE_TEXT: + fz_stroke_text(dev, node->item.text, node->stroke, &ctm, + node->colorspace, node->color, node->alpha); + break; + case FZ_CMD_CLIP_TEXT: + fz_clip_text(dev, node->item.text, &ctm, node->flag); + break; + case FZ_CMD_CLIP_STROKE_TEXT: + fz_clip_stroke_text(dev, node->item.text, node->stroke, &ctm); + break; + case FZ_CMD_IGNORE_TEXT: + fz_ignore_text(dev, node->item.text, &ctm); + break; + case FZ_CMD_FILL_SHADE: + if ((dev->hints & FZ_IGNORE_SHADE) == 0) + fz_fill_shade(dev, node->item.shade, &ctm, node->alpha); + break; + case FZ_CMD_FILL_IMAGE: + if ((dev->hints & FZ_IGNORE_IMAGE) == 0) + fz_fill_image(dev, node->item.image, &ctm, node->alpha); + break; + case FZ_CMD_FILL_IMAGE_MASK: + if ((dev->hints & FZ_IGNORE_IMAGE) == 0) + fz_fill_image_mask(dev, node->item.image, &ctm, + node->colorspace, node->color, node->alpha); + break; + case FZ_CMD_CLIP_IMAGE_MASK: + if ((dev->hints & FZ_IGNORE_IMAGE) == 0) + fz_clip_image_mask(dev, node->item.image, &node_rect, &ctm); + break; + case FZ_CMD_POP_CLIP: + fz_pop_clip(dev); + break; + case FZ_CMD_BEGIN_MASK: + fz_begin_mask(dev, &node_rect, node->flag, node->colorspace, node->color); + break; + case FZ_CMD_END_MASK: + fz_end_mask(dev); + break; + case FZ_CMD_BEGIN_GROUP: + fz_begin_group(dev, &node_rect, + (node->flag & ISOLATED) != 0, (node->flag & KNOCKOUT) != 0, + node->item.blendmode, node->alpha); + break; + case FZ_CMD_END_GROUP: + fz_end_group(dev); + break; + case FZ_CMD_BEGIN_TILE: + { + int cached; + fz_rect tile_rect; + tiled++; + tile_rect.x0 = node->color[2]; + tile_rect.y0 = node->color[3]; + tile_rect.x1 = node->color[4]; + tile_rect.y1 = node->color[5]; + cached = fz_begin_tile_id(dev, &node->rect, &tile_rect, node->color[0], node->color[1], &ctm, node->flag); + if (cached) + node = skip_to_end_tile(node, &progress); + break; + } + case FZ_CMD_END_TILE: + tiled--; + fz_end_tile(dev); + break; + } + } + fz_catch(ctx) + { + /* Swallow the error */ + if (cookie) + cookie->errors++; + fz_warn(ctx, "Ignoring error during interpretation"); + } + } +} diff --git a/source/fitz/load-jpeg.c b/source/fitz/load-jpeg.c new file mode 100644 index 00000000..b968c0cf --- /dev/null +++ b/source/fitz/load-jpeg.c @@ -0,0 +1,111 @@ +#include "mupdf/fitz.h" + +#include <jpeglib.h> + +static void error_exit(j_common_ptr cinfo) +{ + char msg[JMSG_LENGTH_MAX]; + fz_context *ctx = (fz_context *)cinfo->client_data; + + cinfo->err->format_message(cinfo, msg); + fz_throw(ctx, FZ_ERROR_GENERIC, "jpeg error: %s", msg); +} + +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) + { + size_t skip = (size_t)num_bytes; /* size_t may be 64bit */ + if (skip > src->bytes_in_buffer) + skip = (size_t)src->bytes_in_buffer; + src->next_input_byte += skip; + src->bytes_in_buffer -= skip; + } +} + +void +fz_load_jpeg_info(fz_context *ctx, unsigned char *rbuf, int rlen, int *xp, int *yp, int *xresp, int *yresp, fz_colorspace **cspacep) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr err; + struct jpeg_source_mgr src; + + fz_try(ctx) + { + cinfo.client_data = ctx; + cinfo.err = jpeg_std_error(&err); + err.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); + + if (cinfo.num_components == 1) + *cspacep = fz_device_gray(ctx); + else if (cinfo.num_components == 3) + *cspacep = fz_device_rgb(ctx); + else if (cinfo.num_components == 4) + *cspacep = fz_device_cmyk(ctx); + else + fz_throw(ctx, FZ_ERROR_GENERIC, "bad number of components in jpeg: %d", cinfo.num_components); + + *xp = cinfo.image_width; + *yp = cinfo.image_height; + + if (cinfo.density_unit == 1) + { + *xresp = cinfo.X_density; + *yresp = cinfo.Y_density; + } + else if (cinfo.density_unit == 2) + { + *xresp = cinfo.X_density * 254 / 100; + *yresp = cinfo.Y_density * 254 / 100; + } + else + { + *xresp = 0; + *yresp = 0; + } + + if (*xresp <= 0) *xresp = 72; + if (*yresp <= 0) *yresp = 72; + } + fz_always(ctx) + { + jpeg_destroy_decompress(&cinfo); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} diff --git a/source/fitz/load-jpx.c b/source/fitz/load-jpx.c new file mode 100644 index 00000000..cd41277d --- /dev/null +++ b/source/fitz/load-jpx.c @@ -0,0 +1,253 @@ +#include "mupdf/fitz.h" + +/* Without the definition of OPJ_STATIC, compilation fails on windows + * due to the use of __stdcall. We believe it is required on some + * linux toolchains too. */ +#define OPJ_STATIC +#ifndef _MSC_VER +#define OPJ_HAVE_STDINT_H +#endif + +#include <openjpeg.h> + +static void fz_opj_error_callback(const char *msg, void *client_data) +{ + fz_context *ctx = (fz_context *)client_data; + fz_warn(ctx, "openjpeg error: %s", msg); +} + +static void fz_opj_warning_callback(const char *msg, void *client_data) +{ + fz_context *ctx = (fz_context *)client_data; + fz_warn(ctx, "openjpeg warning: %s", msg); +} + +static void fz_opj_info_callback(const char *msg, void *client_data) +{ + /* fz_warn("openjpeg info: %s", msg); */ +} + +typedef struct stream_block_s +{ + unsigned char *data; + int size; + int pos; +} stream_block; + +OPJ_SIZE_T stream_read(void * p_buffer, OPJ_SIZE_T p_nb_bytes, void * p_user_data) +{ + stream_block *sb = (stream_block *)p_user_data; + int len; + + len = sb->size - sb->pos; + if (len < 0) + len = 0; + if (len == 0) + return (OPJ_SIZE_T)-1; /* End of file! */ + if ((OPJ_SIZE_T)len > p_nb_bytes) + len = p_nb_bytes; + memcpy(p_buffer, sb->data + sb->pos, len); + sb->pos += len; + return len; +} + +OPJ_OFF_T stream_skip(OPJ_OFF_T skip, void * p_user_data) +{ + stream_block *sb = (stream_block *)p_user_data; + + if (skip > sb->size - sb->pos) + skip = sb->size - sb->pos; + sb->pos += skip; + return sb->pos; +} + +OPJ_BOOL stream_seek(OPJ_OFF_T seek_pos, void * p_user_data) +{ + stream_block *sb = (stream_block *)p_user_data; + + if (seek_pos > sb->size) + return OPJ_FALSE; + sb->pos = seek_pos; + return OPJ_TRUE; +} + +fz_pixmap * +fz_load_jpx(fz_context *ctx, unsigned char *data, int size, fz_colorspace *defcs, int indexed) +{ + fz_pixmap *img; + fz_colorspace *origcs; + opj_dparameters_t params; + opj_codec_t *codec; + opj_image_t *jpx; + opj_stream_t *stream; + fz_colorspace *colorspace; + unsigned char *p; + OPJ_CODEC_FORMAT format; + int a, n, w, h, depth, sgnd; + int x, y, k, v; + stream_block sb; + + if (size < 2) + fz_throw(ctx, FZ_ERROR_GENERIC, "not enough data to determine image format"); + + /* Check for SOC marker -- if found we have a bare J2K stream */ + if (data[0] == 0xFF && data[1] == 0x4F) + format = OPJ_CODEC_J2K; + else + format = OPJ_CODEC_JP2; + + opj_set_default_decoder_parameters(¶ms); + if (indexed) + params.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG; + + codec = opj_create_decompress(format); + opj_set_info_handler(codec, fz_opj_info_callback, ctx); + opj_set_warning_handler(codec, fz_opj_warning_callback, ctx); + opj_set_error_handler(codec, fz_opj_error_callback, ctx); + if (!opj_setup_decoder(codec, ¶ms)) + { + fz_throw(ctx, FZ_ERROR_GENERIC, "j2k decode failed"); + } + + stream = opj_stream_default_create(OPJ_TRUE); + sb.data = data; + sb.pos = 0; + sb.size = size; + + opj_stream_set_read_function(stream, stream_read); + opj_stream_set_skip_function(stream, stream_skip); + opj_stream_set_seek_function(stream, stream_seek); + opj_stream_set_user_data(stream, &sb); + /* Set the length to avoid an assert */ + opj_stream_set_user_data_length(stream, size); + + if (!opj_read_header(stream, codec, &jpx)) + { + opj_stream_destroy(stream); + opj_destroy_codec(codec); + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to read JPX header"); + } + + if (!opj_decode(codec, stream, jpx)) + { + opj_stream_destroy(stream); + opj_destroy_codec(codec); + opj_image_destroy(jpx); + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to decode JPX image"); + } + + opj_stream_destroy(stream); + opj_destroy_codec(codec); + + /* jpx should never be NULL here, but check anyway */ + if (!jpx) + fz_throw(ctx, FZ_ERROR_GENERIC, "opj_decode failed"); + + for (k = 1; k < (int)jpx->numcomps; k++) + { + if (jpx->comps[k].w != jpx->comps[0].w) + { + opj_image_destroy(jpx); + fz_throw(ctx, FZ_ERROR_GENERIC, "image components have different width"); + } + if (jpx->comps[k].h != jpx->comps[0].h) + { + opj_image_destroy(jpx); + fz_throw(ctx, FZ_ERROR_GENERIC, "image components have different height"); + } + if (jpx->comps[k].prec != jpx->comps[0].prec) + { + opj_image_destroy(jpx); + fz_throw(ctx, FZ_ERROR_GENERIC, "image components have different precision"); + } + } + + n = jpx->numcomps; + w = jpx->comps[0].w; + h = jpx->comps[0].h; + depth = jpx->comps[0].prec; + sgnd = jpx->comps[0].sgnd; + + if (jpx->color_space == OPJ_CLRSPC_SRGB && n == 4) { n = 3; a = 1; } + else if (jpx->color_space == OPJ_CLRSPC_SYCC && n == 4) { n = 3; a = 1; } + else if (n == 2) { n = 1; a = 1; } + else if (n > 4) { n = 4; a = 1; } + else { a = 0; } + + origcs = defcs; + if (defcs) + { + if (defcs->n == n) + { + colorspace = defcs; + } + else + { + fz_warn(ctx, "jpx file and dict colorspaces do not match"); + defcs = NULL; + } + } + + if (!defcs) + { + switch (n) + { + case 1: colorspace = fz_device_gray(ctx); break; + case 3: colorspace = fz_device_rgb(ctx); break; + case 4: colorspace = fz_device_cmyk(ctx); break; + } + } + + fz_try(ctx) + { + img = fz_new_pixmap(ctx, colorspace, w, h); + } + fz_catch(ctx) + { + opj_image_destroy(jpx); + fz_rethrow_message(ctx, "out of memory loading jpx"); + } + + p = img->samples; + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + for (k = 0; k < n + a; k++) + { + v = jpx->comps[k].data[y * w + x]; + if (sgnd) + v = v + (1 << (depth - 1)); + if (depth > 8) + v = v >> (depth - 8); + *p++ = v; + } + if (!a) + *p++ = 255; + } + } + + opj_image_destroy(jpx); + + if (a) + { + if (n == 4) + { + fz_pixmap *tmp = fz_new_pixmap(ctx, fz_device_rgb(ctx), w, h); + fz_convert_pixmap(ctx, tmp, img); + fz_drop_pixmap(ctx, img); + img = tmp; + } + fz_premultiply_pixmap(ctx, img); + } + + if (origcs != defcs) + { + fz_pixmap *tmp = fz_new_pixmap(ctx, origcs, w, h); + fz_convert_pixmap(ctx, tmp, img); + fz_drop_pixmap(ctx, img); + img = tmp; + } + + return img; +} diff --git a/source/fitz/load-png.c b/source/fitz/load-png.c new file mode 100644 index 00000000..ad22e128 --- /dev/null +++ b/source/fitz/load-png.c @@ -0,0 +1,599 @@ +#include "mupdf/fitz.h" + +#include <zlib.h> + +struct info +{ + fz_context *ctx; + unsigned int width, height, depth, n; + int interlace, indexed; + unsigned int size; + unsigned char *samples; + unsigned char palette[256*4]; + int transparency; + int trns[3]; + int xres, yres; +}; + +static inline unsigned int getuint(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_malloc_array(opaque, items, size); +} + +static void zfree(void *opaque, void *address) +{ + fz_free(opaque, 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, unsigned int width, unsigned int height, unsigned int n, unsigned int depth) +{ + unsigned int stride = (width * n * depth + 7) / 8; + unsigned int bpp = (n * depth + 7) / 8; + unsigned int i, row; + + for (row = 0; row < height; row ++) + { + unsigned char *src = samples + (unsigned int)((stride + 1) * row); + unsigned char *dst = samples + (unsigned int)(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 unsigned int adam7_ix[7] = { 0, 4, 0, 2, 0, 1, 0 }; +static const unsigned int adam7_dx[7] = { 8, 8, 4, 4, 2, 2, 1 }; +static const unsigned int adam7_iy[7] = { 0, 0, 4, 0, 2, 0, 1 }; +static const unsigned int adam7_dy[7] = { 8, 8, 8, 4, 4, 2, 2 }; + +static void +png_deinterlace_passes(struct info *info, unsigned int *w, unsigned int *h, unsigned 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, unsigned int *passw, unsigned int *passh, unsigned int *passofs) +{ + unsigned int n = info->n; + unsigned int depth = info->depth; + unsigned int stride = (info->width * n * depth + 7) / 8; + unsigned char *output; + unsigned int p, x, y, k; + + output = fz_malloc_array(info->ctx, info->height, stride); + + for (p = 0; p < 7; p++) + { + unsigned char *sp = info->samples + (passofs[p]); + unsigned int w = passw[p]; + unsigned 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->ctx, info->samples); + info->samples = output; +} + +static void +png_read_ihdr(struct info *info, unsigned char *p, unsigned int size) +{ + int color, compression, filter; + + if (size != 13) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "IHDR chunk is the wrong size"); + + info->width = getuint(p + 0); + info->height = getuint(p + 4); + info->depth = p[8]; + + color = p[9]; + compression = p[10]; + filter = p[11]; + info->interlace = p[12]; + + if (info->width <= 0) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "image width must be > 0"); + if (info->height <= 0) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "image height must be > 0"); + + if (info->depth != 1 && info->depth != 2 && info->depth != 4 && + info->depth != 8 && info->depth != 16) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "image bit depth must be one of 1, 2, 4, 8, 16"); + if (color == 2 && info->depth < 8) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "illegal bit depth for truecolor"); + if (color == 3 && info->depth > 8) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "illegal bit depth for indexed"); + if (color == 4 && info->depth < 8) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "illegal bit depth for grayscale with alpha"); + if (color == 6 && info->depth < 8) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "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 + fz_throw(info->ctx, FZ_ERROR_GENERIC, "unknown color type"); + + if (compression != 0) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "unknown compression method"); + if (filter != 0) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "unknown filter method"); + if (info->interlace != 0 && info->interlace != 1) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "interlace method not supported"); +} + +static void +png_read_plte(struct info *info, unsigned char *p, unsigned int size) +{ + int n = size / 3; + int i; + + if (n > 256) + { + fz_warn(info->ctx, "too many samples in palette"); + n = 256; + } + + 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]; + } + + /* Fill in any missing palette entries */ + for (; i < 256; i++) + { + info->palette[i * 4] = 0; + info->palette[i * 4 + 1] = 0; + info->palette[i * 4 + 2] = 0; + } +} + +static void +png_read_trns(struct info *info, unsigned char *p, unsigned int size) +{ + unsigned int i; + + info->transparency = 1; + + if (info->indexed) + { + if (size > 256) + { + fz_warn(info->ctx, "too many samples in transparency table"); + size = 256; + } + for (i = 0; i < size; i++) + info->palette[i * 4 + 3] = p[i]; + /* Fill in any missing entries */ + for (; i < 256; i++) + info->palette[i * 4 + 3] = 255; + } + else + { + if (size != info->n * 2) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "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); + } +} + +static void +png_read_idat(struct info *info, unsigned char *p, unsigned 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) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "zlib error: %s", stm->msg); + if (stm->avail_in != 0) + { + if (stm->avail_out == 0) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "ran out of output before input"); + fz_throw(info->ctx, FZ_ERROR_GENERIC, "inflate did not consume buffer (%d remaining)", stm->avail_in); + } +} + +static void +png_read_phys(struct info *info, unsigned char *p, unsigned int size) +{ + if (size != 9) + fz_throw(info->ctx, FZ_ERROR_GENERIC, "pHYs chunk is the wrong size"); + if (p[8] == 1) + { + info->xres = getuint(p) * 254 / 10000; + info->yres = getuint(p + 4) * 254 / 10000; + } +} + +static void +png_read_image(fz_context *ctx, struct info *info, unsigned char *p, unsigned int total) +{ + unsigned int passw[7], passh[7], passofs[8]; + unsigned int code, size; + z_stream stm; + + memset(info, 0, sizeof (struct info)); + info->ctx = ctx; + memset(info->palette, 255, sizeof(info->palette)); + info->xres = 96; + info->yres = 96; + + /* Read signature */ + + if (total < 8 + 12 || memcmp(p, png_signature, 8)) + fz_throw(ctx, FZ_ERROR_GENERIC, "not a png image (wrong signature)"); + + p += 8; + total -= 8; + + /* Read IHDR chunk (must come first) */ + + size = getuint(p); + if (total < 12 || size > total - 12) + fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of data in png image"); + + if (!memcmp(p + 4, "IHDR", 4)) + png_read_ihdr(info, p + 8, size); + else + fz_throw(ctx, FZ_ERROR_GENERIC, "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(ctx, info->size); + + stm.zalloc = zalloc; + stm.zfree = zfree; + stm.opaque = ctx; + + stm.next_out = info->samples; + stm.avail_out = info->size; + + code = inflateInit(&stm); + if (code != Z_OK) + { + fz_free(ctx, info->samples); + fz_throw(ctx, FZ_ERROR_GENERIC, "zlib error: %s", stm.msg); + } + + fz_try(ctx) + { + /* Read remaining chunks until IEND */ + while (total > 8) + { + size = getuint(p); + + if (total < 12 || size > total - 12) + fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of data in png image"); + + if (!memcmp(p + 4, "PLTE", 4)) + png_read_plte(info, p + 8, size); + if (!memcmp(p + 4, "tRNS", 4)) + png_read_trns(info, p + 8, size); + if (!memcmp(p + 4, "pHYs", 4)) + png_read_phys(info, p + 8, size); + if (!memcmp(p + 4, "IDAT", 4)) + png_read_idat(info, p + 8, size, &stm); + if (!memcmp(p + 4, "IEND", 4)) + break; + + p += size + 12; + total -= size + 12; + } + } + fz_catch(ctx) + { + inflateEnd(&stm); + fz_free(ctx, info->samples); + fz_rethrow(ctx); + } + + code = inflateEnd(&stm); + if (code != Z_OK) + { + fz_free(ctx, info->samples); + fz_throw(ctx, FZ_ERROR_GENERIC, "zlib error: %s", stm.msg); + } + + /* Apply prediction filter and deinterlacing */ + fz_try(ctx) + { + if (!info->interlace) + png_predict(info->samples, info->width, info->height, info->n, info->depth); + else + png_deinterlace(info, passw, passh, passofs); + } + fz_catch(ctx) + { + fz_free(ctx, info->samples); + fz_rethrow(ctx); + } +} + +static fz_pixmap * +png_expand_palette(fz_context *ctx, struct info *info, fz_pixmap *src) +{ + fz_pixmap *dst = fz_new_pixmap(ctx, fz_device_rgb(ctx), src->w, src->h); + unsigned char *sp = src->samples; + unsigned char *dp = dst->samples; + unsigned int x, y; + + dst->xres = src->xres; + dst->yres = src->yres; + + for (y = info->height; y > 0; y--) + { + for (x = info->width; x > 0; 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(info->ctx, src); + return dst; +} + +static void +png_mask_transparency(struct info *info, fz_pixmap *dst) +{ + unsigned int stride = (info->width * info->n * info->depth + 7) / 8; + unsigned int depth = info->depth; + unsigned int n = info->n; + unsigned int x, y, k, t; + + for (y = 0; y < info->height; y++) + { + unsigned char *sp = info->samples + (unsigned int)(y * stride); + unsigned char *dp = dst->samples + (unsigned int)(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; + } + } +} + +fz_pixmap * +fz_load_png(fz_context *ctx, unsigned char *p, int total) +{ + fz_pixmap *image; + fz_colorspace *colorspace; + struct info png; + int stride; + + png_read_image(ctx, &png, p, total); + + if (png.n == 3 || png.n == 4) + colorspace = fz_device_rgb(ctx); + else + colorspace = fz_device_gray(ctx); + + stride = (png.width * png.n * png.depth + 7) / 8; + + fz_try(ctx) + { + image = fz_new_pixmap(ctx, colorspace, png.width, png.height); + } + fz_catch(ctx) + { + fz_free(png.ctx, png.samples); + fz_rethrow_message(ctx, "out of memory loading png"); + } + + 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(ctx, &png, image); + else if (png.transparency) + png_mask_transparency(&png, image); + + if (png.transparency || png.n == 2 || png.n == 4) + fz_premultiply_pixmap(png.ctx, image); + + fz_free(png.ctx, png.samples); + + return image; +} + +void +fz_load_png_info(fz_context *ctx, unsigned char *p, int total, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep) +{ + struct info png; + + png_read_image(ctx, &png, p, total); + + if (png.n == 3 || png.n == 4) + *cspacep = fz_device_rgb(ctx); + else + *cspacep = fz_device_gray(ctx); + + *wp = png.width; + *hp = png.height; + *xresp = png.xres; + *yresp = png.xres; + fz_free(png.ctx, png.samples); +} diff --git a/source/fitz/load-tiff.c b/source/fitz/load-tiff.c new file mode 100644 index 00000000..5806bc00 --- /dev/null +++ b/source/fitz/load-tiff.c @@ -0,0 +1,867 @@ +#include "mupdf/fitz.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 +{ + fz_context *ctx; + + /* "file" */ + unsigned char *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; + + unsigned stripoffsetslen; + unsigned stripbytecountslen; + unsigned colormaplen; + + /* 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]; + + unsigned char *jpegtables; /* point into "file" buffer */ + unsigned jpegtableslen; + + unsigned char *profile; + int profilesize; + + /* decoded data */ + fz_colorspace *colorspace; + unsigned char *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 unsigned char 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 void +fz_decode_tiff_uncompressed(struct tiff *tiff, fz_stream *stm, unsigned char *wp, int wlen) +{ + fz_read(stm, wp, wlen); + fz_close(stm); +} + +static void +fz_decode_tiff_packbits(struct tiff *tiff, fz_stream *chain, unsigned char *wp, int wlen) +{ + fz_stream *stm = fz_open_rld(chain); + fz_read(stm, wp, wlen); + fz_close(stm); +} + +static void +fz_decode_tiff_lzw(struct tiff *tiff, fz_stream *chain, unsigned char *wp, int wlen) +{ + fz_stream *stm = fz_open_lzwd(chain, 1); + fz_read(stm, wp, wlen); + fz_close(stm); +} + +static void +fz_decode_tiff_flate(struct tiff *tiff, fz_stream *chain, unsigned char *wp, int wlen) +{ + fz_stream *stm = fz_open_flated(chain); + fz_read(stm, wp, wlen); + fz_close(stm); +} + +static void +fz_decode_tiff_fax(struct tiff *tiff, int comp, fz_stream *chain, unsigned char *wp, int wlen) +{ + fz_stream *stm; + int black_is_1 = tiff->photometric == 0; + int k = comp == 4 ? -1 : 0; + int encoded_byte_align = comp == 2; + stm = fz_open_faxd(chain, + k, 0, encoded_byte_align, + tiff->imagewidth, tiff->imagelength, 0, black_is_1); + fz_read(stm, wp, wlen); + fz_close(stm); +} + +static void +fz_decode_tiff_jpeg(struct tiff *tiff, fz_stream *chain, unsigned char *wp, int wlen) +{ + fz_stream *stm = fz_open_dctd(chain, -1); + fz_read(stm, wp, wlen); + fz_close(stm); +} + +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 void +fz_unpredict_tiff(unsigned char *line, int width, int comps, int bits) +{ + unsigned char 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 +fz_invert_tiff(unsigned char *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 void +fz_expand_tiff_colormap(struct tiff *tiff) +{ + int maxval = 1 << tiff->bitspersample; + unsigned char *samples; + unsigned char *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) + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "invalid number of samples for RGBPal"); + + if (tiff->bitspersample != 4 && tiff->bitspersample != 8) + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "invalid number of bits for RGBPal"); + + if (tiff->colormaplen < (unsigned)maxval * 3) + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "insufficient colormap data"); + + stride = tiff->imagewidth * (tiff->samplesperpixel + 2); + + samples = fz_malloc(tiff->ctx, stride * tiff->imagelength); + + for (y = 0; y < tiff->imagelength; y++) + { + src = tiff->samples + (unsigned int)(tiff->stride * y); + dst = samples + (unsigned int)(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; + fz_free(tiff->ctx, tiff->samples); + tiff->samples = samples; +} + +static void +fz_decode_tiff_strips(struct tiff *tiff) +{ + fz_stream *stm; + + /* switch on compression to create a filter */ + /* feed each strip to the filter */ + /* read out the data and pack the samples into a pixmap */ + + /* 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 */ + + unsigned char *wp; + unsigned row; + unsigned strip; + unsigned i; + + if (!tiff->rowsperstrip || !tiff->stripoffsets || !tiff->stripbytecounts) + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "no image data in tiff; maybe it is tiled"); + + if (tiff->stripoffsetslen < (tiff->imagelength - 1) / tiff->rowsperstrip + 1 || + tiff->stripbytecountslen < (tiff->imagelength - 1) / tiff->rowsperstrip + 1) + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "insufficient strip offset data"); + + if (tiff->planar != 1) + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "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(tiff->ctx); + break; + case 1: /* BlackIsZero */ + tiff->colorspace = fz_device_gray(tiff->ctx); + break; + case 2: /* RGB */ + tiff->colorspace = fz_device_rgb(tiff->ctx); + break; + case 3: /* RGBPal */ + tiff->colorspace = fz_device_rgb(tiff->ctx); + break; + case 5: /* CMYK */ + tiff->colorspace = fz_device_cmyk(tiff->ctx); + break; + case 6: /* YCbCr */ + /* it's probably a jpeg ... we let jpeg convert to rgb */ + tiff->colorspace = fz_device_rgb(tiff->ctx); + break; + default: + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "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_malloc_array(tiff->ctx, 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; + unsigned char *rp = tiff->bp + offset; + + if (wp + wlen > tiff->samples + (unsigned int)(tiff->stride * tiff->imagelength)) + wlen = tiff->samples + (unsigned int)(tiff->stride * tiff->imagelength) - wp; + + if (rp + rlen > tiff->ep) + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "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(tiff->ctx, rp, rlen); + + switch (tiff->compression) + { + case 1: + fz_decode_tiff_uncompressed(tiff, stm, wp, wlen); + break; + case 2: + fz_decode_tiff_fax(tiff, 2, stm, wp, wlen); + break; + case 3: + fz_decode_tiff_fax(tiff, 3, stm, wp, wlen); + break; + case 4: + fz_decode_tiff_fax(tiff, 4, stm, wp, wlen); + break; + case 5: + fz_decode_tiff_lzw(tiff, stm, wp, wlen); + break; + case 6: + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "deprecated JPEG in TIFF compression not supported"); + break; + case 7: + fz_decode_tiff_jpeg(tiff, stm, wp, wlen); + break; + case 8: + fz_decode_tiff_flate(tiff, stm, wp, wlen); + break; + case 32773: + fz_decode_tiff_packbits(tiff, stm, wp, wlen); + break; + default: + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "unknown TIFF compression: %d", tiff->compression); + } + + /* 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) + { + unsigned char *p = tiff->samples; + for (i = 0; i < tiff->imagelength; i++) + { + fz_unpredict_tiff(p, tiff->imagewidth, tiff->samplesperpixel, tiff->bitspersample); + p += tiff->stride; + } + } + + /* RGBPal */ + if (tiff->photometric == 3 && tiff->colormap) + fz_expand_tiff_colormap(tiff); + + /* WhiteIsZero .. invert */ + if (tiff->photometric == 0) + { + unsigned char *p = tiff->samples; + for (i = 0; i < tiff->imagelength; i++) + { + fz_invert_tiff(p, tiff->imagewidth, tiff->samplesperpixel, tiff->bitspersample, tiff->extrasamples); + p += tiff->stride; + } + } + + /* Premultiplied transparency */ + if (tiff->extrasamples == 1) + { + /* In GhostXPS we undo the premultiplication here; muxps holds + * all our images premultiplied by default, so nothing to do. + */ + } + + /* Non-premultiplied transparency */ + if (tiff->extrasamples == 2) + { + /* Premultiplied files are corrected for elsewhere */ + } +} + +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 +fz_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 +fz_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 void +fz_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: + fz_read_tiff_tag_value(&tiff->subfiletype, tiff, type, value, 1); + break; + case ImageWidth: + fz_read_tiff_tag_value(&tiff->imagewidth, tiff, type, value, 1); + break; + case ImageLength: + fz_read_tiff_tag_value(&tiff->imagelength, tiff, type, value, 1); + break; + case BitsPerSample: + fz_read_tiff_tag_value(&tiff->bitspersample, tiff, type, value, 1); + break; + case Compression: + fz_read_tiff_tag_value(&tiff->compression, tiff, type, value, 1); + break; + case PhotometricInterpretation: + fz_read_tiff_tag_value(&tiff->photometric, tiff, type, value, 1); + break; + case FillOrder: + fz_read_tiff_tag_value(&tiff->fillorder, tiff, type, value, 1); + break; + case SamplesPerPixel: + fz_read_tiff_tag_value(&tiff->samplesperpixel, tiff, type, value, 1); + break; + case RowsPerStrip: + fz_read_tiff_tag_value(&tiff->rowsperstrip, tiff, type, value, 1); + break; + case XResolution: + fz_read_tiff_tag_value(&tiff->xresolution, tiff, type, value, 1); + break; + case YResolution: + fz_read_tiff_tag_value(&tiff->yresolution, tiff, type, value, 1); + break; + case PlanarConfiguration: + fz_read_tiff_tag_value(&tiff->planar, tiff, type, value, 1); + break; + case T4Options: + fz_read_tiff_tag_value(&tiff->g3opts, tiff, type, value, 1); + break; + case T6Options: + fz_read_tiff_tag_value(&tiff->g4opts, tiff, type, value, 1); + break; + case Predictor: + fz_read_tiff_tag_value(&tiff->predictor, tiff, type, value, 1); + break; + case ResolutionUnit: + fz_read_tiff_tag_value(&tiff->resolutionunit, tiff, type, value, 1); + break; + case YCbCrSubSampling: + fz_read_tiff_tag_value(tiff->ycbcrsubsamp, tiff, type, value, 2); + break; + case ExtraSamples: + fz_read_tiff_tag_value(&tiff->extrasamples, tiff, type, value, 1); + break; + + case ICCProfile: + tiff->profile = fz_malloc(tiff->ctx, count); + /* ICC profile data type is set to UNDEFINED. + * TBYTE reading not correct in fz_read_tiff_tag_value */ + fz_read_tiff_bytes(tiff->profile, tiff, value, count); + tiff->profilesize = count; + break; + + case JPEGTables: + fz_warn(tiff->ctx, "jpeg tables in tiff not implemented"); + tiff->jpegtables = tiff->bp + value; + tiff->jpegtableslen = count; + break; + + case StripOffsets: + tiff->stripoffsets = fz_malloc_array(tiff->ctx, count, sizeof(unsigned)); + fz_read_tiff_tag_value(tiff->stripoffsets, tiff, type, value, count); + tiff->stripoffsetslen = count; + break; + + case StripByteCounts: + tiff->stripbytecounts = fz_malloc_array(tiff->ctx, count, sizeof(unsigned)); + fz_read_tiff_tag_value(tiff->stripbytecounts, tiff, type, value, count); + tiff->stripbytecountslen = count; + break; + + case ColorMap: + tiff->colormap = fz_malloc_array(tiff->ctx, count, sizeof(unsigned)); + fz_read_tiff_tag_value(tiff->colormap, tiff, type, value, count); + tiff->colormaplen = count; + break; + + case TileWidth: + case TileLength: + case TileOffsets: + case TileByteCounts: + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "tiled tiffs not supported"); + + default: + /* printf("unknown tag: %d t=%d n=%d\n", tag, type, count); */ + break; + } +} + +static void +fz_swap_tiff_byte_order(unsigned char *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 void +fz_decode_tiff_header(fz_context *ctx, struct tiff *tiff, unsigned char *buf, int len) +{ + unsigned version; + unsigned offset; + unsigned count; + unsigned i; + + memset(tiff, 0, sizeof(struct tiff)); + tiff->ctx = ctx; + 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) + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "not a TIFF file, wrong magic marker"); + + /* check version */ + version = readshort(tiff); + if (version != 42) + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "not a TIFF file, wrong version marker"); + + /* get offset of IFD */ + offset = readlong(tiff); + + /* + * Read IFD + */ + + tiff->rp = tiff->bp + offset; + + if (tiff->rp < tiff->bp || tiff->rp > tiff->ep) + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "invalid IFD offset %u", offset); + + count = readshort(tiff); + + if (count * 12 > (unsigned)(tiff->ep - tiff->rp)) + fz_throw(tiff->ctx, FZ_ERROR_GENERIC, "overlarge IFD entry count %u", count); + + offset += 2; + for (i = 0; i < count; i++) + { + fz_read_tiff_tag(tiff, offset); + offset += 12; + } +} + +fz_pixmap * +fz_load_tiff(fz_context *ctx, unsigned char *buf, int len) +{ + fz_pixmap *image; + struct tiff tiff; + + fz_try(ctx) + { + fz_decode_tiff_header(ctx, &tiff, buf, len); + + /* Decode the image strips */ + + if (tiff.rowsperstrip > tiff.imagelength) + tiff.rowsperstrip = tiff.imagelength; + + fz_decode_tiff_strips(&tiff); + + /* Byte swap 16-bit images to big endian if necessary */ + if (tiff.bitspersample == 16) + if (tiff.order == TII) + fz_swap_tiff_byte_order(tiff.samples, tiff.imagewidth * tiff.imagelength * tiff.samplesperpixel); + + /* Expand into fz_pixmap struct */ + image = fz_new_pixmap(tiff.ctx, tiff.colorspace, tiff.imagewidth, tiff.imagelength); + 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(tiff.ctx, fz_device_rgb(ctx), image->w, image->h); + fz_convert_pixmap(tiff.ctx, rgb, image); + rgb->xres = image->xres; + rgb->yres = image->yres; + fz_drop_pixmap(ctx, image); + image = rgb; + } + fz_premultiply_pixmap(ctx, image); + } + } + fz_always(ctx) + { + /* Clean up scratch memory */ + if (tiff.colormap) fz_free(ctx, tiff.colormap); + if (tiff.stripoffsets) fz_free(ctx, tiff.stripoffsets); + if (tiff.stripbytecounts) fz_free(ctx, tiff.stripbytecounts); + if (tiff.samples) fz_free(ctx, tiff.samples); + if (tiff.profile) fz_free(ctx, tiff.profile); + } + fz_catch(ctx) + { + fz_rethrow_message(ctx, "out of memory loading tiff"); + } + + return image; +} + +void +fz_load_tiff_info(fz_context *ctx, unsigned char *buf, int len, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep) +{ + struct tiff tiff; + + fz_try(ctx) + { + fz_decode_tiff_header(ctx, &tiff, buf, len); + + *wp = tiff.imagewidth; + *hp = tiff.imagelength; + *xresp = tiff.xresolution; + *yresp = tiff.yresolution; + *cspacep = tiff.colorspace; + } + fz_always(ctx) + { + /* Clean up scratch memory */ + if (tiff.colormap) fz_free(ctx, tiff.colormap); + if (tiff.stripoffsets) fz_free(ctx, tiff.stripoffsets); + if (tiff.stripbytecounts) fz_free(ctx, tiff.stripbytecounts); + if (tiff.samples) fz_free(ctx, tiff.samples); + if (tiff.profile) fz_free(ctx, tiff.profile); + } + fz_catch(ctx) + { + fz_rethrow_message(ctx, "out of memory loading tiff"); + } +} diff --git a/source/fitz/memento.c b/source/fitz/memento.c new file mode 100644 index 00000000..6a159f10 --- /dev/null +++ b/source/fitz/memento.c @@ -0,0 +1,1535 @@ +/* Copyright (C) 2001-2013 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* Inspired by Fortify by Simon P Bullen. */ + +/* Set the following if you're only looking for leaks, not memory overwrites + * to speed the operation */ +/* #define MEMENTO_LEAKONLY */ + +/* Don't keep blocks around if they'd mean losing more than a quarter of + * the freelist. */ +#define MEMENTO_FREELIST_MAX_SINGLE_BLOCK (MEMENTO_FREELIST_MAX/4) + +#define COMPILING_MEMENTO_C + +/* We have some GS specific tweaks; more for the GS build environment than + * anything else. */ +#undef MEMENTO_GS_HACKS + +#ifdef MEMENTO_GS_HACKS +/* For GS we include malloc_.h. Anyone else would just include memento.h */ +#include "malloc_.h" +#ifdef __MACH__ +#include <string.h> +#else +#ifndef memset +void *memset(void *,int,size_t); +#endif +#endif +int atexit(void (*)(void)); +#else +#include "mupdf/memento.h" +#include <stdio.h> +#include <stdlib.h> +#endif + +#ifdef MEMENTO_ANDROID +#include <android/log.h> + +static int +android_fprintf(FILE *file, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + __android_log_vprint(ANDROID_LOG_ERROR,"memento", fmt, args); + va_end(args); +} + +#define fprintf android_fprintf +#define MEMENTO_STACKTRACE_METHOD 0 +#endif + +#ifndef MEMENTO_STACKTRACE_METHOD +#ifdef __GNUC__ +#define MEMENTO_STACKTRACE_METHOD 1 +#endif +#endif + +#if defined(__linux__) +#define MEMENTO_HAS_FORK +#elif defined(__APPLE__) && defined(__MACH__) +#define MEMENTO_HAS_FORK +#endif + +/* Define the underlying allocators, just in case */ +void *MEMENTO_UNDERLYING_MALLOC(size_t); +void MEMENTO_UNDERLYING_FREE(void *); +void *MEMENTO_UNDERLYING_REALLOC(void *,size_t); +void *MEMENTO_UNDERLYING_CALLOC(size_t,size_t); + +/* And some other standard functions we use. We don't include the header + * files, just in case they pull in unexpected others. */ +int atoi(const char *); +char *getenv(const char *); + +/* How far to search for pointers in each block when calculating nestings */ +/* mupdf needs at least 34000ish (sizeof(fz_shade))/ */ +#define MEMENTO_PTRSEARCH 65536 + +#ifndef MEMENTO_MAXPATTERN +#define MEMENTO_MAXPATTERN 0 +#endif + +#ifdef MEMENTO + +#ifdef MEMENTO_GS_HACKS +#include "valgrind.h" +#else +#ifdef HAVE_VALGRIND +#include "valgrind/memcheck.h" +#else +#define VALGRIND_MAKE_MEM_NOACCESS(p,s) do { } while (0==1) +#define VALGRIND_MAKE_MEM_UNDEFINED(p,s) do { } while (0==1) +#define VALGRIND_MAKE_MEM_DEFINED(p,s) do { } while (0==1) +#endif +#endif + +enum { + Memento_PreSize = 16, + Memento_PostSize = 16 +}; + +enum { + Memento_Flag_OldBlock = 1, + Memento_Flag_HasParent = 2, + Memento_Flag_BreakOnFree = 4, + Memento_Flag_BreakOnRealloc = 8 +}; + +/* When we list leaked blocks at the end of execution, we search for pointers + * between blocks in order to be able to give a nice nested view. + * Unfortunately, if you have are running your own allocator (such as + * ghostscripts chunk allocator) you can often find that the header of the + * block always contains pointers to next or previous blocks. This tends to + * mean the nesting displayed is "uninteresting" at best :) + * + * As a hack to get around this, we have a define MEMENTO_SKIP_SEARCH that + * indicates how many bytes to skip over at the start of the chunk. + * This may cause us to miss true nestings, but such is life... + */ +#ifndef MEMENTO_SEARCH_SKIP +#ifdef MEMENTO_GS_HACKS +#define MEMENTO_SEARCH_SKIP (2*sizeof(void *)) +#else +#define MEMENTO_SEARCH_SKIP 0 +#endif +#endif + +typedef struct Memento_BlkHeader Memento_BlkHeader; + +struct Memento_BlkHeader +{ + size_t rawsize; + int sequence; + int lastCheckedOK; + int flags; + Memento_BlkHeader *next; + Memento_BlkHeader *parent; /* Only used while printing out nested list */ + + const char *label; + + /* Entries for nesting display calculations */ + Memento_BlkHeader *child; + Memento_BlkHeader *sibling; + + char preblk[Memento_PreSize]; +}; + +/* In future this could (should) be a smarter data structure, like, say, + * splay trees. For now, we use a list. + */ +typedef struct Memento_Blocks +{ + Memento_BlkHeader *head; + Memento_BlkHeader **tail; +} Memento_Blocks; + +/* And our global structure */ +static struct { + int inited; + Memento_Blocks used; + Memento_Blocks free; + size_t freeListSize; + int sequence; + int paranoia; + int paranoidAt; + int countdown; + int lastChecked; + int breakAt; + int failAt; + int failing; + int nextFailAt; + int squeezeAt; + int squeezing; + int segv; + int pattern; + int nextPattern; + int patternBit; + size_t maxMemory; + size_t alloc; + size_t peakAlloc; + size_t totalAlloc; + size_t numMallocs; + size_t numFrees; + size_t numReallocs; +} globals; + +#define MEMENTO_EXTRASIZE (sizeof(Memento_BlkHeader) + Memento_PostSize) + +/* Round up size S to the next multiple of N (where N is a power of 2) */ +#define MEMENTO_ROUNDUP(S,N) ((S + N-1)&~(N-1)) + +#define MEMBLK_SIZE(s) MEMENTO_ROUNDUP(s + MEMENTO_EXTRASIZE, MEMENTO_MAXALIGN) + +#define MEMBLK_FROMBLK(B) (&((Memento_BlkHeader*)(void *)(B))[-1]) +#define MEMBLK_TOBLK(B) ((void*)(&((Memento_BlkHeader*)(void*)(B))[1])) +#define MEMBLK_POSTPTR(B) \ + (&((char *)(void *)(B))[(B)->rawsize + sizeof(Memento_BlkHeader)]) + +void Memento_breakpoint(void) +{ + /* A handy externally visible function for breakpointing */ +#if 0 /* Enable this to force automatic breakpointing */ +#ifdef DEBUG +#ifdef _MSC_VER + __asm int 3; +#endif +#endif +#endif +} + +static void Memento_addBlockHead(Memento_Blocks *blks, + Memento_BlkHeader *b, + int type) +{ + if (blks->tail == &blks->head) { + /* Adding into an empty list, means the tail changes too */ + blks->tail = &b->next; + } + b->next = blks->head; + blks->head = b; +#ifndef MEMENTO_LEAKONLY + memset(b->preblk, MEMENTO_PREFILL, Memento_PreSize); + memset(MEMBLK_POSTPTR(b), MEMENTO_POSTFILL, Memento_PostSize); +#endif + VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(b), Memento_PostSize); + if (type == 0) { /* malloc */ + VALGRIND_MAKE_MEM_UNDEFINED(MEMBLK_TOBLK(b), b->rawsize); + } else if (type == 1) { /* free */ + VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_TOBLK(b), b->rawsize); + } + VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader)); +} + +static void Memento_addBlockTail(Memento_Blocks *blks, + Memento_BlkHeader *b, + int type) +{ + VALGRIND_MAKE_MEM_DEFINED(blks->tail, sizeof(Memento_BlkHeader *)); + *blks->tail = b; + blks->tail = &b->next; + b->next = NULL; + VALGRIND_MAKE_MEM_NOACCESS(blks->tail, sizeof(Memento_BlkHeader *)); +#ifndef MEMENTO_LEAKONLY + memset(b->preblk, MEMENTO_PREFILL, Memento_PreSize); + memset(MEMBLK_POSTPTR(b), MEMENTO_POSTFILL, Memento_PostSize); +#endif + VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(b), Memento_PostSize); + if (type == 0) { /* malloc */ + VALGRIND_MAKE_MEM_UNDEFINED(MEMBLK_TOBLK(b), b->rawsize); + } else if (type == 1) { /* free */ + VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_TOBLK(b), b->rawsize); + } + VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader)); +} + +typedef struct BlkCheckData { + int found; + int preCorrupt; + int postCorrupt; + int freeCorrupt; + int index; +} BlkCheckData; + +static int Memento_Internal_checkAllocedBlock(Memento_BlkHeader *b, void *arg) +{ +#ifndef MEMENTO_LEAKONLY + int i; + char *p; + int corrupt = 0; + BlkCheckData *data = (BlkCheckData *)arg; + + p = b->preblk; + i = Memento_PreSize; + do { + corrupt |= (*p++ ^ (char)MEMENTO_PREFILL); + } while (--i); + if (corrupt) { + data->preCorrupt = 1; + } + p = MEMBLK_POSTPTR(b); + i = Memento_PreSize; + do { + corrupt |= (*p++ ^ (char)MEMENTO_POSTFILL); + } while (--i); + if (corrupt) { + data->postCorrupt = 1; + } + if ((data->freeCorrupt | data->preCorrupt | data->postCorrupt) == 0) { + b->lastCheckedOK = globals.sequence; + } + data->found |= 1; +#endif + return 0; +} + +static int Memento_Internal_checkFreedBlock(Memento_BlkHeader *b, void *arg) +{ +#ifndef MEMENTO_LEAKONLY + int i; + char *p; + BlkCheckData *data = (BlkCheckData *)arg; + + p = MEMBLK_TOBLK(b); + i = b->rawsize; + /* Attempt to speed this up by checking an (aligned) int at a time */ + do { + if (((size_t)p) & 1) { + if (*p++ != (char)MEMENTO_FREEFILL) + break; + i--; + if (i == 0) + break; + } + if ((i >= 2) && (((size_t)p) & 2)) { + if (*(short *)p != (short)(MEMENTO_FREEFILL | (MEMENTO_FREEFILL<<8))) + goto mismatch; + p += 2; + i -= 2; + if (i == 0) + break; + } + i -= 4; + while (i >= 0) { + if (*(int *)p != (MEMENTO_FREEFILL | + (MEMENTO_FREEFILL<<8) | + (MEMENTO_FREEFILL<<16) | + (MEMENTO_FREEFILL<<24))) + goto mismatch; + p += 4; + i -= 4; + } + i += 4; + if ((i >= 2) && (((size_t)p) & 2)) { + if (*(short *)p != (short)(MEMENTO_FREEFILL | (MEMENTO_FREEFILL<<8))) + goto mismatch; + p += 2; + i -= 2; + } +mismatch: + while (i) { + if (*p++ != (char)MEMENTO_FREEFILL) + break; + i--; + } + } while (0); + if (i) { + data->freeCorrupt = 1; + data->index = b->rawsize-i; + } + return Memento_Internal_checkAllocedBlock(b, arg); +#else + return 0; +#endif +} + +static void Memento_removeBlock(Memento_Blocks *blks, + Memento_BlkHeader *b) +{ + Memento_BlkHeader *head = blks->head; + Memento_BlkHeader *prev = NULL; + while ((head) && (head != b)) { + VALGRIND_MAKE_MEM_DEFINED(head, sizeof(*head)); + prev = head; + head = head->next; + VALGRIND_MAKE_MEM_NOACCESS(prev, sizeof(*prev)); + } + if (head == NULL) { + /* FAIL! Will have been reported to user earlier, so just exit. */ + return; + } + VALGRIND_MAKE_MEM_DEFINED(blks->tail, sizeof(*blks->tail)); + if (*blks->tail == head) { + /* Removing the tail of the list */ + if (prev == NULL) { + /* Which is also the head */ + blks->tail = &blks->head; + } else { + /* Which isn't the head */ + blks->tail = &prev->next; + } + } + if (prev == NULL) { + /* Removing from the head of the list */ + VALGRIND_MAKE_MEM_DEFINED(head, sizeof(*head)); + blks->head = head->next; + VALGRIND_MAKE_MEM_NOACCESS(head, sizeof(*head)); + } else { + /* Removing from not-the-head */ + VALGRIND_MAKE_MEM_DEFINED(head, sizeof(*head)); + VALGRIND_MAKE_MEM_DEFINED(prev, sizeof(*prev)); + prev->next = head->next; + VALGRIND_MAKE_MEM_NOACCESS(head, sizeof(*head)); + VALGRIND_MAKE_MEM_NOACCESS(prev, sizeof(*prev)); + } +} + +static int Memento_Internal_makeSpace(size_t space) +{ + /* If too big, it can never go on the freelist */ + if (space > MEMENTO_FREELIST_MAX_SINGLE_BLOCK) + return 0; + /* Pretend we added it on. */ + globals.freeListSize += space; + /* Ditch blocks until it fits within our limit */ + while (globals.freeListSize > MEMENTO_FREELIST_MAX) { + Memento_BlkHeader *head = globals.free.head; + VALGRIND_MAKE_MEM_DEFINED(head, sizeof(*head)); + globals.free.head = head->next; + globals.freeListSize -= MEMBLK_SIZE(head->rawsize); + MEMENTO_UNDERLYING_FREE(head); + } + /* Make sure we haven't just completely emptied the free list */ + /* (This should never happen, but belt and braces... */ + if (globals.free.head == NULL) + globals.free.tail = &globals.free.head; + return 1; +} + +static int Memento_appBlocks(Memento_Blocks *blks, + int (*app)(Memento_BlkHeader *, + void *), + void *arg) +{ + Memento_BlkHeader *head = blks->head; + Memento_BlkHeader *next; + int result; + while (head) { + VALGRIND_MAKE_MEM_DEFINED(head, sizeof(Memento_BlkHeader)); + VALGRIND_MAKE_MEM_DEFINED(MEMBLK_TOBLK(head), + head->rawsize + Memento_PostSize); + result = app(head, arg); + next = head->next; + VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(head), Memento_PostSize); + VALGRIND_MAKE_MEM_NOACCESS(head, sizeof(Memento_BlkHeader)); + if (result) + return result; + head = next; + } + return 0; +} + +static int Memento_appBlock(Memento_Blocks *blks, + int (*app)(Memento_BlkHeader *, + void *), + void *arg, + Memento_BlkHeader *b) +{ + Memento_BlkHeader *head = blks->head; + Memento_BlkHeader *next; + int result; + while (head && head != b) { + VALGRIND_MAKE_MEM_DEFINED(head, sizeof(Memento_BlkHeader)); + next = head->next; + VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(head), Memento_PostSize); + head = next; + } + if (head == b) { + VALGRIND_MAKE_MEM_DEFINED(head, sizeof(Memento_BlkHeader)); + VALGRIND_MAKE_MEM_DEFINED(MEMBLK_TOBLK(head), + head->rawsize + Memento_PostSize); + result = app(head, arg); + VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(head), Memento_PostSize); + VALGRIND_MAKE_MEM_NOACCESS(head, sizeof(Memento_BlkHeader)); + return result; + } + return 0; +} + +static void showBlock(Memento_BlkHeader *b, int space) +{ + fprintf(stderr, "0x%p:(size=%d,num=%d)", + MEMBLK_TOBLK(b), (int)b->rawsize, b->sequence); + if (b->label) + fprintf(stderr, "%c(%s)", space, b->label); +} + +static void blockDisplay(Memento_BlkHeader *b, int n) +{ + n++; + while (n > 40) + { + fprintf(stderr, "*"); + n -= 40; + } + while(n > 0) + { + int i = n; + if (i > 32) + i = 32; + n -= i; + fprintf(stderr, "%s", &" "[32-i]); + } + showBlock(b, '\t'); + fprintf(stderr, "\n"); +} + +static int Memento_listBlock(Memento_BlkHeader *b, + void *arg) +{ + int *counts = (int *)arg; + blockDisplay(b, 0); + counts[0]++; + counts[1]+= b->rawsize; + return 0; +} + +static void doNestedDisplay(Memento_BlkHeader *b, + int depth) +{ + /* Try and avoid recursion if we can help it */ + do { + blockDisplay(b, depth); + if (b->sibling) { + if (b->child) + doNestedDisplay(b->child, depth+1); + b = b->sibling; + } else { + b = b->child; + depth++; + } + } while (b); +} + +static int ptrcmp(const void *a_, const void *b_) +{ + const char **a = (const char **)a_; + const char **b = (const char **)b_; + return (int)(*a-*b); +} + +static +int Memento_listBlocksNested(void) +{ + int count, size, i; + Memento_BlkHeader *b; + void **blocks, *minptr, *maxptr; + long mask; + + /* Count the blocks */ + count = 0; + size = 0; + for (b = globals.used.head; b; b = b->next) { + size += b->rawsize; + count++; + } + + /* Make our block list */ + blocks = MEMENTO_UNDERLYING_MALLOC(sizeof(void *) * count); + if (blocks == NULL) + return 1; + + /* Populate our block list */ + b = globals.used.head; + minptr = maxptr = MEMBLK_TOBLK(b); + mask = (long)minptr; + for (i = 0; b; b = b->next, i++) { + void *p = MEMBLK_TOBLK(b); + mask &= (long)p; + if (p < minptr) + minptr = p; + if (p > maxptr) + maxptr = p; + blocks[i] = p; + b->flags &= ~Memento_Flag_HasParent; + b->child = NULL; + b->sibling = NULL; + b->parent = NULL; + } + qsort(blocks, count, sizeof(void *), ptrcmp); + + /* Now, calculate tree */ + for (b = globals.used.head; b; b = b->next) { + char *p = MEMBLK_TOBLK(b); + int end = (b->rawsize < MEMENTO_PTRSEARCH ? b->rawsize : MEMENTO_PTRSEARCH); + for (i = MEMENTO_SEARCH_SKIP; i < end; i += sizeof(void *)) { + void *q = *(void **)(&p[i]); + void **r; + + /* Do trivial checks on pointer */ + if ((mask & (int)q) != mask || q < minptr || q > maxptr) + continue; + + /* Search for pointer */ + r = bsearch(&q, blocks, count, sizeof(void *), ptrcmp); + if (r) { + /* Found child */ + Memento_BlkHeader *child = MEMBLK_FROMBLK(*r); + Memento_BlkHeader *parent; + + /* We're assuming tree structure, not graph - ignore second + * and subsequent pointers. */ + if (child->parent != NULL) + continue; + if (child->flags & Memento_Flag_HasParent) + continue; + + /* We're also assuming acyclicness here. If this is one of + * our parents, ignore it. */ + parent = b->parent; + while (parent != NULL && parent != child) + parent = parent->parent; + if (parent == child) + continue; + + child->sibling = b->child; + b->child = child; + child->parent = b; + child->flags |= Memento_Flag_HasParent; + } + } + } + + /* Now display with nesting */ + for (b = globals.used.head; b; b = b->next) { + if ((b->flags & Memento_Flag_HasParent) == 0) + doNestedDisplay(b, 0); + } + fprintf(stderr, " Total number of blocks = %d\n", count); + fprintf(stderr, " Total size of blocks = %d\n", size); + + MEMENTO_UNDERLYING_FREE(blocks); + return 0; +} + +void Memento_listBlocks(void) +{ + fprintf(stderr, "Allocated blocks:\n"); + if (Memento_listBlocksNested()) + { + int counts[2]; + counts[0] = 0; + counts[1] = 0; + Memento_appBlocks(&globals.used, Memento_listBlock, &counts[0]); + fprintf(stderr, " Total number of blocks = %d\n", counts[0]); + fprintf(stderr, " Total size of blocks = %d\n", counts[1]); + } +} + +static int Memento_listNewBlock(Memento_BlkHeader *b, + void *arg) +{ + if (b->flags & Memento_Flag_OldBlock) + return 0; + b->flags |= Memento_Flag_OldBlock; + return Memento_listBlock(b, arg); +} + +void Memento_listNewBlocks(void) { + int counts[2]; + counts[0] = 0; + counts[1] = 0; + fprintf(stderr, "Blocks allocated and still extant since last list:\n"); + Memento_appBlocks(&globals.used, Memento_listNewBlock, &counts[0]); + fprintf(stderr, " Total number of blocks = %d\n", counts[0]); + fprintf(stderr, " Total size of blocks = %d\n", counts[1]); +} + +static void Memento_endStats(void) +{ + fprintf(stderr, "Total memory malloced = %u bytes\n", (unsigned int)globals.totalAlloc); + fprintf(stderr, "Peak memory malloced = %u bytes\n", (unsigned int)globals.peakAlloc); + fprintf(stderr, "%u mallocs, %u frees, %u reallocs\n", (unsigned int)globals.numMallocs, + (unsigned int)globals.numFrees, (unsigned int)globals.numReallocs); + fprintf(stderr, "Average allocation size %u bytes\n", (unsigned int) + (globals.numMallocs != 0 ? globals.totalAlloc/globals.numMallocs: 0)); +} + +void Memento_stats(void) +{ + fprintf(stderr, "Current memory malloced = %u bytes\n", (unsigned int)globals.alloc); + Memento_endStats(); +} + +static void Memento_fin(void) +{ + Memento_checkAllMemory(); + Memento_endStats(); + if (globals.used.head != NULL) { + Memento_listBlocks(); + Memento_breakpoint(); + } + if (globals.segv) { + fprintf(stderr, "Memory dumped on SEGV while squeezing @ %d\n", globals.failAt); + } else if (globals.squeezing) { + if (globals.pattern == 0) + fprintf(stderr, "Memory squeezing @ %d complete\n", globals.squeezeAt); + else + fprintf(stderr, "Memory squeezing @ %d (%d) complete\n", globals.squeezeAt, globals.pattern); + } + if (globals.failing) + { + fprintf(stderr, "MEMENTO_FAILAT=%d\n", globals.failAt); + fprintf(stderr, "MEMENTO_PATTERN=%d\n", globals.pattern); + } + if (globals.nextFailAt != 0) + { + fprintf(stderr, "MEMENTO_NEXTFAILAT=%d\n", globals.nextFailAt); + fprintf(stderr, "MEMENTO_NEXTPATTERN=%d\n", globals.nextPattern); + } +} + +static void Memento_inited(void) +{ + /* A good place for a breakpoint */ +} + +static void Memento_init(void) +{ + char *env; + memset(&globals, 0, sizeof(globals)); + globals.inited = 1; + globals.used.head = NULL; + globals.used.tail = &globals.used.head; + globals.free.head = NULL; + globals.free.tail = &globals.free.head; + globals.sequence = 0; + globals.countdown = 1024; + + env = getenv("MEMENTO_FAILAT"); + globals.failAt = (env ? atoi(env) : 0); + + env = getenv("MEMENTO_PARANOIA"); + globals.paranoia = (env ? atoi(env) : 0); + if (globals.paranoia == 0) + globals.paranoia = 1024; + + env = getenv("MEMENTO_PARANOIDAT"); + globals.paranoidAt = (env ? atoi(env) : 0); + + env = getenv("MEMENTO_SQUEEZEAT"); + globals.squeezeAt = (env ? atoi(env) : 0); + + env = getenv("MEMENTO_PATTERN"); + globals.pattern = (env ? atoi(env) : 0); + + env = getenv("MEMENTO_MAXMEMORY"); + globals.maxMemory = (env ? atoi(env) : 0); + + atexit(Memento_fin); + + Memento_inited(); +} + +#ifdef MEMENTO_HAS_FORK +#include <unistd.h> +#include <sys/wait.h> +#ifdef MEMENTO_STACKTRACE_METHOD +#if MEMENTO_STACKTRACE_METHOD == 1 +#include <signal.h> +#endif +#endif + +/* FIXME: Find some portable way of getting this */ +/* MacOSX has 10240, Ubuntu seems to have 256 */ +#define OPEN_MAX 10240 + +/* stashed_map[j] = i means that filedescriptor i-1 was duplicated to j */ +int stashed_map[OPEN_MAX]; + +#ifdef MEMENTO_STACKTRACE_METHOD +#if MEMENTO_STACKTRACE_METHOD == 1 +extern size_t backtrace(void **, int); +extern void backtrace_symbols_fd(void **, size_t, int); +#endif +#endif + +static void Memento_signal(void) +{ + fprintf(stderr, "SEGV after Memory squeezing @ %d\n", globals.squeezeAt); + +#ifdef MEMENTO_STACKTRACE_METHOD +#if MEMENTO_STACKTRACE_METHOD == 1 + { + void *array[100]; + size_t size; + + size = backtrace(array, 100); + fprintf(stderr, "------------------------------------------------------------------------\n"); + fprintf(stderr, "Backtrace:\n"); + backtrace_symbols_fd(array, size, 2); + fprintf(stderr, "------------------------------------------------------------------------\n"); + } +#endif +#endif + + exit(1); +} + +static int squeeze(void) +{ + pid_t pid; + int i, status; + + if (globals.patternBit < 0) + return 1; + if (globals.squeezing && globals.patternBit >= MEMENTO_MAXPATTERN) + return 1; + + if (globals.patternBit == 0) + globals.squeezeAt = globals.sequence; + + if (!globals.squeezing) { + fprintf(stderr, "Memory squeezing @ %d\n", globals.squeezeAt); + } else + fprintf(stderr, "Memory squeezing @ %d (%x,%x)\n", globals.squeezeAt, globals.pattern, globals.patternBit); + + /* When we fork below, the child is going to snaffle all our file pointers + * and potentially corrupt them. Let's make copies of all of them before + * we fork, so we can restore them when we restart. */ + for (i = 0; i < OPEN_MAX; i++) { + if (stashed_map[i] == 0) { + int j = dup(i); + stashed_map[j] = i+1; + } + } + + pid = fork(); + if (pid == 0) { + /* Child */ + signal(SIGSEGV, Memento_signal); + /* In the child, we always fail the next allocation. */ + if (globals.patternBit == 0) { + globals.patternBit = 1; + } else + globals.patternBit <<= 1; + globals.squeezing = 1; + return 1; + } + + /* In the parent if we hit another allocation, pass it (and record the + * fact we passed it in the pattern. */ + globals.pattern |= globals.patternBit; + globals.patternBit <<= 1; + + /* Wait for pid to finish */ + waitpid(pid, &status, 0); + + if (status != 0) { + fprintf(stderr, "Child status=%d\n", status); + } + + /* Put the files back */ + for (i = 0; i < OPEN_MAX; i++) { + if (stashed_map[i] != 0) { + dup2(i, stashed_map[i]-1); + close(i); + stashed_map[i] = 0; + } + } + + return 0; +} +#else +#include <signal.h> + +static void Memento_signal(void) +{ + globals.segv = 1; + /* If we just return from this function the SEGV will be unhandled, and + * we'll launch into whatever JIT debugging system the OS provides. At + * least fprintf(stderr, something useful first. If MEMENTO_NOJIT is set, then + * just exit to avoid the JIT (and get the usual atexit handling). */ + if (getenv("MEMENTO_NOJIT")) + exit(1); + else + Memento_fin(); +} + +int squeeze(void) +{ + fprintf(stderr, "Memento memory squeezing disabled as no fork!\n"); + return 0; +} +#endif + +static void Memento_startFailing(void) +{ + if (!globals.failing) { + fprintf(stderr, "Starting to fail...\n"); + fflush(stderr); + globals.failing = 1; + globals.failAt = globals.sequence; + globals.nextFailAt = globals.sequence+1; + globals.pattern = 0; + globals.patternBit = 0; + signal(SIGSEGV, Memento_signal); + signal(SIGABRT, Memento_signal); + Memento_breakpoint(); + } +} + +static void Memento_event(void) +{ + globals.sequence++; + if ((globals.sequence >= globals.paranoidAt) && (globals.paranoidAt != 0)) { + globals.paranoia = 1; + globals.countdown = 1; + } + if (--globals.countdown == 0) { + Memento_checkAllMemory(); + globals.countdown = globals.paranoia; + } + + if (globals.sequence == globals.breakAt) { + fprintf(stderr, "Breaking at event %d\n", globals.breakAt); + Memento_breakpoint(); + } +} + +int Memento_breakAt(int event) +{ + globals.breakAt = event; + return event; +} + +void *Memento_label(void *ptr, const char *label) +{ + Memento_BlkHeader *block; + + if (ptr == NULL) + return NULL; + block = MEMBLK_FROMBLK(ptr); + block->label = label; + return ptr; +} + +int Memento_failThisEvent(void) +{ + int failThisOne; + + if (!globals.inited) + Memento_init(); + + Memento_event(); + + if ((globals.sequence >= globals.failAt) && (globals.failAt != 0)) + Memento_startFailing(); + if ((globals.sequence >= globals.squeezeAt) && (globals.squeezeAt != 0)) { + return squeeze(); + } + + if (!globals.failing) + return 0; + failThisOne = ((globals.patternBit & globals.pattern) == 0); + /* If we are failing, and we've reached the end of the pattern and we've + * still got bits available in the pattern word, and we haven't already + * set a nextPattern, then extend the pattern. */ + if (globals.failing && + ((~(globals.patternBit-1) & globals.pattern) == 0) && + (globals.patternBit != 0) && + globals.nextPattern == 0) + { + /* We'll fail this one, and set the 'next' one to pass it. */ + globals.nextFailAt = globals.failAt; + globals.nextPattern = globals.pattern | globals.patternBit; + } + globals.patternBit = (globals.patternBit ? globals.patternBit << 1 : 1); + + return failThisOne; +} + +void *Memento_malloc(size_t s) +{ + Memento_BlkHeader *memblk; + size_t smem = MEMBLK_SIZE(s); + + if (Memento_failThisEvent()) + return NULL; + + if (s == 0) + return NULL; + + globals.numMallocs++; + + if (globals.maxMemory != 0 && globals.alloc + s > globals.maxMemory) + return NULL; + + memblk = MEMENTO_UNDERLYING_MALLOC(smem); + if (memblk == NULL) + return NULL; + + globals.alloc += s; + globals.totalAlloc += s; + if (globals.peakAlloc < globals.alloc) + globals.peakAlloc = globals.alloc; +#ifndef MEMENTO_LEAKONLY + memset(MEMBLK_TOBLK(memblk), MEMENTO_ALLOCFILL, s); +#endif + memblk->rawsize = s; + memblk->sequence = globals.sequence; + memblk->lastCheckedOK = memblk->sequence; + memblk->flags = 0; + memblk->label = 0; + memblk->child = NULL; + memblk->sibling = NULL; + Memento_addBlockHead(&globals.used, memblk, 0); + return MEMBLK_TOBLK(memblk); +} + +void *Memento_calloc(size_t n, size_t s) +{ + void *block = Memento_malloc(n*s); + + if (block) + memset(block, 0, n*s); + return block; +} + +static int checkBlock(Memento_BlkHeader *memblk, const char *action) +{ +#ifndef MEMENTO_LEAKONLY + BlkCheckData data; + + memset(&data, 0, sizeof(data)); + Memento_appBlock(&globals.used, Memento_Internal_checkAllocedBlock, + &data, memblk); + if (!data.found) { + /* Failure! */ + fprintf(stderr, "Attempt to %s block ", action); + showBlock(memblk, 32); + Memento_breakpoint(); + return 1; + } else if (data.preCorrupt || data.postCorrupt) { + fprintf(stderr, "Block "); + showBlock(memblk, ' '); + fprintf(stderr, " found to be corrupted on %s!\n", action); + if (data.preCorrupt) { + fprintf(stderr, "Preguard corrupted\n"); + } + if (data.postCorrupt) { + fprintf(stderr, "Postguard corrupted\n"); + } + fprintf(stderr, "Block last checked OK at allocation %d. Now %d.\n", + memblk->lastCheckedOK, globals.sequence); + Memento_breakpoint(); + return 1; + } +#endif + return 0; +} + +void Memento_free(void *blk) +{ + Memento_BlkHeader *memblk; + + if (!globals.inited) + Memento_init(); + + Memento_event(); + + if (blk == NULL) + return; + + memblk = MEMBLK_FROMBLK(blk); + VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk)); + if (checkBlock(memblk, "free")) + return; + + if (memblk->flags & Memento_Flag_BreakOnFree) + Memento_breakpoint(); + + VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk)); + globals.alloc -= memblk->rawsize; + globals.numFrees++; + + Memento_removeBlock(&globals.used, memblk); + + VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk)); + if (Memento_Internal_makeSpace(MEMBLK_SIZE(memblk->rawsize))) { + VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk)); + VALGRIND_MAKE_MEM_DEFINED(MEMBLK_TOBLK(memblk), + memblk->rawsize + Memento_PostSize); +#ifndef MEMENTO_LEAKONLY + memset(MEMBLK_TOBLK(memblk), MEMENTO_FREEFILL, memblk->rawsize); +#endif + Memento_addBlockTail(&globals.free, memblk, 1); + } else { + MEMENTO_UNDERLYING_FREE(memblk); + } +} + +void *Memento_realloc(void *blk, size_t newsize) +{ + Memento_BlkHeader *memblk, *newmemblk; + size_t newsizemem; + int flags; + + if (blk == NULL) + return Memento_malloc(newsize); + if (newsize == 0) { + Memento_free(blk); + return NULL; + } + + if (Memento_failThisEvent()) + return NULL; + + memblk = MEMBLK_FROMBLK(blk); + if (checkBlock(memblk, "realloc")) + return NULL; + + if (memblk->flags & Memento_Flag_BreakOnRealloc) + Memento_breakpoint(); + + if (globals.maxMemory != 0 && globals.alloc - memblk->rawsize + newsize > globals.maxMemory) + return NULL; + + newsizemem = MEMBLK_SIZE(newsize); + Memento_removeBlock(&globals.used, memblk); + flags = memblk->flags; + newmemblk = MEMENTO_UNDERLYING_REALLOC(memblk, newsizemem); + if (newmemblk == NULL) + { + Memento_addBlockHead(&globals.used, memblk, 2); + return NULL; + } + globals.numReallocs++; + globals.totalAlloc += newsize; + globals.alloc -= newmemblk->rawsize; + globals.alloc += newsize; + if (globals.peakAlloc < globals.alloc) + globals.peakAlloc = globals.alloc; + newmemblk->flags = flags; + if (newmemblk->rawsize < newsize) { + char *newbytes = ((char *)MEMBLK_TOBLK(newmemblk))+newmemblk->rawsize; +#ifndef MEMENTO_LEAKONLY + memset(newbytes, MEMENTO_ALLOCFILL, newsize - newmemblk->rawsize); +#endif + VALGRIND_MAKE_MEM_UNDEFINED(newbytes, newsize - newmemblk->rawsize); + } + newmemblk->rawsize = newsize; +#ifndef MEMENTO_LEAKONLY + memset(newmemblk->preblk, MEMENTO_PREFILL, Memento_PreSize); + memset(MEMBLK_POSTPTR(newmemblk), MEMENTO_POSTFILL, Memento_PostSize); +#endif + Memento_addBlockHead(&globals.used, newmemblk, 2); + return MEMBLK_TOBLK(newmemblk); +} + +int Memento_checkBlock(void *blk) +{ + Memento_BlkHeader *memblk; + + if (blk == NULL) + return 0; + memblk = MEMBLK_FROMBLK(blk); + return checkBlock(memblk, "check"); +} + +static int Memento_Internal_checkAllAlloced(Memento_BlkHeader *memblk, void *arg) +{ + BlkCheckData *data = (BlkCheckData *)arg; + + Memento_Internal_checkAllocedBlock(memblk, data); + if (data->preCorrupt || data->postCorrupt) { + if ((data->found & 2) == 0) { + fprintf(stderr, "Allocated blocks:\n"); + data->found |= 2; + } + fprintf(stderr, " Block "); + showBlock(memblk, ' '); + if (data->preCorrupt) { + fprintf(stderr, " Preguard "); + } + if (data->postCorrupt) { + fprintf(stderr, "%s Postguard ", + (data->preCorrupt ? "&" : "")); + } + fprintf(stderr, "corrupted.\n " + "Block last checked OK at allocation %d. Now %d.\n", + memblk->lastCheckedOK, globals.sequence); + data->preCorrupt = 0; + data->postCorrupt = 0; + data->freeCorrupt = 0; + } + else + memblk->lastCheckedOK = globals.sequence; + return 0; +} + +static int Memento_Internal_checkAllFreed(Memento_BlkHeader *memblk, void *arg) +{ + BlkCheckData *data = (BlkCheckData *)arg; + + Memento_Internal_checkFreedBlock(memblk, data); + if (data->preCorrupt || data->postCorrupt || data->freeCorrupt) { + if ((data->found & 4) == 0) { + fprintf(stderr, "Freed blocks:\n"); + data->found |= 4; + } + fprintf(stderr, " "); + showBlock(memblk, ' '); + if (data->freeCorrupt) { + fprintf(stderr, " index %d (address 0x%p) onwards", data->index, + &((char *)MEMBLK_TOBLK(memblk))[data->index]); + if (data->preCorrupt) { + fprintf(stderr, "+ preguard"); + } + if (data->postCorrupt) { + fprintf(stderr, "+ postguard"); + } + } else { + if (data->preCorrupt) { + fprintf(stderr, " preguard"); + } + if (data->postCorrupt) { + fprintf(stderr, "%s Postguard", + (data->preCorrupt ? "+" : "")); + } + } + fprintf(stderr, " corrupted.\n" + " Block last checked OK at allocation %d. Now %d.\n", + memblk->lastCheckedOK, globals.sequence); + data->preCorrupt = 0; + data->postCorrupt = 0; + data->freeCorrupt = 0; + } + else + memblk->lastCheckedOK = globals.sequence; + return 0; +} + +int Memento_checkAllMemory(void) +{ +#ifndef MEMENTO_LEAKONLY + BlkCheckData data; + + memset(&data, 0, sizeof(data)); + Memento_appBlocks(&globals.used, Memento_Internal_checkAllAlloced, &data); + Memento_appBlocks(&globals.free, Memento_Internal_checkAllFreed, &data); + if (data.found & 6) { + Memento_breakpoint(); + return 1; + } +#endif + return 0; +} + +int Memento_setParanoia(int i) +{ + globals.paranoia = i; + globals.countdown = globals.paranoia; + return i; +} + +int Memento_paranoidAt(int i) +{ + globals.paranoidAt = i; + return i; +} + +int Memento_getBlockNum(void *b) +{ + Memento_BlkHeader *memblk; + if (b == NULL) + return 0; + memblk = MEMBLK_FROMBLK(b); + return (memblk->sequence); +} + +int Memento_check(void) +{ + int result; + + fprintf(stderr, "Checking memory\n"); + result = Memento_checkAllMemory(); + fprintf(stderr, "Memory checked!\n"); + return result; +} + +typedef struct findBlkData { + void *addr; + Memento_BlkHeader *blk; + int flags; +} findBlkData; + +static int Memento_containsAddr(Memento_BlkHeader *b, + void *arg) +{ + findBlkData *data = (findBlkData *)arg; + char *blkend = &((char *)MEMBLK_TOBLK(b))[b->rawsize]; + if ((MEMBLK_TOBLK(b) <= data->addr) && + ((void *)blkend > data->addr)) { + data->blk = b; + data->flags = 1; + return 1; + } + if (((void *)b <= data->addr) && + (MEMBLK_TOBLK(b) > data->addr)) { + data->blk = b; + data->flags = 2; + return 1; + } + if (((void *)blkend <= data->addr) && + ((void *)(blkend + Memento_PostSize) > data->addr)) { + data->blk = b; + data->flags = 3; + return 1; + } + return 0; +} + +int Memento_find(void *a) +{ + findBlkData data; + + data.addr = a; + data.blk = NULL; + data.flags = 0; + Memento_appBlocks(&globals.used, Memento_containsAddr, &data); + if (data.blk != NULL) { + fprintf(stderr, "Address 0x%p is in %sallocated block ", + data.addr, + (data.flags == 1 ? "" : (data.flags == 2 ? + "preguard of " : "postguard of "))); + showBlock(data.blk, ' '); + fprintf(stderr, "\n"); + return data.blk->sequence; + } + data.blk = NULL; + data.flags = 0; + Memento_appBlocks(&globals.free, Memento_containsAddr, &data); + if (data.blk != NULL) { + fprintf(stderr, "Address 0x%p is in %sfreed block ", + data.addr, + (data.flags == 1 ? "" : (data.flags == 2 ? + "preguard of " : "postguard of "))); + showBlock(data.blk, ' '); + fprintf(stderr, "\n"); + return data.blk->sequence; + } + return 0; +} + +void Memento_breakOnFree(void *a) +{ + findBlkData data; + + data.addr = a; + data.blk = NULL; + data.flags = 0; + Memento_appBlocks(&globals.used, Memento_containsAddr, &data); + if (data.blk != NULL) { + fprintf(stderr, "Will stop when address 0x%p (in %sallocated block ", + data.addr, + (data.flags == 1 ? "" : (data.flags == 2 ? + "preguard of " : "postguard of "))); + showBlock(data.blk, ' '); + fprintf(stderr, ") is freed\n"); + data.blk->flags |= Memento_Flag_BreakOnFree; + return; + } + data.blk = NULL; + data.flags = 0; + Memento_appBlocks(&globals.free, Memento_containsAddr, &data); + if (data.blk != NULL) { + fprintf(stderr, "Can't stop on free; address 0x%p is in %sfreed block ", + data.addr, + (data.flags == 1 ? "" : (data.flags == 2 ? + "preguard of " : "postguard of "))); + showBlock(data.blk, ' '); + fprintf(stderr, "\n"); + return; + } + fprintf(stderr, "Can't stop on free; address 0x%p is not in a known block.\n", a); +} + +void Memento_breakOnRealloc(void *a) +{ + findBlkData data; + + data.addr = a; + data.blk = NULL; + data.flags = 0; + Memento_appBlocks(&globals.used, Memento_containsAddr, &data); + if (data.blk != NULL) { + fprintf(stderr, "Will stop when address 0x%p (in %sallocated block ", + data.addr, + (data.flags == 1 ? "" : (data.flags == 2 ? + "preguard of " : "postguard of "))); + showBlock(data.blk, ' '); + fprintf(stderr, ") is freed (or realloced)\n"); + data.blk->flags |= Memento_Flag_BreakOnFree | Memento_Flag_BreakOnRealloc; + return; + } + data.blk = NULL; + data.flags = 0; + Memento_appBlocks(&globals.free, Memento_containsAddr, &data); + if (data.blk != NULL) { + fprintf(stderr, "Can't stop on free/realloc; address 0x%p is in %sfreed block ", + data.addr, + (data.flags == 1 ? "" : (data.flags == 2 ? + "preguard of " : "postguard of "))); + showBlock(data.blk, ' '); + fprintf(stderr, "\n"); + return; + } + fprintf(stderr, "Can't stop on free/realloc; address 0x%p is not in a known block.\n", a); +} + +int Memento_failAt(int i) +{ + globals.failAt = i; + if ((globals.sequence > globals.failAt) && + (globals.failing != 0)) + Memento_startFailing(); + return i; +} + +size_t Memento_setMax(size_t max) +{ + globals.maxMemory = max; + return max; +} + +#else + +/* Just in case anyone has left some debugging code in... */ +void (Memento_breakpoint)(void) +{ +} + +int (Memento_checkBlock)(void *b) +{ + return 0; +} + +int (Memento_checkAllMemory)(void) +{ + return 0; +} + +int (Memento_check)(void) +{ + return 0; +} + +int (Memento_setParanoia)(int i) +{ + return 0; +} + +int (Memento_paranoidAt)(int i) +{ + return 0; +} + +int (Memento_breakAt)(int i) +{ + return 0; +} + +int (Memento_getBlockNum)(void *i) +{ + return 0; +} + +int (Memento_find)(void *a) +{ + return 0; +} + +int (Memento_failAt)(int i) +{ + return 0; +} + +void (Memento_breakOnFree)(void *a) +{ +} + +void (Memento_breakOnRealloc)(void *a) +{ +} + +#undef Memento_malloc +#undef Memento_free +#undef Memento_realloc +#undef Memento_calloc + +void *Memento_malloc(size_t size) +{ + return MEMENTO_UNDERLYING_MALLOC(size); +} + +void Memento_free(void *b) +{ + MEMENTO_UNDERLYING_FREE(b); +} + +void *Memento_realloc(void *b, size_t s) +{ + return MEMENTO_UNDERLYING_REALLOC(b, s); +} + +void *Memento_calloc(size_t n, size_t s) +{ + return MEMENTO_UNDERLYING_CALLOC(n, s); +} + +void (Memento_listBlocks)(void) +{ +} + +void (Memento_listNewBlocks)(void) +{ +} + +size_t (Memento_setMax)(size_t max) +{ + return 0; +} + +void (Memento_stats)(void) +{ +} + +void *(Memento_label)(void *ptr, const char *label) +{ + return ptr; +} + +#endif diff --git a/source/fitz/memory.c b/source/fitz/memory.c new file mode 100644 index 00000000..f9e7b4f6 --- /dev/null +++ b/source/fitz/memory.c @@ -0,0 +1,402 @@ +#include "mupdf/fitz.h" + +/* Enable FITZ_DEBUG_LOCKING_TIMES below if you want to check the times + * for which locks are held too. */ +#ifdef FITZ_DEBUG_LOCKING +#undef FITZ_DEBUG_LOCKING_TIMES +#endif + +static void * +do_scavenging_malloc(fz_context *ctx, unsigned int size) +{ + void *p; + int phase = 0; + + fz_lock(ctx, FZ_LOCK_ALLOC); + do { + p = ctx->alloc->malloc(ctx->alloc->user, size); + if (p != NULL) + { + fz_unlock(ctx, FZ_LOCK_ALLOC); + return p; + } + } while (fz_store_scavenge(ctx, size, &phase)); + fz_unlock(ctx, FZ_LOCK_ALLOC); + + return NULL; +} + +static void * +do_scavenging_realloc(fz_context *ctx, void *p, unsigned int size) +{ + void *q; + int phase = 0; + + fz_lock(ctx, FZ_LOCK_ALLOC); + do { + q = ctx->alloc->realloc(ctx->alloc->user, p, size); + if (q != NULL) + { + fz_unlock(ctx, FZ_LOCK_ALLOC); + return q; + } + } while (fz_store_scavenge(ctx, size, &phase)); + fz_unlock(ctx, FZ_LOCK_ALLOC); + + return NULL; +} + +void * +fz_malloc(fz_context *ctx, unsigned int size) +{ + void *p; + + if (size == 0) + return NULL; + + p = do_scavenging_malloc(ctx, size); + if (!p) + fz_throw(ctx, FZ_ERROR_GENERIC, "malloc of %d bytes failed", size); + return p; +} + +void * +fz_malloc_no_throw(fz_context *ctx, unsigned int size) +{ + return do_scavenging_malloc(ctx, size); +} + +void * +fz_malloc_array(fz_context *ctx, unsigned int count, unsigned int size) +{ + void *p; + + if (count == 0 || size == 0) + return 0; + + if (count > UINT_MAX / size) + fz_throw(ctx, FZ_ERROR_GENERIC, "malloc of array (%d x %d bytes) failed (integer overflow)", count, size); + + p = do_scavenging_malloc(ctx, count * size); + if (!p) + fz_throw(ctx, FZ_ERROR_GENERIC, "malloc of array (%d x %d bytes) failed", count, size); + return p; +} + +void * +fz_malloc_array_no_throw(fz_context *ctx, unsigned int count, unsigned int size) +{ + if (count == 0 || size == 0) + return 0; + + if (count > UINT_MAX / size) + { + fprintf(stderr, "error: malloc of array (%d x %d bytes) failed (integer overflow)", count, size); + return NULL; + } + + return do_scavenging_malloc(ctx, count * size); +} + +void * +fz_calloc(fz_context *ctx, unsigned int count, unsigned int size) +{ + void *p; + + if (count == 0 || size == 0) + return 0; + + if (count > UINT_MAX / size) + { + fz_throw(ctx, FZ_ERROR_GENERIC, "calloc (%d x %d bytes) failed (integer overflow)", count, size); + } + + p = do_scavenging_malloc(ctx, count * size); + if (!p) + { + fz_throw(ctx, FZ_ERROR_GENERIC, "calloc (%d x %d bytes) failed", count, size); + } + memset(p, 0, count*size); + return p; +} + +void * +fz_calloc_no_throw(fz_context *ctx, unsigned int count, unsigned int size) +{ + void *p; + + if (count == 0 || size == 0) + return 0; + + if (count > UINT_MAX / size) + { + fprintf(stderr, "error: calloc (%d x %d bytes) failed (integer overflow)\n", count, size); + return NULL; + } + + p = do_scavenging_malloc(ctx, count * size); + if (p) + { + memset(p, 0, count*size); + } + return p; +} + +void * +fz_resize_array(fz_context *ctx, void *p, unsigned int count, unsigned int size) +{ + void *np; + + if (count == 0 || size == 0) + { + fz_free(ctx, p); + return 0; + } + + if (count > UINT_MAX / size) + fz_throw(ctx, FZ_ERROR_GENERIC, "resize array (%d x %d bytes) failed (integer overflow)", count, size); + + np = do_scavenging_realloc(ctx, p, count * size); + if (!np) + fz_throw(ctx, FZ_ERROR_GENERIC, "resize array (%d x %d bytes) failed", count, size); + return np; +} + +void * +fz_resize_array_no_throw(fz_context *ctx, void *p, unsigned int count, unsigned int size) +{ + if (count == 0 || size == 0) + { + fz_free(ctx, p); + return 0; + } + + if (count > UINT_MAX / size) + { + fprintf(stderr, "error: resize array (%d x %d bytes) failed (integer overflow)\n", count, size); + return NULL; + } + + return do_scavenging_realloc(ctx, p, count * size); +} + +void +fz_free(fz_context *ctx, void *p) +{ + fz_lock(ctx, FZ_LOCK_ALLOC); + ctx->alloc->free(ctx->alloc->user, p); + fz_unlock(ctx, FZ_LOCK_ALLOC); +} + +char * +fz_strdup(fz_context *ctx, const char *s) +{ + int len = strlen(s) + 1; + char *ns = fz_malloc(ctx, len); + memcpy(ns, s, len); + return ns; +} + +char * +fz_strdup_no_throw(fz_context *ctx, const char *s) +{ + int len = strlen(s) + 1; + char *ns = fz_malloc_no_throw(ctx, len); + if (ns) + memcpy(ns, s, len); + return ns; +} + +static void * +fz_malloc_default(void *opaque, unsigned int size) +{ + return malloc(size); +} + +static void * +fz_realloc_default(void *opaque, void *old, unsigned int size) +{ + return realloc(old, size); +} + +static void +fz_free_default(void *opaque, void *ptr) +{ + free(ptr); +} + +fz_alloc_context fz_alloc_default = +{ + NULL, + fz_malloc_default, + fz_realloc_default, + fz_free_default +}; + +static void +fz_lock_default(void *user, int lock) +{ +} + +static void +fz_unlock_default(void *user, int lock) +{ +} + +fz_locks_context fz_locks_default = +{ + NULL, + fz_lock_default, + fz_unlock_default +}; + +#ifdef FITZ_DEBUG_LOCKING + +enum +{ + FZ_LOCK_DEBUG_CONTEXT_MAX = 100 +}; + +fz_context *fz_lock_debug_contexts[FZ_LOCK_DEBUG_CONTEXT_MAX]; +int fz_locks_debug[FZ_LOCK_DEBUG_CONTEXT_MAX][FZ_LOCK_MAX]; +#ifdef FITZ_DEBUG_LOCKING_TIMES +int fz_debug_locking_inited = 0; +int fz_lock_program_start; +int fz_lock_time[FZ_LOCK_DEBUG_CONTEXT_MAX][FZ_LOCK_MAX] = { { 0 } }; +int fz_lock_taken[FZ_LOCK_DEBUG_CONTEXT_MAX][FZ_LOCK_MAX] = { { 0 } }; + +/* We implement our own millisecond clock, as clock() cannot be trusted + * when threads are involved. */ +static int ms_clock(void) +{ +#ifdef _WIN32 + return (int)GetTickCount(); +#else + struct timeval tp; + gettimeofday(&tp, NULL); + return (tp.tv_sec*1000) + (tp.tv_usec/1000); +#endif +} + +static void dump_lock_times(void) +{ + int i, j; + int prog_time = ms_clock() - fz_lock_program_start; + + for (j = 0; j < FZ_LOCK_MAX; j++) + { + int total = 0; + for (i = 0; i < FZ_LOCK_DEBUG_CONTEXT_MAX; i++) + { + total += fz_lock_time[i][j]; + } + printf("Lock %d held for %g seconds (%g%%)\n", j, ((double)total)/1000, 100.0*total/prog_time); + } + printf("Total program time %g seconds\n", ((double)prog_time)/1000); +} + +#endif + +static int find_context(fz_context *ctx) +{ + int i; + + for (i = 0; i < FZ_LOCK_DEBUG_CONTEXT_MAX; i++) + { + if (fz_lock_debug_contexts[i] == ctx) + return i; + if (fz_lock_debug_contexts[i] == NULL) + { + int gottit = 0; + /* We've not locked on this context before, so use + * this one for this new context. We might have other + * threads trying here too though so, so claim it + * atomically. No one has locked on this context + * before, so we are safe to take the ALLOC lock. */ + ctx->locks->lock(ctx->locks->user, FZ_LOCK_ALLOC); + /* If it's still free, then claim it as ours, + * otherwise we'll keep hunting. */ + if (fz_lock_debug_contexts[i] == NULL) + { + gottit = 1; + fz_lock_debug_contexts[i] = ctx; +#ifdef FITZ_DEBUG_LOCKING_TIMES + if (fz_debug_locking_inited == 0) + { + fz_debug_locking_inited = 1; + fz_lock_program_start = ms_clock(); + atexit(dump_lock_times); + } +#endif + } + ctx->locks->unlock(ctx->locks->user, FZ_LOCK_ALLOC); + if (gottit) + return i; + } + } + return -1; +} + +void +fz_assert_lock_held(fz_context *ctx, int lock) +{ + int idx = find_context(ctx); + if (idx < 0) + return; + + if (fz_locks_debug[idx][lock] == 0) + fprintf(stderr, "Lock %d not held when expected\n", lock); +} + +void +fz_assert_lock_not_held(fz_context *ctx, int lock) +{ + int idx = find_context(ctx); + if (idx < 0) + return; + + if (fz_locks_debug[idx][lock] != 0) + fprintf(stderr, "Lock %d held when not expected\n", lock); +} + +void fz_lock_debug_lock(fz_context *ctx, int lock) +{ + int i; + int idx = find_context(ctx); + if (idx < 0) + return; + + if (fz_locks_debug[idx][lock] != 0) + { + fprintf(stderr, "Attempt to take lock %d when held already!\n", lock); + } + for (i = lock-1; i >= 0; i--) + { + if (fz_locks_debug[idx][i] != 0) + { + fprintf(stderr, "Lock ordering violation: Attempt to take lock %d when %d held already!\n", lock, i); + } + } + fz_locks_debug[idx][lock] = 1; +#ifdef FITZ_DEBUG_LOCKING_TIMES + fz_lock_taken[idx][lock] = clock(); +#endif +} + +void fz_lock_debug_unlock(fz_context *ctx, int lock) +{ + int idx = find_context(ctx); + if (idx < 0) + return; + + if (fz_locks_debug[idx][lock] == 0) + { + fprintf(stderr, "Attempt to release lock %d when not held!\n", lock); + } + fz_locks_debug[idx][lock] = 0; +#ifdef FITZ_DEBUG_LOCKING_TIMES + fz_lock_time[idx][lock] += clock() - fz_lock_taken[idx][lock]; +#endif +} + +#endif diff --git a/source/fitz/outline.c b/source/fitz/outline.c new file mode 100644 index 00000000..e26fd378 --- /dev/null +++ b/source/fitz/outline.c @@ -0,0 +1,62 @@ +#include "mupdf/fitz.h" + +void +fz_free_outline(fz_context *ctx, fz_outline *outline) +{ + while (outline) + { + fz_outline *next = outline->next; + fz_free_outline(ctx, outline->down); + fz_free(ctx, outline->title); + fz_free_link_dest(ctx, &outline->dest); + fz_free(ctx, outline); + outline = next; + } +} + +static void +do_debug_outline_xml(fz_output *out, fz_outline *outline, int level) +{ + while (outline) + { + fz_printf(out, "<outline title=\"%s\" page=\"%d\"", outline->title, outline->dest.kind == FZ_LINK_GOTO ? outline->dest.ld.gotor.page + 1 : 0); + if (outline->down) + { + fz_printf(out, ">\n"); + do_debug_outline_xml(out, outline->down, level + 1); + fz_printf(out, "</outline>\n"); + } + else + { + fz_printf(out, " />\n"); + } + outline = outline->next; + } +} + +void +fz_print_outline_xml(fz_context *ctx, fz_output *out, fz_outline *outline) +{ + do_debug_outline_xml(out, outline, 0); +} + +static void +do_debug_outline(fz_output *out, fz_outline *outline, int level) +{ + int i; + while (outline) + { + for (i = 0; i < level; i++) + fz_printf(out, "\t"); + fz_printf(out, "%s\t%d\n", outline->title, outline->dest.kind == FZ_LINK_GOTO ? outline->dest.ld.gotor.page + 1 : 0); + if (outline->down) + do_debug_outline(out, outline->down, level + 1); + outline = outline->next; + } +} + +void +fz_print_outline(fz_context *ctx, fz_output *out, fz_outline *outline) +{ + do_debug_outline(out, outline, 0); +} diff --git a/source/fitz/output-pcl.c b/source/fitz/output-pcl.c new file mode 100644 index 00000000..eae5dad2 --- /dev/null +++ b/source/fitz/output-pcl.c @@ -0,0 +1,856 @@ +#include "mupdf/fitz.h" + +/* Lifted from ghostscript gdevjlm.h */ +/* + * The notion that there is such a thing as a "PCL printer" is a fiction: no + * two "PCL" printers, even at the same PCL level, have identical command + * sets. (The H-P documentation isn't fully accurate either; for example, + * it doesn't reveal that the DeskJet printers implement anything beyond PCL + * 3.) + * + * This file contains feature definitions for a generic monochrome PCL + * driver (gdevdljm.c), and the specific feature values for all such + * printers that Ghostscript currently supports. + */ + +/* Printer spacing capabilities. Include at most one of these. */ +#define PCL_NO_SPACING 0 /* no vertical spacing capability, must be 0 */ +#define PCL3_SPACING 1 /* <ESC>*p+<n>Y (PCL 3) */ +#define PCL4_SPACING 2 /* <ESC>*b<n>Y (PCL 4) */ +#define PCL5_SPACING 4 /* <ESC>*b<n>Y and clear seed row (PCL 5) */ +/* The following is only used internally. */ +#define PCL_ANY_SPACING \ + (PCL3_SPACING | PCL4_SPACING | PCL5_SPACING) + +/* Individual printer properties. Any subset of these may be included. */ +#define PCL_MODE_2_COMPRESSION 8 /* compression mode 2 supported */ + /* (PCL 4) */ +#define PCL_MODE_3_COMPRESSION 16 /* compression modes 2 & 3 supported */ + /* (PCL 5) */ +#define PCL_END_GRAPHICS_DOES_RESET 32 /* <esc>*rB resets all parameters */ +#define PCL_HAS_DUPLEX 64 /* <esc>&l<duplex>S supported */ +#define PCL_CAN_SET_PAPER_SIZE 128 /* <esc>&l<sizecode>A supported */ +#define PCL_CAN_PRINT_COPIES 256 /* <esc>&l<copies>X supported */ +#define HACK__IS_A_LJET4PJL 512 +#define HACK__IS_A_OCE9050 1024 + +/* Shorthands for the most common spacing/compression combinations. */ +#define PCL_MODE0 PCL3_SPACING +#define PCL_MODE0NS PCL_NO_SPACING +#define PCL_MODE2 (PCL4_SPACING | PCL_MODE_2_COMPRESSION) +#define PCL_MODE2P (PCL_NO_SPACING | PCL_MODE_2_COMPRESSION) +#define PCL_MODE3 (PCL5_SPACING | PCL_MODE_3_COMPRESSION) +#define PCL_MODE3NS (PCL_NO_SPACING | PCL_MODE_3_COMPRESSION) + +#define MIN_SKIP_LINES 7 +static const char *const from2to3 = "\033*b3M"; +static const char *const from3to2 = "\033*b2M"; +static const int penalty_from2to3 = 5; /* strlen(from2to3); */ +static const int penalty_from3to2 = 5; /* strlen(from3to2); */ + +/* H-P DeskJet */ +static const fz_pcl_options fz_pcl_options_ljet4 = +{ + (PCL_MODE2 | PCL_END_GRAPHICS_DOES_RESET | PCL_CAN_SET_PAPER_SIZE), + "\033&k1W\033*b2M", + "\033&k1W\033*b2M" +}; + +/* H-P DeskJet 500 */ +static const fz_pcl_options fz_pcl_options_dj500 = +{ + (PCL_MODE3 | PCL_END_GRAPHICS_DOES_RESET | PCL_CAN_SET_PAPER_SIZE), + "\033&k1W", + "\033&k1W" +}; + +/* Kyocera FS-600 */ +static const fz_pcl_options fz_pcl_options_fs600 = +{ + (PCL_MODE3 | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES), + "\033*r0F\033&u%dD", + "\033*r0F\033&u%dD" +}; + +/* H-P original LaserJet */ +/* H-P LaserJet Plus */ +static const fz_pcl_options fz_pcl_options_lj = +{ + (PCL_MODE0), + "\033*b0M", + "\033*b0M" +}; + +/* H-P LaserJet IIp, IId */ +static const fz_pcl_options fz_pcl_options_lj2 = +{ + (PCL_MODE2P | PCL_CAN_SET_PAPER_SIZE), + "\033*r0F\033*b2M", + "\033*r0F\033*b2M" +}; + +/* H-P LaserJet III* */ +static const fz_pcl_options fz_pcl_options_lj3 = +{ + (PCL_MODE3 | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES), + "\033&l-180u36Z\033*r0F", + "\033&l-180u36Z\033*r0F" +}; + +/* H-P LaserJet IIId */ +static const fz_pcl_options fz_pcl_options_lj3d = +{ + (PCL_MODE3 | PCL_HAS_DUPLEX | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES), + "\033&l-180u36Z\033*r0F", + "\033&l180u36Z\033*r0F" +}; + +/* H-P LaserJet 4 */ +static const fz_pcl_options fz_pcl_options_lj4 = +{ + (PCL_MODE3 | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES), + "\033&l-180u36Z\033*r0F\033&u%dD", + "\033&l-180u36Z\033*r0F\033&u%dD" +}; + +/* H-P LaserJet 4 PL */ +static const fz_pcl_options fz_pcl_options_lj4pl = +{ + (PCL_MODE3 | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES | HACK__IS_A_LJET4PJL), + "\033&l-180u36Z\033*r0F\033&u%dD", + "\033&l-180u36Z\033*r0F\033&u%dD" +}; + +/* H-P LaserJet 4d */ +static const fz_pcl_options fz_pcl_options_lj4d = +{ + (PCL_MODE3 | PCL_HAS_DUPLEX | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES), + "\033&l-180u36Z\033*r0F\033&u%dD", + "\033&l180u36Z\033*r0F\033&u%dD" +}; + +/* H-P 2563B line printer */ +static const fz_pcl_options fz_pcl_options_lp2563b = +{ + (PCL_MODE0NS | PCL_CAN_SET_PAPER_SIZE), + "\033*b0M", + "\033*b0M" +}; + +/* OCE 9050 line printer */ +static const fz_pcl_options fz_pcl_options_oce9050 = +{ + (PCL_MODE3NS | PCL_CAN_SET_PAPER_SIZE | HACK__IS_A_OCE9050), + "\033*b0M", + "\033*b0M" +}; + +static void copy_opts(fz_pcl_options *dst, const fz_pcl_options *src) +{ + if (dst) + *dst = *src; +} + +void fz_pcl_preset(fz_context *ctx, fz_pcl_options *opts, const char *preset) +{ + if (preset == NULL || *preset == 0 || !strcmp(preset, "ljet4")) + copy_opts(opts, &fz_pcl_options_ljet4); + else if (!strcmp(preset, "dj500")) + copy_opts(opts, &fz_pcl_options_dj500); + else if (!strcmp(preset, "fs600")) + copy_opts(opts, &fz_pcl_options_fs600); + else if (!strcmp(preset, "lj")) + copy_opts(opts, &fz_pcl_options_lj); + else if (!strcmp(preset, "lj2")) + copy_opts(opts, &fz_pcl_options_lj2); + else if (!strcmp(preset, "lj3")) + copy_opts(opts, &fz_pcl_options_lj3); + else if (!strcmp(preset, "lj3d")) + copy_opts(opts, &fz_pcl_options_lj3d); + else if (!strcmp(preset, "lj4")) + copy_opts(opts, &fz_pcl_options_lj4); + else if (!strcmp(preset, "lj4pl")) + copy_opts(opts, &fz_pcl_options_lj4pl); + else if (!strcmp(preset, "lj4d")) + copy_opts(opts, &fz_pcl_options_lj4d); + else if (!strcmp(preset, "lp2563b")) + copy_opts(opts, &fz_pcl_options_lp2563b); + else if (!strcmp(preset, "oce9050")) + copy_opts(opts, &fz_pcl_options_oce9050); + else + fz_throw(ctx, FZ_ERROR_GENERIC, "Unknown preset '%s'", preset); +} + +void fz_pcl_option(fz_context *ctx, fz_pcl_options *opts, const char *option, int val) +{ + if (opts == NULL) + return; + + if (!strcmp(option, "spacing")) + { + switch (val) + { + case 0: + opts->features &= ~PCL_ANY_SPACING; + break; + case 1: + opts->features = (opts->features & ~PCL_ANY_SPACING) | PCL3_SPACING; + break; + case 2: + opts->features = (opts->features & ~PCL_ANY_SPACING) | PCL4_SPACING; + break; + case 3: + opts->features = (opts->features & ~PCL_ANY_SPACING) | PCL5_SPACING; + break; + default: + fz_throw(ctx, FZ_ERROR_GENERIC, "Unsupported PCL spacing %d (0-3 only)", val); + } + } + else if (!strcmp(option, "mode2")) + { + if (val == 0) + opts->features &= ~PCL_MODE_2_COMPRESSION; + else if (val == 1) + opts->features |= PCL_MODE_2_COMPRESSION; + else + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for mode2 value"); + } + else if (!strcmp(option, "mode3")) + { + if (val == 0) + opts->features &= ~PCL_MODE_3_COMPRESSION; + else if (val == 1) + opts->features |= PCL_MODE_3_COMPRESSION; + else + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for mode3 value"); + } + else if (!strcmp(option, "eog_reset")) + { + if (val == 0) + opts->features &= ~PCL_END_GRAPHICS_DOES_RESET; + else if (val == 1) + opts->features |= PCL_END_GRAPHICS_DOES_RESET; + else + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for eog_reset value"); + } + else if (!strcmp(option, "has_duplex")) + { + if (val == 0) + opts->features &= ~PCL_HAS_DUPLEX; + else if (val == 1) + opts->features |= PCL_HAS_DUPLEX; + else + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for has_duplex value"); + } + else if (!strcmp(option, "has_papersize")) + { + if (val == 0) + opts->features &= ~PCL_CAN_SET_PAPER_SIZE; + else if (val == 1) + opts->features |= PCL_CAN_SET_PAPER_SIZE; + else + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for has_papersize value"); + } + else if (!strcmp(option, "has_copies")) + { + if (val == 0) + opts->features &= ~PCL_CAN_PRINT_COPIES; + else if (val == 1) + opts->features |= PCL_CAN_PRINT_COPIES; + else + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for has_papersize value"); + } + else if (!strcmp(option, "is_ljet4pjl")) + { + if (val == 0) + opts->features &= ~HACK__IS_A_LJET4PJL; + else if (val == 1) + opts->features |= HACK__IS_A_LJET4PJL; + else + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for is_ljet4pjl value"); + } + else if (!strcmp(option, "is_oce9050")) + { + if (val == 0) + opts->features &= ~HACK__IS_A_OCE9050; + else if (val == 1) + opts->features |= HACK__IS_A_OCE9050; + else + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for is_oce9050 value"); + } + else + fz_throw(ctx, FZ_ERROR_GENERIC, "Unknown pcl option '%s'", option); +} + +static void +make_init(fz_pcl_options *pcl, char *buf, unsigned long len, const char *str, int res) +{ + int paper_source = -1; + + snprintf(buf, len, str, res); + + if (pcl->manual_feed_set && pcl->manual_feed) + paper_source = 2; + else if (pcl->media_position_set && pcl->media_position >= 0) + paper_source = pcl->media_position; + if (paper_source >= 0) + { + char buf2[40]; + snprintf(buf2, sizeof(buf2), "\033&l%dH", paper_source); + strncat(buf, buf2, len); + } +} + +static void +pcl_header(fz_output *out, fz_pcl_options *pcl, int num_copies, int xres) +{ + char odd_page_init[80]; + char even_page_init[80]; + + make_init(pcl, odd_page_init, sizeof(odd_page_init), pcl->odd_page_init, xres); + make_init(pcl, even_page_init, sizeof(even_page_init), pcl->even_page_init, xres); + + if (pcl->page_count == 0) + { + if (pcl->features & HACK__IS_A_LJET4PJL) + fz_puts(out, "\033%-12345X@PJL\r\n@PJL ENTER LANGUAGE = PCL\r\n"); + fz_puts(out, "\033E"); /* reset printer */ + /* If the printer supports it, set the paper size */ + /* based on the actual requested size. */ + if (pcl->features & PCL_CAN_SET_PAPER_SIZE) + fz_printf(out, "\033&l%dA", pcl->paper_size); + /* If printer can duplex, set duplex mode appropriately. */ + if (pcl->features & PCL_HAS_DUPLEX) + { + if (pcl->duplex_set) + { + if (pcl->duplex) + { + if (!pcl->tumble) + fz_puts(out, "\033&l1S"); + else + fz_puts(out, "\033&l2S"); + } + else + fz_puts(out, "\033&l0S"); + } + else + { + /* default to duplex for this printer */ + fz_puts(out, "\033&l1S"); + } + } + } + + /* Put out per-page initialization. */ + /* in duplex mode the sheet is already in process, so there are some + * commands which must not be sent to the printer for the 2nd page, + * as this commands will cause the printer to eject the sheet with + * only the 1st page printed. This commands are: + * \033&l%dA (setting paper size) + * \033&l%dH (setting paper tray) + * in simplex mode we set this parameters for each page, + * in duplex mode we set this parameters for each odd page + */ + + if ((pcl->features & PCL_HAS_DUPLEX) && pcl->duplex_set && pcl->duplex) + { + /* We are printing duplex, so change margins as needed */ + if (((pcl->page_count/num_copies)%2) == 0) + { + if (pcl->page_count != 0 && (pcl->features & PCL_CAN_SET_PAPER_SIZE)) + { + fz_printf(out, "\033&l%dA", pcl->paper_size); + } + fz_puts(out, "\033&l0o0l0E"); + fz_puts(out, pcl->odd_page_init); + } + else + fz_puts(out, pcl->even_page_init); + } + else + { + if (pcl->features & PCL_CAN_SET_PAPER_SIZE) + { + fz_printf(out, "\033&l%dA", pcl->paper_size); + } + fz_puts(out, "\033&l0o0l0E"); + fz_puts(out, pcl->odd_page_init); + } + + fz_printf(out, "\033&l%dX", num_copies); /* # of copies */ + + /* End raster graphics, position cursor at top. */ + fz_puts(out, "\033*rB\033*p0x0Y"); + + /* The DeskJet and DeskJet Plus reset everything upon */ + /* receiving \033*rB, so we must reinitialize graphics mode. */ + if (pcl->features & PCL_END_GRAPHICS_DOES_RESET) + { + fz_puts(out, pcl->odd_page_init); /* Assume this does the right thing */ + fz_printf(out, "\033&l%dX", num_copies); /* # of copies */ + } + + /* Set resolution. */ + fz_printf(out, "\033*t%dR", xres); + pcl->page_count++; +} + +void +fz_output_pcl(fz_output *out, const fz_pixmap *pixmap, fz_pcl_options *pcl) +{ + //unsigned char *sp; + //int y, x, sn, dn, ss; + fz_context *ctx; + + if (!out || !pixmap) + return; + + ctx = out->ctx; + + if (pixmap->n != 1 && pixmap->n != 2 && pixmap->n != 4) + fz_throw(ctx, FZ_ERROR_GENERIC, "pixmap must be grayscale or rgb to write as pcl"); + + pcl_header(out, pcl, 1, pixmap->xres); + +#if 0 + sn = pixmap->n; + dn = pixmap->n; + if (dn == 2 || dn == 4) + dn--; + + /* Now output the actual bitmap, using a packbits like compression */ + sp = pixmap->samples; + ss = pixmap->w * sn; + y = 0; + while (y < pixmap->h) + { + int yrep; + + assert(sp == pixmap->samples + y * ss); + + /* Count the number of times this line is repeated */ + for (yrep = 1; yrep < 256 && y+yrep < pixmap->h; yrep++) + { + if (memcmp(sp, sp + yrep * ss, ss) != 0) + break; + } + fz_write_byte(out, yrep-1); + + /* Encode the line */ + x = 0; + while (x < pixmap->w) + { + int d; + + assert(sp == pixmap->samples + y * ss + x * sn); + + /* How far do we have to look to find a repeated value? */ + for (d = 1; d < 128 && x+d < pixmap->w; d++) + { + if (memcmp(sp + (d-1)*sn, sp + d*sn, sn) == 0) + break; + } + if (d == 1) + { + int xrep; + + /* We immediately have a repeat (or we've hit + * the end of the line). Count the number of + * times this value is repeated. */ + for (xrep = 1; xrep < 128 && x+xrep < pixmap->w; xrep++) + { + if (memcmp(sp, sp + xrep*sn, sn) != 0) + break; + } + fz_write_byte(out, xrep-1); + fz_write(out, sp, dn); + sp += sn*xrep; + x += xrep; + } + else + { + fz_write_byte(out, 257-d); + x += d; + while (d > 0) + { + fz_write(out, sp, dn); + sp += sn; + d--; + } + } + } + + /* Move to the next line */ + sp += ss*(yrep-1); + y += yrep; + } +#endif +} + +/* + * Mode 2 Row compression routine for the HP DeskJet & LaserJet IIp. + * Compresses data from row up to end_row, storing the result + * starting at compressed. Returns the number of bytes stored. + * Runs of K<=127 literal bytes are encoded as K-1 followed by + * the bytes; runs of 2<=K<=127 identical bytes are encoded as + * 257-K followed by the byte. + * In the worst case, the result is N+(N/127)+1 bytes long, + * where N is the original byte count (end_row - row). + */ +int +mode2compress(unsigned char *out, unsigned char *in, int in_len) +{ + int x; + int out_len = 0; + int run; + + for (x = 0; x < in_len; x += run) + { + /* How far do we have to look to find a value that isn't repeated? */ + for (run = 1; run < 127 && x+run < in_len; run++) + if (in[0] != in[run]) + break; + if (run > 1) + { + /* We have a run of matching bytes */ + out[out_len++] = 257-run; + out[out_len++] = in[0]; + } + else + { + int i; + + /* How many literals do we need to copy? */ + for (run = 1; run < 127 && x+run < in_len; run++) + if (in[run] == in[run+1]) + break; + out[out_len++] = run-1; + for (i = 0; i < run; i++) + out[out_len++] = in[i]; + } + in += run; + } + return out_len; +} + +/* + * Mode 3 compression routine for the HP LaserJet III family. + * Compresses bytecount bytes starting at current, storing the result + * in compressed, comparing against and updating previous. + * Returns the number of bytes stored. In the worst case, + * the number of bytes is bytecount+(bytecount/8)+1. + */ +int +mode3compress(unsigned char *out, const unsigned char *in, unsigned char *prev, int in_len) +{ + unsigned char *compressed = out; + const unsigned char *cur = in; + const unsigned char *end = in + in_len; + + while (cur < end) { /* Detect a maximum run of unchanged bytes. */ + const unsigned char *run = cur; + const unsigned char *diff; + const unsigned char *stop; + int offset, cbyte; + + while (cur < end && *cur == *prev) { + cur++, prev++; + } + if (cur == end) + break; /* rest of row is unchanged */ + /* Detect a run of up to 8 changed bytes. */ + /* We know that *cur != *prev. */ + diff = cur; + stop = (end - cur > 8 ? cur + 8 : end); + do + { + *prev++ = *cur++; + } + while (cur < stop && *cur != *prev); + /* Now [run..diff) are unchanged, and */ + /* [diff..cur) are changed. */ + /* Generate the command byte(s). */ + offset = diff - run; + cbyte = (cur - diff - 1) << 5; + if (offset < 31) + *out++ = cbyte + offset; + else { + *out++ = cbyte + 31; + offset -= 31; + while (offset >= 255) + *out++ = 255, offset -= 255; + *out++ = offset; + } + /* Copy the changed data. */ + while (diff < cur) + *out++ = *diff++; + } + return out - compressed; +} + +void wind(void) +{} + +void +fz_output_pcl_bitmap(fz_output *out, const fz_bitmap *bitmap, fz_pcl_options *pcl) +{ + unsigned char *data, *out_data; + int y, ss, rmask, line_size; + fz_context *ctx; + int num_blank_lines; + int compression = -1; + unsigned char *prev_row = NULL; + unsigned char *out_row_mode_2 = NULL; + unsigned char *out_row_mode_3 = NULL; + int out_count; + int max_mode_2_size; + int max_mode_3_size; + + if (!out || !bitmap) + return; + + ctx = out->ctx; + + if (pcl->features & HACK__IS_A_OCE9050) + { + /* Enter HPGL/2 mode, begin plot, Initialise (start plot), Enter PCL mode */ + fz_puts(out, "\033%1BBPIN;\033%1A"); + } + + pcl_header(out, pcl, 1, bitmap->xres); + + fz_var(prev_row); + fz_var(out_row_mode_2); + fz_var(out_row_mode_3); + + fz_try(ctx) + { + num_blank_lines = 0; + rmask = ~0 << (-bitmap->w & 7); + line_size = (bitmap->w + 7)/8; + max_mode_2_size = line_size + (line_size/127) + 1; + max_mode_3_size = line_size + (line_size/8) + 1; + prev_row = fz_calloc(ctx, line_size, sizeof(unsigned char)); + out_row_mode_2 = fz_calloc(ctx, max_mode_2_size, sizeof(unsigned char)); + out_row_mode_3 = fz_calloc(ctx, max_mode_3_size, sizeof(unsigned char)); + + /* Transfer raster graphics. */ + data = bitmap->samples; + ss = bitmap->stride; + for (y = 0; y < bitmap->h; y++, data += ss) + { + unsigned char *end_data = data + line_size; + + if ((end_data[-1] & rmask) == 0) + { + end_data--; + while (end_data > data && end_data[-1] == 0) + end_data--; + } + if (end_data == data) + { + /* Blank line */ + num_blank_lines++; + continue; + } + wind(); + + /* We've reached a non-blank line. */ + /* Put out a spacing command if necessary. */ + if (num_blank_lines == y) { + /* We're at the top of a page. */ + if (pcl->features & PCL_ANY_SPACING) + { + if (num_blank_lines > 0) + fz_printf(out, "\033*p+%dY", num_blank_lines * bitmap->yres); + /* Start raster graphics. */ + fz_puts(out, "\033*r1A"); + } + else if (pcl->features & PCL_MODE_3_COMPRESSION) + { + /* Start raster graphics. */ + fz_puts(out, "\033*r1A"); + for (; num_blank_lines; num_blank_lines--) + fz_puts(out, "\033*b0W"); + } + else + { + /* Start raster graphics. */ + fz_puts(out, "\033*r1A"); + for (; num_blank_lines; num_blank_lines--) + fz_puts(out, "\033*bW"); + } + } + + /* Skip blank lines if any */ + else if (num_blank_lines != 0) + { + /* Moving down from current position causes head + * motion on the DeskJet, so if the number of lines + * is small, we're better off printing blanks. + * + * For Canon LBP4i and some others, <ESC>*b<n>Y + * doesn't properly clear the seed row if we are in + * compression mode 3. + */ + if ((num_blank_lines < MIN_SKIP_LINES && compression != 3) || + !(pcl->features & PCL_ANY_SPACING)) + { + int mode_3ns = ((pcl->features & PCL_MODE_3_COMPRESSION) && !(pcl->features & PCL_ANY_SPACING)); + if (mode_3ns && compression != 2) + { + /* Switch to mode 2 */ + fz_puts(out, from3to2); + compression = 2; + } + if (pcl->features & PCL_MODE_3_COMPRESSION) + { + /* Must clear the seed row. */ + fz_puts(out, "\033*b1Y"); + num_blank_lines--; + } + if (mode_3ns) + { + for (; num_blank_lines; num_blank_lines--) + fz_puts(out, "\033*b0W"); + } + else + { + for (; num_blank_lines; num_blank_lines--) + fz_puts(out, "\033*bW"); + } + } + else if (pcl->features & PCL3_SPACING) + fz_printf(out, "\033*p+%dY", num_blank_lines * bitmap->yres); + else + fz_printf(out, "\033*b%dY", num_blank_lines); + /* Clear the seed row (only matters for mode 3 compression). */ + memset(prev_row, 0, line_size); + } + num_blank_lines = 0; + + /* Choose the best compression mode for this particular line. */ + if (pcl->features & PCL_MODE_3_COMPRESSION) + { + /* Compression modes 2 and 3 are both available. Try + * both and see which produces the least output data. + */ + int count3 = mode3compress(out_row_mode_3, data, prev_row, line_size); + int count2 = mode2compress(out_row_mode_2, data, line_size); + int penalty3 = (compression == 3 ? 0 : penalty_from2to3); + int penalty2 = (compression == 2 ? 0 : penalty_from3to2); + + if (count3 + penalty3 < count2 + penalty2) + { + if (compression != 3) + fz_puts(out, from2to3); + compression = 3; + out_data = (unsigned char *)out_row_mode_3; + out_count = count3; + } + else + { + if (compression != 2) + fz_puts(out, from3to2); + compression = 2; + out_data = (unsigned char *)out_row_mode_2; + out_count = count2; + } + } + else if (pcl->features & PCL_MODE_2_COMPRESSION) + { + out_data = out_row_mode_2; + out_count = mode2compress(out_row_mode_2, data, line_size); + } + else + { + out_data = data; + out_count = line_size; + } + + /* Transfer the data */ + fz_printf(out, "\033*b%dW", out_count); + fz_write(out, out_data, out_count); + } + + /* end raster graphics and eject page */ + fz_puts(out, "\033*rB\f"); + + if (pcl->features & HACK__IS_A_OCE9050) + { + /* Pen up, pen select, advance full page, reset */ + fz_puts(out, "\033%1BPUSP0PG;\033E"); + } + } + fz_always(ctx) + { + fz_free(ctx, prev_row); + fz_free(ctx, out_row_mode_2); + fz_free(ctx, out_row_mode_3); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +void +fz_write_pcl(fz_context *ctx, fz_pixmap *pixmap, char *filename, int append, fz_pcl_options *pcl) +{ + FILE *fp; + fz_output *out = NULL; + + fp = fopen(filename, append ? "ab" : "wb"); + if (!fp) + { + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", filename, strerror(errno)); + } + + fz_var(out); + + fz_try(ctx) + { + out = fz_new_output_with_file(ctx, fp); + fz_output_pcl(out, pixmap, pcl); + } + fz_always(ctx) + { + fz_close_output(out); + fclose(fp); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +void +fz_write_pcl_bitmap(fz_context *ctx, fz_bitmap *bitmap, char *filename, int append, fz_pcl_options *pcl) +{ + FILE *fp; + fz_output *out = NULL; + + fp = fopen(filename, append ? "ab" : "wb"); + if (!fp) + { + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", filename, strerror(errno)); + } + + fz_var(out); + + fz_try(ctx) + { + out = fz_new_output_with_file(ctx, fp); + fz_output_pcl_bitmap(out, bitmap, pcl); + } + fz_always(ctx) + { + fz_close_output(out); + fclose(fp); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} diff --git a/source/fitz/output-pwg.c b/source/fitz/output-pwg.c new file mode 100644 index 00000000..fe1fcd2e --- /dev/null +++ b/source/fitz/output-pwg.c @@ -0,0 +1,318 @@ +#include "mupdf/fitz.h" + +void +fz_output_pwg_file_header(fz_output *out) +{ + static const unsigned char pwgsig[4] = { 'R', 'a', 'S', '2' }; + + /* Sync word */ + fz_write(out, pwgsig, 4); +} + +static void +output_header(fz_output *out, const fz_pwg_options *pwg, int xres, int yres, int w, int h, int bpp) +{ + static const char zero[64] = { 0 }; + int i; + + /* Page Header: */ + fz_write(out, pwg ? pwg->media_class : zero, 64); + fz_write(out, pwg ? pwg->media_color : zero, 64); + fz_write(out, pwg ? pwg->media_type : zero, 64); + fz_write(out, pwg ? pwg->output_type : zero, 64); + fz_write_int32be(out, pwg ? pwg->advance_distance : 0); + fz_write_int32be(out, pwg ? pwg->advance_media : 0); + fz_write_int32be(out, pwg ? pwg->collate : 0); + fz_write_int32be(out, pwg ? pwg->cut_media : 0); + fz_write_int32be(out, pwg ? pwg->duplex : 0); + fz_write_int32be(out, xres); + fz_write_int32be(out, yres); + /* CUPS format says that 284->300 are supposed to be the bbox of the + * page in points. PWG says 'Reserved'. */ + for (i=284; i < 300; i += 4) + fz_write(out, zero, 4); + fz_write_int32be(out, pwg ? pwg->insert_sheet : 0); + fz_write_int32be(out, pwg ? pwg->jog : 0); + fz_write_int32be(out, pwg ? pwg->leading_edge : 0); + /* CUPS format says that 312->320 are supposed to be the margins of + * the lower left hand edge of page in points. PWG says 'Reserved'. */ + for (i=312; i < 320; i += 4) + fz_write(out, zero, 4); + fz_write_int32be(out, pwg ? pwg->manual_feed : 0); + fz_write_int32be(out, pwg ? pwg->media_position : 0); + fz_write_int32be(out, pwg ? pwg->media_weight : 0); + fz_write_int32be(out, pwg ? pwg->mirror_print : 0); + fz_write_int32be(out, pwg ? pwg->negative_print : 0); + fz_write_int32be(out, pwg ? pwg->num_copies : 0); + fz_write_int32be(out, pwg ? pwg->orientation : 0); + fz_write_int32be(out, pwg ? pwg->output_face_up : 0); + fz_write_int32be(out, w * 72/ xres); /* Page size in points */ + fz_write_int32be(out, h * 72/ yres); + fz_write_int32be(out, pwg ? pwg->separations : 0); + fz_write_int32be(out, pwg ? pwg->tray_switch : 0); + fz_write_int32be(out, pwg ? pwg->tumble : 0); + fz_write_int32be(out, w); /* Page image in pixels */ + fz_write_int32be(out, h); + fz_write_int32be(out, pwg ? pwg->media_type_num : 0); + fz_write_int32be(out, bpp < 8 ? 1 : 8); /* Bits per color */ + fz_write_int32be(out, bpp); /* Bits per pixel */ + fz_write_int32be(out, (w * bpp + 7)/8); /* Bytes per line */ + fz_write_int32be(out, 0); /* Chunky pixels */ + fz_write_int32be(out, bpp == 1 ? 3 /* Black */ : (bpp == 8 ? 18 /* Sgray */ : 19 /* Srgb */)); /* Colorspace */ + fz_write_int32be(out, pwg ? pwg->compression : 0); + fz_write_int32be(out, pwg ? pwg->row_count : 0); + fz_write_int32be(out, pwg ? pwg->row_feed : 0); + fz_write_int32be(out, pwg ? pwg->row_step : 0); + fz_write_int32be(out, bpp <= 8 ? 1 : 3); /* Num Colors */ + for (i=424; i < 452; i += 4) + fz_write(out, zero, 4); + fz_write_int32be(out, 1); /* TotalPageCount */ + fz_write_int32be(out, 1); /* CrossFeedTransform */ + fz_write_int32be(out, 1); /* FeedTransform */ + fz_write_int32be(out, 0); /* ImageBoxLeft */ + fz_write_int32be(out, 0); /* ImageBoxTop */ + fz_write_int32be(out, w); /* ImageBoxRight */ + fz_write_int32be(out, h); /* ImageBoxBottom */ + for (i=480; i < 1668; i += 4) + fz_write(out, zero, 4); + fz_write(out, pwg ? pwg->rendering_intent : zero, 64); + fz_write(out, pwg ? pwg->page_size_name : zero, 64); +} + +void +fz_output_pwg_page(fz_output *out, const fz_pixmap *pixmap, const fz_pwg_options *pwg) +{ + unsigned char *sp; + int y, x, sn, dn, ss; + fz_context *ctx; + + if (!out || !pixmap) + return; + + ctx = out->ctx; + + if (pixmap->n != 1 && pixmap->n != 2 && pixmap->n != 4) + fz_throw(ctx, FZ_ERROR_GENERIC, "pixmap must be grayscale or rgb to write as pwg"); + + sn = pixmap->n; + dn = pixmap->n; + if (dn == 2 || dn == 4) + dn--; + + output_header(out, pwg, pixmap->xres, pixmap->yres, pixmap->w, pixmap->h, dn*8); + + /* Now output the actual bitmap, using a packbits like compression */ + sp = pixmap->samples; + ss = pixmap->w * sn; + y = 0; + while (y < pixmap->h) + { + int yrep; + + assert(sp == pixmap->samples + y * ss); + + /* Count the number of times this line is repeated */ + for (yrep = 1; yrep < 256 && y+yrep < pixmap->h; yrep++) + { + if (memcmp(sp, sp + yrep * ss, ss) != 0) + break; + } + fz_write_byte(out, yrep-1); + + /* Encode the line */ + x = 0; + while (x < pixmap->w) + { + int d; + + assert(sp == pixmap->samples + y * ss + x * sn); + + /* How far do we have to look to find a repeated value? */ + for (d = 1; d < 128 && x+d < pixmap->w; d++) + { + if (memcmp(sp + (d-1)*sn, sp + d*sn, sn) == 0) + break; + } + if (d == 1) + { + int xrep; + + /* We immediately have a repeat (or we've hit + * the end of the line). Count the number of + * times this value is repeated. */ + for (xrep = 1; xrep < 128 && x+xrep < pixmap->w; xrep++) + { + if (memcmp(sp, sp + xrep*sn, sn) != 0) + break; + } + fz_write_byte(out, xrep-1); + fz_write(out, sp, dn); + sp += sn*xrep; + x += xrep; + } + else + { + fz_write_byte(out, 257-d); + x += d; + while (d > 0) + { + fz_write(out, sp, dn); + sp += sn; + d--; + } + } + } + + /* Move to the next line */ + sp += ss*(yrep-1); + y += yrep; + } +} + +void +fz_output_pwg_bitmap_page(fz_output *out, const fz_bitmap *bitmap, const fz_pwg_options *pwg) +{ + unsigned char *sp; + int y, x, ss; + int byte_width; + + if (!out || !bitmap) + return; + + output_header(out, pwg, bitmap->xres, bitmap->yres, bitmap->w, bitmap->h, 1); + + /* Now output the actual bitmap, using a packbits like compression */ + sp = bitmap->samples; + ss = bitmap->stride; + byte_width = (bitmap->w+7)/8; + y = 0; + while (y < bitmap->h) + { + int yrep; + + assert(sp == bitmap->samples + y * ss); + + /* Count the number of times this line is repeated */ + for (yrep = 1; yrep < 256 && y+yrep < bitmap->h; yrep++) + { + if (memcmp(sp, sp + yrep * ss, byte_width) != 0) + break; + } + fz_write_byte(out, yrep-1); + + /* Encode the line */ + x = 0; + while (x < byte_width) + { + int d; + + assert(sp == bitmap->samples + y * ss + x); + + /* How far do we have to look to find a repeated value? */ + for (d = 1; d < 128 && x+d < byte_width; d++) + { + if (sp[d-1] == sp[d]) + break; + } + if (d == 1) + { + int xrep; + + /* We immediately have a repeat (or we've hit + * the end of the line). Count the number of + * times this value is repeated. */ + for (xrep = 1; xrep < 128 && x+xrep < byte_width; xrep++) + { + if (sp[0] != sp[xrep]) + break; + } + fz_write_byte(out, xrep-1); + fz_write(out, sp, 1); + sp += xrep; + x += xrep; + } + else + { + fz_write_byte(out, 257-d); + fz_write(out, sp, d); + sp += d; + x += d; + } + } + + /* Move to the next line */ + sp += ss*yrep - byte_width; + y += yrep; + } +} + +void +fz_output_pwg(fz_output *out, const fz_pixmap *pixmap, const fz_pwg_options *pwg) +{ + fz_output_pwg_file_header(out); + fz_output_pwg_page(out, pixmap, pwg); +} + +void +fz_write_pwg(fz_context *ctx, fz_pixmap *pixmap, char *filename, int append, const fz_pwg_options *pwg) +{ + FILE *fp; + fz_output *out = NULL; + + fp = fopen(filename, append ? "ab" : "wb"); + if (!fp) + { + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", filename, strerror(errno)); + } + + fz_var(out); + + fz_try(ctx) + { + out = fz_new_output_with_file(ctx, fp); + if (!append) + fz_output_pwg_file_header(out); + fz_output_pwg_page(out, pixmap, pwg); + } + fz_always(ctx) + { + fz_close_output(out); + fclose(fp); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +void +fz_write_pwg_bitmap(fz_context *ctx, fz_bitmap *bitmap, char *filename, int append, const fz_pwg_options *pwg) +{ + FILE *fp; + fz_output *out = NULL; + + fp = fopen(filename, append ? "ab" : "wb"); + if (!fp) + { + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", filename, strerror(errno)); + } + + fz_var(out); + + fz_try(ctx) + { + out = fz_new_output_with_file(ctx, fp); + if (!append) + fz_output_pwg_file_header(out); + fz_output_pwg_bitmap_page(out, bitmap, pwg); + } + fz_always(ctx) + { + fz_close_output(out); + fclose(fp); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} diff --git a/source/fitz/output.c b/source/fitz/output.c new file mode 100644 index 00000000..f98b945e --- /dev/null +++ b/source/fitz/output.c @@ -0,0 +1,100 @@ +#include "mupdf/fitz.h" + +static int +file_printf(fz_output *out, const char *fmt, va_list ap) +{ + FILE *file = (FILE *)out->opaque; + + return vfprintf(file, fmt, ap); +} + +static int +file_write(fz_output *out, const void *buffer, int count) +{ + FILE *file = (FILE *)out->opaque; + + return fwrite(buffer, 1, count, file); +} + +fz_output * +fz_new_output_with_file(fz_context *ctx, FILE *file) +{ + fz_output *out = fz_malloc_struct(ctx, fz_output); + out->ctx = ctx; + out->opaque = file; + out->printf = file_printf; + out->write = file_write; + out->close = NULL; + return out; +} + +void +fz_close_output(fz_output *out) +{ + if (!out) + return; + if (out->close) + out->close(out); + fz_free(out->ctx, out); +} + +int +fz_printf(fz_output *out, const char *fmt, ...) +{ + int ret; + va_list ap; + + if (!out) + return 0; + + va_start(ap, fmt); + ret = out->printf(out, fmt, ap); + va_end(ap); + + return ret; +} + +int +fz_write(fz_output *out, const void *data, int len) +{ + if (!out) + return 0; + return out->write(out, data, len); +} + +int +fz_puts(fz_output *out, const char *str) +{ + if (!out) + return 0; + return out->write(out, str, strlen(str)); +} + +static int +buffer_printf(fz_output *out, const char *fmt, va_list list) +{ + fz_buffer *buffer = (fz_buffer *)out->opaque; + + return fz_buffer_vprintf(out->ctx, buffer, fmt, list); +} + +static int +buffer_write(fz_output *out, const void *data, int len) +{ + fz_buffer *buffer = (fz_buffer *)out->opaque; + + fz_write_buffer(out->ctx, buffer, (unsigned char *)data, len); + return len; +} + +fz_output * +fz_new_output_with_buffer(fz_context *ctx, fz_buffer *buf) +{ + fz_output *out = fz_malloc_struct(ctx, fz_output); + out->ctx = ctx; + out->opaque = buf; + out->printf = buffer_printf; + out->write = buffer_write; + out->close = NULL; + return out; +} diff --git a/source/fitz/path.c b/source/fitz/path.c new file mode 100644 index 00000000..cbdfc7bc --- /dev/null +++ b/source/fitz/path.c @@ -0,0 +1,507 @@ +#include <assert.h> +#include "mupdf/fitz.h" + +fz_path * +fz_new_path(fz_context *ctx) +{ + fz_path *path; + + path = fz_malloc_struct(ctx, fz_path); + path->len = 0; + path->cap = 0; + path->items = NULL; + path->last = -1; + + return path; +} + +fz_path * +fz_clone_path(fz_context *ctx, fz_path *old) +{ + fz_path *path; + + assert(old); + path = fz_malloc_struct(ctx, fz_path); + fz_try(ctx) + { + path->len = old->len; + path->cap = old->len; + path->items = fz_malloc_array(ctx, path->cap, sizeof(fz_path_item)); + memcpy(path->items, old->items, sizeof(fz_path_item) * path->len); + } + fz_catch(ctx) + { + fz_free(ctx, path); + fz_rethrow(ctx); + } + + return path; +} + +void +fz_free_path(fz_context *ctx, fz_path *path) +{ + if (path == NULL) + return; + fz_free(ctx, path->items); + fz_free(ctx, path); +} + +static void +grow_path(fz_context *ctx, fz_path *path, int n) +{ + int newcap = path->cap; + if (path->len + n <= path->cap) + { + path->last = path->len; + return; + } + while (path->len + n > newcap) + newcap = newcap + 36; + path->items = fz_resize_array(ctx, path->items, newcap, sizeof(fz_path_item)); + path->cap = newcap; + path->last = path->len; +} + +fz_point +fz_currentpoint(fz_context *ctx, 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; +} + +void +fz_moveto(fz_context *ctx, fz_path *path, float x, float y) +{ + if (path->last >= 0 && path->items[path->last].k == FZ_MOVETO) + { + /* No point in having MOVETO then MOVETO */ + path->len = path->last; + } + grow_path(ctx, path, 3); + path->items[path->len++].k = FZ_MOVETO; + path->items[path->len++].v = x; + path->items[path->len++].v = y; +} + +void +fz_lineto(fz_context *ctx, fz_path *path, float x, float y) +{ + float x0, y0; + + if (path->last < 0) + { + fz_warn(ctx, "lineto with no current point"); + return; + } + if (path->items[path->last].k == FZ_CLOSE_PATH) + { + x0 = path->items[path->last-2].v; + y0 = path->items[path->last-1].v; + } + else + { + x0 = path->items[path->len-2].v; + y0 = path->items[path->len-1].v; + } + /* Anything other than MoveTo followed by LineTo the same place is a nop */ + if (path->items[path->last].k != FZ_MOVETO && x0 == x && y0 == y) + return; + grow_path(ctx, path, 3); + path->items[path->len++].k = FZ_LINETO; + path->items[path->len++].v = x; + path->items[path->len++].v = y; +} + +void +fz_curveto(fz_context *ctx, fz_path *path, + float x1, float y1, + float x2, float y2, + float x3, float y3) +{ + float x0, y0; + + if (path->last < 0) + { + fz_warn(ctx, "curveto with no current point"); + return; + } + if (path->items[path->last].k == FZ_CLOSE_PATH) + { + x0 = path->items[path->last-2].v; + y0 = path->items[path->last-1].v; + } + else + { + x0 = path->items[path->len-2].v; + y0 = path->items[path->len-1].v; + } + + /* Check for degenerate cases: */ + if (x0 == x1 && y0 == y1) + { + if (x2 == x3 && y2 == y3) + { + /* If (x1,y1)==(x2,y2) and prev wasn't a moveto, then skip */ + if (x1 == x2 && y1 == y2 && path->items[path->last].k != FZ_MOVETO) + return; + /* Otherwise a line will suffice */ + fz_lineto(ctx, path, x3, y3); + return; + } + if (x1 == x2 && y1 == y2) + { + /* A line will suffice */ + fz_lineto(ctx, path, x3, y3); + return; + } + } + else if (x1 == x2 && y1 == y2 && x2 == x3 && y2 == y3) + { + /* A line will suffice */ + fz_lineto(ctx, path, x3, y3); + return; + } + + grow_path(ctx, path, 7); + path->items[path->len++].k = FZ_CURVETO; + path->items[path->len++].v = x1; + path->items[path->len++].v = y1; + path->items[path->len++].v = x2; + path->items[path->len++].v = y2; + path->items[path->len++].v = x3; + path->items[path->len++].v = y3; +} + +void +fz_curvetov(fz_context *ctx, fz_path *path, float x2, float y2, float x3, float y3) +{ + float x1, y1; + if (path->last < 0) + { + fz_warn(ctx, "curvetov with no current point"); + return; + } + if (path->items[path->last].k == FZ_CLOSE_PATH) + { + x1 = path->items[path->last-2].v; + y1 = path->items[path->last-1].v; + } + else + { + x1 = path->items[path->len-2].v; + y1 = path->items[path->len-1].v; + } + fz_curveto(ctx, path, x1, y1, x2, y2, x3, y3); +} + +void +fz_curvetoy(fz_context *ctx, fz_path *path, float x1, float y1, float x3, float y3) +{ + fz_curveto(ctx, path, x1, y1, x3, y3, x3, y3); +} + +void +fz_closepath(fz_context *ctx, fz_path *path) +{ + if (path->last < 0) + { + fz_warn(ctx, "closepath with no current point"); + return; + } + /* CLOSE following a CLOSE is a NOP */ + if (path->items[path->last].k == FZ_CLOSE_PATH) + return; + grow_path(ctx, path, 1); + path->items[path->len++].k = FZ_CLOSE_PATH; +} + +static inline fz_rect *bound_expand(fz_rect *r, const fz_point *p) +{ + if (p->x < r->x0) r->x0 = p->x; + if (p->y < r->y0) r->y0 = p->y; + if (p->x > r->x1) r->x1 = p->x; + if (p->y > r->y1) r->y1 = p->y; + return r; +} + +fz_rect * +fz_bound_path(fz_context *ctx, fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, fz_rect *r) +{ + fz_point p; + int i = 0; + + /* If the path is empty, return the empty rectangle here - don't wait + * for it to be expanded in the stroked case below. */ + if (path->len == 0) + { + *r = fz_empty_rect; + return r; + } + /* A path must start with a moveto - and if that's all there is + * then the path is empty. */ + if (path->len == 3) + { + *r = fz_empty_rect; + return r; + } + + p.x = path->items[1].v; + p.y = path->items[2].v; + fz_transform_point(&p, ctm); + r->x0 = r->x1 = p.x; + r->y0 = r->y1 = p.y; + + while (i < path->len) + { + switch (path->items[i++].k) + { + case FZ_CURVETO: + p.x = path->items[i++].v; + p.y = path->items[i++].v; + bound_expand(r, fz_transform_point(&p, ctm)); + p.x = path->items[i++].v; + p.y = path->items[i++].v; + bound_expand(r, fz_transform_point(&p, ctm)); + p.x = path->items[i++].v; + p.y = path->items[i++].v; + bound_expand(r, fz_transform_point(&p, ctm)); + break; + case FZ_MOVETO: + if (i + 2 == path->len) + { + /* Trailing Moveto - cannot affect bbox */ + i += 2; + break; + } + /* fallthrough */ + case FZ_LINETO: + p.x = path->items[i++].v; + p.y = path->items[i++].v; + bound_expand(r, fz_transform_point(&p, ctm)); + break; + case FZ_CLOSE_PATH: + break; + } + } + + if (stroke) + { + fz_adjust_rect_for_stroke(r, stroke, ctm); + } + + return r; +} + +fz_rect * +fz_adjust_rect_for_stroke(fz_rect *r, const fz_stroke_state *stroke, const fz_matrix *ctm) +{ + float expand; + + if (!stroke) + return r; + + expand = stroke->linewidth; + if (expand == 0) + expand = 1.0f; + expand *= fz_matrix_max_expansion(ctm); + if ((stroke->linejoin == FZ_LINEJOIN_MITER || stroke->linejoin == FZ_LINEJOIN_MITER_XPS) && stroke->miterlimit > 1) + expand *= stroke->miterlimit; + + r->x0 -= expand; + r->y0 -= expand; + r->x1 += expand; + r->y1 += expand; + return r; +} + +void +fz_transform_path(fz_context *ctx, fz_path *path, const fz_matrix *ctm) +{ + int k, i = 0; + + while (i < path->len) + { + switch (path->items[i++].k) + { + case FZ_CURVETO: + for (k = 0; k < 3; k++) + { + fz_transform_point((fz_point *)(void *)&path->items[i].v, ctm); + i += 2; + } + break; + case FZ_MOVETO: + case FZ_LINETO: + fz_transform_point((fz_point *)(void *)&path->items[i].v, ctm); + i += 2; + break; + case FZ_CLOSE_PATH: + break; + } + } +} + +#ifndef NDEBUG +void +fz_print_path(fz_context *ctx, FILE *out, fz_path *path, int indent) +{ + float x, y; + int i = 0; + int n; + while (i < path->len) + { + for (n = 0; n < indent; n++) + fputc(' ', out); + switch (path->items[i++].k) + { + case FZ_MOVETO: + x = path->items[i++].v; + y = path->items[i++].v; + fprintf(out, "%g %g m\n", x, y); + break; + case FZ_LINETO: + x = path->items[i++].v; + y = path->items[i++].v; + fprintf(out, "%g %g l\n", x, y); + break; + case FZ_CURVETO: + x = path->items[i++].v; + y = path->items[i++].v; + fprintf(out, "%g %g ", x, y); + x = path->items[i++].v; + y = path->items[i++].v; + fprintf(out, "%g %g ", x, y); + x = path->items[i++].v; + y = path->items[i++].v; + fprintf(out, "%g %g c\n", x, y); + break; + case FZ_CLOSE_PATH: + fprintf(out, "h\n"); + break; + } + } +} +#endif + +fz_stroke_state * +fz_keep_stroke_state(fz_context *ctx, fz_stroke_state *stroke) +{ + if (!stroke) + return NULL; + + fz_lock(ctx, FZ_LOCK_ALLOC); + if (stroke->refs > 0) + stroke->refs++; + fz_unlock(ctx, FZ_LOCK_ALLOC); + return stroke; +} + +void +fz_drop_stroke_state(fz_context *ctx, fz_stroke_state *stroke) +{ + int drop; + + if (!stroke) + return; + + fz_lock(ctx, FZ_LOCK_ALLOC); + drop = (stroke->refs > 0 ? --stroke->refs == 0 : 0); + fz_unlock(ctx, FZ_LOCK_ALLOC); + if (drop) + fz_free(ctx, stroke); +} + +fz_stroke_state * +fz_new_stroke_state_with_len(fz_context *ctx, int len) +{ + fz_stroke_state *state; + + len -= nelem(state->dash_list); + if (len < 0) + len = 0; + + state = Memento_label(fz_malloc(ctx, sizeof(*state) + sizeof(state->dash_list[0]) * len), "fz_stroke_state"); + state->refs = 1; + state->start_cap = FZ_LINECAP_BUTT; + state->dash_cap = FZ_LINECAP_BUTT; + state->end_cap = FZ_LINECAP_BUTT; + state->linejoin = FZ_LINEJOIN_MITER; + state->linewidth = 1; + state->miterlimit = 10; + state->dash_phase = 0; + state->dash_len = 0; + memset(state->dash_list, 0, sizeof(state->dash_list[0]) * (len + nelem(state->dash_list))); + + return state; +} + +fz_stroke_state * +fz_new_stroke_state(fz_context *ctx) +{ + return fz_new_stroke_state_with_len(ctx, 0); +} + +fz_stroke_state * +fz_unshare_stroke_state_with_len(fz_context *ctx, fz_stroke_state *shared, int len) +{ + int single, unsize, shsize, shlen, drop; + fz_stroke_state *unshared; + + fz_lock(ctx, FZ_LOCK_ALLOC); + single = (shared->refs == 1); + fz_unlock(ctx, FZ_LOCK_ALLOC); + + shlen = shared->dash_len - nelem(shared->dash_list); + if (shlen < 0) + shlen = 0; + shsize = sizeof(*shared) + sizeof(shared->dash_list[0]) * shlen; + len -= nelem(shared->dash_list); + if (len < 0) + len = 0; + if (single && shlen >= len) + return shared; + unsize = sizeof(*unshared) + sizeof(unshared->dash_list[0]) * len; + unshared = Memento_label(fz_malloc(ctx, unsize), "fz_stroke_state"); + memcpy(unshared, shared, (shsize > unsize ? unsize : shsize)); + unshared->refs = 1; + fz_lock(ctx, FZ_LOCK_ALLOC); + drop = (shared->refs > 0 ? --shared->refs == 0 : 0); + fz_unlock(ctx, FZ_LOCK_ALLOC); + if (drop) + fz_free(ctx, shared); + return unshared; +} + +fz_stroke_state * +fz_unshare_stroke_state(fz_context *ctx, fz_stroke_state *shared) +{ + return fz_unshare_stroke_state_with_len(ctx, shared, shared->dash_len); +} diff --git a/source/fitz/pixmap.c b/source/fitz/pixmap.c new file mode 100644 index 00000000..7391c17e --- /dev/null +++ b/source/fitz/pixmap.c @@ -0,0 +1,1062 @@ +#include "mupdf/fitz.h" + +fz_pixmap * +fz_keep_pixmap(fz_context *ctx, fz_pixmap *pix) +{ + return (fz_pixmap *)fz_keep_storable(ctx, &pix->storable); +} + +void +fz_drop_pixmap(fz_context *ctx, fz_pixmap *pix) +{ + fz_drop_storable(ctx, &pix->storable); +} + +void +fz_free_pixmap_imp(fz_context *ctx, fz_storable *pix_) +{ + fz_pixmap *pix = (fz_pixmap *)pix_; + + if (pix->colorspace) + fz_drop_colorspace(ctx, pix->colorspace); + if (pix->free_samples) + fz_free(ctx, pix->samples); + fz_free(ctx, pix); +} + +fz_pixmap * +fz_new_pixmap_with_data(fz_context *ctx, fz_colorspace *colorspace, int w, int h, unsigned char *samples) +{ + fz_pixmap *pix; + + if (w < 0 || h < 0) + fz_throw(ctx, FZ_ERROR_GENERIC, "Illegal dimensions for pixmap %d %d", w, h); + + pix = fz_malloc_struct(ctx, fz_pixmap); + FZ_INIT_STORABLE(pix, 1, fz_free_pixmap_imp); + pix->x = 0; + pix->y = 0; + pix->w = w; + pix->h = h; + pix->interpolate = 1; + pix->xres = 96; + pix->yres = 96; + pix->colorspace = NULL; + pix->n = 1; + + if (colorspace) + { + pix->colorspace = fz_keep_colorspace(ctx, colorspace); + pix->n = 1 + colorspace->n; + } + + pix->samples = samples; + if (samples) + { + pix->free_samples = 0; + } + else + { + fz_try(ctx) + { + if (pix->w + pix->n - 1 > INT_MAX / pix->n) + fz_throw(ctx, FZ_ERROR_GENERIC, "overly wide image"); + pix->samples = fz_malloc_array(ctx, pix->h, pix->w * pix->n); + } + fz_catch(ctx) + { + if (colorspace) + fz_drop_colorspace(ctx, colorspace); + fz_free(ctx, pix); + fz_rethrow(ctx); + } + pix->free_samples = 1; + } + + return pix; +} + +fz_pixmap * +fz_new_pixmap(fz_context *ctx, fz_colorspace *colorspace, int w, int h) +{ + return fz_new_pixmap_with_data(ctx, colorspace, w, h, NULL); +} + +fz_pixmap * +fz_new_pixmap_with_bbox(fz_context *ctx, fz_colorspace *colorspace, const fz_irect *r) +{ + fz_pixmap *pixmap; + pixmap = fz_new_pixmap(ctx, colorspace, r->x1 - r->x0, r->y1 - r->y0); + pixmap->x = r->x0; + pixmap->y = r->y0; + return pixmap; +} + +fz_pixmap * +fz_new_pixmap_with_bbox_and_data(fz_context *ctx, fz_colorspace *colorspace, const fz_irect *r, unsigned char *samples) +{ + fz_pixmap *pixmap = fz_new_pixmap_with_data(ctx, colorspace, r->x1 - r->x0, r->y1 - r->y0, samples); + pixmap->x = r->x0; + pixmap->y = r->y0; + return pixmap; +} + +fz_irect * +fz_pixmap_bbox(fz_context *ctx, fz_pixmap *pix, fz_irect *bbox) +{ + bbox->x0 = pix->x; + bbox->y0 = pix->y; + bbox->x1 = pix->x + pix->w; + bbox->y1 = pix->y + pix->h; + return bbox; +} + +fz_irect * +fz_pixmap_bbox_no_ctx(fz_pixmap *pix, fz_irect *bbox) +{ + bbox->x0 = pix->x; + bbox->y0 = pix->y; + bbox->x1 = pix->x + pix->w; + bbox->y1 = pix->y + pix->h; + return bbox; +} + +int +fz_pixmap_width(fz_context *ctx, fz_pixmap *pix) +{ + return pix->w; +} + +int +fz_pixmap_height(fz_context *ctx, fz_pixmap *pix) +{ + return pix->h; +} + +void +fz_clear_pixmap(fz_context *ctx, fz_pixmap *pix) +{ + memset(pix->samples, 0, (unsigned int)(pix->w * pix->h * pix->n)); +} + +void +fz_clear_pixmap_with_value(fz_context *ctx, fz_pixmap *pix, int value) +{ + if (value == 255) + { + memset(pix->samples, 255, (unsigned int)(pix->w * pix->h * pix->n)); + } + else + { + int k, x, y; + unsigned char *s = pix->samples; + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + for (k = 0; k < pix->n - 1; k++) + *s++ = value; + *s++ = 255; + } + } + } +} + +void +fz_copy_pixmap_rect(fz_context *ctx, fz_pixmap *dest, fz_pixmap *src, const fz_irect *b) +{ + const unsigned char *srcp; + unsigned char *destp; + int x, y, w, destspan, srcspan; + fz_irect local_b, bb; + + local_b = *b; + fz_intersect_irect(&local_b, fz_pixmap_bbox(ctx, dest, &bb)); + fz_intersect_irect(&local_b, fz_pixmap_bbox(ctx, src, &bb)); + w = local_b.x1 - local_b.x0; + y = local_b.y1 - local_b.y0; + if (w <= 0 || y <= 0) + return; + + srcspan = src->w * src->n; + srcp = src->samples + (unsigned int)(srcspan * (local_b.y0 - src->y) + src->n * (local_b.x0 - src->x)); + destspan = dest->w * dest->n; + destp = dest->samples + (unsigned int)(destspan * (local_b.y0 - dest->y) + dest->n * (local_b.x0 - dest->x)); + + if (src->n == dest->n) + { + w *= src->n; + do + { + memcpy(destp, srcp, w); + srcp += srcspan; + destp += destspan; + } + while (--y); + } + else if (src->n == 2 && dest->n == 4) + { + /* Copy, and convert from grey+alpha to rgb+alpha */ + srcspan -= w*2; + destspan -= w*4; + do + { + for (x = w; x > 0; x--) + { + unsigned char v = *srcp++; + unsigned char a = *srcp++; + *destp++ = v; + *destp++ = v; + *destp++ = v; + *destp++ = a; + } + srcp += srcspan; + destp += destspan; + } + while (--y); + } + else if (src->n == 4 && dest->n == 2) + { + /* Copy, and convert from rgb+alpha to grey+alpha */ + srcspan -= w*4; + destspan -= w*2; + do + { + for (x = w; x > 0; x--) + { + int v; + v = *srcp++; + v += *srcp++; + v += *srcp++; + *destp++ = (unsigned char)((v+1)/3); + *destp++ = *srcp++; + } + srcp += srcspan; + destp += destspan; + } + while (--y); + } + else + { + /* FIXME: Crap conversion */ + int z; + int sn = src->n-1; + int dn = dest->n-1; + + srcspan -= w*src->n; + destspan -= w*dest->n; + do + { + for (x = w; x > 0; x--) + { + int v = 0; + for (z = sn; z > 0; z--) + v += *srcp++; + v = (v * dn + (sn>>1)) / sn; + for (z = dn; z > 0; z--) + *destp++ = (unsigned char)v; + *destp++ = *srcp++; + } + srcp += srcspan; + destp += destspan; + } + while (--y); + } +} + +void +fz_clear_pixmap_rect_with_value(fz_context *ctx, fz_pixmap *dest, int value, const fz_irect *b) +{ + unsigned char *destp; + int x, y, w, k, destspan; + fz_irect bb; + fz_irect local_b = *b; + + fz_intersect_irect(&local_b, fz_pixmap_bbox(ctx, dest, &bb)); + w = local_b.x1 - local_b.x0; + y = local_b.y1 - local_b.y0; + if (w <= 0 || y <= 0) + return; + + destspan = dest->w * dest->n; + destp = dest->samples + (unsigned int)(destspan * (local_b.y0 - dest->y) + dest->n * (local_b.x0 - dest->x)); + if (value == 255) + do + { + memset(destp, 255, (unsigned int)(w * dest->n)); + destp += destspan; + } + while (--y); + else + do + { + unsigned char *s = destp; + for (x = 0; x < w; x++) + { + for (k = 0; k < dest->n - 1; k++) + *s++ = value; + *s++ = 255; + } + destp += destspan; + } + while (--y); +} + +void +fz_premultiply_pixmap(fz_context *ctx, fz_pixmap *pix) +{ + unsigned char *s = pix->samples; + unsigned char a; + int k, x, y; + + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + a = s[pix->n - 1]; + for (k = 0; k < pix->n - 1; k++) + s[k] = fz_mul255(s[k], a); + s += pix->n; + } + } +} + +void +fz_unmultiply_pixmap(fz_context *ctx, fz_pixmap *pix) +{ + unsigned char *s = pix->samples; + int a, inva; + int k, x, y; + + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + a = s[pix->n - 1]; + inva = a ? 255 * 256 / a : 0; + for (k = 0; k < pix->n - 1; k++) + s[k] = (s[k] * inva) >> 8; + s += pix->n; + } + } +} + +fz_pixmap * +fz_alpha_from_gray(fz_context *ctx, fz_pixmap *gray, int luminosity) +{ + fz_pixmap *alpha; + unsigned char *sp, *dp; + int len; + fz_irect bbox; + + assert(gray->n == 2); + + alpha = fz_new_pixmap_with_bbox(ctx, NULL, fz_pixmap_bbox(ctx, gray, &bbox)); + dp = alpha->samples; + sp = gray->samples; + if (!luminosity) + sp ++; + + len = gray->w * gray->h; + while (len--) + { + *dp++ = sp[0]; + sp += 2; + } + + return alpha; +} + +void +fz_invert_pixmap(fz_context *ctx, fz_pixmap *pix) +{ + unsigned char *s = pix->samples; + int k, x, y; + + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + for (k = 0; k < pix->n - 1; k++) + s[k] = 255 - s[k]; + s += pix->n; + } + } +} + +void fz_invert_pixmap_rect(fz_pixmap *image, const fz_irect *rect) +{ + unsigned char *p; + int x, y, n; + + int x0 = fz_clampi(rect->x0 - image->x, 0, image->w - 1); + int x1 = fz_clampi(rect->x1 - image->x, 0, image->w - 1); + int y0 = fz_clampi(rect->y0 - image->y, 0, image->h - 1); + int y1 = fz_clampi(rect->y1 - image->y, 0, image->h - 1); + + for (y = y0; y < y1; y++) + { + p = image->samples + (unsigned int)((y * image->w + x0) * image->n); + for (x = x0; x < x1; x++) + { + for (n = image->n; n > 1; n--, p++) + *p = 255 - *p; + p++; + } + } +} + +void +fz_gamma_pixmap(fz_context *ctx, fz_pixmap *pix, float gamma) +{ + unsigned char gamma_map[256]; + unsigned char *s = pix->samples; + int k, x, y; + + for (k = 0; k < 256; k++) + gamma_map[k] = pow(k / 255.0f, gamma) * 255; + + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + for (k = 0; k < pix->n - 1; k++) + s[k] = gamma_map[s[k]]; + s += pix->n; + } + } +} + +/* + * Write pixmap to PNM file (without alpha channel) + */ + +void +fz_write_pnm(fz_context *ctx, fz_pixmap *pixmap, char *filename) +{ + FILE *fp; + unsigned char *p; + int len; + + if (pixmap->n != 1 && pixmap->n != 2 && pixmap->n != 4) + fz_throw(ctx, FZ_ERROR_GENERIC, "pixmap must be grayscale or rgb to write as pnm"); + + fp = fopen(filename, "wb"); + if (!fp) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", filename, strerror(errno)); + + if (pixmap->n == 1 || pixmap->n == 2) + fprintf(fp, "P5\n"); + if (pixmap->n == 4) + fprintf(fp, "P6\n"); + fprintf(fp, "%d %d\n", pixmap->w, pixmap->h); + fprintf(fp, "255\n"); + + len = pixmap->w * pixmap->h; + p = pixmap->samples; + + switch (pixmap->n) + { + case 1: + fwrite(p, 1, len, fp); + break; + case 2: + while (len--) + { + putc(p[0], fp); + p += 2; + } + break; + case 4: + while (len--) + { + putc(p[0], fp); + putc(p[1], fp); + putc(p[2], fp); + p += 4; + } + } + + fclose(fp); +} + +/* + * Write pixmap to PAM file (with or without alpha channel) + */ + +void +fz_write_pam(fz_context *ctx, fz_pixmap *pixmap, char *filename, int savealpha) +{ + unsigned char *sp; + int y, w, k; + FILE *fp; + + int sn = pixmap->n; + int dn = pixmap->n; + if (!savealpha && dn > 1) + dn--; + + fp = fopen(filename, "wb"); + if (!fp) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", filename, strerror(errno)); + + fprintf(fp, "P7\n"); + fprintf(fp, "WIDTH %d\n", pixmap->w); + fprintf(fp, "HEIGHT %d\n", pixmap->h); + fprintf(fp, "DEPTH %d\n", dn); + fprintf(fp, "MAXVAL 255\n"); + if (pixmap->colorspace) + fprintf(fp, "# COLORSPACE %s\n", pixmap->colorspace->name); + switch (dn) + { + case 1: fprintf(fp, "TUPLTYPE GRAYSCALE\n"); break; + case 2: if (sn == 2) fprintf(fp, "TUPLTYPE GRAYSCALE_ALPHA\n"); break; + case 3: if (sn == 4) fprintf(fp, "TUPLTYPE RGB\n"); break; + case 4: if (sn == 4) fprintf(fp, "TUPLTYPE RGB_ALPHA\n"); break; + } + fprintf(fp, "ENDHDR\n"); + + sp = pixmap->samples; + for (y = 0; y < pixmap->h; y++) + { + w = pixmap->w; + while (w--) + { + for (k = 0; k < dn; k++) + putc(sp[k], fp); + sp += sn; + } + } + + fclose(fp); +} + +/* + * Write pixmap to PNG file (with or without alpha channel) + */ + +#include <zlib.h> + +static inline void big32(unsigned char *buf, unsigned int v) +{ + buf[0] = (v >> 24) & 0xff; + buf[1] = (v >> 16) & 0xff; + buf[2] = (v >> 8) & 0xff; + buf[3] = (v) & 0xff; +} + +static void putchunk(char *tag, unsigned char *data, int size, fz_output *out) +{ + unsigned int sum; + fz_write_int32be(out, size); + fz_write(out, tag, 4); + fz_write(out, data, size); + sum = crc32(0, NULL, 0); + sum = crc32(sum, (unsigned char*)tag, 4); + sum = crc32(sum, data, size); + fz_write_int32be(out, sum); +} + +void +fz_write_png(fz_context *ctx, fz_pixmap *pixmap, char *filename, int savealpha) +{ + FILE *fp = fopen(filename, "wb"); + fz_output *out = NULL; + + if (!fp) + { + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", filename, strerror(errno)); + } + + fz_var(out); + + fz_try(ctx) + { + out = fz_new_output_with_file(ctx, fp); + fz_output_png(out, pixmap, savealpha); + } + fz_always(ctx) + { + fz_close_output(out); + fclose(fp); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +void +fz_output_png(fz_output *out, const fz_pixmap *pixmap, int savealpha) +{ + static const unsigned char pngsig[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; + unsigned char head[13]; + unsigned char *udata = NULL; + unsigned char *cdata = NULL; + unsigned char *sp, *dp; + uLong usize, csize; + int y, x, k, sn, dn; + int color; + int err; + fz_context *ctx; + + if (!out || !pixmap) + return; + + ctx = out->ctx; + + fz_var(udata); + fz_var(cdata); + + if (pixmap->n != 1 && pixmap->n != 2 && pixmap->n != 4) + fz_throw(ctx, FZ_ERROR_GENERIC, "pixmap must be grayscale or rgb to write as png"); + + sn = pixmap->n; + dn = pixmap->n; + if (!savealpha && dn > 1) + dn--; + + switch (dn) + { + default: + case 1: color = 0; break; + case 2: color = 4; break; + case 3: color = 2; break; + case 4: color = 6; break; + } + + usize = (pixmap->w * dn + 1) * pixmap->h; + csize = compressBound(usize); + fz_try(ctx) + { + udata = fz_malloc(ctx, usize); + cdata = fz_malloc(ctx, csize); + } + fz_catch(ctx) + { + fz_free(ctx, udata); + fz_rethrow(ctx); + } + + sp = pixmap->samples; + dp = udata; + for (y = 0; y < pixmap->h; y++) + { + *dp++ = 1; /* sub prediction filter */ + for (x = 0; x < pixmap->w; x++) + { + for (k = 0; k < dn; k++) + { + if (x == 0) + dp[k] = sp[k]; + else + dp[k] = sp[k] - sp[k-sn]; + } + sp += sn; + dp += dn; + } + } + + err = compress(cdata, &csize, udata, usize); + if (err != Z_OK) + { + fz_free(ctx, udata); + fz_free(ctx, cdata); + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot compress image data"); + } + + big32(head+0, pixmap->w); + big32(head+4, pixmap->h); + head[8] = 8; /* depth */ + head[9] = color; + head[10] = 0; /* compression */ + head[11] = 0; /* filter */ + head[12] = 0; /* interlace */ + + fz_write(out, pngsig, 8); + putchunk("IHDR", head, 13, out); + putchunk("IDAT", cdata, csize, out); + putchunk("IEND", head, 0, out); + + fz_free(ctx, udata); + fz_free(ctx, cdata); +} + +fz_buffer * +fz_image_as_png(fz_context *ctx, fz_image *image, int w, int h) +{ + fz_pixmap *pix = fz_image_get_pixmap(ctx, image, image->w, image->h); + fz_buffer *buf = NULL; + fz_output *out; + + fz_var(buf); + fz_var(out); + + fz_try(ctx) + { + if (pix->colorspace != fz_device_gray(ctx) || pix->colorspace != fz_device_rgb(ctx)) + { + fz_pixmap *pix2 = fz_new_pixmap(ctx, fz_device_rgb(ctx), pix->w, pix->h); + fz_convert_pixmap(ctx, pix2, pix); + fz_drop_pixmap(ctx, pix); + pix = pix2; + } + buf = fz_new_buffer(ctx, 1024); + out = fz_new_output_with_buffer(ctx, buf); + fz_output_png(out, pix, 0); + } + fz_always(ctx) + { + fz_close_output(out); + fz_drop_pixmap(ctx, pix); + } + fz_catch(ctx) + { + fz_drop_buffer(ctx, buf); + fz_rethrow(ctx); + } + return buf; +} + +unsigned int +fz_pixmap_size(fz_context *ctx, fz_pixmap * pix) +{ + if (pix == NULL) + return 0; + return sizeof(*pix) + pix->n * pix->w * pix->h; +} + +#ifdef ARCH_ARM +static void +fz_subsample_pixmap_ARM(unsigned char *ptr, int w, int h, int f, int factor, + int n, int fwd, int back, int back2, int fwd2, + int divX, int back4, int fwd4, int fwd3, + int divY, int back5, int divXY) +__attribute__((naked)); + +static void +fz_subsample_pixmap_ARM(unsigned char *ptr, int w, int h, int f, int factor, + int n, int fwd, int back, int back2, int fwd2, + int divX, int back4, int fwd4, int fwd3, + int divY, int back5, int divXY) +{ + asm volatile( + ENTER_ARM + "stmfd r13!,{r1,r4-r11,r14} \n" + "@STACK:r1,<9>,factor,n,fwd,back,back2,fwd2,divX,back4,fwd4,fwd3,divY,back5,divXY\n" + "@ r0 = src = ptr \n" + "@ r1 = w \n" + "@ r2 = h \n" + "@ r3 = f \n" + "mov r9, r0 @ r9 = dst = ptr \n" + "ldr r6, [r13,#4*12] @ r6 = fwd \n" + "ldr r7, [r13,#4*13] @ r7 = back \n" + "subs r2, r2, r3 @ r2 = h -= f \n" + "blt 11f @ Skip if less than a full row \n" + "1: @ for (y = h; y > 0; y--) { \n" + "ldr r1, [r13] @ r1 = w \n" + "subs r1, r1, r3 @ r1 = w -= f \n" + "blt 6f @ Skip if less than a full col \n" + "ldr r4, [r13,#4*10] @ r4 = factor \n" + "ldr r8, [r13,#4*14] @ r8 = back2 \n" + "ldr r12,[r13,#4*15] @ r12= fwd2 \n" + "2: @ for (x = w; x > 0; x--) { \n" + "ldr r5, [r13,#4*11] @ for (nn = n; nn > 0; n--) { \n" + "3: @ \n" + "mov r14,#0 @ r14= v = 0 \n" + "sub r5, r5, r3, LSL #8 @ for (xx = f; xx > 0; x--) { \n" + "4: @ \n" + "add r5, r5, r3, LSL #16 @ for (yy = f; yy > 0; y--) { \n" + "5: @ \n" + "ldrb r11,[r0], r6 @ r11= *src src += fwd \n" + "subs r5, r5, #1<<16 @ xx-- \n" + "add r14,r14,r11 @ v += r11 \n" + "bgt 5b @ } \n" + "sub r0, r0, r7 @ src -= back \n" + "adds r5, r5, #1<<8 @ yy-- \n" + "blt 4b @ } \n" + "mov r14,r14,LSR r4 @ r14 = v >>= factor \n" + "strb r14,[r9], #1 @ *d++ = r14 \n" + "sub r0, r0, r8 @ s -= back2 \n" + "subs r5, r5, #1 @ n-- \n" + "bgt 3b @ } \n" + "add r0, r0, r12 @ s += fwd2 \n" + "subs r1, r1, r3 @ x -= f \n" + "bge 2b @ } \n" + "6: @ Less than a full column left \n" + "adds r1, r1, r3 @ x += f \n" + "beq 11f @ if (x == 0) next row \n" + "@ r0 = src \n" + "@ r1 = x \n" + "@ r2 = y \n" + "@ r3 = f \n" + "@ r4 = factor \n" + "@ r6 = fwd \n" + "@ r7 = back \n" + "@STACK:r1,<9>,factor,n,fwd,back,back2,fwd2,divX,back4,fwd4,fwd3,divY,back5,divXY\n" + "ldr r5, [r13,#4*11] @ for (nn = n; nn > 0; n--) { \n" + "ldr r4, [r13,#4*16] @ r4 = divX \n" + "ldr r8, [r13,#4*17] @ r8 = back4 \n" + "ldr r12,[r13,#4*18] @ r12= fwd4 \n" + "8: @ \n" + "mov r14,#0 @ r14= v = 0 \n" + "sub r5, r5, r1, LSL #8 @ for (xx = x; xx > 0; x--) { \n" + "9: @ \n" + "add r5, r5, r3, LSL #16 @ for (yy = f; yy > 0; y--) { \n" + "10: @ \n" + "ldrb r11,[r0], r6 @ r11= *src src += fwd \n" + "subs r5, r5, #1<<16 @ xx-- \n" + "add r14,r14,r11 @ v += r11 \n" + "bgt 10b @ } \n" + "sub r0, r0, r7 @ src -= back \n" + "adds r5, r5, #1<<8 @ yy-- \n" + "blt 9b @ } \n" + "mul r14,r4, r14 @ r14= v *= divX \n" + "mov r14,r14,LSR #16 @ r14= v >>= 16 \n" + "strb r14,[r9], #1 @ *d++ = r14 \n" + "sub r0, r0, r8 @ s -= back4 \n" + "subs r5, r5, #1 @ n-- \n" + "bgt 8b @ } \n" + "add r0, r0, r12 @ s += fwd4 \n" + "11: @ \n" + "ldr r14,[r13,#4*19] @ r14 = fwd3 \n" + "subs r2, r2, r3 @ h -= f \n" + "add r0, r0, r14 @ s += fwd3 \n" + "bge 1b @ } \n" + "adds r2, r2, r3 @ h += f \n" + "beq 21f @ if no stray row, end \n" + "@ So doing one last (partial) row \n" + "@STACK:r1,<9>,factor,n,fwd,back,back2,fwd2,divX,back4,fwd4,fwd3,divY,back5,divXY\n" + "@ r0 = src = ptr \n" + "@ r1 = w \n" + "@ r2 = h \n" + "@ r3 = f \n" + "@ r4 = factor \n" + "@ r5 = n \n" + "@ r6 = fwd \n" + "12: @ for (y = h; y > 0; y--) { \n" + "ldr r1, [r13] @ r1 = w \n" + "ldr r7, [r13,#4*21] @ r7 = back5 \n" + "ldr r8, [r13,#4*14] @ r8 = back2 \n" + "subs r1, r1, r3 @ r1 = w -= f \n" + "blt 17f @ Skip if less than a full col \n" + "ldr r4, [r13,#4*20] @ r4 = divY \n" + "ldr r12,[r13,#4*15] @ r12= fwd2 \n" + "13: @ for (x = w; x > 0; x--) { \n" + "ldr r5, [r13,#4*11] @ for (nn = n; nn > 0; n--) { \n" + "14: @ \n" + "mov r14,#0 @ r14= v = 0 \n" + "sub r5, r5, r3, LSL #8 @ for (xx = f; xx > 0; x--) { \n" + "15: @ \n" + "add r5, r5, r2, LSL #16 @ for (yy = y; yy > 0; y--) { \n" + "16: @ \n" + "ldrb r11,[r0], r6 @ r11= *src src += fwd \n" + "subs r5, r5, #1<<16 @ xx-- \n" + "add r14,r14,r11 @ v += r11 \n" + "bgt 16b @ } \n" + "sub r0, r0, r7 @ src -= back5 \n" + "adds r5, r5, #1<<8 @ yy-- \n" + "blt 15b @ } \n" + "mul r14,r4, r14 @ r14 = x *= divY \n" + "mov r14,r14,LSR #16 @ r14 = v >>= 16 \n" + "strb r14,[r9], #1 @ *d++ = r14 \n" + "sub r0, r0, r8 @ s -= back2 \n" + "subs r5, r5, #1 @ n-- \n" + "bgt 14b @ } \n" + "add r0, r0, r12 @ s += fwd2 \n" + "subs r1, r1, r3 @ x -= f \n" + "bge 13b @ } \n" + "17: @ Less than a full column left \n" + "adds r1, r1, r3 @ x += f \n" + "beq 21f @ if (x == 0) end \n" + "@ r0 = src \n" + "@ r1 = x \n" + "@ r2 = y \n" + "@ r3 = f \n" + "@ r4 = factor \n" + "@ r6 = fwd \n" + "@ r7 = back5 \n" + "@ r8 = back2 \n" + "@STACK:r1,<9>,factor,n,fwd,back,back2,fwd2,divX,back4,fwd4,fwd3,divY,back5,divXY\n" + "ldr r4, [r13,#4*22] @ r4 = divXY \n" + "ldr r5, [r13,#4*11] @ for (nn = n; nn > 0; n--) { \n" + "18: @ \n" + "mov r14,#0 @ r14= v = 0 \n" + "sub r5, r5, r1, LSL #8 @ for (xx = x; xx > 0; x--) { \n" + "19: @ \n" + "add r5, r5, r2, LSL #16 @ for (yy = y; yy > 0; y--) { \n" + "20: @ \n" + "ldrb r11,[r0],r6 @ r11= *src src += fwd \n" + "subs r5, r5, #1<<16 @ xx-- \n" + "add r14,r14,r11 @ v += r11 \n" + "bgt 20b @ } \n" + "sub r0, r0, r7 @ src -= back5 \n" + "adds r5, r5, #1<<8 @ yy-- \n" + "blt 19b @ } \n" + "mul r14,r4, r14 @ r14= v *= divX \n" + "mov r14,r14,LSR #16 @ r14= v >>= 16 \n" + "strb r14,[r9], #1 @ *d++ = r14 \n" + "sub r0, r0, r8 @ s -= back2 \n" + "subs r5, r5, #1 @ n-- \n" + "bgt 18b @ } \n" + "21: @ \n" + "ldmfd r13!,{r1,r4-r11,PC} @ pop, return to thumb \n" + ENTER_THUMB + ); +} + +#endif + +void +fz_subsample_pixmap(fz_context *ctx, fz_pixmap *tile, int factor) +{ + int dst_w, dst_h, w, h, fwd, fwd2, fwd3, back, back2, x, y, n, xx, yy, nn, f; + unsigned char *s, *d; + + if (!tile) + return; + s = d = tile->samples; + f = 1<<factor; + w = tile->w; + h = tile->h; + n = tile->n; + dst_w = (w + f-1)>>factor; + dst_h = (h + f-1)>>factor; + fwd = w*n; + back = f*fwd-n; + back2 = f*n-1; + fwd2 = (f-1)*n; + fwd3 = (f-1)*fwd; + factor *= 2; +#ifdef ARCH_ARM + { + int strayX = w%f; + int divX = (strayX ? 65536/(strayX*f) : 0); + int fwd4 = (strayX-1) * n; + int back4 = strayX*n-1; + int strayY = h%f; + int divY = (strayY ? 65536/(strayY*f) : 0); + int back5 = fwd * strayY - n; + int divXY = (strayY*strayX ? 65536/(strayX*strayY) : 0); + fz_subsample_pixmap_ARM(s, w, h, f, factor, n, fwd, back, + back2, fwd2, divX, back4, fwd4, fwd3, + divY, back5, divXY); + } +#else + for (y = h - f; y >= 0; y -= f) + { + for (x = w - f; x >= 0; x -= f) + { + for (nn = n; nn > 0; nn--) + { + int v = 0; + for (xx = f; xx > 0; xx--) + { + for (yy = f; yy > 0; yy--) + { + v += *s; + s += fwd; + } + s -= back; + } + *d++ = v >> factor; + s -= back2; + } + s += fwd2; + } + /* Do any strays */ + x += f; + if (x > 0) + { + int div = x * f; + int fwd4 = (x-1) * n; + int back4 = x*n-1; + for (nn = n; nn > 0; nn--) + { + int v = 0; + for (xx = x; xx > 0; xx--) + { + for (yy = f; yy > 0; yy--) + { + v += *s; + s += fwd; + } + s -= back; + } + *d++ = v / div; + s -= back4; + } + s += fwd4; + } + s += fwd3; + } + /* Do any stray line */ + y += f; + if (y > 0) + { + int div = y * f; + int back5 = fwd * y - n; + for (x = w - f; x >= 0; x -= f) + { + for (nn = n; nn > 0; nn--) + { + int v = 0; + for (xx = f; xx > 0; xx--) + { + for (yy = y; yy > 0; yy--) + { + v += *s; + s += fwd; + } + s -= back5; + } + *d++ = v / div; + s -= back2; + } + s += fwd2; + } + /* Do any stray at the end of the stray line */ + x += f; + if (x > 0) + { + div = x * y; + for (nn = n; nn > 0; nn--) + { + int v = 0; + for (xx = x; xx > 0; xx--) + { + for (yy = y; yy > 0; yy--) + { + v += *s; + s += fwd; + } + s -= back5; + } + *d++ = v / div; + s -= back2; + } + } + } +#endif + tile->w = dst_w; + tile->h = dst_h; + tile->samples = fz_resize_array(ctx, tile->samples, dst_w * n, dst_h); +} + +void +fz_pixmap_set_resolution(fz_pixmap *pix, int res) +{ + pix->xres = res; + pix->yres = res; +} + +void +fz_md5_pixmap(fz_pixmap *pix, unsigned char digest[16]) +{ + fz_md5 md5; + + fz_md5_init(&md5); + if (pix) + fz_md5_update(&md5, pix->samples, pix->w * pix->h * pix->n); + fz_md5_final(&md5, digest); +} diff --git a/source/fitz/shade.c b/source/fitz/shade.c new file mode 100644 index 00000000..b13dc215 --- /dev/null +++ b/source/fitz/shade.c @@ -0,0 +1,1096 @@ +#include "mupdf/fitz.h" + +#define SWAP(a,b) {fz_vertex *t = (a); (a) = (b); (b) = t;} + +static void +paint_tri(fz_mesh_processor *painter, fz_vertex *v0, fz_vertex *v1, fz_vertex *v2) +{ + painter->process(painter->process_arg, v0, v1, v2); +} + +static void +paint_quad(fz_mesh_processor *painter, fz_vertex *v0, fz_vertex *v1, fz_vertex *v2, fz_vertex *v3) +{ + /* For a quad with corners (in clockwise or anticlockwise order) are + * v0, v1, v2, v3. We can choose to split in in various different ways. + * Arbitrarily we can pick v0, v1, v3 for the first triangle. We then + * have to choose between v1, v2, v3 or v3, v2, v1 (or their equivalent + * rotations) for the second triangle. + * + * v1, v2, v3 has the property that both triangles share the same + * winding (useful if we were ever doing simple back face culling). + * + * v3, v2, v1 has the property that all the 'shared' edges (both + * within this quad, and with adjacent quads) are walked in the same + * direction every time. This can be useful in that depending on the + * implementation/rounding etc walking from A -> B can hit different + * pixels than walking from B->A. + * + * In the event neither of these things matter at the moment, as all + * the process functions where it matters order the edges from top to + * bottom before walking them. + */ + painter->process(painter->process_arg, v0, v1, v3); + painter->process(painter->process_arg, v3, v2, v1); +} + +static void +fz_process_mesh_type1(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_mesh_processor *painter) +{ + float *p = shade->u.f.fn_vals; + int xdivs = shade->u.f.xdivs; + int ydivs = shade->u.f.ydivs; + float x0 = shade->u.f.domain[0][0]; + float y0 = shade->u.f.domain[0][1]; + float x1 = shade->u.f.domain[1][0]; + float y1 = shade->u.f.domain[1][1]; + int xx, yy; + float y, yn, x; + fz_vertex vs[2][2]; + fz_vertex *v = vs[0]; + fz_vertex *vn = vs[1]; + int n = shade->colorspace->n; + fz_matrix local_ctm; + + fz_concat(&local_ctm, &shade->u.f.matrix, ctm); + + y = y0; + for (yy = 0; yy < ydivs; yy++) + { + yn = y0 + (y1 - y0) * (yy + 1) / ydivs; + + x = x0; + v[0].p.x = x; v[0].p.y = y; + fz_transform_point(&v[0].p, &local_ctm); + memcpy(v[0].c, p, n*sizeof(float)); + p += n; + v[1].p.x = x; v[1].p.y = yn; + fz_transform_point(&v[1].p, &local_ctm); + memcpy(v[1].c, p + xdivs*n, n*sizeof(float)); + for (xx = 0; xx < xdivs; xx++) + { + x = x0 + (x1 - x0) * (xx + 1) / xdivs; + + vn[0].p.x = x; vn[0].p.y = y; + fz_transform_point(&vn[0].p, &local_ctm); + memcpy(vn[0].c, p, n*sizeof(float)); + p += n; + vn[1].p.x = x; vn[1].p.y = yn; + fz_transform_point(&vn[1].p, &local_ctm); + memcpy(vn[1].c, p + xdivs*n, n*sizeof(float)); + + paint_quad(painter, &v[0], &vn[0], &vn[1], &v[1]); + SWAP(v,vn); + } + y = yn; + } +} + +/* FIXME: Nasty */ +#define HUGENUM 32000 /* how far to extend linear/radial shadings */ + +static fz_point +fz_point_on_circle(fz_point p, float r, float theta) +{ + p.x = p.x + cosf(theta) * r; + p.y = p.y + sinf(theta) * r; + + return p; +} + +static void +fz_process_mesh_type2(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_mesh_processor *painter) +{ + fz_point p0, p1, dir; + fz_vertex v0, v1, v2, v3; + fz_vertex e0, e1; + float theta; + + p0.x = shade->u.l_or_r.coords[0][0]; + p0.y = shade->u.l_or_r.coords[0][1]; + p1.x = shade->u.l_or_r.coords[1][0]; + p1.y = shade->u.l_or_r.coords[1][1]; + dir.x = p0.y - p1.y; + dir.y = p1.x - p0.x; + fz_transform_point(&p0, ctm); + fz_transform_point(&p1, ctm); + fz_transform_vector(&dir, ctm); + theta = atan2f(dir.y, dir.x); + + v0.p = fz_point_on_circle(p0, HUGENUM, theta); + v1.p = fz_point_on_circle(p1, HUGENUM, theta); + v2.p = fz_point_on_circle(p0, -HUGENUM, theta); + v3.p = fz_point_on_circle(p1, -HUGENUM, theta); + + v0.c[0] = 0; + v1.c[0] = 1; + v2.c[0] = 0; + v3.c[0] = 1; + + paint_quad(painter, &v0, &v2, &v3, &v1); + + if (shade->u.l_or_r.extend[0]) + { + e0.p.x = v0.p.x - (p1.x - p0.x) * HUGENUM; + e0.p.y = v0.p.y - (p1.y - p0.y) * HUGENUM; + + e1.p.x = v2.p.x - (p1.x - p0.x) * HUGENUM; + e1.p.y = v2.p.y - (p1.y - p0.y) * HUGENUM; + + e0.c[0] = 0; + e1.c[0] = 0; + v0.c[0] = 0; + v2.c[0] = 0; + + paint_quad(painter, &e0, &v0, &v2, &e1); + } + + if (shade->u.l_or_r.extend[1]) + { + e0.p.x = v1.p.x + (p1.x - p0.x) * HUGENUM; + e0.p.y = v1.p.y + (p1.y - p0.y) * HUGENUM; + + e1.p.x = v3.p.x + (p1.x - p0.x) * HUGENUM; + e1.p.y = v3.p.y + (p1.y - p0.y) * HUGENUM; + + e0.c[0] = 1; + e1.c[0] = 1; + v1.c[0] = 1; + v3.c[0] = 1; + + paint_quad(painter, &e0, &v1, &v3, &e1); + } +} + +/* FIXME: Nasty */ +#define RADSEGS 32 /* how many segments to generate for radial meshes */ + +static void +fz_paint_annulus(const fz_matrix *ctm, + fz_point p0, float r0, float c0, + fz_point p1, float r1, float c1, + fz_mesh_processor *painter) +{ + fz_vertex t0, t1, t2, t3, b0, b1, b2, b3; + float theta, step; + int i; + + theta = atan2f(p1.y - p0.y, p1.x - p0.x); + step = (float)M_PI * 2 / RADSEGS; + + for (i = 0; i < RADSEGS / 2; i++) + { + t0.p = fz_point_on_circle(p0, r0, theta + i * step); + t1.p = fz_point_on_circle(p0, r0, theta + i * step + step); + t2.p = fz_point_on_circle(p1, r1, theta + i * step); + t3.p = fz_point_on_circle(p1, r1, theta + i * step + step); + b0.p = fz_point_on_circle(p0, r0, theta - i * step); + b1.p = fz_point_on_circle(p0, r0, theta - i * step - step); + b2.p = fz_point_on_circle(p1, r1, theta - i * step); + b3.p = fz_point_on_circle(p1, r1, theta - i * step - step); + + fz_transform_point(&t0.p, ctm); + fz_transform_point(&t1.p, ctm); + fz_transform_point(&t2.p, ctm); + fz_transform_point(&t3.p, ctm); + fz_transform_point(&b0.p, ctm); + fz_transform_point(&b1.p, ctm); + fz_transform_point(&b2.p, ctm); + fz_transform_point(&b3.p, ctm); + + t0.c[0] = c0; + t1.c[0] = c0; + t2.c[0] = c1; + t3.c[0] = c1; + b0.c[0] = c0; + b1.c[0] = c0; + b2.c[0] = c1; + b3.c[0] = c1; + + paint_quad(painter, &t0, &t2, &t3, &t1); + paint_quad(painter, &b0, &b2, &b3, &b1); + } +} + +static void +fz_process_mesh_type3(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_mesh_processor *painter) +{ + fz_point p0, p1; + float r0, r1; + fz_point e; + float er, rs; + + p0.x = shade->u.l_or_r.coords[0][0]; + p0.y = shade->u.l_or_r.coords[0][1]; + r0 = shade->u.l_or_r.coords[0][2]; + + p1.x = shade->u.l_or_r.coords[1][0]; + p1.y = shade->u.l_or_r.coords[1][1]; + r1 = shade->u.l_or_r.coords[1][2]; + + if (shade->u.l_or_r.extend[0]) + { + if (r0 < r1) + rs = r0 / (r0 - r1); + else + rs = -HUGENUM; + + e.x = p0.x + (p1.x - p0.x) * rs; + e.y = p0.y + (p1.y - p0.y) * rs; + er = r0 + (r1 - r0) * rs; + + fz_paint_annulus(ctm, e, er, 0, p0, r0, 0, painter); + } + + fz_paint_annulus(ctm, p0, r0, 0, p1, r1, 1, painter); + + if (shade->u.l_or_r.extend[1]) + { + if (r0 > r1) + rs = r1 / (r1 - r0); + else + rs = -HUGENUM; + + e.x = p1.x + (p0.x - p1.x) * rs; + e.y = p1.y + (p0.y - p1.y) * rs; + er = r1 + (r0 - r1) * rs; + + fz_paint_annulus(ctm, p1, r1, 1, e, er, 1, painter); + } +} + +static inline float read_sample(fz_stream *stream, int bits, float min, float max) +{ + /* we use pow(2,x) because (1<<x) would overflow the math on 32-bit samples */ + float bitscale = 1 / (powf(2, bits) - 1); + return min + fz_read_bits(stream, bits) * (max - min) * bitscale; +} + +static void +fz_process_mesh_type4(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_mesh_processor *painter) +{ + fz_stream *stream = fz_open_compressed_buffer(ctx, shade->buffer); + fz_vertex v[4]; + fz_vertex *va = &v[0]; + fz_vertex *vb = &v[1]; + fz_vertex *vc = &v[2]; + fz_vertex *vd = &v[3]; + int flag, i, ncomp = painter->ncomp; + int bpflag = shade->u.m.bpflag; + int bpcoord = shade->u.m.bpcoord; + int bpcomp = shade->u.m.bpcomp; + float x0 = shade->u.m.x0; + float x1 = shade->u.m.x1; + float y0 = shade->u.m.y0; + float y1 = shade->u.m.y1; + float *c0 = shade->u.m.c0; + float *c1 = shade->u.m.c1; + + fz_try(ctx) + { + while (!fz_is_eof_bits(stream)) + { + flag = fz_read_bits(stream, bpflag); + vd->p.x = read_sample(stream, bpcoord, x0, x1); + vd->p.y = read_sample(stream, bpcoord, y0, y1); + fz_transform_point(&vd->p, ctm); + for (i = 0; i < ncomp; i++) + vd->c[i] = read_sample(stream, bpcomp, c0[i], c1[i]); + + switch (flag) + { + case 0: /* start new triangle */ + SWAP(va, vd); + + fz_read_bits(stream, bpflag); + vb->p.x = read_sample(stream, bpcoord, x0, x1); + vb->p.y = read_sample(stream, bpcoord, y0, y1); + fz_transform_point(&vb->p, ctm); + for (i = 0; i < ncomp; i++) + vb->c[i] = read_sample(stream, bpcomp, c0[i], c1[i]); + + fz_read_bits(stream, bpflag); + vc->p.x = read_sample(stream, bpcoord, x0, x1); + vc->p.y = read_sample(stream, bpcoord, y0, y1); + fz_transform_point(&vc->p, ctm); + for (i = 0; i < ncomp; i++) + vc->c[i] = read_sample(stream, bpcomp, c0[i], c1[i]); + + paint_tri(painter, va, vb, vc); + break; + + case 1: /* Vb, Vc, Vd */ + SWAP(va, vb); + SWAP(vb, vc); + SWAP(vc, vd); + paint_tri(painter, va, vb, vc); + break; + + case 2: /* Va, Vc, Vd */ + SWAP(vb, vc); + SWAP(vc, vd); + paint_tri(painter, va, vb, vc); + break; + } + } + } + fz_always(ctx) + { + fz_close(stream); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +static void +fz_process_mesh_type5(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_mesh_processor *painter) +{ + fz_stream *stream = fz_open_compressed_buffer(ctx, shade->buffer); + fz_vertex *buf = NULL; + fz_vertex *ref = NULL; + int first; + int ncomp = painter->ncomp; + int i, k; + int vprow = shade->u.m.vprow; + int bpcoord = shade->u.m.bpcoord; + int bpcomp = shade->u.m.bpcomp; + float x0 = shade->u.m.x0; + float x1 = shade->u.m.x1; + float y0 = shade->u.m.y0; + float y1 = shade->u.m.y1; + float *c0 = shade->u.m.c0; + float *c1 = shade->u.m.c1; + + fz_var(buf); + fz_var(ref); + + fz_try(ctx) + { + ref = fz_malloc_array(ctx, vprow, sizeof(fz_vertex)); + buf = fz_malloc_array(ctx, vprow, sizeof(fz_vertex)); + first = 1; + + while (!fz_is_eof_bits(stream)) + { + for (i = 0; i < vprow; i++) + { + buf[i].p.x = read_sample(stream, bpcoord, x0, x1); + buf[i].p.y = read_sample(stream, bpcoord, y0, y1); + fz_transform_point(&buf[i].p, ctm); + for (k = 0; k < ncomp; k++) + buf[i].c[k] = read_sample(stream, bpcomp, c0[k], c1[k]); + } + + if (!first) + for (i = 0; i < vprow - 1; i++) + paint_quad(painter, &ref[i], &ref[i+1], &buf[i+1], &buf[i]); + + SWAP(ref,buf); + first = 0; + } + } + fz_always(ctx) + { + fz_free(ctx, ref); + fz_free(ctx, buf); + fz_close(stream); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +/* Subdivide and tessellate tensor-patches */ + +typedef struct tensor_patch_s tensor_patch; + +struct tensor_patch_s +{ + fz_point pole[4][4]; + float color[4][FZ_MAX_COLORS]; +}; + +static void +triangulate_patch(fz_mesh_processor *painter, tensor_patch p) +{ + fz_vertex v0, v1, v2, v3; + int col_len = painter->ncomp * sizeof(v0.c[0]); + + v0.p = p.pole[0][0]; + memcpy(v0.c, p.color[0], col_len); + + v1.p = p.pole[0][3]; + memcpy(v1.c, p.color[1], col_len); + + v2.p = p.pole[3][3]; + memcpy(v2.c, p.color[2], col_len); + + v3.p = p.pole[3][0]; + memcpy(v3.c, p.color[3], col_len); + + paint_quad(painter, &v0, &v1, &v2, &v3); +} + +static inline void midcolor(float *c, float *c1, float *c2, int n) +{ + int i; + for (i = 0; i < n; i++) + c[i] = (c1[i] + c2[i]) * 0.5f; +} + +static void +split_curve(fz_point *pole, fz_point *q0, fz_point *q1, int polestep) +{ + /* + split bezier curve given by control points pole[0]..pole[3] + using de casteljau algo at midpoint and build two new + bezier curves q0[0]..q0[3] and q1[0]..q1[3]. all indices + should be multiplies by polestep == 1 for vertical bezier + curves in patch and == 4 for horizontal bezier curves due + to C's multi-dimensional matrix memory layout. + */ + + float x12 = (pole[1 * polestep].x + pole[2 * polestep].x) * 0.5f; + float y12 = (pole[1 * polestep].y + pole[2 * polestep].y) * 0.5f; + + q0[1 * polestep].x = (pole[0 * polestep].x + pole[1 * polestep].x) * 0.5f; + q0[1 * polestep].y = (pole[0 * polestep].y + pole[1 * polestep].y) * 0.5f; + q1[2 * polestep].x = (pole[2 * polestep].x + pole[3 * polestep].x) * 0.5f; + q1[2 * polestep].y = (pole[2 * polestep].y + pole[3 * polestep].y) * 0.5f; + + q0[2 * polestep].x = (q0[1 * polestep].x + x12) * 0.5f; + q0[2 * polestep].y = (q0[1 * polestep].y + y12) * 0.5f; + q1[1 * polestep].x = (x12 + q1[2 * polestep].x) * 0.5f; + q1[1 * polestep].y = (y12 + q1[2 * polestep].y) * 0.5f; + + q0[3 * polestep].x = (q0[2 * polestep].x + q1[1 * polestep].x) * 0.5f; + q0[3 * polestep].y = (q0[2 * polestep].y + q1[1 * polestep].y) * 0.5f; + q1[0 * polestep].x = (q0[2 * polestep].x + q1[1 * polestep].x) * 0.5f; + q1[0 * polestep].y = (q0[2 * polestep].y + q1[1 * polestep].y) * 0.5f; + + q0[0 * polestep].x = pole[0 * polestep].x; + q0[0 * polestep].y = pole[0 * polestep].y; + q1[3 * polestep].x = pole[3 * polestep].x; + q1[3 * polestep].y = pole[3 * polestep].y; +} + +static void +split_stripe(tensor_patch *p, tensor_patch *s0, tensor_patch *s1, int n) +{ + /* + split all horizontal bezier curves in patch, + creating two new patches with half the width. + */ + split_curve(&p->pole[0][0], &s0->pole[0][0], &s1->pole[0][0], 4); + split_curve(&p->pole[0][1], &s0->pole[0][1], &s1->pole[0][1], 4); + split_curve(&p->pole[0][2], &s0->pole[0][2], &s1->pole[0][2], 4); + split_curve(&p->pole[0][3], &s0->pole[0][3], &s1->pole[0][3], 4); + + /* interpolate the colors for the two new patches. */ + memcpy(s0->color[0], p->color[0], n * sizeof(s0->color[0][0])); + memcpy(s0->color[1], p->color[1], n * sizeof(s0->color[1][0])); + midcolor(s0->color[2], p->color[1], p->color[2], n); + midcolor(s0->color[3], p->color[0], p->color[3], n); + + memcpy(s1->color[0], s0->color[3], n * sizeof(s1->color[0][0])); + memcpy(s1->color[1], s0->color[2], n * sizeof(s1->color[1][0])); + memcpy(s1->color[2], p->color[2], n * sizeof(s1->color[2][0])); + memcpy(s1->color[3], p->color[3], n * sizeof(s1->color[3][0])); +} + +static void +draw_stripe(fz_mesh_processor *painter, tensor_patch *p, int depth) +{ + tensor_patch s0, s1; + + /* split patch into two half-height patches */ + split_stripe(p, &s0, &s1, painter->ncomp); + + depth--; + if (depth == 0) + { + /* if no more subdividing, draw two new patches... */ + triangulate_patch(painter, s1); + triangulate_patch(painter, s0); + } + else + { + /* ...otherwise, continue subdividing. */ + draw_stripe(painter, &s1, depth); + draw_stripe(painter, &s0, depth); + } +} + +static void +split_patch(tensor_patch *p, tensor_patch *s0, tensor_patch *s1, int n) +{ + /* + split all vertical bezier curves in patch, + creating two new patches with half the height. + */ + split_curve(p->pole[0], s0->pole[0], s1->pole[0], 1); + split_curve(p->pole[1], s0->pole[1], s1->pole[1], 1); + split_curve(p->pole[2], s0->pole[2], s1->pole[2], 1); + split_curve(p->pole[3], s0->pole[3], s1->pole[3], 1); + + /* interpolate the colors for the two new patches. */ + memcpy(s0->color[0], p->color[0], n * sizeof(s0->color[0][0])); + midcolor(s0->color[1], p->color[0], p->color[1], n); + midcolor(s0->color[2], p->color[2], p->color[3], n); + memcpy(s0->color[3], p->color[3], n * sizeof(s0->color[3][0])); + + memcpy(s1->color[0], s0->color[1], n * sizeof(s1->color[0][0])); + memcpy(s1->color[1], p->color[1], n * sizeof(s1->color[1][0])); + memcpy(s1->color[2], p->color[2], n * sizeof(s1->color[2][0])); + memcpy(s1->color[3], s0->color[2], n * sizeof(s1->color[3][0])); +} + +static void +draw_patch(fz_mesh_processor *painter, tensor_patch *p, int depth, int origdepth) +{ + tensor_patch s0, s1; + + /* split patch into two half-width patches */ + split_patch(p, &s0, &s1, painter->ncomp); + + depth--; + if (depth == 0) + { + /* if no more subdividing, draw two new patches... */ + draw_stripe(painter, &s0, origdepth); + draw_stripe(painter, &s1, origdepth); + } + else + { + /* ...otherwise, continue subdividing. */ + draw_patch(painter, &s0, depth, origdepth); + draw_patch(painter, &s1, depth, origdepth); + } +} + +static fz_point +compute_tensor_interior( + fz_point a, fz_point b, fz_point c, fz_point d, + fz_point e, fz_point f, fz_point g, fz_point h) +{ + fz_point pt; + + /* see equations at page 330 in pdf 1.7 */ + + pt.x = -4 * a.x; + pt.x += 6 * (b.x + c.x); + pt.x += -2 * (d.x + e.x); + pt.x += 3 * (f.x + g.x); + pt.x += -1 * h.x; + pt.x /= 9; + + pt.y = -4 * a.y; + pt.y += 6 * (b.y + c.y); + pt.y += -2 * (d.y + e.y); + pt.y += 3 * (f.y + g.y); + pt.y += -1 * h.y; + pt.y /= 9; + + return pt; +} + +static void +make_tensor_patch(tensor_patch *p, int type, fz_point *pt) +{ + if (type == 6) + { + /* see control point stream order at page 325 in pdf 1.7 */ + + p->pole[0][0] = pt[0]; + p->pole[0][1] = pt[1]; + p->pole[0][2] = pt[2]; + p->pole[0][3] = pt[3]; + p->pole[1][3] = pt[4]; + p->pole[2][3] = pt[5]; + p->pole[3][3] = pt[6]; + p->pole[3][2] = pt[7]; + p->pole[3][1] = pt[8]; + p->pole[3][0] = pt[9]; + p->pole[2][0] = pt[10]; + p->pole[1][0] = pt[11]; + + /* see equations at page 330 in pdf 1.7 */ + + p->pole[1][1] = compute_tensor_interior( + p->pole[0][0], p->pole[0][1], p->pole[1][0], p->pole[0][3], + p->pole[3][0], p->pole[3][1], p->pole[1][3], p->pole[3][3]); + + p->pole[1][2] = compute_tensor_interior( + p->pole[0][3], p->pole[0][2], p->pole[1][3], p->pole[0][0], + p->pole[3][3], p->pole[3][2], p->pole[1][0], p->pole[3][0]); + + p->pole[2][1] = compute_tensor_interior( + p->pole[3][0], p->pole[3][1], p->pole[2][0], p->pole[3][3], + p->pole[0][0], p->pole[0][1], p->pole[2][3], p->pole[0][3]); + + p->pole[2][2] = compute_tensor_interior( + p->pole[3][3], p->pole[3][2], p->pole[2][3], p->pole[3][0], + p->pole[0][3], p->pole[0][2], p->pole[2][0], p->pole[0][0]); + } + else if (type == 7) + { + /* see control point stream order at page 330 in pdf 1.7 */ + + p->pole[0][0] = pt[0]; + p->pole[0][1] = pt[1]; + p->pole[0][2] = pt[2]; + p->pole[0][3] = pt[3]; + p->pole[1][3] = pt[4]; + p->pole[2][3] = pt[5]; + p->pole[3][3] = pt[6]; + p->pole[3][2] = pt[7]; + p->pole[3][1] = pt[8]; + p->pole[3][0] = pt[9]; + p->pole[2][0] = pt[10]; + p->pole[1][0] = pt[11]; + p->pole[1][1] = pt[12]; + p->pole[1][2] = pt[13]; + p->pole[2][2] = pt[14]; + p->pole[2][1] = pt[15]; + } +} + +/* FIXME: Nasty */ +#define SUBDIV 3 /* how many levels to subdivide patches */ + +static void +fz_process_mesh_type6(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_mesh_processor *painter) +{ + fz_stream *stream = fz_open_compressed_buffer(ctx, shade->buffer); + float color_storage[2][4][FZ_MAX_COLORS]; + fz_point point_storage[2][12]; + int store = 0; + int ncomp = painter->ncomp; + int i, k; + int bpflag = shade->u.m.bpflag; + int bpcoord = shade->u.m.bpcoord; + int bpcomp = shade->u.m.bpcomp; + float x0 = shade->u.m.x0; + float x1 = shade->u.m.x1; + float y0 = shade->u.m.y0; + float y1 = shade->u.m.y1; + float *c0 = shade->u.m.c0; + float *c1 = shade->u.m.c1; + + fz_try(ctx) + { + float (*prevc)[FZ_MAX_COLORS] = NULL; + fz_point *prevp = NULL; + while (!fz_is_eof_bits(stream)) + { + float (*c)[FZ_MAX_COLORS] = color_storage[store]; + fz_point *v = point_storage[store]; + int startcolor; + int startpt; + int flag; + tensor_patch patch; + + flag = fz_read_bits(stream, bpflag); + + if (flag == 0) + { + startpt = 0; + startcolor = 0; + } + else + { + startpt = 4; + startcolor = 2; + } + + for (i = startpt; i < 12; i++) + { + v[i].x = read_sample(stream, bpcoord, x0, x1); + v[i].y = read_sample(stream, bpcoord, y0, y1); + fz_transform_point(&v[i], ctm); + } + + for (i = startcolor; i < 4; i++) + { + for (k = 0; k < ncomp; k++) + c[i][k] = read_sample(stream, bpcomp, c0[k], c1[k]); + } + + if (flag == 0) + { + } + else if (flag == 1 && prevc) + { + v[0] = prevp[3]; + v[1] = prevp[4]; + v[2] = prevp[5]; + v[3] = prevp[6]; + memcpy(c[0], prevc[1], ncomp * sizeof(float)); + memcpy(c[1], prevc[2], ncomp * sizeof(float)); + } + else if (flag == 2 && prevc) + { + v[0] = prevp[6]; + v[1] = prevp[7]; + v[2] = prevp[8]; + v[3] = prevp[9]; + memcpy(c[0], prevc[2], ncomp * sizeof(float)); + memcpy(c[1], prevc[3], ncomp * sizeof(float)); + } + else if (flag == 3 && prevc) + { + v[0] = prevp[ 9]; + v[1] = prevp[10]; + v[2] = prevp[11]; + v[3] = prevp[ 0]; + memcpy(c[0], prevc[3], ncomp * sizeof(float)); + memcpy(c[1], prevc[0], ncomp * sizeof(float)); + } + else + continue; + + make_tensor_patch(&patch, 6, v); + + for (i = 0; i < 4; i++) + memcpy(patch.color[i], c[i], ncomp * sizeof(float)); + + draw_patch(painter, &patch, SUBDIV, SUBDIV); + + prevp = v; + prevc = c; + store ^= 1; + } + } + fz_always(ctx) + { + fz_close(stream); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +static void +fz_process_mesh_type7(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_mesh_processor *painter) +{ + fz_stream *stream = fz_open_compressed_buffer(ctx, shade->buffer); + int bpflag = shade->u.m.bpflag; + int bpcoord = shade->u.m.bpcoord; + int bpcomp = shade->u.m.bpcomp; + float x0 = shade->u.m.x0; + float x1 = shade->u.m.x1; + float y0 = shade->u.m.y0; + float y1 = shade->u.m.y1; + float *c0 = shade->u.m.c0; + float *c1 = shade->u.m.c1; + float color_storage[2][4][FZ_MAX_COLORS]; + fz_point point_storage[2][16]; + int store = 0; + int ncomp = painter->ncomp; + int i, k; + float (*prevc)[FZ_MAX_COLORS] = NULL; + fz_point (*prevp) = NULL; + + fz_try(ctx) + { + while (!fz_is_eof_bits(stream)) + { + float (*c)[FZ_MAX_COLORS] = color_storage[store]; + fz_point *v = point_storage[store]; + int startcolor; + int startpt; + int flag; + tensor_patch patch; + + flag = fz_read_bits(stream, bpflag); + + if (flag == 0) + { + startpt = 0; + startcolor = 0; + } + else + { + startpt = 4; + startcolor = 2; + } + + for (i = startpt; i < 16; i++) + { + v[i].x = read_sample(stream, bpcoord, x0, x1); + v[i].y = read_sample(stream, bpcoord, y0, y1); + fz_transform_point(&v[i], ctm); + } + + for (i = startcolor; i < 4; i++) + { + for (k = 0; k < ncomp; k++) + c[i][k] = read_sample(stream, bpcomp, c0[k], c1[k]); + } + + if (flag == 0) + { + } + else if (flag == 1 && prevc) + { + v[0] = prevp[3]; + v[1] = prevp[4]; + v[2] = prevp[5]; + v[3] = prevp[6]; + memcpy(c[0], prevc[1], ncomp * sizeof(float)); + memcpy(c[1], prevc[2], ncomp * sizeof(float)); + } + else if (flag == 2 && prevc) + { + v[0] = prevp[6]; + v[1] = prevp[7]; + v[2] = prevp[8]; + v[3] = prevp[9]; + memcpy(c[0], prevc[2], ncomp * sizeof(float)); + memcpy(c[1], prevc[3], ncomp * sizeof(float)); + } + else if (flag == 3 && prevc) + { + v[0] = prevp[ 9]; + v[1] = prevp[10]; + v[2] = prevp[11]; + v[3] = prevp[ 0]; + memcpy(c[0], prevc[3], ncomp * sizeof(float)); + memcpy(c[1], prevc[0], ncomp * sizeof(float)); + } + else + continue; /* We have no patch! */ + + make_tensor_patch(&patch, 7, v); + + for (i = 0; i < 4; i++) + memcpy(patch.color[i], c[i], ncomp * sizeof(float)); + + draw_patch(painter, &patch, SUBDIV, SUBDIV); + + prevp = v; + prevc = c; + store ^= 1; + } + } + fz_always(ctx) + { + fz_close(stream); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +void +fz_process_mesh(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, + fz_mesh_process_fn *process, void *process_arg) +{ + fz_mesh_processor painter; + + painter.ctx = ctx; + painter.shade = shade; + painter.process = process; + painter.process_arg = process_arg; + painter.ncomp = (shade->use_function > 0 ? 1 : shade->colorspace->n); + + if (shade->type == FZ_FUNCTION_BASED) + fz_process_mesh_type1(ctx, shade, ctm, &painter); + else if (shade->type == FZ_LINEAR) + fz_process_mesh_type2(ctx, shade, ctm, &painter); + else if (shade->type == FZ_RADIAL) + fz_process_mesh_type3(ctx, shade, ctm, &painter); + else if (shade->type == FZ_MESH_TYPE4) + fz_process_mesh_type4(ctx, shade, ctm, &painter); + else if (shade->type == FZ_MESH_TYPE5) + fz_process_mesh_type5(ctx, shade, ctm, &painter); + else if (shade->type == FZ_MESH_TYPE6) + fz_process_mesh_type6(ctx, shade, ctm, &painter); + else if (shade->type == FZ_MESH_TYPE7) + fz_process_mesh_type7(ctx, shade, ctm, &painter); + else + fz_throw(ctx, FZ_ERROR_GENERIC, "Unexpected mesh type %d\n", shade->type); +} + +static fz_rect * +fz_bound_mesh_type1(fz_context *ctx, fz_shade *shade, fz_rect *bbox) +{ + bbox->x0 = shade->u.f.domain[0][0]; + bbox->y0 = shade->u.f.domain[0][1]; + bbox->x1 = shade->u.f.domain[1][0]; + bbox->y1 = shade->u.f.domain[1][1]; + return fz_transform_rect(bbox, &shade->u.f.matrix); +} + +static fz_rect * +fz_bound_mesh_type2(fz_context *ctx, fz_shade *shade, fz_rect *bbox) +{ + /* FIXME: If axis aligned and not extended, the bbox may only be + * infinite in one direction */ + *bbox = fz_infinite_rect; + return bbox; +} + +static fz_rect * +fz_bound_mesh_type3(fz_context *ctx, fz_shade *shade, fz_rect *bbox) +{ + fz_point p0, p1; + float r0, r1; + + r0 = shade->u.l_or_r.coords[0][2]; + r1 = shade->u.l_or_r.coords[1][2]; + + if (shade->u.l_or_r.extend[0]) + { + if (r0 >= r1) + { + *bbox = fz_infinite_rect; + return bbox; + } + } + + if (shade->u.l_or_r.extend[1]) + { + if (r0 <= r1) + { + *bbox = fz_infinite_rect; + return bbox; + } + } + + p0.x = shade->u.l_or_r.coords[0][0]; + p0.y = shade->u.l_or_r.coords[0][1]; + p1.x = shade->u.l_or_r.coords[1][0]; + p1.y = shade->u.l_or_r.coords[1][1]; + + bbox->x0 = p0.x - r0; bbox->y0 = p0.y - r0; + bbox->x1 = p0.x + r0; bbox->y1 = p0.x + r0; + if (bbox->x0 > p1.x - r1) + bbox->x0 = p1.x - r1; + if (bbox->x1 < p1.x + r1) + bbox->x1 = p1.x + r1; + if (bbox->y0 > p1.y - r1) + bbox->y0 = p1.y - r1; + if (bbox->y1 < p1.y + r1) + bbox->y1 = p1.y + r1; + return bbox; +} + +static fz_rect * +fz_bound_mesh_type4567(fz_context *ctx, fz_shade *shade, fz_rect *bbox) +{ + bbox->x0 = shade->u.m.x0; + bbox->y0 = shade->u.m.y0; + bbox->x1 = shade->u.m.x1; + bbox->y1 = shade->u.m.y1; + return bbox; +} + +static fz_rect * +fz_bound_mesh(fz_context *ctx, fz_shade *shade, fz_rect *bbox) +{ + if (shade->type == FZ_FUNCTION_BASED) + fz_bound_mesh_type1(ctx, shade, bbox); + else if (shade->type == FZ_LINEAR) + fz_bound_mesh_type2(ctx, shade, bbox); + else if (shade->type == FZ_RADIAL) + fz_bound_mesh_type3(ctx, shade, bbox); + else if (shade->type == FZ_MESH_TYPE4 || + shade->type == FZ_MESH_TYPE5 || + shade->type == FZ_MESH_TYPE6 || + shade->type == FZ_MESH_TYPE7) + fz_bound_mesh_type4567(ctx, shade, bbox); + else + fz_throw(ctx, FZ_ERROR_GENERIC, "Unexpected mesh type %d\n", shade->type); + + return bbox; +} + +fz_shade * +fz_keep_shade(fz_context *ctx, fz_shade *shade) +{ + return (fz_shade *)fz_keep_storable(ctx, &shade->storable); +} + +void +fz_free_shade_imp(fz_context *ctx, fz_storable *shade_) +{ + fz_shade *shade = (fz_shade *)shade_; + + if (shade->colorspace) + fz_drop_colorspace(ctx, shade->colorspace); + if (shade->type == FZ_FUNCTION_BASED) + fz_free(ctx, shade->u.f.fn_vals); + fz_free_compressed_buffer(ctx, shade->buffer); + fz_free(ctx, shade); +} + +void +fz_drop_shade(fz_context *ctx, fz_shade *shade) +{ + fz_drop_storable(ctx, &shade->storable); +} + +fz_rect * +fz_bound_shade(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_rect *s) +{ + fz_matrix local_ctm; + fz_rect rect; + + fz_concat(&local_ctm, &shade->matrix, ctm); + *s = shade->bbox; + if (shade->type != FZ_LINEAR && shade->type != FZ_RADIAL) + { + fz_bound_mesh(ctx, shade, &rect); + fz_intersect_rect(s, &rect); + } + return fz_transform_rect(s, &local_ctm); +} + +#ifndef NDEBUG +void +fz_print_shade(fz_context *ctx, FILE *out, fz_shade *shade) +{ + int i; + + fprintf(out, "shading {\n"); + + switch (shade->type) + { + case FZ_FUNCTION_BASED: fprintf(out, "\ttype function_based\n"); break; + case FZ_LINEAR: fprintf(out, "\ttype linear\n"); break; + case FZ_RADIAL: fprintf(out, "\ttype radial\n"); break; + default: /* MESH */ fprintf(out, "\ttype mesh\n"); break; + } + + fprintf(out, "\tbbox [%g %g %g %g]\n", + shade->bbox.x0, shade->bbox.y0, + shade->bbox.x1, shade->bbox.y1); + + fprintf(out, "\tcolorspace %s\n", shade->colorspace->name); + + fprintf(out, "\tmatrix [%g %g %g %g %g %g]\n", + shade->matrix.a, shade->matrix.b, shade->matrix.c, + shade->matrix.d, shade->matrix.e, shade->matrix.f); + + if (shade->use_background) + { + fprintf(out, "\tbackground ["); + for (i = 0; i < shade->colorspace->n; i++) + fprintf(out, "%s%g", i == 0 ? "" : " ", shade->background[i]); + fprintf(out, "]\n"); + } + + if (shade->use_function) + { + fprintf(out, "\tfunction\n"); + } + + fprintf(out, "}\n"); +} +#endif diff --git a/source/fitz/stext-device.c b/source/fitz/stext-device.c new file mode 100644 index 00000000..89cf8566 --- /dev/null +++ b/source/fitz/stext-device.c @@ -0,0 +1,1027 @@ +#include "mupdf/fitz.h" +#include "ucdn.h" + +/* Extract text into an unsorted span soup. */ + +#define LINE_DIST 0.9f +#define SPACE_DIST 0.2f +#define SPACE_MAX_DIST 0.8f +#define PARAGRAPH_DIST 0.5f + +#undef DEBUG_SPANS +#undef DEBUG_INTERNALS +#undef DEBUG_LINE_HEIGHTS +#undef DEBUG_MASKS +#undef DEBUG_ALIGN +#undef DEBUG_INDENTS + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_ADVANCES_H + +typedef struct fz_text_device_s fz_text_device; + +typedef struct span_soup_s span_soup; + +struct fz_text_device_s +{ + fz_text_sheet *sheet; + fz_text_page *page; + span_soup *spans; + fz_text_span *cur_span; + int lastchar; +}; + +static fz_rect * +add_point_to_rect(fz_rect *a, const fz_point *p) +{ + if (p->x < a->x0) + a->x0 = p->x; + if (p->x > a->x1) + a->x1 = p->x; + if (p->y < a->y0) + a->y0 = p->y; + if (p->y > a->y1) + a->y1 = p->y; + return a; +} + +fz_rect * +fz_text_char_bbox(fz_rect *bbox, fz_text_span *span, int i) +{ + fz_point a, d; + const fz_point *max; + fz_text_char *ch; + + if (!span || i >= span->len) + { + *bbox = fz_empty_rect; + } + ch = &span->text[i]; + if (i == span->len-1) + max = &span->max; + else + max = &span->text[i+1].p; + a.x = 0; + a.y = span->ascender_max; + fz_transform_vector(&a, &span->transform); + d.x = 0; + d.y = span->descender_min; + fz_transform_vector(&d, &span->transform); + bbox->x0 = bbox->x1 = ch->p.x + a.x; + bbox->y0 = bbox->y1 = ch->p.y + a.y; + a.x += max->x; + a.y += max->y; + add_point_to_rect(bbox, &a); + a.x = ch->p.x + d.x; + a.y = ch->p.y + d.y; + add_point_to_rect(bbox, &a); + a.x = max->x + d.x; + a.y = max->y + d.y; + add_point_to_rect(bbox, &a); + return bbox; +} + +static void +add_bbox_to_span(fz_text_span *span) +{ + fz_point a, d; + fz_rect *bbox = &span->bbox; + + if (!span) + return; + a.x = 0; + a.y = span->ascender_max; + fz_transform_vector(&a, &span->transform); + d.x = 0; + d.y = span->descender_min; + fz_transform_vector(&d, &span->transform); + bbox->x0 = bbox->x1 = span->min.x + a.x; + bbox->y0 = bbox->y1 = span->min.y + a.y; + a.x += span->max.x; + a.y += span->max.y; + add_point_to_rect(bbox, &a); + a.x = span->min.x + d.x; + a.y = span->min.y + d.y; + add_point_to_rect(bbox, &a); + a.x = span->max.x + d.x; + a.y = span->max.y + d.y; + add_point_to_rect(bbox, &a); +} + +struct span_soup_s +{ + fz_context *ctx; + int len, cap; + fz_text_span **spans; +}; + +static span_soup * +new_span_soup(fz_context *ctx) +{ + span_soup *soup = fz_malloc_struct(ctx, span_soup); + soup->ctx = ctx; + soup->len = 0; + soup->cap = 0; + soup->spans = NULL; + return soup; +} + +static void +free_span_soup(span_soup *soup) +{ + int i; + + if (soup == NULL) + return; + for (i = 0; i < soup->len; i++) + { + fz_free(soup->ctx, soup->spans[i]); + } + fz_free(soup->ctx, soup->spans); + fz_free(soup->ctx, soup); +} + +static void +add_span_to_soup(span_soup *soup, fz_text_span *span) +{ + if (span == NULL) + return; + if (soup->len == soup->cap) + { + int newcap = (soup->cap ? soup->cap * 2 : 16); + soup->spans = fz_resize_array(soup->ctx, soup->spans, newcap, sizeof(*soup->spans)); + soup->cap = newcap; + } + add_bbox_to_span(span); + soup->spans[soup->len++] = span; +} + +static fz_text_line * +push_span(fz_context *ctx, fz_text_device *tdev, fz_text_span *span, int new_line, float distance) +{ + fz_text_line *line; + fz_text_block *block; + fz_text_page *page = tdev->page; + int prev_not_text = 0; + + if (page->len == 0 || page->blocks[page->len-1].type != FZ_PAGE_BLOCK_TEXT) + prev_not_text = 1; + + if (new_line || prev_not_text) + { + float size = fz_matrix_expansion(&span->transform); + /* So, a new line. Part of the same block or not? */ + if (distance == 0 || distance > size * 1.5 || distance < -size * PARAGRAPH_DIST || page->len == 0 || prev_not_text) + { + /* New block */ + if (page->len == page->cap) + { + int newcap = (page->cap ? page->cap*2 : 4); + page->blocks = fz_resize_array(ctx, page->blocks, newcap, sizeof(*page->blocks)); + page->cap = newcap; + } + block = fz_malloc_struct(ctx, fz_text_block); + page->blocks[page->len].type = FZ_PAGE_BLOCK_TEXT; + page->blocks[page->len].u.text = block; + block->cap = 0; + block->len = 0; + block->lines = 0; + block->bbox = fz_empty_rect; + page->len++; + distance = 0; + } + + /* New line */ + block = page->blocks[page->len-1].u.text; + if (block->len == block->cap) + { + int newcap = (block->cap ? block->cap*2 : 4); + block->lines = fz_resize_array(ctx, block->lines, newcap, sizeof(*block->lines)); + block->cap = newcap; + } + block->lines[block->len].first_span = NULL; + block->lines[block->len].last_span = NULL; + block->lines[block->len].distance = distance; + block->lines[block->len].bbox = fz_empty_rect; + block->len++; + } + + /* Find last line and append to it */ + block = page->blocks[page->len-1].u.text; + line = &block->lines[block->len-1]; + + fz_union_rect(&block->lines[block->len-1].bbox, &span->bbox); + fz_union_rect(&block->bbox, &span->bbox); + span->base_offset = (new_line ? 0 : distance); + + if (!line->first_span) + { + line->first_span = line->last_span = span; + span->next = NULL; + } + else + { + line->last_span->next = span; + line->last_span = span; + } + + return line; +} + +#if defined(DEBUG_SPANS) || defined(DEBUG_ALIGN) || defined(DEBUG_INDENTS) +static void +dump_span(fz_text_span *s) +{ + int i; + for (i=0; i < s->len; i++) + { + printf("%c", s->text[i].c); + } +} +#endif + +#ifdef DEBUG_ALIGN +static void +dump_line(fz_text_line *line) +{ + int i; + for (i=0; i < line->len; i++) + { + fz_text_span *s = line->spans[i]; + if (s->spacing > 1) + printf(" "); + dump_span(s); + } + printf("\n"); +} +#endif + +static void +strain_soup(fz_context *ctx, fz_text_device *tdev) +{ + span_soup *soup = tdev->spans; + fz_text_line *last_line = NULL; + fz_text_span *last_span = NULL; + int span_num; + + /* Really dumb implementation to match what we had before */ + for (span_num=0; span_num < soup->len; span_num++) + { + fz_text_span *span = soup->spans[span_num]; + int new_line = 1; + float distance = 0; + float spacing = 0; + soup->spans[span_num] = NULL; + if (last_span) + { + /* If we have a last_span, we must have a last_line */ + /* Do span and last_line share the same baseline? */ + fz_point p, q, perp_r; + float dot; + float size = fz_matrix_expansion(&span->transform); + +#ifdef DEBUG_SPANS + { + printf("Comparing: \""); + dump_span(last_span); + printf("\" and \""); + dump_span(span); + printf("\"\n"); + } +#endif + + p.x = last_line->first_span->max.x - last_line->first_span->min.x; + p.y = last_line->first_span->max.y - last_line->first_span->min.y; + fz_normalize_vector(&p); + q.x = span->max.x - span->min.x; + q.y = span->max.y - span->min.y; + fz_normalize_vector(&q); +#ifdef DEBUG_SPANS + printf("last_span=%g %g -> %g %g = %g %g\n", last_span->min.x, last_span->min.y, last_span->max.x, last_span->max.y, p.x, p.y); + printf("span =%g %g -> %g %g = %g %g\n", span->min.x, span->min.y, span->max.x, span->max.y, q.x, q.y); +#endif + perp_r.y = last_line->first_span->min.x - span->min.x; + perp_r.x = -(last_line->first_span->min.y - span->min.y); + /* Check if p and q are parallel. If so, then this + * line is parallel with the last one. */ + dot = p.x * q.x + p.y * q.y; + if (fabsf(dot) > 0.9995) + { + /* If we take the dot product of normalised(p) and + * perp(r), we get the perpendicular distance from + * one line to the next (assuming they are parallel). */ + distance = p.x * perp_r.x + p.y * perp_r.y; + /* We allow 'small' distances of baseline changes + * to cope with super/subscript. FIXME: We should + * gather subscript/superscript information here. */ + new_line = (fabsf(distance) > size * LINE_DIST); + } + else + { + new_line = 1; + distance = 0; + } + if (!new_line) + { + fz_point delta; + + delta.x = span->min.x - last_span->max.x; + delta.y = span->min.y - last_span->max.y; + + spacing = (p.x * delta.x + p.y * delta.y); + spacing = fabsf(spacing); + /* Only allow changes in baseline (subscript/superscript etc) + * when the spacing is small. */ + if (spacing * fabsf(distance) > size * LINE_DIST && fabsf(distance) > size * 0.1f) + { + new_line = 1; + distance = 0; + spacing = 0; + } + else + { + spacing /= size * SPACE_DIST; + /* Apply the same logic here as when we're adding chars to build spans. */ + if (spacing >= 1 && spacing < (SPACE_MAX_DIST/SPACE_DIST)) + spacing = 1; + } + } +#ifdef DEBUG_SPANS + printf("dot=%g new_line=%d distance=%g size=%g spacing=%g\n", dot, new_line, distance, size, spacing); +#endif + } + span->spacing = spacing; + last_line = push_span(ctx, tdev, span, new_line, distance); + last_span = span; + } +} + +fz_text_sheet * +fz_new_text_sheet(fz_context *ctx) +{ + fz_text_sheet *sheet = fz_malloc(ctx, sizeof *sheet); + sheet->maxid = 0; + sheet->style = NULL; + return sheet; +} + +void +fz_free_text_sheet(fz_context *ctx, fz_text_sheet *sheet) +{ + fz_text_style *style; + + if (sheet == NULL) + return; + + style = sheet->style; + while (style) + { + fz_text_style *next = style->next; + fz_drop_font(ctx, style->font); + fz_free(ctx, style); + style = next; + } + fz_free(ctx, sheet); +} + +static fz_text_style * +fz_lookup_text_style_imp(fz_context *ctx, fz_text_sheet *sheet, + float size, fz_font *font, int wmode, int script) +{ + fz_text_style *style; + + for (style = sheet->style; style; style = style->next) + { + if (style->font == font && + style->size == size && + style->wmode == wmode && + style->script == script) /* FIXME: others */ + { + return style; + } + } + + /* Better make a new one and add it to our list */ + style = fz_malloc(ctx, sizeof *style); + style->id = sheet->maxid++; + style->font = fz_keep_font(ctx, font); + style->size = size; + style->wmode = wmode; + style->script = script; + style->next = sheet->style; + sheet->style = style; + return style; +} + +static fz_text_style * +fz_lookup_text_style(fz_context *ctx, fz_text_sheet *sheet, fz_text *text, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha, fz_stroke_state *stroke) +{ + float size = 1.0f; + fz_font *font = text ? text->font : NULL; + int wmode = text ? text->wmode : 0; + if (ctm && text) + { + fz_matrix tm = text->trm; + fz_matrix trm; + tm.e = 0; + tm.f = 0; + fz_concat(&trm, &tm, ctm); + size = fz_matrix_expansion(&trm); + } + return fz_lookup_text_style_imp(ctx, sheet, size, font, wmode, 0); +} + +fz_text_page * +fz_new_text_page(fz_context *ctx) +{ + fz_text_page *page = fz_malloc(ctx, sizeof(*page)); + page->mediabox = fz_empty_rect; + page->len = 0; + page->cap = 0; + page->blocks = NULL; + page->next = NULL; + return page; +} + +static void +fz_free_text_line_contents(fz_context *ctx, fz_text_line *line) +{ + fz_text_span *span, *next; + for (span = line->first_span; span; span=next) + { + next = span->next; + fz_free(ctx, span->text); + fz_free(ctx, span); + } +} + +static void +fz_free_text_block(fz_context *ctx, fz_text_block *block) +{ + fz_text_line *line; + if (block == NULL) + return; + for (line = block->lines; line < block->lines + block->len; line++) + fz_free_text_line_contents(ctx, line); + fz_free(ctx, block->lines); + fz_free(ctx, block); +} + +static void +fz_free_image_block(fz_context *ctx, fz_image_block *block) +{ + if (block == NULL) + return; + fz_drop_image(ctx, block->image); + fz_drop_colorspace(ctx, block->cspace); + fz_free(ctx, block); +} + +void +fz_free_text_page(fz_context *ctx, fz_text_page *page) +{ + fz_page_block *block; + if (page == NULL) + return; + for (block = page->blocks; block < page->blocks + page->len; block++) + { + switch (block->type) + { + case FZ_PAGE_BLOCK_TEXT: + fz_free_text_block(ctx, block->u.text); + break; + case FZ_PAGE_BLOCK_IMAGE: + fz_free_image_block(ctx, block->u.image); + break; + } + } + fz_free(ctx, page->blocks); + fz_free(ctx, page); +} + +static fz_text_span * +fz_new_text_span(fz_context *ctx, const fz_point *p, int wmode, const fz_matrix *trm) +{ + fz_text_span *span = fz_malloc_struct(ctx, fz_text_span); + span->ascender_max = 0; + span->descender_min = 0; + span->cap = 0; + span->len = 0; + span->min = *p; + span->max = *p; + span->wmode = wmode; + span->transform.a = trm->a; + span->transform.b = trm->b; + span->transform.c = trm->c; + span->transform.d = trm->d; + span->transform.e = 0; + span->transform.f = 0; + span->text = NULL; + span->next = NULL; + return span; +} + +static void +add_char_to_span(fz_context *ctx, fz_text_span *span, int c, fz_point *p, fz_point *max, fz_text_style *style) +{ + if (span->len == span->cap) + { + int newcap = (span->cap ? span->cap * 2 : 16); + span->text = fz_resize_array(ctx, span->text, newcap, sizeof(fz_text_char)); + span->cap = newcap; + span->bbox = fz_empty_rect; + } + span->max = *max; + if (style->ascender > span->ascender_max) + span->ascender_max = style->ascender; + if (style->descender < span->descender_min) + span->descender_min = style->descender; + span->text[span->len].c = c; + span->text[span->len].p = *p; + span->text[span->len].style = style; + span->len++; +} + +static void +fz_add_text_char_imp(fz_context *ctx, fz_text_device *dev, fz_text_style *style, int c, fz_matrix *trm, float adv, int wmode) +{ + int can_append = 1; + int add_space = 0; + fz_point dir, ndir, p, q; + float size; + fz_point delta; + float spacing = 0; + float base_offset = 0; + + if (wmode == 0) + { + dir.x = 1; + dir.y = 0; + } + else + { + dir.x = 0; + dir.y = 1; + } + fz_transform_vector(&dir, trm); + ndir = dir; + fz_normalize_vector(&ndir); + /* dir = direction vector for motion. ndir = normalised(dir) */ + + size = fz_matrix_expansion(trm); + + if (dev->cur_span == NULL || + trm->a != dev->cur_span->transform.a || trm->b != dev->cur_span->transform.b || + trm->c != dev->cur_span->transform.c || trm->d != dev->cur_span->transform.d) + { + /* If the matrix has changed (or if we don't have a span at + * all), then we can't append. */ +#ifdef DEBUG_SPANS + printf("Transform changed\n"); +#endif + can_append = 0; + } + else + { + /* Calculate how far we've moved since the end of the current + * span. */ + delta.x = trm->e - dev->cur_span->max.x; + delta.y = trm->f - dev->cur_span->max.y; + + /* The transform has not changed, so we know we're in the same + * direction. Calculate 2 distances; how far off the previous + * baseline we are, together with how far along the baseline + * we are from the expected position. */ + spacing = ndir.x * delta.x + ndir.y * delta.y; + base_offset = -ndir.y * delta.x + ndir.x * delta.y; + + spacing /= size * SPACE_DIST; + spacing = fabsf(spacing); + if (fabsf(base_offset) < size * 0.1) + { + /* Only a small amount off the baseline - we'll take this */ + if (spacing < 1.0) + { + /* Motion is in line, and small. */ + } + else if (spacing >= 1 && spacing < (SPACE_MAX_DIST/SPACE_DIST)) + { + /* Motion is in line, but large enough + * to warrant us adding a space */ + if (dev->lastchar != ' ' && wmode == 0) + add_space = 1; + } + else + { + /* Motion is in line, but too large - split to a new span */ + can_append = 0; + } + } + else + { + can_append = 0; + spacing = 0; + } + } + +#ifdef DEBUG_SPANS + printf("%c%c append=%d space=%d size=%g spacing=%g base_offset=%g\n", dev->lastchar, c, can_append, add_space, size, spacing, base_offset); +#endif + + p.x = trm->e; + p.y = trm->f; + if (can_append == 0) + { + /* Start a new span */ + add_span_to_soup(dev->spans, dev->cur_span); + dev->cur_span = NULL; + dev->cur_span = fz_new_text_span(ctx, &p, wmode, trm); + dev->cur_span->spacing = 0; + } + if (add_space) + { + q.x = - 0.2f; + q.y = 0; + fz_transform_point(&q, trm); + add_char_to_span(ctx, dev->cur_span, ' ', &p, &q, style); + } + /* Advance the matrix */ + q.x = trm->e += adv * dir.x; + q.y = trm->f += adv * dir.y; + add_char_to_span(ctx, dev->cur_span, c, &p, &q, style); +} + +static void +fz_add_text_char(fz_context *ctx, fz_text_device *dev, fz_text_style *style, int c, fz_matrix *trm, float adv, int wmode) +{ + switch (c) + { + case -1: /* ignore when one unicode character maps to multiple glyphs */ + break; + case 0xFB00: /* ff */ + fz_add_text_char_imp(ctx, dev, style, 'f', trm, adv/2, wmode); + fz_add_text_char_imp(ctx, dev, style, 'f', trm, adv/2, wmode); + break; + case 0xFB01: /* fi */ + fz_add_text_char_imp(ctx, dev, style, 'f', trm, adv/2, wmode); + fz_add_text_char_imp(ctx, dev, style, 'i', trm, adv/2, wmode); + break; + case 0xFB02: /* fl */ + fz_add_text_char_imp(ctx, dev, style, 'f', trm, adv/2, wmode); + fz_add_text_char_imp(ctx, dev, style, 'l', trm, adv/2, wmode); + break; + case 0xFB03: /* ffi */ + fz_add_text_char_imp(ctx, dev, style, 'f', trm, adv/3, wmode); + fz_add_text_char_imp(ctx, dev, style, 'f', trm, adv/3, wmode); + fz_add_text_char_imp(ctx, dev, style, 'i', trm, adv/3, wmode); + break; + case 0xFB04: /* ffl */ + fz_add_text_char_imp(ctx, dev, style, 'f', trm, adv/3, wmode); + fz_add_text_char_imp(ctx, dev, style, 'f', trm, adv/3, wmode); + fz_add_text_char_imp(ctx, dev, style, 'l', trm, adv/3, wmode); + break; + case 0xFB05: /* long st */ + case 0xFB06: /* st */ + fz_add_text_char_imp(ctx, dev, style, 's', trm, adv/2, wmode); + fz_add_text_char_imp(ctx, dev, style, 't', trm, adv/2, wmode); + break; + default: + fz_add_text_char_imp(ctx, dev, style, c, trm, adv, wmode); + break; + } +} + +static void +fz_text_extract(fz_context *ctx, fz_text_device *dev, fz_text *text, const fz_matrix *ctm, fz_text_style *style) +{ + fz_font *font = text->font; + FT_Face face = font->ft_face; + fz_matrix tm = text->trm; + fz_matrix trm; + float adv; + float ascender = 1; + float descender = 0; + int multi; + int i, j, err; + + if (text->len == 0) + return; + + if (font->ft_face) + { + fz_lock(ctx, FZ_LOCK_FREETYPE); + err = FT_Set_Char_Size(font->ft_face, 64, 64, 72, 72); + if (err) + fz_warn(ctx, "freetype set character size: %s", ft_error_string(err)); + ascender = (float)face->ascender / face->units_per_EM; + descender = (float)face->descender / face->units_per_EM; + fz_unlock(ctx, FZ_LOCK_FREETYPE); + } + else if (font->t3procs && !fz_is_empty_rect(&font->bbox)) + { + ascender = font->bbox.y1; + descender = font->bbox.y0; + } + style->ascender = ascender; + style->descender = descender; + + tm.e = 0; + tm.f = 0; + fz_concat(&trm, &tm, ctm); + + for (i = 0; i < text->len; i++) + { + /* Calculate new pen location and delta */ + tm.e = text->items[i].x; + tm.f = text->items[i].y; + fz_concat(&trm, &tm, ctm); + + /* Calculate bounding box and new pen position based on font metrics */ + if (font->ft_face) + { + FT_Fixed ftadv = 0; + int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM; + + /* TODO: freetype returns broken vertical metrics */ + /* if (text->wmode) mask |= FT_LOAD_VERTICAL_LAYOUT; */ + + fz_lock(ctx, FZ_LOCK_FREETYPE); + err = FT_Set_Char_Size(font->ft_face, 64, 64, 72, 72); + if (err) + fz_warn(ctx, "freetype set character size: %s", ft_error_string(err)); + FT_Get_Advance(font->ft_face, text->items[i].gid, mask, &ftadv); + adv = ftadv / 65536.0f; + fz_unlock(ctx, FZ_LOCK_FREETYPE); + } + else + { + adv = font->t3widths[text->items[i].gid]; + } + + /* Check for one glyph to many char mapping */ + for (j = i + 1; j < text->len; j++) + if (text->items[j].gid >= 0) + break; + multi = j - i; + + if (multi == 1) + { + fz_add_text_char(ctx, dev, style, text->items[i].ucs, &trm, adv, text->wmode); + } + else + { + for (j = 0; j < multi; j++) + { + fz_add_text_char(ctx, dev, style, text->items[i + j].ucs, &trm, adv/multi, text->wmode); + } + i += j - 1; + } + + dev->lastchar = text->items[i].ucs; + } +} + +static void +fz_text_fill_text(fz_device *dev, fz_text *text, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_text_device *tdev = dev->user; + fz_text_style *style; + style = fz_lookup_text_style(dev->ctx, tdev->sheet, text, ctm, colorspace, color, alpha, NULL); + fz_text_extract(dev->ctx, tdev, text, ctm, style); +} + +static void +fz_text_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + fz_text_device *tdev = dev->user; + fz_text_style *style; + style = fz_lookup_text_style(dev->ctx, tdev->sheet, text, ctm, colorspace, color, alpha, stroke); + fz_text_extract(dev->ctx, tdev, text, ctm, style); +} + +static void +fz_text_clip_text(fz_device *dev, fz_text *text, const fz_matrix *ctm, int accumulate) +{ + fz_text_device *tdev = dev->user; + fz_text_style *style; + style = fz_lookup_text_style(dev->ctx, tdev->sheet, text, ctm, NULL, NULL, 0, NULL); + fz_text_extract(dev->ctx, tdev, text, ctm, style); +} + +static void +fz_text_clip_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm) +{ + fz_text_device *tdev = dev->user; + fz_text_style *style; + style = fz_lookup_text_style(dev->ctx, tdev->sheet, text, ctm, NULL, NULL, 0, stroke); + fz_text_extract(dev->ctx, tdev, text, ctm, style); +} + +static void +fz_text_ignore_text(fz_device *dev, fz_text *text, const fz_matrix *ctm) +{ + fz_text_device *tdev = dev->user; + fz_text_style *style; + style = fz_lookup_text_style(dev->ctx, tdev->sheet, text, ctm, NULL, NULL, 0, NULL); + fz_text_extract(dev->ctx, tdev, text, ctm, style); +} + +static void +fz_text_fill_image_mask(fz_device *dev, fz_image *img, const fz_matrix *ctm, + fz_colorspace *cspace, float *color, float alpha) +{ + fz_text_device *tdev = dev->user; + fz_text_page *page = tdev->page; + fz_image_block *block; + fz_context *ctx = dev->ctx; + + /* If the alpha is less than 50% then it's probably a watermark or + * effect or something. Skip it */ + if (alpha < 0.5) + return; + + /* New block */ + if (page->len == page->cap) + { + int newcap = (page->cap ? page->cap*2 : 4); + page->blocks = fz_resize_array(ctx, page->blocks, newcap, sizeof(*page->blocks)); + page->cap = newcap; + } + block = fz_malloc_struct(ctx, fz_image_block); + page->blocks[page->len].type = FZ_PAGE_BLOCK_IMAGE; + page->blocks[page->len].u.image = block; + block->image = fz_keep_image(ctx, img); + block->cspace = fz_keep_colorspace(ctx, cspace); + if (cspace) + memcpy(block->colors, color, sizeof(block->colors[0])*cspace->n); + page->len++; +} + +static void +fz_text_fill_image(fz_device *dev, fz_image *img, const fz_matrix *ctm, float alpha) +{ + fz_text_fill_image_mask(dev, img, ctm, NULL, NULL, alpha); +} + +static int +fz_bidi_direction(int bidiclass, int curdir) +{ + switch (bidiclass) + { + /* strong */ + case UCDN_BIDI_CLASS_L: return 1; + case UCDN_BIDI_CLASS_R: return -1; + case UCDN_BIDI_CLASS_AL: return -1; + + /* weak */ + case UCDN_BIDI_CLASS_EN: + case UCDN_BIDI_CLASS_ES: + case UCDN_BIDI_CLASS_ET: + case UCDN_BIDI_CLASS_AN: + case UCDN_BIDI_CLASS_CS: + case UCDN_BIDI_CLASS_NSM: + case UCDN_BIDI_CLASS_BN: + return curdir; + + /* neutral */ + case UCDN_BIDI_CLASS_B: + case UCDN_BIDI_CLASS_S: + case UCDN_BIDI_CLASS_WS: + case UCDN_BIDI_CLASS_ON: + return curdir; + + /* embedding, override, pop ... we don't support them */ + default: + return 0; + } +} + +static void +fz_bidi_reorder_run(fz_text_span *span, int a, int b, int dir) +{ + if (a < b && dir == -1) + { + fz_text_char c; + int m = a + (b - a) / 2; + while (a < m) + { + b--; + c = span->text[a]; + span->text[a] = span->text[b]; + span->text[b] = c; + a++; + } + } +} + +static void +fz_bidi_reorder_span(fz_text_span *span) +{ + int a, b, dir, curdir; + + a = 0; + curdir = 1; + for (b = 0; b < span->len; b++) + { + dir = fz_bidi_direction(ucdn_get_bidi_class(span->text[b].c), curdir); + if (dir != curdir) + { + fz_bidi_reorder_run(span, a, b, curdir); + curdir = dir; + a = b; + } + } + fz_bidi_reorder_run(span, a, b, curdir); +} + +static void +fz_bidi_reorder_text_page(fz_context *ctx, fz_text_page *page) +{ + fz_page_block *pageblock; + fz_text_block *block; + fz_text_line *line; + fz_text_span *span; + + for (pageblock = page->blocks; pageblock < page->blocks + page->len; pageblock++) + if (pageblock->type == FZ_PAGE_BLOCK_TEXT) + for (block = pageblock->u.text, line = block->lines; line < block->lines + block->len; line++) + for (span = line->first_span; span; span = span->next) + fz_bidi_reorder_span(span); +} + +static void +fz_text_begin_page(fz_device *dev, const fz_rect *mediabox, const fz_matrix *ctm) +{ + fz_context *ctx = dev->ctx; + fz_text_device *tdev = dev->user; + + if (tdev->page->len) + { + tdev->page->next = fz_new_text_page(ctx); + tdev->page = tdev->page->next; + } + + tdev->page->mediabox = *mediabox; + fz_transform_rect(&tdev->page->mediabox, ctm); + + tdev->spans = new_span_soup(ctx); +} + +static void +fz_text_end_page(fz_device *dev) +{ + fz_context *ctx = dev->ctx; + fz_text_device *tdev = dev->user; + + add_span_to_soup(tdev->spans, tdev->cur_span); + tdev->cur_span = NULL; + + strain_soup(ctx, tdev); + free_span_soup(tdev->spans); + tdev->spans = NULL; + + /* TODO: smart sorting of blocks in reading order */ + /* TODO: unicode NFC normalization */ + + fz_bidi_reorder_text_page(ctx, tdev->page); +} + +static void +fz_text_free_user(fz_device *dev) +{ + fz_text_device *tdev = dev->user; + free_span_soup(tdev->spans); + fz_free(dev->ctx, tdev); +} + +fz_device * +fz_new_text_device(fz_context *ctx, fz_text_sheet *sheet, fz_text_page *page) +{ + fz_device *dev; + + fz_text_device *tdev = fz_malloc_struct(ctx, fz_text_device); + tdev->sheet = sheet; + tdev->page = page; + tdev->spans = NULL; + tdev->cur_span = NULL; + tdev->lastchar = ' '; + + dev = fz_new_device(ctx, tdev); + dev->hints = FZ_IGNORE_IMAGE | FZ_IGNORE_SHADE; + dev->begin_page = fz_text_begin_page; + dev->end_page = fz_text_end_page; + dev->free_user = fz_text_free_user; + dev->fill_text = fz_text_fill_text; + dev->stroke_text = fz_text_stroke_text; + dev->clip_text = fz_text_clip_text; + dev->clip_stroke_text = fz_text_clip_stroke_text; + dev->ignore_text = fz_text_ignore_text; + dev->fill_image = fz_text_fill_image; + dev->fill_image_mask = fz_text_fill_image_mask; + + return dev; +} diff --git a/source/fitz/stext-output.c b/source/fitz/stext-output.c new file mode 100644 index 00000000..d3241131 --- /dev/null +++ b/source/fitz/stext-output.c @@ -0,0 +1,400 @@ +#include "mupdf/fitz.h" + +#define SUBSCRIPT_OFFSET 0.2f +#define SUPERSCRIPT_OFFSET -0.2f + +#include <ft2build.h> +#include FT_FREETYPE_H + +/* XML, HTML and plain-text output */ + +static int font_is_bold(fz_font *font) +{ + FT_Face face = font->ft_face; + if (face && (face->style_flags & FT_STYLE_FLAG_BOLD)) + return 1; + if (strstr(font->name, "Bold")) + return 1; + return 0; +} + +static int font_is_italic(fz_font *font) +{ + FT_Face face = font->ft_face; + if (face && (face->style_flags & FT_STYLE_FLAG_ITALIC)) + return 1; + if (strstr(font->name, "Italic") || strstr(font->name, "Oblique")) + return 1; + return 0; +} + +static void +fz_print_style_begin(fz_output *out, fz_text_style *style) +{ + int script = style->script; + fz_printf(out, "<span class=\"s%d\">", style->id); + while (script-- > 0) + fz_printf(out, "<sup>"); + while (++script < 0) + fz_printf(out, "<sub>"); +} + +static void +fz_print_style_end(fz_output *out, fz_text_style *style) +{ + int script = style->script; + while (script-- > 0) + fz_printf(out, "</sup>"); + while (++script < 0) + fz_printf(out, "</sub>"); + fz_printf(out, "</span>"); +} + +static void +fz_print_style(fz_output *out, fz_text_style *style) +{ + char *s = strchr(style->font->name, '+'); + s = s ? s + 1 : style->font->name; + fz_printf(out, "span.s%d{font-family:\"%s\";font-size:%gpt;", + style->id, s, style->size); + if (font_is_italic(style->font)) + fz_printf(out, "font-style:italic;"); + if (font_is_bold(style->font)) + fz_printf(out, "font-weight:bold;"); + fz_printf(out, "}\n"); +} + +void +fz_print_text_sheet(fz_context *ctx, fz_output *out, fz_text_sheet *sheet) +{ + fz_text_style *style; + for (style = sheet->style; style; style = style->next) + fz_print_style(out, style); +} + +static void +send_data_base64(fz_output *out, fz_buffer *buffer) +{ + int i, len; + static const char set[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + len = buffer->len/3; + for (i = 0; i < len; i++) + { + int c = buffer->data[3*i]; + int d = buffer->data[3*i+1]; + int e = buffer->data[3*i+2]; + if ((i & 15) == 0) + fz_printf(out, "\n"); + fz_printf(out, "%c%c%c%c", set[c>>2], set[((c&3)<<4)|(d>>4)], set[((d&15)<<2)|(e>>6)], set[e & 63]); + } + i *= 3; + switch (buffer->len-i) + { + case 2: + { + int c = buffer->data[i]; + int d = buffer->data[i+1]; + fz_printf(out, "%c%c%c=", set[c>>2], set[((c&3)<<4)|(d>>4)], set[((d&15)<<2)]); + break; + } + case 1: + { + int c = buffer->data[i]; + fz_printf(out, "%c%c==", set[c>>2], set[(c&3)<<4]); + break; + } + default: + case 0: + break; + } +} + +void +fz_print_text_page_html(fz_context *ctx, fz_output *out, fz_text_page *page) +{ + int block_n, line_n, ch_n; + fz_text_style *style = NULL; + fz_text_line *line; + fz_text_span *span; + void *last_region = NULL; + + fz_printf(out, "<div class=\"page\">\n"); + + for (block_n = 0; block_n < page->len; block_n++) + { + switch (page->blocks[block_n].type) + { + case FZ_PAGE_BLOCK_TEXT: + { + fz_text_block * block = page->blocks[block_n].u.text; + fz_printf(out, "<div class=\"block\"><p>\n"); + for (line_n = 0; line_n < block->len; line_n++) + { + int lastcol=-1; + line = &block->lines[line_n]; + style = NULL; + + if (line->region != last_region) + { + if (last_region) + fz_printf(out, "</div>"); + fz_printf(out, "<div class=\"metaline\">"); + last_region = line->region; + } + fz_printf(out, "<div class=\"line\""); +#ifdef DEBUG_INTERNALS + if (line->region) + fz_printf(out, " region=\"%x\"", line->region); +#endif + fz_printf(out, ">"); + for (span = line->first_span; span; span = span->next) + { + float size = fz_matrix_expansion(&span->transform); + float base_offset = span->base_offset / size; + + if (lastcol != span->column) + { + if (lastcol >= 0) + { + fz_printf(out, "</div>"); + } + /* If we skipped any columns then output some spacer spans */ + while (lastcol < span->column-1) + { + fz_printf(out, "<div class=\"cell\"></div>"); + lastcol++; + } + lastcol++; + /* Now output the span to contain this entire column */ + fz_printf(out, "<div class=\"cell\" style=\""); + { + fz_text_span *sn; + for (sn = span->next; sn; sn = sn->next) + { + if (sn->column != lastcol) + break; + } + fz_printf(out, "width:%g%%;align:%s", span->column_width, (span->align == 0 ? "left" : (span->align == 1 ? "center" : "right"))); + } + if (span->indent > 1) + fz_printf(out, ";padding-left:1em;text-indent:-1em"); + if (span->indent < -1) + fz_printf(out, ";text-indent:1em"); + fz_printf(out, "\">"); + } +#ifdef DEBUG_INTERNALS + fz_printf(out, "<span class=\"internal_span\""); + if (span->column) + fz_printf(out, " col=\"%x\"", span->column); + fz_printf(out, ">"); +#endif + if (span->spacing >= 1) + fz_printf(out, " "); + if (base_offset > SUBSCRIPT_OFFSET) + fz_printf(out, "<sub>"); + else if (base_offset < SUPERSCRIPT_OFFSET) + fz_printf(out, "<sup>"); + for (ch_n = 0; ch_n < span->len; ch_n++) + { + fz_text_char *ch = &span->text[ch_n]; + if (style != ch->style) + { + if (style) + fz_print_style_end(out, style); + fz_print_style_begin(out, ch->style); + style = ch->style; + } + + if (ch->c == '<') + fz_printf(out, "<"); + else if (ch->c == '>') + fz_printf(out, ">"); + else if (ch->c == '&') + fz_printf(out, "&"); + else if (ch->c >= 32 && ch->c <= 127) + fz_printf(out, "%c", ch->c); + else + fz_printf(out, "&#x%x;", ch->c); + } + if (style) + { + fz_print_style_end(out, style); + style = NULL; + } + if (base_offset > SUBSCRIPT_OFFSET) + fz_printf(out, "</sub>"); + else if (base_offset < SUPERSCRIPT_OFFSET) + fz_printf(out, "</sup>"); +#ifdef DEBUG_INTERNALS + fz_printf(out, "</span>"); +#endif + } + /* Close our floating span */ + fz_printf(out, "</div>"); + /* Close the line */ + fz_printf(out, "</div>"); + fz_printf(out, "\n"); + } + /* Close the metaline */ + fz_printf(out, "</div>"); + last_region = NULL; + fz_printf(out, "</p></div>\n"); + break; + } + case FZ_PAGE_BLOCK_IMAGE: + { + fz_image_block *image = page->blocks[block_n].u.image; + fz_printf(out, "<img width=%d height=%d src=\"data:", image->image->w, image->image->h); + switch (image->image->buffer == NULL ? FZ_IMAGE_JPX : image->image->buffer->params.type) + { + case FZ_IMAGE_JPEG: + fz_printf(out, "image/jpeg;base64,"); + send_data_base64(out, image->image->buffer->buffer); + break; + case FZ_IMAGE_PNG: + fz_printf(out, "image/png;base64,"); + send_data_base64(out, image->image->buffer->buffer); + break; + default: + { + fz_buffer *buf = fz_image_as_png(ctx, image->image, image->image->w, image->image->h); + fz_printf(out, "image/png;base64,"); + send_data_base64(out, buf); + fz_drop_buffer(ctx, buf); + break; + } + } + fz_printf(out, "\">\n"); + break; + } + } + } + + fz_printf(out, "</div>\n"); +} + +void +fz_print_text_page_xml(fz_context *ctx, fz_output *out, fz_text_page *page) +{ + int block_n; + + fz_printf(out, "<page width=\"%g\" height=\"%g\">\n", + page->mediabox.x1 - page->mediabox.x0, + page->mediabox.y1 - page->mediabox.y0); + + for (block_n = 0; block_n < page->len; block_n++) + { + switch (page->blocks[block_n].type) + { + case FZ_PAGE_BLOCK_TEXT: + { + fz_text_block *block = page->blocks[block_n].u.text; + fz_text_line *line; + char *s; + + fz_printf(out, "<block bbox=\"%g %g %g %g\">\n", + block->bbox.x0, block->bbox.y0, block->bbox.x1, block->bbox.y1); + for (line = block->lines; line < block->lines + block->len; line++) + { + fz_text_span *span; + fz_printf(out, "<line bbox=\"%g %g %g %g\">\n", + line->bbox.x0, line->bbox.y0, line->bbox.x1, line->bbox.y1); + for (span = line->first_span; span; span = span->next) + { + fz_text_style *style = NULL; + int char_num; + for (char_num = 0; char_num < span->len; char_num++) + { + fz_text_char *ch = &span->text[char_num]; + if (ch->style != style) + { + if (style) + { + fz_printf(out, "</span>\n"); + } + style = ch->style; + s = strchr(style->font->name, '+'); + s = s ? s + 1 : style->font->name; + fz_printf(out, "<span bbox=\"%g %g %g %g\" font=\"%s\" size=\"%g\">\n", + span->bbox.x0, span->bbox.y0, span->bbox.x1, span->bbox.y1, + s, style->size); + } + { + fz_rect rect; + fz_text_char_bbox(&rect, span, char_num); + fz_printf(out, "<char bbox=\"%g %g %g %g\" x=\"%g\" y=\"%g\" c=\"", + rect.x0, rect.y0, rect.x1, rect.y1, ch->p.x, ch->p.y); + } + switch (ch->c) + { + case '<': fz_printf(out, "<"); break; + case '>': fz_printf(out, ">"); break; + case '&': fz_printf(out, "&"); break; + case '"': fz_printf(out, """); break; + case '\'': fz_printf(out, "'"); break; + default: + if (ch->c >= 32 && ch->c <= 127) + fz_printf(out, "%c", ch->c); + else + fz_printf(out, "&#x%x;", ch->c); + break; + } + fz_printf(out, "\"/>\n"); + } + if (style) + fz_printf(out, "</span>\n"); + } + fz_printf(out, "</line>\n"); + } + fz_printf(out, "</block>\n"); + break; + } + case FZ_PAGE_BLOCK_IMAGE: + { + break; + } + } + } + fz_printf(out, "</page>\n"); +} + +void +fz_print_text_page(fz_context *ctx, fz_output *out, fz_text_page *page) +{ + int block_n; + + for (block_n = 0; block_n < page->len; block_n++) + { + switch (page->blocks[block_n].type) + { + case FZ_PAGE_BLOCK_TEXT: + { + fz_text_block *block = page->blocks[block_n].u.text; + fz_text_line *line; + fz_text_char *ch; + char utf[10]; + int i, n; + + for (line = block->lines; line < block->lines + block->len; line++) + { + fz_text_span *span; + for (span = line->first_span; span; span = span->next) + { + for (ch = span->text; ch < span->text + span->len; ch++) + { + n = fz_runetochar(utf, ch->c); + for (i = 0; i < n; i++) + fz_printf(out, "%c", utf[i]); + } + } + fz_printf(out, "\n"); + } + fz_printf(out, "\n"); + break; + } + case FZ_PAGE_BLOCK_IMAGE: + break; + } + } +} diff --git a/source/fitz/stext-paragraph.c b/source/fitz/stext-paragraph.c new file mode 100644 index 00000000..51062938 --- /dev/null +++ b/source/fitz/stext-paragraph.c @@ -0,0 +1,1500 @@ +#include "mupdf/fitz.h" + +/* Assemble span soup into blocks and lines. */ + +#define MY_EPSILON 0.001f + +#undef DEBUG_LINE_HEIGHTS +#undef DEBUG_MASKS +#undef DEBUG_ALIGN +#undef DEBUG_INDENTS + +#undef SPOT_LINE_NUMBERS + +typedef struct line_height_s +{ + float height; + int count; + fz_text_style *style; +} line_height; + +typedef struct line_heights_s +{ + fz_context *ctx; + int cap; + int len; + line_height *lh; +} line_heights; + +static line_heights * +new_line_heights(fz_context *ctx) +{ + line_heights *lh = fz_malloc_struct(ctx, line_heights); + lh->ctx = ctx; + return lh; +} + +static void +free_line_heights(line_heights *lh) +{ + if (!lh) + return; + fz_free(lh->ctx, lh->lh); + fz_free(lh->ctx, lh); +} + +static void +insert_line_height(line_heights *lh, fz_text_style *style, float height) +{ + int i; + +#ifdef DEBUG_LINE_HEIGHTS + printf("style=%x height=%g\n", style, height); +#endif + + /* If we have one already, add it in */ + for (i=0; i < lh->len; i++) + { + /* Match if we are within 5% */ + if (lh->lh[i].style == style && lh->lh[i].height * 0.95 <= height && lh->lh[i].height * 1.05 >= height) + { + /* Ensure that the average height is correct */ + lh->lh[i].height = (lh->lh[i].height * lh->lh[i].count + height) / (lh->lh[i].count+1); + lh->lh[i].count++; + return; + } + } + + /* Otherwise extend (if required) and add it */ + if (lh->cap == lh->len) + { + int newcap = (lh->cap ? lh->cap * 2 : 4); + lh->lh = fz_resize_array(lh->ctx, lh->lh, newcap, sizeof(line_height)); + lh->cap = newcap; + } + + lh->lh[lh->len].count = 1; + lh->lh[lh->len].height = height; + lh->lh[lh->len].style = style; + lh->len++; +} + +static void +cull_line_heights(line_heights *lh) +{ + int i, j, k; + +#ifdef DEBUG_LINE_HEIGHTS + printf("Before culling:\n"); + for (i = 0; i < lh->len; i++) + { + fz_text_style *style = lh->lh[i].style; + printf("style=%x height=%g count=%d\n", style, lh->lh[i].height, lh->lh[i].count); + } +#endif + for (i = 0; i < lh->len; i++) + { + fz_text_style *style = lh->lh[i].style; + int count = lh->lh[i].count; + int max = i; + + /* Find the max for this style */ + for (j = i+1; j < lh->len; j++) + { + if (lh->lh[j].style == style && lh->lh[j].count > count) + { + max = j; + count = lh->lh[j].count; + } + } + + /* Destroy all the ones other than the max */ + if (max != i) + { + lh->lh[i].count = count; + lh->lh[i].height = lh->lh[max].height; + lh->lh[max].count = 0; + } + j = i+1; + for (k = j; k < lh->len; k++) + { + if (lh->lh[k].style != style) + lh->lh[j++] = lh->lh[k]; + } + lh->len = j; + } +#ifdef DEBUG_LINE_HEIGHTS + printf("After culling:\n"); + for (i = 0; i < lh->len; i++) + { + fz_text_style *style = lh->lh[i].style; + printf("style=%x height=%g count=%d\n", style, lh->lh[i].height, lh->lh[i].count); + } +#endif +} + +static float +line_height_for_style(line_heights *lh, fz_text_style *style) +{ + int i; + + for (i=0; i < lh->len; i++) + { + if (lh->lh[i].style == style) + return lh->lh[i].height; + } + return 0.0; /* Never reached */ +} + +static void +split_block(fz_context *ctx, fz_text_page *page, int block_num, int linenum) +{ + int split_len; + fz_text_block *block, *block2; + + if (page->len == page->cap) + { + int new_cap = fz_maxi(16, page->cap * 2); + page->blocks = fz_resize_array(ctx, page->blocks, new_cap, sizeof(*page->blocks)); + page->cap = new_cap; + } + + memmove(page->blocks+block_num+1, page->blocks+block_num, (page->len - block_num)*sizeof(*page->blocks)); + page->len++; + + block2 = fz_malloc_struct(ctx, fz_text_block); + block = page->blocks[block_num].u.text; + + page->blocks[block_num+1].type = FZ_PAGE_BLOCK_TEXT; + page->blocks[block_num+1].u.text = block2; + split_len = block->len - linenum; + block2->bbox = block->bbox; /* FIXME! */ + block2->cap = 0; + block2->len = 0; + block2->lines = NULL; + block2->lines = fz_malloc_array(ctx, split_len, sizeof(fz_text_line)); + block2->cap = block2->len; + block2->len = split_len; + block->len = linenum; + memcpy(block2->lines, block->lines + linenum, split_len * sizeof(fz_text_line)); + block2->lines[0].distance = 0; +} + +static inline int +is_unicode_wspace(int c) +{ + return (c == 9 || /* TAB */ + c == 0x0a || /* HT */ + c == 0x0b || /* LF */ + c == 0x0c || /* VT */ + c == 0x0d || /* FF */ + c == 0x20 || /* CR */ + c == 0x85 || /* NEL */ + c == 0xA0 || /* No break space */ + c == 0x1680 || /* Ogham space mark */ + c == 0x180E || /* Mongolian Vowel Separator */ + c == 0x2000 || /* En quad */ + c == 0x2001 || /* Em quad */ + c == 0x2002 || /* En space */ + c == 0x2003 || /* Em space */ + c == 0x2004 || /* Three-per-Em space */ + c == 0x2005 || /* Four-per-Em space */ + c == 0x2006 || /* Five-per-Em space */ + c == 0x2007 || /* Figure space */ + c == 0x2008 || /* Punctuation space */ + c == 0x2009 || /* Thin space */ + c == 0x200A || /* Hair space */ + c == 0x2028 || /* Line separator */ + c == 0x2029 || /* Paragraph separator */ + c == 0x202F || /* Narrow no-break space */ + c == 0x205F || /* Medium mathematical space */ + c == 0x3000); /* Ideographic space */ +} + +static inline int +is_unicode_bullet(int c) +{ + /* The last 2 aren't strictly bullets, but will do for our usage here */ + return (c == 0x2022 || /* Bullet */ + c == 0x2023 || /* Triangular bullet */ + c == 0x25e6 || /* White bullet */ + c == 0x2043 || /* Hyphen bullet */ + c == 0x2219 || /* Bullet operator */ + c == 149 || /* Ascii bullet */ + c == '*'); +} + +static inline int +is_number(int c) +{ + return ((c >= '0' && c <= '9') || + (c == '.')); +} + +static inline int +is_latin_char(int c) +{ + return ((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z')); +} + +static inline int +is_roman(int c) +{ + return (c == 'i' || c == 'I' || + c == 'v' || c == 'V' || + c == 'x' || c == 'X' || + c == 'l' || c == 'L' || + c == 'c' || c == 'C' || + c == 'm' || c == 'M'); +} + +static int +is_list_entry(fz_text_line *line, fz_text_span *span, int *char_num_ptr) +{ + int char_num; + fz_text_char *chr; + + /* First, skip over any whitespace */ + for (char_num = 0; char_num < span->len; char_num++) + { + chr = &span->text[char_num]; + if (!is_unicode_wspace(chr->c)) + break; + } + *char_num_ptr = char_num; + + if (span != line->first_span || char_num >= span->len) + return 0; + + /* Now we check for various special cases, which we consider to mean + * that this is probably a list entry and therefore should always count + * as a separate paragraph (and hence not be entered in the line height + * table). */ + chr = &span->text[char_num]; + + /* Is the first char on the line, a bullet point? */ + if (is_unicode_bullet(chr->c)) + return 1; + +#ifdef SPOT_LINE_NUMBERS + /* Is the entire first span a number? Or does it start with a number + * followed by ) or : ? Allow to involve single latin chars too. */ + if (is_number(chr->c) || is_latin_char(chr->c)) + { + int cn = char_num; + int met_char = is_latin_char(chr->c); + for (cn = char_num+1; cn < span->len; cn++) + { + fz_text_char *chr2 = &span->text[cn]; + + if (is_latin_char(chr2->c) && !met_char) + { + met_char = 1; + continue; + } + met_char = 0; + if (!is_number(chr2->c) && !is_unicode_wspace(chr2->c)) + break; + else if (chr2->c == ')' || chr2->c == ':') + { + cn = span->len; + break; + } + } + if (cn == span->len) + return 1; + } + + /* Is the entire first span a roman numeral? Or does it start with + * a roman numeral followed by ) or : ? */ + if (is_roman(chr->c)) + { + int cn = char_num; + for (cn = char_num+1; cn < span->len; cn++) + { + fz_text_char *chr2 = &span->text[cn]; + + if (!is_roman(chr2->c) && !is_unicode_wspace(chr2->c)) + break; + else if (chr2->c == ')' || chr2->c == ':') + { + cn = span->len; + break; + } + } + if (cn == span->len) + return 1; + } +#endif + return 0; +} + +typedef struct region_masks_s region_masks; + +typedef struct region_mask_s region_mask; + +typedef struct region_s region; + +struct region_s +{ + float start; + float stop; + float ave_start; + float ave_stop; + int align; + float colw; +}; + +struct region_mask_s +{ + fz_context *ctx; + int freq; + fz_point blv; + int cap; + int len; + float size; + region *mask; +}; + +struct region_masks_s +{ + fz_context *ctx; + int cap; + int len; + region_mask **mask; +}; + +static region_masks * +new_region_masks(fz_context *ctx) +{ + region_masks *rms = fz_malloc_struct(ctx, region_masks); + rms->ctx = ctx; + rms->cap = 0; + rms->len = 0; + rms->mask = NULL; + return rms; +} + +static void +free_region_mask(region_mask *rm) +{ + if (!rm) + return; + fz_free(rm->ctx, rm->mask); + fz_free(rm->ctx, rm); +} + +static void +free_region_masks(region_masks *rms) +{ + int i; + + if (!rms) + return; + for (i=0; i < rms->len; i++) + { + free_region_mask(rms->mask[i]); + } + fz_free(rms->ctx, rms->mask); + fz_free(rms->ctx, rms); +} + +static int region_masks_mergeable(const region_mask *rm1, const region_mask *rm2, float *score) +{ + int i1, i2; + int count = 0; + + *score = 0; + if (fabsf(rm1->blv.x-rm2->blv.x) >= MY_EPSILON || fabsf(rm1->blv.y-rm2->blv.y) >= MY_EPSILON) + return 0; + + for (i1 = 0, i2 = 0; i1 < rm1->len && i2 < rm2->len; ) + { + if (rm1->mask[i1].stop < rm2->mask[i2].start) + { + /* rm1's region is entirely before rm2's */ + *score += rm1->mask[i1].stop - rm1->mask[i1].start; + i1++; + } + else if (rm1->mask[i1].start > rm2->mask[i2].stop) + { + /* rm2's region is entirely before rm1's */ + *score += rm2->mask[i2].stop - rm2->mask[i2].start; + i2++; + } + else + { + float lscore, rscore; + if (rm1->mask[i1].start < rm2->mask[i2].start) + { + if (i2 > 0 && rm2->mask[i2-1].stop >= rm1->mask[i1].start) + return 0; /* Not compatible */ + lscore = rm2->mask[i2].start - rm1->mask[i1].start; + } + else + { + if (i1 > 0 && rm1->mask[i1-1].stop >= rm2->mask[i2].start) + return 0; /* Not compatible */ + lscore = rm1->mask[i1].start - rm2->mask[i2].start; + } + if (rm1->mask[i1].stop > rm2->mask[i2].stop) + { + if (i2+1 < rm2->len && rm2->mask[i2+1].start <= rm1->mask[i1].stop) + return 0; /* Not compatible */ + rscore = rm1->mask[i1].stop - rm2->mask[i2].stop; + } + else + { + if (i1+1 < rm1->len && rm1->mask[i1+1].start <= rm2->mask[i2].stop) + return 0; /* Not compatible */ + rscore = rm2->mask[i2].stop - rm1->mask[i1].stop; + } + /* In order to allow a region to merge, either the + * left, the right, or the centre must agree */ + if (lscore < 1) + { + if (rscore < 1) + { + rscore = 0; + } + lscore = 0; + } + else if (rscore < 1) + { + rscore = 0; + } + else + { + /* Neither Left or right agree. Does the centre? */ + float ave1 = rm1->mask[i1].start + rm1->mask[i1].stop; + float ave2 = rm2->mask[i2].start + rm2->mask[i2].stop; + if (fabsf(ave1-ave2) > 1) + { + /* Nothing agrees, so don't merge */ + return 0; + } + lscore = 0; + rscore = 0; + } + *score += lscore + rscore; + /* These two regions could be merged */ + i1++; + i2++; + } + count++; + } + count += rm1->len-i1 + rm2->len-i2; + return count; +} + +static int region_mask_matches(const region_mask *rm1, const region_mask *rm2, float *score) +{ + int i1, i2; + int close = 1; + + *score = 0; + if (fabsf(rm1->blv.x-rm2->blv.x) >= MY_EPSILON || fabsf(rm1->blv.y-rm2->blv.y) >= MY_EPSILON) + return 0; + + for (i1 = 0, i2 = 0; i1 < rm1->len && i2 < rm2->len; ) + { + if (rm1->mask[i1].stop < rm2->mask[i2].start) + { + /* rm1's region is entirely before rm2's */ + *score += rm1->mask[i1].stop - rm1->mask[i1].start; + i1++; + } + else if (rm1->mask[i1].start > rm2->mask[i2].stop) + { + /* Not compatible */ + return 0; + } + else + { + float lscore, rscore; + if (rm1->mask[i1].start > rm2->mask[i2].start) + { + /* Not compatible */ + return 0; + } + if (rm1->mask[i1].stop < rm2->mask[i2].stop) + { + /* Not compatible */ + return 0; + } + lscore = rm2->mask[i2].start - rm1->mask[i1].start; + rscore = rm1->mask[i1].stop - rm2->mask[i2].stop; + if (lscore < 1) + { + if (rscore < 1) + close++; + close++; + } + else if (rscore < 1) + close++; + else if (fabsf(lscore - rscore) < 1) + { + lscore = fabsf(lscore-rscore); + rscore = 0; + close++; + } + *score += lscore + rscore; + i1++; + i2++; + } + } + if (i1 < rm1->len) + { + /* Still more to go in rm1 */ + if (rm1->mask[i1].start < rm2->mask[rm2->len-1].stop) + return 0; + } + else if (i2 < rm2->len) + { + /* Still more to go in rm2 */ + if (rm2->mask[i2].start < rm1->mask[rm1->len-1].stop) + return 0; + } + + return close; +} + +static void region_mask_merge(region_mask *rm1, const region_mask *rm2, int newlen) +{ + int o, i1, i2; + + /* First, ensure that rm1 is long enough */ + if (rm1->cap < newlen) + { + int newcap = rm1->cap ? rm1->cap : 2; + do + { + newcap *= 2; + } + while (newcap < newlen); + rm1->mask = fz_resize_array(rm1->ctx, rm1->mask, newcap, sizeof(*rm1->mask)); + rm1->cap = newcap; + } + + /* Now run backwards along rm1, filling it out with the merged regions */ + for (o = newlen-1, i1 = rm1->len-1, i2 = rm2->len-1; o >= 0; o--) + { + /* So we read from i1 and i2 and store in o */ + if (i1 < 0) + { + /* Just copy i2 */ + rm1->mask[o] = rm2->mask[i2]; + i2--; + } + else if (i2 < 0) + { + /* Just copy i1 */ + rm1->mask[o] = rm1->mask[i1]; + i1--; + } + else if (rm1->mask[i1].stop < rm2->mask[i2].start) + { + /* rm1's region is entirely before rm2's - copy rm2's */ + rm1->mask[o] = rm2->mask[i2]; + i2--; + } + else if (rm2->mask[i2].stop < rm1->mask[i1].start) + { + /* rm2's region is entirely before rm1's - copy rm1's */ + rm1->mask[o] = rm1->mask[i1]; + i1--; + } + else + { + /* We must be merging */ + rm1->mask[o].ave_start = (rm1->mask[i1].start * rm1->freq + rm2->mask[i2].start * rm2->freq)/(rm1->freq + rm2->freq); + rm1->mask[o].ave_stop = (rm1->mask[i1].stop * rm1->freq + rm2->mask[i2].stop * rm2->freq)/(rm1->freq + rm2->freq); + rm1->mask[o].start = fz_min(rm1->mask[i1].start, rm2->mask[i2].start); + rm1->mask[o].stop = fz_max(rm1->mask[i1].stop, rm2->mask[i2].stop); + i1--; + i2--; + } + } + rm1->freq += rm2->freq; + rm1->len = newlen; +} + +static region_mask *region_masks_match(const region_masks *rms, const region_mask *rm, fz_text_line *line, region_mask *prev_match) +{ + int i; + float best_score = 9999999; + float score; + int best = -1; + int best_count = 0; + + /* If the 'previous match' matches, use it regardless. */ + if (prev_match && region_mask_matches(prev_match, rm, &score)) + { + return prev_match; + } + + /* Run through and find the 'most compatible' region mask. We are + * guaranteed that there will always be at least one compatible one! + */ + for (i=0; i < rms->len; i++) + { + int count = region_mask_matches(rms->mask[i], rm, &score); + if (count > best_count || (count == best_count && (score < best_score || best == -1))) + { + best = i; + best_score = score; + best_count = count; + } + } + assert(best >= 0 && best < rms->len); + + /* So we have the matching mask. */ + return rms->mask[best]; +} + +#ifdef DEBUG_MASKS +static void +dump_region_mask(const region_mask *rm) +{ + int j; + for (j = 0; j < rm->len; j++) + { + printf("%g->%g ", rm->mask[j].start, rm->mask[j].stop); + } + printf("* %d\n", rm->freq); +} + +static void +dump_region_masks(const region_masks *rms) +{ + int i; + + for (i = 0; i < rms->len; i++) + { + region_mask *rm = rms->mask[i]; + dump_region_mask(rm); + } +} +#endif + +static void region_masks_add(region_masks *rms, region_mask *rm) +{ + /* Add rm to rms */ + if (rms->len == rms->cap) + { + int newcap = (rms->cap ? rms->cap * 2 : 4); + rms->mask = fz_resize_array(rms->ctx, rms->mask, newcap, sizeof(*rms->mask)); + rms->cap = newcap; + } + rms->mask[rms->len] = rm; + rms->len++; +} + +static void region_masks_sort(region_masks *rms) +{ + int i, j; + + /* First calculate sizes */ + for (i=0; i < rms->len; i++) + { + region_mask *rm = rms->mask[i]; + float size = 0; + for (j=0; j < rm->len; j++) + { + size += rm->mask[j].stop - rm->mask[j].start; + } + rm->size = size; + } + + /* Now, sort on size */ + /* FIXME: bubble sort - use heapsort for efficiency */ + for (i=0; i < rms->len-1; i++) + { + for (j=i+1; j < rms->len; j++) + { + if (rms->mask[i]->size < rms->mask[j]->size) + { + region_mask *tmp = rms->mask[i]; + rms->mask[i] = rms->mask[j]; + rms->mask[j] = tmp; + } + } + } +} + +static void region_masks_merge(region_masks *rms, region_mask *rm) +{ + int i; + float best_score = 9999999; + float score; + int best = -1; + int best_count = 0; + +#ifdef DEBUG_MASKS + printf("\nAdding:\n"); + dump_region_mask(rm); + printf("To:\n"); + dump_region_masks(rms); +#endif + for (i=0; i < rms->len; i++) + { + int count = region_masks_mergeable(rms->mask[i], rm, &score); + if (count && (score < best_score || best == -1)) + { + best = i; + best_count = count; + best_score = score; + } + } + if (best != -1) + { + region_mask_merge(rms->mask[best], rm, best_count); +#ifdef DEBUG_MASKS + printf("Merges to give:\n"); + dump_region_masks(rms); +#endif + free_region_mask(rm); + return; + } + region_masks_add(rms, rm); +#ifdef DEBUG_MASKS + printf("Adding new one to give:\n"); + dump_region_masks(rms); +#endif +} + +static region_mask * +new_region_mask(fz_context *ctx, const fz_point *blv) +{ + region_mask *rm = fz_malloc_struct(ctx, region_mask); + rm->ctx = ctx; + rm->freq = 1; + rm->blv = *blv; + rm->cap = 0; + rm->len = 0; + rm->mask = NULL; + return rm; +} + +static void +region_mask_project(const region_mask *rm, const fz_point *min, const fz_point *max, float *start, float *end) +{ + /* We project min and max down onto the blv */ + float s = min->x * rm->blv.x + min->y * rm->blv.y; + float e = max->x * rm->blv.x + max->y * rm->blv.y; + if (s > e) + { + *start = e; + *end = s; + } + else + { + *start = s; + *end = e; + } +} + +static void +region_mask_add(region_mask *rm, const fz_point *min, const fz_point *max) +{ + float start, end; + int i, j; + + region_mask_project(rm, min, max, &start, &end); + + /* Now add start/end into our region list. Typically we will be adding + * to the end of the region list, so search from there backwards. */ + for (i = rm->len; i > 0;) + { + if (start > rm->mask[i-1].stop) + break; + i--; + } + /* So we know that our interval can only affect list items >= i. + * We know that start is after our previous end. */ + if (i == rm->len || end < rm->mask[i].start) + { + /* Insert new one. No overlap. No merging */ + if (rm->len == rm->cap) + { + int newcap = (rm->cap ? rm->cap * 2 : 4); + rm->mask = fz_resize_array(rm->ctx, rm->mask, newcap, sizeof(*rm->mask)); + rm->cap = newcap; + } + if (rm->len > i) + memmove(&rm->mask[i+1], &rm->mask[i], (rm->len - i) * sizeof(*rm->mask)); + rm->mask[i].ave_start = start; + rm->mask[i].ave_stop = end; + rm->mask[i].start = start; + rm->mask[i].stop = end; + rm->len++; + } + else + { + /* Extend current one down. */ + rm->mask[i].ave_start = start; + rm->mask[i].start = start; + if (rm->mask[i].stop < end) + { + rm->mask[i].stop = end; + rm->mask[i].ave_stop = end; + /* Our region may now extend upwards too far */ + i++; + j = i; + while (j < rm->len && rm->mask[j].start <= end) + { + rm->mask[i-1].stop = end = rm->mask[j].stop; + j++; + } + if (i != j) + { + /* Move everything from j down to i */ + while (j < rm->len) + { + rm->mask[i++] = rm->mask[j++]; + } + } + rm->len -= j-i; + } + } +} + +static int +region_mask_column(region_mask *rm, const fz_point *min, const fz_point *max, int *align, float *colw, float *left_) +{ + float start, end, left, right; + int i; + + region_mask_project(rm, min, max, &start, &end); + + for (i = 0; i < rm->len; i++) + { + /* The use of MY_EPSILON here is because we might be matching + * start/end values calculated with slightly different blv's */ + if (rm->mask[i].start - MY_EPSILON <= start && rm->mask[i].stop + MY_EPSILON >= end) + break; + } + if (i >= rm->len) + { + *align = 0; + *colw = 0; + return 0; + } + left = start - rm->mask[i].start; + right = rm->mask[i].stop - end; + if (left < 1 && right < 1) + *align = rm->mask[i].align; + else if (left*2 <= right) + *align = 0; /* Left */ + else if (right * 2 < left) + *align = 2; /* Right */ + else + *align = 1; + *left_ = left; + *colw = rm->mask[i].colw; + return i; +} + +static void +region_mask_alignment(region_mask *rm) +{ + int i; + float width = 0; + + for (i = 0; i < rm->len; i++) + { + width += rm->mask[i].stop - rm->mask[i].start; + } + for (i = 0; i < rm->len; i++) + { + region *r = &rm->mask[i]; + float left = r->ave_start - r->start; + float right = r->stop - r->ave_stop; + if (left*2 <= right) + r->align = 0; /* Left */ + else if (right * 2 < left) + r->align = 2; /* Right */ + else + r->align = 1; + r->colw = 100 * (rm->mask[i].stop - rm->mask[i].start) / width; + } +} + +static void +region_masks_alignment(region_masks *rms) +{ + int i; + + for (i = 0; i < rms->len; i++) + { + region_mask_alignment(rms->mask[i]); + } +} + +static int +is_unicode_hyphen(int c) +{ + /* We omit 0x2011 (Non breaking hyphen) and 0x2043 (Hyphen Bullet) + * from this list. */ + return (c == '-' || + c == 0x2010 || /* Hyphen */ + c == 0x002d || /* Hyphen-Minus */ + c == 0x00ad || /* Soft hyphen */ + c == 0x058a || /* Armenian Hyphen */ + c == 0x1400 || /* Canadian Syllabive Hyphen */ + c == 0x1806); /* Mongolian Todo soft hyphen */ +} + +static int +is_unicode_hyphenatable(int c) +{ + /* This is a pretty ad-hoc collection. It may need tuning. */ + return ((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c >= 0x00c0 && c <= 0x00d6) || + (c >= 0x00d8 && c <= 0x00f6) || + (c >= 0x00f8 && c <= 0x02af) || + (c >= 0x1d00 && c <= 0x1dbf) || + (c >= 0x1e00 && c <= 0x1eff) || + (c >= 0x2c60 && c <= 0x2c7f) || + (c >= 0xa722 && c <= 0xa78e) || + (c >= 0xa790 && c <= 0xa793) || + (c >= 0xa7a8 && c <= 0xa7af) || + (c >= 0xfb00 && c <= 0xfb07) || + (c >= 0xff21 && c <= 0xff3a) || + (c >= 0xff41 && c <= 0xff5a)); +} + +static void +dehyphenate(fz_text_span *s1, fz_text_span *s2) +{ + int i; + + for (i = s1->len-1; i > 0; i--) + if (!is_unicode_wspace(s1->text[i].c)) + break; + /* Can't leave an empty span. */ + if (i == 0) + return; + + if (!is_unicode_hyphen(s1->text[i].c)) + return; + if (!is_unicode_hyphenatable(s1->text[i-1].c)) + return; + if (!is_unicode_hyphenatable(s2->text[0].c)) + return; + s1->len = i; + s2->spacing = 0; +} + +void +fz_analyze_text(fz_context *ctx, fz_text_sheet *sheet, fz_text_page *page) +{ + fz_text_line *line; + fz_text_span *span; + line_heights *lh; + region_masks *rms; + int block_num; + + /* Simple paragraph analysis; look for the most common 'inter line' + * spacing. This will be assumed to be our line spacing. Anything + * more than 25% wider than this will be assumed to be a paragraph + * space. */ + + /* Step 1: Gather the line height information */ + lh = new_line_heights(ctx); + for (block_num = 0; block_num < page->len; block_num++) + { + fz_text_block *block; + + if (page->blocks[block_num].type != FZ_PAGE_BLOCK_TEXT) + continue; + block = page->blocks[block_num].u.text; + + for (line = block->lines; line < block->lines + block->len; line++) + { + /* For every style in the line, add lineheight to the + * record for that style. FIXME: This is a nasty n^2 + * algorithm at the moment. */ + fz_text_style *style = NULL; + + if (line->distance == 0) + continue; + + for (span = line->first_span; span; span = span->next) + { + int char_num; + + if (is_list_entry(line, span, &char_num)) + goto list_entry; + + for (; char_num < span->len; char_num++) + { + fz_text_char *chr = &span->text[char_num]; + + /* Ignore any whitespace chars */ + if (is_unicode_wspace(chr->c)) + continue; + + if (chr->style != style) + { + /* Have we had this style before? */ + int match = 0; + fz_text_span *span2; + for (span2 = line->first_span; span2; span2 = span2->next) + { + int char_num2; + for (char_num2 = 0; char_num2 < span2->len; char_num2++) + { + fz_text_char *chr2 = &span2->text[char_num2]; + if (chr2->style == chr->style) + { + match = 1; + break; + } + } + } + if (char_num > 0 && match == 0) + { + fz_text_span *span2 = span; + int char_num2; + for (char_num2 = 0; char_num2 < char_num; char_num2++) + { + fz_text_char *chr2 = &span2->text[char_num2]; + if (chr2->style == chr->style) + { + match = 1; + break; + } + } + } + if (match == 0) + insert_line_height(lh, chr->style, line->distance); + style = chr->style; + } + } +list_entry: + {} + } + } + } + + /* Step 2: Find the most popular line height for each style */ + cull_line_heights(lh); + + /* Step 3: Run through the blocks, breaking each block into two if + * the line height isn't right. */ + for (block_num = 0; block_num < page->len; block_num++) + { + int line_num; + fz_text_block *block; + + if (page->blocks[block_num].type != FZ_PAGE_BLOCK_TEXT) + continue; + block = page->blocks[block_num].u.text; + + for (line_num = 0; line_num < block->len; line_num++) + { + /* For every style in the line, check to see if lineheight + * is correct for that style. FIXME: We check each style + * more than once, currently. */ + int ok = 0; /* -1 = early exit, split now. 0 = split. 1 = don't split. */ + fz_text_style *style = NULL; + line = &block->lines[line_num]; + + if (line->distance == 0) + continue; + +#ifdef DEBUG_LINE_HEIGHTS + printf("line height=%g nspans=%d\n", line->distance, line->len); +#endif + for (span = line->first_span; span; span = span->next) + { + int char_num; + + if (is_list_entry(line, span, &char_num)) + goto force_paragraph; + + /* Now we do the rest of the line */ + for (; char_num < span->len; char_num++) + { + fz_text_char *chr = &span->text[char_num]; + + /* Ignore any whitespace chars */ + if (is_unicode_wspace(chr->c)) + continue; + + if (chr->style != style) + { + float proper_step = line_height_for_style(lh, chr->style); + if (proper_step * 0.95 <= line->distance && line->distance <= proper_step * 1.05) + { + ok = 1; + break; + } + style = chr->style; + } + } + if (ok) + break; + } + if (!ok) + { +force_paragraph: + split_block(ctx, page, block_num, line_num); + break; + } + } + } + free_line_heights(lh); + + /* Simple line region analysis: + * For each line: + * form a list of 'start/stop' points (henceforth a 'region mask') + * find the normalised baseline vector for the line. + * Store the region mask and baseline vector. + * Collate lines that have compatible region masks and identical + * baseline vectors. + * If the collated masks are column-like, then split into columns. + * Otherwise split into tables. + */ + rms = new_region_masks(ctx); + + /* Step 1: Form the region masks and store them into a list with the + * normalised baseline vectors. */ + for (block_num = 0; block_num < page->len; block_num++) + { + fz_text_block *block; + + if (page->blocks[block_num].type != FZ_PAGE_BLOCK_TEXT) + continue; + block = page->blocks[block_num].u.text; + + for (line = block->lines; line < block->lines + block->len; line++) + { + fz_point blv; + region_mask *rm; + +#ifdef DEBUG_MASKS + printf("Line: "); + dump_line(line); +#endif + blv = line->first_span->max; + blv.x -= line->first_span->min.x; + blv.y -= line->first_span->min.y; + fz_normalize_vector(&blv); + + rm = new_region_mask(ctx, &blv); + for (span = line->first_span; span; span = span->next) + { + fz_point *region_min = &span->min; + fz_point *region_max = &span->max; + + /* Treat adjacent spans as one big region */ + while (span->next && span->next->spacing < 1.5) + { + span = span->next; + region_max = &span->max; + } + + region_mask_add(rm, region_min, region_max); + } +#ifdef DEBUG_MASKS + dump_region_mask(rm); +#endif + region_masks_add(rms, rm); + } + } + + /* Step 2: Sort the region_masks by size of masked region */ + region_masks_sort(rms); + +#ifdef DEBUG_MASKS + printf("Sorted list of regions:\n"); + dump_region_masks(rms); +#endif + /* Step 3: Merge the region masks where possible (large ones first) */ + { + int i; + region_masks *rms2; + rms2 = new_region_masks(ctx); + for (i=0; i < rms->len; i++) + { + region_mask *rm = rms->mask[i]; + rms->mask[i] = NULL; + region_masks_merge(rms2, rm); + } + free_region_masks(rms); + rms = rms2; + } + +#ifdef DEBUG_MASKS + printf("Merged list of regions:\n"); + dump_region_masks(rms); +#endif + + /* Step 4: Figure out alignment */ + region_masks_alignment(rms); + + /* Step 5: At this point, we should probably look at the region masks + * to try to guess which ones represent columns on the page. With our + * current code, we could only get blocks of lines that span 2 or more + * columns if the PDF producer wrote text out horizontally across 2 + * or more columns, and we've never seen that (yet!). So we skip this + * step for now. */ + + /* Step 6: Run through the lines again, deciding which ones fit into + * which region mask. */ + { + region_mask *prev_match = NULL; + for (block_num = 0; block_num < page->len; block_num++) + { + fz_text_block *block; + + if (page->blocks[block_num].type != FZ_PAGE_BLOCK_TEXT) + continue; + block = page->blocks[block_num].u.text; + + for (line = block->lines; line < block->lines + block->len; line++) + { + fz_point blv; + region_mask *rm; + region_mask *match; + + blv = line->first_span->max; + blv.x -= line->first_span->min.x; + blv.y -= line->first_span->min.y; + fz_normalize_vector(&blv); + +#ifdef DEBUG_MASKS + dump_line(line); +#endif + rm = new_region_mask(ctx, &blv); + for (span = line->first_span; span; span = span->next) + { + fz_point *region_min = &span->min; + fz_point *region_max = &span->max; + + /* Treat adjacent spans as one big region */ + while (span->next && span->next->spacing < 1.5) + { + span = span->next; + region_max = &span->max; + } + + region_mask_add(rm, region_min, region_max); + } +#ifdef DEBUG_MASKS + printf("Mask: "); + dump_region_mask(rm); +#endif + match = region_masks_match(rms, rm, line, prev_match); + prev_match = match; +#ifdef DEBUG_MASKS + printf("Matches: "); + dump_region_mask(match); +#endif + free_region_mask(rm); + span = line->first_span; + while (span) + { + fz_point *region_min = &span->min; + fz_point *region_max = &span->max; + fz_text_span *sn; + int col, align; + float colw, left; + + /* Treat adjacent spans as one big region */ +#ifdef DEBUG_ALIGN + dump_span(span); +#endif + for (sn = span->next; sn && sn->spacing < 1.5; sn = sn->next) + { + region_max = &sn->max; +#ifdef DEBUG_ALIGN + dump_span(sn); +#endif + } + col = region_mask_column(match, region_min, region_max, &align, &colw, &left); +#ifdef DEBUG_ALIGN + printf(" = col%d colw=%g align=%d\n", col, colw, align); +#endif + do + { + span->column = col; + span->align = align; + span->indent = left; + span->column_width = colw; + span = span->next; + } + while (span != sn); + + if (span) + span = span->next; + } + line->region = match; + } + } + free_region_masks(rms); + } + + /* Step 7: Collate lines within a block that share the same region + * mask. */ + for (block_num = 0; block_num < page->len; block_num++) + { + int line_num; + int prev_line_num; + + fz_text_block *block; + + if (page->blocks[block_num].type != FZ_PAGE_BLOCK_TEXT) + continue; + block = page->blocks[block_num].u.text; + + /* First merge lines. This may leave empty lines behind. */ + for (prev_line_num = 0, line_num = 1; line_num < block->len; line_num++) + { + fz_text_line *prev_line; + line = &block->lines[line_num]; + if (!line->first_span) + continue; + prev_line = &block->lines[prev_line_num]; + if (prev_line->region == line->region) + { + /* We only merge lines if the second line + * only uses 1 of the columns. */ + int col = line->first_span->column; + /* Copy the left value for the first span + * in the first column in this line forward + * for all the rest of the spans in the same + * column. */ + float indent = line->first_span->indent; + for (span = line->first_span->next; span; span = span->next) + { + if (col != span->column) + break; + span->indent = indent; + } + if (span) + { + prev_line_num = line_num; + continue; + } + + /* Merge line into prev_line */ + { + fz_text_span **prev_line_span = &prev_line->first_span; + int try_dehyphen = -1; + fz_text_span *prev_span = NULL; + span = line->first_span; + while (span) + { + /* Skip forwards through the original + * line, until we find a place where + * span should go. */ + if ((*prev_line_span)->column <= span->column) + { + /* The current span we are considering + * in prev_line is earlier than span. + * Just skip forwards in prev_line. */ + prev_span = (*prev_line_span); + prev_line_span = &prev_span->next; + try_dehyphen = span->column; + } + else + { + /* We want to copy span into prev_line. */ + fz_text_span *next = (*prev_line_span)->next; + + if (prev_line_span == &prev_line->first_span) + prev_line->first_span = span; + if (next == NULL) + prev_line->last_span = span; + if (try_dehyphen == span->column) + dehyphenate(prev_span, span); + try_dehyphen = -1; + prev_span = *prev_line_span = span; + span = span->next; + (*prev_line_span)->next = next; + prev_line_span = &span->next; + } + } + while (span || *prev_line_span); + line->first_span = NULL; + line->last_span = NULL; + } + } + else + prev_line_num = line_num; + } + + /* Now get rid of the empty lines */ + for (prev_line_num = 0, line_num = 0; line_num < block->len; line_num++) + { + line = &block->lines[line_num]; + if (line->first_span) + block->lines[prev_line_num++] = *line; + } + block->len = prev_line_num; + + /* Now try to spot indents */ + for (line_num = 0; line_num < block->len; line_num++) + { + fz_text_span *span_num, *sn; + int col, count; + line = &block->lines[line_num]; + + /* Run through the spans... */ + span_num = line->first_span; + { + float indent = 0; + /* For each set of spans that share the same + * column... */ + col = span_num->column; +#ifdef DEBUG_INDENTS + printf("Indent %g: ", span_num->indent); + dump_span(span_num); + printf("\n"); +#endif + + /* find the average indent of all but the first.. */ + for (sn = span_num->next, count = 0; sn && sn->column == col; sn = sn->next, count++) + { +#ifdef DEBUG_INDENTS + printf("Indent %g: ", sn->indent); + dump_span(sn); + printf("\n"); +#endif + indent += sn->indent; + sn->indent = 0; + } + if (sn != span_num->next) + indent /= count; + + /* And compare this indent with the first one... */ +#ifdef DEBUG_INDENTS + printf("Average indent %g ", indent); +#endif + indent -= span_num->indent; +#ifdef DEBUG_INDENTS + printf("delta %g ", indent); +#endif + if (fabsf(indent) < 1) + { + /* No indent worth speaking of */ + indent = 0; + } +#ifdef DEBUG_INDENTS + printf("recorded %g\n", indent); +#endif + span_num->indent = indent; + span_num = sn; + } + for (; span_num; span_num = span_num->next) + { + span_num->indent = 0; + } + } + } +} diff --git a/source/fitz/stext-search.c b/source/fitz/stext-search.c new file mode 100644 index 00000000..f1f0d203 --- /dev/null +++ b/source/fitz/stext-search.c @@ -0,0 +1,279 @@ +#include "mupdf/fitz.h" + +static inline int fz_tolower(int c) +{ + /* TODO: proper unicode case folding */ + if (c >= 'A' && c <= 'Z') + return c - 'A' + 'a'; + return c; +} + +static inline int iswhite(int c) +{ + return c == ' ' || c == '\r' || c == '\n' || c == '\t'; +} + +fz_char_and_box *fz_text_char_at(fz_char_and_box *cab, fz_text_page *page, int idx) +{ + int block_num; + int ofs = 0; + + for (block_num = 0; block_num < page->len; block_num++) + { + fz_text_block *block; + fz_text_line *line; + fz_text_span *span; + + if (page->blocks[block_num].type != FZ_PAGE_BLOCK_TEXT) + continue; + block = page->blocks[block_num].u.text; + for (line = block->lines; line < block->lines + block->len; line++) + { + for (span = line->first_span; span; span = span->next) + { + if (idx < ofs + span->len) + { + cab->c = span->text[idx - ofs].c; + fz_text_char_bbox(&cab->bbox, span, idx - ofs); + return cab; + } + ofs += span->len; + } + /* pseudo-newline */ + if (idx == ofs) + { + cab->bbox = fz_empty_rect; + cab->c = ' '; + return cab; + } + ofs++; + } + } + cab->bbox = fz_empty_rect; + cab->c = 0; + return cab; +} + +static int charat(fz_text_page *page, int idx) +{ + fz_char_and_box cab; + return fz_text_char_at(&cab, page, idx)->c; +} + +static fz_rect *bboxat(fz_text_page *page, int idx, fz_rect *bbox) +{ + fz_char_and_box cab; + /* FIXME: Nasty extra copy */ + *bbox = fz_text_char_at(&cab, page, idx)->bbox; + return bbox; +} + +static int textlen(fz_text_page *page) +{ + int len = 0; + int block_num; + + for (block_num = 0; block_num < page->len; block_num++) + { + fz_text_block *block; + fz_text_line *line; + fz_text_span *span; + + if (page->blocks[block_num].type != FZ_PAGE_BLOCK_TEXT) + continue; + block = page->blocks[block_num].u.text; + for (line = block->lines; line < block->lines + block->len; line++) + { + for (span = line->first_span; span; span = span->next) + { + len += span->len; + } + len++; /* pseudo-newline */ + } + } + return len; +} + +static int match(fz_text_page *page, const char *s, int n) +{ + int orig = n; + int c; + while (*s) + { + s += fz_chartorune(&c, (char *)s); + if (iswhite(c) && iswhite(charat(page, n))) + { + const char *s_next; + + /* Skip over whitespace in the document */ + do + n++; + while (iswhite(charat(page, n))); + + /* Skip over multiple whitespace in the search string */ + while (s_next = s + fz_chartorune(&c, (char *)s), iswhite(c)) + s = s_next; + } + else + { + if (fz_tolower(c) != fz_tolower(charat(page, n))) + return 0; + n++; + } + } + return n - orig; +} + +int +fz_search_text_page(fz_context *ctx, fz_text_page *text, const char *needle, fz_rect *hit_bbox, int hit_max) +{ + int pos, len, i, n, hit_count; + + if (strlen(needle) == 0) + return 0; + + hit_count = 0; + len = textlen(text); + for (pos = 0; pos < len; pos++) + { + n = match(text, needle, pos); + if (n) + { + fz_rect linebox = fz_empty_rect; + for (i = 0; i < n; i++) + { + fz_rect charbox; + bboxat(text, pos + i, &charbox); + if (!fz_is_empty_rect(&charbox)) + { + if (charbox.y0 != linebox.y0 || fz_abs(charbox.x0 - linebox.x1) > 5) + { + if (!fz_is_empty_rect(&linebox) && hit_count < hit_max) + hit_bbox[hit_count++] = linebox; + linebox = charbox; + } + else + { + fz_union_rect(&linebox, &charbox); + } + } + } + if (!fz_is_empty_rect(&linebox) && hit_count < hit_max) + hit_bbox[hit_count++] = linebox; + } + } + + return hit_count; +} + +int +fz_highlight_selection(fz_context *ctx, fz_text_page *page, fz_rect rect, fz_rect *hit_bbox, int hit_max) +{ + fz_rect linebox, charbox; + fz_text_block *block; + fz_text_line *line; + fz_text_span *span; + int i, block_num, hit_count; + + float x0 = rect.x0; + float x1 = rect.x1; + float y0 = rect.y0; + float y1 = rect.y1; + + hit_count = 0; + + for (block_num = 0; block_num < page->len; block_num++) + { + if (page->blocks[block_num].type != FZ_PAGE_BLOCK_TEXT) + continue; + block = page->blocks[block_num].u.text; + for (line = block->lines; line < block->lines + block->len; line++) + { + linebox = fz_empty_rect; + for (span = line->first_span; span; span = span->next) + { + for (i = 0; i < span->len; i++) + { + fz_text_char_bbox(&charbox, span, i); + if (charbox.x1 >= x0 && charbox.x0 <= x1 && charbox.y1 >= y0 && charbox.y0 <= y1) + { + if (charbox.y0 != linebox.y0 || fz_abs(charbox.x0 - linebox.x1) > 5) + { + if (!fz_is_empty_rect(&linebox) && hit_count < hit_max) + hit_bbox[hit_count++] = linebox; + linebox = charbox; + } + else + { + fz_union_rect(&linebox, &charbox); + } + } + } + } + if (!fz_is_empty_rect(&linebox) && hit_count < hit_max) + hit_bbox[hit_count++] = linebox; + } + } + + return hit_count; +} + +char * +fz_copy_selection(fz_context *ctx, fz_text_page *page, fz_rect rect) +{ + fz_buffer *buffer; + fz_rect hitbox; + int c, i, block_num, seen = 0; + char *s; + + float x0 = rect.x0; + float x1 = rect.x1; + float y0 = rect.y0; + float y1 = rect.y1; + + buffer = fz_new_buffer(ctx, 1024); + + for (block_num = 0; block_num < page->len; block_num++) + { + fz_text_block *block; + fz_text_line *line; + fz_text_span *span; + + if (page->blocks[block_num].type != FZ_PAGE_BLOCK_TEXT) + continue; + block = page->blocks[block_num].u.text; + for (line = block->lines; line < block->lines + block->len; line++) + { + for (span = line->first_span; span; span = span->next) + { + if (seen) + { + fz_write_buffer_byte(ctx, buffer, '\n'); + } + + seen = 0; + + for (i = 0; i < span->len; i++) + { + fz_text_char_bbox(&hitbox, span, i); + c = span->text[i].c; + if (c < 32) + c = '?'; + if (hitbox.x1 >= x0 && hitbox.x0 <= x1 && hitbox.y1 >= y0 && hitbox.y0 <= y1) + { + fz_write_buffer_rune(ctx, buffer, c); + seen = 1; + } + } + + seen = (seen && span == line->last_span); + } + } + } + + fz_write_buffer_byte(ctx, buffer, 0); + + s = (char*)buffer->data; + fz_free(ctx, buffer); + return s; +} diff --git a/source/fitz/store.c b/source/fitz/store.c new file mode 100644 index 00000000..609f10dc --- /dev/null +++ b/source/fitz/store.c @@ -0,0 +1,638 @@ +#include "mupdf/fitz.h" + +typedef struct fz_item_s fz_item; + +struct fz_item_s +{ + void *key; + fz_storable *val; + unsigned int size; + fz_item *next; + fz_item *prev; + fz_store *store; + fz_store_type *type; +}; + +struct fz_store_s +{ + int refs; + + /* Every item in the store is kept in a doubly linked list, ordered + * by usage (so LRU entries are at the end). */ + fz_item *head; + fz_item *tail; + + /* We have a hash table that allows to quickly find a subset of the + * entries (those whose keys are indirect objects). */ + fz_hash_table *hash; + + /* We keep track of the size of the store, and keep it below max. */ + unsigned int max; + unsigned int size; +}; + +void +fz_new_store_context(fz_context *ctx, unsigned int max) +{ + fz_store *store; + store = fz_malloc_struct(ctx, fz_store); + fz_try(ctx) + { + store->hash = fz_new_hash_table(ctx, 4096, sizeof(fz_store_hash), FZ_LOCK_ALLOC); + } + fz_catch(ctx) + { + fz_free(ctx, store); + fz_rethrow(ctx); + } + store->refs = 1; + store->head = NULL; + store->tail = NULL; + store->size = 0; + store->max = max; + ctx->store = store; +} + +void * +fz_keep_storable(fz_context *ctx, fz_storable *s) +{ + if (s == NULL) + return NULL; + fz_lock(ctx, FZ_LOCK_ALLOC); + if (s->refs > 0) + s->refs++; + fz_unlock(ctx, FZ_LOCK_ALLOC); + return s; +} + +void +fz_drop_storable(fz_context *ctx, fz_storable *s) +{ + int do_free = 0; + + if (s == NULL) + return; + fz_lock(ctx, FZ_LOCK_ALLOC); + if (s->refs < 0) + { + /* It's a static object. Dropping does nothing. */ + } + else if (--s->refs == 0) + { + /* If we are dropping the last reference to an object, then + * it cannot possibly be in the store (as the store always + * keeps a ref to everything in it, and doesn't drop via + * this method. So we can simply drop the storable object + * itself without any operations on the fz_store. */ + do_free = 1; + } + fz_unlock(ctx, FZ_LOCK_ALLOC); + if (do_free) + s->free(ctx, s); +} + +static void +evict(fz_context *ctx, fz_item *item) +{ + fz_store *store = ctx->store; + int drop; + + store->size -= item->size; + /* Unlink from the linked list */ + if (item->next) + item->next->prev = item->prev; + else + store->tail = item->prev; + if (item->prev) + item->prev->next = item->next; + else + store->head = item->next; + /* Drop a reference to the value (freeing if required) */ + drop = (item->val->refs > 0 && --item->val->refs == 0); + /* Remove from the hash table */ + if (item->type->make_hash_key) + { + fz_store_hash hash = { NULL }; + hash.free = item->val->free; + if (item->type->make_hash_key(&hash, item->key)) + fz_hash_remove(ctx, store->hash, &hash); + } + fz_unlock(ctx, FZ_LOCK_ALLOC); + if (drop) + item->val->free(ctx, item->val); + /* Always drops the key and free the item */ + item->type->drop_key(ctx, item->key); + fz_free(ctx, item); + fz_lock(ctx, FZ_LOCK_ALLOC); +} + +static int +ensure_space(fz_context *ctx, unsigned int tofree) +{ + fz_item *item, *prev; + unsigned int count; + fz_store *store = ctx->store; + + fz_assert_lock_held(ctx, FZ_LOCK_ALLOC); + + /* First check that we *can* free tofree; if not, we'd rather not + * cache this. */ + count = 0; + for (item = store->tail; item; item = item->prev) + { + if (item->val->refs == 1) + { + count += item->size; + if (count >= tofree) + break; + } + } + + /* If we ran out of items to search, then we can never free enough */ + if (item == NULL) + { + return 0; + } + + /* Actually free the items */ + count = 0; + for (item = store->tail; item; item = prev) + { + prev = item->prev; + if (item->val->refs == 1) + { + /* Free this item. Evict has to drop the lock to + * manage that, which could cause prev to be removed + * in the meantime. To avoid that we bump its reference + * count here. This may cause another simultaneous + * evict process to fail to make enough space as prev is + * pinned - but that will only happen if we're near to + * the limit anyway, and it will only cause something to + * not be cached. */ + count += item->size; + if (prev) + prev->val->refs++; + evict(ctx, item); /* Drops then retakes lock */ + /* So the store has 1 reference to prev, as do we, so + * no other evict process can have thrown prev away in + * the meantime. So we are safe to just decrement its + * reference count here. */ + if (prev) + --prev->val->refs; + + if (count >= tofree) + return count; + } + } + + return count; +} + +static void +touch(fz_store *store, fz_item *item) +{ + if (item->next != item) + { + /* Already in the list - unlink it */ + if (item->next) + item->next->prev = item->prev; + else + store->tail = item->prev; + if (item->prev) + item->prev->next = item->next; + else + store->head = item->next; + } + /* Now relink it at the start of the LRU chain */ + item->next = store->head; + if (item->next) + item->next->prev = item; + else + store->tail = item; + store->head = item; + item->prev = NULL; +} + +void * +fz_store_item(fz_context *ctx, void *key, void *val_, unsigned int itemsize, fz_store_type *type) +{ + fz_item *item = NULL; + unsigned int size; + fz_storable *val = (fz_storable *)val_; + fz_store *store = ctx->store; + fz_store_hash hash = { NULL }; + int use_hash = 0; + unsigned pos; + + if (!store) + return NULL; + + fz_var(item); + + if (store->max != FZ_STORE_UNLIMITED && store->max < itemsize) + { + /* Our item would take up more room than we can ever + * possibly have in the store. Just give up now. */ + return NULL; + } + + /* If we fail for any reason, we swallow the exception and continue. + * All that the above program will see is that we failed to store + * the item. */ + fz_try(ctx) + { + item = fz_malloc_struct(ctx, fz_item); + } + fz_catch(ctx) + { + return NULL; + } + + if (type->make_hash_key) + { + hash.free = val->free; + use_hash = type->make_hash_key(&hash, key); + } + + type->keep_key(ctx, key); + fz_lock(ctx, FZ_LOCK_ALLOC); + + /* Fill out the item. To start with, we always set item->next == item + * and item->prev == item. This is so that we can spot items that have + * been put into the hash table without having made it into the linked + * list yet. */ + item->key = key; + item->val = val; + item->size = itemsize; + item->next = item; + item->prev = item; + item->type = type; + + /* If we can index it fast, put it into the hash table. This serves + * to check whether we have one there already. */ + if (use_hash) + { + fz_item *existing; + + fz_try(ctx) + { + /* May drop and retake the lock */ + existing = fz_hash_insert_with_pos(ctx, store->hash, &hash, item, &pos); + } + fz_catch(ctx) + { + /* Any error here means that item never made it into the + * hash - so no one else can have a reference. */ + fz_unlock(ctx, FZ_LOCK_ALLOC); + fz_free(ctx, item); + type->drop_key(ctx, key); + return NULL; + } + if (existing) + { + /* There was one there already! Take a new reference + * to the existing one, and drop our current one. */ + touch(store, existing); + if (existing->val->refs > 0) + existing->val->refs++; + fz_unlock(ctx, FZ_LOCK_ALLOC); + fz_free(ctx, item); + type->drop_key(ctx, key); + return existing->val; + } + } + /* Now bump the ref */ + if (val->refs > 0) + val->refs++; + /* If we haven't got an infinite store, check for space within it */ + if (store->max != FZ_STORE_UNLIMITED) + { + size = store->size + itemsize; + while (size > store->max) + { + /* ensure_space may drop, then retake the lock */ + int saved = ensure_space(ctx, size - store->max); + if (saved == 0) + { + /* Failed to free any space. */ + /* If we are using the hash table, then we've already + * inserted item - remove it. */ + if (use_hash) + { + /* If someone else has already picked up a reference + * to item, then we cannot remove it. Leave it in the + * store, and we'll live with being over budget. We + * know this is the case, if it's in the linked list. */ + if (item->next != item) + break; + fz_hash_remove_fast(ctx, store->hash, &hash, pos); + } + fz_unlock(ctx, FZ_LOCK_ALLOC); + fz_free(ctx, item); + type->drop_key(ctx, key); + if (val->refs > 0) + val->refs--; + return NULL; + } + size -= saved; + } + } + store->size += itemsize; + + /* Regardless of whether it's indexed, it goes into the linked list */ + touch(store, item); + fz_unlock(ctx, FZ_LOCK_ALLOC); + + return NULL; +} + +void * +fz_find_item(fz_context *ctx, fz_store_free_fn *free, void *key, fz_store_type *type) +{ + fz_item *item; + fz_store *store = ctx->store; + fz_store_hash hash = { NULL }; + int use_hash = 0; + + if (!store) + return NULL; + + if (!key) + return NULL; + + if (type->make_hash_key) + { + hash.free = free; + use_hash = type->make_hash_key(&hash, key); + } + + fz_lock(ctx, FZ_LOCK_ALLOC); + if (use_hash) + { + /* We can find objects keyed on indirected objects quickly */ + item = fz_hash_find(ctx, store->hash, &hash); + } + else + { + /* Others we have to hunt for slowly */ + for (item = store->head; item; item = item->next) + { + if (item->val->free == free && !type->cmp_key(item->key, key)) + break; + } + } + if (item) + { + /* LRU the block. This also serves to ensure that any item + * picked up from the hash before it has made it into the + * linked list does not get whipped out again due to the + * store being full. */ + touch(store, item); + /* And bump the refcount before returning */ + if (item->val->refs > 0) + item->val->refs++; + fz_unlock(ctx, FZ_LOCK_ALLOC); + return (void *)item->val; + } + fz_unlock(ctx, FZ_LOCK_ALLOC); + + return NULL; +} + +void +fz_remove_item(fz_context *ctx, fz_store_free_fn *free, void *key, fz_store_type *type) +{ + fz_item *item; + fz_store *store = ctx->store; + int drop; + fz_store_hash hash = { NULL }; + int use_hash = 0; + + if (type->make_hash_key) + { + hash.free = free; + use_hash = type->make_hash_key(&hash, key); + } + + fz_lock(ctx, FZ_LOCK_ALLOC); + if (use_hash) + { + /* We can find objects keyed on indirect objects quickly */ + item = fz_hash_find(ctx, store->hash, &hash); + if (item) + fz_hash_remove(ctx, store->hash, &hash); + } + else + { + /* Others we have to hunt for slowly */ + for (item = store->head; item; item = item->next) + if (item->val->free == free && !type->cmp_key(item->key, key)) + break; + } + if (item) + { + /* Momentarily things can be in the hash table without being + * in the list. Don't attempt to unlink these. We indicate + * such items by setting item->next == item. */ + if (item->next != item) + { + if (item->next) + item->next->prev = item->prev; + else + store->tail = item->prev; + if (item->prev) + item->prev->next = item->next; + else + store->head = item->next; + } + drop = (item->val->refs > 0 && --item->val->refs == 0); + fz_unlock(ctx, FZ_LOCK_ALLOC); + if (drop) + item->val->free(ctx, item->val); + type->drop_key(ctx, item->key); + fz_free(ctx, item); + } + else + fz_unlock(ctx, FZ_LOCK_ALLOC); +} + +void +fz_empty_store(fz_context *ctx) +{ + fz_store *store = ctx->store; + + if (store == NULL) + return; + + fz_lock(ctx, FZ_LOCK_ALLOC); + /* Run through all the items in the store */ + while (store->head) + { + evict(ctx, store->head); /* Drops then retakes lock */ + } + fz_unlock(ctx, FZ_LOCK_ALLOC); +} + +fz_store * +fz_keep_store_context(fz_context *ctx) +{ + if (ctx == NULL || ctx->store == NULL) + return NULL; + fz_lock(ctx, FZ_LOCK_ALLOC); + ctx->store->refs++; + fz_unlock(ctx, FZ_LOCK_ALLOC); + return ctx->store; +} + +void +fz_drop_store_context(fz_context *ctx) +{ + int refs; + if (ctx == NULL || ctx->store == NULL) + return; + fz_lock(ctx, FZ_LOCK_ALLOC); + refs = --ctx->store->refs; + fz_unlock(ctx, FZ_LOCK_ALLOC); + if (refs != 0) + return; + + fz_empty_store(ctx); + fz_free_hash(ctx, ctx->store->hash); + fz_free(ctx, ctx->store); + ctx->store = NULL; +} + +#ifndef NDEBUG +static void +print_item(FILE *out, void *item_) +{ + fz_item *item = (fz_item *)item_; + fprintf(out, " val=%p item=%p\n", item->val, item); + fflush(out); +} + +void +fz_print_store_locked(fz_context *ctx, FILE *out) +{ + fz_item *item, *next; + fz_store *store = ctx->store; + + fprintf(out, "-- resource store contents --\n"); + fflush(out); + + for (item = store->head; item; item = next) + { + next = item->next; + if (next) + next->val->refs++; + fprintf(out, "store[*][refs=%d][size=%d] ", item->val->refs, item->size); + fz_unlock(ctx, FZ_LOCK_ALLOC); + item->type->debug(out, item->key); + fprintf(out, " = %p\n", item->val); + fflush(out); + fz_lock(ctx, FZ_LOCK_ALLOC); + if (next) + next->val->refs--; + } + fprintf(out, "-- resource store hash contents --\n"); + fz_print_hash_details(ctx, out, store->hash, print_item); + fprintf(out, "-- end --\n"); + fflush(out); +} + +void +fz_print_store(fz_context *ctx, FILE *out) +{ + fz_lock(ctx, FZ_LOCK_ALLOC); + fz_print_store_locked(ctx, out); + fz_unlock(ctx, FZ_LOCK_ALLOC); +} +#endif + +/* This is now an n^2 algorithm - not ideal, but it'll only be bad if we are + * actually managing to scavenge lots of blocks back. */ +static int +scavenge(fz_context *ctx, unsigned int tofree) +{ + fz_store *store = ctx->store; + unsigned int count = 0; + fz_item *item, *prev; + + /* Free the items */ + for (item = store->tail; item; item = prev) + { + prev = item->prev; + if (item->val->refs == 1) + { + /* Free this item */ + count += item->size; + evict(ctx, item); /* Drops then retakes lock */ + + if (count >= tofree) + break; + + /* Have to restart search again, as prev may no longer + * be valid due to release of lock in evict. */ + prev = store->tail; + } + } + /* Success is managing to evict any blocks */ + return count != 0; +} + +int fz_store_scavenge(fz_context *ctx, unsigned int size, int *phase) +{ + fz_store *store; + unsigned int max; + + if (ctx == NULL) + return 0; + store = ctx->store; + if (store == NULL) + return 0; + +#ifdef DEBUG_SCAVENGING + printf("Scavenging: store=%d size=%d phase=%d\n", store->size, size, *phase); + fz_print_store_locked(ctx, stderr); + Memento_stats(); +#endif + do + { + unsigned int tofree; + + /* Calculate 'max' as the maximum size of the store for this phase */ + if (*phase >= 16) + max = 0; + else if (store->max != FZ_STORE_UNLIMITED) + max = store->max / 16 * (16 - *phase); + else + max = store->size / (16 - *phase) * (15 - *phase); + (*phase)++; + + /* Slightly baroque calculations to avoid overflow */ + if (size > UINT_MAX - store->size) + tofree = UINT_MAX - max; + else if (size + store->size > max) + continue; + else + tofree = size + store->size - max; + + if (scavenge(ctx, tofree)) + { +#ifdef DEBUG_SCAVENGING + printf("scavenged: store=%d\n", store->size); + fz_print_store(ctx, stderr); + Memento_stats(); +#endif + return 1; + } + } + while (max > 0); + +#ifdef DEBUG_SCAVENGING + printf("scavenging failed\n"); + fz_print_store(ctx, stderr); + Memento_listBlocks(); +#endif + return 0; +} diff --git a/source/fitz/stream-open.c b/source/fitz/stream-open.c new file mode 100644 index 00000000..9e4dc8d4 --- /dev/null +++ b/source/fitz/stream-open.c @@ -0,0 +1,210 @@ +#include "mupdf/fitz.h" + +fz_stream * +fz_new_stream(fz_context *ctx, void *state, + int(*read)(fz_stream *stm, unsigned char *buf, int len), + void(*close)(fz_context *ctx, void *state)) +{ + fz_stream *stm; + + fz_try(ctx) + { + stm = fz_malloc_struct(ctx, fz_stream); + } + fz_catch(ctx) + { + close(ctx, state); + fz_rethrow(ctx); + } + + stm->refs = 1; + stm->error = 0; + stm->eof = 0; + stm->pos = 0; + + stm->bits = 0; + stm->avail = 0; + + stm->bp = stm->buf; + stm->rp = stm->bp; + stm->wp = stm->bp; + stm->ep = stm->buf + sizeof stm->buf; + + stm->state = state; + stm->read = read; + stm->close = close; + stm->seek = NULL; + stm->ctx = ctx; + + return stm; +} + +fz_stream * +fz_keep_stream(fz_stream *stm) +{ + if (stm) + stm->refs ++; + return stm; +} + +void +fz_close(fz_stream *stm) +{ + if (!stm) + return; + stm->refs --; + if (stm->refs == 0) + { + if (stm->close) + stm->close(stm->ctx, stm->state); + fz_free(stm->ctx, stm); + } +} + +/* File stream */ + +static int read_file(fz_stream *stm, unsigned char *buf, int len) +{ + int n = read(*(int*)stm->state, buf, len); + if (n < 0) + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "read error: %s", strerror(errno)); + return n; +} + +static void seek_file(fz_stream *stm, int offset, int whence) +{ + int n = lseek(*(int*)stm->state, offset, whence); + if (n < 0) + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "cannot lseek: %s", strerror(errno)); + stm->pos = n; + stm->rp = stm->bp; + stm->wp = stm->bp; +} + +static void close_file(fz_context *ctx, void *state) +{ + int n = close(*(int*)state); + if (n < 0) + fz_warn(ctx, "close error: %s", strerror(errno)); + fz_free(ctx, state); +} + +fz_stream * +fz_open_fd(fz_context *ctx, int fd) +{ + fz_stream *stm; + int *state; + + state = fz_malloc_struct(ctx, int); + *state = fd; + + fz_try(ctx) + { + stm = fz_new_stream(ctx, state, read_file, close_file); + } + fz_catch(ctx) + { + fz_free(ctx, state); + fz_rethrow(ctx); + } + stm->seek = seek_file; + + return stm; +} + +fz_stream * +fz_open_file(fz_context *ctx, const char *name) +{ +#ifdef _WIN32 + char *s = (char*)name; + wchar_t *wname, *d; + int c, fd; + d = wname = fz_malloc(ctx, (strlen(name)+1) * sizeof(wchar_t)); + while (*s) { + s += fz_chartorune(&c, s); + *d++ = c; + } + *d = 0; + fd = _wopen(wname, O_BINARY | O_RDONLY, 0); + fz_free(ctx, wname); +#else + int fd = open(name, O_BINARY | O_RDONLY, 0); +#endif + if (fd == -1) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open %s", name); + return fz_open_fd(ctx, fd); +} + +#ifdef _WIN32 +fz_stream * +fz_open_file_w(fz_context *ctx, const wchar_t *name) +{ + int fd = _wopen(name, O_BINARY | O_RDONLY, 0); + if (fd == -1) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file %ls", name); + return fz_open_fd(ctx, fd); +} +#endif + +/* Memory stream */ + +static int read_buffer(fz_stream *stm, unsigned char *buf, int len) +{ + return 0; +} + +static void seek_buffer(fz_stream *stm, int offset, int whence) +{ + if (whence == 0) + stm->rp = stm->bp + offset; + if (whence == 1) + stm->rp += offset; + if (whence == 2) + stm->rp = stm->ep - offset; + stm->rp = fz_clampp(stm->rp, stm->bp, stm->ep); + stm->wp = stm->ep; +} + +static void close_buffer(fz_context *ctx, void *state_) +{ + fz_buffer *state = (fz_buffer *)state_; + if (state) + fz_drop_buffer(ctx, state); +} + +fz_stream * +fz_open_buffer(fz_context *ctx, fz_buffer *buf) +{ + fz_stream *stm; + + fz_keep_buffer(ctx, buf); + stm = fz_new_stream(ctx, buf, read_buffer, close_buffer); + stm->seek = seek_buffer; + + stm->bp = buf->data; + stm->rp = buf->data; + stm->wp = buf->data + buf->len; + stm->ep = buf->data + buf->len; + + stm->pos = buf->len; + + return stm; +} + +fz_stream * +fz_open_memory(fz_context *ctx, unsigned char *data, int len) +{ + fz_stream *stm; + + stm = fz_new_stream(ctx, NULL, read_buffer, close_buffer); + stm->seek = seek_buffer; + + stm->bp = data; + stm->rp = data; + stm->wp = data + len; + stm->ep = data + len; + + stm->pos = len; + + return stm; +} diff --git a/source/fitz/stream-read.c b/source/fitz/stream-read.c new file mode 100644 index 00000000..ee3d1cad --- /dev/null +++ b/source/fitz/stream-read.c @@ -0,0 +1,219 @@ +#include "mupdf/fitz.h" + +#define MIN_BOMB (100 << 20) + +int +fz_read(fz_stream *stm, unsigned char *buf, int len) +{ + int count, n; + + count = fz_mini(len, stm->wp - stm->rp); + if (count) + { + memcpy(buf, stm->rp, count); + stm->rp += count; + } + + if (count == len || stm->error || stm->eof) + return count; + + assert(stm->rp == stm->wp); + + if (len - count < stm->ep - stm->bp) + { + n = stm->read(stm, stm->bp, stm->ep - stm->bp); + if (n == 0) + { + stm->eof = 1; + } + else if (n > 0) + { + stm->rp = stm->bp; + stm->wp = stm->bp + n; + stm->pos += n; + } + + n = fz_mini(len - count, stm->wp - stm->rp); + if (n) + { + memcpy(buf + count, stm->rp, n); + stm->rp += n; + count += n; + } + } + else + { + n = stm->read(stm, buf + count, len - count); + if (n == 0) + { + stm->eof = 1; + } + else if (n > 0) + { + stm->pos += n; + count += n; + } + } + + return count; +} + +void +fz_fill_buffer(fz_stream *stm) +{ + int n; + + assert(stm->rp == stm->wp); + + if (stm->error || stm->eof) + return; + + fz_try(stm->ctx) + { + n = stm->read(stm, stm->bp, stm->ep - stm->bp); + if (n == 0) + { + stm->eof = 1; + } + else if (n > 0) + { + stm->rp = stm->bp; + stm->wp = stm->bp + n; + stm->pos += n; + } + } + fz_catch(stm->ctx) + { + /* FIXME: TryLater */ + fz_warn(stm->ctx, "read error; treating as end of file"); + stm->error = 1; + } +} + +fz_buffer * +fz_read_all(fz_stream *stm, int initial) +{ + return fz_read_best(stm, initial, NULL); +} + +fz_buffer * +fz_read_best(fz_stream *stm, int initial, int *truncated) +{ + fz_buffer *buf = NULL; + int n; + fz_context *ctx = stm->ctx; + + fz_var(buf); + + if (truncated) + *truncated = 0; + + fz_try(ctx) + { + if (initial < 1024) + initial = 1024; + + buf = fz_new_buffer(ctx, initial+1); + + while (1) + { + if (buf->len == buf->cap) + fz_grow_buffer(ctx, buf); + + if (buf->len >= MIN_BOMB && buf->len / 200 > initial) + { + fz_throw(ctx, FZ_ERROR_GENERIC, "compression bomb detected"); + } + + n = fz_read(stm, buf->data + buf->len, buf->cap - buf->len); + if (n == 0) + break; + + buf->len += n; + } + } + fz_catch(ctx) + { + /* FIXME: TryLater */ + if (truncated) + { + *truncated = 1; + } + else + { + fz_drop_buffer(ctx, buf); + fz_rethrow(ctx); + } + } + fz_trim_buffer(ctx, buf); + + return buf; +} + +void +fz_read_line(fz_stream *stm, char *mem, int n) +{ + char *s = mem; + int c = EOF; + while (n > 1) + { + c = fz_read_byte(stm); + if (c == EOF) + break; + if (c == '\r') { + c = fz_peek_byte(stm); + if (c == '\n') + fz_read_byte(stm); + break; + } + if (c == '\n') + break; + *s++ = c; + n--; + } + if (n) + *s = '\0'; +} + +int +fz_tell(fz_stream *stm) +{ + return stm->pos - (stm->wp - stm->rp); +} + +void +fz_seek(fz_stream *stm, int offset, int whence) +{ + if (stm->seek) + { + if (whence == 1) + { + offset = fz_tell(stm) + offset; + whence = 0; + } + if (whence == 0) + { + int dist = stm->pos - offset; + if (dist >= 0 && dist <= stm->wp - stm->bp) + { + stm->rp = stm->wp - dist; + stm->eof = 0; + return; + } + } + stm->seek(stm, offset, whence); + stm->eof = 0; + } + else if (whence != 2) + { + if (whence == 0) + offset -= fz_tell(stm); + if (offset < 0) + fz_warn(stm->ctx, "cannot seek backwards"); + /* dog slow, but rare enough */ + while (offset-- > 0) + fz_read_byte(stm); + } + else + fz_warn(stm->ctx, "cannot seek"); +} diff --git a/source/fitz/string.c b/source/fitz/string.c new file mode 100644 index 00000000..b29cdbdc --- /dev/null +++ b/source/fitz/string.c @@ -0,0 +1,264 @@ +#include "mupdf/fitz.h" + +char * +fz_strsep(char **stringp, const char *delim) +{ + char *ret = *stringp; + if (!ret) return NULL; + if ((*stringp = strpbrk(*stringp, delim))) + *((*stringp)++) = '\0'; + return ret; +} + +int +fz_strlcpy(char *dst, const char *src, int siz) +{ + register char *d = dst; + register const char *s = src; + register int n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} + +int +fz_strlcat(char *dst, const char *src, int siz) +{ + register char *d = dst; + register const char *s = src; + register int n = siz; + int dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (*d != '\0' && n-- != 0) + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return dlen + strlen(s); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return dlen + (s - src); /* count does not include NUL */ +} + +enum +{ + UTFmax = 4, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0xFFFD, /* decoding error in UTF */ + Runemax = 0x10FFFF, /* maximum rune value */ +}; + +enum +{ + Bit1 = 7, + Bitx = 6, + Bit2 = 5, + Bit3 = 4, + Bit4 = 3, + Bit5 = 2, + + T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */ + Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */ + T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */ + T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */ + T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */ + T5 = ((1<<(Bit5+1))-1) ^ 0xFF, /* 1111 1000 */ + + Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */ + Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */ + Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */ + Rune4 = (1<<(Bit4+3*Bitx))-1, /* 0001 1111 1111 1111 1111 1111 */ + + Maskx = (1<<Bitx)-1, /* 0011 1111 */ + Testx = Maskx ^ 0xFF, /* 1100 0000 */ + + Bad = Runeerror, +}; + +int +fz_chartorune(int *rune, const char *str) +{ + int c, c1, c2, c3; + long l; + + /* + * one character sequence + * 00000-0007F => T1 + */ + c = *(const unsigned char*)str; + if(c < Tx) { + *rune = c; + return 1; + } + + /* + * two character sequence + * 0080-07FF => T2 Tx + */ + c1 = *(const unsigned char*)(str+1) ^ Tx; + if(c1 & Testx) + goto bad; + if(c < T3) { + if(c < T2) + goto bad; + l = ((c << Bitx) | c1) & Rune2; + if(l <= Rune1) + goto bad; + *rune = l; + return 2; + } + + /* + * three character sequence + * 0800-FFFF => T3 Tx Tx + */ + c2 = *(const unsigned char*)(str+2) ^ Tx; + if(c2 & Testx) + goto bad; + if(c < T4) { + l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; + if(l <= Rune2) + goto bad; + *rune = l; + return 3; + } + + /* + * four character sequence (21-bit value) + * 10000-1FFFFF => T4 Tx Tx Tx + */ + c3 = *(const unsigned char*)(str+3) ^ Tx; + if (c3 & Testx) + goto bad; + if (c < T5) { + l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4; + if (l <= Rune3) + goto bad; + *rune = l; + return 4; + } + /* + * Support for 5-byte or longer UTF-8 would go here, but + * since we don't have that, we'll just fall through to bad. + */ + + /* + * bad decoding + */ +bad: + *rune = Bad; + return 1; +} + +int +fz_runetochar(char *str, int rune) +{ + /* Runes are signed, so convert to unsigned for range check. */ + unsigned long c = (unsigned long)rune; + + /* + * one character sequence + * 00000-0007F => 00-7F + */ + if(c <= Rune1) { + str[0] = c; + return 1; + } + + /* + * two character sequence + * 0080-07FF => T2 Tx + */ + if(c <= Rune2) { + str[0] = T2 | (c >> 1*Bitx); + str[1] = Tx | (c & Maskx); + return 2; + } + + /* + * If the Rune is out of range, convert it to the error rune. + * Do this test here because the error rune encodes to three bytes. + * Doing it earlier would duplicate work, since an out of range + * Rune wouldn't have fit in one or two bytes. + */ + if (c > Runemax) + c = Runeerror; + + /* + * three character sequence + * 0800-FFFF => T3 Tx Tx + */ + if (c <= Rune3) { + str[0] = T3 | (c >> 2*Bitx); + str[1] = Tx | ((c >> 1*Bitx) & Maskx); + str[2] = Tx | (c & Maskx); + return 3; + } + + /* + * four character sequence (21-bit value) + * 10000-1FFFFF => T4 Tx Tx Tx + */ + str[0] = T4 | (c >> 3*Bitx); + str[1] = Tx | ((c >> 2*Bitx) & Maskx); + str[2] = Tx | ((c >> 1*Bitx) & Maskx); + str[3] = Tx | (c & Maskx); + return 4; +} + +int +fz_runelen(int c) +{ + char str[10]; + return fz_runetochar(str, c); +} + +float fz_atof(const char *s) +{ + double d; + + /* The errno voodoo here checks for us reading numbers that are too + * big to fit into a double. The checks for FLT_MAX ensure that we + * don't read a number that's OK as a double and then become invalid + * as we convert to a float. */ + errno = 0; + d = strtod(s, NULL); + if (errno == ERANGE || isnan(d)) { + /* Return 1.0, as it's a small known value that won't cause a divide by 0. */ + return 1.0; + } + d = fz_clampd(d, -FLT_MAX, FLT_MAX); + return (float)d; +} + +int fz_atoi(const char *s) +{ + if (s == NULL) + return 0; + return atoi(s); +} diff --git a/source/fitz/svg-device.c b/source/fitz/svg-device.c new file mode 100644 index 00000000..4dd82120 --- /dev/null +++ b/source/fitz/svg-device.c @@ -0,0 +1,619 @@ +#include "mupdf/fitz.h" + +typedef struct svg_device_s svg_device; + +typedef struct tile_s tile; + +struct tile_s +{ + int pattern; + fz_matrix ctm; + fz_rect view; + fz_rect area; + fz_point step; +}; + +struct svg_device_s +{ + fz_context *ctx; + fz_output *out; + + int id; + + int num_tiles; + int max_tiles; + tile *tiles; +}; + +/* Helper functions */ + +static void +svg_dev_path(svg_device *sdev, fz_path *path) +{ + fz_output *out = sdev->out; + float x, y; + int i = 0; + fz_printf(out, " d=\""); + while (i < path->len) + { + switch (path->items[i++].k) + { + case FZ_MOVETO: + x = path->items[i++].v; + y = path->items[i++].v; + fz_printf(out, "M %g %g ", x, y); + break; + case FZ_LINETO: + x = path->items[i++].v; + y = path->items[i++].v; + fz_printf(out, "L %g %g ", x, y); + break; + case FZ_CURVETO: + x = path->items[i++].v; + y = path->items[i++].v; + fz_printf(out, "C %g %g ", x, y); + x = path->items[i++].v; + y = path->items[i++].v; + fz_printf(out, "%g %g ", x, y); + x = path->items[i++].v; + y = path->items[i++].v; + fz_printf(out, "%g %g ", x, y); + break; + case FZ_CLOSE_PATH: + fz_printf(out, "Z "); + break; + } + } + fz_printf(out, "\""); +} + +static void +svg_dev_ctm(svg_device *sdev, const fz_matrix *ctm) +{ + fz_output *out = sdev->out; + + if (ctm->a != 1.0 || ctm->b != 0 || ctm->c != 0 || ctm->d != 1.0 || ctm->e != 0 || ctm->f != 0) + { + fz_printf(out, " transform=\"matrix(%g,%g,%g,%g,%g,%g)\"", + ctm->a, ctm->b, ctm->c, ctm->d, ctm->e, ctm->f); + } +} + +static void +svg_dev_stroke_state(svg_device *sdev, fz_stroke_state *stroke_state) +{ + fz_output *out = sdev->out; + + fz_printf(out, " stroke-width=\"%g\"", stroke_state->linewidth); + fz_printf(out, " stroke-linecap=\"%s\"", + (stroke_state->start_cap == FZ_LINECAP_SQUARE ? "square" : + (stroke_state->start_cap == FZ_LINECAP_ROUND ? "round" : "butt"))); + if (stroke_state->dash_len != 0) + { + int i; + fz_printf(out, " stroke-dasharray="); + for (i = 0; i < stroke_state->dash_len; i++) + fz_printf(out, "%c%g", (i == 0 ? '\"' : ','), stroke_state->dash_list[i]); + fz_printf(out, "\""); + if (stroke_state->dash_phase != 0) + fz_printf(out, " stroke-dashoffset=\"%g\"", stroke_state->dash_phase); + } + if (stroke_state->linejoin == FZ_LINEJOIN_MITER || stroke_state->linejoin == FZ_LINEJOIN_MITER_XPS) + fz_printf(out, " stroke-miterlimit=\"%g\"", stroke_state->miterlimit); + fz_printf(out, " stroke-linejoin=\"%s\"", + (stroke_state->linejoin == FZ_LINEJOIN_BEVEL ? "bevel" : + (stroke_state->linejoin == FZ_LINEJOIN_ROUND ? "round" : "miter"))); +} + +static void +svg_dev_fill_color(svg_device *sdev, fz_colorspace *colorspace, float *color, float alpha) +{ + fz_context *ctx = sdev->ctx; + fz_output *out = sdev->out; + float rgb[FZ_MAX_COLORS]; + + if (colorspace != fz_device_rgb(ctx)) + { + /* If it's not rgb, make it rgb */ + colorspace->to_rgb(ctx, colorspace, color, rgb); + color = rgb; + } + + if (color[0] == 0 && color[1] == 0 && color[2] == 0) + { + /* don't send a fill, as it will be assumed to be black */ + } + else + fz_printf(out, " fill=\"rgb(%d,%d,%d)\"", (int)(255*color[0] + 0.5), (int)(255*color[1] + 0.5), (int)(255*color[2]+0.5)); + if (alpha != 1) + fz_printf(out, " fill-opacity=\"%g\"", alpha); +} + +static void +svg_dev_stroke_color(svg_device *sdev, fz_colorspace *colorspace, float *color, float alpha) +{ + fz_context *ctx = sdev->ctx; + fz_output *out = sdev->out; + float rgb[FZ_MAX_COLORS]; + + if (colorspace != fz_device_rgb(ctx)) + { + /* If it's not rgb, make it rgb */ + colorspace->to_rgb(ctx, colorspace, color, rgb); + color = rgb; + } + + fz_printf(out, " fill=\"none\" stroke=\"rgb(%d,%d,%d)\"", (int)(255*color[0] + 0.5), (int)(255*color[1] + 0.5), (int)(255*color[2]+0.5)); + if (alpha != 1) + fz_printf(out, " stroke-opacity=\"%g\"", alpha); +} + +static void +svg_dev_text(svg_device *sdev, const fz_matrix *ctm, fz_text *text) +{ + fz_output *out = sdev->out; + int i; + fz_matrix inverse; + fz_matrix local_trm; + float size; + + /* Rely on the fact that trm.{e,f} == 0 */ + size = fz_matrix_expansion(&text->trm); + local_trm.a = text->trm.a / size; + local_trm.b = text->trm.b / size; + local_trm.c = -text->trm.c / size; + local_trm.d = -text->trm.d / size; + local_trm.e = 0; + local_trm.f = 0; + fz_invert_matrix(&inverse, &local_trm); + fz_concat(&local_trm, &local_trm, ctm); + + fz_printf(out, " transform=\"matrix(%g,%g,%g,%g,%g,%g)\"", + local_trm.a, local_trm.b, local_trm.c, local_trm.d, local_trm.e, local_trm.f); + fz_printf(out, " font-size=\"%g\"", size); + fz_printf(out, " font-family=\"%s\"", text->font->name); + + fz_printf(out, " x="); + for (i=0; i < text->len; i++) + { + fz_text_item *it = &text->items[i]; + fz_point p; + p.x = it->x; + p.y = it->y; + fz_transform_point(&p, &inverse); + fz_printf(out, "%c%g", i == 0 ? '\"' : ' ', p.x); + } + fz_printf(out, "\" y="); + for (i=0; i < text->len; i++) + { + fz_text_item *it = &text->items[i]; + fz_point p; + p.x = it->x; + p.y = it->y; + fz_transform_point(&p, &inverse); + fz_printf(out, "%c%g", i == 0 ? '\"' : ' ', p.y); + } + fz_printf(out, "\">\n"); + for (i=0; i < text->len; i++) + { + fz_text_item *it = &text->items[i]; + int c = it->ucs; + if (c >= 32 && c <= 127 && c != '<' && c != '&') + fz_printf(out, "%c", c); + else + fz_printf(out, "&#x%04x;", c); + } + fz_printf(out, "\n</text>\n"); +} + +/* Entry points */ + +static void +svg_dev_fill_path(fz_device *dev, fz_path *path, int even_odd, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + svg_device *sdev = dev->user; + fz_output *out = sdev->out; + + fz_printf(out, "<path"); + svg_dev_ctm(sdev, ctm); + svg_dev_path(sdev, path); + svg_dev_fill_color(sdev, colorspace, color, alpha); + if (even_odd) + fz_printf(out, " fill-rule=\"evenodd\" "); + fz_printf(out, "/>\n"); +} + +static void +svg_dev_stroke_path(fz_device *dev, fz_path *path, fz_stroke_state *stroke, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + svg_device *sdev = dev->user; + fz_output *out = sdev->out; + + fz_printf(out, "<path"); + svg_dev_ctm(sdev, ctm); + svg_dev_stroke_state(sdev, stroke); + svg_dev_stroke_color(sdev, colorspace, color, alpha); + svg_dev_path(sdev, path); + fz_printf(out, "/>\n"); +} + +static void +svg_dev_clip_path(fz_device *dev, fz_path *path, const fz_rect *rect, int even_odd, const fz_matrix *ctm) +{ + svg_device *sdev = dev->user; + fz_output *out = sdev->out; + int num = sdev->id++; + + fz_printf(out, "<clipPath id=\"cp%d\">\n", num); + fz_printf(out, "<path"); + svg_dev_ctm(sdev, ctm); + svg_dev_path(sdev, path); + if (even_odd) + fz_printf(out, " fill-rule=\"evenodd\" "); + fz_printf(out, "/>\n</clipPath>\n<g clip-path=\"url(#cp%d)\">\n", num); +} + +static void +svg_dev_clip_stroke_path(fz_device *dev, fz_path *path, const fz_rect *rect, fz_stroke_state *stroke, const fz_matrix *ctm) +{ + svg_device *sdev = dev->user; + fz_output *out = sdev->out; + fz_context *ctx = dev->ctx; + fz_rect bounds; + int num = sdev->id++; + float white[3] = { 255, 255, 255 }; + + fz_bound_path(ctx, path, stroke, ctm, &bounds); + + fz_printf(out, "<mask id=\"ma%d\" x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" maskUnits=\"userSpaceOnUse\" maskContentUnits=\"userSpaceOnUse\" >\n", + num, bounds.x0, bounds.y0, bounds.x1 - bounds.x0, bounds.y1 - bounds.y0); + fz_printf(out, "<path"); + svg_dev_ctm(sdev, ctm); + svg_dev_stroke_state(sdev, stroke); + svg_dev_stroke_color(sdev, fz_device_rgb(ctx), white, 1); + svg_dev_path(sdev, path); + fz_printf(out, "/>\n</mask>\n<g mask=\"url(#ma%d)\">\n", num); +} + +static void +svg_dev_fill_text(fz_device *dev, fz_text *text, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + svg_device *sdev = dev->user; + fz_output *out = sdev->out; + + fz_printf(out, "<text"); + svg_dev_fill_color(sdev, colorspace, color, alpha); + svg_dev_text(sdev, ctm, text); +} + +static void +svg_dev_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + svg_device *sdev = dev->user; + fz_output *out = sdev->out; + + fz_printf(out, "<text"); + svg_dev_ctm(sdev, ctm); + svg_dev_stroke_state(sdev, stroke); + svg_dev_stroke_color(sdev, colorspace, color, alpha); + svg_dev_text(sdev, ctm, text); +} + +static void +svg_dev_clip_text(fz_device *dev, fz_text *text, const fz_matrix *ctm, int accumulate) +{ + svg_device *sdev = dev->user; + fz_output *out = sdev->out; + fz_context *ctx = dev->ctx; + fz_rect bounds; + int num = sdev->id++; + float white[3] = { 255, 255, 255 }; + + fz_bound_text(ctx, text, NULL, ctm, &bounds); + + fz_printf(out, "<mask id=\"ma%d\" x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" maskUnits=\"userSpaceOnUse\" maskContentUnits=\"userSpaceOnUse\" >\n", + num, bounds.x0, bounds.y0, bounds.x1 - bounds.x0, bounds.y1 - bounds.y0); + fz_printf(out, "<text"); + svg_dev_fill_color(sdev, fz_device_rgb(ctx), white, 1.0f); + svg_dev_text(sdev, ctm, text); + fz_printf(out, "</mask>\n<g mask=\"url(#ma%d)\">\n", num); +} + +static void +svg_dev_clip_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm) +{ + svg_device *sdev = dev->user; + fz_output *out = sdev->out; + fz_context *ctx = dev->ctx; + fz_rect bounds; + int num = sdev->id++; + float white[3] = { 255, 255, 255 }; + + fz_bound_text(ctx, text, NULL, ctm, &bounds); + + fz_printf(out, "<mask id=\"ma%d\" x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" maskUnits=\"userSpaceOnUse\" maskContentUnits=\"userSpaceOnUse\" >\n", + num, bounds.x0, bounds.y0, bounds.x1 - bounds.x0, bounds.y1 - bounds.y0); + fz_printf(out, "<text"); + svg_dev_stroke_state(sdev, stroke); + svg_dev_stroke_color(sdev, fz_device_rgb(ctx), white, 1.0f); + svg_dev_text(sdev, ctm, text); + fz_printf(out, "</mask>\n<g mask=\"url(#ma%d)\">\n", num); +} + +static void +svg_dev_ignore_text(fz_device *dev, fz_text *text, const fz_matrix *ctm) +{ +} + +static void +send_data_base64(fz_output *out, fz_buffer *buffer) +{ + int i, len; + static const char set[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + len = buffer->len/3; + for (i = 0; i < len; i++) + { + int c = buffer->data[3*i]; + int d = buffer->data[3*i+1]; + int e = buffer->data[3*i+2]; + if ((i & 15) == 0) + fz_printf(out, "\n"); + fz_printf(out, "%c%c%c%c", set[c>>2], set[((c&3)<<4)|(d>>4)], set[((d&15)<<2)|(e>>6)], set[e & 63]); + } + i *= 3; + switch (buffer->len-i) + { + case 2: + { + int c = buffer->data[i]; + int d = buffer->data[i+1]; + fz_printf(out, "%c%c%c=", set[c>>2], set[((c&3)<<4)|(d>>4)], set[((d&15)<<2)]); + break; + } + case 1: + { + int c = buffer->data[i]; + fz_printf(out, "%c%c==", set[c>>2], set[(c&3)<<4]); + break; + } + default: + case 0: + break; + } +} + +static void +svg_dev_fill_image(fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) +{ + svg_device *sdev = (svg_device *)dev->user; + fz_context *ctx = dev->ctx; + fz_output *out = sdev->out; + fz_matrix local_ctm = *ctm; + fz_matrix scale = { 1.0f/image->w, 0, 0, 1.0f/image->h, 0, 0}; + + fz_concat(&local_ctm, &scale, ctm); + fz_printf(out, "<image"); + svg_dev_ctm(sdev, &local_ctm); + fz_printf(out, "width=\"%dpx\" height=\"%dpx\" xlink:href=\"data:", image->w, image->h); + switch (image->buffer == NULL ? FZ_IMAGE_JPX : image->buffer->params.type) + { + case FZ_IMAGE_JPEG: + fz_printf(out, "image/jpeg;base64,"); + send_data_base64(out, image->buffer->buffer); + break; + case FZ_IMAGE_PNG: + fz_printf(out, "image/png;base64,"); + send_data_base64(out, image->buffer->buffer); + break; + default: + { + fz_buffer *buf = fz_image_as_png(ctx, image, image->w, image->h); + fz_printf(out, "image/png;base64,"); + send_data_base64(out, buf); + fz_drop_buffer(ctx, buf); + break; + } + } + fz_printf(out, "\"/>\n"); +} + +static void +svg_dev_fill_shade(fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) +{ +} + +static void +svg_dev_fill_image_mask(fz_device *dev, fz_image *image, const fz_matrix *ctm, +fz_colorspace *colorspace, float *color, float alpha) +{ +} + +static void +svg_dev_clip_image_mask(fz_device *dev, fz_image *image, const fz_rect *rect, const fz_matrix *ctm) +{ + svg_device *sdev = dev->user; + fz_output *out = sdev->out; + + fz_printf(out, "<g>\n"); +} + +static void +svg_dev_pop_clip(fz_device *dev) +{ + svg_device *sdev = (svg_device *)dev->user; + fz_output *out = sdev->out; + + /* FIXME */ + fz_printf(out, "</g>\n"); +} + +static void +svg_dev_begin_mask(fz_device *dev, const fz_rect *bbox, int luminosity, fz_colorspace *colorspace, float *color) +{ +} + +static void +svg_dev_end_mask(fz_device *dev) +{ + +} + +static void +svg_dev_begin_group(fz_device *dev, const fz_rect *bbox, int isolated, int knockout, int blendmode, float alpha) +{ + +} + +static void +svg_dev_end_group(fz_device *dev) +{ + +} + +static int +svg_dev_begin_tile(fz_device *dev, const fz_rect *area, const fz_rect *view, float xstep, float ystep, const fz_matrix *ctm, int id) +{ + svg_device *sdev = (svg_device *)dev->user; + fz_output *out = sdev->out; + fz_context *ctx = dev->ctx; + fz_matrix inverse; + int num; + tile *t; + + if (sdev->num_tiles == sdev->max_tiles) + { + int n = (sdev->num_tiles == 0 ? 4 : sdev->num_tiles * 2); + + sdev->tiles = fz_resize_array(ctx, sdev->tiles, n, sizeof(tile)); + sdev->max_tiles = n; + } + num = sdev->num_tiles++; + t = &sdev->tiles[num]; + t->area = *area; + t->view = *view; + t->ctm = *ctm; + t->pattern = sdev->id++; + t->step.x = xstep; + t->step.y = ystep; + + /* view = area of our reference tile in pattern space. + * area = area to tile into in pattern space. + * xstep/ystep = pattern repeat step in pattern space. + * All of these need to be transformed by ctm to get to device space. + * SVG only allows us to specify pattern tiles as axis aligned + * rectangles, so we send these through as is, and ensure that the + * correct matrix is used on the fill. + */ + + /* In svg, the reference tile is taken from (x,y) to (x+width,y+height) + * and is repeated at (x+n*width,y+m*height) for all integer n and m. + * This means that width and height correspond to xstep and ystep. */ + fz_printf(out, "<pattern id=\"pa%d\" patternUnits=\"userSpaceOnUse\" patternContentUnits=\"userSpaceOnUse\"", + t->pattern); + fz_printf(out, " x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\">\n", + view->x0, view->y0, xstep, ystep); + /* All the pattern contents will have their own ctm applied. Let's + * undo the current one to allow for this */ + fz_invert_matrix(&inverse, ctm); + fz_printf(out, "<g"); + svg_dev_ctm(sdev, &inverse); + fz_printf(out, ">\n"); + + return 0; +} + +static void +svg_dev_end_tile(fz_device *dev) +{ + svg_device *sdev = (svg_device *)dev->user; + fz_output *out = sdev->out; + int num; + tile *t; + + if (sdev->num_tiles == 0) + return; + num = --sdev->num_tiles; + t = &sdev->tiles[num]; + + fz_printf(out, "</g>\n</pattern>\n"); + fz_printf(out, "<rect"); + svg_dev_ctm(sdev, &t->ctm); + fz_printf(out, " fill=\"url(#pa%d)\" x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n", + t->pattern, t->area.x0, t->area.y0, t->area.x1 - t->area.x0, t->area.y1 - t->area.y0); +} + +static void +svg_dev_free_user(fz_device *dev) +{ + svg_device *sdev = dev->user; + fz_context *ctx = sdev->ctx; + fz_output *out = sdev->out; + + fz_free(ctx, sdev->tiles); + + fz_printf(out, "</svg>\n"); + + fz_free(ctx, sdev); +} + +fz_device *fz_new_svg_device(fz_context *ctx, fz_output *out, float page_width, float page_height) +{ + svg_device *sdev = fz_malloc_struct(ctx, svg_device); + fz_device *dev; + + fz_try(ctx) + { + sdev->ctx = ctx; + sdev->out = out; + sdev->id = 0; + + dev = fz_new_device(ctx, sdev); + } + fz_catch(ctx) + { + fz_free(ctx, sdev); + fz_rethrow(ctx); + } + + dev->free_user = svg_dev_free_user; + + dev->fill_path = svg_dev_fill_path; + dev->stroke_path = svg_dev_stroke_path; + dev->clip_path = svg_dev_clip_path; + dev->clip_stroke_path = svg_dev_clip_stroke_path; + + dev->fill_text = svg_dev_fill_text; + dev->stroke_text = svg_dev_stroke_text; + dev->clip_text = svg_dev_clip_text; + dev->clip_stroke_text = svg_dev_clip_stroke_text; + dev->ignore_text = svg_dev_ignore_text; + + dev->fill_shade = svg_dev_fill_shade; + dev->fill_image = svg_dev_fill_image; + dev->fill_image_mask = svg_dev_fill_image_mask; + dev->clip_image_mask = svg_dev_clip_image_mask; + + dev->pop_clip = svg_dev_pop_clip; + + dev->begin_mask = svg_dev_begin_mask; + dev->end_mask = svg_dev_end_mask; + dev->begin_group = svg_dev_begin_group; + dev->end_group = svg_dev_end_group; + + dev->begin_tile = svg_dev_begin_tile; + dev->end_tile = svg_dev_end_tile; + + fz_printf(out, "<?xml version=\"1.0\" standalone=\"no\"?>\n"); + fz_printf(out, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); + fz_printf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" " + "xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" " + "width=\"%gcm\" height=\"%gcm\" viewBox=\"0 0 %g %g\">\n", + page_width*2.54/72, page_height*2.54/72, page_width, page_height); + + return dev; +} diff --git a/source/fitz/text.c b/source/fitz/text.c new file mode 100644 index 00000000..81e2e1d0 --- /dev/null +++ b/source/fitz/text.c @@ -0,0 +1,154 @@ +#include "mupdf/fitz.h" + +fz_text * +fz_new_text(fz_context *ctx, fz_font *font, const fz_matrix *trm, int wmode) +{ + fz_text *text; + + text = fz_malloc_struct(ctx, fz_text); + text->font = fz_keep_font(ctx, font); + text->trm = *trm; + text->wmode = wmode; + text->len = 0; + text->cap = 0; + text->items = NULL; + + return text; +} + +void +fz_free_text(fz_context *ctx, fz_text *text) +{ + if (text != NULL) + { + fz_drop_font(ctx, text->font); + fz_free(ctx, text->items); + } + fz_free(ctx, text); +} + +fz_text * +fz_clone_text(fz_context *ctx, fz_text *old) +{ + fz_text *text; + + text = fz_malloc_struct(ctx, fz_text); + text->len = old->len; + fz_try(ctx) + { + text->items = fz_malloc_array(ctx, text->len, sizeof(fz_text_item)); + } + fz_catch(ctx) + { + fz_free(ctx, text); + fz_rethrow(ctx); + } + memcpy(text->items, old->items, text->len * sizeof(fz_text_item)); + text->font = fz_keep_font(ctx, old->font); + text->trm = old->trm; + text->wmode = old->wmode; + text->cap = text->len; + + return text; +} + +fz_rect * +fz_bound_text(fz_context *ctx, fz_text *text, const fz_stroke_state *stroke, const fz_matrix *ctm, fz_rect *bbox) +{ + fz_matrix tm, trm; + fz_rect gbox; + int i; + + if (text->len == 0) + { + *bbox = fz_empty_rect; + return bbox; + } + + // TODO: stroke state + + tm = text->trm; + + tm.e = text->items[0].x; + tm.f = text->items[0].y; + fz_concat(&trm, &tm, ctm); + fz_bound_glyph(ctx, text->font, text->items[0].gid, &trm, bbox); + + for (i = 1; i < text->len; i++) + { + if (text->items[i].gid >= 0) + { + tm.e = text->items[i].x; + tm.f = text->items[i].y; + fz_concat(&trm, &tm, ctm); + fz_bound_glyph(ctx, text->font, text->items[i].gid, &trm, &gbox); + + bbox->x0 = fz_min(bbox->x0, gbox.x0); + bbox->y0 = fz_min(bbox->y0, gbox.y0); + bbox->x1 = fz_max(bbox->x1, gbox.x1); + bbox->y1 = fz_max(bbox->y1, gbox.y1); + } + } + + if (stroke) + fz_adjust_rect_for_stroke(bbox, stroke, ctm); + + /* Compensate for the glyph cache limited positioning precision */ + bbox->x0 -= 1; + bbox->y0 -= 1; + bbox->x1 += 1; + bbox->y1 += 1; + + return bbox; +} + +static void +fz_grow_text(fz_context *ctx, fz_text *text, int n) +{ + int new_cap = text->cap; + if (text->len + n < new_cap) + return; + while (text->len + n > new_cap) + new_cap = new_cap + 36; + text->items = fz_resize_array(ctx, text->items, new_cap, sizeof(fz_text_item)); + text->cap = new_cap; +} + +void +fz_add_text(fz_context *ctx, fz_text *text, int gid, int ucs, float x, float y) +{ + fz_grow_text(ctx, text, 1); + text->items[text->len].ucs = ucs; + text->items[text->len].gid = gid; + text->items[text->len].x = x; + text->items[text->len].y = y; + text->len++; +} + +static int +isxmlmeta(int c) +{ + return c < 32 || c >= 128 || c == '&' || c == '<' || c == '>' || c == '\'' || c == '"'; +} + +static void +do_print_text(FILE *out, fz_text *text, int indent) +{ + int i, n; + for (i = 0; i < text->len; i++) + { + for (n = 0; n < indent; n++) + fputc(' ', out); + if (!isxmlmeta(text->items[i].ucs)) + fprintf(out, "<g ucs=\"%c\" gid=\"%d\" x=\"%g\" y=\"%g\" />\n", + text->items[i].ucs, text->items[i].gid, text->items[i].x, text->items[i].y); + else + fprintf(out, "<g ucs=\"U+%04X\" gid=\"%d\" x=\"%g\" y=\"%g\" />\n", + text->items[i].ucs, text->items[i].gid, text->items[i].x, text->items[i].y); + } +} + +void fz_print_text(fz_context *ctx, FILE *out, fz_text *text) +{ + do_print_text(out, text, 0); +} diff --git a/source/fitz/time.c b/source/fitz/time.c new file mode 100644 index 00000000..0e3d21b5 --- /dev/null +++ b/source/fitz/time.c @@ -0,0 +1,144 @@ +#ifdef _MSC_VER + +#include "mupdf/fitz.h" + +#include <time.h> +#include <windows.h> + +#ifndef _WINRT + +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 + +struct timeval; +struct timezone; + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + + if (tv) + { + GetSystemTimeAsFileTime(&ft); + + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + tmpres /= 10; /*convert into microseconds*/ + /*converting file time to unix epoch*/ + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + + return 0; +} + +#endif /* !_WINRT */ + +char * +fz_utf8_from_wchar(const wchar_t *s) +{ + const wchar_t *src = s; + char *d; + char *dst; + int len = 1; + + while (*src) + { + len += fz_runelen(*src++); + } + + d = malloc(len); + if (d != NULL) + { + dst = d; + src = s; + while (*src) + { + dst += fz_runetochar(dst, *src++); + } + *dst = 0; + } + return d; +} + +wchar_t * +fz_wchar_from_utf8(const char *s) +{ + wchar_t *d, *r; + int c; + r = d = malloc((strlen(s) + 1) * sizeof(wchar_t)); + if (!r) + return NULL; + while (*s) { + s += fz_chartorune(&c, s); + *d++ = c; + } + *d = 0; + return r; +} + +FILE * +fz_fopen_utf8(const char *name, const char *mode) +{ + wchar_t *wname, *wmode; + FILE *file; + + wname = fz_wchar_from_utf8(name); + if (wname == NULL) + { + return NULL; + } + + wmode = fz_wchar_from_utf8(mode); + if (wmode == NULL) + { + free(wname); + return NULL; + } + + file = _wfopen(wname, wmode); + + free(wname); + free(wmode); + return file; +} + +char ** +fz_argv_from_wargv(int argc, wchar_t **wargv) +{ + char **argv; + int i; + + argv = calloc(argc, sizeof(char *)); + if (argv == NULL) + { + fprintf(stderr, "Out of memory while processing command line args!\n"); + exit(1); + } + + for (i = 0; i < argc; i++) + { + argv[i] = fz_utf8_from_wchar(wargv[i]); + if (argv[i] == NULL) + { + fprintf(stderr, "Out of memory while processing command line args!\n"); + exit(1); + } + } + + return argv; +} + +void +fz_free_argv(int argc, char **argv) +{ + int i; + for (i = 0; i < argc; i++) + free(argv[i]); + free(argv); +} + +#endif /* _MSC_VER */ diff --git a/source/fitz/trace-device.c b/source/fitz/trace-device.c new file mode 100644 index 00000000..b3cade91 --- /dev/null +++ b/source/fitz/trace-device.c @@ -0,0 +1,339 @@ +#include "mupdf/fitz.h" + +static void +fz_trace_matrix(const fz_matrix *ctm) +{ + printf(" matrix=\"%g %g %g %g %g %g\"", + ctm->a, ctm->b, ctm->c, ctm->d, ctm->e, ctm->f); +} + +static void +fz_trace_trm(const fz_matrix *trm) +{ + printf(" trm=\"%g %g %g %g\"", + trm->a, trm->b, trm->c, trm->d); +} + +static void +fz_trace_color(fz_colorspace *colorspace, float *color, float alpha) +{ + int i; + printf(" colorspace=\"%s\" color=\"", colorspace->name); + for (i = 0; i < colorspace->n; i++) + printf("%s%g", i == 0 ? "" : " ", color[i]); + printf("\""); + if (alpha < 1) + printf(" alpha=\"%g\"", alpha); +} + +static void +fz_trace_path(fz_path *path, int indent) +{ + float x, y; + int i = 0; + int n; + while (i < path->len) + { + for (n = 0; n < indent; n++) + putchar(' '); + switch (path->items[i++].k) + { + case FZ_MOVETO: + x = path->items[i++].v; + y = path->items[i++].v; + printf("<moveto x=\"%g\" y=\"%g\"/>\n", x, y); + break; + case FZ_LINETO: + x = path->items[i++].v; + y = path->items[i++].v; + printf("<lineto x=\"%g\" y=\"%g\"/>\n", x, y); + break; + case FZ_CURVETO: + x = path->items[i++].v; + y = path->items[i++].v; + printf("<curveto x1=\"%g\" y1=\"%g\"", x, y); + x = path->items[i++].v; + y = path->items[i++].v; + printf(" x2=\"%g\" y2=\"%g\"", x, y); + x = path->items[i++].v; + y = path->items[i++].v; + printf(" x3=\"%g\" y3=\"%g\"/>\n", x, y); + break; + case FZ_CLOSE_PATH: + printf("<closepath/>\n"); + break; + } + } +} + +static void +fz_trace_begin_page(fz_device *dev, const fz_rect *rect, const fz_matrix *ctm) +{ + printf("<page mediabox=\"%g %g %g %g\"", rect->x0, rect->y0, rect->x1, rect->y1); + fz_trace_matrix(ctm); + printf(">\n"); +} + +static void +fz_trace_end_page(fz_device *dev) +{ + printf("</page>\n"); +} + +static void +fz_trace_fill_path(fz_device *dev, fz_path *path, int even_odd, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + printf("<fill_path"); + if (even_odd) + printf(" winding=\"eofill\""); + else + printf(" winding=\"nonzero\""); + fz_trace_color(colorspace, color, alpha); + fz_trace_matrix(ctm); + printf(">\n"); + fz_trace_path(path, 0); + printf("</fill_path>\n"); +} + +static void +fz_trace_stroke_path(fz_device *dev, fz_path *path, fz_stroke_state *stroke, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + int i; + + printf("<stroke_path"); + printf(" linewidth=\"%g\"", stroke->linewidth); + printf(" miterlimit=\"%g\"", stroke->miterlimit); + printf(" linecap=\"%d,%d,%d\"", stroke->start_cap, stroke->dash_cap, stroke->end_cap); + printf(" linejoin=\"%d\"", stroke->linejoin); + + if (stroke->dash_len) + { + printf(" dash_phase=\"%g\" dash=\"", stroke->dash_phase); + for (i = 0; i < stroke->dash_len; i++) + printf("%s%g", i > 0 ? " " : "", stroke->dash_list[i]); + printf("\""); + } + + fz_trace_color(colorspace, color, alpha); + fz_trace_matrix(ctm); + printf(">\n"); + + fz_trace_path(path, 0); + + printf("</stroke_path>\n"); +} + +static void +fz_trace_clip_path(fz_device *dev, fz_path *path, const fz_rect *rect, int even_odd, const fz_matrix *ctm) +{ + printf("<clip_path"); + if (even_odd) + printf(" winding=\"eofill\""); + else + printf(" winding=\"nonzero\""); + fz_trace_matrix(ctm); + if (rect) + printf(" contentbbox=\"%g %g %g %g\">\n", rect->x0, rect->y0, rect->x1, rect->y1); + else + printf(">\n"); + fz_trace_path(path, 0); + printf("</clip_path>\n"); +} + +static void +fz_trace_clip_stroke_path(fz_device *dev, fz_path *path, const fz_rect *rect, fz_stroke_state *stroke, const fz_matrix *ctm) +{ + printf("<clip_stroke_path"); + fz_trace_matrix(ctm); + printf(">\n"); + fz_trace_path(path, 0); + printf("</clip_stroke_path>\n"); +} + +static void +fz_trace_fill_text(fz_device *dev, fz_text *text, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + printf("<fill_text font=\"%s\" wmode=\"%d\"", text->font->name, text->wmode); + fz_trace_color(colorspace, color, alpha); + fz_trace_matrix(ctm); + fz_trace_trm(&text->trm); + printf(">\n"); + fz_print_text(dev->ctx, stdout, text); + printf("</fill_text>\n"); +} + +static void +fz_trace_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha) +{ + printf("<stroke_text font=\"%s\" wmode=\"%d\"", text->font->name, text->wmode); + fz_trace_color(colorspace, color, alpha); + fz_trace_matrix(ctm); + fz_trace_trm(&text->trm); + printf(">\n"); + fz_print_text(dev->ctx, stdout, text); + printf("</stroke_text>\n"); +} + +static void +fz_trace_clip_text(fz_device *dev, fz_text *text, const fz_matrix *ctm, int accumulate) +{ + printf("<clip_text font=\"%s\" wmode=\"%d\"", text->font->name, text->wmode); + printf(" accumulate=\"%d\"", accumulate); + fz_trace_matrix(ctm); + fz_trace_trm(&text->trm); + printf(">\n"); + fz_print_text(dev->ctx, stdout, text); + printf("</clip_text>\n"); +} + +static void +fz_trace_clip_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm) +{ + printf("<clip_stroke_text font=\"%s\" wmode=\"%d\"", text->font->name, text->wmode); + fz_trace_matrix(ctm); + fz_trace_trm(&text->trm); + printf(">\n"); + fz_print_text(dev->ctx, stdout, text); + printf("</clip_stroke_text>\n"); +} + +static void +fz_trace_ignore_text(fz_device *dev, fz_text *text, const fz_matrix *ctm) +{ + printf("<ignore_text font=\"%s\" wmode=\"%d\"", text->font->name, text->wmode); + fz_trace_matrix(ctm); + fz_trace_trm(&text->trm); + printf(">\n"); + fz_print_text(dev->ctx, stdout, text); + printf("</ignore_text>\n"); +} + +static void +fz_trace_fill_image(fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) +{ + printf("<fill_image alpha=\"%g\"", alpha); + fz_trace_matrix(ctm); + printf(" width=\"%d\" height=\"%d\"", image->w, image->h); + printf("/>\n"); +} + +static void +fz_trace_fill_shade(fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) +{ + printf("<fill_shade alpha=\"%g\"", alpha); + fz_trace_matrix(ctm); + printf("/>\n"); +} + +static void +fz_trace_fill_image_mask(fz_device *dev, fz_image *image, const fz_matrix *ctm, +fz_colorspace *colorspace, float *color, float alpha) +{ + printf("<fill_image_mask"); + fz_trace_matrix(ctm); + fz_trace_color(colorspace, color, alpha); + printf(" width=\"%d\" height=\"%d\"", image->w, image->h); + printf("/>\n"); +} + +static void +fz_trace_clip_image_mask(fz_device *dev, fz_image *image, const fz_rect *rect, const fz_matrix *ctm) +{ + printf("<clip_image_mask"); + fz_trace_matrix(ctm); + printf(" width=\"%d\" height=\"%d\"", image->w, image->h); + printf("/>\n"); +} + +static void +fz_trace_pop_clip(fz_device *dev) +{ + printf("<pop_clip/>\n"); +} + +static void +fz_trace_begin_mask(fz_device *dev, const fz_rect *bbox, int luminosity, fz_colorspace *colorspace, float *color) +{ + printf("<mask bbox=\"%g %g %g %g\" s=\"%s\"", + bbox->x0, bbox->y0, bbox->x1, bbox->y1, + luminosity ? "luminosity" : "alpha"); + printf(">\n"); +} + +static void +fz_trace_end_mask(fz_device *dev) +{ + printf("</mask>\n"); +} + +static void +fz_trace_begin_group(fz_device *dev, const fz_rect *bbox, int isolated, int knockout, int blendmode, float alpha) +{ + printf("<group bbox=\"%g %g %g %g\" isolated=\"%d\" knockout=\"%d\" blendmode=\"%s\" alpha=\"%g\">\n", + bbox->x0, bbox->y0, bbox->x1, bbox->y1, + isolated, knockout, fz_blendmode_name(blendmode), alpha); +} + +static void +fz_trace_end_group(fz_device *dev) +{ + printf("</group>\n"); +} + +static int +fz_trace_begin_tile(fz_device *dev, const fz_rect *area, const fz_rect *view, float xstep, float ystep, const fz_matrix *ctm, int id) +{ + printf("<tile"); + printf(" area=\"%g %g %g %g\"", area->x0, area->y0, area->x1, area->y1); + printf(" view=\"%g %g %g %g\"", view->x0, view->y0, view->x1, view->y1); + printf(" xstep=\"%g\" ystep=\"%g\"", xstep, ystep); + fz_trace_matrix(ctm); + printf(">\n"); + return 0; +} + +static void +fz_trace_end_tile(fz_device *dev) +{ + printf("</tile>\n"); +} + +fz_device *fz_new_trace_device(fz_context *ctx) +{ + fz_device *dev = fz_new_device(ctx, NULL); + + dev->begin_page = fz_trace_begin_page; + dev->end_page = fz_trace_end_page; + + dev->fill_path = fz_trace_fill_path; + dev->stroke_path = fz_trace_stroke_path; + dev->clip_path = fz_trace_clip_path; + dev->clip_stroke_path = fz_trace_clip_stroke_path; + + dev->fill_text = fz_trace_fill_text; + dev->stroke_text = fz_trace_stroke_text; + dev->clip_text = fz_trace_clip_text; + dev->clip_stroke_text = fz_trace_clip_stroke_text; + dev->ignore_text = fz_trace_ignore_text; + + dev->fill_shade = fz_trace_fill_shade; + dev->fill_image = fz_trace_fill_image; + dev->fill_image_mask = fz_trace_fill_image_mask; + dev->clip_image_mask = fz_trace_clip_image_mask; + + dev->pop_clip = fz_trace_pop_clip; + + dev->begin_mask = fz_trace_begin_mask; + dev->end_mask = fz_trace_end_mask; + dev->begin_group = fz_trace_begin_group; + dev->end_group = fz_trace_end_group; + + dev->begin_tile = fz_trace_begin_tile; + dev->end_tile = fz_trace_end_tile; + + return dev; +} diff --git a/source/fitz/transition.c b/source/fitz/transition.c new file mode 100644 index 00000000..92582253 --- /dev/null +++ b/source/fitz/transition.c @@ -0,0 +1,165 @@ +#include "mupdf/fitz.h" + +static int +fade(fz_pixmap *tpix, fz_pixmap *opix, fz_pixmap *npix, int time) +{ + unsigned char *t, *o, *n; + int size; + + if (!tpix || !opix || !npix || tpix->w != opix->w || opix->w != npix->w || tpix->h != opix->h || opix->h != npix->h || tpix->n != opix->n || opix->n != npix->n) + return 0; + size = tpix->w * tpix->h * tpix->n; + t = tpix->samples; + o = opix->samples; + n = npix->samples; + while (size-- > 0) + { + int op = *o++; + int np = *n++; + *t++ = ((op<<8) + ((np-op) * time) + 0x80)>>8; + } + return 1; +} + +static int +blind_horiz(fz_pixmap *tpix, fz_pixmap *opix, fz_pixmap *npix, int time) +{ + unsigned char *t, *o, *n; + int blind_height, span, position, y; + + if (!tpix || !opix || !npix || tpix->w != opix->w || opix->w != npix->w || tpix->h != opix->h || opix->h != npix->h || tpix->n != opix->n || opix->n != npix->n) + return 0; + span = tpix->w * tpix->n; + blind_height = (tpix->h+7) / 8; + position = blind_height * time / 256; + t = tpix->samples; + o = opix->samples; + n = npix->samples; + for (y = 0; y < tpix->h; y++) + { + memcpy(t, ((y % blind_height) <= position ? n : o), span); + t += span; + o += span; + n += span; + } + return 1; +} + +static int +blind_vertical(fz_pixmap *tpix, fz_pixmap *opix, fz_pixmap *npix, int time) +{ + unsigned char *t, *o, *n; + int blind_width, span, position, y; + + if (!tpix || !opix || !npix || tpix->w != opix->w || opix->w != npix->w || tpix->h != opix->h || opix->h != npix->h || tpix->n != opix->n || opix->n != npix->n) + return 0; + span = tpix->w * tpix->n; + blind_width = (tpix->w+7) / 8; + position = blind_width * time / 256; + blind_width *= tpix->n; + position *= tpix->n; + t = tpix->samples; + o = opix->samples; + n = npix->samples; + for (y = 0; y < tpix->h; y++) + { + int w, x; + x = 0; + while ((w = span - x) > 0) + { + int p; + if (w > blind_width) + w = blind_width; + p = position; + if (p > w) + p = w; + memcpy(t, n, p); + memcpy(t+position, o+position, w - p); + x += blind_width; + t += w; + o += w; + n += w; + } + } + return 1; +} + +static int +wipe_tb(fz_pixmap *tpix, fz_pixmap *opix, fz_pixmap *npix, int time) +{ + unsigned char *t, *o, *n; + int span, position, y; + + if (!tpix || !opix || !npix || tpix->w != opix->w || opix->w != npix->w || tpix->h != opix->h || opix->h != npix->h || tpix->n != opix->n || opix->n != npix->n) + return 0; + span = tpix->w * tpix->n; + position = tpix->h * time / 256; + t = tpix->samples; + o = opix->samples; + n = npix->samples; + for (y = 0; y < position; y++) + { + memcpy(t, n, span); + t += span; + o += span; + n += span; + } + for (; y < tpix->h; y++) + { + memcpy(t, o, span); + t += span; + o += span; + n += span; + } + return 1; +} + +static int +wipe_lr(fz_pixmap *tpix, fz_pixmap *opix, fz_pixmap *npix, int time) +{ + unsigned char *t, *o, *n; + int span, position, y; + + if (!tpix || !opix || !npix || tpix->w != opix->w || opix->w != npix->w || tpix->h != opix->h || opix->h != npix->h || tpix->n != opix->n || opix->n != npix->n) + return 0; + span = tpix->w * tpix->n; + position = tpix->w * time / 256; + position *= tpix->n; + t = tpix->samples; + o = opix->samples + position; + n = npix->samples; + for (y = 0; y < tpix->h; y++) + { + memcpy(t, n, position); + memcpy(t+position, o, span-position); + t += span; + o += span; + n += span; + } + return 1; +} + +int fz_generate_transition(fz_pixmap *tpix, fz_pixmap *opix, fz_pixmap *npix, int time, fz_transition *trans) +{ + switch (trans->type) + { + default: + case FZ_TRANSITION_FADE: + return fade(tpix, opix, npix, time); + case FZ_TRANSITION_BLINDS: + if (trans->vertical) + return blind_vertical(tpix, opix, npix, time); + else + return blind_horiz(tpix, opix, npix, time); + case FZ_TRANSITION_WIPE: + switch (((trans->direction + 45 + 360) % 360) / 90) + { + default: + case 0: return wipe_lr(tpix, opix, npix, time); + case 1: return wipe_tb(tpix, npix, opix, 256-time); + case 2: return wipe_lr(tpix, npix, opix, 256-time); + case 3: return wipe_tb(tpix, opix, npix, time); + } + } + return 0; +} diff --git a/source/fitz/ucdn.c b/source/fitz/ucdn.c new file mode 100644 index 00000000..ce2cb7c1 --- /dev/null +++ b/source/fitz/ucdn.c @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2012 Grigori Goronzy <greg@kinoho.net> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include "ucdn.h" + +typedef struct { + unsigned char category; + unsigned char combining; + unsigned char bidi_class; + unsigned char mirrored; + unsigned char east_asian_width; + unsigned char normalization_check; + unsigned char script; +} UCDRecord; + +typedef struct { + unsigned short from, to; +} MirrorPair; + +typedef struct { + int start; + short count, index; +} Reindex; + +#include "unicodedata_db.h" + +/* constants required for Hangul (de)composition */ +#define SBASE 0xAC00 +#define LBASE 0x1100 +#define VBASE 0x1161 +#define TBASE 0x11A7 +#define SCOUNT 11172 +#define LCOUNT 19 +#define VCOUNT 21 +#define TCOUNT 28 +#define NCOUNT (VCOUNT * TCOUNT) + +static const UCDRecord *get_ucd_record(unsigned int code) +{ + int index, offset; + + if (code >= 0x110000) + index = 0; + else { + index = index0[code >> (SHIFT1+SHIFT2)] << SHIFT1; + offset = (code >> SHIFT2) & ((1<<SHIFT1) - 1); + index = index1[index + offset] << SHIFT2; + offset = code & ((1<<SHIFT2) - 1); + index = index2[index + offset]; + } + + return &ucd_records[index]; +} + +static const unsigned short *get_decomp_record(unsigned int code) +{ + int index, offset; + + if (code >= 0x110000) + index = 0; + else { + index = decomp_index0[code >> (DECOMP_SHIFT1+DECOMP_SHIFT2)] + << DECOMP_SHIFT1; + offset = (code >> DECOMP_SHIFT2) & ((1<<DECOMP_SHIFT1) - 1); + index = decomp_index1[index + offset] << DECOMP_SHIFT2; + offset = code & ((1<<DECOMP_SHIFT2) - 1); + index = decomp_index2[index + offset]; + } + + return &decomp_data[index]; +} + +static int get_comp_index(unsigned int code, const Reindex *idx) +{ + int i; + + for (i = 0; idx[i].start; i++) { + const Reindex *cur = &idx[i]; + if (code < cur->start) + return -1; + if (code <= cur->start + cur->count) { + return cur->index + (code - cur->start); + } + } + + return -1; +} + +static int compare_mp(const void *a, const void *b) +{ + MirrorPair *mpa = (MirrorPair *)a; + MirrorPair *mpb = (MirrorPair *)b; + return mpa->from - mpb->from; +} + +static int hangul_pair_decompose(unsigned int code, unsigned int *a, unsigned int *b) +{ + int si = code - SBASE; + + if (si < 0 || si >= SCOUNT) + return 0; + + if (si % TCOUNT) { + /* LV,T */ + *a = SBASE + (si / TCOUNT) * TCOUNT; + *b = TBASE + (si % TCOUNT); + return 3; + } else { + /* L,V */ + *a = LBASE + (si / NCOUNT); + *b = VBASE + (si % NCOUNT) / TCOUNT; + return 2; + } +} + +static int hangul_pair_compose(unsigned int *code, unsigned int a, unsigned int b) +{ + if (b < VBASE || b >= (TBASE + TCOUNT)) + return 0; + + if ((a < LBASE || a >= (LBASE + LCOUNT)) + && (a < SBASE || a >= (SBASE + SCOUNT))) + return 0; + + if (a >= SBASE) { + /* LV,T */ + *code = a + (b - TBASE); + return 3; + } else { + /* L,V */ + int li = a - LBASE; + int vi = b - VBASE; + *code = SBASE + li * NCOUNT + vi * TCOUNT; + return 2; + } +} + +static unsigned int decode_utf16(const unsigned short **code_ptr) +{ + const unsigned short *code = *code_ptr; + + if ((code[0] & 0xd800) != 0xd800) { + *code_ptr += 1; + return (unsigned int)code[0]; + } else { + *code_ptr += 2; + return 0x10000 + ((unsigned int)code[1] - 0xdc00) + + (((unsigned int)code[0] - 0xd800) << 10); + } +} + +const char *ucdn_get_unicode_version(void) +{ + return UNIDATA_VERSION; +} + +int ucdn_get_combining_class(unsigned int code) +{ + return get_ucd_record(code)->combining; +} + +int ucdn_get_east_asian_width(unsigned int code) +{ + return get_ucd_record(code)->east_asian_width; +} + +int ucdn_get_general_category(unsigned int code) +{ + return get_ucd_record(code)->category; +} + +int ucdn_get_bidi_class(unsigned int code) +{ + return get_ucd_record(code)->bidi_class; +} + +int ucdn_get_mirrored(unsigned int code) +{ + return get_ucd_record(code)->mirrored; +} + +int ucdn_get_script(unsigned int code) +{ + return get_ucd_record(code)->script; +} + +unsigned int ucdn_mirror(unsigned int code) +{ + MirrorPair mp = {0}; + MirrorPair *res; + + if (get_ucd_record(code)->mirrored == 0) + return code; + + mp.from = code; + res = bsearch(&mp, mirror_pairs, BIDI_MIRROR_LEN, sizeof(MirrorPair), + compare_mp); + + if (res == NULL) + return code; + else + return res->to; +} + +int ucdn_decompose(unsigned int code, unsigned int *a, unsigned int *b) +{ + const unsigned short *rec; + int len; + + if (hangul_pair_decompose(code, a, b)) + return 1; + + rec = get_decomp_record(code); + len = rec[0] >> 8; + + if ((rec[0] & 0xff) != 0 || len == 0) + return 0; + + rec++; + *a = decode_utf16(&rec); + if (len > 1) + *b = decode_utf16(&rec); + else + *b = 0; + + return 1; +} + +int ucdn_compose(unsigned int *code, unsigned int a, unsigned int b) +{ + int l, r, index, indexi, offset; + + if (hangul_pair_compose(code, a, b)) + return 1; + + l = get_comp_index(a, nfc_first); + r = get_comp_index(b, nfc_last); + + if (l < 0 || r < 0) + return 0; + + indexi = l * TOTAL_LAST + r; + index = comp_index0[indexi >> (COMP_SHIFT1+COMP_SHIFT2)] << COMP_SHIFT1; + offset = (indexi >> COMP_SHIFT2) & ((1<<COMP_SHIFT1) - 1); + index = comp_index1[index + offset] << COMP_SHIFT2; + offset = indexi & ((1<<COMP_SHIFT2) - 1); + *code = comp_data[index + offset]; + + return *code != 0; +} + +int ucdn_compat_decompose(unsigned int code, unsigned int *decomposed) +{ + int i, len; + const unsigned short *rec = get_decomp_record(code); + len = rec[0] >> 8; + + if (len == 0) + return 0; + + rec++; + for (i = 0; i < len; i++) + decomposed[i] = decode_utf16(&rec); + + return len; +} diff --git a/source/fitz/ucdn.h b/source/fitz/ucdn.h new file mode 100644 index 00000000..63fe7516 --- /dev/null +++ b/source/fitz/ucdn.h @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2012 Grigori Goronzy <greg@kinoho.net> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef UCDN_H +#define UCDN_H + +#define UCDN_EAST_ASIAN_F 0 +#define UCDN_EAST_ASIAN_H 1 +#define UCDN_EAST_ASIAN_W 2 +#define UCDN_EAST_ASIAN_NA 3 +#define UCDN_EAST_ASIAN_A 4 +#define UCDN_EAST_ASIAN_N 5 + +#define UCDN_SCRIPT_COMMON 0 +#define UCDN_SCRIPT_LATIN 1 +#define UCDN_SCRIPT_GREEK 2 +#define UCDN_SCRIPT_CYRILLIC 3 +#define UCDN_SCRIPT_ARMENIAN 4 +#define UCDN_SCRIPT_HEBREW 5 +#define UCDN_SCRIPT_ARABIC 6 +#define UCDN_SCRIPT_SYRIAC 7 +#define UCDN_SCRIPT_THAANA 8 +#define UCDN_SCRIPT_DEVANAGARI 9 +#define UCDN_SCRIPT_BENGALI 10 +#define UCDN_SCRIPT_GURMUKHI 11 +#define UCDN_SCRIPT_GUJARATI 12 +#define UCDN_SCRIPT_ORIYA 13 +#define UCDN_SCRIPT_TAMIL 14 +#define UCDN_SCRIPT_TELUGU 15 +#define UCDN_SCRIPT_KANNADA 16 +#define UCDN_SCRIPT_MALAYALAM 17 +#define UCDN_SCRIPT_SINHALA 18 +#define UCDN_SCRIPT_THAI 19 +#define UCDN_SCRIPT_LAO 20 +#define UCDN_SCRIPT_TIBETAN 21 +#define UCDN_SCRIPT_MYANMAR 22 +#define UCDN_SCRIPT_GEORGIAN 23 +#define UCDN_SCRIPT_HANGUL 24 +#define UCDN_SCRIPT_ETHIOPIC 25 +#define UCDN_SCRIPT_CHEROKEE 26 +#define UCDN_SCRIPT_CANADIAN_ABORIGINAL 27 +#define UCDN_SCRIPT_OGHAM 28 +#define UCDN_SCRIPT_RUNIC 29 +#define UCDN_SCRIPT_KHMER 30 +#define UCDN_SCRIPT_MONGOLIAN 31 +#define UCDN_SCRIPT_HIRAGANA 32 +#define UCDN_SCRIPT_KATAKANA 33 +#define UCDN_SCRIPT_BOPOMOFO 34 +#define UCDN_SCRIPT_HAN 35 +#define UCDN_SCRIPT_YI 36 +#define UCDN_SCRIPT_OLD_ITALIC 37 +#define UCDN_SCRIPT_GOTHIC 38 +#define UCDN_SCRIPT_DESERET 39 +#define UCDN_SCRIPT_INHERITED 40 +#define UCDN_SCRIPT_TAGALOG 41 +#define UCDN_SCRIPT_HANUNOO 42 +#define UCDN_SCRIPT_BUHID 43 +#define UCDN_SCRIPT_TAGBANWA 44 +#define UCDN_SCRIPT_LIMBU 45 +#define UCDN_SCRIPT_TAI_LE 46 +#define UCDN_SCRIPT_LINEAR_B 47 +#define UCDN_SCRIPT_UGARITIC 48 +#define UCDN_SCRIPT_SHAVIAN 49 +#define UCDN_SCRIPT_OSMANYA 50 +#define UCDN_SCRIPT_CYPRIOT 51 +#define UCDN_SCRIPT_BRAILLE 52 +#define UCDN_SCRIPT_BUGINESE 53 +#define UCDN_SCRIPT_COPTIC 54 +#define UCDN_SCRIPT_NEW_TAI_LUE 55 +#define UCDN_SCRIPT_GLAGOLITIC 56 +#define UCDN_SCRIPT_TIFINAGH 57 +#define UCDN_SCRIPT_SYLOTI_NAGRI 58 +#define UCDN_SCRIPT_OLD_PERSIAN 59 +#define UCDN_SCRIPT_KHAROSHTHI 60 +#define UCDN_SCRIPT_BALINESE 61 +#define UCDN_SCRIPT_CUNEIFORM 62 +#define UCDN_SCRIPT_PHOENICIAN 63 +#define UCDN_SCRIPT_PHAGS_PA 64 +#define UCDN_SCRIPT_NKO 65 +#define UCDN_SCRIPT_SUNDANESE 66 +#define UCDN_SCRIPT_LEPCHA 67 +#define UCDN_SCRIPT_OL_CHIKI 68 +#define UCDN_SCRIPT_VAI 69 +#define UCDN_SCRIPT_SAURASHTRA 70 +#define UCDN_SCRIPT_KAYAH_LI 71 +#define UCDN_SCRIPT_REJANG 72 +#define UCDN_SCRIPT_LYCIAN 73 +#define UCDN_SCRIPT_CARIAN 74 +#define UCDN_SCRIPT_LYDIAN 75 +#define UCDN_SCRIPT_CHAM 76 +#define UCDN_SCRIPT_TAI_THAM 77 +#define UCDN_SCRIPT_TAI_VIET 78 +#define UCDN_SCRIPT_AVESTAN 79 +#define UCDN_SCRIPT_EGYPTIAN_HIEROGLYPHS 80 +#define UCDN_SCRIPT_SAMARITAN 81 +#define UCDN_SCRIPT_LISU 82 +#define UCDN_SCRIPT_BAMUM 83 +#define UCDN_SCRIPT_JAVANESE 84 +#define UCDN_SCRIPT_MEETEI_MAYEK 85 +#define UCDN_SCRIPT_IMPERIAL_ARAMAIC 86 +#define UCDN_SCRIPT_OLD_SOUTH_ARABIAN 87 +#define UCDN_SCRIPT_INSCRIPTIONAL_PARTHIAN 88 +#define UCDN_SCRIPT_INSCRIPTIONAL_PAHLAVI 89 +#define UCDN_SCRIPT_OLD_TURKIC 90 +#define UCDN_SCRIPT_KAITHI 91 +#define UCDN_SCRIPT_BATAK 92 +#define UCDN_SCRIPT_BRAHMI 93 +#define UCDN_SCRIPT_MANDAIC 94 +#define UCDN_SCRIPT_CHAKMA 95 +#define UCDN_SCRIPT_MEROITIC_CURSIVE 96 +#define UCDN_SCRIPT_MEROITIC_HIEROGLYPHS 97 +#define UCDN_SCRIPT_MIAO 98 +#define UCDN_SCRIPT_SHARADA 99 +#define UCDN_SCRIPT_SORA_SOMPENG 100 +#define UCDN_SCRIPT_TAKRI 101 +#define UCDN_SCRIPT_UNKNOWN 102 + +#define UCDN_GENERAL_CATEGORY_CC 0 +#define UCDN_GENERAL_CATEGORY_CF 1 +#define UCDN_GENERAL_CATEGORY_CN 2 +#define UCDN_GENERAL_CATEGORY_CO 3 +#define UCDN_GENERAL_CATEGORY_CS 4 +#define UCDN_GENERAL_CATEGORY_LL 5 +#define UCDN_GENERAL_CATEGORY_LM 6 +#define UCDN_GENERAL_CATEGORY_LO 7 +#define UCDN_GENERAL_CATEGORY_LT 8 +#define UCDN_GENERAL_CATEGORY_LU 9 +#define UCDN_GENERAL_CATEGORY_MC 10 +#define UCDN_GENERAL_CATEGORY_ME 11 +#define UCDN_GENERAL_CATEGORY_MN 12 +#define UCDN_GENERAL_CATEGORY_ND 13 +#define UCDN_GENERAL_CATEGORY_NL 14 +#define UCDN_GENERAL_CATEGORY_NO 15 +#define UCDN_GENERAL_CATEGORY_PC 16 +#define UCDN_GENERAL_CATEGORY_PD 17 +#define UCDN_GENERAL_CATEGORY_PE 18 +#define UCDN_GENERAL_CATEGORY_PF 19 +#define UCDN_GENERAL_CATEGORY_PI 20 +#define UCDN_GENERAL_CATEGORY_PO 21 +#define UCDN_GENERAL_CATEGORY_PS 22 +#define UCDN_GENERAL_CATEGORY_SC 23 +#define UCDN_GENERAL_CATEGORY_SK 24 +#define UCDN_GENERAL_CATEGORY_SM 25 +#define UCDN_GENERAL_CATEGORY_SO 26 +#define UCDN_GENERAL_CATEGORY_ZL 27 +#define UCDN_GENERAL_CATEGORY_ZP 28 +#define UCDN_GENERAL_CATEGORY_ZS 29 + +#define UCDN_BIDI_CLASS_L 0 +#define UCDN_BIDI_CLASS_LRE 1 +#define UCDN_BIDI_CLASS_LRO 2 +#define UCDN_BIDI_CLASS_R 3 +#define UCDN_BIDI_CLASS_AL 4 +#define UCDN_BIDI_CLASS_RLE 5 +#define UCDN_BIDI_CLASS_RLO 6 +#define UCDN_BIDI_CLASS_PDF 7 +#define UCDN_BIDI_CLASS_EN 8 +#define UCDN_BIDI_CLASS_ES 9 +#define UCDN_BIDI_CLASS_ET 10 +#define UCDN_BIDI_CLASS_AN 11 +#define UCDN_BIDI_CLASS_CS 12 +#define UCDN_BIDI_CLASS_NSM 13 +#define UCDN_BIDI_CLASS_BN 14 +#define UCDN_BIDI_CLASS_B 15 +#define UCDN_BIDI_CLASS_S 16 +#define UCDN_BIDI_CLASS_WS 17 +#define UCDN_BIDI_CLASS_ON 18 + +/** + * Return version of the Unicode database. + * + * @return Unicode database version + */ +const char *ucdn_get_unicode_version(void); + +/** + * Get combining class of a codepoint. + * + * @param code Unicode codepoint + * @return combining class value, as defined in UAX#44 + */ +int ucdn_get_combining_class(unsigned int code); + +/** + * Get east-asian width of a codepoint. + * + * @param code Unicode codepoint + * @return value according to UCDN_EAST_ASIAN_* and as defined in UAX#11. + */ +int ucdn_get_east_asian_width(unsigned int code); + +/** + * Get general category of a codepoint. + * + * @param code Unicode codepoint + * @return value according to UCDN_GENERAL_CATEGORY_* and as defined in + * UAX#44. + */ +int ucdn_get_general_category(unsigned int code); + +/** + * Get bidirectional class of a codepoint. + * + * @param code Unicode codepoint + * @return value according to UCDN_BIDI_CLASS_* and as defined in UAX#44. + */ +int ucdn_get_bidi_class(unsigned int code); + +/** + * Get script of a codepoint. + * + * @param code Unicode codepoint + * @return value according to UCDN_SCRIPT_* and as defined in UAX#24. + */ +int ucdn_get_script(unsigned int code); + +/** + * Check if codepoint can be mirrored. + * + * @param code Unicode codepoint + * @return 1 if mirrored character exists, otherwise 0 + */ +int ucdn_get_mirrored(unsigned int code); + +/** + * Mirror a codepoint. + * + * @param code Unicode codepoint + * @return mirrored codepoint or the original codepoint if no + * mirrored character exists + */ +unsigned int ucdn_mirror(unsigned int code); + +/** + * Pairwise canonical decomposition of a codepoint. This includes + * Hangul Jamo decomposition (see chapter 3.12 of the Unicode core + * specification). + * + * Hangul is decomposed into L and V jamos for LV forms, and an + * LV precomposed syllable and a T jamo for LVT forms. + * + * @param code Unicode codepoint + * @param a filled with first codepoint of decomposition + * @param b filled with second codepoint of decomposition, or 0 + * @return success + */ +int ucdn_decompose(unsigned int code, unsigned int *a, unsigned int *b); + +/** + * Compatibility decomposition of a codepoint. + * + * @param code Unicode codepoint + * @param decomposed filled with decomposition, must be able to hold 18 + * characters + * @return length of decomposition or 0 in case none exists + */ +int ucdn_compat_decompose(unsigned int code, unsigned int *decomposed); + +/** + * Pairwise canonical composition of two codepoints. This includes + * Hangul Jamo composition (see chapter 3.12 of the Unicode core + * specification). + * + * Hangul composition expects either L and V jamos, or an LV + * precomposed syllable and a T jamo. This is exactly the inverse + * of pairwise Hangul decomposition. + * + * @param code filled with composition + * @param a first codepoint + * @param b second codepoint + * @return success + */ +int ucdn_compose(unsigned int *code, unsigned int a, unsigned int b); + +#endif diff --git a/source/fitz/unicodedata_db.h b/source/fitz/unicodedata_db.h new file mode 100644 index 00000000..a0a4347a --- /dev/null +++ b/source/fitz/unicodedata_db.h @@ -0,0 +1,4753 @@ +/* this file was generated by makeunicodedata.py 3.2 */ + +#define UNIDATA_VERSION "6.2.0" +/* a list of unique database records */ +static const UCDRecord ucd_records[] = { + {2, 0, 18, 0, 5, 0, 102}, + {0, 0, 14, 0, 5, 0, 0}, + {0, 0, 16, 0, 5, 0, 0}, + {0, 0, 15, 0, 5, 0, 0}, + {0, 0, 17, 0, 5, 0, 0}, + {29, 0, 17, 0, 3, 0, 0}, + {21, 0, 18, 0, 3, 0, 0}, + {21, 0, 10, 0, 3, 0, 0}, + {23, 0, 10, 0, 3, 0, 0}, + {22, 0, 18, 1, 3, 0, 0}, + {18, 0, 18, 1, 3, 0, 0}, + {25, 0, 9, 0, 3, 0, 0}, + {21, 0, 12, 0, 3, 0, 0}, + {17, 0, 9, 0, 3, 0, 0}, + {13, 0, 8, 0, 3, 0, 0}, + {25, 0, 18, 1, 3, 0, 0}, + {25, 0, 18, 0, 3, 0, 0}, + {9, 0, 0, 0, 3, 0, 1}, + {24, 0, 18, 0, 3, 0, 0}, + {16, 0, 18, 0, 3, 0, 0}, + {5, 0, 0, 0, 3, 0, 1}, + {29, 0, 12, 0, 5, 0, 0}, + {21, 0, 18, 0, 4, 0, 0}, + {23, 0, 10, 0, 4, 0, 0}, + {26, 0, 18, 0, 3, 0, 0}, + {24, 0, 18, 0, 4, 0, 0}, + {26, 0, 18, 0, 5, 0, 0}, + {7, 0, 0, 0, 4, 0, 1}, + {20, 0, 18, 1, 5, 0, 0}, + {1, 0, 14, 0, 4, 0, 0}, + {26, 0, 18, 0, 4, 0, 0}, + {26, 0, 10, 0, 4, 0, 0}, + {25, 0, 10, 0, 4, 0, 0}, + {15, 0, 8, 0, 4, 0, 0}, + {5, 0, 0, 0, 5, 0, 0}, + {19, 0, 18, 1, 5, 0, 0}, + {15, 0, 18, 0, 4, 0, 0}, + {9, 0, 0, 0, 5, 0, 1}, + {9, 0, 0, 0, 4, 0, 1}, + {25, 0, 18, 0, 4, 0, 0}, + {5, 0, 0, 0, 4, 0, 1}, + {5, 0, 0, 0, 5, 0, 1}, + {7, 0, 0, 0, 5, 0, 1}, + {8, 0, 0, 0, 5, 0, 1}, + {6, 0, 0, 0, 5, 0, 1}, + {6, 0, 18, 0, 5, 0, 0}, + {6, 0, 0, 0, 5, 0, 0}, + {24, 0, 18, 0, 5, 0, 0}, + {6, 0, 18, 0, 4, 0, 0}, + {6, 0, 0, 0, 4, 0, 0}, + {24, 0, 18, 0, 5, 0, 34}, + {12, 230, 13, 0, 4, 0, 40}, + {12, 232, 13, 0, 4, 0, 40}, + {12, 220, 13, 0, 4, 0, 40}, + {12, 216, 13, 0, 4, 0, 40}, + {12, 202, 13, 0, 4, 0, 40}, + {12, 1, 13, 0, 4, 0, 40}, + {12, 240, 13, 0, 4, 0, 40}, + {12, 0, 13, 0, 4, 0, 40}, + {12, 233, 13, 0, 4, 0, 40}, + {12, 234, 13, 0, 4, 0, 40}, + {9, 0, 0, 0, 5, 0, 2}, + {5, 0, 0, 0, 5, 0, 2}, + {24, 0, 18, 0, 5, 0, 2}, + {2, 0, 18, 0, 5, 0, 102}, + {6, 0, 0, 0, 5, 0, 2}, + {21, 0, 18, 0, 5, 0, 0}, + {9, 0, 0, 0, 4, 0, 2}, + {5, 0, 0, 0, 4, 0, 2}, + {9, 0, 0, 0, 5, 0, 54}, + {5, 0, 0, 0, 5, 0, 54}, + {25, 0, 18, 0, 5, 0, 2}, + {9, 0, 0, 0, 5, 0, 3}, + {9, 0, 0, 0, 4, 0, 3}, + {5, 0, 0, 0, 4, 0, 3}, + {5, 0, 0, 0, 5, 0, 3}, + {26, 0, 0, 0, 5, 0, 3}, + {12, 230, 13, 0, 5, 0, 3}, + {12, 230, 13, 0, 5, 0, 40}, + {11, 0, 13, 0, 5, 0, 3}, + {9, 0, 0, 0, 5, 0, 4}, + {6, 0, 0, 0, 5, 0, 4}, + {21, 0, 0, 0, 5, 0, 4}, + {5, 0, 0, 0, 5, 0, 4}, + {21, 0, 0, 0, 5, 0, 0}, + {17, 0, 18, 0, 5, 0, 4}, + {23, 0, 10, 0, 5, 0, 4}, + {12, 220, 13, 0, 5, 0, 5}, + {12, 230, 13, 0, 5, 0, 5}, + {12, 222, 13, 0, 5, 0, 5}, + {12, 228, 13, 0, 5, 0, 5}, + {12, 10, 13, 0, 5, 0, 5}, + {12, 11, 13, 0, 5, 0, 5}, + {12, 12, 13, 0, 5, 0, 5}, + {12, 13, 13, 0, 5, 0, 5}, + {12, 14, 13, 0, 5, 0, 5}, + {12, 15, 13, 0, 5, 0, 5}, + {12, 16, 13, 0, 5, 0, 5}, + {12, 17, 13, 0, 5, 0, 5}, + {12, 18, 13, 0, 5, 0, 5}, + {12, 19, 13, 0, 5, 0, 5}, + {12, 20, 13, 0, 5, 0, 5}, + {12, 21, 13, 0, 5, 0, 5}, + {12, 22, 13, 0, 5, 0, 5}, + {17, 0, 3, 0, 5, 0, 5}, + {12, 23, 13, 0, 5, 0, 5}, + {21, 0, 3, 0, 5, 0, 5}, + {12, 24, 13, 0, 5, 0, 5}, + {12, 25, 13, 0, 5, 0, 5}, + {7, 0, 3, 0, 5, 0, 5}, + {1, 0, 11, 0, 5, 0, 6}, + {25, 0, 18, 0, 5, 0, 6}, + {25, 0, 4, 0, 5, 0, 6}, + {21, 0, 10, 0, 5, 0, 6}, + {23, 0, 4, 0, 5, 0, 6}, + {21, 0, 12, 0, 5, 0, 0}, + {21, 0, 4, 0, 5, 0, 6}, + {26, 0, 18, 0, 5, 0, 6}, + {12, 230, 13, 0, 5, 0, 6}, + {12, 30, 13, 0, 5, 0, 6}, + {12, 31, 13, 0, 5, 0, 6}, + {12, 32, 13, 0, 5, 0, 6}, + {21, 0, 4, 0, 5, 0, 0}, + {7, 0, 4, 0, 5, 0, 6}, + {6, 0, 4, 0, 5, 0, 0}, + {12, 27, 13, 0, 5, 0, 40}, + {12, 28, 13, 0, 5, 0, 40}, + {12, 29, 13, 0, 5, 0, 40}, + {12, 30, 13, 0, 5, 0, 40}, + {12, 31, 13, 0, 5, 0, 40}, + {12, 32, 13, 0, 5, 0, 40}, + {12, 33, 13, 0, 5, 0, 40}, + {12, 34, 13, 0, 5, 0, 40}, + {12, 220, 13, 0, 5, 0, 40}, + {12, 220, 13, 0, 5, 0, 6}, + {13, 0, 11, 0, 5, 0, 0}, + {21, 0, 11, 0, 5, 0, 6}, + {12, 35, 13, 0, 5, 0, 40}, + {1, 0, 11, 0, 5, 0, 0}, + {6, 0, 4, 0, 5, 0, 6}, + {13, 0, 8, 0, 5, 0, 6}, + {26, 0, 4, 0, 5, 0, 6}, + {21, 0, 4, 0, 5, 0, 7}, + {1, 0, 4, 0, 5, 0, 7}, + {7, 0, 4, 0, 5, 0, 7}, + {12, 36, 13, 0, 5, 0, 7}, + {12, 230, 13, 0, 5, 0, 7}, + {12, 220, 13, 0, 5, 0, 7}, + {7, 0, 4, 0, 5, 0, 8}, + {12, 0, 13, 0, 5, 0, 8}, + {13, 0, 3, 0, 5, 0, 65}, + {7, 0, 3, 0, 5, 0, 65}, + {12, 230, 13, 0, 5, 0, 65}, + {12, 220, 13, 0, 5, 0, 65}, + {6, 0, 3, 0, 5, 0, 65}, + {26, 0, 18, 0, 5, 0, 65}, + {21, 0, 18, 0, 5, 0, 65}, + {7, 0, 3, 0, 5, 0, 81}, + {12, 230, 13, 0, 5, 0, 81}, + {6, 0, 3, 0, 5, 0, 81}, + {21, 0, 3, 0, 5, 0, 81}, + {7, 0, 3, 0, 5, 0, 94}, + {12, 220, 13, 0, 5, 0, 94}, + {21, 0, 3, 0, 5, 0, 94}, + {12, 27, 13, 0, 5, 0, 6}, + {12, 28, 13, 0, 5, 0, 6}, + {12, 29, 13, 0, 5, 0, 6}, + {12, 0, 13, 0, 5, 0, 9}, + {10, 0, 0, 0, 5, 0, 9}, + {7, 0, 0, 0, 5, 0, 9}, + {12, 7, 13, 0, 5, 0, 9}, + {12, 9, 13, 0, 5, 0, 9}, + {12, 230, 13, 0, 5, 0, 9}, + {13, 0, 0, 0, 5, 0, 9}, + {21, 0, 0, 0, 5, 0, 9}, + {6, 0, 0, 0, 5, 0, 9}, + {12, 0, 13, 0, 5, 0, 10}, + {10, 0, 0, 0, 5, 0, 10}, + {7, 0, 0, 0, 5, 0, 10}, + {12, 7, 13, 0, 5, 0, 10}, + {12, 9, 13, 0, 5, 0, 10}, + {13, 0, 0, 0, 5, 0, 10}, + {23, 0, 10, 0, 5, 0, 10}, + {15, 0, 0, 0, 5, 0, 10}, + {26, 0, 0, 0, 5, 0, 10}, + {12, 0, 13, 0, 5, 0, 11}, + {10, 0, 0, 0, 5, 0, 11}, + {7, 0, 0, 0, 5, 0, 11}, + {12, 7, 13, 0, 5, 0, 11}, + {12, 9, 13, 0, 5, 0, 11}, + {13, 0, 0, 0, 5, 0, 11}, + {12, 0, 13, 0, 5, 0, 12}, + {10, 0, 0, 0, 5, 0, 12}, + {7, 0, 0, 0, 5, 0, 12}, + {12, 7, 13, 0, 5, 0, 12}, + {12, 9, 13, 0, 5, 0, 12}, + {13, 0, 0, 0, 5, 0, 12}, + {21, 0, 0, 0, 5, 0, 12}, + {23, 0, 10, 0, 5, 0, 12}, + {12, 0, 13, 0, 5, 0, 13}, + {10, 0, 0, 0, 5, 0, 13}, + {7, 0, 0, 0, 5, 0, 13}, + {12, 7, 13, 0, 5, 0, 13}, + {12, 9, 13, 0, 5, 0, 13}, + {13, 0, 0, 0, 5, 0, 13}, + {26, 0, 0, 0, 5, 0, 13}, + {15, 0, 0, 0, 5, 0, 13}, + {12, 0, 13, 0, 5, 0, 14}, + {7, 0, 0, 0, 5, 0, 14}, + {10, 0, 0, 0, 5, 0, 14}, + {12, 9, 13, 0, 5, 0, 14}, + {13, 0, 0, 0, 5, 0, 14}, + {15, 0, 0, 0, 5, 0, 14}, + {26, 0, 18, 0, 5, 0, 14}, + {23, 0, 10, 0, 5, 0, 14}, + {10, 0, 0, 0, 5, 0, 15}, + {7, 0, 0, 0, 5, 0, 15}, + {12, 0, 13, 0, 5, 0, 15}, + {12, 9, 13, 0, 5, 0, 15}, + {12, 84, 13, 0, 5, 0, 15}, + {12, 91, 13, 0, 5, 0, 15}, + {13, 0, 0, 0, 5, 0, 15}, + {15, 0, 18, 0, 5, 0, 15}, + {26, 0, 0, 0, 5, 0, 15}, + {10, 0, 0, 0, 5, 0, 16}, + {7, 0, 0, 0, 5, 0, 16}, + {12, 7, 13, 0, 5, 0, 16}, + {12, 0, 0, 0, 5, 0, 16}, + {12, 0, 13, 0, 5, 0, 16}, + {12, 9, 13, 0, 5, 0, 16}, + {13, 0, 0, 0, 5, 0, 16}, + {10, 0, 0, 0, 5, 0, 17}, + {7, 0, 0, 0, 5, 0, 17}, + {12, 0, 13, 0, 5, 0, 17}, + {12, 9, 13, 0, 5, 0, 17}, + {13, 0, 0, 0, 5, 0, 17}, + {15, 0, 0, 0, 5, 0, 17}, + {26, 0, 0, 0, 5, 0, 17}, + {10, 0, 0, 0, 5, 0, 18}, + {7, 0, 0, 0, 5, 0, 18}, + {12, 9, 13, 0, 5, 0, 18}, + {12, 0, 13, 0, 5, 0, 18}, + {21, 0, 0, 0, 5, 0, 18}, + {7, 0, 0, 0, 5, 0, 19}, + {12, 0, 13, 0, 5, 0, 19}, + {12, 103, 13, 0, 5, 0, 19}, + {12, 9, 13, 0, 5, 0, 19}, + {23, 0, 10, 0, 5, 0, 0}, + {6, 0, 0, 0, 5, 0, 19}, + {12, 107, 13, 0, 5, 0, 19}, + {21, 0, 0, 0, 5, 0, 19}, + {13, 0, 0, 0, 5, 0, 19}, + {7, 0, 0, 0, 5, 0, 20}, + {12, 0, 13, 0, 5, 0, 20}, + {12, 118, 13, 0, 5, 0, 20}, + {6, 0, 0, 0, 5, 0, 20}, + {12, 122, 13, 0, 5, 0, 20}, + {13, 0, 0, 0, 5, 0, 20}, + {7, 0, 0, 0, 5, 0, 21}, + {26, 0, 0, 0, 5, 0, 21}, + {21, 0, 0, 0, 5, 0, 21}, + {12, 220, 13, 0, 5, 0, 21}, + {13, 0, 0, 0, 5, 0, 21}, + {15, 0, 0, 0, 5, 0, 21}, + {12, 216, 13, 0, 5, 0, 21}, + {22, 0, 18, 1, 5, 0, 21}, + {18, 0, 18, 1, 5, 0, 21}, + {10, 0, 0, 0, 5, 0, 21}, + {12, 129, 13, 0, 5, 0, 21}, + {12, 130, 13, 0, 5, 0, 21}, + {12, 0, 13, 0, 5, 0, 21}, + {12, 132, 13, 0, 5, 0, 21}, + {12, 230, 13, 0, 5, 0, 21}, + {12, 9, 13, 0, 5, 0, 21}, + {26, 0, 0, 0, 5, 0, 0}, + {7, 0, 0, 0, 5, 0, 22}, + {10, 0, 0, 0, 5, 0, 22}, + {12, 0, 13, 0, 5, 0, 22}, + {12, 7, 13, 0, 5, 0, 22}, + {12, 9, 13, 0, 5, 0, 22}, + {13, 0, 0, 0, 5, 0, 22}, + {21, 0, 0, 0, 5, 0, 22}, + {12, 220, 13, 0, 5, 0, 22}, + {26, 0, 0, 0, 5, 0, 22}, + {9, 0, 0, 0, 5, 0, 23}, + {7, 0, 0, 0, 5, 0, 23}, + {6, 0, 0, 0, 5, 0, 23}, + {7, 0, 0, 0, 2, 0, 24}, + {7, 0, 0, 0, 5, 0, 24}, + {7, 0, 0, 0, 5, 0, 25}, + {12, 230, 13, 0, 5, 0, 25}, + {21, 0, 0, 0, 5, 0, 25}, + {15, 0, 0, 0, 5, 0, 25}, + {26, 0, 18, 0, 5, 0, 25}, + {7, 0, 0, 0, 5, 0, 26}, + {17, 0, 18, 0, 5, 0, 27}, + {7, 0, 0, 0, 5, 0, 27}, + {21, 0, 0, 0, 5, 0, 27}, + {29, 0, 17, 0, 5, 0, 28}, + {7, 0, 0, 0, 5, 0, 28}, + {22, 0, 18, 1, 5, 0, 28}, + {18, 0, 18, 1, 5, 0, 28}, + {7, 0, 0, 0, 5, 0, 29}, + {14, 0, 0, 0, 5, 0, 29}, + {7, 0, 0, 0, 5, 0, 41}, + {12, 0, 13, 0, 5, 0, 41}, + {12, 9, 13, 0, 5, 0, 41}, + {7, 0, 0, 0, 5, 0, 42}, + {12, 0, 13, 0, 5, 0, 42}, + {12, 9, 13, 0, 5, 0, 42}, + {7, 0, 0, 0, 5, 0, 43}, + {12, 0, 13, 0, 5, 0, 43}, + {7, 0, 0, 0, 5, 0, 44}, + {12, 0, 13, 0, 5, 0, 44}, + {7, 0, 0, 0, 5, 0, 30}, + {12, 0, 13, 0, 5, 0, 30}, + {10, 0, 0, 0, 5, 0, 30}, + {12, 9, 13, 0, 5, 0, 30}, + {21, 0, 0, 0, 5, 0, 30}, + {6, 0, 0, 0, 5, 0, 30}, + {23, 0, 10, 0, 5, 0, 30}, + {12, 230, 13, 0, 5, 0, 30}, + {13, 0, 0, 0, 5, 0, 30}, + {15, 0, 18, 0, 5, 0, 30}, + {21, 0, 18, 0, 5, 0, 31}, + {17, 0, 18, 0, 5, 0, 31}, + {12, 0, 13, 0, 5, 0, 31}, + {29, 0, 17, 0, 5, 0, 31}, + {13, 0, 0, 0, 5, 0, 31}, + {7, 0, 0, 0, 5, 0, 31}, + {6, 0, 0, 0, 5, 0, 31}, + {12, 228, 13, 0, 5, 0, 31}, + {7, 0, 0, 0, 5, 0, 45}, + {12, 0, 13, 0, 5, 0, 45}, + {10, 0, 0, 0, 5, 0, 45}, + {12, 222, 13, 0, 5, 0, 45}, + {12, 230, 13, 0, 5, 0, 45}, + {12, 220, 13, 0, 5, 0, 45}, + {26, 0, 18, 0, 5, 0, 45}, + {21, 0, 18, 0, 5, 0, 45}, + {13, 0, 0, 0, 5, 0, 45}, + {7, 0, 0, 0, 5, 0, 46}, + {7, 0, 0, 0, 5, 0, 55}, + {10, 0, 0, 0, 5, 0, 55}, + {13, 0, 0, 0, 5, 0, 55}, + {15, 0, 0, 0, 5, 0, 55}, + {26, 0, 18, 0, 5, 0, 55}, + {26, 0, 18, 0, 5, 0, 30}, + {7, 0, 0, 0, 5, 0, 53}, + {12, 230, 13, 0, 5, 0, 53}, + {12, 220, 13, 0, 5, 0, 53}, + {10, 0, 0, 0, 5, 0, 53}, + {21, 0, 0, 0, 5, 0, 53}, + {7, 0, 0, 0, 5, 0, 77}, + {10, 0, 0, 0, 5, 0, 77}, + {12, 0, 13, 0, 5, 0, 77}, + {12, 9, 13, 0, 5, 0, 77}, + {12, 230, 13, 0, 5, 0, 77}, + {12, 220, 13, 0, 5, 0, 77}, + {13, 0, 0, 0, 5, 0, 77}, + {21, 0, 0, 0, 5, 0, 77}, + {6, 0, 0, 0, 5, 0, 77}, + {12, 0, 13, 0, 5, 0, 61}, + {10, 0, 0, 0, 5, 0, 61}, + {7, 0, 0, 0, 5, 0, 61}, + {12, 7, 13, 0, 5, 0, 61}, + {10, 9, 0, 0, 5, 0, 61}, + {13, 0, 0, 0, 5, 0, 61}, + {21, 0, 0, 0, 5, 0, 61}, + {26, 0, 0, 0, 5, 0, 61}, + {12, 230, 13, 0, 5, 0, 61}, + {12, 220, 13, 0, 5, 0, 61}, + {12, 0, 13, 0, 5, 0, 66}, + {10, 0, 0, 0, 5, 0, 66}, + {7, 0, 0, 0, 5, 0, 66}, + {10, 9, 0, 0, 5, 0, 66}, + {12, 9, 13, 0, 5, 0, 66}, + {13, 0, 0, 0, 5, 0, 66}, + {7, 0, 0, 0, 5, 0, 92}, + {12, 7, 13, 0, 5, 0, 92}, + {10, 0, 0, 0, 5, 0, 92}, + {12, 0, 13, 0, 5, 0, 92}, + {10, 9, 0, 0, 5, 0, 92}, + {21, 0, 0, 0, 5, 0, 92}, + {7, 0, 0, 0, 5, 0, 67}, + {10, 0, 0, 0, 5, 0, 67}, + {12, 0, 13, 0, 5, 0, 67}, + {12, 7, 13, 0, 5, 0, 67}, + {21, 0, 0, 0, 5, 0, 67}, + {13, 0, 0, 0, 5, 0, 67}, + {13, 0, 0, 0, 5, 0, 68}, + {7, 0, 0, 0, 5, 0, 68}, + {6, 0, 0, 0, 5, 0, 68}, + {21, 0, 0, 0, 5, 0, 68}, + {21, 0, 0, 0, 5, 0, 66}, + {12, 1, 13, 0, 5, 0, 40}, + {10, 0, 0, 0, 5, 0, 0}, + {7, 0, 0, 0, 5, 0, 0}, + {6, 0, 0, 0, 5, 0, 3}, + {12, 234, 13, 0, 5, 0, 40}, + {12, 214, 13, 0, 5, 0, 40}, + {12, 202, 13, 0, 5, 0, 40}, + {12, 233, 13, 0, 5, 0, 40}, + {8, 0, 0, 0, 5, 0, 2}, + {29, 0, 17, 0, 5, 0, 0}, + {1, 0, 14, 0, 5, 0, 0}, + {1, 0, 14, 0, 5, 0, 40}, + {1, 0, 0, 0, 5, 0, 0}, + {1, 0, 3, 0, 5, 0, 0}, + {17, 0, 18, 0, 4, 0, 0}, + {17, 0, 18, 0, 5, 0, 0}, + {20, 0, 18, 0, 4, 0, 0}, + {19, 0, 18, 0, 4, 0, 0}, + {22, 0, 18, 0, 5, 0, 0}, + {20, 0, 18, 0, 5, 0, 0}, + {27, 0, 17, 0, 5, 0, 0}, + {28, 0, 15, 0, 5, 0, 0}, + {1, 0, 1, 0, 5, 0, 0}, + {1, 0, 5, 0, 5, 0, 0}, + {1, 0, 7, 0, 5, 0, 0}, + {1, 0, 2, 0, 5, 0, 0}, + {1, 0, 6, 0, 5, 0, 0}, + {21, 0, 10, 0, 4, 0, 0}, + {21, 0, 10, 0, 5, 0, 0}, + {16, 0, 18, 0, 5, 0, 0}, + {25, 0, 12, 0, 5, 0, 0}, + {22, 0, 18, 1, 5, 0, 0}, + {18, 0, 18, 1, 5, 0, 0}, + {25, 0, 18, 0, 5, 0, 0}, + {15, 0, 8, 0, 5, 0, 0}, + {25, 0, 9, 0, 5, 0, 0}, + {6, 0, 0, 0, 4, 0, 1}, + {23, 0, 10, 0, 1, 0, 0}, + {11, 0, 13, 0, 5, 0, 40}, + {9, 0, 0, 0, 5, 0, 0}, + {5, 0, 0, 0, 4, 0, 0}, + {26, 0, 10, 0, 5, 0, 0}, + {25, 0, 18, 1, 5, 0, 0}, + {15, 0, 18, 0, 5, 0, 0}, + {14, 0, 0, 0, 4, 0, 1}, + {14, 0, 0, 0, 5, 0, 1}, + {25, 0, 18, 1, 4, 0, 0}, + {25, 0, 10, 0, 5, 0, 0}, + {22, 0, 18, 1, 2, 0, 0}, + {18, 0, 18, 1, 2, 0, 0}, + {26, 0, 0, 0, 4, 0, 0}, + {26, 0, 0, 0, 5, 0, 52}, + {9, 0, 0, 0, 5, 0, 56}, + {5, 0, 0, 0, 5, 0, 56}, + {26, 0, 18, 0, 5, 0, 54}, + {12, 230, 13, 0, 5, 0, 54}, + {21, 0, 18, 0, 5, 0, 54}, + {15, 0, 18, 0, 5, 0, 54}, + {5, 0, 0, 0, 5, 0, 23}, + {7, 0, 0, 0, 5, 0, 57}, + {6, 0, 0, 0, 5, 0, 57}, + {21, 0, 0, 0, 5, 0, 57}, + {12, 9, 13, 0, 5, 0, 57}, + {26, 0, 18, 0, 2, 0, 35}, + {26, 0, 18, 0, 2, 0, 0}, + {29, 0, 17, 0, 0, 0, 0}, + {21, 0, 18, 0, 2, 0, 0}, + {6, 0, 0, 0, 2, 0, 35}, + {7, 0, 0, 0, 2, 0, 0}, + {14, 0, 0, 0, 2, 0, 35}, + {17, 0, 18, 0, 2, 0, 0}, + {22, 0, 18, 0, 2, 0, 0}, + {18, 0, 18, 0, 2, 0, 0}, + {12, 218, 13, 0, 2, 0, 40}, + {12, 228, 13, 0, 2, 0, 40}, + {12, 232, 13, 0, 2, 0, 40}, + {12, 222, 13, 0, 2, 0, 40}, + {10, 224, 0, 0, 2, 0, 24}, + {6, 0, 0, 0, 2, 0, 0}, + {7, 0, 0, 0, 2, 0, 32}, + {12, 8, 13, 0, 2, 0, 40}, + {24, 0, 18, 0, 2, 0, 0}, + {6, 0, 0, 0, 2, 0, 32}, + {7, 0, 0, 0, 2, 0, 33}, + {6, 0, 0, 0, 2, 0, 33}, + {7, 0, 0, 0, 2, 0, 34}, + {26, 0, 0, 0, 2, 0, 0}, + {15, 0, 0, 0, 2, 0, 0}, + {26, 0, 0, 0, 2, 0, 24}, + {26, 0, 18, 0, 2, 0, 24}, + {15, 0, 0, 0, 4, 0, 0}, + {15, 0, 18, 0, 2, 0, 0}, + {26, 0, 0, 0, 2, 0, 33}, + {7, 0, 0, 0, 2, 0, 35}, + {2, 0, 18, 0, 2, 0, 35}, + {2, 0, 18, 0, 2, 0, 102}, + {7, 0, 0, 0, 2, 0, 36}, + {6, 0, 0, 0, 2, 0, 36}, + {26, 0, 18, 0, 2, 0, 36}, + {7, 0, 0, 0, 5, 0, 82}, + {6, 0, 0, 0, 5, 0, 82}, + {21, 0, 0, 0, 5, 0, 82}, + {7, 0, 0, 0, 5, 0, 69}, + {6, 0, 0, 0, 5, 0, 69}, + {21, 0, 18, 0, 5, 0, 69}, + {13, 0, 0, 0, 5, 0, 69}, + {7, 0, 0, 0, 5, 0, 3}, + {21, 0, 18, 0, 5, 0, 3}, + {6, 0, 18, 0, 5, 0, 3}, + {7, 0, 0, 0, 5, 0, 83}, + {14, 0, 0, 0, 5, 0, 83}, + {12, 230, 13, 0, 5, 0, 83}, + {21, 0, 0, 0, 5, 0, 83}, + {24, 0, 0, 0, 5, 0, 0}, + {7, 0, 0, 0, 5, 0, 58}, + {12, 0, 13, 0, 5, 0, 58}, + {12, 9, 13, 0, 5, 0, 58}, + {10, 0, 0, 0, 5, 0, 58}, + {26, 0, 18, 0, 5, 0, 58}, + {15, 0, 0, 0, 5, 0, 0}, + {7, 0, 0, 0, 5, 0, 64}, + {21, 0, 18, 0, 5, 0, 64}, + {10, 0, 0, 0, 5, 0, 70}, + {7, 0, 0, 0, 5, 0, 70}, + {12, 9, 13, 0, 5, 0, 70}, + {21, 0, 0, 0, 5, 0, 70}, + {13, 0, 0, 0, 5, 0, 70}, + {13, 0, 0, 0, 5, 0, 71}, + {7, 0, 0, 0, 5, 0, 71}, + {12, 0, 13, 0, 5, 0, 71}, + {12, 220, 13, 0, 5, 0, 71}, + {21, 0, 0, 0, 5, 0, 71}, + {7, 0, 0, 0, 5, 0, 72}, + {12, 0, 13, 0, 5, 0, 72}, + {10, 0, 0, 0, 5, 0, 72}, + {10, 9, 0, 0, 5, 0, 72}, + {21, 0, 0, 0, 5, 0, 72}, + {12, 0, 13, 0, 5, 0, 84}, + {10, 0, 0, 0, 5, 0, 84}, + {7, 0, 0, 0, 5, 0, 84}, + {12, 7, 13, 0, 5, 0, 84}, + {10, 9, 0, 0, 5, 0, 84}, + {21, 0, 0, 0, 5, 0, 84}, + {6, 0, 0, 0, 5, 0, 84}, + {13, 0, 0, 0, 5, 0, 84}, + {7, 0, 0, 0, 5, 0, 76}, + {12, 0, 13, 0, 5, 0, 76}, + {10, 0, 0, 0, 5, 0, 76}, + {13, 0, 0, 0, 5, 0, 76}, + {21, 0, 0, 0, 5, 0, 76}, + {6, 0, 0, 0, 5, 0, 22}, + {7, 0, 0, 0, 5, 0, 78}, + {12, 230, 13, 0, 5, 0, 78}, + {12, 220, 13, 0, 5, 0, 78}, + {6, 0, 0, 0, 5, 0, 78}, + {21, 0, 0, 0, 5, 0, 78}, + {7, 0, 0, 0, 5, 0, 85}, + {10, 0, 0, 0, 5, 0, 85}, + {12, 0, 13, 0, 5, 0, 85}, + {21, 0, 0, 0, 5, 0, 85}, + {6, 0, 0, 0, 5, 0, 85}, + {12, 9, 13, 0, 5, 0, 85}, + {13, 0, 0, 0, 5, 0, 85}, + {2, 0, 18, 0, 2, 0, 24}, + {4, 0, 0, 0, 5, 0, 102}, + {3, 0, 0, 0, 4, 0, 102}, + {2, 0, 18, 0, 4, 0, 102}, + {12, 26, 13, 0, 5, 0, 5}, + {25, 0, 9, 0, 5, 0, 5}, + {24, 0, 4, 0, 5, 0, 6}, + {18, 0, 18, 0, 5, 0, 0}, + {16, 0, 18, 0, 2, 0, 0}, + {21, 0, 12, 0, 2, 0, 0}, + {21, 0, 10, 0, 2, 0, 0}, + {25, 0, 9, 0, 2, 0, 0}, + {17, 0, 9, 0, 2, 0, 0}, + {25, 0, 18, 1, 2, 0, 0}, + {25, 0, 18, 0, 2, 0, 0}, + {23, 0, 10, 0, 2, 0, 0}, + {21, 0, 18, 0, 0, 0, 0}, + {21, 0, 10, 0, 0, 0, 0}, + {23, 0, 10, 0, 0, 0, 0}, + {22, 0, 18, 1, 0, 0, 0}, + {18, 0, 18, 1, 0, 0, 0}, + {25, 0, 9, 0, 0, 0, 0}, + {21, 0, 12, 0, 0, 0, 0}, + {17, 0, 9, 0, 0, 0, 0}, + {13, 0, 8, 0, 0, 0, 0}, + {25, 0, 18, 1, 0, 0, 0}, + {25, 0, 18, 0, 0, 0, 0}, + {9, 0, 0, 0, 0, 0, 1}, + {24, 0, 18, 0, 0, 0, 0}, + {16, 0, 18, 0, 0, 0, 0}, + {5, 0, 0, 0, 0, 0, 1}, + {21, 0, 18, 0, 1, 0, 0}, + {22, 0, 18, 1, 1, 0, 0}, + {18, 0, 18, 1, 1, 0, 0}, + {7, 0, 0, 0, 1, 0, 33}, + {6, 0, 0, 0, 1, 0, 0}, + {7, 0, 0, 0, 1, 0, 24}, + {26, 0, 18, 0, 0, 0, 0}, + {26, 0, 18, 0, 1, 0, 0}, + {25, 0, 18, 0, 1, 0, 0}, + {1, 0, 18, 0, 5, 0, 0}, + {7, 0, 0, 0, 5, 0, 47}, + {14, 0, 18, 0, 5, 0, 2}, + {15, 0, 18, 0, 5, 0, 2}, + {26, 0, 18, 0, 5, 0, 2}, + {7, 0, 0, 0, 5, 0, 73}, + {7, 0, 0, 0, 5, 0, 74}, + {7, 0, 0, 0, 5, 0, 37}, + {15, 0, 0, 0, 5, 0, 37}, + {7, 0, 0, 0, 5, 0, 38}, + {14, 0, 0, 0, 5, 0, 38}, + {7, 0, 0, 0, 5, 0, 48}, + {21, 0, 0, 0, 5, 0, 48}, + {7, 0, 0, 0, 5, 0, 59}, + {21, 0, 0, 0, 5, 0, 59}, + {14, 0, 0, 0, 5, 0, 59}, + {9, 0, 0, 0, 5, 0, 39}, + {5, 0, 0, 0, 5, 0, 39}, + {7, 0, 0, 0, 5, 0, 49}, + {7, 0, 0, 0, 5, 0, 50}, + {13, 0, 0, 0, 5, 0, 50}, + {7, 0, 3, 0, 5, 0, 51}, + {7, 0, 3, 0, 5, 0, 86}, + {21, 0, 3, 0, 5, 0, 86}, + {15, 0, 3, 0, 5, 0, 86}, + {7, 0, 3, 0, 5, 0, 63}, + {15, 0, 3, 0, 5, 0, 63}, + {21, 0, 18, 0, 5, 0, 63}, + {7, 0, 3, 0, 5, 0, 75}, + {21, 0, 3, 0, 5, 0, 75}, + {7, 0, 3, 0, 5, 0, 97}, + {7, 0, 3, 0, 5, 0, 96}, + {7, 0, 3, 0, 5, 0, 60}, + {12, 0, 13, 0, 5, 0, 60}, + {12, 220, 13, 0, 5, 0, 60}, + {12, 230, 13, 0, 5, 0, 60}, + {12, 1, 13, 0, 5, 0, 60}, + {12, 9, 13, 0, 5, 0, 60}, + {15, 0, 3, 0, 5, 0, 60}, + {21, 0, 3, 0, 5, 0, 60}, + {7, 0, 3, 0, 5, 0, 87}, + {15, 0, 3, 0, 5, 0, 87}, + {21, 0, 3, 0, 5, 0, 87}, + {7, 0, 3, 0, 5, 0, 79}, + {21, 0, 18, 0, 5, 0, 79}, + {7, 0, 3, 0, 5, 0, 88}, + {15, 0, 3, 0, 5, 0, 88}, + {7, 0, 3, 0, 5, 0, 89}, + {15, 0, 3, 0, 5, 0, 89}, + {7, 0, 3, 0, 5, 0, 90}, + {15, 0, 11, 0, 5, 0, 6}, + {10, 0, 0, 0, 5, 0, 93}, + {12, 0, 13, 0, 5, 0, 93}, + {7, 0, 0, 0, 5, 0, 93}, + {12, 9, 13, 0, 5, 0, 93}, + {21, 0, 0, 0, 5, 0, 93}, + {15, 0, 18, 0, 5, 0, 93}, + {13, 0, 0, 0, 5, 0, 93}, + {12, 0, 13, 0, 5, 0, 91}, + {10, 0, 0, 0, 5, 0, 91}, + {7, 0, 0, 0, 5, 0, 91}, + {12, 9, 13, 0, 5, 0, 91}, + {12, 7, 13, 0, 5, 0, 91}, + {21, 0, 0, 0, 5, 0, 91}, + {1, 0, 0, 0, 5, 0, 91}, + {7, 0, 0, 0, 5, 0, 100}, + {13, 0, 0, 0, 5, 0, 100}, + {12, 230, 13, 0, 5, 0, 95}, + {7, 0, 0, 0, 5, 0, 95}, + {12, 0, 13, 0, 5, 0, 95}, + {10, 0, 0, 0, 5, 0, 95}, + {12, 9, 13, 0, 5, 0, 95}, + {13, 0, 0, 0, 5, 0, 95}, + {21, 0, 0, 0, 5, 0, 95}, + {12, 0, 13, 0, 5, 0, 99}, + {10, 0, 0, 0, 5, 0, 99}, + {7, 0, 0, 0, 5, 0, 99}, + {10, 9, 0, 0, 5, 0, 99}, + {21, 0, 0, 0, 5, 0, 99}, + {13, 0, 0, 0, 5, 0, 99}, + {7, 0, 0, 0, 5, 0, 101}, + {12, 0, 13, 0, 5, 0, 101}, + {10, 0, 0, 0, 5, 0, 101}, + {10, 9, 0, 0, 5, 0, 101}, + {12, 7, 13, 0, 5, 0, 101}, + {13, 0, 0, 0, 5, 0, 101}, + {7, 0, 0, 0, 5, 0, 62}, + {14, 0, 0, 0, 5, 0, 62}, + {21, 0, 0, 0, 5, 0, 62}, + {7, 0, 0, 0, 5, 0, 80}, + {7, 0, 0, 0, 5, 0, 98}, + {10, 0, 0, 0, 5, 0, 98}, + {12, 0, 13, 0, 5, 0, 98}, + {6, 0, 0, 0, 5, 0, 98}, + {10, 216, 0, 0, 5, 0, 0}, + {10, 226, 0, 0, 5, 0, 0}, + {12, 230, 13, 0, 5, 0, 2}, + {25, 0, 0, 0, 5, 0, 0}, + {13, 0, 8, 0, 5, 0, 0}, + {26, 0, 0, 0, 2, 0, 32}, +}; + +#define BIDI_MIRROR_LEN 364 +static const MirrorPair mirror_pairs[] = { + {40, 41}, + {41, 40}, + {60, 62}, + {62, 60}, + {91, 93}, + {93, 91}, + {123, 125}, + {125, 123}, + {171, 187}, + {187, 171}, + {3898, 3899}, + {3899, 3898}, + {3900, 3901}, + {3901, 3900}, + {5787, 5788}, + {5788, 5787}, + {8249, 8250}, + {8250, 8249}, + {8261, 8262}, + {8262, 8261}, + {8317, 8318}, + {8318, 8317}, + {8333, 8334}, + {8334, 8333}, + {8712, 8715}, + {8713, 8716}, + {8714, 8717}, + {8715, 8712}, + {8716, 8713}, + {8717, 8714}, + {8725, 10741}, + {8764, 8765}, + {8765, 8764}, + {8771, 8909}, + {8786, 8787}, + {8787, 8786}, + {8788, 8789}, + {8789, 8788}, + {8804, 8805}, + {8805, 8804}, + {8806, 8807}, + {8807, 8806}, + {8808, 8809}, + {8809, 8808}, + {8810, 8811}, + {8811, 8810}, + {8814, 8815}, + {8815, 8814}, + {8816, 8817}, + {8817, 8816}, + {8818, 8819}, + {8819, 8818}, + {8820, 8821}, + {8821, 8820}, + {8822, 8823}, + {8823, 8822}, + {8824, 8825}, + {8825, 8824}, + {8826, 8827}, + {8827, 8826}, + {8828, 8829}, + {8829, 8828}, + {8830, 8831}, + {8831, 8830}, + {8832, 8833}, + {8833, 8832}, + {8834, 8835}, + {8835, 8834}, + {8836, 8837}, + {8837, 8836}, + {8838, 8839}, + {8839, 8838}, + {8840, 8841}, + {8841, 8840}, + {8842, 8843}, + {8843, 8842}, + {8847, 8848}, + {8848, 8847}, + {8849, 8850}, + {8850, 8849}, + {8856, 10680}, + {8866, 8867}, + {8867, 8866}, + {8870, 10974}, + {8872, 10980}, + {8873, 10979}, + {8875, 10981}, + {8880, 8881}, + {8881, 8880}, + {8882, 8883}, + {8883, 8882}, + {8884, 8885}, + {8885, 8884}, + {8886, 8887}, + {8887, 8886}, + {8905, 8906}, + {8906, 8905}, + {8907, 8908}, + {8908, 8907}, + {8909, 8771}, + {8912, 8913}, + {8913, 8912}, + {8918, 8919}, + {8919, 8918}, + {8920, 8921}, + {8921, 8920}, + {8922, 8923}, + {8923, 8922}, + {8924, 8925}, + {8925, 8924}, + {8926, 8927}, + {8927, 8926}, + {8928, 8929}, + {8929, 8928}, + {8930, 8931}, + {8931, 8930}, + {8932, 8933}, + {8933, 8932}, + {8934, 8935}, + {8935, 8934}, + {8936, 8937}, + {8937, 8936}, + {8938, 8939}, + {8939, 8938}, + {8940, 8941}, + {8941, 8940}, + {8944, 8945}, + {8945, 8944}, + {8946, 8954}, + {8947, 8955}, + {8948, 8956}, + {8950, 8957}, + {8951, 8958}, + {8954, 8946}, + {8955, 8947}, + {8956, 8948}, + {8957, 8950}, + {8958, 8951}, + {8968, 8969}, + {8969, 8968}, + {8970, 8971}, + {8971, 8970}, + {9001, 9002}, + {9002, 9001}, + {10088, 10089}, + {10089, 10088}, + {10090, 10091}, + {10091, 10090}, + {10092, 10093}, + {10093, 10092}, + {10094, 10095}, + {10095, 10094}, + {10096, 10097}, + {10097, 10096}, + {10098, 10099}, + {10099, 10098}, + {10100, 10101}, + {10101, 10100}, + {10179, 10180}, + {10180, 10179}, + {10181, 10182}, + {10182, 10181}, + {10184, 10185}, + {10185, 10184}, + {10187, 10189}, + {10189, 10187}, + {10197, 10198}, + {10198, 10197}, + {10205, 10206}, + {10206, 10205}, + {10210, 10211}, + {10211, 10210}, + {10212, 10213}, + {10213, 10212}, + {10214, 10215}, + {10215, 10214}, + {10216, 10217}, + {10217, 10216}, + {10218, 10219}, + {10219, 10218}, + {10220, 10221}, + {10221, 10220}, + {10222, 10223}, + {10223, 10222}, + {10627, 10628}, + {10628, 10627}, + {10629, 10630}, + {10630, 10629}, + {10631, 10632}, + {10632, 10631}, + {10633, 10634}, + {10634, 10633}, + {10635, 10636}, + {10636, 10635}, + {10637, 10640}, + {10638, 10639}, + {10639, 10638}, + {10640, 10637}, + {10641, 10642}, + {10642, 10641}, + {10643, 10644}, + {10644, 10643}, + {10645, 10646}, + {10646, 10645}, + {10647, 10648}, + {10648, 10647}, + {10680, 8856}, + {10688, 10689}, + {10689, 10688}, + {10692, 10693}, + {10693, 10692}, + {10703, 10704}, + {10704, 10703}, + {10705, 10706}, + {10706, 10705}, + {10708, 10709}, + {10709, 10708}, + {10712, 10713}, + {10713, 10712}, + {10714, 10715}, + {10715, 10714}, + {10741, 8725}, + {10744, 10745}, + {10745, 10744}, + {10748, 10749}, + {10749, 10748}, + {10795, 10796}, + {10796, 10795}, + {10797, 10798}, + {10798, 10797}, + {10804, 10805}, + {10805, 10804}, + {10812, 10813}, + {10813, 10812}, + {10852, 10853}, + {10853, 10852}, + {10873, 10874}, + {10874, 10873}, + {10877, 10878}, + {10878, 10877}, + {10879, 10880}, + {10880, 10879}, + {10881, 10882}, + {10882, 10881}, + {10883, 10884}, + {10884, 10883}, + {10891, 10892}, + {10892, 10891}, + {10897, 10898}, + {10898, 10897}, + {10899, 10900}, + {10900, 10899}, + {10901, 10902}, + {10902, 10901}, + {10903, 10904}, + {10904, 10903}, + {10905, 10906}, + {10906, 10905}, + {10907, 10908}, + {10908, 10907}, + {10913, 10914}, + {10914, 10913}, + {10918, 10919}, + {10919, 10918}, + {10920, 10921}, + {10921, 10920}, + {10922, 10923}, + {10923, 10922}, + {10924, 10925}, + {10925, 10924}, + {10927, 10928}, + {10928, 10927}, + {10931, 10932}, + {10932, 10931}, + {10939, 10940}, + {10940, 10939}, + {10941, 10942}, + {10942, 10941}, + {10943, 10944}, + {10944, 10943}, + {10945, 10946}, + {10946, 10945}, + {10947, 10948}, + {10948, 10947}, + {10949, 10950}, + {10950, 10949}, + {10957, 10958}, + {10958, 10957}, + {10959, 10960}, + {10960, 10959}, + {10961, 10962}, + {10962, 10961}, + {10963, 10964}, + {10964, 10963}, + {10965, 10966}, + {10966, 10965}, + {10974, 8870}, + {10979, 8873}, + {10980, 8872}, + {10981, 8875}, + {10988, 10989}, + {10989, 10988}, + {10999, 11000}, + {11000, 10999}, + {11001, 11002}, + {11002, 11001}, + {11778, 11779}, + {11779, 11778}, + {11780, 11781}, + {11781, 11780}, + {11785, 11786}, + {11786, 11785}, + {11788, 11789}, + {11789, 11788}, + {11804, 11805}, + {11805, 11804}, + {11808, 11809}, + {11809, 11808}, + {11810, 11811}, + {11811, 11810}, + {11812, 11813}, + {11813, 11812}, + {11814, 11815}, + {11815, 11814}, + {11816, 11817}, + {11817, 11816}, + {12296, 12297}, + {12297, 12296}, + {12298, 12299}, + {12299, 12298}, + {12300, 12301}, + {12301, 12300}, + {12302, 12303}, + {12303, 12302}, + {12304, 12305}, + {12305, 12304}, + {12308, 12309}, + {12309, 12308}, + {12310, 12311}, + {12311, 12310}, + {12312, 12313}, + {12313, 12312}, + {12314, 12315}, + {12315, 12314}, + {65113, 65114}, + {65114, 65113}, + {65115, 65116}, + {65116, 65115}, + {65117, 65118}, + {65118, 65117}, + {65124, 65125}, + {65125, 65124}, + {65288, 65289}, + {65289, 65288}, + {65308, 65310}, + {65310, 65308}, + {65339, 65341}, + {65341, 65339}, + {65371, 65373}, + {65373, 65371}, + {65375, 65376}, + {65376, 65375}, + {65378, 65379}, + {65379, 65378}, +}; + +/* Reindexing of NFC first characters. */ +#define TOTAL_FIRST 372 +#define TOTAL_LAST 56 +static const Reindex nfc_first[] = { + { 60, 2, 0}, + { 65, 15, 3}, + { 82, 8, 19}, + { 97, 15, 28}, + { 114, 8, 44}, + { 168, 0, 53}, + { 194, 0, 54}, + { 196, 3, 55}, + { 202, 0, 59}, + { 207, 0, 60}, + { 212, 2, 61}, + { 216, 0, 64}, + { 220, 0, 65}, + { 226, 0, 66}, + { 228, 3, 67}, + { 234, 0, 71}, + { 239, 0, 72}, + { 244, 2, 73}, + { 248, 0, 76}, + { 252, 0, 77}, + { 258, 1, 78}, + { 274, 1, 80}, + { 332, 1, 82}, + { 346, 1, 84}, + { 352, 1, 86}, + { 360, 3, 88}, + { 383, 0, 92}, + { 416, 1, 93}, + { 431, 1, 95}, + { 439, 0, 97}, + { 490, 1, 98}, + { 550, 3, 100}, + { 558, 1, 104}, + { 658, 0, 106}, + { 913, 0, 107}, + { 917, 0, 108}, + { 919, 0, 109}, + { 921, 0, 110}, + { 927, 0, 111}, + { 929, 0, 112}, + { 933, 0, 113}, + { 937, 0, 114}, + { 940, 0, 115}, + { 942, 0, 116}, + { 945, 0, 117}, + { 949, 0, 118}, + { 951, 0, 119}, + { 953, 0, 120}, + { 959, 0, 121}, + { 961, 0, 122}, + { 965, 0, 123}, + { 969, 2, 124}, + { 974, 0, 127}, + { 978, 0, 128}, + { 1030, 0, 129}, + { 1040, 0, 130}, + { 1043, 0, 131}, + { 1045, 3, 132}, + { 1050, 0, 136}, + { 1054, 0, 137}, + { 1059, 0, 138}, + { 1063, 0, 139}, + { 1067, 0, 140}, + { 1069, 0, 141}, + { 1072, 0, 142}, + { 1075, 0, 143}, + { 1077, 3, 144}, + { 1082, 0, 148}, + { 1086, 0, 149}, + { 1091, 0, 150}, + { 1095, 0, 151}, + { 1099, 0, 152}, + { 1101, 0, 153}, + { 1110, 0, 154}, + { 1140, 1, 155}, + { 1240, 1, 157}, + { 1256, 1, 159}, + { 1575, 0, 161}, + { 1608, 0, 162}, + { 1610, 0, 163}, + { 1729, 0, 164}, + { 1746, 0, 165}, + { 1749, 0, 166}, + { 2344, 0, 167}, + { 2352, 0, 168}, + { 2355, 0, 169}, + { 2503, 0, 170}, + { 2887, 0, 171}, + { 2962, 0, 172}, + { 3014, 1, 173}, + { 3142, 0, 175}, + { 3263, 0, 176}, + { 3270, 0, 177}, + { 3274, 0, 178}, + { 3398, 1, 179}, + { 3545, 0, 181}, + { 3548, 0, 182}, + { 4133, 0, 183}, + { 6917, 0, 184}, + { 6919, 0, 185}, + { 6921, 0, 186}, + { 6923, 0, 187}, + { 6925, 0, 188}, + { 6929, 0, 189}, + { 6970, 0, 190}, + { 6972, 0, 191}, + { 6974, 1, 192}, + { 6978, 0, 194}, + { 7734, 1, 195}, + { 7770, 1, 197}, + { 7778, 1, 199}, + { 7840, 1, 201}, + { 7864, 1, 203}, + { 7884, 1, 205}, + { 7936, 17, 207}, + { 7960, 1, 225}, + { 7968, 17, 227}, + { 7992, 1, 245}, + { 8000, 1, 247}, + { 8008, 1, 249}, + { 8016, 1, 251}, + { 8025, 0, 253}, + { 8032, 16, 254}, + { 8052, 0, 271}, + { 8060, 0, 272}, + { 8118, 0, 273}, + { 8127, 0, 274}, + { 8134, 0, 275}, + { 8182, 0, 276}, + { 8190, 0, 277}, + { 8592, 0, 278}, + { 8594, 0, 279}, + { 8596, 0, 280}, + { 8656, 0, 281}, + { 8658, 0, 282}, + { 8660, 0, 283}, + { 8707, 0, 284}, + { 8712, 0, 285}, + { 8715, 0, 286}, + { 8739, 0, 287}, + { 8741, 0, 288}, + { 8764, 0, 289}, + { 8771, 0, 290}, + { 8773, 0, 291}, + { 8776, 0, 292}, + { 8781, 0, 293}, + { 8801, 0, 294}, + { 8804, 1, 295}, + { 8818, 1, 297}, + { 8822, 1, 299}, + { 8826, 3, 301}, + { 8834, 1, 305}, + { 8838, 1, 307}, + { 8849, 1, 309}, + { 8866, 0, 311}, + { 8872, 1, 312}, + { 8875, 0, 314}, + { 8882, 3, 315}, + { 12358, 0, 319}, + { 12363, 0, 320}, + { 12365, 0, 321}, + { 12367, 0, 322}, + { 12369, 0, 323}, + { 12371, 0, 324}, + { 12373, 0, 325}, + { 12375, 0, 326}, + { 12377, 0, 327}, + { 12379, 0, 328}, + { 12381, 0, 329}, + { 12383, 0, 330}, + { 12385, 0, 331}, + { 12388, 0, 332}, + { 12390, 0, 333}, + { 12392, 0, 334}, + { 12399, 0, 335}, + { 12402, 0, 336}, + { 12405, 0, 337}, + { 12408, 0, 338}, + { 12411, 0, 339}, + { 12445, 0, 340}, + { 12454, 0, 341}, + { 12459, 0, 342}, + { 12461, 0, 343}, + { 12463, 0, 344}, + { 12465, 0, 345}, + { 12467, 0, 346}, + { 12469, 0, 347}, + { 12471, 0, 348}, + { 12473, 0, 349}, + { 12475, 0, 350}, + { 12477, 0, 351}, + { 12479, 0, 352}, + { 12481, 0, 353}, + { 12484, 0, 354}, + { 12486, 0, 355}, + { 12488, 0, 356}, + { 12495, 0, 357}, + { 12498, 0, 358}, + { 12501, 0, 359}, + { 12504, 0, 360}, + { 12507, 0, 361}, + { 12527, 3, 362}, + { 12541, 0, 366}, + { 69785, 0, 367}, + { 69787, 0, 368}, + { 69797, 0, 369}, + { 69937, 1, 370}, + {0,0,0} +}; + +static const Reindex nfc_last[] = { + { 768, 4, 0}, + { 774, 6, 5}, + { 783, 0, 12}, + { 785, 0, 13}, + { 787, 1, 14}, + { 795, 0, 16}, + { 803, 5, 17}, + { 813, 1, 23}, + { 816, 1, 25}, + { 824, 0, 27}, + { 834, 0, 28}, + { 837, 0, 29}, + { 1619, 2, 30}, + { 2364, 0, 33}, + { 2494, 0, 34}, + { 2519, 0, 35}, + { 2878, 0, 36}, + { 2902, 1, 37}, + { 3006, 0, 39}, + { 3031, 0, 40}, + { 3158, 0, 41}, + { 3266, 0, 42}, + { 3285, 1, 43}, + { 3390, 0, 45}, + { 3415, 0, 46}, + { 3530, 0, 47}, + { 3535, 0, 48}, + { 3551, 0, 49}, + { 4142, 0, 50}, + { 6965, 0, 51}, + { 12441, 1, 52}, + { 69818, 0, 54}, + { 69927, 0, 55}, + {0,0,0} +}; + +#define UCDN_EAST_ASIAN_F 0 +#define UCDN_EAST_ASIAN_H 1 +#define UCDN_EAST_ASIAN_W 2 +#define UCDN_EAST_ASIAN_NA 3 +#define UCDN_EAST_ASIAN_A 4 +#define UCDN_EAST_ASIAN_N 5 + +#define UCDN_SCRIPT_COMMON 0 +#define UCDN_SCRIPT_LATIN 1 +#define UCDN_SCRIPT_GREEK 2 +#define UCDN_SCRIPT_CYRILLIC 3 +#define UCDN_SCRIPT_ARMENIAN 4 +#define UCDN_SCRIPT_HEBREW 5 +#define UCDN_SCRIPT_ARABIC 6 +#define UCDN_SCRIPT_SYRIAC 7 +#define UCDN_SCRIPT_THAANA 8 +#define UCDN_SCRIPT_DEVANAGARI 9 +#define UCDN_SCRIPT_BENGALI 10 +#define UCDN_SCRIPT_GURMUKHI 11 +#define UCDN_SCRIPT_GUJARATI 12 +#define UCDN_SCRIPT_ORIYA 13 +#define UCDN_SCRIPT_TAMIL 14 +#define UCDN_SCRIPT_TELUGU 15 +#define UCDN_SCRIPT_KANNADA 16 +#define UCDN_SCRIPT_MALAYALAM 17 +#define UCDN_SCRIPT_SINHALA 18 +#define UCDN_SCRIPT_THAI 19 +#define UCDN_SCRIPT_LAO 20 +#define UCDN_SCRIPT_TIBETAN 21 +#define UCDN_SCRIPT_MYANMAR 22 +#define UCDN_SCRIPT_GEORGIAN 23 +#define UCDN_SCRIPT_HANGUL 24 +#define UCDN_SCRIPT_ETHIOPIC 25 +#define UCDN_SCRIPT_CHEROKEE 26 +#define UCDN_SCRIPT_CANADIAN_ABORIGINAL 27 +#define UCDN_SCRIPT_OGHAM 28 +#define UCDN_SCRIPT_RUNIC 29 +#define UCDN_SCRIPT_KHMER 30 +#define UCDN_SCRIPT_MONGOLIAN 31 +#define UCDN_SCRIPT_HIRAGANA 32 +#define UCDN_SCRIPT_KATAKANA 33 +#define UCDN_SCRIPT_BOPOMOFO 34 +#define UCDN_SCRIPT_HAN 35 +#define UCDN_SCRIPT_YI 36 +#define UCDN_SCRIPT_OLD_ITALIC 37 +#define UCDN_SCRIPT_GOTHIC 38 +#define UCDN_SCRIPT_DESERET 39 +#define UCDN_SCRIPT_INHERITED 40 +#define UCDN_SCRIPT_TAGALOG 41 +#define UCDN_SCRIPT_HANUNOO 42 +#define UCDN_SCRIPT_BUHID 43 +#define UCDN_SCRIPT_TAGBANWA 44 +#define UCDN_SCRIPT_LIMBU 45 +#define UCDN_SCRIPT_TAI_LE 46 +#define UCDN_SCRIPT_LINEAR_B 47 +#define UCDN_SCRIPT_UGARITIC 48 +#define UCDN_SCRIPT_SHAVIAN 49 +#define UCDN_SCRIPT_OSMANYA 50 +#define UCDN_SCRIPT_CYPRIOT 51 +#define UCDN_SCRIPT_BRAILLE 52 +#define UCDN_SCRIPT_BUGINESE 53 +#define UCDN_SCRIPT_COPTIC 54 +#define UCDN_SCRIPT_NEW_TAI_LUE 55 +#define UCDN_SCRIPT_GLAGOLITIC 56 +#define UCDN_SCRIPT_TIFINAGH 57 +#define UCDN_SCRIPT_SYLOTI_NAGRI 58 +#define UCDN_SCRIPT_OLD_PERSIAN 59 +#define UCDN_SCRIPT_KHAROSHTHI 60 +#define UCDN_SCRIPT_BALINESE 61 +#define UCDN_SCRIPT_CUNEIFORM 62 +#define UCDN_SCRIPT_PHOENICIAN 63 +#define UCDN_SCRIPT_PHAGS_PA 64 +#define UCDN_SCRIPT_NKO 65 +#define UCDN_SCRIPT_SUNDANESE 66 +#define UCDN_SCRIPT_LEPCHA 67 +#define UCDN_SCRIPT_OL_CHIKI 68 +#define UCDN_SCRIPT_VAI 69 +#define UCDN_SCRIPT_SAURASHTRA 70 +#define UCDN_SCRIPT_KAYAH_LI 71 +#define UCDN_SCRIPT_REJANG 72 +#define UCDN_SCRIPT_LYCIAN 73 +#define UCDN_SCRIPT_CARIAN 74 +#define UCDN_SCRIPT_LYDIAN 75 +#define UCDN_SCRIPT_CHAM 76 +#define UCDN_SCRIPT_TAI_THAM 77 +#define UCDN_SCRIPT_TAI_VIET 78 +#define UCDN_SCRIPT_AVESTAN 79 +#define UCDN_SCRIPT_EGYPTIAN_HIEROGLYPHS 80 +#define UCDN_SCRIPT_SAMARITAN 81 +#define UCDN_SCRIPT_LISU 82 +#define UCDN_SCRIPT_BAMUM 83 +#define UCDN_SCRIPT_JAVANESE 84 +#define UCDN_SCRIPT_MEETEI_MAYEK 85 +#define UCDN_SCRIPT_IMPERIAL_ARAMAIC 86 +#define UCDN_SCRIPT_OLD_SOUTH_ARABIAN 87 +#define UCDN_SCRIPT_INSCRIPTIONAL_PARTHIAN 88 +#define UCDN_SCRIPT_INSCRIPTIONAL_PAHLAVI 89 +#define UCDN_SCRIPT_OLD_TURKIC 90 +#define UCDN_SCRIPT_KAITHI 91 +#define UCDN_SCRIPT_BATAK 92 +#define UCDN_SCRIPT_BRAHMI 93 +#define UCDN_SCRIPT_MANDAIC 94 +#define UCDN_SCRIPT_CHAKMA 95 +#define UCDN_SCRIPT_MEROITIC_CURSIVE 96 +#define UCDN_SCRIPT_MEROITIC_HIEROGLYPHS 97 +#define UCDN_SCRIPT_MIAO 98 +#define UCDN_SCRIPT_SHARADA 99 +#define UCDN_SCRIPT_SORA_SOMPENG 100 +#define UCDN_SCRIPT_TAKRI 101 +#define UCDN_SCRIPT_UNKNOWN 102 + +#define UCDN_GENERAL_CATEGORY_CC 0 +#define UCDN_GENERAL_CATEGORY_CF 1 +#define UCDN_GENERAL_CATEGORY_CN 2 +#define UCDN_GENERAL_CATEGORY_CO 3 +#define UCDN_GENERAL_CATEGORY_CS 4 +#define UCDN_GENERAL_CATEGORY_LL 5 +#define UCDN_GENERAL_CATEGORY_LM 6 +#define UCDN_GENERAL_CATEGORY_LO 7 +#define UCDN_GENERAL_CATEGORY_LT 8 +#define UCDN_GENERAL_CATEGORY_LU 9 +#define UCDN_GENERAL_CATEGORY_MC 10 +#define UCDN_GENERAL_CATEGORY_ME 11 +#define UCDN_GENERAL_CATEGORY_MN 12 +#define UCDN_GENERAL_CATEGORY_ND 13 +#define UCDN_GENERAL_CATEGORY_NL 14 +#define UCDN_GENERAL_CATEGORY_NO 15 +#define UCDN_GENERAL_CATEGORY_PC 16 +#define UCDN_GENERAL_CATEGORY_PD 17 +#define UCDN_GENERAL_CATEGORY_PE 18 +#define UCDN_GENERAL_CATEGORY_PF 19 +#define UCDN_GENERAL_CATEGORY_PI 20 +#define UCDN_GENERAL_CATEGORY_PO 21 +#define UCDN_GENERAL_CATEGORY_PS 22 +#define UCDN_GENERAL_CATEGORY_SC 23 +#define UCDN_GENERAL_CATEGORY_SK 24 +#define UCDN_GENERAL_CATEGORY_SM 25 +#define UCDN_GENERAL_CATEGORY_SO 26 +#define UCDN_GENERAL_CATEGORY_ZL 27 +#define UCDN_GENERAL_CATEGORY_ZP 28 +#define UCDN_GENERAL_CATEGORY_ZS 29 + +#define UCDN_BIDI_CLASS_L 0 +#define UCDN_BIDI_CLASS_LRE 1 +#define UCDN_BIDI_CLASS_LRO 2 +#define UCDN_BIDI_CLASS_R 3 +#define UCDN_BIDI_CLASS_AL 4 +#define UCDN_BIDI_CLASS_RLE 5 +#define UCDN_BIDI_CLASS_RLO 6 +#define UCDN_BIDI_CLASS_PDF 7 +#define UCDN_BIDI_CLASS_EN 8 +#define UCDN_BIDI_CLASS_ES 9 +#define UCDN_BIDI_CLASS_ET 10 +#define UCDN_BIDI_CLASS_AN 11 +#define UCDN_BIDI_CLASS_CS 12 +#define UCDN_BIDI_CLASS_NSM 13 +#define UCDN_BIDI_CLASS_BN 14 +#define UCDN_BIDI_CLASS_B 15 +#define UCDN_BIDI_CLASS_S 16 +#define UCDN_BIDI_CLASS_WS 17 +#define UCDN_BIDI_CLASS_ON 18 + +/* index tables for the database records */ +#define SHIFT1 5 +#define SHIFT2 3 +static const unsigned char index0[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 54, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 55, 56, 57, 57, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 68, 69, 70, 70, + 71, 69, 70, 70, 72, 73, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 70, 70, 70, 88, 89, 90, 91, 92, 70, 93, 70, 94, + 95, 70, 70, 70, 70, 96, 70, 70, 70, 70, 70, 70, 70, 70, 70, 97, 97, 97, + 98, 99, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 100, 100, 100, 100, + 101, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 102, 102, + 103, 70, 70, 70, 70, 104, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 105, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 106, 107, 108, 109, 110, + 111, 112, 113, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 114, 70, 115, 116, 117, 118, 119, 120, + 121, 122, 70, 70, 70, 70, 70, 70, 70, 70, 52, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 123, 52, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 124, 125, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 76, 76, 127, 126, 126, 126, 126, 128, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 128, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 129, 130, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 73, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 131, 73, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 131, +}; + +static const unsigned short index1[] = { + 0, 1, 0, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 11, 12, 13, 0, 0, 0, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 29, 31, 32, + 33, 34, 35, 27, 30, 29, 27, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 27, 27, 49, 27, 27, 27, 27, 27, 27, 27, 50, 51, 52, 27, 53, 54, + 53, 54, 54, 54, 54, 54, 55, 54, 54, 54, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 64, 65, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 65, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 97, 97, 97, 98, 98, 98, 98, 99, 100, 101, 101, 101, 101, 102, 103, + 101, 101, 101, 101, 101, 101, 104, 105, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 106, 107, 108, 108, 108, 109, 110, 111, 112, + 112, 112, 112, 113, 114, 115, 116, 117, 118, 119, 120, 106, 121, 121, + 121, 122, 123, 106, 124, 125, 126, 127, 128, 128, 128, 128, 129, 130, + 131, 132, 133, 134, 135, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 145, 145, + 146, 147, 148, 149, 128, 128, 128, 128, 128, 128, 150, 150, 150, 150, + 151, 152, 153, 106, 154, 155, 156, 156, 156, 157, 158, 159, 160, 160, + 161, 162, 163, 164, 165, 166, 167, 167, 167, 168, 106, 106, 106, 106, + 106, 106, 106, 106, 169, 170, 106, 106, 106, 106, 106, 106, 171, 172, + 173, 174, 175, 176, 176, 176, 176, 176, 176, 177, 178, 179, 180, 176, + 181, 182, 183, 184, 185, 186, 187, 188, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 106, 214, 215, 216, 217, 217, 218, + 219, 220, 221, 222, 223, 106, 224, 225, 226, 106, 227, 228, 229, 230, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 106, 241, 242, + 243, 244, 245, 242, 246, 247, 248, 249, 250, 106, 251, 252, 253, 254, + 255, 256, 257, 258, 258, 257, 259, 260, 261, 262, 263, 264, 265, 266, + 106, 267, 268, 269, 270, 271, 271, 270, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 106, 281, 282, 283, 284, 284, 284, 284, 285, 286, 287, + 288, 106, 289, 290, 291, 292, 293, 294, 295, 296, 294, 294, 297, 298, + 295, 299, 300, 301, 106, 106, 302, 106, 303, 304, 304, 304, 304, 304, + 305, 306, 307, 308, 309, 310, 106, 106, 106, 106, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 106, 106, 106, 106, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 331, 331, 331, 333, 334, 335, + 336, 337, 338, 339, 338, 338, 338, 340, 341, 342, 343, 344, 106, 106, + 106, 106, 345, 345, 345, 345, 345, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 345, 356, 357, 349, 358, 359, 359, 359, 359, 360, 361, + 362, 362, 362, 362, 362, 363, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, + 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 366, 366, 366, 366, + 366, 366, 366, 366, 366, 367, 368, 367, 366, 366, 366, 366, 366, 367, + 366, 366, 366, 366, 367, 368, 367, 366, 368, 366, 366, 366, 366, 366, + 366, 366, 367, 366, 366, 366, 366, 366, 366, 366, 366, 369, 370, 371, + 372, 373, 366, 366, 374, 375, 376, 376, 376, 376, 376, 376, 376, 376, + 376, 376, 377, 106, 378, 379, 379, 379, 379, 379, 379, 379, 379, 379, + 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, + 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, + 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, + 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, + 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, 380, 379, 379, + 381, 382, 382, 383, 384, 384, 384, 384, 384, 384, 384, 384, 384, 385, + 386, 106, 387, 388, 389, 106, 390, 390, 391, 106, 392, 392, 393, 106, + 394, 395, 396, 106, 397, 397, 397, 397, 397, 397, 398, 399, 400, 401, + 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 412, 412, 412, + 413, 412, 412, 412, 412, 412, 412, 106, 412, 412, 412, 412, 412, 414, + 379, 379, 379, 379, 379, 379, 379, 379, 415, 106, 416, 416, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 424, 424, 425, 426, 106, 427, 427, + 427, 427, 427, 428, 429, 429, 430, 431, 432, 433, 434, 434, 434, 434, + 435, 435, 436, 437, 438, 438, 438, 438, 438, 438, 439, 440, 441, 442, + 443, 444, 445, 446, 445, 446, 447, 448, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 449, 450, 450, 450, 450, 450, 451, 452, 453, 454, + 455, 456, 457, 458, 459, 460, 461, 462, 462, 462, 463, 464, 465, 466, + 467, 467, 467, 467, 468, 469, 470, 471, 472, 472, 472, 472, 473, 474, + 475, 476, 477, 478, 479, 480, 481, 481, 481, 482, 106, 106, 106, 106, + 106, 106, 106, 106, 483, 106, 484, 485, 486, 487, 488, 106, 54, 54, 54, + 54, 489, 490, 56, 56, 56, 56, 56, 491, 492, 493, 54, 494, 54, 54, 54, + 495, 56, 56, 56, 496, 497, 498, 499, 500, 501, 106, 106, 502, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 503, 504, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 505, 506, 507, 508, 505, 506, + 505, 506, 507, 508, 505, 509, 505, 506, 505, 507, 505, 510, 505, 510, + 505, 510, 511, 512, 513, 514, 515, 516, 505, 517, 518, 519, 520, 521, + 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, + 536, 537, 56, 538, 539, 540, 539, 541, 106, 106, 542, 543, 544, 545, 546, + 106, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, + 560, 559, 561, 562, 563, 564, 565, 566, 567, 568, 569, 568, 570, 571, + 568, 572, 568, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, + 584, 585, 586, 587, 588, 583, 583, 589, 590, 591, 592, 593, 583, 583, + 594, 574, 595, 596, 583, 583, 597, 583, 583, 568, 598, 599, 568, 600, + 601, 602, 603, 603, 603, 603, 603, 603, 603, 603, 604, 568, 568, 605, + 606, 574, 574, 607, 568, 568, 568, 568, 573, 608, 568, 609, 106, 568, + 568, 568, 568, 610, 106, 106, 106, 568, 611, 106, 106, 612, 612, 612, + 612, 612, 613, 613, 614, 615, 615, 615, 615, 615, 615, 615, 615, 615, + 616, 612, 612, 617, 617, 617, 617, 617, 617, 617, 617, 617, 618, 617, + 617, 617, 617, 618, 568, 617, 617, 619, 568, 620, 569, 621, 622, 623, + 624, 569, 568, 619, 572, 568, 574, 625, 626, 622, 627, 568, 568, 568, + 568, 628, 568, 568, 568, 629, 630, 568, 568, 568, 568, 568, 631, 568, + 632, 568, 631, 633, 634, 617, 617, 635, 617, 617, 617, 636, 568, 568, + 568, 568, 568, 568, 637, 568, 568, 572, 568, 568, 638, 639, 612, 640, + 640, 641, 568, 568, 568, 568, 568, 642, 643, 644, 645, 646, 647, 574, + 574, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, + 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, + 648, 648, 648, 648, 648, 574, 574, 574, 574, 574, 574, 574, 574, 574, + 574, 574, 574, 574, 574, 574, 574, 649, 650, 650, 651, 583, 583, 574, + 652, 597, 653, 654, 655, 656, 657, 658, 659, 574, 660, 583, 661, 662, + 663, 664, 645, 574, 574, 586, 652, 664, 665, 666, 667, 583, 583, 583, + 583, 668, 669, 583, 583, 583, 583, 670, 671, 672, 645, 673, 674, 568, + 568, 568, 568, 568, 568, 574, 574, 675, 676, 677, 678, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 679, 679, 679, 679, 679, 680, 681, 681, 681, 681, 681, + 682, 683, 684, 685, 686, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 687, 688, 689, 690, 691, 691, 691, 691, 692, 693, 694, 694, 694, 694, + 694, 694, 694, 695, 696, 697, 366, 366, 368, 106, 368, 368, 368, 368, + 368, 368, 368, 368, 698, 698, 698, 698, 699, 700, 701, 702, 703, 704, + 529, 705, 106, 106, 106, 106, 106, 106, 106, 106, 706, 706, 706, 707, + 706, 706, 706, 706, 706, 706, 706, 706, 706, 706, 708, 106, 706, 706, + 706, 706, 706, 706, 706, 706, 706, 706, 706, 706, 706, 706, 706, 706, + 706, 706, 706, 706, 706, 706, 706, 706, 706, 706, 709, 106, 106, 106, + 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 721, 721, + 721, 721, 721, 721, 721, 721, 722, 723, 724, 725, 725, 725, 725, 725, + 725, 725, 725, 725, 725, 726, 727, 728, 728, 728, 728, 729, 730, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 731, 732, 733, 728, 728, + 728, 734, 710, 710, 710, 710, 711, 106, 725, 725, 735, 735, 735, 736, + 737, 738, 733, 733, 733, 739, 740, 741, 735, 735, 735, 742, 737, 738, + 733, 733, 733, 733, 743, 741, 733, 744, 745, 745, 745, 745, 745, 746, + 745, 745, 745, 745, 745, 745, 745, 745, 745, 745, 745, 733, 733, 733, + 747, 748, 733, 733, 733, 733, 733, 733, 733, 733, 733, 733, 733, 749, + 733, 733, 733, 747, 750, 751, 751, 751, 751, 751, 751, 751, 751, 751, + 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, + 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, + 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, + 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, + 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, + 751, 751, 751, 751, 751, 751, 752, 753, 568, 568, 568, 568, 568, 568, + 568, 568, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, + 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 754, + 753, 753, 753, 753, 753, 753, 755, 755, 756, 755, 755, 755, 755, 755, + 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, + 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, + 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, + 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, + 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, + 755, 755, 755, 757, 758, 758, 758, 758, 758, 758, 759, 106, 760, 760, + 760, 760, 760, 761, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, + 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, + 762, 762, 762, 762, 762, 762, 762, 762, 762, 763, 762, 762, 764, 765, + 106, 106, 101, 101, 101, 101, 101, 766, 767, 768, 101, 101, 101, 769, + 770, 770, 770, 770, 770, 770, 770, 770, 771, 772, 773, 106, 64, 64, 774, + 775, 776, 27, 777, 27, 27, 27, 27, 27, 27, 27, 778, 779, 27, 780, 781, + 106, 27, 782, 106, 106, 106, 106, 106, 106, 106, 106, 106, 783, 784, 785, + 786, 786, 787, 788, 789, 790, 791, 791, 791, 791, 791, 791, 792, 106, + 793, 794, 794, 794, 794, 794, 795, 796, 797, 798, 799, 800, 801, 801, + 802, 803, 804, 805, 806, 806, 807, 808, 809, 809, 810, 811, 812, 813, + 364, 364, 364, 814, 815, 816, 816, 816, 816, 816, 817, 818, 819, 820, + 821, 822, 106, 106, 106, 106, 823, 823, 823, 823, 823, 824, 825, 106, + 826, 827, 828, 829, 345, 345, 830, 831, 832, 832, 832, 832, 832, 832, + 833, 834, 835, 106, 106, 836, 837, 838, 839, 106, 840, 840, 840, 106, + 368, 368, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 837, 837, 837, 837, 841, 842, 843, 844, + 845, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 847, 106, 365, 365, 848, 849, 365, 365, 365, 365, 365, 850, 851, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 852, 851, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 852, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 852, + 853, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, + 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, + 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, + 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, + 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, + 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, + 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 855, 856, 856, + 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, + 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, + 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, + 856, 857, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, + 856, 858, 753, 753, 753, 753, 859, 106, 860, 861, 121, 862, 863, 864, + 865, 121, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 866, 867, 868, 106, 869, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 870, 106, 106, 128, 128, 128, 128, + 128, 128, 128, 128, 871, 128, 128, 128, 128, 128, 128, 106, 106, 106, + 106, 106, 128, 872, 873, 873, 874, 875, 501, 106, 876, 877, 878, 879, + 880, 881, 882, 883, 884, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 885, 886, 887, 888, 889, 890, 891, + 891, 892, 893, 894, 894, 895, 896, 897, 898, 897, 897, 897, 897, 899, + 900, 900, 900, 901, 902, 902, 902, 903, 904, 905, 106, 906, 907, 908, + 907, 907, 909, 907, 907, 910, 907, 911, 907, 911, 106, 106, 106, 106, + 907, 907, 907, 907, 907, 907, 907, 907, 907, 907, 907, 907, 907, 907, + 907, 912, 913, 914, 914, 914, 914, 914, 915, 603, 916, 916, 916, 916, + 916, 916, 917, 918, 919, 920, 568, 609, 106, 106, 106, 106, 106, 106, + 603, 603, 603, 603, 603, 921, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 922, 922, 922, 923, 924, 924, + 924, 924, 924, 924, 925, 106, 106, 106, 106, 106, 926, 926, 926, 927, + 928, 106, 929, 929, 930, 931, 106, 106, 106, 106, 106, 106, 932, 932, + 932, 933, 934, 934, 934, 934, 935, 934, 936, 106, 106, 106, 106, 106, + 937, 937, 937, 937, 937, 938, 938, 938, 938, 938, 939, 939, 939, 939, + 939, 939, 940, 940, 940, 941, 942, 943, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 944, 945, 946, 946, 946, 946, 947, 948, 949, 949, + 950, 951, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 952, 952, 953, 954, 955, 955, + 955, 956, 106, 106, 106, 106, 106, 106, 106, 106, 957, 957, 957, 957, + 958, 958, 958, 959, 106, 106, 106, 106, 106, 106, 106, 106, 960, 961, + 962, 963, 964, 964, 965, 966, 967, 106, 968, 969, 970, 970, 970, 971, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 972, 972, 972, 972, 972, 972, 973, 974, 975, 975, 976, 977, + 978, 978, 979, 980, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 981, 981, 981, 981, 981, 981, 981, 981, + 981, 982, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 983, 983, 983, 984, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 985, 986, 986, 986, 986, 986, 986, 987, 988, 989, 990, 991, 992, 993, + 106, 106, 994, 995, 995, 995, 995, 995, 996, 997, 998, 106, 999, 999, + 999, 1000, 1001, 1002, 1003, 1004, 1004, 1004, 1005, 1006, 1007, 1008, + 1009, 106, 106, 106, 106, 106, 106, 106, 1010, 1011, 1011, 1011, 1011, + 1011, 1012, 1013, 1014, 1015, 1016, 1017, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 1018, 1018, 1018, 1018, 1018, 1019, 1020, 106, 1021, 1022, 106, 106, 106, + 106, 106, 106, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, + 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, + 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, + 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, + 1024, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, + 1025, 1025, 1025, 1025, 1026, 106, 1027, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 1028, 1028, 1028, + 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, + 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, + 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1029, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 770, 770, 770, + 770, 770, 770, 770, 770, 770, 770, 770, 770, 770, 770, 770, 770, 770, + 770, 770, 770, 770, 770, 770, 770, 770, 770, 770, 770, 770, 770, 770, + 770, 770, 770, 770, 770, 770, 770, 770, 1030, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, + 1032, 106, 1033, 1034, 1034, 1034, 1034, 1035, 106, 1036, 1037, 1038, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 1039, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, + 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, + 603, 603, 603, 603, 1040, 106, 603, 603, 603, 603, 1041, 1042, 603, 603, + 603, 603, 603, 603, 1043, 1044, 1045, 1046, 1047, 1048, 603, 603, 603, + 1049, 603, 603, 603, 603, 603, 1040, 106, 106, 106, 106, 919, 919, 919, + 919, 919, 919, 919, 919, 1050, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 610, 106, 914, + 914, 1051, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 1052, 1052, 1052, 1053, 1054, 1054, 1055, 1052, + 1052, 1056, 1057, 1054, 1054, 1052, 1052, 1052, 1053, 1054, 1054, 1058, + 1059, 1060, 1056, 1061, 1062, 1054, 1052, 1052, 1052, 1053, 1054, 1054, + 1063, 1064, 1065, 1066, 1054, 1054, 1054, 1067, 1068, 1069, 1070, 1054, + 1054, 1055, 1052, 1052, 1056, 1054, 1054, 1054, 1052, 1052, 1052, 1053, + 1054, 1054, 1055, 1052, 1052, 1056, 1054, 1054, 1054, 1052, 1052, 1052, + 1053, 1054, 1054, 1055, 1052, 1052, 1056, 1054, 1054, 1054, 1052, 1052, + 1052, 1053, 1054, 1054, 1071, 1052, 1052, 1052, 1072, 1054, 1054, 1073, + 1074, 1052, 1052, 1075, 1054, 1054, 1076, 1055, 1052, 1052, 1077, 1054, + 1054, 1078, 1079, 1052, 1052, 1080, 1054, 1054, 1054, 1081, 1052, 1052, + 1052, 1072, 1054, 1054, 1073, 1082, 1083, 1083, 1083, 1083, 1083, 1083, + 1084, 128, 128, 128, 1085, 1086, 1087, 1088, 1089, 1090, 1085, 1091, + 1085, 1087, 1087, 1092, 128, 1093, 128, 1094, 1095, 1093, 128, 1094, 106, + 106, 106, 106, 106, 106, 1096, 106, 568, 568, 568, 568, 568, 609, 568, + 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 609, 106, 568, + 610, 636, 610, 636, 568, 636, 568, 106, 106, 106, 106, 613, 1097, 615, + 615, 615, 1098, 615, 615, 615, 615, 615, 615, 615, 1099, 615, 615, 615, + 615, 615, 1100, 106, 106, 106, 106, 106, 106, 106, 106, 1101, 603, 603, + 603, 1102, 106, 733, 733, 733, 733, 733, 1103, 733, 1104, 1105, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 568, 568, 568, 568, 1106, 106, 1107, 568, 568, + 568, 568, 568, 568, 568, 568, 1108, 568, 568, 609, 106, 568, 568, 568, + 568, 1109, 611, 106, 106, 568, 568, 1106, 106, 568, 568, 568, 568, 568, + 568, 568, 610, 1110, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, + 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 1111, 568, + 568, 568, 568, 568, 568, 568, 1112, 609, 106, 568, 568, 568, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 1113, 568, 568, 568, 568, 568, 568, 568, 568, 1114, 568, 106, + 106, 106, 106, 106, 106, 568, 568, 568, 568, 568, 568, 568, 568, 1112, + 106, 106, 106, 106, 106, 106, 106, 568, 568, 568, 568, 568, 568, 568, + 568, 568, 568, 568, 568, 568, 568, 609, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 751, 751, 751, + 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, + 751, 751, 751, 751, 751, 751, 751, 751, 751, 1115, 753, 753, 753, 753, + 753, 751, 751, 751, 751, 751, 751, 754, 753, 750, 751, 751, 751, 751, + 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, + 751, 751, 751, 751, 751, 751, 751, 751, 752, 753, 753, 753, 753, 753, + 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, + 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, + 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, + 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 856, + 856, 856, 857, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, + 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, + 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, + 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, + 753, 753, 753, 753, 753, 753, 1116, 1117, 106, 106, 106, 1118, 1118, + 1118, 1118, 1118, 1118, 1118, 1118, 1118, 1118, 1118, 1118, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 873, 873, 873, 873, 873, 873, 873, 873, 873, 873, 873, 873, 873, 873, + 873, 873, 873, 873, 873, 873, 873, 873, 873, 873, 873, 873, 873, 873, + 873, 873, 106, 106, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, + 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, + 854, 854, 854, 854, 854, 854, 854, 1119, +}; + +static const unsigned short index2[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 4, 3, 1, 1, 1, 1, 1, 1, 3, 3, 3, 2, + 5, 6, 6, 7, 8, 7, 6, 6, 9, 10, 6, 11, 12, 13, 12, 12, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 12, 6, 15, 16, 15, 6, 6, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 9, 6, 10, 18, 19, 18, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 9, 16, + 10, 16, 1, 1, 1, 1, 1, 1, 3, 1, 1, 21, 22, 8, 8, 23, 8, 24, 22, 25, 26, + 27, 28, 16, 29, 30, 18, 31, 32, 33, 33, 25, 34, 22, 22, 25, 33, 27, 35, + 36, 36, 36, 22, 37, 37, 37, 37, 37, 37, 38, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 38, 37, 37, 37, 37, 37, 37, 39, 38, 37, 37, 37, 37, 37, 38, 40, + 40, 40, 41, 41, 41, 41, 40, 41, 40, 40, 40, 41, 40, 40, 41, 41, 40, 41, + 40, 40, 41, 41, 41, 39, 40, 40, 40, 41, 40, 41, 40, 41, 37, 40, 37, 41, + 37, 41, 37, 41, 37, 41, 37, 41, 37, 41, 37, 41, 37, 40, 37, 40, 37, 41, + 37, 41, 37, 41, 37, 40, 37, 41, 37, 41, 37, 41, 37, 41, 37, 41, 38, 40, + 37, 40, 38, 40, 37, 41, 37, 41, 40, 37, 41, 37, 41, 37, 41, 38, 40, 38, + 40, 37, 40, 37, 41, 37, 40, 40, 38, 40, 37, 40, 37, 41, 37, 41, 38, 40, + 37, 41, 37, 41, 37, 37, 41, 37, 41, 37, 41, 41, 41, 37, 37, 41, 37, 41, + 37, 37, 41, 37, 37, 37, 41, 41, 37, 37, 37, 37, 41, 37, 37, 41, 37, 37, + 37, 41, 41, 41, 37, 37, 41, 37, 37, 41, 37, 41, 37, 41, 37, 37, 41, 37, + 41, 41, 37, 41, 37, 37, 41, 37, 37, 37, 41, 37, 41, 37, 37, 41, 41, 42, + 37, 41, 41, 41, 42, 42, 42, 42, 37, 43, 41, 37, 43, 41, 37, 43, 41, 37, + 40, 37, 40, 37, 40, 37, 40, 37, 40, 37, 40, 37, 40, 37, 40, 41, 37, 41, + 41, 37, 43, 41, 37, 41, 37, 37, 37, 41, 37, 41, 41, 41, 41, 41, 41, 41, + 37, 37, 41, 37, 37, 41, 41, 37, 41, 37, 37, 37, 37, 41, 41, 40, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 41, + 41, 41, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 45, 46, 46, 46, 46, 46, + 46, 46, 47, 47, 25, 47, 45, 48, 45, 48, 48, 48, 45, 48, 45, 45, 49, 46, + 47, 47, 47, 47, 47, 47, 25, 25, 25, 25, 47, 25, 47, 25, 44, 44, 44, 44, + 44, 47, 47, 47, 47, 47, 50, 50, 45, 47, 46, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 53, 53, + 53, 53, 52, 54, 53, 53, 53, 53, 53, 55, 55, 53, 53, 53, 53, 55, 55, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 56, 56, 56, 56, 56, 53, 53, 53, + 53, 51, 51, 51, 51, 51, 51, 51, 51, 57, 51, 53, 53, 53, 51, 51, 51, 53, + 53, 58, 51, 51, 51, 53, 53, 53, 53, 51, 52, 53, 53, 51, 59, 60, 60, 59, + 60, 60, 59, 51, 51, 51, 51, 51, 61, 62, 61, 62, 45, 63, 61, 62, 64, 64, + 65, 62, 62, 62, 66, 64, 64, 64, 64, 64, 63, 47, 61, 66, 61, 61, 61, 64, + 61, 64, 61, 61, 62, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 64, 67, 67, 67, 67, 67, 67, 67, 61, 61, 62, 62, 62, 62, + 62, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 62, 68, 68, 68, 68, 68, 68, 68, 62, 62, 62, 62, 62, 61, 62, 62, 61, 61, + 61, 62, 62, 62, 61, 62, 61, 62, 61, 62, 61, 62, 61, 62, 69, 70, 69, 70, + 69, 70, 69, 70, 69, 70, 69, 70, 69, 70, 62, 62, 62, 62, 61, 62, 71, 61, + 62, 61, 61, 62, 62, 61, 61, 61, 72, 73, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, 73, 73, 73, 74, 74, 74, 74, + 74, 74, 74, 74, 75, 74, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 72, 75, 72, 75, 72, 75, 72, 75, 72, 75, 76, 77, 77, 78, 78, 77, + 79, 79, 72, 75, 72, 75, 72, 75, 72, 72, 75, 72, 75, 72, 75, 72, 75, 72, + 75, 72, 75, 72, 75, 75, 64, 64, 64, 64, 64, 64, 64, 64, 64, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 64, 64, 81, 82, 82, 82, 82, 82, 82, 64, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 64, 84, 85, 64, 64, 64, 64, 86, 64, 87, + 88, 88, 88, 88, 87, 88, 88, 88, 89, 87, 88, 88, 88, 88, 88, 88, 87, 87, + 87, 87, 87, 87, 88, 88, 87, 88, 88, 89, 90, 88, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 100, 101, 102, 103, 104, 105, 106, 107, 108, 106, 88, + 87, 106, 99, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 64, + 64, 64, 64, 64, 109, 109, 109, 106, 106, 64, 64, 64, 110, 110, 110, 110, + 110, 64, 111, 111, 112, 113, 113, 114, 115, 116, 117, 117, 118, 118, 118, + 118, 118, 118, 118, 118, 119, 120, 121, 122, 64, 64, 116, 122, 123, 123, + 123, 123, 123, 123, 123, 123, 124, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 125, 126, 127, 128, 129, 130, 131, 132, 78, 78, 133, 134, + 118, 118, 118, 118, 118, 134, 118, 118, 134, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 113, 136, 136, 116, 123, 123, 137, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 116, 123, 118, 118, 118, + 118, 118, 118, 118, 138, 117, 118, 118, 118, 118, 134, 118, 139, 139, + 118, 118, 117, 134, 118, 118, 134, 123, 123, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 123, 123, 123, 141, 141, 123, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 64, 143, 144, 145, + 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, + 146, 147, 146, 146, 147, 146, 146, 147, 147, 147, 146, 147, 147, 146, + 147, 146, 146, 146, 147, 146, 147, 146, 147, 146, 147, 146, 146, 64, 64, + 144, 144, 144, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 148, 64, 64, 64, 64, 64, 64, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151, 151, 152, 152, 152, 152, 152, 152, 152, 153, 152, 154, + 154, 155, 156, 156, 156, 154, 64, 64, 64, 64, 64, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, + 159, 158, 158, 158, 158, 158, 158, 158, 158, 158, 159, 158, 158, 158, + 159, 158, 158, 158, 158, 158, 64, 64, 160, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160, 64, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 162, 162, 162, 64, 64, 163, 64, 123, 64, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 64, 64, 64, 64, 64, 64, 64, 118, + 118, 134, 118, 118, 134, 118, 118, 118, 134, 134, 134, 164, 165, 166, + 118, 118, 118, 134, 118, 118, 134, 134, 118, 118, 118, 118, 64, 167, 167, + 167, 168, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, + 169, 169, 167, 168, 170, 169, 168, 168, 168, 167, 167, 167, 167, 167, + 167, 167, 167, 168, 168, 168, 168, 171, 168, 168, 169, 78, 133, 172, 172, + 167, 167, 167, 169, 169, 167, 167, 84, 84, 173, 173, 173, 173, 173, 173, + 173, 173, 173, 173, 174, 175, 169, 169, 169, 169, 169, 169, 64, 169, 169, + 169, 169, 169, 169, 169, 64, 176, 177, 177, 64, 178, 178, 178, 178, 178, + 178, 178, 178, 64, 64, 178, 178, 64, 64, 178, 178, 178, 178, 178, 178, + 178, 178, 178, 178, 178, 178, 178, 178, 64, 178, 178, 178, 178, 178, 178, + 178, 64, 178, 64, 64, 64, 178, 178, 178, 178, 64, 64, 179, 178, 177, 177, + 177, 176, 176, 176, 176, 64, 64, 177, 177, 64, 64, 177, 177, 180, 178, + 64, 64, 64, 64, 64, 64, 64, 64, 177, 64, 64, 64, 64, 178, 178, 64, 178, + 178, 178, 176, 176, 64, 64, 181, 181, 181, 181, 181, 181, 181, 181, 181, + 181, 178, 178, 182, 182, 183, 183, 183, 183, 183, 183, 184, 182, 64, 64, + 64, 64, 64, 185, 185, 186, 64, 187, 187, 187, 187, 187, 187, 64, 64, 64, + 64, 187, 187, 64, 64, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + 187, 187, 187, 187, 64, 187, 187, 187, 187, 187, 187, 187, 64, 187, 187, + 64, 187, 187, 64, 187, 187, 64, 64, 188, 64, 186, 186, 186, 185, 185, 64, + 64, 64, 64, 185, 185, 64, 64, 185, 185, 189, 64, 64, 64, 185, 64, 64, 64, + 64, 64, 64, 64, 187, 187, 187, 187, 64, 187, 64, 64, 64, 64, 64, 64, 64, + 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 185, 185, 187, 187, + 187, 185, 64, 64, 64, 191, 191, 192, 64, 193, 193, 193, 193, 193, 193, + 193, 193, 193, 64, 193, 193, 193, 64, 193, 193, 193, 193, 193, 193, 193, + 193, 193, 193, 193, 193, 193, 193, 64, 193, 193, 193, 193, 193, 193, 193, + 64, 193, 193, 64, 193, 193, 193, 193, 193, 64, 64, 194, 193, 192, 192, + 192, 191, 191, 191, 191, 191, 64, 191, 191, 192, 64, 192, 192, 195, 64, + 64, 193, 64, 64, 64, 64, 64, 64, 64, 193, 193, 191, 191, 64, 64, 196, + 196, 196, 196, 196, 196, 196, 196, 196, 196, 197, 198, 64, 64, 64, 64, + 64, 64, 64, 199, 200, 200, 64, 201, 201, 201, 201, 201, 201, 201, 201, + 64, 64, 201, 201, 64, 64, 201, 201, 201, 201, 201, 201, 201, 201, 201, + 201, 201, 201, 201, 201, 64, 201, 201, 201, 201, 201, 201, 201, 64, 201, + 201, 64, 201, 201, 201, 201, 201, 64, 64, 202, 201, 200, 199, 200, 199, + 199, 199, 199, 64, 64, 200, 200, 64, 64, 200, 200, 203, 64, 64, 64, 64, + 64, 64, 64, 64, 199, 200, 64, 64, 64, 64, 201, 201, 64, 201, 201, 201, + 199, 199, 64, 64, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 205, + 201, 206, 206, 206, 206, 206, 206, 64, 64, 207, 208, 64, 208, 208, 208, + 208, 208, 208, 64, 64, 64, 208, 208, 208, 64, 208, 208, 208, 208, 64, 64, + 64, 208, 208, 64, 208, 64, 208, 208, 64, 64, 64, 208, 208, 64, 64, 64, + 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 64, 64, 64, 64, 209, + 209, 207, 209, 209, 64, 64, 64, 209, 209, 209, 64, 209, 209, 209, 210, + 64, 64, 208, 64, 64, 64, 64, 64, 64, 209, 64, 64, 64, 64, 64, 64, 211, + 211, 211, 211, 211, 211, 211, 211, 211, 211, 212, 212, 212, 213, 213, + 213, 213, 213, 213, 214, 213, 64, 64, 64, 64, 64, 64, 215, 215, 215, 64, + 216, 216, 216, 216, 216, 216, 216, 216, 64, 216, 216, 216, 64, 216, 216, + 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, + 216, 216, 64, 216, 216, 216, 216, 216, 64, 64, 64, 216, 217, 217, 217, + 215, 215, 215, 215, 64, 217, 217, 217, 64, 217, 217, 217, 218, 64, 64, + 64, 64, 64, 64, 64, 219, 220, 64, 216, 216, 64, 64, 64, 64, 64, 64, 216, + 216, 217, 217, 64, 64, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, + 222, 222, 222, 222, 222, 222, 222, 223, 64, 64, 224, 224, 64, 225, 225, + 225, 225, 225, 225, 225, 225, 64, 225, 225, 225, 64, 225, 225, 225, 225, + 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 64, + 225, 225, 225, 225, 225, 64, 64, 226, 225, 224, 227, 224, 224, 224, 224, + 224, 64, 227, 224, 224, 64, 224, 224, 228, 229, 64, 64, 64, 64, 64, 64, + 64, 224, 224, 64, 64, 64, 64, 64, 64, 64, 225, 64, 225, 225, 228, 228, + 64, 64, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 64, 225, 225, + 64, 64, 64, 64, 64, 64, 64, 231, 231, 64, 232, 232, 232, 232, 232, 232, + 232, 232, 64, 232, 232, 232, 64, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 64, 64, 232, 231, 231, 231, + 233, 233, 233, 233, 64, 231, 231, 231, 64, 231, 231, 231, 234, 232, 64, + 64, 64, 64, 64, 64, 64, 64, 231, 232, 232, 233, 233, 64, 64, 235, 235, + 235, 235, 235, 235, 235, 235, 235, 235, 236, 236, 236, 236, 236, 236, 64, + 64, 64, 237, 232, 232, 232, 232, 232, 232, 64, 64, 238, 238, 64, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 64, 64, 64, 239, 239, 239, 239, 239, 239, 239, 239, 64, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 64, 239, 64, 64, 64, 64, + 240, 64, 64, 64, 64, 238, 238, 238, 241, 241, 241, 64, 241, 64, 238, 238, + 238, 238, 238, 238, 238, 238, 64, 64, 238, 238, 242, 64, 64, 64, 64, 243, + 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + 243, 244, 243, 243, 244, 244, 244, 244, 245, 245, 246, 64, 64, 64, 64, + 247, 243, 243, 243, 243, 243, 243, 248, 244, 249, 249, 249, 249, 244, + 244, 244, 250, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 250, + 250, 64, 64, 64, 64, 64, 252, 252, 64, 252, 64, 64, 252, 252, 64, 252, + 64, 64, 252, 64, 64, 64, 64, 64, 64, 252, 252, 252, 252, 64, 252, 252, + 252, 252, 252, 252, 252, 64, 252, 252, 252, 64, 252, 64, 252, 64, 64, + 252, 252, 64, 252, 252, 252, 252, 253, 252, 252, 253, 253, 253, 253, 254, + 254, 64, 253, 253, 252, 64, 64, 252, 252, 252, 252, 252, 64, 255, 64, + 256, 256, 256, 256, 253, 253, 64, 64, 257, 257, 257, 257, 257, 257, 257, + 257, 257, 257, 64, 64, 252, 252, 252, 252, 258, 259, 259, 259, 260, 260, + 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 259, + 260, 259, 259, 259, 261, 261, 259, 259, 259, 259, 259, 259, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 263, 263, 263, 263, 263, 263, + 263, 263, 263, 263, 259, 261, 259, 261, 259, 264, 265, 266, 265, 266, + 267, 267, 258, 258, 258, 258, 258, 258, 258, 258, 64, 258, 258, 258, 258, + 258, 258, 258, 258, 258, 258, 258, 258, 64, 64, 64, 64, 268, 269, 270, + 271, 270, 270, 270, 270, 270, 269, 269, 269, 269, 270, 267, 269, 270, + 272, 272, 273, 260, 272, 272, 258, 258, 258, 258, 258, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 64, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 64, 259, 259, 259, 259, 259, 259, 259, 259, + 261, 259, 259, 259, 259, 259, 259, 64, 259, 259, 260, 260, 260, 260, 260, + 274, 274, 274, 274, 260, 260, 64, 64, 64, 64, 64, 275, 275, 275, 275, + 275, 275, 275, 275, 275, 275, 275, 276, 276, 277, 277, 277, 277, 276, + 277, 277, 277, 277, 277, 278, 276, 279, 279, 276, 276, 277, 277, 275, + 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 281, 281, 281, 281, + 281, 281, 275, 275, 275, 275, 275, 275, 276, 276, 277, 277, 275, 275, + 275, 275, 277, 277, 277, 275, 276, 276, 276, 275, 275, 276, 276, 276, + 276, 276, 276, 276, 275, 275, 275, 277, 277, 277, 277, 275, 275, 275, + 275, 275, 277, 276, 276, 277, 277, 276, 276, 276, 276, 276, 276, 282, + 275, 276, 280, 280, 276, 276, 276, 277, 283, 283, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 64, 284, 64, 64, 64, + 64, 64, 284, 64, 64, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, + 285, 84, 286, 285, 285, 285, 287, 287, 287, 287, 287, 287, 287, 287, 288, + 288, 288, 288, 288, 288, 288, 288, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 64, 289, 289, 289, 289, 64, 64, 289, 289, 289, 289, 289, 289, + 289, 64, 289, 289, 289, 64, 64, 290, 290, 290, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, 64, 64, 64, 293, 293, + 293, 293, 293, 293, 293, 293, 293, 293, 64, 64, 64, 64, 64, 64, 294, 294, + 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 64, 64, 64, 295, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 297, 297, 296, 298, 299, 299, 299, 299, + 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, + 300, 301, 64, 64, 64, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, + 302, 84, 84, 84, 303, 303, 303, 64, 64, 64, 64, 64, 64, 64, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 64, 304, 304, 304, + 304, 305, 305, 306, 64, 64, 64, 307, 307, 307, 307, 307, 307, 307, 307, + 307, 307, 308, 308, 309, 84, 84, 64, 310, 310, 310, 310, 310, 310, 310, + 310, 310, 310, 311, 311, 64, 64, 64, 64, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 64, 312, 312, 312, 64, 313, 313, 64, + 64, 64, 64, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 315, 315, 316, 315, 315, 315, 315, 315, 315, 315, 316, 316, 316, 316, + 316, 316, 316, 316, 315, 316, 316, 315, 315, 315, 315, 315, 315, 315, + 315, 315, 317, 315, 318, 318, 318, 319, 318, 318, 318, 320, 314, 321, 64, + 64, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 64, 64, 64, 64, 64, + 64, 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, 64, 64, 64, 64, 64, + 64, 324, 324, 66, 66, 324, 66, 325, 324, 324, 324, 324, 326, 326, 326, + 327, 64, 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 64, 64, 64, + 64, 64, 64, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 330, + 329, 329, 329, 329, 329, 331, 329, 64, 64, 64, 64, 64, 296, 296, 296, + 296, 296, 296, 64, 64, 332, 332, 332, 332, 332, 332, 332, 332, 332, 332, + 332, 332, 332, 64, 64, 64, 333, 333, 333, 334, 334, 334, 334, 333, 333, + 334, 334, 334, 64, 64, 64, 64, 334, 334, 333, 334, 334, 334, 334, 334, + 334, 335, 336, 337, 64, 64, 64, 64, 338, 64, 64, 64, 339, 339, 340, 340, + 340, 340, 340, 340, 340, 340, 340, 340, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, 64, 64, 341, 341, 341, 341, 341, + 64, 64, 64, 342, 342, 342, 342, 342, 342, 342, 342, 342, 342, 342, 342, + 64, 64, 64, 64, 343, 343, 343, 343, 343, 343, 343, 343, 343, 342, 342, + 342, 342, 342, 342, 342, 343, 343, 64, 64, 64, 64, 64, 64, 344, 344, 344, + 344, 344, 344, 344, 344, 344, 344, 345, 64, 64, 64, 346, 346, 347, 347, + 347, 347, 347, 347, 347, 347, 348, 348, 348, 348, 348, 348, 348, 348, + 348, 348, 348, 348, 348, 348, 348, 349, 350, 351, 351, 351, 64, 64, 352, + 352, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, + 354, 355, 354, 355, 355, 355, 355, 355, 355, 355, 64, 356, 354, 355, 354, + 354, 355, 355, 355, 355, 355, 355, 355, 355, 354, 354, 354, 354, 354, + 354, 355, 355, 357, 357, 357, 357, 357, 357, 357, 357, 64, 64, 358, 359, + 359, 359, 359, 359, 359, 359, 359, 359, 359, 64, 64, 64, 64, 64, 64, 360, + 360, 360, 360, 360, 360, 360, 361, 360, 360, 360, 360, 360, 360, 64, 64, + 362, 362, 362, 362, 363, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 365, 363, 362, 362, 362, 362, 362, 363, + 362, 363, 363, 363, 363, 363, 362, 363, 366, 364, 364, 364, 364, 364, + 364, 364, 64, 64, 64, 64, 367, 367, 367, 367, 367, 367, 367, 367, 367, + 367, 368, 368, 368, 368, 368, 368, 368, 369, 369, 369, 369, 369, 369, + 369, 369, 369, 369, 370, 371, 370, 370, 370, 370, 370, 370, 370, 369, + 369, 369, 369, 369, 369, 369, 369, 369, 64, 64, 64, 372, 372, 373, 374, + 374, 374, 374, 374, 374, 374, 374, 374, 374, 374, 374, 374, 374, 373, + 372, 372, 372, 372, 373, 373, 372, 372, 375, 376, 373, 373, 374, 374, + 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, 374, 374, 374, 374, + 374, 374, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, + 378, 378, 379, 380, 381, 381, 380, 380, 380, 381, 380, 381, 381, 381, + 382, 382, 64, 64, 64, 64, 64, 64, 64, 64, 383, 383, 383, 383, 384, 384, + 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 385, 385, 385, 385, + 385, 385, 385, 385, 386, 386, 386, 386, 386, 386, 386, 386, 385, 385, + 386, 387, 64, 64, 64, 388, 388, 388, 388, 388, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 64, 64, 64, 384, 384, 384, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 392, 392, 392, 392, 392, 392, 393, 393, + 394, 394, 394, 394, 394, 394, 394, 394, 78, 78, 78, 84, 395, 133, 133, + 133, 133, 133, 78, 78, 133, 133, 133, 133, 78, 396, 395, 395, 395, 395, + 395, 395, 395, 397, 397, 397, 397, 133, 397, 397, 397, 397, 396, 396, 78, + 397, 397, 64, 41, 41, 41, 41, 41, 41, 62, 62, 62, 62, 62, 75, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 65, 65, 65, 65, 65, 44, 44, 44, 44, 65, 65, 65, + 65, 65, 41, 41, 41, 41, 41, 398, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 65, 78, 78, 133, 78, 78, + 78, 78, 78, 78, 78, 133, 78, 78, 399, 400, 133, 401, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 64, + 64, 64, 64, 64, 402, 133, 78, 133, 37, 41, 37, 41, 37, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 37, 41, 62, 62, 62, 62, 62, 62, 62, 62, 61, 61, 61, + 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 64, 64, 61, 61, 61, 61, 61, + 61, 64, 64, 64, 61, 64, 61, 64, 61, 64, 61, 403, 403, 403, 403, 403, 403, + 403, 403, 62, 62, 62, 62, 62, 64, 62, 62, 61, 61, 61, 61, 403, 63, 62, + 63, 63, 63, 62, 62, 62, 64, 62, 62, 61, 61, 61, 61, 403, 63, 63, 63, 62, + 62, 62, 62, 64, 64, 62, 62, 61, 61, 61, 61, 64, 63, 63, 63, 61, 61, 61, + 61, 61, 63, 63, 63, 64, 64, 62, 62, 62, 64, 62, 62, 61, 61, 61, 61, 403, + 63, 63, 64, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 405, + 406, 406, 407, 408, 409, 410, 410, 409, 409, 409, 22, 66, 411, 412, 413, + 414, 411, 412, 413, 414, 22, 22, 22, 66, 22, 22, 22, 22, 415, 416, 417, + 418, 419, 420, 421, 21, 422, 423, 422, 422, 423, 22, 66, 66, 66, 28, 35, + 22, 66, 66, 22, 424, 424, 66, 66, 66, 425, 426, 427, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 428, 66, 424, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 404, 405, 405, 405, 405, 405, 64, 64, 64, 64, 64, 405, 405, 405, 405, + 405, 405, 429, 44, 64, 64, 33, 429, 429, 429, 429, 429, 430, 430, 428, + 426, 427, 431, 429, 33, 33, 33, 33, 429, 429, 429, 429, 429, 430, 430, + 428, 426, 427, 64, 44, 44, 44, 44, 44, 64, 64, 64, 247, 247, 247, 247, + 247, 247, 247, 247, 247, 432, 247, 247, 23, 247, 247, 247, 247, 247, 247, + 64, 64, 64, 64, 64, 78, 78, 395, 395, 78, 78, 78, 78, 395, 395, 395, 78, + 78, 433, 433, 433, 433, 78, 433, 433, 433, 395, 395, 78, 133, 78, 395, + 395, 133, 133, 133, 133, 78, 64, 64, 64, 64, 64, 64, 64, 26, 26, 434, 30, + 26, 30, 26, 434, 26, 30, 34, 434, 434, 434, 34, 34, 434, 434, 434, 435, + 26, 434, 30, 26, 428, 434, 434, 434, 434, 434, 26, 26, 26, 30, 30, 26, + 434, 26, 67, 26, 434, 26, 37, 38, 434, 434, 436, 34, 434, 434, 37, 434, + 34, 397, 397, 397, 397, 34, 26, 26, 34, 34, 434, 434, 437, 428, 428, 428, + 428, 434, 34, 34, 34, 34, 26, 428, 26, 26, 41, 274, 438, 438, 438, 36, + 36, 438, 438, 438, 438, 438, 438, 36, 36, 36, 36, 438, 439, 439, 439, + 439, 439, 439, 439, 439, 439, 439, 439, 439, 440, 440, 440, 440, 439, + 439, 440, 440, 440, 440, 440, 440, 440, 440, 440, 37, 41, 440, 440, 440, + 440, 36, 64, 64, 64, 64, 64, 64, 39, 39, 39, 39, 39, 30, 30, 30, 30, 30, + 428, 428, 26, 26, 26, 26, 428, 26, 26, 428, 26, 26, 428, 26, 26, 26, 26, + 26, 26, 26, 428, 26, 26, 26, 26, 26, 26, 26, 26, 26, 30, 30, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 428, 428, 26, 26, 39, 26, 39, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 30, 26, 26, 26, 26, 428, 428, 428, 428, + 428, 428, 428, 428, 428, 428, 428, 428, 39, 437, 441, 441, 437, 428, 428, + 39, 441, 437, 437, 441, 437, 437, 428, 39, 428, 441, 430, 442, 428, 441, + 437, 428, 428, 428, 441, 437, 437, 441, 39, 441, 441, 437, 437, 39, 437, + 39, 437, 39, 39, 39, 39, 441, 441, 437, 441, 437, 437, 437, 437, 437, 39, + 39, 39, 39, 428, 437, 428, 437, 441, 441, 437, 437, 437, 437, 437, 437, + 437, 437, 437, 437, 441, 437, 437, 437, 441, 428, 428, 428, 428, 428, + 441, 437, 437, 437, 428, 428, 428, 428, 428, 428, 428, 428, 428, 437, + 441, 39, 437, 428, 441, 441, 441, 441, 437, 437, 441, 441, 428, 428, 441, + 441, 437, 437, 441, 441, 437, 437, 441, 441, 437, 437, 437, 437, 437, + 428, 428, 437, 437, 437, 437, 428, 428, 39, 428, 428, 437, 39, 428, 428, + 428, 428, 428, 428, 428, 428, 437, 437, 428, 39, 437, 437, 437, 428, 428, + 428, 428, 428, 437, 441, 428, 437, 437, 437, 437, 437, 428, 428, 437, + 437, 428, 428, 428, 428, 437, 437, 437, 437, 437, 437, 437, 437, 428, + 428, 437, 437, 437, 437, 26, 26, 26, 26, 26, 26, 30, 26, 26, 26, 26, 26, + 437, 437, 26, 26, 26, 26, 26, 26, 26, 443, 444, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, + 274, 274, 274, 26, 428, 26, 26, 26, 26, 26, 26, 26, 26, 274, 26, 26, 26, + 26, 26, 428, 428, 428, 428, 428, 428, 428, 428, 428, 26, 26, 26, 26, 428, + 428, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 64, 64, 64, 64, 26, 26, 26, + 26, 26, 26, 26, 64, 26, 26, 26, 64, 64, 64, 64, 64, 36, 36, 36, 36, 36, + 36, 36, 36, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 445, 445, + 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 438, 36, 36, + 36, 36, 36, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 26, 26, 26, + 26, 26, 26, 30, 30, 30, 30, 26, 26, 30, 30, 26, 30, 30, 30, 30, 30, 26, + 26, 30, 30, 26, 26, 30, 39, 26, 26, 26, 26, 30, 30, 26, 26, 30, 39, 26, + 26, 26, 26, 30, 30, 30, 26, 26, 30, 26, 26, 30, 30, 26, 26, 26, 26, 26, + 30, 30, 26, 26, 30, 26, 26, 26, 26, 30, 30, 26, 26, 26, 26, 30, 26, 30, + 26, 30, 26, 30, 26, 26, 26, 26, 26, 30, 30, 26, 30, 30, 30, 26, 30, 30, + 30, 30, 26, 30, 30, 26, 39, 26, 26, 26, 26, 26, 26, 30, 30, 26, 26, 26, + 26, 274, 26, 26, 26, 26, 26, 26, 26, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 26, 30, 30, 30, 26, 30, 26, 26, 26, 26, 64, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 30, 26, 26, 426, 427, 426, 427, 426, 427, 426, + 427, 426, 427, 426, 427, 426, 427, 36, 36, 438, 438, 438, 438, 438, 438, + 438, 438, 438, 438, 438, 438, 26, 26, 26, 26, 437, 428, 428, 437, 437, + 426, 427, 428, 437, 437, 428, 437, 437, 437, 428, 428, 428, 428, 428, + 437, 437, 437, 437, 428, 428, 428, 428, 428, 437, 437, 437, 428, 428, + 428, 437, 437, 437, 437, 9, 10, 9, 10, 9, 10, 9, 10, 426, 427, 446, 446, + 446, 446, 446, 446, 446, 446, 428, 428, 428, 426, 427, 9, 10, 426, 427, + 426, 427, 426, 427, 426, 427, 426, 427, 428, 428, 437, 437, 437, 437, + 437, 437, 428, 428, 428, 428, 428, 428, 428, 428, 437, 428, 428, 428, + 428, 437, 437, 437, 437, 437, 428, 437, 437, 428, 428, 426, 427, 426, + 427, 437, 428, 428, 428, 428, 437, 428, 437, 437, 437, 428, 428, 437, + 437, 428, 428, 428, 428, 428, 428, 428, 428, 428, 428, 437, 437, 437, + 437, 437, 437, 428, 428, 426, 427, 428, 428, 428, 428, 437, 437, 437, + 437, 437, 437, 437, 437, 437, 437, 437, 428, 437, 437, 437, 437, 428, + 428, 437, 428, 437, 428, 428, 437, 428, 437, 437, 437, 437, 428, 428, + 428, 428, 428, 437, 437, 428, 428, 428, 428, 437, 437, 437, 437, 428, + 437, 437, 428, 428, 437, 437, 428, 428, 428, 428, 437, 437, 437, 437, + 437, 437, 437, 437, 437, 437, 437, 428, 428, 437, 437, 437, 437, 437, + 437, 437, 437, 428, 437, 437, 437, 437, 437, 437, 437, 437, 428, 428, + 428, 428, 428, 437, 428, 437, 428, 428, 428, 437, 437, 437, 437, 437, + 428, 428, 428, 428, 437, 428, 428, 428, 437, 437, 437, 437, 437, 428, + 437, 428, 428, 428, 428, 428, 428, 428, 26, 26, 428, 428, 428, 428, 428, + 428, 64, 64, 64, 26, 26, 26, 26, 26, 30, 30, 30, 30, 30, 64, 64, 64, 64, + 64, 64, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, + 447, 447, 64, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, + 448, 448, 448, 64, 37, 41, 37, 37, 37, 41, 41, 37, 41, 37, 41, 37, 41, + 37, 37, 37, 37, 41, 37, 41, 41, 37, 41, 41, 41, 41, 41, 41, 44, 44, 37, + 37, 69, 70, 69, 70, 70, 449, 449, 449, 449, 449, 449, 69, 70, 69, 70, + 450, 450, 450, 69, 70, 64, 64, 64, 64, 64, 451, 451, 451, 451, 452, 451, + 451, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, + 453, 64, 453, 64, 64, 64, 64, 64, 453, 64, 64, 454, 454, 454, 454, 454, + 454, 454, 454, 64, 64, 64, 64, 64, 64, 64, 455, 456, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 457, 77, 77, 77, 77, 77, 77, 77, 77, + 66, 66, 28, 35, 28, 35, 66, 66, 66, 28, 35, 66, 28, 35, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 410, 66, 66, 410, 66, 28, 35, 66, 66, 28, 35, 426, + 427, 426, 427, 426, 427, 426, 427, 66, 66, 66, 66, 66, 45, 66, 66, 410, + 410, 64, 64, 64, 64, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, + 64, 458, 458, 458, 458, 458, 458, 458, 458, 458, 64, 64, 64, 64, 458, + 458, 458, 458, 458, 458, 64, 64, 459, 459, 459, 459, 459, 459, 459, 459, + 459, 459, 459, 459, 64, 64, 64, 64, 460, 461, 461, 461, 459, 462, 463, + 464, 443, 444, 443, 444, 443, 444, 443, 444, 443, 444, 459, 459, 443, + 444, 443, 444, 443, 444, 443, 444, 465, 466, 467, 467, 459, 464, 464, + 464, 464, 464, 464, 464, 464, 464, 468, 469, 470, 471, 472, 472, 465, + 473, 473, 473, 473, 473, 459, 459, 464, 464, 464, 462, 463, 461, 459, 26, + 64, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, + 474, 474, 474, 474, 474, 474, 474, 474, 64, 64, 475, 475, 476, 476, 477, + 477, 474, 465, 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, + 478, 478, 478, 478, 478, 478, 478, 461, 473, 479, 479, 478, 64, 64, 64, + 64, 64, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, + 480, 480, 480, 480, 64, 64, 64, 287, 287, 287, 287, 287, 287, 287, 287, + 287, 287, 287, 287, 287, 287, 64, 481, 481, 482, 482, 482, 482, 481, 481, + 481, 481, 481, 481, 481, 481, 481, 481, 480, 480, 480, 64, 64, 64, 64, + 64, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 484, + 484, 64, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 481, 481, 481, + 481, 481, 481, 485, 485, 485, 485, 485, 485, 485, 485, 459, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 483, + 483, 483, 483, 484, 484, 484, 481, 481, 486, 486, 486, 486, 486, 486, + 486, 481, 481, 481, 481, 459, 459, 459, 459, 487, 487, 487, 487, 487, + 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 64, 481, 481, 481, 481, + 481, 481, 481, 459, 459, 459, 459, 481, 481, 481, 481, 481, 481, 481, + 481, 481, 481, 481, 459, 459, 488, 489, 489, 489, 489, 489, 489, 489, + 489, 489, 489, 489, 489, 489, 489, 489, 489, 489, 489, 489, 489, 488, + 490, 490, 490, 490, 490, 490, 490, 490, 490, 490, 489, 489, 489, 489, + 488, 490, 490, 490, 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, + 491, 491, 491, 492, 491, 491, 491, 491, 491, 491, 491, 64, 64, 64, 493, + 493, 493, 493, 493, 493, 493, 493, 493, 493, 493, 493, 493, 493, 493, 64, + 494, 494, 494, 494, 494, 494, 494, 494, 495, 495, 495, 495, 495, 495, + 496, 496, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, + 498, 499, 499, 499, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, + 497, 497, 64, 64, 64, 64, 72, 75, 72, 75, 72, 75, 501, 77, 79, 79, 79, + 502, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 502, 503, 64, 64, 64, 64, + 64, 64, 64, 77, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 506, + 506, 507, 507, 507, 507, 507, 507, 47, 47, 47, 47, 47, 47, 47, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 47, 47, 37, 41, 37, 41, 37, 41, 41, 41, 37, + 41, 37, 41, 37, 41, 44, 41, 41, 41, 41, 41, 41, 41, 41, 37, 41, 37, 41, + 37, 37, 41, 45, 508, 508, 37, 41, 37, 41, 64, 37, 41, 37, 41, 64, 64, 64, + 64, 37, 41, 37, 64, 64, 64, 64, 64, 44, 44, 41, 42, 42, 42, 42, 42, 509, + 509, 510, 509, 509, 509, 511, 509, 509, 509, 509, 510, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 512, 512, + 510, 510, 512, 513, 513, 513, 513, 64, 64, 64, 64, 514, 514, 514, 514, + 514, 514, 274, 274, 247, 436, 64, 64, 64, 64, 64, 64, 515, 515, 515, 515, + 515, 515, 515, 515, 515, 515, 515, 515, 516, 516, 516, 516, 517, 517, + 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, + 518, 518, 518, 518, 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 519, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 520, 520, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 64, 64, 64, + 64, 64, 64, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 169, 169, + 169, 169, 169, 169, 174, 174, 174, 169, 64, 64, 64, 64, 522, 522, 522, + 522, 522, 522, 522, 522, 522, 522, 523, 523, 523, 523, 523, 523, 523, + 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 524, + 524, 524, 524, 524, 525, 525, 525, 526, 526, 527, 527, 527, 527, 527, + 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 528, 528, 528, 528, + 528, 528, 528, 528, 528, 528, 528, 529, 530, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 531, 287, 287, 287, 287, 287, 64, 64, 64, 532, 532, 532, + 533, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, + 534, 534, 535, 533, 533, 532, 532, 532, 532, 533, 533, 532, 533, 533, + 533, 536, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, + 537, 64, 538, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 64, 64, + 64, 64, 537, 537, 540, 540, 540, 540, 540, 540, 540, 540, 540, 541, 541, + 541, 541, 541, 541, 542, 542, 541, 541, 542, 542, 541, 541, 64, 540, 540, + 540, 541, 540, 540, 540, 540, 540, 540, 540, 540, 541, 542, 64, 64, 543, + 543, 543, 543, 543, 543, 543, 543, 543, 543, 64, 64, 544, 544, 544, 544, + 545, 275, 275, 275, 275, 275, 275, 283, 283, 283, 275, 276, 64, 64, 64, + 64, 546, 546, 546, 546, 546, 546, 546, 546, 547, 546, 547, 547, 548, 546, + 546, 547, 547, 546, 546, 546, 546, 546, 547, 547, 546, 547, 546, 64, 64, + 64, 64, 64, 64, 64, 64, 546, 546, 549, 550, 550, 551, 551, 551, 551, 551, + 551, 551, 551, 551, 551, 551, 552, 553, 553, 552, 552, 554, 554, 551, + 555, 555, 552, 556, 64, 64, 289, 289, 289, 289, 289, 289, 64, 551, 551, + 551, 552, 552, 553, 552, 552, 553, 552, 552, 554, 552, 556, 64, 64, 557, + 557, 557, 557, 557, 557, 557, 557, 557, 557, 64, 64, 64, 64, 64, 64, 287, + 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 287, 64, 64, 64, 64, 288, 288, 288, 288, 288, 288, + 288, 64, 64, 64, 64, 288, 288, 288, 288, 288, 288, 288, 288, 288, 64, 64, + 64, 64, 559, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 559, + 560, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, + 561, 561, 561, 561, 561, 561, 561, 561, 561, 560, 488, 488, 488, 488, + 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, 490, 490, 488, 488, + 490, 490, 490, 490, 490, 490, 41, 41, 41, 41, 41, 41, 41, 64, 64, 64, 64, + 83, 83, 83, 83, 83, 64, 64, 64, 64, 64, 109, 562, 109, 109, 563, 109, + 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 64, 109, 109, + 109, 109, 109, 64, 109, 64, 109, 109, 64, 109, 109, 64, 109, 109, 123, + 123, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, + 564, 564, 564, 64, 64, 64, 64, 64, 64, 64, 64, 64, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 413, 565, 64, 64, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 114, 26, 64, 64, 58, 58, 58, 58, 58, 58, + 58, 58, 461, 461, 461, 461, 461, 461, 461, 466, 467, 461, 64, 64, 64, 64, + 64, 64, 461, 465, 465, 566, 566, 466, 467, 466, 467, 466, 467, 466, 467, + 466, 467, 466, 467, 466, 467, 466, 467, 461, 461, 466, 467, 461, 461, + 461, 461, 566, 566, 566, 567, 461, 567, 64, 461, 567, 461, 461, 465, 443, + 444, 443, 444, 443, 444, 568, 461, 461, 569, 570, 571, 571, 572, 64, 461, + 573, 568, 461, 64, 64, 64, 64, 123, 123, 123, 123, 123, 64, 123, 123, + 123, 123, 123, 123, 123, 64, 64, 405, 64, 574, 574, 575, 576, 575, 574, + 574, 577, 578, 574, 579, 580, 581, 580, 580, 582, 582, 582, 582, 582, + 582, 582, 582, 582, 582, 580, 574, 583, 584, 583, 574, 574, 585, 585, + 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, + 585, 585, 577, 574, 578, 586, 587, 586, 588, 588, 588, 588, 588, 588, + 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 577, 584, + 578, 584, 577, 578, 589, 590, 591, 589, 589, 592, 592, 592, 592, 592, + 592, 592, 592, 592, 592, 593, 592, 592, 592, 592, 592, 592, 592, 592, + 592, 592, 592, 592, 592, 593, 593, 594, 594, 594, 594, 594, 594, 594, + 594, 594, 594, 594, 594, 594, 594, 594, 64, 64, 64, 594, 594, 594, 594, + 594, 594, 64, 64, 594, 594, 594, 64, 64, 64, 576, 576, 584, 586, 595, + 576, 576, 64, 596, 597, 597, 597, 597, 596, 596, 64, 64, 598, 598, 598, + 26, 30, 64, 64, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, + 599, 64, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 64, 599, 599, + 599, 64, 599, 599, 64, 599, 599, 599, 599, 599, 599, 599, 64, 64, 599, + 599, 599, 64, 64, 64, 64, 64, 84, 66, 84, 64, 64, 64, 64, 514, 514, 514, + 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 64, 64, 64, 274, 600, + 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 601, 601, + 601, 601, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, + 602, 602, 602, 602, 602, 601, 64, 64, 64, 64, 64, 274, 274, 274, 274, + 274, 133, 64, 64, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, + 603, 603, 64, 64, 64, 604, 604, 604, 604, 604, 604, 604, 604, 604, 64, + 64, 64, 64, 64, 64, 64, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, + 605, 605, 605, 605, 605, 64, 606, 606, 606, 606, 64, 64, 64, 64, 607, + 607, 607, 607, 607, 607, 607, 607, 607, 608, 607, 607, 607, 607, 607, + 607, 607, 607, 608, 64, 64, 64, 64, 64, 609, 609, 609, 609, 609, 609, + 609, 609, 609, 609, 609, 609, 609, 609, 64, 610, 611, 611, 611, 611, 611, + 611, 611, 611, 611, 611, 611, 611, 64, 64, 64, 64, 612, 613, 613, 613, + 613, 613, 64, 64, 614, 614, 614, 614, 614, 614, 614, 614, 615, 615, 615, + 615, 615, 615, 615, 615, 616, 616, 616, 616, 616, 616, 616, 616, 617, + 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 64, 64, + 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 64, 64, 64, 64, 64, 64, + 619, 619, 619, 619, 619, 619, 64, 64, 619, 64, 619, 619, 619, 619, 619, + 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, + 619, 64, 619, 619, 64, 64, 64, 619, 64, 64, 619, 620, 620, 620, 620, 620, + 620, 620, 620, 620, 620, 620, 620, 620, 620, 64, 621, 622, 622, 622, 622, + 622, 622, 622, 622, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, + 623, 623, 623, 623, 624, 624, 624, 624, 624, 624, 64, 64, 64, 625, 626, + 626, 626, 626, 626, 626, 626, 626, 626, 626, 64, 64, 64, 64, 64, 627, + 628, 628, 628, 628, 628, 628, 628, 628, 629, 629, 629, 629, 629, 629, + 629, 629, 64, 64, 64, 64, 64, 64, 629, 629, 630, 631, 631, 631, 64, 631, + 631, 64, 64, 64, 64, 64, 631, 632, 631, 633, 630, 630, 630, 630, 64, 630, + 630, 630, 64, 630, 630, 630, 630, 630, 630, 630, 630, 630, 630, 630, 630, + 630, 630, 630, 630, 630, 630, 630, 64, 64, 64, 64, 633, 634, 632, 64, 64, + 64, 64, 635, 636, 636, 636, 636, 636, 636, 636, 636, 637, 637, 637, 637, + 637, 637, 637, 637, 637, 64, 64, 64, 64, 64, 64, 64, 638, 638, 638, 638, + 638, 638, 638, 638, 638, 638, 638, 638, 638, 639, 639, 640, 641, 641, + 641, 641, 641, 641, 641, 641, 641, 641, 641, 641, 641, 641, 64, 64, 64, + 642, 642, 642, 642, 642, 642, 642, 643, 643, 643, 643, 643, 643, 643, + 643, 643, 643, 643, 643, 643, 643, 64, 64, 644, 644, 644, 644, 644, 644, + 644, 644, 645, 645, 645, 645, 645, 645, 645, 645, 645, 645, 645, 64, 64, + 64, 64, 64, 646, 646, 646, 646, 646, 646, 646, 646, 647, 647, 647, 647, + 647, 647, 647, 647, 647, 64, 64, 64, 64, 64, 64, 64, 648, 648, 648, 648, + 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 64, 649, 650, 649, + 651, 651, 651, 651, 651, 651, 651, 651, 651, 651, 651, 651, 651, 650, + 650, 650, 650, 650, 650, 650, 650, 650, 650, 650, 650, 650, 650, 652, + 653, 653, 653, 653, 653, 653, 653, 64, 64, 64, 64, 654, 654, 654, 654, + 654, 654, 654, 654, 654, 654, 654, 654, 654, 654, 654, 654, 654, 654, + 654, 654, 655, 655, 655, 655, 655, 655, 655, 655, 655, 655, 656, 656, + 657, 658, 658, 658, 658, 658, 658, 658, 658, 658, 658, 658, 658, 658, + 657, 657, 657, 656, 656, 656, 656, 657, 657, 659, 660, 661, 661, 662, + 661, 661, 661, 661, 64, 64, 64, 64, 64, 64, 663, 663, 663, 663, 663, 663, + 663, 663, 663, 64, 64, 64, 64, 64, 64, 64, 664, 664, 664, 664, 664, 664, + 664, 664, 664, 664, 64, 64, 64, 64, 64, 64, 665, 665, 665, 666, 666, 666, + 666, 666, 666, 666, 666, 666, 666, 666, 666, 666, 666, 666, 666, 666, + 666, 666, 666, 667, 667, 667, 667, 667, 668, 667, 667, 667, 667, 667, + 667, 669, 669, 64, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 671, + 671, 671, 671, 64, 64, 64, 64, 672, 672, 673, 674, 674, 674, 674, 674, + 674, 674, 674, 674, 674, 674, 674, 674, 674, 674, 674, 673, 673, 673, + 672, 672, 672, 672, 672, 672, 672, 672, 672, 673, 675, 674, 674, 674, + 674, 676, 676, 676, 676, 64, 64, 64, 64, 64, 64, 64, 677, 677, 677, 677, + 677, 677, 677, 677, 677, 677, 64, 64, 64, 64, 64, 64, 678, 678, 678, 678, + 678, 678, 678, 678, 678, 678, 678, 679, 680, 679, 680, 680, 679, 679, + 679, 679, 679, 679, 681, 682, 683, 683, 683, 683, 683, 683, 683, 683, + 683, 683, 64, 64, 64, 64, 64, 64, 684, 684, 684, 684, 684, 684, 684, 684, + 684, 684, 684, 684, 684, 684, 684, 64, 685, 685, 685, 685, 685, 685, 685, + 685, 685, 685, 685, 64, 64, 64, 64, 64, 686, 686, 686, 686, 64, 64, 64, + 64, 687, 687, 687, 687, 687, 687, 687, 687, 687, 687, 687, 687, 687, 687, + 687, 64, 504, 64, 64, 64, 64, 64, 64, 64, 688, 688, 688, 688, 688, 688, + 688, 688, 688, 688, 688, 688, 688, 64, 64, 64, 688, 689, 689, 689, 689, + 689, 689, 689, 689, 689, 689, 689, 689, 689, 689, 689, 689, 689, 689, + 689, 689, 689, 689, 64, 64, 64, 64, 64, 64, 64, 64, 690, 690, 690, 690, + 691, 691, 691, 691, 691, 691, 691, 691, 691, 691, 691, 691, 691, 478, + 474, 64, 64, 64, 64, 64, 64, 274, 274, 274, 274, 274, 274, 64, 64, 274, + 274, 274, 274, 274, 274, 274, 64, 64, 274, 274, 274, 274, 274, 274, 274, + 274, 274, 274, 274, 274, 692, 692, 395, 395, 395, 274, 274, 274, 693, + 692, 692, 692, 692, 692, 405, 405, 405, 405, 405, 405, 405, 405, 133, + 133, 133, 133, 133, 133, 133, 133, 274, 274, 78, 78, 78, 78, 78, 133, + 133, 274, 274, 274, 274, 274, 274, 78, 78, 78, 78, 274, 274, 602, 602, + 694, 694, 694, 602, 64, 64, 514, 514, 64, 64, 64, 64, 64, 64, 434, 434, + 434, 434, 434, 434, 434, 434, 434, 434, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 434, 434, 434, 434, 434, 434, + 434, 434, 434, 434, 34, 34, 34, 34, 34, 34, 34, 64, 34, 34, 34, 34, 34, + 34, 434, 64, 434, 434, 64, 64, 434, 64, 64, 434, 434, 64, 64, 434, 434, + 434, 434, 64, 434, 434, 34, 34, 64, 34, 64, 34, 34, 34, 34, 34, 34, 34, + 64, 34, 34, 34, 34, 34, 34, 34, 434, 434, 64, 434, 434, 434, 434, 64, 64, + 434, 434, 434, 434, 434, 434, 434, 434, 64, 434, 434, 434, 434, 434, 434, + 434, 64, 34, 34, 434, 434, 64, 434, 434, 434, 434, 64, 434, 434, 434, + 434, 434, 64, 434, 64, 64, 64, 434, 434, 434, 434, 434, 434, 434, 64, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 64, 64, 434, 695, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 437, 34, 34, 34, 34, 34, 34, 434, 434, 434, 434, + 434, 434, 434, 434, 434, 695, 34, 34, 34, 34, 34, 34, 34, 34, 34, 437, + 34, 34, 434, 434, 434, 434, 434, 695, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 437, 34, 34, 34, 34, 34, 34, 434, 434, 434, 434, 434, 434, 434, 434, 434, + 695, 34, 437, 34, 34, 34, 34, 34, 34, 34, 34, 434, 34, 64, 64, 696, 696, + 696, 696, 696, 696, 696, 696, 696, 696, 123, 123, 123, 123, 64, 123, 123, + 123, 64, 123, 123, 64, 123, 64, 64, 123, 64, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 64, 123, 123, 123, 123, 64, 123, 64, 123, 64, + 64, 64, 64, 64, 64, 123, 64, 64, 64, 64, 123, 64, 123, 64, 123, 64, 123, + 123, 123, 64, 123, 64, 123, 64, 123, 64, 123, 64, 123, 123, 123, 123, 64, + 123, 64, 123, 123, 64, 123, 123, 123, 123, 123, 123, 123, 123, 123, 64, + 64, 64, 64, 64, 123, 123, 123, 64, 123, 123, 123, 111, 111, 64, 64, 64, + 64, 64, 64, 33, 33, 33, 64, 64, 64, 64, 64, 445, 445, 445, 445, 445, 445, + 274, 64, 445, 445, 26, 26, 64, 64, 64, 64, 445, 445, 445, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 274, 274, 697, 481, 481, 64, 64, 64, 64, 64, + 481, 481, 481, 64, 64, 64, 64, 64, 481, 64, 64, 64, 64, 64, 64, 64, 481, + 481, 64, 64, 64, 64, 64, 64, 26, 64, 64, 64, 64, 64, 64, 64, 26, 26, 26, + 26, 26, 26, 64, 26, 26, 26, 26, 26, 26, 64, 64, 64, 26, 26, 26, 26, 26, + 64, 26, 26, 26, 64, 26, 26, 26, 26, 26, 26, 64, 26, 26, 26, 26, 64, 64, + 64, 26, 26, 26, 26, 26, 26, 64, 64, 64, 64, 64, 26, 26, 26, 26, 26, 26, + 64, 64, 64, 64, 26, 26, 26, 489, 489, 489, 489, 489, 489, 488, 490, 490, + 490, 490, 490, 490, 490, 64, 64, 64, 405, 64, 64, 64, 64, 64, 64, 405, + 405, 405, 405, 405, 405, 405, 405, 561, 561, 561, 561, 561, 560, 64, 64, +}; + +/* decomposition data */ +static const unsigned short decomp_data[] = { + 0, 257, 32, 514, 32, 776, 259, 97, 514, 32, 772, 259, 50, 259, 51, 514, + 32, 769, 258, 956, 514, 32, 807, 259, 49, 259, 111, 772, 49, 8260, 52, + 772, 49, 8260, 50, 772, 51, 8260, 52, 512, 65, 768, 512, 65, 769, 512, + 65, 770, 512, 65, 771, 512, 65, 776, 512, 65, 778, 512, 67, 807, 512, 69, + 768, 512, 69, 769, 512, 69, 770, 512, 69, 776, 512, 73, 768, 512, 73, + 769, 512, 73, 770, 512, 73, 776, 512, 78, 771, 512, 79, 768, 512, 79, + 769, 512, 79, 770, 512, 79, 771, 512, 79, 776, 512, 85, 768, 512, 85, + 769, 512, 85, 770, 512, 85, 776, 512, 89, 769, 512, 97, 768, 512, 97, + 769, 512, 97, 770, 512, 97, 771, 512, 97, 776, 512, 97, 778, 512, 99, + 807, 512, 101, 768, 512, 101, 769, 512, 101, 770, 512, 101, 776, 512, + 105, 768, 512, 105, 769, 512, 105, 770, 512, 105, 776, 512, 110, 771, + 512, 111, 768, 512, 111, 769, 512, 111, 770, 512, 111, 771, 512, 111, + 776, 512, 117, 768, 512, 117, 769, 512, 117, 770, 512, 117, 776, 512, + 121, 769, 512, 121, 776, 512, 65, 772, 512, 97, 772, 512, 65, 774, 512, + 97, 774, 512, 65, 808, 512, 97, 808, 512, 67, 769, 512, 99, 769, 512, 67, + 770, 512, 99, 770, 512, 67, 775, 512, 99, 775, 512, 67, 780, 512, 99, + 780, 512, 68, 780, 512, 100, 780, 512, 69, 772, 512, 101, 772, 512, 69, + 774, 512, 101, 774, 512, 69, 775, 512, 101, 775, 512, 69, 808, 512, 101, + 808, 512, 69, 780, 512, 101, 780, 512, 71, 770, 512, 103, 770, 512, 71, + 774, 512, 103, 774, 512, 71, 775, 512, 103, 775, 512, 71, 807, 512, 103, + 807, 512, 72, 770, 512, 104, 770, 512, 73, 771, 512, 105, 771, 512, 73, + 772, 512, 105, 772, 512, 73, 774, 512, 105, 774, 512, 73, 808, 512, 105, + 808, 512, 73, 775, 514, 73, 74, 514, 105, 106, 512, 74, 770, 512, 106, + 770, 512, 75, 807, 512, 107, 807, 512, 76, 769, 512, 108, 769, 512, 76, + 807, 512, 108, 807, 512, 76, 780, 512, 108, 780, 514, 76, 183, 514, 108, + 183, 512, 78, 769, 512, 110, 769, 512, 78, 807, 512, 110, 807, 512, 78, + 780, 512, 110, 780, 514, 700, 110, 512, 79, 772, 512, 111, 772, 512, 79, + 774, 512, 111, 774, 512, 79, 779, 512, 111, 779, 512, 82, 769, 512, 114, + 769, 512, 82, 807, 512, 114, 807, 512, 82, 780, 512, 114, 780, 512, 83, + 769, 512, 115, 769, 512, 83, 770, 512, 115, 770, 512, 83, 807, 512, 115, + 807, 512, 83, 780, 512, 115, 780, 512, 84, 807, 512, 116, 807, 512, 84, + 780, 512, 116, 780, 512, 85, 771, 512, 117, 771, 512, 85, 772, 512, 117, + 772, 512, 85, 774, 512, 117, 774, 512, 85, 778, 512, 117, 778, 512, 85, + 779, 512, 117, 779, 512, 85, 808, 512, 117, 808, 512, 87, 770, 512, 119, + 770, 512, 89, 770, 512, 121, 770, 512, 89, 776, 512, 90, 769, 512, 122, + 769, 512, 90, 775, 512, 122, 775, 512, 90, 780, 512, 122, 780, 258, 115, + 512, 79, 795, 512, 111, 795, 512, 85, 795, 512, 117, 795, 514, 68, 381, + 514, 68, 382, 514, 100, 382, 514, 76, 74, 514, 76, 106, 514, 108, 106, + 514, 78, 74, 514, 78, 106, 514, 110, 106, 512, 65, 780, 512, 97, 780, + 512, 73, 780, 512, 105, 780, 512, 79, 780, 512, 111, 780, 512, 85, 780, + 512, 117, 780, 512, 220, 772, 512, 252, 772, 512, 220, 769, 512, 252, + 769, 512, 220, 780, 512, 252, 780, 512, 220, 768, 512, 252, 768, 512, + 196, 772, 512, 228, 772, 512, 550, 772, 512, 551, 772, 512, 198, 772, + 512, 230, 772, 512, 71, 780, 512, 103, 780, 512, 75, 780, 512, 107, 780, + 512, 79, 808, 512, 111, 808, 512, 490, 772, 512, 491, 772, 512, 439, 780, + 512, 658, 780, 512, 106, 780, 514, 68, 90, 514, 68, 122, 514, 100, 122, + 512, 71, 769, 512, 103, 769, 512, 78, 768, 512, 110, 768, 512, 197, 769, + 512, 229, 769, 512, 198, 769, 512, 230, 769, 512, 216, 769, 512, 248, + 769, 512, 65, 783, 512, 97, 783, 512, 65, 785, 512, 97, 785, 512, 69, + 783, 512, 101, 783, 512, 69, 785, 512, 101, 785, 512, 73, 783, 512, 105, + 783, 512, 73, 785, 512, 105, 785, 512, 79, 783, 512, 111, 783, 512, 79, + 785, 512, 111, 785, 512, 82, 783, 512, 114, 783, 512, 82, 785, 512, 114, + 785, 512, 85, 783, 512, 117, 783, 512, 85, 785, 512, 117, 785, 512, 83, + 806, 512, 115, 806, 512, 84, 806, 512, 116, 806, 512, 72, 780, 512, 104, + 780, 512, 65, 775, 512, 97, 775, 512, 69, 807, 512, 101, 807, 512, 214, + 772, 512, 246, 772, 512, 213, 772, 512, 245, 772, 512, 79, 775, 512, 111, + 775, 512, 558, 772, 512, 559, 772, 512, 89, 772, 512, 121, 772, 259, 104, + 259, 614, 259, 106, 259, 114, 259, 633, 259, 635, 259, 641, 259, 119, + 259, 121, 514, 32, 774, 514, 32, 775, 514, 32, 778, 514, 32, 808, 514, + 32, 771, 514, 32, 779, 259, 611, 259, 108, 259, 115, 259, 120, 259, 661, + 256, 768, 256, 769, 256, 787, 512, 776, 769, 256, 697, 514, 32, 837, 256, + 59, 514, 32, 769, 512, 168, 769, 512, 913, 769, 256, 183, 512, 917, 769, + 512, 919, 769, 512, 921, 769, 512, 927, 769, 512, 933, 769, 512, 937, + 769, 512, 970, 769, 512, 921, 776, 512, 933, 776, 512, 945, 769, 512, + 949, 769, 512, 951, 769, 512, 953, 769, 512, 971, 769, 512, 953, 776, + 512, 965, 776, 512, 959, 769, 512, 965, 769, 512, 969, 769, 258, 946, + 258, 952, 258, 933, 512, 978, 769, 512, 978, 776, 258, 966, 258, 960, + 258, 954, 258, 961, 258, 962, 258, 920, 258, 949, 258, 931, 512, 1045, + 768, 512, 1045, 776, 512, 1043, 769, 512, 1030, 776, 512, 1050, 769, 512, + 1048, 768, 512, 1059, 774, 512, 1048, 774, 512, 1080, 774, 512, 1077, + 768, 512, 1077, 776, 512, 1075, 769, 512, 1110, 776, 512, 1082, 769, 512, + 1080, 768, 512, 1091, 774, 512, 1140, 783, 512, 1141, 783, 512, 1046, + 774, 512, 1078, 774, 512, 1040, 774, 512, 1072, 774, 512, 1040, 776, 512, + 1072, 776, 512, 1045, 774, 512, 1077, 774, 512, 1240, 776, 512, 1241, + 776, 512, 1046, 776, 512, 1078, 776, 512, 1047, 776, 512, 1079, 776, 512, + 1048, 772, 512, 1080, 772, 512, 1048, 776, 512, 1080, 776, 512, 1054, + 776, 512, 1086, 776, 512, 1256, 776, 512, 1257, 776, 512, 1069, 776, 512, + 1101, 776, 512, 1059, 772, 512, 1091, 772, 512, 1059, 776, 512, 1091, + 776, 512, 1059, 779, 512, 1091, 779, 512, 1063, 776, 512, 1095, 776, 512, + 1067, 776, 512, 1099, 776, 514, 1381, 1410, 512, 1575, 1619, 512, 1575, + 1620, 512, 1608, 1620, 512, 1575, 1621, 512, 1610, 1620, 514, 1575, 1652, + 514, 1608, 1652, 514, 1735, 1652, 514, 1610, 1652, 512, 1749, 1620, 512, + 1729, 1620, 512, 1746, 1620, 512, 2344, 2364, 512, 2352, 2364, 512, 2355, + 2364, 512, 2325, 2364, 512, 2326, 2364, 512, 2327, 2364, 512, 2332, 2364, + 512, 2337, 2364, 512, 2338, 2364, 512, 2347, 2364, 512, 2351, 2364, 512, + 2503, 2494, 512, 2503, 2519, 512, 2465, 2492, 512, 2466, 2492, 512, 2479, + 2492, 512, 2610, 2620, 512, 2616, 2620, 512, 2582, 2620, 512, 2583, 2620, + 512, 2588, 2620, 512, 2603, 2620, 512, 2887, 2902, 512, 2887, 2878, 512, + 2887, 2903, 512, 2849, 2876, 512, 2850, 2876, 512, 2962, 3031, 512, 3014, + 3006, 512, 3015, 3006, 512, 3014, 3031, 512, 3142, 3158, 512, 3263, 3285, + 512, 3270, 3285, 512, 3270, 3286, 512, 3270, 3266, 512, 3274, 3285, 512, + 3398, 3390, 512, 3399, 3390, 512, 3398, 3415, 512, 3545, 3530, 512, 3545, + 3535, 512, 3548, 3530, 512, 3545, 3551, 514, 3661, 3634, 514, 3789, 3762, + 514, 3755, 3737, 514, 3755, 3745, 257, 3851, 512, 3906, 4023, 512, 3916, + 4023, 512, 3921, 4023, 512, 3926, 4023, 512, 3931, 4023, 512, 3904, 4021, + 512, 3953, 3954, 512, 3953, 3956, 512, 4018, 3968, 514, 4018, 3969, 512, + 4019, 3968, 514, 4019, 3969, 512, 3953, 3968, 512, 3986, 4023, 512, 3996, + 4023, 512, 4001, 4023, 512, 4006, 4023, 512, 4011, 4023, 512, 3984, 4021, + 512, 4133, 4142, 259, 4316, 512, 6917, 6965, 512, 6919, 6965, 512, 6921, + 6965, 512, 6923, 6965, 512, 6925, 6965, 512, 6929, 6965, 512, 6970, 6965, + 512, 6972, 6965, 512, 6974, 6965, 512, 6975, 6965, 512, 6978, 6965, 259, + 65, 259, 198, 259, 66, 259, 68, 259, 69, 259, 398, 259, 71, 259, 72, 259, + 73, 259, 74, 259, 75, 259, 76, 259, 77, 259, 78, 259, 79, 259, 546, 259, + 80, 259, 82, 259, 84, 259, 85, 259, 87, 259, 97, 259, 592, 259, 593, 259, + 7426, 259, 98, 259, 100, 259, 101, 259, 601, 259, 603, 259, 604, 259, + 103, 259, 107, 259, 109, 259, 331, 259, 111, 259, 596, 259, 7446, 259, + 7447, 259, 112, 259, 116, 259, 117, 259, 7453, 259, 623, 259, 118, 259, + 7461, 259, 946, 259, 947, 259, 948, 259, 966, 259, 967, 261, 105, 261, + 114, 261, 117, 261, 118, 261, 946, 261, 947, 261, 961, 261, 966, 261, + 967, 259, 1085, 259, 594, 259, 99, 259, 597, 259, 240, 259, 604, 259, + 102, 259, 607, 259, 609, 259, 613, 259, 616, 259, 617, 259, 618, 259, + 7547, 259, 669, 259, 621, 259, 7557, 259, 671, 259, 625, 259, 624, 259, + 626, 259, 627, 259, 628, 259, 629, 259, 632, 259, 642, 259, 643, 259, + 427, 259, 649, 259, 650, 259, 7452, 259, 651, 259, 652, 259, 122, 259, + 656, 259, 657, 259, 658, 259, 952, 512, 65, 805, 512, 97, 805, 512, 66, + 775, 512, 98, 775, 512, 66, 803, 512, 98, 803, 512, 66, 817, 512, 98, + 817, 512, 199, 769, 512, 231, 769, 512, 68, 775, 512, 100, 775, 512, 68, + 803, 512, 100, 803, 512, 68, 817, 512, 100, 817, 512, 68, 807, 512, 100, + 807, 512, 68, 813, 512, 100, 813, 512, 274, 768, 512, 275, 768, 512, 274, + 769, 512, 275, 769, 512, 69, 813, 512, 101, 813, 512, 69, 816, 512, 101, + 816, 512, 552, 774, 512, 553, 774, 512, 70, 775, 512, 102, 775, 512, 71, + 772, 512, 103, 772, 512, 72, 775, 512, 104, 775, 512, 72, 803, 512, 104, + 803, 512, 72, 776, 512, 104, 776, 512, 72, 807, 512, 104, 807, 512, 72, + 814, 512, 104, 814, 512, 73, 816, 512, 105, 816, 512, 207, 769, 512, 239, + 769, 512, 75, 769, 512, 107, 769, 512, 75, 803, 512, 107, 803, 512, 75, + 817, 512, 107, 817, 512, 76, 803, 512, 108, 803, 512, 7734, 772, 512, + 7735, 772, 512, 76, 817, 512, 108, 817, 512, 76, 813, 512, 108, 813, 512, + 77, 769, 512, 109, 769, 512, 77, 775, 512, 109, 775, 512, 77, 803, 512, + 109, 803, 512, 78, 775, 512, 110, 775, 512, 78, 803, 512, 110, 803, 512, + 78, 817, 512, 110, 817, 512, 78, 813, 512, 110, 813, 512, 213, 769, 512, + 245, 769, 512, 213, 776, 512, 245, 776, 512, 332, 768, 512, 333, 768, + 512, 332, 769, 512, 333, 769, 512, 80, 769, 512, 112, 769, 512, 80, 775, + 512, 112, 775, 512, 82, 775, 512, 114, 775, 512, 82, 803, 512, 114, 803, + 512, 7770, 772, 512, 7771, 772, 512, 82, 817, 512, 114, 817, 512, 83, + 775, 512, 115, 775, 512, 83, 803, 512, 115, 803, 512, 346, 775, 512, 347, + 775, 512, 352, 775, 512, 353, 775, 512, 7778, 775, 512, 7779, 775, 512, + 84, 775, 512, 116, 775, 512, 84, 803, 512, 116, 803, 512, 84, 817, 512, + 116, 817, 512, 84, 813, 512, 116, 813, 512, 85, 804, 512, 117, 804, 512, + 85, 816, 512, 117, 816, 512, 85, 813, 512, 117, 813, 512, 360, 769, 512, + 361, 769, 512, 362, 776, 512, 363, 776, 512, 86, 771, 512, 118, 771, 512, + 86, 803, 512, 118, 803, 512, 87, 768, 512, 119, 768, 512, 87, 769, 512, + 119, 769, 512, 87, 776, 512, 119, 776, 512, 87, 775, 512, 119, 775, 512, + 87, 803, 512, 119, 803, 512, 88, 775, 512, 120, 775, 512, 88, 776, 512, + 120, 776, 512, 89, 775, 512, 121, 775, 512, 90, 770, 512, 122, 770, 512, + 90, 803, 512, 122, 803, 512, 90, 817, 512, 122, 817, 512, 104, 817, 512, + 116, 776, 512, 119, 778, 512, 121, 778, 514, 97, 702, 512, 383, 775, 512, + 65, 803, 512, 97, 803, 512, 65, 777, 512, 97, 777, 512, 194, 769, 512, + 226, 769, 512, 194, 768, 512, 226, 768, 512, 194, 777, 512, 226, 777, + 512, 194, 771, 512, 226, 771, 512, 7840, 770, 512, 7841, 770, 512, 258, + 769, 512, 259, 769, 512, 258, 768, 512, 259, 768, 512, 258, 777, 512, + 259, 777, 512, 258, 771, 512, 259, 771, 512, 7840, 774, 512, 7841, 774, + 512, 69, 803, 512, 101, 803, 512, 69, 777, 512, 101, 777, 512, 69, 771, + 512, 101, 771, 512, 202, 769, 512, 234, 769, 512, 202, 768, 512, 234, + 768, 512, 202, 777, 512, 234, 777, 512, 202, 771, 512, 234, 771, 512, + 7864, 770, 512, 7865, 770, 512, 73, 777, 512, 105, 777, 512, 73, 803, + 512, 105, 803, 512, 79, 803, 512, 111, 803, 512, 79, 777, 512, 111, 777, + 512, 212, 769, 512, 244, 769, 512, 212, 768, 512, 244, 768, 512, 212, + 777, 512, 244, 777, 512, 212, 771, 512, 244, 771, 512, 7884, 770, 512, + 7885, 770, 512, 416, 769, 512, 417, 769, 512, 416, 768, 512, 417, 768, + 512, 416, 777, 512, 417, 777, 512, 416, 771, 512, 417, 771, 512, 416, + 803, 512, 417, 803, 512, 85, 803, 512, 117, 803, 512, 85, 777, 512, 117, + 777, 512, 431, 769, 512, 432, 769, 512, 431, 768, 512, 432, 768, 512, + 431, 777, 512, 432, 777, 512, 431, 771, 512, 432, 771, 512, 431, 803, + 512, 432, 803, 512, 89, 768, 512, 121, 768, 512, 89, 803, 512, 121, 803, + 512, 89, 777, 512, 121, 777, 512, 89, 771, 512, 121, 771, 512, 945, 787, + 512, 945, 788, 512, 7936, 768, 512, 7937, 768, 512, 7936, 769, 512, 7937, + 769, 512, 7936, 834, 512, 7937, 834, 512, 913, 787, 512, 913, 788, 512, + 7944, 768, 512, 7945, 768, 512, 7944, 769, 512, 7945, 769, 512, 7944, + 834, 512, 7945, 834, 512, 949, 787, 512, 949, 788, 512, 7952, 768, 512, + 7953, 768, 512, 7952, 769, 512, 7953, 769, 512, 917, 787, 512, 917, 788, + 512, 7960, 768, 512, 7961, 768, 512, 7960, 769, 512, 7961, 769, 512, 951, + 787, 512, 951, 788, 512, 7968, 768, 512, 7969, 768, 512, 7968, 769, 512, + 7969, 769, 512, 7968, 834, 512, 7969, 834, 512, 919, 787, 512, 919, 788, + 512, 7976, 768, 512, 7977, 768, 512, 7976, 769, 512, 7977, 769, 512, + 7976, 834, 512, 7977, 834, 512, 953, 787, 512, 953, 788, 512, 7984, 768, + 512, 7985, 768, 512, 7984, 769, 512, 7985, 769, 512, 7984, 834, 512, + 7985, 834, 512, 921, 787, 512, 921, 788, 512, 7992, 768, 512, 7993, 768, + 512, 7992, 769, 512, 7993, 769, 512, 7992, 834, 512, 7993, 834, 512, 959, + 787, 512, 959, 788, 512, 8000, 768, 512, 8001, 768, 512, 8000, 769, 512, + 8001, 769, 512, 927, 787, 512, 927, 788, 512, 8008, 768, 512, 8009, 768, + 512, 8008, 769, 512, 8009, 769, 512, 965, 787, 512, 965, 788, 512, 8016, + 768, 512, 8017, 768, 512, 8016, 769, 512, 8017, 769, 512, 8016, 834, 512, + 8017, 834, 512, 933, 788, 512, 8025, 768, 512, 8025, 769, 512, 8025, 834, + 512, 969, 787, 512, 969, 788, 512, 8032, 768, 512, 8033, 768, 512, 8032, + 769, 512, 8033, 769, 512, 8032, 834, 512, 8033, 834, 512, 937, 787, 512, + 937, 788, 512, 8040, 768, 512, 8041, 768, 512, 8040, 769, 512, 8041, 769, + 512, 8040, 834, 512, 8041, 834, 512, 945, 768, 256, 940, 512, 949, 768, + 256, 941, 512, 951, 768, 256, 942, 512, 953, 768, 256, 943, 512, 959, + 768, 256, 972, 512, 965, 768, 256, 973, 512, 969, 768, 256, 974, 512, + 7936, 837, 512, 7937, 837, 512, 7938, 837, 512, 7939, 837, 512, 7940, + 837, 512, 7941, 837, 512, 7942, 837, 512, 7943, 837, 512, 7944, 837, 512, + 7945, 837, 512, 7946, 837, 512, 7947, 837, 512, 7948, 837, 512, 7949, + 837, 512, 7950, 837, 512, 7951, 837, 512, 7968, 837, 512, 7969, 837, 512, + 7970, 837, 512, 7971, 837, 512, 7972, 837, 512, 7973, 837, 512, 7974, + 837, 512, 7975, 837, 512, 7976, 837, 512, 7977, 837, 512, 7978, 837, 512, + 7979, 837, 512, 7980, 837, 512, 7981, 837, 512, 7982, 837, 512, 7983, + 837, 512, 8032, 837, 512, 8033, 837, 512, 8034, 837, 512, 8035, 837, 512, + 8036, 837, 512, 8037, 837, 512, 8038, 837, 512, 8039, 837, 512, 8040, + 837, 512, 8041, 837, 512, 8042, 837, 512, 8043, 837, 512, 8044, 837, 512, + 8045, 837, 512, 8046, 837, 512, 8047, 837, 512, 945, 774, 512, 945, 772, + 512, 8048, 837, 512, 945, 837, 512, 940, 837, 512, 945, 834, 512, 8118, + 837, 512, 913, 774, 512, 913, 772, 512, 913, 768, 256, 902, 512, 913, + 837, 514, 32, 787, 256, 953, 514, 32, 787, 514, 32, 834, 512, 168, 834, + 512, 8052, 837, 512, 951, 837, 512, 942, 837, 512, 951, 834, 512, 8134, + 837, 512, 917, 768, 256, 904, 512, 919, 768, 256, 905, 512, 919, 837, + 512, 8127, 768, 512, 8127, 769, 512, 8127, 834, 512, 953, 774, 512, 953, + 772, 512, 970, 768, 256, 912, 512, 953, 834, 512, 970, 834, 512, 921, + 774, 512, 921, 772, 512, 921, 768, 256, 906, 512, 8190, 768, 512, 8190, + 769, 512, 8190, 834, 512, 965, 774, 512, 965, 772, 512, 971, 768, 256, + 944, 512, 961, 787, 512, 961, 788, 512, 965, 834, 512, 971, 834, 512, + 933, 774, 512, 933, 772, 512, 933, 768, 256, 910, 512, 929, 788, 512, + 168, 768, 256, 901, 256, 96, 512, 8060, 837, 512, 969, 837, 512, 974, + 837, 512, 969, 834, 512, 8182, 837, 512, 927, 768, 256, 908, 512, 937, + 768, 256, 911, 512, 937, 837, 256, 180, 514, 32, 788, 256, 8194, 256, + 8195, 258, 32, 258, 32, 258, 32, 258, 32, 258, 32, 257, 32, 258, 32, 258, + 32, 258, 32, 257, 8208, 514, 32, 819, 258, 46, 514, 46, 46, 770, 46, 46, + 46, 257, 32, 514, 8242, 8242, 770, 8242, 8242, 8242, 514, 8245, 8245, + 770, 8245, 8245, 8245, 514, 33, 33, 514, 32, 773, 514, 63, 63, 514, 63, + 33, 514, 33, 63, 1026, 8242, 8242, 8242, 8242, 258, 32, 259, 48, 259, + 105, 259, 52, 259, 53, 259, 54, 259, 55, 259, 56, 259, 57, 259, 43, 259, + 8722, 259, 61, 259, 40, 259, 41, 259, 110, 261, 48, 261, 49, 261, 50, + 261, 51, 261, 52, 261, 53, 261, 54, 261, 55, 261, 56, 261, 57, 261, 43, + 261, 8722, 261, 61, 261, 40, 261, 41, 261, 97, 261, 101, 261, 111, 261, + 120, 261, 601, 261, 104, 261, 107, 261, 108, 261, 109, 261, 110, 261, + 112, 261, 115, 261, 116, 514, 82, 115, 770, 97, 47, 99, 770, 97, 47, 115, + 262, 67, 514, 176, 67, 770, 99, 47, 111, 770, 99, 47, 117, 258, 400, 514, + 176, 70, 262, 103, 262, 72, 262, 72, 262, 72, 262, 104, 262, 295, 262, + 73, 262, 73, 262, 76, 262, 108, 262, 78, 514, 78, 111, 262, 80, 262, 81, + 262, 82, 262, 82, 262, 82, 515, 83, 77, 770, 84, 69, 76, 515, 84, 77, + 262, 90, 256, 937, 262, 90, 256, 75, 256, 197, 262, 66, 262, 67, 262, + 101, 262, 69, 262, 70, 262, 77, 262, 111, 258, 1488, 258, 1489, 258, + 1490, 258, 1491, 262, 105, 770, 70, 65, 88, 262, 960, 262, 947, 262, 915, + 262, 928, 262, 8721, 262, 68, 262, 100, 262, 101, 262, 105, 262, 106, + 772, 49, 8260, 55, 772, 49, 8260, 57, 1028, 49, 8260, 49, 48, 772, 49, + 8260, 51, 772, 50, 8260, 51, 772, 49, 8260, 53, 772, 50, 8260, 53, 772, + 51, 8260, 53, 772, 52, 8260, 53, 772, 49, 8260, 54, 772, 53, 8260, 54, + 772, 49, 8260, 56, 772, 51, 8260, 56, 772, 53, 8260, 56, 772, 55, 8260, + 56, 516, 49, 8260, 258, 73, 514, 73, 73, 770, 73, 73, 73, 514, 73, 86, + 258, 86, 514, 86, 73, 770, 86, 73, 73, 1026, 86, 73, 73, 73, 514, 73, 88, + 258, 88, 514, 88, 73, 770, 88, 73, 73, 258, 76, 258, 67, 258, 68, 258, + 77, 258, 105, 514, 105, 105, 770, 105, 105, 105, 514, 105, 118, 258, 118, + 514, 118, 105, 770, 118, 105, 105, 1026, 118, 105, 105, 105, 514, 105, + 120, 258, 120, 514, 120, 105, 770, 120, 105, 105, 258, 108, 258, 99, 258, + 100, 258, 109, 772, 48, 8260, 51, 512, 8592, 824, 512, 8594, 824, 512, + 8596, 824, 512, 8656, 824, 512, 8660, 824, 512, 8658, 824, 512, 8707, + 824, 512, 8712, 824, 512, 8715, 824, 512, 8739, 824, 512, 8741, 824, 514, + 8747, 8747, 770, 8747, 8747, 8747, 514, 8750, 8750, 770, 8750, 8750, + 8750, 512, 8764, 824, 512, 8771, 824, 512, 8773, 824, 512, 8776, 824, + 512, 61, 824, 512, 8801, 824, 512, 8781, 824, 512, 60, 824, 512, 62, 824, + 512, 8804, 824, 512, 8805, 824, 512, 8818, 824, 512, 8819, 824, 512, + 8822, 824, 512, 8823, 824, 512, 8826, 824, 512, 8827, 824, 512, 8834, + 824, 512, 8835, 824, 512, 8838, 824, 512, 8839, 824, 512, 8866, 824, 512, + 8872, 824, 512, 8873, 824, 512, 8875, 824, 512, 8828, 824, 512, 8829, + 824, 512, 8849, 824, 512, 8850, 824, 512, 8882, 824, 512, 8883, 824, 512, + 8884, 824, 512, 8885, 824, 256, 12296, 256, 12297, 263, 49, 263, 50, 263, + 51, 263, 52, 263, 53, 263, 54, 263, 55, 263, 56, 263, 57, 519, 49, 48, + 519, 49, 49, 519, 49, 50, 519, 49, 51, 519, 49, 52, 519, 49, 53, 519, 49, + 54, 519, 49, 55, 519, 49, 56, 519, 49, 57, 519, 50, 48, 770, 40, 49, 41, + 770, 40, 50, 41, 770, 40, 51, 41, 770, 40, 52, 41, 770, 40, 53, 41, 770, + 40, 54, 41, 770, 40, 55, 41, 770, 40, 56, 41, 770, 40, 57, 41, 1026, 40, + 49, 48, 41, 1026, 40, 49, 49, 41, 1026, 40, 49, 50, 41, 1026, 40, 49, 51, + 41, 1026, 40, 49, 52, 41, 1026, 40, 49, 53, 41, 1026, 40, 49, 54, 41, + 1026, 40, 49, 55, 41, 1026, 40, 49, 56, 41, 1026, 40, 49, 57, 41, 1026, + 40, 50, 48, 41, 514, 49, 46, 514, 50, 46, 514, 51, 46, 514, 52, 46, 514, + 53, 46, 514, 54, 46, 514, 55, 46, 514, 56, 46, 514, 57, 46, 770, 49, 48, + 46, 770, 49, 49, 46, 770, 49, 50, 46, 770, 49, 51, 46, 770, 49, 52, 46, + 770, 49, 53, 46, 770, 49, 54, 46, 770, 49, 55, 46, 770, 49, 56, 46, 770, + 49, 57, 46, 770, 50, 48, 46, 770, 40, 97, 41, 770, 40, 98, 41, 770, 40, + 99, 41, 770, 40, 100, 41, 770, 40, 101, 41, 770, 40, 102, 41, 770, 40, + 103, 41, 770, 40, 104, 41, 770, 40, 105, 41, 770, 40, 106, 41, 770, 40, + 107, 41, 770, 40, 108, 41, 770, 40, 109, 41, 770, 40, 110, 41, 770, 40, + 111, 41, 770, 40, 112, 41, 770, 40, 113, 41, 770, 40, 114, 41, 770, 40, + 115, 41, 770, 40, 116, 41, 770, 40, 117, 41, 770, 40, 118, 41, 770, 40, + 119, 41, 770, 40, 120, 41, 770, 40, 121, 41, 770, 40, 122, 41, 263, 65, + 263, 66, 263, 67, 263, 68, 263, 69, 263, 70, 263, 71, 263, 72, 263, 73, + 263, 74, 263, 75, 263, 76, 263, 77, 263, 78, 263, 79, 263, 80, 263, 81, + 263, 82, 263, 83, 263, 84, 263, 85, 263, 86, 263, 87, 263, 88, 263, 89, + 263, 90, 263, 97, 263, 98, 263, 99, 263, 100, 263, 101, 263, 102, 263, + 103, 263, 104, 263, 105, 263, 106, 263, 107, 263, 108, 263, 109, 263, + 110, 263, 111, 263, 112, 263, 113, 263, 114, 263, 115, 263, 116, 263, + 117, 263, 118, 263, 119, 263, 120, 263, 121, 263, 122, 263, 48, 1026, + 8747, 8747, 8747, 8747, 770, 58, 58, 61, 514, 61, 61, 770, 61, 61, 61, + 512, 10973, 824, 261, 106, 259, 86, 259, 11617, 258, 27597, 258, 40863, + 258, 19968, 258, 20008, 258, 20022, 258, 20031, 258, 20057, 258, 20101, + 258, 20108, 258, 20128, 258, 20154, 258, 20799, 258, 20837, 258, 20843, + 258, 20866, 258, 20886, 258, 20907, 258, 20960, 258, 20981, 258, 20992, + 258, 21147, 258, 21241, 258, 21269, 258, 21274, 258, 21304, 258, 21313, + 258, 21340, 258, 21353, 258, 21378, 258, 21430, 258, 21448, 258, 21475, + 258, 22231, 258, 22303, 258, 22763, 258, 22786, 258, 22794, 258, 22805, + 258, 22823, 258, 22899, 258, 23376, 258, 23424, 258, 23544, 258, 23567, + 258, 23586, 258, 23608, 258, 23662, 258, 23665, 258, 24027, 258, 24037, + 258, 24049, 258, 24062, 258, 24178, 258, 24186, 258, 24191, 258, 24308, + 258, 24318, 258, 24331, 258, 24339, 258, 24400, 258, 24417, 258, 24435, + 258, 24515, 258, 25096, 258, 25142, 258, 25163, 258, 25903, 258, 25908, + 258, 25991, 258, 26007, 258, 26020, 258, 26041, 258, 26080, 258, 26085, + 258, 26352, 258, 26376, 258, 26408, 258, 27424, 258, 27490, 258, 27513, + 258, 27571, 258, 27595, 258, 27604, 258, 27611, 258, 27663, 258, 27668, + 258, 27700, 258, 28779, 258, 29226, 258, 29238, 258, 29243, 258, 29247, + 258, 29255, 258, 29273, 258, 29275, 258, 29356, 258, 29572, 258, 29577, + 258, 29916, 258, 29926, 258, 29976, 258, 29983, 258, 29992, 258, 30000, + 258, 30091, 258, 30098, 258, 30326, 258, 30333, 258, 30382, 258, 30399, + 258, 30446, 258, 30683, 258, 30690, 258, 30707, 258, 31034, 258, 31160, + 258, 31166, 258, 31348, 258, 31435, 258, 31481, 258, 31859, 258, 31992, + 258, 32566, 258, 32593, 258, 32650, 258, 32701, 258, 32769, 258, 32780, + 258, 32786, 258, 32819, 258, 32895, 258, 32905, 258, 33251, 258, 33258, + 258, 33267, 258, 33276, 258, 33292, 258, 33307, 258, 33311, 258, 33390, + 258, 33394, 258, 33400, 258, 34381, 258, 34411, 258, 34880, 258, 34892, + 258, 34915, 258, 35198, 258, 35211, 258, 35282, 258, 35328, 258, 35895, + 258, 35910, 258, 35925, 258, 35960, 258, 35997, 258, 36196, 258, 36208, + 258, 36275, 258, 36523, 258, 36554, 258, 36763, 258, 36784, 258, 36789, + 258, 37009, 258, 37193, 258, 37318, 258, 37324, 258, 37329, 258, 38263, + 258, 38272, 258, 38428, 258, 38582, 258, 38585, 258, 38632, 258, 38737, + 258, 38750, 258, 38754, 258, 38761, 258, 38859, 258, 38893, 258, 38899, + 258, 38913, 258, 39080, 258, 39131, 258, 39135, 258, 39318, 258, 39321, + 258, 39340, 258, 39592, 258, 39640, 258, 39647, 258, 39717, 258, 39727, + 258, 39730, 258, 39740, 258, 39770, 258, 40165, 258, 40565, 258, 40575, + 258, 40613, 258, 40635, 258, 40643, 258, 40653, 258, 40657, 258, 40697, + 258, 40701, 258, 40718, 258, 40723, 258, 40736, 258, 40763, 258, 40778, + 258, 40786, 258, 40845, 258, 40860, 258, 40864, 264, 32, 258, 12306, 258, + 21313, 258, 21316, 258, 21317, 512, 12363, 12441, 512, 12365, 12441, 512, + 12367, 12441, 512, 12369, 12441, 512, 12371, 12441, 512, 12373, 12441, + 512, 12375, 12441, 512, 12377, 12441, 512, 12379, 12441, 512, 12381, + 12441, 512, 12383, 12441, 512, 12385, 12441, 512, 12388, 12441, 512, + 12390, 12441, 512, 12392, 12441, 512, 12399, 12441, 512, 12399, 12442, + 512, 12402, 12441, 512, 12402, 12442, 512, 12405, 12441, 512, 12405, + 12442, 512, 12408, 12441, 512, 12408, 12442, 512, 12411, 12441, 512, + 12411, 12442, 512, 12358, 12441, 514, 32, 12441, 514, 32, 12442, 512, + 12445, 12441, 521, 12424, 12426, 512, 12459, 12441, 512, 12461, 12441, + 512, 12463, 12441, 512, 12465, 12441, 512, 12467, 12441, 512, 12469, + 12441, 512, 12471, 12441, 512, 12473, 12441, 512, 12475, 12441, 512, + 12477, 12441, 512, 12479, 12441, 512, 12481, 12441, 512, 12484, 12441, + 512, 12486, 12441, 512, 12488, 12441, 512, 12495, 12441, 512, 12495, + 12442, 512, 12498, 12441, 512, 12498, 12442, 512, 12501, 12441, 512, + 12501, 12442, 512, 12504, 12441, 512, 12504, 12442, 512, 12507, 12441, + 512, 12507, 12442, 512, 12454, 12441, 512, 12527, 12441, 512, 12528, + 12441, 512, 12529, 12441, 512, 12530, 12441, 512, 12541, 12441, 521, + 12467, 12488, 258, 4352, 258, 4353, 258, 4522, 258, 4354, 258, 4524, 258, + 4525, 258, 4355, 258, 4356, 258, 4357, 258, 4528, 258, 4529, 258, 4530, + 258, 4531, 258, 4532, 258, 4533, 258, 4378, 258, 4358, 258, 4359, 258, + 4360, 258, 4385, 258, 4361, 258, 4362, 258, 4363, 258, 4364, 258, 4365, + 258, 4366, 258, 4367, 258, 4368, 258, 4369, 258, 4370, 258, 4449, 258, + 4450, 258, 4451, 258, 4452, 258, 4453, 258, 4454, 258, 4455, 258, 4456, + 258, 4457, 258, 4458, 258, 4459, 258, 4460, 258, 4461, 258, 4462, 258, + 4463, 258, 4464, 258, 4465, 258, 4466, 258, 4467, 258, 4468, 258, 4469, + 258, 4448, 258, 4372, 258, 4373, 258, 4551, 258, 4552, 258, 4556, 258, + 4558, 258, 4563, 258, 4567, 258, 4569, 258, 4380, 258, 4573, 258, 4575, + 258, 4381, 258, 4382, 258, 4384, 258, 4386, 258, 4387, 258, 4391, 258, + 4393, 258, 4395, 258, 4396, 258, 4397, 258, 4398, 258, 4399, 258, 4402, + 258, 4406, 258, 4416, 258, 4423, 258, 4428, 258, 4593, 258, 4594, 258, + 4439, 258, 4440, 258, 4441, 258, 4484, 258, 4485, 258, 4488, 258, 4497, + 258, 4498, 258, 4500, 258, 4510, 258, 4513, 259, 19968, 259, 20108, 259, + 19977, 259, 22235, 259, 19978, 259, 20013, 259, 19979, 259, 30002, 259, + 20057, 259, 19993, 259, 19969, 259, 22825, 259, 22320, 259, 20154, 770, + 40, 4352, 41, 770, 40, 4354, 41, 770, 40, 4355, 41, 770, 40, 4357, 41, + 770, 40, 4358, 41, 770, 40, 4359, 41, 770, 40, 4361, 41, 770, 40, 4363, + 41, 770, 40, 4364, 41, 770, 40, 4366, 41, 770, 40, 4367, 41, 770, 40, + 4368, 41, 770, 40, 4369, 41, 770, 40, 4370, 41, 1026, 40, 4352, 4449, 41, + 1026, 40, 4354, 4449, 41, 1026, 40, 4355, 4449, 41, 1026, 40, 4357, 4449, + 41, 1026, 40, 4358, 4449, 41, 1026, 40, 4359, 4449, 41, 1026, 40, 4361, + 4449, 41, 1026, 40, 4363, 4449, 41, 1026, 40, 4364, 4449, 41, 1026, 40, + 4366, 4449, 41, 1026, 40, 4367, 4449, 41, 1026, 40, 4368, 4449, 41, 1026, + 40, 4369, 4449, 41, 1026, 40, 4370, 4449, 41, 1026, 40, 4364, 4462, 41, + 1794, 40, 4363, 4457, 4364, 4453, 4523, 41, 1538, 40, 4363, 4457, 4370, + 4462, 41, 770, 40, 19968, 41, 770, 40, 20108, 41, 770, 40, 19977, 41, + 770, 40, 22235, 41, 770, 40, 20116, 41, 770, 40, 20845, 41, 770, 40, + 19971, 41, 770, 40, 20843, 41, 770, 40, 20061, 41, 770, 40, 21313, 41, + 770, 40, 26376, 41, 770, 40, 28779, 41, 770, 40, 27700, 41, 770, 40, + 26408, 41, 770, 40, 37329, 41, 770, 40, 22303, 41, 770, 40, 26085, 41, + 770, 40, 26666, 41, 770, 40, 26377, 41, 770, 40, 31038, 41, 770, 40, + 21517, 41, 770, 40, 29305, 41, 770, 40, 36001, 41, 770, 40, 31069, 41, + 770, 40, 21172, 41, 770, 40, 20195, 41, 770, 40, 21628, 41, 770, 40, + 23398, 41, 770, 40, 30435, 41, 770, 40, 20225, 41, 770, 40, 36039, 41, + 770, 40, 21332, 41, 770, 40, 31085, 41, 770, 40, 20241, 41, 770, 40, + 33258, 41, 770, 40, 33267, 41, 263, 21839, 263, 24188, 263, 25991, 263, + 31631, 778, 80, 84, 69, 519, 50, 49, 519, 50, 50, 519, 50, 51, 519, 50, + 52, 519, 50, 53, 519, 50, 54, 519, 50, 55, 519, 50, 56, 519, 50, 57, 519, + 51, 48, 519, 51, 49, 519, 51, 50, 519, 51, 51, 519, 51, 52, 519, 51, 53, + 263, 4352, 263, 4354, 263, 4355, 263, 4357, 263, 4358, 263, 4359, 263, + 4361, 263, 4363, 263, 4364, 263, 4366, 263, 4367, 263, 4368, 263, 4369, + 263, 4370, 519, 4352, 4449, 519, 4354, 4449, 519, 4355, 4449, 519, 4357, + 4449, 519, 4358, 4449, 519, 4359, 4449, 519, 4361, 4449, 519, 4363, 4449, + 519, 4364, 4449, 519, 4366, 4449, 519, 4367, 4449, 519, 4368, 4449, 519, + 4369, 4449, 519, 4370, 4449, 1287, 4366, 4449, 4535, 4352, 4457, 1031, + 4364, 4462, 4363, 4468, 519, 4363, 4462, 263, 19968, 263, 20108, 263, + 19977, 263, 22235, 263, 20116, 263, 20845, 263, 19971, 263, 20843, 263, + 20061, 263, 21313, 263, 26376, 263, 28779, 263, 27700, 263, 26408, 263, + 37329, 263, 22303, 263, 26085, 263, 26666, 263, 26377, 263, 31038, 263, + 21517, 263, 29305, 263, 36001, 263, 31069, 263, 21172, 263, 31192, 263, + 30007, 263, 22899, 263, 36969, 263, 20778, 263, 21360, 263, 27880, 263, + 38917, 263, 20241, 263, 20889, 263, 27491, 263, 19978, 263, 20013, 263, + 19979, 263, 24038, 263, 21491, 263, 21307, 263, 23447, 263, 23398, 263, + 30435, 263, 20225, 263, 36039, 263, 21332, 263, 22812, 519, 51, 54, 519, + 51, 55, 519, 51, 56, 519, 51, 57, 519, 52, 48, 519, 52, 49, 519, 52, 50, + 519, 52, 51, 519, 52, 52, 519, 52, 53, 519, 52, 54, 519, 52, 55, 519, 52, + 56, 519, 52, 57, 519, 53, 48, 514, 49, 26376, 514, 50, 26376, 514, 51, + 26376, 514, 52, 26376, 514, 53, 26376, 514, 54, 26376, 514, 55, 26376, + 514, 56, 26376, 514, 57, 26376, 770, 49, 48, 26376, 770, 49, 49, 26376, + 770, 49, 50, 26376, 522, 72, 103, 778, 101, 114, 103, 522, 101, 86, 778, + 76, 84, 68, 263, 12450, 263, 12452, 263, 12454, 263, 12456, 263, 12458, + 263, 12459, 263, 12461, 263, 12463, 263, 12465, 263, 12467, 263, 12469, + 263, 12471, 263, 12473, 263, 12475, 263, 12477, 263, 12479, 263, 12481, + 263, 12484, 263, 12486, 263, 12488, 263, 12490, 263, 12491, 263, 12492, + 263, 12493, 263, 12494, 263, 12495, 263, 12498, 263, 12501, 263, 12504, + 263, 12507, 263, 12510, 263, 12511, 263, 12512, 263, 12513, 263, 12514, + 263, 12516, 263, 12518, 263, 12520, 263, 12521, 263, 12522, 263, 12523, + 263, 12524, 263, 12525, 263, 12527, 263, 12528, 263, 12529, 263, 12530, + 1034, 12450, 12497, 12540, 12488, 1034, 12450, 12523, 12501, 12449, 1034, + 12450, 12531, 12506, 12450, 778, 12450, 12540, 12523, 1034, 12452, 12491, + 12531, 12464, 778, 12452, 12531, 12481, 778, 12454, 12457, 12531, 1290, + 12456, 12473, 12463, 12540, 12489, 1034, 12456, 12540, 12459, 12540, 778, + 12458, 12531, 12473, 778, 12458, 12540, 12512, 778, 12459, 12452, 12522, + 1034, 12459, 12521, 12483, 12488, 1034, 12459, 12525, 12522, 12540, 778, + 12460, 12525, 12531, 778, 12460, 12531, 12510, 522, 12462, 12460, 778, + 12462, 12491, 12540, 1034, 12461, 12517, 12522, 12540, 1034, 12462, + 12523, 12480, 12540, 522, 12461, 12525, 1290, 12461, 12525, 12464, 12521, + 12512, 1546, 12461, 12525, 12513, 12540, 12488, 12523, 1290, 12461, + 12525, 12527, 12483, 12488, 778, 12464, 12521, 12512, 1290, 12464, 12521, + 12512, 12488, 12531, 1290, 12463, 12523, 12476, 12452, 12525, 1034, + 12463, 12525, 12540, 12493, 778, 12465, 12540, 12473, 778, 12467, 12523, + 12490, 778, 12467, 12540, 12509, 1034, 12469, 12452, 12463, 12523, 1290, + 12469, 12531, 12481, 12540, 12512, 1034, 12471, 12522, 12531, 12464, 778, + 12475, 12531, 12481, 778, 12475, 12531, 12488, 778, 12480, 12540, 12473, + 522, 12487, 12471, 522, 12489, 12523, 522, 12488, 12531, 522, 12490, + 12494, 778, 12494, 12483, 12488, 778, 12495, 12452, 12484, 1290, 12497, + 12540, 12475, 12531, 12488, 778, 12497, 12540, 12484, 1034, 12496, 12540, + 12524, 12523, 1290, 12500, 12450, 12473, 12488, 12523, 778, 12500, 12463, + 12523, 522, 12500, 12467, 522, 12499, 12523, 1290, 12501, 12449, 12521, + 12483, 12489, 1034, 12501, 12451, 12540, 12488, 1290, 12502, 12483, + 12471, 12455, 12523, 778, 12501, 12521, 12531, 1290, 12504, 12463, 12479, + 12540, 12523, 522, 12506, 12477, 778, 12506, 12491, 12498, 778, 12504, + 12523, 12484, 778, 12506, 12531, 12473, 778, 12506, 12540, 12472, 778, + 12505, 12540, 12479, 1034, 12509, 12452, 12531, 12488, 778, 12508, 12523, + 12488, 522, 12507, 12531, 778, 12509, 12531, 12489, 778, 12507, 12540, + 12523, 778, 12507, 12540, 12531, 1034, 12510, 12452, 12463, 12525, 778, + 12510, 12452, 12523, 778, 12510, 12483, 12495, 778, 12510, 12523, 12463, + 1290, 12510, 12531, 12471, 12519, 12531, 1034, 12511, 12463, 12525, + 12531, 522, 12511, 12522, 1290, 12511, 12522, 12496, 12540, 12523, 522, + 12513, 12460, 1034, 12513, 12460, 12488, 12531, 1034, 12513, 12540, + 12488, 12523, 778, 12516, 12540, 12489, 778, 12516, 12540, 12523, 778, + 12518, 12450, 12531, 1034, 12522, 12483, 12488, 12523, 522, 12522, 12521, + 778, 12523, 12500, 12540, 1034, 12523, 12540, 12502, 12523, 522, 12524, + 12512, 1290, 12524, 12531, 12488, 12466, 12531, 778, 12527, 12483, 12488, + 514, 48, 28857, 514, 49, 28857, 514, 50, 28857, 514, 51, 28857, 514, 52, + 28857, 514, 53, 28857, 514, 54, 28857, 514, 55, 28857, 514, 56, 28857, + 514, 57, 28857, 770, 49, 48, 28857, 770, 49, 49, 28857, 770, 49, 50, + 28857, 770, 49, 51, 28857, 770, 49, 52, 28857, 770, 49, 53, 28857, 770, + 49, 54, 28857, 770, 49, 55, 28857, 770, 49, 56, 28857, 770, 49, 57, + 28857, 770, 50, 48, 28857, 770, 50, 49, 28857, 770, 50, 50, 28857, 770, + 50, 51, 28857, 770, 50, 52, 28857, 778, 104, 80, 97, 522, 100, 97, 522, + 65, 85, 778, 98, 97, 114, 522, 111, 86, 522, 112, 99, 522, 100, 109, 778, + 100, 109, 178, 778, 100, 109, 179, 522, 73, 85, 522, 24179, 25104, 522, + 26157, 21644, 522, 22823, 27491, 522, 26126, 27835, 1034, 26666, 24335, + 20250, 31038, 522, 112, 65, 522, 110, 65, 522, 956, 65, 522, 109, 65, + 522, 107, 65, 522, 75, 66, 522, 77, 66, 522, 71, 66, 778, 99, 97, 108, + 1034, 107, 99, 97, 108, 522, 112, 70, 522, 110, 70, 522, 956, 70, 522, + 956, 103, 522, 109, 103, 522, 107, 103, 522, 72, 122, 778, 107, 72, 122, + 778, 77, 72, 122, 778, 71, 72, 122, 778, 84, 72, 122, 522, 956, 8467, + 522, 109, 8467, 522, 100, 8467, 522, 107, 8467, 522, 102, 109, 522, 110, + 109, 522, 956, 109, 522, 109, 109, 522, 99, 109, 522, 107, 109, 778, 109, + 109, 178, 778, 99, 109, 178, 522, 109, 178, 778, 107, 109, 178, 778, 109, + 109, 179, 778, 99, 109, 179, 522, 109, 179, 778, 107, 109, 179, 778, 109, + 8725, 115, 1034, 109, 8725, 115, 178, 522, 80, 97, 778, 107, 80, 97, 778, + 77, 80, 97, 778, 71, 80, 97, 778, 114, 97, 100, 1290, 114, 97, 100, 8725, + 115, 1546, 114, 97, 100, 8725, 115, 178, 522, 112, 115, 522, 110, 115, + 522, 956, 115, 522, 109, 115, 522, 112, 86, 522, 110, 86, 522, 956, 86, + 522, 109, 86, 522, 107, 86, 522, 77, 86, 522, 112, 87, 522, 110, 87, 522, + 956, 87, 522, 109, 87, 522, 107, 87, 522, 77, 87, 522, 107, 937, 522, 77, + 937, 1034, 97, 46, 109, 46, 522, 66, 113, 522, 99, 99, 522, 99, 100, + 1034, 67, 8725, 107, 103, 778, 67, 111, 46, 522, 100, 66, 522, 71, 121, + 522, 104, 97, 522, 72, 80, 522, 105, 110, 522, 75, 75, 522, 75, 77, 522, + 107, 116, 522, 108, 109, 522, 108, 110, 778, 108, 111, 103, 522, 108, + 120, 522, 109, 98, 778, 109, 105, 108, 778, 109, 111, 108, 522, 80, 72, + 1034, 112, 46, 109, 46, 778, 80, 80, 77, 522, 80, 82, 522, 115, 114, 522, + 83, 118, 522, 87, 98, 778, 86, 8725, 109, 778, 65, 8725, 109, 514, 49, + 26085, 514, 50, 26085, 514, 51, 26085, 514, 52, 26085, 514, 53, 26085, + 514, 54, 26085, 514, 55, 26085, 514, 56, 26085, 514, 57, 26085, 770, 49, + 48, 26085, 770, 49, 49, 26085, 770, 49, 50, 26085, 770, 49, 51, 26085, + 770, 49, 52, 26085, 770, 49, 53, 26085, 770, 49, 54, 26085, 770, 49, 55, + 26085, 770, 49, 56, 26085, 770, 49, 57, 26085, 770, 50, 48, 26085, 770, + 50, 49, 26085, 770, 50, 50, 26085, 770, 50, 51, 26085, 770, 50, 52, + 26085, 770, 50, 53, 26085, 770, 50, 54, 26085, 770, 50, 55, 26085, 770, + 50, 56, 26085, 770, 50, 57, 26085, 770, 51, 48, 26085, 770, 51, 49, + 26085, 778, 103, 97, 108, 259, 42863, 259, 294, 259, 339, 256, 35912, + 256, 26356, 256, 36554, 256, 36040, 256, 28369, 256, 20018, 256, 21477, + 256, 40860, 256, 40860, 256, 22865, 256, 37329, 256, 21895, 256, 22856, + 256, 25078, 256, 30313, 256, 32645, 256, 34367, 256, 34746, 256, 35064, + 256, 37007, 256, 27138, 256, 27931, 256, 28889, 256, 29662, 256, 33853, + 256, 37226, 256, 39409, 256, 20098, 256, 21365, 256, 27396, 256, 29211, + 256, 34349, 256, 40478, 256, 23888, 256, 28651, 256, 34253, 256, 35172, + 256, 25289, 256, 33240, 256, 34847, 256, 24266, 256, 26391, 256, 28010, + 256, 29436, 256, 37070, 256, 20358, 256, 20919, 256, 21214, 256, 25796, + 256, 27347, 256, 29200, 256, 30439, 256, 32769, 256, 34310, 256, 34396, + 256, 36335, 256, 38706, 256, 39791, 256, 40442, 256, 30860, 256, 31103, + 256, 32160, 256, 33737, 256, 37636, 256, 40575, 256, 35542, 256, 22751, + 256, 24324, 256, 31840, 256, 32894, 256, 29282, 256, 30922, 256, 36034, + 256, 38647, 256, 22744, 256, 23650, 256, 27155, 256, 28122, 256, 28431, + 256, 32047, 256, 32311, 256, 38475, 256, 21202, 256, 32907, 256, 20956, + 256, 20940, 256, 31260, 256, 32190, 256, 33777, 256, 38517, 256, 35712, + 256, 25295, 256, 27138, 256, 35582, 256, 20025, 256, 23527, 256, 24594, + 256, 29575, 256, 30064, 256, 21271, 256, 30971, 256, 20415, 256, 24489, + 256, 19981, 256, 27852, 256, 25976, 256, 32034, 256, 21443, 256, 22622, + 256, 30465, 256, 33865, 256, 35498, 256, 27578, 256, 36784, 256, 27784, + 256, 25342, 256, 33509, 256, 25504, 256, 30053, 256, 20142, 256, 20841, + 256, 20937, 256, 26753, 256, 31975, 256, 33391, 256, 35538, 256, 37327, + 256, 21237, 256, 21570, 256, 22899, 256, 24300, 256, 26053, 256, 28670, + 256, 31018, 256, 38317, 256, 39530, 256, 40599, 256, 40654, 256, 21147, + 256, 26310, 256, 27511, 256, 36706, 256, 24180, 256, 24976, 256, 25088, + 256, 25754, 256, 28451, 256, 29001, 256, 29833, 256, 31178, 256, 32244, + 256, 32879, 256, 36646, 256, 34030, 256, 36899, 256, 37706, 256, 21015, + 256, 21155, 256, 21693, 256, 28872, 256, 35010, 256, 35498, 256, 24265, + 256, 24565, 256, 25467, 256, 27566, 256, 31806, 256, 29557, 256, 20196, + 256, 22265, 256, 23527, 256, 23994, 256, 24604, 256, 29618, 256, 29801, + 256, 32666, 256, 32838, 256, 37428, 256, 38646, 256, 38728, 256, 38936, + 256, 20363, 256, 31150, 256, 37300, 256, 38584, 256, 24801, 256, 20102, + 256, 20698, 256, 23534, 256, 23615, 256, 26009, 256, 27138, 256, 29134, + 256, 30274, 256, 34044, 256, 36988, 256, 40845, 256, 26248, 256, 38446, + 256, 21129, 256, 26491, 256, 26611, 256, 27969, 256, 28316, 256, 29705, + 256, 30041, 256, 30827, 256, 32016, 256, 39006, 256, 20845, 256, 25134, + 256, 38520, 256, 20523, 256, 23833, 256, 28138, 256, 36650, 256, 24459, + 256, 24900, 256, 26647, 256, 29575, 256, 38534, 256, 21033, 256, 21519, + 256, 23653, 256, 26131, 256, 26446, 256, 26792, 256, 27877, 256, 29702, + 256, 30178, 256, 32633, 256, 35023, 256, 35041, 256, 37324, 256, 38626, + 256, 21311, 256, 28346, 256, 21533, 256, 29136, 256, 29848, 256, 34298, + 256, 38563, 256, 40023, 256, 40607, 256, 26519, 256, 28107, 256, 33256, + 256, 31435, 256, 31520, 256, 31890, 256, 29376, 256, 28825, 256, 35672, + 256, 20160, 256, 33590, 256, 21050, 256, 20999, 256, 24230, 256, 25299, + 256, 31958, 256, 23429, 256, 27934, 256, 26292, 256, 36667, 256, 34892, + 256, 38477, 256, 35211, 256, 24275, 256, 20800, 256, 21952, 256, 22618, + 256, 26228, 256, 20958, 256, 29482, 256, 30410, 256, 31036, 256, 31070, + 256, 31077, 256, 31119, 256, 38742, 256, 31934, 256, 32701, 256, 34322, + 256, 35576, 256, 36920, 256, 37117, 256, 39151, 256, 39164, 256, 39208, + 256, 40372, 256, 37086, 256, 38583, 256, 20398, 256, 20711, 256, 20813, + 256, 21193, 256, 21220, 256, 21329, 256, 21917, 256, 22022, 256, 22120, + 256, 22592, 256, 22696, 256, 23652, 256, 23662, 256, 24724, 256, 24936, + 256, 24974, 256, 25074, 256, 25935, 256, 26082, 256, 26257, 256, 26757, + 256, 28023, 256, 28186, 256, 28450, 256, 29038, 256, 29227, 256, 29730, + 256, 30865, 256, 31038, 256, 31049, 256, 31048, 256, 31056, 256, 31062, + 256, 31069, 256, 31117, 256, 31118, 256, 31296, 256, 31361, 256, 31680, + 256, 32244, 256, 32265, 256, 32321, 256, 32626, 256, 32773, 256, 33261, + 256, 33401, 256, 33401, 256, 33879, 256, 35088, 256, 35222, 256, 35585, + 256, 35641, 256, 36051, 256, 36104, 256, 36790, 256, 36920, 256, 38627, + 256, 38911, 256, 38971, 256, 24693, 256, 55376, 57070, 256, 33304, 256, + 20006, 256, 20917, 256, 20840, 256, 20352, 256, 20805, 256, 20864, 256, + 21191, 256, 21242, 256, 21917, 256, 21845, 256, 21913, 256, 21986, 256, + 22618, 256, 22707, 256, 22852, 256, 22868, 256, 23138, 256, 23336, 256, + 24274, 256, 24281, 256, 24425, 256, 24493, 256, 24792, 256, 24910, 256, + 24840, 256, 24974, 256, 24928, 256, 25074, 256, 25140, 256, 25540, 256, + 25628, 256, 25682, 256, 25942, 256, 26228, 256, 26391, 256, 26395, 256, + 26454, 256, 27513, 256, 27578, 256, 27969, 256, 28379, 256, 28363, 256, + 28450, 256, 28702, 256, 29038, 256, 30631, 256, 29237, 256, 29359, 256, + 29482, 256, 29809, 256, 29958, 256, 30011, 256, 30237, 256, 30239, 256, + 30410, 256, 30427, 256, 30452, 256, 30538, 256, 30528, 256, 30924, 256, + 31409, 256, 31680, 256, 31867, 256, 32091, 256, 32244, 256, 32574, 256, + 32773, 256, 33618, 256, 33775, 256, 34681, 256, 35137, 256, 35206, 256, + 35222, 256, 35519, 256, 35576, 256, 35531, 256, 35585, 256, 35582, 256, + 35565, 256, 35641, 256, 35722, 256, 36104, 256, 36664, 256, 36978, 256, + 37273, 256, 37494, 256, 38524, 256, 38627, 256, 38742, 256, 38875, 256, + 38911, 256, 38923, 256, 38971, 256, 39698, 256, 40860, 256, 55370, 56394, + 256, 55370, 56388, 256, 55372, 57301, 256, 15261, 256, 16408, 256, 16441, + 256, 55380, 56905, 256, 55383, 56528, 256, 55391, 57043, 256, 40771, 256, + 40846, 514, 102, 102, 514, 102, 105, 514, 102, 108, 770, 102, 102, 105, + 770, 102, 102, 108, 514, 383, 116, 514, 115, 116, 514, 1396, 1398, 514, + 1396, 1381, 514, 1396, 1387, 514, 1406, 1398, 514, 1396, 1389, 512, 1497, + 1460, 512, 1522, 1463, 262, 1506, 262, 1488, 262, 1491, 262, 1492, 262, + 1499, 262, 1500, 262, 1501, 262, 1512, 262, 1514, 262, 43, 512, 1513, + 1473, 512, 1513, 1474, 512, 64329, 1473, 512, 64329, 1474, 512, 1488, + 1463, 512, 1488, 1464, 512, 1488, 1468, 512, 1489, 1468, 512, 1490, 1468, + 512, 1491, 1468, 512, 1492, 1468, 512, 1493, 1468, 512, 1494, 1468, 512, + 1496, 1468, 512, 1497, 1468, 512, 1498, 1468, 512, 1499, 1468, 512, 1500, + 1468, 512, 1502, 1468, 512, 1504, 1468, 512, 1505, 1468, 512, 1507, 1468, + 512, 1508, 1468, 512, 1510, 1468, 512, 1511, 1468, 512, 1512, 1468, 512, + 1513, 1468, 512, 1514, 1468, 512, 1493, 1465, 512, 1489, 1471, 512, 1499, + 1471, 512, 1508, 1471, 514, 1488, 1500, 267, 1649, 268, 1649, 267, 1659, + 268, 1659, 269, 1659, 270, 1659, 267, 1662, 268, 1662, 269, 1662, 270, + 1662, 267, 1664, 268, 1664, 269, 1664, 270, 1664, 267, 1658, 268, 1658, + 269, 1658, 270, 1658, 267, 1663, 268, 1663, 269, 1663, 270, 1663, 267, + 1657, 268, 1657, 269, 1657, 270, 1657, 267, 1700, 268, 1700, 269, 1700, + 270, 1700, 267, 1702, 268, 1702, 269, 1702, 270, 1702, 267, 1668, 268, + 1668, 269, 1668, 270, 1668, 267, 1667, 268, 1667, 269, 1667, 270, 1667, + 267, 1670, 268, 1670, 269, 1670, 270, 1670, 267, 1671, 268, 1671, 269, + 1671, 270, 1671, 267, 1677, 268, 1677, 267, 1676, 268, 1676, 267, 1678, + 268, 1678, 267, 1672, 268, 1672, 267, 1688, 268, 1688, 267, 1681, 268, + 1681, 267, 1705, 268, 1705, 269, 1705, 270, 1705, 267, 1711, 268, 1711, + 269, 1711, 270, 1711, 267, 1715, 268, 1715, 269, 1715, 270, 1715, 267, + 1713, 268, 1713, 269, 1713, 270, 1713, 267, 1722, 268, 1722, 267, 1723, + 268, 1723, 269, 1723, 270, 1723, 267, 1728, 268, 1728, 267, 1729, 268, + 1729, 269, 1729, 270, 1729, 267, 1726, 268, 1726, 269, 1726, 270, 1726, + 267, 1746, 268, 1746, 267, 1747, 268, 1747, 267, 1709, 268, 1709, 269, + 1709, 270, 1709, 267, 1735, 268, 1735, 267, 1734, 268, 1734, 267, 1736, + 268, 1736, 267, 1655, 267, 1739, 268, 1739, 267, 1733, 268, 1733, 267, + 1737, 268, 1737, 267, 1744, 268, 1744, 269, 1744, 270, 1744, 269, 1609, + 270, 1609, 523, 1574, 1575, 524, 1574, 1575, 523, 1574, 1749, 524, 1574, + 1749, 523, 1574, 1608, 524, 1574, 1608, 523, 1574, 1735, 524, 1574, 1735, + 523, 1574, 1734, 524, 1574, 1734, 523, 1574, 1736, 524, 1574, 1736, 523, + 1574, 1744, 524, 1574, 1744, 525, 1574, 1744, 523, 1574, 1609, 524, 1574, + 1609, 525, 1574, 1609, 267, 1740, 268, 1740, 269, 1740, 270, 1740, 523, + 1574, 1580, 523, 1574, 1581, 523, 1574, 1605, 523, 1574, 1609, 523, 1574, + 1610, 523, 1576, 1580, 523, 1576, 1581, 523, 1576, 1582, 523, 1576, 1605, + 523, 1576, 1609, 523, 1576, 1610, 523, 1578, 1580, 523, 1578, 1581, 523, + 1578, 1582, 523, 1578, 1605, 523, 1578, 1609, 523, 1578, 1610, 523, 1579, + 1580, 523, 1579, 1605, 523, 1579, 1609, 523, 1579, 1610, 523, 1580, 1581, + 523, 1580, 1605, 523, 1581, 1580, 523, 1581, 1605, 523, 1582, 1580, 523, + 1582, 1581, 523, 1582, 1605, 523, 1587, 1580, 523, 1587, 1581, 523, 1587, + 1582, 523, 1587, 1605, 523, 1589, 1581, 523, 1589, 1605, 523, 1590, 1580, + 523, 1590, 1581, 523, 1590, 1582, 523, 1590, 1605, 523, 1591, 1581, 523, + 1591, 1605, 523, 1592, 1605, 523, 1593, 1580, 523, 1593, 1605, 523, 1594, + 1580, 523, 1594, 1605, 523, 1601, 1580, 523, 1601, 1581, 523, 1601, 1582, + 523, 1601, 1605, 523, 1601, 1609, 523, 1601, 1610, 523, 1602, 1581, 523, + 1602, 1605, 523, 1602, 1609, 523, 1602, 1610, 523, 1603, 1575, 523, 1603, + 1580, 523, 1603, 1581, 523, 1603, 1582, 523, 1603, 1604, 523, 1603, 1605, + 523, 1603, 1609, 523, 1603, 1610, 523, 1604, 1580, 523, 1604, 1581, 523, + 1604, 1582, 523, 1604, 1605, 523, 1604, 1609, 523, 1604, 1610, 523, 1605, + 1580, 523, 1605, 1581, 523, 1605, 1582, 523, 1605, 1605, 523, 1605, 1609, + 523, 1605, 1610, 523, 1606, 1580, 523, 1606, 1581, 523, 1606, 1582, 523, + 1606, 1605, 523, 1606, 1609, 523, 1606, 1610, 523, 1607, 1580, 523, 1607, + 1605, 523, 1607, 1609, 523, 1607, 1610, 523, 1610, 1580, 523, 1610, 1581, + 523, 1610, 1582, 523, 1610, 1605, 523, 1610, 1609, 523, 1610, 1610, 523, + 1584, 1648, 523, 1585, 1648, 523, 1609, 1648, 779, 32, 1612, 1617, 779, + 32, 1613, 1617, 779, 32, 1614, 1617, 779, 32, 1615, 1617, 779, 32, 1616, + 1617, 779, 32, 1617, 1648, 524, 1574, 1585, 524, 1574, 1586, 524, 1574, + 1605, 524, 1574, 1606, 524, 1574, 1609, 524, 1574, 1610, 524, 1576, 1585, + 524, 1576, 1586, 524, 1576, 1605, 524, 1576, 1606, 524, 1576, 1609, 524, + 1576, 1610, 524, 1578, 1585, 524, 1578, 1586, 524, 1578, 1605, 524, 1578, + 1606, 524, 1578, 1609, 524, 1578, 1610, 524, 1579, 1585, 524, 1579, 1586, + 524, 1579, 1605, 524, 1579, 1606, 524, 1579, 1609, 524, 1579, 1610, 524, + 1601, 1609, 524, 1601, 1610, 524, 1602, 1609, 524, 1602, 1610, 524, 1603, + 1575, 524, 1603, 1604, 524, 1603, 1605, 524, 1603, 1609, 524, 1603, 1610, + 524, 1604, 1605, 524, 1604, 1609, 524, 1604, 1610, 524, 1605, 1575, 524, + 1605, 1605, 524, 1606, 1585, 524, 1606, 1586, 524, 1606, 1605, 524, 1606, + 1606, 524, 1606, 1609, 524, 1606, 1610, 524, 1609, 1648, 524, 1610, 1585, + 524, 1610, 1586, 524, 1610, 1605, 524, 1610, 1606, 524, 1610, 1609, 524, + 1610, 1610, 525, 1574, 1580, 525, 1574, 1581, 525, 1574, 1582, 525, 1574, + 1605, 525, 1574, 1607, 525, 1576, 1580, 525, 1576, 1581, 525, 1576, 1582, + 525, 1576, 1605, 525, 1576, 1607, 525, 1578, 1580, 525, 1578, 1581, 525, + 1578, 1582, 525, 1578, 1605, 525, 1578, 1607, 525, 1579, 1605, 525, 1580, + 1581, 525, 1580, 1605, 525, 1581, 1580, 525, 1581, 1605, 525, 1582, 1580, + 525, 1582, 1605, 525, 1587, 1580, 525, 1587, 1581, 525, 1587, 1582, 525, + 1587, 1605, 525, 1589, 1581, 525, 1589, 1582, 525, 1589, 1605, 525, 1590, + 1580, 525, 1590, 1581, 525, 1590, 1582, 525, 1590, 1605, 525, 1591, 1581, + 525, 1592, 1605, 525, 1593, 1580, 525, 1593, 1605, 525, 1594, 1580, 525, + 1594, 1605, 525, 1601, 1580, 525, 1601, 1581, 525, 1601, 1582, 525, 1601, + 1605, 525, 1602, 1581, 525, 1602, 1605, 525, 1603, 1580, 525, 1603, 1581, + 525, 1603, 1582, 525, 1603, 1604, 525, 1603, 1605, 525, 1604, 1580, 525, + 1604, 1581, 525, 1604, 1582, 525, 1604, 1605, 525, 1604, 1607, 525, 1605, + 1580, 525, 1605, 1581, 525, 1605, 1582, 525, 1605, 1605, 525, 1606, 1580, + 525, 1606, 1581, 525, 1606, 1582, 525, 1606, 1605, 525, 1606, 1607, 525, + 1607, 1580, 525, 1607, 1605, 525, 1607, 1648, 525, 1610, 1580, 525, 1610, + 1581, 525, 1610, 1582, 525, 1610, 1605, 525, 1610, 1607, 526, 1574, 1605, + 526, 1574, 1607, 526, 1576, 1605, 526, 1576, 1607, 526, 1578, 1605, 526, + 1578, 1607, 526, 1579, 1605, 526, 1579, 1607, 526, 1587, 1605, 526, 1587, + 1607, 526, 1588, 1605, 526, 1588, 1607, 526, 1603, 1604, 526, 1603, 1605, + 526, 1604, 1605, 526, 1606, 1605, 526, 1606, 1607, 526, 1610, 1605, 526, + 1610, 1607, 782, 1600, 1614, 1617, 782, 1600, 1615, 1617, 782, 1600, + 1616, 1617, 523, 1591, 1609, 523, 1591, 1610, 523, 1593, 1609, 523, 1593, + 1610, 523, 1594, 1609, 523, 1594, 1610, 523, 1587, 1609, 523, 1587, 1610, + 523, 1588, 1609, 523, 1588, 1610, 523, 1581, 1609, 523, 1581, 1610, 523, + 1580, 1609, 523, 1580, 1610, 523, 1582, 1609, 523, 1582, 1610, 523, 1589, + 1609, 523, 1589, 1610, 523, 1590, 1609, 523, 1590, 1610, 523, 1588, 1580, + 523, 1588, 1581, 523, 1588, 1582, 523, 1588, 1605, 523, 1588, 1585, 523, + 1587, 1585, 523, 1589, 1585, 523, 1590, 1585, 524, 1591, 1609, 524, 1591, + 1610, 524, 1593, 1609, 524, 1593, 1610, 524, 1594, 1609, 524, 1594, 1610, + 524, 1587, 1609, 524, 1587, 1610, 524, 1588, 1609, 524, 1588, 1610, 524, + 1581, 1609, 524, 1581, 1610, 524, 1580, 1609, 524, 1580, 1610, 524, 1582, + 1609, 524, 1582, 1610, 524, 1589, 1609, 524, 1589, 1610, 524, 1590, 1609, + 524, 1590, 1610, 524, 1588, 1580, 524, 1588, 1581, 524, 1588, 1582, 524, + 1588, 1605, 524, 1588, 1585, 524, 1587, 1585, 524, 1589, 1585, 524, 1590, + 1585, 525, 1588, 1580, 525, 1588, 1581, 525, 1588, 1582, 525, 1588, 1605, + 525, 1587, 1607, 525, 1588, 1607, 525, 1591, 1605, 526, 1587, 1580, 526, + 1587, 1581, 526, 1587, 1582, 526, 1588, 1580, 526, 1588, 1581, 526, 1588, + 1582, 526, 1591, 1605, 526, 1592, 1605, 524, 1575, 1611, 523, 1575, 1611, + 781, 1578, 1580, 1605, 780, 1578, 1581, 1580, 781, 1578, 1581, 1580, 781, + 1578, 1581, 1605, 781, 1578, 1582, 1605, 781, 1578, 1605, 1580, 781, + 1578, 1605, 1581, 781, 1578, 1605, 1582, 780, 1580, 1605, 1581, 781, + 1580, 1605, 1581, 780, 1581, 1605, 1610, 780, 1581, 1605, 1609, 781, + 1587, 1581, 1580, 781, 1587, 1580, 1581, 780, 1587, 1580, 1609, 780, + 1587, 1605, 1581, 781, 1587, 1605, 1581, 781, 1587, 1605, 1580, 780, + 1587, 1605, 1605, 781, 1587, 1605, 1605, 780, 1589, 1581, 1581, 781, + 1589, 1581, 1581, 780, 1589, 1605, 1605, 780, 1588, 1581, 1605, 781, + 1588, 1581, 1605, 780, 1588, 1580, 1610, 780, 1588, 1605, 1582, 781, + 1588, 1605, 1582, 780, 1588, 1605, 1605, 781, 1588, 1605, 1605, 780, + 1590, 1581, 1609, 780, 1590, 1582, 1605, 781, 1590, 1582, 1605, 780, + 1591, 1605, 1581, 781, 1591, 1605, 1581, 781, 1591, 1605, 1605, 780, + 1591, 1605, 1610, 780, 1593, 1580, 1605, 780, 1593, 1605, 1605, 781, + 1593, 1605, 1605, 780, 1593, 1605, 1609, 780, 1594, 1605, 1605, 780, + 1594, 1605, 1610, 780, 1594, 1605, 1609, 780, 1601, 1582, 1605, 781, + 1601, 1582, 1605, 780, 1602, 1605, 1581, 780, 1602, 1605, 1605, 780, + 1604, 1581, 1605, 780, 1604, 1581, 1610, 780, 1604, 1581, 1609, 781, + 1604, 1580, 1580, 780, 1604, 1580, 1580, 780, 1604, 1582, 1605, 781, + 1604, 1582, 1605, 780, 1604, 1605, 1581, 781, 1604, 1605, 1581, 781, + 1605, 1581, 1580, 781, 1605, 1581, 1605, 780, 1605, 1581, 1610, 781, + 1605, 1580, 1581, 781, 1605, 1580, 1605, 781, 1605, 1582, 1580, 781, + 1605, 1582, 1605, 781, 1605, 1580, 1582, 781, 1607, 1605, 1580, 781, + 1607, 1605, 1605, 781, 1606, 1581, 1605, 780, 1606, 1581, 1609, 780, + 1606, 1580, 1605, 781, 1606, 1580, 1605, 780, 1606, 1580, 1609, 780, + 1606, 1605, 1610, 780, 1606, 1605, 1609, 780, 1610, 1605, 1605, 781, + 1610, 1605, 1605, 780, 1576, 1582, 1610, 780, 1578, 1580, 1610, 780, + 1578, 1580, 1609, 780, 1578, 1582, 1610, 780, 1578, 1582, 1609, 780, + 1578, 1605, 1610, 780, 1578, 1605, 1609, 780, 1580, 1605, 1610, 780, + 1580, 1581, 1609, 780, 1580, 1605, 1609, 780, 1587, 1582, 1609, 780, + 1589, 1581, 1610, 780, 1588, 1581, 1610, 780, 1590, 1581, 1610, 780, + 1604, 1580, 1610, 780, 1604, 1605, 1610, 780, 1610, 1581, 1610, 780, + 1610, 1580, 1610, 780, 1610, 1605, 1610, 780, 1605, 1605, 1610, 780, + 1602, 1605, 1610, 780, 1606, 1581, 1610, 781, 1602, 1605, 1581, 781, + 1604, 1581, 1605, 780, 1593, 1605, 1610, 780, 1603, 1605, 1610, 781, + 1606, 1580, 1581, 780, 1605, 1582, 1610, 781, 1604, 1580, 1605, 780, + 1603, 1605, 1605, 780, 1604, 1580, 1605, 780, 1606, 1580, 1581, 780, + 1580, 1581, 1610, 780, 1581, 1580, 1610, 780, 1605, 1580, 1610, 780, + 1601, 1605, 1610, 780, 1576, 1581, 1610, 781, 1603, 1605, 1605, 781, + 1593, 1580, 1605, 781, 1589, 1605, 1605, 780, 1587, 1582, 1610, 780, + 1606, 1580, 1610, 779, 1589, 1604, 1746, 779, 1602, 1604, 1746, 1035, + 1575, 1604, 1604, 1607, 1035, 1575, 1603, 1576, 1585, 1035, 1605, 1581, + 1605, 1583, 1035, 1589, 1604, 1593, 1605, 1035, 1585, 1587, 1608, 1604, + 1035, 1593, 1604, 1610, 1607, 1035, 1608, 1587, 1604, 1605, 779, 1589, + 1604, 1609, 4619, 1589, 1604, 1609, 32, 1575, 1604, 1604, 1607, 32, 1593, + 1604, 1610, 1607, 32, 1608, 1587, 1604, 1605, 2059, 1580, 1604, 32, 1580, + 1604, 1575, 1604, 1607, 1035, 1585, 1740, 1575, 1604, 265, 44, 265, + 12289, 265, 12290, 265, 58, 265, 59, 265, 33, 265, 63, 265, 12310, 265, + 12311, 265, 8230, 265, 8229, 265, 8212, 265, 8211, 265, 95, 265, 95, 265, + 40, 265, 41, 265, 123, 265, 125, 265, 12308, 265, 12309, 265, 12304, 265, + 12305, 265, 12298, 265, 12299, 265, 12296, 265, 12297, 265, 12300, 265, + 12301, 265, 12302, 265, 12303, 265, 91, 265, 93, 258, 8254, 258, 8254, + 258, 8254, 258, 8254, 258, 95, 258, 95, 258, 95, 271, 44, 271, 12289, + 271, 46, 271, 59, 271, 58, 271, 63, 271, 33, 271, 8212, 271, 40, 271, 41, + 271, 123, 271, 125, 271, 12308, 271, 12309, 271, 35, 271, 38, 271, 42, + 271, 43, 271, 45, 271, 60, 271, 62, 271, 61, 271, 92, 271, 36, 271, 37, + 271, 64, 523, 32, 1611, 526, 1600, 1611, 523, 32, 1612, 523, 32, 1613, + 523, 32, 1614, 526, 1600, 1614, 523, 32, 1615, 526, 1600, 1615, 523, 32, + 1616, 526, 1600, 1616, 523, 32, 1617, 526, 1600, 1617, 523, 32, 1618, + 526, 1600, 1618, 267, 1569, 267, 1570, 268, 1570, 267, 1571, 268, 1571, + 267, 1572, 268, 1572, 267, 1573, 268, 1573, 267, 1574, 268, 1574, 269, + 1574, 270, 1574, 267, 1575, 268, 1575, 267, 1576, 268, 1576, 269, 1576, + 270, 1576, 267, 1577, 268, 1577, 267, 1578, 268, 1578, 269, 1578, 270, + 1578, 267, 1579, 268, 1579, 269, 1579, 270, 1579, 267, 1580, 268, 1580, + 269, 1580, 270, 1580, 267, 1581, 268, 1581, 269, 1581, 270, 1581, 267, + 1582, 268, 1582, 269, 1582, 270, 1582, 267, 1583, 268, 1583, 267, 1584, + 268, 1584, 267, 1585, 268, 1585, 267, 1586, 268, 1586, 267, 1587, 268, + 1587, 269, 1587, 270, 1587, 267, 1588, 268, 1588, 269, 1588, 270, 1588, + 267, 1589, 268, 1589, 269, 1589, 270, 1589, 267, 1590, 268, 1590, 269, + 1590, 270, 1590, 267, 1591, 268, 1591, 269, 1591, 270, 1591, 267, 1592, + 268, 1592, 269, 1592, 270, 1592, 267, 1593, 268, 1593, 269, 1593, 270, + 1593, 267, 1594, 268, 1594, 269, 1594, 270, 1594, 267, 1601, 268, 1601, + 269, 1601, 270, 1601, 267, 1602, 268, 1602, 269, 1602, 270, 1602, 267, + 1603, 268, 1603, 269, 1603, 270, 1603, 267, 1604, 268, 1604, 269, 1604, + 270, 1604, 267, 1605, 268, 1605, 269, 1605, 270, 1605, 267, 1606, 268, + 1606, 269, 1606, 270, 1606, 267, 1607, 268, 1607, 269, 1607, 270, 1607, + 267, 1608, 268, 1608, 267, 1609, 268, 1609, 267, 1610, 268, 1610, 269, + 1610, 270, 1610, 523, 1604, 1570, 524, 1604, 1570, 523, 1604, 1571, 524, + 1604, 1571, 523, 1604, 1573, 524, 1604, 1573, 523, 1604, 1575, 524, 1604, + 1575, 264, 33, 264, 34, 264, 35, 264, 36, 264, 37, 264, 38, 264, 39, 264, + 40, 264, 41, 264, 42, 264, 43, 264, 44, 264, 45, 264, 46, 264, 47, 264, + 48, 264, 49, 264, 50, 264, 51, 264, 52, 264, 53, 264, 54, 264, 55, 264, + 56, 264, 57, 264, 58, 264, 59, 264, 60, 264, 61, 264, 62, 264, 63, 264, + 64, 264, 65, 264, 66, 264, 67, 264, 68, 264, 69, 264, 70, 264, 71, 264, + 72, 264, 73, 264, 74, 264, 75, 264, 76, 264, 77, 264, 78, 264, 79, 264, + 80, 264, 81, 264, 82, 264, 83, 264, 84, 264, 85, 264, 86, 264, 87, 264, + 88, 264, 89, 264, 90, 264, 91, 264, 92, 264, 93, 264, 94, 264, 95, 264, + 96, 264, 97, 264, 98, 264, 99, 264, 100, 264, 101, 264, 102, 264, 103, + 264, 104, 264, 105, 264, 106, 264, 107, 264, 108, 264, 109, 264, 110, + 264, 111, 264, 112, 264, 113, 264, 114, 264, 115, 264, 116, 264, 117, + 264, 118, 264, 119, 264, 120, 264, 121, 264, 122, 264, 123, 264, 124, + 264, 125, 264, 126, 264, 10629, 264, 10630, 272, 12290, 272, 12300, 272, + 12301, 272, 12289, 272, 12539, 272, 12530, 272, 12449, 272, 12451, 272, + 12453, 272, 12455, 272, 12457, 272, 12515, 272, 12517, 272, 12519, 272, + 12483, 272, 12540, 272, 12450, 272, 12452, 272, 12454, 272, 12456, 272, + 12458, 272, 12459, 272, 12461, 272, 12463, 272, 12465, 272, 12467, 272, + 12469, 272, 12471, 272, 12473, 272, 12475, 272, 12477, 272, 12479, 272, + 12481, 272, 12484, 272, 12486, 272, 12488, 272, 12490, 272, 12491, 272, + 12492, 272, 12493, 272, 12494, 272, 12495, 272, 12498, 272, 12501, 272, + 12504, 272, 12507, 272, 12510, 272, 12511, 272, 12512, 272, 12513, 272, + 12514, 272, 12516, 272, 12518, 272, 12520, 272, 12521, 272, 12522, 272, + 12523, 272, 12524, 272, 12525, 272, 12527, 272, 12531, 272, 12441, 272, + 12442, 272, 12644, 272, 12593, 272, 12594, 272, 12595, 272, 12596, 272, + 12597, 272, 12598, 272, 12599, 272, 12600, 272, 12601, 272, 12602, 272, + 12603, 272, 12604, 272, 12605, 272, 12606, 272, 12607, 272, 12608, 272, + 12609, 272, 12610, 272, 12611, 272, 12612, 272, 12613, 272, 12614, 272, + 12615, 272, 12616, 272, 12617, 272, 12618, 272, 12619, 272, 12620, 272, + 12621, 272, 12622, 272, 12623, 272, 12624, 272, 12625, 272, 12626, 272, + 12627, 272, 12628, 272, 12629, 272, 12630, 272, 12631, 272, 12632, 272, + 12633, 272, 12634, 272, 12635, 272, 12636, 272, 12637, 272, 12638, 272, + 12639, 272, 12640, 272, 12641, 272, 12642, 272, 12643, 264, 162, 264, + 163, 264, 172, 264, 175, 264, 166, 264, 165, 264, 8361, 272, 9474, 272, + 8592, 272, 8593, 272, 8594, 272, 8595, 272, 9632, 272, 9675, 512, 55300, + 56473, 55300, 56506, 512, 55300, 56475, 55300, 56506, 512, 55300, 56485, + 55300, 56506, 512, 55300, 56625, 55300, 56615, 512, 55300, 56626, 55300, + 56615, 512, 55348, 56663, 55348, 56677, 512, 55348, 56664, 55348, 56677, + 512, 55348, 56671, 55348, 56686, 512, 55348, 56671, 55348, 56687, 512, + 55348, 56671, 55348, 56688, 512, 55348, 56671, 55348, 56689, 512, 55348, + 56671, 55348, 56690, 512, 55348, 56761, 55348, 56677, 512, 55348, 56762, + 55348, 56677, 512, 55348, 56763, 55348, 56686, 512, 55348, 56764, 55348, + 56686, 512, 55348, 56763, 55348, 56687, 512, 55348, 56764, 55348, 56687, + 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, + 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, + 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, + 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, + 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, + 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, + 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, + 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, + 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, + 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, + 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, + 103, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, + 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, + 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, + 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, + 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, + 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, + 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, + 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, + 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, + 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 67, 262, 68, 262, 71, + 262, 74, 262, 75, 262, 78, 262, 79, 262, 80, 262, 81, 262, 83, 262, 84, + 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, + 262, 99, 262, 100, 262, 102, 262, 104, 262, 105, 262, 106, 262, 107, 262, + 108, 262, 109, 262, 110, 262, 112, 262, 113, 262, 114, 262, 115, 262, + 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, + 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, + 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, + 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, + 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, + 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, + 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, + 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, + 262, 68, 262, 69, 262, 70, 262, 71, 262, 74, 262, 75, 262, 76, 262, 77, + 262, 78, 262, 79, 262, 80, 262, 81, 262, 83, 262, 84, 262, 85, 262, 86, + 262, 87, 262, 88, 262, 89, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, + 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, + 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, + 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, + 262, 65, 262, 66, 262, 68, 262, 69, 262, 70, 262, 71, 262, 73, 262, 74, + 262, 75, 262, 76, 262, 77, 262, 79, 262, 83, 262, 84, 262, 85, 262, 86, + 262, 87, 262, 88, 262, 89, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, + 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, + 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, + 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, + 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, + 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, + 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, + 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, + 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, + 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, + 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, + 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, + 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, + 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, + 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, + 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, + 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, + 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, + 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, + 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, + 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, + 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, + 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, + 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, + 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, + 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, + 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, + 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, + 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, + 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, + 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, + 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, + 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, + 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, + 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, + 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, + 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, + 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, + 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, + 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, + 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, + 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, + 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, + 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, + 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, + 262, 121, 262, 122, 262, 305, 262, 567, 262, 913, 262, 914, 262, 915, + 262, 916, 262, 917, 262, 918, 262, 919, 262, 920, 262, 921, 262, 922, + 262, 923, 262, 924, 262, 925, 262, 926, 262, 927, 262, 928, 262, 929, + 262, 1012, 262, 931, 262, 932, 262, 933, 262, 934, 262, 935, 262, 936, + 262, 937, 262, 8711, 262, 945, 262, 946, 262, 947, 262, 948, 262, 949, + 262, 950, 262, 951, 262, 952, 262, 953, 262, 954, 262, 955, 262, 956, + 262, 957, 262, 958, 262, 959, 262, 960, 262, 961, 262, 962, 262, 963, + 262, 964, 262, 965, 262, 966, 262, 967, 262, 968, 262, 969, 262, 8706, + 262, 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, 262, 982, 262, 913, + 262, 914, 262, 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, 920, + 262, 921, 262, 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, 927, + 262, 928, 262, 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, 934, + 262, 935, 262, 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, 947, + 262, 948, 262, 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, 954, + 262, 955, 262, 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, 961, + 262, 962, 262, 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, 968, + 262, 969, 262, 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, + 262, 982, 262, 913, 262, 914, 262, 915, 262, 916, 262, 917, 262, 918, + 262, 919, 262, 920, 262, 921, 262, 922, 262, 923, 262, 924, 262, 925, + 262, 926, 262, 927, 262, 928, 262, 929, 262, 1012, 262, 931, 262, 932, + 262, 933, 262, 934, 262, 935, 262, 936, 262, 937, 262, 8711, 262, 945, + 262, 946, 262, 947, 262, 948, 262, 949, 262, 950, 262, 951, 262, 952, + 262, 953, 262, 954, 262, 955, 262, 956, 262, 957, 262, 958, 262, 959, + 262, 960, 262, 961, 262, 962, 262, 963, 262, 964, 262, 965, 262, 966, + 262, 967, 262, 968, 262, 969, 262, 8706, 262, 1013, 262, 977, 262, 1008, + 262, 981, 262, 1009, 262, 982, 262, 913, 262, 914, 262, 915, 262, 916, + 262, 917, 262, 918, 262, 919, 262, 920, 262, 921, 262, 922, 262, 923, + 262, 924, 262, 925, 262, 926, 262, 927, 262, 928, 262, 929, 262, 1012, + 262, 931, 262, 932, 262, 933, 262, 934, 262, 935, 262, 936, 262, 937, + 262, 8711, 262, 945, 262, 946, 262, 947, 262, 948, 262, 949, 262, 950, + 262, 951, 262, 952, 262, 953, 262, 954, 262, 955, 262, 956, 262, 957, + 262, 958, 262, 959, 262, 960, 262, 961, 262, 962, 262, 963, 262, 964, + 262, 965, 262, 966, 262, 967, 262, 968, 262, 969, 262, 8706, 262, 1013, + 262, 977, 262, 1008, 262, 981, 262, 1009, 262, 982, 262, 913, 262, 914, + 262, 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, 920, 262, 921, + 262, 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, 927, 262, 928, + 262, 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, 934, 262, 935, + 262, 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, 947, 262, 948, + 262, 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, 954, 262, 955, + 262, 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, 961, 262, 962, + 262, 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, 968, 262, 969, + 262, 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, 262, 982, + 262, 988, 262, 989, 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, 262, 53, + 262, 54, 262, 55, 262, 56, 262, 57, 262, 48, 262, 49, 262, 50, 262, 51, + 262, 52, 262, 53, 262, 54, 262, 55, 262, 56, 262, 57, 262, 48, 262, 49, + 262, 50, 262, 51, 262, 52, 262, 53, 262, 54, 262, 55, 262, 56, 262, 57, + 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, 262, 53, 262, 54, 262, 55, + 262, 56, 262, 57, 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, 262, 53, + 262, 54, 262, 55, 262, 56, 262, 57, 262, 1575, 262, 1576, 262, 1580, 262, + 1583, 262, 1608, 262, 1586, 262, 1581, 262, 1591, 262, 1610, 262, 1603, + 262, 1604, 262, 1605, 262, 1606, 262, 1587, 262, 1593, 262, 1601, 262, + 1589, 262, 1602, 262, 1585, 262, 1588, 262, 1578, 262, 1579, 262, 1582, + 262, 1584, 262, 1590, 262, 1592, 262, 1594, 262, 1646, 262, 1722, 262, + 1697, 262, 1647, 262, 1576, 262, 1580, 262, 1607, 262, 1581, 262, 1610, + 262, 1603, 262, 1604, 262, 1605, 262, 1606, 262, 1587, 262, 1593, 262, + 1601, 262, 1589, 262, 1602, 262, 1588, 262, 1578, 262, 1579, 262, 1582, + 262, 1590, 262, 1594, 262, 1580, 262, 1581, 262, 1610, 262, 1604, 262, + 1606, 262, 1587, 262, 1593, 262, 1589, 262, 1602, 262, 1588, 262, 1582, + 262, 1590, 262, 1594, 262, 1722, 262, 1647, 262, 1576, 262, 1580, 262, + 1607, 262, 1581, 262, 1591, 262, 1610, 262, 1603, 262, 1605, 262, 1606, + 262, 1587, 262, 1593, 262, 1601, 262, 1589, 262, 1602, 262, 1588, 262, + 1578, 262, 1579, 262, 1582, 262, 1590, 262, 1592, 262, 1594, 262, 1646, + 262, 1697, 262, 1575, 262, 1576, 262, 1580, 262, 1583, 262, 1607, 262, + 1608, 262, 1586, 262, 1581, 262, 1591, 262, 1610, 262, 1604, 262, 1605, + 262, 1606, 262, 1587, 262, 1593, 262, 1601, 262, 1589, 262, 1602, 262, + 1585, 262, 1588, 262, 1578, 262, 1579, 262, 1582, 262, 1584, 262, 1590, + 262, 1592, 262, 1594, 262, 1576, 262, 1580, 262, 1583, 262, 1608, 262, + 1586, 262, 1581, 262, 1591, 262, 1610, 262, 1604, 262, 1605, 262, 1606, + 262, 1587, 262, 1593, 262, 1601, 262, 1589, 262, 1602, 262, 1585, 262, + 1588, 262, 1578, 262, 1579, 262, 1582, 262, 1584, 262, 1590, 262, 1592, + 262, 1594, 514, 48, 46, 514, 48, 44, 514, 49, 44, 514, 50, 44, 514, 51, + 44, 514, 52, 44, 514, 53, 44, 514, 54, 44, 514, 55, 44, 514, 56, 44, 514, + 57, 44, 770, 40, 65, 41, 770, 40, 66, 41, 770, 40, 67, 41, 770, 40, 68, + 41, 770, 40, 69, 41, 770, 40, 70, 41, 770, 40, 71, 41, 770, 40, 72, 41, + 770, 40, 73, 41, 770, 40, 74, 41, 770, 40, 75, 41, 770, 40, 76, 41, 770, + 40, 77, 41, 770, 40, 78, 41, 770, 40, 79, 41, 770, 40, 80, 41, 770, 40, + 81, 41, 770, 40, 82, 41, 770, 40, 83, 41, 770, 40, 84, 41, 770, 40, 85, + 41, 770, 40, 86, 41, 770, 40, 87, 41, 770, 40, 88, 41, 770, 40, 89, 41, + 770, 40, 90, 41, 770, 12308, 83, 12309, 263, 67, 263, 82, 519, 67, 68, + 519, 87, 90, 266, 65, 266, 66, 266, 67, 266, 68, 266, 69, 266, 70, 266, + 71, 266, 72, 266, 73, 266, 74, 266, 75, 266, 76, 266, 77, 266, 78, 266, + 79, 266, 80, 266, 81, 266, 82, 266, 83, 266, 84, 266, 85, 266, 86, 266, + 87, 266, 88, 266, 89, 266, 90, 522, 72, 86, 522, 77, 86, 522, 83, 68, + 522, 83, 83, 778, 80, 80, 86, 522, 87, 67, 515, 77, 67, 515, 77, 68, 522, + 68, 74, 522, 12411, 12363, 522, 12467, 12467, 266, 12469, 266, 25163, + 266, 23383, 266, 21452, 266, 12487, 266, 20108, 266, 22810, 266, 35299, + 266, 22825, 266, 20132, 266, 26144, 266, 28961, 266, 26009, 266, 21069, + 266, 24460, 266, 20877, 266, 26032, 266, 21021, 266, 32066, 266, 29983, + 266, 36009, 266, 22768, 266, 21561, 266, 28436, 266, 25237, 266, 25429, + 266, 19968, 266, 19977, 266, 36938, 266, 24038, 266, 20013, 266, 21491, + 266, 25351, 266, 36208, 266, 25171, 266, 31105, 266, 31354, 266, 21512, + 266, 28288, 266, 26377, 266, 26376, 266, 30003, 266, 21106, 266, 21942, + 770, 12308, 26412, 12309, 770, 12308, 19977, 12309, 770, 12308, 20108, + 12309, 770, 12308, 23433, 12309, 770, 12308, 28857, 12309, 770, 12308, + 25171, 12309, 770, 12308, 30423, 12309, 770, 12308, 21213, 12309, 770, + 12308, 25943, 12309, 263, 24471, 263, 21487, 256, 20029, 256, 20024, 256, + 20033, 256, 55360, 56610, 256, 20320, 256, 20398, 256, 20411, 256, 20482, + 256, 20602, 256, 20633, 256, 20711, 256, 20687, 256, 13470, 256, 55361, + 56890, 256, 20813, 256, 20820, 256, 20836, 256, 20855, 256, 55361, 56604, + 256, 13497, 256, 20839, 256, 20877, 256, 55361, 56651, 256, 20887, 256, + 20900, 256, 20172, 256, 20908, 256, 20917, 256, 55396, 56799, 256, 20981, + 256, 20995, 256, 13535, 256, 21051, 256, 21062, 256, 21106, 256, 21111, + 256, 13589, 256, 21191, 256, 21193, 256, 21220, 256, 21242, 256, 21253, + 256, 21254, 256, 21271, 256, 21321, 256, 21329, 256, 21338, 256, 21363, + 256, 21373, 256, 21375, 256, 21375, 256, 21375, 256, 55362, 56876, 256, + 28784, 256, 21450, 256, 21471, 256, 55362, 57187, 256, 21483, 256, 21489, + 256, 21510, 256, 21662, 256, 21560, 256, 21576, 256, 21608, 256, 21666, + 256, 21750, 256, 21776, 256, 21843, 256, 21859, 256, 21892, 256, 21892, + 256, 21913, 256, 21931, 256, 21939, 256, 21954, 256, 22294, 256, 22022, + 256, 22295, 256, 22097, 256, 22132, 256, 20999, 256, 22766, 256, 22478, + 256, 22516, 256, 22541, 256, 22411, 256, 22578, 256, 22577, 256, 22700, + 256, 55365, 56548, 256, 22770, 256, 22775, 256, 22790, 256, 22810, 256, + 22818, 256, 22882, 256, 55365, 57000, 256, 55365, 57066, 256, 23020, 256, + 23067, 256, 23079, 256, 23000, 256, 23142, 256, 14062, 256, 14076, 256, + 23304, 256, 23358, 256, 23358, 256, 55366, 56776, 256, 23491, 256, 23512, + 256, 23527, 256, 23539, 256, 55366, 57112, 256, 23551, 256, 23558, 256, + 24403, 256, 23586, 256, 14209, 256, 23648, 256, 23662, 256, 23744, 256, + 23693, 256, 55367, 56804, 256, 23875, 256, 55367, 56806, 256, 23918, 256, + 23915, 256, 23932, 256, 24033, 256, 24034, 256, 14383, 256, 24061, 256, + 24104, 256, 24125, 256, 24169, 256, 14434, 256, 55368, 56707, 256, 14460, + 256, 24240, 256, 24243, 256, 24246, 256, 24266, 256, 55400, 57234, 256, + 24318, 256, 55368, 57137, 256, 55368, 57137, 256, 33281, 256, 24354, 256, + 24354, 256, 14535, 256, 55372, 57016, 256, 55384, 56794, 256, 24418, 256, + 24427, 256, 14563, 256, 24474, 256, 24525, 256, 24535, 256, 24569, 256, + 24705, 256, 14650, 256, 14620, 256, 24724, 256, 55369, 57044, 256, 24775, + 256, 24904, 256, 24908, 256, 24910, 256, 24908, 256, 24954, 256, 24974, + 256, 25010, 256, 24996, 256, 25007, 256, 25054, 256, 25074, 256, 25078, + 256, 25104, 256, 25115, 256, 25181, 256, 25265, 256, 25300, 256, 25424, + 256, 55370, 57100, 256, 25405, 256, 25340, 256, 25448, 256, 25475, 256, + 25572, 256, 55370, 57329, 256, 25634, 256, 25541, 256, 25513, 256, 14894, + 256, 25705, 256, 25726, 256, 25757, 256, 25719, 256, 14956, 256, 25935, + 256, 25964, 256, 55372, 56330, 256, 26083, 256, 26360, 256, 26185, 256, + 15129, 256, 26257, 256, 15112, 256, 15076, 256, 20882, 256, 20885, 256, + 26368, 256, 26268, 256, 32941, 256, 17369, 256, 26391, 256, 26395, 256, + 26401, 256, 26462, 256, 26451, 256, 55372, 57283, 256, 15177, 256, 26618, + 256, 26501, 256, 26706, 256, 26757, 256, 55373, 56429, 256, 26766, 256, + 26655, 256, 26900, 256, 15261, 256, 26946, 256, 27043, 256, 27114, 256, + 27304, 256, 55373, 56995, 256, 27355, 256, 15384, 256, 27425, 256, 55374, + 56487, 256, 27476, 256, 15438, 256, 27506, 256, 27551, 256, 27578, 256, + 27579, 256, 55374, 56973, 256, 55367, 56587, 256, 55374, 57082, 256, + 27726, 256, 55375, 56508, 256, 27839, 256, 27853, 256, 27751, 256, 27926, + 256, 27966, 256, 28023, 256, 27969, 256, 28009, 256, 28024, 256, 28037, + 256, 55375, 56606, 256, 27956, 256, 28207, 256, 28270, 256, 15667, 256, + 28363, 256, 28359, 256, 55375, 57041, 256, 28153, 256, 28526, 256, 55375, + 57182, 256, 55375, 57230, 256, 28614, 256, 28729, 256, 28702, 256, 28699, + 256, 15766, 256, 28746, 256, 28797, 256, 28791, 256, 28845, 256, 55361, + 56613, 256, 28997, 256, 55376, 56931, 256, 29084, 256, 55376, 57259, 256, + 29224, 256, 29237, 256, 29264, 256, 55377, 56840, 256, 29312, 256, 29333, + 256, 55377, 57141, 256, 55378, 56340, 256, 29562, 256, 29579, 256, 16044, + 256, 29605, 256, 16056, 256, 16056, 256, 29767, 256, 29788, 256, 29809, + 256, 29829, 256, 29898, 256, 16155, 256, 29988, 256, 55379, 56374, 256, + 30014, 256, 55379, 56466, 256, 30064, 256, 55368, 56735, 256, 30224, 256, + 55379, 57249, 256, 55379, 57272, 256, 55380, 56388, 256, 16380, 256, + 16392, 256, 30452, 256, 55380, 56563, 256, 55380, 56562, 256, 55380, + 56601, 256, 55380, 56627, 256, 30494, 256, 30495, 256, 30495, 256, 30538, + 256, 16441, 256, 30603, 256, 16454, 256, 16534, 256, 55381, 56349, 256, + 30798, 256, 30860, 256, 30924, 256, 16611, 256, 55381, 56870, 256, 31062, + 256, 55381, 56986, 256, 55381, 57029, 256, 31119, 256, 31211, 256, 16687, + 256, 31296, 256, 31306, 256, 31311, 256, 55382, 56700, 256, 55382, 56999, + 256, 55382, 56999, 256, 31470, 256, 16898, 256, 55382, 57259, 256, 31686, + 256, 31689, 256, 16935, 256, 55383, 56448, 256, 31954, 256, 17056, 256, + 31976, 256, 31971, 256, 32000, 256, 55383, 57222, 256, 32099, 256, 17153, + 256, 32199, 256, 32258, 256, 32325, 256, 17204, 256, 55384, 56872, 256, + 55384, 56903, 256, 17241, 256, 55384, 57049, 256, 32634, 256, 55384, + 57150, 256, 32661, 256, 32762, 256, 32773, 256, 55385, 56538, 256, 55385, + 56611, 256, 32864, 256, 55385, 56744, 256, 32880, 256, 55372, 57183, 256, + 17365, 256, 32946, 256, 33027, 256, 17419, 256, 33086, 256, 23221, 256, + 55385, 57255, 256, 55385, 57269, 256, 55372, 57235, 256, 55372, 57244, + 256, 33281, 256, 33284, 256, 36766, 256, 17515, 256, 33425, 256, 33419, + 256, 33437, 256, 21171, 256, 33457, 256, 33459, 256, 33469, 256, 33510, + 256, 55386, 57148, 256, 33509, 256, 33565, 256, 33635, 256, 33709, 256, + 33571, 256, 33725, 256, 33767, 256, 33879, 256, 33619, 256, 33738, 256, + 33740, 256, 33756, 256, 55387, 56374, 256, 55387, 56683, 256, 55387, + 56533, 256, 17707, 256, 34033, 256, 34035, 256, 34070, 256, 55388, 57290, + 256, 34148, 256, 55387, 57132, 256, 17757, 256, 17761, 256, 55387, 57265, + 256, 55388, 56530, 256, 17771, 256, 34384, 256, 34396, 256, 34407, 256, + 34409, 256, 34473, 256, 34440, 256, 34574, 256, 34530, 256, 34681, 256, + 34600, 256, 34667, 256, 34694, 256, 17879, 256, 34785, 256, 34817, 256, + 17913, 256, 34912, 256, 34915, 256, 55389, 56935, 256, 35031, 256, 35038, + 256, 17973, 256, 35066, 256, 13499, 256, 55390, 56494, 256, 55390, 56678, + 256, 18110, 256, 18119, 256, 35488, 256, 35565, 256, 35722, 256, 35925, + 256, 55391, 56488, 256, 36011, 256, 36033, 256, 36123, 256, 36215, 256, + 55391, 57135, 256, 55362, 56324, 256, 36299, 256, 36284, 256, 36336, 256, + 55362, 56542, 256, 36564, 256, 36664, 256, 55393, 56786, 256, 55393, + 56813, 256, 37012, 256, 37105, 256, 37137, 256, 55393, 57134, 256, 37147, + 256, 37432, 256, 37591, 256, 37592, 256, 37500, 256, 37881, 256, 37909, + 256, 55394, 57338, 256, 38283, 256, 18837, 256, 38327, 256, 55395, 56695, + 256, 18918, 256, 38595, 256, 23986, 256, 38691, 256, 55396, 56645, 256, + 55396, 56858, 256, 19054, 256, 19062, 256, 38880, 256, 55397, 56330, 256, + 19122, 256, 55397, 56470, 256, 38923, 256, 38923, 256, 38953, 256, 55397, + 56758, 256, 39138, 256, 19251, 256, 39209, 256, 39335, 256, 39362, 256, + 39422, 256, 19406, 256, 55398, 57136, 256, 39698, 256, 40000, 256, 40189, + 256, 19662, 256, 19693, 256, 40295, 256, 55400, 56526, 256, 19704, 256, + 55400, 56581, 256, 55400, 56846, 256, 55400, 56977, 256, 40635, 256, + 19798, 256, 40697, 256, 40702, 256, 40709, 256, 40719, 256, 40726, 256, + 40763, 256, 55401, 56832, +}; + +/* index tables for the decomposition data */ +#define DECOMP_SHIFT1 6 +#define DECOMP_SHIFT2 4 +static const unsigned char decomp_index0[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 13, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 14, 15, 5, 5, 5, 5, 16, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 17, 18, + 5, 5, 5, 5, 5, 19, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, +}; + +static const unsigned short decomp_index1[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 0, 0, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 0, 0, 0, 0, 0, 0, 0, + 25, 0, 26, 27, 0, 0, 0, 0, 0, 28, 0, 0, 29, 30, 31, 32, 33, 34, 35, 0, + 36, 37, 38, 0, 39, 0, 40, 0, 41, 0, 0, 0, 0, 42, 43, 44, 45, 0, 0, 0, 0, + 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 48, 0, 0, 0, + 0, 49, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 0, 53, 0, 0, 0, 0, + 0, 0, 54, 55, 0, 0, 0, 0, 0, 56, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 58, 59, 0, 0, 0, 60, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, + 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, + 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 67, 0, 68, 0, 0, 69, 0, 0, 0, 70, + 71, 72, 73, 74, 75, 76, 77, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 81, 0, + 82, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 84, 85, 86, 87, 88, 89, 0, 90, 91, 92, 0, 0, 0, 0, + 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 0, 131, 132, 133, 134, 0, 0, 0, + 0, 0, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 0, 146, 0, + 0, 0, 147, 0, 148, 149, 150, 0, 151, 152, 153, 0, 154, 0, 0, 0, 155, 0, + 0, 0, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 0, + 0, 0, 0, 0, 0, 167, 0, 0, 0, 0, 0, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 171, 0, 0, 0, 0, 0, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 0, 0, 187, 0, 0, 188, 189, 190, 191, 192, 0, + 193, 194, 195, 196, 197, 0, 198, 0, 0, 0, 199, 200, 201, 202, 203, 204, + 205, 0, 0, 0, 0, 0, 0, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, 0, + 0, 0, 0, 0, 239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, + 269, 0, 0, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 0, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 0, 305, 306, 307, 308, 309, + 310, 311, 312, 0, 0, 313, 0, 314, 0, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, + 336, 337, 338, 339, 340, 341, 342, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 343, + 344, 0, 0, 0, 0, 0, 0, 0, 345, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 346, 347, 0, 0, 0, 0, 348, 349, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, + 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, + 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, + 406, 407, 408, 409, 410, 411, 412, 413, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 414, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 426, 427, 428, 429, 430, 0, 431, 0, 0, 432, 0, 0, 0, 0, 0, 0, + 433, 434, 435, 436, 437, 438, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 439, 440, 441, 442, 443, 444, 445, + 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, + 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, +}; + +static const unsigned short decomp_index2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 3, 0, 6, 0, 0, 0, 0, 8, 0, 0, 11, 13, 15, 18, 0, 0, 20, 23, 25, 0, 27, + 31, 35, 0, 39, 42, 45, 48, 51, 54, 0, 57, 60, 63, 66, 69, 72, 75, 78, 81, + 0, 84, 87, 90, 93, 96, 99, 0, 0, 102, 105, 108, 111, 114, 0, 0, 117, 120, + 123, 126, 129, 132, 0, 135, 138, 141, 144, 147, 150, 153, 156, 159, 0, + 162, 165, 168, 171, 174, 177, 0, 0, 180, 183, 186, 189, 192, 0, 195, 198, + 201, 204, 207, 210, 213, 216, 219, 222, 225, 228, 231, 234, 237, 240, + 243, 0, 0, 246, 249, 252, 255, 258, 261, 264, 267, 270, 273, 276, 279, + 282, 285, 288, 291, 294, 297, 300, 303, 0, 0, 306, 309, 312, 315, 318, + 321, 324, 327, 330, 0, 333, 336, 339, 342, 345, 348, 0, 351, 354, 357, + 360, 363, 366, 369, 372, 0, 0, 375, 378, 381, 384, 387, 390, 393, 0, 0, + 396, 399, 402, 405, 408, 411, 0, 0, 414, 417, 420, 423, 426, 429, 432, + 435, 438, 441, 444, 447, 450, 453, 456, 459, 462, 465, 0, 0, 468, 471, + 474, 477, 480, 483, 486, 489, 492, 495, 498, 501, 504, 507, 510, 513, + 516, 519, 522, 525, 528, 531, 534, 537, 539, 542, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 545, 548, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 551, 554, 557, 560, 563, 566, 569, 572, 575, 578, 581, 584, 587, + 590, 593, 596, 599, 602, 605, 608, 611, 614, 617, 620, 623, 0, 626, 629, + 632, 635, 638, 641, 0, 0, 644, 647, 650, 653, 656, 659, 662, 665, 668, + 671, 674, 677, 680, 683, 686, 689, 0, 0, 692, 695, 698, 701, 704, 707, + 710, 713, 716, 719, 722, 725, 728, 731, 734, 737, 740, 743, 746, 749, + 752, 755, 758, 761, 764, 767, 770, 773, 776, 779, 782, 785, 788, 791, + 794, 797, 0, 0, 800, 803, 0, 0, 0, 0, 0, 0, 806, 809, 812, 815, 818, 821, + 824, 827, 830, 833, 836, 839, 842, 845, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 848, 850, 852, 854, 856, 858, 860, 862, 864, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 866, 869, 872, 875, 878, 881, 0, 0, 884, 886, 888, + 890, 892, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 894, 896, 0, 898, 900, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 903, 0, 0, 0, 0, 0, 905, 0, 0, 0, + 908, 0, 0, 0, 0, 0, 910, 913, 916, 919, 921, 924, 927, 0, 930, 0, 933, + 936, 939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 942, 945, 948, 951, 954, 957, 960, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 963, 966, 969, 972, 975, + 0, 978, 980, 982, 984, 987, 990, 992, 0, 0, 0, 0, 0, 0, 0, 0, 0, 994, + 996, 998, 0, 1000, 1002, 0, 0, 0, 1004, 0, 0, 0, 0, 0, 0, 1006, 1009, 0, + 1012, 0, 0, 0, 1015, 0, 0, 0, 0, 1018, 1021, 1024, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1027, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1030, 0, 0, + 0, 0, 0, 0, 1033, 1036, 0, 1039, 0, 0, 0, 1042, 0, 0, 0, 0, 1045, 1048, + 1051, 0, 0, 0, 0, 0, 0, 0, 1054, 1057, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1060, + 1063, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1066, 1069, 1072, 1075, 0, + 0, 1078, 1081, 0, 0, 1084, 1087, 1090, 1093, 1096, 1099, 0, 0, 1102, + 1105, 1108, 1111, 1114, 1117, 0, 0, 1120, 1123, 1126, 1129, 1132, 1135, + 1138, 1141, 1144, 1147, 1150, 1153, 0, 0, 1156, 1159, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1165, 1168, + 1171, 1174, 1177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1180, 1183, + 1186, 1189, 0, 0, 0, 0, 0, 0, 0, 1192, 0, 1195, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1201, 0, 0, 0, 0, 0, 0, 0, 1204, 0, 0, 1207, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1210, 1213, 1216, + 1219, 1222, 1225, 1228, 1231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1234, + 1237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1240, 1243, 0, 1246, + 0, 0, 0, 1249, 0, 0, 1252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1255, 1258, 1261, 0, 0, 1264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1267, + 0, 0, 1270, 1273, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1276, + 1279, 0, 0, 0, 0, 0, 0, 1282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1285, 1288, 1291, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1294, 0, 0, 0, 0, 0, 0, 0, 1297, 0, 0, 0, 0, 0, 0, 1300, 1303, 0, 1306, + 1309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1312, 1315, 1318, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1321, 0, 1324, 1327, 1330, 0, 0, 0, 0, + 1333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1336, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1339, 1342, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1345, 0, 0, 0, 0, 0, 0, 1347, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1350, 0, 0, 0, 0, 1353, 0, 0, 0, 0, 1356, 0, 0, + 0, 0, 1359, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1362, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1365, 0, 1368, 1371, 1374, 1377, 1380, 0, 0, 0, 0, 0, 0, 0, + 1383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1386, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1389, 0, 0, 0, 0, 1392, 0, 0, 0, 0, 1395, 0, 0, 0, 0, + 1398, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1401, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1404, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1407, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1409, 0, 1412, 0, 1415, 0, + 1418, 0, 1421, 0, 0, 0, 1424, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1427, 0, 1430, 0, 0, 1433, 1436, 0, 1439, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1442, 1444, 1446, 0, 1448, 1450, 1452, 1454, 1456, 1458, 1460, 1462, + 1464, 1466, 1468, 0, 1470, 1472, 1474, 1476, 1478, 1480, 1482, 1484, + 1486, 1488, 1490, 1492, 1494, 1496, 1498, 1500, 1502, 1504, 0, 1506, + 1508, 1510, 1512, 1514, 1516, 1518, 1520, 1522, 1524, 1526, 1528, 1530, + 1532, 1534, 1536, 1538, 1540, 1542, 1544, 1546, 1548, 1550, 1552, 1554, + 1556, 1558, 1560, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1562, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1564, 1566, 1568, 1570, + 1572, 1574, 1576, 1578, 1580, 1582, 1584, 1586, 1588, 1590, 1592, 1594, + 1596, 1598, 1600, 1602, 1604, 1606, 1608, 1610, 1612, 1614, 1616, 1618, + 1620, 1622, 1624, 1626, 1628, 1630, 1632, 1634, 1636, 1638, 1641, 1644, + 1647, 1650, 1653, 1656, 1659, 1662, 1665, 1668, 1671, 1674, 1677, 1680, + 1683, 1686, 1689, 1692, 1695, 1698, 1701, 1704, 1707, 1710, 1713, 1716, + 1719, 1722, 1725, 1728, 1731, 1734, 1737, 1740, 1743, 1746, 1749, 1752, + 1755, 1758, 1761, 1764, 1767, 1770, 1773, 1776, 1779, 1782, 1785, 1788, + 1791, 1794, 1797, 1800, 1803, 1806, 1809, 1812, 1815, 1818, 1821, 1824, + 1827, 1830, 1833, 1836, 1839, 1842, 1845, 1848, 1851, 1854, 1857, 1860, + 1863, 1866, 1869, 1872, 1875, 1878, 1881, 1884, 1887, 1890, 1893, 1896, + 1899, 1902, 1905, 1908, 1911, 1914, 1917, 1920, 1923, 1926, 1929, 1932, + 1935, 1938, 1941, 1944, 1947, 1950, 1953, 1956, 1959, 1962, 1965, 1968, + 1971, 1974, 1977, 1980, 1983, 1986, 1989, 1992, 1995, 1998, 2001, 2004, + 2007, 2010, 2013, 2016, 2019, 2022, 2025, 2028, 2031, 2034, 2037, 2040, + 2043, 2046, 2049, 2052, 2055, 2058, 2061, 2064, 2067, 2070, 2073, 2076, + 2079, 2082, 2085, 2088, 2091, 2094, 2097, 2100, 2103, 0, 0, 0, 0, 2106, + 2109, 2112, 2115, 2118, 2121, 2124, 2127, 2130, 2133, 2136, 2139, 2142, + 2145, 2148, 2151, 2154, 2157, 2160, 2163, 2166, 2169, 2172, 2175, 2178, + 2181, 2184, 2187, 2190, 2193, 2196, 2199, 2202, 2205, 2208, 2211, 2214, + 2217, 2220, 2223, 2226, 2229, 2232, 2235, 2238, 2241, 2244, 2247, 2250, + 2253, 2256, 2259, 2262, 2265, 2268, 2271, 2274, 2277, 2280, 2283, 2286, + 2289, 2292, 2295, 2298, 2301, 2304, 2307, 2310, 2313, 2316, 2319, 2322, + 2325, 2328, 2331, 2334, 2337, 2340, 2343, 2346, 2349, 2352, 2355, 2358, + 2361, 2364, 2367, 2370, 2373, 0, 0, 0, 0, 0, 0, 2376, 2379, 2382, 2385, + 2388, 2391, 2394, 2397, 2400, 2403, 2406, 2409, 2412, 2415, 2418, 2421, + 2424, 2427, 2430, 2433, 2436, 2439, 0, 0, 2442, 2445, 2448, 2451, 2454, + 2457, 0, 0, 2460, 2463, 2466, 2469, 2472, 2475, 2478, 2481, 2484, 2487, + 2490, 2493, 2496, 2499, 2502, 2505, 2508, 2511, 2514, 2517, 2520, 2523, + 2526, 2529, 2532, 2535, 2538, 2541, 2544, 2547, 2550, 2553, 2556, 2559, + 2562, 2565, 2568, 2571, 0, 0, 2574, 2577, 2580, 2583, 2586, 2589, 0, 0, + 2592, 2595, 2598, 2601, 2604, 2607, 2610, 2613, 0, 2616, 0, 2619, 0, + 2622, 0, 2625, 2628, 2631, 2634, 2637, 2640, 2643, 2646, 2649, 2652, + 2655, 2658, 2661, 2664, 2667, 2670, 2673, 2676, 2679, 2681, 2684, 2686, + 2689, 2691, 2694, 2696, 2699, 2701, 2704, 2706, 2709, 0, 0, 2711, 2714, + 2717, 2720, 2723, 2726, 2729, 2732, 2735, 2738, 2741, 2744, 2747, 2750, + 2753, 2756, 2759, 2762, 2765, 2768, 2771, 2774, 2777, 2780, 2783, 2786, + 2789, 2792, 2795, 2798, 2801, 2804, 2807, 2810, 2813, 2816, 2819, 2822, + 2825, 2828, 2831, 2834, 2837, 2840, 2843, 2846, 2849, 2852, 2855, 2858, + 2861, 2864, 2867, 0, 2870, 2873, 2876, 2879, 2882, 2885, 2887, 2890, + 2893, 2895, 2898, 2901, 2904, 2907, 2910, 0, 2913, 2916, 2919, 2922, + 2924, 2927, 2929, 2932, 2935, 2938, 2941, 2944, 2947, 2950, 0, 0, 2952, + 2955, 2958, 2961, 2964, 2967, 0, 2969, 2972, 2975, 2978, 2981, 2984, + 2987, 2989, 2992, 2995, 2998, 3001, 3004, 3007, 3010, 3012, 3015, 3018, + 3020, 0, 0, 3022, 3025, 3028, 0, 3031, 3034, 3037, 3040, 3042, 3045, + 3047, 3050, 3052, 0, 3055, 3057, 3059, 3061, 3063, 3065, 3067, 3069, + 3071, 3073, 3075, 0, 0, 0, 0, 0, 0, 3077, 0, 0, 0, 0, 0, 3079, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3082, 3084, 3087, 0, 0, 0, 0, 0, 0, 0, 0, + 3091, 0, 0, 0, 3093, 3096, 0, 3100, 3103, 0, 0, 0, 0, 3107, 0, 3110, 0, + 0, 0, 0, 0, 0, 0, 0, 3113, 3116, 3119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3122, 0, 0, 0, 0, 0, 0, 0, 3127, 3129, 3131, 0, 0, 3133, 3135, + 3137, 3139, 3141, 3143, 3145, 3147, 3149, 3151, 3153, 3155, 3157, 3159, + 3161, 3163, 3165, 3167, 3169, 3171, 3173, 3175, 3177, 3179, 3181, 3183, + 3185, 0, 3187, 3189, 3191, 3193, 3195, 3197, 3199, 3201, 3203, 3205, + 3207, 3209, 3211, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3213, 0, 0, 0, 0, 0, + 0, 0, 3216, 3220, 3224, 3226, 0, 3229, 3233, 3237, 0, 3239, 3242, 3244, + 3246, 3248, 3250, 3252, 3254, 3256, 3258, 3260, 0, 3262, 3264, 0, 0, + 3267, 3269, 3271, 3273, 3275, 0, 0, 3277, 3280, 3284, 0, 3287, 0, 3289, + 0, 3291, 0, 3293, 3295, 3297, 3299, 0, 3301, 3303, 3305, 0, 3307, 3309, + 3311, 3313, 3315, 3317, 3319, 0, 3321, 3325, 3327, 3329, 3331, 3333, 0, + 0, 0, 0, 3335, 3337, 3339, 3341, 3343, 0, 0, 0, 0, 0, 0, 3345, 3349, + 3353, 3358, 3362, 3366, 3370, 3374, 3378, 3382, 3386, 3390, 3394, 3398, + 3402, 3406, 3409, 3411, 3414, 3418, 3421, 3423, 3426, 3430, 3435, 3438, + 3440, 3443, 3447, 3449, 3451, 3453, 3455, 3457, 3460, 3464, 3467, 3469, + 3472, 3476, 3481, 3484, 3486, 3489, 3493, 3495, 3497, 3499, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3501, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3505, 3508, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3511, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3514, 3517, 3520, 0, 0, 0, 0, + 3523, 0, 0, 0, 0, 3526, 0, 0, 3529, 0, 0, 0, 0, 0, 0, 0, 3532, 0, 3535, + 0, 0, 0, 0, 0, 3538, 3541, 0, 3545, 3548, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3552, 0, 0, 3555, 0, 0, 3558, 0, 3561, 0, 0, 0, 0, 0, + 0, 3564, 0, 3567, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3570, 3573, 3576, 3579, + 3582, 0, 0, 3585, 3588, 0, 0, 3591, 3594, 0, 0, 0, 0, 0, 0, 3597, 3600, + 0, 0, 3603, 3606, 0, 0, 3609, 3612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3615, 3618, 3621, 3624, 3627, 3630, 3633, 3636, 0, 0, + 0, 0, 0, 0, 3639, 3642, 3645, 3648, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3651, 3653, 0, 0, 0, 0, 0, 3655, 3657, 3659, 3661, 3663, 3665, 3667, + 3669, 3671, 3673, 3676, 3679, 3682, 3685, 3688, 3691, 3694, 3697, 3700, + 3703, 3706, 3710, 3714, 3718, 3722, 3726, 3730, 3734, 3738, 3742, 3747, + 3752, 3757, 3762, 3767, 3772, 3777, 3782, 3787, 3792, 3797, 3800, 3803, + 3806, 3809, 3812, 3815, 3818, 3821, 3824, 3828, 3832, 3836, 3840, 3844, + 3848, 3852, 3856, 3860, 3864, 3868, 3872, 3876, 3880, 3884, 3888, 3892, + 3896, 3900, 3904, 3908, 3912, 3916, 3920, 3924, 3928, 3932, 3936, 3940, + 3944, 3948, 3952, 3956, 3960, 3964, 3968, 3972, 3974, 3976, 3978, 3980, + 3982, 3984, 3986, 3988, 3990, 3992, 3994, 3996, 3998, 4000, 4002, 4004, + 4006, 4008, 4010, 4012, 4014, 4016, 4018, 4020, 4022, 4024, 4026, 4028, + 4030, 4032, 4034, 4036, 4038, 4040, 4042, 4044, 4046, 4048, 4050, 4052, + 4054, 4056, 4058, 4060, 4062, 4064, 4066, 4068, 4070, 4072, 4074, 4076, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4078, 0, 0, 0, 0, 0, + 0, 0, 4083, 4087, 4090, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4094, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4097, + 4099, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4101, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4103, 0, 0, 0, 4105, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4107, 4109, 4111, 4113, 4115, 4117, 4119, 4121, + 4123, 4125, 4127, 4129, 4131, 4133, 4135, 4137, 4139, 4141, 4143, 4145, + 4147, 4149, 4151, 4153, 4155, 4157, 4159, 4161, 4163, 4165, 4167, 4169, + 4171, 4173, 4175, 4177, 4179, 4181, 4183, 4185, 4187, 4189, 4191, 4193, + 4195, 4197, 4199, 4201, 4203, 4205, 4207, 4209, 4211, 4213, 4215, 4217, + 4219, 4221, 4223, 4225, 4227, 4229, 4231, 4233, 4235, 4237, 4239, 4241, + 4243, 4245, 4247, 4249, 4251, 4253, 4255, 4257, 4259, 4261, 4263, 4265, + 4267, 4269, 4271, 4273, 4275, 4277, 4279, 4281, 4283, 4285, 4287, 4289, + 4291, 4293, 4295, 4297, 4299, 4301, 4303, 4305, 4307, 4309, 4311, 4313, + 4315, 4317, 4319, 4321, 4323, 4325, 4327, 4329, 4331, 4333, 4335, 4337, + 4339, 4341, 4343, 4345, 4347, 4349, 4351, 4353, 4355, 4357, 4359, 4361, + 4363, 4365, 4367, 4369, 4371, 4373, 4375, 4377, 4379, 4381, 4383, 4385, + 4387, 4389, 4391, 4393, 4395, 4397, 4399, 4401, 4403, 4405, 4407, 4409, + 4411, 4413, 4415, 4417, 4419, 4421, 4423, 4425, 4427, 4429, 4431, 4433, + 4435, 4437, 4439, 4441, 4443, 4445, 4447, 4449, 4451, 4453, 4455, 4457, + 4459, 4461, 4463, 4465, 4467, 4469, 4471, 4473, 4475, 4477, 4479, 4481, + 4483, 4485, 4487, 4489, 4491, 4493, 4495, 4497, 4499, 4501, 4503, 4505, + 4507, 4509, 4511, 4513, 4515, 4517, 4519, 4521, 4523, 4525, 4527, 4529, + 4531, 4533, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4535, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4537, 0, 4539, 4541, 4543, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4545, 0, 4548, 0, 4551, 0, + 4554, 0, 4557, 0, 4560, 0, 4563, 0, 4566, 0, 4569, 0, 4572, 0, 4575, 0, + 4578, 0, 0, 4581, 0, 4584, 0, 4587, 0, 0, 0, 0, 0, 0, 4590, 4593, 0, + 4596, 4599, 0, 4602, 4605, 0, 4608, 4611, 0, 4614, 4617, 0, 0, 0, 0, 0, + 0, 4620, 0, 0, 0, 0, 0, 0, 4623, 4626, 0, 4629, 4632, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4635, 0, 4638, 0, 4641, 0, 4644, 0, 4647, 0, 4650, 0, + 4653, 0, 4656, 0, 4659, 0, 4662, 0, 4665, 0, 4668, 0, 0, 4671, 0, 4674, + 0, 4677, 0, 0, 0, 0, 0, 0, 4680, 4683, 0, 4686, 4689, 0, 4692, 4695, 0, + 4698, 4701, 0, 4704, 4707, 0, 0, 0, 0, 0, 0, 4710, 0, 0, 4713, 4716, + 4719, 4722, 0, 0, 0, 4725, 4728, 0, 4731, 4733, 4735, 4737, 4739, 4741, + 4743, 4745, 4747, 4749, 4751, 4753, 4755, 4757, 4759, 4761, 4763, 4765, + 4767, 4769, 4771, 4773, 4775, 4777, 4779, 4781, 4783, 4785, 4787, 4789, + 4791, 4793, 4795, 4797, 4799, 4801, 4803, 4805, 4807, 4809, 4811, 4813, + 4815, 4817, 4819, 4821, 4823, 4825, 4827, 4829, 4831, 4833, 4835, 4837, + 4839, 4841, 4843, 4845, 4847, 4849, 4851, 4853, 4855, 4857, 4859, 4861, + 4863, 4865, 4867, 4869, 4871, 4873, 4875, 4877, 4879, 4881, 4883, 4885, + 4887, 4889, 4891, 4893, 4895, 4897, 4899, 4901, 4903, 4905, 4907, 4909, + 4911, 4913, 4915, 4917, 0, 0, 0, 4919, 4921, 4923, 4925, 4927, 4929, + 4931, 4933, 4935, 4937, 4939, 4941, 4943, 4945, 4947, 4951, 4955, 4959, + 4963, 4967, 4971, 4975, 4979, 4983, 4987, 4991, 4995, 4999, 5003, 5008, + 5013, 5018, 5023, 5028, 5033, 5038, 5043, 5048, 5053, 5058, 5063, 5068, + 5073, 5078, 5086, 0, 5093, 5097, 5101, 5105, 5109, 5113, 5117, 5121, + 5125, 5129, 5133, 5137, 5141, 5145, 5149, 5153, 5157, 5161, 5165, 5169, + 5173, 5177, 5181, 5185, 5189, 5193, 5197, 5201, 5205, 5209, 5213, 5217, + 5221, 5225, 5229, 5233, 5237, 5239, 5241, 5243, 0, 0, 0, 0, 0, 0, 0, 0, + 5245, 5249, 5252, 5255, 5258, 5261, 5264, 5267, 5270, 5273, 5276, 5279, + 5282, 5285, 5288, 5291, 5294, 5296, 5298, 5300, 5302, 5304, 5306, 5308, + 5310, 5312, 5314, 5316, 5318, 5320, 5322, 5325, 5328, 5331, 5334, 5337, + 5340, 5343, 5346, 5349, 5352, 5355, 5358, 5361, 5364, 5370, 5375, 0, + 5378, 5380, 5382, 5384, 5386, 5388, 5390, 5392, 5394, 5396, 5398, 5400, + 5402, 5404, 5406, 5408, 5410, 5412, 5414, 5416, 5418, 5420, 5422, 5424, + 5426, 5428, 5430, 5432, 5434, 5436, 5438, 5440, 5442, 5444, 5446, 5448, + 5450, 5452, 5454, 5456, 5458, 5460, 5462, 5464, 5466, 5468, 5470, 5472, + 5474, 5476, 5479, 5482, 5485, 5488, 5491, 5494, 5497, 5500, 5503, 5506, + 5509, 5512, 5515, 5518, 5521, 5524, 5527, 5530, 5533, 5536, 5539, 5542, + 5545, 5548, 5552, 5556, 5560, 5563, 5567, 5570, 5574, 5576, 5578, 5580, + 5582, 5584, 5586, 5588, 5590, 5592, 5594, 5596, 5598, 5600, 5602, 5604, + 5606, 5608, 5610, 5612, 5614, 5616, 5618, 5620, 5622, 5624, 5626, 5628, + 5630, 5632, 5634, 5636, 5638, 5640, 5642, 5644, 5646, 5648, 5650, 5652, + 5654, 5656, 5658, 5660, 5662, 5664, 5666, 0, 5668, 5673, 5678, 5683, + 5687, 5692, 5696, 5700, 5706, 5711, 5715, 5719, 5723, 5728, 5733, 5737, + 5741, 5744, 5748, 5753, 5758, 5761, 5767, 5774, 5780, 5784, 5790, 5796, + 5801, 5805, 5809, 5813, 5818, 5824, 5829, 5833, 5837, 5841, 5844, 5847, + 5850, 5853, 5857, 5861, 5867, 5871, 5876, 5882, 5886, 5889, 5892, 5898, + 5903, 5909, 5913, 5919, 5922, 5926, 5930, 5934, 5938, 5942, 5947, 5951, + 5954, 5958, 5962, 5966, 5971, 5975, 5979, 5983, 5989, 5994, 5997, 6003, + 6006, 6011, 6016, 6020, 6024, 6028, 6033, 6036, 6040, 6045, 6048, 6054, + 6058, 6061, 6064, 6067, 6070, 6073, 6076, 6079, 6082, 6085, 6088, 6092, + 6096, 6100, 6104, 6108, 6112, 6116, 6120, 6124, 6128, 6132, 6136, 6140, + 6144, 6148, 6152, 6155, 6158, 6162, 6165, 6168, 6171, 6175, 6179, 6182, + 6185, 6188, 6191, 6194, 6199, 6202, 6205, 6208, 6211, 6214, 6217, 6220, + 6223, 6227, 6232, 6235, 6238, 6241, 6244, 6247, 6250, 6253, 6257, 6261, + 6265, 6269, 6272, 6275, 6278, 6281, 6284, 6287, 6290, 6293, 6296, 6299, + 6303, 6307, 6310, 6314, 6318, 6322, 6325, 6329, 6333, 6338, 6341, 6345, + 6349, 6353, 6357, 6363, 6370, 6373, 6376, 6379, 6382, 6385, 6388, 6391, + 6394, 6397, 6400, 6403, 6406, 6409, 6412, 6415, 6418, 6421, 6424, 6429, + 6432, 6435, 6438, 6443, 6447, 6450, 6453, 6456, 6459, 6462, 6465, 6468, + 6471, 6474, 6477, 6481, 6484, 6487, 6491, 6495, 6498, 6503, 6507, 6510, + 6513, 6516, 6519, 6523, 6527, 6530, 6533, 6536, 6539, 6542, 6545, 6548, + 6551, 6554, 6558, 6562, 6566, 6570, 6574, 6578, 6582, 6586, 6590, 6594, + 6598, 6602, 6606, 6610, 6614, 6618, 6622, 6626, 6630, 6634, 6638, 6642, + 6646, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 6648, 6650, 0, 0, 0, 0, 0, 0, 6652, 6654, 6656, 6658, 6660, 6662, + 6664, 6666, 6668, 6670, 6672, 6674, 6676, 6678, 6680, 6682, 6684, 6686, + 6688, 6690, 6692, 6694, 6696, 6698, 6700, 6702, 6704, 6706, 6708, 6710, + 6712, 6714, 6716, 6718, 6720, 6722, 6724, 6726, 6728, 6730, 6732, 6734, + 6736, 6738, 6740, 6742, 6744, 6746, 6748, 6750, 6752, 6754, 6756, 6758, + 6760, 6762, 6764, 6766, 6768, 6770, 6772, 6774, 6776, 6778, 6780, 6782, + 6784, 6786, 6788, 6790, 6792, 6794, 6796, 6798, 6800, 6802, 6804, 6806, + 6808, 6810, 6812, 6814, 6816, 6818, 6820, 6822, 6824, 6826, 6828, 6830, + 6832, 6834, 6836, 6838, 6840, 6842, 6844, 6846, 6848, 6850, 6852, 6854, + 6856, 6858, 6860, 6862, 6864, 6866, 6868, 6870, 6872, 6874, 6876, 6878, + 6880, 6882, 6884, 6886, 6888, 6890, 6892, 6894, 6896, 6898, 6900, 6902, + 6904, 6906, 6908, 6910, 6912, 6914, 6916, 6918, 6920, 6922, 6924, 6926, + 6928, 6930, 6932, 6934, 6936, 6938, 6940, 6942, 6944, 6946, 6948, 6950, + 6952, 6954, 6956, 6958, 6960, 6962, 6964, 6966, 6968, 6970, 6972, 6974, + 6976, 6978, 6980, 6982, 6984, 6986, 6988, 6990, 6992, 6994, 6996, 6998, + 7000, 7002, 7004, 7006, 7008, 7010, 7012, 7014, 7016, 7018, 7020, 7022, + 7024, 7026, 7028, 7030, 7032, 7034, 7036, 7038, 7040, 7042, 7044, 7046, + 7048, 7050, 7052, 7054, 7056, 7058, 7060, 7062, 7064, 7066, 7068, 7070, + 7072, 7074, 7076, 7078, 7080, 7082, 7084, 7086, 7088, 7090, 7092, 7094, + 7096, 7098, 7100, 7102, 7104, 7106, 7108, 7110, 7112, 7114, 7116, 7118, + 7120, 7122, 7124, 7126, 7128, 7130, 7132, 7134, 7136, 7138, 7140, 7142, + 7144, 7146, 7148, 7150, 7152, 7154, 7156, 7158, 7160, 7162, 7164, 7166, + 7168, 7170, 7172, 7174, 7176, 7178, 7180, 7182, 7184, 7186, 7188, 7190, + 0, 0, 7192, 0, 7194, 0, 0, 7196, 7198, 7200, 7202, 7204, 7206, 7208, + 7210, 7212, 7214, 0, 7216, 0, 7218, 0, 0, 7220, 7222, 0, 0, 0, 7224, + 7226, 7228, 7230, 7232, 7234, 7236, 7238, 7240, 7242, 7244, 7246, 7248, + 7250, 7252, 7254, 7256, 7258, 7260, 7262, 7264, 7266, 7268, 7270, 7272, + 7274, 7276, 7278, 7280, 7282, 7284, 7286, 7288, 7290, 7292, 7294, 7296, + 7298, 7300, 7302, 7304, 7306, 7308, 7310, 7312, 7314, 7316, 7318, 7320, + 7322, 7324, 7326, 7328, 7330, 7332, 7334, 7336, 7338, 7340, 7342, 7344, + 7346, 7348, 7350, 7352, 7354, 7356, 7359, 0, 0, 7361, 7363, 7365, 7367, + 7369, 7371, 7373, 7375, 7377, 7379, 7381, 7383, 7385, 7387, 7389, 7391, + 7393, 7395, 7397, 7399, 7401, 7403, 7405, 7407, 7409, 7411, 7413, 7415, + 7417, 7419, 7421, 7423, 7425, 7427, 7429, 7431, 7433, 7435, 7437, 7439, + 7441, 7443, 7445, 7447, 7449, 7451, 7453, 7455, 7457, 7459, 7461, 7463, + 7465, 7467, 7469, 7471, 7473, 7475, 7477, 7479, 7481, 7483, 7485, 7487, + 7489, 7491, 7493, 7495, 7497, 7499, 7501, 7503, 7505, 7507, 7509, 7511, + 7513, 7515, 7517, 7519, 7521, 7523, 7525, 7527, 7529, 7531, 7533, 7535, + 7537, 7539, 7541, 7543, 7545, 7547, 7549, 7551, 7554, 7557, 7560, 7562, + 7564, 7566, 7569, 7572, 7575, 7577, 0, 0, 0, 0, 0, 0, 7579, 7582, 7585, + 7588, 7592, 7596, 7599, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7602, 7605, + 7608, 7611, 7614, 0, 0, 0, 0, 0, 7617, 0, 7620, 7623, 7625, 7627, 7629, + 7631, 7633, 7635, 7637, 7639, 7641, 7643, 7646, 7649, 7652, 7655, 7658, + 7661, 7664, 7667, 7670, 7673, 7676, 7679, 0, 7682, 7685, 7688, 7691, + 7694, 0, 7697, 0, 7700, 7703, 0, 7706, 7709, 0, 7712, 7715, 7718, 7721, + 7724, 7727, 7730, 7733, 7736, 7739, 7742, 7744, 7746, 7748, 7750, 7752, + 7754, 7756, 7758, 7760, 7762, 7764, 7766, 7768, 7770, 7772, 7774, 7776, + 7778, 7780, 7782, 7784, 7786, 7788, 7790, 7792, 7794, 7796, 7798, 7800, + 7802, 7804, 7806, 7808, 7810, 7812, 7814, 7816, 7818, 7820, 7822, 7824, + 7826, 7828, 7830, 7832, 7834, 7836, 7838, 7840, 7842, 7844, 7846, 7848, + 7850, 7852, 7854, 7856, 7858, 7860, 7862, 7864, 7866, 7868, 7870, 7872, + 7874, 7876, 7878, 7880, 7882, 7884, 7886, 7888, 7890, 7892, 7894, 7896, + 7898, 7900, 7902, 7904, 7906, 7908, 7910, 7912, 7914, 7916, 7918, 7920, + 7922, 7924, 7926, 7928, 7930, 7932, 7934, 7936, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 7938, 7940, 7942, 7944, 7946, 7948, 7950, + 7952, 7954, 7956, 7958, 7960, 7962, 7964, 7966, 7968, 7970, 7972, 7974, + 7976, 7978, 7980, 7982, 7984, 7987, 7990, 7993, 7996, 7999, 8002, 8005, + 8008, 8011, 8014, 8017, 8020, 8023, 8026, 8029, 8032, 8035, 8038, 8040, + 8042, 8044, 8046, 8049, 8052, 8055, 8058, 8061, 8064, 8067, 8070, 8073, + 8076, 8079, 8082, 8085, 8088, 8091, 8094, 8097, 8100, 8103, 8106, 8109, + 8112, 8115, 8118, 8121, 8124, 8127, 8130, 8133, 8136, 8139, 8142, 8145, + 8148, 8151, 8154, 8157, 8160, 8163, 8166, 8169, 8172, 8175, 8178, 8181, + 8184, 8187, 8190, 8193, 8196, 8199, 8202, 8205, 8208, 8211, 8214, 8217, + 8220, 8223, 8226, 8229, 8232, 8235, 8238, 8241, 8244, 8247, 8250, 8253, + 8256, 8259, 8262, 8265, 8268, 8271, 8274, 8277, 8280, 8283, 8286, 8289, + 8292, 8295, 8298, 8301, 8304, 8307, 8310, 8313, 8316, 8319, 8322, 8325, + 8328, 8332, 8336, 8340, 8344, 8348, 8352, 8355, 8358, 8361, 8364, 8367, + 8370, 8373, 8376, 8379, 8382, 8385, 8388, 8391, 8394, 8397, 8400, 8403, + 8406, 8409, 8412, 8415, 8418, 8421, 8424, 8427, 8430, 8433, 8436, 8439, + 8442, 8445, 8448, 8451, 8454, 8457, 8460, 8463, 8466, 8469, 8472, 8475, + 8478, 8481, 8484, 8487, 8490, 8493, 8496, 8499, 8502, 8505, 8508, 8511, + 8514, 8517, 8520, 8523, 8526, 8529, 8532, 8535, 8538, 8541, 8544, 8547, + 8550, 8553, 8556, 8559, 8562, 8565, 8568, 8571, 8574, 8577, 8580, 8583, + 8586, 8589, 8592, 8595, 8598, 8601, 8604, 8607, 8610, 8613, 8616, 8619, + 8622, 8625, 8628, 8631, 8634, 8637, 8640, 8643, 8646, 8649, 8652, 8655, + 8658, 8661, 8664, 8667, 8670, 8673, 8676, 8679, 8682, 8685, 8688, 8691, + 8694, 8697, 8700, 8703, 8706, 8709, 8712, 8715, 8718, 8721, 8724, 8727, + 8730, 8733, 8736, 8739, 8742, 8745, 8748, 8751, 8754, 8757, 8760, 8763, + 8766, 8769, 8772, 8775, 8778, 8782, 8786, 8790, 8793, 8796, 8799, 8802, + 8805, 8808, 8811, 8814, 8817, 8820, 8823, 8826, 8829, 8832, 8835, 8838, + 8841, 8844, 8847, 8850, 8853, 8856, 8859, 8862, 8865, 8868, 8871, 8874, + 8877, 8880, 8883, 8886, 8889, 8892, 8895, 8898, 8901, 8904, 8907, 8910, + 8913, 8916, 8919, 8922, 8925, 8928, 8931, 8934, 8937, 8940, 8943, 8946, + 8949, 8952, 8955, 8958, 8961, 8964, 8967, 8970, 8973, 8976, 8979, 8982, + 8985, 8988, 8991, 8994, 8997, 9000, 9003, 9006, 0, 0, 9009, 9013, 9017, + 9021, 9025, 9029, 9033, 9037, 9041, 9045, 9049, 9053, 9057, 9061, 9065, + 9069, 9073, 9077, 9081, 9085, 9089, 9093, 9097, 9101, 9105, 9109, 9113, + 9117, 9121, 9125, 9129, 9133, 9137, 9141, 9145, 9149, 9153, 9157, 9161, + 9165, 9169, 9173, 9177, 9181, 9185, 9189, 9193, 9197, 9201, 9205, 9209, + 9213, 9217, 9221, 9225, 9229, 9233, 9237, 9241, 9245, 9249, 9253, 9257, + 9261, 0, 0, 9265, 9269, 9273, 9277, 9281, 9285, 9289, 9293, 9297, 9301, + 9305, 9309, 9313, 9317, 9321, 9325, 9329, 9333, 9337, 9341, 9345, 9349, + 9353, 9357, 9361, 9365, 9369, 9373, 9377, 9381, 9385, 9389, 9393, 9397, + 9401, 9405, 9409, 9413, 9417, 9421, 9425, 9429, 9433, 9437, 9441, 9445, + 9449, 9453, 9457, 9461, 9465, 9469, 9473, 9477, 0, 0, 0, 0, 0, 0, 0, 0, + 9481, 9485, 9489, 9494, 9499, 9504, 9509, 9514, 9519, 9524, 9528, 9547, + 9556, 0, 0, 0, 9561, 9563, 9565, 9567, 9569, 9571, 9573, 9575, 9577, + 9579, 0, 0, 0, 0, 0, 0, 9581, 9583, 9585, 9587, 9589, 9591, 9593, 9595, + 9597, 9599, 9601, 9603, 9605, 9607, 9609, 9611, 9613, 9615, 9617, 9619, + 9621, 0, 0, 9623, 9625, 9627, 9629, 9631, 9633, 9635, 9637, 9639, 9641, + 9643, 9645, 0, 9647, 9649, 9651, 9653, 9655, 9657, 9659, 9661, 9663, + 9665, 9667, 9669, 9671, 9673, 9675, 9677, 9679, 9681, 9683, 0, 9685, + 9687, 9689, 9691, 0, 0, 0, 0, 9693, 9696, 9699, 0, 9702, 0, 9705, 9708, + 9711, 9714, 9717, 9720, 9723, 9726, 9729, 9732, 9735, 9737, 9739, 9741, + 9743, 9745, 9747, 9749, 9751, 9753, 9755, 9757, 9759, 9761, 9763, 9765, + 9767, 9769, 9771, 9773, 9775, 9777, 9779, 9781, 9783, 9785, 9787, 9789, + 9791, 9793, 9795, 9797, 9799, 9801, 9803, 9805, 9807, 9809, 9811, 9813, + 9815, 9817, 9819, 9821, 9823, 9825, 9827, 9829, 9831, 9833, 9835, 9837, + 9839, 9841, 9843, 9845, 9847, 9849, 9851, 9853, 9855, 9857, 9859, 9861, + 9863, 9865, 9867, 9869, 9871, 9873, 9875, 9877, 9879, 9881, 9883, 9885, + 9887, 9889, 9891, 9893, 9895, 9897, 9899, 9901, 9903, 9905, 9907, 9909, + 9911, 9913, 9915, 9917, 9919, 9921, 9923, 9925, 9927, 9929, 9931, 9933, + 9935, 9937, 9939, 9941, 9943, 9945, 9947, 9949, 9951, 9953, 9955, 9957, + 9959, 9961, 9963, 9965, 9967, 9969, 9972, 9975, 9978, 9981, 9984, 9987, + 9990, 0, 0, 0, 0, 9993, 9995, 9997, 9999, 10001, 10003, 10005, 10007, + 10009, 10011, 10013, 10015, 10017, 10019, 10021, 10023, 10025, 10027, + 10029, 10031, 10033, 10035, 10037, 10039, 10041, 10043, 10045, 10047, + 10049, 10051, 10053, 10055, 10057, 10059, 10061, 10063, 10065, 10067, + 10069, 10071, 10073, 10075, 10077, 10079, 10081, 10083, 10085, 10087, + 10089, 10091, 10093, 10095, 10097, 10099, 10101, 10103, 10105, 10107, + 10109, 10111, 10113, 10115, 10117, 10119, 10121, 10123, 10125, 10127, + 10129, 10131, 10133, 10135, 10137, 10139, 10141, 10143, 10145, 10147, + 10149, 10151, 10153, 10155, 10157, 10159, 10161, 10163, 10165, 10167, + 10169, 10171, 10173, 10175, 10177, 10179, 10181, 10183, 10185, 10187, + 10189, 10191, 10193, 10195, 10197, 10199, 10201, 10203, 10205, 10207, + 10209, 10211, 10213, 10215, 10217, 10219, 10221, 10223, 10225, 10227, + 10229, 10231, 10233, 10235, 10237, 10239, 10241, 10243, 10245, 10247, + 10249, 10251, 10253, 10255, 10257, 10259, 10261, 10263, 10265, 10267, + 10269, 10271, 10273, 10275, 10277, 10279, 10281, 10283, 10285, 10287, + 10289, 10291, 10293, 10295, 10297, 10299, 10301, 10303, 10305, 10307, + 10309, 10311, 10313, 10315, 10317, 10319, 10321, 10323, 10325, 10327, + 10329, 10331, 10333, 10335, 10337, 10339, 10341, 10343, 10345, 10347, + 10349, 10351, 10353, 10355, 10357, 10359, 10361, 10363, 10365, 10367, + 10369, 10371, 0, 0, 0, 10373, 10375, 10377, 10379, 10381, 10383, 0, 0, + 10385, 10387, 10389, 10391, 10393, 10395, 0, 0, 10397, 10399, 10401, + 10403, 10405, 10407, 0, 0, 10409, 10411, 10413, 0, 0, 0, 10415, 10417, + 10419, 10421, 10423, 10425, 10427, 0, 10429, 10431, 10433, 10435, 10437, + 10439, 10441, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10443, 0, 10448, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10453, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10458, 10463, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10468, 10473, 10478, 10483, 10488, 10493, 10498, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10503, 10508, 10513, 10518, + 10523, 10528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10533, 10535, + 10537, 10539, 10541, 10543, 10545, 10547, 10549, 10551, 10553, 10555, + 10557, 10559, 10561, 10563, 10565, 10567, 10569, 10571, 10573, 10575, + 10577, 10579, 10581, 10583, 10585, 10587, 10589, 10591, 10593, 10595, + 10597, 10599, 10601, 10603, 10605, 10607, 10609, 10611, 10613, 10615, + 10617, 10619, 10621, 10623, 10625, 10627, 10629, 10631, 10633, 10635, + 10637, 10639, 10641, 10643, 10645, 10647, 10649, 10651, 10653, 10655, + 10657, 10659, 10661, 10663, 10665, 10667, 10669, 10671, 10673, 10675, + 10677, 10679, 10681, 10683, 10685, 10687, 10689, 10691, 10693, 10695, + 10697, 10699, 10701, 0, 10703, 10705, 10707, 10709, 10711, 10713, 10715, + 10717, 10719, 10721, 10723, 10725, 10727, 10729, 10731, 10733, 10735, + 10737, 10739, 10741, 10743, 10745, 10747, 10749, 10751, 10753, 10755, + 10757, 10759, 10761, 10763, 10765, 10767, 10769, 10771, 10773, 10775, + 10777, 10779, 10781, 10783, 10785, 10787, 10789, 10791, 10793, 10795, + 10797, 10799, 10801, 10803, 10805, 10807, 10809, 10811, 10813, 10815, + 10817, 10819, 10821, 10823, 10825, 10827, 10829, 10831, 10833, 10835, + 10837, 10839, 10841, 10843, 0, 10845, 10847, 0, 0, 10849, 0, 0, 10851, + 10853, 0, 0, 10855, 10857, 10859, 10861, 0, 10863, 10865, 10867, 10869, + 10871, 10873, 10875, 10877, 10879, 10881, 10883, 10885, 0, 10887, 0, + 10889, 10891, 10893, 10895, 10897, 10899, 10901, 0, 10903, 10905, 10907, + 10909, 10911, 10913, 10915, 10917, 10919, 10921, 10923, 10925, 10927, + 10929, 10931, 10933, 10935, 10937, 10939, 10941, 10943, 10945, 10947, + 10949, 10951, 10953, 10955, 10957, 10959, 10961, 10963, 10965, 10967, + 10969, 10971, 10973, 10975, 10977, 10979, 10981, 10983, 10985, 10987, + 10989, 10991, 10993, 10995, 10997, 10999, 11001, 11003, 11005, 11007, + 11009, 11011, 11013, 11015, 11017, 11019, 11021, 11023, 11025, 11027, + 11029, 11031, 0, 11033, 11035, 11037, 11039, 0, 0, 11041, 11043, 11045, + 11047, 11049, 11051, 11053, 11055, 0, 11057, 11059, 11061, 11063, 11065, + 11067, 11069, 0, 11071, 11073, 11075, 11077, 11079, 11081, 11083, 11085, + 11087, 11089, 11091, 11093, 11095, 11097, 11099, 11101, 11103, 11105, + 11107, 11109, 11111, 11113, 11115, 11117, 11119, 11121, 11123, 11125, 0, + 11127, 11129, 11131, 11133, 0, 11135, 11137, 11139, 11141, 11143, 0, + 11145, 0, 0, 0, 11147, 11149, 11151, 11153, 11155, 11157, 11159, 0, + 11161, 11163, 11165, 11167, 11169, 11171, 11173, 11175, 11177, 11179, + 11181, 11183, 11185, 11187, 11189, 11191, 11193, 11195, 11197, 11199, + 11201, 11203, 11205, 11207, 11209, 11211, 11213, 11215, 11217, 11219, + 11221, 11223, 11225, 11227, 11229, 11231, 11233, 11235, 11237, 11239, + 11241, 11243, 11245, 11247, 11249, 11251, 11253, 11255, 11257, 11259, + 11261, 11263, 11265, 11267, 11269, 11271, 11273, 11275, 11277, 11279, + 11281, 11283, 11285, 11287, 11289, 11291, 11293, 11295, 11297, 11299, + 11301, 11303, 11305, 11307, 11309, 11311, 11313, 11315, 11317, 11319, + 11321, 11323, 11325, 11327, 11329, 11331, 11333, 11335, 11337, 11339, + 11341, 11343, 11345, 11347, 11349, 11351, 11353, 11355, 11357, 11359, + 11361, 11363, 11365, 11367, 11369, 11371, 11373, 11375, 11377, 11379, + 11381, 11383, 11385, 11387, 11389, 11391, 11393, 11395, 11397, 11399, + 11401, 11403, 11405, 11407, 11409, 11411, 11413, 11415, 11417, 11419, + 11421, 11423, 11425, 11427, 11429, 11431, 11433, 11435, 11437, 11439, + 11441, 11443, 11445, 11447, 11449, 11451, 11453, 11455, 11457, 11459, + 11461, 11463, 11465, 11467, 11469, 11471, 11473, 11475, 11477, 11479, + 11481, 11483, 11485, 11487, 11489, 11491, 11493, 11495, 11497, 11499, + 11501, 11503, 11505, 11507, 11509, 11511, 11513, 11515, 11517, 11519, + 11521, 11523, 11525, 11527, 11529, 11531, 11533, 11535, 11537, 11539, + 11541, 11543, 11545, 11547, 11549, 11551, 11553, 11555, 11557, 11559, + 11561, 11563, 11565, 11567, 11569, 11571, 11573, 11575, 11577, 11579, + 11581, 11583, 11585, 11587, 11589, 11591, 11593, 11595, 11597, 11599, + 11601, 11603, 11605, 11607, 11609, 11611, 11613, 11615, 11617, 11619, + 11621, 11623, 11625, 11627, 11629, 11631, 11633, 11635, 11637, 11639, + 11641, 11643, 11645, 11647, 11649, 11651, 11653, 11655, 11657, 11659, + 11661, 11663, 11665, 11667, 11669, 11671, 11673, 11675, 11677, 11679, + 11681, 11683, 11685, 11687, 11689, 11691, 11693, 11695, 11697, 11699, + 11701, 11703, 11705, 11707, 11709, 11711, 11713, 11715, 11717, 11719, + 11721, 11723, 11725, 11727, 11729, 11731, 11733, 11735, 11737, 11739, + 11741, 11743, 11745, 11747, 11749, 11751, 11753, 11755, 11757, 11759, + 11761, 11763, 11765, 11767, 11769, 11771, 11773, 11775, 11777, 11779, + 11781, 11783, 11785, 11787, 11789, 11791, 11793, 11795, 11797, 11799, + 11801, 11803, 11805, 11807, 11809, 11811, 11813, 11815, 11817, 11819, + 11821, 11823, 11825, 11827, 11829, 11831, 11833, 11835, 11837, 11839, 0, + 0, 11841, 11843, 11845, 11847, 11849, 11851, 11853, 11855, 11857, 11859, + 11861, 11863, 11865, 11867, 11869, 11871, 11873, 11875, 11877, 11879, + 11881, 11883, 11885, 11887, 11889, 11891, 11893, 11895, 11897, 11899, + 11901, 11903, 11905, 11907, 11909, 11911, 11913, 11915, 11917, 11919, + 11921, 11923, 11925, 11927, 11929, 11931, 11933, 11935, 11937, 11939, + 11941, 11943, 11945, 11947, 11949, 11951, 11953, 11955, 11957, 11959, + 11961, 11963, 11965, 11967, 11969, 11971, 11973, 11975, 11977, 11979, + 11981, 11983, 11985, 11987, 11989, 11991, 11993, 11995, 11997, 11999, + 12001, 12003, 12005, 12007, 12009, 12011, 12013, 12015, 12017, 12019, + 12021, 12023, 12025, 12027, 12029, 12031, 12033, 12035, 12037, 12039, + 12041, 12043, 12045, 12047, 12049, 12051, 12053, 12055, 12057, 12059, + 12061, 12063, 12065, 12067, 12069, 12071, 12073, 12075, 12077, 12079, + 12081, 12083, 12085, 12087, 12089, 12091, 12093, 12095, 12097, 12099, + 12101, 12103, 12105, 12107, 12109, 12111, 12113, 12115, 12117, 12119, + 12121, 12123, 12125, 12127, 12129, 12131, 12133, 12135, 12137, 12139, + 12141, 12143, 12145, 12147, 12149, 12151, 12153, 12155, 12157, 12159, + 12161, 12163, 12165, 12167, 12169, 12171, 12173, 12175, 12177, 12179, + 12181, 12183, 12185, 12187, 12189, 12191, 12193, 12195, 12197, 12199, + 12201, 12203, 12205, 12207, 12209, 12211, 12213, 12215, 12217, 12219, + 12221, 12223, 12225, 12227, 12229, 12231, 12233, 12235, 12237, 12239, + 12241, 12243, 12245, 12247, 12249, 12251, 12253, 12255, 12257, 12259, + 12261, 12263, 12265, 12267, 12269, 12271, 12273, 12275, 12277, 12279, + 12281, 12283, 12285, 12287, 12289, 12291, 12293, 12295, 12297, 12299, + 12301, 12303, 12305, 12307, 12309, 12311, 12313, 12315, 12317, 12319, + 12321, 12323, 12325, 12327, 12329, 12331, 12333, 12335, 12337, 12339, + 12341, 12343, 12345, 12347, 12349, 12351, 12353, 12355, 12357, 12359, + 12361, 12363, 12365, 12367, 12369, 12371, 12373, 12375, 12377, 12379, + 12381, 12383, 12385, 12387, 12389, 12391, 12393, 12395, 12397, 12399, + 12401, 12403, 12405, 12407, 12409, 12411, 12413, 12415, 12417, 12419, + 12421, 12423, 0, 0, 12425, 12427, 12429, 12431, 12433, 12435, 12437, + 12439, 12441, 12443, 12445, 12447, 12449, 12451, 12453, 12455, 12457, + 12459, 12461, 12463, 12465, 12467, 12469, 12471, 12473, 12475, 12477, + 12479, 12481, 12483, 12485, 12487, 12489, 12491, 12493, 12495, 12497, + 12499, 12501, 12503, 12505, 12507, 12509, 12511, 12513, 12515, 12517, + 12519, 12521, 12523, 12525, 12527, 12529, 12531, 0, 12533, 12535, 12537, + 12539, 12541, 12543, 12545, 12547, 12549, 12551, 12553, 12555, 12557, + 12559, 12561, 12563, 12565, 12567, 12569, 12571, 12573, 12575, 12577, + 12579, 12581, 12583, 12585, 0, 12587, 12589, 0, 12591, 0, 0, 12593, 0, + 12595, 12597, 12599, 12601, 12603, 12605, 12607, 12609, 12611, 12613, 0, + 12615, 12617, 12619, 12621, 0, 12623, 0, 12625, 0, 0, 0, 0, 0, 0, 12627, + 0, 0, 0, 0, 12629, 0, 12631, 0, 12633, 0, 12635, 12637, 12639, 0, 12641, + 12643, 0, 12645, 0, 0, 12647, 0, 12649, 0, 12651, 0, 12653, 0, 12655, 0, + 12657, 12659, 0, 12661, 0, 0, 12663, 12665, 12667, 12669, 0, 12671, + 12673, 12675, 12677, 12679, 12681, 12683, 0, 12685, 12687, 12689, 12691, + 0, 12693, 12695, 12697, 12699, 0, 12701, 0, 12703, 12705, 12707, 12709, + 12711, 12713, 12715, 12717, 12719, 12721, 0, 12723, 12725, 12727, 12729, + 12731, 12733, 12735, 12737, 12739, 12741, 12743, 12745, 12747, 12749, + 12751, 12753, 12755, 0, 0, 0, 0, 0, 12757, 12759, 12761, 0, 12763, 12765, + 12767, 12769, 12771, 0, 12773, 12775, 12777, 12779, 12781, 12783, 12785, + 12787, 12789, 12791, 12793, 12795, 12797, 12799, 12801, 12803, 12805, 0, + 0, 0, 0, 12807, 12810, 12813, 12816, 12819, 12822, 12825, 12828, 12831, + 12834, 12837, 0, 0, 0, 0, 0, 12840, 12844, 12848, 12852, 12856, 12860, + 12864, 12868, 12872, 12876, 12880, 12884, 12888, 12892, 12896, 12900, + 12904, 12908, 12912, 12916, 12920, 12924, 12928, 12932, 12936, 12940, + 12944, 12948, 12950, 12952, 12955, 0, 12958, 12960, 12962, 12964, 12966, + 12968, 12970, 12972, 12974, 12976, 12978, 12980, 12982, 12984, 12986, + 12988, 12990, 12992, 12994, 12996, 12998, 13000, 13002, 13004, 13006, + 13008, 13010, 13013, 13016, 13019, 13022, 13026, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 13029, 13032, 0, 0, 0, 0, 13035, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 13038, 13041, 13044, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 13046, 13048, 13050, 13052, 13054, 13056, 13058, 13060, 13062, 13064, + 13066, 13068, 13070, 13072, 13074, 13076, 13078, 13080, 13082, 13084, + 13086, 13088, 13090, 13092, 13094, 13096, 13098, 13100, 13102, 13104, + 13106, 13108, 13110, 13112, 13114, 13116, 13118, 13120, 13122, 13124, + 13126, 13128, 13130, 0, 0, 0, 0, 0, 13132, 13136, 13140, 13144, 13148, + 13152, 13156, 13160, 13164, 0, 0, 0, 0, 0, 0, 0, 13168, 13170, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13172, 13174, 13176, 13178, 13181, + 13183, 13185, 13187, 13189, 13191, 13193, 13195, 13197, 13199, 13202, + 13204, 13206, 13208, 13210, 13213, 13215, 13217, 13219, 13222, 13224, + 13226, 13228, 13230, 13232, 13235, 13237, 13239, 13241, 13243, 13245, + 13247, 13249, 13251, 13253, 13255, 13257, 13259, 13261, 13263, 13265, + 13267, 13269, 13271, 13273, 13275, 13277, 13279, 13281, 13284, 13286, + 13288, 13290, 13293, 13295, 13297, 13299, 13301, 13303, 13305, 13307, + 13309, 13311, 13313, 13315, 13317, 13319, 13321, 13323, 13325, 13327, + 13329, 13331, 13333, 13335, 13337, 13339, 13341, 13343, 13345, 13347, + 13349, 13351, 13353, 13355, 13357, 13360, 13362, 13364, 13366, 13368, + 13370, 13372, 13375, 13378, 13380, 13382, 13384, 13386, 13388, 13390, + 13392, 13394, 13396, 13398, 13401, 13403, 13405, 13407, 13409, 13412, + 13414, 13416, 13418, 13420, 13422, 13424, 13426, 13428, 13430, 13433, + 13435, 13438, 13440, 13442, 13444, 13446, 13448, 13450, 13452, 13454, + 13456, 13458, 13460, 13463, 13465, 13467, 13469, 13471, 13473, 13476, + 13478, 13481, 13484, 13486, 13488, 13490, 13492, 13495, 13498, 13500, + 13502, 13504, 13506, 13508, 13510, 13512, 13514, 13516, 13518, 13520, + 13523, 13525, 13527, 13529, 13531, 13533, 13535, 13537, 13539, 13541, + 13543, 13545, 13547, 13549, 13551, 13553, 13555, 13557, 13559, 13561, + 13564, 13566, 13568, 13570, 13572, 13574, 13577, 13579, 13581, 13583, + 13585, 13587, 13589, 13591, 13593, 13595, 13597, 13599, 13602, 13604, + 13606, 13608, 13610, 13612, 13614, 13616, 13618, 13620, 13622, 13624, + 13626, 13628, 13630, 13632, 13634, 13636, 13638, 13641, 13643, 13645, + 13647, 13649, 13651, 13654, 13656, 13658, 13660, 13662, 13664, 13666, + 13668, 13670, 13673, 13675, 13677, 13679, 13682, 13684, 13686, 13688, + 13690, 13692, 13694, 13697, 13700, 13703, 13705, 13708, 13710, 13712, + 13714, 13716, 13718, 13720, 13722, 13724, 13726, 13728, 13731, 13733, + 13735, 13737, 13739, 13741, 13743, 13746, 13748, 13750, 13753, 13756, + 13758, 13760, 13762, 13764, 13766, 13768, 13770, 13772, 13774, 13777, + 13779, 13782, 13784, 13787, 13789, 13791, 13793, 13796, 13798, 13800, + 13803, 13806, 13808, 13810, 13812, 13814, 13816, 13818, 13820, 13822, + 13824, 13826, 13828, 13830, 13832, 13835, 13837, 13840, 13842, 13845, + 13847, 13850, 13853, 13856, 13858, 13860, 13862, 13865, 13868, 13871, + 13874, 13876, 13878, 13880, 13882, 13884, 13886, 13888, 13890, 13893, + 13895, 13897, 13899, 13901, 13904, 13906, 13909, 13912, 13914, 13916, + 13918, 13920, 13922, 13924, 13927, 13930, 13933, 13935, 13937, 13940, + 13942, 13944, 13946, 13949, 13951, 13953, 13955, 13957, 13959, 13962, + 13964, 13966, 13968, 13970, 13972, 13974, 13977, 13980, 13982, 13985, + 13987, 13990, 13992, 13994, 13996, 13999, 14002, 14004, 14007, 14009, + 14012, 14014, 14016, 14018, 14020, 14022, 14024, 14027, 14030, 14033, + 14036, 14038, 14040, 14042, 14044, 14046, 14048, 14050, 14052, 14054, + 14056, 14058, 14060, 14063, 14065, 14067, 14069, 14071, 14073, 14075, + 14077, 14079, 14081, 14083, 14085, 14087, 14090, 14093, 14096, 14098, + 14100, 14102, 14104, 14107, 14109, 14112, 14114, 14116, 14119, 14122, + 14124, 14126, 14128, 14130, 14132, 14134, 14136, 14138, 14140, 14142, + 14144, 14146, 14148, 14150, 14152, 14154, 14156, 14158, 14160, 14163, + 14165, 14167, 14169, 14171, 14173, 14176, 14179, 14181, 14183, 14185, + 14187, 14189, 14191, 14194, 14196, 14198, 14200, 14202, 14205, 14208, + 14210, 14212, 14214, 14217, 14219, 14221, 14224, 14227, 14229, 14231, + 14233, 14236, 14238, 14240, 14242, 14244, 14246, 14248, 14250, 14253, + 14255, 14257, 14259, 14262, 14264, 14266, 14268, 14270, 14273, 14276, + 14278, 14280, 14282, 14285, 14287, 14290, 14292, 14294, 14296, 14299, + 14301, 14303, 14305, 14307, 14309, 14311, 14313, 14316, 14318, 14320, + 14322, 14324, 14326, 14328, 14331, 14333, 14336, 14339, 14342, 14344, + 14346, 14348, 14350, 14352, 14354, 14356, 14358, 0, 0, +}; + +/* NFC pairs */ +#define COMP_SHIFT1 2 +#define COMP_SHIFT2 1 +static const unsigned short comp_index0[] = { + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, 6, + 0, 0, 0, 0, 7, 0, 8, 9, 0, 0, 0, 10, 11, 12, 0, 0, 0, 0, 13, 14, 15, 16, + 0, 0, 0, 17, 18, 19, 20, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 22, 23, 24, 0, 0, + 0, 0, 25, 26, 27, 28, 0, 0, 0, 29, 30, 31, 32, 0, 0, 0, 33, 0, 0, 0, 0, + 0, 0, 34, 35, 36, 37, 0, 0, 0, 38, 39, 40, 41, 0, 0, 0, 42, 0, 43, 0, 0, + 0, 0, 44, 45, 46, 47, 0, 0, 0, 48, 49, 50, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, + 0, 52, 53, 54, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, 59, 60, 61, 62, 0, 0, + 0, 63, 64, 65, 66, 0, 0, 0, 67, 0, 68, 0, 0, 0, 0, 69, 0, 70, 0, 0, 0, 0, + 71, 0, 0, 0, 0, 0, 0, 72, 73, 74, 0, 0, 0, 0, 75, 76, 77, 78, 0, 0, 0, + 79, 80, 81, 0, 0, 0, 0, 82, 0, 83, 84, 0, 0, 0, 85, 86, 87, 0, 0, 0, 0, + 88, 89, 90, 91, 0, 0, 0, 92, 93, 94, 95, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, + 97, 98, 99, 0, 0, 0, 0, 100, 101, 102, 103, 0, 0, 0, 104, 105, 106, 107, + 0, 0, 0, 108, 109, 0, 0, 0, 0, 0, 110, 111, 112, 113, 0, 0, 0, 114, 115, + 116, 117, 0, 0, 0, 118, 0, 119, 0, 0, 0, 0, 120, 121, 122, 123, 0, 0, 0, + 124, 125, 126, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0, 128, 129, 130, 131, 0, + 0, 0, 132, 133, 134, 0, 0, 0, 0, 135, 136, 137, 138, 0, 0, 0, 139, 140, + 141, 142, 0, 0, 0, 143, 0, 144, 0, 0, 0, 0, 145, 146, 147, 0, 0, 0, 0, + 148, 0, 0, 0, 0, 0, 0, 149, 150, 151, 0, 0, 0, 0, 152, 153, 154, 155, 0, + 0, 0, 156, 0, 0, 157, 0, 0, 0, 158, 159, 0, 0, 0, 0, 0, 160, 0, 0, 0, 0, + 0, 0, 161, 0, 0, 0, 0, 0, 0, 162, 0, 0, 0, 0, 0, 0, 163, 0, 0, 0, 0, 0, + 0, 164, 165, 0, 0, 0, 0, 0, 166, 0, 0, 0, 0, 0, 0, 167, 168, 0, 0, 0, 0, + 0, 169, 0, 0, 0, 0, 0, 0, 170, 0, 0, 0, 0, 0, 0, 171, 0, 0, 0, 0, 0, 0, + 172, 173, 0, 0, 0, 0, 0, 174, 175, 0, 0, 0, 0, 0, 176, 0, 0, 0, 0, 0, 0, + 177, 0, 0, 0, 0, 0, 0, 178, 0, 0, 0, 0, 0, 0, 179, 0, 0, 0, 0, 0, 0, 180, + 181, 0, 0, 0, 0, 0, 182, 0, 0, 0, 0, 0, 0, 183, 184, 0, 0, 0, 0, 0, 185, + 0, 0, 0, 0, 0, 0, 186, 0, 0, 0, 0, 0, 0, 187, 0, 0, 0, 0, 0, 0, 188, 189, + 0, 0, 0, 0, 0, 190, 191, 0, 0, 0, 0, 0, 192, 193, 0, 0, 0, 0, 0, 194, 0, + 0, 0, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 196, 0, 0, 0, 0, 0, 0, 197, 0, 0, + 0, 0, 0, 0, 198, 0, 0, 0, 0, 0, 0, 199, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, + 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 202, 0, 0, 0, 0, 0, 0, 203, 0, 0, 0, 0, + 0, 0, 204, 0, 0, 0, 0, 0, 0, 205, 0, 0, 0, 0, 0, 0, 206, 0, 0, 0, 0, 0, + 0, 207, 208, 209, 0, 0, 0, 0, 210, 211, 212, 0, 0, 0, 0, 213, 214, 215, + 0, 0, 0, 0, 216, 217, 218, 0, 0, 0, 0, 0, 219, 0, 0, 0, 0, 0, 220, 0, 0, + 0, 0, 0, 0, 221, 0, 0, 0, 0, 0, 0, 222, 0, 0, 0, 0, 0, 0, 223, 0, 0, 0, + 0, 0, 0, 224, 0, 0, 0, 0, 0, 0, 225, 0, 0, 0, 0, 0, 0, 226, 0, 0, 0, 0, + 0, 0, 227, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 229, 230, 0, 231, 0, + 0, 0, 232, 233, 0, 0, 0, 0, 0, 234, 235, 0, 236, 0, 0, 0, 237, 238, 0, 0, + 0, 0, 0, 239, 240, 0, 0, 0, 0, 0, 0, 241, 0, 0, 0, 0, 0, 242, 243, 0, 0, + 0, 0, 0, 244, 245, 0, 246, 0, 0, 0, 0, 0, 0, 247, 0, 0, 0, 0, 0, 0, 248, + 0, 0, 0, 249, 250, 0, 251, 0, 0, 0, 252, 253, 0, 0, 0, 0, 0, 254, 255, 0, + 256, 0, 0, 0, 257, 258, 0, 259, 0, 0, 0, 260, 261, 0, 0, 0, 0, 0, 0, 262, + 0, 0, 0, 0, 0, 263, 264, 0, 265, 0, 0, 0, 266, 267, 0, 268, 0, 0, 0, 269, + 0, 0, 270, 0, 0, 0, 271, 0, 0, 272, 0, 0, 0, 0, 0, 0, 273, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 275, 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, 0, 277, 0, + 0, 0, 0, 0, 0, 278, 0, 0, 0, 0, 0, 0, 279, 0, 0, 0, 0, 0, 0, 280, 0, 0, + 0, 0, 0, 0, 281, 0, 0, 0, 0, 0, 0, 282, 0, 0, 0, 0, 0, 0, 283, 0, 0, 0, + 0, 0, 0, 284, 285, 0, 0, 0, 0, 0, 286, 0, 0, 0, 0, 0, 0, 287, 0, 0, 0, 0, + 0, 0, 288, 0, 0, 0, 0, 0, 0, 289, 0, 0, 0, 0, 0, 0, 290, 0, 0, 0, 0, 0, + 0, 291, 0, 0, 0, 0, 0, 0, 292, 0, 0, 0, 0, 0, 0, 293, 0, 0, 0, 0, 0, 0, + 294, 0, 0, 0, 0, 0, 0, 295, 0, 0, 0, 0, 0, 0, 296, 0, 0, 0, 0, 0, 0, 297, + 298, 0, 0, 0, 0, 0, 299, 0, 0, 0, 0, 0, 0, 300, 0, 0, 0, 0, 0, 0, 301, 0, + 0, 0, 0, 0, 0, 302, 0, 0, 0, 0, 0, 0, 0, 303, 0, 0, 0, 0, 0, 0, 304, 0, + 0, 0, 0, 0, 305, 0, 0, 0, 0, 0, 0, 306, 0, 0, 0, 0, 0, 0, 307, 0, 0, 0, + 0, 0, 0, 308, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 310, 0, 0, 0, 0, 0, 311, 0, + 0, 0, 0, 0, 0, 312, 0, 0, 0, 0, 0, 0, 313, 0, 0, 0, 0, 0, 0, 314, 0, 0, + 0, 0, 0, 0, 315, 0, 0, 0, 0, 0, 0, 0, 316, 0, 0, 0, 0, 0, 0, 317, 0, 0, + 0, 0, 0, 0, 318, 0, 0, 0, 0, 0, 0, 319, 0, 0, 0, 0, 0, 0, 320, 0, 0, 0, + 0, 0, 0, 0, 321, 0, 0, 0, 0, 0, 322, 323, 0, 0, 0, 0, 0, 324, 0, 0, 0, 0, + 0, 0, 0, 325, 0, 0, 0, 0, 0, 0, 326, 0, 0, 0, 0, 0, 0, 327, 0, 0, 0, 0, + 0, 0, 328, 0, 0, 0, 0, 0, 0, 329, 0, 0, 0, 0, 0, 0, 330, 0, 0, 0, 0, 0, + 0, 331, 332, 0, 0, 0, 0, 0, 333, 0, 0, 0, 0, 0, 0, 0, 334, 0, 0, 0, 0, 0, + 0, 335, 0, 0, 0, 0, 0, 0, 336, 0, 0, 0, 0, 0, 0, 337, 0, 0, 0, 0, 0, 0, + 338, 0, 0, 0, 0, 0, 0, 339, 0, 0, 0, 0, 0, 0, 340, 0, 0, 0, 0, 0, 0, 341, + 0, 0, 0, 0, 0, 0, 342, 0, 0, 0, 0, 0, 0, 343, 0, 0, 0, 0, 0, 0, 344, 0, + 0, 0, 0, 0, 0, 345, 346, 0, 0, 0, 0, 0, 0, 347, 0, 0, 0, 0, 0, 0, 348, 0, + 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 350, 0, 0, 0, 0, 0, 0, 351, 0, 0, + 0, 0, 0, 0, 352, 0, 0, 0, 0, 0, 0, 353, 0, 0, 0, 0, 0, 0, 354, 0, 0, 0, + 0, 0, 0, 355, 0, 0, 0, 0, 0, 0, 356, 0, 0, 0, 0, 0, 0, 357, 0, 0, 0, 0, + 0, 0, 358, 0, 0, 359, 0, 0, 0, 360, 0, 0, 361, 0, 0, 0, 0, 0, 0, 362, 0, + 0, 0, 0, 0, 0, 363, 0, 0, 0, 0, 0, 0, 364, 0, 0, 0, 0, 0, 0, 365, 0, 0, + 0, 0, 0, 0, 366, 0, 0, 0, 0, 0, 0, 367, 0, 0, 0, 368, 0, 0, 369, 0, 0, 0, + 370, 0, 0, 371, 0, 0, 0, 0, 0, 0, 372, 0, 0, 0, 0, 0, 0, 373, 0, 0, 0, 0, + 0, 0, 374, 0, 0, 0, 0, 0, 0, 375, 0, 0, 0, 0, 0, 0, 376, 0, 0, 0, 0, 0, + 0, 377, 0, 0, 0, 378, 0, 0, 0, 0, 0, 0, 379, 0, 0, 0, 0, 0, 0, 380, 0, 0, + 0, 0, 0, 0, 381, 0, 0, 0, 0, 0, 0, 382, 0, 0, 383, 0, 0, 0, 384, 0, 0, + 385, 0, 0, 0, 0, 0, 0, 386, 0, 0, 0, 0, 0, 0, 387, 0, 0, 0, 0, 0, 0, 388, + 0, 0, 0, 0, 0, 0, 389, 0, 0, 0, 0, 0, 0, 390, 0, 0, 0, 0, 0, 0, 391, 0, + 0, 0, 392, 0, 0, 393, 0, 0, 0, 394, 0, 0, 395, 0, 0, 0, 0, 0, 0, 396, 0, + 0, 0, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, 398, 0, 0, 0, 0, 0, 0, 399, 0, 0, + 0, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 401, 0, 0, 0, 402, 0, 0, 403, 0, 0, 0, + 404, 0, 0, 405, 0, 0, 0, 406, 0, 0, 407, 0, 0, 0, 408, 0, 0, 409, 0, 0, + 0, 410, 0, 0, 0, 0, 0, 0, 411, 0, 0, 0, 0, 0, 0, 412, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 414, 0, 0, 415, 0, 0, 0, 416, 0, 0, 417, 0, 0, 0, + 418, 0, 0, 419, 0, 0, 0, 420, 0, 0, 421, 0, 0, 0, 422, 0, 0, 423, 0, 0, + 0, 0, 0, 0, 424, 0, 0, 0, 0, 0, 0, 425, 0, 0, 0, 0, 0, 0, 426, 0, 0, 0, + 0, 0, 0, 427, 0, 0, 0, 0, 0, 0, 428, 0, 0, 0, 0, 0, 0, 429, 0, 0, 0, 430, + 0, 0, 431, 0, 0, 0, 432, 0, 0, 433, 0, 0, 0, 0, 0, 0, 434, 0, 0, 0, 0, 0, + 0, 435, 0, 0, 0, 0, 0, 0, 436, 0, 0, 0, 0, 0, 0, 437, 0, 0, 0, 0, 0, 0, + 438, 0, 0, 0, 0, 0, 0, 439, 0, 0, 0, 0, 0, 0, 440, 0, 0, 0, 0, 0, 0, 441, + 0, 0, 0, 0, 0, 0, 442, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 444, 0, 0, 445, 0, + 0, 0, 0, 0, 0, 446, 0, 0, 0, 0, 0, 0, 447, 0, 0, 0, 448, 0, 0, 449, 0, 0, + 0, 0, 0, 0, 450, 0, 0, 0, 0, 0, 0, 451, 0, 0, 0, 0, 0, 0, 452, 0, 0, 0, + 0, 0, 0, 453, 0, 0, 0, 0, 0, 0, 454, 0, 0, 0, 0, 0, 0, 455, 0, 0, 0, 0, + 0, 0, 456, 0, 0, 0, 0, 0, 0, 457, 0, 0, 0, 0, 0, 0, 458, 0, 0, 0, 0, 0, + 0, 459, 0, 0, 0, 0, 0, 0, 460, 0, 0, 0, 0, 0, 0, 461, 0, 0, 0, 0, 0, 0, + 462, 0, 0, 0, 0, 0, 0, 463, 0, 0, 0, 0, 0, 0, 464, 0, 0, 0, 0, 0, 0, 465, + 0, 0, 0, 0, 0, 0, 466, 0, 0, 0, 0, 0, 0, 467, 0, 0, 0, 0, 0, 0, 468, 0, + 0, 0, 0, 0, 0, 469, 0, 0, 0, 0, 0, 0, 470, 0, 0, 0, 0, 0, 0, 471, 0, 0, + 0, 0, 0, 0, 472, 0, 0, 0, 0, 0, 0, 473, 0, 0, 0, 0, 0, 0, 474, 0, 0, 0, + 0, 0, 0, 475, 0, 0, 0, 0, 0, 0, 476, 0, 0, 0, 0, 0, 0, 477, 0, 0, 0, 0, + 0, 0, 478, 0, 0, 0, 0, 0, 0, 479, 0, 0, 0, 0, 0, 0, 480, 0, 0, 0, 0, 0, + 0, 481, 0, 0, 0, 0, 0, 0, 482, 0, 0, 0, 0, 0, 0, 483, 0, 0, 0, 0, 0, 0, + 484, 0, 0, 0, 0, 0, 0, 485, 0, 0, 0, 0, 0, 0, 486, 0, 0, 0, 0, 0, 0, 487, + 0, 0, 0, 0, 0, 0, 488, 0, 0, 0, 0, 0, 0, 489, 0, 0, 0, 0, 0, 0, 490, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 491, 0, 0, 0, 0, 0, 0, 492, 0, 0, 0, 0, 0, 0, + 493, 0, 0, 0, 0, 0, 0, 494, 0, 0, 0, 0, 0, 0, 495, 0, 0, 0, 0, 0, 0, 496, + 0, 0, 0, 0, 0, 0, 497, 0, 0, 0, 0, 0, 0, 498, 0, 0, 0, 0, 0, 0, 499, 0, + 0, 0, 0, 0, 0, 500, 0, 0, 0, 0, 0, 0, 501, 0, 0, 0, 0, 0, 0, 502, 0, 0, + 0, 0, 0, 0, 503, 0, 0, 0, 0, 0, 0, 504, 0, 0, 0, 0, 0, 0, 505, 0, 0, 0, + 0, 0, 0, 506, 0, 0, 0, 0, 0, 0, 507, 0, 0, 0, 0, 0, 0, 508, 0, 0, 0, 0, + 0, 0, 509, 0, 0, 0, 0, 0, 0, 510, 0, 0, 0, 0, 0, 0, 511, 0, 0, 0, 0, 0, + 0, 512, 0, 0, 0, 0, 0, 0, 513, 0, 0, 0, 0, 0, 0, 514, 0, 0, 0, 0, 0, 0, + 515, 0, 0, 0, 0, 0, 0, 516, 0, 0, 0, 0, 0, 0, 517, 0, 0, 0, 0, 0, 0, 518, + 0, 0, 0, 0, 0, 0, 519, 0, 0, 0, 0, 0, 0, 520, 0, 0, 0, 0, 0, 0, 521, 0, + 0, 0, 0, 0, 0, 522, 0, 0, 0, 0, 0, 0, 523, 0, 0, 0, 0, 0, 0, 524, 0, 0, + 0, 0, 0, 0, 525, 0, 0, 0, 0, 0, 0, 526, 0, 0, 0, 0, 0, 0, 527, 0, 0, 0, + 0, 0, 0, 528, 0, 0, 0, 0, 0, 0, 529, 0, 0, 0, 0, 0, 0, 530, 0, 0, 0, 0, + 0, 0, 531, 0, 0, 0, 0, 0, 0, 532, 0, 0, 0, 0, 0, 0, 533, 0, 0, 0, 0, 0, + 0, 534, 0, 0, 0, 0, 0, 0, 535, 0, 0, 0, 0, 0, 0, 536, 0, 0, 0, 0, 0, 0, + 537, 0, 0, 0, 0, 0, 0, 538, 0, 0, 0, 0, 0, 0, 539, 0, 0, 0, 0, 0, 0, 540, + 0, 0, 0, 0, 0, 0, 541, 0, 0, 0, 0, 0, 0, 542, 0, 0, 0, 0, 0, 0, 543, +}; + +static const unsigned short comp_index1[] = { + 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 4, 5, 6, 7, 8, 9, 10, 0, + 11, 12, 0, 13, 0, 0, 0, 14, 15, 0, 0, 0, 0, 16, 0, 0, 17, 18, 0, 19, 0, + 20, 0, 0, 0, 0, 21, 0, 0, 0, 0, 22, 0, 23, 0, 0, 24, 0, 25, 26, 0, 27, 0, + 0, 28, 29, 30, 31, 32, 33, 34, 0, 35, 0, 36, 37, 38, 0, 0, 0, 0, 0, 0, + 39, 40, 41, 42, 43, 0, 44, 0, 0, 0, 0, 45, 0, 0, 46, 0, 47, 0, 48, 0, 0, + 49, 0, 50, 0, 51, 0, 0, 0, 52, 53, 54, 55, 56, 57, 58, 0, 59, 0, 0, 60, + 61, 0, 0, 0, 0, 62, 0, 0, 63, 0, 0, 0, 0, 64, 0, 0, 65, 0, 66, 0, 0, 67, + 0, 0, 68, 0, 0, 0, 0, 69, 0, 0, 70, 0, 71, 72, 0, 73, 0, 0, 74, 0, 0, 75, + 76, 0, 0, 0, 77, 78, 0, 79, 0, 80, 0, 0, 81, 0, 82, 83, 0, 84, 0, 0, 85, + 86, 87, 88, 89, 90, 91, 0, 92, 0, 0, 93, 94, 0, 0, 95, 96, 0, 0, 97, 0, + 98, 99, 0, 100, 0, 101, 0, 0, 102, 0, 0, 103, 104, 0, 105, 0, 106, 0, 0, + 107, 0, 108, 0, 0, 0, 0, 109, 0, 110, 0, 0, 111, 0, 112, 113, 0, 114, 0, + 0, 115, 116, 117, 118, 119, 120, 121, 0, 122, 123, 0, 124, 125, 0, 0, 0, + 0, 126, 0, 0, 127, 0, 0, 0, 128, 129, 0, 130, 131, 0, 0, 0, 0, 0, 0, 132, + 133, 134, 135, 136, 137, 0, 0, 0, 138, 0, 0, 0, 139, 140, 0, 141, 0, 142, + 0, 0, 143, 0, 0, 0, 0, 144, 0, 0, 145, 146, 147, 148, 149, 150, 151, 0, + 152, 153, 0, 154, 0, 0, 0, 155, 156, 0, 0, 0, 0, 157, 0, 0, 158, 159, 0, + 160, 0, 161, 0, 0, 0, 0, 162, 0, 0, 0, 0, 163, 0, 164, 0, 0, 165, 0, 166, + 167, 0, 168, 0, 0, 169, 170, 171, 172, 173, 174, 175, 0, 176, 0, 177, + 178, 179, 0, 0, 0, 0, 0, 0, 180, 181, 182, 183, 184, 0, 185, 0, 0, 0, 0, + 186, 0, 0, 187, 0, 188, 0, 189, 0, 0, 190, 0, 191, 0, 192, 193, 0, 0, + 194, 195, 196, 197, 198, 199, 200, 0, 201, 0, 0, 202, 203, 0, 0, 0, 0, + 204, 0, 0, 0, 205, 0, 0, 206, 0, 0, 0, 0, 207, 0, 0, 208, 0, 209, 0, 0, + 210, 0, 0, 211, 0, 0, 0, 0, 212, 0, 0, 213, 0, 214, 215, 0, 216, 0, 0, + 217, 0, 0, 218, 219, 0, 0, 0, 220, 221, 0, 222, 0, 223, 0, 0, 224, 0, + 225, 226, 0, 227, 0, 0, 228, 229, 230, 231, 232, 233, 234, 0, 235, 0, 0, + 236, 237, 0, 0, 238, 239, 0, 0, 240, 0, 241, 242, 0, 243, 0, 244, 0, 0, + 245, 0, 0, 246, 247, 0, 248, 0, 249, 0, 0, 250, 0, 251, 0, 0, 0, 0, 252, + 0, 253, 0, 0, 254, 0, 255, 256, 0, 257, 0, 0, 258, 259, 260, 261, 262, + 263, 264, 0, 265, 266, 0, 267, 268, 0, 0, 0, 0, 269, 0, 0, 270, 0, 0, 0, + 271, 272, 0, 273, 274, 0, 0, 0, 275, 0, 0, 0, 0, 0, 0, 276, 277, 278, + 279, 280, 281, 0, 0, 0, 282, 0, 0, 0, 283, 284, 0, 285, 0, 286, 0, 0, + 287, 0, 0, 0, 0, 288, 0, 0, 289, 0, 0, 0, 0, 0, 290, 0, 291, 292, 0, 0, + 293, 0, 0, 0, 0, 0, 294, 0, 295, 0, 0, 0, 296, 0, 297, 0, 298, 0, 0, 0, + 299, 300, 0, 0, 301, 0, 0, 0, 302, 0, 0, 0, 303, 304, 0, 0, 305, 0, 0, 0, + 306, 0, 307, 308, 0, 0, 309, 0, 310, 0, 0, 0, 311, 0, 312, 0, 0, 313, 0, + 0, 314, 315, 0, 0, 316, 0, 0, 0, 0, 0, 317, 0, 318, 0, 0, 0, 319, 0, 320, + 0, 321, 0, 0, 0, 322, 323, 0, 0, 324, 0, 0, 0, 325, 0, 0, 0, 326, 327, 0, + 0, 328, 0, 0, 0, 329, 0, 330, 331, 0, 0, 332, 0, 333, 0, 0, 0, 334, 0, + 335, 0, 0, 336, 0, 0, 337, 338, 0, 0, 339, 0, 0, 0, 340, 341, 0, 0, 342, + 0, 0, 0, 343, 0, 0, 0, 344, 0, 0, 0, 345, 0, 0, 0, 346, 0, 0, 0, 0, 0, 0, + 347, 0, 0, 0, 348, 0, 0, 0, 349, 0, 0, 0, 350, 351, 0, 0, 0, 352, 0, 0, + 0, 0, 0, 0, 353, 0, 0, 0, 354, 0, 0, 0, 355, 356, 357, 0, 0, 358, 0, 0, + 0, 359, 0, 0, 0, 360, 361, 0, 0, 362, 0, 0, 0, 363, 0, 0, 0, 364, 365, 0, + 0, 366, 0, 0, 0, 367, 0, 0, 0, 368, 369, 0, 0, 370, 0, 0, 0, 371, 0, 0, + 0, 0, 372, 0, 0, 0, 0, 373, 0, 0, 0, 374, 0, 0, 0, 375, 0, 0, 0, 376, 0, + 0, 0, 377, 0, 0, 0, 378, 0, 0, 0, 379, 0, 0, 0, 380, 0, 0, 381, 0, 0, + 382, 0, 383, 0, 0, 0, 0, 384, 0, 0, 385, 0, 386, 0, 0, 0, 0, 0, 0, 387, + 388, 0, 0, 0, 0, 0, 0, 389, 0, 0, 390, 0, 391, 0, 392, 393, 0, 0, 0, 394, + 395, 0, 0, 0, 0, 0, 0, 396, 0, 0, 0, 397, 398, 0, 399, 400, 0, 0, 0, 401, + 402, 0, 0, 0, 0, 0, 0, 403, 0, 0, 404, 0, 0, 0, 405, 0, 0, 0, 406, 0, + 407, 0, 408, 0, 0, 0, 0, 409, 0, 0, 410, 0, 411, 0, 0, 0, 0, 0, 0, 412, + 413, 0, 0, 0, 0, 0, 0, 414, 0, 0, 415, 0, 416, 0, 417, 418, 0, 0, 0, 419, + 0, 0, 420, 0, 421, 0, 0, 0, 0, 0, 0, 422, 0, 0, 0, 423, 424, 0, 425, 426, + 0, 0, 0, 427, 0, 0, 428, 0, 429, 0, 0, 0, 0, 0, 0, 430, 0, 0, 431, 0, + 432, 0, 0, 0, 0, 0, 433, 0, 434, 0, 0, 0, 0, 0, 435, 0, 0, 0, 436, 0, + 437, 0, 0, 438, 0, 0, 0, 439, 0, 0, 440, 441, 442, 0, 0, 0, 443, 0, 444, + 445, 0, 0, 446, 447, 0, 0, 0, 448, 449, 0, 450, 451, 452, 0, 0, 0, 0, 0, + 0, 453, 0, 0, 454, 455, 0, 456, 0, 0, 0, 0, 0, 457, 0, 0, 0, 458, 0, 0, + 0, 459, 0, 0, 460, 461, 462, 0, 0, 0, 463, 0, 464, 465, 0, 0, 466, 467, + 0, 0, 0, 468, 469, 0, 470, 471, 472, 0, 0, 0, 0, 0, 0, 473, 0, 0, 474, + 475, 0, 476, 0, 0, 0, 0, 0, 477, 0, 0, 0, 478, 0, 0, 0, 479, 0, 0, 0, + 480, 0, 0, 481, 0, 0, 0, 482, 0, 0, 0, 0, 483, 0, 0, 0, 484, 0, 0, 0, + 485, 0, 0, 0, 486, 0, 0, 0, 487, 488, 0, 0, 0, 0, 0, 0, 489, 0, 0, 0, + 490, 0, 0, 0, 491, 0, 0, 0, 492, 0, 0, 0, 493, 494, 0, 0, 0, 495, 0, 0, + 0, 496, 0, 0, 0, 0, 497, 0, 0, 0, 0, 498, 499, 500, 0, 0, 0, 0, 0, 0, + 501, 502, 0, 0, 0, 0, 0, 0, 503, 504, 0, 0, 0, 0, 505, 0, 0, 0, 506, 507, + 0, 0, 508, 0, 0, 0, 0, 509, 510, 0, 0, 511, 0, 0, 0, 0, 512, 513, 0, 0, + 0, 0, 0, 0, 514, 0, 515, 0, 0, 0, 516, 0, 0, 0, 517, 0, 0, 0, 518, 0, 0, + 0, 519, 0, 0, 0, 520, 0, 0, 0, 521, 0, 0, 0, 522, 0, 0, 0, 523, 0, 0, 0, + 524, 0, 0, 0, 525, 0, 0, 0, 526, 0, 0, 0, 0, 527, 0, 0, 0, 528, 0, 0, 0, + 529, 0, 0, 0, 530, 0, 0, 0, 0, 531, 0, 0, 0, 532, 0, 533, 534, 0, 0, 535, + 536, 0, 0, 537, 0, 0, 0, 538, 0, 0, 0, 539, 0, 0, 0, 540, 0, 0, 541, 0, + 0, 0, 0, 0, 542, 0, 543, 0, 0, 0, 0, 0, 544, 0, 0, 0, 545, 0, 0, 0, 546, + 0, 0, 0, 547, 0, 0, 0, 548, 0, 0, 0, 549, 0, 0, 0, 550, 0, 551, 0, 0, 0, + 0, 0, 552, 0, 553, 0, 0, 0, 0, 0, 554, 0, 0, 0, 555, 0, 0, 0, 556, 0, 0, + 0, 557, 0, 0, 0, 558, 0, 0, 0, 559, 0, 0, 0, 560, 0, 561, 0, 0, 0, 562, + 0, 0, 0, 563, 0, 0, 0, 564, 0, 0, 0, 565, 0, 0, 0, 0, 0, 566, 0, 567, 0, + 0, 0, 0, 0, 568, 0, 0, 0, 569, 0, 0, 0, 570, 0, 0, 0, 571, 0, 0, 0, 572, + 0, 0, 0, 573, 0, 0, 0, 574, 0, 575, 0, 0, 0, 0, 0, 576, 0, 577, 0, 0, 0, + 0, 0, 578, 0, 0, 0, 579, 0, 0, 0, 580, 0, 0, 0, 581, 0, 0, 0, 582, 0, 0, + 0, 583, 0, 0, 0, 584, 0, 585, 0, 0, 0, 0, 0, 586, 0, 587, 0, 0, 0, 0, 0, + 588, 0, 589, 0, 0, 0, 0, 0, 590, 0, 591, 0, 0, 0, 0, 0, 592, 0, 593, 0, + 0, 0, 594, 0, 0, 0, 595, 0, 0, 0, 596, 0, 0, 0, 597, 0, 0, 0, 0, 0, 598, + 0, 599, 0, 0, 0, 0, 0, 600, 0, 601, 0, 0, 0, 0, 0, 602, 0, 603, 0, 0, 0, + 0, 0, 604, 0, 605, 0, 0, 0, 0, 0, 606, 0, 0, 0, 607, 0, 0, 0, 608, 0, 0, + 0, 609, 0, 0, 0, 610, 0, 0, 0, 611, 0, 0, 0, 612, 0, 613, 0, 0, 0, 0, 0, + 614, 0, 615, 0, 0, 0, 0, 0, 616, 0, 0, 0, 617, 0, 0, 0, 618, 0, 0, 0, + 619, 0, 0, 0, 620, 0, 0, 0, 621, 0, 0, 0, 622, 0, 0, 0, 623, 0, 0, 0, + 624, 0, 0, 0, 625, 0, 0, 0, 626, 0, 627, 0, 0, 0, 0, 0, 628, 0, 0, 0, + 629, 0, 0, 0, 630, 0, 631, 0, 0, 0, 0, 0, 632, 0, 0, 633, 0, 0, 0, 634, + 0, 0, 0, 635, 0, 0, 0, 636, 0, 0, 0, 637, 0, 0, 0, 638, 0, 0, 0, 639, 0, + 0, 0, 640, 0, 0, 0, 641, 0, 0, 0, 642, 0, 0, 0, 643, 0, 0, 0, 644, 0, 0, + 0, 645, 0, 0, 0, 646, 0, 0, 0, 647, 0, 0, 0, 648, 0, 0, 0, 649, 0, 0, 0, + 650, 0, 0, 0, 651, 0, 0, 0, 652, 0, 0, 0, 653, 0, 0, 0, 654, 0, 0, 0, + 655, 0, 0, 0, 656, 0, 0, 0, 657, 0, 0, 0, 658, 0, 0, 0, 659, 0, 0, 0, + 660, 0, 0, 0, 661, 0, 0, 0, 662, 0, 0, 0, 663, 0, 0, 0, 664, 0, 0, 0, + 665, 0, 0, 0, 666, 0, 0, 0, 667, 0, 0, 0, 668, 0, 0, 0, 669, 0, 0, 0, + 670, 0, 0, 0, 671, 0, 0, 0, 672, 0, 0, 0, 673, 0, 0, 0, 0, 674, 0, 0, 0, + 675, 0, 0, 0, 676, 0, 0, 0, 677, 0, 0, 0, 678, 0, 0, 0, 679, 0, 0, 0, + 680, 0, 0, 0, 681, 0, 0, 0, 682, 0, 0, 0, 683, 0, 0, 0, 684, 0, 0, 0, + 685, 0, 0, 0, 686, 0, 0, 0, 687, 0, 0, 0, 688, 0, 0, 0, 689, 0, 0, 0, + 690, 0, 0, 0, 691, 0, 0, 0, 692, 0, 0, 0, 693, 0, 0, 0, 694, 0, 0, 0, + 695, 0, 0, 0, 696, 0, 0, 0, 697, 0, 0, 0, 698, 0, 0, 0, 699, 0, 0, 0, + 700, 0, 0, 0, 701, 0, 0, 0, 702, 0, 0, 0, 703, 0, 0, 0, 704, 0, 0, 0, + 705, 0, 0, 0, 706, 0, 0, 0, 707, 0, 0, 0, 708, 0, 0, 0, 709, 0, 0, 0, + 710, 0, 0, 0, 711, 0, 0, 0, 712, 0, 0, 0, 713, 0, 0, 0, 714, 0, 0, 0, + 715, 0, 0, 0, 716, 0, 0, 0, 717, 0, 0, 0, 718, 0, 0, 0, 719, 0, 0, 0, + 720, 0, 0, 0, 721, 0, 0, 0, 0, 722, 0, 0, 0, 723, 0, 0, 0, 724, 0, 0, 0, + 725, 0, 0, 0, 726, +}; + +static const unsigned int comp_data[] = { + 0, 0, 0, 8814, 0, 8800, 0, 8815, 192, 193, 194, 195, 256, 258, 550, 196, + 7842, 197, 0, 461, 512, 514, 0, 7840, 0, 7680, 260, 0, 7682, 0, 0, 7684, + 7686, 0, 0, 262, 264, 0, 266, 0, 0, 268, 0, 199, 7690, 0, 0, 270, 0, + 7692, 0, 7696, 0, 7698, 7694, 0, 200, 201, 202, 7868, 274, 276, 278, 203, + 7866, 0, 0, 282, 516, 518, 0, 7864, 0, 552, 280, 7704, 0, 7706, 7710, 0, + 0, 500, 284, 0, 7712, 286, 288, 0, 0, 486, 0, 290, 292, 0, 7714, 7718, 0, + 542, 0, 7716, 0, 7720, 7722, 0, 204, 205, 206, 296, 298, 300, 304, 207, + 7880, 0, 0, 463, 520, 522, 0, 7882, 302, 0, 0, 7724, 308, 0, 0, 7728, 0, + 488, 0, 7730, 0, 310, 7732, 0, 0, 313, 0, 317, 0, 7734, 0, 315, 0, 7740, + 7738, 0, 0, 7742, 7744, 0, 0, 7746, 504, 323, 0, 209, 7748, 0, 0, 327, 0, + 7750, 0, 325, 0, 7754, 7752, 0, 210, 211, 212, 213, 332, 334, 558, 214, + 7886, 0, 336, 465, 524, 526, 416, 7884, 490, 0, 0, 7764, 7766, 0, 0, 340, + 7768, 0, 0, 344, 528, 530, 0, 7770, 0, 342, 7774, 0, 0, 346, 348, 0, + 7776, 0, 0, 352, 0, 7778, 536, 350, 7786, 0, 0, 356, 0, 7788, 538, 354, + 0, 7792, 7790, 0, 217, 218, 219, 360, 362, 364, 0, 220, 7910, 366, 368, + 467, 532, 534, 431, 7908, 7794, 0, 370, 7798, 0, 7796, 0, 7804, 0, 7806, + 7808, 7810, 372, 0, 7814, 7812, 0, 7816, 7818, 7820, 7922, 221, 374, + 7928, 562, 0, 7822, 376, 7926, 0, 0, 7924, 0, 377, 7824, 0, 379, 0, 0, + 381, 0, 7826, 7828, 0, 224, 225, 226, 227, 257, 259, 551, 228, 7843, 229, + 0, 462, 513, 515, 0, 7841, 0, 7681, 261, 0, 7683, 0, 0, 7685, 7687, 0, 0, + 263, 265, 0, 267, 0, 0, 269, 0, 231, 7691, 0, 0, 271, 0, 7693, 0, 7697, + 0, 7699, 7695, 0, 232, 233, 234, 7869, 275, 277, 279, 235, 7867, 0, 0, + 283, 517, 519, 0, 7865, 0, 553, 281, 7705, 0, 7707, 7711, 0, 0, 501, 285, + 0, 7713, 287, 289, 0, 0, 487, 0, 291, 293, 0, 7715, 7719, 0, 543, 0, + 7717, 0, 7721, 7723, 0, 7830, 0, 236, 237, 238, 297, 299, 301, 0, 239, + 7881, 0, 0, 464, 521, 523, 0, 7883, 303, 0, 0, 7725, 309, 0, 0, 496, 0, + 7729, 0, 489, 0, 7731, 0, 311, 7733, 0, 0, 314, 0, 318, 0, 7735, 0, 316, + 0, 7741, 7739, 0, 0, 7743, 7745, 0, 0, 7747, 505, 324, 0, 241, 7749, 0, + 0, 328, 0, 7751, 0, 326, 0, 7755, 7753, 0, 242, 243, 244, 245, 333, 335, + 559, 246, 7887, 0, 337, 466, 525, 527, 417, 7885, 491, 0, 0, 7765, 7767, + 0, 0, 341, 7769, 0, 0, 345, 529, 531, 0, 7771, 0, 343, 7775, 0, 0, 347, + 349, 0, 7777, 0, 0, 353, 0, 7779, 537, 351, 7787, 7831, 0, 357, 0, 7789, + 539, 355, 0, 7793, 7791, 0, 249, 250, 251, 361, 363, 365, 0, 252, 7911, + 367, 369, 468, 533, 535, 432, 7909, 7795, 0, 371, 7799, 0, 7797, 0, 7805, + 0, 7807, 7809, 7811, 373, 0, 7815, 7813, 0, 7832, 0, 7817, 7819, 7821, + 7923, 253, 375, 7929, 563, 0, 7823, 255, 7927, 7833, 0, 7925, 0, 378, + 7825, 0, 380, 0, 0, 382, 0, 7827, 7829, 0, 8173, 901, 8129, 0, 7846, + 7844, 0, 7850, 7848, 0, 478, 0, 0, 506, 0, 508, 482, 0, 0, 7688, 7872, + 7870, 0, 7876, 7874, 0, 0, 7726, 7890, 7888, 0, 7894, 7892, 0, 0, 7756, + 556, 0, 0, 7758, 554, 0, 0, 510, 475, 471, 469, 0, 0, 473, 7847, 7845, 0, + 7851, 7849, 0, 479, 0, 0, 507, 0, 509, 483, 0, 0, 7689, 7873, 7871, 0, + 7877, 7875, 0, 0, 7727, 7891, 7889, 0, 7895, 7893, 0, 0, 7757, 557, 0, 0, + 7759, 555, 0, 0, 511, 476, 472, 470, 0, 0, 474, 7856, 7854, 0, 7860, + 7858, 0, 7857, 7855, 0, 7861, 7859, 0, 7700, 7702, 7701, 7703, 7760, + 7762, 7761, 7763, 7780, 0, 7781, 0, 7782, 0, 7783, 0, 0, 7800, 0, 7801, + 0, 7802, 0, 7803, 7835, 0, 7900, 7898, 0, 7904, 7902, 0, 0, 7906, 7901, + 7899, 0, 7905, 7903, 0, 0, 7907, 7914, 7912, 0, 7918, 7916, 0, 0, 7920, + 7915, 7913, 0, 7919, 7917, 0, 0, 7921, 0, 494, 492, 0, 493, 0, 480, 0, + 481, 0, 0, 7708, 0, 7709, 560, 0, 561, 0, 0, 495, 8122, 902, 8121, 8120, + 7944, 7945, 0, 8124, 8136, 904, 7960, 7961, 8138, 905, 7976, 7977, 0, + 8140, 8154, 906, 8153, 8152, 0, 938, 7992, 7993, 8184, 908, 8008, 8009, + 0, 8172, 8170, 910, 8169, 8168, 0, 939, 0, 8025, 8186, 911, 8040, 8041, + 0, 8188, 0, 8116, 0, 8132, 8048, 940, 8113, 8112, 7936, 7937, 8118, 8115, + 8050, 941, 7952, 7953, 8052, 942, 7968, 7969, 8134, 8131, 8054, 943, + 8145, 8144, 0, 970, 7984, 7985, 8150, 0, 8056, 972, 8000, 8001, 8164, + 8165, 8058, 973, 8161, 8160, 0, 971, 8016, 8017, 8166, 0, 8060, 974, + 8032, 8033, 8182, 8179, 8146, 912, 8151, 0, 8162, 944, 8167, 0, 0, 8180, + 0, 979, 0, 980, 0, 1031, 0, 1232, 0, 1234, 0, 1027, 1024, 0, 0, 1238, 0, + 1025, 0, 1217, 0, 1244, 0, 1246, 1037, 0, 1250, 1049, 0, 1252, 0, 1036, + 0, 1254, 1262, 1038, 0, 1264, 1266, 0, 0, 1268, 0, 1272, 0, 1260, 0, + 1233, 0, 1235, 0, 1107, 1104, 0, 0, 1239, 0, 1105, 0, 1218, 0, 1245, 0, + 1247, 1117, 0, 1251, 1081, 0, 1253, 0, 1116, 0, 1255, 1263, 1118, 0, + 1265, 1267, 0, 0, 1269, 0, 1273, 0, 1261, 0, 1111, 1142, 0, 1143, 0, 0, + 1242, 0, 1243, 0, 1258, 0, 1259, 1570, 1571, 1573, 0, 0, 1572, 0, 1574, + 0, 1730, 0, 1747, 0, 1728, 0, 2345, 0, 2353, 0, 2356, 2507, 2508, 2891, + 2888, 2892, 0, 2964, 0, 0, 3018, 3020, 0, 0, 3019, 0, 3144, 0, 3264, + 3274, 3271, 3272, 0, 0, 3275, 0, 3402, 3404, 0, 0, 3403, 0, 3546, 3548, + 3550, 0, 3549, 4134, 0, 0, 6918, 0, 6920, 0, 6922, 0, 6924, 0, 6926, 0, + 6930, 0, 6971, 0, 6973, 0, 6976, 0, 6977, 0, 6979, 7736, 0, 7737, 0, + 7772, 0, 7773, 0, 7784, 0, 7785, 0, 7852, 0, 0, 7862, 7853, 0, 0, 7863, + 7878, 0, 7879, 0, 7896, 0, 7897, 0, 7938, 7940, 7942, 8064, 7939, 7941, + 7943, 8065, 0, 8066, 0, 8067, 0, 8068, 0, 8069, 0, 8070, 0, 8071, 7946, + 7948, 7950, 8072, 7947, 7949, 7951, 8073, 0, 8074, 0, 8075, 0, 8076, 0, + 8077, 0, 8078, 0, 8079, 7954, 7956, 7955, 7957, 7962, 7964, 7963, 7965, + 7970, 7972, 7974, 8080, 7971, 7973, 7975, 8081, 0, 8082, 0, 8083, 0, + 8084, 0, 8085, 0, 8086, 0, 8087, 7978, 7980, 7982, 8088, 7979, 7981, + 7983, 8089, 0, 8090, 0, 8091, 0, 8092, 0, 8093, 0, 8094, 0, 8095, 7986, + 7988, 7990, 0, 7987, 7989, 7991, 0, 7994, 7996, 7998, 0, 7995, 7997, + 7999, 0, 8002, 8004, 8003, 8005, 8010, 8012, 8011, 8013, 8018, 8020, + 8022, 0, 8019, 8021, 8023, 0, 8027, 8029, 8031, 0, 8034, 8036, 8038, + 8096, 8035, 8037, 8039, 8097, 0, 8098, 0, 8099, 0, 8100, 0, 8101, 0, + 8102, 0, 8103, 8042, 8044, 8046, 8104, 8043, 8045, 8047, 8105, 0, 8106, + 0, 8107, 0, 8108, 0, 8109, 0, 8110, 0, 8111, 0, 8114, 0, 8130, 0, 8178, + 0, 8119, 8141, 8142, 8143, 0, 0, 8135, 0, 8183, 8157, 8158, 8159, 0, 0, + 8602, 0, 8603, 0, 8622, 0, 8653, 0, 8655, 0, 8654, 0, 8708, 0, 8713, 0, + 8716, 0, 8740, 0, 8742, 0, 8769, 0, 8772, 0, 8775, 0, 8777, 0, 8813, 0, + 8802, 0, 8816, 0, 8817, 0, 8820, 0, 8821, 0, 8824, 0, 8825, 0, 8832, 0, + 8833, 0, 8928, 0, 8929, 0, 8836, 0, 8837, 0, 8840, 0, 8841, 0, 8930, 0, + 8931, 0, 8876, 0, 8877, 0, 8878, 0, 8879, 0, 8938, 0, 8939, 0, 8940, 0, + 8941, 12436, 0, 12364, 0, 12366, 0, 12368, 0, 12370, 0, 12372, 0, 12374, + 0, 12376, 0, 12378, 0, 12380, 0, 12382, 0, 12384, 0, 12386, 0, 12389, 0, + 12391, 0, 12393, 0, 12400, 12401, 12403, 12404, 12406, 12407, 12409, + 12410, 12412, 12413, 12446, 0, 12532, 0, 12460, 0, 12462, 0, 12464, 0, + 12466, 0, 12468, 0, 12470, 0, 12472, 0, 12474, 0, 12476, 0, 12478, 0, + 12480, 0, 12482, 0, 12485, 0, 12487, 0, 12489, 0, 12496, 12497, 12499, + 12500, 12502, 12503, 12505, 12506, 12508, 12509, 12535, 0, 12536, 0, + 12537, 0, 12538, 0, 12542, 0, 69786, 0, 69788, 0, 69803, 0, 0, 69934, 0, + 69935, +}; diff --git a/source/fitz/xml.c b/source/fitz/xml.c new file mode 100644 index 00000000..8c97562c --- /dev/null +++ b/source/fitz/xml.c @@ -0,0 +1,460 @@ +#include "mupdf/fitz.h" + +struct parser +{ + fz_xml *head; + fz_context *ctx; +}; + +struct attribute +{ + char name[40]; + char *value; + struct attribute *next; +}; + +struct fz_xml_s +{ + char name[40]; + char *text; + struct attribute *atts; + fz_xml *up, *down, *next; +}; + +static inline void indent(int n) +{ + while (n--) putchar(' '); +} + +void fz_debug_xml(fz_xml *item, int level) +{ + while (item) { + if (item->text) { + printf("%s\n", item->text); + } else { + 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"); + fz_debug_xml(item->down, level + 1); + indent(level); + printf("</%s>\n", item->name); + } + else { + printf("/>\n"); + } + } + item = item->next; + } +} + +fz_xml *fz_xml_next(fz_xml *item) +{ + return item->next; +} + +fz_xml *fz_xml_down(fz_xml *item) +{ + return item->down; +} + +char *fz_xml_text(fz_xml *item) +{ + return item->text; +} + +char *fz_xml_tag(fz_xml *item) +{ + return item->name; +} + +char *fz_xml_att(fz_xml *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(fz_context *ctx, struct attribute *att) +{ + while (att) { + struct attribute *next = att->next; + if (att->value) + fz_free(ctx, att->value); + fz_free(ctx, att); + att = next; + } +} + +void fz_free_xml(fz_context *ctx, fz_xml *item) +{ + while (item) + { + fz_xml *next = item->next; + if (item->text) + fz_free(ctx, item->text); + if (item->atts) + xml_free_attribute(ctx, item->atts); + if (item->down) + fz_free_xml(ctx, item->down); + fz_free(ctx, item); + item = next; + } +} + +void fz_detach_xml(fz_xml *node) +{ + if (node->up) + node->up->down = NULL; +} + +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 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 void xml_emit_open_tag(struct parser *parser, char *a, char *b) +{ + fz_xml *head, *tail; + + head = fz_malloc_struct(parser->ctx, fz_xml); + if (b - a > sizeof(head->name) - 1) + b = a + sizeof(head->name) - 1; + memcpy(head->name, a, b - a); + head->name[b - a] = 0; + + head->atts = NULL; + head->text = 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) +{ + fz_xml *head = parser->head; + struct attribute *att; + + att = fz_malloc_struct(parser->ctx, struct attribute); + if (b - a > sizeof(att->name) - 1) + b = a + sizeof(att->name) - 1; + 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) +{ + fz_xml *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(parser->ctx, b - a + 1); + while (a < b) { + if (*a == '&') { + a += xml_parse_entity(&c, a); + s += fz_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 void xml_emit_text(struct parser *parser, char *a, char *b) +{ + static char *empty = ""; + fz_xml *head; + char *s; + int c; + + /* Skip all-whitespace text nodes */ + for (s = a; s < b; s++) + if (!iswhite(*s)) + break; + if (s == b) + return; + + xml_emit_open_tag(parser, empty, empty); + head = parser->head; + + /* entities are all longer than UTFmax so runetochar is safe */ + s = head->text = fz_malloc(parser->ctx, b - a + 1); + while (a < b) { + if (*a == '&') { + a += xml_parse_entity(&c, a); + s += fz_runetochar(s, c); + } + else { + *s++ = *a++; + } + } + *s = 0; + + xml_emit_close_tag(parser); +} + +static char *xml_parse_document_imp(struct parser *x, char *p) +{ + char *mark; + int quote; + +parse_text: + mark = p; + while (*p && *p != '<') ++p; + xml_emit_text(x, mark, 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(fz_context *doc, unsigned char *s, int n, int *dofree) +{ + unsigned char *e = s + n; + char *dst, *d; + int c; + + if (s[0] == 0xFE && s[1] == 0xFF) { + s += 2; + dst = d = fz_malloc(doc, n * 2); + while (s + 1 < e) { + c = s[0] << 8 | s[1]; + d += fz_runetochar(d, c); + s += 2; + } + *d = 0; + *dofree = 1; + return dst; + } + + if (s[0] == 0xFF && s[1] == 0xFE) { + s += 2; + dst = d = fz_malloc(doc, n * 2); + while (s + 1 < e) { + c = s[0] | s[1] << 8; + d += fz_runetochar(d, c); + s += 2; + } + *d = 0; + *dofree = 1; + return dst; + } + + *dofree = 0; + + if (s[0] == 0xEF && s[1] == 0xBB && s[2] == 0xBF) + return (char*)s+3; + + return (char*)s; +} + +fz_xml * +fz_parse_xml(fz_context *ctx, unsigned char *s, int n) +{ + struct parser parser; + fz_xml root; + char *p, *error; + int dofree; + + /* s is already null-terminated (see xps_new_part) */ + + memset(&root, 0, sizeof(root)); + parser.head = &root; + parser.ctx = ctx; + + p = convert_to_utf8(ctx, s, n, &dofree); + + fz_try(ctx) + { + error = xml_parse_document_imp(&parser, p); + if (error) + fz_throw(ctx, FZ_ERROR_GENERIC, "%s", error); + } + fz_always(ctx) + { + if (dofree) + fz_free(ctx, p); + } + fz_catch(ctx) + { + fz_free_xml(ctx, root.down); + fz_rethrow(ctx); + } + + return root.down; +} |