mirror of
https://github.com/reactos/reactos.git
synced 2024-09-10 21:04:26 +00:00
493 lines
8.7 KiB
C
493 lines
8.7 KiB
C
![]() |
#include "fitz.h"
|
||
|
#include "mupdf.h"
|
||
|
|
||
|
/*
|
||
|
* 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 *decompress;
|
||
|
fz_filter *predict;
|
||
|
fz_error *error;
|
||
|
char *s;
|
||
|
|
||
|
s = fz_toname(f);
|
||
|
|
||
|
if (!strcmp(s, "ASCIIHexDecode") || !strcmp(s, "AHx"))
|
||
|
return fz_newahxd(fp, p);
|
||
|
|
||
|
if (!strcmp(s, "ASCII85Decode") || !strcmp(s, "A85"))
|
||
|
return fz_newa85d(fp, p);
|
||
|
|
||
|
if (!strcmp(s, "CCITTFaxDecode") || !strcmp(s, "CCF"))
|
||
|
return fz_newfaxd(fp, p);
|
||
|
|
||
|
if (!strcmp(s, "DCTDecode") || !strcmp(s, "DCT"))
|
||
|
return fz_newdctd(fp, p);
|
||
|
|
||
|
if (!strcmp(s, "RunLengthDecode") || !strcmp(s, "RL"))
|
||
|
return fz_newrld(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(&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);
|
||
|
}
|
||
|
|
||
|
if (!strcmp(s, "LZWDecode") || !strcmp(s, "LZW"))
|
||
|
{
|
||
|
if (fz_isdict(p))
|
||
|
{
|
||
|
fz_obj *obj = fz_dictgets(p, "Predictor");
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
#ifdef HAVE_JBIG2DEC
|
||
|
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 *
|
||
|
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;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < fz_arraylen(fs); i++)
|
||
|
{
|
||
|
f = fz_arrayget(fs, i);
|
||
|
if (fz_isarray(ps))
|
||
|
p = fz_arrayget(ps, i);
|
||
|
else
|
||
|
p = nil;
|
||
|
|
||
|
error = buildonefilter(&tail, f, p);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
if (head)
|
||
|
{
|
||
|
error = fz_newpipeline(&newhead, head, tail);
|
||
|
fz_dropfilter(head);
|
||
|
fz_dropfilter(tail);
|
||
|
if (error)
|
||
|
{
|
||
|
fz_dropfilter(newhead);
|
||
|
return error;
|
||
|
}
|
||
|
head = newhead;
|
||
|
}
|
||
|
else
|
||
|
head = tail;
|
||
|
}
|
||
|
|
||
|
*filterp = head;
|
||
|
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 *
|
||
|
buildrawfilter(fz_filter **filterp, pdf_xref *xref, fz_obj *stmobj, int oid, int gen)
|
||
|
{
|
||
|
fz_error *error;
|
||
|
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(&base, len);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
if (xref->crypt)
|
||
|
{
|
||
|
fz_filter *crypt;
|
||
|
fz_filter *pipe;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Construct a filter to decode a stream, without
|
||
|
* constraining to stream length, and without decryption.
|
||
|
*/
|
||
|
fz_error *
|
||
|
pdf_buildinlinefilter(fz_filter **filterp, fz_obj *stmobj)
|
||
|
{
|
||
|
fz_obj *filters;
|
||
|
fz_obj *params;
|
||
|
|
||
|
filters = fz_dictgetsa(stmobj, "Filter", "F");
|
||
|
params = fz_dictgetsa(stmobj, "DecodeParms", "DP");
|
||
|
|
||
|
*filterp = nil;
|
||
|
|
||
|
if (filters)
|
||
|
{
|
||
|
if (fz_isname(filters))
|
||
|
return buildonefilter(filterp, filters, params);
|
||
|
else if (fz_arraylen(filters) > 0)
|
||
|
return buildfilterchain(filterp, nil, filters, params);
|
||
|
}
|
||
|
|
||
|
/* uh oh, no filter */
|
||
|
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Construct a filter to decode a stream, constraining
|
||
|
* to stream length and decrypting.
|
||
|
*/
|
||
|
static fz_error *
|
||
|
pdf_buildfilter(fz_filter **filterp, pdf_xref *xref, fz_obj *stmobj, int oid, int gen)
|
||
|
{
|
||
|
fz_error *error;
|
||
|
fz_filter *base, *pipe, *tmp;
|
||
|
fz_obj *filters;
|
||
|
fz_obj *params;
|
||
|
|
||
|
error = buildrawfilter(&base, xref, stmobj, oid, gen);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
filters = fz_dictgetsa(stmobj, "Filter", "F");
|
||
|
params = fz_dictgetsa(stmobj, "DecodeParms", "DP");
|
||
|
|
||
|
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);
|
||
|
if (error)
|
||
|
goto cleanup2;
|
||
|
|
||
|
error = fz_newpipeline(&pipe, base, tmp);
|
||
|
fz_dropfilter(base);
|
||
|
fz_dropfilter(tmp);
|
||
|
if (error)
|
||
|
goto cleanup2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
error = buildfilterchain(&pipe, base, filters, params);
|
||
|
if (error)
|
||
|
goto cleanup2;
|
||
|
}
|
||
|
|
||
|
if (params)
|
||
|
fz_dropobj(params);
|
||
|
|
||
|
fz_dropobj(filters);
|
||
|
|
||
|
*filterp = pipe;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*filterp = base;
|
||
|
}
|
||
|
|
||
|
return nil;
|
||
|
|
||
|
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.
|
||
|
* Using xref->file while this is open is a bad idea.
|
||
|
*/
|
||
|
fz_error *
|
||
|
pdf_openrawstream(fz_stream **stmp, pdf_xref *xref, int oid, int gen)
|
||
|
{
|
||
|
pdf_xrefentry *x;
|
||
|
fz_error *error;
|
||
|
fz_filter *filter;
|
||
|
int n;
|
||
|
|
||
|
if (oid < 0 || oid >= xref->len)
|
||
|
return fz_throw("rangecheck: object id out of range");
|
||
|
|
||
|
x = xref->table + oid;
|
||
|
|
||
|
error = pdf_cacheobject(xref, oid, gen);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
if (x->stmbuf)
|
||
|
{
|
||
|
return fz_openrbuffer(stmp, x->stmbuf);
|
||
|
}
|
||
|
|
||
|
if (x->stmofs)
|
||
|
{
|
||
|
error = buildrawfilter(&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_ioerror(xref->file);
|
||
|
}
|
||
|
|
||
|
error = fz_openrfilter(stmp, filter, xref->file);
|
||
|
|
||
|
fz_dropfilter(filter);
|
||
|
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
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(fz_stream **stmp, pdf_xref *xref, int oid, int gen)
|
||
|
{
|
||
|
pdf_xrefentry *x;
|
||
|
fz_error *error;
|
||
|
fz_stream *rawstm;
|
||
|
fz_filter *filter;
|
||
|
int n;
|
||
|
|
||
|
if (oid < 0 || oid >= xref->len)
|
||
|
return fz_throw("rangecheck: object id out of range");
|
||
|
|
||
|
x = xref->table + oid;
|
||
|
|
||
|
error = pdf_cacheobject(xref, oid, gen);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
if (x->stmbuf)
|
||
|
{
|
||
|
error = pdf_buildfilter(&filter, xref, x->obj, oid, gen);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
error = fz_openrbuffer(&rawstm, x->stmbuf);
|
||
|
if (error)
|
||
|
{
|
||
|
fz_dropfilter(filter);
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
error = fz_openrfilter(stmp, filter, rawstm);
|
||
|
fz_dropfilter(filter);
|
||
|
fz_dropstream(rawstm);
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
if (x->stmofs)
|
||
|
{
|
||
|
error = pdf_buildfilter(&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_ioerror(xref->file);
|
||
|
}
|
||
|
|
||
|
error = fz_openrfilter(stmp, filter, xref->file);
|
||
|
fz_dropfilter(filter);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
return fz_throw("syntaxerror: object is not a stream");
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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)
|
||
|
{
|
||
|
fz_error *error;
|
||
|
fz_stream *stm;
|
||
|
int n;
|
||
|
|
||
|
error = pdf_openrawstream(&stm, xref, oid, gen);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
n = fz_readall(bufp, stm);
|
||
|
if (n < 0)
|
||
|
error = fz_ioerror(stm);
|
||
|
else
|
||
|
error = nil;
|
||
|
|
||
|
fz_dropstream(stm);
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Load uncompressed contents of a stream into buf.
|
||
|
*/
|
||
|
fz_error *
|
||
|
pdf_loadstream(fz_buffer **bufp, pdf_xref *xref, int oid, int gen)
|
||
|
{
|
||
|
fz_error *error;
|
||
|
fz_stream *stm;
|
||
|
int n;
|
||
|
|
||
|
error = pdf_openstream(&stm, xref, oid, gen);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
n = fz_readall(bufp, stm);
|
||
|
if (n < 0)
|
||
|
error = fz_ioerror(stm);
|
||
|
else
|
||
|
error = nil;
|
||
|
|
||
|
fz_dropstream(stm);
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|