static void gatherresourceinfo(int page, pdf_obj *rsrc) { pdf_obj *pageobj; pdf_obj *pageref; pdf_obj *font; pdf_obj *xobj; pdf_obj *shade; pdf_obj *pattern; pdf_obj *subrsrc; int i; pageobj = xref->page_objs[page-1]; pageref = xref->page_refs[page-1]; if (!pageobj) fz_throw(ctx, "cannot retrieve info from page %d", page); font = pdf_dict_gets(rsrc, "Font"); if (font) { int n; gatherfonts(page, pageref, pageobj, font); n = pdf_dict_len(font); for (i = 0; i < n; i++) { pdf_obj *obj = pdf_dict_get_val(font, i); subrsrc = pdf_dict_gets(obj, "Resources"); if (subrsrc && pdf_objcmp(rsrc, subrsrc)) gatherresourceinfo(page, subrsrc); } } xobj = pdf_dict_gets(rsrc, "XObject"); if (xobj) { int n; gatherimages(page, pageref, pageobj, xobj); gatherforms(page, pageref, pageobj, xobj); gatherpsobjs(page, pageref, pageobj, xobj); n = pdf_dict_len(xobj); for (i = 0; i < n; i++) { pdf_obj *obj = pdf_dict_get_val(xobj, i); subrsrc = pdf_dict_gets(obj, "Resources"); if (subrsrc && pdf_objcmp(rsrc, subrsrc)) gatherresourceinfo(page, subrsrc); } } shade = pdf_dict_gets(rsrc, "Shading"); if (shade) gathershadings(page, pageref, pageobj, shade); pattern = pdf_dict_gets(rsrc, "Pattern"); if (pattern) { int n; gatherpatterns(page, pageref, pageobj, pattern); n = pdf_dict_len(pattern); for (i = 0; i < n; i++) { pdf_obj *obj = pdf_dict_get_val(pattern, i); subrsrc = pdf_dict_gets(obj, "Resources"); if (subrsrc && pdf_objcmp(rsrc, subrsrc)) gatherresourceinfo(page, subrsrc); } } }
static fz_pixmap * decomp_image_from_stream(fz_context *ctx, fz_stream *stm, pdf_image *image, int in_line, int indexed, int l2factor, int native_l2factor, int cache) { fz_pixmap *tile = NULL; fz_pixmap *existing_tile; int stride, len, i; unsigned char *samples = NULL; int f = 1<<native_l2factor; int w = (image->base.w + f-1) >> native_l2factor; int h = (image->base.h + f-1) >> native_l2factor; /* SumatraPDF: prevent use of uninitialized value */ pdf_image_key *key = NULL; fz_var(key); fz_var(tile); fz_var(samples); fz_try(ctx) { tile = fz_new_pixmap(ctx, image->base.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, "cannot read image data"); } /* Make sure we read the EOF marker (for inline images only) */ /* cf. http://code.google.com/p/sumatrapdf/issues/detail?id=1980 */ if (in_line && 0) { 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) { 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) pdf_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 = pdf_expand_indexed_pixmap(ctx, tile); fz_drop_pixmap(ctx, tile); tile = conv; } else { fz_decode_tile(tile, image->decode); } }
static int next_flated(fz_stream *stm, int required) { fz_flate *state = stm->state; fz_stream *chain = state->chain; z_streamp zp = &state->z; int code; unsigned char *outbuf = state->buffer; int outlen = sizeof(state->buffer); if (stm->eof) return EOF; zp->next_out = outbuf; zp->avail_out = outlen; while (zp->avail_out > 0) { zp->avail_in = fz_available(chain, 1); zp->next_in = chain->rp; code = inflate(zp, Z_SYNC_FLUSH); chain->rp = chain->wp - zp->avail_in; if (code == Z_STREAM_END) { break; } else if (code == Z_BUF_ERROR) { fz_warn(stm->ctx, "premature end of data in flate filter"); break; } else if (code == Z_DATA_ERROR && zp->avail_in == 0) { fz_warn(stm->ctx, "ignoring zlib error: %s", zp->msg); break; } else if (code == Z_DATA_ERROR && !strcmp(zp->msg, "incorrect data check")) { fz_warn(stm->ctx, "ignoring zlib error: %s", zp->msg); chain->rp = chain->wp; break; } else if (code != Z_OK) { fz_throw(stm->ctx, FZ_ERROR_GENERIC, "zlib error: %s", zp->msg); } } stm->rp = state->buffer; stm->wp = state->buffer + outlen - zp->avail_out; stm->pos += outlen - zp->avail_out; if (stm->rp == stm->wp) { stm->eof = 1; return EOF; } return *stm->rp++; }
static fz_error parsebfrange(pdf_cmap *cmap, fz_stream *file) { fz_error error; char buf[256]; pdf_token_e tok; int len; int lo, hi, dst; while (1) { error = lexcmap(&tok, file, buf, sizeof buf, &len); if (error) return fz_rethrow(error, "syntaxerror in cmap"); if (tok == TENDBFRANGE) return fz_okay; else if (tok != PDF_TSTRING) return fz_throw("expected string or endbfrange"); lo = codefromstring(buf, len); error = lexcmap(&tok, file, buf, sizeof buf, &len); if (error) return fz_rethrow(error, "syntaxerror in cmap"); if (tok != PDF_TSTRING) return fz_throw("expected string"); hi = codefromstring(buf, len); error = lexcmap(&tok, file, buf, sizeof buf, &len); if (error) return fz_rethrow(error, "syntaxerror in cmap"); if (tok == PDF_TSTRING) { if (len == 2) { dst = codefromstring(buf, len); pdf_maprangetorange(cmap, lo, hi, dst); } else { int dststr[256]; int i; if (len / 2) { for (i = 0; i < len / 2; i++) dststr[i] = codefromstring(buf + i * 2, 2); while (lo <= hi) { dststr[i-1] ++; pdf_maponetomany(cmap, lo, dststr, i); lo ++; } } } } else if (tok == PDF_TOARRAY) { error = parsebfrangearray(cmap, file, lo, hi); if (error) return fz_rethrow(error, "cannot map bfrange"); } else { return fz_throw("expected string or array or endbfrange"); } } }
static fz_error * rendersolid(fz_renderer *gc, fz_solidnode *solid, fz_matrix ctm) { fz_error *error; float rgb[3]; unsigned char a, r, g, b; unsigned char *p; int n; if (gc->maskonly) return fz_throw("assert: mask only renderer"); if (gc->model->n != 3) return fz_throw("assert: non-rgb renderer"); fz_convertcolor(solid->cs, solid->samples, gc->model, rgb); gc->argb[0] = solid->a * 255; gc->argb[1] = rgb[0] * solid->a * 255; gc->argb[2] = rgb[1] * solid->a * 255; gc->argb[3] = rgb[2] * solid->a * 255; gc->argb[4] = rgb[0] * 255; gc->argb[5] = rgb[1] * 255; gc->argb[6] = rgb[2] * 255; DEBUG("solid %s [%d %d %d %d];\n", solid->cs->name, gc->argb[0], gc->argb[1], gc->argb[2], gc->argb[3]); if (gc->flag == FOVER) { p = gc->over->samples; n = gc->over->w * gc->over->h; } else { error = fz_newpixmapwithrect(&gc->dest, gc->clip, 4); if (error) return error; p = gc->dest->samples; n = gc->dest->w * gc->dest->h; } a = gc->argb[0]; r = gc->argb[1]; g = gc->argb[2]; b = gc->argb[3]; if (((unsigned)p & 3)) { while (n--) { p[0] = a; p[1] = r; p[2] = g; p[3] = b; p += 4; } } else { unsigned *pw = (unsigned *)p; #if BYTE_ORDER == LITTLE_ENDIAN unsigned argb = a | (r << 8) | (g << 16) | (b << 24); #else unsigned argb = (a << 24) | (r << 16) | (g << 8) | b; #endif while (n--) { *pw++ = argb; } } return fz_okay; }
fz_error * fz_processpipeline(fz_filter *filter, fz_buffer *in, fz_buffer *out) { fz_pipeline *p = (fz_pipeline*)filter; fz_error *e; if (p->buffer->eof) goto tail; if (p->tailneedsin && p->head->produced) goto tail; head: e = fz_process(p->head, in, p->buffer); if (e == fz_ioneedin) return fz_ioneedin; else if (e == fz_ioneedout) { if (p->tailneedsin && !p->head->produced) { fz_error *be = nil; if (p->buffer->rp > p->buffer->bp) be = fz_rewindbuffer(p->buffer); else be = fz_growbuffer(p->buffer); if (be) return be; goto head; } goto tail; } else if (e == fz_iodone) goto tail; else return e; tail: e = fz_process(p->tail, p->buffer, out); if (e == fz_ioneedin) { if (p->buffer->eof) return fz_throw("ioerror: premature eof in pipeline"); p->tailneedsin = 1; goto head; } else if (e == fz_ioneedout) { p->tailneedsin = 0; return fz_ioneedout; } else if (e == fz_iodone) return fz_iodone; else return e; }
static void check_allowed_subtypes(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed) { pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); if (!is_allowed_subtype(ctx, annot, property, allowed)) fz_throw(ctx, FZ_ERROR_GENERIC, "%s annotations have no %s property", pdf_to_name(ctx, subtype), pdf_to_name(ctx, property)); }
static fz_error loadsimplefont(pdf_fontdesc **fontdescp, pdf_xref *xref, fz_obj *dict) { fz_error error; fz_obj *descriptor = nil; fz_obj *encoding = nil; fz_obj *widths = nil; unsigned short *etable = nil; pdf_fontdesc *fontdesc; fz_irect bbox; FT_Face face; FT_CharMap cmap; int kind; int symbolic; char *basefont; char *fontname; char *estrings[256]; char ebuffer[256][32]; int i, k, n; int fterr; basefont = fz_toname(fz_dictgets(dict, "BaseFont")); fontname = cleanfontname(basefont); /* * Load font file */ fontdesc = pdf_newfontdesc(); if (!fontdesc) return fz_rethrow(-1, "out of memory"); pdf_logfont("load simple font (%d %d R) ptr=%p {\n", fz_tonum(dict), fz_togen(dict), fontdesc); pdf_logfont("basefont0 %s\n", basefont); pdf_logfont("basefont1 %s\n", fontname); descriptor = fz_dictgets(dict, "FontDescriptor"); if (descriptor && basefont == fontname) error = pdf_loadfontdescriptor(fontdesc, xref, descriptor, nil); else error = pdf_loadbuiltinfont(fontdesc, fontname); if (error) goto cleanup; face = fontdesc->font->ftface; kind = ftkind(face); pdf_logfont("ft name '%s' '%s'\n", face->family_name, face->style_name); bbox.x0 = (face->bbox.xMin * 1000) / face->units_per_EM; bbox.y0 = (face->bbox.yMin * 1000) / face->units_per_EM; bbox.x1 = (face->bbox.xMax * 1000) / face->units_per_EM; bbox.y1 = (face->bbox.yMax * 1000) / face->units_per_EM; pdf_logfont("ft bbox [%d %d %d %d]\n", bbox.x0, bbox.y0, bbox.x1, bbox.y1); if (bbox.x0 == bbox.x1) fz_setfontbbox(fontdesc->font, -1000, -1000, 2000, 2000); else fz_setfontbbox(fontdesc->font, bbox.x0, bbox.y0, bbox.x1, bbox.y1); /* * Encoding */ symbolic = fontdesc->flags & 4; if (face->num_charmaps > 0) cmap = face->charmaps[0]; else cmap = nil; for (i = 0; i < face->num_charmaps; i++) { FT_CharMap test = face->charmaps[i]; if (kind == TYPE1) { if (test->platform_id == 7) cmap = test; } if (kind == TRUETYPE) { if (test->platform_id == 1 && test->encoding_id == 0) cmap = test; if (test->platform_id == 3 && test->encoding_id == 1) cmap = test; } } if (cmap) { fterr = FT_Set_Charmap(face, cmap); if (fterr) fz_warn("freetype could not set cmap: %s", ft_errorstring(fterr)); } else fz_warn("freetype could not find any cmaps"); etable = fz_malloc(sizeof(unsigned short) * 256); if (!etable) goto cleanup; for (i = 0; i < 256; i++) { estrings[i] = nil; etable[i] = 0; } encoding = fz_dictgets(dict, "Encoding"); if (encoding && !(kind == TRUETYPE && symbolic)) { if (fz_isname(encoding)) pdf_loadencoding(estrings, fz_toname(encoding)); if (fz_isdict(encoding)) { fz_obj *base, *diff, *item; base = fz_dictgets(encoding, "BaseEncoding"); if (fz_isname(base)) pdf_loadencoding(estrings, fz_toname(base)); else if (!fontdesc->isembedded) pdf_loadencoding(estrings, "StandardEncoding"); diff = fz_dictgets(encoding, "Differences"); if (fz_isarray(diff)) { n = fz_arraylen(diff); k = 0; for (i = 0; i < n; i++) { item = fz_arrayget(diff, i); if (fz_isint(item)) k = fz_toint(item); if (fz_isname(item)) estrings[k++] = fz_toname(item); if (k < 0) k = 0; if (k > 255) k = 255; } } } if (kind == TYPE1) { pdf_logfont("encode type1/cff by strings\n"); for (i = 0; i < 256; i++) if (estrings[i]) etable[i] = FT_Get_Name_Index(face, estrings[i]); else etable[i] = ftcharindex(face, i); } if (kind == TRUETYPE) { /* Unicode cmap */ if (face->charmap && face->charmap->platform_id == 3) { pdf_logfont("encode truetype via unicode\n"); for (i = 0; i < 256; i++) if (estrings[i]) { int aglbuf[256]; int aglnum; aglnum = pdf_lookupagl(estrings[i], aglbuf, nelem(aglbuf)); if (aglnum != 1) etable[i] = FT_Get_Name_Index(face, estrings[i]); else etable[i] = ftcharindex(face, aglbuf[0]); } else etable[i] = ftcharindex(face, i); } /* MacRoman cmap */ else if (face->charmap && face->charmap->platform_id == 1) { pdf_logfont("encode truetype via macroman\n"); for (i = 0; i < 256; i++) if (estrings[i]) { k = mrecode(estrings[i]); if (k <= 0) etable[i] = FT_Get_Name_Index(face, estrings[i]); else etable[i] = ftcharindex(face, k); } else etable[i] = ftcharindex(face, i); } /* Symbolic cmap */ else { pdf_logfont("encode truetype symbolic\n"); for (i = 0; i < 256; i++) { etable[i] = ftcharindex(face, i); fterr = FT_Get_Glyph_Name(face, etable[i], ebuffer[i], 32); if (fterr) { error = fz_throw("freetype get glyph name (gid %d): %s", etable[i], ft_errorstring(fterr)); goto cleanup; } if (ebuffer[i][0]) estrings[i] = ebuffer[i]; } } } } else { pdf_logfont("encode builtin\n"); for (i = 0; i < 256; i++) { etable[i] = ftcharindex(face, i); if (etable[i] == 0) continue; if (FT_HAS_GLYPH_NAMES(face)) { fterr = FT_Get_Glyph_Name(face, etable[i], ebuffer[i], 32); if (fterr) { error = fz_throw("freetype get glyph name (gid %d): %s", etable[i], ft_errorstring(fterr)); goto cleanup; } if (ebuffer[i][0]) estrings[i] = ebuffer[i]; } } } error = pdf_newidentitycmap(&fontdesc->encoding, 0, 1); if (error) goto cleanup; fontdesc->ncidtogid = 256; fontdesc->cidtogid = etable; error = pdf_loadtounicode(fontdesc, xref, estrings, nil, fz_dictgets(dict, "ToUnicode")); if (error) goto cleanup; /* * Widths */ pdf_setdefaulthmtx(fontdesc, fontdesc->missingwidth); widths = fz_dictgets(dict, "Widths"); if (widths) { int first, last; first = fz_toint(fz_dictgets(dict, "FirstChar")); last = fz_toint(fz_dictgets(dict, "LastChar")); if (first < 0 || last > 255 || first > last) first = last = 0; for (i = 0; i < last - first + 1; i++) { int wid = fz_toint(fz_arrayget(widths, i)); error = pdf_addhmtx(fontdesc, i + first, i + first, wid); if (error) goto cleanup; } } else { fterr = FT_Set_Char_Size(face, 1000, 1000, 72, 72); if (fterr) fz_warn("freetype set character size: %s", ft_errorstring(fterr)); for (i = 0; i < 256; i++) { error = pdf_addhmtx(fontdesc, i, i, ftwidth(fontdesc, i)); if (error) goto cleanup; } } error = pdf_endhmtx(fontdesc); if (error) goto cleanup; pdf_logfont("}\n"); *fontdescp = fontdesc; return fz_okay; cleanup: fz_free(etable); fz_dropfont(fontdesc->font); fz_free(fontdesc); return fz_rethrow(error, "cannot load simple font"); }
static fz_error loadcidfont(pdf_fontdesc **fontdescp, pdf_xref *xref, fz_obj *dict, fz_obj *encoding, fz_obj *tounicode) { fz_error error; fz_obj *widths = nil; fz_obj *descriptor; pdf_fontdesc *fontdesc; FT_Face face; fz_irect bbox; int kind; char collection[256]; char *basefont; int i, k, fterr; /* * Get font name and CID collection */ basefont = fz_toname(fz_dictgets(dict, "BaseFont")); { fz_obj *cidinfo; fz_obj *obj; char tmpstr[64]; int tmplen; cidinfo = fz_dictgets(dict, "CIDSystemInfo"); if (!cidinfo) return fz_throw("cid font is missing info"); obj = fz_dictgets(cidinfo, "Registry"); tmplen = MIN(sizeof tmpstr - 1, fz_tostrlen(obj)); memcpy(tmpstr, fz_tostrbuf(obj), tmplen); tmpstr[tmplen] = '\0'; strlcpy(collection, tmpstr, sizeof collection); strlcat(collection, "-", sizeof collection); obj = fz_dictgets(cidinfo, "Ordering"); tmplen = MIN(sizeof tmpstr - 1, fz_tostrlen(obj)); memcpy(tmpstr, fz_tostrbuf(obj), tmplen); tmpstr[tmplen] = '\0'; strlcat(collection, tmpstr, sizeof collection); } /* * Load font file */ fontdesc = pdf_newfontdesc(); if (!fontdesc) return fz_rethrow(-1, "out of memory"); pdf_logfont("load cid font (%d %d R) ptr=%p {\n", fz_tonum(dict), fz_togen(dict), fontdesc); pdf_logfont("basefont %s\n", basefont); pdf_logfont("collection %s\n", collection); descriptor = fz_dictgets(dict, "FontDescriptor"); if (descriptor) error = pdf_loadfontdescriptor(fontdesc, xref, descriptor, collection); else error = fz_throw("syntaxerror: missing font descriptor"); if (error) goto cleanup; face = fontdesc->font->ftface; kind = ftkind(face); bbox.x0 = (face->bbox.xMin * 1000) / face->units_per_EM; bbox.y0 = (face->bbox.yMin * 1000) / face->units_per_EM; bbox.x1 = (face->bbox.xMax * 1000) / face->units_per_EM; bbox.y1 = (face->bbox.yMax * 1000) / face->units_per_EM; pdf_logfont("ft bbox [%d %d %d %d]\n", bbox.x0, bbox.y0, bbox.x1, bbox.y1); if (bbox.x0 == bbox.x1) fz_setfontbbox(fontdesc->font, -1000, -1000, 2000, 2000); else fz_setfontbbox(fontdesc->font, bbox.x0, bbox.y0, bbox.x1, bbox.y1); /* * Encoding */ if (fz_isname(encoding)) { pdf_logfont("encoding /%s\n", fz_toname(encoding)); if (!strcmp(fz_toname(encoding), "Identity-H")) error = pdf_newidentitycmap(&fontdesc->encoding, 0, 2); else if (!strcmp(fz_toname(encoding), "Identity-V")) error = pdf_newidentitycmap(&fontdesc->encoding, 1, 2); else error = pdf_loadsystemcmap(&fontdesc->encoding, fz_toname(encoding)); } else if (fz_isindirect(encoding)) { pdf_logfont("encoding %d %d R\n", fz_tonum(encoding), fz_togen(encoding)); error = pdf_loadembeddedcmap(&fontdesc->encoding, xref, encoding); } else { error = fz_throw("syntaxerror: font missing encoding"); } if (error) goto cleanup; pdf_setfontwmode(fontdesc, pdf_getwmode(fontdesc->encoding)); pdf_logfont("wmode %d\n", pdf_getwmode(fontdesc->encoding)); if (kind == TRUETYPE) { fz_obj *cidtogidmap; cidtogidmap = fz_dictgets(dict, "CIDToGIDMap"); if (fz_isindirect(cidtogidmap)) { unsigned short *map; fz_buffer *buf; int len; pdf_logfont("cidtogidmap stream\n"); error = pdf_loadstream(&buf, xref, fz_tonum(cidtogidmap), fz_togen(cidtogidmap)); if (error) goto cleanup; len = (buf->wp - buf->rp) / 2; map = fz_malloc(len * sizeof(unsigned short)); if (!map) { fz_dropbuffer(buf); error = fz_rethrow(-1, "out of memory: cidtogidmap"); goto cleanup; } for (i = 0; i < len; i++) map[i] = (buf->rp[i * 2] << 8) + buf->rp[i * 2 + 1]; fontdesc->ncidtogid = len; fontdesc->cidtogid = map; fz_dropbuffer(buf); } /* if truetype font is external, cidtogidmap should not be identity */ /* so we map from cid to unicode and then map that through the (3 1) */ /* unicode cmap to get a glyph id */ else if (fontdesc->font->ftsubstitute) { pdf_logfont("emulate ttf cidfont\n"); fterr = FT_Select_Charmap(face, ft_encoding_unicode); if (fterr) { error = fz_throw("fonterror: no unicode cmap when emulating CID font: %s", ft_errorstring(fterr)); goto cleanup; } if (!strcmp(collection, "Adobe-CNS1")) error = pdf_loadsystemcmap(&fontdesc->tottfcmap, "Adobe-CNS1-UCS2"); else if (!strcmp(collection, "Adobe-GB1")) error = pdf_loadsystemcmap(&fontdesc->tottfcmap, "Adobe-GB1-UCS2"); else if (!strcmp(collection, "Adobe-Japan1")) error = pdf_loadsystemcmap(&fontdesc->tottfcmap, "Adobe-Japan1-UCS2"); else if (!strcmp(collection, "Adobe-Japan2")) error = pdf_loadsystemcmap(&fontdesc->tottfcmap, "Adobe-Japan2-UCS2"); else if (!strcmp(collection, "Adobe-Korea1")) error = pdf_loadsystemcmap(&fontdesc->tottfcmap, "Adobe-Korea1-UCS2"); else error = fz_okay; if (error) { error = fz_rethrow(error, "cannot load system cmap %s", collection); goto cleanup; } } } error = pdf_loadtounicode(fontdesc, xref, nil, collection, tounicode); if (error) goto cleanup; /* * Horizontal */ pdf_setdefaulthmtx(fontdesc, fz_toint(fz_dictgets(dict, "DW"))); widths = fz_dictgets(dict, "W"); if (widths) { int c0, c1, w; fz_obj *obj; for (i = 0; i < fz_arraylen(widths); ) { c0 = fz_toint(fz_arrayget(widths, i)); obj = fz_arrayget(widths, i + 1); if (fz_isarray(obj)) { for (k = 0; k < fz_arraylen(obj); k++) { w = fz_toint(fz_arrayget(obj, k)); error = pdf_addhmtx(fontdesc, c0 + k, c0 + k, w); if (error) goto cleanup; } i += 2; } else { c1 = fz_toint(obj); w = fz_toint(fz_arrayget(widths, i + 2)); error = pdf_addhmtx(fontdesc, c0, c1, w); if (error) goto cleanup; i += 3; } } } error = pdf_endhmtx(fontdesc); if (error) goto cleanup; /* * Vertical */ if (pdf_getwmode(fontdesc->encoding) == 1) { fz_obj *obj; int dw2y = 880; int dw2w = -1000; obj = fz_dictgets(dict, "DW2"); if (obj) { dw2y = fz_toint(fz_arrayget(obj, 0)); dw2w = fz_toint(fz_arrayget(obj, 1)); } pdf_setdefaultvmtx(fontdesc, dw2y, dw2w); widths = fz_dictgets(dict, "W2"); if (widths) { int c0, c1, w, x, y, k; for (i = 0; i < fz_arraylen(widths); ) { c0 = fz_toint(fz_arrayget(widths, i)); obj = fz_arrayget(widths, i + 1); if (fz_isarray(obj)) { for (k = 0; k < fz_arraylen(obj); k += 3) { w = fz_toint(fz_arrayget(obj, k + 0)); x = fz_toint(fz_arrayget(obj, k + 1)); y = fz_toint(fz_arrayget(obj, k + 2)); error = pdf_addvmtx(fontdesc, c0 + k, c0 + k, x, y, w); if (error) goto cleanup; } i += 2; } else { c1 = fz_toint(obj); w = fz_toint(fz_arrayget(widths, i + 2)); x = fz_toint(fz_arrayget(widths, i + 3)); y = fz_toint(fz_arrayget(widths, i + 4)); error = pdf_addvmtx(fontdesc, c0, c1, x, y, w); if (error) goto cleanup; i += 5; } } } error = pdf_endvmtx(fontdesc); if (error) goto cleanup; } pdf_logfont("}\n"); *fontdescp = fontdesc; return fz_okay; cleanup: if (widths) fz_dropobj(widths); fz_dropfont(fontdesc->font); fz_free(fontdesc); return fz_rethrow(error, "cannot load cid font"); }
pdf_xobject * pdf_load_xobject(pdf_document *xref, pdf_obj *dict) { pdf_xobject *form; pdf_obj *obj; fz_context *ctx = xref->ctx; if ((form = pdf_find_item(ctx, pdf_free_xobject_imp, dict))) { return form; } form = fz_malloc_struct(ctx, pdf_xobject); FZ_INIT_STORABLE(form, 1, pdf_free_xobject_imp); form->resources = NULL; form->contents = NULL; form->colorspace = NULL; form->me = NULL; /* Store item immediately, to avoid possible recursion if objects refer back to this one */ pdf_store_item(ctx, dict, form, pdf_xobject_size(form)); obj = pdf_dict_gets(dict, "BBox"); form->bbox = pdf_to_rect(ctx, obj); obj = pdf_dict_gets(dict, "Matrix"); if (obj) form->matrix = pdf_to_matrix(ctx, obj); else form->matrix = fz_identity; form->isolated = 0; form->knockout = 0; form->transparency = 0; obj = pdf_dict_gets(dict, "Group"); if (obj) { pdf_obj *attrs = obj; form->isolated = pdf_to_bool(pdf_dict_gets(attrs, "I")); form->knockout = pdf_to_bool(pdf_dict_gets(attrs, "K")); obj = pdf_dict_gets(attrs, "S"); if (pdf_is_name(obj) && !strcmp(pdf_to_name(obj), "Transparency")) form->transparency = 1; obj = pdf_dict_gets(attrs, "CS"); if (obj) { form->colorspace = pdf_load_colorspace(xref, obj); if (!form->colorspace) fz_throw(ctx, "cannot load xobject colorspace"); } } form->resources = pdf_dict_gets(dict, "Resources"); if (form->resources) pdf_keep_obj(form->resources); fz_try(ctx) { form->contents = pdf_load_stream(xref, pdf_to_num(dict), pdf_to_gen(dict)); } fz_catch(ctx) { pdf_remove_item(ctx, pdf_free_xobject_imp, dict); pdf_drop_xobject(ctx, form); fz_throw(ctx, "cannot load xobject content stream (%d %d R)", pdf_to_num(dict), pdf_to_gen(dict)); } form->me = pdf_keep_obj(dict); return form; }
static fz_image *render_to_pixmap(fz_context *ctx, HBITMAP hbmp, SizeI size) { int w = size.dx, h = size.dy; int stride = ((w * 3 + 3) / 4) * 4; unsigned char *data = (unsigned char *)fz_malloc(ctx, stride * h); BITMAPINFO bmi = { 0 }; bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); bmi.bmiHeader.biWidth = w; bmi.bmiHeader.biHeight = -h; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 24; bmi.bmiHeader.biCompression = BI_RGB; HDC hDC = GetDC(NULL); int res = GetDIBits(hDC, hbmp, 0, h, data, &bmi, DIB_RGB_COLORS); ReleaseDC(NULL, hDC); if (!res) { fz_free(ctx, data); fz_throw(ctx, FZ_ERROR_GENERIC, "GetDIBits failed"); } // convert BGR with padding to RGB without padding unsigned char *out = data; bool is_grayscale = true; for (int y = 0; y < h; y++) { const unsigned char *in = data + y * stride; unsigned char green, blue; for (int x = 0; x < w; x++) { is_grayscale = is_grayscale && in[0] == in[1] && in[0] == in[2]; blue = *in++; green = *in++; *out++ = *in++; *out++ = green; *out++ = blue; } } // convert grayscale RGB to proper grayscale if (is_grayscale) { const unsigned char *in = out = data; for (int i = 0; i < w * h; i++) { *out++ = *in++; in += 2; } } fz_compressed_buffer *buf = NULL; fz_var(buf); fz_try(ctx) { buf = fz_malloc_struct(ctx, fz_compressed_buffer); buf->buffer = fz_new_buffer(ctx, w * h * 4 + 10); buf->params.type = FZ_IMAGE_FLATE; buf->params.u.flate.predictor = 1; z_stream zstm = { 0 }; zstm.next_in = data; zstm.avail_in = out - data; zstm.next_out = buf->buffer->data; zstm.avail_out = buf->buffer->cap; res = deflateInit(&zstm, 9); if (res != Z_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "deflate failure %d", res); res = deflate(&zstm, Z_FINISH); if (res != Z_STREAM_END) fz_throw(ctx, FZ_ERROR_GENERIC, "deflate failure %d", res); buf->buffer->len = zstm.total_out; res = deflateEnd(&zstm); if (res != Z_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "deflate failure %d", res); } fz_always(ctx) { fz_free(ctx, data); } fz_catch(ctx) { fz_free_compressed_buffer(ctx, buf); fz_rethrow(ctx); } fz_colorspace *cs = is_grayscale ? fz_device_gray(ctx) : fz_device_rgb(ctx); return fz_new_image(ctx, w, h, 8, cs, 96, 96, 0, 0, NULL, NULL, buf, NULL); }
static fz_pixmap * decomp_image_from_stream(fz_context *ctx, fz_stream *stm, pdf_image *image, int in_line, int indexed, int factor, int dont_cache) { fz_pixmap *tile = NULL; fz_pixmap *existing_tile; int stride, len, i; unsigned char *samples = NULL; int w = (image->base.w + (factor-1)) / factor; int h = (image->base.h + (factor-1)) / factor; pdf_image_key *key; fz_var(tile); fz_var(samples); fz_try(ctx) { tile = fz_new_pixmap(ctx, image->base.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, "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) { 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) pdf_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 = pdf_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); } /* SumatraPDF: don't cache preloaded images */ /* TODO: in this case, the image keeps a reference to the pixmap and * the key for storing the pixmap keeps a reference to the image which * leads to a catch-22 and neither is ever evicted from the cache */ if (dont_cache) return tile; /* Now we try to cache the pixmap. Any failure here will just result * in us not caching. */ fz_try(ctx) { key = fz_malloc_struct(ctx, pdf_image_key); key->refs = 1; key->image = fz_keep_image(ctx, &image->base); key->factor = factor; existing_tile = fz_store_item(ctx, key, tile, fz_pixmap_size(ctx, tile), &pdf_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) { pdf_drop_image_key(ctx, key); } fz_catch(ctx) { /* Do nothing */ } return tile; }
static pdf_image * pdf_load_image_imp(pdf_document *xref, pdf_obj *rdb, pdf_obj *dict, fz_stream *cstm, int forcemask) { fz_stream *stm = NULL; pdf_image *image = NULL; pdf_obj *obj, *res; int w, h, bpc, n; int imagemask; int interpolate; int indexed; fz_image *mask = NULL; /* explicit mask/softmask image */ int usecolorkey; int i; fz_context *ctx = xref->ctx; fz_var(stm); fz_var(mask); image = fz_malloc_struct(ctx, pdf_image); /* SumatraPDF: fix memory leak */ FZ_INIT_STORABLE(&image->base, 1, pdf_free_image); fz_try(ctx) { /* special case for JPEG2000 images */ if (pdf_is_jpx_image(ctx, dict)) { pdf_load_jpx(xref, dict, image); /* RJW: "cannot load jpx image" */ if (forcemask) { fz_pixmap *mask_pixmap; if (image->n != 2) fz_throw(ctx, "softmask must be grayscale"); mask_pixmap = fz_alpha_from_gray(ctx, image->tile, 1); fz_drop_pixmap(ctx, image->tile); image->tile = mask_pixmap; } break; /* Out of fz_try */ } w = pdf_to_int(pdf_dict_getsa(dict, "Width", "W")); h = pdf_to_int(pdf_dict_getsa(dict, "Height", "H")); bpc = pdf_to_int(pdf_dict_getsa(dict, "BitsPerComponent", "BPC")); imagemask = pdf_to_bool(pdf_dict_getsa(dict, "ImageMask", "IM")); interpolate = pdf_to_bool(pdf_dict_getsa(dict, "Interpolate", "I")); indexed = 0; usecolorkey = 0; mask = NULL; if (imagemask) bpc = 1; if (w == 0) fz_throw(ctx, "image width is zero"); if (h == 0) fz_throw(ctx, "image height is zero"); if (bpc == 0) fz_throw(ctx, "image depth is zero"); if (bpc > 16) fz_throw(ctx, "image depth is too large: %d", bpc); if (w > (1 << 16)) fz_throw(ctx, "image is too wide"); if (h > (1 << 16)) fz_throw(ctx, "image is too high"); obj = pdf_dict_getsa(dict, "ColorSpace", "CS"); if (obj && !imagemask && !forcemask) { /* colorspace resource lookup is only done for inline images */ if (pdf_is_name(obj)) { res = pdf_dict_get(pdf_dict_gets(rdb, "ColorSpace"), obj); if (res) obj = res; } image->base.colorspace = pdf_load_colorspace(xref, obj); /* RJW: "cannot load image colorspace" */ if (!strcmp(image->base.colorspace->name, "Indexed")) indexed = 1; n = image->base.colorspace->n; } else { n = 1; } obj = pdf_dict_getsa(dict, "Decode", "D"); if (obj) { for (i = 0; i < n * 2; i++) image->decode[i] = pdf_to_real(pdf_array_get(obj, i)); } else { float maxval = indexed ? (1 << bpc) - 1 : 1; for (i = 0; i < n * 2; i++) image->decode[i] = i & 1 ? maxval : 0; } obj = pdf_dict_getsa(dict, "SMask", "Mask"); if (pdf_is_dict(obj)) { /* Not allowed for inline images */ if (!cstm) { mask = (fz_image *)pdf_load_image_imp(xref, rdb, obj, NULL, 1); /* RJW: "cannot load image mask/softmask" */ } } else if (pdf_is_array(obj)) { usecolorkey = 1; for (i = 0; i < n * 2; i++) { if (!pdf_is_int(pdf_array_get(obj, i))) { fz_warn(ctx, "invalid value in color key mask"); usecolorkey = 0; } image->colorkey[i] = pdf_to_int(pdf_array_get(obj, i)); } } /* Now, do we load a ref, or do we load the actual thing? */ image->params.type = PDF_IMAGE_RAW; FZ_INIT_STORABLE(&image->base, 1, pdf_free_image); image->base.get_pixmap = pdf_image_get_pixmap; image->base.w = w; image->base.h = h; image->n = n; image->bpc = bpc; image->interpolate = interpolate; image->imagemask = imagemask; image->usecolorkey = usecolorkey; image->base.mask = mask; image->params.colorspace = image->base.colorspace; /* Uses the same ref as for the base one */ if (!indexed && !cstm) { /* Just load the compressed image data now and we can * decode it on demand. */ image->buffer = pdf_load_image_stream(xref, pdf_to_num(dict), pdf_to_gen(dict), &image->params); break; /* Out of fz_try */ } /* We need to decompress the image now */ if (cstm) { int stride = (w * image->n * image->bpc + 7) / 8; stm = pdf_open_inline_stream(xref, dict, stride * h, cstm, NULL); } else { stm = pdf_open_stream(xref, pdf_to_num(dict), pdf_to_gen(dict)); /* RJW: "cannot open image data stream (%d 0 R)", pdf_to_num(dict) */ } image->tile = decomp_image_from_stream(ctx, stm, image, cstm != NULL, indexed, 1, 1); } fz_catch(ctx) { fz_drop_image(ctx, &image->base); fz_rethrow(ctx); } return image; }
int main(int argc, char **argv) #endif { enum { NO_FILE_OPENED, NO_INFO_GATHERED, INFO_SHOWN } state; char *filename = ""; char *password = ""; int show = ALL; int c; while ((c = fz_getopt(argc, argv, "mfispxd:")) != -1) { switch (c) { case 'm': if (show == ALL) show = DIMENSIONS; else show |= DIMENSIONS; break; case 'f': if (show == ALL) show = FONTS; else show |= FONTS; break; case 'i': if (show == ALL) show = IMAGES; else show |= IMAGES; break; case 's': if (show == ALL) show = SHADINGS; else show |= SHADINGS; break; case 'p': if (show == ALL) show = PATTERNS; else show |= PATTERNS; break; case 'x': if (show == ALL) show = XOBJS; else show |= XOBJS; break; case 'd': password = fz_optarg; break; default: infousage(); break; } } if (fz_optind == argc) infousage(); ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); if (!ctx) { fprintf(stderr, "cannot initialise context\n"); exit(1); } state = NO_FILE_OPENED; while (fz_optind < argc) { if (strstr(argv[fz_optind], ".pdf") || strstr(argv[fz_optind], ".PDF")) { if (state == NO_INFO_GATHERED) { showinfo(filename, show, "1-"); closexref(); } closexref(); filename = argv[fz_optind]; printf("%s:\n", filename); xref = pdf_open_document(ctx, filename); if (pdf_needs_password(xref)) if (!pdf_authenticate_password(xref, password)) fz_throw(ctx, "cannot authenticate password: %s", filename); pagecount = pdf_count_pages(xref); showglobalinfo(); state = NO_INFO_GATHERED; } else { showinfo(filename, show, argv[fz_optind]); state = INFO_SHOWN; } fz_optind++; } if (state == NO_INFO_GATHERED) showinfo(filename, show, "1-"); closexref(); fz_free_context(ctx); return 0; }
static void drawbmp(fz_context *ctx, fz_document *doc, fz_page *page, fz_display_list *list, int pagenum, fz_cookie *cookie) { float zoom; fz_matrix ctm; fz_irect ibounds; fz_rect bounds, tbounds; int w, h; fz_device *dev; HDC dc, dc_main; RECT rc; HBRUSH bg_brush; HBITMAP hbmp; BITMAPINFO bmi = { 0 }; int bmp_data_len; char *bmp_data; int as_tga = !strstr(output, ".bmp"); fz_bound_page(doc, page, &bounds); zoom = resolution / 72; fz_pre_scale(fz_rotate(&ctm, rotation), zoom, zoom); tbounds = bounds; fz_round_rect(&ibounds, fz_transform_rect(&tbounds, &ctm)); w = width; h = height; if (res_specified) { fz_round_rect(&ibounds, &tbounds); if (w && ibounds.x1 - ibounds.x0 <= w) w = 0; if (h && ibounds.y1 - ibounds.y0 <= h) h = 0; } if (w || h) { float scalex = w / (tbounds.x1 - tbounds.x0); float scaley = h / (tbounds.y1 - tbounds.y0); fz_matrix scale_mat; if (w == 0) scalex = fit ? 1.0f : scaley; if (h == 0) scaley = fit ? 1.0f : scalex; if (!fit) scalex = scaley = min(scalex, scaley); fz_concat(&ctm, &ctm, fz_scale(&scale_mat, scalex, scaley)); tbounds = bounds; fz_transform_rect(&tbounds, &ctm); } fz_round_rect(&ibounds, &tbounds); fz_rect_from_irect(&tbounds, &ibounds); w = ibounds.x1 - ibounds.x0; h = ibounds.y1 - ibounds.y0; dc_main = GetDC(NULL); dc = CreateCompatibleDC(dc_main); hbmp = CreateCompatibleBitmap(dc_main, w, h); DeleteObject(SelectObject(dc, hbmp)); SetRect(&rc, 0, 0, w, h); bg_brush = CreateSolidBrush(RGB(0xFF,0xFF,0xFF)); FillRect(dc, &rc, bg_brush); DeleteObject(bg_brush); dev = fz_new_gdiplus_device(ctx, dc, &tbounds); if (list) fz_run_display_list(list, dev, &ctm, &tbounds, cookie); else fz_run_page(doc, page, dev, &ctm, cookie); fz_free_device(dev); bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); bmi.bmiHeader.biWidth = w; bmi.bmiHeader.biHeight = as_tga ? -h : h; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = as_tga ? 32 : 24; bmi.bmiHeader.biCompression = BI_RGB; bmp_data_len = as_tga ? w * h * 4 : ((w * 3 + 3) / 4) * 4 * h; bmp_data = fz_malloc(ctx, bmp_data_len); if (!GetDIBits(dc, hbmp, 0, h, bmp_data, &bmi, DIB_RGB_COLORS)) fz_throw(ctx, "cannot draw page %d in PDF file '%s'", pagenum, filename); DeleteDC(dc); ReleaseDC(NULL, dc_main); DeleteObject(hbmp); if (output) { char buf[512]; FILE *f; sprintf(buf, output, pagenum); f = fopen(buf, "wb"); if (!f) fz_throw(ctx, "could not create raster file '%s'", buf); if (as_tga) { fz_pixmap *pix = fz_new_pixmap_with_data(ctx, fz_device_bgr, w, h, bmp_data); fz_write_tga(ctx, pix, buf, 0); fz_drop_pixmap(ctx, pix); } else { BITMAPFILEHEADER bmpfh = { 0 }; static const int one = 1; if (!*(char *)&one) fz_throw(ctx, "rendering to BMP is not supported on big-endian architectures"); bmpfh.bfType = MAKEWORD('B', 'M'); bmpfh.bfOffBits = sizeof(bmpfh) + sizeof(bmi); bmpfh.bfSize = bmpfh.bfOffBits + bmp_data_len; fwrite(&bmpfh, sizeof(bmpfh), 1, f); fwrite(&bmi, sizeof(bmi), 1, f); fwrite(bmp_data, 1, bmp_data_len, f); } fclose(f); } if (showmd5) { fz_pixmap *pix = fz_new_pixmap_with_data(ctx, fz_device_bgr, bmp_data_len / 4 / h, h, bmp_data); unsigned char digest[16]; int i; fz_md5_pixmap(pix, digest); printf(" "); for (i = 0; i < 16; i++) printf("%02x", digest[i]); fz_drop_pixmap(ctx, pix); } fz_free(ctx, bmp_data); }
static void ps_write_header(fz_context *ctx, fz_band_writer *writer_) { ps_band_writer *writer = (ps_band_writer *)writer_; fz_output *out = writer->super.out; int w = writer->super.w; int h = writer->super.h; int n = writer->super.n; int alpha = writer->super.alpha; int xres = writer->super.xres; int yres = writer->super.yres; int pagenum = writer->super.pagenum; int w_points = (w * 72 + (xres>>1)) / xres; int h_points = (h * 72 + (yres>>1)) / yres; float sx = w/(float)w_points; float sy = h/(float)h_points; int err; if (alpha != 0) fz_throw(ctx, FZ_ERROR_GENERIC, "Postscript output cannot have alpha"); writer->super.w = w; writer->super.h = h; writer->super.n = n; err = deflateInit(&writer->stream, Z_DEFAULT_COMPRESSION); if (err != Z_OK) { fz_free(ctx, writer); fz_throw(ctx, FZ_ERROR_GENERIC, "compression error %d", err); } fz_printf(ctx, out, "%%%%Page: %d %d\n", pagenum, pagenum); fz_printf(ctx, out, "%%%%PageBoundingBox: 0 0 %d %d\n", w_points, h_points); fz_printf(ctx, out, "%%%%BeginPageSetup\n"); fz_printf(ctx, out, "<</PageSize [%d %d]>> setpagedevice\n", w_points, h_points); fz_printf(ctx, out, "%%%%EndPageSetup\n\n"); fz_printf(ctx, out, "/DataFile currentfile /FlateDecode filter def\n\n"); switch(n) { case 2: fz_printf(ctx, out, "/DeviceGray setcolorspace\n"); break; case 4: fz_printf(ctx, out, "/DeviceRGB setcolorspace\n"); break; case 5: fz_printf(ctx, out, "/DeviceCMYK setcolorspace\n"); break; default: fz_throw(ctx, FZ_ERROR_GENERIC, "Unexpected colorspace for ps output"); } fz_printf(ctx, out, "<<\n" "/ImageType 1\n" "/Width %d\n" "/Height %d\n" "/ImageMatrix [ %f 0 0 -%f 0 %d ]\n" "/MultipleDataSources false\n" "/DataSource DataFile\n" "/BitsPerComponent 8\n" //"/Decode [0 1]\n" "/Interpolate false\n" ">>\n" "image\n" , w, h, sx, sy, h); }
static void drawpage(fz_context *ctx, fz_document *doc, int pagenum) { fz_page *page; fz_display_list *list = NULL; fz_device *dev = NULL; int start; fz_cookie cookie = { 0 }; int needshot = 0; fz_var(list); fz_var(dev); if (showtime) { start = gettime(); } fz_try(ctx) { page = fz_load_page(doc, pagenum - 1); } fz_catch(ctx) { fz_throw(ctx, "cannot load page %d in file '%s'", pagenum, filename); } if (mujstest_file) { fz_interactive *inter = fz_interact(doc); fz_widget *widget = NULL; if (inter) widget = fz_first_widget(inter, page); if (widget) { fprintf(mujstest_file, "GOTO %d\n", pagenum); needshot = 1; } for (;widget; widget = fz_next_widget(inter, widget)) { fz_rect rect; int w, h, len; int type = fz_widget_get_type(widget); fz_bound_widget(widget, &rect); w = (rect.x1 - rect.x0); h = (rect.y1 - rect.y0); ++mujstest_count; switch (type) { default: fprintf(mujstest_file, "%% UNKNOWN %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); break; case FZ_WIDGET_TYPE_PUSHBUTTON: fprintf(mujstest_file, "%% PUSHBUTTON %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); break; case FZ_WIDGET_TYPE_CHECKBOX: fprintf(mujstest_file, "%% CHECKBOX %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); break; case FZ_WIDGET_TYPE_RADIOBUTTON: fprintf(mujstest_file, "%% RADIOBUTTON %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); break; case FZ_WIDGET_TYPE_TEXT: { int maxlen = fz_text_widget_max_len(inter, widget); int texttype = fz_text_widget_content_type(inter, widget); /* If height is low, assume a single row, and base * the width off that. */ if (h < 10) { w = (w+h-1) / (h ? h : 1); h = 1; } /* Otherwise, if width is low, work off height */ else if (w < 10) { h = (w+h-1) / (w ? w : 1); w = 1; } else { w = (w+9)/10; h = (h+9)/10; } len = w*h; if (len < 2) len = 2; if (len > maxlen) len = maxlen; fprintf(mujstest_file, "%% TEXT %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); switch (texttype) { default: case FZ_WIDGET_CONTENT_UNRESTRAINED: fprintf(mujstest_file, "TEXT %d ", mujstest_count); escape_string(mujstest_file, len-3, lorem); fprintf(mujstest_file, "\n"); break; case FZ_WIDGET_CONTENT_NUMBER: fprintf(mujstest_file, "TEXT %d\n", mujstest_count); break; case FZ_WIDGET_CONTENT_SPECIAL: fprintf(mujstest_file, "TEXT %lld\n", 46702919800LL + mujstest_count); break; case FZ_WIDGET_CONTENT_DATE: fprintf(mujstest_file, "TEXT Jun %d 1979\n", 1 + ((13 + mujstest_count) % 30)); break; case FZ_WIDGET_CONTENT_TIME: ++mujstest_count; fprintf(mujstest_file, "TEXT %02d:%02d\n", ((mujstest_count/60) % 24), mujstest_count % 60); break; } break; } case FZ_WIDGET_TYPE_LISTBOX: fprintf(mujstest_file, "%% LISTBOX %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); break; case FZ_WIDGET_TYPE_COMBOBOX: fprintf(mujstest_file, "%% COMBOBOX %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1); break; } fprintf(mujstest_file, "CLICK %0.2f %0.2f\n", (rect.x0+rect.x1)/2, (rect.y0+rect.y1)/2); } } if (uselist) { fz_try(ctx) { list = fz_new_display_list(ctx); dev = fz_new_list_device(ctx, list); fz_run_page(doc, page, dev, &fz_identity, &cookie); } fz_always(ctx) { fz_free_device(dev); dev = NULL; } fz_catch(ctx) { fz_free_display_list(ctx, list); fz_free_page(doc, page); fz_throw(ctx, "cannot draw page %d in file '%s'", pagenum, filename); } } if (showxml) { fz_try(ctx) { dev = fz_new_trace_device(ctx); fz_printf(out, "<page number=\"%d\">\n", pagenum); if (list) fz_run_display_list(list, dev, &fz_identity, &fz_infinite_rect, &cookie); else fz_run_page(doc, page, dev, &fz_identity, &cookie); fz_printf(out, "</page>\n"); } fz_always(ctx) { fz_free_device(dev); dev = NULL; } fz_catch(ctx) { fz_free_display_list(ctx, list); fz_free_page(doc, page); fz_rethrow(ctx); } } if (showtext) { fz_text_page *text = NULL; fz_var(text); fz_try(ctx) { fz_rect bounds; text = fz_new_text_page(ctx, fz_bound_page(doc, page, &bounds)); dev = fz_new_text_device(ctx, sheet, text); if (list) fz_run_display_list(list, dev, &fz_identity, &fz_infinite_rect, &cookie); else fz_run_page(doc, page, dev, &fz_identity, &cookie); fz_free_device(dev); dev = NULL; if (showtext == TEXT_XML) { fz_print_text_page_xml(ctx, out, text); } else if (showtext == TEXT_HTML) { fz_text_analysis(ctx, sheet, text); fz_print_text_page_html(ctx, out, text); } else if (showtext == TEXT_PLAIN) { fz_print_text_page(ctx, out, text); fz_printf(out, "\f\n"); } } fz_always(ctx) { fz_free_device(dev); dev = NULL; fz_free_text_page(ctx, text); } fz_catch(ctx) { fz_free_display_list(ctx, list); fz_free_page(doc, page); fz_rethrow(ctx); } } if (showmd5 || showtime) printf("page %s %d", filename, pagenum); #ifdef GDI_PLUS_BMP_RENDERER // hack: use -G0 to "enable GDI+" when saving as TGA if (output && (strstr(output, ".bmp") || strstr(output, ".tga") && !gamma_value)) drawbmp(ctx, doc, page, list, pagenum, &cookie); else #endif if (output || showmd5 || showtime) { float zoom; fz_matrix ctm; fz_rect bounds, tbounds; fz_irect ibounds; fz_pixmap *pix = NULL; int w, h; fz_var(pix); fz_bound_page(doc, page, &bounds); zoom = resolution / 72; fz_pre_scale(fz_rotate(&ctm, rotation), zoom, zoom); tbounds = bounds; fz_round_rect(&ibounds, fz_transform_rect(&tbounds, &ctm)); /* Make local copies of our width/height */ w = width; h = height; /* If a resolution is specified, check to see whether w/h are * exceeded; if not, unset them. */ if (res_specified) { int t; t = ibounds.x1 - ibounds.x0; if (w && t <= w) w = 0; t = ibounds.y1 - ibounds.y0; if (h && t <= h) h = 0; } /* Now w or h will be 0 unless they need to be enforced. */ if (w || h) { float scalex = w / (tbounds.x1 - tbounds.x0); float scaley = h / (tbounds.y1 - tbounds.y0); fz_matrix scale_mat; if (fit) { if (w == 0) scalex = 1.0f; if (h == 0) scaley = 1.0f; } else { if (w == 0) scalex = scaley; if (h == 0) scaley = scalex; } if (!fit) { if (scalex > scaley) scalex = scaley; else scaley = scalex; } fz_scale(&scale_mat, scalex, scaley); fz_concat(&ctm, &ctm, &scale_mat); tbounds = bounds; fz_transform_rect(&tbounds, &ctm); } fz_round_rect(&ibounds, &tbounds); fz_rect_from_irect(&tbounds, &ibounds); /* TODO: banded rendering and multi-page ppm */ fz_try(ctx) { pix = fz_new_pixmap_with_bbox(ctx, colorspace, &ibounds); if (savealpha) fz_clear_pixmap(ctx, pix); else fz_clear_pixmap_with_value(ctx, pix, 255); dev = fz_new_draw_device(ctx, pix); if (list) fz_run_display_list(list, dev, &ctm, &tbounds, &cookie); else fz_run_page(doc, page, dev, &ctm, &cookie); fz_free_device(dev); dev = NULL; if (invert) fz_invert_pixmap(ctx, pix); if (gamma_value != 1) fz_gamma_pixmap(ctx, pix, gamma_value); if (savealpha) fz_unmultiply_pixmap(ctx, pix); if (output) { char buf[512]; sprintf(buf, output, pagenum); if (strstr(output, ".pgm") || strstr(output, ".ppm") || strstr(output, ".pnm")) fz_write_pnm(ctx, pix, buf); else if (strstr(output, ".pam")) fz_write_pam(ctx, pix, buf, savealpha); else if (strstr(output, ".png")) fz_write_png(ctx, pix, buf, savealpha); else if (strstr(output, ".pbm")) { fz_bitmap *bit = fz_halftone_pixmap(ctx, pix, NULL); fz_write_pbm(ctx, bit, buf); fz_drop_bitmap(ctx, bit); } /* SumatraPDF: support TGA as output format */ else if (strstr(output, ".tga")) fz_write_tga(ctx, pix, buf, savealpha); } if (showmd5) { unsigned char digest[16]; int i; fz_md5_pixmap(pix, digest); printf(" "); for (i = 0; i < 16; i++) printf("%02x", digest[i]); } } fz_always(ctx) { fz_free_device(dev); dev = NULL; fz_drop_pixmap(ctx, pix); } fz_catch(ctx) { fz_free_display_list(ctx, list); fz_free_page(doc, page); fz_rethrow(ctx); } }
static void savefont(fz_obj *dict, int num, int gen) { fz_error error; char name[1024]; char *subtype; fz_buffer *buf; fz_obj *stream = nil; fz_obj *obj; char *ext = ""; FILE *f; unsigned char *p; char *fontname = "font"; obj = fz_dictgets(dict, "FontName"); if (obj) fontname = fz_toname(obj); obj = fz_dictgets(dict, "FontFile"); if (obj) { stream = obj; ext = "pfa"; } obj = fz_dictgets(dict, "FontFile2"); if (obj) { stream = obj; ext = "ttf"; } obj = fz_dictgets(dict, "FontFile3"); if (obj) { stream = obj; obj = fz_dictgets(obj, "Subtype"); if (obj && !fz_isname(obj)) die(fz_throw("Invalid font descriptor subtype")); subtype = fz_toname(obj); if (!strcmp(subtype, "Type1C")) ext = "cff"; else if (!strcmp(subtype, "CIDFontType0C")) ext = "cid"; else die(fz_throw("Unhandled font type '%s'", subtype)); } if (!stream) { fz_warn("Unhandled font type"); return; } error = fz_newbuffer(&buf, 0); if (error) die(error); error = pdf_loadstream(&buf, xref, fz_tonum(stream), fz_togen(stream)); if (error) die(error); sprintf(name, "%s-%04d.%s", fontname, num, ext); f = fopen(name, "wb"); if (f == NULL) die(fz_throw("Error creating image file")); for (p = buf->rp; p < buf->wp; p ++) fprintf(f, "%c", *p); if (fclose(f) < 0) die(fz_throw("Error closing image file")); fz_dropbuffer(buf); }
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) { fz_rethrow_if(ctx, FZ_ERROR_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; }
static void saveimage(fz_obj *obj, int num, int gen) { pdf_image *img = nil; fz_obj *ref; fz_error error; fz_pixmap *pix; char name[1024]; FILE *f; int bpc; int w; int h; int n; int x; int y; error = fz_newindirect(&ref, num, gen, xref); if (error) die(error); error = pdf_newstore(&xref->store); if (error) die(error); error = pdf_loadimage(&img, xref, ref); if (error) die(error); n = img->super.n; w = img->super.w; h = img->super.h; bpc = img->bpc; error = fz_newpixmap(&pix, 0, 0, w, h, n + 1); if (error) die(error); error = img->super.loadtile(&img->super, pix); if (error) die(error); if (bpc == 1 && n == 0) { fz_pixmap *temp; error = fz_newpixmap(&temp, pix->x, pix->y, pix->w, pix->h, pdf_devicergb->n + 1); if (error) die(error); for (y = 0; y < pix->h; y++) for (x = 0; x < pix->w; x++) { int pixel = y * pix->w + x; temp->samples[pixel * temp->n + 0] = 255; temp->samples[pixel * temp->n + 1] = pix->samples[pixel]; temp->samples[pixel * temp->n + 2] = pix->samples[pixel]; temp->samples[pixel * temp->n + 3] = pix->samples[pixel]; } fz_droppixmap(pix); pix = temp; } if (img->super.cs && strcmp(img->super.cs->name, "DeviceRGB")) { fz_pixmap *temp; error = fz_newpixmap(&temp, pix->x, pix->y, pix->w, pix->h, pdf_devicergb->n + 1); if (error) die(error); fz_convertpixmap(img->super.cs, pix, pdf_devicergb, temp); fz_droppixmap(pix); pix = temp; } sprintf(name, "img-%04d.pnm", num); f = fopen(name, "wb"); if (f == NULL) die(fz_throw("Error creating image file")); fprintf(f, "P6\n%d %d\n%d\n", w, h, 255); for (y = 0; y < pix->h; y++) for (x = 0; x < pix->w; x++) { fz_sample *sample = &pix->samples[(y * pix->w + x) * (pdf_devicergb->n + 1)]; unsigned char r = sample[1]; unsigned char g = sample[2]; unsigned char b = sample[3]; fprintf(f, "%c%c%c", r, g, b); } if (fclose(f) < 0) die(fz_throw("Error closing image file")); fz_droppixmap(pix); pdf_dropstore(xref->store); xref->store = nil; fz_dropimage(&img->super); fz_dropobj(ref); }
pdf_annot * pdf_create_annot(fz_context *ctx, pdf_page *page, enum pdf_annot_type type) { pdf_annot *annot = NULL; pdf_document *doc = page->doc; pdf_obj *annot_obj = pdf_new_dict(ctx, doc, 0); pdf_obj *ind_obj = NULL; fz_var(annot); fz_var(ind_obj); fz_try(ctx) { int ind_obj_num; const char *type_str; pdf_obj *annot_arr; type_str = pdf_string_from_annot_type(ctx, type); if (type == PDF_ANNOT_UNKNOWN) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create unknown annotation"); annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots)); if (annot_arr == NULL) { annot_arr = pdf_new_array(ctx, doc, 0); pdf_dict_put_drop(ctx, page->obj, PDF_NAME(Annots), annot_arr); } pdf_dict_put(ctx, annot_obj, PDF_NAME(Type), PDF_NAME(Annot)); pdf_dict_put_name(ctx, annot_obj, PDF_NAME(Subtype), type_str); /* Make printable as default */ pdf_dict_put_int(ctx, annot_obj, PDF_NAME(F), PDF_ANNOT_IS_PRINT); /* Both annotation object and annotation structure are now created. Insert the object in the hierarchy and the structure in the page's array. */ ind_obj_num = pdf_create_object(ctx, doc); pdf_update_object(ctx, doc, ind_obj_num, annot_obj); ind_obj = pdf_new_indirect(ctx, doc, ind_obj_num, 0); pdf_array_push(ctx, annot_arr, ind_obj); annot = pdf_new_annot(ctx, page, ind_obj); annot->ap = NULL; /* Linking must be done after any call that might throw because pdf_drop_annots below actually frees a list. Put the new annot at the end of the list, so that it will be drawn last. */ *page->annot_tailp = annot; page->annot_tailp = &annot->next; doc->dirty = 1; } fz_always(ctx) { pdf_drop_obj(ctx, annot_obj); pdf_drop_obj(ctx, ind_obj); } fz_catch(ctx) { pdf_drop_annots(ctx, annot); fz_rethrow(ctx); } return annot; }
static void drawbmp(fz_context *ctx, fz_document *doc, fz_page *page, fz_display_list *list, int pagenum) { float zoom; fz_matrix ctm; fz_bbox bbox; fz_rect bounds, bounds2; int w, h; fz_device *dev; HDC dc, dc_main; RECT rc; HBRUSH bg_brush; HBITMAP hbmp; BITMAPINFO bmi = { 0 }; int bmp_data_len; char *bmp_data; bounds = fz_bound_page(doc, page); zoom = resolution / 72; ctm = fz_scale(zoom, zoom); ctm = fz_concat(ctm, fz_rotate(rotation)); bounds2 = fz_transform_rect(ctm, bounds); w = width; h = height; if (res_specified) { bbox = fz_round_rect(bounds2); if (w && bbox.x1 - bbox.x0 <= w) w = 0; if (h && bbox.y1 - bbox.y0 <= h) h = 0; } if (w || h) { float scalex = w / (bounds2.x1 - bounds2.x0); float scaley = h / (bounds2.y1 - bounds2.y0); if (w == 0) scalex = fit ? 1.0f : scaley; if (h == 0) scaley = fit ? 1.0f : scalex; if (!fit) scalex = scaley = min(scalex, scaley); ctm = fz_concat(ctm, fz_scale(scalex, scaley)); bounds2 = fz_transform_rect(ctm, bounds); } bbox = fz_round_rect(bounds2); w = bbox.x1 - bbox.x0; h = bbox.y1 - bbox.y0; dc_main = GetDC(NULL); dc = CreateCompatibleDC(dc_main); hbmp = CreateCompatibleBitmap(dc_main, w, h); DeleteObject(SelectObject(dc, hbmp)); SetRect(&rc, 0, 0, w, h); bg_brush = CreateSolidBrush(RGB(0xFF,0xFF,0xFF)); FillRect(dc, &rc, bg_brush); DeleteObject(bg_brush); dev = fz_new_gdiplus_device(ctx, dc, bbox); if (list) fz_run_display_list(list, dev, ctm, bbox, NULL); else fz_run_page(doc, page, dev, ctm, NULL); fz_free_device(dev); bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); bmi.bmiHeader.biWidth = w; bmi.bmiHeader.biHeight = h; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 24; bmi.bmiHeader.biCompression = BI_RGB; bmp_data_len = ((w * 3 + 3) / 4) * 4 * h; bmp_data = fz_malloc(ctx, bmp_data_len + 1); if (!GetDIBits(dc, hbmp, 0, h, bmp_data, &bmi, DIB_RGB_COLORS)) fz_throw(ctx, "cannot draw page %d in PDF file '%s'", pagenum, filename); DeleteDC(dc); ReleaseDC(NULL, dc_main); DeleteObject(hbmp); if (output) { char buf[512]; FILE *f; sprintf(buf, output, pagenum); f = fopen(buf, "wb"); if (!f) fz_throw(ctx, "could not create raster file '%s'", buf); if (strstr(output, ".bmp")) { BITMAPFILEHEADER bmpfh = { 0 }; static const int one = 1; if (!*(char *)&one) fz_throw(ctx, "rendering to BMP is not supported on big-endian architectures"); bmpfh.bfType = MAKEWORD('B', 'M'); bmpfh.bfOffBits = sizeof(bmpfh) + sizeof(bmi); bmpfh.bfSize = bmpfh.bfOffBits + bmp_data_len; fwrite(&bmpfh, sizeof(bmpfh), 1, f); fwrite(&bmi, sizeof(bmi), 1, f); fwrite(bmp_data, 1, bmp_data_len, f); } else { unsigned short width = w, height = h, k; fwrite("\0\0\x0A\0\0\0\0\0\0\0\0\0", 1, 12, f); putc(width & 0xFF, f); putc((width >> 8) & 0xFF, f); putc(height & 0xFF, f); putc((height >> 8) & 0xFF, f); fwrite("\x18\0", 1, 2, f); for (k = 0; k < height; k++) { int i, j; char *line = bmp_data + bmp_data_len / h * k; for (i = 0, j = 1; i < width; i += j, j = 1) { #define memeq3(a, b) (*(WORD *)(a) == *(WORD *)(b) && (a)[2] == (b)[2]) for (; i + j < width && j < 128 && memeq3(line + i * 3, line + (i + j) * 3); j++); if (j > 1) { putc(j - 1 + 128, f); fwrite(line + i * 3, 1, 3, f); } else { for (; i + j < width && j < 128 && !memeq3(line + (i + j - 1) * 3, line + (i + j) * 3) != 0; j++); putc(j - 1, f); fwrite(line + i * 3, 1, j * 3, f); } #undef memeq3 } } fwrite("\0\0\0\0\0\0\0\0TRUEVISION-XFILE.\0", 1, 26, f); } fclose(f); } if (showmd5) { fz_pixmap *pix = fz_new_pixmap_with_data(ctx, fz_device_rgb, bmp_data_len / 4 / h, h, bmp_data); unsigned char digest[16]; int i; fz_md5_pixmap(pix, digest); printf(" "); for (i = 0; i < 16; i++) printf("%02x", digest[i]); fz_drop_pixmap(ctx, pix); } fz_free(ctx, bmp_data); }
static int next_sgilog16(fz_context *ctx, fz_stream *stm, size_t max) { fz_sgilog16 *state = stm->state; uint16_t *p; uint16_t *ep; uint8_t *q; int shift; (void)max; if (state->run < 0) return EOF; memset(state->temp, 0, state->w * sizeof(uint16_t)); for (shift = 8; shift >= 0; shift -= 8) { p = state->temp; ep = p + state->w; while (p < ep) { if (state->n == 0) { state->run = fz_read_byte(ctx, state->chain); if (state->run < 0) { state->run = -1; fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of data in run length decode"); } if (state->run < 128) state->n = state->run; else { state->n = state->run - 126; state->c = fz_read_byte(ctx, state->chain); if (state->c < 0) { state->run = -1; fz_throw(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(ctx, state->chain); if (c < 0) { state->run = -1; fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of data in run length decode"); } *p++ |= c<<shift; state->n--; } } else { while (p < ep && state->n) { *p++ |= state->c<<shift; state->n--; } } } } p = state->temp; q = (uint8_t *)p; ep = p + state->w; while (p < ep) { *q++ = sgilog16val(ctx, *p++); } stm->rp = (uint8_t *)(state->temp); stm->wp = q; stm->pos += q - stm->rp; if (q == stm->rp) return EOF; return *stm->rp++; }
pdf_obj * pdf_parse_array(fz_context *ctx, pdf_document *doc, fz_stream *file, pdf_lexbuf *buf) { pdf_obj *ary = NULL; pdf_obj *obj = NULL; int a = 0, b = 0, n = 0; pdf_token tok; pdf_obj *op = NULL; fz_var(obj); ary = pdf_new_array(ctx, doc, 4); fz_try(ctx) { while (1) { tok = pdf_lex(ctx, file, buf); if (tok != PDF_TOK_INT && tok != PDF_TOK_R) { if (n > 0) { obj = pdf_new_int(ctx, doc, a); pdf_array_push(ctx, ary, obj); pdf_drop_obj(ctx, obj); obj = NULL; } if (n > 1) { obj = pdf_new_int(ctx, doc, b); pdf_array_push(ctx, ary, obj); pdf_drop_obj(ctx, obj); obj = NULL; } n = 0; } if (tok == PDF_TOK_INT && n == 2) { obj = pdf_new_int(ctx, doc, a); pdf_array_push(ctx, ary, obj); pdf_drop_obj(ctx, obj); obj = NULL; a = b; n --; } switch (tok) { case PDF_TOK_CLOSE_ARRAY: op = ary; goto end; case PDF_TOK_INT: if (n == 0) a = buf->i; if (n == 1) b = buf->i; n ++; break; case PDF_TOK_R: if (n != 2) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot parse indirect reference in array"); obj = pdf_new_indirect(ctx, doc, a, b); pdf_array_push(ctx, ary, obj); pdf_drop_obj(ctx, obj); obj = NULL; n = 0; break; case PDF_TOK_OPEN_ARRAY: obj = pdf_parse_array(ctx, doc, file, buf); pdf_array_push(ctx, ary, obj); pdf_drop_obj(ctx, obj); obj = NULL; break; case PDF_TOK_OPEN_DICT: obj = pdf_parse_dict(ctx, doc, file, buf); pdf_array_push(ctx, ary, obj); pdf_drop_obj(ctx, obj); obj = NULL; break; case PDF_TOK_NAME: obj = pdf_new_name(ctx, doc, buf->scratch); pdf_array_push(ctx, ary, obj); pdf_drop_obj(ctx, obj); obj = NULL; break; case PDF_TOK_REAL: obj = pdf_new_real(ctx, doc, buf->f); pdf_array_push(ctx, ary, obj); pdf_drop_obj(ctx, obj); obj = NULL; break; case PDF_TOK_STRING: obj = pdf_new_string(ctx, doc, buf->scratch, buf->len); pdf_array_push(ctx, ary, obj); pdf_drop_obj(ctx, obj); obj = NULL; break; case PDF_TOK_TRUE: obj = pdf_new_bool(ctx, doc, 1); pdf_array_push(ctx, ary, obj); pdf_drop_obj(ctx, obj); obj = NULL; break; case PDF_TOK_FALSE: obj = pdf_new_bool(ctx, doc, 0); pdf_array_push(ctx, ary, obj); pdf_drop_obj(ctx, obj); obj = NULL; break; case PDF_TOK_NULL: obj = pdf_new_null(ctx, doc); pdf_array_push(ctx, ary, obj); pdf_drop_obj(ctx, obj); obj = NULL; break; default: fz_throw(ctx, FZ_ERROR_GENERIC, "cannot parse token in array"); } } end: {} } fz_catch(ctx) { pdf_drop_obj(ctx, obj); pdf_drop_obj(ctx, ary); fz_rethrow_message(ctx, "cannot parse array"); } return op; }
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_event_mgr_t evtmgr; opj_dparameters_t params; opj_dinfo_t *info; opj_cio_t *cio; opj_image_t *jpx; fz_colorspace *colorspace; unsigned char *p; int format; int a, n, w, h, depth, sgnd; int x, y, k, v; if (size < 2) fz_throw(ctx, "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 = CODEC_J2K; else format = CODEC_JP2; memset(&evtmgr, 0, sizeof(evtmgr)); evtmgr.error_handler = fz_opj_error_callback; evtmgr.warning_handler = fz_opj_warning_callback; evtmgr.info_handler = fz_opj_info_callback; opj_set_default_decoder_parameters(¶ms); if (indexed) params.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG; info = opj_create_decompress(format); opj_set_event_mgr((opj_common_ptr)info, &evtmgr, ctx); opj_setup_decoder(info, ¶ms); cio = opj_cio_open((opj_common_ptr)info, data, size); jpx = opj_decode(info, cio); opj_cio_close(cio); opj_destroy_decompress(info); if (!jpx) fz_throw(ctx, "opj_decode failed"); for (k = 1; k < jpx->numcomps; k++) { if (jpx->comps[k].w != jpx->comps[0].w) { opj_image_destroy(jpx); fz_throw(ctx, "image components have different width"); } if (jpx->comps[k].h != jpx->comps[0].h) { opj_image_destroy(jpx); fz_throw(ctx, "image components have different height"); } if (jpx->comps[k].prec != jpx->comps[0].prec) { opj_image_destroy(jpx); fz_throw(ctx, "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 == CLRSPC_SRGB && n == 4) { n = 3; a = 1; } else if (jpx->color_space == 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; break; case 3: colorspace = fz_device_rgb; break; case 4: colorspace = fz_device_cmyk; break; } } fz_try(ctx) { img = fz_new_pixmap(ctx, colorspace, w, h); } fz_catch(ctx) { opj_image_destroy(jpx); fz_throw(ctx, "out of memory"); } 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, 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; }
pdf_obj * pdf_parse_dict(fz_context *ctx, pdf_document *doc, fz_stream *file, pdf_lexbuf *buf) { pdf_obj *dict; pdf_obj *key = NULL; pdf_obj *val = NULL; pdf_token tok; int a, b; dict = pdf_new_dict(ctx, doc, 8); fz_var(key); fz_var(val); fz_try(ctx) { while (1) { tok = pdf_lex(ctx, file, buf); skip: if (tok == PDF_TOK_CLOSE_DICT) break; /* for BI .. ID .. EI in content streams */ if (tok == PDF_TOK_KEYWORD && !strcmp(buf->scratch, "ID")) break; if (tok != PDF_TOK_NAME) fz_throw(ctx, FZ_ERROR_GENERIC, "invalid key in dict"); key = pdf_new_name(ctx, doc, buf->scratch); tok = pdf_lex(ctx, file, buf); switch (tok) { case PDF_TOK_OPEN_ARRAY: val = pdf_parse_array(ctx, doc, file, buf); break; case PDF_TOK_OPEN_DICT: val = pdf_parse_dict(ctx, doc, file, buf); break; case PDF_TOK_NAME: val = pdf_new_name(ctx, doc, buf->scratch); break; case PDF_TOK_REAL: val = pdf_new_real(ctx, doc, buf->f); break; case PDF_TOK_STRING: val = pdf_new_string(ctx, doc, buf->scratch, buf->len); break; case PDF_TOK_TRUE: val = pdf_new_bool(ctx, doc, 1); break; case PDF_TOK_FALSE: val = pdf_new_bool(ctx, doc, 0); break; case PDF_TOK_NULL: val = pdf_new_null(ctx, doc); break; case PDF_TOK_INT: /* 64-bit to allow for numbers > INT_MAX and overflow */ a = buf->i; tok = pdf_lex(ctx, file, buf); if (tok == PDF_TOK_CLOSE_DICT || tok == PDF_TOK_NAME || (tok == PDF_TOK_KEYWORD && !strcmp(buf->scratch, "ID"))) { val = pdf_new_int(ctx, doc, a); pdf_dict_put(ctx, dict, key, val); pdf_drop_obj(ctx, val); val = NULL; pdf_drop_obj(ctx, key); key = NULL; goto skip; } if (tok == PDF_TOK_INT) { b = buf->i; tok = pdf_lex(ctx, file, buf); if (tok == PDF_TOK_R) { val = pdf_new_indirect(ctx, doc, a, b); break; } } fz_throw(ctx, FZ_ERROR_GENERIC, "invalid indirect reference in dict"); default: fz_throw(ctx, FZ_ERROR_GENERIC, "unknown token in dict"); } pdf_dict_put(ctx, dict, key, val); pdf_drop_obj(ctx, val); val = NULL; pdf_drop_obj(ctx, key); key = NULL; } } fz_catch(ctx) { pdf_drop_obj(ctx, dict); pdf_drop_obj(ctx, key); pdf_drop_obj(ctx, val); fz_rethrow_message(ctx, "cannot parse dict"); } return dict; }
static fz_error parsedict(fz_obj **obj, pdf_xref *xref, char **sp, struct vap *v) { fz_error error = fz_okay; fz_obj *dict = nil; fz_obj *key = nil; fz_obj *val = nil; char *s = *sp; error = fz_newdict(&dict, 8); if (error) return fz_rethrow(error, "cannot create dict"); s += 2; /* skip "<<" */ while (*s) { skipwhite(&s); /* end-of-dict marker >> */ if (*s == '>') { s ++; if (*s == '>') { s ++; break; } error = fz_throw("malformed >> marker"); goto cleanup; } /* non-name as key, bail */ if (*s != '/') { error = fz_throw("key is not a name"); goto cleanup; } error = parsename(&key, &s); if (error) { error = fz_rethrow(error, "cannot parse key"); goto cleanup; } skipwhite(&s); error = parseobj(&val, xref, &s, v); if (error) { error = fz_rethrow(error, "cannot parse value"); goto cleanup; } error = fz_dictput(dict, key, val); if (error) { error = fz_rethrow(error, "cannot insert dict entry"); goto cleanup; } fz_dropobj(val); val = nil; fz_dropobj(key); key = nil; } *obj = dict; *sp = s; return fz_okay; cleanup: if (val) fz_dropobj(val); if (key) fz_dropobj(key); if (dict) fz_dropobj(dict); *obj = nil; *sp = s; return error; /* already rethrown */ }
pdf_obj * pdf_parse_ind_obj(fz_context *ctx, pdf_document *doc, fz_stream *file, pdf_lexbuf *buf, int *onum, int *ogen, int *ostmofs, int *try_repair) { pdf_obj *obj = NULL; int num = 0, gen = 0, stm_ofs; pdf_token tok; int a, b; fz_var(obj); tok = pdf_lex(ctx, file, buf); if (tok != PDF_TOK_INT) { if (try_repair) *try_repair = 1; fz_throw(ctx, FZ_ERROR_GENERIC, "expected object number"); } num = buf->i; tok = pdf_lex(ctx, file, buf); if (tok != PDF_TOK_INT) { if (try_repair) *try_repair = 1; fz_throw(ctx, FZ_ERROR_GENERIC, "expected generation number (%d ? obj)", num); } gen = buf->i; tok = pdf_lex(ctx, file, buf); if (tok != PDF_TOK_OBJ) { if (try_repair) *try_repair = 1; fz_throw(ctx, FZ_ERROR_GENERIC, "expected 'obj' keyword (%d %d ?)", num, gen); } tok = pdf_lex(ctx, file, buf); switch (tok) { case PDF_TOK_OPEN_ARRAY: obj = pdf_parse_array(ctx, doc, file, buf); break; case PDF_TOK_OPEN_DICT: obj = pdf_parse_dict(ctx, doc, file, buf); break; case PDF_TOK_NAME: obj = pdf_new_name(ctx, doc, buf->scratch); break; case PDF_TOK_REAL: obj = pdf_new_real(ctx, doc, buf->f); break; case PDF_TOK_STRING: obj = pdf_new_string(ctx, doc, buf->scratch, buf->len); break; case PDF_TOK_TRUE: obj = pdf_new_bool(ctx, doc, 1); break; case PDF_TOK_FALSE: obj = pdf_new_bool(ctx, doc, 0); break; case PDF_TOK_NULL: obj = pdf_new_null(ctx, doc); break; case PDF_TOK_INT: a = buf->i; tok = pdf_lex(ctx, file, buf); if (tok == PDF_TOK_STREAM || tok == PDF_TOK_ENDOBJ) { obj = pdf_new_int(ctx, doc, a); goto skip; } if (tok == PDF_TOK_INT) { b = buf->i; tok = pdf_lex(ctx, file, buf); if (tok == PDF_TOK_R) { obj = pdf_new_indirect(ctx, doc, a, b); break; } } fz_throw(ctx, FZ_ERROR_GENERIC, "expected 'R' keyword (%d %d R)", num, gen); case PDF_TOK_ENDOBJ: obj = pdf_new_null(ctx, doc); goto skip; default: fz_throw(ctx, FZ_ERROR_GENERIC, "syntax error in object (%d %d R)", num, gen); } fz_try(ctx) { tok = pdf_lex(ctx, file, buf); } fz_catch(ctx) { pdf_drop_obj(ctx, obj); fz_rethrow_message(ctx, "cannot parse indirect object (%d %d R)", num, gen); } skip: if (tok == PDF_TOK_STREAM) { int c = fz_read_byte(ctx, file); while (c == ' ') c = fz_read_byte(ctx, file); if (c == '\r') { c = fz_peek_byte(ctx, file); if (c != '\n') fz_warn(ctx, "line feed missing after stream begin marker (%d %d R)", num, gen); else fz_read_byte(ctx, file); } stm_ofs = fz_tell(ctx, file); } else if (tok == PDF_TOK_ENDOBJ) { stm_ofs = 0; } else { fz_warn(ctx, "expected 'endobj' or 'stream' keyword (%d %d R)", num, gen); stm_ofs = 0; } if (onum) *onum = num; if (ogen) *ogen = gen; if (ostmofs) *ostmofs = stm_ofs; return obj; }
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 pdf_font_desc * load_cid_font(pdf_document *xref, pdf_obj *dict, pdf_obj *encoding, pdf_obj *to_unicode) { pdf_obj *widths; pdf_obj *descriptor; pdf_font_desc *fontdesc; FT_Face face; int kind; char collection[256]; char *basefont; int i, k, fterr; pdf_obj *obj; int dw; fz_context *ctx = xref->ctx; fz_var(fontdesc); fz_try(ctx) { /* Get font name and CID collection */ basefont = pdf_to_name(pdf_dict_gets(dict, "BaseFont")); { pdf_obj *cidinfo; char tmpstr[64]; int tmplen; cidinfo = pdf_dict_gets(dict, "CIDSystemInfo"); if (!cidinfo) fz_throw(ctx, "cid font is missing info"); obj = pdf_dict_gets(cidinfo, "Registry"); tmplen = MIN(sizeof tmpstr - 1, pdf_to_str_len(obj)); memcpy(tmpstr, pdf_to_str_buf(obj), tmplen); tmpstr[tmplen] = '\0'; fz_strlcpy(collection, tmpstr, sizeof collection); fz_strlcat(collection, "-", sizeof collection); obj = pdf_dict_gets(cidinfo, "Ordering"); tmplen = MIN(sizeof tmpstr - 1, pdf_to_str_len(obj)); memcpy(tmpstr, pdf_to_str_buf(obj), tmplen); tmpstr[tmplen] = '\0'; fz_strlcat(collection, tmpstr, sizeof collection); } /* Load font file */ fontdesc = pdf_new_font_desc(ctx); descriptor = pdf_dict_gets(dict, "FontDescriptor"); if (!descriptor) fz_throw(ctx, "syntaxerror: missing font descriptor"); pdf_load_font_descriptor(fontdesc, xref, descriptor, collection, basefont); face = fontdesc->font->ft_face; kind = ft_kind(face); /* Encoding */ if (pdf_is_name(encoding)) { if (!strcmp(pdf_to_name(encoding), "Identity-H")) fontdesc->encoding = pdf_new_identity_cmap(ctx, 0, 2); else if (!strcmp(pdf_to_name(encoding), "Identity-V")) fontdesc->encoding = pdf_new_identity_cmap(ctx, 1, 2); else fontdesc->encoding = pdf_load_system_cmap(ctx, pdf_to_name(encoding)); } else if (pdf_is_indirect(encoding)) { fontdesc->encoding = pdf_load_embedded_cmap(xref, encoding); } else { fz_throw(ctx, "syntaxerror: font missing encoding"); } fontdesc->size += pdf_cmap_size(ctx, fontdesc->encoding); pdf_set_font_wmode(ctx, fontdesc, pdf_cmap_wmode(ctx, fontdesc->encoding)); if (kind == TRUETYPE) { pdf_obj *cidtogidmap; cidtogidmap = pdf_dict_gets(dict, "CIDToGIDMap"); if (pdf_is_indirect(cidtogidmap)) { fz_buffer *buf; buf = pdf_load_stream(xref, pdf_to_num(cidtogidmap), pdf_to_gen(cidtogidmap)); fontdesc->cid_to_gid_len = (buf->len) / 2; fontdesc->cid_to_gid = fz_malloc_array(ctx, fontdesc->cid_to_gid_len, sizeof(unsigned short)); fontdesc->size += fontdesc->cid_to_gid_len * sizeof(unsigned short); for (i = 0; i < fontdesc->cid_to_gid_len; i++) fontdesc->cid_to_gid[i] = (buf->data[i * 2] << 8) + buf->data[i * 2 + 1]; fz_drop_buffer(ctx, buf); } /* if truetype font is external, cidtogidmap should not be identity */ /* so we map from cid to unicode and then map that through the (3 1) */ /* unicode cmap to get a glyph id */ else if (fontdesc->font->ft_substitute) { fterr = FT_Select_Charmap(face, ft_encoding_unicode); if (fterr) { fz_throw(ctx, "fonterror: no unicode cmap when emulating CID font: %s", ft_error_string(fterr)); } if (!strcmp(collection, "Adobe-CNS1")) fontdesc->to_ttf_cmap = pdf_load_system_cmap(ctx, "Adobe-CNS1-UCS2"); else if (!strcmp(collection, "Adobe-GB1")) fontdesc->to_ttf_cmap = pdf_load_system_cmap(ctx, "Adobe-GB1-UCS2"); else if (!strcmp(collection, "Adobe-Japan1")) fontdesc->to_ttf_cmap = pdf_load_system_cmap(ctx, "Adobe-Japan1-UCS2"); else if (!strcmp(collection, "Adobe-Japan2")) fontdesc->to_ttf_cmap = pdf_load_system_cmap(ctx, "Adobe-Japan2-UCS2"); else if (!strcmp(collection, "Adobe-Korea1")) fontdesc->to_ttf_cmap = pdf_load_system_cmap(ctx, "Adobe-Korea1-UCS2"); /* RJW: "cannot load system cmap %s", collection */ } } pdf_load_to_unicode(xref, fontdesc, NULL, collection, to_unicode); /* RJW: "cannot load to_unicode" */ /* Horizontal */ dw = 1000; obj = pdf_dict_gets(dict, "DW"); if (obj) dw = pdf_to_int(obj); pdf_set_default_hmtx(ctx, fontdesc, dw); widths = pdf_dict_gets(dict, "W"); if (widths) { int c0, c1, w, n, m; n = pdf_array_len(widths); for (i = 0; i < n; ) { c0 = pdf_to_int(pdf_array_get(widths, i)); obj = pdf_array_get(widths, i + 1); if (pdf_is_array(obj)) { m = pdf_array_len(obj); for (k = 0; k < m; k++) { w = pdf_to_int(pdf_array_get(obj, k)); pdf_add_hmtx(ctx, fontdesc, c0 + k, c0 + k, w); } i += 2; } else { c1 = pdf_to_int(obj); w = pdf_to_int(pdf_array_get(widths, i + 2)); pdf_add_hmtx(ctx, fontdesc, c0, c1, w); i += 3; } } } pdf_end_hmtx(ctx, fontdesc); /* Vertical */ if (pdf_cmap_wmode(ctx, fontdesc->encoding) == 1) { int dw2y = 880; int dw2w = -1000; obj = pdf_dict_gets(dict, "DW2"); if (obj) { dw2y = pdf_to_int(pdf_array_get(obj, 0)); dw2w = pdf_to_int(pdf_array_get(obj, 1)); } pdf_set_default_vmtx(ctx, fontdesc, dw2y, dw2w); widths = pdf_dict_gets(dict, "W2"); if (widths) { int c0, c1, w, x, y, n; n = pdf_array_len(widths); for (i = 0; i < n; ) { c0 = pdf_to_int(pdf_array_get(widths, i)); obj = pdf_array_get(widths, i + 1); if (pdf_is_array(obj)) { int m = pdf_array_len(obj); for (k = 0; k * 3 < m; k ++) { w = pdf_to_int(pdf_array_get(obj, k * 3 + 0)); x = pdf_to_int(pdf_array_get(obj, k * 3 + 1)); y = pdf_to_int(pdf_array_get(obj, k * 3 + 2)); pdf_add_vmtx(ctx, fontdesc, c0 + k, c0 + k, x, y, w); } i += 2; } else { c1 = pdf_to_int(obj); w = pdf_to_int(pdf_array_get(widths, i + 2)); x = pdf_to_int(pdf_array_get(widths, i + 3)); y = pdf_to_int(pdf_array_get(widths, i + 4)); pdf_add_vmtx(ctx, fontdesc, c0, c1, x, y, w); i += 5; } } } pdf_end_vmtx(ctx, fontdesc); } } fz_catch(ctx) { pdf_drop_font(ctx, fontdesc); fz_throw(ctx, "cannot load cid font (%d %d R)", pdf_to_num(dict), pdf_to_gen(dict)); } return fontdesc; }