2007-09-29 08:39:35 +00:00
|
|
|
#include <fitz.h>
|
|
|
|
#include <mupdf.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sweep and mark reachable objects
|
|
|
|
*/
|
|
|
|
|
|
|
|
static fz_error *sweepref(pdf_xref *xref, fz_obj *ref);
|
|
|
|
|
|
|
|
static fz_error *
|
|
|
|
sweepobj(pdf_xref *xref, fz_obj *obj)
|
|
|
|
{
|
|
|
|
fz_error *error;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (fz_isdict(obj))
|
|
|
|
{
|
|
|
|
for (i = 0; i < fz_dictlen(obj); i++)
|
|
|
|
{
|
|
|
|
error = sweepobj(xref, fz_dictgetval(obj, i));
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fz_isarray(obj))
|
|
|
|
{
|
|
|
|
for (i = 0; i < fz_arraylen(obj); i++)
|
|
|
|
{
|
|
|
|
error = sweepobj(xref, fz_arrayget(obj, i));
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fz_isindirect(obj))
|
|
|
|
return sweepref(xref, obj);
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
static fz_error *
|
|
|
|
sweepref(pdf_xref *xref, fz_obj *ref)
|
|
|
|
{
|
|
|
|
fz_error *error;
|
|
|
|
fz_obj *obj;
|
|
|
|
int oid;
|
|
|
|
|
|
|
|
oid = fz_tonum(ref);
|
|
|
|
|
|
|
|
if (oid < 0 || oid >= xref->len)
|
|
|
|
return fz_throw("rangecheck: object number out of range");
|
|
|
|
|
|
|
|
if (xref->table[oid].mark)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
xref->table[oid].mark = 1;
|
|
|
|
|
|
|
|
error = pdf_loadindirect(&obj, xref, ref);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
error = sweepobj(xref, obj);
|
|
|
|
fz_dropobj(obj);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Garbage collect objects not reachable from
|
|
|
|
* the trailer dictionary
|
|
|
|
*/
|
|
|
|
|
|
|
|
fz_error *
|
|
|
|
pdf_garbagecollect(pdf_xref *xref)
|
|
|
|
{
|
|
|
|
fz_error *error;
|
|
|
|
int i, g;
|
|
|
|
|
|
|
|
pdf_logxref("garbage collect {\n");
|
|
|
|
|
|
|
|
for (i = 0; i < xref->len; i++)
|
|
|
|
xref->table[i].mark = 0;
|
|
|
|
|
|
|
|
error = sweepobj(xref, xref->trailer);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
for (i = 0; i < xref->len; i++)
|
|
|
|
{
|
|
|
|
pdf_xrefentry *x = xref->table + i;
|
|
|
|
g = x->gen;
|
|
|
|
if (x->type == 'o')
|
|
|
|
g = 0;
|
|
|
|
if (!x->mark && x->type != 'f' && x->type != 'd')
|
|
|
|
pdf_deleteobject(xref, i, g);
|
|
|
|
}
|
|
|
|
|
|
|
|
pdf_logxref("}\n");
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Transplant (copy) objects and streams from one file to another
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct pair
|
|
|
|
{
|
|
|
|
int soid, sgen;
|
|
|
|
int doid, dgen;
|
|
|
|
};
|
|
|
|
|
|
|
|
static fz_error *
|
|
|
|
remaprefs(fz_obj **newp, fz_obj *old, struct pair *map, int n)
|
|
|
|
{
|
|
|
|
fz_error *error;
|
|
|
|
int i, o, g;
|
|
|
|
fz_obj *tmp, *key;
|
|
|
|
|
|
|
|
if (fz_isindirect(old))
|
|
|
|
{
|
|
|
|
o = fz_tonum(old);
|
|
|
|
g = fz_togen(old);
|
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
if (map[i].soid == o && map[i].sgen == g)
|
|
|
|
return fz_newindirect(newp, map[i].doid, map[i].dgen);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (fz_isarray(old))
|
|
|
|
{
|
|
|
|
error = fz_newarray(newp, fz_arraylen(old));
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
for (i = 0; i < fz_arraylen(old); i++)
|
|
|
|
{
|
|
|
|
tmp = fz_arrayget(old, i);
|
|
|
|
error = remaprefs(&tmp, tmp, map, n);
|
|
|
|
if (error)
|
|
|
|
goto cleanup;
|
|
|
|
error = fz_arraypush(*newp, tmp);
|
|
|
|
fz_dropobj(tmp);
|
|
|
|
if (error)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (fz_isdict(old))
|
|
|
|
{
|
|
|
|
error = fz_newdict(newp, fz_dictlen(old));
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
for (i = 0; i < fz_dictlen(old); i++)
|
|
|
|
{
|
|
|
|
key = fz_dictgetkey(old, i);
|
|
|
|
tmp = fz_dictgetval(old, i);
|
|
|
|
error = remaprefs(&tmp, tmp, map, n);
|
|
|
|
if (error)
|
|
|
|
goto cleanup;
|
|
|
|
error = fz_dictput(*newp, key, tmp);
|
|
|
|
fz_dropobj(tmp);
|
|
|
|
if (error)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*newp = fz_keepobj(old);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
fz_dropobj(*newp);
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
fz_error *error;
|
|
|
|
struct pair *map;
|
|
|
|
fz_obj *old, *new;
|
|
|
|
fz_buffer *stm;
|
|
|
|
int i, n;
|
|
|
|
|
|
|
|
pdf_logxref("transplant {\n");
|
|
|
|
|
|
|
|
for (i = 0; i < src->len; i++)
|
|
|
|
src->table[i].mark = 0;
|
|
|
|
|
|
|
|
error = sweepobj(src, root);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2007-10-19 23:05:02 +00:00
|
|
|
for (n = 0, i = 0; i < src->len; i++)
|
2007-09-29 08:39:35 +00:00
|
|
|
if (src->table[i].mark)
|
|
|
|
n++;
|
|
|
|
|
|
|
|
pdf_logxref("marked %d\n", n);
|
|
|
|
|
|
|
|
map = fz_malloc(sizeof(struct pair) * n);
|
|
|
|
if (!map)
|
|
|
|
return fz_outofmem;
|
|
|
|
|
2007-10-19 23:05:02 +00:00
|
|
|
for (n = 0, i = 0; i < src->len; i++)
|
2007-09-29 08:39:35 +00:00
|
|
|
{
|
|
|
|
if (src->table[i].mark)
|
|
|
|
{
|
|
|
|
map[n].soid = i;
|
|
|
|
map[n].sgen = src->table[i].gen;
|
|
|
|
if (src->table[i].type == 'o')
|
|
|
|
map[n].sgen = 0;
|
|
|
|
error = pdf_allocobject(dst, &map[n].doid, &map[n].dgen);
|
|
|
|
if (error)
|
|
|
|
goto cleanup;
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
error = remaprefs(newp, root, map, n);
|
|
|
|
if (error)
|
|
|
|
goto cleanup;
|
|
|
|
|
2007-10-19 23:05:02 +00:00
|
|
|
for (i = 0; i < n; i++)
|
2007-09-29 08:39:35 +00:00
|
|
|
{
|
|
|
|
pdf_logxref("copyfrom %d %d to %d %d\n",
|
|
|
|
map[i].soid, map[i].sgen,
|
|
|
|
map[i].doid, map[i].dgen);
|
|
|
|
|
|
|
|
error = pdf_loadobject(&old, src, map[i].soid, map[i].sgen);
|
|
|
|
if (error)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (pdf_isstream(src, map[i].soid, map[i].sgen))
|
|
|
|
{
|
|
|
|
error = pdf_loadrawstream(&stm, src, map[i].soid, map[i].sgen);
|
|
|
|
if (error)
|
|
|
|
goto cleanup;
|
|
|
|
pdf_updatestream(dst, map[i].doid, map[i].dgen, stm);
|
|
|
|
fz_dropbuffer(stm);
|
|
|
|
}
|
|
|
|
|
|
|
|
error = remaprefs(&new, old, map, n);
|
|
|
|
fz_dropobj(old);
|
|
|
|
if (error)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
pdf_updateobject(dst, map[i].doid, map[i].dgen, new);
|
|
|
|
fz_dropobj(new);
|
|
|
|
}
|
|
|
|
|
|
|
|
pdf_logxref("}\n");
|
|
|
|
|
|
|
|
fz_free(map);
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
fz_free(map);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|