static void jpx_ycc_to_rgb(fz_context *ctx, fz_pixmap *pix, int cbsign, int crsign) { int w = pix->w; int h = pix->h; int stride = pix->stride; int x, y; for (y = 0; y < h; y++) { unsigned char * row = &pix->samples[stride * y]; for (x = 0; x < w; x++) { int ycc[3]; ycc[0] = row[x * 3 + 0]; ycc[1] = row[x * 3 + 1]; ycc[2] = row[x * 3 + 2]; /* consciously skip Y */ if (cbsign) ycc[1] -= 128; if (crsign) ycc[2] -= 128; row[x * 3 + 0] = fz_clampi(ycc[0] + 1.402f * ycc[2], 0, 255); row[x * 3 + 1] = fz_clampi(ycc[0] - 0.34413f * ycc[1] - 0.71414f * ycc[2], 0, 255); row[x * 3 + 2] = fz_clampi(ycc[0] + 1.772f * ycc[1], 0, 255); } } }
static void showinfo(char *filename, int show, char *pagelist) { int page, spage, epage; char *spec, *dash; int allpages; int pagecount; if (!doc) infousage(); allpages = !strcmp(pagelist, "1-"); pagecount = pdf_count_pages(doc); spec = fz_strsep(&pagelist, ","); while (spec && pagecount) { dash = strchr(spec, '-'); if (dash == spec) spage = epage = pagecount; else spage = epage = atoi(spec); if (dash) { if (strlen(dash) > 1) epage = atoi(dash + 1); else epage = pagecount; } if (spage > epage) page = spage, spage = epage, epage = page; spage = fz_clampi(spage, 1, pagecount); epage = fz_clampi(epage, 1, pagecount); if (allpages) printf("Retrieving info from pages %d-%d...\n", spage, epage); for (page = spage; page <= epage; page++) { gatherpageinfo(page, show); if (!allpages) { printf("Page %d:\n", page); printinfo(filename, show, page); printf("\n"); } } spec = fz_strsep(&pagelist, ","); } if (allpages) printinfo(filename, show, -1); }
static void merge_range(char *range) { int page, spage, epage, src_pagecount, des_pagecount; char *spec, *dash; pdf_graft_map *graft_map; src_pagecount = fz_count_pages(ctx, (fz_document*) doc_src); des_pagecount = fz_count_pages(ctx, (fz_document*) doc_des); spec = fz_strsep(&range, ","); graft_map = pdf_new_graft_map(ctx, doc_src); fz_try(ctx) { while (spec) { dash = strchr(spec, '-'); if (dash == spec) spage = epage = src_pagecount; else spage = epage = atoi(spec); if (dash) { if (strlen(dash) > 1) epage = atoi(dash + 1); else epage = src_pagecount; } spage = fz_clampi(spage, 1, src_pagecount); epage = fz_clampi(epage, 1, src_pagecount); if (spage < epage) for (page = spage; page <= epage; page++, des_pagecount++) page_merge(page, des_pagecount + 1, graft_map); else for (page = spage; page >= epage; page--, des_pagecount++) page_merge(page, des_pagecount + 1, graft_map); spec = fz_strsep(&range, ","); } } fz_always(ctx) { pdf_drop_graft_map(ctx, graft_map); } fz_catch(ctx) { fz_rethrow(ctx); } }
/* cf. http://bugs.ghostscript.com/show_bug.cgi?id=693517 */ static void fz_unblend_masked_tile(fz_context *ctx, fz_pixmap *tile, fz_image *image) { /* fz_image_get_pixmap tends to return too large tiles */ int min_w = 1 << (int)floorf(logf(tile->w) / logf(2)); int min_h = 1 << (int)floorf(logf(tile->h) / logf(2)); fz_pixmap *mask = image->mask->get_pixmap(ctx, image->mask, min_w, min_h); unsigned char *s = mask->samples, *end = s + mask->w * mask->h; unsigned char *d = tile->samples; int k; if (tile->w != mask->w || tile->h != mask->h) { fz_warn(ctx, "mask must be of same size as image for /Matte"); fz_drop_pixmap(ctx, mask); return; } for (; s < end; s++, d += tile->n) { if (!*s) continue; for (k = 0; k < image->n; k++) d[k] = fz_clampi(image->colorkey[k] + (d[k] - image->colorkey[k]) * 255 / *s, 0, 255); } fz_drop_pixmap(ctx, mask); tile->single_bit = 0; /* SumatraPDF: allow optimizing 1-bit pixmaps */ }
static void reload(void) { fz_drop_outline(ctx, outline); fz_drop_document(ctx, doc); doc = fz_open_document(ctx, filename); if (fz_needs_password(ctx, doc)) { if (!fz_authenticate_password(ctx, doc, password)) { fprintf(stderr, "Invalid password.\n"); exit(1); } } fz_layout_document(ctx, doc, layout_w, layout_h, layout_em); fz_try(ctx) outline = fz_load_outline(ctx, doc); fz_catch(ctx) outline = NULL; pdf = pdf_specifics(ctx, doc); if (pdf) pdf_enable_js(ctx, pdf); currentpage = fz_clampi(currentpage, 0, fz_count_pages(ctx, doc) - 1); render_page(); update_title(); }
static void fz_unblend_masked_tile(fz_context *ctx, fz_pixmap *tile, fz_image *image) { fz_pixmap *mask = fz_image_get_pixmap(ctx, image->mask, tile->w, tile->h); unsigned char *s = mask->samples, *end = s + mask->w * mask->h; unsigned char *d = tile->samples; int k; if (tile->w != mask->w || tile->h != mask->h) { fz_warn(ctx, "mask must be of same size as image for /Matte"); fz_drop_pixmap(ctx, mask); return; } for (; s < end; s++, d += tile->n) { if (*s == 0) for (k = 0; k < image->n; k++) d[k] = image->colorkey[k]; else for (k = 0; k < image->n; k++) d[k] = fz_clampi(image->colorkey[k] + (d[k] - image->colorkey[k]) * 255 / *s, 0, 255); } fz_drop_pixmap(ctx, mask); }
static const unsigned char * gif_read_lsd(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end) { if (end - p < 7) fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in logical screen descriptor in gif image"); info->width = p[1] << 8 | p[0]; info->height = p[3] << 8 | p[2]; if (info->width <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image width must be > 0"); if (info->height <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image height must be > 0"); if (info->height > UINT_MAX / info->width / 3 /* components */) fz_throw(ctx, FZ_ERROR_GENERIC, "image dimensions might overflow"); info->has_gct = (p[4] >> 7) & 0x1; if (info->has_gct) { info->gct_entries = 1 << ((p[4] & 0x7) + 1); info->gct_background = fz_clampi(p[5], 0, info->gct_entries - 1); } info->aspect = p[6]; info->xres = 96; info->yres= 96; if (info->aspect > 0) info->yres = (((float) info->aspect + 15) / 64) * 96; return p + 7; }
static fz_colorspace * load_indexed(pdf_document *doc, pdf_obj *array) { fz_context *ctx = doc->ctx; pdf_obj *baseobj = pdf_array_get(array, 1); pdf_obj *highobj = pdf_array_get(array, 2); pdf_obj *lookupobj = pdf_array_get(array, 3); fz_colorspace *base = NULL; fz_colorspace *cs; int i, n, high; unsigned char *lookup = NULL; fz_var(base); fz_var(lookup); fz_try(ctx) { base = pdf_load_colorspace(doc, baseobj); high = pdf_to_int(highobj); high = fz_clampi(high, 0, 255); n = base->n * (high + 1); lookup = fz_malloc_array(ctx, 1, n); if (pdf_is_string(lookupobj) && pdf_to_str_len(lookupobj) >= n) { unsigned char *buf = (unsigned char *) pdf_to_str_buf(lookupobj); for (i = 0; i < n; i++) lookup[i] = buf[i]; } else if (pdf_is_indirect(lookupobj)) { fz_stream *file = NULL; fz_var(file); fz_try(ctx) { file = pdf_open_stream(doc, pdf_to_num(lookupobj), pdf_to_gen(lookupobj)); i = fz_read(file, lookup, n); if (i < n) memset(lookup+i, 0, n-i); } fz_always(ctx) { fz_close(file); } fz_catch(ctx) { fz_rethrow_message(ctx, "cannot open colorspace lookup table (%d 0 R)", pdf_to_num(lookupobj)); } } else { fz_rethrow_message(ctx, "cannot parse colorspace lookup table"); } cs = fz_new_indexed_colorspace(ctx, base, high, lookup); }
static void jump_to_page(int newpage) { newpage = fz_clampi(newpage, 0, fz_count_pages(ctx, doc) - 1); clear_future(); push_history(); currentpage = newpage; push_history(); }
static int showpages(fz_context *ctx, pdf_document *doc, fz_output *out, char *pagelist) { int page, spage, epage; char *spec, *dash; int pagecount; int ret = 0; if (!doc) infousage(); pagecount = pdf_count_pages(ctx, doc); spec = fz_strsep(&pagelist, ","); while (spec && pagecount) { dash = strchr(spec, '-'); if (dash == spec) spage = epage = pagecount; else spage = epage = atoi(spec); if (dash) { if (strlen(dash) > 1) epage = atoi(dash + 1); else epage = pagecount; } if (spage > epage) page = spage, spage = epage, epage = page; spage = fz_clampi(spage, 1, pagecount); epage = fz_clampi(epage, 1, pagecount); for (page = spage; page <= epage; page++) { ret |= showpage(ctx, doc, out, page); } spec = fz_strsep(&pagelist, ","); } return ret; }
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 fz_unblend_masked_tile(fz_context *ctx, fz_pixmap *tile, fz_image *image) { fz_pixmap *mask = fz_get_pixmap_from_image(ctx, image->mask, NULL, NULL, NULL, NULL); unsigned char *s = mask->samples; unsigned char *d = tile->samples; int n = tile->n; int k; int sstride = mask->stride - mask->w * mask->n; int dstride = tile->stride - tile->w * tile->n; int h = mask->h; if (tile->w != mask->w || tile->h != mask->h) { fz_warn(ctx, "mask must be of same size as image for /Matte"); fz_drop_pixmap(ctx, mask); return; } if (mask->w != 0) { while (h--) { int w = mask->w; do { if (*s == 0) for (k = 0; k < image->n; k++) d[k] = image->colorkey[k]; else for (k = 0; k < image->n; k++) d[k] = fz_clampi(image->colorkey[k] + (d[k] - image->colorkey[k]) * 255 / *s, 0, 255); s++; d += n; } while (--w); s += sstride; d += dstride; } } fz_drop_pixmap(ctx, mask); }
static void gif_read_line(fz_context *ctx, struct info *info, unsigned char *dest, int ct_entries, const unsigned char *ct, unsigned int y, unsigned char *sp) { unsigned int index = (info->image_top + y) * info->width + info->image_left; unsigned char *dp = &dest[index * 4]; unsigned char *mp = &info->mask[index]; unsigned int x, k; if (info->image_top + y >= info->height) return; for (x = 0; x < info->image_width && info->image_left + x < info->width; x++, sp++, mp++, dp += 4) if (!info->has_transparency || *sp != info->transparent) { *mp = 0x02; for (k = 0; k < 3; k++) dp[k] = ct[fz_clampi(*sp, 0, ct_entries - 1) * 3 + k]; dp[3] = 255; } else if (*mp == 0x01) *mp = 0x00; }
static void drawrange(fz_context *ctx, fz_document *doc, char *range) { int page, spage, epage, pagecount; char *spec, *dash; pagecount = fz_count_pages(doc); spec = fz_strsep(&range, ","); while (spec) { dash = strchr(spec, '-'); if (dash == spec) spage = epage = pagecount; else spage = epage = atoi(spec); if (dash) { if (strlen(dash) > 1) epage = atoi(dash + 1); else epage = pagecount; } spage = fz_clampi(spage, 1, pagecount); epage = fz_clampi(epage, 1, pagecount); current_doc = doc; current_ctx = ctx; current_epage = epage; for (page = spage; page <= epage; ) { if (page == 0) page = 1; current_page = page; drawpage(ctx, doc, page); draw_decors(page, epage); yutani_flip(yctx, window); yutani_msg_t * m = NULL; while (1) { m = yutani_poll(yctx); if (m) { switch (m->type) { case YUTANI_MSG_KEY_EVENT: { struct yutani_msg_key_event * ke = (void*)m->data; if (ke->event.action == KEY_ACTION_DOWN) { switch (ke->event.keycode) { case KEY_ESCAPE: case 'q': yutani_close(yctx, window); exit(0); break; case KEY_ARROW_LEFT: case 'a': page--; goto _continue; case KEY_ARROW_RIGHT: case 's': page++; if (page > epage) page = epage; goto _continue; case KEY_F12: toggle_decorations(); goto _continue; default: break; } } } break; case YUTANI_MSG_SESSION_END: yutani_close(yctx, window); exit(0); break; case YUTANI_MSG_WINDOW_FOCUS_CHANGE: { struct yutani_msg_window_focus_change * wf = (void*)m->data; yutani_window_t * win = hashmap_get(yctx->windows, (void*)wf->wid); if (win) { win->focused = wf->focused; goto _continue; } } break; case YUTANI_MSG_RESIZE_OFFER: { struct yutani_msg_window_resize * wr = (void*)m->data; resize_finish(wr->width, wr->height); goto _continue; } break; case YUTANI_MSG_WINDOW_MOUSE_EVENT: if (decor_handle_event(yctx, m) == DECOR_CLOSE) { yutani_close(yctx, window); exit(0); } break; default: break; } } free(m); } _continue: free(m); } spec = fz_strsep(&range, ","); } }
static void pdf_process_extgstate(fz_context *ctx, pdf_processor *proc, pdf_csi *csi, pdf_obj *dict) { pdf_obj *obj; obj = pdf_dict_get(ctx, dict, PDF_NAME_LW); if (pdf_is_number(ctx, obj) && proc->op_w) proc->op_w(ctx, proc, pdf_to_real(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_LC); if (pdf_is_int(ctx, obj) && proc->op_J) proc->op_J(ctx, proc, fz_clampi(pdf_to_int(ctx, obj), 0, 2)); obj = pdf_dict_get(ctx, dict, PDF_NAME_LJ); if (pdf_is_int(ctx, obj) && proc->op_j) proc->op_j(ctx, proc, fz_clampi(pdf_to_int(ctx, obj), 0, 2)); obj = pdf_dict_get(ctx, dict, PDF_NAME_ML); if (pdf_is_number(ctx, obj) && proc->op_M) proc->op_M(ctx, proc, pdf_to_real(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_D); if (pdf_is_array(ctx, obj) && proc->op_d) { pdf_obj *dash_array = pdf_array_get(ctx, obj, 0); pdf_obj *dash_phase = pdf_array_get(ctx, obj, 1); proc->op_d(ctx, proc, dash_array, pdf_to_real(ctx, dash_phase)); } obj = pdf_dict_get(ctx, dict, PDF_NAME_RI); if (pdf_is_name(ctx, obj) && proc->op_ri) proc->op_ri(ctx, proc, pdf_to_name(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_FL); if (pdf_is_number(ctx, obj) && proc->op_i) proc->op_i(ctx, proc, pdf_to_real(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_Font); if (pdf_is_array(ctx, obj) && proc->op_Tf) { pdf_obj *font_ref = pdf_array_get(ctx, obj, 0); pdf_obj *font_size = pdf_array_get(ctx, obj, 1); pdf_font_desc *font = load_font_or_hail_mary(ctx, csi->doc, csi->rdb, font_ref, 0, csi->cookie); fz_try(ctx) proc->op_Tf(ctx, proc, "ExtGState", font, pdf_to_real(ctx, font_size)); fz_always(ctx) pdf_drop_font(ctx, font); fz_catch(ctx) fz_rethrow(ctx); } /* transfer functions */ obj = pdf_dict_get(ctx, dict, PDF_NAME_TR2); if (pdf_is_name(ctx, obj)) if (!pdf_name_eq(ctx, obj, PDF_NAME_Identity) && !pdf_name_eq(ctx, obj, PDF_NAME_Default)) fz_warn(ctx, "ignoring transfer function"); if (!obj) /* TR is ignored in the presence of TR2 */ { pdf_obj *tr = pdf_dict_get(ctx, dict, PDF_NAME_TR); if (pdf_is_name(ctx, tr)) if (!pdf_name_eq(ctx, tr, PDF_NAME_Identity)) fz_warn(ctx, "ignoring transfer function"); } /* transparency state */ obj = pdf_dict_get(ctx, dict, PDF_NAME_CA); if (pdf_is_number(ctx, obj) && proc->op_gs_CA) proc->op_gs_CA(ctx, proc, pdf_to_real(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_ca); if (pdf_is_number(ctx, obj) && proc->op_gs_ca) proc->op_gs_ca(ctx, proc, pdf_to_real(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_BM); if (pdf_is_array(ctx, obj)) obj = pdf_array_get(ctx, obj, 0); if (pdf_is_name(ctx, obj) && proc->op_gs_BM) proc->op_gs_BM(ctx, proc, pdf_to_name(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_SMask); if (proc->op_gs_SMask) { if (pdf_is_dict(ctx, obj)) { pdf_xobject *xobj; pdf_obj *group, *s, *bc, *tr; float softmask_bc[FZ_MAX_COLORS]; fz_colorspace *colorspace; int colorspace_n = 1; int k, luminosity; fz_var(xobj); group = pdf_dict_get(ctx, obj, PDF_NAME_G); if (!group) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot load softmask xobject (%d 0 R)", pdf_to_num(ctx, obj)); xobj = pdf_load_xobject(ctx, csi->doc, group); fz_try(ctx) { colorspace = pdf_xobject_colorspace(ctx, xobj); if (colorspace) { colorspace_n = fz_colorspace_n(ctx, colorspace); fz_drop_colorspace(ctx, colorspace); } /* Default background color is black. */ for (k = 0; k < colorspace_n; k++) softmask_bc[k] = 0; /* Which in CMYK means not all zeros! This should really be * a test for subtractive color spaces, but this will have * to do for now. */ if (colorspace == fz_device_cmyk(ctx)) softmask_bc[3] = 1.0; bc = pdf_dict_get(ctx, obj, PDF_NAME_BC); if (pdf_is_array(ctx, bc)) { for (k = 0; k < colorspace_n; k++) softmask_bc[k] = pdf_to_real(ctx, pdf_array_get(ctx, bc, k)); } s = pdf_dict_get(ctx, obj, PDF_NAME_S); if (pdf_name_eq(ctx, s, PDF_NAME_Luminosity)) luminosity = 1; else luminosity = 0; tr = pdf_dict_get(ctx, obj, PDF_NAME_TR); if (tr && !pdf_name_eq(ctx, tr, PDF_NAME_Identity)) fz_warn(ctx, "ignoring transfer function"); proc->op_gs_SMask(ctx, proc, xobj, csi->rdb, softmask_bc, luminosity); } fz_always(ctx) { pdf_drop_xobject(ctx, xobj); } fz_catch(ctx) { fz_rethrow(ctx); } } else if (pdf_is_name(ctx, obj) && pdf_name_eq(ctx, obj, PDF_NAME_None)) { proc->op_gs_SMask(ctx, proc, NULL, NULL, NULL, 0); } }
static void retainpages(fz_context *ctx, globals *glo, int argc, char **argv) { pdf_obj *oldroot, *root, *pages, *kids, *countobj, *parent, *olddests; pdf_document *doc = glo->doc; int argidx = 0; pdf_obj *names_list = NULL; int pagecount; int i; /* Keep only pages/type and (reduced) dest entries to avoid * references to unretained pages */ oldroot = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME_Root); pages = pdf_dict_get(ctx, oldroot, PDF_NAME_Pages); olddests = pdf_load_name_tree(ctx, doc, PDF_NAME_Dests); root = pdf_new_dict(ctx, doc, 2); pdf_dict_put(ctx, root, PDF_NAME_Type, pdf_dict_get(ctx, oldroot, PDF_NAME_Type)); pdf_dict_put(ctx, root, PDF_NAME_Pages, pdf_dict_get(ctx, oldroot, PDF_NAME_Pages)); pdf_update_object(ctx, doc, pdf_to_num(ctx, oldroot), root); pdf_drop_obj(ctx, root); /* Create a new kids array with only the pages we want to keep */ parent = pdf_new_indirect(ctx, doc, pdf_to_num(ctx, pages), pdf_to_gen(ctx, pages)); kids = pdf_new_array(ctx, doc, 1); /* Retain pages specified */ while (argc - argidx) { int page, spage, epage; char *spec, *dash; char *pagelist = argv[argidx]; pagecount = pdf_count_pages(ctx, doc); spec = fz_strsep(&pagelist, ","); while (spec) { dash = strchr(spec, '-'); if (dash == spec) spage = epage = pagecount; else spage = epage = atoi(spec); if (dash) { if (strlen(dash) > 1) epage = atoi(dash + 1); else epage = pagecount; } spage = fz_clampi(spage, 1, pagecount); epage = fz_clampi(epage, 1, pagecount); if (spage < epage) for (page = spage; page <= epage; ++page) retainpage(ctx, doc, parent, kids, page); else for (page = spage; page >= epage; --page) retainpage(ctx, doc, parent, kids, page); spec = fz_strsep(&pagelist, ","); } argidx++; } pdf_drop_obj(ctx, parent); /* Update page count and kids array */ countobj = pdf_new_int(ctx, doc, pdf_array_len(ctx, kids)); pdf_dict_put(ctx, pages, PDF_NAME_Count, countobj); pdf_drop_obj(ctx, countobj); pdf_dict_put(ctx, pages, PDF_NAME_Kids, kids); pdf_drop_obj(ctx, kids); /* Also preserve the (partial) Dests name tree */ if (olddests) { pdf_obj *names = pdf_new_dict(ctx, doc, 1); pdf_obj *dests = pdf_new_dict(ctx, doc, 1); int len = pdf_dict_len(ctx, olddests); names_list = pdf_new_array(ctx, doc, 32); for (i = 0; i < len; i++) { pdf_obj *key = pdf_dict_get_key(ctx, olddests, i); pdf_obj *val = pdf_dict_get_val(ctx, olddests, i); pdf_obj *dest = pdf_dict_get(ctx, val, PDF_NAME_D); dest = pdf_array_get(ctx, dest ? dest : val, 0); if (pdf_array_contains(ctx, pdf_dict_get(ctx, pages, PDF_NAME_Kids), dest)) { pdf_obj *key_str = pdf_new_string(ctx, doc, pdf_to_name(ctx, key), strlen(pdf_to_name(ctx, key))); pdf_array_push(ctx, names_list, key_str); pdf_array_push(ctx, names_list, val); pdf_drop_obj(ctx, key_str); } } root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME_Root); pdf_dict_put(ctx, dests, PDF_NAME_Names, names_list); pdf_dict_put(ctx, names, PDF_NAME_Dests, dests); pdf_dict_put(ctx, root, PDF_NAME_Names, names); pdf_drop_obj(ctx, names); pdf_drop_obj(ctx, dests); pdf_drop_obj(ctx, names_list); pdf_drop_obj(ctx, olddests); } /* Force the next call to pdf_count_pages to recount */ glo->doc->page_count = 0; /* Edit each pages /Annot list to remove any links that point to * nowhere. */ pagecount = pdf_count_pages(ctx, doc); for (i = 0; i < pagecount; i++) { pdf_obj *pageref = pdf_lookup_page_obj(ctx, doc, i); pdf_obj *pageobj = pdf_resolve_indirect(ctx, pageref); pdf_obj *annots = pdf_dict_get(ctx, pageobj, PDF_NAME_Annots); int len = pdf_array_len(ctx, annots); int j; for (j = 0; j < len; j++) { pdf_obj *o = pdf_array_get(ctx, annots, j); pdf_obj *p; if (!pdf_name_eq(ctx, pdf_dict_get(ctx, o, PDF_NAME_Subtype), PDF_NAME_Link)) continue; p = pdf_dict_get(ctx, o, PDF_NAME_A); if (!pdf_name_eq(ctx, pdf_dict_get(ctx, p, PDF_NAME_S), PDF_NAME_GoTo)) continue; if (string_in_names_list(ctx, pdf_dict_get(ctx, p, PDF_NAME_D), names_list)) continue; /* FIXME: Should probably look at Next too */ /* Remove this annotation */ pdf_array_delete(ctx, annots, j); j--; } } }
static fz_colorspace * load_indexed(pdf_document *xref, pdf_obj *array) { struct indexed *idx = NULL; fz_context *ctx = xref->ctx; pdf_obj *baseobj = pdf_array_get(array, 1); pdf_obj *highobj = pdf_array_get(array, 2); pdf_obj *lookup = pdf_array_get(array, 3); fz_colorspace *base = NULL; fz_colorspace *cs = NULL; int i, n; fz_var(idx); fz_var(base); fz_var(cs); fz_try(ctx) { base = pdf_load_colorspace(xref, baseobj); idx = fz_malloc_struct(ctx, struct indexed); idx->lookup = NULL; idx->base = base; idx->high = pdf_to_int(highobj); idx->high = fz_clampi(idx->high, 0, 255); n = base->n * (idx->high + 1); idx->lookup = fz_malloc_array(ctx, 1, n); 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) + n + (base ? base->size : 0); if (pdf_is_string(lookup) && pdf_to_str_len(lookup) == n) { unsigned char *buf = (unsigned char *) pdf_to_str_buf(lookup); for (i = 0; i < n; i++) idx->lookup[i] = buf[i]; } else if (pdf_is_indirect(lookup)) { fz_stream *file = NULL; fz_var(file); fz_try(ctx) { file = pdf_open_stream(xref, pdf_to_num(lookup), pdf_to_gen(lookup)); i = fz_read(file, idx->lookup, n); } fz_always(ctx) { fz_close(file); } fz_catch(ctx) { fz_throw(ctx, "cannot open colorspace lookup table (%d 0 R)", pdf_to_num(lookup)); } } else { fz_throw(ctx, "cannot parse colorspace lookup table"); } }
static fz_pixmap * pnm_ascii_read_image(fz_context *ctx, struct info *pnm, unsigned char *p, unsigned char *e, int onlymeta, int bitmap) { fz_pixmap *img = NULL; p = pnm_read_number(ctx, p, e, &pnm->width); p = pnm_read_white(ctx, p, e, 0); if (bitmap) { p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 1); pnm->maxval = 1; } else { p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 0); p = pnm_read_number(ctx, p, e, &pnm->maxval); p = pnm_read_white(ctx, p, e, 0); if (pnm->maxval < 0 || pnm->maxval >= 65536) fz_throw(ctx, FZ_ERROR_GENERIC, "maximum sample value of out range in pnm image: %d", pnm->maxval); } if (!onlymeta) { unsigned char *dp; int x, y, k; img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, 0); dp = img->samples; if (bitmap) { for (y = 0; y < pnm->height; y++) { for (x = 0; x < pnm->width; x++) { int v = 0; p = pnm_read_number(ctx, p, e, &v); p = pnm_read_white(ctx, p, e, 0); if (v == 0) *dp = 0x00; else *dp = 0xff; dp++; } } } else { for (y = 0; y < pnm->height; y++) for (x = 0; x < pnm->width; x++) for (k = 0; k < img->colorspace->n; k++) { int v = 0; p = pnm_read_number(ctx, p, e, &v); p = pnm_read_white(ctx, p, e, 0); v = fz_clampi(v, 0, pnm->maxval); *dp++ = map_color(ctx, v, pnm->maxval, 255); } } } return img; }
static void do_app(void) { if (ui.key == KEY_F4 && ui.mod == GLFW_MOD_ALT) quit(); if (ui.down || ui.middle || ui.right || ui.key) showinfo = 0; if (!ui.focus && ui.key) { switch (ui.key) { case 'q': quit(); break; case 'm': if (number == 0) push_history(); else if (number > 0 && number < nelem(marks)) marks[number] = currentpage; break; case 't': if (number == 0) { if (history_count > 0) pop_history(); } else if (number > 0 && number < nelem(marks)) { jump_to_page(marks[number]); } break; case 'T': if (number == 0) { if (future_count > 0) pop_future(); } break; case 'N': search_dir = -1; if (search_hit_page == currentpage) search_page = currentpage + search_dir; else search_page = currentpage; if (search_page >= 0 && search_page < fz_count_pages(ctx, doc)) { search_hit_page = -1; if (search_needle) search_active = 1; } break; case 'n': search_dir = 1; if (search_hit_page == currentpage) search_page = currentpage + search_dir; else search_page = currentpage; if (search_page >= 0 && search_page < fz_count_pages(ctx, doc)) { search_hit_page = -1; if (search_needle) search_active = 1; } break; case 'f': toggle_fullscreen(); break; case 'w': shrinkwrap(); break; case 'r': reload(); break; case 'o': toggle_outline(); break; case 'W': auto_zoom_w(); break; case 'H': auto_zoom_h(); break; case 'Z': auto_zoom(); break; case 'z': currentzoom = number > 0 ? number : DEFRES; break; case '<': currentpage -= 10 * fz_maxi(number, 1); break; case '>': currentpage += 10 * fz_maxi(number, 1); break; case ',': case KEY_PAGE_UP: currentpage -= fz_maxi(number, 1); break; case '.': case KEY_PAGE_DOWN: currentpage += fz_maxi(number, 1); break; case 'b': number = fz_maxi(number, 1); while (number--) smart_move_backward(); break; case ' ': number = fz_maxi(number, 1); while (number--) smart_move_forward(); break; case 'g': jump_to_page(number - 1); break; case 'G': jump_to_page(fz_count_pages(ctx, doc) - 1); break; case '+': currentzoom = zoom_in(currentzoom); break; case '-': currentzoom = zoom_out(currentzoom); break; case '[': currentrotate += 90; break; case ']': currentrotate -= 90; break; case 'l': showlinks = !showlinks; break; case 'i': showinfo = !showinfo; break; case '/': search_dir = 1; showsearch = 1; search_input.p = search_input.text; search_input.q = search_input.end; break; case '?': search_dir = -1; showsearch = 1; search_input.p = search_input.text; search_input.q = search_input.end; break; case KEY_UP: scroll_y -= 10; break; case KEY_DOWN: scroll_y += 10; break; case KEY_LEFT: scroll_x -= 10; break; case KEY_RIGHT: scroll_x += 10; break; } if (ui.key >= '0' && ui.key <= '9') number = number * 10 + ui.key - '0'; else number = 0; currentpage = fz_clampi(currentpage, 0, fz_count_pages(ctx, doc) - 1); currentzoom = fz_clamp(currentzoom, MINRES, MAXRES); while (currentrotate < 0) currentrotate += 360; while (currentrotate >= 360) currentrotate -= 360; if (search_hit_page != currentpage) search_hit_page = -1; /* clear highlights when navigating */ ui_needs_update = 1; ui.key = 0; /* we ate the key event, so zap it */ } }
static fz_pixmap * pnm_ascii_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, int bitmap, const unsigned char **out) { fz_pixmap *img = NULL; p = pnm_read_number(ctx, p, e, &pnm->width); p = pnm_read_white(ctx, p, e, 0); if (bitmap) { p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 1); pnm->maxval = 1; } else { p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 0); p = pnm_read_number(ctx, p, e, &pnm->maxval); p = pnm_read_white(ctx, p, e, 0); } if (pnm->maxval <= 0 || pnm->maxval >= 65536) fz_throw(ctx, FZ_ERROR_GENERIC, "maximum sample value of out range in pnm image: %d", pnm->maxval); pnm->bitdepth = bitdepth_from_maxval(pnm->maxval); if (pnm->height <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image height must be > 0"); if (pnm->width <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image width must be > 0"); if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1)) fz_throw(ctx, FZ_ERROR_GENERIC, "image too large"); if (onlymeta) { int x, y, k; int w, h, n; w = pnm->width; h = pnm->height; n = fz_colorspace_n(ctx, pnm->cs); if (bitmap) { for (y = 0; y < h; y++) for (x = -1; x < w; x++) { p = pnm_read_number(ctx, p, e, NULL); p = pnm_read_white(ctx, p, e, 0); } } else { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) { p = pnm_read_number(ctx, p, e, NULL); p = pnm_read_white(ctx, p, e, 0); } } } else { unsigned char *dp; int x, y, k; int w, h, n; img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, 0); dp = img->samples; w = img->w; h = img->h; n = img->n; if (bitmap) { for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int v = 0; p = pnm_read_number(ctx, p, e, &v); p = pnm_read_white(ctx, p, e, 0); *dp++ = v ? 0x00 : 0xff; } } } else { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) { int v = 0; p = pnm_read_number(ctx, p, e, &v); p = pnm_read_white(ctx, p, e, 0); v = fz_clampi(v, 0, pnm->maxval); *dp++ = map_color(ctx, v, pnm->maxval, 255); } } } if (out) *out = p; return img; }
static void retainpages(int argc, char **argv) { pdf_obj *oldroot, *root, *pages, *kids, *countobj, *parent, *olddests; /* Keep only pages/type and (reduced) dest entries to avoid * references to unretained pages */ oldroot = pdf_dict_gets(xref->trailer, "Root"); pages = pdf_dict_gets(oldroot, "Pages"); olddests = pdf_load_name_tree(xref, "Dests"); root = pdf_new_dict(ctx, 2); pdf_dict_puts(root, "Type", pdf_dict_gets(oldroot, "Type")); pdf_dict_puts(root, "Pages", pdf_dict_gets(oldroot, "Pages")); pdf_update_object(xref, pdf_to_num(oldroot), root); pdf_drop_obj(root); /* Create a new kids array with only the pages we want to keep */ parent = pdf_new_indirect(ctx, pdf_to_num(pages), pdf_to_gen(pages), xref); kids = pdf_new_array(ctx, 1); /* Retain pages specified */ while (argc - fz_optind) { int page, spage, epage, pagecount; char *spec, *dash; char *pagelist = argv[fz_optind]; pagecount = pdf_count_pages(xref); spec = fz_strsep(&pagelist, ","); while (spec) { dash = strchr(spec, '-'); if (dash == spec) spage = epage = pagecount; else spage = epage = atoi(spec); if (dash) { if (strlen(dash) > 1) epage = atoi(dash + 1); else epage = pagecount; } if (spage > epage) page = spage, spage = epage, epage = page; spage = fz_clampi(spage, 1, pagecount); epage = fz_clampi(epage, 1, pagecount); for (page = spage; page <= epage; page++) { pdf_obj *pageobj = xref->page_objs[page-1]; pdf_obj *pageref = xref->page_refs[page-1]; pdf_dict_puts(pageobj, "Parent", parent); /* Store page object in new kids array */ pdf_array_push(kids, pageref); } spec = fz_strsep(&pagelist, ","); } fz_optind++; } pdf_drop_obj(parent); /* Update page count and kids array */ countobj = pdf_new_int(ctx, pdf_array_len(kids)); pdf_dict_puts(pages, "Count", countobj); pdf_drop_obj(countobj); pdf_dict_puts(pages, "Kids", kids); pdf_drop_obj(kids); /* Also preserve the (partial) Dests name tree */ if (olddests) { int i; pdf_obj *names = pdf_new_dict(ctx, 1); pdf_obj *dests = pdf_new_dict(ctx, 1); pdf_obj *names_list = pdf_new_array(ctx, 32); int len = pdf_dict_len(olddests); for (i = 0; i < len; i++) { pdf_obj *key = pdf_dict_get_key(olddests, i); pdf_obj *val = pdf_dict_get_val(olddests, i); pdf_obj *key_str = pdf_new_string(ctx, pdf_to_name(key), strlen(pdf_to_name(key))); pdf_obj *dest = pdf_dict_gets(val, "D"); dest = pdf_array_get(dest ? dest : val, 0); if (pdf_array_contains(pdf_dict_gets(pages, "Kids"), dest)) { pdf_array_push(names_list, key_str); pdf_array_push(names_list, val); } pdf_drop_obj(key_str); } root = pdf_dict_gets(xref->trailer, "Root"); pdf_dict_puts(dests, "Names", names_list); pdf_dict_puts(names, "Dests", dests); pdf_dict_puts(root, "Names", names); pdf_drop_obj(names); pdf_drop_obj(dests); pdf_drop_obj(names_list); pdf_drop_obj(olddests); } }
static void retainpages(fz_context *ctx, globals *glo, int argc, char **argv) { pdf_obj *oldroot, *root, *pages, *kids, *countobj, *parent, *olddests; pdf_document *doc = glo->doc; int argidx = 0; pdf_obj *names_list = NULL; pdf_obj *outlines; int pagecount; int i; int *page_object_nums; /* Keep only pages/type and (reduced) dest entries to avoid * references to unretained pages */ oldroot = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME_Root); pages = pdf_dict_get(ctx, oldroot, PDF_NAME_Pages); olddests = pdf_load_name_tree(ctx, doc, PDF_NAME_Dests); outlines = pdf_dict_get(ctx, oldroot, PDF_NAME_Outlines); root = pdf_new_dict(ctx, doc, 3); pdf_dict_put(ctx, root, PDF_NAME_Type, pdf_dict_get(ctx, oldroot, PDF_NAME_Type)); pdf_dict_put(ctx, root, PDF_NAME_Pages, pdf_dict_get(ctx, oldroot, PDF_NAME_Pages)); pdf_dict_put(ctx, root, PDF_NAME_Outlines, outlines); pdf_update_object(ctx, doc, pdf_to_num(ctx, oldroot), root); /* Create a new kids array with only the pages we want to keep */ parent = pdf_new_indirect(ctx, doc, pdf_to_num(ctx, pages), pdf_to_gen(ctx, pages)); kids = pdf_new_array(ctx, doc, 1); /* Retain pages specified */ while (argc - argidx) { int page, spage, epage; char *spec, *dash; char *pagelist = argv[argidx]; pagecount = pdf_count_pages(ctx, doc); spec = fz_strsep(&pagelist, ","); while (spec) { dash = strchr(spec, '-'); if (dash == spec) spage = epage = pagecount; else spage = epage = atoi(spec); if (dash) { if (strlen(dash) > 1) epage = atoi(dash + 1); else epage = pagecount; } spage = fz_clampi(spage, 1, pagecount); epage = fz_clampi(epage, 1, pagecount); if (spage < epage) for (page = spage; page <= epage; ++page) retainpage(ctx, doc, parent, kids, page); else for (page = spage; page >= epage; --page) retainpage(ctx, doc, parent, kids, page); spec = fz_strsep(&pagelist, ","); } argidx++; } pdf_drop_obj(ctx, parent); /* Update page count and kids array */ countobj = pdf_new_int(ctx, doc, pdf_array_len(ctx, kids)); pdf_dict_put(ctx, pages, PDF_NAME_Count, countobj); pdf_drop_obj(ctx, countobj); pdf_dict_put(ctx, pages, PDF_NAME_Kids, kids); pdf_drop_obj(ctx, kids); /* Force the next call to pdf_count_pages to recount */ glo->doc->page_count = 0; pagecount = pdf_count_pages(ctx, doc); page_object_nums = fz_calloc(ctx, pagecount, sizeof(*page_object_nums)); for (i = 0; i < pagecount; i++) { pdf_obj *pageref = pdf_lookup_page_obj(ctx, doc, i); page_object_nums[i] = pdf_to_num(ctx, pageref); } /* If we had an old Dests tree (now reformed as an olddests * dictionary), keep any entries in there that point to * valid pages. This may mean we keep more than we need, but * it's safe at least. */ if (olddests) { pdf_obj *names = pdf_new_dict(ctx, doc, 1); pdf_obj *dests = pdf_new_dict(ctx, doc, 1); int len = pdf_dict_len(ctx, olddests); names_list = pdf_new_array(ctx, doc, 32); for (i = 0; i < len; i++) { pdf_obj *key = pdf_dict_get_key(ctx, olddests, i); pdf_obj *val = pdf_dict_get_val(ctx, olddests, i); pdf_obj *dest = pdf_dict_get(ctx, val, PDF_NAME_D); dest = pdf_array_get(ctx, dest ? dest : val, 0); if (dest_is_valid_page(ctx, dest, page_object_nums, pagecount)) { pdf_obj *key_str = pdf_new_string(ctx, doc, pdf_to_name(ctx, key), strlen(pdf_to_name(ctx, key))); pdf_array_push(ctx, names_list, key_str); pdf_array_push(ctx, names_list, val); pdf_drop_obj(ctx, key_str); } } pdf_dict_put(ctx, dests, PDF_NAME_Names, names_list); pdf_dict_put(ctx, names, PDF_NAME_Dests, dests); pdf_dict_put(ctx, root, PDF_NAME_Names, names); pdf_drop_obj(ctx, names); pdf_drop_obj(ctx, dests); pdf_drop_obj(ctx, olddests); } /* Edit each pages /Annot list to remove any links that point to * nowhere. */ for (i = 0; i < pagecount; i++) { pdf_obj *pageref = pdf_lookup_page_obj(ctx, doc, i); pdf_obj *pageobj = pdf_resolve_indirect(ctx, pageref); pdf_obj *annots = pdf_dict_get(ctx, pageobj, PDF_NAME_Annots); int len = pdf_array_len(ctx, annots); int j; for (j = 0; j < len; j++) { pdf_obj *o = pdf_array_get(ctx, annots, j); if (!pdf_name_eq(ctx, pdf_dict_get(ctx, o, PDF_NAME_Subtype), PDF_NAME_Link)) continue; if (!dest_is_valid(ctx, o, pagecount, page_object_nums, names_list)) { /* Remove this annotation */ pdf_array_delete(ctx, annots, j); j--; } } } if (strip_outlines(ctx, doc, outlines, pagecount, page_object_nums, names_list) == 0) { pdf_dict_del(ctx, root, PDF_NAME_Outlines); } fz_free(ctx, page_object_nums); pdf_drop_obj(ctx, names_list); pdf_drop_obj(ctx, root); }