diff options
Diffstat (limited to 'Build/source/libs/pplib/pplib-src/src/ppstream.c')
-rw-r--r-- | Build/source/libs/pplib/pplib-src/src/ppstream.c | 491 |
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(); +} |