diff options
author | Tor Andersson <tor@ghostscript.com> | 2004-11-18 05:48:01 +0100 |
---|---|---|
committer | Tor Andersson <tor@ghostscript.com> | 2004-11-18 05:48:01 +0100 |
commit | 2724ed5685e87dd6491df510de384ffe03223991 (patch) | |
tree | 679fa99f06fceef44bb6103f2ddb672c942e5e31 | |
parent | 7f5094e6980bf3193eee2de2053f54ba64088d90 (diff) | |
download | mupdf-2724ed5685e87dd6491df510de384ffe03223991.tar.xz |
cleaned up filter/buffer refcounting and stream error handling
-rw-r--r-- | mupdf/cmap.c | 65 | ||||
-rw-r--r-- | mupdf/crypt.c | 27 | ||||
-rw-r--r-- | mupdf/doctor.c | 13 | ||||
-rw-r--r-- | mupdf/lex.c | 4 | ||||
-rw-r--r-- | mupdf/repair.c | 3 | ||||
-rw-r--r-- | mupdf/save.c | 7 | ||||
-rw-r--r-- | mupdf/stream.c | 279 | ||||
-rw-r--r-- | mupdf/xref.c | 22 |
8 files changed, 310 insertions, 110 deletions
diff --git a/mupdf/cmap.c b/mupdf/cmap.c index 33c3a4ab..a5768748 100644 --- a/mupdf/cmap.c +++ b/mupdf/cmap.c @@ -280,7 +280,6 @@ pdf_parsecmap(fz_cmap **cmapp, fz_file *file) error = fz_newcmap(&cmap); if (error) return error; - *cmapp = cmap; strcpy(key, ".notdef"); @@ -362,14 +361,17 @@ pdf_parsecmap(fz_cmap **cmapp, fz_file *file) if (error) goto cleanup; + *cmapp = cmap; return nil; cleanup: fz_dropcmap(cmap); - *cmapp = nil; return error; } +/* + * Load CMap stream in PDF file + */ fz_error * pdf_loadembeddedcmap(fz_cmap **cmapp, pdf_xref *xref, fz_obj *stmref) { @@ -384,11 +386,6 @@ pdf_loadembeddedcmap(fz_cmap **cmapp, pdf_xref *xref, fz_obj *stmref) if (error) return error; -printf(" embedded cmap: "); -fz_debugobj(stmobj); -printf("\n"); -fflush(stdout); - error = pdf_openstream(xref, fz_tonum(stmref), fz_togen(stmref)); if (error) goto cleanup; @@ -406,34 +403,36 @@ fflush(stdout); obj = fz_dictgets(stmobj, "UseCMap"); if (fz_isname(obj)) { - printf(" usecmap predefined: %s\n", fz_toname(obj)); error = pdf_loadsystemcmap(&usecmap, fz_toname(obj)); if (error) goto cleanup; fz_setusecmap(cmap, usecmap); + fz_dropcmap(usecmap); } else if (fz_isindirect(obj)) { - printf(" usecmap recursive pdf obj\n"); error = pdf_loadembeddedcmap(&usecmap, xref, obj); if (error) goto cleanup; fz_setusecmap(cmap, usecmap); + fz_dropcmap(usecmap); } - *cmapp = cmap; - fz_dropobj(stmobj); + + *cmapp = cmap; return nil; cleanup: if (cmap) fz_dropcmap(cmap); fz_dropobj(stmobj); - *cmapp = nil; return error; } +/* + * Load predefined CMap from system + */ fz_error * pdf_loadsystemcmap(fz_cmap **cmapp, char *name) { @@ -442,10 +441,9 @@ pdf_loadsystemcmap(fz_cmap **cmapp, char *name) char *cmapdir; char *usecmapname; fz_cmap *usecmap; + fz_cmap *cmap; char path[1024]; - *cmapp = nil; - cmapdir = getenv("CMAPDIR"); if (!cmapdir) return fz_throw("ioerror: CMAPDIR environment not set"); @@ -454,68 +452,71 @@ pdf_loadsystemcmap(fz_cmap **cmapp, char *name) strlcat(path, "/", sizeof path); strlcat(path, name, sizeof path); -printf(" system cmap loading %s\n", path); - error = fz_openfile(&file, path, FZ_READ); if (error) goto cleanup; - error = pdf_parsecmap(cmapp, file); + error = pdf_parsecmap(&cmap, file); if (error) goto cleanup; fz_closefile(file); - usecmapname = fz_getusecmapname(*cmapp); + usecmapname = fz_getusecmapname(cmap); if (usecmapname) { -printf(" system cmap: usecmap %s\n", usecmapname); error = pdf_loadsystemcmap(&usecmap, usecmapname); if (error) goto cleanup; - fz_setusecmap(*cmapp, usecmap); + fz_setusecmap(cmap, usecmap); + fz_dropcmap(usecmap); } + *cmapp = cmap; return nil; cleanup: - if (*cmapp) - fz_dropcmap(*cmapp); + if (cmap) + fz_dropcmap(cmap); if (file) fz_closefile(file); - *cmapp = nil; return error; } +/* + * Create an Identity-* CMap (for both 1 and 2-byte encodings) + */ fz_error * pdf_makeidentitycmap(fz_cmap **cmapp, int wmode, int bytes) { - fz_error *error = nil; + fz_error *error; + fz_cmap *cmap; - error = fz_newcmap(cmapp); + error = fz_newcmap(&cmap); if (error) return error; - error = fz_addcodespacerange(*cmapp, 0x0000, 0xffff, bytes); + error = fz_addcodespacerange(cmap, 0x0000, 0xffff, bytes); if (error) { - fz_dropcmap(*cmapp); + fz_dropcmap(cmap); return error; } - error = fz_addcidrange(*cmapp, 0x0000, 0xffff, 0); + error = fz_addcidrange(cmap, 0x0000, 0xffff, 0); if (error) { - fz_dropcmap(*cmapp); + fz_dropcmap(cmap); return error; } - error = fz_endcidrange(*cmapp); + error = fz_endcidrange(cmap); if (error) { - fz_dropcmap(*cmapp); + fz_dropcmap(cmap); return error; } - fz_setwmode(*cmapp, wmode); + fz_setwmode(cmap, wmode); + *cmapp = cmap; return nil; } diff --git a/mupdf/crypt.c b/mupdf/crypt.c index 93ff8959..03633c9e 100644 --- a/mupdf/crypt.c +++ b/mupdf/crypt.c @@ -44,6 +44,10 @@ static void padpassword(unsigned char *buf, char *pw) memcpy(buf + len, padding, 32 - len); } +/* + * Create crypt object for decrypting given the + * Encoding dictionary and file ID + */ fz_error * pdf_newdecrypt(pdf_crypt **cp, fz_obj *enc, fz_obj *id) { @@ -58,8 +62,9 @@ pdf_newdecrypt(pdf_crypt **cp, fz_obj *enc, fz_obj *id) if (m != 1 && m != 2) return fz_throw("unsupported encryption: %d", m); - crypt = *cp = fz_malloc(sizeof(pdf_crypt)); - if (!crypt) return fz_outofmem; + crypt = fz_malloc(sizeof(pdf_crypt)); + if (!crypt) + return fz_outofmem; crypt->encrypt = fz_keepobj(enc); crypt->id = nil; @@ -109,10 +114,11 @@ pdf_newdecrypt(pdf_crypt **cp, fz_obj *enc, fz_obj *id) memset(crypt->key, 0, 16); + *cp = crypt; return nil; cleanup: - if (crypt) fz_free(crypt); + pdf_dropcrypt(crypt); return fz_throw("corrupt encryption dictionary"); } @@ -265,14 +271,17 @@ createuser(pdf_crypt *crypt, char *userpw) } } +/* + * Create crypt object for encrypting, given passwords, + * permissions, and file ID + */ fz_error * -pdf_newencrypt(pdf_crypt **cp, - char *userpw, char *ownerpw, int p, int n, fz_obj *id) +pdf_newencrypt(pdf_crypt **cp, char *userpw, char *ownerpw, int p, int n, fz_obj *id) { fz_error *error; pdf_crypt *crypt; - crypt = *cp = fz_malloc(sizeof(pdf_crypt)); + crypt = fz_malloc(sizeof(pdf_crypt)); if (!crypt) return fz_outofmem; @@ -328,6 +337,9 @@ pdf_setpassword(pdf_crypt *crypt, char *userpw) return nil; } +/* + * Recursively (and destructively!) de/encrypt all strings in obj + */ void pdf_cryptobj(pdf_crypt *crypt, fz_obj *obj, int oid, int gid) { @@ -359,6 +371,9 @@ pdf_cryptobj(pdf_crypt *crypt, fz_obj *obj, int oid, int gid) } } +/* + * Create filter suitable for de/encrypting a stream + */ fz_error * pdf_cryptstream(fz_filter **fp, pdf_crypt *crypt, int oid, int gid) { diff --git a/mupdf/doctor.c b/mupdf/doctor.c index 8596c7ba..ed50b768 100644 --- a/mupdf/doctor.c +++ b/mupdf/doctor.c @@ -1,6 +1,10 @@ #include <fitz.h> #include <mupdf.h> +/* + * Sweep and mark reachable objects + */ + static fz_error *sweepref(pdf_xref *xref, fz_obj *ref); static fz_error * @@ -68,7 +72,8 @@ sweepref(pdf_xref *xref, fz_obj *ref) } /* - * Garbage collection + * Garbage collect objects not reachable from + * the trailer dictionary */ fz_error * @@ -172,6 +177,11 @@ cleanup: return error; } +/* + * Recursively copy objects from src to dst xref. + * Start with root object in src xref. + * Put the dst copy of root into newp. + */ fz_error * pdf_transplant(pdf_xref *dst, pdf_xref *src, fz_obj **newp, fz_obj *root) { @@ -227,6 +237,7 @@ pdf_transplant(pdf_xref *dst, pdf_xref *src, fz_obj **newp, fz_obj *root) if (error) goto cleanup; pdf_updatestream(dst, map[i].doid, map[i].dgen, stm); + fz_dropbuffer(stm); } error = remaprefs(&new, old, map, n); diff --git a/mupdf/lex.c b/mupdf/lex.c index f7d6292b..36e7f50a 100644 --- a/mupdf/lex.c +++ b/mupdf/lex.c @@ -49,7 +49,7 @@ static inline int fromhex(int ch) return 0; } -static void +static inline void lexwhite(fz_file *f) { int c; @@ -62,7 +62,7 @@ lexwhite(fz_file *f) } } -static void +static inline void lexcomment(fz_file *f) { int c; diff --git a/mupdf/repair.c b/mupdf/repair.c index cf9917ea..a41f10ed 100644 --- a/mupdf/repair.c +++ b/mupdf/repair.c @@ -283,8 +283,9 @@ pdf_repairpdf(pdf_xref **xrefp, char *filename) } } - *xrefp = xref; fz_free(list); + + *xrefp = xref; return nil; cleanup: diff --git a/mupdf/save.c b/mupdf/save.c index 1fcc31da..2639c993 100644 --- a/mupdf/save.c +++ b/mupdf/save.c @@ -20,10 +20,9 @@ writestream(fz_file *out, pdf_xref *xref, pdf_crypt *encrypt, int oid, int gen) return error; error = fz_pushfilter(out, ef); - if (error) { - fz_dropfilter(ef); + fz_dropfilter(ef); + if (error) return error; - } } error = pdf_openrawstream(xref, oid, gen); @@ -79,7 +78,7 @@ writeobject(fz_file *out, pdf_xref *xref, pdf_crypt *encrypt, int oid, int gen) if (encrypt) pdf_cryptobj(encrypt, x->obj, oid, gen); - if (x->stmbuf || x->stmofs) + if (pdf_isstream(xref, oid, gen)) { error = writestream(out, xref, encrypt, oid, gen); if (error) diff --git a/mupdf/stream.c b/mupdf/stream.c index 13b79642..3ccb61b3 100644 --- a/mupdf/stream.c +++ b/mupdf/stream.c @@ -1,13 +1,35 @@ #include <fitz.h> #include <mupdf.h> -/* TODO: error cleanup */ +/* + * Check if an object is a stream or not. + */ +int +pdf_isstream(pdf_xref *xref, int oid, int gen) +{ + fz_error *error; + + if (oid < 0 || oid >= xref->len) + return 0; + + error = pdf_cacheobject(xref, oid, gen); + if (error) { + fz_warn("%s", error->msg); + fz_droperror(error); + return 0; + } + + return xref->table[oid].stmbuf || xref->table[oid].stmofs; +} +/* + * Create a filter given a name and param dictionary. + */ static fz_error * buildonefilter(fz_filter **fp, fz_obj *f, fz_obj *p) { - fz_filter *predf; - fz_filter *realf; + fz_filter *decompress; + fz_filter *predict; fz_error *error; char *s; @@ -28,19 +50,28 @@ buildonefilter(fz_filter **fp, fz_obj *f, fz_obj *p) if (!strcmp(s, "RunLengthDecode") || !strcmp(s, "RL")) return fz_newrld(fp, p); - if (!strcmp(s, "JPXDecode")) - return fz_newjpxd(fp, p); - if (!strcmp(s, "FlateDecode") || !strcmp(s, "Fl")) { if (fz_isdict(p)) { fz_obj *obj = fz_dictgets(p, "Predictor"); - if (obj) { - error = fz_newflated(&realf, p); - error = fz_newpredictd(&predf, p); - error = fz_newpipeline(fp, realf, predf); - return nil; + if (obj) + { + error = fz_newflated(&decompress, p); + if (error) + return error; + + error = fz_newpredictd(&predict, p); + if (error) + { + fz_dropfilter(decompress); + return error; + } + + error = fz_newpipeline(fp, decompress, predict); + fz_dropfilter(decompress); + fz_dropfilter(predict); + return error; } } return fz_newflated(fp, p); @@ -51,28 +82,54 @@ buildonefilter(fz_filter **fp, fz_obj *f, fz_obj *p) if (fz_isdict(p)) { fz_obj *obj = fz_dictgets(p, "Predictor"); - if (obj) { - error = fz_newlzwd(&realf, p); - error = fz_newpredictd(&predf, p); - error = fz_newpipeline(fp, realf, predf); - return nil; + if (obj) + { + error = fz_newlzwd(&decompress, p); + if (error) + return error; + + error = fz_newpredictd(&predict, p); + if (error) + { + fz_dropfilter(decompress); + return error; + } + + error = fz_newpipeline(fp, decompress, predict); + fz_dropfilter(decompress); + fz_dropfilter(predict); + return error; } } return fz_newlzwd(fp, p); } - if (!strcmp(s, "JBIG2Decode")) { +#ifdef HAVE_JBIG2 + if (!strcmp(s, "JBIG2Decode")) + { /* TODO: extract and feed JBIG2Global */ return fz_newjbig2d(fp, p); } +#endif + +#ifdef HAVE_JASPER + if (!strcmp(s, "JPXDecode")) + return fz_newjpxd(fp, p); +#endif return fz_throw("syntaxerror: unknown filter: %s", s); } +/* + * Build a chain of filters given filter names and param dicts. + * If head is given, start filter chain with it. + * Assume ownership of head. + */ static fz_error * -buildfilters(fz_filter **filterp, fz_filter *head, fz_obj *fs, fz_obj *ps) +buildfilterchain(fz_filter **filterp, fz_filter *head, fz_obj *fs, fz_obj *ps) { fz_error *error; + fz_filter *newhead; fz_filter *tail; fz_obj *f; fz_obj *p; @@ -87,8 +144,21 @@ buildfilters(fz_filter **filterp, fz_filter *head, fz_obj *fs, fz_obj *ps) p = nil; error = buildonefilter(&tail, f, p); + if (error) + return error; + if (head) - error = fz_newpipeline(&head, head, tail); + { + error = fz_newpipeline(&newhead, head, tail); + fz_dropfilter(head); + fz_dropfilter(tail); + if (error) + { + fz_dropfilter(newhead); + return error; + } + head = newhead; + } else head = tail; } @@ -97,30 +167,62 @@ buildfilters(fz_filter **filterp, fz_filter *head, fz_obj *fs, fz_obj *ps) return nil; } +/* + * Build a filter for reading raw stream data. + * This is a null filter to constrain reading to the + * stream length, followed by a decryption filter. + */ static fz_error * makerawfilter(fz_filter **filterp, pdf_xref *xref, fz_obj *stmobj, int oid, int gen) { fz_error *error; - fz_filter *pipe, *cf; + fz_filter *base; fz_obj *stmlen; + int len; stmlen = fz_dictgets(stmobj, "Length"); error = pdf_resolve(&stmlen, xref); + if (error) + return error; + len = fz_toint(stmlen); + fz_dropobj(stmlen); - error = fz_newnullfilter(&pipe, fz_toint(stmlen)); + error = fz_newnullfilter(&base, len); + if (error) + return error; if (xref->crypt) { - error = pdf_cryptstream(&cf, xref->crypt, oid, gen); - error = fz_newpipeline(&pipe, pipe, cf); - } + fz_filter *crypt; + fz_filter *pipe; - fz_dropobj(stmlen); + error = pdf_cryptstream(&crypt, xref->crypt, oid, gen); + if (error) + { + fz_dropfilter(base); + return error; + } + + error = fz_newpipeline(&pipe, base, crypt); + fz_dropfilter(base); + fz_dropfilter(crypt); + if (error) + return error; + + *filterp = pipe; + } + else + { + *filterp = base; + } - *filterp = pipe; return nil; } +/* + * Construct a filter to decode a stream, without + * constraining to stream length, and without decryption. + */ fz_error * pdf_decodefilter(fz_filter **filterp, fz_obj *stmobj) { @@ -135,7 +237,7 @@ pdf_decodefilter(fz_filter **filterp, fz_obj *stmobj) if (fz_isname(filters)) return buildonefilter(filterp, filters, params); else - return buildfilters(filterp, nil, filters, params); + return buildfilterchain(filterp, nil, filters, params); } else return fz_newnullfilter(filterp, -1); @@ -143,15 +245,21 @@ pdf_decodefilter(fz_filter **filterp, fz_obj *stmobj) return nil; } +/* + * Construct a filter to decode a stream, constraining + * to stream length and decrypting. + */ static fz_error * makedecodefilter(fz_filter **filterp, pdf_xref *xref, fz_obj *stmobj, int oid, int gen) { fz_error *error; - fz_filter *pipe, *tmp; + fz_filter *base, *pipe, *tmp; fz_obj *filters; fz_obj *params; - error = makerawfilter(&pipe, xref, stmobj, oid, gen); + error = makerawfilter(&base, xref, stmobj, oid, gen); + if (error) + return error; filters = fz_dictgetsa(stmobj, "Filter", "F"); params = fz_dictgetsa(stmobj, "DecodeParms", "DP"); @@ -159,45 +267,58 @@ makedecodefilter(fz_filter **filterp, pdf_xref *xref, fz_obj *stmobj, int oid, i if (filters) { error = pdf_resolve(&filters, xref); + if (error) + goto cleanup0; + if (params) + { error = pdf_resolve(¶ms, xref); + if (error) + goto cleanup1; + } if (fz_isname(filters)) { error = buildonefilter(&tmp, filters, params); - error = fz_newpipeline(&pipe, pipe, tmp); + if (error) + goto cleanup2; + + error = fz_newpipeline(&pipe, base, tmp); + fz_dropfilter(tmp); + if (error) + goto cleanup2; } else - error = buildfilters(&pipe, pipe, filters, params); + { + error = buildfilterchain(&pipe, base, filters, params); + if (error) + goto cleanup2; + } if (params) fz_dropobj(params); + fz_dropobj(filters); } *filterp = pipe; - return nil; -} - -int -pdf_isstream(pdf_xref *xref, int oid, int gen) -{ - fz_error *error; - if (oid < 0 || oid >= xref->len) - return 0; - - error = pdf_cacheobject(xref, oid, gen); - if (error) { - fz_warn("%s", error); - fz_droperror(error); - return 0; - } - - return xref->table[oid].stmbuf || xref->table[oid].stmofs; +cleanup2: + if (params) + fz_dropobj(params); +cleanup1: + fz_dropobj(filters); +cleanup0: + fz_dropfilter(base); + return error; } +/* + * Open a stream for reading the raw (compressed but decrypted) data. + * Put the opened file in xref->stream. Using xref->file while this + * is open is a bad idea. + */ fz_error * pdf_openrawstream(pdf_xref *xref, int oid, int gen) { @@ -212,6 +333,8 @@ pdf_openrawstream(pdf_xref *xref, int oid, int gen) x = xref->table + oid; error = pdf_cacheobject(xref, oid, gen); + if (error) + return error; if (x->stmbuf) { @@ -221,8 +344,23 @@ pdf_openrawstream(pdf_xref *xref, int oid, int gen) if (x->stmofs) { error = makerawfilter(&filter, xref, x->obj, oid, gen); + if (error) + return error; + n = fz_seek(xref->file, x->stmofs, 0); + if (n == -1) + { + fz_dropfilter(filter); + return fz_ferror(xref->file); + } + error = fz_pushfilter(xref->file, filter); + if (error) + { + fz_dropfilter(filter); + return error; + } + xref->stream = xref->file; return nil; } @@ -230,6 +368,11 @@ pdf_openrawstream(pdf_xref *xref, int oid, int gen) return fz_throw("syntaxerror: object is not a stream"); } +/* + * Open a stream for reading uncompressed data. + * Put the opened file in xref->stream. + * Using xref->file while a stream is open is a Bad idea. + */ fz_error * pdf_openstream(pdf_xref *xref, int oid, int gen) { @@ -244,20 +387,45 @@ pdf_openstream(pdf_xref *xref, int oid, int gen) x = xref->table + oid; error = pdf_cacheobject(xref, oid, gen); + if (error) + return error; if (x->stmbuf) { error = makedecodefilter(&filter, xref, x->obj, oid, gen); + if (error) + return error; + error = fz_openbuffer(&xref->stream, x->stmbuf, FZ_READ); + if (error) + { + fz_dropfilter(filter); + return error; + } + error = fz_pushfilter(xref->stream, filter); - return nil; + fz_dropfilter(filter); + return error; } if (x->stmofs) { error = makedecodefilter(&filter, xref, x->obj, oid, gen); + if (error) + return error; + n = fz_seek(xref->file, x->stmofs, 0); + if (n == -1) + { + fz_dropfilter(filter); + return fz_ferror(xref->file); + } + error = fz_pushfilter(xref->file, filter); + fz_dropfilter(filter); + if (error) + return error; + xref->stream = xref->file; return nil; } @@ -265,6 +433,10 @@ pdf_openstream(pdf_xref *xref, int oid, int gen) return fz_throw("syntaxerror: object is not a stream"); } +/* + * Close the xref->stream file opened by either + * pdf_openrawstream or pdf_openstream. + */ void pdf_closestream(pdf_xref *xref) { @@ -275,7 +447,9 @@ pdf_closestream(pdf_xref *xref) xref->stream = nil; } - +/* + * Load raw (compressed but decrypted) contents of a stream into buf. + */ fz_error * pdf_loadrawstream(fz_buffer **bufp, pdf_xref *xref, int oid, int gen) { @@ -292,6 +466,9 @@ pdf_loadrawstream(fz_buffer **bufp, pdf_xref *xref, int oid, int gen) return error; } +/* + * Load uncompressed contents of a stream into buf. + */ fz_error * pdf_loadstream(fz_buffer **bufp, pdf_xref *xref, int oid, int gen) { diff --git a/mupdf/xref.c b/mupdf/xref.c index 421eeb70..8a22b238 100644 --- a/mupdf/xref.c +++ b/mupdf/xref.c @@ -55,26 +55,22 @@ pdf_decryptpdf(pdf_xref *xref) { error = pdf_resolve(&encrypt, xref); if (error) - goto cleanup; + return error; error = pdf_resolve(&id, xref); if (error) - goto cleanup; + { + fz_dropobj(encrypt); + return error; + } error = pdf_newdecrypt(&xref->crypt, encrypt, id); - if (error) - goto cleanup; - fz_dropobj(encrypt); fz_dropobj(id); + return error; } return nil; - -cleanup: - if (encrypt) fz_dropobj(encrypt); - if (id) fz_dropobj(id); - return error; } void @@ -110,12 +106,13 @@ pdf_debugpdf(pdf_xref *xref) printf("xref\n0 %d\n", xref->len); for (i = 0; i < xref->len; i++) { - printf("%010d %05d %c | %d %d\n", + printf("%010d %05d %c | %d %c%c\n", xref->table[i].ofs, xref->table[i].gen, xref->table[i].type, xref->table[i].obj ? xref->table[i].obj->refs : 0, - xref->table[i].stmofs); + xref->table[i].stmofs ? 'f' : '-', + xref->table[i].stmbuf ? 'b' : '-'); } } @@ -253,7 +250,6 @@ pdf_updateobject(pdf_xref *xref, int oid, int gen, fz_obj *obj) if (x->obj) fz_dropobj(x->obj); - x->obj = fz_keepobj(obj); if (x->type == 'f' || x->type == 'd') |