summaryrefslogtreecommitdiff
path: root/Build/source/libs/pplib/pplib-src/src/ppstream.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/pplib/pplib-src/src/ppstream.c')
-rw-r--r--Build/source/libs/pplib/pplib-src/src/ppstream.c491
1 files changed, 491 insertions, 0 deletions
diff --git a/Build/source/libs/pplib/pplib-src/src/ppstream.c b/Build/source/libs/pplib/pplib-src/src/ppstream.c
new file mode 100644
index 00000000000..c88d7e7fc33
--- /dev/null
+++ b/Build/source/libs/pplib/pplib-src/src/ppstream.c
@@ -0,0 +1,491 @@
+
+#include "ppfilter.h"
+#include "pplib.h"
+
+ppstream * ppstream_create (ppdoc *pdf, ppdict *dict, size_t offset)
+{
+ ppstream *stream;
+ stream = (ppstream *)ppstruct_take(&pdf->heap, sizeof(ppstream));
+ stream->dict = dict;
+ stream->offset = offset;
+ //if (!ppdict_rget_uint(dict, "Length", &stream->length)) // may be indirect pointing PPNONE at this moment
+ // stream->length = 0;
+ stream->length = 0;
+ stream->filespec = NULL;
+ stream->filter.filters = NULL;
+ stream->filter.params = NULL;
+ stream->filter.count = 0;
+ stream->input = &pdf->input;
+ stream->I = NULL;
+ stream->cryptkey = NULL;
+ stream->flags = 0;
+ return stream;
+}
+
+static iof * ppstream_predictor (ppdict *params, iof *N)
+{
+ ppint predictor, rowsamples, components, samplebits;
+
+ if (!ppdict_get_int(params, "Predictor", &predictor) || predictor <= 1)
+ return N;
+ if (!ppdict_get_int(params, "Columns", &rowsamples) || rowsamples == 0) // sanity, filter probably expects >0
+ rowsamples = 1;;
+ if (!ppdict_get_int(params, "Colors", &components) || components == 0) // ditto
+ components = 1;
+ if (!ppdict_get_int(params, "BitsPerComponent", &samplebits) || samplebits == 0)
+ samplebits = 8;
+ return iof_filter_predictor_decoder(N, (int)predictor, (int)rowsamples, (int)components, (int)samplebits);
+}
+
+static iof * ppstream_decoder (ppstream *stream, ppstreamtp filtertype, ppdict *params, iof *N)
+{
+ int flags;
+ iof *F, *P;
+ ppint earlychange;
+ ppstring *cryptkey;
+
+ switch (filtertype)
+ {
+ case PPSTREAM_BASE16:
+ return iof_filter_base16_decoder(N);
+ case PPSTREAM_BASE85:
+ return iof_filter_base85_decoder(N);
+ case PPSTREAM_RUNLENGTH:
+ return iof_filter_runlength_decoder(N);
+ case PPSTREAM_FLATE:
+ if ((F = iof_filter_flate_decoder(N)) != NULL)
+ {
+ if (params != NULL)
+ {
+ if ((P = ppstream_predictor(params, F)) != NULL)
+ return P;
+ iof_close(F);
+ break;
+ }
+ return F;
+ }
+ break;
+ case PPSTREAM_LZW:
+ flags = LZW_DECODER_DEFAULTS;
+ if (params != NULL && ppdict_get_int(params, "EarlyChange", &earlychange) && earlychange == 0) // integer, not boolean
+ flags &= ~LZW_EARLY_INDEX;
+ if ((F = iof_filter_lzw_decoder(N, flags)) != NULL)
+ {
+ if (params != NULL)
+ {
+ if ((P = ppstream_predictor(params, F)) != NULL)
+ return P;
+ iof_close(F);
+ break;
+ }
+ return F;
+ }
+ break;
+ case PPSTREAM_CRYPT:
+ if ((cryptkey = stream->cryptkey) == NULL)
+ return N; // /Identity crypt
+ if (stream->flags & PPSTREAM_ENCRYPTED_AES)
+ return iof_filter_aes_decoder(N, cryptkey->data, cryptkey->size);
+ if (stream->flags & PPSTREAM_ENCRYPTED_RC4)
+ return iof_filter_rc4_decoder(N, cryptkey->data, cryptkey->size);
+ return NULL; // if neither AES or RC4 but cryptkey present, something went wrong; see ppstream_info()
+ case PPSTREAM_CCITT:
+ case PPSTREAM_DCT:
+ case PPSTREAM_JBIG2:
+ case PPSTREAM_JPX:
+ break;
+ }
+ return NULL;
+}
+
+#define ppstream_source(stream) iof_filter_stream_coreader((iof_file *)((stream)->input), (size_t)((stream)->offset), (size_t)((stream)->length))
+#define ppstream_auxsource(filename) iof_filter_file_reader(filename)
+
+static ppname * ppstream_get_filter_name (ppobj *filterobj, size_t index)
+{
+ if (filterobj->type == PPNAME)
+ return index == 0 ? filterobj->name : NULL;
+ if (filterobj->type == PPARRAY)
+ return pparray_get_name(filterobj->array, index);
+ return NULL;
+}
+
+static ppdict * ppstream_get_filter_params (ppobj *paramsobj, size_t index)
+{
+ if (paramsobj->type == PPDICT)
+ return index == 0 ? paramsobj->dict : NULL;
+ if (paramsobj->type == PPARRAY)
+ return pparray_rget_dict(paramsobj->array, index);
+ return NULL;
+}
+
+static const char * ppstream_aux_filename (ppobj *filespec)
+{ // mockup, here we should decode the string
+ if (filespec->type == PPSTRING)
+ {
+ return (const char *)(filespec->string);
+ }
+ // else might be a dict - todo
+ return NULL;
+}
+
+#define ppstream_image_filter(fcode) (fcode == PPSTREAM_DCT || fcode == PPSTREAM_CCITT || fcode == PPSTREAM_JBIG2 || fcode == PPSTREAM_JPX)
+
+iof * ppstream_read (ppstream *stream, int decode, int all)
+{
+ iof *I, *F;
+ ppstreamtp *filtertypes, filtertype;
+ int owncrypt;
+ ppdict **filterparams, *fparams;
+ size_t index, filtercount;
+ const char *filename;
+
+ if (ppstream_iof(stream) != NULL)
+ return NULL; // usage error
+
+ if (stream->filespec != NULL)
+ {
+ filename = ppstream_aux_filename(stream->filespec); // mockup, basic support
+ I = filename != NULL ? ppstream_auxsource(filename) : NULL;
+ }
+ else
+ {
+ I = ppstream_source(stream);
+ }
+ if (I == NULL)
+ return NULL;
+
+ /* If the stream is encrypted, decipher is the first to be applied */
+ owncrypt = (stream->flags & PPSTREAM_ENCRYPTED_OWN) != 0;
+ if (!owncrypt)
+ {
+ if (stream->cryptkey != NULL && stream->filespec == NULL)
+ { /* implied global crypt; does not apply to external files (pdf psec page 115), except for embedded file streams (not supported so far) */
+ if ((F = ppstream_decoder(stream, PPSTREAM_CRYPT, NULL, I)) == NULL)
+ goto stream_error;
+ I = F;
+ } /* otherwise no crypt at all or /Identity */
+ }
+
+ if (decode || owncrypt)
+ {
+ if ((filtercount = stream->filter.count) > 0)
+ {
+ filtertypes = stream->filter.filters;
+ filterparams = stream->filter.params;
+ for (index = 0; index < filtercount; ++index)
+ {
+ fparams = filterparams != NULL ? filterparams[index] : NULL;
+ filtertype = filtertypes[index];
+ if ((F = ppstream_decoder(stream, filtertype, fparams, I)) != NULL)
+ {
+ I = F;
+ if (owncrypt && !decode && filtertype == PPSTREAM_CRYPT)
+ break; // /Crypt filter should always be first, so in practise we return decrypted but compressed
+ continue;
+ }
+ if (!ppstream_image_filter(filtertype))
+ goto stream_error; // failed to create non-image filter, something unexpected
+ break;
+ }
+ }
+ }
+ if (all)
+ iof_load(I);
+ else
+ iof_input(I);
+ stream->I = I;
+ return I;
+stream_error:
+ iof_close(I);
+ return NULL;
+}
+
+uint8_t * ppstream_first (ppstream *stream, size_t *size, int decode)
+{
+ iof *I;
+ if ((I = ppstream_read(stream, decode, 0)) != NULL)
+ {
+ *size = (size_t)iof_left(I);
+ return I->pos;
+ }
+ *size = 0;
+ return NULL;
+}
+
+uint8_t * ppstream_next (ppstream *stream, size_t *size)
+{
+ iof *I;
+ if ((I = ppstream_iof(stream)) != NULL)
+ {
+ I->pos = I->end;
+ if ((*size = iof_input(I)) > 0)
+ return I->pos;
+ }
+ *size = 0;
+ return NULL;
+}
+
+uint8_t * ppstream_all (ppstream *stream, size_t *size, int decode)
+{
+ iof *I;
+ if ((I = ppstream_read(stream, decode, 1)) != NULL)
+ {
+ *size = (size_t)iof_left(I);
+ return I->pos;
+ }
+ *size = 0;
+ return NULL;
+}
+
+void ppstream_done (ppstream *stream)
+{
+ iof *I;
+ if ((I = ppstream_iof(stream)) != NULL)
+ {
+ iof_close(I);
+ stream->I = NULL;
+ }
+}
+
+/* fetching stream info
+PJ20180916: revealed it makes sense to do a lilbit more just after parsing stream entry to simplify stream operations
+and extend ppstream api
+*/
+
+/* stream filters */
+
+const char * ppstream_filter_name[] = {
+ "ASCIIHexDecode",
+ "ASCII85Decode",
+ "RunLengthDecode",
+ "FlateDecode",
+ "LZWDecode",
+ "CCITTFaxDecode",
+ "DCTDecode",
+ "JBIG2Decode",
+ "JPXDecode",
+ "Crypt"
+};
+
+int ppstream_filter_type (ppname *name, ppstreamtp *filtertype)
+{
+ switch (name->data[0])
+ {
+ case 'A':
+ if (ppname_is(name, "ASCIIHexDecode")) { *filtertype = PPSTREAM_BASE16; return 1; }
+ if (ppname_is(name, "ASCII85Decode")) { *filtertype = PPSTREAM_BASE85; return 1; }
+ break;
+ case 'R':
+ if (ppname_is(name, "RunLengthDecode")) { *filtertype = PPSTREAM_RUNLENGTH; return 1; }
+ break;
+ case 'F':
+ if (ppname_is(name, "FlateDecode")) { *filtertype = PPSTREAM_FLATE; return 1; }
+ break;
+ case 'L':
+ if (ppname_is(name, "LZWDecode")) { *filtertype = PPSTREAM_LZW; return 1; }
+ break;
+ case 'D':
+ if (ppname_is(name, "DCTDecode")) { *filtertype = PPSTREAM_DCT; return 1; }
+ break;
+ case 'C':
+ if (ppname_is(name, "CCITTFaxDecode")) { *filtertype = PPSTREAM_CCITT; return 1; }
+ if (ppname_is(name, "Crypt")) { *filtertype = PPSTREAM_CRYPT; return 1; }
+ break;
+ case 'J':
+ if (ppname_is(name, "JPXDecode")) { *filtertype = PPSTREAM_JPX; return 1; }
+ if (ppname_is(name, "JBIG2Decode")) { *filtertype = PPSTREAM_JBIG2; return 1; }
+ break;
+ }
+ return 0;
+}
+
+void ppstream_info (ppstream *stream, ppdoc *pdf)
+{ // called in ppdoc_load_entries() for every stream, but after loading non-stream objects (eg. /Length..)
+ ppdict *dict, *fparams;
+ ppobj *fobj, *pobj;
+ ppname *fname, *tname, *owncryptfilter = NULL;
+ ppcrypt *crypt;
+ ppref *ref;
+ size_t i;
+ int cflags;
+
+ ppstreamtp *filtertypes = NULL, filtertype;
+ ppdict **filterparams = NULL;
+ size_t filtercount = 0, farraysize = 0;
+
+ const char *filterkey, *paramskey;
+
+ dict = stream->dict;
+ ppdict_rget_uint(dict, "Length", &stream->length);
+
+ if ((stream->filespec = ppdict_get_obj(dict, "F")) != NULL)
+ {
+ stream->flags |= PPSTREAM_NOT_SUPPORTED;
+ filterkey = "FFilter", paramskey = "FDecodeParms";
+ }
+ else
+ filterkey = "Filter", paramskey = "DecodeParms";
+
+ if ((fobj = ppdict_rget_obj(dict, filterkey)) != NULL)
+ {
+ switch (fobj->type)
+ {
+ case PPNAME:
+ farraysize = 1;
+ break;
+ case PPARRAY:
+ farraysize = fobj->array->size;
+ break;
+ default:
+ break;
+ }
+ if (farraysize > 0)
+ {
+ filtertypes = (ppstreamtp *)ppstruct_take(&pdf->heap, farraysize * sizeof(ppstreamtp));
+ if ((pobj = ppdict_rget_obj(dict, paramskey)) != NULL)
+ {
+ filterparams = (ppdict **)ppstruct_take(&pdf->heap, farraysize * sizeof(ppdict *));
+ }
+ for (i = 0; i < farraysize; ++i)
+ {
+ if ((fname = ppstream_get_filter_name(fobj, i)) != NULL && ppstream_filter_type(fname, &filtertype))
+ {
+ filtertypes[filtercount] = filtertype;
+ if (pobj != NULL)
+ {
+ fparams = ppstream_get_filter_params(pobj, i);
+ filterparams[filtercount] = fparams;
+ }
+ else
+ fparams = NULL;
+ switch (filtertype)
+ {
+ case PPSTREAM_BASE16:
+ case PPSTREAM_BASE85:
+ case PPSTREAM_RUNLENGTH:
+ case PPSTREAM_FLATE:
+ case PPSTREAM_LZW:
+ stream->flags |= PPSTREAM_FILTER;
+ break;
+ case PPSTREAM_CCITT:
+ case PPSTREAM_DCT:
+ case PPSTREAM_JBIG2:
+ case PPSTREAM_JPX:
+ stream->flags |= PPSTREAM_IMAGE;
+ break;
+ case PPSTREAM_CRYPT:
+ stream->flags |= PPSTREAM_ENCRYPTED_OWN;
+ owncryptfilter = fparams != NULL ? ppdict_get_name(fparams, "Name") : NULL; // /Type /CryptFilterDecodeParms /Name ...
+ if (i != 0) // we assume it is first
+ stream->flags |= PPSTREAM_NOT_SUPPORTED;
+ break;
+ }
+ ++filtercount;
+ }
+ else
+ {
+ stream->flags |= PPSTREAM_NOT_SUPPORTED;
+ }
+ }
+ }
+ }
+ stream->filter.filters = filtertypes;
+ stream->filter.params = filterparams;
+ stream->filter.count = filtercount;
+
+ if ((crypt = pdf->crypt) == NULL || (ref = crypt->ref) == NULL)
+ return;
+ if (stream->flags & PPSTREAM_ENCRYPTED_OWN)
+ {
+ /* Seems a common habit to use just /Crypt filter name with no params, which defaults to /Identity.
+ A real example with uncompressed metadata: <</Filter[/Crypt]/Length 4217/Subtype/XML/Type/Metadata>> */
+ if (owncryptfilter != NULL && !ppname_is(owncryptfilter, "Identity") && stream->filespec == NULL) // ?
+ {
+ if (crypt->map != NULL && ppcrypt_type(crypt, owncryptfilter, NULL, &cflags))
+ {
+ if (cflags & PPCRYPT_INFO_AES)
+ stream->flags |= PPSTREAM_ENCRYPTED_AES;
+ else if (cflags & PPCRYPT_INFO_RC4)
+ stream->flags |= PPSTREAM_ENCRYPTED_RC4;
+ }
+ }
+ }
+ else
+ {
+ if ((crypt->flags & PPCRYPT_NO_METADATA) && (tname = ppdict_get_name(dict, "Type")) != NULL && ppname_is(tname, "Metadata"))
+ ; /* special treatment of metadata stream; we assume that explicit /Filter /Crypt setup overrides document level setup of EncryptMetadata. */
+ else if (stream->filespec == NULL) /* external files are not encrypted, expect embedded files (not supported yet) */
+ {
+ if (crypt->flags & PPCRYPT_STREAM_RC4)
+ stream->flags |= PPSTREAM_ENCRYPTED_RC4;
+ else if (crypt->flags & PPCRYPT_STREAM_AES)
+ stream->flags |= PPSTREAM_ENCRYPTED_AES;
+ }
+ }
+
+ /* finally, if the stream is encrypted with non-identity crypt (implicit or explicit), make and save the crypt key */
+ if (stream->flags & PPSTREAM_ENCRYPTED)
+ stream->cryptkey = ppcrypt_stmkey(crypt, ref, ((stream->flags & PPSTREAM_ENCRYPTED_AES) != 0), &pdf->heap);
+}
+
+void ppstream_filter_info (ppstream *stream, ppstream_filter *info, int decode)
+{
+ size_t from, index;
+ ppstreamtp filtertype;
+ ppdict *params;
+
+ *info = stream->filter;
+ if (info->count > 0)
+ {
+ from = (stream->flags & PPSTREAM_ENCRYPTED_OWN) && info->filters[0] == PPSTREAM_CRYPT ? 1 : 0;
+ if (decode)
+ {
+ for (index = from; index < info->count; ++index)
+ {
+ filtertype = info->filters[index];
+ if (ppstream_image_filter(filtertype))
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ index = from;
+ }
+ if (index > 0) {
+ info->count -= index;
+ if (info->count > 0)
+ {
+ info->filters += index;
+ if (info->params != NULL)
+ {
+ info->params += index;
+ for (index = 0, params = NULL; index < info->count; ++index)
+ if ((params = info->params[index]) != NULL)
+ break;
+ if (params == NULL)
+ info->params = NULL;
+ }
+ }
+ else
+ {
+ info->filters = NULL;
+ info->params = NULL;
+ }
+ }
+ }
+}
+
+/* */
+
+void ppstream_init_buffers (void)
+{
+ iof_filters_init();
+}
+
+void ppstream_free_buffers (void)
+{
+ iof_filters_free();
+}