/* SumatraPDF: partial support for link borders */ static pdf_annot * pdf_create_link_annot(pdf_xref *xref, fz_obj *obj) { fz_obj *border, *dashes; fz_buffer *content; fz_rect rect; float rgb[3]; int i; border = fz_dict_gets(xref->ctx, obj, "Border"); if (fz_to_real(xref->ctx, fz_array_get(xref->ctx, border, 2)) <= 0) return NULL; pdf_get_annot_color(xref->ctx, obj, rgb); dashes = fz_array_get(xref->ctx, border, 3); rect = pdf_to_rect(xref->ctx, fz_dict_gets(xref->ctx, obj, "Rect")); obj = pdf_clone_for_view_only(xref, obj); // TODO: draw rounded rectangles if the first two /Border values are non-zero content = fz_new_buffer(xref->ctx, 128); fz_buffer_printf(xref->ctx, content, "q %.4f w [", fz_to_real(xref->ctx, fz_array_get(xref->ctx, border, 2))); for (i = 0; i < fz_array_len(xref->ctx, dashes); i++) fz_buffer_printf(xref->ctx, content, "%.4f ", fz_to_real(xref->ctx, fz_array_get(xref->ctx, dashes, i))); fz_buffer_printf(xref->ctx, content, "] 0 d %.4f %.4f %.4f RG 0 0 %.4f %.4f re S Q", rgb[0], rgb[1], rgb[2], rect.x1 - rect.x0, rect.y1 - rect.y0); return pdf_create_annot(xref->ctx, rect, obj, content, NULL, 0); }
fz_rect pdf_to_rect(fz_obj *array) { fz_rect r; float a = fz_to_real(fz_array_get(array, 0)); float b = fz_to_real(fz_array_get(array, 1)); float c = fz_to_real(fz_array_get(array, 2)); float d = fz_to_real(fz_array_get(array, 3)); r.x0 = MIN(a, c); r.y0 = MIN(b, d); r.x1 = MAX(a, c); r.y1 = MAX(b, d); return r; }
fz_error pdf_load_pattern(pdf_pattern **patp, pdf_xref *xref, fz_obj *dict) { fz_error error; pdf_pattern *pat; fz_obj *obj; if ((*patp = pdf_find_item(xref->store, pdf_drop_pattern, dict))) { pdf_keep_pattern(*patp); return fz_okay; } pat = fz_malloc(sizeof(pdf_pattern)); pat->refs = 1; pat->resources = NULL; pat->contents = NULL; /* Store pattern now, to avoid possible recursion if objects refer back to this one */ pdf_store_item(xref->store, pdf_keep_pattern, pdf_drop_pattern, dict, pat); pat->ismask = fz_to_int(fz_dict_gets(dict, "PaintType")) == 2; pat->xstep = fz_to_real(fz_dict_gets(dict, "XStep")); pat->ystep = fz_to_real(fz_dict_gets(dict, "YStep")); obj = fz_dict_gets(dict, "BBox"); pat->bbox = pdf_to_rect(obj); obj = fz_dict_gets(dict, "Matrix"); if (obj) pat->matrix = pdf_to_matrix(obj); else pat->matrix = fz_identity; pat->resources = fz_dict_gets(dict, "Resources"); if (pat->resources) fz_keep_obj(pat->resources); error = pdf_load_stream(&pat->contents, xref, fz_to_num(dict), fz_to_gen(dict)); if (error) { pdf_remove_item(xref->store, pdf_drop_pattern, dict); pdf_drop_pattern(pat); return fz_rethrow(error, "cannot load pattern stream (%d %d R)", fz_to_num(dict), fz_to_gen(dict)); } *patp = pat; return fz_okay; }
static void pdf_get_annot_color(fz_context *ctx, fz_obj *obj, float rgb[3]) { int k; obj = fz_dict_gets(ctx, obj, "C"); for (k = 0; k < 3; k++) rgb[k] = fz_to_real(ctx, fz_array_get(ctx, obj, k)); }
/* a: top/left to bottom/right; b: bottom/left to top/right */ static void pdf_get_quadrilaterals(fz_context *ctx, fz_obj *quad_points, int i, fz_rect *a, fz_rect *b) { a->x0 = fz_to_real(ctx, fz_array_get(ctx, quad_points, i * 8 + 0)); a->y0 = fz_to_real(ctx, fz_array_get(ctx, quad_points, i * 8 + 1)); b->x1 = fz_to_real(ctx, fz_array_get(ctx, quad_points, i * 8 + 2)); b->y1 = fz_to_real(ctx, fz_array_get(ctx, quad_points, i * 8 + 3)); b->x0 = fz_to_real(ctx, fz_array_get(ctx, quad_points, i * 8 + 4)); b->y0 = fz_to_real(ctx, fz_array_get(ctx, quad_points, i * 8 + 5)); a->x1 = fz_to_real(ctx, fz_array_get(ctx, quad_points, i * 8 + 6)); a->y1 = fz_to_real(ctx, fz_array_get(ctx, quad_points, i * 8 + 7)); }
static void pdf_load_radial_shading(fz_shade *shade, pdf_xref *xref, fz_obj *dict, int funcs, pdf_function **func) { fz_obj *obj; float d0, d1; int e0, e1; float x0, y0, r0, x1, y1, r1; struct vertex p1, p2; fz_context *ctx = xref->ctx; obj = fz_dict_gets(ctx, dict, "Coords"); x0 = fz_to_real(ctx, fz_array_get(ctx, obj, 0)); y0 = fz_to_real(ctx, fz_array_get(ctx, obj, 1)); r0 = fz_to_real(ctx, fz_array_get(ctx, obj, 2)); x1 = fz_to_real(ctx, fz_array_get(ctx, obj, 3)); y1 = fz_to_real(ctx, fz_array_get(ctx, obj, 4)); r1 = fz_to_real(ctx, fz_array_get(ctx, obj, 5)); d0 = 0; d1 = 1; obj = fz_dict_gets(ctx, dict, "Domain"); if (fz_array_len(ctx, obj) == 2) { d0 = fz_to_real(ctx, fz_array_get(ctx, obj, 0)); d1 = fz_to_real(ctx, fz_array_get(ctx, obj, 1)); } e0 = e1 = 0; obj = fz_dict_gets(ctx, dict, "Extend"); if (fz_array_len(ctx, obj) == 2) { e0 = fz_to_bool(ctx, fz_array_get(ctx, obj, 0)); e1 = fz_to_bool(ctx, fz_array_get(ctx, obj, 1)); } pdf_sample_shade_function(ctx, shade, funcs, func, d0, d1); shade->type = FZ_RADIAL; shade->extend[0] = e0; shade->extend[1] = e1; p1.x = x0; p1.y = y0; p1.c[0] = r0; pdf_add_vertex(ctx, shade, &p1); p2.x = x1; p2.y = y1; p2.c[0] = r1; pdf_add_vertex(ctx, shade, &p2); }
fz_matrix pdf_to_matrix(fz_obj *array) { fz_matrix m; m.a = fz_to_real(fz_array_get(array, 0)); m.b = fz_to_real(fz_array_get(array, 1)); m.c = fz_to_real(fz_array_get(array, 2)); m.d = fz_to_real(fz_array_get(array, 3)); m.e = fz_to_real(fz_array_get(array, 4)); m.f = fz_to_real(fz_array_get(array, 5)); return m; }
static void pdf_load_mesh_params(pdf_xref *xref, fz_obj *dict, struct mesh_params *p) { fz_obj *obj; int i, n; fz_context *ctx = xref->ctx; p->x0 = p->y0 = 0; p->x1 = p->y1 = 1; for (i = 0; i < FZ_MAX_COLORS; i++) { p->c0[i] = 0; p->c1[i] = 1; } p->vprow = fz_to_int(ctx, fz_dict_gets(ctx, dict, "VerticesPerRow")); p->bpflag = fz_to_int(ctx, fz_dict_gets(ctx, dict, "BitsPerFlag")); p->bpcoord = fz_to_int(ctx, fz_dict_gets(ctx, dict, "BitsPerCoordinate")); p->bpcomp = fz_to_int(ctx, fz_dict_gets(ctx, dict, "BitsPerComponent")); obj = fz_dict_gets(ctx, dict, "Decode"); if (fz_array_len(ctx, obj) >= 6) { n = (fz_array_len(ctx, obj) - 4) / 2; p->x0 = fz_to_real(ctx, fz_array_get(ctx, obj, 0)); p->x1 = fz_to_real(ctx, fz_array_get(ctx, obj, 1)); p->y0 = fz_to_real(ctx, fz_array_get(ctx, obj, 2)); p->y1 = fz_to_real(ctx, fz_array_get(ctx, obj, 3)); for (i = 0; i < n; i++) { p->c0[i] = fz_to_real(ctx, fz_array_get(ctx, obj, 4 + i * 2)); p->c1[i] = fz_to_real(ctx, fz_array_get(ctx, obj, 5 + i * 2)); } } if (p->vprow < 2) p->vprow = 2; if (p->bpflag != 2 && p->bpflag != 4 && p->bpflag != 8) p->bpflag = 8; if (p->bpcoord != 1 && p->bpcoord != 2 && p->bpcoord != 4 && p->bpcoord != 8 && p->bpcoord != 12 && p->bpcoord != 16 && p->bpcoord != 24 && p->bpcoord != 32) p->bpcoord = 8; if (p->bpcomp != 1 && p->bpcomp != 2 && p->bpcomp != 4 && p->bpcomp != 8 && p->bpcomp != 12 && p->bpcomp != 16) p->bpcomp = 8; }
static void pdf_load_axial_shading(fz_shade *shade, pdf_xref *xref, fz_obj *dict, int funcs, pdf_function **func) { fz_obj *obj; float d0, d1; int e0, e1; float x0, y0, x1, y1; struct vertex p1, p2; obj = fz_dict_gets(dict, "Coords"); x0 = fz_to_real(fz_array_get(obj, 0)); y0 = fz_to_real(fz_array_get(obj, 1)); x1 = fz_to_real(fz_array_get(obj, 2)); y1 = fz_to_real(fz_array_get(obj, 3)); d0 = 0; d1 = 1; obj = fz_dict_gets(dict, "Domain"); if (fz_array_len(obj) == 2) { d0 = fz_to_real(fz_array_get(obj, 0)); d1 = fz_to_real(fz_array_get(obj, 1)); } e0 = e1 = 0; obj = fz_dict_gets(dict, "Extend"); if (fz_array_len(obj) == 2) { e0 = fz_to_bool(fz_array_get(obj, 0)); e1 = fz_to_bool(fz_array_get(obj, 1)); } pdf_sample_shade_function(shade, funcs, func, d0, d1); shade->type = FZ_LINEAR; shade->extend[0] = e0; shade->extend[1] = e1; p1.x = x0; p1.y = y0; p1.c[0] = 0; pdf_add_vertex(shade, &p1); p2.x = x1; p2.y = y1; p2.c[0] = 0; pdf_add_vertex(shade, &p2); }
static mume_rect_t _pdf_doc_get_link_dest_rect( struct _pdf_doc *self, pdf_link *link) { mume_rect_t rect = mume_rect_empty; fz_obj *dest = link->dest; fz_obj *obj = fz_array_get(dest, 1); const char *type = fz_to_name(obj); if (strcmp(type, "XYZ") == 0) { /* NULL values for the coordinates mean: keep the * current position. */ if (!fz_is_null(fz_array_get(dest, 2))) rect.x = round(fz_to_real(fz_array_get(dest, 2))); if (!fz_is_null(fz_array_get(dest, 3))) rect.y = round(fz_to_real(fz_array_get(dest, 3))); } else if (strcmp(type, "FitR") == 0) { double x0, y0, x1, y1; x0 = fz_to_real(fz_array_get(dest, 2)); y0 = fz_to_real(fz_array_get(dest, 5)); x1 = fz_to_real(fz_array_get(dest, 4)); y1 = fz_to_real(fz_array_get(dest, 3)); rect.x = round(x0); rect.y = round(y0); rect.width = round(x1 - x0); rect.height = round(y1 - y0); } else if (strcmp(type, "FitH") == 0 || strcmp(type, "FitBH") == 0) { rect.y = round(fz_to_real(fz_array_get(dest, 2))); } return rect; }
static fz_error pdf_load_image_imp(fz_pixmap **imgp, pdf_xref *xref, fz_obj *rdb, fz_obj *dict, fz_stream *cstm, int forcemask) { fz_stream *stm; fz_pixmap *tile; fz_obj *obj, *res; fz_error error; int w, h, bpc, n; int imagemask; int interpolate; int indexed; fz_colorspace *colorspace; fz_pixmap *mask; /* explicit mask/softmask image */ int usecolorkey; int colorkey[FZ_MAX_COLORS * 2]; float decode[FZ_MAX_COLORS * 2]; int stride; unsigned char *samples; int i, len; /* special case for JPEG2000 images */ if (pdf_is_jpx_image(dict)) { tile = NULL; error = pdf_load_jpx_image(&tile, xref, dict); if (error) return fz_rethrow(error, "cannot load jpx image"); if (forcemask) { if (tile->n != 2) { fz_drop_pixmap(tile); return fz_throw("softmask must be grayscale"); } mask = fz_alpha_from_gray(tile, 1); fz_drop_pixmap(tile); *imgp = mask; return fz_okay; } *imgp = tile; return fz_okay; } w = fz_to_int(fz_dict_getsa(dict, "Width", "W")); h = fz_to_int(fz_dict_getsa(dict, "Height", "H")); bpc = fz_to_int(fz_dict_getsa(dict, "BitsPerComponent", "BPC")); imagemask = fz_to_bool(fz_dict_getsa(dict, "ImageMask", "IM")); interpolate = fz_to_bool(fz_dict_getsa(dict, "Interpolate", "I")); indexed = 0; usecolorkey = 0; colorspace = NULL; mask = NULL; if (imagemask) bpc = 1; if (w == 0) return fz_throw("image width is zero"); if (h == 0) return fz_throw("image height is zero"); if (bpc == 0) return fz_throw("image depth is zero"); if (w > (1 << 16)) return fz_throw("image is too wide"); if (h > (1 << 16)) return fz_throw("image is too high"); obj = fz_dict_getsa(dict, "ColorSpace", "CS"); if (obj && !imagemask && !forcemask) { /* colorspace resource lookup is only done for inline images */ if (fz_is_name(obj)) { res = fz_dict_get(fz_dict_gets(rdb, "ColorSpace"), obj); if (res) obj = res; } error = pdf_load_colorspace(&colorspace, xref, obj); if (error) return fz_rethrow(error, "cannot load image colorspace"); if (!strcmp(colorspace->name, "Indexed")) indexed = 1; n = colorspace->n; } else { n = 1; } obj = fz_dict_getsa(dict, "Decode", "D"); if (obj) { for (i = 0; i < n * 2; i++) decode[i] = fz_to_real(fz_array_get(obj, i)); } else { float maxval = indexed ? (1 << bpc) - 1 : 1; for (i = 0; i < n * 2; i++) decode[i] = i & 1 ? maxval : 0; } obj = fz_dict_getsa(dict, "SMask", "Mask"); if (fz_is_dict(obj)) { /* Not allowed for inline images */ if (!cstm) { error = pdf_load_image_imp(&mask, xref, rdb, obj, NULL, 1); if (error) { if (colorspace) fz_drop_colorspace(colorspace); return fz_rethrow(error, "cannot load image mask/softmask"); } } } else if (fz_is_array(obj)) { usecolorkey = 1; for (i = 0; i < n * 2; i++) colorkey[i] = fz_to_int(fz_array_get(obj, i)); } /* Allocate now, to fail early if we run out of memory */ tile = fz_new_pixmap_with_limit(colorspace, w, h); if (!tile) { if (colorspace) fz_drop_colorspace(colorspace); if (mask) fz_drop_pixmap(mask); return fz_throw("out of memory"); } if (colorspace) fz_drop_colorspace(colorspace); tile->mask = mask; tile->interpolate = interpolate; stride = (w * n * bpc + 7) / 8; if (cstm) { stm = pdf_open_inline_stream(cstm, xref, dict, stride * h); } else { error = pdf_open_stream(&stm, xref, fz_to_num(dict), fz_to_gen(dict)); if (error) { fz_drop_pixmap(tile); return fz_rethrow(error, "cannot open image data stream (%d 0 R)", fz_to_num(dict)); } } samples = fz_calloc(h, stride); len = fz_read(stm, samples, h * stride); if (len < 0) { fz_close(stm); fz_free(samples); fz_drop_pixmap(tile); return fz_rethrow(len, "cannot read image data"); } /* Make sure we read the EOF marker (for inline images only) */ if (cstm) { unsigned char tbuf[512]; int tlen = fz_read(stm, tbuf, sizeof tbuf); if (tlen < 0) fz_catch(tlen, "ignoring error at end of image"); if (tlen > 0) fz_warn("ignoring garbage at end of image"); } fz_close(stm); /* Pad truncated images */ if (len < stride * h) { fz_warn("padding truncated image (%d 0 R)", fz_to_num(dict)); memset(samples + len, 0, stride * h - len); } /* Invert 1-bit image masks */ if (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, n, bpc, stride, indexed); fz_free(samples); if (usecolorkey) pdf_mask_color_key(tile, n, colorkey); if (indexed) { fz_pixmap *conv; fz_decode_indexed_tile(tile, decode, (1 << bpc) - 1); conv = pdf_expand_indexed_pixmap(tile); fz_drop_pixmap(tile); tile = conv; } else { fz_decode_tile(tile, decode); } *imgp = tile; return fz_okay; }
static fz_error pdf_load_shading_dict(fz_shade **shadep, pdf_xref *xref, fz_obj *dict, fz_matrix transform) { fz_error error; fz_shade *shade; pdf_function *func[FZ_MAX_COLORS] = { NULL }; fz_stream *stream = NULL; fz_obj *obj; int funcs; int type; int i; shade = fz_malloc(sizeof(fz_shade)); shade->refs = 1; shade->type = FZ_MESH; shade->use_background = 0; shade->use_function = 0; shade->matrix = transform; shade->bbox = fz_infinite_rect; shade->extend[0] = 0; shade->extend[1] = 0; shade->mesh_len = 0; shade->mesh_cap = 0; shade->mesh = NULL; shade->colorspace = NULL; funcs = 0; obj = fz_dict_gets(dict, "ShadingType"); type = fz_to_int(obj); obj = fz_dict_gets(dict, "ColorSpace"); if (!obj) { fz_drop_shade(shade); return fz_throw("shading colorspace is missing"); } error = pdf_load_colorspace(&shade->colorspace, xref, obj); if (error) { fz_drop_shade(shade); return fz_rethrow(error, "cannot load colorspace (%d %d R)", fz_to_num(obj), fz_to_gen(obj)); } obj = fz_dict_gets(dict, "Background"); if (obj) { shade->use_background = 1; for (i = 0; i < shade->colorspace->n; i++) shade->background[i] = fz_to_real(fz_array_get(obj, i)); } obj = fz_dict_gets(dict, "BBox"); if (fz_is_array(obj)) { shade->bbox = pdf_to_rect(obj); } obj = fz_dict_gets(dict, "Function"); if (fz_is_dict(obj)) { funcs = 1; error = pdf_load_function(&func[0], xref, obj); if (error) { error = fz_rethrow(error, "cannot load shading function (%d %d R)", fz_to_num(obj), fz_to_gen(obj)); goto cleanup; } } else if (fz_is_array(obj)) { funcs = fz_array_len(obj); if (funcs != 1 && funcs != shade->colorspace->n) { error = fz_throw("incorrect number of shading functions"); goto cleanup; } for (i = 0; i < funcs; i++) { error = pdf_load_function(&func[i], xref, fz_array_get(obj, i)); if (error) { error = fz_rethrow(error, "cannot load shading function (%d %d R)", fz_to_num(obj), fz_to_gen(obj)); goto cleanup; } } } if (type >= 4 && type <= 7) { error = pdf_open_stream(&stream, xref, fz_to_num(dict), fz_to_gen(dict)); if (error) { error = fz_rethrow(error, "cannot open shading stream (%d %d R)", fz_to_num(dict), fz_to_gen(dict)); goto cleanup; } } switch (type) { case 1: pdf_load_function_based_shading(shade, xref, dict, func[0]); break; case 2: pdf_load_axial_shading(shade, xref, dict, funcs, func); break; case 3: pdf_load_radial_shading(shade, xref, dict, funcs, func); break; case 4: pdf_load_type4_shade(shade, xref, dict, funcs, func, stream); break; case 5: pdf_load_type5_shade(shade, xref, dict, funcs, func, stream); break; case 6: pdf_load_type6_shade(shade, xref, dict, funcs, func, stream); break; case 7: pdf_load_type7_shade(shade, xref, dict, funcs, func, stream); break; default: error = fz_throw("unknown shading type: %d", type); goto cleanup; } if (stream) fz_close(stream); for (i = 0; i < funcs; i++) if (func[i]) pdf_drop_function(func[i]); *shadep = shade; return fz_okay; cleanup: if (stream) fz_close(stream); for (i = 0; i < funcs; i++) if (func[i]) pdf_drop_function(func[i]); fz_drop_shade(shade); return fz_rethrow(error, "cannot load shading type %d (%d %d R)", type, fz_to_num(dict), fz_to_gen(dict)); }
static void pdf_load_function_based_shading(fz_shade *shade, pdf_xref *xref, fz_obj *dict, pdf_function *func) { fz_obj *obj; float x0, y0, x1, y1; fz_matrix matrix; struct vertex v[4]; int xx, yy; float x, y; float xn, yn; int i; x0 = y0 = 0; x1 = y1 = 1; obj = fz_dict_gets(dict, "Domain"); if (fz_array_len(obj) == 4) { x0 = fz_to_real(fz_array_get(obj, 0)); x1 = fz_to_real(fz_array_get(obj, 1)); y0 = fz_to_real(fz_array_get(obj, 2)); y1 = fz_to_real(fz_array_get(obj, 3)); } matrix = fz_identity; obj = fz_dict_gets(dict, "Matrix"); if (fz_array_len(obj) == 6) matrix = pdf_to_matrix(obj); for (yy = 0; yy < FUNSEGS; yy++) { y = y0 + (y1 - y0) * yy / FUNSEGS; yn = y0 + (y1 - y0) * (yy + 1) / FUNSEGS; for (xx = 0; xx < FUNSEGS; xx++) { x = x0 + (x1 - x0) * xx / FUNSEGS; xn = x0 + (x1 - x0) * (xx + 1) / FUNSEGS; v[0].x = x; v[0].y = y; v[1].x = xn; v[1].y = y; v[2].x = xn; v[2].y = yn; v[3].x = x; v[3].y = yn; for (i = 0; i < 4; i++) { fz_point pt; float fv[2]; fv[0] = v[i].x; fv[1] = v[i].y; pdf_eval_function(func, fv, 2, v[i].c, shade->colorspace->n); pt.x = v[i].x; pt.y = v[i].y; pt = fz_transform_point(matrix, pt); v[i].x = pt.x; v[i].y = pt.y; } pdf_add_quad(shade, &v[0], &v[1], &v[2], &v[3]); } } }