diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/luapplib/src/ppload.c')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/luapplib/src/ppload.c | 2769 |
1 files changed, 0 insertions, 2769 deletions
diff --git a/Build/source/texk/web2c/luatexdir/luapplib/src/ppload.c b/Build/source/texk/web2c/luatexdir/luapplib/src/ppload.c deleted file mode 100644 index 0e72039d84f..00000000000 --- a/Build/source/texk/web2c/luatexdir/luapplib/src/ppload.c +++ /dev/null @@ -1,2769 +0,0 @@ - -#include "pplib.h" - -const char * ppobj_kind[] = { "none", "null", "bool", "integer", "number", "name", "string", "array", "dict", "stream", "ref" }; - -#define ignored_char(c) (c == 0x20 || c == 0x0A || c == 0x0D || c == 0x09 || c == 0x00) -#define newline_char(c) (c == 0x0A || c == 0x0D) -#define IGNORED_CHAR_CASE 0x20: case 0x0A: case 0x0D: case 0x09: case 0x00 -#define NEWLINE_CHAR_CASE 0x0A: case 0x0D -#define DIGIT_CHAR_CASE '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9' -#define OCTAL_CHAR_CASE '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7' - -#define MAX_INT_DIGITS 32 - -#define PP_LENGTH_UNKNOWN ((size_t)-1) - -static const char * ppref_str (ppuint refnumber, ppuint refversion) -{ - static char buffer[MAX_INT_DIGITS + 1 + MAX_INT_DIGITS + 1 + 1 + 1]; - sprintf(buffer, "%lu %lu R", (unsigned long)(refnumber), (unsigned long)(refversion)); - return buffer; -} - -/* name */ - -/* -pdf spec page 57: -"The name may include any regular characters, but not delimiter or white-space characters (see Section 3.1, “Lexical Conventions”)." -"The token / (a slash followed by no regular characters) is a valid name" -"Beginning with PDF 1.2, any character except null (character code 0) may be included in a name by writing its 2-digit hexadecimal code, -preceded by the number sign character (#); see implementation notes 3 and 4 in Appendix H. This syntax is required to represent any of the -delimiter or white-space characters or the number sign character itself; it is recommended but not required for characters whose codes -are outside the range 33 (!) to 126 (~)." - -This suggests we should accept bytes 128..255 as a part of the name. -*/ - -// pdf name delimiters: 0..32, ()<>[]{}/% -// # treated specially -// .+- are valid part of name; keep in mind names such as -| | |- .notdef ABCDEF+Font etc. -static const int8_t ppname_byte_lookup[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, '#', 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 -}; - -/* -20190827: The end of the name is any byte with 0 lookup value. When reading a ref object or objstm stream containing -a single name, we may get input byte IOFEOF (-1), which must not be treated as 255. So a check for (c >= 0) is needed, -otherwise we keep writing byte 255 to the output buffer until not enough memory. -*/ - -#define ppnamebyte(c) (c >= 0 && ppname_byte_lookup[(uint8_t)(c)]) - -static const int8_t pphex_byte_lookup[] = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, - -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 -}; - -/* no need for (c >= 0) check here */ - -#define pphex(c) pphex_byte_lookup[(uint8_t)(c)] - -#define PPNAME_INIT (7 + 1) - -static ppname * ppscan_name (iof *I, ppheap *heap) -{ - ppname *encoded, *decoded; - iof *O; - int decode, c; - uint8_t *p, *e; - int8_t h1, h2; - - O = ppbytes_buffer(heap, PPNAME_INIT); - for (decode = 0, c = iof_char(I); ppnamebyte(c); c = iof_next(I)) - { - if (c == '#') decode = 1; - iof_put(O, c); - } - encoded = (ppname *)ppstruct_take(heap, sizeof(ppname)); - encoded->data = ppbytes_flush(heap, O, &encoded->size); - if (decode) - { - O = ppbytes_buffer(heap, encoded->size); // decoded always a bit smaller - for (p = (uint8_t *)encoded->data, e = p + encoded->size; p < e; ++p) - { - if (*p == '#' && p + 2 < e && (h1 = pphex(p[1])) >= 0 && (h2 = pphex(p[2])) >= 0) - { - iof_set(O, ((h1 << 4)|h2)); - p += 2; - } - else - iof_set(O, *p); - } - decoded = (ppname *)ppstruct_take(heap, sizeof(ppname)); - decoded->data = ppbytes_flush(heap, O, &decoded->size); - encoded->flags = PPNAME_ENCODED; - decoded->flags = PPNAME_DECODED; - encoded->alterego = decoded, decoded->alterego = encoded; - } - else - { - encoded->flags = 0; - encoded->alterego = encoded; - } - return encoded; -} - -static ppname * ppscan_exec (iof *I, ppheap *heap, uint8_t firstbyte) -{ - ppname *encoded, *decoded; - iof *O; - int decode, c; - uint8_t *p, *e; - int8_t h1, h2; - - O = ppbytes_buffer(heap, PPNAME_INIT); - iof_put(O, firstbyte); - for (decode = 0, c = iof_char(I); ppnamebyte(c); c = iof_next(I)) - { - if (c == '#') decode = 1; - iof_put(O, c); - } - encoded = (ppname *)ppstruct_take(heap, sizeof(ppname)); - encoded->data = ppbytes_flush(heap, O, &encoded->size); - if (decode) - { - O = ppbytes_buffer(heap, encoded->size); - for (p = (uint8_t *)encoded->data, e = p + encoded->size; p < e; ++p) - { - if (*p == '#' && p + 2 < e && (h1 = pphex(p[1])) >= 0 && (h2 = pphex(p[2])) >= 0) - { - iof_set(O, ((h1 << 4)|h2)); - p += 2; - } - else - iof_set(O, *p); - } - decoded = (ppname *)ppstruct_take(heap, sizeof(ppname)); - decoded->data = ppbytes_flush(heap, O, &decoded->size); - encoded->flags = PPNAME_EXEC|PPNAME_ENCODED; - decoded->flags = PPNAME_EXEC|PPNAME_DECODED; - encoded->alterego = decoded, decoded->alterego = encoded; - } - else - { - encoded->flags = PPNAME_EXEC; - encoded->alterego = encoded; - } - return encoded; -} - -static ppname * ppname_internal (const void *data, size_t size, int flags, ppheap *heap) -{ // so far needed only for 'EI' operator - ppname *encoded; - encoded = (ppname *)ppstruct_take(heap, sizeof(ppname)); - encoded->data = (ppbyte *)ppbytes_take(heap, size + 1); - memcpy(encoded->data, data, size); - encoded->data[size] = '\0'; - encoded->size = size; - encoded->alterego = encoded; - encoded->flags = flags; - return encoded; -} - -#define ppexec_internal(data, size, heap) ppname_internal(data, size, PPNAME_EXEC, heap) - -ppname * ppname_decoded (ppname *name) -{ - return (name->flags & PPNAME_ENCODED) ? name->alterego : name; -} - -ppname * ppname_encoded (ppname *name) -{ - return (name->flags & PPNAME_DECODED) ? name->alterego : name; -} - -ppbyte * ppname_decoded_data (ppname *name) -{ - return (name->flags & PPNAME_ENCODED) ? name->alterego->data : name->data; -} - -ppbyte * ppname_encoded_data (ppname *name) -{ - return (name->flags & PPNAME_DECODED) ? name->alterego->data : name->data; -} - -/* string */ - -static const int8_t ppstring_byte_escape[] = { /* -1 escaped with octal, >0 escaped with \\, 0 left intact*/ - -1,-1,-1,-1,-1,-1,-1,-1,'b','t','n',-1,'f','r',-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - 0, 0, 0, 0, 0, 0, 0, 0,'(',')', 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 -}; - -//// pp string - -#define PPSTRING_INIT (7 + 1) - -#define ppstring_check_bom(decoded) ((void)\ - (decoded->size >= 2 ? (ppstring_utf16be_bom(decoded->data) ? (decoded->flags |= PPSTRING_UTF16BE) : \ - (ppstring_utf16le_bom(decoded->data) ? (decoded->flags |= PPSTRING_UTF16LE) : 0)) : 0)) - -#define ppstring_check_bom2(decoded, encoded) ((void)\ - (decoded->size >= 2 ? (ppstring_utf16be_bom(decoded->data) ? ((decoded->flags |= PPSTRING_UTF16BE), (encoded->flags |= PPSTRING_UTF16BE)) : \ - (ppstring_utf16le_bom(decoded->data) ? ((decoded->flags |= PPSTRING_UTF16LE), (encoded->flags |= PPSTRING_UTF16LE)) : 0)) : 0)) - -#define ppstring_utf16be_bom(data) (data[0] == '\xFE' && data[1] == '\xFF') -#define ppstring_utf16le_bom(data) (data[0] == '\xFF' && data[1] == '\xFE') - -#define ppstringesc(c) ppstring_byte_escape[(uint8_t)(c)] - -static ppstring * ppscan_string (iof *I, ppheap *heap) -{ - ppstring *encoded, *decoded; - iof *O; - int c, decode, balance; - uint8_t *p, *e; - - O = ppbytes_buffer(heap, PPSTRING_INIT); - for (decode = 0, balance = 0, c = iof_char(I); c >= 0; ) - { - switch (c) - { - case '\\': - decode = 1; - iof_put(O, '\\'); - if ((c = iof_next(I)) >= 0) - { - iof_put(O, c); - c = iof_next(I); - } - break; - case '(': // may be unescaped if balanced - ++balance; - iof_put(O, '('); - c = iof_next(I); - break; - case ')': - if (balance == 0) - { - c = IOFEOF; - ++I->pos; - break; - } - --balance; - iof_put(O, ')'); - c = iof_next(I); - break; - default: - iof_put(O, c); - c = iof_next(I); - } - } - encoded = (ppstring *)ppstruct_take(heap, sizeof(ppstring)); - encoded->data = ppbytes_flush(heap, O, &encoded->size); - if (decode) - { - O = ppbytes_buffer(heap, encoded->size); // decoded can only be smaller - for (p = (uint8_t *)encoded->data, e = p + encoded->size; p < e; ++p) - { - if (*p == '\\') - { - if (++p >= e) - break; - switch (*p) - { - case OCTAL_CHAR_CASE: - c = *p - '0'; - if (++p < e && *p >= '0' && *p <= '7') - { - c = (c << 3) + *p - '0'; - if (++p < e && *p >= '0' && *p <= '7') - c = (c << 3) + *p - '0'; - } - iof_set(O, c); - break; - case 'n': - iof_set(O, '\n'); - break; - case 'r': - iof_set(O, '\r'); - break; - case 't': - iof_set(O, '\t'); - break; - case 'b': - iof_set(O, '\b'); - break; - case 'f': - iof_set(O, '\f'); - break; - case NEWLINE_CHAR_CASE: // not a part of the string, ignore (pdf spec page 55) - break; - case '(': case ')': case '\\': - default: // for anything else backslash is ignored (pdf spec page 54) - iof_set(O, *p); - break; - } - } - else - iof_set(O, *p); - } - decoded = (ppstring *)ppstruct_take(heap, sizeof(ppstring)); - decoded->data = ppbytes_flush(heap, O, &decoded->size); - encoded->flags = PPSTRING_ENCODED; - decoded->flags = PPSTRING_DECODED; - encoded->alterego = decoded, decoded->alterego = encoded; - ppstring_check_bom2(decoded, encoded); - } - else - { - encoded->flags = 0; - encoded->alterego = encoded; - ppstring_check_bom(encoded); - } - return encoded; -} - -static ppstring * ppscan_base16 (iof *I, ppheap *heap) -{ - ppstring *encoded, *decoded; - iof *O; - int c; - uint8_t *p, *e; - int8_t h1, h2; - - O = ppbytes_buffer(heap, PPSTRING_INIT); - for (c = iof_char(I); (pphex(c) >= 0 || ignored_char(c)); c = iof_next(I)) - iof_put(O, c); - if (c == '>') - ++I->pos; - encoded = (ppstring *)ppstruct_take(heap, sizeof(ppstring)); - encoded->data = ppbytes_flush(heap, O, &encoded->size); - - O = ppbytes_buffer(heap, ((encoded->size + 1) >> 1) + 1); // decoded can only be smaller - for (p = (uint8_t *)encoded->data, e = p + encoded->size; p < e; ++p) - { - if ((h1 = pphex(*p)) < 0) // ignored - continue; - for (h2 = 0, ++p; p < e && (h2 = pphex(*p)) < 0; ++p); - iof_set(O, (h1 << 4)|h2); - } - decoded = (ppstring *)ppstruct_take(heap, sizeof(ppstring)); - decoded->data = ppbytes_flush(heap, O, &decoded->size); - - encoded->flags = PPSTRING_BASE16|PPSTRING_ENCODED; - decoded->flags = PPSTRING_BASE16|PPSTRING_DECODED; - encoded->alterego = decoded, decoded->alterego = encoded; - - ppstring_check_bom2(decoded, encoded); - return encoded; -} - -static ppstring * ppstring_buffer (iof *O, ppheap *heap) -{ - ppstring *encoded, *decoded; - uint8_t *p, *e; - - decoded = (ppstring *)ppstruct_take(heap, sizeof(ppstring)); - decoded->data = ppbytes_flush(heap, O, &decoded->size); - - O = ppbytes_buffer(heap, (decoded->size << 1) + 1); // the exact size known - for (p = (uint8_t *)decoded->data, e = p + decoded->size; p < e; ++p) - iof_set2(O, base16_uc_alphabet[(*p) >> 4], base16_uc_alphabet[(*p) & 0xF]); - encoded = ppstruct_take(heap, sizeof(ppstring)); - encoded->data = ppbytes_flush(heap, O, &encoded->size); - encoded->flags = PPSTRING_BASE16|PPSTRING_ENCODED; - decoded->flags = PPSTRING_BASE16|PPSTRING_DECODED; - encoded->alterego = decoded, decoded->alterego = encoded; - // ppstring_check_bom2(decoded, encoded); // ? - return encoded; -} - -ppstring * ppstring_internal (const void *data, size_t size, ppheap *heap) -{ // so far used only for crypt key - iof *O; - O = ppbytes_buffer(heap, size); - memcpy(O->buf, data, size); - O->pos = O->buf + size; - return ppstring_buffer(O, heap); -} - -/* base85; local function for that to make that part independent from utilbasexx */ - -static const int8_t ppstring_base85_lookup[] = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, - 15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30, - 31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46, - 47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62, - 63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78, - 79,80,81,82,83,84,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 -}; - -#define base85_value(c) ppstring_base85_lookup[(uint8_t)(c)] - -#define base85_code(c1, c2, c3, c4, c5) ((((c1 * 85 + c2) * 85 + c3) * 85 + c4) * 85 + c5) -#define base85_eof(c) (c == '~' || c < 0) - -static iof_status ppscan_base85_decode (iof *I, iof *O) -{ - int c1, c2, c3, c4, c5; - uint32_t code; - while (iof_ensure(O, 4)) - { - do { c1 = iof_get(I); } while (ignored_char(c1)); - if (base85_eof(c1)) - return IOFEOF; - switch (c1) - { - case 'z': - iof_set4(O, '\0', '\0', '\0', '\0'); - continue; - case 'y': - iof_set4(O, ' ', ' ', ' ', ' '); - continue; - } - do { c2 = iof_get(I); } while (ignored_char(c2)); - if (base85_eof(c2)) - return IOFERR; - do { c3 = iof_get(I); } while (ignored_char(c3)); - if (base85_eof(c3)) - { - if ((c1 = base85_value(c1)) < 0 || (c2 = base85_value(c2)) < 0) - return IOFERR; - code = base85_code(c1, c2, 84, 84, 84); /* padding with 'u' (117); 117-33 = 84 */ - iof_set(O, (code >> 24)); - return IOFEOF; - } - do { c4 = iof_get(I); } while (ignored_char(c4)); - if (base85_eof(c4)) - { - if ((c1 = base85_value(c1)) < 0 || (c2 = base85_value(c2)) < 0 || (c3 = base85_value(c3)) < 0) - return IOFERR; - code = base85_code(c1, c2, c3, 84, 84); - iof_set2(O, code>>24, ((code>>16) & 0xff)); - return IOFEOF; - } - do { c5 = iof_get(I); } while (ignored_char(c5)); - if (base85_eof(c5)) - { - if ((c1 = base85_value(c1)) < 0 || (c2 = base85_value(c2)) < 0 || - (c3 = base85_value(c3)) < 0 || (c4 = base85_value(c4)) < 0) - return IOFERR; - code = base85_code(c1, c2, c3, c4, 84); - iof_set3(O, (code >> 24), ((code >> 16) & 0xff), ((code >> 8) & 0xff)); - return IOFEOF; - } - if ((c1 = base85_value(c1)) < 0 || (c2 = base85_value(c2)) < 0 || (c3 = base85_value(c3)) < 0 || - (c4 = base85_value(c4)) < 0 || (c5 = base85_value(c5)) < 0) - return IOFERR; - code = base85_code(c1, c2, c3, c4, c5); - iof_set4(O, (code >> 24), ((code >> 16) & 0xff), ((code >> 8) & 0xff), (code & 0xff)); - } - return IOFFULL; -} - -static ppstring * ppscan_base85 (iof *I, ppheap *heap) -{ // base85 alphabet is 33..117, adobe also hires 'z' and 'y' for compression - ppstring *encoded, *decoded; - iof *O, B; - int c; - - O = ppbytes_buffer(heap, PPSTRING_INIT); - for (c = iof_char(I); (c >= '!' && c <= 'u') || c == 'z' || c == 'y'; c = iof_next(I)) - iof_put(O, c); - if (c == '~') - if ((c = iof_next(I)) == '>') - ++I->pos; - encoded = (ppstring *)ppstruct_take(heap, sizeof(ppstring)); - encoded->data = ppbytes_flush(heap, O, &encoded->size); - - iof_string_reader(&B, encoded->data, encoded->size); - O = ppbytes_buffer(heap, (encoded->size * 5 / 4) + 1); // may be larger that that because of 'z' and 'y' - ppscan_base85_decode(&B, O); - decoded = (ppstring *)ppstruct_take(heap, sizeof(ppstring)); - decoded->data = ppbytes_flush(heap, O, &decoded->size); - - encoded->flags = PPSTRING_BASE85|PPSTRING_ENCODED; - decoded->flags = PPSTRING_BASE85|PPSTRING_DECODED; - encoded->alterego = decoded, decoded->alterego = encoded; - - ppstring_check_bom2(decoded, encoded); - return encoded; -} - -ppstring * ppstring_decoded (ppstring *string) -{ - return (string->flags & PPSTRING_ENCODED) ? string->alterego : string; -} - -ppstring * ppstring_encoded (ppstring *string) -{ - return (string->flags & PPSTRING_DECODED) ? string->alterego : string; -} - -ppbyte * ppstring_decoded_data (ppstring *string) -{ - return (string->flags & PPSTRING_ENCODED) ? string->alterego->data : string->data; -} - -ppbyte * ppstring_encoded_data (ppstring *string) -{ - return (string->flags & PPSTRING_DECODED) ? string->alterego->data : string->data; -} - - -/* encrypted string */ - -static ppstring * ppscan_crypt_string (iof *I, ppcrypt *crypt, ppheap *heap) -{ - ppstring *encoded, *decoded; - iof *O; - int c, b, balance, encode; - uint8_t *p, *e; - size_t size; - - O = ppbytes_buffer(heap, PPSTRING_INIT); - for (balance = 0, encode = 0, c = iof_char(I); c >= 0; ) - { - switch (c) - { - case '\\': - if ((c = iof_next(I)) < 0) - break; - encode = 1; - switch (c) - { - case OCTAL_CHAR_CASE: - b = c - '0'; - if ((c = iof_next(I)) >= 0 && c >= '0' && c <= '7') - { - b = (b << 3) + c - '0'; - if ((c = iof_next(I)) >= 0 && c >= '0' && c <= '7') - { - b = (b << 3) + c - '0'; - c = iof_next(I); - } - } - iof_put(O, b); - // c is set to the next char - break; - case 'n': - iof_put(O, '\n'); - c = iof_next(I); - break; - case 'r': - iof_put(O, '\r'); - c = iof_next(I); - break; - case 't': - iof_put(O, '\t'); - c = iof_next(I); - break; - case 'b': - iof_put(O, '\b'); - c = iof_next(I); - break; - case 'f': - iof_put(O, '\f'); - c = iof_next(I); - break; - case NEWLINE_CHAR_CASE: // not a part of the string, ignore (pdf spec page 55) - c = iof_next(I); - break; - case '(': case ')': case '\\': - default: // for enything else backslash is ignored (pdf spec page 54) - iof_put(O, c); - c = iof_next(I); - break; - } - break; - case '(': - ++balance; - encode = 1; - iof_put(O, '('); - c = iof_next(I); - break; - case ')': - if (balance == 0) - { - c = IOFEOF; - ++I->pos; - } - else - { - --balance; - //encode = 1; - iof_put(O, ')'); - c = iof_next(I); - } - break; - default: - if (ppstringesc(c) != 0) - encode = 1; - iof_put(O, c); - c = iof_next(I); - } - } - /* decrypt the buffer in place, update size */ - if (ppstring_decrypt(crypt, O->buf, iof_size(O), O->buf, &size)) - O->pos = O->buf + size; - decoded = (ppstring *)ppstruct_take(heap, sizeof(ppstring)); - decoded->data = ppbytes_flush(heap, O, &decoded->size); - /* make encoded counterpart */ - if (encode) - { - O = ppbytes_buffer(heap, decoded->size + 1); // we don't know - for (p = (uint8_t *)decoded->data, e = p + decoded->size; p < e; ++p) - { - b = ppstringesc(*p); - switch (b) - { - case 0: - iof_put(O, *p); - break; - case -1: - iof_put4(O, '\\', ((*p) >> 6) + '0', (((*p) >> 3) & 7) + '0', ((*p) & 7) + '0'); - break; - default: - iof_put2(O, '\\', b); - break; - } - } - encoded = (ppstring *)ppstruct_take(heap, sizeof(ppstring)); - encoded->data = ppbytes_flush(heap, O, &encoded->size); - encoded->flags = PPSTRING_ENCODED; - decoded->flags = PPSTRING_DECODED; - encoded->alterego = decoded, decoded->alterego = encoded; - ppstring_check_bom2(decoded, encoded); - } - else - { - decoded->flags = 0; - decoded->alterego = decoded; - ppstring_check_bom(decoded); - encoded = decoded; - } - return encoded; -} - -static ppstring * ppscan_crypt_base16 (iof *I, ppcrypt *crypt, ppheap *heap) -{ - ppstring *encoded, *decoded; - iof *O; - int c; - uint8_t *p, *e; - int8_t h1, h2; - size_t size; - - O = ppbytes_buffer(heap, PPSTRING_INIT); - for (c = iof_char(I); c != '>'; ) - { - if ((h1 = pphex(c)) < 0) - { - if (ignored_char(c)) - { - c = iof_next(I); - continue; - } - break; - } - do { - c = iof_next(I); - if ((h2 = pphex(c)) >= 0) - { - c = iof_next(I); - break; - } - if (!ignored_char(c)) // c == '>' || c < 0 or some crap - { - h2 = 0; - break; - } - } while (1); - iof_put(O, (h1 << 4)|h2); - } - if (c == '>') - ++I->pos; - /* decrypt the buffer in place, update size */ - if (ppstring_decrypt(crypt, O->buf, iof_size(O), O->buf, &size)) - O->pos = O->buf + size; - decoded = (ppstring *)ppstruct_take(heap, sizeof(ppstring)); - decoded->data = ppbytes_flush(heap, O, &decoded->size); - - O = ppbytes_buffer(heap, (decoded->size << 1) + 1); - for (p = (uint8_t *)decoded->data, e = p + decoded->size; p < e; ++p) - iof_set2(O, base16_uc_alphabet[(*p) >> 4], base16_uc_alphabet[(*p) & 0xF]); - encoded = (ppstring *)ppstruct_take(heap, sizeof(ppstring)); - encoded->data = ppbytes_flush(heap, O, &encoded->size); - - encoded->flags = PPSTRING_BASE16|PPSTRING_ENCODED; - decoded->flags = PPSTRING_BASE16|PPSTRING_DECODED; - encoded->alterego = decoded, decoded->alterego = encoded; - - ppstring_check_bom2(decoded, encoded); - return encoded; -} - -/* scanner stack */ - -#define PPSTACK_BUFFER 512 - -static void ppstack_init (ppstack *stack, ppheap *heap) -{ - stack->buf = stack->pos = (ppobj *)pp_malloc(PPSTACK_BUFFER * sizeof(ppobj)); - stack->size = 0; - stack->space = PPSTACK_BUFFER; - stack->heap = heap; -} - -#define ppstack_free_buffer(stack) (pp_free((stack)->buf)) - -static void ppstack_resize (ppstack *stack) -{ - ppobj *newbuffer; - stack->space <<= 1; - newbuffer = (ppobj *)pp_malloc(stack->space * sizeof(ppobj)); - memcpy(newbuffer, stack->buf, stack->size * sizeof(ppobj)); - ppstack_free_buffer(stack); - stack->buf = newbuffer; - stack->pos = newbuffer + stack->size; -} - -#define ppstack_push(stack) ((void)((stack)->size < (stack)->space || (ppstack_resize(stack), 0)), ++(stack)->size, (stack)->pos++) -#define ppstack_pop(stack, n) ((stack)->size -= (n), (stack)->pos -= (n)) -#define ppstack_at(stack, i) ((stack)->buf + i) -#define ppstack_clear(stack) ((stack)->pos = (stack)->buf, (stack)->size = 0) - -/* scanner commons */ - -#define ppscan_uint(I, u) iof_get_usize(I, u) -#define ppread_uint(s, u) string_to_usize((const char *)(s), u) - -static ppobj * ppscan_numobj (iof *I, ppobj *obj, int negative) -{ - ppint integer; - ppnum number; - int exponent; - int c; - c = iof_char(I); - iof_scan_integer(I, c, integer); - switch(c) - { - case '.': - { - number = (ppnum)integer; - c = iof_next(I); - iof_scan_fraction(I, c, number, exponent); - double_negative_exp10(number, exponent); - obj->type = PPNUM, obj->number = negative ? -number : number; - break; - } - default: - obj->type = PPINT, obj->integer = negative ? -integer : integer; - break; - } - return obj; -} - -static ppobj * ppscan_numobj_frac (iof *I, ppobj *obj, int negative) -{ - ppnum number; - int c, exponent; - - number = 0.0; - c = iof_next(I); - iof_scan_fraction(I, c, number, exponent); - double_negative_exp10(number, exponent); - obj->type = PPNUM, obj->number = negative ? -number : number; - return obj; -} - -static int ppscan_find (iof *I) -{ // skips whitechars and comments - int c; - for (c = iof_char(I); ; c = iof_next(I)) - { - switch (c) - { - case IGNORED_CHAR_CASE: - break; - case '%': { - do { - if ((c = iof_next(I)) < 0) - return c; - } while (!newline_char(c)); - break; - } - default: - return c; - } - } - return c; // never reached -} - -static int ppscan_keyword (iof *I, const char *keyword, size_t size) -{ - size_t i; - int c; - if ((size_t)iof_left(I) >= size) - { - if (memcmp(I->pos, keyword, size) != 0) - return 0; - I->pos += size; - return 1; - } - // sticky case, we can't go back - for (i = 0, c = iof_char(I); i < size; ++i, ++keyword, c = iof_next(I)) - if (c < 0 || *keyword != c) /* PJ20190503 bugfix: there was (i!=c), we actually never get here anyway */ - return 0; - return 1; -} - -#define ppscan_key(I, literal) ppscan_keyword(I, "" literal, sizeof(literal) - 1) - -/* objects parser */ - -static ppref * ppref_unresolved (ppheap *heap, ppuint refnumber, ppuint refversion) -{ - ppref *ref = (ppref *)ppstruct_take(heap, sizeof(ppref)); - memset(ref, 0, sizeof(ppref)); - ref->object.type = PPNONE; - ref->number = refnumber; - ref->version = refversion; - return ref; -} - -#define PPMARK PPNONE - -static ppobj * ppscan_obj (iof *I, ppdoc *pdf, ppxref *xref) -{ - int c; - ppobj *obj; - size_t mark, size; - ppuint refnumber, refversion; - ppref *ref; - ppstack *stack; - ppcrypt *crypt; - - stack = &pdf->stack; - c = iof_char(I); - switch (c) - { - case DIGIT_CHAR_CASE: - return ppscan_numobj(I, ppstack_push(stack), 0); - case '.': - return ppscan_numobj_frac(I, ppstack_push(stack), 0); - case '+': - ++I->pos; - return ppscan_numobj(I, ppstack_push(stack), 0); - case '-': - ++I->pos; - return ppscan_numobj(I, ppstack_push(stack), 1); - case '/': - ++I->pos; - obj = ppstack_push(stack); - obj->type = PPNAME; - obj->name = ppscan_name(I, &pdf->heap); - return obj; - case '(': - ++I->pos; - obj = ppstack_push(stack); - obj->type = PPSTRING; - if (ppcrypt_ref(pdf, crypt)) - obj->string = ppscan_crypt_string(I, crypt, &pdf->heap); - else - obj->string = ppscan_string(I, &pdf->heap); - return obj; - case '[': - mark = stack->size; - obj = ppstack_push(stack); - obj->type = PPMARK; // ppscan_obj() checks types backward for 'R', so set the type immediatelly (reserved for PPARRAY) - obj->any = NULL; - ++I->pos; - for (c = ppscan_find(I); c != ']'; c = ppscan_find(I)) - { - if (ppscan_obj(I, pdf, xref) == NULL) - { // callers assume that NULL returns means nothing pushed - size = stack->size - mark; // pop items AND the obj reserved for array - ppstack_pop(stack, size); - return NULL; - } - } - ++I->pos; - size = stack->size - mark - 1; - obj = ppstack_at(stack, mark); // stack might have been realocated - obj->type = PPARRAY; - obj->array = pparray_create(ppstack_at(stack, mark + 1), size, &pdf->heap); - ppstack_pop(stack, size); // pop array items, leave the array on top - return obj; - case '<': - if ((c = iof_next(I)) == '<') - { - mark = stack->size; - obj = ppstack_push(stack); - obj->type = PPMARK; - obj->any = NULL; - ++I->pos; - for (c = ppscan_find(I); c != '>'; c = ppscan_find(I)) - { - if (ppscan_obj(I, pdf, xref) == NULL) - { - size = stack->size - mark; - ppstack_pop(stack, size); - return NULL; - } - } - if (iof_next(I) == '>') - ++I->pos; - size = stack->size - mark - 1; - obj = ppstack_at(stack, mark); - obj->type = PPDICT; - obj->dict = ppdict_create(ppstack_at(stack, mark + 1), size, &pdf->heap); - ppstack_pop(stack, size); - return obj; - } - obj = ppstack_push(stack); - obj->type = PPSTRING; - if (ppcrypt_ref(pdf, crypt)) - obj->string = ppscan_crypt_base16(I, crypt, &pdf->heap); - else - obj->string = ppscan_base16(I, &pdf->heap); - return obj; - case 'R': - if (stack->size >= 2 && stack->pos[-1].type == PPINT && stack->pos[-2].type == PPINT) - { - ++I->pos; - obj = &stack->pos[-2]; - refnumber = (ppuint)obj->integer; - ppstack_pop(stack, 1); // pop version number, retype obj to a reference - if (xref == NULL || (ref = ppxref_find(xref, refnumber)) == NULL) - { /* pdf spec page 64: unresolvable reference is not an error, should just be treated as a reference to null. - we also need this to read trailer, where refs can't be resolved yet */ - refversion = (obj + 1)->integer; - //if (xref != NULL) - // loggerf("unresolved reference %s", ppref_str(refnumber, refversion)); - ref = ppref_unresolved(stack->heap, refnumber, refversion); - } - obj->type = PPREF; - obj->ref = ref; - return obj; - } - break; - case 't': - if (iof_next(I) == 'r' && iof_next(I) == 'u' && iof_next(I) == 'e') - { - ++I->pos; - obj = ppstack_push(stack); - obj->type = PPBOOL; - obj->integer = 1; - return obj; - } - break; - case 'f': - if (iof_next(I) == 'a' && iof_next(I) == 'l' && iof_next(I) == 's' && iof_next(I) == 'e') - { - ++I->pos; - obj = ppstack_push(stack); - obj->type = PPBOOL; - obj->integer = 0; - return obj; - } - break; - case 'n': - if (iof_next(I) == 'u' && iof_next(I) == 'l' && iof_next(I) == 'l') - { - ++I->pos; - obj = ppstack_push(stack); - obj->type = PPNULL; - obj->any = NULL; - return obj; - } - break; - } - return NULL; -} - -/* -A variant for contents streams (aka postscript); wise of operators, blind to references. -We are still PDF, so we don't care about postscript specific stuff such as radix numbers -and scientific numbers notation. It takes ppstack * as context (no ppdoc *) to be able -to run contents parser beyond the scope of ppdoc heap. -*/ - -static ppstring * ppstring_inline (iof *I, ppdict *imagedict, ppheap *heap); - -static ppobj * ppscan_psobj (iof *I, ppstack *stack) -{ - int c; - ppobj *obj, *op; - size_t size, mark; - ppname *exec; - ppbyte *data; - - c = iof_char(I); - switch (c) - { - case DIGIT_CHAR_CASE: - return ppscan_numobj(I, ppstack_push(stack), 0); - case '.': - return ppscan_numobj_frac(I, ppstack_push(stack), 0); - case '+': - c = iof_next(I); - if (base10_digit(c)) // '+.abc' is probably an executable name, but we are not in postscript - return ppscan_numobj(I, ppstack_push(stack), 0); - else if (c == '.') - return ppscan_numobj_frac(I, ppstack_push(stack), 0); - obj = ppstack_push(stack); - obj->type = PPNAME; - obj->name = ppscan_exec(I, stack->heap, '+'); - return obj; - case '-': - c = iof_next(I); - if (base10_digit(c)) // ditto, we would handle type1 '-|' '|-' operators though - return ppscan_numobj(I, ppstack_push(stack), 1); - else if (c == '.') - return ppscan_numobj_frac(I, ppstack_push(stack), 1); - obj = ppstack_push(stack); - obj->type = PPNAME; - obj->name = ppscan_exec(I, stack->heap, '-'); - return obj; - case '/': - ++I->pos; - obj = ppstack_push(stack); - obj->type = PPNAME; - obj->name = ppscan_name(I, stack->heap); - return obj; - case '(': - ++I->pos; - obj = ppstack_push(stack); - obj->type = PPSTRING; - obj->string = ppscan_string(I, stack->heap); - return obj; - case '[': - mark = stack->size; - obj = ppstack_push(stack); - obj->type = PPMARK; - obj->any = NULL; - ++I->pos; - for (c = ppscan_find(I); c != ']'; c = ppscan_find(I)) - { - if (ppscan_psobj(I, stack) == NULL) - { - size = stack->size - mark; - ppstack_pop(stack, size); - return NULL; - } - } - ++I->pos; - size = stack->size - mark - 1; - obj = ppstack_at(stack, mark); - obj->type = PPARRAY; - obj->array = pparray_create(ppstack_at(stack, mark + 1), size, stack->heap); - ppstack_pop(stack, size); - return obj; - case '<': - if ((c = iof_next(I)) == '<') - { - mark = stack->size; - obj = ppstack_push(stack); - obj->type = PPMARK; - obj->any = NULL; - ++I->pos; - for (c = ppscan_find(I); c != '>'; c = ppscan_find(I)) - { - if (ppscan_psobj(I, stack) == NULL) - { - size = stack->size - mark; - ppstack_pop(stack, size); - return NULL; - } - } - if (iof_next(I) == '>') - ++I->pos; - size = stack->size - mark - 1; - obj = ppstack_at(stack, mark); - obj->type = PPDICT; - obj->dict = ppdict_create(ppstack_at(stack, mark + 1), size, stack->heap); - ppstack_pop(stack, size); - return obj; - } - obj = ppstack_push(stack); - obj->type = PPSTRING; - if (c == '~') - ++I->pos, obj->string = ppscan_base85(I, stack->heap); - else - obj->string = ppscan_base16(I, stack->heap); - return obj; - default: - if (!ppnamebyte(c)) - break; // forbid empty names; dead loop otherwise - ++I->pos; - /* true false null practically don't occur in streams so it makes sense to assume that we get an operator name here. - If it happen to be a keyword we could give back those several bytes to the heap but.. heap buffer is tricky enough. */ - exec = ppscan_exec(I, stack->heap, (uint8_t)c); - data = exec->data; - obj = ppstack_push(stack); - switch (data[0]) - { - case 't': - if (data[1] == 'r' && data[2] == 'u' && data[3] == 'e' && data[4] == '\0') - { - obj->type = PPBOOL; - obj->integer = 1; - return obj; - } - break; - case 'f': - if (data[1] == 'a' && data[2] == 'l' && data[3] == 's' && data[4] == 'e' && data[5] == '\0') - { - obj->type = PPBOOL; - obj->integer = 0; - return obj; - } - break; - case 'n': - if (data[1] == 'u' && data[2] == 'l' && data[3] == 'l' && data[4] == '\0') - { - obj->type = PPNULL; - obj->any = NULL; - return obj; - } - break; - case 'B': - /* - Inline images break rules of operand/operator syntax, so 'BI/ID' operators need to be treated as special syntactic keywords. - - BI <keyval pairs> ID<whitechar?><imagedata><whitechar?>EI - - We treat the image as a single syntactic token; BI starts collecting a dict, ID is the beginning of the data. Effectively EI - operator obtains two operands - dict and string. It is ok to put three items onto the stack, callers dont't assume there is just one. - */ - if (data[1] == 'I' && data[2] == '\0') - { - ppdict *imagedict; - ppname *name; - /* key val pairs -> dict */ - mark = stack->size - 1; - obj->type = PPMARK; - obj->any = NULL; - for (c = ppscan_find(I); ; c = ppscan_find(I)) - { - if ((op = ppscan_psobj(I, stack)) == NULL) - { - size = stack->size - mark; - ppstack_pop(stack, size); - return NULL; - } - if (op->type == PPNAME) - { - name = op->name; - if (name->flags & PPNAME_EXEC) - { - if (name->size != 2 || name->data[0] != 'I' || name->data[1] != 'D') - { // weird - size = stack->size - mark; - ppstack_pop(stack, size); - return NULL; - } - break; - } - } - } - size = stack->size - mark - 1; - obj = ppstack_at(stack, mark); - obj->type = PPDICT; - obj->dict = imagedict = ppdict_create(ppstack_at(stack, mark + 1), size, stack->heap); - ppstack_pop(stack, size); - /* put image data string */ - obj = ppstack_push(stack); - obj->type = PPSTRING; - obj->string = ppstring_inline(I, imagedict, stack->heap);; - /* put EI operator name */ - obj = ppstack_push(stack); - obj->type = PPNAME; - obj->name = ppexec_internal("EI", 2, stack->heap); - return obj; - } - break; - } - obj->type = PPNAME; - obj->name = exec; - return obj; - } - return NULL; -} - -/* -We try to get the exact inline image length from its dict params. If cannot predict the length, we have to scan the input until 'EI'. -I've checked on may examples that it gives the same results but one can never be sure, as 'EI' might happen to be a part of the data. -Stripping white char is also very heuristic; \0 is a white char in PDF and very likely to be a data byte.. weak method (pdf spec page 352). - -Revision 20190327: inline images may be compressed, in which case we can't predict the length. -*/ - -static size_t inline_image_length (ppdict *dict) -{ - ppuint w, h, bpc, colors; - ppname *cs; - - if (ppdict_get_name(dict, "F") == NULL) - { - if (ppdict_get_uint(dict, "W", &w) && ppdict_get_uint(dict, "H", &h) && ppdict_get_uint(dict, "BPC", &bpc) && (cs = ppdict_get_name(dict, "CS")) != NULL) - { - if (ppname_is(cs, "DeviceGray")) - colors = 1; - else if (ppname_is(cs, "DeviceRGB")) - colors = 3; - else if (ppname_is(cs, "DeviceCMYK")) - colors = 4; - else - return PP_LENGTH_UNKNOWN; - return (w * h * bpc * colors + 7) >> 3; - } - } - return PP_LENGTH_UNKNOWN; -} - -static ppstring * ppstring_inline (iof *I, ppdict *imagedict, ppheap *heap) -{ - iof *O; - int c, d, e; - size_t length, leftin, leftout, bytes; - - c = iof_char(I); - if (ignored_char(c)) - c = iof_next(I); - - length = inline_image_length(imagedict); - if (length != PP_LENGTH_UNKNOWN) - { - O = ppbytes_buffer(heap, length); - while (length > 0 && iof_readable(I) && iof_writable(O)) - { - leftin = iof_left(I); - leftout = iof_left(O); - bytes = length; - if (bytes > leftin) bytes = leftin; - if (bytes > leftout) bytes = leftout; - memcpy(O->pos, I->pos, bytes); - I->pos += bytes; - O->pos += bytes; - length -= bytes; - } - // gobble EI - if (ppscan_find(I) == 'E') - if (iof_next(I) == 'I') - ++I->pos; - } - else - { - O = ppbytes_buffer(heap, PPSTRING_INIT); // ? - while (c >= 0) - { - if (c == 'E') - { - d = iof_next(I); - if (d == 'I') - { - e = iof_next(I); - if (!ppnamebyte(e)) - { /* strip one newline from the end and stop */ - if (O->pos - 2 >= O->buf) // sanity - { - c = *(O->pos - 1); - if (ignored_char(c)) - { - if (c == 0x0A && *(O->pos - 2) == 0x0D) - O->pos -= 2; - else - O->pos -= 1; - } - } - break; - } - iof_put2(O, c, d); - c = e; - } - else - { - iof_put(O, c); - c = d; - } - } - else - { - iof_put(O, c); - c = iof_next(I); - } - } - } - return ppstring_buffer(O, heap); -} - -/* input reader */ - -/* -PDF input is a pseudo file that either keeps FILE * or data. Reader iof * is a proxy to input -that provides byte-by-byte interface. Our iof structure is capable to link iof_file *input, -but t avoid redundant checks on IOF_DATA flag, here we link iof *I directly to FILE * or mem buffer. -When reading from file we need an internal buffer, which should be kept rather small, as it is -only used to parse xrefs and objects (no streams). We allocate the buffer from a private heap -(not static) to avoid conflicts when processing >1 pdfs at once. Besides, the input buffer may be -needed after loading the document, eg. to access references raw data. -*/ - -#define PPDOC_BUFFER 0xFFF // keep that small, it is only used to parse body objects - -static void ppdoc_reader_init (ppdoc *pdf, iof_file *input) -{ - iof *I; - pdf->input = *input; - input = &pdf->input; - input->refcount = 1; - I = &pdf->reader; - if (input->flags & IOF_DATA) - { - pdf->buffer = NULL; // input iof_file is the buffer - iof_string_reader(I, NULL, 0); - } - else - { - pdf->buffer = (uint8_t *)ppbytes_take(&pdf->heap, PPDOC_BUFFER); - iof_setup_file_handle_reader(I, NULL, 0, iof_file_get_fh(input)); // gets IOF_FILE_HANDLE flag and FILE * - I->space = PPDOC_BUFFER; // used on refill - } -} - -/* -Whenever we need to read the input file, we fseek the to the given offset and fread to the private buffer. -The length we need is not always predictable, in which case PPDOC_BUFFER bytes are read (keep it small). -I->buf = I->pos is set to the beginning, I->end set to the end (end is the first byte one shouldn't read). -*/ - -static iof * ppdoc_reader (ppdoc *pdf, size_t offset, size_t length) -{ - iof_file *input; - iof *I; - input = &pdf->input; - I = &pdf->reader; - if (iof_file_seek(input, (long)offset, SEEK_SET) != 0) - return NULL; - I->flags &= ~IOF_STOPPED; - if (input->flags & IOF_DATA) - { - I->buf = I->pos = input->pos; - I->end = (length == PP_LENGTH_UNKNOWN || I->pos + length >= input->end) ? input->end : (I->pos + length); - } - else - { - I->buf = I->pos = pdf->buffer; // ->buf is actually permanently equal pdf->buffer but we might need some tricks - if (length == PP_LENGTH_UNKNOWN || length > PPDOC_BUFFER) - length = PPDOC_BUFFER; - length = fread(I->buf, 1, length, I->file); - I->end = I->buf + length; - } - return I; -} - -/* The position from the beginning of input -- for data buffer: (pdf->input.pos - pdf->input.buf) + (I->pos - I->buf) - I->buf == pdf->input.pos, so this resolves to (I->pos - pdf->input.buf), independent from I->buf -- for file buffer: ftell(pdf->input.file) - (I->end - I->pos) -*/ - -#define ppdoc_reader_tell(pdf, I) ((size_t)(((pdf)->input.flags & IOF_DATA) ? ((I)->pos - (pdf)->input.buf) : (ftell(iof_file_get_fh(&(pdf)->input)) - ((I)->end - (I)->pos)))) - -/* pdf */ - -#define PPDOC_HEADER 10 // "%PDF-?.??\n" - -static int ppdoc_header (ppdoc *pdf, uint8_t header[PPDOC_HEADER]) -{ - size_t i; - if (memcmp(header, "%PDF-", 5) != 0) - return 0; - for (i = 5; i < PPDOC_HEADER - 1 && !ignored_char(header[i]); ++i) - pdf->version[i - 5] = header[i]; - pdf->version[i - 5] = '\0'; - return 1; -} - -static int ppdoc_tail (ppdoc *pdf, iof_file *input, size_t *pxrefoffset) -{ - int c; - uint8_t tail[4*10], *p, back, tailbytes; - - if (iof_file_seek(input, 0, SEEK_END) != 0) - return 0; - pdf->filesize = (size_t)iof_file_tell(input); - // simple heuristic to avoid fgetc() / fseek(-2) hiccup: keep seeking back by len(startxref) + 1 == 10 - // until a letter found (assuming liberal white characters and tail length) - for (back = 1, tailbytes = 0; ; ++back) - { - if (iof_file_seek(input, -10, SEEK_CUR) != 0) - return 0; - tailbytes += 10; - c = iof_file_getc(input); - tailbytes -= 1; - switch (c) - { - case IGNORED_CHAR_CASE: - case DIGIT_CHAR_CASE: - case '%': case 'E': case 'O': case 'F': - if (back > 4) // 2 should be enough - return 0; - continue; - case 's': case 't': case 'a': case 'r': case 'x': case 'e': case 'f': - if (iof_file_read(tail, 1, tailbytes, input) != tailbytes) - return 0; - tail[tailbytes] = '\0'; - for (p = &tail[0]; ; ++p) - { - if (*p == '\0') - return 0; - if ((c = base10_value(*p)) >= 0) - break; - } - ppread_uint(p, pxrefoffset); - return 1; - default: - return 0; - } - } - return 0; // never reached -} - -/* xref/body */ - -static int ppscan_start_entry (iof *I, ppref *ref) -{ - ppuint u; - ppscan_find(I); if (!ppscan_uint(I, &u) || u != ref->number) return 0; - ppscan_find(I); if (!ppscan_uint(I, &u) || u != ref->version) return 0; - ppscan_find(I); if (!ppscan_key(I, "obj")) return 0; - ppscan_find(I); - return 1; -} - -static int ppscan_skip_entry (iof *I) -{ - ppuint u; - ppscan_find(I); if (!ppscan_uint(I, &u)) return 0; - ppscan_find(I); if (!ppscan_uint(I, &u)) return 0; - ppscan_find(I); if (!ppscan_key(I, "obj")) return 0; - ppscan_find(I); - return 1; -} - -static int ppscan_start_stream (iof *I, ppdoc *pdf, size_t *streamoffset) -{ - int c; - ppscan_find(I); - if (ppscan_key(I, "stream")) - { // PJ20180912 bugfix: we were gobbling white characters (also null byte), while "stream" may be followed by EOL - // pdf spec page 60: "CARRIAGE RETURN and a LINE FEED or just a LINE FEED, and not by a CARRIAGE RETURN alone" - c = iof_char(I); - if (c == 0x0D) - { - if (iof_next(I) == 0x0A) // should be - ++I->pos; - } - else if (c == 0x0A) - { - ++I->pos; - } - *streamoffset = ppdoc_reader_tell(pdf, I); - return 1; - } - return 0; -} - -static ppxref * ppxref_load (ppdoc *pdf, size_t xrefoffset); -static ppxref * ppxref_load_chain (ppdoc *pdf, ppxref *xref); - -/* Parsing xref table - - 1 10 // first ref number and refs count - 0000000000 00000 n // 10-digits offset, 5 digits version, type identifier - 0000000000 00000 n // n states for normal I guess - 0000000000 00000 f // f states for free (not used) - ... - -Free entries seem to be a relic of ancient times, completelly useless for us. To avoid parsing xref table twice, -we waste some space on free entries by allocating one plane of refs for each section. Later on we slice sections, -so that effectively free entries are not involved in map. - -Subsequent refs gets number, version and offset. Other fields initialized when parsing PDF body. - -Having xref table loaded, we sort sections for future binary search (xref with objects count == 0 is considered invalid). - -Then we have to deal with the trailer dict. In general, to load objects and resolve references we need a complete chain -of xrefs (not only the top). To load the previous xref, we need its offset, which is given in trailer. So we have to -parse the trailer ignoring references, which might be unresolvable at this point (objects parser makes a dummy check -for xref != NULL on refs resolving ppscan_obj(), which irritates me but I don't want a separate parser for trailer..). -The same applies to xref streams, in which we have parse the trailer not having xref map at all. So the procedure is: - - - load xref map, initialize references, make it ready to search - - parse trailer ignoring references - - get /Prev xref offset and load older xref (linked list via ->prev) - - sort all refs in all xrefs by offset - - parse refs in order resolving references in contained objects - - fix trailer references - -First created xref becomes a pdf->xref (top xref). We link that early to control offsets already read (insane loops?). -*/ - -// Every xref table item "0000000000 00000 n" is said to be terminated with 2-byte EOL but we don't like relying on whites. -#define xref_item_length (10 + 1 + 5 + 1 + 1) - -static ppxref * ppxref_load_table (iof *I, ppdoc *pdf, size_t xrefoffset) -{ - ppxref *xref; - ppxsec *xrefsection; - ppref *ref; - ppuint first, count, refindex; - uint8_t buffer[xref_item_length + 1]; - const char *p; - const ppobj *obj; - - buffer[xref_item_length] = '\0'; - xref = ppxref_create(pdf, 0, xrefoffset); - if (pdf->xref == NULL) pdf->xref = xref; - - for (ppscan_find(I); ppscan_uint(I, &first); ppscan_find(I)) - { - ppscan_find(I); - if (!ppscan_uint(I, &count)) - return NULL; - if (count == 0) // weird - continue; - xref->count += count; - xrefsection = NULL; - ref = (ppref *)ppstruct_take(&pdf->heap, count * sizeof(ppref)); - for (refindex = 0; refindex < count; ++refindex, ++ref) - { - ref->xref = xref; - ref->number = first + refindex; - ppscan_find(I); - iof_read(I, buffer, xref_item_length); - switch (buffer[xref_item_length - 1]) - { - case 'n': - if (xrefsection == NULL) - { - xrefsection = ppxref_push_section(xref, &pdf->heap); - xrefsection->first = ref->number; - xrefsection->refs = ref; - } - xrefsection->last = ref->number; - for (p = (const char *)buffer; *p == '0'; ++p); - p = ppread_uint(p, &ref->offset); - for ( ; *p == ' ' || *p == '0'; ++p); - p = ppread_uint(p, &ref->version); - ref->object.type = PPNONE; // init for sanity - ref->object.any = NULL; - ref->length = 0; - break; - case 'f': - default: - --ref; - xrefsection = NULL; - --xref->count; - } - } - } - /* sort section */ - ppxref_sort(xref); // case of xref->size == 0 handled by ppxref_load_chain() - /* get trailer ignoring refs */ - if (!ppscan_key(I, "trailer")) - return NULL; - ppscan_find(I); - if ((obj = ppscan_obj(I, pdf, NULL)) == NULL) - return NULL; - ppstack_pop(&pdf->stack, 1); - if (obj->type != PPDICT) - return NULL; - xref->trailer = *obj; - return ppxref_load_chain(pdf, xref); -} - -/* Parsing xref stream -First we load the trailer, ignoring references. Dict defines sections and fields lengths: - - /Size % max ref number plus 1 - /Index [ first count first count ... ] % a pair of numbers for every section, defaults to [0 Size] - /W [w1 w2 w3] % fields lengths, 0 states for omitted field - -xref stream data is a continuous stream of binary number triplets. First number is a type: - - 0 - free entry (as 'f' in xref table) - 1 - normal entry, followed by offset an version (as 'n' in xref table) - 2 - compressed entry, followed by parent object stream number and entry index - -0 and 1 are handled as 'n' and 'f' entries in xref table. For type 2 we normally initialize -ref->number and ref->version (the later is implicitly 0). ref->offset is set to 0 (invalid offset), -which is recognized by objects loader. -*/ - -#define XREF_STREAM_MAX_FIELD 4 - -static ppxref * ppxref_load_stream (iof *I, ppdoc *pdf, size_t xrefoffset) -{ - ppxref *xref; - ppxsec *xrefsection; - ppref *ref; - ppobj *obj; - ppstream *xrefstream; - size_t streamoffset; - ppuint w1, w2, w3, w, bufferbytes; - uint8_t buffer[3 * XREF_STREAM_MAX_FIELD], *b; - ppuint first, count, f1, f2, f3; - pparray *fieldwidths, *sectionindices; - ppobj sectionmock[2], *sectionfirst, *sectioncount; - size_t sections, sectionindex, refindex; - - if (!ppscan_skip_entry(I)) - return NULL; - if ((obj = ppscan_obj(I, pdf, NULL)) == NULL) - return NULL; - ppstack_pop(&pdf->stack, 1); - if (obj->type != PPDICT || !ppscan_start_stream(I, pdf, &streamoffset)) - return NULL; - xrefstream = ppstream_create(pdf, obj->dict, streamoffset); - ppstream_info(xrefstream, pdf); - if ((fieldwidths = ppdict_get_array(xrefstream->dict, "W")) != NULL) - { - if (!pparray_get_uint(fieldwidths, 0, &w1)) w1 = 0; - if (!pparray_get_uint(fieldwidths, 1, &w2)) w2 = 0; - if (!pparray_get_uint(fieldwidths, 2, &w3)) w3 = 0; - } - else - w1 = w2 = w3 = 0; - if (w1 > XREF_STREAM_MAX_FIELD || w2 > XREF_STREAM_MAX_FIELD || w3 > XREF_STREAM_MAX_FIELD) - return NULL; - bufferbytes = w1 + w2 + w3; - if ((sectionindices = ppdict_get_array(xrefstream->dict, "Index")) != NULL) - { - sections = sectionindices->size >> 1; - sectionfirst = sectionindices->data; - } - else - { - sections = 1; - sectionmock[0].type = PPINT; - sectionmock[0].integer = 0; - sectionmock[1].type = PPINT; - if (!ppdict_get_int(xrefstream->dict, "Size", §ionmock[1].integer)) - sectionmock[1].integer = 0; - sectionfirst = §ionmock[0]; - } - if ((I = ppstream_read(xrefstream, 1, 0)) == NULL) - return NULL; // we fseek() so original I is useless anyway - xref = ppxref_create(pdf, sections, xrefoffset); - if (pdf->xref == NULL) pdf->xref = xref; - xref->trailer.type = PPSTREAM; - xref->trailer.stream = xrefstream; - for (sectionindex = 0; sectionindex < sections; ++sectionindex, sectionfirst += 2) - { - sectioncount = sectionfirst + 1; - first = 0, count = 0; // warnings - if (!ppobj_get_uint(sectionfirst, first) || !ppobj_get_uint(sectioncount, count)) - goto xref_stream_error; - if (count == 0) - continue; - xref->count += count; - xrefsection = NULL; - ref = (ppref *)ppstruct_take(&pdf->heap, count * sizeof(ppref)); - for (refindex = 0; refindex < count; ++refindex, ++ref) - { - ref->xref = xref; - ref->number = first + refindex; - if (iof_read(I, buffer, bufferbytes) != bufferbytes) - goto xref_stream_error; - b = buffer; - if (w1 == 0) - f1 = 1; // default type is 1 - else - for (f1 = 0, w = 0; w < w1; f1 = (f1 << 8)|(*b), ++w, ++b); - for (f2 = 0, w = 0; w < w2; f2 = (f2 << 8)|(*b), ++w, ++b); - for (f3 = 0, w = 0; w < w3; f3 = (f3 << 8)|(*b), ++w, ++b); - switch (f1) - { - case 0: - //--ref; - xrefsection = NULL; - --xref->count; - break; - case 1: - if (xrefsection == NULL) - { - xrefsection = ppxref_push_section(xref, &pdf->heap); - xrefsection->first = ref->number; - xrefsection->refs = ref; - } - xrefsection->last = ref->number; - ref->offset = f2; - ref->version = f3; - ref->object.type = PPNONE; - ref->object.any = NULL; - ref->length = 0; - break; - case 2: - if (xrefsection == NULL) - { - xrefsection = ppxref_push_section(xref, &pdf->heap); - xrefsection->first = ref->number; - xrefsection->refs = ref; - } - xrefsection->last = ref->number; - ref->offset = 0; // f2 is parent objstm, f3 is index in parent, both useless - ref->version = 0; // compressed objects has implicit version == 0 - ref->object.type = PPNONE; - ref->object.any = NULL; - ref->length = 0; - break; - default: - goto xref_stream_error; - } - } - } - /* sort sections */ - ppxref_sort(xref); // case of xref->size == 0 handled by ppxref_load_chain() - /* close the stream _before_ loading prev xref */ - ppstream_done(xrefstream); - /* load prev and return */ - return ppxref_load_chain(pdf, xref); -xref_stream_error: - ppstream_done(xrefstream); - return NULL; -} - -/* -The following procedure loads xref /Prev, links xref->prev and typically returns xref. -Some docs contain empty xref (one section with zero objects) that is actually a proxy -to xref stream referred as /XRefStm (genuine concept of xrefs old/new style xrefs in -the same doc). In case of 0-length xref we ignore the proxy and return the target xref -(otherwise we would need annoying sanity check for xref->size > 0 on every ref search). -*/ - -static ppxref * ppxref_load_chain (ppdoc *pdf, ppxref *xref) -{ - ppdict *trailer; - ppuint xrefoffset; - ppxref *prevxref, *nextxref; - - trailer = ppxref_trailer(xref); - if (!ppdict_get_uint(trailer, "Prev", &xrefoffset)) // XRefStm is useless - return xref; - for (nextxref = pdf->xref; nextxref != NULL; nextxref = nextxref->prev) - if (nextxref->offset == xrefoffset) // insane - return NULL; - if ((prevxref = ppxref_load(pdf, (size_t)xrefoffset)) == NULL) - return NULL; - if (xref->size > 0) - { - xref->prev = prevxref; - return xref; - } - if (pdf->xref == xref) - pdf->xref = prevxref; - return prevxref; -} - -static ppxref * ppxref_load (ppdoc *pdf, size_t xrefoffset) -{ - iof *I; - - if ((I = ppdoc_reader(pdf, xrefoffset, PP_LENGTH_UNKNOWN)) == NULL) - return NULL; - ppscan_find(I); - if (ppscan_key(I, "xref")) - return ppxref_load_table(I, pdf, xrefoffset); - return ppxref_load_stream(I, pdf, xrefoffset); - // iof_close(I) does nothing here -} - -static void ppoffmap_sort (ppref **left, ppref **right) -{ - ppref **l, **r, *t; - ppuint pivot; - l = left, r = right; - pivot = (*(l + ((r - l) / 2)))->offset; - do - { // don't read from pointer! - while ((*l)->offset < pivot) ++l; - while ((*r)->offset > pivot) --r; - if (l <= r) - { - t = *l; - *l = *r; - *r = t; - ++l, --r; - } - } while (l <= r); - if (left < r) - ppoffmap_sort(left, r); - if (l < right) - ppoffmap_sort(l, right); -} - - -static void fix_trailer_references (ppdoc *pdf) -{ - ppxref *xref; - ppdict *trailer; - ppname **pkey; - ppobj *obj; - ppref *ref; - for (xref = pdf->xref; xref != NULL; xref = xref->prev) - { - if ((trailer = ppxref_trailer(xref)) == NULL) - continue; - for (ppdict_first(trailer, pkey, obj); *pkey != NULL; ppdict_next(pkey, obj)) - { // no need to go deeper in structs, all items in trailer except info and root must be direct refs - if (obj->type != PPREF) - continue; - ref = obj->ref; - if (ref->offset == 0) // unresolved? - if ((ref = ppxref_find(xref, ref->number)) != NULL) - obj->ref = ref; // at this moment the reference still points nothing, but should be the one with the proper offset - } - } -} - -/* -Here comes a procedure that loads all entries from all document bodies. We resolve references while -parsing objects and to make resolving correct, we need a complete chain of xref maps, and a knowledge -about possible linearized dict (first offset). So loading refs sorted by offsets makes sense (not sure -if it matters nowadays but we also avoid fseek() by large offsets). - -Here is the proc: - - - create a list of all refs in all bodies - - sort the list by offsets - - for every ref from the sorted list: - - estimate object length to avoid fread-ing more than necessary (not perfect but enough) - - fseek() to the proper offset, fread() entry data or its part - - parse the object with ppscan_obj(I, pdf, xref), where xref is not necessarily top pdf->xref - (since v0.98 xref actually no longer matters, see xref_find() notes) - - save the actual ref->length (not used so far, but we keep that so..) - - make a stream if a dict is followed by "stream" keyword, also save the stream offset - - free the list - -PJ2080916: Luigi and Hans fixeed a bug (rev 6491); a document having a stream with /Length being -a reference, that was stored in /ObjStm, and therefore not yet resolved when caching /Length key -value as stream->offset (ppstream_info()). At the end, references were resolved propertly, but -the stream was no readable; stream->offset == 0. In rev6491 ObjStm streams are loaded before -others streams. -*/ - -static int ppdoc_load_objstm (ppstream *stream, ppdoc *pdf, ppxref *xref); - -#define ppref_is_objstm(ref, stream, type) \ - ((ref)->xref->trailer.type == PPSTREAM && (type = ppdict_get_name((stream)->dict, "Type")) != NULL && ppname_is(type, "ObjStm")) - - -static void ppdoc_load_entries (ppdoc *pdf) -{ - size_t objects, sectionindex, refnumber, offindex; - size_t streams = 0, object_streams = 0, redundant_indirections = 0; - ppnum linearized; - ppref **offmap, **pref, *ref; - ppxref *xref; - ppxsec *xsec; - ppobj *obj; - ppname *type; - ppcrypt *crypt; - ppstream *stream; - - if ((objects = (size_t)ppdoc_objects(pdf)) == 0) // can't happen - return; - pref = offmap = (ppref **)pp_malloc(objects * sizeof(ppref *)); - objects = 0; // recount refs with offset > 0 - for (xref = pdf->xref; xref != NULL; xref = xref->prev) - for (sectionindex = 0, xsec = xref->sects; sectionindex < xref->size; ++sectionindex, ++xsec) - for (refnumber = xsec->first, ref = xsec->refs; refnumber <= xsec->last; ++refnumber, ++ref) - if (ref->offset > 0) // 0 means compressed or insane - *pref++ = ref, ++objects; - ppoffmap_sort(offmap, offmap + objects - 1); - - crypt = pdf->crypt; - for (offindex = 0, pref = offmap; offindex < objects; ) - { - ref = *pref; - ++pref; - ++offindex; - if (ref->object.type != PPNONE) // might be preloaded already (/Encrypt dict, stream filter dicts, stream /Length..) - continue; - if (offindex < objects) - ref->length = (*pref)->offset - ref->offset; - else - ref->length = pdf->filesize > ref->offset ? pdf->filesize - ref->offset : 0; - if (crypt != NULL) - { - ppcrypt_start_ref(crypt, ref); - obj = ppdoc_load_entry(pdf, ref); - ppcrypt_end_ref(crypt); - } - else - { - obj = ppdoc_load_entry(pdf, ref); - } - switch (obj->type) - { - case PPDICT: /* Check if the object at first offset is linearized dict. We need that to resolve all references properly. */ - if (offindex == 1 && ppdict_get_num(obj->dict, "Linearized", &linearized)) // /Linearized value is a version number, default 1.0 - pdf->flags |= PPDOC_LINEARIZED; - break; - case PPSTREAM: - ++streams; - if (ppref_is_objstm(ref, obj->stream, type)) - ++object_streams; - break; - case PPREF: - ++redundant_indirections; - break; - default: - break; - } - } - - /* cut references pointing to references (rare). doing for all effectively cuts all insane chains */ - for (pref = offmap; redundant_indirections > 0; ) - { - ref = *pref++; - if (ref->object.type == PPREF) - { - --redundant_indirections; - ref->object = ref->object.ref->object; - } - } - - /* load pdf 1.5 object streams _before_ other streams */ - for (pref = offmap; object_streams > 0; ) - { - ref = *pref++; - obj = &ref->object; - if (obj->type != PPSTREAM) - continue; - stream = obj->stream; - if (ppref_is_objstm(ref, stream, type)) - { - --object_streams; - if (crypt != NULL) - { - ppcrypt_start_ref(crypt, ref); - ppstream_info(stream, pdf); - ppcrypt_end_ref(crypt); - } - else - { - ppstream_info(stream, pdf); - } - if (!ppdoc_load_objstm(stream, pdf, ref->xref)) - loggerf("invalid objects stream %s at offset " PPSIZEF, ppref_str(ref->number, ref->version), ref->offset); - - } - } - - /* now handle other streams */ - for (pref = offmap; streams > 0; ) - { - ref = *pref++; - obj = &ref->object; - if (obj->type != PPSTREAM) - continue; - --streams; - stream = obj->stream; - if (crypt != NULL) - { - ppcrypt_start_ref(crypt, ref); - ppstream_info(stream, pdf); - ppcrypt_end_ref(crypt); - } - else - { - ppstream_info(stream, pdf); - } - } - pp_free(offmap); -} - -ppobj * ppdoc_load_entry (ppdoc *pdf, ppref *ref) -{ - iof *I; - size_t length; - ppxref *xref; - ppobj *obj; - ppstack *stack; - size_t streamoffset; - ppref *refref; - ppuint refnumber, refversion; - - length = ref->length > 0 ? ref->length : PP_LENGTH_UNKNOWN; // estimated or unknown - if ((I = ppdoc_reader(pdf, ref->offset, length)) == NULL || !ppscan_start_entry(I, ref)) - { - loggerf("invalid %s offset " PPSIZEF, ppref_str(ref->number, ref->version), ref->offset); - return &ref->object; // PPNONE - } - stack = &pdf->stack; - xref = ref->xref; - if ((obj = ppscan_obj(I, pdf, xref)) == NULL) - { - loggerf("invalid %s object at offset " PPSIZEF, ppref_str(ref->number, ref->version), ref->offset); - return &ref->object; // PPNONE - } - ref->object = *obj; - ppstack_pop(stack, 1); - obj = &ref->object; - ref->length = ppdoc_reader_tell(pdf, I) - ref->offset; - if (obj->type == PPDICT) - { - if (ppscan_start_stream(I, pdf, &streamoffset)) - { - obj->type = PPSTREAM; - obj->stream = ppstream_create(pdf, obj->dict, streamoffset); - } - } - else if (obj->type == PPINT) - { - ppscan_find(I); - if (ppscan_uint(I, &refversion) && ppscan_find(I) == 'R') - { - refnumber = (ppuint)obj->integer; - if ((refref = ppxref_find(xref, refnumber)) != NULL) - { - obj->type = PPREF; - obj->ref = refref; - } - else - { - obj->type = PPNONE; // as ppref_unresolved() - obj->any = NULL; - } - } - } - return obj; -} - -/* Loading entries from object stream - - /N is the number of contained entries - /First is the offset of the first item - -The stream consists of N pairs of numbers <objnum> <offset> <objnum> <offset> ... -Offsets are ascending (relative to the first), but ref numbers order is arbitrary. -PDF spec says there might be some additional data between objects, so we should obey offsets. -Which means we should basically load the stream at once (may be needed anyway to grab the stream [...]). -*/ - -static int ppdoc_load_objstm (ppstream *stream, ppdoc *pdf, ppxref *xref) -{ - ppdict *dict; // stream dict, actually still on stack - ppref *ref; - ppobj *obj; - ppuint items, firstoffset, offset, objnum, i, invalid = 0; - iof *I; - uint8_t *firstdata, *indexdata; - ppstack *stack; - - dict = stream->dict; - if (!ppdict_rget_uint(dict, "N", &items) || !ppdict_rget_uint(dict, "First", &firstoffset)) - return 0; - if ((I = ppstream_read(stream, 1, 1)) == NULL) - return 0; - firstdata = I->pos + firstoffset; - if (firstdata >= I->end) - goto invalid_objstm; - stack = &pdf->stack; - //if (pdf->crypt != NULL) - // ppcrypt_end_ref(pdf->crypt); // objects are not encrypted, pdf->crypt->ref ensured NULL - for (i = 0; i < items; ++i) - { - ppscan_find(I); - if (!ppscan_uint(I, &objnum)) - goto invalid_objstm; - ppscan_find(I); - if (!ppscan_uint(I, &offset)) - goto invalid_objstm; - if ((ref = ppxref_find_local(xref, objnum)) == NULL || ref->object.type != PPNONE) - { - loggerf("invalid compressed object number " PPUINTF " at position " PPUINTF, objnum, i); - ++invalid; - continue; - } - if (firstdata + offset >= I->end) - { - loggerf("invalid compressed object offset " PPUINTF " at position " PPUINTF, offset, i); - ++invalid; - continue; - } - indexdata = I->pos; // save position - I->pos = firstdata + offset; // go to the object - ppscan_find(I); - if ((obj = ppscan_obj(I, pdf, xref)) != NULL) - { - ref->object = *obj; - ppstack_pop(stack, 1); - // nothing more needed, as obj can never be indirect ref or stream - } - else - { - ++invalid; - loggerf("invalid compressed object %s at stream offset " PPUINTF, ppref_str(objnum, 0), offset); - } - I->pos = indexdata; // restore position and read next from index - } - ppstream_done(stream); - return invalid == 0; -invalid_objstm: - ppstream_done(stream); - return 0; -} - -/* main PDF loader proc */ - -ppcrypt_status ppdoc_crypt_pass (ppdoc *pdf, const void *userpass, size_t userpasslength, const void *ownerpass, size_t ownerpasslength) -{ - switch (pdf->cryptstatus) - { - case PPCRYPT_NONE: - case PPCRYPT_DONE: - case PPCRYPT_FAIL: - break; - case PPCRYPT_PASS: // initial status or really needs password - pdf->cryptstatus = ppdoc_crypt_init(pdf, userpass, userpasslength, ownerpass, ownerpasslength); - switch (pdf->cryptstatus) - { - case PPCRYPT_NONE: - case PPCRYPT_DONE: - ppdoc_load_entries(pdf); - break; - case PPCRYPT_PASS: // user needs to check ppdoc_crypt_status() and recall ppdoc_crypt_pass() with the proper password - case PPCRYPT_FAIL: // hopeless.. - break; - } - break; - } - return pdf->cryptstatus; -} - -static ppdoc * ppdoc_read (ppdoc *pdf, iof_file *input) -{ - uint8_t header[PPDOC_HEADER]; - size_t xrefoffset; - - input = &pdf->input; - if (iof_file_read(header, 1, PPDOC_HEADER, input) != PPDOC_HEADER || !ppdoc_header(pdf, header)) - return NULL; - if (!ppdoc_tail(pdf, input, &xrefoffset)) - return NULL; - if (ppxref_load(pdf, xrefoffset) == NULL) - return NULL; - fix_trailer_references(pdf); // after loading xrefs but before accessing trailer refs (/Encrypt might be a reference) - // check encryption, if any, try empty password - switch (ppdoc_crypt_pass(pdf, "", 0, NULL, 0)) - { - case PPCRYPT_NONE: // no encryption - case PPCRYPT_DONE: // encryption with an empty password - case PPCRYPT_PASS: // the user needs to check ppdoc_crypt_status() and call ppdoc_crypt_pass() - break; - case PPCRYPT_FAIL: // hopeless - //loggerf("decryption failed"); - //return NULL; - break; - } - return pdf; -} - -static void ppdoc_pages_init (ppdoc *pdf); - -/* -20191214: We used to allocate ppdoc, as all other structs, from the internal heap: - - ppheap heap; - ppheap_init(&heap); - pdf = (ppdoc *)ppstruct_take(&heap, sizeof(ppdoc)); - pdf->heap = heap; - ppbytes_buffer_init(&pdf->heap); - ... - -So ppdoc pdf was allocated from the heap owned by the pdf itself. Somewhat tricky, but should work fine, -as from that point nothing refered to a local heap variable addres. For some reason that causes a crash -on openbsd. -*/ - -static ppdoc * ppdoc_create (iof_file *input) -{ - ppdoc *pdf; - - pdf = (ppdoc *)pp_malloc(sizeof(ppdoc)); - ppheap_init(&pdf->heap); - ppbytes_buffer_init(&pdf->heap); - ppstack_init(&pdf->stack, &pdf->heap); - ppdoc_reader_init(pdf, input); - ppdoc_pages_init(pdf); - pdf->xref = NULL; - pdf->crypt = NULL; - pdf->cryptstatus = PPCRYPT_PASS; // check on ppdoc_read() -> ppdoc_crypt_pass() - pdf->flags = 0; - pdf->version[0] = '\0'; - if (ppdoc_read(pdf, &pdf->input) != NULL) - return pdf; - ppdoc_free(pdf); - return NULL; -} - -ppdoc * ppdoc_load (const char *filename) -{ - FILE *file; - iof_file input; - if ((file = fopen(filename, "rb")) == NULL) - return NULL; - iof_file_init(&input, file); - input.flags |= IOF_CLOSE_FILE; - return ppdoc_create(&input); -} - -ppdoc * ppdoc_filehandle (FILE *file, int closefile) -{ - iof_file input; - if (file == NULL) - return NULL; - iof_file_init(&input, file); - if (closefile) - input.flags |= IOF_CLOSE_FILE; - return ppdoc_create(&input); -} - -ppdoc * ppdoc_mem (const void *data, size_t size) -{ - iof_file input; - iof_file_rdata_init(&input, data, size); - input.flags |= IOF_BUFFER_ALLOC; - return ppdoc_create(&input); -} - -void ppdoc_free (ppdoc *pdf) -{ - iof_file_decref(&pdf->input); - ppstack_free_buffer(&pdf->stack); - ppheap_free(&pdf->heap); - pp_free(pdf); -} - -ppcrypt_status ppdoc_crypt_status (ppdoc *pdf) -{ - return pdf->cryptstatus; -} - -ppint ppdoc_permissions (ppdoc *pdf) -{ - return pdf->crypt != NULL ? pdf->crypt->permissions : (ppint)0xFFFFFFFFFFFFFFFF; -} - -/* pages access */ - -static pparray * pppage_node (ppdict *dict, ppuint *count, ppname **type) -{ - ppname **pkey, *key; - ppobj *obj; - pparray *kids = NULL; - *count = 0; - *type = NULL; - for (ppdict_first(dict, pkey, obj); (key = *pkey) != NULL; ppdict_next(pkey, obj)) - { - switch (key->data[0]) - { - case 'T': - if (ppname_is(key, "Type")) - *type = ppobj_get_name(obj); - break; - case 'C': - if (ppname_is(key, "Count")) - ppobj_rget_uint(obj, *count); - break; - case 'K': - if (ppname_is(key, "Kids")) - kids = ppobj_rget_array(obj); - break; - } - } - return kids; -} - -#define ppname_is_page(type) (type != NULL && ppname_is(type, "Page")) - -ppuint ppdoc_page_count (ppdoc *pdf) -{ - ppref *ref; - ppname *type; - ppuint count; - if ((ref = ppxref_pages(pdf->xref)) == NULL) - return 0; - if (pppage_node(ref->object.dict, &count, &type) == NULL) - return ppname_is_page(type) ? 1 : 0; // acrobat and ghostscript accept documents with root /Pages entry being a reference to a sole /Page object - return count; -} - -ppref * ppdoc_page (ppdoc *pdf, ppuint index) -{ - ppdict *dict; - ppuint count; - pparray *kids; - size_t size, i; - ppobj *r, *o; - ppref *ref; - ppname *type; - - if ((ref = ppxref_pages(pdf->xref)) == NULL) - return NULL; - dict = ref->object.dict; - if ((kids = pppage_node(dict, &count, &type)) != NULL) - { - if (index < 1 || index > count) - return NULL; - } - else - { - return index == 1 && ppname_is_page(type) ? ref : NULL; - } -scan_array: - if (index <= count / 2) - { // probably shorter way from the beginning - for (i = 0, size = kids->size, r = pparray_at(kids, 0); i < size; ++i, ++r) - { - if (r->type != PPREF) - return NULL; - o = &r->ref->object; - if (o->type != PPDICT) - return NULL; - dict = o->dict; - if ((kids = pppage_node(dict, &count, &type)) != NULL) - { - if (index <= count) - goto scan_array; - index -= count; - continue; - } - if (index == 1 && ppname_is_page(type)) - return r->ref; - --index; - } - } - else if ((size = kids->size) > 0) // for safe (size-1) - { // probably shorter way from the end - index = count - index + 1; - for (i = 0, r = pparray_at(kids, size - 1); i < size; ++i, --r) - { - if (r->type != PPREF) - return NULL; - o = &r->ref->object; - if (o->type != PPDICT) - return NULL; - dict = o->dict; - if ((kids = pppage_node(dict, &count, &type)) != NULL) - { - if (index <= count) { - index = count - index + 1; - goto scan_array; - } - index -= count; - continue; - } - if (index == 1 && ppname_is_page(type)) - return r->ref; - --index; - } - } - return NULL; -} - -/* -Through pages iterator. Iterating over pages tree just on the base of /Kids and /Parent keys -is ineffective, as to get next pageref we need to take parent, find the pageref in /Kids, -take next (or go upper).. Annoying. We use a dedicated stack for pages iterator. This could -actually be done with pdf->stack, but some operations may clear it, so safer to keep it independent -Besides, its depth is constant (set on first use), so no need for allocs. -*/ - -static void ppdoc_pages_init (ppdoc *pdf) -{ - pppages *pages; - pages = &pdf->pages; - pages->root = pages->parent = &pages->buffer[0]; - pages->depth = 0; - pages->space = PPPAGES_STACK_DEPTH; -} - -static ppkids * pppages_push (ppdoc *pdf, pparray *kids) -{ - ppkids *newroot, *bounds; - pppages *pages; - pages = &pdf->pages; - if (pages->depth == pages->space) - { - pages->space <<= 1; - newroot = (ppkids *)ppstruct_take(&pdf->heap, pages->space * sizeof(ppkids)); - memcpy(newroot, pages->root, pages->depth * sizeof(ppkids)); - pages->root = newroot; - } - bounds = pages->parent = &pages->root[pages->depth++]; - bounds->current = pparray_at(kids, 0); - bounds->sentinel = pparray_at(kids, kids->size); - return bounds; -} - -#define pppages_pop(pages) (--((pages)->parent), --((pages)->depth)) - -static ppref * ppdoc_pages_group_first (ppdoc *pdf, ppref *ref) -{ - ppdict *dict; - pparray *kids; - ppuint count; - ppname *type; - ppobj *o; - - dict = ref->object.dict; // typecheck made by callers - while ((kids = pppage_node(dict, &count, &type)) != NULL) - { - if ((o = pparray_get_obj(kids, 0)) == NULL) // empty /Kids - return ppdoc_next_page(pdf); - if ((ref = ppobj_get_ref(o)) == NULL || ref->object.type != PPDICT) - return NULL; - pppages_push(pdf, kids); - dict = ref->object.dict; - } - return ppname_is_page(type) ? ref : NULL; -} - -ppref * ppdoc_first_page (ppdoc *pdf) -{ - ppref *ref; - pppages *pages; - if ((ref = ppdoc_pages(pdf)) == NULL) - return NULL; - pages = &pdf->pages; - pages->parent = pages->root; - pages->depth = 0; - return ppdoc_pages_group_first(pdf, ref); -} - -ppref * ppdoc_next_page (ppdoc *pdf) -{ - pppages *pages; - ppkids *bounds; - ppref *ref; - ppobj *obj; - pages = &pdf->pages; - while (pages->depth > 0) - { - bounds = pages->parent; - obj = ++bounds->current; - if (obj < bounds->sentinel) - { - if (obj->type != PPREF) - return NULL; - ref = obj->ref; - if (ref->object.type != PPDICT) - return NULL; - return ppdoc_pages_group_first(pdf, ref); - } - else - { // no next node, go upper - pppages_pop(pages); - } - } - return NULL; -} - -/* context */ - -ppcontext * ppcontext_new (void) -{ - ppcontext *context; - context = (ppcontext *)pp_malloc(sizeof(ppcontext)); - ppheap_init(&context->heap); - ppbytes_buffer_init(&context->heap); - ppstack_init(&context->stack, &context->heap); - return context; -} - -void ppcontext_done (ppcontext *context) -{ - ppheap_renew(&context->heap); - ppstack_clear(&context->stack); -} - -void ppcontext_free (ppcontext *context) -{ - ppstack_free_buffer(&context->stack); - ppheap_free(&context->heap); - pp_free(context); -} - -/* page contents streams */ - -//#define ppcontents_first_stream(array) pparray_rget_stream(array, 0) - -static ppstream * ppcontents_first_stream (pparray *array) -{ - size_t i; - ppobj *obj; - ppref *ref; - for (pparray_first(array, i, obj); i < array->size; pparray_next(i, obj)) - if ((ref = ppobj_get_ref(obj)) != NULL && ref->object.type == PPSTREAM) - return ref->object.stream; - return NULL; -} - -static ppstream * ppcontents_next_stream (pparray *array, ppstream *stream) -{ - size_t i; - ppobj *obj; - ppref *ref; - for (pparray_first(array, i, obj); i < array->size; pparray_next(i, obj)) - if ((ref = ppobj_get_ref(obj)) != NULL && ref->object.type == PPSTREAM && ref->object.stream == stream) - if (++i < array->size && (ref = ppobj_get_ref(obj + 1)) != NULL && ref->object.type == PPSTREAM) - return ref->object.stream; - return NULL; -} - -ppstream * ppcontents_first (ppdict *dict) -{ - ppobj *contentsobj; - if ((contentsobj = ppdict_rget_obj(dict, "Contents")) == NULL) - return NULL; - switch (contentsobj->type) - { - case PPARRAY: - return ppcontents_first_stream(contentsobj->array); - case PPSTREAM: - return contentsobj->stream; - default: - break; - } - return NULL; -} - -ppstream * ppcontents_next (ppdict *dict, ppstream *stream) -{ - ppobj *contentsobj; - if ((contentsobj = ppdict_rget_obj(dict, "Contents")) == NULL) - return NULL; - switch (contentsobj->type) - { - case PPARRAY: - return ppcontents_next_stream(contentsobj->array, stream); - case PPSTREAM: - break; - default: - break; - } - return NULL; -} - -static ppobj * ppcontents_op (iof *I, ppstack *stack, size_t *psize, ppname **pname) -{ - ppobj *obj; - ppstack_clear(stack); - do { - if (ppscan_find(I) < 0) - return NULL; - if ((obj = ppscan_psobj(I, stack)) == NULL) - return NULL; - } while (obj->type != PPNAME || !ppname_exec(obj->name)); - *pname = obj->name; - *psize = stack->size - 1; - return stack->buf; -} - -ppobj * ppcontents_first_op (ppcontext *context, ppstream *stream, size_t *psize, ppname **pname) -{ - iof *I; - if ((I = ppstream_read(stream, 1, 0)) == NULL) - return NULL; - return ppcontents_op(I, &context->stack, psize, pname); -} - -ppobj * ppcontents_next_op (ppcontext *context, ppstream *stream, size_t *psize, ppname **pname) -{ - return ppcontents_op(ppstream_iof(stream), &context->stack, psize, pname); -} - -ppobj * ppcontents_parse (ppcontext *context, ppstream *stream, size_t *psize) -{ - iof *I; - ppstack *stack; - ppobj *obj; - stack = &context->stack; - ppstack_clear(stack); - if ((I = ppstream_read(stream, 1, 0)) == NULL) - return NULL; - while (ppscan_find(I) >= 0) - if ((obj = ppscan_psobj(I, stack)) == NULL) - goto error; - *psize = stack->size; - ppstream_done(stream); - return stack->buf; -error: - ppstream_done(stream); - return NULL; -} - -/* boxes */ - -pprect * pparray_to_rect (pparray *array, pprect *rect) -{ - ppobj *obj; - if (array->size != 4) - return NULL; - obj = pparray_at(array, 0); - if (!ppobj_get_num(obj, rect->lx)) return NULL; - obj = pparray_at(array, 1); - if (!ppobj_get_num(obj, rect->ly)) return NULL; - obj = pparray_at(array, 2); - if (!ppobj_get_num(obj, rect->rx)) return NULL; - obj = pparray_at(array, 3); - if (!ppobj_get_num(obj, rect->ry)) return NULL; - return rect; -} - -pprect * ppdict_get_rect (ppdict *dict, const char *name, pprect *rect) -{ - pparray *array; - return (array = ppdict_rget_array(dict, name)) != NULL ? pparray_to_rect(array, rect) : NULL; -} - -pprect * ppdict_get_box (ppdict *dict, const char *name, pprect *rect) -{ - do { - if (ppdict_get_rect(dict, name, rect) != NULL) - return rect; - dict = ppdict_rget_dict(dict, "Parent"); - } while (dict != NULL); - return NULL; -} - -ppmatrix * pparray_to_matrix (pparray *array, ppmatrix *matrix) -{ - ppobj *obj; - if (array->size != 6) - return NULL; - obj = pparray_at(array, 0); - if (!ppobj_get_num(obj, matrix->xx)) return NULL; - obj = pparray_at(array, 1); - if (!ppobj_get_num(obj, matrix->xy)) return NULL; - obj = pparray_at(array, 2); - if (!ppobj_get_num(obj, matrix->yx)) return NULL; - obj = pparray_at(array, 3); - if (!ppobj_get_num(obj, matrix->yy)) return NULL; - obj = pparray_at(array, 4); - if (!ppobj_get_num(obj, matrix->x)) return NULL; - obj = pparray_at(array, 5); - if (!ppobj_get_num(obj, matrix->y)) return NULL; - return matrix; -} - -ppmatrix * ppdict_get_matrix (ppdict *dict, const char *name, ppmatrix *matrix) -{ - pparray *array; - return (array = ppdict_rget_array(dict, name)) != NULL ? pparray_to_matrix(array, matrix) : NULL; -} - -/* logger */ - -void pplog_callback (pplogger_callback logger, void *alien) -{ - logger_callback((logger_function)logger, alien); -} - -int pplog_prefix (const char *prefix) -{ - return logger_prefix(prefix); -} - -/* version */ - -const char * ppdoc_version_string (ppdoc *pdf) -{ - return pdf->version; -} - -int ppdoc_version_number (ppdoc *pdf, int *minor) -{ - *minor = pdf->version[2] - '0'; - return pdf->version[0] - '0'; -} - -/* doc info */ - -size_t ppdoc_file_size (ppdoc *pdf) -{ - return pdf->filesize; -} - -ppuint ppdoc_objects (ppdoc *pdf) -{ - ppuint count; - ppxref *xref; - for (count = 0, xref = pdf->xref; xref != NULL; xref = xref->prev) - count += xref->count; - return count; -} - -size_t ppdoc_memory (ppdoc *pdf, size_t *waste) -{ - mem_info info; - size_t used; - ppbytes_heap_info(&pdf->heap, &info, 0); - ppstruct_heap_info(&pdf->heap, &info, 1); - - *waste = info.ghosts + info.blockghosts + info.left; // info.ghosts == 0 - used = info.used + *waste; - used += pdf->stack.space * sizeof(ppobj); - return used; -} |