diff options
author | Sam James <sam@gentoo.org> | 2022-03-29 10:27:10 +0100 |
---|---|---|
committer | Sam James <sam@gentoo.org> | 2022-04-17 12:53:05 +0100 |
commit | 085bde903b9e684c3c1160e4df912bea9a660997 (patch) | |
tree | c4f5e6e9f2422e869ca5bc0b944520d451001282 /pdf | |
parent | Import Ghostscript 9.55 (diff) | |
download | ghostscript-gpl-patches-085bde903b9e684c3c1160e4df912bea9a660997.tar.gz ghostscript-gpl-patches-085bde903b9e684c3c1160e4df912bea9a660997.tar.bz2 ghostscript-gpl-patches-085bde903b9e684c3c1160e4df912bea9a660997.zip |
Import Ghostscript 9.56.0ghostscript-9.56
Signed-off-by: Sam James <sam@gentoo.org>
Diffstat (limited to 'pdf')
60 files changed, 5838 insertions, 3048 deletions
diff --git a/pdf/ghostpdf.c b/pdf/ghostpdf.c index 5080a839..8d88b090 100644 --- a/pdf/ghostpdf.c +++ b/pdf/ghostpdf.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -24,6 +24,7 @@ #include "pdf_file.h" #include "pdf_loop_detect.h" #include "pdf_trans.h" +#include "pdf_font_types.h" #include "pdf_gstate.h" #include "stream.h" #include "strmio.h" @@ -303,82 +304,15 @@ static int pdfi_output_page_info(pdf_context *ctx, uint64_t page_num) * 'unknown' message. */ const char *pdf_error_strings[] = { - "no error", - "no header detected", - "header lacks a version number", - "no startxref token found", - "startxref offset invalid", - "couldn't read hybrid file's XrefStm", - "error in xref table", - "too few entries in xref table", - "content stream lacks endstream", - "request for unknown filter", - "missing white space after number", - "malformed number", - "invalid unescaped character '(' in string", - "invalid object number", - "object lacks an endobj", - "error executing PDF token", - "potential token is too long", - "Page object doe snot have /Page type", - "circular reference to indirect object", - "couldn't repair PDF file", - "PDF file was repaired", - "error reading a stream", - "obj token missing", - "error in Page dictionary", - "out of memory", - "error reading page dictionary", - "stack underflow", - "error in stream dictionary", - "stream inherited a resource", - "counting down reference to freed object", - "error in transparency XObject", - "object lacks a required Subtype", - "error in image colour", - "" /* last error, should not be used */ +#define PARAM(A,B) B +#include "pdf_errors.h" + "" /* last error, should not be used */ }; const char *pdf_warning_strings[] = { - "no warning", - "incorrect xref size", - "used inline filter name inappropriately", - "used inline colour space inappropriately", - "used inline image key inappropriately", - "recoverable image error", - "recoverable error in image dictionary", - "encountered more Q than q", - "encountered more q than Q", - "garbage left on stack", - "stack underflow", - "error in group definition", - "invalid operator used in text block", - "used invalid operator in CharProc", - "BT found inside a text block", - "ET found outside text block", - "text operator outside text block", - "degenerate text matrix", - "bad ICC colour profile, using alternate", - "bad ICC vs number components, using components", - "bad value for text rendering mode", - "error in shading", - "error in pattern", - "non standard operator found - ignoring", - "number uses illegal exponent form", - "Stream has inappropriate /Contents entry", - "bad DecodeParms", - "error in Mask", - "error in annotation Appearance", - "badly escaped name", - "typecheck error", - "bad trailer dictionary", - "error in annotation", - "failed to create ICC profile link", - "overflowed a real reading a number, assuming 0", - "failed to read a valid number, assuming 0", - "A DeviceN space used the /All ink name.", - "Couldn't retrieve MediaBox for page, using current media size", - "" /* Last warning shuld not be used */ +#define PARAM(A,B) B +#include "pdf_warnings.h" + "" /* Last warning should not be used */ }; const char *gs_error_strings[] = { @@ -521,6 +455,37 @@ void pdfi_verbose_warning(pdf_context *ctx, int gs_error, const char *gs_lib_fun } } +void pdfi_set_error_var(pdf_context *ctx, int gs_error, const char *gs_lib_function, pdf_error pdfi_error, const char *pdfi_function_name, const char *fmt, ...) +{ + if (pdfi_error != 0) + ctx->pdf_errors[pdfi_error / (sizeof(char) * 8)] |= 1 << pdfi_error % (sizeof(char) * 8); + if (ctx->args.verbose_errors) { + char extra_info[gp_file_name_sizeof]; + va_list args; + + va_start(args, fmt); + (void)vsnprintf(extra_info, sizeof(extra_info), fmt, args); + va_end(args); + + pdfi_verbose_error(ctx, gs_error, gs_lib_function, pdfi_error, pdfi_function_name, extra_info); + } +} + +void pdfi_set_warning_var(pdf_context *ctx, int gs_error, const char *gs_lib_function, pdf_warning pdfi_warning, const char *pdfi_function_name, const char *fmt, ...) +{ + ctx->pdf_warnings[pdfi_warning / (sizeof(char) * 8)] |= 1 << pdfi_warning % (sizeof(char) * 8); + if (ctx->args.verbose_warnings) { + char extra_info[gp_file_name_sizeof]; + va_list args; + + va_start(args, fmt); + (void)vsnprintf(extra_info, sizeof(extra_info), fmt, args); + va_end(args); + + pdfi_verbose_warning(ctx, gs_error, gs_lib_function, pdfi_warning, pdfi_function_name, extra_info); + } +} + void pdfi_log_info(pdf_context *ctx, const char *pdfi_function, const char *info) { #ifdef DEBUG @@ -529,7 +494,7 @@ void pdfi_log_info(pdf_context *ctx, const char *pdfi_function, const char *info #endif } -static void +void pdfi_report_errors(pdf_context *ctx) { int code, i, j; @@ -872,7 +837,7 @@ int pdfi_prep_collection(pdf_context *ctx, uint64_t *TotalFiles, char ***names_a /* Start by setting up the file to be read. Apply a SubFileDecode so that, if the input stream * is not compressed we will stop reading when we get to the end of the stream. */ - if (pdfi_dict_knownget_number(ctx, stream_dict, "Length", &L)) { + if (pdfi_dict_knownget_number(ctx, stream_dict, "Length", &L) > 0) { code = pdfi_apply_SubFileDecode_filter(ctx, (int)L, NULL, ctx->main_stream, &SubFile_stream, false); if (code >= 0) @@ -1063,7 +1028,12 @@ static int pdfi_init_file(pdf_context *ctx) } if (ctx->Trailer) { - code = pdfi_dict_get(ctx, ctx->Trailer, "Encrypt", &o); + /* See comment in pdfi_read_Root() (pdf_doc.c) for details */ + pdf_dict *d = ctx->Trailer; + + pdfi_countup(d); + code = pdfi_dict_get(ctx, d, "Encrypt", &o); + pdfi_countdown(d); if (code < 0 && code != gs_error_undefined) goto exit; if (code == 0) { @@ -1204,9 +1174,9 @@ int pdfi_set_input_stream(pdf_context *ctx, stream *stm) char extra_info[gp_file_name_sizeof]; if (ctx->filename) - gs_sprintf(extra_info, "%% File %s does not appear to be a PDF file (no %%PDF in first 2Kb of file)\n", ctx->filename); + gs_snprintf(extra_info, sizeof(extra_info), "%% File %s does not appear to be a PDF file (no %%PDF in first 2Kb of file)\n", ctx->filename); else - gs_sprintf(extra_info, "%% File does not appear to be a PDF stream (no %%PDF in first 2Kb of stream)\n"); + gs_snprintf(extra_info, sizeof(extra_info), "%% File does not appear to be a PDF stream (no %%PDF in first 2Kb of stream)\n"); pdfi_set_error(ctx, 0, NULL, E_PDF_NOHEADER, "pdfi_set_input_stream", extra_info); } else { @@ -1281,7 +1251,7 @@ int pdfi_set_input_stream(pdf_context *ctx, stream *stm) byte *b = Buffer + read; /* Success! stop now */ - if(sscanf((char *)b, " %ld", &ctx->startxref) != 1) { + if(sscanf((char *)b, " %"PRIdOFFSET"", &ctx->startxref) != 1) { dmprintf(ctx->memory, "Unable to read offset of xref from PDF file\n"); } break; @@ -1298,7 +1268,7 @@ int pdfi_set_input_stream(pdf_context *ctx, stream *stm) */ if (last_lineend) { leftover = last_lineend - Buffer; - memcpy(Buffer + bytes - leftover, last_lineend, leftover); + memmove(Buffer + bytes - leftover, last_lineend, leftover); bytes -= leftover; } else leftover = 0; @@ -1362,7 +1332,7 @@ static size_t pdfi_grdir_path_string_match(const byte *str, size_t sl0, byte *pa int pdfi_add_paths_to_search_paths(pdf_context *ctx, const char *ppath, int l, bool fontpath) { - int i, slen, npaths = (l > 0); + int i, slen, npaths = (l > 0) ? 1 : 0; const char *p = ppath; char *ps; const char *pe = p + l + 1; @@ -1500,6 +1470,64 @@ static void pdfi_free_search_paths(pdf_context *ctx) } gs_free_object(ctx->memory, (byte *)ctx->search_paths.resource_paths, "array of paths"); gs_free_object(ctx->memory, (byte *)ctx->search_paths.font_paths, "array of font paths"); + + if (ctx->search_paths.genericresourcedir.persistent == false) + gs_free_object(ctx->memory, (byte *)ctx->search_paths.genericresourcedir.data, "generic resource directory"); +} + +static void pdfi_free_fontmapfiles(pdf_context *ctx) +{ + int i; + for (i = 0; i < ctx->num_fontmapfiles; i++) { + gs_free_object(ctx->memory, ctx->fontmapfiles[i].data, "fontmapfiles string body"); + } + gs_free_object(ctx->memory, ctx->fontmapfiles, "fontmapfiles array"); +} + +/* The fontmap file list doesn't extend, later settings in the command line override earlier ones + (Unlike the "-I" search paths above). + */ +int pdfi_add_fontmapfiles(pdf_context *ctx, const char *ppath, int l) +{ + int i, nfilenames = (l > 0) ? 1 : 0; + const char *p = ppath; + char *ps; + const char *pe = p + l + 1; + int code = 0; + + pdfi_free_fontmapfiles(ctx); + + for (ps = (char *)p; ps < pe; ps++) { + if (*ps == gp_file_name_list_separator) + nfilenames++; + } + if (nfilenames > 0) { + ctx->fontmapfiles = (gs_string *)gs_alloc_bytes(ctx->memory, sizeof(gs_string) * nfilenames, "array of fontmap files"); + if (ctx->fontmapfiles == NULL) { + return_error(gs_error_VMerror); + } + else { + memset(ctx->fontmapfiles, 0x00, sizeof(gs_string) * nfilenames); + ctx->num_fontmapfiles = nfilenames; + + for (i = 0; i < nfilenames; i++) { + for (ps = (char *)p; ps < pe; ps++) { + if (*ps == gp_file_name_list_separator) + break; + } + ctx->fontmapfiles[i].data = gs_alloc_bytes(ctx->memory, ps - p, "fontmap file name body"); + if (ctx->fontmapfiles[i].data == NULL) { + code = gs_note_error(gs_error_VMerror); + goto done; + } + memcpy(ctx->fontmapfiles[i].data, p, ps - p); + ctx->fontmapfiles[i].size = ps - p; + p = ps + 1; + } + } + } +done: + return code; } /***********************************************************************************/ @@ -1527,10 +1555,6 @@ pdf_context *pdfi_create_context(gs_memory_t *mem) ctx = (pdf_context *) gs_alloc_bytes(pmem, sizeof(pdf_context), "pdf_create_context"); -#if PDFI_LEAK_CHECK - ctx->memstat = mstat; -#endif - pgs = gs_gstate_alloc(pmem); if (!ctx || !pgs) @@ -1544,6 +1568,14 @@ pdf_context *pdfi_create_context(gs_memory_t *mem) memset(ctx, 0, sizeof(pdf_context)); ctx->memory = pmem; + ctx->type = PDF_CTX; + ctx->flags = 0; + ctx->refcnt = 1; + ctx->ctx = ctx; + +#if PDFI_LEAK_CHECK + ctx->memstat = mstat; +#endif ctx->stack_bot = (pdf_obj **)gs_alloc_bytes(ctx->memory, INITIAL_STACK_SIZE * sizeof (pdf_obj *), "pdf_imp_allocate_interp_stack"); if (ctx->stack_bot == NULL) { @@ -1575,7 +1607,20 @@ pdf_context *pdfi_create_context(gs_memory_t *mem) } ctx->pgs = pgs; - pdfi_gstate_set_client(ctx, pgs); + code = pdfi_gstate_set_client(ctx, pgs); + if (code < 0) { + gs_free_object(ctx->memory, ctx->font_dir, "pdf_create_context"); + gs_free_object(pmem, ctx->stack_bot, "pdf_create_context"); + gs_free_object(pmem, ctx, "pdf_create_context"); + gs_gstate_free(pgs); + return NULL; + } + + /* Some (but not all) path construction operations can either return + * an error or clamp values when out of range. In order to match Ghostscript's + * PDF interpreter written in PostScript, we need to clamp them. + */ + gs_setlimitclamp(pgs, true); /* Declare PDL client support for high level patterns, for the benefit * of pdfwrite and other high-level devices @@ -1606,6 +1651,13 @@ pdf_context *pdfi_create_context(gs_memory_t *mem) * grestore back to the initial state, it immediately saves another one. */ code = gs_gsave(ctx->pgs); + if (code < 0) { + gs_free_object(ctx->memory, ctx->font_dir, "pdf_create_context"); + gs_free_object(pmem, ctx->stack_bot, "pdf_create_context"); + gs_gstate_free(ctx->pgs); + gs_free_object(pmem, ctx, "pdf_create_context"); + return NULL; + } #if REFCNT_DEBUG ctx->UID = 1; #endif @@ -1734,6 +1786,16 @@ int pdfi_clear_context(pdf_context *ctx) ctx->PagesTree = NULL; } + if (ctx->args.cidsubstpath.data != NULL) { + gs_free_object(ctx->memory, ctx->args.cidsubstpath.data, "cidsubstpath.data"); + ctx->args.cidsubstpath.data = NULL; + } + + if (ctx->args.cidsubstfont.data != NULL) { + gs_free_object(ctx->memory, ctx->args.cidsubstfont.data, "cidsubstpath.data"); + ctx->args.cidsubstfont.data = NULL; + } + pdfi_free_cstring_array(ctx, &ctx->args.showannottypes); pdfi_free_cstring_array(ctx, &ctx->args.preserveannottypes); @@ -1859,6 +1921,8 @@ int pdfi_clear_context(pdf_context *ctx) pdfi_countdown(ctx->pdffontmap); ctx->pdffontmap = NULL; + pdfi_countdown(ctx->pdfnativefontmap); + ctx->pdfnativefontmap = NULL; return 0; } @@ -1895,7 +1959,16 @@ int pdfi_free_context(pdf_context *ctx) } pdfi_free_search_paths(ctx); + pdfi_free_fontmapfiles(ctx); + if (ctx->pdfcidfmap != NULL) { + pdfi_countdown(ctx->pdfcidfmap); + ctx->pdfcidfmap = NULL; + } + if (ctx->pdffontmap != NULL) { + pdfi_countdown(ctx->pdffontmap); + ctx->pdffontmap = NULL; + } gs_free_object(ctx->memory, ctx, "pdfi_free_context"); #if PDFI_LEAK_CHECK gs_memory_status(mem, &mstat); @@ -1918,21 +1991,39 @@ int pdfi_free_context(pdf_context *ctx) * it seems to be what happens, so we can't rely on grestore to put back the interpreter context, but must * do so ourselves. * - * Hence the 'from_PS' routine fills in pointers with the current context and procs, wit the expectation that + * Hence the 'from_PS' routine fills in pointers with the current context and procs, with the expectation that * these will be saved and used to restore the data in the 'to_PS' routine. */ -void pdfi_gstate_from_PS(pdf_context *ctx, gs_gstate *pgs, void **saved_client_data, gs_gstate_client_procs *saved_procs) +/* NOTE see the comments in zpdfops.c just under the declaration of the pdfi_switch_t strcuture regarding + * complications with the ICC profile cache. + */ + +int pdfi_gstate_from_PS(pdf_context *ctx, gs_gstate *pgs, pdfi_switch_t *i_switch, gsicc_profile_cache_t *profile_cache) { - *saved_client_data = pgs->client_data; - *saved_procs = pgs->client_procs; - pdfi_gstate_set_client(ctx, pgs); - return; + int code; + i_switch->pgs = ctx->pgs; + i_switch->procs = pgs->client_procs; + i_switch->client_data = (void *)pgs->client_data; + i_switch->profile_cache = pgs->icc_profile_cache; + code = pdfi_gstate_set_client(ctx, pgs); + if (code < 0) + return code; + i_switch->psfont = pgs->font; + pgs->icc_profile_cache = profile_cache; + rc_increment(pgs->icc_profile_cache); + pgs->font = NULL; + ctx->pgs = pgs; + return code; } -void pdfi_gstate_to_PS(pdf_context *ctx, gs_gstate *pgs, void *client_data, const gs_gstate_client_procs *procs) +void pdfi_gstate_to_PS(pdf_context *ctx, gs_gstate *pgs, pdfi_switch_t *i_switch) { pgs->client_procs.free(pgs->client_data, pgs->memory, pgs); pgs->client_data = NULL; - gs_gstate_set_client(pgs, client_data, procs, true); - return; + rc_decrement(pgs->icc_profile_cache, "pdfi_gstate_to_PS"); + pgs->icc_profile_cache = i_switch->profile_cache; + gs_gstate_set_client(pgs, i_switch->client_data, &i_switch->procs, true); + ctx->pgs->font = NULL; + ctx->pgs = i_switch->pgs; + pgs->font = i_switch->psfont; } diff --git a/pdf/ghostpdf.h b/pdf/ghostpdf.h index 3cc1b8cd..49c91808 100644 --- a/pdf/ghostpdf.h +++ b/pdf/ghostpdf.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -32,6 +32,18 @@ #define PDFI_LEAK_CHECK 0 #endif +/* A structure for setting/resetting the interpreter graphics state + * and some graphics state content when switching between Ghostscript + * and pdfi, when running under GS. + */ +typedef struct pdf_context_switch { + gs_gstate *pgs; + gs_font *psfont; + gs_gstate_client_procs procs; + void *client_data; + void *profile_cache; +} pdfi_switch_t; + /* * The interpreter context. */ @@ -42,82 +54,13 @@ * how to deal with this. */ typedef enum pdf_error_e { - E_PDF_NOERROR, - E_PDF_NOHEADER, - E_PDF_NOHEADERVERSION, - E_PDF_NOSTARTXREF, - E_PDF_BADSTARTXREF, - E_PDF_BADXREFSTREAM, - E_PDF_BADXREF, - E_PDF_SHORTXREF, - E_PDF_MISSINGENDSTREAM, - E_PDF_UNKNOWNFILTER, - E_PDF_MISSINGWHITESPACE, - E_PDF_MALFORMEDNUMBER, - E_PDF_UNESCAPEDSTRING, - E_PDF_BADOBJNUMBER, - E_PDF_MISSINGENDOBJ, - E_PDF_TOKENERROR, - E_PDF_KEYWORDTOOLONG, - E_PDF_BADPAGETYPE, - E_PDF_CIRCULARREF, - E_PDF_UNREPAIRABLE, - E_PDF_REPAIRED, - E_PDF_BADSTREAM, - E_PDF_MISSINGOBJ, - E_PDF_BADPAGEDICT, - E_PDF_OUTOFMEMORY, - E_PDF_PAGEDICTERROR, - E_PDF_STACKUNDERFLOWERROR, - E_PDF_BADSTREAMDICT, - E_PDF_INHERITED_STREAM_RESOURCE, - E_PDF_DEREF_FREE_OBJ, - E_PDF_INVALID_TRANS_XOBJECT, - E_PDF_NO_SUBTYPE, - E_PDF_IMAGECOLOR_ERROR, - E_PDF_MAX_ERROR /* Must be last entry, add new errors immediately before this and update pdf_error_strings in ghostpdf.c */ +#include "pdf_errors.h" + E_PDF_MAX_ERROR /* last entry */ }pdf_error; typedef enum pdf_warning_e { - W_PDF_NOWARNING, - W_PDF_BAD_XREF_SIZE, - W_PDF_BAD_INLINEFILTER, - W_PDF_BAD_INLINECOLORSPACE, - W_PDF_BAD_INLINEIMAGEKEY, - W_PDF_IMAGE_ERROR, - W_PDF_BAD_IMAGEDICT, - W_PDF_TOOMANYQ, - W_PDF_TOOMANYq, - W_PDF_STACKGARBAGE, - W_PDF_STACKUNDERFLOW, - W_PDF_GROUPERROR, - W_PDF_OPINVALIDINTEXT, - W_PDF_NOTINCHARPROC, - W_PDF_NESTEDTEXTBLOCK, - W_PDF_ETNOTEXTBLOCK, - W_PDF_TEXTOPNOBT, - W_PDF_DEGENERATETM, - W_PDF_BADICC_USE_ALT, - W_PDF_BADICC_USECOMPS, - W_PDF_BADTRSWITCH, - W_PDF_BADSHADING, - W_PDF_BADPATTERN, - W_PDF_NONSTANDARD_OP, - W_PDF_NUM_EXPONENT, - W_PDF_STREAM_HAS_CONTENTS, - W_PDF_STREAM_BAD_DECODEPARMS, - W_PDF_MASK_ERROR, - W_PDF_ANNOT_AP_ERROR, - W_PDF_BAD_NAME_ESCAPE, - W_PDF_TYPECHECK, - W_PDF_BAD_TRAILER, - W_PDF_ANNOT_ERROR, - W_PDF_BAD_ICC_PROFILE_LINK, - W_PDF_OVERFLOW_REAL, - W_PDF_INVALID_REAL, - W_PDF_DEVICEN_USES_ALL, - W_PDF_BAD_MEDIABOX, - W_PDF_MAX_WARNING /* Must be last entry, add new warnings immediately before this and update pdf_warning_strings in ghostpdf.c */ +#include "pdf_warnings.h" + W_PDF_MAX_WARNING /* last entry */ } pdf_warning; #define PDF_ERROR_BYTE_SIZE ((E_PDF_MAX_ERROR - 1) / (sizeof(char) * 8) + 1) @@ -132,12 +75,11 @@ typedef enum pdf_crypt_filter_e { CRYPT_AESV3, /* 256-bit AES */ } pdf_crypt_filter; - -typedef enum pdf_overprint_control_e { - PDF_OVERPRINT_ENABLE = 0,/* Default */ - PDF_OVERPRINT_DISABLE, - PDF_OVERPRINT_SIMULATE -} pdf_overprint_control_t; +typedef enum pdf_type3_d_type_e { + pdf_type3_d_none, + pdf_type3_d0, + pdf_type3_d1 +} pdf_type3_d_type; #define INITIAL_STACK_SIZE 32 #define MAX_STACK_SIZE 524288 @@ -188,6 +130,7 @@ typedef struct cmd_args_s { bool dopdfmarks; bool preserveannots; char **preserveannottypes; /* Null terminated array of strings, NULL if none */ + bool preservemarkedcontent; bool nouserunit; bool renderttnotdef; bool pdfinfo; @@ -196,11 +139,14 @@ typedef struct cmd_args_s { bool ditherppi; int PDFX3Profile_num; char *UseOutputIntent; - pdf_overprint_control_t overprint_control; /* Overprint -- enabled, disabled, simulated */ char *PageList; bool QUIET; bool verbose_errors; bool verbose_warnings; + gs_string cidsubstpath; + gs_string cidsubstfont; + bool ignoretounicode; + bool nonativefontmap; } cmd_args_t; typedef struct encryption_state_s { @@ -285,8 +231,14 @@ typedef struct text_state_s { /* We need to know if we're in a type 3 CharProc which has executed a 'd1' operator. * Colour operators are technically invalid if we are in a 'd1' context and we must * ignore them. + * OSS-fuzz #45320 has a type 3 font with a BuildChar which has a 'RG' before the + * d1. This is (obviously) illegal because the spec says the first operation must + * be either a d0 or d1, in addition because of the graphics state depth hackery + * (see comments in pdf_d0() in pdf_font.c) this messes up the reference counting + * of the colour spaces, leading to a crash. So what was a boolean flag is now an + * enumerated type; pdf_type3_d_none, pdf_type3_d0 or pdf_type3_d1. */ - bool CharProc_is_d1; + pdf_type3_d_type CharProc_d_type; /* If there is no current point when we do a BT we start by doing a 0 0 moveto in order * to establish an initial point. However, this also starts a path. When we finish * off with a BT we need to clear that path by doing a newpath, otherwise we might @@ -342,6 +294,7 @@ typedef struct search_paths_s typedef struct pdf_context_s { + pdf_obj_common; void *instance; gs_memory_t *memory; @@ -376,6 +329,7 @@ typedef struct pdf_context_s /* Optional/Marked Content stuff */ void *OFFlevels; uint64_t BMClevel; + bool BDCWasOC; /* Bitfields recording whether any errors or warnings were encountered */ char pdf_errors[PDF_ERROR_BYTE_SIZE]; @@ -424,6 +378,9 @@ typedef struct pdf_context_s /* Document level PDF objects */ xref_table_t *xref_table; + /* Warning! Do not use ctx->Trailer directly as it may be replaced if the file is repaired. + * See pdf_doc.c, pdf_read_Root() + */ pdf_dict *Trailer; pdf_dict *Root; pdf_dict *Info; @@ -433,9 +390,6 @@ typedef struct pdf_context_s pdf_dict *AcroForm; bool NeedAppearances; /* From AcroForm, if any */ - - /* Interpreter level PDF objects */ - /* The interpreter operand stack */ uint32_t stack_size; pdf_obj **stack_bot; @@ -463,8 +417,13 @@ typedef struct pdf_context_s /* A name table :-( */ pdfi_name_entry_t *name_table; + gs_string *fontmapfiles; + int num_fontmapfiles; + search_paths_t search_paths; pdf_dict *pdffontmap; + pdf_dict *pdfnativefontmap; /* Explicit mappings take precedence, hence we need separate dictionaries */ + pdf_dict *pdfcidfmap; /* These function pointers can be replaced by ones intended to replicate * PostScript functionality when running inside the Ghostscript PostScript @@ -475,7 +434,7 @@ typedef struct pdf_context_s int (*get_glyph_index)(gs_font *font, byte *str, uint size, uint *glyph); #if REFCNT_DEBUG - uint64_t UID; + uint64_t ref_UID; #endif #if CACHE_STATISTICS uint64_t hits; @@ -493,6 +452,7 @@ typedef struct pdf_context_s int pdfi_add_paths_to_search_paths(pdf_context *ctx, const char *ppath, int l, bool fontpath); int pdfi_add_initial_paths_to_search_paths(pdf_context *ctx, const char *ppath, int l); +int pdfi_add_fontmapfiles(pdf_context *ctx, const char *ppath, int l); pdf_context *pdfi_create_context(gs_memory_t *pmem); int pdfi_clear_context(pdf_context *ctx); @@ -506,9 +466,10 @@ int pdfi_set_input_stream(pdf_context *ctx, stream *stm); int pdfi_process_pdf_file(pdf_context *ctx, char *filename); int pdfi_prep_collection(pdf_context *ctx, uint64_t *TotalFiles, char ***names_array); int pdfi_close_pdf_file(pdf_context *ctx); -void pdfi_gstate_from_PS(pdf_context *ctx, gs_gstate *pgs, void **saved_client_data, gs_gstate_client_procs *saved_procs); -void pdfi_gstate_to_PS(pdf_context *ctx, gs_gstate *pgs, void *client_data, const gs_gstate_client_procs *procs); +int pdfi_gstate_from_PS(pdf_context *ctx, gs_gstate *pgs, pdfi_switch_t *i_switch, gsicc_profile_cache_t *profile_cache); +void pdfi_gstate_to_PS(pdf_context *ctx, gs_gstate *pgs, pdfi_switch_t *i_switch); +void pdfi_report_errors(pdf_context *ctx); void pdfi_verbose_error(pdf_context *ctx, int gs_error, const char *gs_lib_function, int pdfi_error, const char *pdfi_function_name, const char *extra_info); void pdfi_verbose_warning(pdf_context *ctx, int gs_error, const char *gs_lib_function, int pdfi_warning, const char *pdfi_function_name, const char *extra_info); void pdfi_log_info(pdf_context *ctx, const char *pdfi_function, const char *info); @@ -528,6 +489,10 @@ static inline void pdfi_set_warning(pdf_context *ctx, int gs_error, const char * pdfi_verbose_warning(ctx, gs_error, gs_lib_function, pdfi_warning, pdfi_function_name, extra_info); } +/* Variants of the above that work in a printf style. */ +void pdfi_set_error_var(pdf_context *ctx, int gs_error, const char *gs_lib_function, pdf_error pdfi_error, const char *pdfi_function_name, const char *fmt, ...); +void pdfi_set_warning_var(pdf_context *ctx, int gs_error, const char *gs_lib_function, pdf_warning pdfi_warning, const char *pdfi_function_name, const char *fmt, ...); + #define PURGE_CACHE_PER_PAGE 0 #if PURGE_CACHE_PER_PAGE diff --git a/pdf/pdf.mak b/pdf/pdf.mak index ccb70916..7040a72e 100644 --- a/pdf/pdf.mak +++ b/pdf/pdf.mak @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2021 Artifex Software, Inc. +# Copyright (C) 2018-2022 Artifex Software, Inc. # All Rights Reserved. # # This software is provided AS-IS with no warranty, either express or @@ -60,7 +60,7 @@ PDFINCLUDES=$(PDFSRC)*.h $(GLGEN)arch.h $(strmio_h) $(stream_h) $(gsmatrix_h) $( $(jpeglib__h) $(sdct_h) $(spdiffx_h) $(PDFOBJ)ghostpdf.$(OBJ): $(PDFSRC)ghostpdf.c $(PDFINCLUDES) $(plmain_h) $(stream_h) $(strmio_h) \ - $(gsmchunk_h) $(PDF_MAK) $(MAKEDIRS) + $(gsmchunk_h) $(gsstate_h) $(gsicc_manage_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)ghostpdf.c $(PDFO_)ghostpdf.$(OBJ) $(PDFOBJ)pdf_dict.$(OBJ): $(PDFSRC)pdf_dict.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS) @@ -73,18 +73,18 @@ $(PDFOBJ)pdf_xref.$(OBJ): $(PDFSRC)pdf_xref.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDI $(PDFCCC) $(PDFSRC)pdf_xref.c $(PDFO_)pdf_xref.$(OBJ) $(PDFOBJ)pdf_fapi.$(OBJ): $(PDFSRC)pdf_fapi.c $(PDFINCLUDES) \ - $(memory__h) $(gsmemory_h) $(gserrors_h) $(gxdevice_h) $(gxfont_h) $(gxfcid_h) \ - $(gzstate_h) $(gxchar_h) $(gdebug_h) $(gxfapi_h) $(gscoord_h) $(gspath_h) $(gscencs_h) \ - $(gsagl_h) \ - $(PDF_MAK) $(MAKEDIRS) + $(memory__h) $(gsmemory_h) $(gserrors_h) $(gxdevice_h) $(gxfont_h) $(gxfont0_h) \ + $(gxfcid_h) $(gzstate_h) $(gxchar_h) $(gdebug_h) $(gxfapi_h) $(gscoord_h) \ + $(gspath_h) $(gscencs_h) $(gsagl_h) $(gxfont1_h) $(gscrypt1_h) \ + $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_fapi.c $(PDFO_)pdf_fapi.$(OBJ) $(PDFOBJ)pdf_font.$(OBJ): $(PDFSRC)pdf_font.c $(PDFINCLUDES) $(PDF_MAK) \ - $(gscencs_h) $(stream_h) $(strmio_h) $(MAKEDIRS) + $(gscencs_h) $(stream_h) $(strmio_h) $(gsstate_h) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_font.c $(PDFO_)pdf_font.$(OBJ) $(PDFOBJ)pdf_font0.$(OBJ): $(PDFSRC)pdf_font0.c $(PDFINCLUDES) $(PDF_MAK) \ - $(gxfont_h) $(gxfont0_h) $(MAKEDIRS) + $(gxfont_h) $(gxfont0_h) $(gsutil_h) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_font0.c $(PDFO_)pdf_font0.$(OBJ) $(PDFOBJ)pdf_ciddec.$(OBJ): $(PDFSRC)pdf_ciddec.c $(PDFINCLUDES) $(MAKEDIRS) @@ -92,11 +92,12 @@ $(PDFOBJ)pdf_ciddec.$(OBJ): $(PDFSRC)pdf_ciddec.c $(PDFINCLUDES) $(MAKEDIRS) $(PDFOBJ)pdf_font1.$(OBJ): $(PDFSRC)pdf_font1.c $(PDFINCLUDES) \ $(gsgdata_h) $(gstype1_h) $(gscencs_h) $(strmio_h) $(strimpl_h) $(stream_h) \ - $(sfilter_h) $(PDF_MAK) $(MAKEDIRS) + $(sfilter_h) $(gxtype1_h) $(gsutil_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_font1.c $(PDFO_)pdf_font1.$(OBJ) $(PDFOBJ)pdf_font1C.$(OBJ): $(PDFSRC)pdf_font1C.c $(PDFINCLUDES) \ $(gscedata_h) $(gscencs_h) $(gxfont0_h) $(gxfcid_h) \ + $(gxtype1_h) $(gsutil_h) \ $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_font1C.c $(PDFO_)pdf_font1C.$(OBJ) @@ -105,63 +106,66 @@ $(PDFOBJ)pdf_fontps.$(OBJ): $(PDFSRC)pdf_fontps.c $(PDFINCLUDES) \ $(PDFCCC) $(PDFSRC)pdf_fontps.c $(PDFO_)pdf_fontps.$(OBJ) $(PDFOBJ)pdf_font3.$(OBJ): $(PDFSRC)pdf_font3.c $(PDFINCLUDES) \ - $(gscencs_h) $(gscedata_h) $(gsccode_h) $(gsuid_h) \ + $(gscencs_h) $(gscedata_h) $(gsccode_h) $(gsuid_h) $(gsutil_h) \ $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_font3.c $(PDFO_)pdf_font3.$(OBJ) $(PDFOBJ)pdf_fontTT.$(OBJ): $(PDFSRC)pdf_fontTT.c $(PDFINCLUDES) \ - $(gxfont42_h) $(gscencs_h) $(gsagl_h) $(PDF_MAK) $(MAKEDIRS) + $(gxfont42_h) $(gscencs_h) $(gsagl_h) $(gsutil_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_fontTT.c $(PDFO_)pdf_fontTT.$(OBJ) $(PDFOBJ)pdf_font9.$(OBJ): $(PDFSRC)pdf_font9.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_font9.c $(PDFO_)pdf_font9.$(OBJ) $(PDFOBJ)pdf_font11.$(OBJ): $(PDFSRC)pdf_font11.c $(PDFINCLUDES) $(gxfont42_h) \ - $(gxfcid_h) $(PDF_MAK) $(MAKEDIRS) + $(gxfcid_h) $(gsutil_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_font11.c $(PDFO_)pdf_font11.$(OBJ) $(PDFOBJ)pdf_cmap.$(OBJ): $(PDFSRC)pdf_cmap.c $(PDFINCLUDES) \ - $(strmio_h) $(stream_h) $(scanchar_h) $(PDF_MAK) $(MAKEDIRS) + $(strmio_h) $(stream_h) $(scanchar_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_cmap.c $(PDFO_)pdf_cmap.$(OBJ) $(PDFOBJ)pdf_fmap.$(OBJ): $(PDFSRC)pdf_fmap.c $(PDFINCLUDES) \ - $(strmio_h) $(stream_h) $(scanchar_h) $(PDF_MAK) $(MAKEDIRS) + $(strmio_h) $(stream_h) $(scanchar_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_fmap.c $(PDFO_)pdf_fmap.$(OBJ) $(PDFOBJ)pdf_text.$(OBJ): $(PDFSRC)pdf_text.c $(PDFINCLUDES) \ - $(gsstate_h) $(gsmatrix_h) $(gdevbbox_h) \ - $(PDF_MAK) $(MAKEDIRS) + $(gsstate_h) $(gsmatrix_h) $(gdevbbox_h) $(gspaint_h) \ + $(gscoord_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_text.c $(PDFO_)pdf_text.$(OBJ) $(PDFOBJ)pdf_shading.$(OBJ): $(PDFSRC)pdf_shading.c $(PDFINCLUDES) \ - $(gxshade_h) $(gsptype2_h) $(gsfunc0_h) \ - $(PDF_MAK) $(MAKEDIRS) + $(gsfunc3_h) $(gxshade_h) $(gsptype2_h) $(gsfunc0_h) \ + $(gscolor3_h) $(gsstate_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_shading.c $(PDFO_)pdf_shading.$(OBJ) $(PDFOBJ)pdf_func.$(OBJ): $(PDFSRC)pdf_func.c $(PDFINCLUDES) \ - $(gsdsrc_h) $(gsfunc0_h) $(gsfunc3_h) $(gsfunc4_h) $(stream_h) \ - $(PDF_MAK) $(MAKEDIRS) + $(gsdsrc_h) $(gsfunc0_h) $(gsfunc3_h) $(gsfunc4_h) $(stream_h) \ + $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_func.c $(PDFO_)pdf_func.$(OBJ) $(PDFOBJ)pdf_image.$(OBJ): $(PDFSRC)pdf_image.c $(PDFINCLUDES) \ - $(stream_h) $(gspath2_h) $(gsiparm4_h) $(gsiparm3_h) $(gsiparm3x_h) \ - $(gsform1_h) $(gstrans_h) \ + $(stream_h) $(gsicc_cache_h) $(gspath2_h) $(gsiparm4_h) $(gsiparm3_h) $(gsiparm3x_h) \ + $(gsform1_h) $(gstrans_h) $(gxdevsop_h) $(gspath_h) $(gsstate_h) $(gscoord_h) \ $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_image.c $(PDFO_)pdf_image.$(OBJ) -$(PDFOBJ)pdf_page.$(OBJ): $(PDFSRC)pdf_page.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS) +$(PDFOBJ)pdf_page.$(OBJ): $(PDFSRC)pdf_page.c $(PDFINCLUDES) \ + $(gscoord_h) $(gspaint_h) $(gsstate_h) $(gspath2_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_page.c $(PDFO_)pdf_page.$(OBJ) $(PDFOBJ)pdf_annot.$(OBJ): $(PDFSRC)pdf_annot.c $(PDFINCLUDES) $(gspath2_h) $(gxfarith_h) \ - $(PDF_MAK) $(MAKEDIRS) + $(gxdevsop_h) $(gsstrtok_h) $(gscoord_h) $(gsline_h) $(gsutil_h) \ + $(gspaint_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_annot.c $(PDFO_)pdf_annot.$(OBJ) -$(PDFOBJ)pdf_mark.$(OBJ): $(PDFSRC)pdf_mark.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS) +$(PDFOBJ)pdf_mark.$(OBJ): $(PDFSRC)pdf_mark.c $(PDFINCLUDES) $(gscoord_h) \ + $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_mark.c $(PDFO_)pdf_mark.$(OBJ) $(PDFOBJ)pdf_sec.$(OBJ): $(PDFSRC)pdf_sec.c $(PDFINCLUDES) \ - $(strmio_h) $(smd5_h) $(sarc4_h) $(aes_h) $(sha2_h) \ - $(PDF_MAK) $(MAKEDIRS) + $(strmio_h) $(smd5_h) $(sarc4_h) $(aes_h) $(sha2_h) \ + $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_sec.c $(PDFO_)pdf_sec.$(OBJ) $(PDFOBJ)pdf_utf8_mswin32_.$(OBJ): $(PDFSRC)pdf_utf8.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS) @@ -182,72 +186,79 @@ $(PDFOBJ)pdf_utf8.$(OBJ): $(PDFOBJ)pdf_utf8_$(GSPLATFORM).$(OBJ) $(PDF_MAK) $(MA $(PDFOBJ)pdf_stack.$(OBJ): $(PDFSRC)pdf_stack.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_stack.c $(PDFO_)pdf_stack.$(OBJ) -$(PDFOBJ)pdf_gstate.$(OBJ): $(PDFSRC)pdf_gstate.c $(PDFINCLUDES) \ - $(gsmatrix_h) $(gslparam_h) $(gstparam_h) $(gxdht_h) $(gxht_h) $(gzht_h) $(gsht_h) \ - $(PDF_MAK) $(MAKEDIRS) +$(PDFOBJ)pdf_gstate.$(OBJ): $(PDFSRC)pdf_gstate.c $(PDFINCLUDES) $(gsstate_h) \ + $(gsmatrix_h) $(gslparam_h) $(gstparam_h) $(gxdht_h) $(gxht_h) $(gzht_h) $(gsht_h) \ + $(gscoord_h) $(gsutil_h) $(gscolor3_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_gstate.c $(PDFO_)pdf_gstate.$(OBJ) $(PDFOBJ)pdf_colour.$(OBJ): $(PDFSRC)pdf_colour.c $(PDFINCLUDES) \ - $(gsicc_manage_h) $(gsicc_create_h) $(gsptype2_h) $(gscsepr_h) \ - $(stream_h) $(strmio_h) $(gscdevn_h) $(gxcdevn_h) $(PDF_MAK) $(MAKEDIRS) + $(gsicc_manage_h) $(gsicc_profilecache_h) $(gsicc_create_h) $(gsicc_cache_h) $(gsptype2_h) $(gscsepr_h) \ + $(stream_h) $(strmio_h) $(gscdevn_h) $(gxcdevn_h) $(gscolor_h) $(gsicc_h) $(gsstate_h) \ + $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_colour.c $(PDFO_)pdf_colour.$(OBJ) $(PDFOBJ)pdf_pattern.$(OBJ): $(PDFSRC)pdf_pattern.c $(PDFINCLUDES) \ - $(gsicc_manage_h) $(gsicc_profilecache_h) $(gsicc_create_h) $(gscsepr_h) \ - $(stream_h) $(strmio_h) $(gscdevn_h) \ - $(PDF_MAK) $(MAKEDIRS) + $(gsicc_manage_h) $(gsicc_profilecache_h) $(gsicc_create_h) $(gsptype2_h) \ + $(gxdevsop_h) $(gscsepr_h) $(stream_h) $(strmio_h) $(gscdevn_h) $(gscoord_h) \ + $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_pattern.c $(PDFO_)pdf_pattern.$(OBJ) -$(PDFOBJ)pdf_path.$(OBJ): $(PDFSRC)pdf_path.c $(PDFINCLUDES) $(gstypes_h) $(PDF_MAK) $(MAKEDIRS) +$(PDFOBJ)pdf_path.$(OBJ): $(PDFSRC)pdf_path.c $(PDFINCLUDES) $(gstypes_h) \ + $(gspath_h) $(gspaint_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_path.c $(PDFO_)pdf_path.$(OBJ) $(PDFOBJ)pdf_loop_detect.$(OBJ): $(PDFSRC)pdf_loop_detect.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_loop_detect.c $(PDFO_)pdf_loop_detect.$(OBJ) $(PDFOBJ)pdf_int.$(OBJ): $(PDFSRC)pdf_int.c $(PDFINCLUDES) $(plmain_h) \ - $(stream_h) $(strmio_h) $(PDF_MAK) $(MAKEDIRS) + $(stream_h) $(strmio_h) $(gsgstate_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_int.c $(PDFO_)pdf_int.$(OBJ) $(PDFOBJ)pdf_file_luratech.$(OBJ): $(PDFSRC)pdf_file.c $(sjpeg_h) $(stream_h) $(strimpl_h) \ - $(strmio_h) $(gpmisc_h) $(simscale_h) $(szlibx_h) $(spngx_h) $(spdiffx_h) $(slzw_h) $(sstring_h) \ - $(sa85d_h) $(scfx_h) $(srlx_h) $(jpeglib__h) $(sdct_h) $(sjpeg_h) $(sfilter_h) $(sarc4_h) \ - $(saes_h) $(ssha2_h) $(sjbig2_luratech_h) $(sjpx_luratech_h) \ + $(strmio_h) $(gpmisc_h) $(simscale_h) $(szlibx_h) $(spngx_h) $(spdiffx_h) $(slzw_h) \ + $(sstring_h) $(sa85d_h) $(scfx_h) $(srlx_h) $(jpeglib__h) $(sdct_h) $(sjpeg_h) \ + $(sfilter_h) $(sarc4_h) $(saes_h) $(ssha2_h) $(gxdevsop_h) \ + $(sjbig2_luratech_h) $(sjpx_luratech_h) \ $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS) $(PDFLURCC) $(PDFSRC)pdf_file.c $(PDFO_)pdf_file_luratech.$(OBJ) $(PDFOBJ)pdf_file_jbig2dec.$(OBJ): $(PDFSRC)pdf_file.c $(sjpeg_h) $(stream_h) $(strimpl_h) \ - $(strmio_h) $(simscale_h) $(szlibx_h) $(spngx_h) $(spdiffx_h) $(slzw_h) $(sstring_h) \ - $(sa85d_h) $(scfx_h) $(srlx_h) $(jpeglib__h) $(sdct_h) $(sjpeg_h) $(sfilter_h) $(sarc4_h) \ - $(saes_h) $(ssha2_h) $(sjbig2_h) $(sjpx_openjpeg_h) \ + $(strmio_h) $(gpmisc_h) $(simscale_h) $(szlibx_h) $(spngx_h) $(spdiffx_h) $(slzw_h) \ + $(sstring_h) $(sa85d_h) $(scfx_h) $(srlx_h) $(jpeglib__h) $(sdct_h) $(sjpeg_h) \ + $(sfilter_h) $(sarc4_h) $(saes_h) $(ssha2_h) $(gxdevsop_h) \ + $(sjpx_openjpeg_h) \ $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS) $(PDFJB2CC) $(PDFSRC)pdf_file.c $(PDFO_)pdf_file_jbig2dec.$(OBJ) $(PDFOBJ)pdf_file.$(OBJ): $(PDFOBJ)pdf_file_$(JBIG2_LIB).$(OBJ) $(CP_) $(PDFOBJ)pdf_file_$(JBIG2_LIB).$(OBJ) $(PDFOBJ)pdf_file.$(OBJ) -$(PDFOBJ)pdf_trans.$(OBJ): $(PDFSRC)pdf_trans.c $(PDFINCLUDES) $(gstparam_h) $(PDF_MAK) $(MAKEDIRS) +$(PDFOBJ)pdf_trans.$(OBJ): $(PDFSRC)pdf_trans.c $(PDFINCLUDES) $(gstparam_h) \ + $(gsicc_manage_h) $(gscoord_h) $(gsstate_h) $(gspath_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_trans.c $(PDFO_)pdf_trans.$(OBJ) -$(PDFOBJ)pdf_device.$(OBJ): $(PDFSRC)pdf_device.c $(PDFINCLUDES) $(gdevvec_h) \ - $(PDF_MAK) $(MAKEDIRS) +$(PDFOBJ)pdf_device.$(OBJ): $(PDFSRC)pdf_device.c $(PDFINCLUDES) $(gsdevice_h) $(gspaint_h) \ + $(gdevvec_h) $(gxdevsop_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_device.c $(PDFO_)pdf_device.$(OBJ) -$(PDFOBJ)pdf_misc.$(OBJ): $(PDFSRC)pdf_misc.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS) +$(PDFOBJ)pdf_misc.$(OBJ): $(PDFSRC)pdf_misc.c $(PDFINCLUDES) $(gspath_h) $(gspaint_h) \ + $(gsicc_manage_h) $(gsstate_h) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_misc.c $(PDFO_)pdf_misc.$(OBJ) $(PDFOBJ)pdf_optcontent.$(OBJ): $(PDFSRC)pdf_optcontent.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_optcontent.c $(PDFO_)pdf_optcontent.$(OBJ) -$(PDFOBJ)pdf_check.$(OBJ): $(PDFSRC)pdf_check.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS) +$(PDFOBJ)pdf_check.$(OBJ): $(PDFSRC)pdf_check.c $(PDFINCLUDES) $(gsdevice_h) $(gspaint_h) \ + $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_check.c $(PDFO_)pdf_check.$(OBJ) $(PDFOBJ)pdf_deref.$(OBJ): $(PDFSRC)pdf_deref.c $(PDFINCLUDES) $(strmio_h) $(stream_h) \ - $(PDF_MAK) $(MAKEDIRS) + $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_deref.c $(PDFO_)pdf_deref.$(OBJ) $(PDFOBJ)pdf_repair.$(OBJ): $(PDFSRC)pdf_repair.c $(PDFINCLUDES) \ - $(strmio_h) $(stream_h) \ - $(PDF_MAK) $(MAKEDIRS) + $(strmio_h) $(stream_h) \ + $(PDF_MAK) $(MAKEDIRS) $(PDFCCC) $(PDFSRC)pdf_repair.c $(PDFO_)pdf_repair.$(OBJ) $(PDFOBJ)pdf_obj.$(OBJ): $(PDFSRC)pdf_obj.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS) diff --git a/pdf/pdf_annot.c b/pdf/pdf_annot.c index 116081c2..7b96be1c 100644 --- a/pdf/pdf_annot.c +++ b/pdf/pdf_annot.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -24,6 +24,7 @@ #include "pdf_loop_detect.h" #include "pdf_colour.h" #include "pdf_trans.h" +#include "pdf_font_types.h" #include "pdf_gstate.h" #include "pdf_misc.h" #include "pdf_optcontent.h" @@ -1076,8 +1077,7 @@ pdfi_annot_display_formatted_text(pdf_context *ctx, pdf_dict *annot, code = pdfi_string_bbox(ctx, temp_string, &bbox, &awidth, false); if (code < 0) goto exit; - if (linestart || ((x + awidth.x) <= x_max)) { - } else { + if (!linestart && ((x + awidth.x) > x_max)) { x = x_start; linestart = true; } @@ -1447,8 +1447,8 @@ static int pdfi_annot_draw_LE_one(pdf_context *ctx, pdf_dict *annot, pdf_name *L } if (!dispatch_ptr->name) { char str[100]; - memcpy(str, (const char *)LE->data, LE->length); - str[LE->length] = '\0'; + memcpy(str, (const char *)LE->data, LE->length < 100 ? LE->length : 99); + str[LE->length < 100 ? LE->length : 99] = '\0'; dbgmprintf1(ctx->memory, "ANNOT: WARNING No handler for LE %s\n", str); } @@ -3091,6 +3091,11 @@ static int pdfi_annot_draw_PolyLine(pdf_context *ctx, pdf_dict *annot, pdf_obj * code = pdfi_dict_knownget_type(ctx, annot, "Vertices", PDF_ARRAY, (pdf_obj **)&Vertices); if (code < 0) goto exit; + if (code == 0) { + code = gs_note_error(gs_error_undefined); + goto exit; + } + size = pdfi_array_size(Vertices); if (size == 0) { code = 0; @@ -3155,7 +3160,7 @@ static int pdfi_annot_draw_Polygon(pdf_context *ctx, pdf_dict *annot, pdf_obj *N if (code < 0) goto exit1; code = pdfi_dict_knownget_type(ctx, annot, "Vertices", PDF_ARRAY, (pdf_obj **)&Vertices); - if (code < 0) goto exit; + if (code <= 0) goto exit; code = pdfi_annot_path_array(ctx, annot, Vertices); if (code < 0) goto exit1; @@ -3708,8 +3713,8 @@ static int pdfi_annot_draw_NotImplemented(pdf_context *ctx, pdf_dict *annot, pdf code = pdfi_dict_get_type(ctx, annot, "Subtype", PDF_NAME, (pdf_obj **)&Subtype); if (code < 0) goto exit; - memcpy(str, (const char *)Subtype->data, Subtype->length); - str[Subtype->length] = '\0'; + memcpy(str, (const char *)Subtype->data, Subtype->length < 100 ? Subtype->length : 99); + str[Subtype->length < 100 ? Subtype->length : 99] = '\0'; dbgmprintf1(ctx->memory, "ANNOT: No AP, default appearance for Subtype %s Not Implemented\n", str); exit: @@ -3880,8 +3885,8 @@ static int pdfi_annot_draw(pdf_context *ctx, pdf_dict *annot, pdf_name *subtype) } if (!dispatch_ptr->subtype) { char str[100]; - memcpy(str, (const char *)subtype->data, subtype->length); - str[subtype->length] = '\0'; + memcpy(str, (const char *)subtype->data, subtype->length < 100 ? subtype->length : 99); + str[subtype->length < 100 ? subtype->length : 99] = '\0'; dbgmprintf1(ctx->memory, "ANNOT: No handler for subtype %s\n", str); /* Not necessarily an error? We can just render the AP if there is one */ @@ -4107,6 +4112,117 @@ static int pdfi_annot_preserve_modAP(pdf_context *ctx, pdf_dict *annot, pdf_name /* Make a temporary copy of the annotation dict with some fields left out or * modified, then do a pdfmark on it */ + +const char *PermittedKeys[] = { + /* These keys are valid for all annotation types, we specifically do not allow /P or /Parent */ + "Type", + "Subtype", + "Rect", + "Contents", + "NM", + "M", + "F", + "AP", + "AS", + "Border", + "C", + "StructParent", + "OC", + "AF", + "ca", + "CA", + "BM", + "Lang", + /* Keys by annotation type (some are common to more than one type, only one entry per key) */ + /* Markup Annotations we specifically do not permit RT, IRT or Popup */ + "T", + "RC", + "CreationDate", + "Subj", + "IT", + "ExData", + /* Text annotations */ + "Open", + "Name", + "State", + "StateModel", + /* This isn't specified as being allowed, but Acrobat does something with it, so we need to preserve it */ + "Rotate", + /* Link annotations */ + "A", + "Dest", + "H", + "PA", + "QuadPoints", + /* FreeText annotations */ + "DA", + "Q", + "DS", + "CL", + "IT", + "BE", + "RD", + "BS", + "LE", + /* Line Annotations */ + "L", + "LE", + "IC", + "LL", + "LLE", + "Cap", + "LLO", + "CP", + "Measure", + "CO", + /* Square and Circle annotations */ + "Path", + /* Polygon and PolyLine annotations */ + "Vertices", + /* Text Markup annotations */ + /* Caret annotations */ + "Sy", + /* Rubber Stamp annotations */ + /* Ink annotations */ + "InkList", + /* Popup annotations */ + "Open", + /* File attachment annotation */ + "FS", + /* Sound annotations */ + "Sound", + /* Movie annotations */ + "Movie", + /* Screen annotations */ + "MK", + "AA", + /* We don't handle Widget annotations as annotations, we draw them */ + /* Printer's Mark annotations */ + /* Trap Network annotations */ + /* Watermark annotations */ + "FixedPrint", + "Matrix", + "H", + "V", + /* Redaction annotations */ + "RO", + "OverlayText", + "Repeat", + /* Projection annotations */ + /* 3D and RichMedia annotations */ +}; + +static int isKnownKey(pdf_context *ctx, pdf_name *Key) +{ + int i = 0; + + for (i = 0; i < sizeof(PermittedKeys) / sizeof (const char *); i++) { + if (pdfi_name_is(Key, PermittedKeys[i])) + return 1; + } + return 0; +} + static int pdfi_annot_preserve_mark(pdf_context *ctx, pdf_dict *annot, pdf_name *subtype) { int code = 0; @@ -4136,51 +4252,44 @@ static int pdfi_annot_preserve_mark(pdf_context *ctx, pdf_dict *annot, pdf_name while (code >= 0) { resolve = false; - if (pdfi_name_is(Key, "Popup") || pdfi_name_is(Key, "IRT") || pdfi_name_is(Key, "RT") || - pdfi_name_is(Key, "P") || pdfi_name_is(Key, "Parent")) { - /* Delete some keys - * These would not be handled correctly and are optional. - * (see pdf_draw.ps/loadannot()) - * TODO: Could probably handle some of these since they are typically - * just references, and we do have a way to handle references? - * Look into it later... - */ + if (!isKnownKey(ctx, Key)) { code = pdfi_dict_delete_pair(ctx, tempdict, Key); if (code < 0) goto exit; - } else if (pdfi_name_is(Key, "AP")) { - /* Special handling for AP -- have fun! */ - code = pdfi_annot_preserve_modAP(ctx, tempdict, Key); - if (code < 0) goto exit; - } else if (pdfi_name_is(Key, "QuadPoints")) { - code = pdfi_annot_preserve_modQP(ctx, tempdict, Key); - if (code < 0) goto exit; - } else if (pdfi_name_is(Key, "A")) { - code = pdfi_mark_modA(ctx, tempdict); - if (code < 0) goto exit; - } else if (pdfi_name_is(Key, "Dest")) { - if (ctx->args.no_pdfmark_dests) { - /* If omitting dests, such as for multi-page output, then omit this whole annotation */ - code = 0; - goto exit; - } - code = pdfi_mark_modDest(ctx, tempdict); - if (code < 0) goto exit; - } else if (pdfi_name_is(Key, "StructTreeRoot")) { - /* TODO: Bug691785 has Link annots with /StructTreeRoot - * It is super-circular, and causes issues. - * GS code only adds in certain values for Link so it doesn't - * run into a problem. I am just going to delete it. - * There should be a better solution to handle circular stuff - * generically. - */ - code = pdfi_dict_delete_pair(ctx, tempdict, Key); - if (code < 0) goto exit; - } else if (pdfi_name_is(Key, "Sound") || pdfi_name_is(Key, "Movie")) { - resolve = false; } else { - resolve = true; + if (pdfi_name_is(Key, "AP")) { + /* Special handling for AP -- have fun! */ + code = pdfi_annot_preserve_modAP(ctx, tempdict, Key); + if (code < 0) goto exit; + } else if (pdfi_name_is(Key, "QuadPoints")) { + code = pdfi_annot_preserve_modQP(ctx, tempdict, Key); + if (code < 0) goto exit; + } else if (pdfi_name_is(Key, "A")) { + code = pdfi_pdfmark_modA(ctx, tempdict); + if (code < 0) goto exit; + } else if (pdfi_name_is(Key, "Dest")) { + if (ctx->args.no_pdfmark_dests) { + /* If omitting dests, such as for multi-page output, then omit this whole annotation */ + code = 0; + goto exit; + } + code = pdfi_pdfmark_modDest(ctx, tempdict); + if (code < 0) goto exit; + } else if (pdfi_name_is(Key, "StructTreeRoot")) { + /* TODO: Bug691785 has Link annots with /StructTreeRoot + * It is super-circular, and causes issues. + * GS code only adds in certain values for Link so it doesn't + * run into a problem. I am just going to delete it. + * There should be a better solution to handle circular stuff + * generically. + */ + code = pdfi_dict_delete_pair(ctx, tempdict, Key); + if (code < 0) goto exit; + } else if (pdfi_name_is(Key, "Sound") || pdfi_name_is(Key, "Movie")) { + resolve = false; + } else { + resolve = true; + } } - if (resolve) { code = pdfi_dict_get_by_key(ctx, annot, (const pdf_name *)Key, &Value); if (code < 0) goto exit; @@ -4212,9 +4321,9 @@ static int pdfi_annot_preserve_mark(pdf_context *ctx, pdf_dict *annot, pdf_name gs_currentmatrix(ctx->pgs, &ctm); if (pdfi_name_is(subtype, "Link")) - code = pdfi_mark_from_dict(ctx, tempdict, &ctm, "LNK"); + code = pdfi_pdfmark_from_dict(ctx, tempdict, &ctm, "LNK"); else - code = pdfi_mark_from_dict(ctx, tempdict, &ctm, "ANN"); + code = pdfi_pdfmark_from_dict(ctx, tempdict, &ctm, "ANN"); if (code < 0) goto exit; exit: diff --git a/pdf/pdf_array.c b/pdf/pdf_array.c index 07a90812..5f269fe5 100644 --- a/pdf/pdf_array.c +++ b/pdf/pdf_array.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -53,7 +53,7 @@ int pdfi_array_alloc(pdf_context *ctx, uint64_t size, pdf_array **a) /* Make a null object */ code = pdfi_object_alloc(ctx, PDF_NULL, 1, &n); if (code < 0) { - pdfi_countdown(*a); + pdfi_free_object((pdf_obj *)(*a)); *a = NULL; return code; } @@ -121,7 +121,7 @@ int pdfi_array_from_stack(pdf_context *ctx, uint32_t indirect_num, uint32_t indi /* Fetch object from array, resolving indirect reference if needed * setref -- indicates whether to replace indirect ref with the object */ -static int pdfi_array_fetch(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o, bool setref) +int pdfi_array_fetch(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o, bool setref, bool cache) { int code; pdf_obj *obj; @@ -139,7 +139,13 @@ static int pdfi_array_fetch(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_ pdf_obj *o1 = NULL; pdf_indirect_ref *r = (pdf_indirect_ref *)obj; - code = pdfi_deref_loop_detect(ctx, r->ref_object_num, r->ref_generation_num, &o1); + if (r->ref_object_num == a->object_num) + return_error(gs_error_circular_reference); + + if (cache) + code = pdfi_deref_loop_detect(ctx, r->ref_object_num, r->ref_generation_num, &o1); + else + code = pdfi_deref_loop_detect_nocache(ctx, r->ref_object_num, r->ref_generation_num, &o1); if (code < 0) return code; @@ -154,19 +160,6 @@ static int pdfi_array_fetch(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_ return 0; } -/* The object returned by pdfi_array_get has its reference count incremented by 1 to - * indicate the reference now held by the caller, in **o. - */ -int pdfi_array_get(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o) -{ - int code; - - code = pdfi_array_fetch(ctx, a, index, o, true); - if (code < 0) return code; - - return 0; -} - /* Get element from array without resolving PDF_INDIRECT dereferences. * It looks to me like some usages need to do the checking themselves to * avoid circular references? Can remove this if not really needed. @@ -190,7 +183,7 @@ int pdfi_array_get_no_store_R(pdf_context *ctx, pdf_array *a, uint64_t index, pd { int code; - code = pdfi_array_fetch(ctx, a, index, o, false); + code = pdfi_array_fetch(ctx, a, index, o, false, false); if (code < 0) return code; return 0; @@ -267,7 +260,7 @@ bool pdfi_array_known(pdf_context *ctx, pdf_array *a, pdf_obj *o, int *index) pdf_obj *val; int code; - code = pdfi_array_fetch(ctx, a, i, &val, true); + code = pdfi_array_fetch(ctx, a, i, &val, true, true); if (code < 0) continue; if (val->object_num == o->object_num) { diff --git a/pdf/pdf_array.h b/pdf/pdf_array.h index f118737e..37097aaa 100644 --- a/pdf/pdf_array.h +++ b/pdf/pdf_array.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -20,12 +20,25 @@ static inline uint64_t pdfi_array_size(pdf_array *a) { return a->size; } +int pdfi_array_fetch(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o, bool setref, bool cache); +/* The object returned by pdfi_array_get has its reference count incremented by 1 to + * indicate the reference now held by the caller, in **o. + */ +static int inline pdfi_array_get(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o) +{ + return pdfi_array_fetch(ctx, a, index, o, true, true); +} + +static int inline pdfi_array_get_nocache(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o) +{ + return pdfi_array_fetch(ctx, a, index, o, true, false); +} + void pdfi_free_array(pdf_obj *o); int pdfi_array_alloc(pdf_context *ctx, uint64_t size, pdf_array **a); int pdfi_array_from_stack(pdf_context *ctx, uint32_t indirect_num, uint32_t indirect_gen); int pdfi_array_get_no_deref(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o); int pdfi_array_get_no_store_R(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o); -int pdfi_array_get(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o); int pdfi_array_get_type(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj_type t, pdf_obj **o); int pdfi_array_get_int(pdf_context *ctx, pdf_array *a, uint64_t index, int64_t *i); int pdfi_array_get_number(pdf_context *ctx, pdf_array *a, uint64_t index, double *f); diff --git a/pdf/pdf_check.c b/pdf/pdf_check.c index 13d974cb..00933bdf 100644 --- a/pdf/pdf_check.c +++ b/pdf/pdf_check.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -24,10 +24,12 @@ #include "pdf_loop_detect.h" #include "pdf_colour.h" #include "pdf_trans.h" +#include "pdf_font_types.h" #include "pdf_gstate.h" #include "pdf_misc.h" #include "pdf_check.h" #include "pdf_device.h" +#include "gsdevice.h" /* For gs_setdevice_no_erase */ #include "gspaint.h" /* For gs_erasepage() */ /* For performance and resource reasons we do not want to install the transparency blending @@ -133,7 +135,8 @@ pdfi_check_init_tracker(pdf_context *ctx, pdfi_check_tracker_t *tracker) memset(tracker->CheckedResources, 0x00, tracker->size); - if (ctx->device_state.spot_capable || ctx->args.overprint_control == PDF_OVERPRINT_SIMULATE) { + if (ctx->device_state.spot_capable || + (ctx->pgs->device->icc_struct->overprint_control) == gs_overprint_control_simulate) { code = pdfi_dict_alloc(ctx, 32, &tracker->spot_dict); if (code < 0) goto cleanup; @@ -1143,6 +1146,8 @@ int pdfi_check_page(pdf_context *ctx, pdf_dict *page_dict, bool do_setup) */ pdfi_device_set_flags(ctx); code = pdfi_check_init_tracker(ctx, &tracker); + if (code < 0) + goto exit; /* Check for spots and transparency in this page */ code = pdfi_check_page_inner(ctx, page_dict, &tracker); @@ -1156,20 +1161,88 @@ int pdfi_check_page(pdf_context *ctx, pdf_dict *page_dict, bool do_setup) /* If setup requested, tell the device about spots and transparency */ if (do_setup) { gs_c_param_list list; + int a = 0; + pdf_name *Key = NULL; + pdf_obj *Value = NULL; + uint64_t index = 0; gs_c_param_list_write(&list, ctx->memory); /* If there are spot colours (and by inference, the device renders spot plates) then * send the number of Spots to the device, so it can setup correctly. */ - if (tracker.spot_dict) - param_write_int((gs_param_list *)&list, "PageSpotColors", &spots); + if (tracker.spot_dict) { + /* There is some awkwardness here. If the SeparationColorNames setting + * fails, we want to ignore it (this can mean that we exceeded the maximum + * number of colourants and some will be converted to CMYK). But if that happens, + * any other parameters in the same list which haven't already been prcoessed + * will be lost. So we need to send two lists, the SeparationColorNames and + * 'everything else'. + */ + if (spots > 0) { + gs_param_string_array sa; + gs_param_string *table = NULL; + + table = (gs_param_string *)gs_alloc_byte_array(ctx->memory, spots, sizeof(gs_param_string), "SeparationNames"); + if (table != NULL) + { + memset(table, 0x00, spots * sizeof(gs_param_string)); + + code = pdfi_dict_first(ctx, tracker.spot_dict, (pdf_obj **)&Key, &Value, &index); + while (code >= 0) + { + if (Key->type == PDF_NAME) { + table[a].data = ((pdf_string *)Key)->data; + table[a].size = ((pdf_string *)Key)->length; + table[a++].persistent = false; + } + /* Although we count down the returned PDF objects here, the pointers + * to the name data remain valid and won't move. Provided we don't + * retain the pointers after we free the tracker dictionary this is + * safe to do. + */ + pdfi_countdown(Key); + Key = NULL; + pdfi_countdown(Value); + Value = NULL; + code = pdfi_dict_next(ctx, tracker.spot_dict, (pdf_obj **)&Key, &Value, &index); + } + sa.data = table; + sa.size = spots; + sa.persistent = false; + + (void)param_write_string_array((gs_param_list *)&list, "SeparationColorNames", &sa); + gs_c_param_list_read(&list); + code = gs_putdeviceparams(ctx->pgs->device, (gs_param_list *)&list); + gs_c_param_list_release(&list); - code = param_write_bool((gs_param_list *)&list, "PageUsesTransparency", - &tracker.transparent); + gs_free_object(ctx->memory, table, "SeparationNames"); + if (code > 0) { + /* The device was closed, we need to reopen it */ + code = gs_setdevice_no_erase(ctx->pgs, ctx->pgs->device); + if (code < 0) + goto exit; + gs_erasepage(ctx->pgs); + } + + /* Reset the list back to being writeable */ + gs_c_param_list_write(&list, ctx->memory); + } + else { + code = gs_note_error(gs_error_VMerror); + goto exit; + } + } + /* Update the number of spots */ + param_write_int((gs_param_list *)&list, "PageSpotColors", &spots); + } + /* Update the page transparency */ + (void)param_write_bool((gs_param_list *)&list, "PageUsesTransparency", + &tracker.transparent); gs_c_param_list_read(&list); code = gs_putdeviceparams(ctx->pgs->device, (gs_param_list *)&list); gs_c_param_list_release(&list); + if (code > 0) { /* The device was closed, we need to reopen it */ code = gs_setdevice_no_erase(ctx->pgs, ctx->pgs->device); @@ -1180,10 +1253,15 @@ int pdfi_check_page(pdf_context *ctx, pdf_dict *page_dict, bool do_setup) } /* Set our values in the context, for caller */ - ctx->page.has_transparency = tracker.transparent; + if (!ctx->args.notransparency) + ctx->page.has_transparency = tracker.transparent; ctx->page.num_spots = spots; ctx->page.has_OP = tracker.has_overprint; + /* High level devices do not render overprint */ + if (ctx->device_state.HighLevelDevice) + ctx->page.has_OP = false; + exit: (void)pdfi_check_free_tracker(ctx, &tracker); return code; diff --git a/pdf/pdf_cmap.c b/pdf/pdf_cmap.c index fcf363ff..d7198e24 100644 --- a/pdf/pdf_cmap.c +++ b/pdf/pdf_cmap.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2021 Artifex Software, Inc. +/* Copyright (C) 2020-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -33,6 +33,7 @@ static int cmap_usecmap_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte pdf_cmap *pdficmap = (pdf_cmap *)s->client_data; pdf_name *n = NULL; pdf_cmap *upcmap = NULL; + int code = 0; if (pdf_ps_stack_count(s) < 1) return_error(gs_error_stackunderflow); @@ -40,7 +41,7 @@ static int cmap_usecmap_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte /* If we've already got some definitions, ignore the usecmap op */ if (pdficmap->code_space.num_ranges == 0) { byte *nstr = NULL; - int code, len = s->cur[0].size; + int len = s->cur[0].size; if (pdf_ps_obj_has_type(&(s->cur[0]), PDF_PS_OBJ_NAME)) { nstr = s->cur[0].val.name; @@ -49,9 +50,10 @@ static int cmap_usecmap_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte nstr = s->cur[0].val.string; } else { - return_error(gs_error_typecheck); + code = gs_note_error(gs_error_typecheck); } - code = pdfi_name_alloc(pdficmap->ctx, nstr, len, (pdf_obj **)&n); + if (code >= 0) + code = pdfi_name_alloc(pdficmap->ctx, nstr, len, (pdf_obj **)&n); if (code >= 0) { pdfi_countup(n); code = pdfi_read_cmap(pdficmap->ctx, (pdf_obj *)n, &upcmap); @@ -77,10 +79,13 @@ static int cmap_usecmap_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte } } } - } pdfi_countdown(upcmap); pdfi_countdown(n); + if (code < 0) { + (void)pdf_ps_stack_pop(s, 1); + return code; + } return pdf_ps_stack_pop(s, 1); } @@ -103,23 +108,38 @@ static int cmap_endcodespacerange_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte * /* increment to_pop to cover the mark object */ numranges = to_pop++; while (numranges % 2) numranges--; + if (numranges > 200) { + (void)pdf_ps_stack_pop(s, to_pop); + return_error(gs_error_syntaxerror); + } - if (numranges > 0 && pdf_ps_obj_has_type(&(s->cur[0]), PDF_PS_OBJ_STRING) && - pdf_ps_obj_has_type(&(s->cur[-1]), PDF_PS_OBJ_STRING)) { + if (numranges > 0 + && pdf_ps_obj_has_type(&(s->cur[0]), PDF_PS_OBJ_STRING) && s->cur[0].size <= MAX_CMAP_CODE_SIZE + && pdf_ps_obj_has_type(&(s->cur[-1]), PDF_PS_OBJ_STRING) && s->cur[-1].size <= MAX_CMAP_CODE_SIZE) { code_space->num_ranges += numranges >> 1; code_space->ranges = (gx_code_space_range_t *)gs_alloc_byte_array(mem, code_space->num_ranges, sizeof(gx_code_space_range_t), "cmap_endcodespacerange_func(ranges)"); - if (nr > 0) { - memcpy(code_space->ranges, gcsr, nr); - gs_free_object(mem, gcsr, "cmap_endcodespacerange_func(gcsr"); - } + if (code_space->ranges != NULL) { + if (nr > 0) { + memcpy(code_space->ranges, gcsr, nr); + gs_free_object(mem, gcsr, "cmap_endcodespacerange_func(gcsr"); + } - for (i = nr; i < code_space->num_ranges; i++) { - memcpy(code_space->ranges[i].first, s->cur[-((i * 2) + 1)].val.string, s->cur[-((i * 2) + 1)].size); - memcpy(code_space->ranges[i].last, s->cur[-(i * 2)].val.string, s->cur[-(i * 2)].size); - code_space->ranges[i].size = s->cur[-(i * 2)].size; + for (i = nr; i < code_space->num_ranges; i++) { + int si = i - nr; + int s1 = s->cur[-((si * 2) + 1)].size < MAX_CMAP_CODE_SIZE ? s->cur[-((si * 2) + 1)].size : MAX_CMAP_CODE_SIZE; + int s2 = s->cur[-(si * 2)].size < MAX_CMAP_CODE_SIZE ? s->cur[-(si * 2)].size : MAX_CMAP_CODE_SIZE; + + memcpy(code_space->ranges[i].first, s->cur[-((si * 2) + 1)].val.string, s1); + memcpy(code_space->ranges[i].last, s->cur[-(si * 2)].val.string, s2); + code_space->ranges[i].size = s->cur[-(si * 2)].size; + } + } + else { + (void)pdf_ps_stack_pop(s, to_pop); + return_error(gs_error_VMerror); } } return pdf_ps_stack_pop(s, to_pop); @@ -151,6 +171,10 @@ static int general_endcidrange_func(gs_memory_t *mem, pdf_ps_ctx_t *s, pdf_cmap * startcode, endcode and basecid */ while (ncodemaps % 3) ncodemaps--; + if (ncodemaps > 300) { + (void)pdf_ps_stack_pop(s, to_pop); + return_error(gs_error_syntaxerror); + } stobj = &s->cur[-ncodemaps] + 1; @@ -173,6 +197,12 @@ static int general_endcidrange_func(gs_memory_t *mem, pdf_ps_ctx_t *s, pdf_cmap preflen = 1; } + if (preflen > MAX_CMAP_CODE_SIZE || stobj[i].size - preflen > MAX_CMAP_CODE_SIZE || stobj[i + 1].size - preflen > MAX_CMAP_CODE_SIZE + || stobj[i].size - preflen < 0 || stobj[i + 1].size - preflen < 0) { + (void)pdf_ps_stack_pop(s, to_pop); + return_error(gs_error_syntaxerror); + } + /* Find how many bytes we need for the cidbase value */ /* We always store at least two bytes for the cidbase value */ for (valuelen = 16; valuelen < 32 && (cidbase >> valuelen) > 0; valuelen += 1) @@ -214,7 +244,8 @@ static int general_endcidrange_func(gs_memory_t *mem, pdf_ps_ctx_t *s, pdf_cmap if (cmap_insert_map(cmap_range, pdfir) < 0) break; } else { - break; + (void)pdf_ps_stack_pop(s, to_pop); + return_error(gs_error_VMerror); } } } @@ -247,6 +278,11 @@ static int cmap_endfbrange_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, by */ while (ncodemaps % 3) ncodemaps--; + if (ncodemaps > 300) { + (void)pdf_ps_stack_pop(s, to_pop); + return_error(gs_error_syntaxerror); + } + stobj = &s->cur[-ncodemaps] + 1; for (i = 0; i < ncodemaps; i += 3) { /* Lazy: to make the loop below simpler, put single @@ -256,6 +292,7 @@ static int cmap_endfbrange_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, by pdf_ps_stack_object_t *arr; arr = (pdf_ps_stack_object_t *) gs_alloc_bytes(mem, sizeof(pdf_ps_stack_object_t), "cmap_endfbrange_func(pdf_ps_stack_object_t"); if (arr == NULL) { + (void)pdf_ps_stack_pop(s, to_pop); return_error(gs_error_VMerror); } else { @@ -355,7 +392,8 @@ static int cmap_endfbrange_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, by if (cmap_insert_map(&(pdficmap->cmap_range), pdfir) < 0) break; } else { - break; + (void)pdf_ps_stack_pop(s, to_pop); + return_error(gs_error_VMerror); } } } @@ -377,6 +415,11 @@ static int general_endcidchar_func(gs_memory_t *mem, pdf_ps_ctx_t *s, pdf_cmap * */ while (ncodemaps % 2) ncodemaps--; + if (ncodemaps > 200) { + (void)pdf_ps_stack_pop(s, to_pop); + return_error(gs_error_syntaxerror); + } + stobj = &s->cur[-ncodemaps] + 1; for (i = 0; i < ncodemaps; i += 2) { @@ -428,7 +471,8 @@ static int general_endcidchar_func(gs_memory_t *mem, pdf_ps_ctx_t *s, pdf_cmap * if (cmap_insert_map(cmap_range, pdfir) < 0) break; } else { - break; + (void)pdf_ps_stack_pop(s, to_pop); + return_error(gs_error_VMerror); } } } @@ -454,6 +498,11 @@ static int cmap_endbfchar_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byt pdf_ps_stack_object_t *stobj; int i, j; + if (ncodemaps > 200) { + (void)pdf_ps_stack_pop(s, ncodemaps); + return_error(gs_error_syntaxerror); + } + stobj = &s->cur[-ncodemaps] + 1; for (i = 0; i < ncodemaps; i += 2) { @@ -554,7 +603,7 @@ static int cmap_def_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte *buf pdficmap->cmaptype = s->cur[0].val.i; } else { - pdficmap->type = 1; + pdficmap->cmaptype = 1; } } else if (!memcmp(s->cur[-1].val.name, CMAP_NAME_AND_LEN("XUID"))) { @@ -638,6 +687,9 @@ pdf_cmap_open_file(pdf_context *ctx, gs_string *cmap_name, byte **buf, int64_t * const char *path_pfx = "CMap/"; fname[0] = '\0'; + if (strlen(path_pfx) + cmap_name->size >= gp_file_name_sizeof) + return_error(gs_error_rangecheck); + strncat(fname, path_pfx, strlen(path_pfx)); strncat(fname, (char *)cmap_name->data, cmap_name->size); code = pdfi_open_resource_file(ctx, (const char *)fname, (const int)strlen(fname), &s); @@ -831,6 +883,9 @@ pdfi_read_cmap(pdf_context *ctx, pdf_obj *cmap, pdf_cmap **pcmap) } } } + else { + goto error_out; + } return 0; error_out: diff --git a/pdf/pdf_colour.c b/pdf/pdf_colour.c index e6061db1..dcc3ebf0 100644 --- a/pdf/pdf_colour.c +++ b/pdf/pdf_colour.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -24,9 +24,12 @@ #include "pdf_misc.h" #include "gsicc_manage.h" #include "gsicc_profilecache.h" +#include "gsicc_cache.h" + #include "gsicc_create.h" #include "gsptype2.h" + #include "pdf_file.h" #include "pdf_dict.h" #include "pdf_loop_detect.h" @@ -189,7 +192,7 @@ static int pdfi_check_for_spots_by_array(pdf_context *ctx, pdf_array *color_arra if (code < 0) goto exit; - code = pdfi_dict_put_obj(ctx, spot_dict, name, dummy); + code = pdfi_dict_put_obj(ctx, spot_dict, name, dummy, true); pdfi_countdown(name); if (code < 0) break; @@ -216,7 +219,7 @@ static int pdfi_check_for_spots_by_array(pdf_context *ctx, pdf_array *color_arra if (code < 0) goto exit; - code = pdfi_dict_put_obj(ctx, spot_dict, (pdf_obj *)space, dummy); + code = pdfi_dict_put_obj(ctx, spot_dict, (pdf_obj *)space, dummy, true); goto exit; } else { code = pdfi_find_resource(ctx, (unsigned char *)"ColorSpace", @@ -305,9 +308,15 @@ int pdfi_ri(pdf_context *ctx) static void pdfi_cspace_free_callback(gs_memory_t * mem, void *cs) { gs_color_space *pcs = (gs_color_space *)cs; - pdf_context *ctx = (pdf_context *)pcs->interpreter_data; + pdf_obj *o = (pdf_obj *)pcs->interpreter_data; + pdf_context *ctx = NULL; gs_function_t *pfn; + if (o == NULL) + return; + + ctx = o->ctx; + if (gs_color_space_get_index(pcs) == gs_color_space_index_Separation) { /* Handle cleanup of Separation functions if applicable */ pfn = gs_cspace_get_sepr_function(pcs); @@ -321,6 +330,10 @@ static void pdfi_cspace_free_callback(gs_memory_t * mem, void *cs) if (pfn) pdfi_free_function(ctx, pfn); } + if (o->type != PDF_CTX) { + pdfi_countdown(o); + pcs->interpreter_data = NULL; + } } int pdfi_gs_setgray(pdf_context *ctx, double d) @@ -328,7 +341,7 @@ int pdfi_gs_setgray(pdf_context *ctx, double d) int code = 0; /* PDF Reference 1.7 p423, any colour operators in a CharProc, following a d1, should be ignored */ - if (ctx->text.inside_CharProc && ctx->text.CharProc_is_d1) + if (ctx->text.inside_CharProc && ctx->text.CharProc_d_type != pdf_type3_d0) return 0; if (ctx->page.DefaultGray_cs != NULL) { @@ -337,15 +350,15 @@ int pdfi_gs_setgray(pdf_context *ctx, double d) code = gs_setcolorspace(ctx->pgs, ctx->page.DefaultGray_cs); if (code < 0) return code; - pdfi_set_colour_callback(ctx->pgs->color[0].color_space, ctx, NULL); cc.paint.values[0] = d; + cc.pattern = 0; return gs_setcolor(ctx->pgs, &cc); } else { code = gs_setgray(ctx->pgs, d); if (code < 0) return code; - pdfi_set_colour_callback(ctx->pgs->color[0].color_space, ctx, pdfi_cspace_free_callback); } + pdfi_set_colour_callback(ctx->pgs->color[0].color_space, ctx, pdfi_cspace_free_callback); return 0; } @@ -354,7 +367,7 @@ int pdfi_gs_setrgbcolor(pdf_context *ctx, double r, double g, double b) int code = 0; /* PDF Reference 1.7 p423, any colour operators in a CharProc, following a d1, should be ignored */ - if (ctx->text.inside_CharProc && ctx->text.CharProc_is_d1) + if (ctx->text.inside_CharProc && ctx->text.CharProc_d_type != pdf_type3_d0) return 0; if (ctx->page.DefaultRGB_cs != NULL) { @@ -367,6 +380,7 @@ int pdfi_gs_setrgbcolor(pdf_context *ctx, double r, double g, double b) cc.paint.values[0] = r; cc.paint.values[1] = g; cc.paint.values[2] = b; + cc.pattern = 0; return gs_setcolor(ctx->pgs, &cc); } else { code = gs_setrgbcolor(ctx->pgs, r, g, b); @@ -382,7 +396,7 @@ static int pdfi_gs_setcmykcolor(pdf_context *ctx, double c, double m, double y, int code = 0; /* PDF Reference 1.7 p423, any colour operators in a CharProc, following a d1, should be ignored */ - if (ctx->text.inside_CharProc && ctx->text.CharProc_is_d1) + if (ctx->text.inside_CharProc && ctx->text.CharProc_d_type != pdf_type3_d0) return 0; if (ctx->page.DefaultCMYK_cs != NULL) { @@ -391,18 +405,18 @@ static int pdfi_gs_setcmykcolor(pdf_context *ctx, double c, double m, double y, code = gs_setcolorspace(ctx->pgs, ctx->page.DefaultCMYK_cs); if (code < 0) return code; - pdfi_set_colour_callback(ctx->pgs->color[0].color_space, ctx, NULL); cc.paint.values[0] = c; cc.paint.values[1] = m; cc.paint.values[2] = y; cc.paint.values[3] = k; + cc.pattern = 0; return gs_setcolor(ctx->pgs, &cc); } else { code = gs_setcmykcolor(ctx->pgs, c, m, y, k); if (code < 0) return code; - pdfi_set_colour_callback(ctx->pgs->color[0].color_space, ctx, pdfi_cspace_free_callback); } + pdfi_set_colour_callback(ctx->pgs->color[0].color_space, ctx, pdfi_cspace_free_callback); return 0; } @@ -413,7 +427,7 @@ int pdfi_gs_setcolorspace(pdf_context *ctx, gs_color_space *pcs) */ if (ctx->pgs->color[0].color_space->id != pcs->id) { /* PDF Reference 1.7 p423, any colour operators in a CharProc, following a d1, should be ignored */ - if (ctx->text.inside_CharProc && ctx->text.CharProc_is_d1) + if (ctx->text.inside_CharProc && ctx->text.CharProc_d_type != pdf_type3_d0) return 0; pdfi_set_colour_callback(pcs, ctx, pdfi_cspace_free_callback); @@ -699,9 +713,14 @@ int pdfi_setstrokecolor(pdf_context *ctx) int ncomps, code; gs_client_color cc; + cc.pattern = 0; gs_swapcolors_quick(ctx->pgs); pcs = gs_currentcolorspace(ctx->pgs); ncomps = cs_num_components(pcs); + if (ncomps < 1) { + gs_swapcolors_quick(ctx->pgs); + return_error(gs_error_syntaxerror); + } code = pdfi_get_color_from_stack(ctx, &cc, ncomps); if (code == 0) { code = gs_setcolor(ctx->pgs, &cc); @@ -716,7 +735,10 @@ int pdfi_setfillcolor(pdf_context *ctx) int ncomps, code; gs_client_color cc; + cc.pattern = 0; ncomps = cs_num_components(pcs); + if (ncomps < 1) + return_error(gs_error_syntaxerror); code = pdfi_get_color_from_stack(ctx, &cc, ncomps); if (code == 0) { code = gs_setcolor(ctx->pgs, &cc); @@ -750,16 +772,18 @@ pdfi_setcolorN(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, boo if (pdfi_count_stack(ctx) < 1) { code = gs_note_error(gs_error_stackunderflow); - goto cleanupExit; + goto cleanupExit1; } + memset(&cc, 0x00, sizeof(gs_client_color)); + if (pcs->type == &gs_color_space_type_Pattern) is_pattern = true; if (is_pattern) { if (ctx->stack_top[-1]->type != PDF_NAME) { pdfi_clearstack(ctx); code = gs_note_error(gs_error_syntaxerror); - goto cleanupExit; + goto cleanupExit0; } base_space = pcs->base_space; code = pdfi_pattern_set(ctx, stream_dict, page_dict, (pdf_name *)ctx->stack_top[-1], &cc); @@ -768,23 +792,27 @@ pdfi_setcolorN(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, boo /* Ignore the pattern if we failed to set it */ pdfi_set_warning(ctx, 0, NULL, W_PDF_BADPATTERN, "pdfi_setcolorN", (char *)"PATTERN: Error setting pattern"); code = 0; - goto cleanupExit; + goto cleanupExit1; } if (base_space && pattern_instance_uses_base_space(cc.pattern)) ncomps = cs_num_components(base_space); else ncomps = 0; - } else { + } else ncomps = cs_num_components(pcs); - cc.pattern = NULL; - } - if (ncomps > 0) + if (ncomps > 0) { code = pdfi_get_color_from_stack(ctx, &cc, ncomps); - if (code < 0) - goto cleanupExit; + if (code < 0) + goto cleanupExit1; + } if (pcs->type == &gs_color_space_type_Indexed) { + if (ncomps <= 0) + { + code = gs_note_error(gs_error_rangecheck); + goto cleanupExit1; + } if (cc.paint.values[0] < 0) cc.paint.values[0] = 0.0; else @@ -806,6 +834,7 @@ pdfi_setcolorN(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, boo code = gs_setcolor(ctx->pgs, &cc); +cleanupExit1: if (is_pattern) /* cc is a local scope variable, holding a reference to a pattern. * We need to count the refrence down before the variable goes out of scope @@ -813,7 +842,7 @@ pdfi_setcolorN(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, boo */ rc_decrement(cc.pattern, "pdfi_setcolorN"); -cleanupExit: +cleanupExit0: if (!is_fill) gs_swapcolors_quick(ctx->pgs); return code; @@ -824,7 +853,7 @@ cleanupExit: /* Starting with the ICCBased colour space */ /* This routine is mostly a copy of seticc() in zicc.c */ -static int pdfi_create_icc(pdf_context *ctx, char *Name, stream *s, int ncomps, int *icc_N, float *range_buff, gs_color_space **ppcs) +static int pdfi_create_icc(pdf_context *ctx, char *Name, stream *s, int ncomps, int *icc_N, float *range_buff, ulong dictkey, gs_color_space **ppcs) { int code, k; gs_color_space * pcs; @@ -981,13 +1010,16 @@ static int pdfi_create_icc(pdf_context *ctx, char *Name, stream *s, int ncomps, rc_adjust(picc_profile, -2, "pdfi_create_icc"); rc_increment(pcs->cmm_icc_profile_data); } + /* Add the color space to the profile cache */ + if (dictkey != 0) + gsicc_add_cs(ctx->pgs, pcs, dictkey); if (ppcs!= NULL){ *ppcs = pcs; pdfi_set_colour_callback(pcs, ctx, pdfi_cspace_free_callback); } else { code = pdfi_gs_setcolorspace(ctx, pcs); - rc_decrement_only_cs(pcs, "pdfi_seticc_cal"); + rc_decrement_only_cs(pcs, "pdfi_create_icc"); } /* The context has taken a reference to the colorspace. We no longer need @@ -1003,6 +1035,34 @@ static int pdfi_create_iccprofile(pdf_context *ctx, pdf_stream *ICC_obj, char *c byte *profile_buffer; gs_offset_t savedoffset; int code, code1; + ulong dictkey = 0; + + /* See if the color space is in the profile cache */ + /* NOTE! 0 indicates a named colour space for JPX images, do not attempt to + * find a cached space for this. Conveniently should we somehow manage to get + * here from an array or other object which is not an indirect reference then we will + * again not attempt to cache the space or lookup the cache. + */ + if (!gs_currentoverrideicc(ctx->pgs)) { + if (ICC_obj->object_num != 0) { + gs_color_space *pcs = NULL; + + pcs = gsicc_find_cs(ICC_obj->object_num, ctx->pgs); + if (pcs != NULL) { + if (ppcs!= NULL){ + *ppcs = pcs; + } else { + code = pdfi_gs_setcolorspace(ctx, pcs); + rc_decrement_only_cs(pcs, "pdfi_create_iccprofile"); + } + *icc_N = gs_color_space_num_components(pcs); + /* We're passing back a new reference, increment the count */ + rc_adjust_only(pcs, 1, "pdfi_create_iccprofile, return cached ICC profile"); + return 0; + } + dictkey = ICC_obj->object_num; + } + } /* Save the current stream position, and move to the start of the profile stream */ savedoffset = pdfi_tell(ctx->main_stream); @@ -1022,7 +1082,7 @@ static int pdfi_create_iccprofile(pdf_context *ctx, pdf_stream *ICC_obj, char *c } /* Now, finally, we can call the code to create and set the profile */ - code = pdfi_create_icc(ctx, cname, profile_stream->s, (int)N, icc_N, range, ppcs); + code = pdfi_create_icc(ctx, cname, profile_stream->s, (int)N, icc_N, range, dictkey, ppcs); code1 = pdfi_close_memory_stream(ctx, profile_buffer, profile_stream); @@ -1059,6 +1119,10 @@ static int pdfi_create_iccbased(pdf_context *ctx, pdf_array *color_array, int in code = pdfi_dict_get_int(ctx, dict, "N", &N); if (code < 0) goto done; + if (N != 1 && N != 3 && N != 4) { + code = gs_note_error(gs_error_rangecheck); + goto done; + } code = pdfi_dict_knownget(ctx, dict, "Name", &Name); if (code > 0) { if(Name->type == PDF_STRING || Name->type == PDF_NAME) { @@ -1186,8 +1250,11 @@ static int pdfi_create_iccbased(pdf_context *ctx, pdf_array *color_array, int in break; } } - if (ppcs!= NULL) + if (ppcs!= NULL) { *ppcs = pcs; + if (pcs != NULL) + pdfi_set_colour_callback(pcs, ctx, pdfi_cspace_free_callback); + } else { if (pcs != NULL) { code = pdfi_gs_setcolorspace(ctx, pcs); @@ -1351,6 +1418,7 @@ pdfi_seticc_cal(pdf_context *ctx, float *white, float *black, float *gamma, if (ppcs!= NULL){ *ppcs = pcs; + pdfi_set_colour_callback(pcs, ctx, pdfi_cspace_free_callback); } else { code = pdfi_gs_setcolorspace(ctx, pcs); rc_decrement_only_cs(pcs, "pdfi_seticc_cal"); @@ -1404,7 +1472,7 @@ static int pdfi_create_CalGray(pdf_context *ctx, pdf_array *color_array, int ind goto exit; } - if (pdfi_dict_knownget_type(ctx, CalGray_dict, "BlackPoint", PDF_ARRAY, (pdf_obj **)&PDFArray)) { + if (pdfi_dict_knownget_type(ctx, CalGray_dict, "BlackPoint", PDF_ARRAY, (pdf_obj **)&PDFArray) > 0) { if (pdfi_array_size(PDFArray) != 3){ code = gs_note_error(gs_error_rangecheck); goto exit; @@ -1426,7 +1494,7 @@ static int pdfi_create_CalGray(pdf_context *ctx, pdf_array *color_array, int ind PDFArray = NULL; } - if (pdfi_dict_knownget_number(ctx, CalGray_dict, "Gamma", &f)) + if (pdfi_dict_knownget_number(ctx, CalGray_dict, "Gamma", &f) > 0) Gamma = (float)f; /* The PDF 1.7 reference states that Gamma * (if present) must be positive. @@ -1487,7 +1555,7 @@ static int pdfi_create_CalRGB(pdf_context *ctx, pdf_array *color_array, int inde goto exit; } - if (pdfi_dict_knownget_type(ctx, CalRGB_dict, "BlackPoint", PDF_ARRAY, (pdf_obj **)&PDFArray)) { + if (pdfi_dict_knownget_type(ctx, CalRGB_dict, "BlackPoint", PDF_ARRAY, (pdf_obj **)&PDFArray) > 0) { if (pdfi_array_size(PDFArray) != 3){ code = gs_note_error(gs_error_rangecheck); goto exit; @@ -1509,7 +1577,7 @@ static int pdfi_create_CalRGB(pdf_context *ctx, pdf_array *color_array, int inde PDFArray = NULL; } - if (pdfi_dict_knownget_type(ctx, CalRGB_dict, "Gamma", PDF_ARRAY, (pdf_obj **)&PDFArray)) { + if (pdfi_dict_knownget_type(ctx, CalRGB_dict, "Gamma", PDF_ARRAY, (pdf_obj **)&PDFArray) > 0) { if (pdfi_array_size(PDFArray) != 3){ code = gs_note_error(gs_error_rangecheck); goto exit; @@ -1524,7 +1592,7 @@ static int pdfi_create_CalRGB(pdf_context *ctx, pdf_array *color_array, int inde PDFArray = NULL; } - if (pdfi_dict_knownget_type(ctx, CalRGB_dict, "Matrix", PDF_ARRAY, (pdf_obj **)&PDFArray)) { + if (pdfi_dict_knownget_type(ctx, CalRGB_dict, "Matrix", PDF_ARRAY, (pdf_obj **)&PDFArray) > 0) { if (pdfi_array_size(PDFArray) != 9){ code = gs_note_error(gs_error_rangecheck); goto exit; @@ -1603,6 +1671,7 @@ static int pdfi_create_Separation(pdf_context *ctx, pdf_array *color_array, int goto pdfi_separation_error; rc_decrement(pcs_alt, "pdfi_create_Separation"); + pcs_alt = NULL; pcs->params.separation.mem = ctx->memory; pcs->params.separation.sep_type = sep_type; pcs->params.separation.sep_name = (char *)gs_alloc_bytes(ctx->memory->non_gc_memory, name->length + 1, "pdfi_setseparationspace(ink)"); @@ -1621,6 +1690,7 @@ static int pdfi_create_Separation(pdf_context *ctx, pdf_array *color_array, int */ code = pdfi_gs_setcolorspace(ctx, pcs); *ppcs = pcs; + pdfi_set_colour_callback(pcs, ctx, pdfi_cspace_free_callback); } else { code = pdfi_gs_setcolorspace(ctx, pcs); /* release reference from construction */ @@ -1754,10 +1824,12 @@ all_error: if (o->type == PDF_ARRAY) { ArrayAlternate = (pdf_array *)o; code = pdfi_create_colorspace_by_array(ctx, ArrayAlternate, 0, stream_dict, page_dict, &pcs_alt, inline_image); - if (code < 0) { - pdfi_countdown(o); + if (code < 0) + /* OSS-fuzz error 42973; we don't need to count down 'o' here because + * we have assigned it to ArrayAlternate and both the success and error + * paths count down ArrayAlternate. + */ goto pdfi_devicen_error; - } } else { code = gs_error_typecheck; @@ -2066,7 +2138,7 @@ pdfi_create_indexed(pdf_context *ctx, pdf_array *color_array, int index, num_values = (hival+1) * cs_num_components(pcs_base); if (num_values > lookup_length) { - dmprintf2(ctx->memory, "WARNING: pdfi_create_indexed() got %ld values, expected at least %d values\n", + dmprintf2(ctx->memory, "WARNING: pdfi_create_indexed() got %"PRIi64" values, expected at least %d values\n", lookup_length, num_values); code = gs_note_error(gs_error_rangecheck); goto exit; @@ -2201,7 +2273,7 @@ static int pdfi_create_JPX_space(pdf_context *ctx, const char *name, int num_com int code, icc_N; float range_buff[6] = {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f}; - code = pdfi_create_icc(ctx, (char *)name, NULL, num_components, &icc_N, range_buff, ppcs); + code = pdfi_create_icc(ctx, (char *)name, NULL, num_components, &icc_N, range_buff, 0, ppcs); return code; } @@ -2272,7 +2344,10 @@ pdfi_create_colorspace_by_array(pdf_context *ctx, pdf_array *color_array, int in } else if (pdfi_name_is(space, "Separation")) { code = pdfi_create_Separation(ctx, color_array, index, stream_dict, page_dict, ppcs, inline_image); } else { - code = pdfi_find_resource(ctx, (unsigned char *)"ColorSpace", + if (stream_dict == NULL) + code = gs_note_error(gs_error_syntaxerror); + else + code = pdfi_find_resource(ctx, (unsigned char *)"ColorSpace", space, (pdf_dict *)stream_dict, page_dict, (pdf_obj **)&a); if (code < 0) goto exit; @@ -2332,13 +2407,32 @@ pdfi_create_colorspace_by_name(pdf_context *ctx, pdf_name *name, code = pdfi_create_JPX_space(ctx, "sgray", 1, ppcs); } else { pdf_obj *ref_space = NULL; + + if (ppcs == NULL && check_same_current_space(ctx, name) == 1) + return 0; + code = pdfi_find_resource(ctx, (unsigned char *)"ColorSpace", name, (pdf_dict *)stream_dict, page_dict, &ref_space); if (code < 0) return code; + if (ref_space->type == PDF_NAME) { + if (ref_space->object_num != 0 && ref_space->object_num == name->object_num) { + pdfi_countdown(ref_space); + return_error(gs_error_circular_reference); + } + } + /* recursion */ code = pdfi_create_colorspace(ctx, ref_space, stream_dict, page_dict, ppcs, inline_image); + + if (code >= 0) { + if (ppcs != NULL) + pdfi_set_colourspace_name(ctx, *ppcs, name); + else + pdfi_set_colourspace_name(ctx, ctx->pgs->color[0].color_space, name); + } + pdfi_countdown(ref_space); return code; } @@ -2357,7 +2451,7 @@ pdfi_create_colorspace_by_name(pdf_context *ctx, pdf_name *name, */ int pdfi_create_icc_colorspace_from_stream(pdf_context *ctx, pdf_c_stream *stream, gs_offset_t offset, - unsigned int length, int comps, int *icc_N, gs_color_space **ppcs) + unsigned int length, int comps, int *icc_N, ulong dictkey, gs_color_space **ppcs) { pdf_c_stream *profile_stream = NULL; byte *profile_buffer; @@ -2380,7 +2474,7 @@ pdfi_create_icc_colorspace_from_stream(pdf_context *ctx, pdf_c_stream *stream, g } /* Now, finally, we can call the code to create and set the profile */ - code = pdfi_create_icc(ctx, NULL, profile_stream->s, comps, icc_N, range, ppcs); + code = pdfi_create_icc(ctx, NULL, profile_stream->s, comps, icc_N, range, dictkey, ppcs); code1 = pdfi_close_memory_stream(ctx, profile_buffer, profile_stream); @@ -2408,7 +2502,7 @@ int pdfi_create_colorspace(pdf_context *ctx, pdf_obj *space, pdf_dict *stream_di return_error(gs_error_typecheck); } } - if (ppcs && *ppcs && code >= 0) + if (code >= 0 && ppcs && *ppcs) (void)(*ppcs)->type->install_cspace(*ppcs, ctx->pgs); (void)pdfi_loop_detector_cleartomark(ctx); @@ -2569,32 +2663,47 @@ static int pdfi_device_setoutputintent(pdf_context *ctx, pdf_dict *profile_dict, Finally, we will use the output intent profile for the default profile of the proper Device profile in the icc manager, again, unless someone has explicitly set this default profile. + + All of this is skipped if we are forcing oveprint simulation with + the output intent set, in which case we will push the pdf14 device + to render directly to the the output intent color space and then + do a final transform to the target color space. */ dev_comps = dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE]->num_comps; index = gsicc_get_default_type(dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE]); - if (ncomps == dev_comps && index < gs_color_space_index_DevicePixel) { - /* The OI profile is the same type as the profile for the device and a - "default" profile for the device was not externally set. So we go - ahead and use the OI profile as the device profile. Care needs to be - taken here to keep from screwing up any device parameters. We will - use a keyword of OIProfile for the user/device parameter to indicate - its usage. Also, note conflicts if one is setting object dependent - color management */ - dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE] = picc_profile; - rc_increment(picc_profile); - if_debug0m(gs_debug_flag_icc, ctx->memory, "[icc] OutputIntent used for device profile\n"); - } else { - if (dev_profile->proof_profile == NULL) { - /* This means that we should use the OI profile as the proofing - profile. Note that if someone already has specified a - proofing profile it is unclear what they are trying to do - with the output intent. In this case, we will use it - just for the source data below */ - dev_profile->proof_profile = picc_profile; + + /* If we are doing simulate overprint and the output intent is different than + what the device profile is the we will end up pushing the pdf14 device + and doing a rendering to the output intent color space. Keep the device + profile as is, and do not do a proofing profile */ + + if (!(ctx->pgs->device->icc_struct->overprint_control == gs_overprint_control_simulate && + !gsicc_profiles_equal(dev_profile->oi_profile, dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE]))) { + if (ncomps == dev_comps && index < gs_color_space_index_DevicePixel) { + /* The OI profile is the same type as the profile for the device and a + "default" profile for the device was not externally set. So we go + ahead and use the OI profile as the device profile. Care needs to be + taken here to keep from screwing up any device parameters. We will + use a keyword of OIProfile for the user/device parameter to indicate + its usage. Also, note conflicts if one is setting object dependent + color management */ + dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE] = picc_profile; rc_increment(picc_profile); - if_debug0m(gs_debug_flag_icc, ctx->memory, "[icc] OutputIntent used for proof profile\n"); + if_debug0m(gs_debug_flag_icc, ctx->memory, "[icc] OutputIntent used for device profile\n"); + } else { + if (dev_profile->proof_profile == NULL) { + /* This means that we should use the OI profile as the proofing + profile. Note that if someone already has specified a + proofing profile it is unclear what they are trying to do + with the output intent. In this case, we will use it + just for the source data below */ + dev_profile->proof_profile = picc_profile; + rc_increment(picc_profile); + if_debug0m(gs_debug_flag_icc, ctx->memory, "[icc] OutputIntent used for proof profile\n"); + } } } + /* Now the source colors. See which source color space needs to use the output intent ICC profile */ index = gsicc_get_default_type(source_profile); @@ -2674,3 +2783,171 @@ int pdfi_color_setoutputintent(pdf_context *ctx, pdf_dict *intent_dict, pdf_stre pdfi_seek(ctx, ctx->main_stream, savedoffset, SEEK_SET); return code; } + +static int Check_Default_Space(pdf_context *ctx, pdf_obj *space, pdf_dict *source_dict, int num_components) +{ + pdf_obj *primary = NULL; + pdf_obj *ref_space = NULL; + int code = 0; + + if (space->type == PDF_NAME) + { + if (pdfi_name_is((const pdf_name *)space, "DeviceGray")) + return (num_components == 1 ? 0 : gs_error_rangecheck); + if (pdfi_name_is((const pdf_name *)space, "DeviceCMYK")) + return (num_components == 4 ? 0 : gs_error_rangecheck); + if (pdfi_name_is((const pdf_name *)space, "DeviceRGB")) + return (num_components == 3 ? 0 : gs_error_rangecheck); + + code = pdfi_find_resource(ctx, (unsigned char *)"ColorSpace", (pdf_name *)space, (pdf_dict *)source_dict, + NULL, &ref_space); + if (code < 0) + return code; + + if (ref_space->type == PDF_NAME) { + if (ref_space->object_num != 0 && ref_space->object_num == space->object_num) { + pdfi_countdown(ref_space); + return_error(gs_error_circular_reference); + } + if (pdfi_name_is((const pdf_name *)ref_space, "DeviceGray")) { + pdfi_countdown(ref_space); + return (num_components == 1 ? 0 : gs_error_rangecheck); + } + if (pdfi_name_is((const pdf_name *)ref_space, "DeviceCMYK")) { + pdfi_countdown(ref_space); + return (num_components == 4 ? 0 : gs_error_rangecheck); + } + if (pdfi_name_is((const pdf_name *)ref_space, "DeviceRGB")) { + pdfi_countdown(ref_space); + return (num_components == 3 ? 0 : gs_error_rangecheck); + } + pdfi_countdown(ref_space); + return_error(gs_error_typecheck); + } + space = ref_space; + } + + if (space->type == PDF_ARRAY) { + code = pdfi_array_get(ctx, (pdf_array *)space, 0, &primary); + if (code < 0) + goto exit; + + if (primary->type == PDF_NAME) { + if (pdfi_name_is((pdf_name *)primary, "Lab")) { + code = gs_note_error(gs_error_typecheck); + goto exit; + } + if (pdfi_name_is((pdf_name *)primary, "Pattern")) { + code = gs_note_error(gs_error_typecheck); + goto exit; + } + if (pdfi_name_is((pdf_name *)primary, "Indexed")) { + code = gs_note_error(gs_error_typecheck); + goto exit; + } + } + } else + code = gs_note_error(gs_error_typecheck); + +exit: + pdfi_countdown(primary); + pdfi_countdown(ref_space); + return code; +} + +int pdfi_setup_DefaultSpaces(pdf_context *ctx, pdf_dict *source_dict) +{ + int code = 0; + pdf_dict *resources_dict = NULL, *colorspaces_dict = NULL; + pdf_obj *DefaultSpace = NULL; + + if (ctx->args.NOSUBSTDEVICECOLORS) + return 0; + + /* Create any required DefaultGray, DefaultRGB or DefaultCMYK + * spaces. + */ + code = pdfi_dict_knownget(ctx, source_dict, "Resources", (pdf_obj **)&resources_dict); + if (code > 0) { + code = pdfi_dict_knownget(ctx, resources_dict, "ColorSpace", (pdf_obj **)&colorspaces_dict); + if (code > 0) { + code = pdfi_dict_knownget(ctx, colorspaces_dict, "DefaultGray", &DefaultSpace); + if (code > 0) { + gs_color_space *pcs; + + code = Check_Default_Space(ctx, DefaultSpace, source_dict, 1); + if (code >= 0) { + code = pdfi_create_colorspace(ctx, DefaultSpace, NULL, source_dict, &pcs, false); + /* If any given Default* space fails simply ignore it, we wil then use the Device + * space instead, this is as per the spec. + */ + if (code >= 0) { + if (gs_color_space_num_components(pcs) == 1) { + ctx->page.DefaultGray_cs = pcs; + pdfi_set_colour_callback(pcs, ctx, NULL); + } else { + pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_DEFAULTSPACE, "pdfi_setup_DefaultSpaces", NULL); + rc_decrement(pcs, "setup_DefautSpaces"); + } + } + } else + pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_DEFAULTSPACE, "pdfi_setup_DefaultSpaces", NULL); + } + pdfi_countdown(DefaultSpace); + DefaultSpace = NULL; + code = pdfi_dict_knownget(ctx, colorspaces_dict, "DefaultRGB", &DefaultSpace); + if (code > 0) { + gs_color_space *pcs; + + code = Check_Default_Space(ctx, DefaultSpace, source_dict, 1); + if (code >= 0) { + code = pdfi_create_colorspace(ctx, DefaultSpace, NULL, source_dict, &pcs, false); + /* If any given Default* space fails simply ignore it, we wil then use the Device + * space instead, this is as per the spec. + */ + if (code >= 0) { + if (gs_color_space_num_components(pcs) == 3) { + ctx->page.DefaultRGB_cs = pcs; + pdfi_set_colour_callback(pcs, ctx, NULL); + } else { + rc_decrement(pcs, "setup_DefautSpaces"); + pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_DEFAULTSPACE, "pdfi_setup_DefaultSpaces", NULL); + } + } + } else + pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_DEFAULTSPACE, "pdfi_setup_DefaultSpaces", NULL); + } + pdfi_countdown(DefaultSpace); + DefaultSpace = NULL; + code = pdfi_dict_knownget(ctx, colorspaces_dict, "DefaultCMYK", &DefaultSpace); + if (code > 0) { + gs_color_space *pcs; + + code = Check_Default_Space(ctx, DefaultSpace, source_dict, 1); + if (code >= 0) { + code = pdfi_create_colorspace(ctx, DefaultSpace, NULL, source_dict, &pcs, false); + /* If any given Default* space fails simply ignore it, we wil then use the Device + * space instead, this is as per the spec. + */ + if (code >= 0) { + if (gs_color_space_num_components(pcs) == 4) { + ctx->page.DefaultCMYK_cs = pcs; + pdfi_set_colour_callback(pcs, ctx, NULL); + } else { + pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_DEFAULTSPACE, "pdfi_setup_DefaultSpaces", NULL); + rc_decrement(pcs, "setup_DefautSpaces"); + } + } + } else + pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_DEFAULTSPACE, "pdfi_setup_DefaultSpaces", NULL); + } + pdfi_countdown(DefaultSpace); + DefaultSpace = NULL; + } + } + + pdfi_countdown(DefaultSpace); + pdfi_countdown(resources_dict); + pdfi_countdown(colorspaces_dict); + return 0; +} diff --git a/pdf/pdf_colour.h b/pdf/pdf_colour.h index 5cc9f0eb..263c3d9d 100644 --- a/pdf/pdf_colour.h +++ b/pdf/pdf_colour.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -20,13 +20,49 @@ #include "gscolor1.h" #include "gscspace.h" +#include "pdf_stack.h" /* for pdfi_countup/countdown */ +#include "pdf_misc.h" /* for pdf_name_cmp */ + +static inline void pdfi_set_colourspace_name(pdf_context *ctx, gs_color_space *pcs, pdf_name *n) +{ + if (pcs->interpreter_data != NULL) { + pdf_obj *o = (pdf_obj *)(pcs->interpreter_data); + if (o != NULL && o->type == PDF_NAME) { + pdfi_countdown(o); + pcs->interpreter_data = NULL; + } + } + + if (n != NULL) { + pcs->interpreter_data = n; + pdfi_countup(n); + } else { + if (pcs->interpreter_data == NULL) + pcs->interpreter_data = ctx; + } +} static inline void pdfi_set_colour_callback(gs_color_space *pcs, pdf_context *ctx, gs_cspace_free_proc_t pdfi_cspace_free_callback) { - pcs->interpreter_data = ctx; + if (pcs->interpreter_data == NULL) + pcs->interpreter_data = ctx; pcs->interpreter_free_cspace_proc = pdfi_cspace_free_callback; } +static inline int check_same_current_space(pdf_context *ctx, pdf_name *n) +{ + pdf_obj *o = (pdf_obj *)(ctx->pgs->color[0].color_space->interpreter_data); + + if (o == NULL || o->type != PDF_NAME) + return 0; + + if (pdfi_name_cmp(n, (pdf_name *)o) == 0) { + if (n->object_num == o->object_num && n->indirect_num == o->indirect_num) + return 1; + } + return 0; +} + int pdfi_setgraystroke(pdf_context *ctx); int pdfi_setgrayfill(pdf_context *ctx); int pdfi_setrgbstroke(pdf_context *ctx); @@ -50,11 +86,14 @@ int pdfi_gs_setcolorspace(pdf_context *ctx, gs_color_space *pcs); int pdfi_setcolorspace(pdf_context *ctx, pdf_obj *space, pdf_dict *stream_dict, pdf_dict *page_dict); int pdfi_create_colorspace(pdf_context *ctx, pdf_obj *space, pdf_dict *stream_dict, pdf_dict *page_dict, gs_color_space **ppcs, bool inline_image); int pdfi_create_icc_colorspace_from_stream(pdf_context *ctx, pdf_c_stream *stream, gs_offset_t offset, - unsigned int length, int comps, int *icc_N, gs_color_space **ppcs); + unsigned int length, int comps, int *icc_N, ulong dictkey, gs_color_space **ppcs); /* Page level spot colour detection and enumeration */ int pdfi_check_ColorSpace_for_spots(pdf_context *ctx, pdf_obj *space, pdf_dict *parent_dict, pdf_dict *page_dict, pdf_dict *spot_dict); int pdfi_color_setoutputintent(pdf_context *ctx, pdf_dict *intent_dict, pdf_stream *profile); +/* Sets up the DefaultRGB, DefaultCMYK and DefaultGray colour spaces if present */ +int pdfi_setup_DefaultSpaces(pdf_context *ctx, pdf_dict *source_dict); + #endif diff --git a/pdf/pdf_deref.c b/pdf/pdf_deref.c index fb3ce67b..91d77f63 100644 --- a/pdf/pdf_deref.c +++ b/pdf/pdf_deref.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2021 Artifex Software, Inc. +/* Copyright (C) 2020-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -240,12 +240,33 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ pdfi_pop(ctx, 1); dict = NULL; pdfi_push(ctx, (pdf_obj *)stream_obj); - pdfi_countdown(stream_obj); /* get rid of extra ref */ stream_obj->stream_dict->indirect_num = stream_obj->stream_dict->object_num = objnum; stream_obj->stream_dict->indirect_gen = stream_obj->stream_dict->generation_num = gen; stream_obj->stream_offset = offset; + /* Exceptional code. Normally we do not need to worry about detecting circular references + * when reading objects, because we do not dereference any indirect objects. However streams + * are a slight exception in that we do get the Length from the stream dictionay and if that + * is an indirect reference, then we dereference it. + * OSS-fuzz bug 43247 has a stream where the value associated iwht the /Length is an indirect + * reference to the same stream object, and leads to infinite recursion. So deal with that + * possibility here. + */ + code = pdfi_loop_detector_mark(ctx); + if (code < 0) { + pdfi_countdown(stream_obj); /* get rid of extra ref */ + return code; + } + if (pdfi_loop_detector_check_object(ctx, stream_obj->object_num)) + return_error(gs_error_circular_reference); + + code = pdfi_loop_detector_add_object(ctx, stream_obj->object_num); + if (code < 0) { + pdfi_countdown(stream_obj); /* get rid of extra ref */ + return code; + } + /* This code may be a performance overhead, it simply skips over the stream contents * and checks that the stream ends with a 'endstream endobj' pair. We could add a * 'go faster' flag for users who are certain their PDF files are well-formed. This @@ -256,20 +277,28 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ if (code < 0) { char extra_info[gp_file_name_sizeof]; - gs_sprintf(extra_info, "Stream object %u missing mandatory keyword /Length, unable to verify the stream length.\n", objnum); + (void)pdfi_loop_detector_cleartomark(ctx); + gs_snprintf(extra_info, sizeof(extra_info), "Stream object %u missing mandatory keyword /Length, unable to verify the stream length.\n", objnum); pdfi_set_error(ctx, 0, NULL, E_PDF_BADSTREAM, "pdfi_read_stream_object", extra_info); + pdfi_countdown(stream_obj); /* get rid of extra ref */ return 0; } + code = pdfi_loop_detector_cleartomark(ctx); + if (code < 0) { + pdfi_countdown(stream_obj); /* get rid of extra ref */ + return code; + } if (i < 0 || (i + offset)> ctx->main_stream_length) { char extra_info[gp_file_name_sizeof]; - gs_sprintf(extra_info, "Stream object %u has /Length which, when added to offset of object, exceeds file size.\n", objnum); + gs_snprintf(extra_info, sizeof(extra_info), "Stream object %u has /Length which, when added to offset of object, exceeds file size.\n", objnum); pdfi_set_error(ctx, 0, NULL, E_PDF_BADSTREAM, "pdfi_read_stream_object", extra_info); } else { code = pdfi_seek(ctx, ctx->main_stream, i, SEEK_CUR); if (code < 0) { pdfi_pop(ctx, 1); + pdfi_countdown(stream_obj); /* get rid of extra ref */ return code; } @@ -280,21 +309,28 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ if (code < 0 || pdfi_count_stack(ctx) < 2) { char extra_info[gp_file_name_sizeof]; - gs_sprintf(extra_info, "Failed to find a valid object at end of stream object %u.\n", objnum); + gs_snprintf(extra_info, sizeof(extra_info), "Failed to find a valid object at end of stream object %u.\n", objnum); pdfi_log_info(ctx, "pdfi_read_stream_object", extra_info); + /* It is possible for pdfi_read_token to clear the stack, losing the stream object. If that + * happens give up. + */ + if (pdfi_count_stack(ctx) == 0) { + pdfi_countdown(stream_obj); /* get rid of extra ref */ + return code; + } } else { if (((pdf_obj *)ctx->stack_top[-1])->type != PDF_KEYWORD) { char extra_info[gp_file_name_sizeof]; - gs_sprintf(extra_info, "Failed to find 'endstream' keyword at end of stream object %u.\n", objnum); + gs_snprintf(extra_info, sizeof(extra_info), "Failed to find 'endstream' keyword at end of stream object %u.\n", objnum); pdfi_set_error(ctx, 0, NULL, E_PDF_MISSINGENDOBJ, "pdfi_read_stream_object", extra_info); } else { keyword = ((pdf_keyword *)ctx->stack_top[-1]); if (keyword->key != TOKEN_ENDSTREAM) { char extra_info[gp_file_name_sizeof]; - gs_sprintf(extra_info, "Stream object %u has an incorrect /Length of %"PRIu64"\n", objnum, i); + gs_snprintf(extra_info, sizeof(extra_info), "Stream object %u has an incorrect /Length of %"PRIu64"\n", objnum, i); pdfi_log_info(ctx, "pdfi_read_stream_object", extra_info); } else { /* Cache the Length in the stream object and mark it valid */ @@ -316,17 +352,21 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ */ if (stream_obj->length_valid != true) { char Buffer[10]; - unsigned int loop, bytes, total = 0; + unsigned int bytes, total = 0; + int c = 0; code = pdfi_seek(ctx, ctx->main_stream, stream_obj->stream_offset, SEEK_SET); if (code < 0) { + pdfi_countdown(stream_obj); /* get rid of extra ref */ pdfi_pop(ctx, 1); return code; } memset(Buffer, 0x00, 10); bytes = pdfi_read_bytes(ctx, (byte *)Buffer, 1, 9, ctx->main_stream); - if (bytes < 9) + if (bytes < 9) { + pdfi_countdown(stream_obj); /* get rid of extra ref */ return_error(gs_error_ioerror); + } total = bytes; do { @@ -340,19 +380,22 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ stream_obj->length_valid = true; break; } - for (loop = 0;loop < 9;loop++){ - Buffer[loop] = Buffer[loop + 1]; - } - bytes = pdfi_read_bytes(ctx, (byte *)&Buffer[9], 1, 1, ctx->main_stream); - total += bytes; - } while(bytes); - if (bytes <= 0) + memmove(Buffer, Buffer+1, 9); + c = pdfi_read_byte(ctx, ctx->main_stream); + if (c < 0) + break; + Buffer[9] = (byte)c; + total++; + } while(1); + pdfi_countdown(stream_obj); /* get rid of extra ref */ + if (c < 0) return_error(gs_error_ioerror); return 0; } code = pdfi_read_token(ctx, ctx->main_stream, objnum, gen); if (code < 0) { + pdfi_countdown(stream_obj); /* get rid of extra ref */ if (ctx->args.pdfstoponerror) return code; else @@ -363,10 +406,13 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ return 0; } - if (pdfi_count_stack(ctx) < 2) + if (pdfi_count_stack(ctx) < 2) { + pdfi_countdown(stream_obj); /* get rid of extra ref */ return_error(gs_error_stackunderflow); + } if (((pdf_obj *)ctx->stack_top[-1])->type != PDF_KEYWORD) { + pdfi_countdown(stream_obj); /* get rid of extra ref */ pdfi_pop(ctx, 1); if (ctx->args.pdfstoponerror) return_error(gs_error_typecheck); @@ -376,6 +422,8 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ */ return 0; } + pdfi_countdown(stream_obj); /* get rid of extra ref */ + keyword = ((pdf_keyword *)ctx->stack_top[-1]); if (keyword->key != TOKEN_ENDOBJ) { pdfi_pop(ctx, 2); @@ -402,11 +450,15 @@ int pdfi_read_bare_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_t stream_ if (code < 0) return code; + if (code == 0) + /* failed to read a token */ + return_error(gs_error_syntaxerror); + do { /* move all the saved offsets up by one */ saved_offset[0] = saved_offset[1]; saved_offset[1] = saved_offset[2]; - saved_offset[2] = pdfi_unread_tell(ctx);; + saved_offset[2] = pdfi_unread_tell(ctx); code = pdfi_read_token(ctx, s, objnum, gen); if (code < 0) { @@ -415,6 +467,7 @@ int pdfi_read_bare_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_t stream_ } if (s->eof) return_error(gs_error_syntaxerror); + code = 0; }while (ctx->stack_top[-1]->type != PDF_KEYWORD); keyword = ((pdf_keyword *)ctx->stack_top[-1]); @@ -494,6 +547,9 @@ static int pdfi_read_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_t strea code = pdfi_read_token(ctx, s, 0, 0); if (code < 0) return code; + if (code == 0) + return_error(gs_error_syntaxerror); + if (stack_size >= pdfi_count_stack(ctx)) return gs_note_error(gs_error_ioerror); if (((pdf_obj *)ctx->stack_top[-1])->type != PDF_INT) { @@ -506,6 +562,9 @@ static int pdfi_read_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_t strea code = pdfi_read_token(ctx, s, 0, 0); if (code < 0) return code; + if (code == 0) + return_error(gs_error_syntaxerror); + if (stack_size >= pdfi_count_stack(ctx)) return gs_note_error(gs_error_ioerror); if (((pdf_obj *)ctx->stack_top[-1])->type != PDF_INT) { @@ -535,14 +594,13 @@ static int pdfi_read_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_t strea } static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **object, - const xref_entry *entry) + const xref_entry *entry, bool cache) { int code = 0; - xref_entry *compressed_entry = &ctx->xref_table->xref[entry->u.compressed.compressed_stream_num]; + xref_entry *compressed_entry; pdf_c_stream *compressed_stream = NULL; pdf_c_stream *SubFile_stream = NULL; pdf_c_stream *Object_stream = NULL; - char Buffer[256]; int i = 0, object_length = 0; int64_t num_entries, found_object; int64_t Length; @@ -552,6 +610,11 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p pdf_name *Type = NULL; pdf_obj *temp_obj; + if (entry->u.compressed.compressed_stream_num > ctx->xref_table->xref_size - 1) + return_error(gs_error_undefined); + + compressed_entry = &ctx->xref_table->xref[entry->u.compressed.compressed_stream_num]; + if (ctx->args.pdfdebug) { dmprintf1(ctx->memory, "%% Reading compressed object (%"PRIi64" 0 obj)", obj); dmprintf1(ctx->memory, " from ObjStm with object number %"PRIi64"\n", compressed_entry->object_num); @@ -569,6 +632,11 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p if (code < 0) goto exit; + if (pdfi_count_stack(ctx) < 1) { + code = gs_note_error(gs_error_stackunderflow); + goto exit; + } + if ((ctx->stack_top[-1])->type != PDF_STREAM) { pdfi_pop(ctx, 1); code = gs_note_error(gs_error_typecheck); @@ -639,6 +707,10 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p code = pdfi_read_token(ctx, compressed_stream, obj, gen); if (code < 0) goto exit; + if (code == 0) { + code = gs_note_error(gs_error_syntaxerror); + goto exit; + } temp_obj = ctx->stack_top[-1]; if (temp_obj->type != PDF_INT) { code = gs_note_error(gs_error_typecheck); @@ -650,9 +722,14 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p code = pdfi_read_token(ctx, compressed_stream, obj, gen); if (code < 0) goto exit; + if (code == 0) { + code = gs_note_error(gs_error_syntaxerror); + goto exit; + } temp_obj = ctx->stack_top[-1]; if (temp_obj->type != PDF_INT) { pdfi_pop(ctx, 1); + code = gs_note_error(gs_error_typecheck); goto exit; } if (i == entry->u.compressed.object_index) { @@ -671,8 +748,8 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p /* Skip to the offset of the object we want to read */ for (i=0;i < offset;i++) { - code = pdfi_read_bytes(ctx, (byte *)&Buffer[0], 1, 1, compressed_stream); - if (code <= 0) { + int c = pdfi_read_byte(ctx, compressed_stream); + if (c < 0) { code = gs_note_error(gs_error_ioerror); goto exit; } @@ -696,6 +773,10 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p code = pdfi_read_token(ctx, Object_stream, obj, gen); if (code < 0) goto exit; + if (code == 0) { + code = gs_note_error(gs_error_syntaxerror); + goto exit; + } if (ctx->stack_top[-1]->type == PDF_ARRAY_MARK || ctx->stack_top[-1]->type == PDF_DICT_MARK) { int start_depth = pdfi_count_stack(ctx); @@ -704,6 +785,10 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p code = pdfi_read_token(ctx, Object_stream, obj, gen); if (code < 0) goto exit; + if (code == 0) { + code = gs_note_error(gs_error_syntaxerror); + goto exit; + } if (compressed_stream->eof == true) { code = gs_note_error(gs_error_ioerror); goto exit; @@ -720,10 +805,12 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p pdfi_countup(*object); pdfi_pop(ctx, 1); - code = pdfi_add_to_cache(ctx, *object); - if (code < 0) { - pdfi_countdown(*object); - goto exit; + if (cache) { + code = pdfi_add_to_cache(ctx, *object); + if (code < 0) { + pdfi_countdown(*object); + goto exit; + } } exit: @@ -742,7 +829,7 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p /* pdf_dereference returns an object with a reference count of at least 1, this represents the * reference being held by the caller (in **object) when we return from this function. */ -int pdfi_dereference(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **object) +static int pdfi_dereference_main(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **object, bool cache) { xref_entry *entry; int code, stack_depth = pdfi_count_stack(ctx); @@ -757,7 +844,7 @@ int pdfi_dereference(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **obj if (obj >= ctx->xref_table->xref_size) { char extra_info[gp_file_name_sizeof]; - gs_sprintf(extra_info, "Error, attempted to dereference object %"PRIu64", which is not present in the xref table\n", obj); + gs_snprintf(extra_info, sizeof(extra_info), "Error, attempted to dereference object %"PRIu64", which is not present in the xref table\n", obj); pdfi_set_error(ctx, 0, NULL, E_PDF_BADOBJNUMBER, "pdfi_dereference", extra_info); if(ctx->args.pdfstoponerror) @@ -777,13 +864,18 @@ int pdfi_dereference(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **obj if (entry->free) { char extra_info[gp_file_name_sizeof]; - gs_sprintf(extra_info, "Attempt to dereference free object %"PRIu64", trying next object number as offset.\n", entry->object_num); + gs_snprintf(extra_info, sizeof(extra_info), "Attempt to dereference free object %"PRIu64", trying next object number as offset.\n", entry->object_num); pdfi_set_error(ctx, 0, NULL, E_PDF_DEREF_FREE_OBJ, "pdfi_dereference", extra_info); } if (ctx->loop_detection) { if (pdfi_loop_detector_check_object(ctx, obj) == true) return_error(gs_error_circular_reference); + if (entry->free) { + code = pdfi_loop_detector_add_object(ctx, obj); + if (code < 0) + return code; + } } if (entry->cache != NULL){ pdf_obj_cache_entry *cache_entry = entry->cache; @@ -802,12 +894,11 @@ int pdfi_dereference(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **obj /* This is an object in a compressed object stream */ ctx->encryption.decrypt_strings = false; - code = pdfi_deref_compressed(ctx, obj, gen, object, entry); + code = pdfi_deref_compressed(ctx, obj, gen, object, entry, cache); if (code < 0 || *object == NULL) goto error; } else { pdf_c_stream *SubFile_stream = NULL; - pdf_string *EODString; #if CACHE_STATISTICS ctx->misses++; #endif @@ -817,20 +908,17 @@ int pdfi_dereference(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **obj if (code < 0) goto error; - code = pdfi_name_alloc(ctx, (byte *)"trailer", 6, (pdf_obj **)&EODString); + code = pdfi_apply_SubFileDecode_filter(ctx, 0, "trailer", ctx->main_stream, &SubFile_stream, false); if (code < 0) goto error; - pdfi_countup(EODString); - - code = pdfi_apply_SubFileDecode_filter(ctx, 0, EODString, ctx->main_stream, &SubFile_stream, false); - if (code < 0) { - pdfi_countdown(EODString); - goto error; - } code = pdfi_read_object(ctx, SubFile_stream, entry->u.uncompressed.offset); - pdfi_countdown(EODString); + /* pdfi_read_object() could do a repair, which would invalidate the xref and rebuild it. + * reload the xref entry to be certain it is valid. + */ + entry = &ctx->xref_table->xref[obj]; + pdfi_close_file(ctx, SubFile_stream); if (code < 0) { int code1 = 0; @@ -848,7 +936,7 @@ int pdfi_dereference(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **obj code1 = pdfi_repair_file(ctx); if (code1 == 0) - return pdfi_dereference(ctx, obj, gen, object); + return pdfi_dereference_main(ctx, obj, gen, object, cache); /* Repair failed, just give up and return an error */ return code; } @@ -857,10 +945,12 @@ int pdfi_dereference(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **obj *object = ctx->stack_top[-1]; pdfi_countup(*object); pdfi_pop(ctx, 1); - code = pdfi_add_to_cache(ctx, *object); - if (code < 0) { - pdfi_countdown(*object); - goto error; + if (cache) { + code = pdfi_add_to_cache(ctx, *object); + if (code < 0) { + pdfi_countdown(*object); + goto error; + } } } else { pdfi_pop(ctx, 1); @@ -897,6 +987,16 @@ error: return code; } +int pdfi_dereference(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **object) +{ + return pdfi_dereference_main(ctx, obj, gen, object, true); +} + +int pdfi_dereference_nocache(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **object) +{ + return pdfi_dereference_main(ctx, obj, gen, object, false); +} + /* do a derefence with loop detection */ int pdfi_deref_loop_detect(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **object) { @@ -911,6 +1011,18 @@ int pdfi_deref_loop_detect(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj return code; } +int pdfi_deref_loop_detect_nocache(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **object) +{ + int code; + + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + return code; + + code = pdfi_dereference_nocache(ctx, obj, gen, object); + (void)pdfi_loop_detector_cleartomark(ctx); + return code; +} static int pdfi_resolve_indirect_array(pdf_context *ctx, pdf_obj *obj, bool recurse) { @@ -921,7 +1033,20 @@ static int pdfi_resolve_indirect_array(pdf_context *ctx, pdf_obj *obj, bool recu arraysize = pdfi_array_size(array); for (index = 0; index < arraysize; index++) { + if (ctx->loop_detection != NULL) { + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + return code; + } + code = pdfi_array_get_no_store_R(ctx, array, index, &object); + + if (ctx->loop_detection != NULL) { + int code1 = pdfi_loop_detector_cleartomark(ctx); + if (code1 < 0) + return code1; + } + if (code == gs_error_circular_reference) { /* Just leave as an indirect ref */ code = 0; @@ -931,7 +1056,7 @@ static int pdfi_resolve_indirect_array(pdf_context *ctx, pdf_obj *obj, bool recu if (object->type != PDF_STREAM) code = pdfi_array_put(ctx, array, index, object); if (recurse) - code = pdfi_resolve_indirect(ctx, object, recurse); + code = pdfi_resolve_indirect_loop_detect(ctx, NULL, object, recurse); } if (code < 0) goto exit; @@ -958,8 +1083,24 @@ static int pdfi_resolve_indirect_dict(pdf_context *ctx, pdf_obj *obj, bool recur * circular references. */ for (index=0; index<dictsize; index ++) { - Key = (pdf_name *)dict->keys[index]; + Key = (pdf_name *)dict->list[index].key; + if (pdfi_name_is(Key, "Parent")) + continue; + + if (ctx->loop_detection != NULL) { + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + return code; + } + code = pdfi_dict_get_no_store_R_key(ctx, dict, Key, &Value); + + if (ctx->loop_detection != NULL) { + int code1 = pdfi_loop_detector_cleartomark(ctx); + if (code1 < 0) + return code1; + } + if (code == gs_error_circular_reference) { /* Just leave as an indirect ref */ code = 0; @@ -967,9 +1108,9 @@ static int pdfi_resolve_indirect_dict(pdf_context *ctx, pdf_obj *obj, bool recur if (code < 0) goto exit; /* don't store the object if it's a stream (leave as a ref) */ if (Value->type != PDF_STREAM) - pdfi_dict_put_obj(ctx, dict, (pdf_obj *)Key, Value); + pdfi_dict_put_obj(ctx, dict, (pdf_obj *)Key, Value, true); if (recurse) - code = pdfi_resolve_indirect(ctx, Value, recurse); + code = pdfi_resolve_indirect_loop_detect(ctx, NULL, Value, recurse); } if (code < 0) goto exit; @@ -1016,11 +1157,16 @@ int pdfi_resolve_indirect_loop_detect(pdf_context *ctx, pdf_obj *parent, pdf_obj code = pdfi_loop_detector_add_object(ctx, parent->object_num); if (code < 0) goto exit; } + if (value->object_num != 0) { + if (pdfi_loop_detector_check_object(ctx, value->object_num)) { + code = gs_note_error(gs_error_circular_reference); + goto exit; + } code = pdfi_loop_detector_add_object(ctx, value->object_num); if (code < 0) goto exit; } - code = pdfi_resolve_indirect(ctx, value, false); + code = pdfi_resolve_indirect(ctx, value, recurse); exit: (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current loop */ diff --git a/pdf/pdf_deref.h b/pdf/pdf_deref.h index 132be7dd..71d89168 100644 --- a/pdf/pdf_deref.h +++ b/pdf/pdf_deref.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2021 Artifex Software, Inc. +/* Copyright (C) 2020-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -19,7 +19,9 @@ int replace_cache_entry(pdf_context *ctx, pdf_obj *o); int is_compressed_object(pdf_context *ctx, uint32_t obj, uint32_t gen); int pdfi_dereference(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **object); +int pdfi_dereference_nocache(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **object); int pdfi_deref_loop_detect(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **object); +int pdfi_deref_loop_detect_nocache(pdf_context *ctx, uint64_t obj, uint64_t gen, pdf_obj **object); int pdfi_read_bare_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_t stream_offset, uint32_t objnum, uint32_t gen); int pdfi_resolve_indirect(pdf_context *ctx, pdf_obj *value, bool recurse); int pdfi_resolve_indirect_loop_detect(pdf_context *ctx, pdf_obj *parent, pdf_obj *value, bool recurse); diff --git a/pdf/pdf_device.c b/pdf/pdf_device.c index 6d7547e2..9ed97133 100644 --- a/pdf/pdf_device.c +++ b/pdf/pdf_device.c @@ -18,8 +18,10 @@ #include "pdf_int.h" #include "pdf_stack.h" #include "pdf_device.h" -#include "gdevvec.h" /* for gs_device_vector */ -#include "gxdevsop.h" /* For special ops : dev_param_req_t */ +#include "gsdevice.h" /* For gs_setdevice_no_erase */ +#include "gspaint.h" /* For gs_erasepage */ +#include "gdevvec.h" /* for gs_device_vector */ +#include "gxdevsop.h" /* For special ops : dev_param_req_t */ int pdfi_device_check_param(gx_device *dev, const char *param, gs_c_param_list *list) { diff --git a/pdf/pdf_dict.c b/pdf/pdf_dict.c index 4b65404e..34356dfe 100644 --- a/pdf/pdf_dict.c +++ b/pdf/pdf_dict.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -24,6 +24,9 @@ #include "pdf_loop_detect.h" #include "pdf_misc.h" +static int pdfi_dict_find(pdf_context *ctx, pdf_dict *d, const char *Key, bool sort); +static int pdfi_dict_find_key(pdf_context *ctx, pdf_dict *d, const pdf_name *Key, bool sort); + void pdfi_free_dict(pdf_obj *o) { pdf_dict *d = (pdf_dict *)o; @@ -34,15 +37,14 @@ void pdfi_free_dict(pdf_obj *o) for (i=0;i < d->entries;i++) { #if DEBUG_DICT - name = (pdf_name *)d->keys[i]; + name = (pdf_name *)d->list[i].key; #endif - if (d->values[i] != NULL) - pdfi_countdown(d->values[i]); - if (d->keys[i] != NULL) - pdfi_countdown(d->keys[i]); + if (d->list[i].value != NULL) + pdfi_countdown(d->list[i].value); + if (d->list[i].key != NULL) + pdfi_countdown(d->list[i].key); } - gs_free_object(OBJ_MEMORY(d), d->keys, "pdf interpreter free dictionary keys"); - gs_free_object(OBJ_MEMORY(d), d->values, "pdf interpreter free dictioanry values"); + gs_free_object(OBJ_MEMORY(d), d->list, "pdf interpreter free dictionary key/values"); gs_free_object(OBJ_MEMORY(d), d, "pdf interpreter free dictionary"); } @@ -55,31 +57,22 @@ static int pdfi_dict_delete_inner(pdf_context *ctx, pdf_dict *d, pdf_name *n, co pdf_name *name; #endif - for (i=0;i < d->entries;i++) { -#if DEBUG_DICT - name = (pdf_name *)d->keys[i]; -#endif - if (n != NULL) { - if (pdfi_name_cmp(n, (pdf_name *)d->keys[i]) == 0) - break; - } else { - if (pdfi_name_is((pdf_name *)d->keys[i], str)) - break; - } + if (n != NULL) + i = pdfi_dict_find_key(ctx, d, (const pdf_name *)n, false); + else + i = pdfi_dict_find(ctx, d, str, false); - } - if (i >= d->entries) - return_error(gs_error_undefined); - - pdfi_countdown(d->keys[i]); - pdfi_countdown(d->values[i]); - for( ;i < d->entries - 1;i++) { - d->keys[i] = d->keys[i + 1]; - d->values[i] = d->values[i + 1]; - } - d->keys[i] = NULL; - d->values[i] = NULL; + if (i < 0) + return i; + + pdfi_countdown(d->list[i].key); + pdfi_countdown(d->list[i].value); d->entries--; + if (i != d->entries) + memmove(&d->list[i], &d->list[i+1], (d->entries - i) * sizeof(d->list[0])); + d->list[d->entries].key = NULL; + d->list[d->entries].value = NULL; + d->is_sorted = false; return 0; } @@ -104,7 +97,17 @@ int pdfi_dict_alloc(pdf_context *ctx, uint64_t size, pdf_dict **d) return pdfi_object_alloc(ctx, PDF_DICT, size, (pdf_obj **)d); } -int pdfi_dict_from_stack(pdf_context *ctx, uint32_t indirect_num, uint32_t indirect_gen) +static int pdfi_dict_name_from_string(pdf_context *ctx, pdf_string *s, pdf_name **n) +{ + int code = pdfi_object_alloc(ctx, PDF_NAME, s->length, (pdf_obj **)n); + if (code >= 0) { + memcpy((*n)->data, s->data, s->length); + pdfi_countup(*n); + } + return code; +} + +int pdfi_dict_from_stack(pdf_context *ctx, uint32_t indirect_num, uint32_t indirect_gen, bool convert_string_keys) { uint64_t index = 0; pdf_dict *d = NULL; @@ -138,17 +141,31 @@ int pdfi_dict_from_stack(pdf_context *ctx, uint32_t indirect_num, uint32_t indir /* In PDF keys are *required* to be names, so we ought to check that here */ if (((pdf_obj *)ctx->stack_top[-2])->type == PDF_NAME) { - d->keys[i] = ctx->stack_top[-2]; - pdfi_countup(d->keys[i]); + d->list[i].key = ctx->stack_top[-2]; + pdfi_countup(d->list[i].key); #if DEBUG_DICT - key = (pdf_name *)d->keys[i]; + key = (pdf_name *)d->list[i].key; #endif - d->values[i] = ctx->stack_top[-1]; - pdfi_countup(d->values[i]); + d->list[i].value = ctx->stack_top[-1]; + pdfi_countup(d->list[i].value); } else { - pdfi_free_dict((pdf_obj *)d); - pdfi_clear_to_mark(ctx); - return_error(gs_error_typecheck); + if (convert_string_keys && ((pdf_obj *)ctx->stack_top[-2])->type == PDF_STRING) { + pdf_name *n; + code = pdfi_dict_name_from_string(ctx, (pdf_string *)ctx->stack_top[-2], &n); + if (code < 0) { + pdfi_free_dict((pdf_obj *)d); + pdfi_clear_to_mark(ctx); + return_error(gs_error_typecheck); + } + d->list[i].key = (pdf_obj *)n; /* pdfi_dict_name_from_string() sets refcnt to 1 */ + d->list[i].value = ctx->stack_top[-1]; + pdfi_countup(d->list[i].value); + } + else { + pdfi_free_dict((pdf_obj *)d); + pdfi_clear_to_mark(ctx); + return_error(gs_error_typecheck); + } } pdfi_pop(ctx, 2); @@ -187,40 +204,155 @@ pdfi_dict_get2(pdf_context *ctx, pdf_dict *d, const char *Key1, return code; } +static int pdfi_dict_compare_entry(const void *a, const void *b) +{ + pdf_name *key_a = (pdf_name *)((pdf_dict_entry *)a)->key, *key_b = (pdf_name *)((pdf_dict_entry *)b)->key; + + if (key_a == NULL) { + if (key_b == NULL) + return 0; + else + return 1; + } + + if (key_b == NULL) + return -1; + + if (key_a->length != key_b->length) + return key_a->length - key_b->length; + + return strncmp((const char *)key_a->data, (const char *)key_b->data, key_a->length); +} + +static int pdfi_dict_find_sorted(pdf_context *ctx, pdf_dict *d, const char *Key) +{ + int start = 0, end = d->size - 1, middle = 0, keylen = strlen(Key); + pdf_name *test_key; + + while (start <= end) { + middle = start + (end - start) / 2; + test_key = (pdf_name *)d->list[middle].key; + + /* Sorting pushes unused key/values (NULL) to the end of the dictionary */ + if (test_key == NULL) { + end = middle - 1; + continue; + } + + if (test_key->length == keylen) { + int result = strncmp((const char *)test_key->data, Key, keylen); + + if (result == 0) + return middle; + if (result < 0) + start = middle + 1; + else + end = middle - 1; + } else { + if (test_key->length < keylen) + start = middle + 1; + else + end = middle -1; + } + } + return gs_note_error(gs_error_undefined); +} + +static int pdfi_dict_find_unsorted(pdf_context *ctx, pdf_dict *d, const char *Key) +{ + int i; + pdf_name *t; + + for (i=0;i< d->entries;i++) { + t = (pdf_name *)d->list[i].key; + + if (t && t->type == PDF_NAME) { + if (pdfi_name_is((pdf_name *)t, Key)) { + return i; + } + } + } + return_error(gs_error_undefined); +} + +static int pdfi_dict_find(pdf_context *ctx, pdf_dict *d, const char *Key, bool sort) +{ + if (!d->is_sorted) { + if (d->entries > 32 && sort) { + qsort(d->list, d->size, sizeof(pdf_dict_entry), pdfi_dict_compare_entry); + d->is_sorted = true; + return pdfi_dict_find_sorted(ctx, d, Key); + } else + return pdfi_dict_find_unsorted(ctx, d, Key); + } else + return pdfi_dict_find_sorted(ctx, d, Key); +} + +static int pdfi_dict_find_key(pdf_context *ctx, pdf_dict *d, const pdf_name *Key, bool sort) +{ + char *Test = NULL; + int index = 0; + + Test = (char *)gs_alloc_bytes(ctx->memory, Key->length + 1, "pdfi_dict_find_key"); + if (Test == NULL) + return_error(gs_error_VMerror); + + memcpy(Test, Key->data, Key->length); + Test[Key->length] = 0x00; + + index = pdfi_dict_find(ctx, d, Test, sort); + + gs_free_object(ctx->memory, Test, "pdfi_dict_find_key"); + return index; +} + /* The object returned by pdfi_dict_get has its reference count incremented by 1 to * indicate the reference now held by the caller, in **o. */ -int pdfi_dict_get(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj **o) +int pdfi_dict_get_common(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj **o, bool cache) { - int i=0, code; - pdf_name *t; + int index = 0, code = 0; *o = NULL; if (d->type != PDF_DICT) return_error(gs_error_typecheck); - for (i=0;i< d->entries;i++) { - t = (pdf_name *)d->keys[i]; + index = pdfi_dict_find(ctx, d, Key, true); + if (index < 0) + return index; - if (t && t->type == PDF_NAME) { - if (pdfi_name_is((pdf_name *)t, Key)) { - if (d->values[i]->type == PDF_INDIRECT) { - pdf_indirect_ref *r = (pdf_indirect_ref *)d->values[i]; - - code = pdfi_deref_loop_detect(ctx, r->ref_object_num, r->ref_generation_num, o); - if (code < 0) - return code; - pdfi_countdown(d->values[i]); - d->values[i] = *o; - } - *o = d->values[i]; - pdfi_countup(*o); - return 0; - } + if (d->list[index].value->type == PDF_INDIRECT) { + pdf_indirect_ref *r = (pdf_indirect_ref *)d->list[index].value; + + if (r->ref_object_num == d->object_num) + return_error(gs_error_circular_reference); + + if (cache) + code = pdfi_deref_loop_detect(ctx, r->ref_object_num, r->ref_generation_num, o); + else + code = pdfi_deref_loop_detect_nocache(ctx, r->ref_object_num, r->ref_generation_num, o); + if (code < 0) + return code; + /* The file Bug690138.pdf has font dictionaries which contain ToUnicode keys where + * the value is an indirect reference to the same font object. If we replace the + * indirect reference in the dictionary with the font dictionary it becomes self + * referencing and never counts down to 0, leading to a memory leak. + * This is clearly an error, so flag it and don't replace the indirect reference. + */ + if ((*o)->object_num == 0 || (*o)->object_num != d->object_num) + { + pdfi_countdown(d->list[index].value); + d->list[index].value = *o; + } else { + pdfi_set_error(ctx, 0, NULL, E_DICT_SELF_REFERENCE, "pdfi_dict_get", NULL); + return 0; } } - return_error(gs_error_undefined); + *o = d->list[index].value; + pdfi_countup(*o); + + return code; } /* Get object from dict without resolving indirect references @@ -228,26 +360,20 @@ int pdfi_dict_get(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj **o) */ int pdfi_dict_get_no_deref(pdf_context *ctx, pdf_dict *d, const pdf_name *Key, pdf_obj **o) { - int i=0; - pdf_name *t; + int index=0; *o = NULL; if (d->type != PDF_DICT) return_error(gs_error_typecheck); - for (i=0;i< d->entries;i++) { - t = (pdf_name *)d->keys[i]; + index = pdfi_dict_find_key(ctx, d, Key, true); + if (index < 0) + return index; - if (t && t->type == PDF_NAME) { - if (pdfi_name_cmp((pdf_name *)t, Key)== 0) { - *o = d->values[i]; - pdfi_countup(*o); - return 0; - } - } - } - return_error(gs_error_undefined); + *o = d->list[index].value; + pdfi_countup(*o); + return 0; } /* Get by pdf_name rather than by char * @@ -256,64 +382,52 @@ int pdfi_dict_get_no_deref(pdf_context *ctx, pdf_dict *d, const pdf_name *Key, p */ int pdfi_dict_get_by_key(pdf_context *ctx, pdf_dict *d, const pdf_name *Key, pdf_obj **o) { - int i=0, code; - pdf_name *t; + int index=0, code = 0; *o = NULL; if (d->type != PDF_DICT) return_error(gs_error_typecheck); - for (i=0;i< d->entries;i++) { - t = (pdf_name *)d->keys[i]; + index = pdfi_dict_find_key(ctx, d, Key, true); + if (index < 0) + return index; - if (t && t->type == PDF_NAME) { - if (pdfi_name_cmp((pdf_name *)t, Key)== 0) { - if (d->values[i]->type == PDF_INDIRECT) { - pdf_indirect_ref *r = (pdf_indirect_ref *)d->values[i]; - - code = pdfi_deref_loop_detect(ctx, r->ref_object_num, r->ref_generation_num, o); - if (code < 0) - return code; - pdfi_countdown(d->values[i]); - d->values[i] = *o; - } - *o = d->values[i]; - pdfi_countup(*o); - return 0; - } - } + if (d->list[index].value->type == PDF_INDIRECT) { + pdf_indirect_ref *r = (pdf_indirect_ref *)d->list[index].value; + + code = pdfi_deref_loop_detect(ctx, r->ref_object_num, r->ref_generation_num, o); + if (code < 0) + return code; + pdfi_countdown(d->list[index].value); + d->list[index].value = *o; } - return_error(gs_error_undefined); + *o = d->list[index].value; + pdfi_countup(*o); + return 0; } /* Get indirect reference without de-referencing it */ int pdfi_dict_get_ref(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_indirect_ref **o) { - int i=0; - pdf_name *t; + int index=0; *o = NULL; if (d->type != PDF_DICT) return_error(gs_error_typecheck); - for (i=0;i< d->entries;i++) { - t = (pdf_name *)d->keys[i]; + index = pdfi_dict_find(ctx, d, Key, true); + if (index < 0) + return index; - if (t && t->type == PDF_NAME) { - if (pdfi_name_is((pdf_name *)t, Key)) { - if (d->values[i]->type == PDF_INDIRECT) { - *o = (pdf_indirect_ref *)d->values[i]; - pdfi_countup(*o); - return 0; - } else { - return_error(gs_error_typecheck); - } - } - } + if (d->list[index].value->type == PDF_INDIRECT) { + *o = (pdf_indirect_ref *)d->list[index].value; + pdfi_countup(*o); + return 0; + } else { + return_error(gs_error_typecheck); } - return_error(gs_error_undefined); } /* As per pdfi_dict_get(), but doesn't replace an indirect reference in a dictionary with a @@ -326,42 +440,32 @@ int pdfi_dict_get_ref(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_indire static int pdfi_dict_get_no_store_R_inner(pdf_context *ctx, pdf_dict *d, const char *strKey, const pdf_name *nameKey, pdf_obj **o) { - int i=0, code; - pdf_name *t; - bool match = false; + int index=0, code = 0; *o = NULL; if (d->type != PDF_DICT) return_error(gs_error_typecheck); - for (i=0;i< d->entries;i++) { - t = (pdf_name *)d->keys[i]; + if (strKey == NULL) + index = pdfi_dict_find_key(ctx, d, nameKey, true); + else + index = pdfi_dict_find(ctx, d, strKey, true); - if (t && t->type == PDF_NAME) { - if (strKey != NULL) { - if (pdfi_name_is(t, strKey)) - match = true; - } else { - if (!pdfi_name_cmp(t, nameKey)) - match = true; - } - if (match) { - if (d->values[i]->type == PDF_INDIRECT) { - pdf_indirect_ref *r = (pdf_indirect_ref *)d->values[i]; - - code = pdfi_dereference(ctx, r->ref_object_num, r->ref_generation_num, o); - if (code < 0) - return code; - } else { - *o = d->values[i]; - pdfi_countup(*o); - } - return 0; - } - } + if (index < 0) + return index; + + if (d->list[index].value->type == PDF_INDIRECT) { + pdf_indirect_ref *r = (pdf_indirect_ref *)d->list[index].value; + + code = pdfi_dereference(ctx, r->ref_object_num, r->ref_generation_num, o); + if (code < 0) + return code; + } else { + *o = d->list[index].value; + pdfi_countup(*o); } - return_error(gs_error_undefined); + return 0; } /* Wrapper to pdfi_dict_no_store_R_inner(), takes a char * as Key */ @@ -715,12 +819,14 @@ int pdfi_make_int_array_from_dict(pdf_context *ctx, int **parray, pdf_dict *dict return array_size; } -/* Put into dictionary with key as object */ -int pdfi_dict_put_obj(pdf_context *ctx, pdf_dict *d, pdf_obj *Key, pdf_obj *value) +/* Put into dictionary with key as object - + If the key already exists, we'll only replace it + if "replace" is true. +*/ +int pdfi_dict_put_obj(pdf_context *ctx, pdf_dict *d, pdf_obj *Key, pdf_obj *value, bool replace) { - uint64_t i; - pdf_obj **new_keys, **new_values; - pdf_name *n; + int i; + pdf_dict_entry *new_list; if (d->type != PDF_DICT) return_error(gs_error_typecheck); @@ -729,29 +835,27 @@ int pdfi_dict_put_obj(pdf_context *ctx, pdf_dict *d, pdf_obj *Key, pdf_obj *valu return_error(gs_error_typecheck); /* First, do we have a Key/value pair already ? */ - for (i=0;i< d->entries;i++) { - n = (pdf_name *)d->keys[i]; - if (n && n->type == PDF_NAME) { - if (pdfi_name_cmp((pdf_name *)Key, n) == 0) { - if (d->values[i] == value) - /* We already have this value stored with this key.... */ - return 0; - pdfi_countdown(d->values[i]); - d->values[i] = value; - pdfi_countup(value); - return 0; - } - } + i = pdfi_dict_find_key(ctx, d, (pdf_name *)Key, false); + if (i >= 0) { + if (d->list[i].value == value || replace == false) + /* We already have this value stored with this key.... */ + return 0; + pdfi_countdown(d->list[i].value); + d->list[i].value = value; + pdfi_countup(value); + return 0; } + d->is_sorted = false; + /* Nope, its a new Key */ if (d->size > d->entries) { /* We have a hole, find and use it */ for (i=0;i< d->size;i++) { - if (d->keys[i] == NULL) { - d->keys[i] = Key; + if (d->list[i].key == NULL) { + d->list[i].key = Key; pdfi_countup(Key); - d->values[i] = value; + d->list[i].value = value; pdfi_countup(value); d->entries++; return 0; @@ -759,24 +863,18 @@ int pdfi_dict_put_obj(pdf_context *ctx, pdf_dict *d, pdf_obj *Key, pdf_obj *valu } } - new_keys = (pdf_obj **)gs_alloc_bytes(ctx->memory, (d->size + 1) * sizeof(pdf_obj *), "pdfi_dict_put reallocate dictionary keys"); - new_values = (pdf_obj **)gs_alloc_bytes(ctx->memory, (d->size + 1) * sizeof(pdf_obj *), "pdfi_dict_put reallocate dictionary values"); - if (new_keys == NULL || new_values == NULL){ - gs_free_object(ctx->memory, new_keys, "pdfi_dict_put memory allocation failure"); - gs_free_object(ctx->memory, new_values, "pdfi_dict_put memory allocation failure"); + new_list = (pdf_dict_entry *)gs_alloc_bytes(ctx->memory, (d->size + 1) * sizeof(pdf_dict_entry), "pdfi_dict_put reallocate dictionary key/values"); + if (new_list == NULL) { return_error(gs_error_VMerror); } - memcpy(new_keys, d->keys, d->size * sizeof(pdf_obj *)); - memcpy(new_values, d->values, d->size * sizeof(pdf_obj *)); + memcpy(new_list, d->list, d->size * sizeof(pdf_dict_entry)); - gs_free_object(ctx->memory, d->keys, "pdfi_dict_put key reallocation"); - gs_free_object(ctx->memory, d->values, "pdfi_dict_put value reallocation"); + gs_free_object(ctx->memory, d->list, "pdfi_dict_put key/value reallocation"); - d->keys = new_keys; - d->values = new_values; + d->list = new_list; - d->keys[d->size] = Key; - d->values[d->size] = value; + d->list[d->size].key = Key; + d->list[d->size].value = value; d->size++; d->entries++; pdfi_countup(Key); @@ -796,7 +894,7 @@ int pdfi_dict_put(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj *value return code; pdfi_countup(key); - code = pdfi_dict_put_obj(ctx, d, key, value); + code = pdfi_dict_put_obj(ctx, d, key, value, true); pdfi_countdown(key); /* get rid of extra ref */ return code; } @@ -847,9 +945,10 @@ int pdfi_dict_copy(pdf_context *ctx, pdf_dict *target, pdf_dict *source) int i=0, code = 0; for (i=0;i< source->entries;i++) { - code = pdfi_dict_put_obj(ctx, target, source->keys[i], source->values[i]); + code = pdfi_dict_put_obj(ctx, target, source->list[i].key, source->list[i].value, true); if (code < 0) return code; + target->is_sorted = source->is_sorted; } return 0; } @@ -857,26 +956,34 @@ int pdfi_dict_copy(pdf_context *ctx, pdf_dict *target, pdf_dict *source) int pdfi_dict_known(pdf_context *ctx, pdf_dict *d, const char *Key, bool *known) { int i; - pdf_name *t; if (d->type != PDF_DICT) return_error(gs_error_typecheck); *known = false; - for (i=0;i< d->entries;i++) { - t = (pdf_name *)d->keys[i]; + i = pdfi_dict_find(ctx, d, Key, true); + if (i >= 0) + *known = true; - if (t && t->type == PDF_NAME) { - if (pdfi_name_is(t, Key)) { - *known = true; - break; - } - } - } return 0; } -/* Tests if a Key is present in the dictionary, if it is, retrieves the value associted with the +int pdfi_dict_known_by_key(pdf_context *ctx, pdf_dict *d, pdf_name *Key, bool *known) +{ + int i; + + if (d->type != PDF_DICT) + return_error(gs_error_typecheck); + + *known = false; + i = pdfi_dict_find_key(ctx, d, Key, true); + if (i >= 0) + *known = true; + + return 0; +} + +/* Tests if a Key is present in the dictionary, if it is, retrieves the value associated with the * key. Returns < 0 for error, 0 if the key is not found > 0 if the key is present, and initialises * the value in the arguments. Since this uses pdf_dict_get(), the returned value has its * reference count incremented by 1, just like pdfi_dict_get(). @@ -944,28 +1051,6 @@ int pdfi_dict_knownget_number(pdf_context *ctx, pdf_dict *d, const char *Key, do return 1; } -int pdfi_dict_known_by_key(pdf_context *ctx, pdf_dict *d, pdf_name *Key, bool *known) -{ - int i; - pdf_obj *t; - - if (d->type != PDF_DICT) - return_error(gs_error_typecheck); - - *known = false; - for (i=0;i< d->entries;i++) { - t = d->keys[i]; - - if (t && t->type == PDF_NAME) { - if (pdfi_name_cmp((pdf_name *)t, Key) == 0) { - *known = true; - break; - } - } - } - return 0; -} - int pdfi_dict_next(pdf_context *ctx, pdf_dict *d, pdf_obj **Key, pdf_obj **Value, uint64_t *index) { int code; @@ -987,14 +1072,14 @@ int pdfi_dict_next(pdf_context *ctx, pdf_dict *d, pdf_obj **Key, pdf_obj **Value * dictionary somehow ends up with NULL keys in the allocated * section. */ - *Key = d->keys[*index]; + *Key = d->list[*index].key; if (*Key == NULL) { (*index)++; continue; } - if (d->values[*index]->type == PDF_INDIRECT) { - pdf_indirect_ref *r = (pdf_indirect_ref *)d->values[*index]; + if (d->list[*index].value->type == PDF_INDIRECT) { + pdf_indirect_ref *r = (pdf_indirect_ref *)d->list[*index].value; pdf_obj *o; code = pdfi_dereference(ctx, r->ref_object_num, r->ref_generation_num, &o); @@ -1005,7 +1090,7 @@ int pdfi_dict_next(pdf_context *ctx, pdf_dict *d, pdf_obj **Key, pdf_obj **Value *Value = o; break; } else { - *Value = d->values[*index]; + *Value = d->list[*index].value; pdfi_countup(*Value); break; } @@ -1037,7 +1122,7 @@ int pdfi_dict_key_next(pdf_context *ctx, pdf_dict *d, pdf_obj **Key, uint64_t *i return gs_error_undefined; } - *Key = d->keys[*i]; + *Key = d->list[*i].key; if (*Key == NULL) { (*i)++; continue; @@ -1063,15 +1148,16 @@ int pdfi_merge_dicts(pdf_context *ctx, pdf_dict *target, pdf_dict *source) bool known = false; for (i=0;i< source->entries;i++) { - code = pdfi_dict_known_by_key(ctx, target, (pdf_name *)source->keys[i], &known); + code = pdfi_dict_known_by_key(ctx, target, (pdf_name *)source->list[i].key, &known); if (code < 0) return code; if (!known) { - code = pdfi_dict_put_obj(ctx, target, source->keys[i], source->values[i]); + code = pdfi_dict_put_obj(ctx, target, source->list[i].key, source->list[i].value, true); if (code < 0) return code; } } + target->is_sorted = false; return 0; } diff --git a/pdf/pdf_dict.h b/pdf/pdf_dict.h index 269f6be5..c5a8743b 100644 --- a/pdf/pdf_dict.h +++ b/pdf/pdf_dict.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -20,24 +20,35 @@ static inline uint64_t pdfi_dict_entries(pdf_dict *d) { return d->entries; } +int pdfi_dict_get_common(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj **o, bool cache); +static inline int pdfi_dict_get(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj **o) +{ + return pdfi_dict_get_common(ctx, d, Key, o, true); +} + +static inline int pdfi_dict_get_nocache(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj **o) +{ + return pdfi_dict_get_common(ctx, d, Key, o, true); +} + + void pdfi_free_dict(pdf_obj *o); int pdfi_dict_delete_pair(pdf_context *ctx, pdf_dict *d, pdf_name *n); int pdfi_dict_delete(pdf_context *ctx, pdf_dict *d, const char *str); int pdfi_dict_alloc(pdf_context *ctx, uint64_t size, pdf_dict **d); -int pdfi_dict_from_stack(pdf_context *ctx, uint32_t indirect_num, uint32_t indirect_gen); +int pdfi_dict_from_stack(pdf_context *ctx, uint32_t indirect_num, uint32_t indirect_gen, bool convert_string_keys); int pdfi_dict_known(pdf_context *ctx, pdf_dict *d, const char *Key, bool *known); int pdfi_dict_known_by_key(pdf_context *ctx, pdf_dict *d, pdf_name *Key, bool *known); int pdfi_dict_knownget(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj **o); int pdfi_dict_knownget_type(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj_type type, pdf_obj **o); int pdfi_dict_knownget_number(pdf_context *ctx, pdf_dict *d, const char *Key, double *f); int pdfi_merge_dicts(pdf_context *ctx, pdf_dict *target, pdf_dict *source); -int pdfi_dict_put_obj(pdf_context *ctx, pdf_dict *d, pdf_obj *Key, pdf_obj *value); +int pdfi_dict_put_obj(pdf_context *ctx, pdf_dict *d, pdf_obj *Key, pdf_obj *value, bool replace); int pdfi_dict_put(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj *value); int pdfi_dict_put_int(pdf_context *ctx, pdf_dict *d, const char *Key, int64_t value); int pdfi_dict_put_bool(pdf_context *ctx, pdf_dict *d, const char *Key, bool value); int pdfi_dict_put_name(pdf_context *ctx, pdf_dict *d, const char *Key, const char *name); int pdfi_dict_get2(pdf_context *ctx, pdf_dict *d, const char *Key1, const char *Key2, pdf_obj **o); -int pdfi_dict_get(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj **o); int pdfi_dict_get_no_deref(pdf_context *ctx, pdf_dict *d, const pdf_name *Key, pdf_obj **o); int pdfi_dict_get_by_key(pdf_context *ctx, pdf_dict *d, const pdf_name *Key, pdf_obj **o); int pdfi_dict_get_no_store_R_key(pdf_context *ctx, pdf_dict *d, const pdf_name *Key, pdf_obj **o); diff --git a/pdf/pdf_doc.c b/pdf/pdf_doc.c index 5f87d079..be71dfcb 100644 --- a/pdf/pdf_doc.c +++ b/pdf/pdf_doc.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2021 Artifex Software, Inc. +/* Copyright (C) 2020-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -105,11 +105,16 @@ int pdfi_read_Info(pdf_context *ctx) { pdf_dict *Info; int code; + pdf_dict *d; if (ctx->args.pdfdebug) dmprintf(ctx->memory, "%% Reading Info dictionary\n"); + /* See comment in pdfi_read_Root() for details */ + d = ctx->Trailer; + pdfi_countup(d); code = pdfi_dict_get_type(ctx, ctx->Trailer, "Info", PDF_DICT, (pdf_obj **)&Info); + pdfi_countdown(d); if (code < 0) return code; @@ -117,7 +122,7 @@ int pdfi_read_Info(pdf_context *ctx) dmprintf(ctx->memory, "\n"); pdfi_device_set_flags(ctx); - pdfi_write_docinfo_pdfmark(ctx, Info); + pdfi_pdfmark_write_docinfo(ctx, Info); /* We don't pdfi_countdown(Info) now, because we've transferred our * reference to the pointer in the pdf_context structure. @@ -330,7 +335,7 @@ static int pdfi_get_child(pdf_context *ctx, pdf_array *Kids, int i, pdf_dict **p goto errorExit; pdfi_countup(Key); - code = pdfi_dict_put_obj(ctx, leaf_dict, (pdf_obj *)Key, (pdf_obj *)node); + code = pdfi_dict_put_obj(ctx, leaf_dict, (pdf_obj *)Key, (pdf_obj *)node, true); if (code < 0) goto errorExit; code = pdfi_dict_put(ctx, leaf_dict, "Type", (pdf_obj *)Key); @@ -339,8 +344,15 @@ static int pdfi_get_child(pdf_context *ctx, pdf_array *Kids, int i, pdf_dict **p code = pdfi_array_put(ctx, Kids, i, (pdf_obj *)leaf_dict); if (code < 0) goto errorExit; + leaf_dict = NULL; } } else { + if (ctx->loop_detection != NULL) { + if (node->object_num != 0 && pdfi_loop_detector_check_object(ctx, node->object_num)) { + code = gs_note_error(gs_error_circular_reference); + goto errorExit; + } + } child = (pdf_dict *)node; pdfi_countup(child); } @@ -349,6 +361,7 @@ static int pdfi_get_child(pdf_context *ctx, pdf_array *Kids, int i, pdf_dict **p child = NULL; errorExit: + pdfi_free_object((pdf_obj *)leaf_dict); pdfi_countdown(child); pdfi_countdown(node); pdfi_countdown(Type); @@ -496,15 +509,15 @@ int pdfi_get_page_dict(pdf_context *ctx, pdf_dict *d, uint64_t page_num, uint64_ } else { if (pdfi_name_is(Type, "PageRef")) { if ((*page_offset) == page_num) { - pdf_dict *d = NULL; + pdf_dict *page_dict = NULL; - code = pdfi_dict_get(ctx, child, "PageRef", (pdf_obj **)&d); + code = pdfi_dict_get(ctx, child, "PageRef", (pdf_obj **)&page_dict); if (code < 0) goto exit; - code = pdfi_merge_dicts(ctx, d, inheritable); - *target = d; + code = pdfi_merge_dicts(ctx, page_dict, inheritable); + *target = page_dict; pdfi_countup(*target); - pdfi_countdown(d); + pdfi_countdown(page_dict); goto exit; } else { *page_offset += 1; @@ -590,30 +603,45 @@ int pdfi_find_resource(pdf_context *ctx, unsigned char *Type, pdf_name *name, *o = NULL; - /* Check the provided dict */ - code = pdfi_resource_knownget_typedict(ctx, Type, dict, &typedict); - if (code < 0) - goto exit; - if (code > 0) { - code = pdfi_dict_get_no_store_R_key(ctx, typedict, name, o); - if (code != gs_error_undefined) + /* Check the provided dict, stream_dict can be NULL if we are trying to find a Default* ColorSpace */ + if (dict != NULL) { + code = pdfi_resource_knownget_typedict(ctx, Type, dict, &typedict); + if (code < 0) goto exit; - } - - /* Check the Parents, if any */ - code = pdfi_dict_knownget_type(ctx, dict, "Parent", PDF_DICT, (pdf_obj **)&Parent); - if (code < 0) - goto exit; - if (code > 0) { - if (Parent->object_num != ctx->page.CurrentPageDict->object_num) { - code = pdfi_find_resource(ctx, Type, name, Parent, page_dict, o); + if (code > 0) { + code = pdfi_dict_get_no_store_R_key(ctx, typedict, name, o); if (code != gs_error_undefined) goto exit; } - } - pdfi_countdown(typedict); - typedict = NULL; + /* Check the Parents, if any */ + code = pdfi_dict_knownget_type(ctx, dict, "Parent", PDF_DICT, (pdf_obj **)&Parent); + if (code < 0) + goto exit; + if (code > 0) { + if (Parent->object_num != ctx->page.CurrentPageDict->object_num) { + if (pdfi_loop_detector_check_object(ctx, Parent->object_num) == true) + return_error(gs_error_circular_reference); + + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + return code; + + code = pdfi_loop_detector_add_object(ctx, dict->object_num); + if (code < 0) { + (void)pdfi_loop_detector_cleartomark(ctx); + return code; + } + code = pdfi_find_resource(ctx, Type, name, Parent, page_dict, o); + (void)pdfi_loop_detector_cleartomark(ctx); + if (code != gs_error_undefined) + goto exit; + } + } + + pdfi_countdown(typedict); + typedict = NULL; + } /* Normally page_dict can't be (or shouldn't be) NULL. However, if we are processing * a TYpe 3 font, then the 'page dict' is the Resources dictionary of that font. If @@ -682,68 +710,10 @@ exit: return code; } -/* Count how many children an outline entry has - * This is separate just to keep the code from getting cluttered. - */ -static int pdfi_doc_outline_count(pdf_context *ctx, pdf_dict *outline, int64_t *count) -{ - int code = 0; - pdf_dict *child = NULL; - pdf_dict *Next = NULL; - - /* Handle this outline entry */ - code = pdfi_loop_detector_mark(ctx); - if (code < 0) - goto exit1; - - /* Count the children (don't deref them, we don't want to leave them hanging around) */ - code = pdfi_dict_get_no_store_R(ctx, outline, "First", (pdf_obj **)&child); - if (code < 0 || child->type != PDF_DICT) { - /* TODO: flag a warning? */ - code = 0; - goto exit; - } - - if (child->object_num != 0) { - code = pdfi_loop_detector_add_object(ctx, child->object_num); - if (code < 0) - goto exit; - } - - do { - (*count) ++; - - code = pdfi_dict_get_no_store_R(ctx, child, "Next", (pdf_obj **)&Next); - if (code == gs_error_circular_reference) { - code = 0; - goto exit; - } - if (code == gs_error_undefined) { - code = 0; - break; - } - - if (code < 0 || Next->type != PDF_DICT) - goto exit; - - pdfi_countdown(child); - child = Next; - } while (true); - - exit: - (void)pdfi_loop_detector_cleartomark(ctx); - exit1: - pdfi_countdown(child); - pdfi_countdown(Next); - return code; -} - /* Mark the actual outline */ static int pdfi_doc_mark_the_outline(pdf_context *ctx, pdf_dict *outline) { int code = 0; - int64_t count = 0; - int64_t numkids = 0; pdf_dict *tempdict = NULL; uint64_t dictsize; uint64_t index; @@ -752,19 +722,6 @@ static int pdfi_doc_mark_the_outline(pdf_context *ctx, pdf_dict *outline) /* Basically we only do /Count, /Title, /A, /C, /F * The /First, /Last, /Next, /Parent get written magically by pdfwrite */ - /* Count how many kids there are */ - code = pdfi_doc_outline_count(ctx, outline, &numkids); - - /* If no kids, see if there is a Count */ - if (numkids == 0) { - code = pdfi_dict_get_int(ctx, outline, "Count", &count); - if (code < 0 && code != gs_error_undefined) - goto exit; - if (count < 0) - count = -count; - } else { - count = numkids; - } /* Make a temporary copy of the outline dict */ dictsize = pdfi_dict_entries(outline); @@ -792,14 +749,9 @@ static int pdfi_doc_mark_the_outline(pdf_context *ctx, pdf_dict *outline) */ code = pdfi_dict_delete_pair(ctx, tempdict, Key); } else if (pdfi_name_is(Key, "A")) { - code = pdfi_mark_modA(ctx, tempdict); + code = pdfi_pdfmark_modA(ctx, tempdict); } else if (pdfi_name_is(Key, "Dest")) { - code = pdfi_mark_modDest(ctx, tempdict); - } else if (pdfi_name_is(Key, "Count")) { - /* Delete any count we find in the dict - * We will use our value below - */ - code = pdfi_dict_delete_pair(ctx, tempdict, Key); + code = pdfi_pdfmark_modDest(ctx, tempdict); } if (code < 0) goto exit; @@ -815,15 +767,8 @@ static int pdfi_doc_mark_the_outline(pdf_context *ctx, pdf_dict *outline) } if (code < 0) goto exit; - /* If count is non-zero, put in dictionary */ - if (count != 0) { - code = pdfi_dict_put_int(ctx, tempdict, "Count", count); - if (code < 0) - goto exit; - } - /* Write the pdfmark */ - code = pdfi_mark_from_dict(ctx, tempdict, NULL, "OUT"); + code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "OUT"); if (code < 0) goto exit; @@ -976,14 +921,18 @@ static int pdfi_doc_Outlines(pdf_context *ctx) static int pdfi_doc_Info(pdf_context *ctx) { int code = 0; - pdf_dict *Info = NULL; + pdf_dict *Info = NULL, *d = NULL; pdf_dict *tempdict = NULL; uint64_t dictsize; uint64_t index; pdf_name *Key = NULL; pdf_obj *Value = NULL; - code = pdfi_dict_knownget_type(ctx, ctx->Trailer, "Info", PDF_DICT, (pdf_obj **)&Info); + /* See comment in pdfi_read_Root() for details */ + d = ctx->Trailer; + pdfi_countup(d); + code = pdfi_dict_knownget_type(ctx, d, "Info", PDF_DICT, (pdf_obj **)&Info); + pdfi_countdown(d); if (code <= 0) { /* TODO: flag a warning */ goto exit; @@ -1003,7 +952,7 @@ static int pdfi_doc_Info(pdf_context *ctx) if (pdfi_name_is(Key, "Author") || pdfi_name_is(Key, "Creator") || pdfi_name_is(Key, "Title") || pdfi_name_is(Key, "Subject") || pdfi_name_is(Key, "Keywords")) { - code = pdfi_dict_put_obj(ctx, tempdict, (pdf_obj *)Key, Value); + code = pdfi_dict_put_obj(ctx, tempdict, (pdf_obj *)Key, Value, true); if (code < 0) goto exit; } @@ -1021,7 +970,7 @@ static int pdfi_doc_Info(pdf_context *ctx) if (code < 0) goto exit; /* Write the pdfmark */ - code = pdfi_mark_from_dict(ctx, tempdict, NULL, "DOCINFO"); + code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "DOCINFO"); exit: pdfi_countdown(Key); @@ -1037,14 +986,28 @@ static int pdfi_doc_PageLabels(pdf_context *ctx) int code; pdf_dict *PageLabels = NULL; + if (ctx->loop_detection) { + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + return code; + } + code = pdfi_dict_knownget_type(ctx, ctx->Root, "PageLabels", PDF_DICT, (pdf_obj **)&PageLabels); if (code <= 0) { + if (ctx->loop_detection) + (void)pdfi_loop_detector_cleartomark(ctx); /* TODO: flag a warning */ goto exit; } + if (ctx->loop_detection) { + code = pdfi_loop_detector_cleartomark(ctx); + if (code < 0) + goto exit; + } + /* This will send the PageLabels object as a 'pdfpagelabels' setdeviceparams */ - code = pdfi_mark_object(ctx, (pdf_obj *)PageLabels, "pdfpagelabels"); + code = pdfi_pdfmark_object(ctx, (pdf_obj *)PageLabels, "pdfpagelabels"); if (code < 0) goto exit; @@ -1175,7 +1138,7 @@ static int pdfi_doc_EmbeddedFiles_Names(pdf_context *ctx, pdf_array *names) code = pdfi_array_get_type(ctx, names, index+1, PDF_DICT, (pdf_obj **)&filespec); if (code < 0) goto exit; - code = pdfi_mark_embed_filespec(ctx, name, filespec); + code = pdfi_pdfmark_embed_filespec(ctx, name, filespec); if (code < 0) goto exit; pdfi_countdown(name); diff --git a/pdf/pdf_errors.h b/pdf/pdf_errors.h new file mode 100644 index 00000000..e52a6c74 --- /dev/null +++ b/pdf/pdf_errors.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2022 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + +#ifndef PARAM +#define PARAM(A,B) A +#endif +PARAM(E_PDF_NOERROR, "no error"), +PARAM(E_PDF_NOHEADER, "no header detected"), +PARAM(E_PDF_NOHEADERVERSION, "header lacks a version number"), +PARAM(E_PDF_NOSTARTXREF, "no startxref token found"), +PARAM(E_PDF_BADSTARTXREF, "startxref offset invalid"), +PARAM(E_PDF_BADXREFSTREAM, "couldn't read hybrid file's XrefStm"), +PARAM(E_PDF_BADXREF, "error in xref table"), +PARAM(E_PDF_SHORTXREF, "too few entries in xref table"), +PARAM(E_PDF_MISSINGENDSTREAM, "content stream lacks endstream"), +PARAM(E_PDF_UNKNOWNFILTER, "request for unknown filter"), +PARAM(E_PDF_MISSINGWHITESPACE, "missing white space after number"), +PARAM(E_PDF_MALFORMEDNUMBER, "malformed number"), +PARAM(E_PDF_UNESCAPEDSTRING, "unbalanced or unescaped character '(' in string"), +PARAM(E_PDF_BADOBJNUMBER, "invalid object number"), +PARAM(E_PDF_MISSINGENDOBJ, "object lacks an endobj"), +PARAM(E_PDF_TOKENERROR, "error executing PDF token"), +PARAM(E_PDF_KEYWORDTOOLONG, "potential token is too long"), +PARAM(E_PDF_BADPAGETYPE, "Page object doe snot have /Page type"), +PARAM(E_PDF_CIRCULARREF, "circular reference to indirect object"), +PARAM(E_PDF_UNREPAIRABLE, "couldn't repair PDF file"), +PARAM(E_PDF_REPAIRED, "PDF file was repaired"), +PARAM(E_PDF_BADSTREAM, "error reading a stream"), +PARAM(E_PDF_MISSINGOBJ, "obj token missing"), +PARAM(E_PDF_BADPAGEDICT, "error in Page dictionary"), +PARAM(E_PDF_OUTOFMEMORY, "out of memory"), +PARAM(E_PDF_PAGEDICTERROR, "error reading page dictionary"), +PARAM(E_PDF_STACKUNDERFLOWERROR, "stack underflow"), +PARAM(E_PDF_BADSTREAMDICT, "error in stream dictionary"), +PARAM(E_PDF_INHERITED_STREAM_RESOURCE, "stream inherited a resource"), +PARAM(E_PDF_DEREF_FREE_OBJ, "counting down reference to freed object"), +PARAM(E_PDF_INVALID_TRANS_XOBJECT, "error in transparency XObject"), +PARAM(E_PDF_NO_SUBTYPE, "object lacks a required Subtype"), +PARAM(E_PDF_IMAGECOLOR_ERROR, "error in image colour"), +PARAM(E_DICT_SELF_REFERENCE, "dictionary contains a key which (indirectly) references the dictionary."), +PARAM(E_IMAGE_MASKWITHCOLOR, "Image has both ImageMask and ColorSpace keys."), +PARAM(E_PDF_INVALID_DECRYPT_LEN, "Invalid /Length in Encryption dictionary (not in range 40-128 or not a multiple of 8)."), + +#undef PARAM diff --git a/pdf/pdf_fapi.c b/pdf/pdf_fapi.c index df92acc4..3e4420c4 100644 --- a/pdf/pdf_fapi.c +++ b/pdf/pdf_fapi.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -550,7 +550,7 @@ pdfi_fapi_get_float(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index, fl break; } if (v->type == PDF_INT) { - *ret = v->value.i; + *ret = (float)v->value.i; } else { *ret = (float)v->value.d; @@ -688,19 +688,24 @@ pdfi_fapi_get_gsubr(gs_fapi_font *ff, int index, byte *buf, int buf_length) } else { int leniv = (pfont->data.lenIV > 0 ? pfont->data.lenIV : 0); - pdf_string *subrstring; + pdf_string *subrstring = NULL; code = pdfi_array_get(pdffont2->ctx, pdffont2->GlobalSubrs, index, (pdf_obj **)&subrstring); if (code >= 0) { - code = subrstring->length - leniv; - if (buf && buf_length >= code) { - if (ff->need_decrypt && pfont->data.lenIV >= 0) { - decode_bytes(buf, subrstring->data, code + leniv, pfont->data.lenIV); - } - else { - memcpy(buf, subrstring->data, code); + if (subrstring->type == PDF_STRING) { + code = subrstring->length - leniv; + if (buf && buf_length >= code) { + if (ff->need_decrypt && pfont->data.lenIV >= 0) { + decode_bytes(buf, subrstring->data, code + leniv, pfont->data.lenIV); + } + else { + memcpy(buf, subrstring->data, code); + } } } + else { + code = gs_note_error(gs_error_invalidfont); + } pdfi_countdown(subrstring); } } @@ -746,19 +751,27 @@ pdfi_fapi_get_subr(gs_fapi_font *ff, int index, byte *buf, int buf_length) int leniv = (pfont->data.lenIV > 0 ? pfont->data.lenIV : 0); pdf_string *subrstring; - code = pdfi_array_get(pdffont2->ctx, pdffont2->Subrs, index, (pdf_obj **)&subrstring); + if (pdffont2->Subrs == NULL) + code = gs_note_error(gs_error_invalidfont); + else + code = pdfi_array_get(pdffont2->ctx, pdffont2->Subrs, index, (pdf_obj **)&subrstring); if (code >= 0) { - if (subrstring->length > 0) { - code = subrstring->length - leniv; - if (buf && buf_length >= code) { - if (ff->need_decrypt && pfont->data.lenIV >= 0) { - decode_bytes(buf, subrstring->data, code + leniv, pfont->data.lenIV); - } - else { - memcpy(buf, subrstring->data, code); + if (subrstring->type == PDF_STRING) { + if (subrstring->length > 0) { + code = subrstring->length - leniv; + if (buf && buf_length >= code) { + if (ff->need_decrypt && pfont->data.lenIV >= 0) { + decode_bytes(buf, subrstring->data, code + leniv, pfont->data.lenIV); + } + else { + memcpy(buf, subrstring->data, code); + } } } } + else { + code = gs_note_error(gs_error_invalidfont); + } pdfi_countdown(subrstring); } } @@ -914,7 +927,7 @@ pdfi_fapi_get_glyphname_or_cid(gs_text_enum_t *penum, gs_font_base * pbfont, gs_ else if (pbfont->FontType == ft_encrypted2) { pdf_font_cff *cfffont = (pdf_font_cff *)pbfont->client_data; pdf_name *glyphname = NULL; - pdf_string *charstring = NULL; + pdf_string *charstr = NULL; gs_const_string gname; code = (*ctx->get_glyph_name)((gs_font *)pbfont, ccode, &gname); @@ -928,22 +941,22 @@ pdfi_fapi_get_glyphname_or_cid(gs_text_enum_t *penum, gs_font_base * pbfont, gs_ pdfi_countdown(glyphname); return code; } - code = pdfi_dict_get_by_key(cfffont->ctx, cfffont->CharStrings, glyphname, (pdf_obj **)&charstring); + code = pdfi_dict_get_by_key(cfffont->ctx, cfffont->CharStrings, glyphname, (pdf_obj **)&charstr); pdfi_countdown(glyphname); if (code < 0) { - code = pdfi_dict_get(cfffont->ctx, cfffont->CharStrings, ".notdef", (pdf_obj **)&charstring); + code = pdfi_dict_get(cfffont->ctx, cfffont->CharStrings, ".notdef", (pdf_obj **)&charstr); } if (code < 0) return code; - I->ff.char_data = charstring->data; - I->ff.char_data_len = charstring->length; + I->ff.char_data = charstr->data; + I->ff.char_data_len = charstr->length; cr->client_char_code = 0; cr->char_codes[0] = 0; cr->is_glyph_index = true; - pdfi_countdown(charstring); + pdfi_countdown(charstr); return code; } else if (pbfont->FontType == ft_TrueType) { @@ -1184,8 +1197,6 @@ pdfi_fapi_get_glyph(gs_fapi_font * ff, gs_glyph char_code, byte * buf, int buf_l cstrlen = I->ff.char_data_len - leniv; if (buf && buf_length >= cstrlen) { - memcpy(buf, I->ff.char_data, I->ff.char_data_len); - if (ff->need_decrypt && pfont->data.lenIV >= 0) decode_bytes(buf, I->ff.char_data, cstrlen + leniv, leniv); else @@ -1364,7 +1375,7 @@ static int pdfi_fapi_build_char(gs_show_enum * penum, gs_gstate * pgs, gs_font * pfont, gs_char chr, gs_glyph glyph) { - int code; + int code = 0; gs_font_base *pbfont1; gs_fapi_server *I; @@ -1382,9 +1393,16 @@ pdfi_fapi_build_char(gs_show_enum * penum, gs_gstate * pgs, gs_font * pfont, I->ff.client_font_data2 = cidpfont; } } + /* If between the font's creation and now another interpreter has driven FAPI (i.e. in a Postscript Begin/EndPage + context, the FAPI server data may end up set appropriately for the other interpreter, if that's happened, put + ours back before trying to interpret the glyph. + */ + if (((gs_fapi_server *)pbfont1->FAPI)->ff.get_glyphname_or_cid != pdfi_fapi_get_glyphname_or_cid) { + code = pdfi_fapi_passfont((pdf_font *)pbfont1->client_data, 0, NULL, NULL, NULL, 0); + } - code = gs_fapi_do_char((gs_font *)pbfont1, pgs, (gs_text_enum_t *) penum, NULL, false, - NULL, NULL, chr, glyph, 0); + if (code >= 0) + code = gs_fapi_do_char((gs_font *)pbfont1, pgs, (gs_text_enum_t *) penum, NULL, false, NULL, NULL, chr, glyph, 0); return (code); } @@ -1452,9 +1470,7 @@ pdfi_fapi_passfont(pdf_font *font, int subfont, char *fapi_request, /* doesn't really matter for non-ttf */ *local_pdf_ff_stub.ttf_cmap_req = *nonsymbolic_req; } - /* The plfont should contain everything we need, but setting the client data for the server - * to pbfont makes as much sense as setting it to NULL. - */ + gs_fapi_set_servers_client_data(pbfont->memory, (const gs_fapi_font *)&local_pdf_ff_stub, (gs_font *)pbfont); diff --git a/pdf/pdf_file.c b/pdf/pdf_file.c index 214d448d..5698866e 100644 --- a/pdf/pdf_file.c +++ b/pdf/pdf_file.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -57,6 +57,8 @@ # include "sjpx.h" #endif +extern const uint file_default_buffer_size; + static void pdfi_close_filter_chain(pdf_context *ctx, stream *s, stream *target); /* Utility routine to create a pdf_c_stream object */ @@ -102,6 +104,9 @@ pdfi_filter_open(uint buffer_size, if (sst == NULL) return_error(gs_error_VMerror); } + if (buffer_size < 128) + buffer_size = file_default_buffer_size; + code = file_open_stream((char *)0, 0, "r", buffer_size, &s, (gx_io_device *)0, (iodev_proc_fopen_t)0, mem); if (code < 0) { @@ -232,7 +237,7 @@ int pdfi_apply_Arc4_filter(pdf_context *ctx, pdf_string *Key, pdf_c_stream *sour stream *new_s; int min_size = 2048; - s_arcfour_set_key(&state, (const unsigned char *)Key->data, Key->length); + s_arcfour_set_key(&state, (const unsigned char *)Key->data, Key->length); /* lgtm [cpp/weak-cryptographic-algorithm] */ code = pdfi_filter_open(min_size, &s_filter_read_procs, (const stream_template *)&s_arcfour_template, (const stream_state *)&state, ctx->memory->non_gc_memory, &new_s); if (code < 0) @@ -315,6 +320,7 @@ static int pdfi_Flate_filter(pdf_context *ctx, pdf_dict *d, stream *source, stre stream_zlib_state zls; uint min_size = 2048; int code; + stream *Flate_source = NULL; memset(&zls, 0, sizeof(zls)); @@ -329,9 +335,10 @@ static int pdfi_Flate_filter(pdf_context *ctx, pdf_dict *d, stream *source, stre source = *new_stream; if (d && d->type == PDF_DICT) { + Flate_source = (*new_stream)->strm; code = pdfi_Predictor_filter(ctx, d, source, new_stream); if (code < 0) - pdfi_close_filter_chain(ctx, source, NULL); + pdfi_close_filter_chain(ctx, source, Flate_source); } return code; } @@ -344,7 +351,7 @@ pdfi_JBIG2Decode_filter(pdf_context *ctx, pdf_dict *dict, pdf_dict *decode, uint min_size = s_jbig2decode_template.min_out_size; int code; pdf_stream *Globals = NULL; - byte *buf; + byte *buf = NULL; int64_t buflen; void *globalctx; @@ -367,8 +374,6 @@ pdfi_JBIG2Decode_filter(pdf_context *ctx, pdf_dict *dict, pdf_dict *decode, goto cleanupExit; s_jbig2decode_set_global_data((stream_state*)&state, NULL, globalctx); - - gs_free_object(ctx->memory, buf, "pdfi_JBIG2Decode_filter (Globals buf)"); } } } @@ -383,6 +388,7 @@ pdfi_JBIG2Decode_filter(pdf_context *ctx, pdf_dict *dict, pdf_dict *decode, code = 0; cleanupExit: + gs_free_object(ctx->memory, buf, "pdfi_JBIG2Decode_filter (Globals buf)"); pdfi_countdown(Globals); return code; } @@ -1058,6 +1064,9 @@ int pdfi_filter(pdf_context *ctx, pdf_stream *stream_obj, pdf_c_stream *source, if (ctx->encryption.is_encrypted && !inline_image) { int64_t Length; + if (ctx->encryption.StmF == CRYPT_IDENTITY) + return pdfi_filter_no_decryption(ctx, stream_obj, source, new_stream, inline_image); + code = pdfi_dict_get_type(ctx, stream_dict, "StreamKey", PDF_STRING, (pdf_obj **)&StreamKey); if (code == gs_error_undefined) { code = pdfi_compute_objkey(ctx, (pdf_obj *)stream_dict, &StreamKey); @@ -1084,7 +1093,7 @@ int pdfi_filter(pdf_context *ctx, pdf_stream *stream_obj, pdf_c_stream *source, */ Length = pdfi_stream_length(ctx, stream_obj); - if (Length <= 0 || ctx->encryption.StrF == CRYPT_IDENTITY) { + if (Length <= 0) { /* Don't treat as an encrypted stream if Length is 0 */ pdfi_countdown(StreamKey); return pdfi_filter_no_decryption(ctx, stream_obj, source, new_stream, inline_image); @@ -1146,7 +1155,7 @@ error: * NB! The EODString can't be tracked by the stream code. The caller is responsible for * managing the lifetime of this object. It must remain valid until the filter is closed. */ -int pdfi_apply_SubFileDecode_filter(pdf_context *ctx, int EODCount, pdf_string *EODString, pdf_c_stream *source, pdf_c_stream **new_stream, bool inline_image) +int pdfi_apply_SubFileDecode_filter(pdf_context *ctx, int EODCount, const char *EODString, pdf_c_stream *source, pdf_c_stream **new_stream, bool inline_image) { int code; stream_SFD_state state; @@ -1161,9 +1170,8 @@ int pdfi_apply_SubFileDecode_filter(pdf_context *ctx, int EODCount, pdf_string * s_SFD_template.set_defaults((stream_state *)&state); if (EODString != NULL) { - state.eod.data = EODString->data; - state.eod.size = EODString->length; - min_size = EODString->length; + state.eod.data = (const byte *)EODString; + state.eod.size = strlen(EODString); } if (EODCount > 0) @@ -1176,6 +1184,12 @@ int pdfi_apply_SubFileDecode_filter(pdf_context *ctx, int EODCount, pdf_string * return code; code = pdfi_alloc_stream(ctx, new_s, source->s, new_stream); + if (code < 0) { + gs_free_object(ctx->memory->non_gc_memory, new_s->state, "pdfi_apply_SubFileDecode_filter"); + gs_free_object(ctx->memory->non_gc_memory, new_s->cbuf, "pdfi_apply_SubFileDecode_filter"); + gs_free_object(ctx->memory->non_gc_memory, new_s, "pdfi_apply_SubFileDecode_filter"); + return code; + } new_s->strm = source->s; if (source->unread_size != 0) { (*new_stream)->unread_size = source->unread_size; @@ -1277,17 +1291,18 @@ int pdfi_open_memory_stream_from_filtered_stream(pdf_context *ctx, pdf_stream *s code = pdfi_filter(ctx, stream_obj, compressed_stream, &decompressed_stream, false); if (code < 0) { pdfi_close_memory_stream(ctx, *Buffer, *new_pdf_stream); - gs_free_object(ctx->memory, *Buffer, "pdfi_open_memory_stream_from_filtered_stream"); *Buffer = NULL; *new_pdf_stream = NULL; return code; } do { - byte b; - code = pdfi_read_bytes(ctx, &b, 1, 1, decompressed_stream); + byte b[512]; + code = pdfi_read_bytes(ctx, (byte *)&b, 1, 512, decompressed_stream); if (code <= 0) break; - decompressed_length++; + decompressed_length+=code; + if (code < 512) + break; } while (true); pdfi_close_file(ctx, decompressed_stream); @@ -1359,10 +1374,14 @@ int pdfi_open_memory_stream_from_memory(pdf_context *ctx, unsigned int size, byt int pdfi_close_memory_stream(pdf_context *ctx, byte *Buffer, pdf_c_stream *source) { - sclose(source->s); gs_free_object(ctx->memory, Buffer, "open memory stream(buffer)"); - gs_free_object(ctx->memory, source->s, "open memory stream(stream)"); - gs_free_object(ctx->memory, source, "open memory stream(pdf_stream)"); + if (source != NULL) { + if (source->s != NULL) { + sclose(source->s); + gs_free_object(ctx->memory, source->s, "open memory stream(stream)"); + } + gs_free_object(ctx->memory, source, "open memory stream(pdf_stream)"); + } return 0; } @@ -1418,25 +1437,58 @@ gs_offset_t pdfi_tell(pdf_c_stream *s) return stell(s->s); } +int pdfi_unread_byte(pdf_context *ctx, pdf_c_stream *s, char c) +{ + if (s->unread_size == UNREAD_BUFFER_SIZE) + return_error(gs_error_ioerror); + + s->unget_buffer[s->unread_size++] = c; + + return 0; +} + int pdfi_unread(pdf_context *ctx, pdf_c_stream *s, byte *Buffer, uint32_t size) { if (size + s->unread_size > UNREAD_BUFFER_SIZE) return_error(gs_error_ioerror); - if (s->unread_size) { - uint32_t index = s->unread_size - 1; - - do { - s->unget_buffer[size + index] = s->unget_buffer[index]; - } while(index--); + Buffer += size; + while (size) { + s->unget_buffer[s->unread_size++] = *--Buffer; + size--; } - memcpy(s->unget_buffer, Buffer, size); - s->unread_size += size; - return 0; } +int pdfi_read_byte(pdf_context *ctx, pdf_c_stream *s) +{ + int32_t code; + + if (s->eof && s->unread_size == 0) + return EOFC; + + if (s->unread_size) + return (byte)s->unget_buffer[--s->unread_size]; + + /* TODO the Ghostscript code uses sbufptr(s) to avoid a memcpy + * at some point we should modify this code to do so as well. + */ + code = spgetc(s->s); + if (code == EOFC) { + s->eof = true; + return EOFC; + } else if (code == gs_error_ioerror) { + pdfi_set_error(ctx, code, "sgets", E_PDF_BADSTREAM, "pdfi_read_bytes", NULL); + s->eof = true; + return EOFC; + } else if(code == ERRC) { + return ERRC; + } + return (int)code; +} + + int pdfi_read_bytes(pdf_context *ctx, byte *Buffer, uint32_t size, uint32_t count, pdf_c_stream *s) { uint32_t i = 0, total = size * count; @@ -1447,38 +1499,32 @@ int pdfi_read_bytes(pdf_context *ctx, byte *Buffer, uint32_t size, uint32_t coun return 0; if (s->unread_size) { - if (s->unread_size >= total) { - memcpy(Buffer, s->unget_buffer, total); - for(i=0;i < s->unread_size - total;i++) { - s->unget_buffer[i] = s->unget_buffer[i + total]; - } - s->unread_size -= total; - return total; - } else { - memcpy(Buffer, s->unget_buffer, s->unread_size); - total -= s->unread_size; - Buffer += s->unread_size; - i = s->unread_size; - s->unread_size = 0; - if (s->eof) - return i; + i = s->unread_size; + if (i >= total) + i = total; + bytes = i; + while (bytes) { + *Buffer++ = s->unget_buffer[--s->unread_size]; + bytes--; } + total -= i; + if (total == 0 || s->eof) + return i; } - if (total) { - /* TODO the Ghostscript code uses sbufptr(s) to avoid a memcpy - * at some point we should modify this code to do so as well. - */ - code = sgets(s->s, Buffer, total, &bytes); - if (code == EOFC) { - s->eof = true; - } else if (code == gs_error_ioerror) { - pdfi_set_error(ctx, code, "sgets", E_PDF_BADSTREAM, "pdfi_read_bytes", NULL); - s->eof = true; - } else if(code == ERRC) { - bytes = ERRC; - } else { - bytes = bytes + i; - } + + /* TODO the Ghostscript code uses sbufptr(s) to avoid a memcpy + * at some point we should modify this code to do so as well. + */ + code = sgets(s->s, Buffer, total, &bytes); + if (code == EOFC) { + s->eof = true; + } else if (code == gs_error_ioerror) { + pdfi_set_error(ctx, code, "sgets", E_PDF_BADSTREAM, "pdfi_read_bytes", NULL); + s->eof = true; + } else if(code == ERRC) { + bytes = ERRC; + } else { + bytes = bytes + i; } return bytes; @@ -1495,8 +1541,6 @@ pdfi_stream_to_buffer(pdf_context *ctx, pdf_stream *stream_obj, byte **buf, int6 byte *Buffer = NULL; int code = 0; int64_t buflen = 0; - int bytes; - char c; gs_offset_t savedoffset; pdf_c_stream *stream; bool filtered; @@ -1526,12 +1570,11 @@ pdfi_stream_to_buffer(pdf_context *ctx, pdf_stream *stream_obj, byte **buf, int6 if (code < 0) { goto exit; } - /* Find out how big it is */ - do { - bytes = sfread(&c, 1, 1, stream->s); - if (bytes > 0) - buflen++; - } while (bytes >= 0); + while (seofp(stream->s) != true && serrorp(stream->s) != true) { + (void)sbufskip(stream->s, sbufavailable(stream->s)); + s_process_read_buf(stream->s); + buflen += sbufavailable(stream->s); + } pdfi_close_file(ctx, stream); } else { buflen = pdfi_stream_length(ctx, stream_obj); @@ -1548,6 +1591,8 @@ pdfi_stream_to_buffer(pdf_context *ctx, pdf_stream *stream_obj, byte **buf, int6 goto exit; if (filtered || ctx->encryption.is_encrypted) { code = pdfi_filter(ctx, stream_obj, ctx->main_stream, &stream, false); + if (code < 0) + goto exit; sfread(Buffer, 1, buflen, stream->s); pdfi_close_file(ctx, stream); } else { diff --git a/pdf/pdf_file.h b/pdf/pdf_file.h index a02d7f75..d1fafbb6 100644 --- a/pdf/pdf_file.h +++ b/pdf/pdf_file.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -85,12 +85,14 @@ int pdfi_filter(pdf_context *ctx, pdf_stream *stream_obj, pdf_c_stream *source, int pdfi_filter_no_decryption(pdf_context *ctx, pdf_stream *d, pdf_c_stream *source, pdf_c_stream **new_stream, bool inline_image); void pdfi_close_file(pdf_context *ctx, pdf_c_stream *s); int pdfi_read_bytes(pdf_context *ctx, byte *Buffer, uint32_t size, uint32_t count, pdf_c_stream *s); +int pdfi_read_byte(pdf_context *ctx, pdf_c_stream *s); int pdfi_unread(pdf_context *ctx, pdf_c_stream *s, byte *Buffer, uint32_t size); +int pdfi_unread_byte(pdf_context *ctx, pdf_c_stream *s, char c); int pdfi_seek(pdf_context *ctx, pdf_c_stream *s, gs_offset_t offset, uint32_t origin); gs_offset_t pdfi_unread_tell(pdf_context *ctx); gs_offset_t pdfi_tell(pdf_c_stream *s); -int pdfi_apply_SubFileDecode_filter(pdf_context *ctx, int EODCount, pdf_string *EODString, pdf_c_stream *source, pdf_c_stream **new_stream, bool inline_image); +int pdfi_apply_SubFileDecode_filter(pdf_context *ctx, int EODCount, const char *EODString, pdf_c_stream *source, pdf_c_stream **new_stream, bool inline_image); int pdfi_open_memory_stream(pdf_context *ctx, unsigned int size, byte **Buffer, pdf_c_stream *source, pdf_c_stream **new_stream); int pdfi_close_memory_stream(pdf_context *ctx, byte *Buffer, pdf_c_stream *source); int pdfi_open_memory_stream_from_stream(pdf_context *ctx, unsigned int size, byte **Buffer, pdf_c_stream *source, pdf_c_stream **new_pdf_stream, bool retain_ownership); diff --git a/pdf/pdf_fmap.c b/pdf/pdf_fmap.c index 8fabb757..ee05af1c 100644 --- a/pdf/pdf_fmap.c +++ b/pdf/pdf_fmap.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2021 Artifex Software, Inc. +/* Copyright (C) 2020-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -20,9 +20,11 @@ #include "pdf_int.h" #include "pdf_types.h" +#include "pdf_array.h" #include "pdf_dict.h" #include "pdf_stack.h" #include "pdf_file.h" +#include "pdf_misc.h" #include "pdf_fmap.h" typedef struct @@ -31,33 +33,44 @@ typedef struct const char *mappedname; } pdfi_custom_fmap_entry; -pdfi_custom_fmap_entry pdfi_custom_fmap_enties[] = +pdfi_custom_fmap_entry pdfi_custom_fmap_entries[] = { {"Helv", "Helvetica"}, {NULL, NULL} }; +static inline bool pdfi_fmap_file_exists(pdf_context *ctx, pdf_string *fname) +{ + char nm[gp_file_name_sizeof]; + struct stat buf; + int code = gs_error_invalidfileaccess; + + if (fname->length < gp_file_name_sizeof) { + memcpy(nm, fname->data, fname->length); + nm[fname->length] = '\0'; + code = gp_stat(ctx->memory, (const char *)nm, &buf); + } + return code >= 0; +} + static int -pdf_fontmap_open_file(pdf_context *ctx, byte **buf, int *buflen) +pdf_fontmap_open_file(pdf_context *ctx, const char *mapfilename, byte **buf, int *buflen) { int code = 0; stream *s; char fname[gp_file_name_sizeof]; const char *path_pfx = "Init/"; - const char *fmap_default = "Fontmap.GS"; - const char *prestring = "<<\n"; - const char *poststring = ">>\nendstream\n"; - const int prestringlen = strlen(prestring); + const char *poststring = "\nendstream"; const int poststringlen = strlen(poststring); fname[0] = '\0'; - if (strlen(path_pfx) + strlen(fmap_default) + 1 > gp_file_name_sizeof) + if (strlen(path_pfx) + strlen(mapfilename) + 1 > gp_file_name_sizeof) return_error(gs_error_invalidfileaccess); - code = pdfi_open_resource_file(ctx, fmap_default, strlen(fmap_default), &s); + code = pdfi_open_resource_file(ctx, mapfilename, strlen(mapfilename), &s); if (code < 0) { strncat(fname, path_pfx, strlen(path_pfx)); - strncat(fname, (char *)fmap_default, strlen(fmap_default)); + strncat(fname, (char *)mapfilename, strlen(mapfilename)); code = pdfi_open_resource_file(ctx, fname, strlen(fname), &s); } @@ -66,14 +79,14 @@ pdf_fontmap_open_file(pdf_context *ctx, byte **buf, int *buflen) sfseek(s, 0, SEEK_END); *buflen = sftell(s); sfseek(s, 0, SEEK_SET); - *buf = gs_alloc_bytes(ctx->memory, *buflen + prestringlen + poststringlen, "pdf_cmap_open_file(buf)"); + *buf = gs_alloc_bytes(ctx->memory, *buflen + poststringlen, "pdf_cmap_open_file(buf)"); if (*buf != NULL) { - memcpy(*buf, prestring, prestringlen); - sfread((*buf) + prestringlen, 1, *buflen, s); - memcpy((*buf) + *buflen + prestringlen, poststring, poststringlen); - *buflen += prestringlen + poststringlen; + sfread((*buf), 1, *buflen, s); + memcpy((*buf) + *buflen, poststring, poststringlen); + *buflen += poststringlen; /* This is naff, but works for now When parsing Fontmap in PS, ";" is defined as "def" + We don't need either, because the dictionary is built from the stack. */ for (i = 0; i < *buflen - 1; i++) { if ((*buf)[i] == ';') { @@ -90,53 +103,95 @@ pdf_fontmap_open_file(pdf_context *ctx, byte **buf, int *buflen) } static int -pdf_make_fontmap(pdf_context *ctx) +pdf_make_fontmap(pdf_context *ctx, const char *default_fmapname, int cidfmap) { byte *fmapbuf = NULL; int code, fmapbuflen; pdf_c_stream *fmapstr = NULL; pdf_stream fakedict = {0}; - pdfi_custom_fmap_entry *pcfe = pdfi_custom_fmap_enties; - int i; - + pdfi_custom_fmap_entry *pcfe = pdfi_custom_fmap_entries; + int i, j = 0; + char fmapname[gp_file_name_sizeof]; pdf_c_stream fakemainstream = {0}; + int stacksize = pdfi_count_stack(ctx); + + strncpy(fmapname, default_fmapname, strlen(default_fmapname) + 1); - code = pdf_fontmap_open_file(ctx, &fmapbuf, &fmapbuflen); + code = pdfi_mark_stack(ctx, PDF_DICT_MARK); if (code < 0) - return code; + goto done; - code = pdfi_open_memory_stream_from_memory(ctx, fmapbuflen, fmapbuf, &fmapstr, true); - if (code >= 0) { - int stacksize = pdfi_count_stack(ctx); + do { + if (j < ctx->num_fontmapfiles) { + memcpy(fmapname, ctx->fontmapfiles[j].data, ctx->fontmapfiles[j].size); + fmapname[ctx->fontmapfiles[j].size] = '\0'; + } - if (ctx->main_stream == NULL) { - ctx->main_stream = &fakemainstream; + code = pdf_fontmap_open_file(ctx, (const char *)fmapname, &fmapbuf, &fmapbuflen); + if (code < 0) { + if (ctx->args.QUIET != true) { + (void)outwrite(ctx->memory, "Warning: ", 9); + if (cidfmap) + (void)outwrite(ctx->memory, "cidfmap file ", 13); + else + (void)outwrite(ctx->memory, "Fontmap file \"", 14); + (void)outwrite(ctx->memory, fmapname, strlen(fmapname)); + (void)outwrite(ctx->memory, "\" not found.\n", 13); + code = 0; + } } - code = pdfi_interpret_content_stream(ctx, fmapstr, &fakedict, NULL); - if (ctx->main_stream == &fakemainstream) { - ctx->main_stream = NULL; + else { + code = pdfi_open_memory_stream_from_memory(ctx, fmapbuflen, fmapbuf, &fmapstr, true); + if (code >= 0) { + + if (ctx->main_stream == NULL) { + ctx->main_stream = &fakemainstream; + } + + code = pdfi_interpret_content_stream(ctx, fmapstr, &fakedict, NULL); + if (ctx->main_stream == &fakemainstream) { + ctx->main_stream = NULL; + } + gs_free_object(ctx->memory, fmapbuf, "pdf_make_fontmap(fmapbuf)"); + } } - if (pdfi_count_stack(ctx) > stacksize && ctx->stack_top[-1]->type == PDF_DICT) { - ctx->pdffontmap = (pdf_dict *)ctx->stack_top[-1]; - pdfi_countup(ctx->pdffontmap); + j++; + } while(j < ctx->num_fontmapfiles && cidfmap == false && code >= 0); + + if (code >= 0) { + if (pdfi_count_stack(ctx) > stacksize) { + code = pdfi_dict_from_stack(ctx, 0, 0, true); + if (code < 0) + goto done; + + if (cidfmap == true) { + ctx->pdfcidfmap = (pdf_dict *)ctx->stack_top[-1]; + pdfi_countup(ctx->pdfcidfmap); + } + else { + ctx->pdffontmap = (pdf_dict *)ctx->stack_top[-1]; + pdfi_countup(ctx->pdffontmap); + } pdfi_pop(ctx, 1); code = 0; /* Add our internal aliases to the fontmap. */ - for (i = 0; pcfe[i].keyname != NULL; i++) { - pdf_obj *value; - bool k; - - /* We don't want to *replace* entries */ - if (pdfi_dict_known(ctx, ctx->pdffontmap, pcfe[i].keyname, &k) >= 0 - && k != true) { - code = pdfi_name_alloc(ctx, (byte *)pcfe[i].mappedname, strlen(pcfe[i].mappedname), &value); - if (code < 0) - continue; - pdfi_countup(value); - /* If dict_put throws an error, we just carry on - hence the (void) */ - (void)pdfi_dict_put(ctx, ctx->pdffontmap, pcfe[i].keyname, value); - pdfi_countdown(value); + if (cidfmap == false) { + for (i = 0; pcfe[i].keyname != NULL; i++) { + pdf_obj *value; + bool k; + + /* We don't want to *replace* entries */ + if (pdfi_dict_known(ctx, ctx->pdffontmap, pcfe[i].keyname, &k) >= 0 + && k != true) { + code = pdfi_name_alloc(ctx, (byte *)pcfe[i].mappedname, strlen(pcfe[i].mappedname), &value); + if (code < 0) + continue; + pdfi_countup(value); + /* If dict_put throws an error, we just carry on - hence the (void) */ + (void)pdfi_dict_put(ctx, ctx->pdffontmap, pcfe[i].keyname, value); + pdfi_countdown(value); + } } } } @@ -144,38 +199,821 @@ pdf_make_fontmap(pdf_context *ctx) code = gs_note_error(gs_error_syntaxerror); } } - gs_free_object(ctx->memory, fmapbuf, "pdf_make_fontmap(fmapbuf)"); +done: + /* We always want to leave here with a valid map dictionary + even if it's empty + */ + if (cidfmap == true) { + if (ctx->pdfcidfmap == NULL) { + code = pdfi_dict_alloc(ctx, 0, &ctx->pdfcidfmap); + pdfi_countup(ctx->pdfcidfmap); + } + } + else { + if (ctx->pdffontmap == NULL) { + code = pdfi_dict_alloc(ctx, 0, &ctx->pdffontmap); + pdfi_countup(ctx->pdffontmap); + } + } + pdfi_clearstack(ctx); + return code; +} + +enum { + no_type_font = -1, + type0_font = 0, + type1_font = 1, + cff_font = 2, + type3_font = 3, + tt_font = 42 +}; + +/* For font file scanning we want to treat OTTO as TTF (rather than, for "normal" + font loading, treat OTTO as CFF, hence we need a different type picker. + */ +static int pdfi_font_scan_type_picker(byte *buf, int64_t buflen) +{ +#define MAKEMAGIC(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + + if (buflen >= 4) { + if (MAKEMAGIC(buf[0], buf[1], buf[2], buf[3]) == MAKEMAGIC(0, 1, 0, 0) + || MAKEMAGIC(buf[0], buf[1], buf[2], buf[3]) == MAKEMAGIC('t', 'r', 'u', 'e') + || MAKEMAGIC(buf[0], buf[1], buf[2], buf[3]) == MAKEMAGIC('t', 't', 'c', 'f')) { + return tt_font; + } + else if (MAKEMAGIC(buf[0], buf[1], buf[2], buf[3]) == MAKEMAGIC('O', 'T', 'T', 'O')) { + return tt_font; + } + else if (MAKEMAGIC(buf[0], buf[1], buf[2], 0) == MAKEMAGIC('%', '!', 'P', 0)) { + return type1_font; /* pfa */ + } + else if (MAKEMAGIC(buf[0], buf[1], buf[2], 0) == MAKEMAGIC(1, 0, 4, 0)) { + return cff_font; /* 1C/CFF */ + } + else if (MAKEMAGIC(buf[0], buf[1], 0, 0) == MAKEMAGIC(128, 1, 0, 0)) { + return type1_font; /* pfb */ + } + } + return no_type_font; +#undef MAKEMAGIC +} + +static int pdfi_add__to_native_fontmap(pdf_context *ctx, const char *fontname, const char *filepath, const int index) +{ + int code; + pdf_string *fpstr; + + if (ctx->pdfnativefontmap == NULL) { + /* 32 is just an arbitrary starting point */ + code = pdfi_dict_alloc(ctx, 32, &ctx->pdfnativefontmap); + if (code < 0) + return code; + pdfi_countup(ctx->pdfnativefontmap); + } + /* index == -1 is a file with a single font in it */ + if (index == -1) { + code = pdfi_object_alloc(ctx, PDF_STRING, strlen(filepath), (pdf_obj **)&fpstr); + if (code < 0) + return code; + pdfi_countup(fpstr); + memcpy(fpstr->data, filepath, fpstr->length); + code = pdfi_dict_put(ctx, ctx->pdfnativefontmap, fontname, (pdf_obj *)fpstr); + pdfi_countdown(fpstr); + } + else { + pdf_dict *recdict; + pdf_num *ind; + + code = pdfi_object_alloc(ctx, PDF_DICT, 2, (pdf_obj **)&recdict); + if (code < 0) + return code; + pdfi_countup(recdict); + + code = pdfi_object_alloc(ctx, PDF_STRING, strlen(filepath), (pdf_obj **)&fpstr); + if (code < 0) { + pdfi_countdown(recdict); + return code; + } + pdfi_countup(fpstr); + memcpy(fpstr->data, filepath, fpstr->length); + code = pdfi_dict_put(ctx, recdict, "Path", (pdf_obj *)fpstr); + pdfi_countdown(fpstr); + if (code < 0) { + pdfi_countdown(recdict); + return code; + } + + code = pdfi_object_alloc(ctx, PDF_INT, 0, (pdf_obj **)&ind); + if (code < 0) { + pdfi_countdown(recdict); + return code; + } + ind->value.i = index; + pdfi_countup(ind); + code = pdfi_dict_put(ctx, recdict, "Index", (pdf_obj *)ind); + pdfi_countdown(ind); + if (code < 0) { + pdfi_countdown(recdict); + return code; + } + code = pdfi_dict_put(ctx, ctx->pdfnativefontmap, fontname, (pdf_obj *)recdict); + pdfi_countdown(recdict); + } + return code; } +static inline int +pdfi_end_ps_token(int c) +{ + return (c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA) || (c == '/') || (c == '\0'); +} + +/* Naive way to find a Type 1 /FontName key */ +static int pdfi_type1_add_to_native_map(pdf_context *ctx, stream *f, char *fname, char *pname, int pname_size) +{ + gs_string buf; + uint count = 0; + int code = gs_error_undefined; + char *namestr = NULL, *enamestr; + char *typestr; + bool pin_eol = false; /* initialised just to placate coverity */ + int type = -1; + buf.data = (byte *)pname; + buf.size = pname_size; + + /* It's not an absolute requirement, but it is a de facto standard that + /FontType and /FontName keys start in column 0 of their lines + */ + while ((code = sreadline(f, NULL, NULL, NULL, &buf, NULL, &count, &pin_eol, NULL)) >= 0) { + if (buf.size > 9 && memcmp(buf.data, "/FontName", 9) == 0) { + namestr = (char *)buf.data + 9; + while (pdfi_end_ps_token(*namestr)) + namestr++; + if (*namestr != '\0') { + while(*namestr == 0x20 || *namestr == 0x9) + namestr++; + if (*namestr == '/') + namestr++; + enamestr = namestr; + while(!pdfi_end_ps_token((int)*enamestr)) + enamestr++; + count = enamestr - namestr > pname_size - 1 ? pname_size - 1 : enamestr - namestr; + memcpy(pname, namestr, count); + pname[count] = '\0'; + buf.data += count + 1; + buf.size -= count + 1; + if (type == 1) + break; + } + } + else if (buf.size > 9 && memcmp(buf.data, "/FontType", 9) == 0) { + typestr = (char *)buf.data + 9; + while (pdfi_end_ps_token(*typestr)) + typestr++; + if (*typestr != '\0') { + while(*typestr == 0x20 || *typestr == 0x9) + namestr++; + if (*typestr == '/') + typestr++; + enamestr = typestr; + while(!pdfi_end_ps_token((int)*enamestr)) + enamestr++; + count = enamestr - typestr > pname_size - 1 ? pname_size - 1 : enamestr - typestr; + if (count == 1 && *typestr == '1') { + type = 1; + if (namestr != NULL) + break; + } + } + } + else if (buf.size >= 17 && memcmp(buf.data, "currentfile eexec", 17) == 0) + break; + count = 0; + } + if (type == 1 && namestr != NULL) { + code = pdfi_add__to_native_fontmap(ctx, (const char *)pname, (const char *)fname, -1); + } + return code < 0 ? code : gs_error_handled; +} + +static inline int +u16(const byte *p) +{ + return (p[0] << 8) | p[1]; +} + +static inline int +sru16(stream *s) +{ + byte p[2]; + int code = sfread(p, 1, 2, s); + + if (code < 0) + return 0; + + return u16(p); +} + +static inline int +u32(const byte *p) +{ + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; +} + +static inline int +sru32(stream *s) +{ + byte p[4]; + int code = sfread(p, 1, 4, s); + if (code < 0) + return 0; + + return u32(p); +} + +static int pdfi_ttf_add_to_native_map(pdf_context *ctx, stream *f, byte magic[4], char *fname, char *pname, int pname_size) +{ + int ntables, i, j, k, code2, code = gs_error_undefined; + char table[4]; + int nte, storageOffset; + bool include_index = false; + uint32_t nfonts = 1, tableoffs; + int findex; + gs_offset_t l; + + code2 = sfseek(f, 0, SEEK_END); + if (code2 < 0) + return code; + l = stell(f); + /* 4 to skip the magic number */ + code2 = sfseek(f, 4, SEEK_SET); + if (code2 < 0) + return code; + + if (memcmp(magic, "ttcf", 4) == 0) { + uint32_t ver; + include_index = true; + ver = sru32(f); + if (ver != 0x00010000 && ver !=0x00020000) { + dmprintf1(ctx->memory, "Unknown TTC header version %08X.\n", ver); + return_error(gs_error_invalidaccess); + } + nfonts = sru32(f); + /* There isn't a specific limit on the number of fonts, + freetype limits to 255, so we'll do the same here + */ + if (nfonts > 255) + return_error(gs_error_invalidfont); + } + + for (findex = 0; findex < nfonts; findex++) { + if (include_index == true ) { + if (findex * 4 > l) + break; + code2 = sfseek(f, findex * 4, SEEK_CUR); + if (code2 < 0) + return code2; + + tableoffs = sru32(f); + if (tableoffs + 4 > l) + break; + code2 = sfseek(f, tableoffs + 4, SEEK_SET); + if (code2 < 0) + return code2; + } + + ntables = sru16(f); + /* Similar to above - no spec limit, but we do the same + as freetype + */ + if (ntables > 255) + continue; + + /* Skip the remainder of the invariant header bytes */ + (void)sru16(f); + (void)sru32(f); + + for (i = 0, code2 = 0; i < ntables && code2 >= 0; i++) { + if (stell(f) + 4 > l) { + code2 = gs_error_ioerror; + continue; + } + code2 = sfread(table, 1, 4, f); + if (code2 < 0) + return code2; + if (!memcmp(table, "name", 4)) { + uint table_pos, table_len; + byte *namet; + (void)sru32(f); /* skip checksum */ + + table_pos = sru32(f); + table_len = sru32(f); + if (table_pos + table_len > l) { + code2 = gs_error_ioerror; + continue; + } + + code2 = sfseek(f, table_pos, SEEK_SET); + if (code2 < 0) + continue; + + namet = (byte *)gs_alloc_bytes(ctx->memory, table_len, "pdfi_ttf_add_to_native_map"); + if (namet == NULL) + return_error(gs_error_VMerror); + code2 = sfread(namet, 1, table_len, f); + if (code2 < 0) { + gs_free_object(ctx->memory, namet,"pdfi_ttf_add_to_native_map"); + continue; + } + nte = u16(namet + 2); + /* Again, arbitrary limit... */ + if (nte > 255) { + gs_free_object(ctx->memory, namet,"pdfi_ttf_add_to_native_map"); + continue; + } + storageOffset = u16(namet + 4); + + for (j = 0; j < nte; j++) { + byte *rec = namet + 6 + j * 12; + if (u16(rec + 6) == 6) { + int nl = u16(rec + 8); + int noffs = u16(rec + 10); + if (nl + noffs + storageOffset > table_len) { + break; + } + memcpy(pname, namet + storageOffset + noffs, nl); + pname[nl] = '\0'; + for (k = 0; k < nl; k++) + if (pname[k] < 32 || pname[k] > 126) /* is it a valid name? */ + break; + if (k == nl) { + code = 0; + break; + } + } + } + if (code == gs_error_undefined) { + for (j = 0; j < nte; j++) { + byte *rec = namet + 6 + j * 12; + if (u16(rec + 6) == 4) { + int nl = u16(rec + 8); + int noffs = u16(rec + 10); + if (nl + noffs + storageOffset > table_len) { + break; + } + memcpy(pname, namet + storageOffset + noffs, nl); + pname[nl] = '\0'; + for (k = 0; k < nl; k++) + if (pname[k] < 32 || pname[k] > 126) /* is it a valid name? */ + break; + if (k == nl) + code = 0; + break; + } + } + } + gs_free_object(ctx->memory, namet, "pdfi_ttf_add_to_native_map"); + break; + } + else { + sfseek(f, 12, SEEK_CUR); + } + } + if (code >= 0) + code = pdfi_add__to_native_fontmap(ctx, (const char *)pname, (const char *)fname, (include_index == true ? findex : -1)); + } + return code; +} + +static const char *font_scan_skip_list[] = { + ".afm", + ".bat", + ".c", + ".cmd", + ".com", + ".dir", + ".dll", + ".doc", + ".drv", + ".exe", + ".fon", + ".fot", + ".h", + ".o", + ".obj", + ".pfm", + ".pss", + ".txt", + ".gz", + ".pcf" +}; + +static bool font_scan_skip_file(char *fname) +{ + size_t l2, l = strlen(fname); + bool skip = false; + int i; + + for (i = 0; i < (sizeof(font_scan_skip_list)/sizeof(*font_scan_skip_list)); i++) { + l2 = strlen(font_scan_skip_list[i]); + if (memcmp(font_scan_skip_list[i], fname + l - l2, l2) == 0) { + skip = true; + break; + } + } + return skip; +} + +static int pdfi_generate_native_fontmap(pdf_context *ctx) +{ + file_enum *fe; + int i; + char *patrn= NULL; + char *result = NULL; + char *working = NULL; + byte magic[4]; /* We only (currently) use up to 4 bytes for type guessing */ + stream *sf; + int code = 0, l; + uint nread; + + if (ctx->pdfnativefontmap != NULL) /* Only run this once */ + return 0; + if (ctx->args.nonativefontmap == true) { + /* Basically create an empty dictionary */ + code = pdfi_dict_alloc(ctx, 1, &ctx->pdfnativefontmap); + if (code < 0) + return code; + pdfi_countup(ctx->pdfnativefontmap); + return 0; + } + + patrn = (char *)gs_alloc_bytes(ctx->memory, gp_file_name_sizeof, "pdfi_generate_native_fontmap"); + result = (char *)gs_alloc_bytes(ctx->memory, gp_file_name_sizeof, "pdfi_generate_native_fontmap"); + working = (char *)gs_alloc_bytes(ctx->memory, gp_file_name_sizeof, "pdfi_generate_native_fontmap"); + if (patrn == NULL || result == NULL || working == NULL) { + gs_free_object(ctx->memory, patrn, "pdfi_generate_native_fontmap"); + gs_free_object(ctx->memory, result, "pdfi_generate_native_fontmap"); + gs_free_object(ctx->memory, working, "pdfi_generate_native_fontmap"); + return_error(gs_error_VMerror); + } + + for (i = 0; i < ctx->search_paths.num_font_paths; i++) { + + memcpy(patrn, ctx->search_paths.font_paths[i].data, ctx->search_paths.font_paths[i].size); + memcpy(patrn + ctx->search_paths.font_paths[i].size, "/*", 2); + patrn[ctx->search_paths.font_paths[i].size + 2] = '\0'; + + fe = gp_enumerate_files_init(ctx->memory, (const char *)patrn, strlen(patrn)); + while ((l = gp_enumerate_files_next(ctx->memory, fe, result, gp_file_name_sizeof - 1)) != ~(uint) 0) { + int type; + result[l] = '\0'; + + if (font_scan_skip_file(result)) + continue; + + sf = sfopen(result, "r", ctx->memory); + code = sgets(sf, magic, 4, &nread); + if (code < 0 || nread < 4) { + sfclose(sf); + continue; + } + + code = sfseek(sf, 0, SEEK_SET); + if (code < 0) { + sfclose(sf); + continue; + } + /* Slightly naff: in this one case, we want to treat OTTO fonts + as Truetype, so we lookup the TTF 'name' table - it's more efficient + than decoding the CFF, and probably will give more expected results + */ + if (memcmp(magic, "OTTO", 4) == 0 || memcmp(magic, "ttcf", 4) == 0) { + type = tt_font; + } + else { + type = pdfi_font_scan_type_picker((byte *)magic, 4); + } + switch(type) { + case tt_font: + code = pdfi_ttf_add_to_native_map(ctx, sf, magic, result, working, gp_file_name_sizeof); + break; + case cff_font: + code = gs_error_undefined; + break; + case type1_font: + default: + code = pdfi_type1_add_to_native_map(ctx, sf, result, working, gp_file_name_sizeof); + break; + } + sfclose(sf); + /* We ignore most errors, on the basis it probably means it wasn't a valid font file */ + if (code == gs_error_VMerror) + break; + code = 0; + } + /* We only need to explicitly destroy the enumerator if we exit before enumeration is complete */ + if (code < 0) + gp_enumerate_files_close(ctx->memory, fe); + } + +#if 0 + if (ctx->pdfnativefontmap != NULL) { + uint64_t ind; + int find = -1; + pdf_name *key = NULL; + pdf_obj *v = NULL; + pdf_string *val = NULL; + (void)pdfi_dict_key_first(ctx, ctx->pdfnativefontmap, (pdf_obj **) &key, &ind); + (void)pdfi_dict_get_by_key(ctx, ctx->pdfnativefontmap, key, (pdf_obj **)&v); + for (j = 0; j < key->length; j++) + dprintf1("%c", key->data[j]); + if (v->type == PDF_DICT) { + pdf_num *n; + pdf_string *val2; + code = pdfi_dict_get(ctx, (pdf_dict *)v, "Index", (pdf_obj **)&n); + if (code >= 0 && n->type == PDF_INT) + find = n->value.i; + else + code = 0; + (void)pdfi_dict_get(ctx, (pdf_dict *)v, "Path", (pdf_obj **)&val2); + val = val2; + } + else { + val = (pdf_string *)v; + } + dprintf(" "); + for (j = 0; j < val->length; j++) + dprintf1("%c", val->data[j]); + if (find != -1) { + dprintf1(" Index = %d", find); + find = -1; + } + + dprintf("\n"); + pdfi_countdown(key); + pdfi_countdown(val); + + while (pdfi_dict_key_next(ctx, ctx->pdfnativefontmap, (pdf_obj **) &key, &ind) >= 0 && ind > 0) { + (void)pdfi_dict_get_by_key(ctx, ctx->pdfnativefontmap, key, (pdf_obj **)&v); + for (j = 0; j < key->length; j++) + dprintf1("%c", key->data[j]); + if (v->type == PDF_DICT) { + pdf_num *n; + pdf_string *val2; + code = pdfi_dict_get(ctx, (pdf_dict *)v, "Index", (pdf_obj **)&n); + if (code >= 0 && n->type == PDF_INT) + find = n->value.i; + else + code = 0; + (void)pdfi_dict_get(ctx, (pdf_dict *)v, "Path", (pdf_obj **)&val2); + val = val2; + } + else { + val = (pdf_string *)v; + } + dprintf(" "); + for (j = 0; j < val->length; j++) + dprintf1("%c", val->data[j]); + if (find != -1) { + dprintf1(" Index = %d", find); + find = -1; + } + pdfi_countdown(key); + pdfi_countdown(val); + dprintf("\n"); + } + } +#endif + + gs_free_object(ctx->memory, patrn, "pdfi_generate_native_fontmap"); + gs_free_object(ctx->memory, result, "pdfi_generate_native_fontmap"); + gs_free_object(ctx->memory, working, "pdfi_generate_native_fontmap"); + return 0; +} int -pdf_fontmap_lookup_font(pdf_context *ctx, pdf_name *fname, pdf_obj **mapname) +pdf_fontmap_lookup_font(pdf_context *ctx, pdf_name *fname, pdf_obj **mapname, int *findex) { - int code = 0; + int code; pdf_obj *mname; + *findex = -1; + if (ctx->pdffontmap == NULL) { - code = pdf_make_fontmap(ctx); + const char *fmap_default = "Fontmap.GS"; + char *fmapname = (char *)fmap_default; + code = pdf_make_fontmap(ctx, fmapname, false); if (code < 0) { return code; } } + if (ctx->pdfnativefontmap == NULL) { + code = pdfi_generate_native_fontmap(ctx); + if (code < 0) + return code; + } + code = pdfi_dict_get_by_key(ctx, ctx->pdffontmap, fname, &mname); + if (code >= 0) { + /* Fontmap can map in multiple "jump" i.e. + name -> substitute name + subsitute name -> file name + So we want to loop until we no more hits. + */ + while(1) { + pdf_obj *mname2; + code = pdfi_dict_get_by_key(ctx, ctx->pdffontmap, (pdf_name *)mname, &mname2); + if (code < 0) break; + pdfi_countdown(mname); + mname = mname2; + } + } + else if (ctx->pdfnativefontmap != NULL) { + pdf_obj *record; + code = pdfi_dict_get_by_key(ctx, ctx->pdfnativefontmap, fname, &record); + if (code < 0) + return code; + if (record->type == PDF_STRING) { + mname = record; + } + else { + pdf_num *ind; + code = pdfi_dict_get(ctx, (pdf_dict *)record, "Path", &mname); + if (code < 0) { + pdfi_countdown(record); + return code; + } + code = pdfi_dict_get(ctx, (pdf_dict *)record, "Index", (pdf_obj **)&ind); + if (code >= 0 && ind->type == PDF_INT) { + *findex = ind->value.i; + } + } + } + + if (mname != NULL && mname->type == PDF_STRING && pdfi_fmap_file_exists(ctx, (pdf_string *)mname)) { + *mapname = mname; + code = 0; + } + else if (mname != NULL && mname->type == PDF_NAME) { /* If we map to a name, we assume (for now) we have the font as a "built-in" */ + *mapname = mname; + code = 0; + } + else { + pdfi_countdown(mname); + code = gs_note_error(gs_error_undefined); + } + return code; +} + +int +pdf_fontmap_lookup_cidfont(pdf_context *ctx, pdf_dict *font_dict, pdf_name *name, pdf_obj **mapname, int *findex) +{ + int code = 0; + pdf_obj *cidname = NULL; + pdf_obj *mname; + + if (ctx->pdfcidfmap == NULL) { + const char *cidfmap_default = "cidfmap"; + char *cidfmapname = (char *)cidfmap_default; + code = pdf_make_fontmap(ctx, cidfmapname, true); + if (code < 0) { + return code; + } + } + if (name == NULL || name->type != PDF_NAME) { + code = pdfi_dict_get(ctx, font_dict, "BaseFont", &cidname); + if (code < 0 || cidname->type != PDF_NAME) { + pdfi_countdown(cidname); + return_error(gs_error_undefined); + } + + } + else { + cidname = (pdf_obj *)name; + pdfi_countup(cidname); + } + + code = pdfi_dict_get_by_key(ctx, ctx->pdfcidfmap, (pdf_name *)cidname, &mname); + pdfi_countdown(cidname); if (code < 0) return code; - /* Fontmap can map in multiple "jump" i.e. + /* As above cidfmap can map in multiple "jump" i.e. name -> substitute name - subsitute name -> file name + subsitute name -> "record" So we want to loop until we no more hits. */ while(1) { pdf_obj *mname2; - code = pdfi_dict_get_by_key(ctx, ctx->pdffontmap, (pdf_name *)mname, &mname2); + code = pdfi_dict_get_by_key(ctx, ctx->pdfcidfmap, (pdf_name *)mname, &mname2); if (code < 0) break; pdfi_countdown(mname); mname = mname2; } - *mapname = mname; - return 0; + if (mname->type == PDF_DICT) { + pdf_dict *rec = (pdf_dict *)mname; + pdf_name *filetype; + pdf_name *path = NULL; + pdf_num *ind = NULL; + pdf_array *mcsi = NULL; + pdf_dict *ocsi = NULL; + pdf_string *ord1 = NULL, *ord2 = NULL; + pdf_num *sup1, *sup2; + + code = pdfi_dict_get(ctx, rec, "FileType", (pdf_obj **)&filetype); + /* We only handle TTF files, just now */ + if (code < 0 || filetype->type != PDF_NAME || filetype->length != 8 || memcmp(filetype->data, "TrueType", 8) != 0) { + pdfi_countdown(filetype); + pdfi_countdown(rec); + return_error(gs_error_undefined); + } + pdfi_countdown(filetype); + + code = pdfi_dict_get(ctx, rec, "CSI", (pdf_obj **)&mcsi); + if (code < 0 || mcsi->type != PDF_ARRAY) { + pdfi_countdown(mcsi); + pdfi_countdown(rec); + return_error(gs_error_undefined); + } + + code = pdfi_dict_get(ctx, font_dict, "CIDSystemInfo", (pdf_obj **)&ocsi); + if (code < 0 || ocsi->type != PDF_DICT) { + pdfi_countdown(ocsi); + pdfi_countdown(mcsi); + pdfi_countdown(rec); + return_error(gs_error_undefined); + } + code = pdfi_dict_get(ctx, ocsi, "Ordering", (pdf_obj **)&ord1); + if (code < 0 || ord1->type != PDF_STRING) { + pdfi_countdown(ord1); + pdfi_countdown(ocsi); + pdfi_countdown(mcsi); + pdfi_countdown(rec); + return_error(gs_error_undefined); + } + code = pdfi_array_get(ctx, mcsi, 0, (pdf_obj **)&ord2); + if (code < 0 || ord2->type != PDF_STRING) { + pdfi_countdown(ord1); + pdfi_countdown(ord2); + pdfi_countdown(ocsi); + pdfi_countdown(mcsi); + pdfi_countdown(rec); + return_error(gs_error_undefined); + } + if (pdfi_string_cmp(ord1, ord2) != 0) { + pdfi_countdown(ord1); + pdfi_countdown(ord2); + pdfi_countdown(ocsi); + pdfi_countdown(mcsi); + pdfi_countdown(rec); + return_error(gs_error_undefined); + } + pdfi_countdown(ord1); + pdfi_countdown(ord2); + code = pdfi_dict_get(ctx, ocsi, "Supplement", (pdf_obj **)¹); + if (code < 0 || sup1->type != PDF_INT) { + pdfi_countdown(ord1); + pdfi_countdown(ocsi); + pdfi_countdown(mcsi); + pdfi_countdown(rec); + return_error(gs_error_undefined); + } + code = pdfi_array_get(ctx, mcsi, 1, (pdf_obj **)²); + if (code < 0 || sup2->type != PDF_INT || sup1->value.i != sup2->value.i) { + pdfi_countdown(sup1); + pdfi_countdown(sup2); + pdfi_countdown(ocsi); + pdfi_countdown(mcsi); + pdfi_countdown(rec); + return_error(gs_error_undefined); + } + pdfi_countdown(sup1); + pdfi_countdown(sup2); + pdfi_countdown(ocsi); + pdfi_countdown(mcsi); + + code = pdfi_dict_get(ctx, rec, "Path", (pdf_obj **)&path); + if (code < 0 || path->type != PDF_STRING || !pdfi_fmap_file_exists(ctx, (pdf_string *)path)) { + pdfi_countdown(rec); + return_error(gs_error_undefined); + } + + *mapname = (pdf_obj *)path; + code = pdfi_dict_get(ctx, rec, "Index", (pdf_obj **)&ind); + if (code >= 0 && ind->type != PDF_INT) { + *findex = ind->value.i; + } + else { + *findex = 0; + } + pdfi_countdown(ind); + code = 0; + + } + else { + *mapname = (pdf_obj *)mname; + code = 0; + } + + return code; } diff --git a/pdf/pdf_fmap.h b/pdf/pdf_fmap.h index 602bb0cf..c7650203 100644 --- a/pdf/pdf_fmap.h +++ b/pdf/pdf_fmap.h @@ -17,4 +17,10 @@ or might be a string. */ int -pdf_fontmap_lookup_font(pdf_context *ctx, pdf_name *fname, pdf_obj **mapname); +pdf_fontmap_lookup_font(pdf_context *ctx, pdf_name *fname, pdf_obj **mapname, int *findex); + +/* The name parameter is to allow for internally derived font names to be looked up, + like, for example, /Adobe-Japan1 + */ +int +pdf_fontmap_lookup_cidfont(pdf_context *ctx, pdf_dict *font_dict, pdf_name *name, pdf_obj **mapname, int *findex); diff --git a/pdf/pdf_font.c b/pdf/pdf_font.c index 7fa8a427..fa716056 100644 --- a/pdf/pdf_font.c +++ b/pdf/pdf_font.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -16,6 +16,8 @@ /* Font operations for the PDF interpreter */ #include "pdf_int.h" +#include "pdf_font_types.h" +#include "pdf_gstate.h" #include "pdf_file.h" #include "pdf_dict.h" #include "pdf_loop_detect.h" @@ -24,7 +26,6 @@ #include "pdf_stack.h" #include "pdf_misc.h" #include "pdf_doc.h" -#include "pdf_font_types.h" #include "pdf_font0.h" #include "pdf_font1.h" #include "pdf_font1C.h" @@ -41,12 +42,15 @@ static int pdfi_gs_setfont(pdf_context *ctx, gs_font *pfont) { int code = 0; - pdf_font *old_font = pdfi_get_current_pdf_font(ctx); + pdfi_int_gstate *igs = (pdfi_int_gstate *)ctx->pgs->client_data; + pdf_font *old_font = igs->current_font; code = gs_setfont(ctx->pgs, pfont); - if (code >= 0) + if (code >= 0) { + igs->current_font = (pdf_font *)pfont->client_data; + pdfi_countup(igs->current_font); pdfi_countdown(old_font); - + } return code; } @@ -145,6 +149,19 @@ pdfi_font_match_glyph_widths(pdf_font *pdfont) return code; } +/* Print a name object to stdout */ +static void pdfi_print_font_name(pdf_context *ctx, pdf_name *n) +{ + if (ctx->args.QUIET != true) + (void)outwrite(ctx->memory, (const char *)n->data, n->length); +} + +/* Print a null terminated string to stdout */ +static void pdfi_print_string(pdf_context *ctx, const char *str) +{ + if (ctx->args.QUIET != true) + (void)outwrite(ctx->memory, str, strlen(str)); +} /* Call with a CIDFont name to try to find the CIDFont on disk call if with ffname NULL to load the default fallback CIDFont @@ -152,25 +169,179 @@ pdfi_font_match_glyph_widths(pdf_font *pdfont) Currently only loads subsitute - DroidSansFallback */ static int -pdfi_open_CIDFont_substitute_file(pdf_context * ctx, pdf_dict *font_dict, pdf_dict *fontdesc, bool fallback, byte ** buf, int64_t * buflen) +pdfi_open_CIDFont_substitute_file(pdf_context * ctx, pdf_dict *font_dict, pdf_dict *fontdesc, bool fallback, byte ** buf, int64_t * buflen, int *findex) { - int code = gs_error_invalidfont; + int code = 0; + char fontfname[gp_file_name_sizeof]; + stream *s; + pdf_name *cidname = NULL; + gs_const_string fname; + + (void)pdfi_dict_get(ctx, font_dict, "BaseFont", (pdf_obj **)&cidname); if (fallback == true) { - char fontfname[gp_file_name_sizeof]; - const char *fsprefix = "CIDFSubst/"; + pdf_string *mname = NULL; + pdf_dict *csi = NULL; + + code = pdfi_dict_get(ctx, font_dict, "CIDSystemInfo", (pdf_obj **)&csi); + if (code >= 0 && csi->type == PDF_DICT) { + pdf_string *csi_reg = NULL, *csi_ord = NULL; + + if (pdfi_dict_get(ctx, csi, "Registry", (pdf_obj **)&csi_reg) >= 0 + && pdfi_dict_get(ctx, csi, "Ordering", (pdf_obj **)&csi_ord) >= 0 + && csi_reg->type == PDF_STRING && csi_ord->type == PDF_STRING + && csi_reg->length + csi_ord->length + 1 < gp_file_name_sizeof - 1) { + pdf_name *reg_ord; + memcpy(fontfname, csi_reg->data, csi_reg->length); + memcpy(fontfname + csi_reg->length, "-", 1); + memcpy(fontfname + csi_reg->length + 1, csi_ord->data, csi_ord->length); + fontfname[csi_reg->length + csi_ord->length + 1] = '\0'; + + code = pdfi_name_alloc(ctx, (byte *)fontfname, strlen(fontfname), (pdf_obj **) ®_ord); + if (code >= 0) { + pdfi_countup(reg_ord); + code = pdf_fontmap_lookup_cidfont(ctx, font_dict, reg_ord, (pdf_obj **)&mname, findex); + pdfi_countdown(reg_ord); + } + } + pdfi_countdown(csi_reg); + pdfi_countdown(csi_ord); + } + pdfi_countdown(csi); + + if (mname == NULL || mname->type != PDF_STRING) + code = pdf_fontmap_lookup_cidfont(ctx, font_dict, NULL, (pdf_obj **)&mname, findex); + + if (code < 0 || mname->type != PDF_STRING) { + const char *fsprefix = "CIDFSubst/"; + int fsprefixlen = strlen(fsprefix); + const char *defcidfallack = "DroidSansFallback.ttf"; + int defcidfallacklen = strlen(defcidfallack); + + pdfi_countdown(mname); + + if (ctx->args.nocidfallback == true) { + code = gs_note_error(gs_error_invalidfont); + } + else { + if (ctx->args.cidsubstpath.data == NULL) { + memcpy(fontfname, fsprefix, fsprefixlen); + } + else { + memcpy(fontfname, ctx->args.cidsubstpath.data, ctx->args.cidsubstpath.size); + fsprefixlen = ctx->args.cidsubstpath.size; + } + + if (ctx->args.cidsubstfont.data == NULL) { + int len = 0; + if (gp_getenv("CIDSUBSTFONT", (char *)0, &len) < 0 && len + fsprefixlen + 1 < gp_file_name_sizeof) { + (void)gp_getenv("CIDSUBSTFONT", (char *)(fontfname + fsprefixlen), &defcidfallacklen); + } + else { + memcpy(fontfname + fsprefixlen, defcidfallack, defcidfallacklen); + } + } + else { + memcpy(fontfname, ctx->args.cidsubstfont.data, ctx->args.cidsubstfont.size); + defcidfallacklen = ctx->args.cidsubstfont.size; + } + fontfname[fsprefixlen + defcidfallacklen] = '\0'; + + code = pdfi_open_resource_file(ctx, fontfname, strlen(fontfname), &s); + if (code < 0) { + code = gs_note_error(gs_error_invalidfont); + } + else { + if (cidname) { + pdfi_print_string(ctx, "Loading CIDFont "); + pdfi_print_font_name(ctx, (pdf_name *)cidname); + pdfi_print_string(ctx, " substitute from "); + } + else { + pdfi_print_string(ctx, "Loading nameless CIDFont from "); + } + sfilename(s, &fname); + if (fname.size < gp_file_name_sizeof) { + memcpy(fontfname, fname.data, fname.size); + fontfname[fname.size] = '\0'; + } + else { + strcpy(fontfname, "unnamed file"); + } + pdfi_print_string(ctx, fontfname); + pdfi_print_string(ctx, "\n"); + + + sfseek(s, 0, SEEK_END); + *buflen = sftell(s); + sfseek(s, 0, SEEK_SET); + *buf = gs_alloc_bytes(ctx->memory, *buflen, "pdfi_open_CIDFont_file(buf)"); + if (*buf != NULL) { + sfread(*buf, 1, *buflen, s); + } + else { + code = gs_note_error(gs_error_VMerror); + } + sfclose(s); + } + } + } + else { + code = pdfi_open_resource_file(ctx, (const char *)mname->data, mname->length, &s); + pdfi_countdown(mname); + if (code < 0) { + code = gs_note_error(gs_error_invalidfont); + } + else { + if (cidname) { + pdfi_print_string(ctx, "Loading CIDFont "); + pdfi_print_font_name(ctx, (pdf_name *)cidname); + pdfi_print_string(ctx, " (or substitute) from "); + } + else { + pdfi_print_string(ctx, "Loading nameless CIDFont from "); + } + sfilename(s, &fname); + if (fname.size < gp_file_name_sizeof) { + memcpy(fontfname, fname.data, fname.size); + fontfname[fname.size] = '\0'; + } + else { + strcpy(fontfname, "unnamed file"); + } + pdfi_print_string(ctx, fontfname); + pdfi_print_string(ctx, "\n"); + sfseek(s, 0, SEEK_END); + *buflen = sftell(s); + sfseek(s, 0, SEEK_SET); + *buf = gs_alloc_bytes(ctx->memory, *buflen, "pdfi_open_CIDFont_file(buf)"); + if (*buf != NULL) { + sfread(*buf, 1, *buflen, s); + } + else { + code = gs_note_error(gs_error_VMerror); + } + sfclose(s); + } + } + } + else { + const char *fsprefix = "CIDFont/"; const int fsprefixlen = strlen(fsprefix); - const char *defcidfallack = "DroidSansFallback.ttf"; - const int defcidfallacklen = strlen(defcidfallack); - stream *s; - code = 0; + + if (cidname == NULL || cidname->type != PDF_NAME + || fsprefixlen + cidname->length >= gp_file_name_sizeof) + goto exit; memcpy(fontfname, fsprefix, fsprefixlen); - memcpy(fontfname + fsprefixlen, defcidfallack, defcidfallacklen); - fontfname[fsprefixlen + defcidfallacklen] = '\0'; + memcpy(fontfname + fsprefixlen, cidname->data, cidname->length); + fontfname[fsprefixlen + cidname->length] = '\0'; code = pdfi_open_resource_file(ctx, fontfname, strlen(fontfname), &s); - if (code >= 0) { + if (code < 0) { + code = gs_note_error(gs_error_invalidfont); + } + else { sfseek(s, 0, SEEK_END); *buflen = sftell(s); sfseek(s, 0, SEEK_SET); @@ -179,14 +350,15 @@ pdfi_open_CIDFont_substitute_file(pdf_context * ctx, pdf_dict *font_dict, pdf_di sfread(*buf, 1, *buflen, s); } else { - code = gs_note_error(gs_error_VMerror); + code = gs_note_error(gs_error_invalidfont); } sfclose(s); } } - else { - code = gs_error_invalidfont; - } + +exit: + if (cidname != NULL) + pdfi_countdown(cidname); return code; } @@ -316,16 +488,44 @@ static const char *pdfi_font_substitute_by_flags(unsigned int flags) return "Helvetica"; /* Really shouldn't ever happen */ } -static void pdfi_emprint_font_name(pdf_context *ctx, pdf_name *n) +enum { + no_type_font = -1, + type0_font = 0, + type1_font = 1, + cff_font = 2, + type3_font = 3, + tt_font = 42 +}; + +static int pdfi_fonttype_picker(byte *buf, int64_t buflen) { - int i; - for (i = 0; i < n->length; i++) { - dmprintf1(ctx->memory, "%c", n->data[i]); +#define MAKEMAGIC(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + + if (buflen >= 4) { + if (MAKEMAGIC(buf[0], buf[1], buf[2], buf[3]) == MAKEMAGIC(0, 1, 0, 0) + || MAKEMAGIC(buf[0], buf[1], buf[2], buf[3]) == MAKEMAGIC('t', 'r', 'u', 'e') + || MAKEMAGIC(buf[0], buf[1], buf[2], buf[3]) == MAKEMAGIC('t', 't', 'c', 'f')) { + return tt_font; + } + else if (MAKEMAGIC(buf[0], buf[1], buf[2], buf[3]) == MAKEMAGIC('O', 'T', 'T', 'O')) { + return cff_font; /* OTTO will end up as CFF */ + } + else if (MAKEMAGIC(buf[0], buf[1], buf[2], 0) == MAKEMAGIC('%', '!', 'P', 0)) { + return type1_font; /* pfa */ + } + else if (MAKEMAGIC(buf[0], buf[1], buf[2], 0) == MAKEMAGIC(1, 0, 4, 0)) { + return cff_font; /* 1C/CFF */ + } + else if (MAKEMAGIC(buf[0], buf[1], 0, 0) == MAKEMAGIC(128, 1, 0, 0)) { + return type1_font; /* pfb */ + } } + return no_type_font; +#undef MAKEMAGIC } static int -pdfi_open_font_substitute_file(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *fontdesc, bool fallback, byte **buf, int64_t *buflen) +pdfi_open_font_substitute_file(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *fontdesc, bool fallback, byte **buf, int64_t *buflen, int *findex) { int code; char fontfname[gp_file_name_sizeof]; @@ -335,7 +535,7 @@ pdfi_open_font_substitute_file(pdf_context *ctx, pdf_dict *font_dict, pdf_dict * const char *fn; code = pdfi_dict_knownget_type(ctx, font_dict, "BaseFont", PDF_NAME, &basefont); - if (code < 0 || ((pdf_name *)basefont)->length == 0) + if (code < 0 || basefont == NULL || ((pdf_name *)basefont)->length == 0) fallback = true; if (fallback == true) { @@ -368,40 +568,51 @@ pdfi_open_font_substitute_file(pdf_context *ctx, pdf_dict *font_dict, pdf_dict * pdfi_countup(fontname); } } - code = pdf_fontmap_lookup_font(ctx, (pdf_name *) fontname, &mapname); + code = pdf_fontmap_lookup_font(ctx, (pdf_name *) fontname, &mapname, findex); if (code < 0) { mapname = fontname; pdfi_countup(mapname); code = 0; } - if (mapname->type == PDF_NAME) { + if (mapname->type == PDF_NAME || mapname->type == PDF_STRING) { pdf_name *mname = (pdf_name *) mapname; if (mname->length + 1 < gp_file_name_sizeof) { memcpy(fontfname, mname->data, mname->length); fontfname[mname->length] = '\0'; } else { + pdfi_countdown(mapname); + pdfi_countdown(fontname); return_error(gs_error_invalidfileaccess); } } + else { + pdfi_countdown(mapname); + pdfi_countdown(fontname); + return_error(gs_error_invalidfileaccess); + } code = pdfi_open_font_file(ctx, fontfname, strlen(fontfname), &s); if (code >= 0) { gs_const_string fname; if (basefont) { - dmprintf(ctx->memory, "Loading font "); - pdfi_emprint_font_name(ctx, (pdf_name *)basefont); - dmprintf(ctx->memory, " (or substitute) from "); + pdfi_print_string(ctx, "Loading font "); + pdfi_print_font_name(ctx, (pdf_name *)basefont); + pdfi_print_string(ctx, " (or substitute) from "); } else { - dmprintf(ctx->memory, "Loading nameless font from "); + pdfi_print_string(ctx, "Loading nameless font from "); } sfilename(s, &fname); if (fname.size < gp_file_name_sizeof) { memcpy(fontfname, fname.data, fname.size); fontfname[fname.size] = '\0'; } - dmprintf1(ctx->memory, "%s.\n", fontfname); + else { + strcpy(fontfname, "unnamed file"); + } + pdfi_print_string(ctx, fontfname); + pdfi_print_string(ctx, "\n"); sfseek(s, 0, SEEK_END); *buflen = sftell(s); @@ -423,42 +634,6 @@ pdfi_open_font_substitute_file(pdf_context *ctx, pdf_dict *font_dict, pdf_dict * } enum { - no_type_font = -1, - type0_font = 0, - type1_font = 1, - cff_font = 2, - type3_font = 3, - tt_font = 42 -}; - -static int pdfi_fonttype_picker(byte *buf, int64_t buflen) -{ -#define MAKEMAGIC(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) - - if (buflen >= 4) { - if (MAKEMAGIC(buf[0], buf[1], buf[2], buf[3]) == MAKEMAGIC(0, 1, 0, 0) - || MAKEMAGIC(buf[0], buf[1], buf[2], buf[3]) == MAKEMAGIC('t', 'r', 'u', 'e') - || MAKEMAGIC(buf[0], buf[1], buf[2], buf[3]) == MAKEMAGIC('t', 't', 'c', 'f')) { - return tt_font; - } - else if (MAKEMAGIC(buf[0], buf[1], buf[2], buf[3]) == MAKEMAGIC('O', 'T', 'T', 'O')) { - return cff_font; /* OTTO will end up as CFF */ - } - else if (MAKEMAGIC(buf[0], buf[1], buf[2], 0) == MAKEMAGIC('%', '!', 'P', 0)) { - return type1_font; /* pfa */ - } - else if (MAKEMAGIC(buf[0], buf[1], buf[2], 0) == MAKEMAGIC(1, 0, 4, 0)) { - return cff_font; /* 1C/CFF */ - } - else if (MAKEMAGIC(buf[0], buf[1], 0, 0) == MAKEMAGIC(128, 1, 0, 0)) { - return type1_font; /* pfb */ - } - } - return no_type_font; -#undef MAKEMAGIC -} - -enum { font_embedded = 0, font_from_file = 1, font_substitute = 2 @@ -477,6 +652,7 @@ int pdfi_load_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, byte *fbuf = NULL; int64_t fbuflen; int substitute = font_embedded; + int findex = -1; code = pdfi_dict_get_type(ctx, font_dict, "Type", PDF_NAME, (pdf_obj **)&Type); if (code < 0) @@ -489,7 +665,12 @@ int pdfi_load_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, /* Beyond Type 0 and Type 3, there is no point trusting the Subtype key */ if (code >= 0 && pdfi_name_is(Subtype, "Type0")) { - code = pdfi_read_type0_font(ctx, (pdf_dict *)font_dict, stream_dict, page_dict, &ppdffont); + if (cidfont == true) { + code = gs_note_error(gs_error_invalidfont); + } + else { + code = pdfi_read_type0_font(ctx, (pdf_dict *)font_dict, stream_dict, page_dict, &ppdffont); + } } else if (code >= 0 && pdfi_name_is(Subtype, "Type3")) { code = pdfi_read_type3_font(ctx, (pdf_dict *)font_dict, stream_dict, page_dict, &ppdffont); @@ -549,7 +730,8 @@ int pdfi_load_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, if (fftype != no_type_font) sftype = fftype; else { - if (pdfi_name_is(Subtype, "Type1") || pdfi_name_is(Subtype, "MMType1")) + /* If we don't have a Subtype, can't work it out, try Type 1 */ + if (Subtype == NULL || pdfi_name_is(Subtype, "Type1") || pdfi_name_is(Subtype, "MMType1")) sftype = type1_font; else if (pdfi_name_is(Subtype, "Type1C")) sftype = cff_font; @@ -570,18 +752,22 @@ int pdfi_load_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, case tt_font: { if (cidfont) - code = pdfi_read_cidtype2_font(ctx, font_dict, stream_dict, page_dict, fbuf, fbuflen, &ppdffont); + code = pdfi_read_cidtype2_font(ctx, font_dict, stream_dict, page_dict, fbuf, fbuflen, findex, &ppdffont); else - code = pdfi_read_truetype_font(ctx, font_dict, stream_dict, page_dict, fbuf, fbuflen, &ppdffont); + code = pdfi_read_truetype_font(ctx, font_dict, stream_dict, page_dict, fbuf, fbuflen, findex, &ppdffont); fbuf = NULL; } break; default: code = gs_note_error(gs_error_invalidfont); } + if (code < 0 && substitute == font_embedded) { - dmprintf2(ctx->memory, "**** Error: can't process embedded stream for font object %d %d.\n", font_dict->object_num, font_dict->generation_num); - dmprintf(ctx->memory, "**** Attempting to load substitute font.\n"); + char obj[129]; + pdfi_print_string(ctx, "**** Warning: cannot process embedded stream for font object "); + gs_snprintf(obj, 128, "%d %d\n", (int)font_dict->object_num, (int)font_dict->generation_num); + pdfi_print_string(ctx, obj); + pdfi_print_string(ctx, "**** Attempting to load a substitute font.\n"); } } @@ -594,9 +780,9 @@ int pdfi_load_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, substitute = font_from_file; if (cidfont == true) { - code = pdfi_open_CIDFont_substitute_file(ctx, font_dict, fontdesc, false, &fbuf, &fbuflen); + code = pdfi_open_CIDFont_substitute_file(ctx, font_dict, fontdesc, false, &fbuf, &fbuflen, &findex); if (code < 0) { - code = pdfi_open_CIDFont_substitute_file(ctx, font_dict, fontdesc, true, &fbuf, &fbuflen); + code = pdfi_open_CIDFont_substitute_file(ctx, font_dict, fontdesc, true, &fbuf, &fbuflen, &findex); substitute |= font_substitute; } @@ -604,9 +790,9 @@ int pdfi_load_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, goto exit; } else { - code = pdfi_open_font_substitute_file(ctx, font_dict, fontdesc, false, &fbuf, &fbuflen); + code = pdfi_open_font_substitute_file(ctx, font_dict, fontdesc, false, &fbuf, &fbuflen, &findex); if (code < 0) { - code = pdfi_open_font_substitute_file(ctx, font_dict, fontdesc, true, &fbuf, &fbuflen); + code = pdfi_open_font_substitute_file(ctx, font_dict, fontdesc, true, &fbuf, &fbuflen, &findex); substitute |= font_substitute; } @@ -646,6 +832,7 @@ int pdfi_load_dict_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_ { int code; gs_font *pfont; + pdf_font *pdfif; if (font_dict->type == PDF_FONT) { pdfi_countup(font_dict); @@ -666,10 +853,8 @@ int pdfi_load_dict_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_ if (pfont != ctx->pgs->font) { code = pdfi_gs_setfont(ctx, pfont); } - else { - pdf_font *pdfif = (pdf_font *)pfont->client_data; - pdfi_countdown(pdfif); - } + pdfif = (pdf_font *)pfont->client_data; + pdfi_countdown(pdfif); if (code < 0) goto exit; @@ -984,6 +1169,8 @@ int pdfi_d0(pdf_context *ctx) if (ctx->text.inside_CharProc == false) pdfi_set_warning(ctx, 0, NULL, W_PDF_NOTINCHARPROC, "pdfi_d0", NULL); + ctx->text.CharProc_d_type = pdf_type3_d0; + if (pdfi_count_stack(ctx) < 2) { code = gs_note_error(gs_error_stackunderflow); goto d0_error; @@ -1064,9 +1251,9 @@ int pdfi_d1(pdf_context *ctx) if (ctx->text.inside_CharProc == false) pdfi_set_warning(ctx, 0, NULL, W_PDF_NOTINCHARPROC, "pdfi_d1", NULL); - ctx->text.CharProc_is_d1 = true; + ctx->text.CharProc_d_type = pdf_type3_d1; - if (pdfi_count_stack(ctx) < 2) { + if (pdfi_count_stack(ctx) < 6) { code = gs_note_error(gs_error_stackunderflow); goto d1_error; } @@ -1188,6 +1375,29 @@ int pdfi_free_font(pdf_obj *font) return 0; } +static inline int pdfi_encoding_name_to_index(pdf_name *name) +{ + int ind = gs_error_undefined; + if (name->type == PDF_NAME) { + if (pdfi_name_is(name, "StandardEncoding")) { + ind = ENCODING_INDEX_STANDARD; + } else { + if (pdfi_name_is(name, "WinAnsiEncoding")){ + ind = ENCODING_INDEX_WINANSI; + } else { + if (pdfi_name_is(name, "MacRomanEncoding")){ + ind = ENCODING_INDEX_MACROMAN; + } else { + if (pdfi_name_is(name, "MacExpertEncoding")){ + ind = ENCODING_INDEX_MACEXPERT; + } + } + } + } + } + return ind; +} + /* * Routine to fill in an array with each of the glyph names from a given * 'standard' Encoding. @@ -1203,25 +1413,13 @@ static int pdfi_build_Encoding(pdf_context *ctx, pdf_name *name, pdf_array *Enco if (pdfi_array_size(Encoding) < 256) return gs_note_error(gs_error_rangecheck); - if (pdfi_name_is(name, "StandardEncoding")) { - gs_encoding = ENCODING_INDEX_STANDARD; - } else { - if (pdfi_name_is(name, "WinAnsiEncoding")){ - gs_encoding = ENCODING_INDEX_WINANSI; - } else { - if (pdfi_name_is(name, "MacRomanEncoding")){ - gs_encoding = ENCODING_INDEX_MACROMAN; - } else { - if (pdfi_name_is(name, "MacExpertEncoding")){ - gs_encoding = ENCODING_INDEX_MACEXPERT; - } else { - return_error(gs_error_undefined); - } - } - } - } - i = 0; - for (i=0;i<256;i++) { + code = pdfi_encoding_name_to_index(name); + if (code < 0) + return code; + gs_encoding = (unsigned char)code; + code = 0; + + for (i = 0;i<256;i++) { temp = gs_c_known_encode(i, gs_encoding); gs_c_glyph_name(temp, &str); code = pdfi_name_alloc(ctx, (byte *)str.data, str.size, (pdf_obj **)&n); @@ -1284,6 +1482,18 @@ int pdfi_create_Encoding(pdf_context *ctx, pdf_obj *pdf_Encoding, pdf_obj *font_ } else { code = pdfi_dict_get(ctx, (pdf_dict *)pdf_Encoding, "BaseEncoding", (pdf_obj **)&n); + if (code >= 0) { + if (pdfi_encoding_name_to_index(n) < 0) { + pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_FONT_BASEENC, "pdfi_create_Encoding", NULL); + pdfi_countdown(n); + n = NULL; + code = gs_error_undefined; + } + else if (pdfi_name_is(n, "StandardEncoding") == true) { + pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_FONT_BASEENC, "pdfi_create_Encoding", NULL); + } + } + if (code < 0) { code = pdfi_name_alloc(ctx, (byte *)"StandardEncoding", 16, (pdf_obj **)&n); if (code < 0) { @@ -1293,6 +1503,7 @@ int pdfi_create_Encoding(pdf_context *ctx, pdf_obj *pdf_Encoding, pdf_obj *font_ } pdfi_countup(n); } + code = pdfi_build_Encoding(ctx, n, (pdf_array *)*Encoding); if (code < 0) { pdfi_countdown(*Encoding); @@ -1375,11 +1586,81 @@ gs_glyph pdfi_encode_char(gs_font * pfont, gs_char chr, gs_glyph_space_t not_use return g; } +int pdfi_tounicode_char_to_unicode(pdf_context *ctx, pdf_cmap *tounicode, gs_glyph glyph, int ch, ushort *unicode_return, unsigned int length) +{ + int i, l = 0; + int code = gs_error_undefined; + unsigned char *ucode = (unsigned char *)unicode_return; + + if (tounicode != NULL) { + gs_cmap_lookups_enum_t lenum; + gs_cmap_lookups_enum_init((const gs_cmap_t *)tounicode->gscmap, 0, &lenum); + while (l == 0 && (code = gs_cmap_enum_next_lookup(ctx->memory, &lenum)) == 0) { + gs_cmap_lookups_enum_t counter = lenum; + while (l == 0 && (code = gs_cmap_enum_next_entry(&counter) == 0)) { + if (counter.entry.value_type == CODE_VALUE_CID) { + unsigned int v = 0; + for (i = 0; i < counter.entry.key_size; i++) { + v |= (counter.entry.key[0][counter.entry.key_size - i - 1]) << (i * 8); + } + if (ch == v) { + if (counter.entry.value.size == 1) { + l = 2; + if (ucode != NULL && length >= l) { + ucode[0] = counter.entry.value.data[0]; + ucode[1] = counter.entry.value.data[1]; + } + } + else if (counter.entry.value.size == 2) { + l = 2; + if (ucode != NULL && length >= l) { + ucode[0] = counter.entry.value.data[0]; + ucode[1] = counter.entry.value.data[1]; + } + } + else if (counter.entry.value.size == 3) { + l = 4; + if (ucode != NULL && length >= l) { + ucode[0] = counter.entry.value.data[0]; + ucode[1] = counter.entry.value.data[1]; + ucode[2] = counter.entry.value.data[2]; + ucode[3] = 0; + } + } + else { + l = 4; + if (ucode != NULL && length >= l) { + ucode[0] = counter.entry.value.data[0]; + ucode[1] = counter.entry.value.data[1]; + ucode[2] = counter.entry.value.data[1]; + ucode[3] = counter.entry.value.data[3]; + } + } + } + } + } + } + if (l > 0) + code = l; + } + + return code; +} + /* Get the unicode valude for a glyph FIXME - not written yet */ int pdfi_decode_glyph(gs_font * font, gs_glyph glyph, int ch, ushort *unicode_return, unsigned int length) { - return 0; + pdf_font *pdffont = (pdf_font *)font->client_data; + int code = 0; + + if (pdffont->pdfi_font_type != e_pdf_cidfont_type0 && pdffont->pdfi_font_type != e_pdf_cidfont_type1 + && pdffont->pdfi_font_type != e_pdf_cidfont_type2 && pdffont->pdfi_font_type != e_pdf_cidfont_type4) { + code = pdfi_tounicode_char_to_unicode(pdffont->ctx, (pdf_cmap *)pdffont->ToUnicode, glyph, ch, unicode_return, length); + } + if (code < 0) code = 0; + + return code; } int pdfi_glyph_index(gs_font *pfont, byte *str, uint size, uint *glyph) @@ -1562,8 +1843,7 @@ static int pdfi_font_set_internal_inner(pdf_context *ctx, const byte *fontname, code = pdfi_set_font_internal(ctx, font, point_size); exit: - if (code < 0) - pdfi_countdown(font); /* Keep the ref if succeeded */ + pdfi_countdown(font); return code; } diff --git a/pdf/pdf_font.h b/pdf/pdf_font.h index 381ba729..da76aeb8 100644 --- a/pdf/pdf_font.h +++ b/pdf/pdf_font.h @@ -61,6 +61,7 @@ int pdfi_create_Encoding(pdf_context *ctx, pdf_obj *pdf_Encoding, pdf_obj *font_ gs_glyph pdfi_encode_char(gs_font * pfont, gs_char chr, gs_glyph_space_t not_used); int pdfi_glyph_index(gs_font *pfont, byte *str, uint size, uint *glyph); int pdfi_glyph_name(gs_font * pfont, gs_glyph glyph, gs_const_string * pstr); +int pdfi_tounicode_char_to_unicode(pdf_context *ctx, pdf_cmap *tounicode, gs_glyph glyph, int ch, ushort *unicode_return, unsigned int length); int pdfi_decode_glyph(gs_font * font, gs_glyph glyph, int ch, ushort *unicode_return, unsigned int length); /* This is in pdf_fapi.c, but since it is the only exported function diff --git a/pdf/pdf_font0.c b/pdf/pdf_font0.c index 86baec8d..a322bfb4 100644 --- a/pdf/pdf_font0.c +++ b/pdf/pdf_font0.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -77,7 +77,6 @@ pdfi_font0_map_glyph_to_unicode(gs_font *font, gs_glyph glyph, int ch, ushort *u int code = gs_error_undefined, i; uchar *unicode_return = (uchar *)u; pdf_cidfont_type2 *decfont = NULL; - pdf_cmap *tounicode = (pdf_cmap *)pt0font->ToUnicode; pdfi_cid_subst_nwp_table_t *substnwp = pt0font->substnwp; code = pdfi_array_get(pt0font->ctx, pt0font->DescendantFonts, 0, (pdf_obj **)&decfont); @@ -89,63 +88,7 @@ pdfi_font0_map_glyph_to_unicode(gs_font *font, gs_glyph glyph, int ch, ushort *u code = gs_error_undefined; while (1) { /* Loop to make retrying with a substitute CID easier */ /* Favour the ToUnicode if one exists */ - if (tounicode) { - int l = 0; - gs_cmap_lookups_enum_t lenum; - gs_cmap_lookups_enum_init((const gs_cmap_t *)tounicode->gscmap, 0, &lenum); - while (l == 0 && (code = gs_cmap_enum_next_lookup(font->memory, &lenum)) == 0) { - gs_cmap_lookups_enum_t counter = lenum; - while (l == 0 && (code = gs_cmap_enum_next_entry(&counter) == 0)) { - if (counter.entry.value_type == CODE_VALUE_CID) { - unsigned int v = 0; - for (i = 0; i < counter.entry.key_size; i++) { - v |= (counter.entry.key[0][counter.entry.key_size - i - 1]) << (i * 8); - } - if (ch == v) { - if (counter.entry.value.size == 1) { - l = 2; - if (unicode_return != NULL && length >= l) { - unicode_return[0] = counter.entry.value.data[0]; - unicode_return[1] = counter.entry.value.data[1]; - } - } - else if (counter.entry.value.size == 2) { - l = 2; - if (unicode_return != NULL && length >= l) { - unicode_return[0] = counter.entry.value.data[0]; - unicode_return[1] = counter.entry.value.data[1]; - } - } - else if (counter.entry.value.size == 3) { - l = 4; - if (unicode_return != NULL && length >= l) { - unicode_return[0] = counter.entry.value.data[0]; - unicode_return[1] = counter.entry.value.data[1]; - unicode_return[2] = counter.entry.value.data[2]; - unicode_return[3] = 0; - } - } - else { - l = 4; - if (unicode_return != NULL && length >= l) { - unicode_return[0] = counter.entry.value.data[0]; - unicode_return[1] = counter.entry.value.data[1]; - unicode_return[2] = counter.entry.value.data[1]; - unicode_return[3] = counter.entry.value.data[3]; - } - } - } - } - else { - l = 0; - } - } - } - if (l > 0) - code = l; - else - code = gs_error_undefined; - } + code = pdfi_tounicode_char_to_unicode(pt0font->ctx, (pdf_cmap *)pt0font->ToUnicode, glyph, ch, u, length); if (code == gs_error_undefined && pt0font->decoding) { const int *n; @@ -310,17 +253,22 @@ int pdfi_read_type0_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream basefont = NULL; } - code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tounicode); - if (code >= 0 && tounicode->type == PDF_STREAM) { - pdf_cmap *tu = NULL; - code = pdfi_read_cmap(ctx, tounicode, &tu); - pdfi_countdown(tounicode); - tounicode = (pdf_obj *)tu; + if (ctx->args.ignoretounicode != true) { + code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tounicode); + if (code >= 0 && tounicode->type == PDF_STREAM) { + pdf_cmap *tu = NULL; + code = pdfi_read_cmap(ctx, tounicode, &tu); + pdfi_countdown(tounicode); + tounicode = (pdf_obj *)tu; + } + if (code < 0 || (tounicode != NULL && tounicode->type != PDF_CMAP)) { + pdfi_countdown(tounicode); + tounicode = NULL; + code = 0; + } } - if (code < 0 || (tounicode != NULL && tounicode->type != PDF_CMAP)) { - pdfi_countdown(tounicode); + else { tounicode = NULL; - code = 0; } if (descpfont == NULL) { @@ -332,6 +280,11 @@ int pdfi_read_type0_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream descpfont = (pdf_font *)pf->client_data; } + if (descpfont->pdfi_font_type < e_pdf_cidfont_type0 || descpfont->pdfi_font_type > e_pdf_cidfont_type4) { + code = gs_note_error(gs_error_invalidfont); + goto error; + } + if (descpfont != NULL && ((pdf_cidfont_t *)descpfont)->substitute) { pdf_obj *csi = NULL; pdf_string *reg = NULL, *ord = NULL; @@ -342,7 +295,8 @@ int pdfi_read_type0_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream if (code >= 0) { (void)pdfi_dict_get(ctx, (pdf_dict *)csi, "Registry", (pdf_obj **)®); (void)pdfi_dict_get(ctx, (pdf_dict *)csi, "Ordering", (pdf_obj **)&ord); - if (reg != NULL && ord != NULL) { + if (reg != NULL && reg->type == PDF_STRING + && ord != NULL && ord->type == PDF_STRING) { r = (char *)reg->data; rlen = reg->length; o = (char *)ord->data; @@ -404,6 +358,7 @@ int pdfi_read_type0_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream pdft0->indirect_gen = font_dict->indirect_gen; pdft0->Encoding = (pdf_obj *)pcmap; pdft0->ToUnicode = tounicode; + tounicode = NULL; pdft0->DescendantFonts = arr; pdft0->PDF_font = font_dict; pdfi_countup(font_dict); @@ -537,12 +492,7 @@ int pdfi_read_type0_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream /* object_num can be zero if the dictionary was defined inline */ if (pdft0->object_num != 0) { - code = replace_cache_entry(ctx, (pdf_obj *)pdft0); - if (code < 0) { - gs_free_object(ctx->memory, pfont0, "pdfi_read_type0_font(pfont0)"); - code = gs_note_error(gs_error_VMerror); - goto error; - } + (void)replace_cache_entry(ctx, (pdf_obj *)pdft0); } *ppdffont = (pdf_font *)pdft0; diff --git a/pdf/pdf_font0.h b/pdf/pdf_font0.h index a8a76c60..f033fbf5 100644 --- a/pdf/pdf_font0.h +++ b/pdf/pdf_font0.h @@ -26,7 +26,7 @@ int pdfi_read_type0_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, pdf_font **ppdffont); int pdfi_free_font_type0(pdf_obj *font); -int pdfi_read_cidtype2_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, byte *buf, int64_t buflen, pdf_font **ppfont); +int pdfi_read_cidtype2_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, byte *buf, int64_t buflen, int findex, pdf_font **ppfont); int pdfi_free_font_cidtype2(pdf_obj *font); diff --git a/pdf/pdf_font1.c b/pdf/pdf_font1.c index 6586d891..87ba858b 100644 --- a/pdf/pdf_font1.c +++ b/pdf/pdf_font1.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -506,6 +506,7 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic pdf_obj *mapname = NULL; pdf_obj *tmp = NULL; pdf_font_type1 *t1f = NULL; + pdf_obj *tounicode = NULL; ps_font_interp_private fpriv = { 0 }; (void)pdfi_dict_knownget_type(ctx, font_dict, "FontDescriptor", PDF_DICT, &fontdesc); @@ -556,6 +557,9 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic t1f->object_num = font_dict->object_num; t1f->generation_num = font_dict->generation_num; + t1f->indirect_num = font_dict->indirect_num; + t1f->indirect_gen = font_dict->indirect_gen; + t1f->PDF_font = font_dict; pdfi_countup(font_dict); t1f->BaseFont = basefont; @@ -584,6 +588,26 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic t1f->descflags |= 4; } + if (ctx->args.ignoretounicode != true) { + code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tounicode); + if (code >= 0 && tounicode->type == PDF_STREAM) { + pdf_cmap *tu = NULL; + code = pdfi_read_cmap(ctx, tounicode, &tu); + pdfi_countdown(tounicode); + tounicode = (pdf_obj *)tu; + } + if (code < 0 || (tounicode != NULL && tounicode->type != PDF_CMAP)) { + pdfi_countdown(tounicode); + tounicode = NULL; + code = 0; + } + } + else { + tounicode = NULL; + } + t1f->ToUnicode = tounicode; + tounicode = NULL; + code = pdfi_dict_knownget_type(ctx, font_dict, "FirstChar", PDF_INT, &tmp); if (code == 1) { t1f->FirstChar = ((pdf_num *) tmp)->value.i; @@ -683,14 +707,6 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic pdfi_countup(t1f->Encoding); } - code = pdfi_dict_knownget(ctx, font_dict, "ToUnicode", &tmp); - if (code == 1) { - t1f->ToUnicode = tmp; - tmp = NULL; - } - else { - t1f->ToUnicode = NULL; - } t1f->CharStrings = fpriv.u.t1.CharStrings; pdfi_countup(t1f->CharStrings); pdfi_patch_charstrings_dict(t1f->CharStrings); @@ -719,9 +735,7 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic } /* object_num can be zero if the dictionary was defined inline */ if (t1f->object_num != 0) { - code = replace_cache_entry(ctx, (pdf_obj *) t1f); - if (code < 0) - goto error; + (void)replace_cache_entry(ctx, (pdf_obj *) t1f); } *ppdffont = (pdf_font *) t1f; } @@ -730,6 +744,7 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic error: pdfi_countdown(fontdesc); pdfi_countdown(basefont); + pdfi_countdown(tounicode); pdfi_countdown(mapname); pdfi_countdown(tmp); pdfi_countdown(fpriv.u.t1.Encoding); @@ -761,9 +776,6 @@ pdfi_free_font_type1(pdf_obj *font) pdf_font_type1 *t1f = (pdf_font_type1 *) font; int i; - if (t1f->pfont->UID.xvalues != NULL) { - gs_free_object(OBJ_MEMORY(font), t1f->pfont->UID.xvalues, "pdfi_free_font_type1(xuid)"); - } gs_free_object(OBJ_MEMORY(font), t1f->pfont, "Free Type 1 gs_font"); pdfi_countdown(t1f->PDF_font); diff --git a/pdf/pdf_font11.c b/pdf/pdf_font11.c index ebe0cd21..09712f60 100644 --- a/pdf/pdf_font11.c +++ b/pdf/pdf_font11.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2021 Artifex Software, Inc. +/* Copyright (C) 2020-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -276,7 +276,7 @@ pdfi_alloc_cidtype2_font(pdf_context *ctx, pdf_cidfont_type2 **font, bool is_cid return 0; } -int pdfi_read_cidtype2_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, byte *buf, int64_t buflen, pdf_font **ppfont) +int pdfi_read_cidtype2_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, byte *buf, int64_t buflen, int findex, pdf_font **ppfont) { pdf_cidfont_type2 *font; int code = 0; @@ -306,6 +306,9 @@ int pdfi_read_cidtype2_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str pdfi_countup(font_dict); font->object_num = font_dict->object_num; font->generation_num = font_dict->generation_num; + font->indirect_num = font_dict->indirect_num; + font->indirect_gen = font_dict->indirect_gen; + font->FontDescriptor = (pdf_dict *)fontdesc; fontdesc = NULL; @@ -382,6 +385,48 @@ int pdfi_read_cidtype2_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str obj = NULL; } + cid2 = (gs_font_cid2 *)font->pfont; + + code = pdfi_dict_knownget_type(ctx, font_dict, "CIDSystemInfo", PDF_DICT, (pdf_obj **)&obj); + if (code <= 0) { + cid2->cidata.common.CIDSystemInfo.Registry.data = NULL; + cid2->cidata.common.CIDSystemInfo.Registry.size = 0; + cid2->cidata.common.CIDSystemInfo.Ordering.data = NULL; + cid2->cidata.common.CIDSystemInfo.Ordering.size = 0; + } + else { + pdf_num *suppl = NULL; + + code = pdfi_dict_knownget_type(ctx, (pdf_dict *)obj, "Registry", PDF_STRING, (pdf_obj **)&font->registry); + if (code <= 0) { + cid2->cidata.common.CIDSystemInfo.Registry.data = NULL; + cid2->cidata.common.CIDSystemInfo.Registry.size = 0; + } + else { + cid2->cidata.common.CIDSystemInfo.Registry.data = font->registry->data; + cid2->cidata.common.CIDSystemInfo.Registry.size = font->registry->length; + } + code = pdfi_dict_knownget_type(ctx, (pdf_dict *)obj, "Ordering", PDF_STRING, (pdf_obj **)&font->ordering); + if (code <= 0) { + cid2->cidata.common.CIDSystemInfo.Ordering.data = NULL; + cid2->cidata.common.CIDSystemInfo.Ordering.size = 0; + } + else { + cid2->cidata.common.CIDSystemInfo.Ordering.data = font->ordering->data; + cid2->cidata.common.CIDSystemInfo.Ordering.size = font->ordering->length; + } + code = pdfi_dict_knownget_type(ctx, (pdf_dict *)obj, "Supplement", PDF_INT, (pdf_obj **)&suppl); + if (code <= 0) { + cid2->cidata.common.CIDSystemInfo.Supplement = font->supplement = 0; + } + else { + cid2->cidata.common.CIDSystemInfo.Supplement = font->supplement = suppl->value.i; + } + pdfi_countdown(suppl); + } + pdfi_countdown(obj); + obj = NULL; + code = gs_type42_font_init((gs_font_type42 *)font->pfont, 0); if (code < 0) { goto error; @@ -390,7 +435,6 @@ int pdfi_read_cidtype2_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str font->pfont->procs.glyph_info = pdfi_cidtype2_glyph_info; font->pfont->procs.enumerate_glyph = pdfi_cidtype2_enumerate_glyph; - cid2 = (gs_font_cid2 *)font->pfont; if (font->cidtogidmap.size > 0) { gs_font_cid2 *cid2 = (gs_font_cid2 *)font->pfont; if (cid2->data.numGlyphs > font->cidtogidmap.size >> 1) @@ -425,9 +469,7 @@ int pdfi_read_cidtype2_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str /* object_num can be zero if the dictionary was defined inline */ if (font->object_num != 0) { - code = replace_cache_entry(ctx, (pdf_obj *)font); - if (code < 0) - goto error; + (void)replace_cache_entry(ctx, (pdf_obj *)font); } *ppfont = (pdf_font *)font; @@ -454,6 +496,8 @@ int pdfi_free_font_cidtype2(pdf_obj *font) pdfi_countdown(pdfcidf->W); pdfi_countdown(pdfcidf->DW2); pdfi_countdown(pdfcidf->W2); + pdfi_countdown(pdfcidf->registry); + pdfi_countdown(pdfcidf->ordering); gs_free_object(OBJ_MEMORY(pdfcidf), pdfcidf, "pdfi_free_font_cidtype2(pdfcidf)"); return 0; diff --git a/pdf/pdf_font1C.c b/pdf/pdf_font1C.c index 10570912..f4788845 100644 --- a/pdf/pdf_font1C.c +++ b/pdf/pdf_font1C.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -128,7 +128,7 @@ pdfi_cff_glyph_data(gs_font_type1 *pfont, gs_glyph glyph, gs_glyph_data_t *pgd) */ if (cfffont->Encoding == NULL) { char indstring[33]; - int l = gs_snprintf(indstring, 32, "%u", (unsigned int)glyph); + int l = gs_snprintf(indstring, sizeof(indstring), "%u", (unsigned int)glyph); code = pdfi_name_alloc(ctx, (byte *) indstring, l, (pdf_obj **) &glyphname); if (code >= 0) @@ -431,18 +431,21 @@ pdfi_cff_cid_glyph_data(gs_font_base *pbfont, gs_glyph glyph, gs_glyph_data_t *p gid = pdffont9->cidtogidmap.data[gid << 1] << 8 | pdffont9->cidtogidmap.data[(gid << 1) + 1]; } - l = snprintf(nbuf, 64, "%" PRId64, gid); + l = gs_snprintf(nbuf, sizeof(nbuf), "%" PRId64, gid); code = pdfi_name_alloc(pdffont9->ctx, (byte *) nbuf, l, (pdf_obj **) &glyphname); if (code >= 0) { pdfi_countup(glyphname); code = pdfi_dict_get_by_key(pdffont9->ctx, pdffont9->CharStrings, glyphname, (pdf_obj **) &charstring); - if (code >= 0 && charstring->length > 1) { - if (gscidfont->cidata.FDBytes == 0) - *pfidx = 0; - else - *pfidx = (int)charstring->data[0]; - if (pgd) + if (code >= 0 && charstring->length >= gscidfont->cidata.FDBytes) { + if (gscidfont->cidata.FDBytes != 0) { + if ((int)charstring->data[0] > gscidfont->cidata.FDArray_size) + code = gs_note_error(gs_error_invalidfont); + else + *pfidx = (int)charstring->data[0]; + } + + if (code >= 0 && pgd && ((int64_t)charstring->length - (int64_t)gscidfont->cidata.FDBytes) >= 0) gs_glyph_data_from_bytes(pgd, charstring->data + gscidfont->cidata.FDBytes, 0, charstring->length - gscidfont->cidata.FDBytes, NULL); } } @@ -452,6 +455,7 @@ pdfi_cff_cid_glyph_data(gs_font_base *pbfont, gs_glyph glyph, gs_glyph_data_t *p return code; } + static int pdfi_cff_cidfont_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat, int members, gs_glyph_info_t *info) @@ -541,7 +545,6 @@ u32(const byte *p) } - static int subrbias(int count) { @@ -787,15 +790,17 @@ pdfi_read_cff_integer(byte *p, byte *e, int b0, int *val) return p; } +#define PDFI_CFF_STACK_SIZE 48 + static int -pdfi_read_cff_dict(byte *p, byte *e, pdfi_gs_cff_font_priv *ptpriv, cff_font_offsets *offsets) +pdfi_read_cff_dict(byte *p, byte *e, pdfi_gs_cff_font_priv *ptpriv, cff_font_offsets *offsets, bool topdict) { pdfi_cff_font_priv *font = &ptpriv->pdfcffpriv; struct { int ival; float fval; - } args[48]; + } args[PDFI_CFF_STACK_SIZE]; int offset; int b0, n; double f; @@ -808,7 +813,7 @@ pdfi_read_cff_dict(byte *p, byte *e, pdfi_gs_cff_font_priv *ptpriv, cff_font_off offset = p - font->cffdata; n = 0; - while (p < e) { + while (p < e && code >= 0) { b0 = *p++; switch (b0) { @@ -832,186 +837,288 @@ pdfi_read_cff_dict(byte *p, byte *e, pdfi_gs_cff_font_priv *ptpriv, cff_font_off } b0 = 0x100 | *p++; } - if (b0 == 13) { /* UniqueID */ - } - - if (b0 == 14) { /* XUID */ - } + switch (b0) { + case 13: /* UniqueID */ + break; - if (b0 == 15) { - offsets->charset_off = args[0].ival; - } + case 14: + break; /* XUID */ - if (b0 == 16) { - offsets->encoding_off = args[0].ival; - } + /* some CFF file offsets */ + case 15: + { + if (args[0].ival < 0) { + code = gs_note_error(gs_error_invalidfont); + break; + } + offsets->charset_off = args[0].ival; + break; + } + case 16: + { + if (args[0].ival < 0) { + code = gs_note_error(gs_error_invalidfont); + break; + } + offsets->encoding_off = args[0].ival; + break; + } + case 17: + { + if (args[0].ival < 0) { + code = gs_note_error(gs_error_invalidfont); + break; + } + font->charstrings = font->cffdata + args[0].ival; + break; + } - /* some CFF file offsets */ + case 18: + { + offsets->private_size = args[0].ival; + if (args[1].ival < 0) { + code = gs_note_error(gs_error_invalidfont); + break; + } + offsets->private_off = args[1].ival; + /* Catch a broken font with a self referencing Private dict */ + if (topdict == true) + do_priv = offsets->private_size > 0 ? true : false; + else { + do_priv = false; + code = gs_error_invalidfont; + break; + } + break; + } - if (b0 == 17) { - font->charstrings = font->cffdata + args[0].ival; - } + case 19: + { + if (args[0].ival < 0) { + code = gs_note_error(gs_error_invalidfont); + break; + } + font->subrs = font->cffdata + offset + args[0].ival; + break; + } - if (b0 == 18) { - offsets->private_size = args[0].ival; - offsets->private_off = args[1].ival; - do_priv = offsets->private_size > 0 ? true : false; - } + case 256 | 30: + { + code = pdfi_make_string_from_sid(font->ctx, (pdf_obj **) &font->registry, font, offsets, args[0].ival); + if (code < 0) + break; + code = pdfi_make_string_from_sid(font->ctx, (pdf_obj **) &font->ordering, font, offsets, args[1].ival); + if (code < 0) + break; + font->supplement = args[2].ival; + offsets->have_ros = true; + ptpriv->FontType = ft_CID_encrypted; + break; + } - if (b0 == 19) { - font->subrs = font->cffdata + offset + args[0].ival; - } + case 256 | 34: + { + font->cidcount = args[0].ival; + break; + } - if (b0 == (256 | 30)) { - code = pdfi_make_string_from_sid(font->ctx, (pdf_obj **) &font->registry, font, offsets, args[0].ival); - if (code < 0) - return code; - code = pdfi_make_string_from_sid(font->ctx, (pdf_obj **) &font->ordering, font, offsets, args[1].ival); - if (code < 0) - return code; - font->supplement = args[2].ival; - offsets->have_ros = true; - ptpriv->FontType = ft_CID_encrypted; - } + case 256 | 35: + { + font->uidbase = args[0].ival; + break; + } - if (b0 == (256 | 34)) { - font->cidcount = args[0].ival; - } + case 256 | 36: + { + offsets->fdarray_off = args[0].ival; + break; + } - if (b0 == (256 | 35)) { - font->uidbase = args[0].ival; - } + case 256 | 37: + { + offsets->fdselect_off = args[0].ival; + break; + } - if (b0 == (256 | 36)) { - offsets->fdarray_off = args[0].ival; - } + case 256 | 38: + { + pdf_string *fnamestr = NULL; - if (b0 == (256 | 37)) { - offsets->fdselect_off = args[0].ival; - } + code = pdfi_make_string_from_sid(font->ctx, (pdf_obj **) &fnamestr, font, offsets, args[0].ival); + if (code >= 0) { + memcpy(ptpriv->font_name.chars, fnamestr->data, fnamestr->length); + memcpy(ptpriv->key_name.chars, fnamestr->data, fnamestr->length); + ptpriv->font_name.size = ptpriv->key_name.size = fnamestr->length; + pdfi_countdown(fnamestr); + } + break; + } - if (b0 == (256 | 38)) { - pdf_string *fnamestr = NULL; + /* Type1 stuff that need to be set for the ptpriv struct */ - code = pdfi_make_string_from_sid(font->ctx, (pdf_obj **) &fnamestr, font, offsets, args[0].ival); - if (code >= 0) { - memcpy(ptpriv->font_name.chars, fnamestr->data, fnamestr->length); - memcpy(ptpriv->key_name.chars, fnamestr->data, fnamestr->length); - ptpriv->font_name.size = ptpriv->key_name.size = fnamestr->length; - pdfi_countdown(fnamestr); + case 256 | 6: + { + if (args[0].ival == 1) { + ptpriv->type1data.interpret = gs_type1_interpret; + ptpriv->type1data.lenIV = -1; /* FIXME */ + } + break; } - } - /* Type1 stuff that need to be set for the ptpriv struct */ + case 256 | 7: + { + ptpriv->FontMatrix.xx = args[0].fval; + ptpriv->FontMatrix.xy = args[1].fval; + ptpriv->FontMatrix.yx = args[2].fval; + ptpriv->FontMatrix.yy = args[3].fval; + ptpriv->FontMatrix.tx = args[4].fval; + ptpriv->FontMatrix.ty = args[5].fval; + offsets->have_matrix = true; + break; + } + case 5: + { + ptpriv->FontBBox.p.x = args[0].fval; + ptpriv->FontBBox.p.y = args[1].fval; + ptpriv->FontBBox.q.x = args[2].fval; + ptpriv->FontBBox.q.y = args[3].fval; + break; + } - if (b0 == (256 | 6)) { - if (args[0].ival == 1) { - ptpriv->type1data.interpret = gs_type1_interpret; - ptpriv->type1data.lenIV = -1; /* FIXME */ + case 20: + { + ptpriv->type1data.defaultWidthX = float2fixed(args[0].fval); + break; } - } - if (b0 == (256 | 7)) { - ptpriv->FontMatrix.xx = args[0].fval; - ptpriv->FontMatrix.xy = args[1].fval; - ptpriv->FontMatrix.yx = args[2].fval; - ptpriv->FontMatrix.yy = args[3].fval; - ptpriv->FontMatrix.tx = args[4].fval; - ptpriv->FontMatrix.ty = args[5].fval; - offsets->have_matrix = true; - } + case 21: + { + ptpriv->type1data.nominalWidthX = float2fixed(args[0].fval); + break; + } - if (b0 == 5) { - ptpriv->FontBBox.p.x = args[0].fval; - ptpriv->FontBBox.p.y = args[1].fval; - ptpriv->FontBBox.q.x = args[2].fval; - ptpriv->FontBBox.q.y = args[3].fval; - } + case 256 | 19: + { + ptpriv->type1data.initialRandomSeed = args[0].ival; + break; + } - if (b0 == 20) - ptpriv->type1data.defaultWidthX = float2fixed(args[0].fval); + case 6: + { + if (n > max_BlueValues * 2) n = max_BlueValues * 2; + ptpriv->type1data.BlueValues.count = n; + ptpriv->type1data.BlueValues.values[0] = args[0].fval; + for (i = 1; i < n; i++) { + ptpriv->type1data.BlueValues.values[i] = ptpriv->type1data.BlueValues.values[i - 1] + args[i].fval; + } + break; + } - if (b0 == 21) - ptpriv->type1data.nominalWidthX = float2fixed(args[0].fval); + case 7: + { + if (n > max_OtherBlues * 2) n = max_OtherBlues * 2; + ptpriv->type1data.OtherBlues.count = n; + ptpriv->type1data.OtherBlues.values[0] = args[0].fval; + for (i = 1; i < n; i++) { + ptpriv->type1data.OtherBlues.values[i] = ptpriv->type1data.OtherBlues.values[i - 1] + args[i].fval; + } + break; + } - if (b0 == (256 | 19)) - ptpriv->type1data.initialRandomSeed = args[0].ival; + case 8: + { + if (n > max_FamilyBlues * 2) n = max_FamilyBlues * 2; + ptpriv->type1data.FamilyBlues.count = n; + ptpriv->type1data.FamilyBlues.values[0] = args[0].fval; + for (i = 1; i < n; i++) { + ptpriv->type1data.FamilyBlues.values[i] = ptpriv->type1data.FamilyBlues.values[i - 1] + args[i].fval; + } + break; + } - if (b0 == 6) { - ptpriv->type1data.BlueValues.count = n; - ptpriv->type1data.BlueValues.values[0] = args[0].fval; - for (i = 1; i < n; i++) { - ptpriv->type1data.BlueValues.values[i] = ptpriv->type1data.BlueValues.values[i - 1] + args[i].fval; + case 9: + { + if (n > max_FamilyOtherBlues * 2) n = max_FamilyOtherBlues * 2; + ptpriv->type1data.FamilyOtherBlues.count = n; + ptpriv->type1data.FamilyOtherBlues.values[0] = args[0].fval; + for (i = 1; i < n; i++) { + ptpriv->type1data.FamilyOtherBlues.values[i] = ptpriv->type1data.FamilyOtherBlues.values[i - 1] + args[i].fval; + } + break; } - } - if (b0 == 7) { - ptpriv->type1data.OtherBlues.count = n; - ptpriv->type1data.OtherBlues.values[0] = args[0].fval; - for (i = 1; i < n; i++) { - ptpriv->type1data.OtherBlues.values[i] = ptpriv->type1data.OtherBlues.values[i - 1] + args[i].fval; + case 10: + { + ptpriv->type1data.StdHW.count = 1; + ptpriv->type1data.StdHW.values[0] = args[0].fval; + break; } - } - if (b0 == 8) { - ptpriv->type1data.FamilyBlues.count = n; - ptpriv->type1data.FamilyBlues.values[0] = args[0].fval; - for (i = 1; i < n; i++) { - ptpriv->type1data.FamilyBlues.values[i] = ptpriv->type1data.FamilyBlues.values[i - 1] + args[i].fval; + case 11: + { + ptpriv->type1data.StdVW.count = 1; + ptpriv->type1data.StdVW.values[0] = args[0].fval; + break; } - } - if (b0 == 9) { - ptpriv->type1data.FamilyOtherBlues.count = n; - ptpriv->type1data.FamilyOtherBlues.values[0] = args[0].fval; - for (i = 1; i < n; i++) { - ptpriv->type1data.FamilyOtherBlues.values[i] = ptpriv->type1data.FamilyOtherBlues.values[i - 1] + args[i].fval; + case 256 | 9: + { + ptpriv->type1data.BlueScale = args[0].fval; + break; } - } - if (b0 == 10) { - ptpriv->type1data.StdHW.count = 1; - ptpriv->type1data.StdHW.values[0] = args[0].fval; - } + case 256 | 10: + { + ptpriv->type1data.BlueShift = args[0].fval; + break; + } - if (b0 == 11) { - ptpriv->type1data.StdVW.count = 1; - ptpriv->type1data.StdVW.values[0] = args[0].fval; - } + case 256 | 11: + { + ptpriv->type1data.BlueFuzz = (int)args[0].fval; + break; + } - if (b0 == (256 | 9)) - ptpriv->type1data.BlueScale = args[0].fval; + case 256 | 12: + { + if (n > max_StemSnap) n = max_StemSnap; + ptpriv->type1data.StemSnapH.count = n; + for (f = 0, i = 0; i < n; f += args[i].fval, i++) + ptpriv->type1data.StemSnapH.values[i] = f; + break; + } - if (b0 == (256 | 10)) - ptpriv->type1data.BlueShift = args[0].fval; + case 256 | 13: + { + if (n > max_StemSnap) n = max_StemSnap; + ptpriv->type1data.StemSnapV.count = n; + for (f = 0, i = 0; i < n; f += args[i].fval, i++) + ptpriv->type1data.StemSnapV.values[i] = f; + break; + } - if (b0 == (256 | 11)) - ptpriv->type1data.BlueFuzz = (int)args[0].fval; + case 256 | 14: + { + ptpriv->type1data.ForceBold = args[0].ival; + break; + } - if (b0 == (256 | 12)) { - ptpriv->type1data.StemSnapH.count = n; - for (f = 0, i = 0; i < n; f += args[i].fval, i++) - ptpriv->type1data.StemSnapH.values[i] = f; - } + case 256 | 17: + { + ptpriv->type1data.LanguageGroup = args[0].ival; + break; + } - if (b0 == (256 | 13)) { - ptpriv->type1data.StemSnapV.count = n; - for (f = 0, i = 0; i < n; f += args[i].fval, i++) - ptpriv->type1data.StemSnapV.values[i] = f; + case 256 | 18: + { + ptpriv->type1data.ExpansionFactor = args[0].fval; + break; + } + default: + break; } - - if (b0 == (256 | 14)) - ptpriv->type1data.ForceBold = args[0].ival; - - if (b0 == (256 | 17)) - ptpriv->type1data.LanguageGroup = args[0].ival; - - if (b0 == (256 | 18)) - ptpriv->type1data.ExpansionFactor = args[0].fval; - n = 0; } - else { if (b0 == 30) { p = pdfi_read_cff_real(p, e, &args[n].fval); @@ -1041,6 +1148,10 @@ pdfi_read_cff_dict(byte *p, byte *e, pdfi_gs_cff_font_priv *ptpriv, cff_font_off dmprintf1(ptpriv->memory, "CFF: corrupt dictionary operand (b0 = %d)", b0); } } + if (n >= PDFI_CFF_STACK_SIZE) { + code = gs_error_invalidfont; + break; + } } /* recurse for the private dictionary */ @@ -1053,7 +1164,7 @@ pdfi_read_cff_dict(byte *p, byte *e, pdfi_gs_cff_font_priv *ptpriv, cff_font_off if (p == NULL) code = gs_error_invalidfont; else - code = pdfi_read_cff_dict(font->cffdata + offsets->private_off, dend, ptpriv, offsets); + code = pdfi_read_cff_dict(font->cffdata + offsets->private_off, dend, ptpriv, offsets, false); if (code < 0) dmprintf(ptpriv->memory, "CFF: cannot read private dictionary"); @@ -1101,6 +1212,11 @@ pdfi_count_cff_index(byte *p, byte *e, int *countp) p += offsize; p--; /* stupid offsets */ + if (last < 0) { + gs_throw(-1, "corrupt index"); + return 0; + } + if (p + last > e) { gs_throw(-1, "not enough data for index data"); return 0; @@ -1268,79 +1384,84 @@ pdfi_cff_build_encoding(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv, cff_fon } } else { - code = pdfi_object_alloc(ctx, PDF_ARRAY, 256, (pdf_obj **) &font->Encoding); - if (code < 0) - return code; + if (font->cffdata + offsets->encoding_off > font->cffend) { + code = gs_note_error(gs_error_invalidfont); + } + else { + code = pdfi_object_alloc(ctx, PDF_ARRAY, 256, (pdf_obj **) &font->Encoding); + if (code < 0) + return code; - code = pdfi_name_alloc(ctx, (byte *) ".notdef", 7, (pdf_obj **) &ndname); - if (code < 0) - return code; + code = pdfi_name_alloc(ctx, (byte *) ".notdef", 7, (pdf_obj **) &ndname); + if (code < 0) + return code; - pdfi_countup(font->Encoding); - pdfi_countup(ndname); - code = 0; - /* Prepopulate with notdefs */ - for (i = 0; i < 256 && code >= 0; i++) { - code = pdfi_array_put(ctx, font->Encoding, (uint64_t) i, (pdf_obj *) ndname); - } + pdfi_countup(font->Encoding); + pdfi_countup(ndname); + code = 0; + /* Prepopulate with notdefs */ + for (i = 0; i < 256 && code >= 0; i++) { + code = pdfi_array_put(ctx, font->Encoding, (uint64_t) i, (pdf_obj *) ndname); + } - if (code >= 0) { - byte *p = font->cffdata + offsets->encoding_off; + if (code >= 0) { + byte *p = font->cffdata + offsets->encoding_off; - enc_format = p[0]; + enc_format = p[0]; - lp = pdfi_find_cff_index(font->charstrings, font->cffend, 0, &s, &e); - if (lp == NULL) { - code = gs_note_error(gs_error_rangecheck); - goto done; - } - code = pdfi_object_alloc(ctx, PDF_STRING, e - s, (pdf_obj **) &pstr); - if (code < 0) - goto done; - memcpy(pstr->data, s, e - s); - pdfi_countup(pstr); - code = - pdfi_dict_put_obj(ctx, font->CharStrings, (pdf_obj *) ndname, (pdf_obj *) pstr); - pdfi_countdown(pstr); - if (code < 0) { - goto done; - } - pdfi_countdown(ndname); - ndname = NULL; /* just to avoid bad things! */ + lp = pdfi_find_cff_index(font->charstrings, font->cffend, 0, &s, &e); + if (lp == NULL) { + code = gs_note_error(gs_error_rangecheck); + goto done; + } + code = pdfi_object_alloc(ctx, PDF_STRING, e - s, (pdf_obj **) &pstr); + if (code < 0) + goto done; + memcpy(pstr->data, s, e - s); + pdfi_countup(pstr); + code = + pdfi_dict_put_obj(ctx, font->CharStrings, (pdf_obj *) ndname, (pdf_obj *) pstr, true); + pdfi_countdown(pstr); + if (code < 0) { + goto done; + } + pdfi_countdown(ndname); + ndname = NULL; /* just to avoid bad things! */ + + if ((enc_format &0x7f) == 0) { + unsigned int n_codes = p[1]; - if ((enc_format &0x7f) == 0) { - unsigned int n_codes = p[1]; + if (p + 2 + n_codes > font->cffend) { + return_error(gs_error_invalidfont); + } + gid2char[0] = 0; + for (i = 0; i < n_codes; i++) { + gid2char[i + 1] = p[2 + i]; + } + memset(gid2char + n_codes + 1, 0, sizeof(gid2char) - n_codes - 1); + supp_enc_offset = 2 + n_codes; + } + else if ((enc_format &0x7f) == 1) { + unsigned int n_ranges = p[1]; + unsigned int first, left, j, k = 1; - if (p + 2 + n_codes > font->cffend) { - return_error(gs_error_invalidfont); + if (p + 2 + 2 * n_ranges > font->cffend) { + return_error(gs_error_invalidfont); + } + gid2char[0] = 0; + for (i = 0; i < n_ranges; i++) { + first = p[2 + 2 * i]; + left = p[3 + 2 * i]; + for (j = 0; j <= left && k < 256; j++) + gid2char[k++] = first + j; + } + memset(gid2char + k, 0, sizeof(gid2char) - k); + supp_enc_offset = 2 * n_ranges + 2; } - gid2char[0] = 0; - for (i = 0; i < n_codes; i++) { - gid2char[i + 1] = p[2 + i]; + else { + return_error(gs_error_rangecheck); } - memset(gid2char + n_codes + 1, 0, sizeof(gid2char) - n_codes - 1); - supp_enc_offset = 2 + n_codes; - } - else if ((enc_format &0x7f) == 1) { - unsigned int n_ranges = p[1]; - unsigned int first, left, j, k = 1; - - if (p + 2 + 2 * n_ranges > font->cffend) { - return_error(gs_error_invalidfont); - } - gid2char[0] = 0; - for (i = 0; i < n_ranges; i++) { - first = p[2 + 2 * i]; - left = p[3 + 2 * i]; - for (j = 0; j <= left && k < 256; j++) - gid2char[k++] = first + j; - } - memset(gid2char + k, 0, sizeof(gid2char) - k); - supp_enc_offset = 2 * n_ranges + 2; - } - else { - return_error(gs_error_rangecheck); } } } @@ -1361,7 +1482,7 @@ pdfi_cff_build_encoding(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv, cff_fon pdfi_countup(pstr); if (ptpriv->forcecid) { char buf[40]; - int len = gs_sprintf(buf, "%d", 0); + int len = gs_snprintf(buf, sizeof(buf), "%d", 0); code = pdfi_name_alloc(ctx, (byte *) buf, len, &gname); if (code < 0) { @@ -1378,7 +1499,7 @@ pdfi_cff_build_encoding(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv, cff_fon } pdfi_countup(gname); } - code = pdfi_dict_put_obj(ctx, font->CharStrings, gname, (pdf_obj *) pstr); + code = pdfi_dict_put_obj(ctx, font->CharStrings, gname, (pdf_obj *) pstr, true); pdfi_countdown(pstr); pdfi_countdown(gname); if (code < 0) @@ -1399,7 +1520,7 @@ pdfi_cff_build_encoding(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv, cff_fon if (ptpriv->forcecid) { char buf[40]; - int len = gs_sprintf(buf, "%d", gid); + int len = gs_snprintf(buf, sizeof(buf), "%d", gid); code = pdfi_name_alloc(ctx, (byte *) buf, len, &gname); if (code < 0) { @@ -1415,7 +1536,7 @@ pdfi_cff_build_encoding(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv, cff_fon } if ((code = pdfi_make_name_from_sid(ctx, &gname, font, offsets, sid)) < 0) { char buf[40]; - int len = gs_sprintf(buf, "sid-%d", sid); + int len = gs_snprintf(buf, sizeof(buf), "sid-%d", sid); code = pdfi_name_alloc(ctx, (byte *) buf, len, &gname); if (code < 0) { @@ -1425,7 +1546,7 @@ pdfi_cff_build_encoding(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv, cff_fon } } pdfi_countup(gname); - code = pdfi_dict_put_obj(ctx, font->CharStrings, gname, (pdf_obj *) pstr); + code = pdfi_dict_put_obj(ctx, font->CharStrings, gname, (pdf_obj *) pstr, true); pdfi_countdown(pstr); if (code < 0) { pdfi_countdown(gname); @@ -1437,7 +1558,7 @@ pdfi_cff_build_encoding(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv, cff_fon pdfi_countdown(gname); } - if (offsets->encoding_off > 1 && (enc_format &0x80)) { + if (offsets->encoding_off > 1 && (enc_format & 0x80)) { unsigned int n_supp, charcode, sid; byte *p = font->cffdata + offsets->encoding_off + supp_enc_offset; pdf_obj *gname; @@ -1450,7 +1571,7 @@ pdfi_cff_build_encoding(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv, cff_fon if ((code = pdfi_make_name_from_sid(ctx, &gname, font, offsets, sid)) < 0) { char buf[40]; - int len = gs_sprintf(buf, "sid-%d", sid); + int len = gs_snprintf(buf, sizeof(buf), "sid-%d", sid); if (len > 0) code = pdfi_name_alloc(ctx, (byte *) buf, len, &gname); @@ -1534,6 +1655,7 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) /* String index */ pstore = p; p = pdfi_find_cff_index(p, e, 0, &strp, &stre); + offsets.strings_off = pstore - font->cffdata; p = pdfi_count_cff_index(pstore, e, &count); @@ -1550,7 +1672,7 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) font->NumGlobalSubrs = 0; } /* Read the top and private dictionaries */ - code = pdfi_read_cff_dict(dictp, dicte, ptpriv, &offsets); + code = pdfi_read_cff_dict(dictp, dicte, ptpriv, &offsets, true); if (code < 0) return gs_rethrow(code, "cannot read top dictionary"); @@ -1576,6 +1698,7 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) font->GlobalSubrs->refcnt = 1; for (i = 0; i < font->NumGlobalSubrs; i++) { pdf_string *gsubrstr; + pdf_obj *nullobj = NULL; p = pdfi_find_cff_index(font->gsubrs, font->cffend, i, &strp, &stre); if (p) { @@ -1591,6 +1714,17 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) } } } + else { + code = 0; + if (nullobj == NULL) + code = pdfi_object_alloc(ctx, PDF_NULL, 1, (pdf_obj **) &nullobj); + if (code >= 0) + code = pdfi_array_put(ctx, font->GlobalSubrs, (uint64_t) i, (pdf_obj *) nullobj); + if (code < 0) { + pdfi_countdown(font->GlobalSubrs); + font->GlobalSubrs = NULL; + } + } } } } @@ -1598,10 +1732,11 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) font->Subrs = NULL; if (font->NumSubrs > 0) { code = pdfi_object_alloc(ctx, PDF_ARRAY, font->NumSubrs, (pdf_obj **) &font->Subrs); - if (code >= 0) { + if (code >= 0 && font->Subrs != NULL) { font->Subrs->refcnt = 1; for (i = 0; i < font->NumSubrs; i++) { pdf_string *subrstr; + pdf_obj *nullobj = NULL; p = pdfi_find_cff_index(font->subrs, font->cffend, i, &strp, &stre); if (p) { @@ -1615,6 +1750,19 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) } } } + else { + code = 0; + if (nullobj == NULL) + code = pdfi_object_alloc(ctx, PDF_NULL, 1, (pdf_obj **) &nullobj); + if (code >= 0) + code = pdfi_array_put(ctx, font->Subrs, (uint64_t) i, (pdf_obj *) nullobj); + if (code < 0) { + pdfi_countdown(font->Subrs); + font->Subrs = NULL; + font->NumSubrs = 0; + break; + } + } } } } @@ -1641,6 +1789,9 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) charset_proc = expert_subset_charset_proc; break; default:{ + if (font->cffdata + offsets.charset_off >= font->cffend) + return_error(gs_error_rangecheck); + switch ((int)font->cffdata[offsets.charset_off]) { case 0: charset_proc = format0_charset_proc; @@ -1663,7 +1814,7 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) int (*fdselect_proc)(const byte *p, const byte *pe, unsigned int i); p = pdfi_count_cff_index(font->cffdata + offsets.fdarray_off, e, &fdarray_size); - if (!p) + if (!p || fdarray_size < 1 || fdarray_size > 64) /* 64 is arbitrary, but seems a reasonable upper limit */ return gs_rethrow(-1, "cannot read charstrings index"); ptpriv->cidata.FDBytes = 1; /* Basically, always 1 just now */ @@ -1700,7 +1851,7 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) if (fddicte > font->cffend) fddicte = font->cffend; - code = pdfi_read_cff_dict(fddictp, fddicte, &fdptpriv, &offsets); + code = pdfi_read_cff_dict(fddictp, fddicte, &fdptpriv, &offsets, true); if (code < 0) { ptpriv->cidata.FDArray[i] = NULL; code = gs_note_error(gs_error_invalidfont); @@ -1782,6 +1933,9 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) } } else { + if (font->cffdata + offsets.fdselect_off > font->cffend) + return_error(gs_error_rangecheck); + switch ((int)font->cffdata[offsets.fdselect_off]) { case 0: fdselect_proc = format0_fdselect_proc; @@ -2124,17 +2278,16 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, /* Vestigial magic number check - we can't check the third byte, as we have out of spec fonts that have a head size > 4 */ - if (fbuf[0] == 1 && fbuf[1] == 0) { + if (fbuf[0] == 1 && fbuf[1] == 0 && code >= 0) { pdfi_gs_cff_font_priv cffpriv; - if (code >= 0) { - pdfi_init_cff_font_priv(ctx, &cffpriv, fbuf, fbuflen, false); - cffpriv.forcecid = forcecid; - code = pdfi_read_cff(ctx, &cffpriv); - } + pdfi_init_cff_font_priv(ctx, &cffpriv, fbuf, fbuflen, false); + cffpriv.forcecid = forcecid; + code = pdfi_read_cff(ctx, &cffpriv); + if (code >= 0) { if (cffpriv.FontType == ft_CID_encrypted) { - pdf_obj *obj; + pdf_obj *obj = NULL; pdf_cidfont_type0 *cffcid; gs_font_cid0 *pfont; @@ -2155,9 +2308,50 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pfont->FAPI = NULL; pfont->base = (gs_font *) cffcid->pfont; - cffcid->registry = cffpriv.pdfcffpriv.registry; - cffcid->ordering = cffpriv.pdfcffpriv.ordering; - cffcid->supplement = cffpriv.pdfcffpriv.supplement; + code = pdfi_dict_knownget_type(ctx, font_dict, "CIDSystemInfo", PDF_DICT, (pdf_obj **)&obj); + if (code <= 0) { + cffcid->registry = cffpriv.pdfcffpriv.registry; + cffcid->ordering = cffpriv.pdfcffpriv.ordering; + cffcid->supplement = cffpriv.pdfcffpriv.supplement; + } + else { + pdf_num *suppl = NULL; + + code = pdfi_dict_knownget_type(ctx, (pdf_dict *)obj, "Registry", PDF_STRING, (pdf_obj **)&cffcid->registry); + if (code <= 0) { + cffcid->registry = cffpriv.pdfcffpriv.registry; + } + else { + pdfi_countdown(cffpriv.pdfcffpriv.registry); + cffpriv.pdfcffpriv.registry = NULL; + } + + code = pdfi_dict_knownget_type(ctx, (pdf_dict *)obj, "Ordering", PDF_STRING, (pdf_obj **)&cffcid->ordering); + if (code <= 0) { + cffcid->ordering = cffpriv.pdfcffpriv.ordering; + } + else { + pdfi_countdown(cffpriv.pdfcffpriv.ordering); + cffpriv.pdfcffpriv.ordering = NULL; + } + code = pdfi_dict_knownget_type(ctx, (pdf_dict *)obj, "Supplement", PDF_INT, (pdf_obj **)&suppl); + if (code <= 0 || suppl->type != PDF_INT) { + cffcid->supplement = cffpriv.pdfcffpriv.supplement; + } + else { + cffcid->supplement = suppl->value.i; + } + pdfi_countdown(suppl); + } + pdfi_countdown(obj); + obj = NULL; + + pfont->cidata.common.CIDSystemInfo.Registry.data = cffcid->registry->data; + pfont->cidata.common.CIDSystemInfo.Registry.size = cffcid->registry->length; + pfont->cidata.common.CIDSystemInfo.Ordering.data = cffcid->ordering->data; + pfont->cidata.common.CIDSystemInfo.Ordering.size = cffcid->ordering->length; + pfont->cidata.common.CIDSystemInfo.Supplement = cffcid->supplement; + cffcid->FontDescriptor = (pdf_dict *) fontdesc; fontdesc = NULL; @@ -2167,19 +2361,13 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, cffcid->cidtogidmap.data = NULL; cffcid->cidtogidmap.size = 0; - pfont->cidata.common.CIDSystemInfo.Registry.data = cffcid->registry->data; - pfont->cidata.common.CIDSystemInfo.Registry.size = cffcid->registry->length; - pfont->cidata.common.CIDSystemInfo.Ordering.data = cffcid->ordering->data; - pfont->cidata.common.CIDSystemInfo.Ordering.size = cffcid->ordering->length; - pfont->cidata.common.CIDSystemInfo.Supplement = cffcid->supplement; pfont->client_data = cffcid; cffcid->object_num = font_dict->object_num; cffcid->generation_num = font_dict->generation_num; cffcid->indirect_num = font_dict->indirect_num; cffcid->indirect_gen = font_dict->indirect_gen; - cffcid->PDF_font = font_dict; - pdfi_countup(font_dict); + cffcid->CharStrings = cffpriv.pdfcffpriv.CharStrings; cffpriv.pdfcffpriv.CharStrings = NULL; @@ -2253,6 +2441,7 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, code = pdfi_font_generate_pseudo_XUID(ctx, font_dict, (gs_font_base *)cffcid->pfont); if (code < 0) uid_set_invalid(&cffcid->pfont->UID); + } else if (forcecid) { pdf_obj *obj; @@ -2329,8 +2518,7 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, fdcfffont->object_num = 0; fdcfffont->generation_num = 0; - fdcfffont->PDF_font = font_dict; - pdfi_countup(font_dict); + (void)pdfi_dict_knownget_type(ctx, font_dict, "BaseFont", PDF_NAME, &basefont); fdcfffont->BaseFont = basefont; fdcfffont->Name = basefont; @@ -2340,9 +2528,12 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, cffpriv.pdfcffpriv.Encoding = NULL; fdcfffont->CharStrings = cffpriv.pdfcffpriv.CharStrings; + cffpriv.pdfcffpriv.CharStrings = NULL; fdcfffont->Subrs = cffpriv.pdfcffpriv.Subrs; + cffpriv.pdfcffpriv.Subrs = NULL; fdcfffont->NumSubrs = cffpriv.pdfcffpriv.NumSubrs; fdcfffont->GlobalSubrs = cffpriv.pdfcffpriv.GlobalSubrs; + cffpriv.pdfcffpriv.GlobalSubrs = NULL; fdcfffont->NumGlobalSubrs = cffpriv.pdfcffpriv.NumGlobalSubrs; cffcid->CharStrings = fdcfffont->CharStrings; @@ -2356,9 +2547,6 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, cffcid->FontDescriptor = (pdf_dict *) fontdesc; fontdesc = NULL; - cffcid->PDF_font = font_dict; - pdfi_countup(font_dict); - cffcid->registry = registry; cffcid->ordering = ordering; registry = ordering = NULL; @@ -2389,11 +2577,9 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, cffcid->generation_num = font_dict->generation_num; cffcid->indirect_num = font_dict->indirect_num; cffcid->indirect_gen = font_dict->indirect_gen; + cffcid->PDF_font = font_dict; pdfi_countup(font_dict); - cffcid->CharStrings = cffpriv.pdfcffpriv.CharStrings; - cffcid->Subrs = cffpriv.pdfcffpriv.Subrs; - cffcid->GlobalSubrs = cffpriv.pdfcffpriv.GlobalSubrs; cffcid->cidtogidmap.data = NULL; cffcid->cidtogidmap.size = 0; @@ -2457,6 +2643,7 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, else { pdf_font_cff *cfffont; gs_font_type1 *pfont = NULL; + pdf_obj *tounicode = NULL; code = pdfi_alloc_cff_font(ctx, &cfffont, font_dict->object_num, false); pfont = (gs_font_type1 *) cfffont->pfont; @@ -2472,8 +2659,9 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, cfffont->object_num = font_dict->object_num; cfffont->generation_num = font_dict->generation_num; - cfffont->PDF_font = font_dict; - pdfi_countup(font_dict); + cfffont->indirect_num = font_dict->indirect_num; + cfffont->indirect_gen = font_dict->indirect_gen; + (void)pdfi_dict_knownget_type(ctx, font_dict, "BaseFont", PDF_NAME, &basefont); cfffont->BaseFont = basefont; cfffont->Name = basefont; @@ -2604,18 +2792,28 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, cfffont->Encoding = cffpriv.pdfcffpriv.Encoding; cffpriv.pdfcffpriv.Encoding = NULL; } - - code = pdfi_dict_knownget(ctx, font_dict, "ToUnicode", &tmp); - if (code == 1) { - cfffont->ToUnicode = tmp; - tmp = NULL; + if (ctx->args.ignoretounicode != true) { + code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tounicode); + if (code >= 0 && tounicode->type == PDF_STREAM) { + pdf_cmap *tu = NULL; + code = pdfi_read_cmap(ctx, tounicode, &tu); + pdfi_countdown(tounicode); + tounicode = (pdf_obj *)tu; + } + if (code < 0 || (tounicode != NULL && tounicode->type != PDF_CMAP)) { + pdfi_countdown(tounicode); + tounicode = NULL; + code = 0; + } } else { - cfffont->ToUnicode = NULL; + tounicode = NULL; } + cfffont->ToUnicode = tounicode; + tounicode = NULL; } } - error: +error: if (code < 0) { pdfi_countdown(cffpriv.pdfcffpriv.Subrs); pdfi_countdown(cffpriv.pdfcffpriv.GlobalSubrs); @@ -2632,24 +2830,20 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, gs_free_object(ctx->memory, cffpriv.cidata.FDArray, "pdfi_read_cff_font(gs_font FDArray, error)"); } } - if (code >= 0) { + else { code = gs_definefont(ctx->font_dir, (gs_font *) ppdfont->pfont); - if (code < 0) { - goto error; - } - code = pdfi_fapi_passfont((pdf_font *) ppdfont, 0, NULL, NULL, NULL, 0); - if (code < 0) { - goto error; - } + if (code >= 0) + code = pdfi_fapi_passfont((pdf_font *) ppdfont, 0, NULL, NULL, NULL, 0); + /* object_num can be zero if the dictionary was defined inline */ - if (ppdfont->object_num != 0) { - code = replace_cache_entry(ctx, (pdf_obj *) ppdfont); - if (code < 0) - goto error; + if (code >= 0 && ppdfont->object_num != 0) { + (void)replace_cache_entry(ctx, (pdf_obj *) ppdfont); + } + if (code >= 0) { + *ppdffont = (pdf_font *) ppdfont; + ppdfont = NULL; } - *ppdffont = (pdf_font *) ppdfont; - ppdfont = NULL; } } gs_free_object(ctx->memory, pfbuf, "pdfi_read_cff_font(fbuf)"); diff --git a/pdf/pdf_font3.c b/pdf/pdf_font3.c index 155d8be5..7ecd3bff 100644 --- a/pdf/pdf_font3.c +++ b/pdf/pdf_font3.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -19,10 +19,10 @@ #include "pdf_stack.h" #include "pdf_array.h" #include "pdf_dict.h" +#include "pdf_font_types.h" #include "pdf_gstate.h" #include "pdf_font.h" #include "pdf_font3.h" -#include "pdf_font_types.h" #include "pdf_deref.h" #include "gscencs.h" #include "gscedata.h" /* For the encoding arrays */ @@ -81,7 +81,7 @@ pdfi_type3_build_char(gs_show_enum * penum, gs_gstate * pgs, gs_font * pfont, OBJ_CTX(font)->text.BlockDepth = 0; OBJ_CTX(font)->text.inside_CharProc = true; - OBJ_CTX(font)->text.CharProc_is_d1 = false; + OBJ_CTX(font)->text.CharProc_d_type = pdf_type3_d_none; { /* It turns out that if a type 3 font uses a stroke to draw, and does not @@ -110,9 +110,11 @@ pdfi_type3_build_char(gs_show_enum * penum, gs_gstate * pgs, gs_font * pfont, /* Use the utility routine above to copy the fill colour to the stroke colour */ pdfi_type3_copy_color(&OBJ_CTX(font)->pgs->color[0], &OBJ_CTX(font)->pgs->color[1]); - pdfi_gsave(OBJ_CTX(font)); - pdfi_run_context(OBJ_CTX(font), CharProc, font->PDF_font, true, "CharProc"); - pdfi_grestore(OBJ_CTX(font)); + code = pdfi_gsave(OBJ_CTX(font)); + if (code >= 0) { + code = pdfi_run_context(OBJ_CTX(font), CharProc, font->PDF_font, true, "CharProc"); + (void)pdfi_grestore(OBJ_CTX(font)); + } /* Use the utility routine above to copy the temporary copy to the stroke colour */ pdfi_type3_copy_color(&tmp_color, &OBJ_CTX(font)->pgs->color[1]); @@ -120,7 +122,7 @@ pdfi_type3_build_char(gs_show_enum * penum, gs_gstate * pgs, gs_font * pfont, } OBJ_CTX(font)->text.inside_CharProc = false; - OBJ_CTX(font)->text.CharProc_is_d1 = false; + OBJ_CTX(font)->text.CharProc_d_type = pdf_type3_d_none; OBJ_CTX(font)->text.BlockDepth = SavedTextBlockDepth; build_char_error: @@ -220,6 +222,7 @@ int pdfi_free_font_type3(pdf_obj *font) pdfi_countdown(t3font->FontDescriptor); pdfi_countdown(t3font->CharProcs); pdfi_countdown(t3font->Encoding); + pdfi_countdown(t3font->ToUnicode); gs_free_object(OBJ_MEMORY(font), font, "Free type 3 font"); return 0; } @@ -231,6 +234,7 @@ int pdfi_read_type3_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream pdf_font_type3 *font = NULL; pdf_obj *obj = NULL; double f; + pdf_obj *tounicode = NULL; *ppdffont = NULL; code = alloc_type3_font(ctx, &font); @@ -238,6 +242,9 @@ int pdfi_read_type3_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream return code; font->object_num = font_dict->object_num; + font->generation_num = font_dict->generation_num; + font->indirect_num = font_dict->indirect_num; + font->indirect_gen = font_dict->indirect_gen; code = pdfi_dict_get_type(ctx, font_dict, "FontBBox", PDF_ARRAY, &obj); if (code < 0) @@ -315,6 +322,27 @@ int pdfi_read_type3_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream font->PDF_font = font_dict; pdfi_countup(font_dict); + if (ctx->args.ignoretounicode != true) { + code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tounicode); + if (code >= 0 && tounicode->type == PDF_STREAM) { + pdf_cmap *tu = NULL; + code = pdfi_read_cmap(ctx, tounicode, &tu); + pdfi_countdown(tounicode); + tounicode = (pdf_obj *)tu; + } + if (code < 0 || (tounicode != NULL && tounicode->type != PDF_CMAP)) { + pdfi_countdown(tounicode); + tounicode = NULL; + code = 0; + } + } + else { + tounicode = NULL; + } + + font->ToUnicode = tounicode; + tounicode = NULL; + code = replace_cache_entry(ctx, (pdf_obj *)font); if (code < 0) goto font3_error; diff --git a/pdf/pdf_fontTT.c b/pdf/pdf_fontTT.c index 44f3799e..aec3ee7b 100644 --- a/pdf/pdf_fontTT.c +++ b/pdf/pdf_fontTT.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -61,7 +61,7 @@ static gs_glyph pdfi_ttf_encode_char(gs_font *pfont, gs_char chr, gs_glyph_space uint ID; int code; - if ((ttfont->descflags & 4) != 0) { + if ((ttfont->descflags & 4) != 0 || sp == GLYPH_SPACE_INDEX) { int code = pdfi_fapi_check_cmap_for_GID(pfont, (uint)chr, &ID); if (code < 0 || ID == 0) code = pdfi_fapi_check_cmap_for_GID(pfont, (uint)(chr | 0xf0 << 8), &ID); @@ -94,11 +94,8 @@ static uint pdfi_type42_get_glyph_index(gs_font_type42 *pfont, gs_glyph glyph) uint cc = 0; int i, code = 0; - if (glyph >= GS_MIN_GLYPH_INDEX) - glyph -= GS_MIN_GLYPH_INDEX; - - if ((ttfont->descflags & 4) != 0) { - gind = (uint)glyph; + if ((ttfont->descflags & 4) != 0 || glyph >= GS_MIN_GLYPH_INDEX) { + gind = (uint)glyph < GS_MIN_GLYPH_INDEX ? glyph : glyph - GS_MIN_GLYPH_INDEX; } else { pdf_context *ctx = (pdf_context *)ttfont->ctx; @@ -247,7 +244,7 @@ static int pdfi_ttf_glyph_name(gs_font *pfont, gs_glyph glyph, gs_const_string * if (code < 0) { char buf[64]; int l; - l = gs_sprintf(buf, "~gs~gName~%04x", (uint)glyph); + l = gs_snprintf(buf, sizeof(buf), "~gs~gName~%04x", (uint)glyph); code = (*ctx->get_glyph_index)(pfont, (byte *)buf, l, &ID); } else { @@ -356,7 +353,7 @@ static int pdfi_set_type42_data_procs(gs_font_type42 *pfont) return 0; } -int pdfi_read_truetype_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, byte *buf, int64_t buflen, pdf_font **ppdffont) +int pdfi_read_truetype_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, byte *buf, int64_t buflen, int findex, pdf_font **ppdffont) { pdf_font_truetype *font = NULL; int code = 0, num_chars = 0, i; @@ -367,6 +364,7 @@ int pdfi_read_truetype_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str int64_t descflags; bool encoding_known = false; bool forced_symbolic = false; + pdf_obj *tounicode = NULL; if (ppdffont == NULL) return_error(gs_error_invalidaccess); @@ -383,6 +381,10 @@ int pdfi_read_truetype_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str code = gs_note_error(gs_error_invalidfont); goto error; } + font->object_num = font_dict->object_num; + font->generation_num = font_dict->generation_num; + font->indirect_num = font_dict->indirect_num; + font->indirect_gen = font_dict->indirect_gen; font->FontDescriptor = (pdf_dict *)fontdesc; fontdesc = NULL; @@ -450,6 +452,26 @@ int pdfi_read_truetype_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str pdfi_countdown(obj); obj = NULL; + if (ctx->args.ignoretounicode != true) { + code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tounicode); + if (code >= 0 && tounicode->type == PDF_STREAM) { + pdf_cmap *tu = NULL; + code = pdfi_read_cmap(ctx, tounicode, &tu); + pdfi_countdown(tounicode); + tounicode = (pdf_obj *)tu; + } + if (code < 0 || (tounicode != NULL && tounicode->type != PDF_CMAP)) { + pdfi_countdown(tounicode); + tounicode = NULL; + code = 0; + } + } + else { + tounicode = NULL; + } + font->ToUnicode = tounicode; + tounicode = NULL; + code = pdfi_dict_get_int(ctx, font->FontDescriptor, "Flags", &descflags); if (code < 0) descflags = 0; @@ -576,9 +598,7 @@ int pdfi_read_truetype_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str /* object_num can be zero if the dictionary was defined inline */ if (font->object_num != 0) { - code = replace_cache_entry(ctx, (pdf_obj *)font); - if (code < 0) - goto error; + (void)replace_cache_entry(ctx, (pdf_obj *)font); } *ppdffont = (pdf_font *)font; @@ -615,6 +635,8 @@ int pdfi_free_font_truetype(pdf_obj *font) pdfi_countdown(ttfont->FontDescriptor); pdfi_countdown(ttfont->Encoding); pdfi_countdown(ttfont->BaseFont); + pdfi_countdown(ttfont->PDF_font); + pdfi_countdown(ttfont->ToUnicode); gs_free_object(OBJ_MEMORY(ttfont), ttfont, "Free TrueType font"); return 0; diff --git a/pdf/pdf_fontTT.h b/pdf/pdf_fontTT.h index 1ce4cf8b..90e4086e 100644 --- a/pdf/pdf_fontTT.h +++ b/pdf/pdf_fontTT.h @@ -18,7 +18,7 @@ #ifndef PDF_TRUETYPE_FONT #define PDF_TRUETYPE_FONT -int pdfi_read_truetype_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, byte *buf, int64_t buflen, pdf_font **ppdffont); +int pdfi_read_truetype_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, byte *buf, int64_t buflen, int findex, pdf_font **ppdffont); int pdfi_free_font_truetype(pdf_obj *font); #endif diff --git a/pdf/pdf_fontps.c b/pdf/pdf_fontps.c index 7ee9d2f0..3ab399b9 100644 --- a/pdf/pdf_fontps.c +++ b/pdf/pdf_fontps.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2021 Artifex Software, Inc. +/* Copyright (C) 2020-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -178,7 +178,7 @@ pdfi_pscript_interpret(pdf_ps_ctx_t *cs, byte *pdfpsbuf, int64_t buflen) *pdfpsbuf != char_CR) pdfpsbuf++; - if (*pdfpsbuf == char_EOL) + if (pdfpsbuf < buflim && *pdfpsbuf == char_EOL) pdfpsbuf++; } break; @@ -376,6 +376,29 @@ pdfi_pscript_interpret(pdf_ps_ctx_t *cs, byte *pdfpsbuf, int64_t buflen) return code; } +static inline bool pdf_ps_name_cmp(pdf_ps_stack_object_t *obj, const char *namestr) +{ + byte *d = NULL; + int l1, l2; + + if (namestr) { + l2 = strlen(namestr); + } + + if (obj->type == PDF_PS_OBJ_NAME) { + d = obj->val.name; + l1 = obj->size; + } + else if (obj->type == PDF_PS_OBJ_STRING) { + d = obj->val.name; + l1 = obj->size; + } + if (d != NULL && namestr != NULL && l1 == l2) { + return memcmp(d, namestr, l1) == 0 ? true : false; + } + return false; +} + static int ps_font_def_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte *bufend) { @@ -387,464 +410,494 @@ ps_font_def_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte *bufend) } if (pdf_ps_obj_has_type(&s->cur[-1], PDF_PS_OBJ_NAME)) { - if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("FontName"))) { - int fnlen = 0; - char *pname = NULL; - - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_NAME)) { - fnlen = s->cur[0].size > gs_font_name_max ? gs_font_name_max : s->cur[0].size; - pname = (char *)s->cur[0].val.name; - } - else if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_STRING)) { - fnlen = s->cur[0].size > gs_font_name_max ? gs_font_name_max : s->cur[0].size; - pname = (char *)s->cur[0].val.string; - } - if (pname) { - memcpy(priv->gsu.gst1.key_name.chars, pname, fnlen); - priv->gsu.gst1.key_name.chars[fnlen] = '\0'; - priv->gsu.gst1.key_name.size = fnlen; - - memcpy(priv->gsu.gst1.font_name.chars, pname, fnlen); - priv->gsu.gst1.font_name.chars[fnlen] = '\0'; - priv->gsu.gst1.font_name.size = fnlen; - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("PaintType"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { - priv->gsu.gst1.PaintType = s->cur[0].val.i; - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("StrokeWidth"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_FLOAT)) { - priv->gsu.gst1.StrokeWidth = s->cur[0].val.f; - } - else if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { - priv->gsu.gst1.StrokeWidth = (float)s->cur[0].val.i; - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("WMode"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { - priv->gsu.gst1.WMode = s->cur[0].val.i; - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("lenIV"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { - priv->gsu.gst1.data.lenIV = s->cur[0].val.i; - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("UniqueID"))) { - /* Ignore UniqueID if we already have a XUID */ - if (priv->gsu.gst1.UID.id >= 0) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { - uid_set_UniqueID(&priv->gsu.gst1.UID, s->cur[0].val.i); - } - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("XUID"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { - int i, size = s->cur[0].size; - long *xvals = (long *)gs_alloc_bytes(mem, size *sizeof(long), "ps_font_def_func(xuid vals)"); - - if (xvals != NULL) { - for (i = 0; i < size; i++) { - if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { - xvals[i] = s->cur[0].val.arr[i].val.i; - } - else { - gs_free_object(mem, xvals, "ps_font_def_func(xuid vals)"); - xvals = NULL; - break; - } - } - } - if (xvals != NULL) { - if (priv->gsu.gst1.UID.xvalues != NULL) - gs_free_object(mem, priv->gsu.gst1.UID.xvalues, "ps_font_def_func(old xuid vals)"); - uid_set_XUID(&priv->gsu.gst1.UID, xvals, size); - } - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("FontBBox"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { - int i, j; - double bbox[4] = { 0, 0, 1000, 1000 }; - if (pdf_ps_obj_has_type(&s->cur[0].val.arr[0], PDF_PS_OBJ_ARRAY)) { /* This is (probably) a Blend/FontBBox entry */ - code = pdfi_array_alloc(s->pdfi_ctx, s->cur[0].size, &priv->u.t1.blendfontbbox); - if (code >= 0) { - pdfi_countup(priv->u.t1.blendfontbbox); - for (i = 0; i < s->cur[0].size; i++) { - pdf_ps_stack_object_t *arr = &s->cur[0].val.arr[i]; - pdf_array *parr; - pdf_num *n; - if (pdf_ps_obj_has_type(arr, PDF_PS_OBJ_ARRAY)) { - code = pdfi_array_alloc(s->pdfi_ctx, arr->size, &parr); - if (code < 0) - break; - pdfi_countup(parr); - - for (j = 0; j < arr->size; j++) { - if (pdf_ps_obj_has_type(&arr->val.arr[j], PDF_PS_OBJ_INTEGER)) { - code = pdfi_object_alloc(s->pdfi_ctx, PDF_INT, 0, (pdf_obj **)&n); - if (code >= 0) - n->value.i = arr->val.arr[j].val.i; - } - else if (pdf_ps_obj_has_type(&arr->val.arr[j], PDF_PS_OBJ_FLOAT)) { - code = pdfi_object_alloc(s->pdfi_ctx, PDF_REAL, 0, (pdf_obj **)&n); - if (code >= 0) - n->value.d = arr->val.arr[j].val.f; - } - else { - code = pdfi_object_alloc(s->pdfi_ctx, PDF_INT, 0, (pdf_obj **)&n); - if (code >= 0) - n->value.i = 0; - } - if (code < 0) - break; - pdfi_countup(n); - code = pdfi_array_put(s->pdfi_ctx, parr, j, (pdf_obj *)n); - pdfi_countdown(n); - if (code < 0) break; - } - } - if (code >= 0) - code = pdfi_array_put(s->pdfi_ctx, priv->u.t1.blendfontbbox, i, (pdf_obj *)parr); - pdfi_countdown(parr); - } - } - } - else if (s->cur[0].size >= 4) { - for (i = 0; i < 4; i++) { - if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { - bbox[i] = (double)s->cur[0].val.arr[i].val.i; - } - else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_FLOAT)) { - bbox[i] = (double)s->cur[0].val.arr[i].val.f; - } - } - priv->gsu.gst1.FontBBox.p.x = bbox[0]; - priv->gsu.gst1.FontBBox.p.y = bbox[1]; - priv->gsu.gst1.FontBBox.q.x = bbox[2]; - priv->gsu.gst1.FontBBox.q.y = bbox[3]; - } - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("FontType"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { - priv->gsu.gst1.FontType = s->cur[0].val.i; - priv->u.t1.pdfi_font_type = s->cur[0].val.i == 1 ? e_pdf_font_type1 : e_pdf_cidfont_type0; - } - else { - priv->gsu.gst1.FontType = 1; - priv->u.t1.pdfi_font_type = e_pdf_font_type1; - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("FontMatrix"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY) && s->cur[0].size >= 6) { - int i; - double fmat[6] = { 0.001, 0, 0, 0.001, 0, 0 }; - for (i = 0; i < 6; i++) { - if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { - fmat[i] = (double)s->cur[0].val.arr[i].val.i; - } - else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_FLOAT)) { - fmat[i] = (double)s->cur[0].val.arr[i].val.f; - } - } - priv->gsu.gst1.FontMatrix.xx = fmat[0]; - priv->gsu.gst1.FontMatrix.xy = fmat[1]; - priv->gsu.gst1.FontMatrix.yx = fmat[2]; - priv->gsu.gst1.FontMatrix.yy = fmat[3]; - priv->gsu.gst1.FontMatrix.tx = fmat[4]; - priv->gsu.gst1.FontMatrix.ty = fmat[5]; - priv->gsu.gst1.orig_FontMatrix = priv->gsu.gst1.FontMatrix; - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("BlueValues"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { - int i, size = s->cur[0].size < 14 ? s->cur[0].size : 14; - - for (i = 0; i < size; i++) { - if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { - priv->gsu.gst1.data.BlueValues.values[i] = - (float)s->cur[0].val.arr[i].val.i; - } - else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_FLOAT)) { - priv->gsu.gst1.data.BlueValues.values[i] = s->cur[0].val.arr[i].val.f; - } - else { - if (i == 0) - priv->gsu.gst1.data.BlueValues.values[i] = 0; - else - priv->gsu.gst1.data.BlueValues.values[i] = priv->gsu.gst1.data.BlueValues.values[i - 1] + 1; - } - } - priv->gsu.gst1.data.BlueValues.count = size; - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("BlueScale"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { - priv->gsu.gst1.data.BlueScale = (float)s->cur[0].val.i; - } - else if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_FLOAT)) { - priv->gsu.gst1.data.BlueScale = (float)s->cur[0].val.f; - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("StdHW"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { - if (pdf_ps_obj_has_type(&s->cur[0].val.arr[0], PDF_PS_OBJ_INTEGER)) { - priv->gsu.gst1.data.StdHW.values[0] = (float)s->cur[0].val.arr[0].val.i; - priv->gsu.gst1.data.StdHW.count = 1; - } - else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[0], PDF_PS_OBJ_FLOAT)) { - priv->gsu.gst1.data.StdHW.values[0] = s->cur[0].val.arr[0].val.f; - priv->gsu.gst1.data.StdHW.count = 1; - } - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("StdVW"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { - if (pdf_ps_obj_has_type(&s->cur[0].val.arr[0], PDF_PS_OBJ_INTEGER)) { - priv->gsu.gst1.data.StdVW.values[0] = (float)s->cur[0].val.arr[0].val.i; - priv->gsu.gst1.data.StdVW.count = 1; - } - else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[0], PDF_PS_OBJ_FLOAT)) { - priv->gsu.gst1.data.StdVW.values[0] = s->cur[0].val.arr[0].val.f; - priv->gsu.gst1.data.StdVW.count = 1; - } - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("StemSnapH"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { - int i, size = s->cur[0].size > 12 ? 12 : s->cur[0].size; - - for (i = 0; i < size; i++) { - if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { - priv->gsu.gst1.data.StemSnapH.values[i] = (float)s->cur[0].val.arr[i].val.i; - } - else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_FLOAT)) { - priv->gsu.gst1.data.StemSnapH.values[i] = s->cur[0].val.arr[i].val.f; - } - } - priv->gsu.gst1.data.StemSnapH.count = size; - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("StemSnapV"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { - int i, size = s->cur[0].size > 12 ? 12 : s->cur[0].size; - - for (i = 0; i < size; i++) { - if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { - priv->gsu.gst1.data.StemSnapV.values[i] = (float)s->cur[0].val.arr[i].val.i; - } - else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_FLOAT)) { - priv->gsu.gst1.data.StemSnapV.values[i] = s->cur[0].val.arr[i].val.f; - } - } - priv->gsu.gst1.data.StemSnapH.count = size; - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("Encoding"))) { - pdf_array *new_enc = NULL; - - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_NAME)) { - pdf_name *pname; - - code = pdfi_name_alloc(s->pdfi_ctx, (byte *) s->cur[0].val.name, s->cur[0].size, (pdf_obj **) &pname); - if (code >= 0) { - pdfi_countup(pname); - - code = pdfi_create_Encoding(s->pdfi_ctx, (pdf_obj *) pname, NULL, (pdf_obj **) &new_enc); - if (code >= 0) { - pdfi_countdown(priv->u.t1.Encoding); - priv->u.t1.Encoding = new_enc; - } - pdfi_countdown(pname); - } - } - else if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { - int i; - - code = pdfi_array_alloc(s->pdfi_ctx, s->cur[0].size, &new_enc); - if (code >= 0) { - pdfi_countup(new_enc); - for (i = 0; i < s->cur[0].size; i++) { - pdf_name *n = NULL; - byte *nm = (byte *) s->cur[0].val.arr[i].val.name; - int nlen = s->cur[0].val.arr[i].size; - - code = pdfi_name_alloc(s->pdfi_ctx, (byte *) nm, nlen, (pdf_obj **) &n); - if (code < 0) - break; - pdfi_countup(n); - code = pdfi_array_put(s->pdfi_ctx, new_enc, (uint64_t) i, (pdf_obj *) n); - pdfi_countdown(n); - if (code < 0) - break; - } - if (code < 0) { - pdfi_countdown(new_enc); - } - else { - pdfi_countdown(priv->u.t1.Encoding); - priv->u.t1.Encoding = new_enc; - new_enc = NULL; - } - } - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("BlendDesignPositions"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { - code = pdfi_array_alloc(s->pdfi_ctx, s->cur[0].size, &priv->u.t1.blenddesignpositions); - if (code >= 0) { - int i, j; - pdfi_countup(priv->u.t1.blenddesignpositions); - - for (i = 0; i < s->cur[0].size && code >= 0; i++) { - pdf_ps_stack_object_t *so = &s->cur[0].val.arr[i]; - - if (pdf_ps_obj_has_type(so, PDF_PS_OBJ_ARRAY)) { - pdf_array *sa; - code = pdfi_array_alloc(s->pdfi_ctx, so->size, &sa); - if (code >= 0) { - pdfi_countup(sa); - for (j = 0; j < so->size; j++) { - pdf_num *n; - if (pdf_ps_obj_has_type(&so->val.arr[j], PDF_PS_OBJ_INTEGER)) { - code = pdfi_object_alloc(s->pdfi_ctx, PDF_INT, 0, (pdf_obj **)&n); - if (code >= 0) - n->value.i = so->val.arr[j].val.i; - } - else if (pdf_ps_obj_has_type(&so->val.arr[j], PDF_PS_OBJ_FLOAT)) { - code = pdfi_object_alloc(s->pdfi_ctx, PDF_REAL, 0, (pdf_obj **)&n); - if (code >= 0) - n->value.d = so->val.arr[j].val.f; - } - else { - code = pdfi_object_alloc(s->pdfi_ctx, PDF_INT, 0, (pdf_obj **)&n); - if (code >= 0) - n->value.i = 0; - } - if (code < 0) - break; - pdfi_countup(n); - code = pdfi_array_put(s->pdfi_ctx, sa, j, (pdf_obj *)n); - pdfi_countdown(n); - if (code < 0) break; - } - } - if (code >= 0) { - pdfi_array_put(s->pdfi_ctx, priv->u.t1.blenddesignpositions, i, (pdf_obj *)sa); - } - pdfi_countdown(sa); - } - } - } - - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("BlendAxisTypes"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { - int i; - code = pdfi_array_alloc(s->pdfi_ctx, s->cur[0].size, &priv->u.t1.blendaxistypes); - if (code >= 0) { - pdfi_countup(priv->u.t1.blendaxistypes); - for (i = 0; i < s->cur[0].size; i++) { - pdf_ps_stack_object_t *so = &s->cur[0].val.arr[i]; - pdf_name *n; - if (pdf_ps_obj_has_type(so, PDF_PS_OBJ_NAME)) { - code = pdfi_object_alloc(s->pdfi_ctx, PDF_NAME, so->size, (pdf_obj **)&n); - if (code >= 0) { - pdfi_countup(n); - memcpy(n->data, so->val.name, so->size); - n->length = so->size; - code = pdfi_array_put(s->pdfi_ctx, priv->u.t1.blendaxistypes, i, (pdf_obj *)n); - pdfi_countdown(n); - } - } - if (code < 0) - break; - } - } - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("BlendDesignMap"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { - int i, j, k; - pdf_ps_stack_object_t *arr1 = &s->cur[0], *arr2, *arr3; - pdf_array *parr2, *parr3; - code = pdfi_array_alloc(s->pdfi_ctx, arr1->size, &priv->u.t1.blenddesignmap); - if (code >= 0) { - pdfi_countup(priv->u.t1.blenddesignmap); - for (i = 0; i < arr1->size && code >= 0; i++) { - if (pdf_ps_obj_has_type(&arr1->val.arr[i], PDF_PS_OBJ_ARRAY)) { - arr2 = &arr1->val.arr[i]; - code = pdfi_array_alloc(s->pdfi_ctx, arr2->size, &parr2); - if (code < 0) - break; - for (j = 0; j < arr2->size; j++) { - pdf_num *n; - - arr3 = &arr2->val.arr[j]; - code = pdfi_array_alloc(s->pdfi_ctx, arr3->size, &parr3); - if (code < 0) - break; - - for (k = 0; k < arr3->size; k++) { - if (pdf_ps_obj_has_type(&arr3->val.arr[k], PDF_PS_OBJ_INTEGER)) { - code = pdfi_object_alloc(s->pdfi_ctx, PDF_INT, 0, (pdf_obj **)&n); - if (code >= 0) - n->value.i = arr3->val.arr[k].val.i; - } - else if (pdf_ps_obj_has_type(&arr1->val.arr[i], PDF_PS_OBJ_FLOAT)) { - code = pdfi_object_alloc(s->pdfi_ctx, PDF_REAL, 0, (pdf_obj **)&n); - if (code >= 0) - n->value.d = arr3->val.arr[k].val.f; - } - else { - code = pdfi_object_alloc(s->pdfi_ctx, PDF_INT, 0, (pdf_obj **)&n); - if (code >= 0) - n->value.i = 0; - } - if (code < 0) - break; - pdfi_countup(n); - code = pdfi_array_put(s->pdfi_ctx, parr3, k, (pdf_obj *)n); - pdfi_countdown(n); - if (code < 0) - break; - } - if (code < 0) - break; - pdfi_countup(parr3); - code = pdfi_array_put(s->pdfi_ctx, parr2, j, (pdf_obj *)parr3); - pdfi_countdown(parr3); - } - if (code < 0) - break; - pdfi_countup(parr2); - code = pdfi_array_put(s->pdfi_ctx, priv->u.t1.blenddesignmap, i, (pdf_obj *)parr2); - pdfi_countdown(parr2); - } - } - } - } - } - else if (!memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("WeightVector"))) { - if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { - int i; - for (i = 0; i < s->cur[0].size; i++) { - if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { - priv->gsu.gst1.data.WeightVector.values[i] = s->cur[0].val.arr[i].val.i; - } - else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_FLOAT)) { - priv->gsu.gst1.data.WeightVector.values[i] = s->cur[0].val.arr[i].val.f; - } - else { - priv->gsu.gst1.data.WeightVector.values[i] = 0; - } - } - priv->gsu.gst1.data.WeightVector.count = s->cur[0].size; - } + switch (s->cur[-1].size) { + + case 4: + if (pdf_ps_name_cmp(&s->cur[-1], "XUID")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { + int i, size = s->cur[0].size; + long *xvals = (long *)gs_alloc_bytes(mem, size *sizeof(long), "ps_font_def_func(xuid vals)"); + + if (xvals != NULL) { + for (i = 0; i < size; i++) { + if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { + xvals[i] = s->cur[0].val.arr[i].val.i; + } + else { + gs_free_object(mem, xvals, "ps_font_def_func(xuid vals)"); + xvals = NULL; + break; + } + } + } + if (xvals != NULL) { + if (priv->gsu.gst1.UID.xvalues != NULL) + gs_free_object(mem, priv->gsu.gst1.UID.xvalues, "ps_font_def_func(old xuid vals)"); + uid_set_XUID(&priv->gsu.gst1.UID, xvals, size); + } + } + } + break; + + case 5: + if (pdf_ps_name_cmp(&s->cur[-1], "StdHW")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY) && s->cur[0].size > 0) { + if (pdf_ps_obj_has_type(&s->cur[0].val.arr[0], PDF_PS_OBJ_INTEGER)) { + priv->gsu.gst1.data.StdHW.values[0] = (float)s->cur[0].val.arr[0].val.i; + priv->gsu.gst1.data.StdHW.count = 1; + } + else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[0], PDF_PS_OBJ_FLOAT)) { + priv->gsu.gst1.data.StdHW.values[0] = s->cur[0].val.arr[0].val.f; + priv->gsu.gst1.data.StdHW.count = 1; + } + } + } + else if (pdf_ps_name_cmp(&s->cur[-1], "StdVW")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY) && s->cur[0].size > 0) { + if (pdf_ps_obj_has_type(&s->cur[0].val.arr[0], PDF_PS_OBJ_INTEGER)) { + priv->gsu.gst1.data.StdVW.values[0] = (float)s->cur[0].val.arr[0].val.i; + priv->gsu.gst1.data.StdVW.count = 1; + } + else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[0], PDF_PS_OBJ_FLOAT)) { + priv->gsu.gst1.data.StdVW.values[0] = s->cur[0].val.arr[0].val.f; + priv->gsu.gst1.data.StdVW.count = 1; + } + } + } + else if (pdf_ps_name_cmp(&s->cur[-1], "WMode")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { + priv->gsu.gst1.WMode = s->cur[0].val.i; + } + } + else if (pdf_ps_name_cmp(&s->cur[-1], "lenIV")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { + priv->gsu.gst1.data.lenIV = s->cur[0].val.i; + } + } + break; + + case 8: + if (pdf_ps_name_cmp(&s->cur[-1], "FontName")) { + int fnlen = 0; + char *pname = NULL; + + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_NAME)) { + fnlen = s->cur[0].size > gs_font_name_max ? gs_font_name_max : s->cur[0].size; + pname = (char *)s->cur[0].val.name; + } + else if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_STRING)) { + fnlen = s->cur[0].size > gs_font_name_max ? gs_font_name_max : s->cur[0].size; + pname = (char *)s->cur[0].val.string; + } + if (pname) { + memcpy(priv->gsu.gst1.key_name.chars, pname, fnlen); + priv->gsu.gst1.key_name.chars[fnlen] = '\0'; + priv->gsu.gst1.key_name.size = fnlen; + + memcpy(priv->gsu.gst1.font_name.chars, pname, fnlen); + priv->gsu.gst1.font_name.chars[fnlen] = '\0'; + priv->gsu.gst1.font_name.size = fnlen; + } + } + else if (pdf_ps_name_cmp(&s->cur[-1], "FontBBox")) { + if (s->cur[0].size > 0 && pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { + int i, j; + double bbox[4] = { 0, 0, 1000, 1000 }; + if (pdf_ps_obj_has_type(&s->cur[0].val.arr[0], PDF_PS_OBJ_ARRAY)) { /* This is (probably) a Blend/FontBBox entry */ + code = pdfi_array_alloc(s->pdfi_ctx, s->cur[0].size, &priv->u.t1.blendfontbbox); + if (code >= 0) { + pdfi_countup(priv->u.t1.blendfontbbox); + for (i = 0; i < s->cur[0].size; i++) { + pdf_ps_stack_object_t *arr = &s->cur[0].val.arr[i]; + pdf_array *parr = NULL; + pdf_num *n; + if (pdf_ps_obj_has_type(arr, PDF_PS_OBJ_ARRAY)) { + code = pdfi_array_alloc(s->pdfi_ctx, arr->size, &parr); + if (code < 0) + break; + pdfi_countup(parr); + + for (j = 0; j < arr->size; j++) { + if (pdf_ps_obj_has_type(&arr->val.arr[j], PDF_PS_OBJ_INTEGER)) { + code = pdfi_object_alloc(s->pdfi_ctx, PDF_INT, 0, (pdf_obj **)&n); + if (code >= 0) + n->value.i = arr->val.arr[j].val.i; + } + else if (pdf_ps_obj_has_type(&arr->val.arr[j], PDF_PS_OBJ_FLOAT)) { + code = pdfi_object_alloc(s->pdfi_ctx, PDF_REAL, 0, (pdf_obj **)&n); + if (code >= 0) + n->value.d = arr->val.arr[j].val.f; + } + else { + code = pdfi_object_alloc(s->pdfi_ctx, PDF_INT, 0, (pdf_obj **)&n); + if (code >= 0) + n->value.i = 0; + } + if (code < 0) + break; + pdfi_countup(n); + code = pdfi_array_put(s->pdfi_ctx, parr, j, (pdf_obj *)n); + pdfi_countdown(n); + if (code < 0) break; + } + } + if (code >= 0) + code = pdfi_array_put(s->pdfi_ctx, priv->u.t1.blendfontbbox, i, (pdf_obj *)parr); + pdfi_countdown(parr); + } + } + } + else if (s->cur[0].size >= 4) { + for (i = 0; i < 4; i++) { + if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { + bbox[i] = (double)s->cur[0].val.arr[i].val.i; + } + else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_FLOAT)) { + bbox[i] = (double)s->cur[0].val.arr[i].val.f; + } + } + priv->gsu.gst1.FontBBox.p.x = bbox[0]; + priv->gsu.gst1.FontBBox.p.y = bbox[1]; + priv->gsu.gst1.FontBBox.q.x = bbox[2]; + priv->gsu.gst1.FontBBox.q.y = bbox[3]; + } + } + } + else if (pdf_ps_name_cmp(&s->cur[-1], "FontType")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { + priv->gsu.gst1.FontType = s->cur[0].val.i; + priv->u.t1.pdfi_font_type = s->cur[0].val.i == 1 ? e_pdf_font_type1 : e_pdf_cidfont_type0; + } + else { + priv->gsu.gst1.FontType = 1; + priv->u.t1.pdfi_font_type = e_pdf_font_type1; + } + } + else if (pdf_ps_name_cmp(&s->cur[-1], "Encoding")) { + pdf_array *new_enc = NULL; + + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_NAME)) { + pdf_name *pname; + + code = pdfi_name_alloc(s->pdfi_ctx, (byte *) s->cur[0].val.name, s->cur[0].size, (pdf_obj **) &pname); + if (code >= 0) { + pdfi_countup(pname); + + code = pdfi_create_Encoding(s->pdfi_ctx, (pdf_obj *) pname, NULL, (pdf_obj **) &new_enc); + if (code >= 0) { + pdfi_countdown(priv->u.t1.Encoding); + priv->u.t1.Encoding = new_enc; + } + pdfi_countdown(pname); + } + } + else if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { + int i; + + code = pdfi_array_alloc(s->pdfi_ctx, s->cur[0].size, &new_enc); + if (code >= 0) { + pdfi_countup(new_enc); + for (i = 0; i < s->cur[0].size; i++) { + pdf_name *n = NULL; + byte *nm = (byte *) s->cur[0].val.arr[i].val.name; + int nlen = s->cur[0].val.arr[i].size; + + code = pdfi_name_alloc(s->pdfi_ctx, (byte *) nm, nlen, (pdf_obj **) &n); + if (code < 0) + break; + pdfi_countup(n); + code = pdfi_array_put(s->pdfi_ctx, new_enc, (uint64_t) i, (pdf_obj *) n); + pdfi_countdown(n); + if (code < 0) + break; + } + if (code < 0) { + pdfi_countdown(new_enc); + } + else { + pdfi_countdown(priv->u.t1.Encoding); + priv->u.t1.Encoding = new_enc; + new_enc = NULL; + } + } + } + } + else if (pdf_ps_name_cmp(&s->cur[-1], "UniqueID")) { + /* Ignore UniqueID if we already have a XUID */ + if (priv->gsu.gst1.UID.id >= 0) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { + uid_set_UniqueID(&priv->gsu.gst1.UID, s->cur[0].val.i); + } + } + } + break; + + case 9: + if (pdf_ps_name_cmp(&s->cur[-1], "PaintType")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { + priv->gsu.gst1.PaintType = s->cur[0].val.i; + } + } + else if (pdf_ps_name_cmp(&s->cur[-1], "StemSnapH")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { + int i, size = s->cur[0].size > 12 ? 12 : s->cur[0].size; + + for (i = 0; i < size; i++) { + if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { + priv->gsu.gst1.data.StemSnapH.values[i] = (float)s->cur[0].val.arr[i].val.i; + } + else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_FLOAT)) { + priv->gsu.gst1.data.StemSnapH.values[i] = s->cur[0].val.arr[i].val.f; + } + } + priv->gsu.gst1.data.StemSnapH.count = size; + } + } + else if (pdf_ps_name_cmp(&s->cur[-1], "StemSnapV")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { + int i, size = s->cur[0].size > 12 ? 12 : s->cur[0].size; + + for (i = 0; i < size; i++) { + if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { + priv->gsu.gst1.data.StemSnapV.values[i] = (float)s->cur[0].val.arr[i].val.i; + } + else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_FLOAT)) { + priv->gsu.gst1.data.StemSnapV.values[i] = s->cur[0].val.arr[i].val.f; + } + } + priv->gsu.gst1.data.StemSnapH.count = size; + } + } + else if (pdf_ps_name_cmp(&s->cur[-1], "BlueScale")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { + priv->gsu.gst1.data.BlueScale = (float)s->cur[0].val.i; + } + else if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_FLOAT)) { + priv->gsu.gst1.data.BlueScale = (float)s->cur[0].val.f; + } + } + break; + + case 10: + if (pdf_ps_name_cmp(&s->cur[-1], "FontMatrix")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY) && s->cur[0].size >= 6) { + int i; + double fmat[6] = { 0.001, 0, 0, 0.001, 0, 0 }; + for (i = 0; i < 6; i++) { + if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { + fmat[i] = (double)s->cur[0].val.arr[i].val.i; + } + else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_FLOAT)) { + fmat[i] = (double)s->cur[0].val.arr[i].val.f; + } + } + priv->gsu.gst1.FontMatrix.xx = fmat[0]; + priv->gsu.gst1.FontMatrix.xy = fmat[1]; + priv->gsu.gst1.FontMatrix.yx = fmat[2]; + priv->gsu.gst1.FontMatrix.yy = fmat[3]; + priv->gsu.gst1.FontMatrix.tx = fmat[4]; + priv->gsu.gst1.FontMatrix.ty = fmat[5]; + priv->gsu.gst1.orig_FontMatrix = priv->gsu.gst1.FontMatrix; + } + } + else if (pdf_ps_name_cmp(&s->cur[-1], "BlueValues")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { + int i, size = s->cur[0].size < 14 ? s->cur[0].size : 14; + + for (i = 0; i < size; i++) { + if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { + priv->gsu.gst1.data.BlueValues.values[i] = + (float)s->cur[0].val.arr[i].val.i; + } + else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_FLOAT)) { + priv->gsu.gst1.data.BlueValues.values[i] = s->cur[0].val.arr[i].val.f; + } + else { + if (i == 0) + priv->gsu.gst1.data.BlueValues.values[i] = 0; + else + priv->gsu.gst1.data.BlueValues.values[i] = priv->gsu.gst1.data.BlueValues.values[i - 1] + 1; + } + } + priv->gsu.gst1.data.BlueValues.count = size; + } + } + break; + + case 11: + if (pdf_ps_name_cmp(&s->cur[-1], "StrokeWidth")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_FLOAT)) { + priv->gsu.gst1.StrokeWidth = s->cur[0].val.f; + } + else if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_INTEGER)) { + priv->gsu.gst1.StrokeWidth = (float)s->cur[0].val.i; + } + } + break; + case 12: + if (pdf_ps_name_cmp(&s->cur[-1], "WeightVector")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { + int i; + for (i = 0; i < s->cur[0].size; i++) { + if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_INTEGER)) { + priv->gsu.gst1.data.WeightVector.values[i] = (float)s->cur[0].val.arr[i].val.i; + } + else if (pdf_ps_obj_has_type(&s->cur[0].val.arr[i], PDF_PS_OBJ_FLOAT)) { + priv->gsu.gst1.data.WeightVector.values[i] = s->cur[0].val.arr[i].val.f; + } + else { + priv->gsu.gst1.data.WeightVector.values[i] = 0; + } + } + priv->gsu.gst1.data.WeightVector.count = s->cur[0].size; + } + } + break; + case 14: + if (pdf_ps_name_cmp(&s->cur[-1], "BlendAxisTypes")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { + int i; + code = pdfi_array_alloc(s->pdfi_ctx, s->cur[0].size, &priv->u.t1.blendaxistypes); + if (code >= 0) { + pdfi_countup(priv->u.t1.blendaxistypes); + for (i = 0; i < s->cur[0].size; i++) { + pdf_ps_stack_object_t *so = &s->cur[0].val.arr[i]; + pdf_name *n; + if (pdf_ps_obj_has_type(so, PDF_PS_OBJ_NAME)) { + code = pdfi_object_alloc(s->pdfi_ctx, PDF_NAME, so->size, (pdf_obj **)&n); + if (code >= 0) { + pdfi_countup(n); + memcpy(n->data, so->val.name, so->size); + n->length = so->size; + code = pdfi_array_put(s->pdfi_ctx, priv->u.t1.blendaxistypes, i, (pdf_obj *)n); + pdfi_countdown(n); + } + } + if (code < 0) + break; + } + } + } + } + else if (pdf_ps_name_cmp(&s->cur[-1], "BlendDesignMap")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { + int i, j, k; + pdf_ps_stack_object_t *arr1 = &s->cur[0], *arr2, *arr3; + pdf_array *parr2, *parr3; + code = pdfi_array_alloc(s->pdfi_ctx, arr1->size, &priv->u.t1.blenddesignmap); + if (code >= 0) { + pdfi_countup(priv->u.t1.blenddesignmap); + for (i = 0; i < arr1->size && code >= 0; i++) { + if (pdf_ps_obj_has_type(&arr1->val.arr[i], PDF_PS_OBJ_ARRAY)) { + arr2 = &arr1->val.arr[i]; + code = pdfi_array_alloc(s->pdfi_ctx, arr2->size, &parr2); + if (code < 0) + break; + for (j = 0; j < arr2->size; j++) { + pdf_num *n; + + arr3 = &arr2->val.arr[j]; + code = pdfi_array_alloc(s->pdfi_ctx, arr3->size, &parr3); + if (code < 0) + break; + + for (k = 0; k < arr3->size; k++) { + if (pdf_ps_obj_has_type(&arr3->val.arr[k], PDF_PS_OBJ_INTEGER)) { + code = pdfi_object_alloc(s->pdfi_ctx, PDF_INT, 0, (pdf_obj **)&n); + if (code >= 0) + n->value.i = arr3->val.arr[k].val.i; + } + else if (pdf_ps_obj_has_type(&arr1->val.arr[i], PDF_PS_OBJ_FLOAT)) { + code = pdfi_object_alloc(s->pdfi_ctx, PDF_REAL, 0, (pdf_obj **)&n); + if (code >= 0) + n->value.d = arr3->val.arr[k].val.f; + } + else { + code = pdfi_object_alloc(s->pdfi_ctx, PDF_INT, 0, (pdf_obj **)&n); + if (code >= 0) + n->value.i = 0; + } + if (code < 0) + break; + pdfi_countup(n); + code = pdfi_array_put(s->pdfi_ctx, parr3, k, (pdf_obj *)n); + pdfi_countdown(n); + if (code < 0) + break; + } + if (code < 0) + break; + pdfi_countup(parr3); + code = pdfi_array_put(s->pdfi_ctx, parr2, j, (pdf_obj *)parr3); + pdfi_countdown(parr3); + } + if (code < 0) + break; + pdfi_countup(parr2); + code = pdfi_array_put(s->pdfi_ctx, priv->u.t1.blenddesignmap, i, (pdf_obj *)parr2); + pdfi_countdown(parr2); + } + } + } + } + } + break; + + case 20: + if (pdf_ps_name_cmp(&s->cur[-1], "BlendDesignPositions")) { + if (pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_ARRAY)) { + code = pdfi_array_alloc(s->pdfi_ctx, s->cur[0].size, &priv->u.t1.blenddesignpositions); + if (code >= 0) { + int i, j; + pdfi_countup(priv->u.t1.blenddesignpositions); + + for (i = 0; i < s->cur[0].size && code >= 0; i++) { + pdf_ps_stack_object_t *so = &s->cur[0].val.arr[i]; + + if (pdf_ps_obj_has_type(so, PDF_PS_OBJ_ARRAY)) { + pdf_array *sa; + code = pdfi_array_alloc(s->pdfi_ctx, so->size, &sa); + if (code >= 0) { + pdfi_countup(sa); + for (j = 0; j < so->size; j++) { + pdf_num *n; + if (pdf_ps_obj_has_type(&so->val.arr[j], PDF_PS_OBJ_INTEGER)) { + code = pdfi_object_alloc(s->pdfi_ctx, PDF_INT, 0, (pdf_obj **)&n); + if (code >= 0) + n->value.i = so->val.arr[j].val.i; + } + else if (pdf_ps_obj_has_type(&so->val.arr[j], PDF_PS_OBJ_FLOAT)) { + code = pdfi_object_alloc(s->pdfi_ctx, PDF_REAL, 0, (pdf_obj **)&n); + if (code >= 0) + n->value.d = so->val.arr[j].val.f; + } + else { + code = pdfi_object_alloc(s->pdfi_ctx, PDF_INT, 0, (pdf_obj **)&n); + if (code >= 0) + n->value.i = 0; + } + if (code < 0) + break; + pdfi_countup(n); + code = pdfi_array_put(s->pdfi_ctx, sa, j, (pdf_obj *)n); + pdfi_countdown(n); + if (code < 0) break; + } + } + if (code >= 0) { + pdfi_array_put(s->pdfi_ctx, priv->u.t1.blenddesignpositions, i, (pdf_obj *)sa); + } + pdfi_countdown(sa); + } + } + } + + } + } + break; + + default: + break; } } @@ -956,6 +1009,10 @@ ps_font_eexec_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte *bufend) stream *strm; int c; + if (bufend <= buf) { + return_error(gs_error_invalidfont); + } + strm = push_eexec_filter(mem, buf, bufend); while (1) { c = sgetc(strm); @@ -1121,7 +1178,6 @@ pdf_ps_RD_oper_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte *bufend) pdf_ps_obj_has_type(&s->cur[-1], PDF_PS_OBJ_NAME)) { pdf_string *str = NULL; pdf_obj *key = NULL; - bool key_known; size = s->cur[0].val.i; buf++; @@ -1133,24 +1189,21 @@ pdf_ps_RD_oper_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte *bufend) pdfi_countup(key); if (buf + size < bufend) { - code = pdfi_dict_known_by_key(s->pdfi_ctx, priv->u.t1.CharStrings, (pdf_name *)key, &key_known); - if (code >=0 && key_known != true) { - code = pdfi_object_alloc(s->pdfi_ctx, PDF_STRING, size, (pdf_obj **) &str); - if (code < 0) { - pdfi_countdown(key); - (void)pdf_ps_stack_pop(s, 2); - return code; - } - pdfi_countup(str); - memcpy(str->data, buf, size); - - code = pdfi_dict_put_obj(s->pdfi_ctx, priv->u.t1.CharStrings, key, (pdf_obj *) str); - if (code < 0) { - pdfi_countdown(str); - pdfi_countdown(key); - (void)pdf_ps_stack_pop(s, 2); - return code; - } + code = pdfi_object_alloc(s->pdfi_ctx, PDF_STRING, size, (pdf_obj **) &str); + if (code < 0) { + pdfi_countdown(key); + (void)pdf_ps_stack_pop(s, 2); + return code; + } + pdfi_countup(str); + memcpy(str->data, buf, size); + + code = pdfi_dict_put_obj(s->pdfi_ctx, priv->u.t1.CharStrings, key, (pdf_obj *) str, false); + if (code < 0) { + pdfi_countdown(str); + pdfi_countdown(key); + (void)pdf_ps_stack_pop(s, 2); + return code; } } pdfi_countdown(str); @@ -1177,7 +1230,7 @@ pdf_ps_put_oper_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte *bufend) pdf_ps_obj_has_type(&s->cur[-2], PDF_PS_OBJ_ARRAY) && pdf_ps_obj_has_type(&s->cur[-1], PDF_PS_OBJ_INTEGER) && pdf_ps_obj_has_type(&s->cur[0], PDF_PS_OBJ_NAME)) { - if (s->cur[-1].val.i < s->cur[-2].size) { + if (s->cur[-1].val.i >= 0 && s->cur[-1].val.i < s->cur[-2].size) { pdf_ps_make_name(&s->cur[-2].val.arr[s->cur[-1].val.i], s->cur[0].val.name, s->cur[0].size); } } diff --git a/pdf/pdf_fontps.h b/pdf/pdf_fontps.h index 45810a28..835f0066 100644 --- a/pdf/pdf_fontps.h +++ b/pdf/pdf_fontps.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2021 Artifex Software, Inc. +/* Copyright (C) 2020-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -201,6 +201,9 @@ static inline int pdf_ps_stack_push(pdf_ps_ctx_t *s) s->cur = s->stack + currsize - 1; s->toplim = s->stack + newsize - PDF_PS_STACK_GROW_SIZE; } + else { + return_error(gs_error_VMerror); + } } } s->cur++; diff --git a/pdf/pdf_func.c b/pdf/pdf_func.c index 9b7d5bb7..9537152e 100644 --- a/pdf/pdf_func.c +++ b/pdf/pdf_func.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -146,15 +146,15 @@ static int pdfi_parse_type4_func_stream(pdf_context *ctx, pdf_c_stream *function_stream, int depth, byte *ops, unsigned int *size) { int code; - byte c; + int c; char TokenBuffer[17]; unsigned int Size, IsReal; byte *clause = NULL; byte *p = (ops ? ops + *size : NULL); - do { - code = pdfi_read_bytes(ctx, &c, 1, 1, function_stream); - if (code < 0) + while (1) { + c = pdfi_read_byte(ctx, function_stream); + if (c < 0) break; switch(c) { case 0x20: @@ -202,32 +202,30 @@ pdfi_parse_type4_func_stream(pdf_context *ctx, pdf_c_stream *function_stream, in IsReal = 1; else IsReal = 0; - TokenBuffer[0] = c; - do { - code = pdfi_read_bytes(ctx, &c, 1, 1, function_stream); - if (code < 0) - return code; - if (code == 0) + TokenBuffer[0] = (byte)c; + while (1) { + c = pdfi_read_byte(ctx, function_stream); + if (c < 0) return_error(gs_error_syntaxerror); if (c == '.'){ if (IsReal == 1) - code = gs_error_syntaxerror; + return_error(gs_error_syntaxerror); else { - TokenBuffer[Size++] = c; + TokenBuffer[Size++] = (byte)c; IsReal = 1; } } else { if (c >= '0' && c <= '9') { - TokenBuffer[Size++] = c; + TokenBuffer[Size++] = (byte)c; } else break; } if (Size > NUMBERTOKENSIZE) return_error(gs_error_syntaxerror); - } while (code >= 0); + } TokenBuffer[Size] = 0x00; - pdfi_unread(ctx, function_stream, &c, 1); + pdfi_unread_byte(ctx, function_stream, (byte)c); if (IsReal == 1) { *size += put_float(&p, atof(TokenBuffer)); } else { @@ -239,21 +237,19 @@ pdfi_parse_type4_func_stream(pdf_context *ctx, pdf_c_stream *function_stream, in /* parse an operator */ Size = 1; - TokenBuffer[0] = c; - do { - code = pdfi_read_bytes(ctx, &c, 1, 1, function_stream); - if (code < 0) - return code; - if (code == 0) + TokenBuffer[0] = (byte)c; + while (1) { + c = pdfi_read_byte(ctx, function_stream); + if (c < 0) return_error(gs_error_syntaxerror); if (c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d || c == '{' || c == '}') break; - TokenBuffer[Size++] = c; + TokenBuffer[Size++] = (byte)c; if (Size > OPTOKENSIZE) return_error(gs_error_syntaxerror); - } while(code >= 0); + } TokenBuffer[Size] = 0x00; - pdfi_unread(ctx, function_stream, &c, 1); + pdfi_unread_byte(ctx, function_stream, (byte)c); for (i=0;i < NumOps;i++) { Op = (op_struct_t *)&ops_table[i]; if (Op->length < Size) @@ -278,9 +274,9 @@ pdfi_parse_type4_func_stream(pdf_context *ctx, pdf_c_stream *function_stream, in } break; } - } while (code >= 0); + } - return code; + return 0; } static int @@ -335,12 +331,13 @@ pdfi_build_function_4(pdf_context *ctx, gs_function_params_t * mnDR, ops[size] = PtCr_return; code = pdfi_close_memory_stream(ctx, data_source_buffer, function_stream); - if (code < 0) { - function_stream = NULL; + function_stream = NULL; + if (code < 0) goto function_4_error; - } params.ops.data = (const byte *)ops; + /* ops will now be freed with the function params, NULL ops now to avoid double free on error */ + ops = NULL; params.ops.size = size + 1; code = gs_function_PtCr_init(ppfn, ¶ms, ctx->memory); if (code < 0) @@ -459,7 +456,7 @@ pdfi_build_function_0(pdf_context *ctx, gs_function_params_t * mnDR, for (i=0;i<params.m;i++) { inputs *= params.Size[i]; } - samples = params.n * params.BitsPerSample; + samples = params.n * (uint64_t)params.BitsPerSample; samples *= inputs; samples = samples >> 3; if (samples > Length) { @@ -566,7 +563,18 @@ pdfi_build_function_3(pdf_context *ctx, gs_function_params_t * mnDR, for (i = 0; i < params.k; ++i) { pdf_obj * rsubfn = NULL; - code = pdfi_array_get(ctx, (pdf_array *)Functions, (int64_t)i, &rsubfn); + /* This is basically hacky. The test file /tests_private/pdf/pdf_1.7_ATS/WWTW61EC_file.pdf + * has a number of shadings on page 2. Although there are numerous shadings, they each use one + * of four functions. However, these functions are themselves type 3 functions with 255 + * sub-functions. Because our cache only has 200 entries (at this moment), this overfills + * the cache, ejecting all the cached objects (and then some). Which means that we throw + * out any previous shadings or functions, meaning that on every use we have to reread them. This is, + * obviously, slow. So in the hope that reuse of *sub_functions* is unlikely, we choose to + * read the subfunction without caching. This means the main shadings, and the functions, + * remain cached so we can reuse them saving an enormous amount of time. If we ever find a file + * which significantly reuses sub-functions we may need to revisit this. + */ + code = pdfi_array_get_nocache(ctx, (pdf_array *)Functions, (int64_t)i, &rsubfn); if (code < 0) goto function_3_error; diff --git a/pdf/pdf_gstate.c b/pdf/pdf_gstate.c index 7544aa48..c1444b4b 100644 --- a/pdf/pdf_gstate.c +++ b/pdf/pdf_gstate.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -17,6 +17,7 @@ #include "pdf_int.h" #include "pdf_doc.h" +#include "pdf_font_types.h" #include "pdf_gstate.h" #include "pdf_stack.h" #include "pdf_dict.h" @@ -113,7 +114,9 @@ pdfi_gstate_copy_cb(void *to, const void *from) */ if (igs_to != NULL) { pdfi_gstate_smask_free(igs_to); + pdfi_countdown(igs_to->current_font); *(pdfi_int_gstate *) igs_to = *igs_from; + pdfi_countup(igs_to->current_font); pdfi_gstate_smask_install(igs_to, igs_from->memory, igs_from->SMask, igs_from->GroupGState); } return 0; @@ -127,6 +130,7 @@ pdfi_gstate_free_cb(void *old, gs_memory_t * mem, gs_gstate *pgs) if (old == NULL) return; pdfi_gstate_smask_free(igs); + pdfi_countdown(igs->current_font); /* We need to use the graphics state memory, in case we are running under Ghostscript. */ gs_free_object(pgs->memory, igs, "pdfi_gstate_free"); } @@ -145,6 +149,8 @@ pdfi_gstate_set_client(pdf_context *ctx, gs_gstate *pgs) /* We need to use the graphics state memory, in case we are running under Ghostscript. */ igs = pdfi_gstate_alloc_cb(pgs->memory); + if (igs == NULL) + return_error(gs_error_VMerror); igs->ctx = ctx; gs_gstate_set_client(pgs, igs, &pdfi_gstate_procs, true /* TODO: client_has_pattern_streams ? */); return 0; @@ -252,93 +258,38 @@ int pdfi_op_Q(pdf_context *ctx) return code; } +/* We want pdfi_grestore() so we can track and warn of "too many Qs" + * in the interests of symmetry, we also have pdfi_gsave() + */ int pdfi_gsave(pdf_context *ctx) { - int code; - - code = gs_gsave(ctx->pgs); - - if(code < 0) - return code; - else { - pdfi_countup_current_font(ctx); - return 0; - } + return gs_gsave(ctx->pgs); } int pdfi_grestore(pdf_context *ctx) { - int code; - pdf_font *font = NULL, *font1 = NULL; + int code = 0; /* Make sure we have encountered as many gsave operations in this * stream as grestores. If not, log an error */ if (ctx->pgs->level > ctx->current_stream_save.gsave_level) { - font = pdfi_get_current_pdf_font(ctx); - code = gs_grestore(ctx->pgs); - - font1 = pdfi_get_current_pdf_font(ctx); - if (font != NULL && (font != font1 || ((pdf_obj *)font)->refcnt > 1)) { - /* TODO: This countdown might have been causing memory corruption (dangling pointer) - * but seems to be okay now. Maybe was fixed by other memory issue. 8-28-19 - * If you come upon this comment in the future and it all seems fine, feel free to - * clean this up... (delete comment, remove the commented out warning message, etc) - */ -#if REFCNT_DEBUG - dbgmprintf2(ctx->memory, "pdfi_grestore() counting down font UID %ld, refcnt %d\n", - font->UID, font->refcnt); -#endif - // dbgmprintf(ctx->memory, "WARNING pdfi_grestore() DISABLED pdfi_countdown (FIXME!)\n"); - pdfi_countdown(font); - } - - return code; } else { /* We don't throw an error here, we just ignore it and continue */ pdfi_set_warning(ctx, 0, NULL, W_PDF_TOOMANYQ, "pdfi_grestore", (char *)"ignoring q"); } - return 0; + return code; } -/* gs_setgstate is somewhat unpleasant from our point of view, because it replaces - * the content of the graphics state, without going through our pdfi_gsave/pdfi_grestore - * functionaltiy. In particular we replace the current font in the graphics state when - * we call it, and this means we *don't* count down the PDF_font object reference count - * which leads to an incorrect count and either memory leaks or early freeing. - * This function *requires* that the calling function will do a pdfi_gsave *before* - * calling pdfi_setgstate, and a pdfi_grestore *after* calling pdfi_gs_setgstate. - * it correctly increments/decrements the font reference counts for that condition - * and no other. - */ int pdfi_gs_setgstate(gs_gstate * pgs, const gs_gstate * pfrom) { - pdf_font *font = NULL; int code = 0; - /* We are going to release a reference to the font from the graphics state - * (if there is one) so count it down to keep things straight. - */ - if (pgs->font) { - font = (pdf_font *)pgs->font->client_data; - if (font) - pdfi_countdown(font); - } - code = gs_setgstate(pgs, pfrom); if (code < 0) return code; - /* The copied gstate may have contained a font, and we expect to do a - * pdfi_grestore on exit from here, which will count down the font - * so count it up now in preparation. - */ - if (pgs->font) { - font = (pdf_font *)pgs->font->client_data; - if (font) - pdfi_countup(font); - } return code; } @@ -924,6 +875,11 @@ static int pdfi_set_all_transfers(pdf_context *ctx, pdf_array *a, pdf_dict *page pdfi_countdown(o); goto exit; } + if (pfn[i]->params.m != 1 || pfn[i]->params.n != 1) { + pdfi_countdown(o); + code = gs_note_error(gs_error_rangecheck); + goto exit; + } } else { pdfi_countdown(o); code = gs_note_error(gs_error_typecheck); @@ -987,7 +943,6 @@ static int pdfi_set_all_transfers(pdf_context *ctx, pdf_array *a, pdf_dict *page } } exit: -// (void)pdfi_seek(ctx, ctx->main_stream, saved_stream_offset, SEEK_SET); for (i = 0; i < 4; i++) { pdfi_free_function(ctx, pfn[i]); } @@ -1006,6 +961,11 @@ static int pdfi_set_gray_transfer(pdf_context *ctx, pdf_obj *tr_obj, pdf_dict *p if (code < 0) return code; + if (pfn->params.m != 1 || pfn->params.n != 1) { + (void)pdfi_free_function(ctx, pfn); + return_error(gs_error_rangecheck); + } + gs_settransfer_remap(ctx->pgs, gs_mapped_transfer, false); for (i = 0; i < transfer_map_size; i++) { float v, f; @@ -1206,6 +1166,7 @@ static int build_type1_halftone(pdf_context *ctx, pdf_dict *halftone_dict, pdf_d gs_screen_enum *penum = NULL; gs_point pt; gx_transfer_map *pmap = NULL; + bool as; code = pdfi_dict_get_number(ctx, halftone_dict, "Frequency", &f); if (code < 0) @@ -1219,6 +1180,12 @@ static int build_type1_halftone(pdf_context *ctx, pdf_dict *halftone_dict, pdf_d if (code < 0) return code; + code = pdfi_dict_get_bool(ctx, halftone_dict, "AccurateScreens", &as); + if (code == gs_error_undefined) + as = 0; + else if (code < 0) + return code; + order = (gx_ht_order *)gs_alloc_bytes(ctx->memory, sizeof(gx_ht_order), "build_type1_halftone"); if (order == NULL) { code = gs_note_error(gs_error_VMerror); @@ -1275,6 +1242,7 @@ static int build_type1_halftone(pdf_context *ctx, pdf_dict *halftone_dict, pdf_d phtc->params.spot.transfer = (code > 0 ? (gs_mapping_proc) 0 : gs_mapped_transfer); phtc->params.spot.transfer_closure.proc = 0; phtc->params.spot.transfer_closure.data = 0; + phtc->params.spot.accurate_screens = as; phtc->type = ht_type_spot; code = pdfi_get_name_index(ctx, name, len, (unsigned int *)&phtc->cname); if (code < 0) @@ -2199,6 +2167,16 @@ static int GS_CA(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict if (code < 0) return code; + if (d1 > 1.0) { + pdfi_set_warning(ctx, 0, NULL, W_PDF_CA_OUTOFRANGE, "GS_CA", NULL); + d1 = 1.0; + } + + if (d1 < 0.0) { + pdfi_set_warning(ctx, 0, NULL, W_PDF_CA_OUTOFRANGE, "GS_CA", NULL); + d1 = 0.0; + } + code = gs_setstrokeconstantalpha(ctx->pgs, d1); return code; } @@ -2212,6 +2190,16 @@ static int GS_ca(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict if (code < 0) return code; + if (d1 > 1.0) { + pdfi_set_warning(ctx, 0, NULL, W_PDF_CA_OUTOFRANGE, "GS_ca", NULL); + d1 = 1.0; + } + + if (d1 < 0.0) { + pdfi_set_warning(ctx, 0, NULL, W_PDF_CA_OUTOFRANGE, "GS_ca", NULL); + d1 = 0.0; + } + code = gs_setfillconstantalpha(ctx->pgs, d1); return code; } diff --git a/pdf/pdf_gstate.h b/pdf/pdf_gstate.h index 37bc55a9..2b314971 100644 --- a/pdf/pdf_gstate.h +++ b/pdf/pdf_gstate.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -23,6 +23,7 @@ typedef struct int_gstate_s { pdf_context *ctx; pdf_dict *SMask; /* PDF only, null | dictionary | true */ gs_gstate *GroupGState; /* gstate associated with the SMask */ + pdf_font *current_font; /* This is the pdfi font pointed at by the "client_data" pointer in the gs_font in the gs_gstate */ gs_memory_t *memory; } pdfi_int_gstate; diff --git a/pdf/pdf_image.c b/pdf/pdf_image.c index a52e5e8e..554cc344 100644 --- a/pdf/pdf_image.c +++ b/pdf/pdf_image.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -17,6 +17,7 @@ #include "pdf_int.h" #include "pdf_stack.h" +#include "pdf_font_types.h" #include "pdf_gstate.h" #include "pdf_doc.h" #include "pdf_page.h" @@ -470,6 +471,14 @@ pdfi_get_image_info(pdf_context *ctx, pdf_stream *image_obj, goto errorExit; } } + if (info->Height < 0) { + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_IMAGEDICT, "pdfi_get_image_info", NULL); + if (ctx->args.pdfstoponwarning) { + code = gs_note_error(gs_error_rangecheck); + goto errorExit; + } + info->Height = 0; + } /* Required */ code = pdfi_dict_get_number2(ctx, image_dict, "Width", "W", &temp_f); @@ -483,6 +492,14 @@ pdfi_get_image_info(pdf_context *ctx, pdf_stream *image_obj, goto errorExit; } } + if (info->Width < 0) { + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_IMAGEDICT, "pdfi_get_image_info", NULL); + if (ctx->args.pdfstoponwarning) { + code = gs_note_error(gs_error_rangecheck); + goto errorExit; + } + info->Width = 0; + } /* Optional, default false */ code = pdfi_dict_get_bool2(ctx, image_dict, "ImageMask", "IM", &info->ImageMask); @@ -699,13 +716,13 @@ pdfi_render_image(pdf_context *ctx, gs_pixel_image_t *pim, pdf_c_stream *image_s { int code; gs_image_enum *penum = NULL; - byte *buffer = NULL; - uint64_t linelen, bytes_left; + uint64_t bytes_left; uint64_t bytes_used = 0; uint64_t bytes_avail = 0; gs_const_string plane_data[GS_IMAGE_MAX_COMPONENTS]; int main_plane=0, mask_plane=0; bool no_progress = false; + int min_left; #if DEBUG_IMAGES dbgmprintf(ctx->memory, "pdfi_render_image BEGIN\n"); @@ -715,8 +732,9 @@ pdfi_render_image(pdf_context *ctx, gs_pixel_image_t *pim, pdf_c_stream *image_s if (code < 0) return code; - /* Disable overprint mode for images */ - gs_setoverprintmode(ctx->pgs, 0); + /* Disable overprint mode for images that are not a mask */ + if (!ImageMask) + gs_setoverprintmode(ctx->pgs, 0); penum = gs_image_enum_alloc(ctx->memory, "pdfi_render_image (gs_image_enum_alloc)"); if (!penum) { @@ -761,37 +779,36 @@ pdfi_render_image(pdf_context *ctx, gs_pixel_image_t *pim, pdf_c_stream *image_s main_plane = 0; } - /* Going to feed the data one line at a time. - * This isn't required by gs_image_next_planes(), but it might make things simpler. - */ - linelen = pdfi_get_image_line_size((gs_data_image_t *)pim, comps); bytes_left = pdfi_get_image_data_size((gs_data_image_t *)pim, comps); - buffer = gs_alloc_bytes(ctx->memory, linelen, "pdfi_render_image (buffer)"); - if (!buffer) { - code = gs_note_error(gs_error_VMerror); - goto cleanupExit; - } while (bytes_left > 0) { uint used[GS_IMAGE_MAX_COMPONENTS]; - if (bytes_avail == 0) { - code = pdfi_read_bytes(ctx, buffer, 1, linelen, image_stream); - if (code < 0) { - dmprintf3(ctx->memory, - "WARNING: Image data error (pdfi_read_bytes) bytes_left=%ld, linelen=%ld, code=%d\n", - bytes_left, linelen, code); - goto cleanupExit; - } - if (code != linelen) { - dmprintf3(ctx->memory, "WARNING: Image data mismatch, bytes_left=%ld, linelen=%ld, code=%d\n", - bytes_left, linelen, code); - code = gs_note_error(gs_error_limitcheck); + while ((bytes_avail = sbufavailable(image_stream->s)) <= (min_left = sbuf_min_left(image_stream->s))) { + switch (image_stream->s->end_status) { + case 0: + s_process_read_buf(image_stream->s); + continue; + case EOFC: + case INTC: + case CALLC: + break; + default: + /* case ERRC: */ + code = gs_note_error(gs_error_ioerror); goto cleanupExit; } + break; /* for EOFC */ } - plane_data[main_plane].data = buffer + bytes_used; - plane_data[main_plane].size = linelen - bytes_used; + /* + * Note that in the EOF case, we can get here with no data + * available. + */ + if (bytes_avail >= min_left) + bytes_avail = (bytes_avail - min_left); /* may be 0 */ + + plane_data[main_plane].data = sbufptr(image_stream->s); + plane_data[main_plane].size = bytes_avail; code = gs_image_next_planes(penum, plane_data, used); if (code < 0) { @@ -810,6 +827,8 @@ pdfi_render_image(pdf_context *ctx, gs_pixel_image_t *pim, pdf_c_stream *image_s no_progress = false; } + (void)sbufskip(image_stream->s, used[main_plane]); + /* It might not always consume all the data, but so far the only case * I have seen with that was one that had mask data. * In that case, it used all of plane 0, and none of plane 1 on the first pass. @@ -820,14 +839,12 @@ pdfi_render_image(pdf_context *ctx, gs_pixel_image_t *pim, pdf_c_stream *image_s */ bytes_used = used[main_plane]; bytes_left -= bytes_used; - bytes_avail = linelen - bytes_used; + bytes_avail -= bytes_used; } code = 0; cleanupExit: - if (buffer) - gs_free_object(ctx->memory, buffer, "pdfi_render_image (buffer)"); if (penum) gs_image_cleanup_and_free_enum(penum, ctx->pgs); pdfi_grestore(ctx); @@ -876,7 +893,8 @@ pdfi_data_image_params(pdf_context *ctx, pdfi_image_info_t *info, float minval, maxval; /* TODO: Is there a less hacky way to identify Indexed case? */ - if (pcs && pcs->type == &gs_color_space_type_Indexed) { + if (pcs && (pcs->type == &gs_color_space_type_Indexed || + pcs->type == &gs_color_space_type_Indexed_Named)) { /* Default value is [0,N], where N=2^n-1, our hival (which depends on BPC)*/ minval = 0.0; maxval = (float)((1 << info->BPC) - 1); @@ -1230,7 +1248,7 @@ static int pdfi_create_JPX_Lab(pdf_context *ctx, pdf_obj **ColorSpace) } num = NULL; - code = pdfi_dict_put_obj(ctx, Params, (pdf_obj *)WhitePointName, (pdf_obj *)WhitePoint); + code = pdfi_dict_put_obj(ctx, Params, (pdf_obj *)WhitePointName, (pdf_obj *)WhitePoint, true); if (code < 0) goto cleanupExit; @@ -1268,7 +1286,7 @@ cleanupExit: static int pdfi_image_get_color(pdf_context *ctx, pdf_c_stream *source, pdfi_image_info_t *image_info, - int *comps, gs_color_space **pcs) + int *comps, ulong dictkey, gs_color_space **pcs) { int code = 0; pdfi_jpx_info_t *jpx_info = &image_info->jpx_info; @@ -1276,11 +1294,33 @@ pdfi_image_get_color(pdf_context *ctx, pdf_c_stream *source, pdfi_image_info_t * char *backup_color_name = NULL; bool using_enum_cs = false; - /* NOTE: Spec says ImageMask and ColorSpace mutually exclusive */ + /* NOTE: Spec says ImageMask and ColorSpace mutually exclusive + * We need to make sure we do not have both set or we could end up treating + * an image as a mask or vice versa and trying to use a non-existent colour space. + */ if (image_info->ImageMask) { - *comps = 1; - *pcs = NULL; - return 0; + if (image_info->ColorSpace == NULL) { + *comps = 1; + *pcs = NULL; + if (image_info->Mask) { + pdfi_countdown(image_info->Mask); + image_info->Mask = NULL; + } + return 0; + } else { + if (image_info->BPC != 1 || image_info->Mask != NULL) { + pdfi_set_error(ctx, 0, NULL, E_IMAGE_MASKWITHCOLOR, "pdfi_image_get_color", "BitsPerComonent is not 1, so treating as an image"); + image_info->ImageMask = 0; + } + else { + pdfi_set_error(ctx, 0, NULL, E_IMAGE_MASKWITHCOLOR, "pdfi_image_get_color", "BitsPerComonent is 1, so treating as a mask"); + pdfi_countdown(image_info->ColorSpace); + image_info->ColorSpace = NULL; + *comps = 1; + *pcs = NULL; + return 0; + } + } } ColorSpace = image_info->ColorSpace; @@ -1303,7 +1343,7 @@ pdfi_image_get_color(pdf_context *ctx, pdf_c_stream *source, pdfi_image_info_t * code = pdfi_create_icc_colorspace_from_stream(ctx, source, jpx_info->icc_offset, jpx_info->icc_length, jpx_info->comps, &dummy, - pcs); + dictkey, pcs); if (code < 0) { dmprintf2(ctx->memory, "WARNING JPXDecode: Error setting icc colorspace (offset=%d,len=%d)\n", @@ -1356,8 +1396,9 @@ pdfi_image_get_color(pdf_context *ctx, pdf_c_stream *source, pdfi_image_info_t * { char extra_info[gp_file_name_sizeof]; /* TODO: Could try DeviceRGB instead of erroring out? */ - gs_sprintf(extra_info, "**** Error: JPXDecode: Unsupported EnumCS %d\n", jpx_info->cs_enum); + gs_snprintf(extra_info, sizeof(extra_info), "**** Error: JPXDecode: Unsupported EnumCS %d\n", jpx_info->cs_enum); pdfi_set_error(ctx, 0, NULL, E_PDF_IMAGECOLOR_ERROR, "pdfi_image_get_color", extra_info); + code = gs_note_error(gs_error_rangecheck); goto cleanupExit; } } @@ -1396,9 +1437,16 @@ pdfi_image_get_color(pdf_context *ctx, pdf_c_stream *source, pdfi_image_info_t * if (ColorSpace->type == PDF_NAME) { pdf_name *name = (pdf_name *)ColorSpace; char str[100]; - memcpy(str, (const char *)name->data, name->length); - str[name->length] = '\0'; - dmprintf1(ctx->memory, "NAME:%s\n", str); + int length = name->length; + + if (length > 0) { + if (length > 99) + length = 99; + + memcpy(str, (const char *)name->data, length); + str[length] = '\0'; + dmprintf1(ctx->memory, "NAME:%s\n", str); + } } else { dmprintf(ctx->memory, "(not a name)\n"); } @@ -1540,7 +1588,7 @@ static int pdfi_do_image(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *stream_dict, pdf_stream *image_stream, pdf_c_stream *source, bool inline_image) { - pdf_c_stream *new_stream = NULL; + pdf_c_stream *new_stream = NULL, *SFD_stream = NULL; int code = 0, code1 = 0; int comps = 0; gs_color_space *pcs = NULL; @@ -1558,12 +1606,15 @@ pdfi_do_image(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *stream_dict, pdf_ uint64_t mask_size = 0; pdfi_int_gstate *igs = (pdfi_int_gstate *)ctx->pgs->client_data; bool transparency_group = false; + bool op_blend_mode = false; + int blend_mode; bool need_smask_cleanup = false; bool maybe_jpxdecode = false; pdfi_trans_state_t trans_state; int saved_intent; gs_offset_t stream_offset; float save_strokeconstantalpha = 0.0f, save_fillconstantalpha = 0.0f; + int trans_required; #if DEBUG_IMAGES dbgmprintf(ctx->memory, "pdfi_do_image BEGIN\n"); @@ -1660,7 +1711,7 @@ pdfi_do_image(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *stream_dict, pdf_ } /* Get the color for this image */ - code = pdfi_image_get_color(ctx, source, &image_info, &comps, &pcs); + code = pdfi_image_get_color(ctx, source, &image_info, &comps, image_stream->object_num, &pcs); if (code < 0) goto cleanupExit; @@ -1742,9 +1793,22 @@ pdfi_do_image(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *stream_dict, pdf_ code = pdfi_gs_setcolorspace(ctx, pcs); if (code < 0) goto cleanupExit; + + /* Try to set the device color again as that failure + is why we are here. If it fails again, something + is very wrong */ + code = gx_set_dev_color(ctx->pgs); + if (code < 0) + goto cleanupExit; } } } + else { + if (image_info.ImageMask == 0) { + code = gs_note_error(gs_error_undefined); + goto cleanupExit; + } + } /* Make a fake SMask dict if needed for JPXDecode */ if (ctx->page.has_transparency && image_info.is_JPXDecode && image_info.SMaskInData != 0) { @@ -1765,6 +1829,21 @@ pdfi_do_image(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *stream_dict, pdf_ goto cleanupExit; need_smask_cleanup = true; } + + /* If we are in an overprint situation this group will need + to have its blend mode set to compatible overprint. */ + if (ctx->page.needs_OP) { + if (pdfi_trans_okOPcs(ctx)) { + if (gs_currentfilloverprint(ctx->pgs)) { + blend_mode = gs_currentblendmode(ctx->pgs); + code = gs_setblendmode(ctx->pgs, BLEND_MODE_CompatibleOverprint); + op_blend_mode = true; + if (code < 0) + goto cleanupExit; + } + } + } + if (has_Matte) code = pdfi_trans_begin_isolated_group(ctx, true, pcs); else @@ -1887,8 +1966,15 @@ pdfi_do_image(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *stream_dict, pdf_ } /* Setup the data stream for the image data */ - if (!inline_image) + if (!inline_image) { pdfi_seek(ctx, source, stream_offset, SEEK_SET); + + code = pdfi_apply_SubFileDecode_filter(ctx, 0, "endstream", source, &SFD_stream, false); + if (code < 0) + goto cleanupExit; + source = SFD_stream; + } + code = pdfi_filter(ctx, image_stream, source, &new_stream, inline_image); if (code < 0) goto cleanupExit; @@ -1936,7 +2022,18 @@ pdfi_do_image(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *stream_dict, pdf_ code = pdfi_apply_imscale_filter(ctx, 0, image_info.Width, image_info.Height, s, &new_stream); if (code < 0) goto cleanupExit; + /* This adds the filter to the 'chain' of filter we created. When we close this filter + * it closes all the filters in the chain, back to either the SubFileDecode filter or the + * original main stream. If we don't patch this up then we leak memory for any filters + * applied in pdfi_filter above. + */ + new_stream->original = s->original; + /* We'e created a new 'new_stream', which is a C stream, to hold the filter chain + * but we still need to free the original C stream 'wrapper' we created with pdfi_filter() + * Do that now. + */ + gs_free_object(ctx->memory, s, "free stream replaced by adding image scaling filter"); image_info.Width *= 4; image_info.Height *= 4; pim->Width *= 4; @@ -1947,9 +2044,13 @@ pdfi_do_image(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *stream_dict, pdf_ } } - code = pdfi_image_setup_trans(ctx, &trans_state); - if (code < 0) - goto cleanupExit; + trans_required = pdfi_trans_required(ctx); + + if (trans_required) { + code = pdfi_image_setup_trans(ctx, &trans_state); + if (code < 0) + goto cleanupExit; + } /* Render the image */ code = pdfi_render_image(ctx, pim, new_stream, @@ -1960,9 +2061,11 @@ pdfi_do_image(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *stream_dict, pdf_ dmprintf1(ctx->memory, "WARNING: pdfi_do_image: error %d from pdfi_render_image\n", code); } - code1 = pdfi_trans_teardown(ctx, &trans_state); - if (code == 0) - code = code1; + if (trans_required) { + code1 = pdfi_trans_teardown(ctx, &trans_state); + if (code == 0) + code = code1; + } cleanupExit: if (code < 0) @@ -1980,8 +2083,14 @@ pdfi_do_image(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *stream_dict, pdf_ pdfi_trans_end_smask_notify(ctx); } + if (op_blend_mode) { + code = gs_setblendmode(ctx->pgs, blend_mode); + } + if (new_stream) pdfi_close_file(ctx, new_stream); + if (SFD_stream) + pdfi_close_file(ctx, SFD_stream); if (mask_buffer) gs_free_object(ctx->memory, mask_buffer, "pdfi_do_image (mask_buffer)"); @@ -2017,7 +2126,7 @@ int pdfi_ID(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, pdf_c_ * been decrypted, we don't need to decrypt any strings contained in the * inline dictionary. */ - code = pdfi_dict_from_stack(ctx, 0, 0); + code = pdfi_dict_from_stack(ctx, 0, 0, false); if (code < 0) /* pdfi_dict_from_stack cleans up the stack so we don't need to in case of an error */ return code; @@ -2200,6 +2309,8 @@ static int pdfi_form_stream_hack(pdf_context *ctx, pdf_dict *form_dict, pdf_stre return 0; if (!ctx->args.pdfstoponerror) { + pdf_obj *Parent = NULL; + pdf_dict *d = NULL; pdf_dict *stream_dict = NULL; code = pdfi_dict_knownget_type(ctx, form_dict, "Contents", PDF_STREAM, @@ -2209,6 +2320,27 @@ static int pdfi_form_stream_hack(pdf_context *ctx, pdf_dict *form_dict, pdf_stre code = gs_note_error(gs_error_typecheck); goto exit; } + + d = form_dict; + pdfi_countup(d); + do { + code = pdfi_dict_knownget(ctx, d, "Parent", (pdf_obj **)&Parent); + if (code > 0) { + if (Parent->object_num == stream_obj->object_num) { + pdfi_countdown(d); + pdfi_countdown(Parent); + pdfi_set_error(ctx, 0, NULL, E_PDF_BADSTREAMDICT, "pdfi_form_stream_hack", NULL); + code = gs_note_error(gs_error_undefined); + goto exit; + } + pdfi_countdown(d); + d = (pdf_dict *)Parent; + } else { + pdfi_countdown(d); + break; + } + } while (1); + code = pdfi_dict_from_obj(ctx, (pdf_obj *)stream_obj, &stream_dict); if (code < 0) { pdfi_set_error(ctx, 0, NULL, E_PDF_BADSTREAMDICT, "pdfi_form_stream_hack", NULL); @@ -2402,7 +2534,7 @@ int pdfi_do_image_or_form(pdf_context *ctx, pdf_dict *stream_dict, gs_offset_t savedoffset; if (xobject_obj->type != PDF_STREAM) { - gs_note_error(gs_error_typecheck); + code = gs_note_error(gs_error_typecheck); goto exit; } savedoffset = pdfi_tell(ctx->main_stream); @@ -2480,6 +2612,7 @@ int pdfi_Do(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) goto exit; } + code = pdfi_loop_detector_cleartomark(ctx); /* NOTE: Used to have a pdfi_gsave/pdfi_grestore around this, but it actually makes * things render incorrectly (and isn't in the PS code). * It also causes demo.ai.pdf to crash. @@ -2494,14 +2627,12 @@ int pdfi_Do(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) // pdfi_gsave(ctx); code = pdfi_do_image_or_form(ctx, stream_dict, page_dict, o); // pdfi_grestore(ctx); - if (code < 0) - goto exit; + pdfi_countdown(o); + pdfi_pop(ctx, 1); + return code; - exit: - if (code < 0) - (void)pdfi_loop_detector_cleartomark(ctx); - else - code = pdfi_loop_detector_cleartomark(ctx); +exit: + (void)pdfi_loop_detector_cleartomark(ctx); exit1: /* No need to countdown 'n' because that points to the stack object, and we're going to pop that */ pdfi_countdown(o); diff --git a/pdf/pdf_int.c b/pdf/pdf_int.c index 555653b6..9493922b 100644 --- a/pdf/pdf_int.c +++ b/pdf/pdf_int.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -25,7 +25,7 @@ #include "pdf_image.h" #include "pdf_shading.h" #include "pdf_font.h" -#include "pdf_font.h" +#include "pdf_font_types.h" #include "pdf_cmap.h" #include "pdf_text.h" #include "pdf_gstate.h" @@ -37,6 +37,8 @@ #include "pdf_optcontent.h" #include "pdf_sec.h" +#include "gsstate.h" /* for gs_gstate_free */ + /* we use -ve returns for error, 0 for success and +ve for 'take an action' */ /* Defining tis return so we do not need to define a new error */ #define REPAIRED_KEYWORD 1 @@ -106,83 +108,114 @@ static int fromhex(char c) */ int pdfi_skip_white(pdf_context *ctx, pdf_c_stream *s) { - uint32_t read = 0; - int32_t bytes = 0; - byte c; + int c; do { - bytes = pdfi_read_bytes(ctx, &c, 1, 1, s); - if (bytes < 0) - return_error(gs_error_ioerror); - if (bytes == 0) + c = pdfi_read_byte(ctx, s); + if (c < 0) return 0; - read += bytes; - } while (bytes != 0 && iswhite(c)); + } while (iswhite(c)); - if (read > 0) - pdfi_unread(ctx, s, &c, 1); + pdfi_unread_byte(ctx, s, (byte)c); return 0; } int pdfi_skip_eol(pdf_context *ctx, pdf_c_stream *s) { - uint32_t read = 0; - int32_t bytes = 0; - byte c; + int c; do { - bytes = pdfi_read_bytes(ctx, &c, 1, 1, s); - if (bytes == 0) - return 0; - if (read) { - if (c == 0x0A) - return 0; - pdfi_unread(ctx, s, &c, 1); + c = pdfi_read_byte(ctx, s); + if (c < 0 || c == 0x0a) return 0; - } - if (c == 0x0D) - read++; - } while (c != 0x0A); + } while (c != 0x0d); + c = pdfi_read_byte(ctx, s); + if (c == 0x0a) + return 0; + if (c >= 0) + pdfi_unread_byte(ctx, s, (byte)c); return 0; } +/* Fast(ish) but inaccurate strtof, with Adobe overflow handling, + * lifted from MuPDF. */ +static float acrobat_compatible_atof(char *s) +{ + int neg = 0; + int i = 0; + + while (*s == '-') { + neg = 1; + ++s; + } + while (*s == '+') { + ++s; + } + + while (*s >= '0' && *s <= '9') { + /* We deliberately ignore overflow here. + * Tests show that Acrobat handles * overflows in exactly the same way we do: + * 123450000000000000000678 is read as 678. + */ + i = i * 10 + (*s - '0'); + ++s; + } + + if (*s == '.') { + float v = (float)i; + float n = 0; + float d = 1; + ++s; + while (*s >= '0' && *s <= '9') { + n = 10 * n + (*s - '0'); + d = 10 * d; + ++s; + } + v += n / d; + return neg ? -v : v; + } else { + return (float)(neg ? -i : i); + } +} + static int pdfi_read_num(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_num, uint32_t indirect_gen) { byte Buffer[256]; - char Max[256]; - int MaxLen = 0; unsigned short index = 0; - short bytes; bool real = false; bool has_decimal_point = false; bool has_exponent = false; unsigned short exponent_index = 0; pdf_num *num; - int code = 0, malformed = false, doubleneg = false, recovered = false; + int code = 0, malformed = false, doubleneg = false, recovered = false, negative = false; + int int_val = 0; pdfi_skip_white(ctx, s); do { - bytes = pdfi_read_bytes(ctx, (byte *)&Buffer[index], 1, 1, s); - if (bytes == 0 && s->eof) { + int c = pdfi_read_byte(ctx, s); + if (c == EOFC) { Buffer[index] = 0x00; break; } - if (bytes <= 0) + if (c < 0) return_error(gs_error_ioerror); - if (iswhite((char)Buffer[index])) { + if (iswhite(c)) { + Buffer[index] = 0x00; + break; + } else if (isdelimiter(c)) { + pdfi_unread_byte(ctx, s, (byte)c); Buffer[index] = 0x00; break; - } else { - if (isdelimiter((char)Buffer[index])) { - pdfi_unread(ctx, s, (byte *)&Buffer[index], 1); - Buffer[index] = 0x00; - break; - } } - if (Buffer[index] == '.') { + Buffer[index] = (byte)c; + + if (c >= '0' && c <= '9') { + if (!(malformed && recovered)) + int_val = int_val*10 + c - '0'; + } else if (c == '.') { if (has_decimal_point == true) { if (ctx->args.pdfstoponerror) return_error(gs_error_syntaxerror); @@ -191,7 +224,7 @@ static int pdfi_read_num(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_nu has_decimal_point = true; real = true; } - } else if (Buffer[index] == 'e' || Buffer[index] == 'E') { + } else if (c == 'e' || c == 'E') { /* TODO: technically scientific notation isn't in PDF spec, * but gs seems to accept it, so we should also? */ @@ -205,32 +238,47 @@ static int pdfi_read_num(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_nu exponent_index = index; real = true; } - } else if (Buffer[index] == '-' || Buffer[index] == '+') { + } else if (c == '-') { + /* Any - sign not at the start of the string, or just after an exponent + * indicates a malformed number. */ if (!(index == 0 || (has_exponent && index == exponent_index+1))) { + pdfi_set_error(ctx, 0, NULL, E_PDF_MALFORMEDNUMBER, "pdfi_read_num", NULL); if (ctx->args.pdfstoponerror) return_error(gs_error_syntaxerror); - /* Acrobat weirdness. We need to know if a number starts with two - signs - * because Acrobat treats real and integers defined this way differently! - * Double-negated integers are treated as 0, and reals are treated as if - * they had one negative sign. We can't tell whether the number is a real - * or not yet, we do that below. - */ - pdfi_set_error(ctx, 0, NULL, E_PDF_MALFORMEDNUMBER, "pdfi_read_num", NULL); - if (Buffer[index - 1] == '-') { - doubleneg = true; - index -= 1; + if (Buffer[index - 1] != '-') { + /* We are parsing a number line 123-56. We should continue parsing, but + * ignore anything from the second -. */ + malformed = true; + Buffer[index] = 0; + recovered = true; } - else { + } + if (!has_exponent && !(malformed && recovered)) { + doubleneg = negative; + negative = 1; + } + } else if (c == '+') { + if (index == 0 || (has_exponent && index == exponent_index+1)) { + /* Just drop the + it's pointless, and it'll get in the way + * of our negation handling for floats. */ + index--; + } else { + pdfi_set_error(ctx, 0, NULL, E_PDF_MALFORMEDNUMBER, "pdfi_read_num", NULL); + if (ctx->args.pdfstoponerror) + return_error(gs_error_syntaxerror); + if (Buffer[index - 1] != '-') { + /* We are parsing a number line 123-56. We should continue parsing, but + * ignore anything from the second -. */ malformed = true; - Buffer[index] = 0x00; + Buffer[index] = 0; recovered = true; } } - } else if (Buffer[index] < 0x30 || Buffer[index] > 0x39) { + } else { pdfi_set_error(ctx, 0, NULL, E_PDF_MISSINGWHITESPACE, "pdfi_read_num", (char *)"Ignoring missing white space while parsing number"); if (ctx->args.pdfstoponerror) return_error(gs_error_syntaxerror); - pdfi_unread(ctx, s, (byte *)&Buffer[index], 1); + pdfi_unread_byte(ctx, s, (byte)c); Buffer[index] = 0x00; break; } @@ -238,38 +286,6 @@ static int pdfi_read_num(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_nu return_error(gs_error_syntaxerror); } while(1); - if (!real && index > 7) { - /* Check for integer overflow, represent as real if so */ - gs_sprintf(Max, "%d", (max_uint >> 1)); - MaxLen = strlen((const char *)Max); - - if (index >= MaxLen) { - int j = 0; - - if (Buffer[j] == '-') - j++; - - while (Buffer[j] == '0') - j++; - - if (index - j > MaxLen) - real = true; - - if (index - j == MaxLen) - { - real = false; - - for (;j< MaxLen; j++) { - if (Buffer[j] == Max[j]) - continue; - if (Buffer[j] > Max[j]) - real=true; - break; - } - } - } - } - if (real && (!malformed || (malformed && recovered))) code = pdfi_object_alloc(ctx, PDF_REAL, 0, (pdf_obj **)&num); else @@ -278,67 +294,21 @@ static int pdfi_read_num(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_nu return code; if ((malformed && !recovered) || (!real && doubleneg)) { - char extra_info[gp_file_name_sizeof]; - - gs_sprintf(extra_info, "Treating malformed number %s as 0", Buffer); - pdfi_set_error(ctx, 0, NULL, E_PDF_MALFORMEDNUMBER, "pdfi_read_num", extra_info); + pdfi_set_error_var(ctx, 0, NULL, E_PDF_MALFORMEDNUMBER, "pdfi_read_num", "Treating malformed number %s as 0", Buffer); num->value.i = 0; - } else { - if (real) { - float tempf; - char *dot = NULL, mfloat[256]; /* We limit numbers to 255 above so it can't exceed this */ - - /* Check for overflow */ - gs_sprintf(mfloat, "%f", MAX_FLOAT); - dot = strstr(mfloat, "."); - *dot = 0x00; - - dot = strstr((char *)Buffer, "."); - if (dot != NULL) - *dot = 0x00; - if (strlen((char *)Buffer) > strlen(mfloat)) { - if (dot != NULL) - *dot = '.'; - if (ctx->args.pdfdebug) - dmprintf1(ctx->memory, "overflow reading real number : %s\n", Buffer); - pdfi_set_warning(ctx, 0, NULL, W_PDF_OVERFLOW_REAL, "pdfi_read_num", NULL); - num->value.d = 0.0; - } - else { - if (strlen((char *)Buffer) == strlen(mfloat)) { - int fi = 0; - - for (fi = 0;fi < strlen((const char *)Buffer);fi++) { - if (Buffer[fi] > mfloat[fi]) { - if (dot != NULL) - *dot = '.'; - if (ctx->args.pdfdebug) - dmprintf1(ctx->memory, "overflow reading real number : %s\n", Buffer); - pdfi_set_warning(ctx, 0, NULL, W_PDF_OVERFLOW_REAL, "pdfi_read_num", NULL); - num->value.d = 0.0; - } - } - } - if (dot != NULL) - *dot = '.'; - if (sscanf((const char *)Buffer, "%f", &tempf) == 0) { - if (ctx->args.pdfdebug) - dmprintf1(ctx->memory, "failed to read real number : %s\n", Buffer); - pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_REAL, "pdfi_read_num", NULL); - num->value.d = 0.0; - } - num->value.d = tempf; - } + } else if (has_exponent) { + float f; + if (sscanf((char *)Buffer, "%g", &f) == 1) { + num->value.d = f; } else { - int tempi; - if (sscanf((const char *)Buffer, "%d", &tempi) == 0) { - if (ctx->args.pdfdebug) - dmprintf1(ctx->memory, "failed to read integer : %s\n", Buffer); - gs_free_object(OBJ_MEMORY(num), num, "pdfi_read_num error"); - return_error(gs_error_syntaxerror); - } - num->value.i = tempi; + pdfi_set_error_var(ctx, 0, NULL, E_PDF_MALFORMEDNUMBER, "pdfi_read_num", "Treating malformed float %s as 0", Buffer); + num->value.d = 0; } + } else if (real) { + num->value.d = acrobat_compatible_atof((char *)Buffer); + } else { + /* The doubleneg case is taken care of above. */ + num->value.i = negative ? -int_val : int_val; } if (ctx->args.pdfdebug) { if (real) @@ -371,31 +341,29 @@ static int pdfi_read_name(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_n return_error(gs_error_VMerror); do { - bytes = pdfi_read_bytes(ctx, (byte *)&Buffer[index], 1, 1, s); - if (bytes == 0 && s->eof) + int c = pdfi_read_byte(ctx, s); + if (c < 0) break; - if (bytes <= 0) - return_error(gs_error_ioerror); - if (iswhite((char)Buffer[index])) { + if (iswhite((char)c)) { + Buffer[index] = 0x00; + break; + } else if (isdelimiter((char)c)) { + pdfi_unread_byte(ctx, s, (char)c); Buffer[index] = 0x00; break; - } else { - if (isdelimiter((char)Buffer[index])) { - pdfi_unread(ctx, s, (byte *)&Buffer[index], 1); - Buffer[index] = 0x00; - break; - } } + Buffer[index] = (char)c; /* Check for and convert escaped name characters */ - if (Buffer[index] == '#') { + if (c == '#') { byte NumBuf[2]; bytes = pdfi_read_bytes(ctx, (byte *)&NumBuf, 1, 2, s); if (bytes < 2 || (!ishex(NumBuf[0]) || !ishex(NumBuf[1]))) { pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_NAME_ESCAPE, "pdfi_read_name", NULL); pdfi_unread(ctx, s, (byte *)NumBuf, bytes); + /* This leaves the name buffer with a # in it, rather than anything sane! */ } else Buffer[index] = (fromhex(NumBuf[0]) << 4) + fromhex(NumBuf[1]); @@ -439,12 +407,11 @@ static int pdfi_read_name(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_n static int pdfi_read_hexstring(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_num, uint32_t indirect_gen) { - char *Buffer, *NewBuf = NULL, HexBuf[2]; + char *Buffer, *NewBuf = NULL; unsigned short index = 0; - short bytes = 0; uint32_t size = 256; pdf_string *string = NULL; - int code; + int code, hex0, hex1; Buffer = (char *)gs_alloc_bytes(ctx->memory, size, "pdfi_read_hexstring"); if (Buffer == NULL) @@ -455,44 +422,36 @@ static int pdfi_read_hexstring(pdf_context *ctx, pdf_c_stream *s, uint32_t indir do { do { - bytes = pdfi_read_bytes(ctx, (byte *)HexBuf, 1, 1, s); - if (bytes == 0 && s->eof) + hex0 = pdfi_read_byte(ctx, s); + if (hex0 < 0) break; - if (bytes <= 0) { - code = gs_note_error(gs_error_ioerror); - goto exit; - } - } while(iswhite(HexBuf[0])); - if (bytes == 0 && s->eof) + } while(iswhite(hex0)); + if (hex0 < 0) break; - if (HexBuf[0] == '>') + if (hex0 == '>') break; if (ctx->args.pdfdebug) - dmprintf1(ctx->memory, "%c", HexBuf[0]); + dmprintf1(ctx->memory, "%c", (char)hex0); do { - bytes = pdfi_read_bytes(ctx, (byte *)&HexBuf[1], 1, 1, s); - if (bytes == 0 && s->eof) + hex1 = pdfi_read_byte(ctx, s); + if (hex1 < 0) break; - if (bytes <= 0) { - code = gs_note_error(gs_error_ioerror); - goto exit; - } - } while(iswhite(HexBuf[1])); - if (bytes == 0 && s->eof) + } while(iswhite(hex1)); + if (hex1 < 0) break; - if (!ishex(HexBuf[0]) || !ishex(HexBuf[1])) { + if (!ishex(hex0) || !ishex(hex1)) { code = gs_note_error(gs_error_syntaxerror); goto exit; } if (ctx->args.pdfdebug) - dmprintf1(ctx->memory, "%c", HexBuf[1]); + dmprintf1(ctx->memory, "%c", (char)hex1); - Buffer[index] = (fromhex(HexBuf[0]) << 4) + fromhex(HexBuf[1]); + Buffer[index] = (fromhex(hex0) << 4) + fromhex(hex1); if (index++ >= size - 1) { NewBuf = (char *)gs_alloc_bytes(ctx->memory, size + 256, "pdfi_read_hexstring"); @@ -534,12 +493,11 @@ static int pdfi_read_hexstring(pdf_context *ctx, pdf_c_stream *s, uint32_t indir static int pdfi_read_string(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_num, uint32_t indirect_gen) { - char *Buffer, *NewBuf = NULL, octal[3]; + char *Buffer, *NewBuf = NULL; unsigned short index = 0; - short bytes = 0; uint32_t size = 256; pdf_string *string = NULL; - int code, octal_index = 0, nesting = 0; + int c, code, nesting = 0; bool escape = false, skip_eol = false, exit_loop = false; Buffer = (char *)gs_alloc_bytes(ctx->memory, size, "pdfi_read_string"); @@ -559,140 +517,109 @@ static int pdfi_read_string(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect size += 256; } - bytes = pdfi_read_bytes(ctx, (byte *)&Buffer[index], 1, 1, s); + c = pdfi_read_byte(ctx, s); - if (bytes == 0 && s->eof) - break; - if (bytes <= 0) { + if (c < 0) { + if (nesting > 0) + pdfi_set_error(ctx, 0, NULL, E_PDF_UNESCAPEDSTRING, "pdfi_read_string", NULL); Buffer[index] = 0x00; break; } if (skip_eol) { - if (Buffer[index] == 0x0a || Buffer[index] == 0x0d) + if (c == 0x0a || c == 0x0d) continue; skip_eol = false; } + Buffer[index] = (char)c; if (escape) { escape = false; - if (Buffer[index] == 0x0a || Buffer[index] == 0x0d) { - skip_eol = true; - continue; - } - if (octal_index) { - byte dummy[2]; - dummy[0] = '\\'; - dummy[1] = Buffer[index]; - code = pdfi_unread(ctx, s, dummy, 2); - if (code < 0) { - gs_free_object(ctx->memory, Buffer, "pdfi_read_string"); - return code; - } - Buffer[index] = octal[0]; - if (octal_index == 2) - Buffer[index] = (Buffer[index] * 8) + octal[1]; - octal_index = 0; - } else { - switch (Buffer[index]) { - case 'n': - Buffer[index] = 0x0a; - break; - case 'r': - Buffer[index] = 0x0d; - break; - case 't': - Buffer[index] = 0x09; - break; - case 'b': - Buffer[index] = 0x08; - break; - case 'f': - Buffer[index] = 0x0c; - break; - case '(': - case ')': - case '\\': - break; - default: - if (Buffer[index] >= 0x30 && Buffer[index] <= 0x37) { - octal[octal_index] = Buffer[index] - 0x30; - octal_index++; - continue; - } - /* PDF Reference, literal strings, if the character following a - * escape \ character is not recognised, then it is ignored. - */ - escape = false; - index++; - continue; - } - } - } else { - switch(Buffer[index]) { + switch (Buffer[index]) { case 0x0a: case 0x0d: - if (octal_index != 0) { - code = pdfi_unread(ctx, s, (byte *)&Buffer[index], 1); - if (code < 0) { - gs_free_object(ctx->memory, Buffer, "pdfi_read_string"); - return code; - } - Buffer[index] = octal[0]; - if (octal_index == 2) - Buffer[index] = (Buffer[index] * 8) + octal[1]; - octal_index = 0; - } else { - Buffer[index] = 0x0a; - skip_eol = true; - } + skip_eol = true; + continue; + case 'n': + Buffer[index] = 0x0a; + break; + case 'r': + Buffer[index] = 0x0d; + break; + case 't': + Buffer[index] = 0x09; + break; + case 'b': + Buffer[index] = 0x08; break; + case 'f': + Buffer[index] = 0x0c; + break; + case '(': case ')': - if (octal_index != 0) { - code = pdfi_unread(ctx, s, (byte *)&Buffer[index], 1); - if (code < 0) { - gs_free_object(ctx->memory, Buffer, "pdfi_read_string"); - return code; - } - Buffer[index] = octal[0]; - if (octal_index == 2) - Buffer[index] = (Buffer[index] * 8) + octal[1]; - octal_index = 0; + case '\\': + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + /* Octal chars can be 1, 2 or 3 chars in length, terminated either + * by being 3 chars long, EOFC, or a non-octal char. We do not allow + * line breaks in the middle of octal chars. */ + int c1 = pdfi_read_byte(ctx, s); + c -= '0'; + if (c1 < 0) { + /* Nothing to do, or unread */ + } else if (c1 < '0' || c1 > '7') { + pdfi_unread_byte(ctx, s, (char)c1); } else { - if (nesting == 0) { - Buffer[index] = 0x00; - exit_loop = true; + c = c*8 + c1 - '0'; + c1 = pdfi_read_byte(ctx, s); + if (c1 < 0) { + /* Nothing to do, or unread */ + } else if (c1 < '0' || c1 > '7') { + pdfi_unread_byte(ctx, s, (char)c1); } else - nesting--; + c = c*8 + c1 - '0'; } + Buffer[index] = c; + break; + } + default: + /* PDF Reference, literal strings, if the character following a + * escape \ character is not recognised, then it is ignored. + */ + escape = false; + index++; + continue; + } + } else { + switch(Buffer[index]) { + case 0x0d: + Buffer[index] = 0x0a; + /*fallthrough*/ + case 0x0a: + skip_eol = true; + break; + case ')': + if (nesting == 0) { + Buffer[index] = 0x00; + exit_loop = true; + } else + nesting--; break; case '\\': escape = true; continue; case '(': - pdfi_set_error(ctx, 0, NULL, E_PDF_UNESCAPEDSTRING, "pdfi_read_string", NULL); nesting++; break; default: - if (octal_index) { - if (Buffer[index] >= 0x30 && Buffer[index] <= 0x37) { - octal[octal_index] = Buffer[index] - 0x30; - if (++octal_index < 3) - continue; - Buffer[index] = (octal[0] * 64) + (octal[1] * 8) + octal[2]; - octal_index = 0; - } else { - code = pdfi_unread(ctx, s, (byte *)&Buffer[index], 1); - if (code < 0) { - gs_free_object(ctx->memory, Buffer, "pdfi_read_string"); - return code; - } - Buffer[index] = octal[0]; - if (octal_index == 2) - Buffer[index] = (Buffer[index] * 8) + octal[1]; - octal_index = 0; - } - } break; } } @@ -744,6 +671,9 @@ int pdfi_read_dict(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_num, uin code = pdfi_read_token(ctx, s, indirect_num, indirect_gen); if (code < 0) return code; + if (code == 0) + return_error(gs_error_syntaxerror); + if (ctx->stack_top[-1]->type != PDF_DICT_MARK) return_error(gs_error_typecheck); depth = pdfi_count_stack(ctx); @@ -752,32 +682,29 @@ int pdfi_read_dict(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_num, uin code = pdfi_read_token(ctx, s, indirect_num, indirect_gen); if (code < 0) return code; + if (code == 0) + return_error(gs_error_syntaxerror); } while(pdfi_count_stack(ctx) > depth); return 0; } int pdfi_skip_comment(pdf_context *ctx, pdf_c_stream *s) { - byte Buffer; - short bytes = 0; + int c; if (ctx->args.pdfdebug) dmprintf (ctx->memory, " %%"); do { - bytes = pdfi_read_bytes(ctx, (byte *)&Buffer, 1, 1, s); - if (bytes < 0) - return_error(gs_error_ioerror); + c = pdfi_read_byte(ctx, s); + if (c < 0) + break; - if (bytes > 0) { - if (ctx->args.pdfdebug) - dmprintf1 (ctx->memory, " %c", Buffer); + if (ctx->args.pdfdebug) + dmprintf1 (ctx->memory, " %c", (char)c); + + } while (c != 0x0a && c != 0x0d); - if ((Buffer == 0x0A) || (Buffer == 0x0D)) { - break; - } - } - } while (bytes); return 0; } @@ -791,30 +718,23 @@ static int pdfi_read_keyword(pdf_context *ctx, pdf_c_stream *s, uint32_t indirec { byte Buffer[256]; unsigned short index = 0; - short bytes = 0; - int code; + int c, code; pdf_keyword *keyword; pdfi_skip_white(ctx, s); do { - bytes = pdfi_read_bytes(ctx, (byte *)&Buffer[index], 1, 1, s); - if (bytes < 0) - return_error(gs_error_ioerror); + c = pdfi_read_byte(ctx, s); + if (c < 0) + break; - if (bytes > 0) { - if (iswhite(Buffer[index])) { - pdfi_unread(ctx, s, (byte *)&Buffer[index], 1); - break; - } else { - if (isdelimiter(Buffer[index])) { - pdfi_unread(ctx, s, (byte *)&Buffer[index], 1); - break; - } - } - index++; + if (iswhite(c) || isdelimiter(c)) { + pdfi_unread_byte(ctx, s, (byte)c); + break; } - } while (bytes && index < 255); + Buffer[index] = (byte)c; + index++; + } while (index < 255); if (index >= 255 || index == 0) { if (ctx->args.pdfstoponerror) @@ -990,19 +910,18 @@ static int pdfi_read_keyword(pdf_context *ctx, pdf_c_stream *s, uint32_t indirec */ int pdfi_read_token(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_num, uint32_t indirect_gen) { - int32_t bytes = 0; - char Buffer[256]; - int code; + int c, code; +rescan: pdfi_skip_white(ctx, s); - bytes = pdfi_read_bytes(ctx, (byte *)Buffer, 1, 1, s); - if (bytes < 0) - return (gs_error_ioerror); - if (bytes == 0 && s->eof) + c = pdfi_read_byte(ctx, s); + if (c == EOFC) return 0; + if (c < 0) + return_error(gs_error_ioerror); - switch(Buffer[0]) { + switch(c) { case 0x30: case 0x31: case 0x32: @@ -1016,94 +935,112 @@ int pdfi_read_token(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_num, ui case '+': case '-': case '.': - pdfi_unread(ctx, s, (byte *)&Buffer[0], 1); + pdfi_unread_byte(ctx, s, (byte)c); code = pdfi_read_num(ctx, s, indirect_num, indirect_gen); if (code < 0) return code; break; case '/': - return pdfi_read_name(ctx, s, indirect_num, indirect_gen); + code = pdfi_read_name(ctx, s, indirect_num, indirect_gen); + if (code < 0) + return code; + return 1; break; case '<': - bytes = pdfi_read_bytes(ctx, (byte *)&Buffer[1], 1, 1, s); - if (bytes <= 0) + c = pdfi_read_byte(ctx, s); + if (c < 0) return (gs_error_ioerror); - if (iswhite(Buffer[1])) { + if (iswhite(c)) { code = pdfi_skip_white(ctx, s); if (code < 0) return code; - bytes = pdfi_read_bytes(ctx, (byte *)&Buffer[1], 1, 1, s); + c = pdfi_read_byte(ctx, s); } - if (Buffer[1] == '<') { + if (c == '<') { if (ctx->args.pdfdebug) dmprintf (ctx->memory, " <<\n"); - return pdfi_mark_stack(ctx, PDF_DICT_MARK); - } else { - if (Buffer[1] == '>') { - pdfi_unread(ctx, s, (byte *)&Buffer[1], 1); - return pdfi_read_hexstring(ctx, s, indirect_num, indirect_gen); - } else { - if (ishex(Buffer[1])) { - pdfi_unread(ctx, s, (byte *)&Buffer[1], 1); - return pdfi_read_hexstring(ctx, s, indirect_num, indirect_gen); - } - else - return_error(gs_error_syntaxerror); - } + code = pdfi_mark_stack(ctx, PDF_DICT_MARK); + if (code < 0) + return code; + return 1; + } else if (c == '>') { + pdfi_unread_byte(ctx, s, (byte)c); + code = pdfi_read_hexstring(ctx, s, indirect_num, indirect_gen); + if (code < 0) + return code; + return 1; + } else if (ishex(c)) { + pdfi_unread_byte(ctx, s, (byte)c); + code = pdfi_read_hexstring(ctx, s, indirect_num, indirect_gen); + if (code < 0) + return code; } + else + return_error(gs_error_syntaxerror); break; case '>': - bytes = pdfi_read_bytes(ctx, (byte *)&Buffer[1], 1, 1, s); - if (bytes <= 0) + c = pdfi_read_byte(ctx, s); + if (c < 0) return (gs_error_ioerror); - if (Buffer[1] == '>') - return pdfi_dict_from_stack(ctx, indirect_num, indirect_gen); - else { - pdfi_unread(ctx, s, (byte *)&Buffer[1], 1); + if (c == '>') { + code = pdfi_dict_from_stack(ctx, indirect_num, indirect_gen, false); + if (code < 0) + return code; + return 1; + } else { + pdfi_unread_byte(ctx, s, (byte)c); return_error(gs_error_syntaxerror); } break; case '(': - return pdfi_read_string(ctx, s, indirect_num, indirect_gen); + code = pdfi_read_string(ctx, s, indirect_num, indirect_gen); + if (code < 0) + return code; + return 1; break; case '[': if (ctx->args.pdfdebug) dmprintf (ctx->memory, "["); - return pdfi_mark_stack(ctx, PDF_ARRAY_MARK); + code = pdfi_mark_stack(ctx, PDF_ARRAY_MARK); + if (code < 0) + return code; + return 1; break; case ']': code = pdfi_array_from_stack(ctx, indirect_num, indirect_gen); - if (code < 0) { - if (code == gs_error_VMerror || code == gs_error_ioerror || ctx->args.pdfstoponerror) - return code; - pdfi_clearstack(ctx); - return pdfi_read_token(ctx, s, indirect_num, indirect_gen); - } + if (code < 0) + return code; break; case '{': if (ctx->args.pdfdebug) dmprintf (ctx->memory, "{"); - return pdfi_mark_stack(ctx, PDF_PROC_MARK); + code = pdfi_mark_stack(ctx, PDF_PROC_MARK); + if (code < 0) + return code; + return 1; break; case '}': pdfi_clear_to_mark(ctx); - return pdfi_read_token(ctx, s, indirect_num, indirect_gen); + goto rescan; break; case '%': pdfi_skip_comment(ctx, s); - return pdfi_read_token(ctx, s, indirect_num, indirect_gen); + goto rescan; break; default: - if (isdelimiter(Buffer[0])) { + if (isdelimiter(c)) { if (ctx->args.pdfstoponerror) return_error(gs_error_syntaxerror); - return pdfi_read_token(ctx, s, indirect_num, indirect_gen); + goto rescan; } - pdfi_unread(ctx, s, (byte *)&Buffer[0], 1); - return pdfi_read_keyword(ctx, s, indirect_num, indirect_gen); + pdfi_unread_byte(ctx, s, (byte)c); + code = pdfi_read_keyword(ctx, s, indirect_num, indirect_gen); + if (code < 0) + return code; + return 1; break; } - return 0; + return 1; } /* In contrast to the 'read' functions, the 'make' functions create an object with a @@ -1183,7 +1120,7 @@ static int search_table_1(pdf_context *ctx, unsigned char *str, pdf_keyword **ke { int i, code = 0; - for (i = 0; i < 39; i++) { + for (i = 0; i < 27; i++) { if (memcmp(str, op_table_1[i], 1) == 0) { code = pdfi_object_alloc(ctx, PDF_KEYWORD, 1, (pdf_obj **)key); if (code < 0) @@ -1316,29 +1253,21 @@ static int split_bogus_operator(pdf_context *ctx, pdf_c_stream *source, pdf_dict if (code <= 0) goto error_exit; - if (code > 0) { - switch(keyword->length - 1) { - case 1: - code = search_table_1(ctx, &keyword->data[key1->length], &key2); - break; - case 2: - code = search_table_1(ctx, &keyword->data[key1->length], &key2); - break; - case 3: - code = search_table_1(ctx, &keyword->data[key1->length], &key2); - break; - default: - goto error_exit; - } - if (code <= 0) + switch(keyword->length - 1) { + case 1: + code = search_table_1(ctx, &keyword->data[key1->length], &key2); + break; + case 2: + code = search_table_1(ctx, &keyword->data[key1->length], &key2); + break; + case 3: + code = search_table_1(ctx, &keyword->data[key1->length], &key2); + break; + default: goto error_exit; - if (code > 0) - goto match; } - pdfi_countdown(key1); - pdfi_countdown(key2); - key1 = NULL; - key2 = NULL; + if (code <= 0) + goto error_exit; match: /* If we get here, we have two PDF_KEYWORD objects. We push them on the stack @@ -1448,10 +1377,6 @@ static int pdfi_interpret_stream_operator(pdf_context *ctx, pdf_c_stream *source pdfi_pop(ctx, 1); code = pdfi_setdash(ctx); break; - case K2('E','I'): /* end inline image */ - pdfi_pop(ctx, 1); - code = pdfi_EI(ctx); - break; case K2('d','0'): /* set type 3 font glyph width */ pdfi_pop(ctx, 1); code = pdfi_d0(ctx); @@ -1466,10 +1391,11 @@ static int pdfi_interpret_stream_operator(pdf_context *ctx, pdf_c_stream *source break; case K2('D','P'): /* define marked content point with property list */ pdfi_pop(ctx, 1); - if (pdfi_count_stack(ctx) >= 2) { - pdfi_pop(ctx, 2); - } else - pdfi_clearstack(ctx); + code = pdfi_op_DP(ctx, stream_dict, page_dict); + break; + case K2('E','I'): /* end inline image */ + pdfi_pop(ctx, 1); + code = pdfi_EI(ctx); break; case K2('E','T'): /* end text */ pdfi_pop(ctx, 1); @@ -1548,8 +1474,7 @@ static int pdfi_interpret_stream_operator(pdf_context *ctx, pdf_c_stream *source break; case K2('M','P'): /* define marked content point */ pdfi_pop(ctx, 1); - if (pdfi_count_stack(ctx) >= 1) - pdfi_pop(ctx, 1); + code = pdfi_op_MP(ctx); break; case K1('n'): /* newpath */ pdfi_pop(ctx, 1); @@ -1763,86 +1688,12 @@ void initialise_stream_save(pdf_context *ctx) ctx->current_stream_save.stack_count = pdfi_count_total_stack(ctx); } -static int setup_stream_DefaultSpaces(pdf_context *ctx, pdf_dict *stream_dict) -{ - int code = 0; - pdf_dict *resources_dict = NULL, *colorspaces_dict = NULL; - pdf_obj *DefaultSpace = NULL; - - /* Create any required DefaultGray, DefaultRGB or DefaultCMYK - * spaces. - */ - - if (ctx->args.NOSUBSTDEVICECOLORS) - return 0; - - code = pdfi_dict_knownget(ctx, stream_dict, "Resources", (pdf_obj **)&resources_dict); - if (code > 0) { - code = pdfi_dict_knownget(ctx, resources_dict, "ColorSpace", (pdf_obj **)&colorspaces_dict); - if (code > 0) { - code = pdfi_dict_knownget(ctx, colorspaces_dict, "DefaultGray", &DefaultSpace); - if (code > 0) { - gs_color_space *pcs; - code = pdfi_create_colorspace(ctx, DefaultSpace, NULL, stream_dict, &pcs, false); - /* If any given Default* space fails simply ignore it, we wil then use the Device - * space (or page level Default) instead, this is as per the spec. - */ - if (code >= 0) { - if (ctx->page.DefaultGray_cs) - rc_decrement_only(ctx->page.DefaultGray_cs, "setup_stream_DefaultSpaces"); - ctx->page.DefaultGray_cs = pcs; - pdfi_set_colour_callback(pcs, ctx, NULL); - } - } - pdfi_countdown(DefaultSpace); - DefaultSpace = NULL; - code = pdfi_dict_knownget(ctx, colorspaces_dict, "DefaultRGB", &DefaultSpace); - if (code > 0) { - gs_color_space *pcs; - code = pdfi_create_colorspace(ctx, DefaultSpace, NULL, stream_dict, &pcs, false); - /* If any given Default* space fails simply ignore it, we wil then use the Device - * space (or page level Default) instead, this is as per the spec. - */ - if (code >= 0) { - if (ctx->page.DefaultRGB_cs) - rc_decrement_only(ctx->page.DefaultRGB_cs, "setup_stream_DefaultSpaces"); - ctx->page.DefaultRGB_cs = pcs; - pdfi_set_colour_callback(pcs, ctx, NULL); - } - } - pdfi_countdown(DefaultSpace); - DefaultSpace = NULL; - code = pdfi_dict_knownget(ctx, colorspaces_dict, "DefaultCMYK", &DefaultSpace); - if (code > 0) { - gs_color_space *pcs; - code = pdfi_create_colorspace(ctx, DefaultSpace, NULL, stream_dict, &pcs, false); - /* If any given Default* space fails simply ignore it, we wil then use the Device - * space (or page level Default) instead, this is as per the spec. - */ - if (code >= 0) { - if (ctx->page.DefaultCMYK_cs) - rc_decrement_only(ctx->page.DefaultCMYK_cs, "setup_stream_DefaultSpaces"); - ctx->page.DefaultCMYK_cs = pcs; - pdfi_set_colour_callback(pcs, ctx, NULL); - } - } - pdfi_countdown(DefaultSpace); - DefaultSpace = NULL; - } - } - - pdfi_countdown(DefaultSpace); - pdfi_countdown(resources_dict); - pdfi_countdown(colorspaces_dict); - return 0; -} - /* Run a stream in a sub-context (saves/restores DefaultQState) */ int pdfi_run_context(pdf_context *ctx, pdf_stream *stream_obj, pdf_dict *page_dict, bool stoponerror, const char *desc) { - int code; - gs_gstate *DefaultQState; + int code = 0, code1 = 0; + gs_gstate *DefaultQState = NULL; /* Save any existing Default* colour spaces */ gs_color_space *PageDefaultGray = ctx->page.DefaultGray_cs; gs_color_space *PageDefaultRGB = ctx->page.DefaultRGB_cs; @@ -1859,12 +1710,29 @@ int pdfi_run_context(pdf_context *ctx, pdf_stream *stream_obj, /* If the stream has any Default* colour spaces, replace the page level ones. * This will derement the reference counts to the current spaces if they are replaced. */ - setup_stream_DefaultSpaces(ctx, stream_obj->stream_dict); + code = pdfi_setup_DefaultSpaces(ctx, stream_obj->stream_dict); + if (code < 0) + goto exit; + + code = pdfi_copy_DefaultQState(ctx, &DefaultQState); + if (code < 0) + goto exit; + + code = pdfi_set_DefaultQState(ctx, ctx->pgs); + if (code < 0) + goto exit; - pdfi_copy_DefaultQState(ctx, &DefaultQState); - pdfi_set_DefaultQState(ctx, ctx->pgs); code = pdfi_interpret_inner_content_stream(ctx, stream_obj, page_dict, stoponerror, desc); - pdfi_restore_DefaultQState(ctx, &DefaultQState); + + code1 = pdfi_restore_DefaultQState(ctx, &DefaultQState); + if (code >= 0) + code = code1; + +exit: + if (DefaultQState != NULL) { + gs_gstate_free(DefaultQState); + DefaultQState = NULL; + } /* Count down any Default* colour spaces */ rc_decrement(ctx->page.DefaultGray_cs, "pdfi_run_context"); @@ -2031,6 +1899,24 @@ pdfi_interpret_content_stream(pdf_context *ctx, pdf_c_stream *content_stream, int code; pdf_c_stream *stream; pdf_keyword *keyword; + pdf_stream *s = ctx->current_stream; + + /* Check this stream, and all the streams currently being executed, to see + * if the stream we've been given is already in train. If it is, then we + * have encountered recursion. This can happen if a non-page stream such + * as a Form or Pattern uses a Resource, but does not declare it in it's + * Resources, and instead inherits it from the parent. We cannot detect that + * before the Resource is used, so all we can do is check here. + */ + while (s != NULL && s->type == PDF_STREAM) { + if (s->object_num > 0) { + if (s->object_num == stream_obj->object_num) { + pdfi_set_error(ctx, 0, NULL, E_PDF_CIRCULARREF, "pdfi_interpret_content_stream", "Aborting stream"); + return_error(gs_error_circular_reference); + } + } + s = (pdf_stream *)s->parent_obj; + } if (content_stream != NULL) { stream = content_stream; diff --git a/pdf/pdf_loop_detect.c b/pdf/pdf_loop_detect.c index 7d08b182..7ba05987 100644 --- a/pdf/pdf_loop_detect.c +++ b/pdf/pdf_loop_detect.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -84,7 +84,7 @@ bool pdfi_loop_detector_check_object(pdf_context *ctx, uint64_t object) for (i=0;i < ctx->loop_detection_entries;i++) { if (ctx->loop_detection[i] == object) { char info_string[256]; - gs_sprintf(info_string, "Error! circular reference to object %"PRIu64" detected.\n", object); + gs_snprintf(info_string, sizeof(info_string), "Error! circular reference to object %"PRIu64" detected.\n", object); pdfi_set_error(ctx, 0, NULL, E_PDF_CIRCULARREF, "pdfi_loop_detector_check_object", info_string); return true; } diff --git a/pdf/pdf_mark.c b/pdf/pdf_mark.c index 4f678f10..d625e34a 100644 --- a/pdf/pdf_mark.c +++ b/pdf/pdf_mark.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2021 Artifex Software, Inc. +/* Copyright (C) 2020-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -29,7 +29,7 @@ #include "gscoord.h" /* For gs_currentmatrix */ -static int pdfi_mark_setparam_obj(pdf_context *ctx, pdf_obj *obj, gs_param_string *entry) +static int pdfi_pdfmark_setparam_obj(pdf_context *ctx, pdf_obj *obj, gs_param_string *entry) { int code = 0; byte *data = NULL; @@ -44,7 +44,7 @@ static int pdfi_mark_setparam_obj(pdf_context *ctx, pdf_obj *obj, gs_param_strin return 0; } -static int pdfi_mark_setparam_pair(pdf_context *ctx, pdf_name *Key, pdf_obj *Value, +static int pdfi_pdfmark_setparam_pair(pdf_context *ctx, pdf_name *Key, pdf_obj *Value, gs_param_string *entry) { int code = 0; @@ -55,11 +55,11 @@ static int pdfi_mark_setparam_pair(pdf_context *ctx, pdf_name *Key, pdf_obj *Val goto exit; } - code = pdfi_mark_setparam_obj(ctx, (pdf_obj *)Key, entry); + code = pdfi_pdfmark_setparam_obj(ctx, (pdf_obj *)Key, entry); if (code < 0) goto exit; - code = pdfi_mark_setparam_obj(ctx, Value, entry+1); + code = pdfi_pdfmark_setparam_obj(ctx, Value, entry+1); if (code < 0) goto exit; @@ -69,12 +69,12 @@ static int pdfi_mark_setparam_pair(pdf_context *ctx, pdf_name *Key, pdf_obj *Val /* Note: this isn't part of the obj_to_string() stuff */ -static int pdfi_mark_ctm_str(pdf_context *ctx, gs_matrix *ctm, byte **data, int *len) +static int pdfi_pdfmark_ctm_str(pdf_context *ctx, gs_matrix *ctm, byte **data, int *len) { int size = 100; char *buf; - buf = (char *)gs_alloc_bytes(ctx->memory, size, "pdfi_mark_ctm_str(data)"); + buf = (char *)gs_alloc_bytes(ctx->memory, size, "pdfi_pdfmark_ctm_str(data)"); if (buf == NULL) return_error(gs_error_VMerror); snprintf(buf, size, "[%.4f %.4f %.4f %.4f %.4f %.4f]", @@ -85,7 +85,7 @@ static int pdfi_mark_ctm_str(pdf_context *ctx, gs_matrix *ctm, byte **data, int } /* Write an string array command to the device (e.g. pdfmark) */ -static int pdfi_mark_write_array(pdf_context *ctx, gs_param_string_array *array_list, const char *command) +static int pdfi_pdfmark_write_array(pdf_context *ctx, gs_param_string_array *array_list, const char *command) { gs_c_param_list list; int code; @@ -117,7 +117,7 @@ static int pdfi_mark_write_array(pdf_context *ctx, gs_param_string_array *array_ } /* Write an string array command to the device (e.g. pdfmark) */ -static int pdfi_mark_write_string(pdf_context *ctx, gs_param_string *param_string, const char *command) +static int pdfi_pdfmark_write_string(pdf_context *ctx, gs_param_string *param_string, const char *command) { gs_c_param_list list; int code; @@ -162,7 +162,7 @@ static int pdfi_mark_write_string(pdf_context *ctx, gs_param_string *param_strin See plparams.c/process_pdfmark() */ -static int pdfi_mark_from_dict_withlabel(pdf_context *ctx, pdf_indirect_ref *label, +static int pdfi_pdfmark_from_dict_withlabel(pdf_context *ctx, pdf_indirect_ref *label, pdf_dict *dict, gs_matrix *ctm, const char *type) { int code = 0; @@ -193,7 +193,7 @@ static int pdfi_mark_from_dict_withlabel(pdf_context *ctx, pdf_indirect_ref *lab size += 1; parray = (gs_param_string *)gs_alloc_bytes(ctx->memory, size*sizeof(gs_param_string), - "pdfi_mark_from_dict(parray)"); + "pdfi_pdfmark_from_dict(parray)"); if (parray == NULL) { code = gs_note_error(gs_error_VMerror); goto exit; @@ -201,7 +201,7 @@ static int pdfi_mark_from_dict_withlabel(pdf_context *ctx, pdf_indirect_ref *lab memset(parray, 0, size*sizeof(gs_param_string)); if (label) { - code = pdfi_mark_setparam_obj(ctx, (pdf_obj *)label, parray+0); + code = pdfi_pdfmark_setparam_obj(ctx, (pdf_obj *)label, parray+0); offset += 1; } @@ -212,7 +212,7 @@ static int pdfi_mark_from_dict_withlabel(pdf_context *ctx, pdf_indirect_ref *lab code = pdfi_dict_get_no_deref(ctx, dict, Key, &Value); if (code < 0) goto exit; - code = pdfi_mark_setparam_pair(ctx, Key, Value, parray+offset+(keynum*2)); + code = pdfi_pdfmark_setparam_pair(ctx, Key, Value, parray+offset+(keynum*2)); if (code < 0) goto exit; pdfi_countdown(Key); @@ -233,7 +233,7 @@ static int pdfi_mark_from_dict_withlabel(pdf_context *ctx, pdf_indirect_ref *lab if (code < 0) goto exit; /* CTM */ - code = pdfi_mark_ctm_str(ctx, ctm, &ctm_data, &ctm_len); + code = pdfi_pdfmark_ctm_str(ctx, ctm, &ctm_data, &ctm_len); if (code < 0) goto exit; parray[size-2].data = ctm_data; parray[size-2].size = ctm_len; @@ -246,7 +246,7 @@ static int pdfi_mark_from_dict_withlabel(pdf_context *ctx, pdf_indirect_ref *lab array_list.persistent = false; array_list.size = size; - code = pdfi_mark_write_array(ctx, &array_list, "pdfmark"); + code = pdfi_pdfmark_write_array(ctx, &array_list, "pdfmark"); exit: pdfi_countdown(Key); @@ -256,25 +256,25 @@ static int pdfi_mark_from_dict_withlabel(pdf_context *ctx, pdf_indirect_ref *lab /* Free the param data except the last two which are handled separately */ for (i=0; i<size-2; i++) { if (parray[i].data) - gs_free_object(ctx->memory, (byte *)parray[i].data, "pdfi_mark_from_dict(parray)"); + gs_free_object(ctx->memory, (byte *)parray[i].data, "pdfi_pdfmark_from_dict(parray)"); } } if (ctm_data) - gs_free_object(ctx->memory, ctm_data, "pdfi_mark_from_dict(ctm_data)"); - gs_free_object(ctx->memory, parray, "pdfi_mark_from_dict(parray)"); + gs_free_object(ctx->memory, ctm_data, "pdfi_pdfmark_from_dict(ctm_data)"); + gs_free_object(ctx->memory, parray, "pdfi_pdfmark_from_dict(parray)"); return code; } /* Do a pdfmark from a dictionary */ -int pdfi_mark_from_dict(pdf_context *ctx, pdf_dict *dict, gs_matrix *ctm, const char *type) +int pdfi_pdfmark_from_dict(pdf_context *ctx, pdf_dict *dict, gs_matrix *ctm, const char *type) { - return pdfi_mark_from_dict_withlabel(ctx, NULL, dict, ctm, type); + return pdfi_pdfmark_from_dict_withlabel(ctx, NULL, dict, ctm, type); } /* Does a pdfmark, from a c-array of pdf_obj's * This will put in a dummy ctm if none provided */ -static int pdfi_mark_from_objarray(pdf_context *ctx, pdf_obj **objarray, int len, +int pdfi_pdfmark_from_objarray(pdf_context *ctx, pdf_obj **objarray, int len, gs_matrix *ctm, const char *type) { int code = 0; @@ -295,7 +295,7 @@ static int pdfi_mark_from_objarray(pdf_context *ctx, pdf_obj **objarray, int len size = len + 2; /* data + CTM + type */ parray = (gs_param_string *)gs_alloc_bytes(ctx->memory, size*sizeof(gs_param_string), - "pdfi_mark_from_objarray(parray)"); + "pdfi_pdfmark_from_objarray(parray)"); if (parray == NULL) { code = gs_note_error(gs_error_VMerror); goto exit; @@ -303,12 +303,12 @@ static int pdfi_mark_from_objarray(pdf_context *ctx, pdf_obj **objarray, int len memset(parray, 0, size *sizeof(gs_param_string)); for (i=0; i<len; i++) { - code = pdfi_mark_setparam_obj(ctx, objarray[i], parray+i); + code = pdfi_pdfmark_setparam_obj(ctx, objarray[i], parray+i); if (code < 0) goto exit; } /* CTM */ - code = pdfi_mark_ctm_str(ctx, ctm, &ctm_data, &ctm_len); + code = pdfi_pdfmark_ctm_str(ctx, ctm, &ctm_data, &ctm_len); if (code < 0) goto exit; parray[len].data = ctm_data; parray[len].size = ctm_len; @@ -321,24 +321,24 @@ static int pdfi_mark_from_objarray(pdf_context *ctx, pdf_obj **objarray, int len array_list.persistent = false; array_list.size = size; - code = pdfi_mark_write_array(ctx, &array_list, "pdfmark"); + code = pdfi_pdfmark_write_array(ctx, &array_list, "pdfmark"); exit: if (parray != NULL) { for (i=0; i<len; i++) { - gs_free_object(ctx->memory, (byte *)parray[i].data, "pdfi_mark_from_objarray(parray)"); + gs_free_object(ctx->memory, (byte *)parray[i].data, "pdfi_pdfmark_from_objarray(parray)"); } } if (ctm_data) - gs_free_object(ctx->memory, ctm_data, "pdfi_mark_from_objarray(ctm_data)"); - gs_free_object(ctx->memory, parray, "pdfi_mark_from_objarray(parray)"); + gs_free_object(ctx->memory, ctm_data, "pdfi_pdfmark_from_objarray(ctm_data)"); + gs_free_object(ctx->memory, parray, "pdfi_pdfmark_from_objarray(parray)"); return code; } /* Send an arbitrary object as a string, with command 'cmd' * This is not a pdfmark, has no ctm. */ -int pdfi_mark_object(pdf_context *ctx, pdf_obj *object, const char *cmd) +int pdfi_pdfmark_object(pdf_context *ctx, pdf_obj *object, const char *cmd) { gs_param_string param_string; int code = 0; @@ -348,27 +348,21 @@ int pdfi_mark_object(pdf_context *ctx, pdf_obj *object, const char *cmd) code = pdfi_loop_detector_mark(ctx); if (code < 0) goto exit; - if (object->object_num != 0) { - code = pdfi_loop_detector_add_object(ctx, object->object_num); - if (code < 0) { - (void)pdfi_loop_detector_cleartomark(ctx); - goto exit; - } - } - code = pdfi_resolve_indirect(ctx, object, true); + + code = pdfi_resolve_indirect_loop_detect(ctx, NULL, object, true); (void)pdfi_loop_detector_cleartomark(ctx); if (code < 0) goto exit; - code = pdfi_mark_setparam_obj(ctx, object, ¶m_string); + code = pdfi_pdfmark_setparam_obj(ctx, object, ¶m_string); if (code < 0) goto exit; - code = pdfi_mark_write_string(ctx, ¶m_string, cmd); + code = pdfi_pdfmark_write_string(ctx, ¶m_string, cmd); exit: if (param_string.data != NULL) - gs_free_object(ctx->memory, (byte *)param_string.data, "free data transferred to param_string in pdfi_mark_object\n"); + gs_free_object(ctx->memory, (byte *)param_string.data, "free data transferred to param_string in pdfi_pdfmark_object\n"); return code; } @@ -380,7 +374,7 @@ exit: * Removes /Dest and inserts two key pairs: /Page N and /View <view_info> * N is the page number, which starts at 1, not 0. */ -static int pdfi_mark_add_Page_View(pdf_context *ctx, pdf_dict *link_dict, pdf_array *dest_array) +static int pdfi_pdfmark_add_Page_View(pdf_context *ctx, pdf_dict *link_dict, pdf_array *dest_array) { int code = 0; int i; @@ -437,7 +431,7 @@ static int pdfi_mark_add_Page_View(pdf_context *ctx, pdf_dict *link_dict, pdf_ar } /* Lookup a Dest string(or name) in the Names array and try to resolve it */ -static int pdfi_mark_handle_dest_names(pdf_context *ctx, pdf_dict *link_dict, +static int pdfi_pdfmark_handle_dest_names(pdf_context *ctx, pdf_dict *link_dict, pdf_obj *dest, pdf_array *Names) { int code = 0; @@ -484,21 +478,26 @@ static int pdfi_mark_handle_dest_names(pdf_context *ctx, pdf_dict *link_dict, goto exit; } - /* Next entry is supposed to be a dict */ + /* Next entry is either a dictionary (with a /D key) or an array */ code = pdfi_array_get(ctx, Names, i+1, (pdf_obj **)&D_dict); if (code < 0) goto exit; - if (D_dict->type != PDF_DICT) { - /* TODO: flag a warning? */ - code = 0; - goto exit; - } - /* Dict is supposed to contain key "D" with Dest array */ - code = pdfi_dict_knownget_type(ctx, D_dict, "D", PDF_ARRAY, (pdf_obj **)&dest_array); - if (code <= 0) goto exit; + if (D_dict->type == PDF_DICT) { + /* Dict is supposed to contain key "D" with Dest array */ + code = pdfi_dict_knownget_type(ctx, D_dict, "D", PDF_ARRAY, (pdf_obj **)&dest_array); + if (code <= 0) goto exit; + } else { + if (D_dict->type == PDF_ARRAY) { + dest_array = (pdf_array *)D_dict; + D_dict = NULL; + } else { + code = gs_note_error(gs_error_typecheck); + goto exit; + } + } /* Process the dest_array to replace with /Page /View */ - code = pdfi_mark_add_Page_View(ctx, link_dict, dest_array); + code = pdfi_pdfmark_add_Page_View(ctx, link_dict, dest_array); if (code < 0) goto exit; exit: @@ -511,7 +510,7 @@ static int pdfi_mark_handle_dest_names(pdf_context *ctx, pdf_dict *link_dict, /* Special handling for "Dest" in Links * Will replace /Dest with /Page /View in link_dict (for pdfwrite) */ -int pdfi_mark_modDest(pdf_context *ctx, pdf_dict *link_dict) +int pdfi_pdfmark_modDest(pdf_context *ctx, pdf_dict *link_dict) { int code = 0; pdf_dict *Dests = NULL; @@ -532,7 +531,7 @@ int pdfi_mark_modDest(pdf_context *ctx, pdf_dict *link_dict) switch (Dest->type) { case PDF_ARRAY: - code = pdfi_mark_add_Page_View(ctx, link_dict, (pdf_array *)Dest); + code = pdfi_pdfmark_add_Page_View(ctx, link_dict, (pdf_array *)Dest); if (code < 0) goto exit; break; case PDF_NAME: @@ -550,7 +549,7 @@ int pdfi_mark_modDest(pdf_context *ctx, pdf_dict *link_dict) code = gs_note_error(gs_error_typecheck); goto exit; } - code = pdfi_mark_add_Page_View(ctx, link_dict, dest_array); + code = pdfi_pdfmark_add_Page_View(ctx, link_dict, dest_array); if (code < 0) goto exit; } else if (Names_dict != NULL) { /* Looking in Catalog(Root) for /Names<</Dests<</Names [name dict array]>>>> */ @@ -567,7 +566,7 @@ int pdfi_mark_modDest(pdf_context *ctx, pdf_dict *link_dict) /* TODO: Not found -- not sure if there is another case here or not */ goto exit; } - code = pdfi_mark_handle_dest_names(ctx, link_dict, Dest, Names); + code = pdfi_pdfmark_handle_dest_names(ctx, link_dict, Dest, Names); if (code < 0) goto exit; } else { /* TODO: Ignore it -- flag a warning? */ @@ -595,7 +594,7 @@ int pdfi_mark_modDest(pdf_context *ctx, pdf_dict *link_dict) /* Special handling for "A" in Link annotations and Outlines * Will delete A if handled and if A_key is provided. */ -int pdfi_mark_modA(pdf_context *ctx, pdf_dict *dict) +int pdfi_pdfmark_modA(pdf_context *ctx, pdf_dict *dict) { int code = 0; pdf_dict *A_dict = NULL; @@ -620,7 +619,7 @@ int pdfi_mark_modA(pdf_context *ctx, pdf_dict *dict) code = pdfi_dict_known(ctx, A_dict, "URI", &known); if (code < 0) goto exit; if (known) { - code = pdfi_resolve_indirect_loop_detect(ctx, (pdf_obj *)NULL, (pdf_obj *)dict, true); + code = pdfi_resolve_indirect_loop_detect(ctx, (pdf_obj *)NULL, (pdf_obj *)A_dict, true); goto exit; } @@ -646,7 +645,7 @@ int pdfi_mark_modA(pdf_context *ctx, pdf_dict *dict) goto exit; } /* Process the D array to replace with /Page /View */ - code = pdfi_mark_add_Page_View(ctx, dict, D_array); + code = pdfi_pdfmark_add_Page_View(ctx, dict, D_array); if (code < 0) goto exit; delete_A = true; } else if (pdfi_name_is(S_name, "GoToR") || pdfi_name_is(S_name, "Launch")) { @@ -713,7 +712,6 @@ int pdfi_mark_modA(pdf_context *ctx, pdf_dict *dict) } else if (deref_A) { pdfi_countdown(A_dict); A_dict = NULL; - code = pdfi_dict_get(ctx, dict, "A", (pdf_obj **)&A_dict); } pdfi_countdown(A_dict); pdfi_countdown(S_name); @@ -725,7 +723,7 @@ int pdfi_mark_modA(pdf_context *ctx, pdf_dict *dict) * Send an OBJ (_objdef) command * (_objdef) (<label>) (/type) (/<type>) OBJ */ -static int pdfi_mark_objdef_begin(pdf_context *ctx, pdf_indirect_ref *label, const char *type) +static int pdfi_pdfmark_objdef_begin(pdf_context *ctx, pdf_indirect_ref *label, const char *type) { int code; pdf_obj *objarray[4]; @@ -746,7 +744,7 @@ static int pdfi_mark_objdef_begin(pdf_context *ctx, pdf_indirect_ref *label, con code = pdfi_obj_charstr_to_name(ctx, type, (pdf_name **)&objarray[3]); if (code < 0) goto exit; - code = pdfi_mark_from_objarray(ctx, objarray, num_objects, NULL, "OBJ"); + code = pdfi_pdfmark_from_objarray(ctx, objarray, num_objects, NULL, "OBJ"); if (code < 0) goto exit; exit: @@ -759,7 +757,7 @@ static int pdfi_mark_objdef_begin(pdf_context *ctx, pdf_indirect_ref *label, con * Send a CLOSE command * (<label>) CLOSE */ -static int pdfi_mark_objdef_close(pdf_context *ctx, pdf_indirect_ref *label) +static int pdfi_pdfmark_objdef_close(pdf_context *ctx, pdf_indirect_ref *label) { int code; pdf_obj *objarray[1]; @@ -771,7 +769,7 @@ static int pdfi_mark_objdef_close(pdf_context *ctx, pdf_indirect_ref *label) objarray[0] = (pdf_obj *)label; pdfi_countup(label); - code = pdfi_mark_from_objarray(ctx, objarray, num_objects, NULL, "CLOSE"); + code = pdfi_pdfmark_from_objarray(ctx, objarray, num_objects, NULL, "CLOSE"); if (code < 0) goto exit; exit: @@ -780,7 +778,7 @@ static int pdfi_mark_objdef_close(pdf_context *ctx, pdf_indirect_ref *label) return code; } -static int pdfi_mark_stream_contents(pdf_context *ctx, pdf_indirect_ref *label, pdf_stream *stream) +static int pdfi_pdfmark_stream_contents(pdf_context *ctx, pdf_indirect_ref *label, pdf_stream *stream) { int code; pdf_obj *objarray[2]; @@ -794,7 +792,7 @@ static int pdfi_mark_stream_contents(pdf_context *ctx, pdf_indirect_ref *label, pdfi_countup(stream); stream->is_marking = true; - code = pdfi_mark_from_objarray(ctx, objarray, num_objects, NULL, ".PUTSTREAM"); + code = pdfi_pdfmark_from_objarray(ctx, objarray, num_objects, NULL, ".PUTSTREAM"); if (code < 0) goto exit; exit: @@ -805,7 +803,7 @@ static int pdfi_mark_stream_contents(pdf_context *ctx, pdf_indirect_ref *label, } /* Mark a stream object */ -int pdfi_mark_stream(pdf_context *ctx, pdf_stream *stream) +int pdfi_pdfmark_stream(pdf_context *ctx, pdf_stream *stream) { int code; pdf_dict *streamdict = NULL; @@ -858,16 +856,16 @@ int pdfi_mark_stream(pdf_context *ctx, pdf_stream *stream) } if (code < 0) goto exit; - code = pdfi_mark_objdef_begin(ctx, streamref, "stream"); + code = pdfi_pdfmark_objdef_begin(ctx, streamref, "stream"); if (code < 0) goto exit; - code = pdfi_mark_from_dict_withlabel(ctx, streamref, tempdict, NULL, ".PUTDICT"); + code = pdfi_pdfmark_from_dict_withlabel(ctx, streamref, tempdict, NULL, ".PUTDICT"); if (code < 0) goto exit; - code = pdfi_mark_stream_contents(ctx, streamref, stream); + code = pdfi_pdfmark_stream_contents(ctx, streamref, stream); if (code < 0) goto exit; - code = pdfi_mark_objdef_close(ctx, streamref); + code = pdfi_pdfmark_objdef_close(ctx, streamref); if (code < 0) goto exit; exit: @@ -877,7 +875,7 @@ int pdfi_mark_stream(pdf_context *ctx, pdf_stream *stream) } /* Mark a dict object */ -int pdfi_mark_dict(pdf_context *ctx, pdf_dict *dict) +int pdfi_pdfmark_dict(pdf_context *ctx, pdf_dict *dict) { int code; pdf_indirect_ref *dictref = NULL; @@ -898,10 +896,10 @@ int pdfi_mark_dict(pdf_context *ctx, pdf_dict *dict) dictref->ref_generation_num = dict->generation_num; dictref->is_marking = true; - code = pdfi_mark_objdef_begin(ctx, dictref, "dict"); + code = pdfi_pdfmark_objdef_begin(ctx, dictref, "dict"); if (code < 0) goto exit; - code = pdfi_mark_from_dict_withlabel(ctx, dictref, dict, NULL, ".PUTDICT"); + code = pdfi_pdfmark_from_dict_withlabel(ctx, dictref, dict, NULL, ".PUTDICT"); if (code < 0) goto exit; exit: @@ -909,7 +907,7 @@ int pdfi_mark_dict(pdf_context *ctx, pdf_dict *dict) return code; } -static int pdfi_mark_filespec(pdf_context *ctx, pdf_string *name, pdf_dict *filespec) +static int pdfi_pdfmark_filespec(pdf_context *ctx, pdf_string *name, pdf_dict *filespec) { int code; pdf_dict *tempdict = NULL; @@ -928,7 +926,7 @@ static int pdfi_mark_filespec(pdf_context *ctx, pdf_string *name, pdf_dict *file code = pdfi_dict_put(ctx, tempdict, "FS", (pdf_obj *)filespec); if (code < 0) goto exit; - code = pdfi_mark_from_dict(ctx, tempdict, NULL, "EMBED"); + code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "EMBED"); if (code < 0) goto exit; exit: @@ -937,11 +935,11 @@ static int pdfi_mark_filespec(pdf_context *ctx, pdf_string *name, pdf_dict *file } /* embed a file */ -int pdfi_mark_embed_filespec(pdf_context *ctx, pdf_string *name, pdf_dict *filespec) +int pdfi_pdfmark_embed_filespec(pdf_context *ctx, pdf_string *name, pdf_dict *filespec) { int code; - code = pdfi_mark_filespec(ctx, name, filespec); + code = pdfi_pdfmark_filespec(ctx, name, filespec); if (code < 0) goto exit; exit: @@ -952,7 +950,7 @@ int pdfi_mark_embed_filespec(pdf_context *ctx, pdf_string *name, pdf_dict *files * Create and emit a /DOCINFO pdfmark for any and all of Title, * Author, Subject, Keywords and Creator */ -void pdfi_write_docinfo_pdfmark(pdf_context *ctx, pdf_dict *info_dict) +void pdfi_pdfmark_write_docinfo(pdf_context *ctx, pdf_dict *info_dict) { int i, code = 0; pdf_dict *Info = NULL; @@ -972,14 +970,14 @@ void pdfi_write_docinfo_pdfmark(pdf_context *ctx, pdf_dict *info_dict) for (i=0;i<5;i++) { - if (pdfi_dict_knownget(ctx, info_dict, KeyNames[i], &o)) + if (pdfi_dict_knownget(ctx, info_dict, KeyNames[i], &o) > 0) { (void)pdfi_dict_put(ctx, Info, KeyNames[i], (pdf_obj *)o); pdfi_countdown(o); } } - code = pdfi_mark_from_dict(ctx, Info, NULL, "DOCINFO"); + code = pdfi_pdfmark_from_dict(ctx, Info, NULL, "DOCINFO"); exit: pdfi_countdown(Info); return; @@ -996,7 +994,7 @@ exit: * to adjust the various Box entries (note this routine must be called * early!). */ -void pdfi_write_boxes_pdfmark(pdf_context *ctx, pdf_dict *page_dict) +void pdfi_pdfmark_write_boxes(pdf_context *ctx, pdf_dict *page_dict) { int i, code = 0; pdf_dict *BoxDict = NULL; @@ -1040,7 +1038,7 @@ void pdfi_write_boxes_pdfmark(pdf_context *ctx, pdf_dict *page_dict) for (i=0;i<4;i++) { /* Check each Bos name in turn */ - if (pdfi_dict_knownget(ctx, page_dict, BoxNames[i], &o)){ + if (pdfi_dict_knownget(ctx, page_dict, BoxNames[i], &o) > 0){ gs_rect box; pdf_array *new_array = NULL; @@ -1076,7 +1074,7 @@ void pdfi_write_boxes_pdfmark(pdf_context *ctx, pdf_dict *page_dict) } /* Send all the Box entries to the device */ - (void)pdfi_mark_from_dict(ctx, BoxDict, NULL, "PAGE"); + (void)pdfi_pdfmark_from_dict(ctx, BoxDict, NULL, "PAGE"); exit: pdfi_countdown(BoxDict); diff --git a/pdf/pdf_mark.h b/pdf/pdf_mark.h index b732d190..653eafb7 100644 --- a/pdf/pdf_mark.h +++ b/pdf/pdf_mark.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2021 Artifex Software, Inc. +/* Copyright (C) 2020-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -18,15 +18,16 @@ #ifndef PDF_MARK #define PDF_MARK -int pdfi_mark_from_dict(pdf_context *ctx, pdf_dict *dict, gs_matrix *ctm, const char *type); -int pdfi_mark_object(pdf_context *ctx, pdf_obj *object, const char *label); -int pdfi_mark_modDest(pdf_context *ctx, pdf_dict *dict); -int pdfi_mark_modA(pdf_context *ctx, pdf_dict *dict); -int pdfi_mark_stream(pdf_context *ctx, pdf_stream *stream); -int pdfi_mark_dict(pdf_context *ctx, pdf_dict *dict); -int pdfi_mark_embed_filespec(pdf_context *ctx, pdf_string *name, pdf_dict *filespec); -int pdfi_mark_get_objlabel(pdf_context *ctx, pdf_obj *obj, char **label); -void pdfi_write_boxes_pdfmark(pdf_context *ctx, pdf_dict *page_dict); -void pdfi_write_docinfo_pdfmark(pdf_context *ctx, pdf_dict *info_dict); +int pdfi_pdfmark_from_dict(pdf_context *ctx, pdf_dict *dict, gs_matrix *ctm, const char *type); +int pdfi_pdfmark_from_objarray(pdf_context *ctx, pdf_obj **objarray, int len, gs_matrix *ctm, const char *type); +int pdfi_pdfmark_object(pdf_context *ctx, pdf_obj *object, const char *label); +int pdfi_pdfmark_modDest(pdf_context *ctx, pdf_dict *dict); +int pdfi_pdfmark_modA(pdf_context *ctx, pdf_dict *dict); +int pdfi_pdfmark_stream(pdf_context *ctx, pdf_stream *stream); +int pdfi_pdfmark_dict(pdf_context *ctx, pdf_dict *dict); +int pdfi_pdfmark_embed_filespec(pdf_context *ctx, pdf_string *name, pdf_dict *filespec); +int pdfi_pdfmark_get_objlabel(pdf_context *ctx, pdf_obj *obj, char **label); +void pdfi_pdfmark_write_boxes(pdf_context *ctx, pdf_dict *page_dict); +void pdfi_pdfmark_write_docinfo(pdf_context *ctx, pdf_dict *info_dict); #endif diff --git a/pdf/pdf_misc.c b/pdf/pdf_misc.c index 1240d5bc..11c5dde5 100644 --- a/pdf/pdf_misc.c +++ b/pdf/pdf_misc.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -18,8 +18,10 @@ #include "pdf_int.h" #include "pdf_stack.h" #include "pdf_misc.h" +#include "pdf_font_types.h" #include "pdf_gstate.h" #include "gspath.h" /* For gs_strokepath() */ +#include "gspaint.h" /* For gs_erasepage() */ #include "gsicc_manage.h" /* For gsicc_get_default_type() */ #include "gsstate.h" /* for gs_setrenderingintent() */ diff --git a/pdf/pdf_obj.c b/pdf/pdf_obj.c index 931f2137..58bd59b0 100644 --- a/pdf/pdf_obj.c +++ b/pdf/pdf_obj.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2021 Artifex Software, Inc. +/* Copyright (C) 2020-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -23,6 +23,7 @@ #include "pdf_deref.h" /* for replace_cache_entry() */ #include "pdf_mark.h" #include "pdf_file.h" /* for pdfi_stream_to_buffer() */ +#include "pdf_loop_detect.h" /***********************************************************************************/ /* Functions to create the various kinds of 'PDF objects', Created objects have a */ @@ -49,7 +50,7 @@ int pdfi_object_alloc(pdf_context *ctx, pdf_obj_type type, unsigned int size, pd break; case PDF_STRING: case PDF_NAME: - bytes = sizeof(pdf_string); + bytes = sizeof(pdf_string) + size - sizeof(PDF_NAME_DECLARED_LENGTH); break; case PDF_ARRAY: bytes = sizeof(pdf_array); @@ -64,7 +65,7 @@ int pdfi_object_alloc(pdf_context *ctx, pdf_obj_type type, unsigned int size, pd bytes = sizeof(pdf_bool); break; case PDF_KEYWORD: - bytes = sizeof(pdf_keyword); + bytes = sizeof(pdf_keyword) + size - sizeof(PDF_NAME_DECLARED_LENGTH); break; /* The following aren't PDF object types, but are objects we either want to * reference count, or store on the stack. @@ -99,17 +100,7 @@ int pdfi_object_alloc(pdf_context *ctx, pdf_obj_type type, unsigned int size, pd case PDF_KEYWORD: case PDF_STRING: case PDF_NAME: - { - unsigned char *data = NULL; - data = (unsigned char *)gs_alloc_bytes(ctx->memory, size, "pdfi_object_alloc"); - if (data == NULL) { - gs_free_object(ctx->memory, *obj, "pdfi_object_alloc"); - *obj = NULL; - return_error(gs_error_VMerror); - } - ((pdf_string *)*obj)->data = data; - ((pdf_string *)*obj)->length = size; - } + ((pdf_string *)*obj)->length = size; break; case PDF_ARRAY: { @@ -131,23 +122,18 @@ int pdfi_object_alloc(pdf_context *ctx, pdf_obj_type type, unsigned int size, pd break; case PDF_DICT: { - pdf_obj **keys = NULL, **values = NULL; + pdf_dict_entry *entries = NULL; ((pdf_dict *)*obj)->size = size; if (size > 0) { - keys = (pdf_obj **)gs_alloc_bytes(ctx->memory, size * sizeof(pdf_obj *), "pdfi_object_alloc"); - values = (pdf_obj **)gs_alloc_bytes(ctx->memory, size * sizeof(pdf_obj *), "pdfi_object_alloc"); - if (keys == NULL || values == NULL) { + entries = (pdf_dict_entry *)gs_alloc_bytes(ctx->memory, size * sizeof(pdf_dict_entry), "pdfi_object_alloc"); + if (entries == NULL) { gs_free_object(ctx->memory, *obj, "pdfi_object_alloc"); - gs_free_object(ctx->memory, keys, "pdfi_object_alloc"); - gs_free_object(ctx->memory, values, "pdfi_object_alloc"); *obj = NULL; return_error(gs_error_VMerror); } - ((pdf_dict *)*obj)->values = values; - ((pdf_dict *)*obj)->keys = keys; - memset(((pdf_dict *)*obj)->values, 0x00, size * sizeof(pdf_obj *)); - memset(((pdf_dict *)*obj)->keys, 0x00, size * sizeof(pdf_obj *)); + ((pdf_dict *)*obj)->list = entries; + memset(((pdf_dict *)*obj)->list, 0x00, size * sizeof(pdf_dict_entry)); } } break; @@ -160,7 +146,7 @@ int pdfi_object_alloc(pdf_context *ctx, pdf_obj_type type, unsigned int size, pd break; } #if REFCNT_DEBUG - (*obj)->UID = ctx->UID++; + (*obj)->UID = ctx->ref_UID++; dmprintf2(ctx->memory, "Allocated object of type %c with UID %"PRIi64"\n", (*obj)->type, (*obj)->UID); #endif return 0; @@ -205,18 +191,13 @@ static void pdfi_free_namestring(pdf_obj *o) /* Currently names and strings are the same, so a single cast is OK */ pdf_name *n = (pdf_name *)o; - if (n->data != NULL) - gs_free_object(OBJ_MEMORY(n), n->data, "pdf interpreter free name or string data"); gs_free_object(OBJ_MEMORY(n), n, "pdf interpreter free name or string"); } static void pdfi_free_keyword(pdf_obj *o) { - /* Currently names and strings are the same, so a single cast is OK */ pdf_keyword *k = (pdf_keyword *)o; - if (k->data != NULL) - gs_free_object(OBJ_MEMORY(k), k->data, "pdf interpreter free keyword data"); gs_free_object(OBJ_MEMORY(k), k, "pdf interpreter free keyword"); } @@ -238,6 +219,8 @@ static void pdfi_free_stream(pdf_obj *o) void pdfi_free_object(pdf_obj *o) { + if (o == NULL) + return; switch(o->type) { case PDF_ARRAY_MARK: case PDF_DICT_MARK: @@ -540,7 +523,7 @@ static int pdfi_obj_int_str(pdf_context *ctx, pdf_obj *obj, byte **data, int *le buf = (char *)gs_alloc_bytes(ctx->memory, size, "pdfi_obj_int_str(data)"); if (buf == NULL) return_error(gs_error_VMerror); - snprintf(buf, size, "%ld", number->value.i); + snprintf(buf, size, "%"PRId64"", number->value.i); *data = (byte *)buf; *len = strlen(buf); return code; @@ -554,7 +537,7 @@ static int pdfi_obj_getrefstr(pdf_context *ctx, uint64_t object_num, uint32_t ge buf = (char *)gs_alloc_bytes(ctx->memory, size, "pdfi_obj_getrefstr(data)"); if (buf == NULL) return_error(gs_error_VMerror); - snprintf(buf, size, "%ld %d R", object_num, generation); + snprintf(buf, size, "%"PRId64" %d R", object_num, generation); *data = (byte *)buf; *len = strlen(buf); return 0; @@ -573,7 +556,7 @@ static int pdfi_obj_indirect_str(pdf_context *ctx, pdf_obj *obj, byte **data, in ref->is_highlevelform = false; } else { if (!ref->is_marking) { - code = pdfi_dereference(ctx, ref->ref_object_num, ref->ref_generation_num, &object); + code = pdfi_deref_loop_detect(ctx, ref->ref_object_num, ref->ref_generation_num, &object); if (code == gs_error_undefined) { /* Do something sensible for undefined reference (this would be a broken file) */ /* TODO: Flag an error? */ @@ -584,10 +567,10 @@ static int pdfi_obj_indirect_str(pdf_context *ctx, pdf_obj *obj, byte **data, in goto exit; if (code == 0) { if (object->type == PDF_STREAM) { - code = pdfi_mark_stream(ctx, (pdf_stream *)object); + code = pdfi_pdfmark_stream(ctx, (pdf_stream *)object); if (code < 0) goto exit; } else if (object->type == PDF_DICT) { - code = pdfi_mark_dict(ctx, (pdf_dict *)object); + code = pdfi_pdfmark_dict(ctx, (pdf_dict *)object); if (code < 0) goto exit; } else { code = pdfi_obj_to_string(ctx, object, data, len); @@ -833,6 +816,10 @@ static int pdfi_obj_dict_str(pdf_context *ctx, pdf_obj *obj, byte **data, int *l uint64_t index, dictsize; uint64_t itemnum = 0; + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + return code; + code = pdfi_bufstream_init(ctx, &bufstream); if (code < 0) goto exit; @@ -851,6 +838,18 @@ static int pdfi_obj_dict_str(pdf_context *ctx, pdf_obj *obj, byte **data, int *l /* Note: We specifically fetch without dereferencing, so there will be no circular * references to handle here. */ + /* Wrong.... */ + + if (dict->object_num !=0 ) { + if (pdfi_loop_detector_check_object(ctx, dict->object_num)) { + code = gs_note_error(gs_error_circular_reference); + goto exit; + } + code = pdfi_loop_detector_add_object(ctx, dict->object_num); + if (code < 0) + goto exit; + } + /* Get each (key,val) pair from dict and setup param for it */ code = pdfi_dict_key_first(ctx, dict, (pdf_obj **)&Key, &index); while (code >= 0) { @@ -914,6 +913,10 @@ static int pdfi_obj_dict_str(pdf_context *ctx, pdf_obj *obj, byte **data, int *l pdfi_countdown(Key); pdfi_countdown(Value); pdfi_bufstream_free(ctx, &bufstream); + if (code < 0) + (void)pdfi_loop_detector_cleartomark(ctx); + else + code = pdfi_loop_detector_cleartomark(ctx); return code; } diff --git a/pdf/pdf_optcontent.c b/pdf/pdf_optcontent.c index d0dab6c6..484d72c9 100644 --- a/pdf/pdf_optcontent.c +++ b/pdf/pdf_optcontent.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -18,10 +18,12 @@ #include "pdf_int.h" #include "pdf_stack.h" #include "pdf_misc.h" +#include "pdf_font_types.h" #include "pdf_gstate.h" #include "pdf_dict.h" #include "pdf_array.h" #include "pdf_doc.h" +#include "pdf_mark.h" #include "pdf_optcontent.h" @@ -301,8 +303,8 @@ pdfi_oc_is_ocg_visible(pdf_context *ctx, pdf_dict *ocdict) is_visible = pdfi_oc_check_OCG_usage(ctx, ocdict); } else { char str[100]; - memcpy(str, (const char *)type->data, type->length); - str[type->length] = '\0'; + memcpy(str, (const char *)type->data, type->length < 100 ? type->length : 99); + str[type->length < 100 ? type->length : 99] = '\0'; dmprintf1(ctx->memory, "WARNING: OC dict type is %s, expected OCG or OCMD\n", str); } @@ -432,20 +434,116 @@ int pdfi_oc_free(pdf_context *ctx) return code; } +int pdfi_op_MP(pdf_context *ctx) +{ + pdf_obj *o = NULL; + int code = 0; + + if (pdfi_count_stack(ctx) < 1) + return_error(gs_error_stackunderflow); + + if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) + goto exit; + + o = ctx->stack_top[-1]; + if (o->type != PDF_NAME) { + pdfi_pop(ctx, 1); + return_error(gs_error_typecheck); + } + + code = pdfi_pdfmark_from_objarray(ctx, &o, 1, NULL, "MP"); + ctx->BMClevel ++; + +exit: + pdfi_pop(ctx, 1); + return code; +} + +int pdfi_op_DP(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) +{ + pdf_name *properties = NULL; + int code = 0; + pdf_obj **objarray = NULL; + + if (pdfi_count_stack(ctx) < 2) { + pdfi_clearstack(ctx); + return gs_note_error(gs_error_stackunderflow); + } + + if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) + goto exit; + + if ((ctx->stack_top[-2])->type != PDF_NAME) { + code = gs_note_error(gs_error_typecheck); + goto exit; + } + + objarray = (pdf_obj **)gs_alloc_bytes(ctx->memory, 2 * sizeof(pdf_obj *), "pdfi_op_DP"); + if (objarray == NULL) { + code = gs_note_error(gs_error_VMerror); + goto exit; + } + + objarray[0] = ctx->stack_top[-2]; + + if ((ctx->stack_top[-1])->type == PDF_NAME) { + code = pdfi_find_resource(ctx, (unsigned char *)"Properties", (pdf_name *)ctx->stack_top[-1], stream_dict, page_dict, (pdf_obj **)&properties); + if(code < 0) + goto exit; + if (properties->type != PDF_DICT) { + code = gs_note_error(gs_error_typecheck); + goto exit; + } + objarray[1] = (pdf_obj *)properties; + } else { + if ((ctx->stack_top[-1])->type != PDF_DICT) { + code = gs_note_error(gs_error_VMerror); + goto exit; + } + objarray[1] = ctx->stack_top[-1]; + } + + code = pdfi_pdfmark_from_objarray(ctx, objarray, 2, NULL, "DP"); + + exit: + if (objarray != NULL) + gs_free_object(ctx->memory, objarray, "free pdfi_op_DP"); + pdfi_pop(ctx, 2); /* pop args */ + pdfi_countdown(properties); + return code; +} + /* begin marked content sequence */ -/* TODO: Incomplete implementation, it is ignoring the argument */ int pdfi_op_BMC(pdf_context *ctx) { - if (pdfi_count_stack(ctx) >= 1) { + pdf_obj *o = NULL; + int code = 0; + + /* This will also prevent us writing out an EMC if the BMC is in any way invalid */ + ctx->BDCWasOC = true; + + if (pdfi_count_stack(ctx) < 1) + return_error(gs_error_stackunderflow); + + if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) + goto exit; + + o = ctx->stack_top[-1]; + if (o->type != PDF_NAME) { pdfi_pop(ctx, 1); - } else - pdfi_clearstack(ctx); + return_error(gs_error_typecheck); + } + + ctx->BDCWasOC = false; + code = pdfi_pdfmark_from_objarray(ctx, &o, 1, NULL, "BMC"); ctx->BMClevel ++; - return 0; + +exit: + pdfi_pop(ctx, 1); + return code; } /* begin marked content sequence with property list */ -/* TODO: Incomplete implementation, only tries to do something sensible for OC */ int pdfi_op_BDC(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) { pdf_name *tag = NULL; @@ -453,28 +551,62 @@ int pdfi_op_BDC(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) pdf_dict *oc_dict = NULL; int code = 0; bool ocg_is_visible; + pdf_obj **objarray = NULL; + + /* This will also prevent us writing out an EMC if the BDC is in any way invalid */ + ctx->BDCWasOC = true; if (pdfi_count_stack(ctx) < 2) { - /* TODO: Flag error? */ pdfi_clearstack(ctx); - return 0; + return gs_note_error(gs_error_stackunderflow); } ctx->BMClevel ++; - /* Check if second arg is OC and handle it if so */ tag = (pdf_name *)ctx->stack_top[-2]; if (tag->type != PDF_NAME) goto exit; - if (!pdfi_name_is(tag, "OC")) + + if (!pdfi_name_is(tag, "OC")) { + ctx->BDCWasOC = false; + if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) + goto exit; + + objarray = (pdf_obj **)gs_alloc_bytes(ctx->memory, 2 * sizeof(pdf_obj *), "pdfi_op_BDC"); + if (objarray == NULL) { + code = gs_note_error(gs_error_VMerror); + goto exit; + } + + objarray[0] = ctx->stack_top[-2]; + + if ((ctx->stack_top[-1])->type == PDF_NAME) { + code = pdfi_find_resource(ctx, (unsigned char *)"Properties", (pdf_name *)ctx->stack_top[-1], stream_dict, page_dict, (pdf_obj **)&oc_dict); + if(code < 0) + goto exit; + if (oc_dict->type != PDF_DICT) { + code = gs_note_error(gs_error_typecheck); + goto exit; + } + objarray[1] = (pdf_obj *)oc_dict; + } else { + if ((ctx->stack_top[-1])->type != PDF_DICT) { + code = gs_note_error(gs_error_VMerror); + goto exit; + } + objarray[1] = ctx->stack_top[-1]; + } + + code = pdfi_pdfmark_from_objarray(ctx, objarray, 2, NULL, "BDC"); goto exit; + } /* Check if first arg is a name and handle it if so */ /* TODO: spec says it could also be an inline dict that we should be able to handle, * but I am just matching what gs does for now, and it doesn't handle that case. */ properties = (pdf_name *)ctx->stack_top[-1]; - if (tag->type != PDF_NAME) + if (properties->type != PDF_NAME) goto exit; /* If it's a name, look it up in Properties */ @@ -491,6 +623,8 @@ int pdfi_op_BDC(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) code = pdfi_oc_levels_set(ctx, ctx->OFFlevels, ctx->BMClevel); exit: + if (objarray != NULL) + gs_free_object(ctx->memory, objarray, "free pdfi_op_BDC"); pdfi_pop(ctx, 2); /* pop args */ pdfi_countdown(oc_dict); return code; @@ -499,9 +633,14 @@ int pdfi_op_BDC(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) /* end marked content sequence */ int pdfi_op_EMC(pdf_context *ctx) { - int code; + int code, code1 = 0; + + if (ctx->device_state.writepdfmarks && ctx->args.preservemarkedcontent && !ctx->BDCWasOC) + code1 = pdfi_pdfmark_from_objarray(ctx, NULL, 0, NULL, "EMC"); code = pdfi_oc_levels_clear(ctx, ctx->OFFlevels, ctx->BMClevel); + if (code == 0) + code = code1; /* TODO: Should we flag error on too many EMC? */ if (ctx->BMClevel > 0) diff --git a/pdf/pdf_optcontent.h b/pdf/pdf_optcontent.h index aa25b017..375da5d7 100644 --- a/pdf/pdf_optcontent.h +++ b/pdf/pdf_optcontent.h @@ -21,6 +21,8 @@ bool pdfi_oc_is_ocg_visible(pdf_context *ctx, pdf_dict *ocdict); int pdfi_oc_init(pdf_context *ctx); int pdfi_oc_free(pdf_context *ctx); bool pdfi_oc_is_off(pdf_context *ctx); +int pdfi_op_MP(pdf_context *ctx); +int pdfi_op_DP(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict); int pdfi_op_BMC(pdf_context *ctx); int pdfi_op_BDC(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict); int pdfi_op_EMC(pdf_context *ctx); diff --git a/pdf/pdf_page.c b/pdf/pdf_page.c index 58833076..f2389fea 100644 --- a/pdf/pdf_page.c +++ b/pdf/pdf_page.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -26,6 +26,7 @@ #include "pdf_loop_detect.h" #include "pdf_colour.h" #include "pdf_trans.h" +#include "pdf_font_types.h" #include "pdf_gstate.h" #include "pdf_misc.h" #include "pdf_optcontent.h" @@ -677,72 +678,10 @@ static void release_page_DefaultSpaces(pdf_context *ctx) static int setup_page_DefaultSpaces(pdf_context *ctx, pdf_dict *page_dict) { - int code = 0; - pdf_dict *resources_dict = NULL, *colorspaces_dict = NULL; - pdf_obj *DefaultSpace = NULL; - /* First off, discard any dangling Default* colour spaces, just in case. */ release_page_DefaultSpaces(ctx); - if (ctx->args.NOSUBSTDEVICECOLORS) - return 0; - - /* Create any required DefaultGray, DefaultRGB or DefaultCMYK - * spaces. - */ - code = pdfi_dict_knownget(ctx, page_dict, "Resources", (pdf_obj **)&resources_dict); - if (code > 0) { - code = pdfi_dict_knownget(ctx, resources_dict, "ColorSpace", (pdf_obj **)&colorspaces_dict); - if (code > 0) { - code = pdfi_dict_knownget(ctx, colorspaces_dict, "DefaultGray", &DefaultSpace); - if (code > 0) { - gs_color_space *pcs; - code = pdfi_create_colorspace(ctx, DefaultSpace, NULL, page_dict, &pcs, false); - /* If any given Default* space fails simply ignore it, we wil then use the Device - * space instead, this is as per the spec. - */ - if (code >= 0) { - ctx->page.DefaultGray_cs = pcs; - pdfi_set_colour_callback(pcs, ctx, NULL); - } - } - pdfi_countdown(DefaultSpace); - DefaultSpace = NULL; - code = pdfi_dict_knownget(ctx, colorspaces_dict, "DefaultRGB", &DefaultSpace); - if (code > 0) { - gs_color_space *pcs; - code = pdfi_create_colorspace(ctx, DefaultSpace, NULL, page_dict, &pcs, false); - /* If any given Default* space fails simply ignore it, we wil then use the Device - * space instead, this is as per the spec. - */ - if (code >= 0) { - ctx->page.DefaultRGB_cs = pcs; - pdfi_set_colour_callback(pcs, ctx, NULL); - } - } - pdfi_countdown(DefaultSpace); - DefaultSpace = NULL; - code = pdfi_dict_knownget(ctx, colorspaces_dict, "DefaultCMYK", &DefaultSpace); - if (code > 0) { - gs_color_space *pcs; - code = pdfi_create_colorspace(ctx, DefaultSpace, NULL, page_dict, &pcs, false); - /* If any given Default* space fails simply ignore it, we wil then use the Device - * space instead, this is as per the spec. - */ - if (code >= 0) { - ctx->page.DefaultCMYK_cs = pcs; - pdfi_set_colour_callback(pcs, ctx, NULL); - } - } - pdfi_countdown(DefaultSpace); - DefaultSpace = NULL; - } - } - - pdfi_countdown(DefaultSpace); - pdfi_countdown(resources_dict); - pdfi_countdown(colorspaces_dict); - return 0; + return(pdfi_setup_DefaultSpaces(ctx, page_dict)); } int pdfi_page_render(pdf_context *ctx, uint64_t page_num, bool init_graphics) @@ -766,18 +705,18 @@ int pdfi_page_render(pdf_context *ctx, uint64_t page_num, bool init_graphics) char extra_info[256]; page_dict_error = true; - gs_sprintf(extra_info, "*** ERROR: Page %ld has invalid Page dict, skipping\n", page_num+1); + gs_snprintf(extra_info, sizeof(extra_info), "*** ERROR: Page %ld has invalid Page dict, skipping\n", page_num+1); pdfi_set_error(ctx, 0, NULL, E_PDF_PAGEDICTERROR, "pdfi_page_render", extra_info); if (code != gs_error_VMerror && !ctx->args.pdfstoponerror) code = 0; - goto exit2; + goto exit3; } pdfi_device_set_flags(ctx); code = pdfi_check_page(ctx, page_dict, init_graphics); if (code < 0) - goto exit2; + goto exit3; if (ctx->args.pdfdebug) { dbgmprintf2(ctx->memory, "Current page %ld transparency setting is %d", page_num+1, @@ -791,7 +730,7 @@ int pdfi_page_render(pdf_context *ctx, uint64_t page_num, bool init_graphics) code = pdfi_dict_knownget_type(ctx, page_dict, "Group", PDF_DICT, (pdf_obj **)&group_dict); if (code < 0) - goto exit2; + goto exit3; if (group_dict != NULL) page_group_known = true; @@ -829,7 +768,7 @@ int pdfi_page_render(pdf_context *ctx, uint64_t page_num, bool init_graphics) } /* Write the various CropBox, TrimBox etc to the device */ - pdfi_write_boxes_pdfmark(ctx, page_dict); + pdfi_pdfmark_write_boxes(ctx, page_dict); code = setup_page_DefaultSpaces(ctx, page_dict); if (code < 0) @@ -866,7 +805,7 @@ int pdfi_page_render(pdf_context *ctx, uint64_t page_num, bool init_graphics) /* We don't retain the PDF14 device */ code = gs_push_pdf14trans_device(ctx->pgs, false, false, trans_depth, ctx->page.num_spots); if (code >= 0) { - if (page_group_known) { + if (ctx->page.has_transparency && page_group_known) { code = pdfi_trans_begin_page_group(ctx, page_dict, group_dict); /* If setting the page group failed for some reason, abandon the page group, * but continue with the page @@ -889,18 +828,19 @@ int pdfi_page_render(pdf_context *ctx, uint64_t page_num, bool init_graphics) pdfi_set_DefaultQState(ctx, ctx->pgs); /* Render one page (including annotations) */ + if (!ctx->args.QUIET) + outprintf(ctx->memory, "Page %"PRId64"\n", page_num + 1); + code = pdfi_process_one_page(ctx, page_dict); - if (ctx->page.has_transparency && page_group_known) { + if (need_pdf14 && ctx->page.has_transparency && page_group_known) { code1 = pdfi_trans_end_group(ctx); } - pdfi_countdown(ctx->page.CurrentPageDict); - ctx->page.CurrentPageDict = NULL; - if (need_pdf14) { if (code1 < 0) { (void)gs_abort_pdf14trans_device(ctx->pgs); + code = code1; goto exit1; } @@ -910,10 +850,15 @@ int pdfi_page_render(pdf_context *ctx, uint64_t page_num, bool init_graphics) } } - exit1: +exit1: pdfi_free_DefaultQState(ctx); pdfi_grestore(ctx); - exit2: + +exit2: + pdfi_countdown(ctx->page.CurrentPageDict); + ctx->page.CurrentPageDict = NULL; + +exit3: pdfi_countdown(page_dict); pdfi_countdown(group_dict); diff --git a/pdf/pdf_path.c b/pdf/pdf_path.c index ef6f1328..a9724def 100644 --- a/pdf/pdf_path.c +++ b/pdf/pdf_path.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -16,6 +16,7 @@ /* Path operations for the PDF interpreter */ #include "pdf_int.h" +#include "pdf_font_types.h" #include "pdf_gstate.h" #include "pdf_path.h" #include "pdf_stack.h" @@ -120,22 +121,28 @@ static int pdfi_fill_inner(pdf_context *ctx, bool use_eofill) if (pdfi_oc_is_off(ctx)) goto exit; - code = pdfi_gsave(ctx); - if (code < 0) goto exit; - code = pdfi_trans_setup(ctx, &state, NULL, TRANSPARENCY_Caller_Fill); if (code == 0) { + /* If we don't gsave/grestore round the fill, then the file + * /tests_private/pdf/sumatra/954_-_dashed_lines_hardly_visible.pdf renders + * incorrectly. However we must not gsave/grestore round the trans_setup + * trans_teardown, because that might set pgs->soft_mask_id and if we restore + * back to a point where that is not set then pdfwrite doesn't work properly. + */ + code = pdfi_gsave(ctx); + if (code < 0) goto exit; + if (use_eofill) code = gs_eofill(ctx->pgs); else code = gs_fill(ctx->pgs); + code1 = pdfi_grestore(ctx); + if (code == 0) code = code1; + code1 = pdfi_trans_teardown(ctx, &state); if (code == 0) code = code1; } - code1 = pdfi_grestore(ctx); - if (code == 0) code = code1; - exit: code1 = pdfi_newpath(ctx); if (code == 0) code = code1; @@ -164,20 +171,27 @@ int pdfi_stroke(pdf_context *ctx) if (pdfi_oc_is_off(ctx)) goto exit; - code = pdfi_gsave(ctx); - if (code < 0) goto exit; +/* code = pdfi_gsave(ctx); + if (code < 0) goto exit;*/ gs_swapcolors_quick(ctx->pgs); code = pdfi_trans_setup(ctx, &state, NULL, TRANSPARENCY_Caller_Stroke); if (code == 0) { + code = pdfi_gsave(ctx); + if (code < 0) goto exit; + code = gs_stroke(ctx->pgs); + + code1 = pdfi_grestore(ctx); + if (code == 0) code = code1; + code1 = pdfi_trans_teardown(ctx, &state); if (code == 0) code = code1; } gs_swapcolors_quick(ctx->pgs); - code1 = pdfi_grestore(ctx); - if (code == 0) code = code1; +/* code1 = pdfi_grestore(ctx); + if (code == 0) code = code1;*/ exit: code1 = pdfi_newpath(ctx); @@ -378,22 +392,23 @@ static int pdfi_B_inner(pdf_context *ctx, bool use_eofill) if (pdfi_oc_is_off(ctx)) goto exit; - code = pdfi_gsave(ctx); - if (code < 0) goto exit; - code = pdfi_trans_setup(ctx, &state, NULL, TRANSPARENCY_Caller_FillStroke); if (code == 0) { + code = pdfi_gsave(ctx); + if (code < 0) goto exit; + if (use_eofill) code = gs_eofillstroke(ctx->pgs, &code1); else code = gs_fillstroke(ctx->pgs, &code1); + + code1 = pdfi_grestore(ctx); + if (code == 0) code = code1; + code1 = pdfi_trans_teardown(ctx, &state); if (code >= 0) code = code1; } - code1 = pdfi_grestore(ctx); - if (code == 0) code = code1; - exit: code1 = pdfi_newpath(ctx); if (code == 0) code = code1; diff --git a/pdf/pdf_pattern.c b/pdf/pdf_pattern.c index 9d4f9eaa..57a16449 100644 --- a/pdf/pdf_pattern.c +++ b/pdf/pdf_pattern.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -21,6 +21,7 @@ #include "pdf_pattern.h" #include "pdf_stack.h" #include "pdf_array.h" +#include "pdf_font_types.h" #include "pdf_gstate.h" #include "pdf_file.h" #include "pdf_dict.h" @@ -116,8 +117,7 @@ static void pdfi_free_pattern_context(pdf_pattern_context_t *context) static bool pdfi_pattern_purge_proc(gx_color_tile * ctile, void *proc_data) { - gs_id id = (gs_id)proc_data; - if (ctile->id == id) + if (ctile->id == *((gx_bitmap_id *)proc_data)) return true; return false; } @@ -138,7 +138,7 @@ void pdfi_pattern_cleanup(gs_memory_t * mem, void *p) context->shading == NULL && context->ctx->pgs->pattern_cache != NULL && gx_pattern_cache_get_entry(context->ctx->pgs, pinst->id, &pctile) == 0 && gx_pattern_tile_is_clist(pctile)) { - gx_pattern_cache_winnow(gstate_pattern_cache(context->ctx->pgs), pdfi_pattern_purge_proc, (void *)(pctile->id)); + gx_pattern_cache_winnow(gstate_pattern_cache(context->ctx->pgs), pdfi_pattern_purge_proc, (void *)(&pctile->id)); } if (context != NULL) { @@ -240,10 +240,6 @@ pdfi_pattern_paint_high_level(const gs_client_color *pcc, gs_gstate *pgs_ignore) gx_device_color *pdc = gs_currentdevicecolor_inline(pgs); pattern_accum_param_s param; - code = gx_pattern_cache_add_dummy_entry(pgs, pinst, pgs->device->color_info.depth); - if (code < 0) - return code; - code = pdfi_gsave(ctx); if (code < 0) return code; @@ -293,6 +289,17 @@ pdfi_pattern_paint_high_level(const gs_client_color *pcc, gs_gstate *pgs_ignore) code = pdfi_grestore(ctx); if (code < 0) return code; + + /* We create the dummy cache entry last, after we've executed the Pattern PaintProc. This is because + * if we ran another Pattern during the PaintProc, and that pattern has an id which happens to + * collide with the id of this pattern, it would overwrite the entry in the pattern cache. + * Deferring the entry in the cache until we are complete prevents this happening. + * For an example see Bug693422.pdf. + */ + code = gx_pattern_cache_add_dummy_entry(pgs, pinst, pgs->device->color_info.depth); + if (code < 0) + return code; + return gs_error_handled; errorExit: @@ -439,6 +446,14 @@ pdfi_setpattern_type1(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_di if (code < 0) goto exit; + /* The pattern instance holds the pattern step as floats, make sure they + * will fit. + */ + if (XStep < -MAX_FLOAT || XStep > MAX_FLOAT || YStep < -MAX_FLOAT || YStep > MAX_FLOAT) { + code = gs_note_error(gs_error_rangecheck); + goto exit; + } + /* The spec says Resources are required, but in fact this doesn't seem to be true. * (tests_private/pdf/sumatra/infinite_pattern_recursion.pdf) */ @@ -468,11 +483,15 @@ pdfi_setpattern_type1(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_di goto exit; } - /* See if pattern uses transparency */ - if (ctx->page.has_transparency) { - code = pdfi_check_Pattern_transparency(ctx, pdict, page_dict, &transparency); - if (code < 0) - goto exit; + /* See if pattern uses transparency, or if we are in an overprint + simulation situation */ + if (ctx->page.simulate_op) + transparency = true; + else + if (ctx->page.has_transparency) { + code = pdfi_check_Pattern_transparency(ctx, pdict, page_dict, &transparency); + if (code < 0) + goto exit; } /* TODO: Resources? Maybe I should check that they are all valid before proceeding, or something? */ @@ -512,7 +531,7 @@ pdfi_setpattern_type1(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_di if (code < 0) goto exit; exit: - pdfi_countdown(context); + gs_free_object(ctx->memory, context, "pdfi_setpattern_type1(context)"); pdfi_countdown(Resources); pdfi_countdown(Matrix); pdfi_countdown(BBox); diff --git a/pdf/pdf_repair.c b/pdf/pdf_repair.c index dafcd4c0..2c2badf2 100644 --- a/pdf/pdf_repair.c +++ b/pdf/pdf_repair.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2021 Artifex Software, Inc. +/* Copyright (C) 2020-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -52,7 +52,7 @@ static int pdfi_repair_add_object(pdf_context *ctx, uint64_t obj, uint64_t gen, ctx->xref_table->type = PDF_XREF_TABLE; ctx->xref_table->xref_size = obj + 1; #if REFCNT_DEBUG - ctx->xref_table->UID = ctx->UID++; + ctx->xref_table->UID = ctx->ref_UID++; dmprintf1(ctx->memory, "Allocated xref table with UID %"PRIi64"\n", ctx->xref_table->UID); #endif pdfi_countup(ctx->xref_table); @@ -109,24 +109,24 @@ int pdfi_repair_file(pdf_context *ctx) */ pdfi_seek(ctx, ctx->main_stream, 0, SEEK_SET); { - char Buffer[10], test[] = "%PDF"; + static const char test[] = "%PDF"; int index = 0; do { - code = pdfi_read_bytes(ctx, (byte *)&Buffer[index], 1, 1, ctx->main_stream); - if (code < 0) + int c = pdfi_read_byte(ctx, ctx->main_stream); + if (c < 0) goto exit; - if (Buffer[index] == test[index]) + if (c == test[index]) index++; else index = 0; - } while (index < 4 && ctx->main_stream->eof == false); - if (memcmp(Buffer, test, 4) != 0) { + } while (index < 4); + if (index != 4) { code = gs_note_error(gs_error_undefined); goto exit; } - pdfi_unread(ctx, ctx->main_stream, (byte *)Buffer, 4); + pdfi_unread(ctx, ctx->main_stream, (byte *)test, 4); pdfi_skip_comment(ctx, ctx->main_stream); } if (ctx->main_stream->eof == true) { @@ -188,7 +188,7 @@ int pdfi_repair_file(pdf_context *ctx) /* move all the saved offsets up by one */ saved_offset[0] = saved_offset[1]; saved_offset[1] = saved_offset[2]; - saved_offset[2] = pdfi_unread_tell(ctx);; + saved_offset[2] = pdfi_unread_tell(ctx); code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); if (code < 0) { @@ -228,21 +228,22 @@ int pdfi_repair_file(pdf_context *ctx) break; } else { if (k->key == TOKEN_STREAM) { - char Buffer[10], test[] = "endstream"; + static const char test[] = "endstream"; int index = 0; do { - code = pdfi_read_bytes(ctx, (byte *)&Buffer[index], 1, 1, ctx->main_stream); - if (code < 0) { - if (code != gs_error_VMerror && code != gs_error_ioerror) - continue; + int c = pdfi_read_byte(ctx, ctx->main_stream); + if (c == EOFC) + break; + if (c < 0) goto exit; - } - if (Buffer[index] == test[index]) + if (c == test[index]) index++; + else if (c == test[0]) /* Pesky 'e' appears twice */ + index = 1; else index = 0; - } while (index < 9 && ctx->main_stream->eof == false); + } while (index < 9); do { code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); if (code < 0) { @@ -250,16 +251,18 @@ int pdfi_repair_file(pdf_context *ctx) continue; goto exit; } - if (ctx->stack_top[-1]->type == PDF_KEYWORD){ - pdf_keyword *k = (pdf_keyword *)ctx->stack_top[-1]; - if (k->key == TOKEN_ENDOBJ) { - code = pdfi_repair_add_object(ctx, object_num, generation_num, offset); - if (code < 0) { - if (code != gs_error_VMerror && code != gs_error_ioerror) - break; - goto exit; + if (code > 0) { + if (ctx->stack_top[-1]->type == PDF_KEYWORD){ + pdf_keyword *k = (pdf_keyword *)ctx->stack_top[-1]; + if (k->key == TOKEN_ENDOBJ) { + code = pdfi_repair_add_object(ctx, object_num, generation_num, offset); + if (code < 0) { + if (code != gs_error_VMerror && code != gs_error_ioerror) + break; + goto exit; + } + break; } - break; } } }while(ctx->main_stream->eof == false); @@ -287,7 +290,7 @@ int pdfi_repair_file(pdf_context *ctx) } else { if (k->key == TOKEN_TRAILER) { code = pdfi_read_bare_object(ctx, ctx->main_stream, 0, 0, 0); - if (code == 0 && ctx->stack_top[-1]->type == PDF_DICT) { + if (code == 0 && pdfi_count_stack(ctx) > 0 && ctx->stack_top[-1]->type == PDF_DICT) { if (ctx->Trailer) { pdf_dict *d = (pdf_dict *)ctx->stack_top[-1]; bool known = false; @@ -420,13 +423,15 @@ int pdfi_repair_file(pdf_context *ctx) if (code == 0) { for (j=0;j < N; j++) { code = pdfi_read_token(ctx, compressed_stream, 0, 0); - if (code == 0) { + if (code == 0) + break; + if (code > 0) { o = ctx->stack_top[-1]; if (((pdf_obj *)o)->type == PDF_INT) { obj_num = ((pdf_num *)o)->value.i; pdfi_pop(ctx, 1); code = pdfi_read_token(ctx, compressed_stream, 0, 0); - if (code == 0) { + if (code > 0) { o = ctx->stack_top[-1]; if (((pdf_obj *)o)->type == PDF_INT) { offset = ((pdf_num *)o)->value.i; @@ -436,11 +441,16 @@ int pdfi_repair_file(pdf_context *ctx) code = gs_note_error(gs_error_rangecheck); goto exit; } - ctx->xref_table->xref[obj_num].compressed = true; - ctx->xref_table->xref[obj_num].free = false; - ctx->xref_table->xref[obj_num].object_num = obj_num; - ctx->xref_table->xref[obj_num].u.compressed.compressed_stream_num = i; - ctx->xref_table->xref[obj_num].u.compressed.object_index = j; + if (obj_num >= ctx->xref_table->xref_size) + code = pdfi_repair_add_object(ctx, obj_num, 0, 0); + + if (code >= 0) { + ctx->xref_table->xref[obj_num].compressed = true; + ctx->xref_table->xref[obj_num].free = false; + ctx->xref_table->xref[obj_num].object_num = obj_num; + ctx->xref_table->xref[obj_num].u.compressed.compressed_stream_num = i; + ctx->xref_table->xref[obj_num].u.compressed.object_index = j; + } } } } diff --git a/pdf/pdf_sec.c b/pdf/pdf_sec.c index ff608052..fa7131f8 100644 --- a/pdf/pdf_sec.c +++ b/pdf/pdf_sec.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2021 Artifex Software, Inc. +/* Copyright (C) 2020-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -57,6 +57,7 @@ static int pdf_compute_encryption_key_preR5(pdf_context *ctx, char *Password, in gs_md5_state_t md5; pdf_array *a = NULL; pdf_string *s = NULL; + pdf_dict *d = NULL; *EKey = NULL; /* Algorithm 3.2 */ @@ -86,7 +87,11 @@ static int pdf_compute_encryption_key_preR5(pdf_context *ctx, char *Password, in gs_md5_append(&md5, (gs_md5_byte_t *)P, 4); /* 5. Pass the first element of the file's file identifier array */ - code = pdfi_dict_get_type(ctx, ctx->Trailer, "ID", PDF_ARRAY, (pdf_obj **)&a); + /* See comment in pdfi_read_Root() for details of why we indirect through 'd' */ + d = ctx->Trailer; + pdfi_countup(d); + code = pdfi_dict_get_type(ctx, d, "ID", PDF_ARRAY, (pdf_obj **)&a); + pdfi_countdown(d); if (code < 0) { if (code == gs_error_undefined) { emprintf(ctx->memory, "\n **** Error: ID key in the trailer is required for encrypted files.\n"); @@ -430,6 +435,7 @@ static int check_user_password_preR5(pdf_context *ctx, char *Password, int Len, gs_md5_state_t md5; pdf_string *s = NULL; pdf_array *a = NULL; + pdf_dict *d = NULL; /* Algorithm 3.6, step 1 * perform all but the last step of Algorithm 3,4 (Revision 2) @@ -482,7 +488,11 @@ static int check_user_password_preR5(pdf_context *ctx, char *Password, int Len, * Pass the 32 byte padding string from step 1 of Algorithm 3.2 to an MD5 hash */ gs_md5_init(&md5); gs_md5_append(&md5, (gs_md5_byte_t *)PadString, 32); - code = pdfi_dict_get_type(ctx, ctx->Trailer, "ID", PDF_ARRAY, (pdf_obj **)&a); + /* See comment in pdfi_read_Root() for details of why we indirect through 'd' */ + d = ctx->Trailer; + pdfi_countup(d); + code = pdfi_dict_get_type(ctx, d, "ID", PDF_ARRAY, (pdf_obj **)&a); + pdfi_countdown(d); if (code < 0) { if (code == gs_error_undefined) { emprintf(ctx->memory, "\n **** Error: ID key in the trailer is required for encrypted files.\n"); @@ -918,6 +928,9 @@ int pdfi_decrypt_string(pdf_context *ctx, pdf_string *string) pdf_string *EKey = NULL; char *Buffer = NULL; + if (ctx->encryption.StrF == CRYPT_IDENTITY) + return 0; + if (!is_compressed_object(ctx, string->indirect_num, string->indirect_gen)) { Buffer = (char *)gs_alloc_bytes(ctx->memory, string->length, "pdfi_decrypt_string"); if (Buffer == NULL) @@ -978,7 +991,7 @@ static int pdfi_read_Encrypt_dict(pdf_context *ctx, int *KeyLen) { int code = 0; pdf_dict *CF_dict = NULL, *StdCF_dict = NULL; - pdf_dict *d = NULL; + pdf_dict *d = NULL, *d1 = NULL; pdf_obj *o = NULL; pdf_string *s = NULL; int64_t i64; @@ -987,7 +1000,12 @@ static int pdfi_read_Encrypt_dict(pdf_context *ctx, int *KeyLen) if (ctx->args.pdfdebug) dmprintf(ctx->memory, "%% Checking for Encrypt dictionary\n"); - code = pdfi_dict_get(ctx, ctx->Trailer, "Encrypt", (pdf_obj **)&d); + /* See comment in pdfi_read_Root() for details of why we indirect through 'd' */ + d1 = ctx->Trailer; + pdfi_countup(d1); + code = pdfi_dict_get(ctx, d1, "Encrypt", (pdf_obj **)&d); + pdfi_countdown(d1); + d1 = NULL; /* Undefined is acceptable here, it just means the PDF file is not ostensibly encrypted */ /* NB pdfi_process_pdf_file() always checks for the Encrypt dictionary before we @@ -1122,6 +1140,10 @@ static int pdfi_read_Encrypt_dict(pdf_context *ctx, int *KeyLen) code = pdfi_dict_knownget_type(ctx, d, "StrF", PDF_NAME, &o); if (code < 0) goto done; + if (code == 0) { + code = gs_note_error(gs_error_undefined); + goto done; + } if (!pdfi_name_is((pdf_name *)o, "StdCF")) { if (pdfi_name_is((pdf_name *)o, "Identity")) { ctx->encryption.StrF = CRYPT_IDENTITY; @@ -1236,6 +1258,8 @@ static int check_password_R5(pdf_context *ctx, char *Password, int PasswordLen, int code; if (PasswordLen != 0) { + pdf_string *P = NULL, *P_UTF8 = NULL; + code = check_user_password_R5(ctx, Password, PasswordLen, KeyLen); if (code >= 0) return 0; @@ -1247,33 +1271,28 @@ static int check_password_R5(pdf_context *ctx, char *Password, int PasswordLen, /* If the supplied Password fails as the user *and* owner password, maybe its in * the locale, not UTF-8, try converting to UTF-8 */ + code = pdfi_object_alloc(ctx, PDF_STRING, strlen(ctx->encryption.Password), (pdf_obj **)&P); + if (code < 0) + return code; + memcpy(P->data, Password, PasswordLen); + pdfi_countup(P); + code = locale_to_utf8(ctx, P, &P_UTF8); if (code < 0) { - pdf_string *P = NULL, *P_UTF8 = NULL; - - code = pdfi_object_alloc(ctx, PDF_STRING, strlen(ctx->encryption.Password), (pdf_obj **)&P); - if (code < 0) { - return code; - } - memcpy(P->data, Password, PasswordLen); - pdfi_countup(P); - code = locale_to_utf8(ctx, P, &P_UTF8); - if (code < 0) { - pdfi_countdown(P); - return code; - } - code = check_user_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen); - if (code >= 0) { - pdfi_countdown(P); - pdfi_countdown(P_UTF8); - return code; - } - - code = check_owner_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen); + pdfi_countdown(P); + return code; + } + code = check_user_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen); + if (code >= 0) { pdfi_countdown(P); pdfi_countdown(P_UTF8); - if (code >= 0) - return code; + return code; } + + code = check_owner_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen); + pdfi_countdown(P); + pdfi_countdown(P_UTF8); + if (code >= 0) + return code; } code = check_user_password_R5(ctx, (char *)"", 0, KeyLen); if (code >= 0) @@ -1287,6 +1306,8 @@ static int check_password_R6(pdf_context *ctx, char *Password, int PasswordLen, int code; if (PasswordLen != 0) { + pdf_string *P = NULL, *P_UTF8 = NULL; + code = check_user_password_R6(ctx, Password, PasswordLen, KeyLen); if (code >= 0) return 0; @@ -1297,32 +1318,28 @@ static int check_password_R6(pdf_context *ctx, char *Password, int PasswordLen, /* If the supplied Password fails as the user *and* owner password, maybe its in * the locale, not UTF-8, try converting to UTF-8 */ + code = pdfi_object_alloc(ctx, PDF_STRING, strlen(ctx->encryption.Password), (pdf_obj **)&P); + if (code < 0) + return code; + memcpy(P->data, Password, PasswordLen); + pdfi_countup(P); + code = locale_to_utf8(ctx, P, &P_UTF8); if (code < 0) { - pdf_string *P = NULL, *P_UTF8 = NULL; - - code = pdfi_object_alloc(ctx, PDF_STRING, strlen(ctx->encryption.Password), (pdf_obj **)&P); - if (code < 0) - return code; - memcpy(P->data, Password, PasswordLen); - pdfi_countup(P); - code = locale_to_utf8(ctx, P, &P_UTF8); - if (code < 0) { - pdfi_countdown(P); - return code; - } - code = check_user_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen); - if (code >= 0) { - pdfi_countdown(P); - pdfi_countdown(P_UTF8); - return code; - } - - code = check_owner_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen); + pdfi_countdown(P); + return code; + } + code = check_user_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen); + if (code >= 0) { pdfi_countdown(P); pdfi_countdown(P_UTF8); - if (code >= 0) - return code; + return code; } + + code = check_owner_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen); + pdfi_countdown(P); + pdfi_countdown(P_UTF8); + if (code >= 0) + return code; } code = check_user_password_R6(ctx, (char *)"", 0, KeyLen); if (code >= 0) @@ -1352,6 +1369,10 @@ int pdfi_initialise_Decryption(pdf_context *ctx) case 2: /* Set up the defaults if not already set */ /* Revision 2 is always 40-bit RC4 */ + if (KeyLen != 0 && (KeyLen < 40 || KeyLen > 128 || KeyLen % 8 != 0)) { + pdfi_set_error(ctx, 0, NULL, E_PDF_INVALID_DECRYPT_LEN, "pdfi_initialise_Decryption", NULL); + return_error(gs_error_rangecheck); + } if (KeyLen == 0) KeyLen = 40; if (ctx->encryption.StmF == CRYPT_NONE) @@ -1363,8 +1384,9 @@ int pdfi_initialise_Decryption(pdf_context *ctx) case 3: /* Set up the defaults if not already set */ /* Revision 3 is always 128-bit RC4 */ - if (KeyLen == 0) - KeyLen = 128; + if (KeyLen != 0 && KeyLen != 128) + pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_DECRYPT_LEN, "pdfi_initialise_Decryption", NULL); + KeyLen = 128; if (ctx->encryption.StmF == CRYPT_NONE) ctx->encryption.StmF = CRYPT_V2; if (ctx->encryption.StrF == CRYPT_NONE) @@ -1372,16 +1394,20 @@ int pdfi_initialise_Decryption(pdf_context *ctx) code = check_password_preR5(ctx, ctx->encryption.Password, ctx->encryption.PasswordLen, KeyLen, 3); break; case 4: - /* Revision 4 is either AES or RC4, but its always 128-bits */ - if (KeyLen == 0) + if (ctx->encryption.StrF != CRYPT_IDENTITY || ctx->encryption.StmF != CRYPT_IDENTITY) { + /* Revision 4 is either AES or RC4, but its always 128-bits */ + if (KeyLen != 0) + pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_DECRYPT_LEN, "pdfi_initialise_Decryption", NULL); KeyLen = 128; - /* We can't set the encryption filter, so we have to hope the PDF file did */ - code = check_password_preR5(ctx, ctx->encryption.Password, ctx->encryption.PasswordLen, KeyLen, 4); + /* We can't set the encryption filter, so we have to hope the PDF file did */ + code = check_password_preR5(ctx, ctx->encryption.Password, ctx->encryption.PasswordLen, KeyLen, 4); + } break; case 5: /* Set up the defaults if not already set */ - if (KeyLen == 0) - KeyLen = 256; + if (KeyLen != 0) + pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_DECRYPT_LEN, "pdfi_initialise_Decryption", NULL); + KeyLen = 256; if (ctx->encryption.StmF == CRYPT_NONE) ctx->encryption.StmF = CRYPT_AESV2; if (ctx->encryption.StrF == CRYPT_NONE) @@ -1391,8 +1417,9 @@ int pdfi_initialise_Decryption(pdf_context *ctx) case 6: /* Set up the defaults if not already set */ /* Revision 6 is always 256-bit AES */ - if (KeyLen == 0) - KeyLen = 256; + if (KeyLen != 0) + pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_DECRYPT_LEN, "pdfi_initialise_Decryption", NULL); + KeyLen = 256; if (ctx->encryption.StmF == CRYPT_NONE) ctx->encryption.StmF = CRYPT_AESV3; if (ctx->encryption.StrF == CRYPT_NONE) diff --git a/pdf/pdf_shading.c b/pdf/pdf_shading.c index 01a2d0e1..c4e93e88 100644 --- a/pdf/pdf_shading.c +++ b/pdf/pdf_shading.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -17,6 +17,7 @@ #include "pdf_int.h" #include "pdf_stack.h" +#include "pdf_font_types.h" #include "pdf_gstate.h" #include "pdf_shading.h" #include "pdf_dict.h" @@ -35,6 +36,7 @@ #include "gsptype2.h" #include "gsfunc0.h" /* For gs_function */ #include "gscolor3.h" /* For gs_shfill() */ +#include "gsstate.h" /* For gs_setoverprintmode */ static int pdfi_build_shading_function(pdf_context *ctx, gs_function_t **ppfn, const float *shading_domain, int num_inputs, pdf_dict *shading_dict, pdf_dict *page_dict) { @@ -705,8 +707,7 @@ pdfi_shading_build(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, goto shading_error; pdfi_countdown(cspace); - if (code >= 0) - *ppsh = psh; + *ppsh = psh; return code; shading_error: @@ -810,7 +811,7 @@ pdfi_shading_setup_trans(pdf_context *ctx, pdfi_trans_state_t *state, pdf_obj *S /* If we didn't get a BBox for the shading, then we need to create one, in order to * pass it to the transparency setup, which (potentially, at least, uses it to set * up a transparency group. - * In the basence of anything better, we take the currnet clip, turn that into a path + * In the absence of anything better, we take the current clip, turn that into a path * and then get the bounding box of that path. Obviously we don't want to disturb the * current path in the graphics state, so we do a gsave/grestore round it. */ @@ -852,6 +853,7 @@ int pdfi_shading(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) gs_shading_t *psh = NULL; gs_offset_t savedoffset; pdfi_trans_state_t trans_state; + int trans_required; if (pdfi_count_stack(ctx) < 1) return_error(gs_error_stackunderflow); @@ -889,11 +891,18 @@ int pdfi_shading(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) if (code < 0) goto exit2; + /* Shadings fills can't use overprint mode */ + code = gs_setoverprintmode(ctx->pgs, 0); + if (code < 0) + goto exit2; + code = pdfi_shading_build(ctx, stream_dict, page_dict, Shading, &psh); if (code < 0) goto exit2; - if (ctx->page.has_transparency) { + trans_required = pdfi_trans_required(ctx); + + if (trans_required) { code = pdfi_shading_setup_trans(ctx, &trans_state, Shading); if (code < 0) goto exit2; @@ -905,7 +914,7 @@ int pdfi_shading(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) code = 0; } - if (ctx->page.has_transparency) { + if (trans_required) { code1 = pdfi_trans_teardown(ctx, &trans_state); if (code == 0) code = code1; diff --git a/pdf/pdf_text.c b/pdf/pdf_text.c index 4bc6d907..e6345d0f 100644 --- a/pdf/pdf_text.c +++ b/pdf/pdf_text.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -21,9 +21,9 @@ #include "pdf_image.h" #include "pdf_colour.h" #include "pdf_stack.h" -#include "pdf_gstate.h" #include "pdf_font.h" #include "pdf_font_types.h" +#include "pdf_gstate.h" #include "pdf_trans.h" #include "pdf_optcontent.h" @@ -486,6 +486,11 @@ static int pdfi_show_set_params(pdf_context *ctx, pdf_string *s, gs_text_params_ } text->size = s->length; } + else { + code = gs_note_error(gs_error_invalidfont); + goto text_params_error; + } + return 0; text_params_error: @@ -1081,6 +1086,13 @@ int pdfi_Tj(pdf_context *ctx) if (s->type != PDF_STRING) return_error(gs_error_typecheck); + /* We can't rely on the stack reference because an error during + the text operation (i.e. retrieving objects for glyph metrics + may cause the stack to be cleared. + */ + pdfi_countup(s); + pdfi_pop(ctx, 1); + /* Save the CTM for later restoration */ saved = ctm_only(ctx->pgs); gs_currentpoint(ctx->pgs, &initial_point); @@ -1155,7 +1167,7 @@ Tj_error: ctx->pgs->line_params.half_width = linewidth; exit: - pdfi_pop(ctx, 1); + pdfi_countdown(s); return code; } @@ -1190,6 +1202,8 @@ int pdfi_TJ(pdf_context *ctx) pdfi_pop(ctx, 1); return gs_note_error(gs_error_typecheck); } + pdfi_countup(a); + pdfi_pop(ctx, 1); /* Save the CTM for later restoration */ saved = ctm_only(ctx->pgs); @@ -1301,7 +1315,7 @@ TJ_error: ctx->pgs->line_params.half_width = linewidth; exit: - pdfi_pop(ctx, 1); + pdfi_countdown(a); return code; } @@ -1449,9 +1463,13 @@ int pdfi_Tr(pdf_context *ctx) * accumulated text to a clip, then set the text rendering mode * to the non-clip mode, and perform an implicit BT. */ - pdfi_ET(ctx); + code = pdfi_ET(ctx); + if (code < 0) + return code; gs_settextrenderingmode(ctx->pgs, mode); - pdfi_BT(ctx); + code = pdfi_BT(ctx); + if (code < 0) + return code; } else gs_settextrenderingmode(ctx->pgs, mode); diff --git a/pdf/pdf_trans.c b/pdf/pdf_trans.c index 93ab8a6c..df0194d7 100644 --- a/pdf/pdf_trans.c +++ b/pdf/pdf_trans.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -20,6 +20,7 @@ #include "pdf_trans.h" #include "pdf_dict.h" #include "pdf_colour.h" +#include "pdf_font_types.h" #include "pdf_gstate.h" #include "pdf_array.h" #include "pdf_image.h" @@ -32,6 +33,7 @@ #include "gscoord.h" /* For gs_setmatrix()*/ #include "gsstate.h" /* For gs_currentstrokeoverprint() and others */ #include "gspath.h" /* For gs_clippath() */ +#include "gsicc_cache.h" /* For gsicc_profiles_equal() */ /* Implement the TransferFunction using a Function. */ static int @@ -156,13 +158,15 @@ static int pdfi_trans_set_mask(pdf_context *ctx, pdfi_int_gstate *igs, int color code = pdfi_dict_knownget_type(ctx, SMask, "S", PDF_NAME, (pdf_obj **)&S); if (code <= 0) { dmprintf(ctx->memory, "WARNING: Missing 'S' in SMask (defaulting to Luminosity)\n"); + subtype = TRANSPARENCY_MASK_Luminosity; } - if (pdfi_name_is(S, "Luminosity")) { + else if (pdfi_name_is(S, "Luminosity")) { subtype = TRANSPARENCY_MASK_Luminosity; } else if (pdfi_name_is(S, "Alpha")) { subtype = TRANSPARENCY_MASK_Alpha; } else { dmprintf(ctx->memory, "WARNING: Unknown subtype 'S' in SMask (defaulting to Luminosity)\n"); + subtype = TRANSPARENCY_MASK_Luminosity; } /* TR is transfer function (Optional) */ @@ -172,6 +176,11 @@ static int pdfi_trans_set_mask(pdf_context *ctx, pdfi_int_gstate *igs, int color code = pdfi_build_function(ctx, &gsfunc, NULL, 1, TR, NULL); if (code < 0) goto exit; + if (gsfunc->params.m != 1 || gsfunc->params.n != 1) { + pdfi_free_function(ctx, gsfunc); + gsfunc = NULL; + dmprintf(ctx->memory, "WARNING: Ignoring invalid TR (number of inpuits or outputs not 1) in SMask\n"); + } } else if (TR->type == PDF_NAME) { if (!pdfi_name_is((pdf_name *)TR, "Identity")) { dmprintf(ctx->memory, "WARNING: Unknown TR in SMask\n"); @@ -401,6 +410,32 @@ static int pdfi_transparency_group_common(pdf_context *ctx, pdf_dict *page_dict, return pdfi_gs_begin_transparency_group(ctx->pgs, ¶ms, (const gs_rect *)bbox, group_type); } +static bool pdfi_outputprofile_matches_oiprofile(pdf_context *ctx) +{ + cmm_dev_profile_t *profile_struct; + int code; + int k; + + code = dev_proc(ctx->pgs->device, get_profile)(ctx->pgs->device, &profile_struct); + if (code < 0) + return true; /* Assume they match by default and in error condition */ + + if (profile_struct->oi_profile == NULL) + return true; /* no OI profile so no special case to worry about */ + else { + /* Check the device profile(s). If any of them do not match, then + we assume there is not a match and it may be necessary to + use the pdf14 device to prerender to the OI profile */ + for (k = 0; k < NUM_DEVICE_PROFILES; k++) { + if (profile_struct->device_profile[k] != NULL) { + if (!gsicc_profiles_equal(profile_struct->oi_profile, profile_struct->device_profile[k])) + return false; + } + } + return true; + } +} + /* Begin a simple group * pathbbox -- bbox to use, but can be NULL */ @@ -591,14 +626,19 @@ void pdfi_trans_set_needs_OP(pdf_context *ctx) ctx->page.needs_OP = false; ctx->page.simulate_op = false; - switch(ctx->args.overprint_control) { - case PDF_OVERPRINT_DISABLE: + switch(ctx->pgs->device->icc_struct->overprint_control) { + case gs_overprint_control_disable: /* Use defaults */ break; - case PDF_OVERPRINT_SIMULATE: + case gs_overprint_control_simulate: if (!device_transparency && ctx->page.has_OP) { if (is_cmyk) { - if (ctx->page.num_spots > 0) { + /* If the page has spots and the device is not spot capable OR + if the output intent profile is to be used, but we have + a device output profile that is different, then we will be + doing simulation with the pdf14 device buffer */ + if ((ctx->page.num_spots > 0 && !ctx->device_state.spot_capable) || + !pdfi_outputprofile_matches_oiprofile(ctx)) { ctx->page.needs_OP = true; ctx->page.simulate_op = true; } @@ -608,7 +648,7 @@ void pdfi_trans_set_needs_OP(pdf_context *ctx) } } break; - case PDF_OVERPRINT_ENABLE: + case gs_overprint_control_enable: default: if (!is_cmyk || device_transparency) ctx->page.needs_OP = false; @@ -624,7 +664,7 @@ void pdfi_trans_set_needs_OP(pdf_context *ctx) } /* Figures out if current colorspace is okay for Overprint (see pdf_ops.ps/okOPcs and setupOPtrans) */ -static bool pdfi_trans_okOPcs(pdf_context *ctx) +bool pdfi_trans_okOPcs(pdf_context *ctx) { gs_color_space_index csi; @@ -707,11 +747,22 @@ int pdfi_trans_setup(pdf_context *ctx, pdfi_trans_state_t *state, gs_rect *bbox, /* TODO: error handling... */ if (need_group) { + bool isolated = false; + mode = gs_currentblendmode(ctx->pgs); + stroked_bbox = (caller == TRANSPARENCY_Caller_Stroke || caller == TRANSPARENCY_Caller_FillStroke); + /* When changing to compatible overprint bm, the group pushed must be non-isolated. The exception - is if we have a softmask. See /setupOPtrans in pdf_ops.ps */ - code = pdfi_trans_begin_simple_group(ctx, bbox, stroked_bbox, igs->SMask != NULL, false); - state->GroupPushed = true; + is if we have a softmask AND the blend mode is not normal and not compatible. + See /setupOPtrans in pdf_ops.ps */ + if (igs->SMask != NULL && mode != BLEND_MODE_Normal && mode != BLEND_MODE_Compatible) + isolated = true; + code = pdfi_trans_begin_simple_group(ctx, bbox, stroked_bbox, isolated, false); + + /* Group was not pushed if error */ + if (code >= 0) + state->GroupPushed = true; + state->saveStrokeAlpha = gs_getstrokeconstantalpha(ctx->pgs); state->saveFillAlpha = gs_getfillconstantalpha(ctx->pgs); code = gs_setfillconstantalpha(ctx->pgs, 1.0); @@ -725,12 +776,33 @@ int pdfi_trans_setup(pdf_context *ctx, pdfi_trans_state_t *state, gs_rect *bbox, return code; } +int pdfi_trans_required(pdf_context *ctx) +{ + gs_blend_mode_t mode; + + if (!ctx->page.has_transparency) + return 0; + + mode = gs_currentblendmode(ctx->pgs); + if ((mode == BLEND_MODE_Normal || mode == BLEND_MODE_Compatible) && + ctx->pgs->fillconstantalpha == 1 && + ctx->pgs->strokeconstantalpha == 1 && + ((pdfi_int_gstate *)ctx->pgs->client_data)->SMask == NULL) + return 0; + + return 1; +} + int pdfi_trans_setup_text(pdf_context *ctx, pdfi_trans_state_t *state, bool is_show) { - int Trmode = gs_currenttextrenderingmode(ctx->pgs); + int Trmode; int code, code1; gs_rect bbox; + if (!pdfi_trans_required(ctx)) + return 0; + + Trmode = gs_currenttextrenderingmode(ctx->pgs); code = gs_gsave(ctx->pgs); if (code < 0) goto exit; @@ -766,11 +838,10 @@ int pdfi_trans_setup_text(pdf_context *ctx, pdfi_trans_state_t *state, bool is_s int pdfi_trans_teardown_text(pdf_context *ctx, pdfi_trans_state_t *state) { - int code = 0; + if (!pdfi_trans_required(ctx)) + return 0; - code = pdfi_trans_teardown(ctx, state); - - return code; + return pdfi_trans_teardown(ctx, state); } int pdfi_trans_teardown(pdf_context *ctx, pdfi_trans_state_t *state) diff --git a/pdf/pdf_trans.h b/pdf/pdf_trans.h index e0057b74..3b71001d 100644 --- a/pdf/pdf_trans.h +++ b/pdf/pdf_trans.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2021 Artifex Software, Inc. +/* Copyright (C) 2019-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -33,6 +33,8 @@ typedef enum { TRANSPARENCY_Caller_FillStroke /* Also includes EOFillStroke */ } pdfi_transparency_caller_t; +bool pdfi_trans_okOPcs(pdf_context* ctx); +int pdfi_trans_required(pdf_context *ctx); int pdfi_trans_setup_text(pdf_context *ctx, pdfi_trans_state_t *state, bool is_show); int pdfi_trans_teardown_text(pdf_context *ctx, pdfi_trans_state_t *state); int pdfi_trans_setup(pdf_context *ctx, pdfi_trans_state_t *state, gs_rect *bbox, pdfi_transparency_caller_t caller); diff --git a/pdf/pdf_types.h b/pdf/pdf_types.h index 9b4a9519..b8213e0b 100644 --- a/pdf/pdf_types.h +++ b/pdf/pdf_types.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -56,7 +56,13 @@ typedef enum pdf_obj_type_e { PDF_ARRAY_MARK = '[', PDF_DICT_MARK = '<', PDF_PROC_MARK = '{', - PDF_CMAP = 'C' + PDF_CMAP = 'C', + /* Lastly, for the benefit of duplicate colour space identification, we store either + * a name for a colour space, or if there is no name, the context (we can get the + * context from the name object if there is one). We need to be able to tell if a + * pdf_obj is a name or a context. + */ + PDF_CTX = 'c' } pdf_obj_type; #if REFCNT_DEBUG @@ -103,6 +109,8 @@ typedef enum pdf_obj_type_e { uint16_t indirect_gen #endif +#define PDF_NAME_DECLARED_LENGTH 4096 + typedef struct pdf_obj_s { pdf_obj_common; } pdf_obj; @@ -124,13 +132,13 @@ typedef struct pdf_num_s { typedef struct pdf_string_s { pdf_obj_common; uint32_t length; - unsigned char *data; + unsigned char data[PDF_NAME_DECLARED_LENGTH]; } pdf_string; typedef struct pdf_name_s { pdf_obj_common; uint32_t length; - unsigned char *data; + unsigned char data[PDF_NAME_DECLARED_LENGTH]; } pdf_name; typedef enum pdf_key_e { @@ -143,13 +151,13 @@ typedef enum pdf_key_e { TOKEN_STARTXREF, TOKEN_TRAILER, TOKEN_INVALID_KEY, -}pdf_key; +} pdf_key; typedef struct pdf_keyword_s { pdf_obj_common; uint32_t length; - unsigned char *data; pdf_key key; + unsigned char data[PDF_NAME_DECLARED_LENGTH]; } pdf_keyword; typedef struct pdf_array_s { @@ -158,13 +166,18 @@ typedef struct pdf_array_s { pdf_obj **values; } pdf_array; +typedef struct pdf_dict_entry_s { + pdf_obj *key; + pdf_obj *value; +} pdf_dict_entry; + typedef struct pdf_dict_s { pdf_obj_common; uint64_t size; uint64_t entries; - pdf_obj **keys; - pdf_obj **values; - bool dict_written; /* Has dict been written (for pdfwrite) */ + pdf_dict_entry *list; + bool dict_written; /* Has dict been written (for pdfwrite) */ + bool is_sorted; /* true if the dictionary has been sorted by Key, for faster searching */ } pdf_dict; typedef struct pdf_stream_s { diff --git a/pdf/pdf_warnings.h b/pdf/pdf_warnings.h new file mode 100644 index 00000000..21b2403f --- /dev/null +++ b/pdf/pdf_warnings.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2022 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + +#ifndef PARAM +#define PARAM(A,B) A +#endif +PARAM(W_PDF_NOWARNING, "no warning"), +PARAM(W_PDF_BAD_XREF_SIZE, "incorrect xref size"), +PARAM(W_PDF_BAD_INLINEFILTER, "used inline filter name inappropriately"), +PARAM(W_PDF_BAD_INLINECOLORSPACE, "used inline colour space inappropriately"), +PARAM(W_PDF_BAD_INLINEIMAGEKEY, "used inline image key inappropriately"), +PARAM(W_PDF_IMAGE_ERROR, "recoverable image error"), +PARAM(W_PDF_BAD_IMAGEDICT, "recoverable error in image dictionary"), +PARAM(W_PDF_TOOMANYQ, "encountered more Q than q"), +PARAM(W_PDF_TOOMANYq, "encountered more q than Q"), +PARAM(W_PDF_STACKGARBAGE, "garbage left on stack"), +PARAM(W_PDF_STACKUNDERFLOW, "stack underflow"), +PARAM(W_PDF_GROUPERROR, "error in group definition"), +PARAM(W_PDF_OPINVALIDINTEXT, "invalid operator used in text block"), +PARAM(W_PDF_NOTINCHARPROC, "used invalid operator in CharProc"), +PARAM(W_PDF_NESTEDTEXTBLOCK, "BT found inside a text block"), +PARAM(W_PDF_ETNOTEXTBLOCK, "ET found outside text block"), +PARAM(W_PDF_TEXTOPNOBT, "text operator outside text block"), +PARAM(W_PDF_DEGENERATETM, "degenerate text matrix"), +PARAM(W_PDF_BADICC_USE_ALT, "bad ICC colour profile, using alternate"), +PARAM(W_PDF_BADICC_USECOMPS, "bad ICC vs number components, using components"), +PARAM(W_PDF_BADTRSWITCH, "bad value for text rendering mode"), +PARAM(W_PDF_BADSHADING, "error in shading"), +PARAM(W_PDF_BADPATTERN, "error in pattern"), +PARAM(W_PDF_NONSTANDARD_OP, "non standard operator found - ignoring"), +PARAM(W_PDF_NUM_EXPONENT, "number uses illegal exponent form"), +PARAM(W_PDF_STREAM_HAS_CONTENTS, "Stream has inappropriate /Contents entry"), +PARAM(W_PDF_STREAM_BAD_DECODEPARMS, "bad DecodeParms"), +PARAM(W_PDF_MASK_ERROR, "error in Mask"), +PARAM(W_PDF_ANNOT_AP_ERROR, "error in annotation Appearance"), +PARAM(W_PDF_BAD_NAME_ESCAPE, "badly escaped name"), +PARAM(W_PDF_TYPECHECK, "typecheck error"), +PARAM(W_PDF_BAD_TRAILER, "bad trailer dictionary"), +PARAM(W_PDF_ANNOT_ERROR, "error in annotation"), +PARAM(W_PDF_BAD_ICC_PROFILE_LINK, "failed to create ICC profile link"), +PARAM(W_PDF_OVERFLOW_REAL, "overflowed a real reading a number, assuming 0"), +PARAM(W_PDF_INVALID_REAL, "failed to read a valid number, assuming 0"), +PARAM(W_PDF_DEVICEN_USES_ALL, "A DeviceN space used the /All ink name."), +PARAM(W_PDF_BAD_MEDIABOX, "Couldn't retrieve MediaBox for page, using current media size"), +PARAM(W_PDF_CA_OUTOFRANGE, "CA or ca value not in range 0.0 to 1.0, clamped to range."), +PARAM(W_PDF_INVALID_DEFAULTSPACE, "Invalid DefaultGray, DefaultRGB or DefaultCMYK space specified, ignored."), +PARAM(W_PDF_INVALID_DECRYPT_LEN, "Invalid /Length supplied in Encryption dictionary."), +PARAM(W_PDF_INVALID_FONT_BASEENC, "Ignoring invalid BaseEncoding name in font"), + +#undef PARAM diff --git a/pdf/pdf_xref.c b/pdf/pdf_xref.c index 27c1501e..7e611130 100644 --- a/pdf/pdf_xref.c +++ b/pdf/pdf_xref.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -175,8 +175,10 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd code = pdfi_dict_get_int(ctx, sdict, "Size", &size); if (code < 0) return code; + if (size < 1) + return 0; - if (size < 0) + if (size < 0 || size > floor((double)ARCH_MAX_SIZE_T / (double)sizeof(xref_entry))) return_error(gs_error_rangecheck); /* If this is the first xref stream then allocate the xref table and store the trailer */ @@ -197,7 +199,7 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd ctx->xref_table->type = PDF_XREF_TABLE; ctx->xref_table->xref_size = size; #if REFCNT_DEBUG - ctx->xref_table->UID = ctx->UID++; + ctx->xref_table->UID = ctx->ref_UID++; dmprintf1(ctx->memory, "Allocated xref table with UID %"PRIi64"\n", ctx->xref_table->UID); #endif pdfi_countup(ctx->xref_table); @@ -205,6 +207,9 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd ctx->Trailer = sdict; pdfi_countup(sdict); } else { + if (size > ctx->xref_table->xref_size) + return_error(gs_error_rangecheck); + code = pdfi_merge_dicts(ctx, ctx->Trailer, sdict); if (code < 0) { if (code == gs_error_VMerror || ctx->args.pdfstoponerror) @@ -321,7 +326,7 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd for (i=0;i < pdfi_array_size(a);i+=2){ code = pdfi_array_get_int(ctx, a, (uint64_t)i, &start); - if (code < 0) { + if (code < 0 || start < 0) { pdfi_countdown(a); pdfi_close_file(ctx, XRefStrm); pdfi_countdown(ctx->xref_table); @@ -332,7 +337,6 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd code = pdfi_array_get_int(ctx, a, (uint64_t)i+1, &end); if (code < 0) { pdfi_countdown(a); - pdfi_countdown(start); pdfi_close_file(ctx, XRefStrm); pdfi_countdown(ctx->xref_table); ctx->xref_table = NULL; @@ -390,6 +394,8 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); if (code < 0) return code; + if (code == 0) + return_error(gs_error_syntaxerror); if (((pdf_obj *)ctx->stack_top[-1])->type == PDF_KEYWORD && ((pdf_keyword *)ctx->stack_top[-1])->key == TOKEN_XREF) { /* Read old-style xref table */ @@ -412,7 +418,7 @@ static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s) /* Its an integer, lets try for index gen obj as a XRef stream */ code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); - if (code < 0) + if (code <= 0) return(pdfi_repair_file(ctx)); if (((pdf_obj *)ctx->stack_top[-1])->type != PDF_INT) { @@ -426,6 +432,10 @@ static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s) pdfi_pop(ctx, 1); return code; } + if (code == 0) { + pdfi_pop(ctx, 1); + return_error(gs_error_syntaxerror); + } if (((pdf_obj *)ctx->stack_top[-1])->type != PDF_KEYWORD) { /* Second element is not an integer, not a valid xref */ @@ -449,10 +459,10 @@ static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s) do { code = pdfi_read_token(ctx, ctx->main_stream, obj_num, gen_num); - if (code < 0) + if (code <= 0) return pdfi_repair_file(ctx); - if (((pdf_obj *)ctx->stack_top[-1])->type == PDF_KEYWORD) { + if (pdfi_count_stack(ctx) >= 2 && ((pdf_obj *)ctx->stack_top[-1])->type == PDF_KEYWORD) { keyword = (pdf_keyword *)ctx->stack_top[-1]; if (keyword->key == TOKEN_STREAM) { pdf_dict *dict; @@ -491,7 +501,7 @@ static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s) /* TODO: Not positive this will actually have a length -- just use 0 */ char extra_info[gp_file_name_sizeof]; - gs_sprintf(extra_info, "Xref Stream object %u missing mandatory keyword /Length\n", obj_num); + gs_snprintf(extra_info, sizeof(extra_info), "Xref Stream object %u missing mandatory keyword /Length\n", obj_num); pdfi_set_error(ctx, 0, NULL, E_PDF_BADSTREAM, "pdfi_read_xref_stream_dict", extra_info); code = 0; Length = 0; @@ -525,36 +535,49 @@ static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s) static int skip_to_digit(pdf_context *ctx, pdf_c_stream *s, unsigned int limit) { - byte c; - int bytes, read = 0; + int c, read = 0; do { - bytes = pdfi_read_bytes(ctx, &c, 1, 1, s); - if (bytes == 0) + c = pdfi_read_byte(ctx, s); + if (c < 0) return_error(gs_error_ioerror); - if (c >= 0x30 && c <= 0x39) { - pdfi_unread(ctx, s, &c, 1); - break; + if (c >= '0' && c <= '9') { + pdfi_unread_byte(ctx, s, (byte)c); + return read; } - read += bytes; - }while (read < limit); + read++; + } while (read < limit); + return read; } -static int read_digits(pdf_context *ctx, pdf_c_stream *s, byte *Buffer, unsigned int limit) +static int read_digits(pdf_context *ctx, pdf_c_stream *s, byte *Buffer, int limit) { - int bytes, read = 0; + int c, read = 0; + + /* Since the "limit" is a value calculated by the caller, + it's easier to check it in one place (here) than before + every call. + */ + if (limit <= 0) + return_error(gs_error_syntaxerror); + + /* We assume that Buffer always has limit+1 bytes available, so we can + * safely terminate it. */ do { - bytes = pdfi_read_bytes(ctx, &Buffer[read], 1, 1, s); - if (bytes == 0) + c = pdfi_read_byte(ctx, s); + if (c < 0) return_error(gs_error_ioerror); - if (Buffer[read] < 0x30 || Buffer[read] > 0x39) { - pdfi_unread(ctx, s, &Buffer[read], 1); + if (c < '0' || c > '9') { + pdfi_unread_byte(ctx, s, c); break; } - read += bytes; - }while (read < limit); + *Buffer++ = (byte)c; + read++; + } while (read < limit); + *Buffer = 0; + return read; } @@ -562,9 +585,8 @@ static int read_digits(pdf_context *ctx, pdf_c_stream *s, byte *Buffer, unsigned static int read_xref_entry_slow(pdf_context *ctx, pdf_c_stream *s, gs_offset_t *offset, uint32_t *generation_num, unsigned char *free) { byte Buffer[20]; - int code, read = 0, bytes; + int c, code, read = 0; - memset(Buffer, 0x00, 20); /* First off, find a number. If we don't find one, and read 20 bytes, throw an error */ code = skip_to_digit(ctx, s, 20); if (code < 0) @@ -575,7 +597,6 @@ static int read_xref_entry_slow(pdf_context *ctx, pdf_c_stream *s, gs_offset_t * code = read_digits(ctx, s, (byte *)&Buffer, (read > 10 ? 20 - read : 10)); if (code < 0) return code; - Buffer[code] = 0x00; read += code; *offset = atol((const char *)Buffer); @@ -587,23 +608,22 @@ static int read_xref_entry_slow(pdf_context *ctx, pdf_c_stream *s, gs_offset_t * read += code; /* and read it */ - code = read_digits(ctx, s, (byte *)&Buffer, (read > 15 ? 20 - read : 5)); + code = read_digits(ctx, s, (byte *)&Buffer, (read > 15 ? 20 - read : 5)); if (code < 0) return code; - Buffer[code] = 0x00; read += code; *generation_num = atol((const char *)Buffer); do { - bytes = pdfi_read_bytes(ctx, &Buffer[0], 1, 1, s); - if (bytes == 0) + c = pdfi_read_byte(ctx, s); + if (c < 0) return_error(gs_error_ioerror); - read += bytes; - if (Buffer[0] == 0x09 || Buffer[0] == 0x20) + read ++; + if (c == 0x09 || c == 0x20) continue; - if (Buffer[0] == 'n' || Buffer[0] == 'f') { - *free = Buffer[0]; + if (c == 'n' || c == 'f') { + *free = (unsigned char)c; break; } else { return_error(gs_error_syntaxerror); @@ -613,9 +633,11 @@ static int read_xref_entry_slow(pdf_context *ctx, pdf_c_stream *s, gs_offset_t * return_error(gs_error_syntaxerror); do { - bytes = pdfi_read_bytes(ctx, &Buffer[0], 1, 1, s); - read += bytes; - if (Buffer[0] == 0x20 || Buffer[0] == 0x09 || Buffer[0] == 0x0d || Buffer[0] == 0x0a) + c = pdfi_read_byte(ctx, s); + if (c < 0) + return_error(gs_error_syntaxerror); + read++; + if (c == 0x20 || c == 0x09 || c == 0x0d || c == 0x0a) continue; } while (read < 20); return 0; @@ -626,7 +648,7 @@ static int write_offset(byte *B, gs_offset_t o, unsigned int g, unsigned char fr byte b[20], *ptr = B; int index = 0; - gs_sprintf((char *)b, "%"PRId64"", o); + gs_snprintf((char *)b, sizeof(b), "%"PRIdOFFSET"", o); if (strlen((const char *)b) > 10) return_error(gs_error_rangecheck); for(index=0;index < 10 - strlen((const char *)b); index++) { @@ -636,7 +658,7 @@ static int write_offset(byte *B, gs_offset_t o, unsigned int g, unsigned char fr ptr += strlen((const char *)b); *ptr++ = 0x20; - gs_sprintf((char *)b, "%d", g); + gs_snprintf((char *)b, sizeof(b), "%d", g); if (strlen((const char *)b) > 5) return_error(gs_error_rangecheck); for(index=0;index < 5 - strlen((const char *)b);index++) { @@ -682,6 +704,11 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio return_error(gs_error_typecheck); } + if (((pdf_num *)o)->value.i < 0) { + pdfi_pop(ctx, 1); + return_error(gs_error_rangecheck); + } + *section_start = start = ((pdf_num *)o)->value.i; code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); @@ -689,6 +716,10 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio pdfi_pop(ctx, 1); return code; } + if (code == 0) { + pdfi_pop(ctx, 1); + return_error(gs_error_syntaxerror); + } o = ctx->stack_top[-1]; if (o->type != PDF_INT) { @@ -696,6 +727,14 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio pdfi_pop(ctx, 2); return_error(gs_error_typecheck); } + + /* Zero sized xref sections are valid; see the file attached to + * bug 704947 for an example. */ + if (((pdf_num *)o)->value.i < 0) { + pdfi_pop(ctx, 2); + return_error(gs_error_rangecheck); + } + *section_size = size = ((pdf_num *)o)->value.i; pdfi_pop(ctx, 2); @@ -716,7 +755,7 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio return_error(gs_error_VMerror); } #if REFCNT_DEBUG - ctx->xref_table->UID = ctx->UID++; + ctx->xref_table->UID = ctx->ref_UID++; dmprintf1(ctx->memory, "Allocated xref table with UID %"PRIi64"\n", ctx->xref_table->UID); #endif @@ -746,7 +785,7 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio return_error(gs_error_ioerror); j = 19; while (Buffer[j] != 0x0D && Buffer[j] != 0x0A) { - pdfi_unread(ctx, s, (byte *)&Buffer[j], 1); + pdfi_unread_byte(ctx, s, (byte)Buffer[j]); if (--j < 0) { dmprintf(ctx->memory, "Invalid xref entry, line terminator missing.\n"); code = read_xref_entry_slow(ctx, s, &off, &gen, &free); @@ -755,6 +794,7 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio code = write_offset((byte *)Buffer, off, gen, free); if (code < 0) return code; + j = 19; break; } } @@ -762,7 +802,7 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio if (entry->object_num != 0) continue; - if (sscanf(Buffer, "%ld %d %c", &entry->u.uncompressed.offset, &entry->u.uncompressed.generation_num, &free) != 3) { + if (sscanf(Buffer, "%"PRIdOFFSET" %d %c", &entry->u.uncompressed.offset, &entry->u.uncompressed.generation_num, &free) != 3) { dmprintf(ctx->memory, "Invalid xref entry, incorrect format.\n"); pdfi_unread(ctx, s, (byte *)Buffer, 20); code = read_xref_entry_slow(ctx, s, &off, &gen, &free); @@ -801,8 +841,8 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) if (code < 0) return code; - if (section_start + section_size > max_obj) - max_obj = section_start + section_size; + if (section_size > 0 && section_start + section_size - 1 > max_obj) + max_obj = section_start + section_size - 1; if (ctx->stack_top - o > 0) { k = (pdf_keyword *)ctx->stack_top[-1]; @@ -850,7 +890,7 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) pdfi_pop(ctx, 1); return code; } - if (size < 0) { + if (size < 0 || size > floor((double)ARCH_MAX_SIZE_T / (double)sizeof(xref_entry))) { pdfi_pop(ctx, 1); return_error(gs_error_rangecheck); } @@ -862,7 +902,7 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) } memset(ctx->xref_table, 0x00, sizeof(xref_table_t)); #if REFCNT_DEBUG - ctx->xref_table->UID = ctx->UID++; + ctx->xref_table->UID = ctx->ref_UID++; dmprintf1(ctx->memory, "Allocated xref table with UID %"PRIi64"\n", ctx->xref_table->UID); #endif @@ -931,6 +971,10 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) return code; } + /* This can happen if pdfi_read_xref_stream tries to repair a broken PDF file */ + if (d != ctx->Trailer) + d = ctx->Trailer; + pdfi_loop_detector_cleartomark(ctx); } @@ -976,6 +1020,8 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); if (code < 0) return(code); + if (code == 0) + return_error(gs_error_syntaxerror); if (((pdf_obj *)ctx->stack_top[-1])->type == PDF_KEYWORD && ((pdf_keyword *)ctx->stack_top[-1])->key == TOKEN_XREF) { /* Read old-style xref table */ @@ -1057,21 +1103,21 @@ int pdfi_read_xref(pdf_context *ctx) entry = &ctx->xref_table->xref[i]; if(entry->compressed) { dmprintf(ctx->memory, "*"); - gs_sprintf(Buffer, "%ld", entry->object_num); + gs_snprintf(Buffer, sizeof(Buffer), "%"PRId64"", entry->object_num); j = 10 - strlen(Buffer); while(j--) { dmprintf(ctx->memory, " "); } dmprintf1(ctx->memory, "%s ", Buffer); - gs_sprintf(Buffer, "%ld", entry->u.compressed.compressed_stream_num); + gs_snprintf(Buffer, sizeof(Buffer), "%ld", entry->u.compressed.compressed_stream_num); j = 10 - strlen(Buffer); while(j--) { dmprintf(ctx->memory, " "); } dmprintf1(ctx->memory, "%s ", Buffer); - gs_sprintf(Buffer, "%ld", entry->u.compressed.object_index); + gs_snprintf(Buffer, sizeof(Buffer), "%ld", entry->u.compressed.object_index); j = 10 - strlen(Buffer); while(j--) { dmprintf(ctx->memory, " "); @@ -1081,21 +1127,21 @@ int pdfi_read_xref(pdf_context *ctx) else { dmprintf(ctx->memory, " "); - gs_sprintf(Buffer, "%ld", entry->object_num); + gs_snprintf(Buffer, sizeof(Buffer), "%ld", entry->object_num); j = 10 - strlen(Buffer); while(j--) { dmprintf(ctx->memory, " "); } dmprintf1(ctx->memory, "%s ", Buffer); - gs_sprintf(Buffer, "%ld", entry->u.uncompressed.offset); + gs_snprintf(Buffer, sizeof(Buffer), "%"PRIdOFFSET"", entry->u.uncompressed.offset); j = 10 - strlen(Buffer); while(j--) { dmprintf(ctx->memory, " "); } dmprintf1(ctx->memory, "%s ", Buffer); - gs_sprintf(Buffer, "%ld", entry->u.uncompressed.generation_num); + gs_snprintf(Buffer, sizeof(Buffer), "%ld", entry->u.uncompressed.generation_num); j = 10 - strlen(Buffer); while(j--) { dmprintf(ctx->memory, " "); diff --git a/pdf/pdfromfs.mak b/pdf/pdfromfs.mak index bc01a590..527d43c2 100644 --- a/pdf/pdfromfs.mak +++ b/pdf/pdfromfs.mak @@ -20,4 +20,4 @@ PDF_FONT_ROMFS_ARGS= # The -C turns "compaction" on, -B off. For debugging convenience # it's off just now. # PDF_ROMFS_ARGS=-d Resource/ -P $(PSRESDIR)$(D) -C CMap$(D)* -PDF_ROMFS_ARGS=-d Resource/ -P $(PSRESDIR)$(D) -C CMap$(D)* -B Font$(D)* CIDFSubst$(D)* -C Init$(D)Fontmap.GS +PDF_ROMFS_ARGS=-d Resource/ -P $(PSRESDIR)$(D) -C CMap$(D)* -B Font$(D)* CIDFSubst$(D)* -C Init$(D)Fontmap.GS Init$(D)cidfmap diff --git a/pdf/pdftop.c b/pdf/pdftop.c index 6dbe1817..5e147cdd 100644 --- a/pdf/pdftop.c +++ b/pdf/pdftop.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -62,7 +62,7 @@ pdf_detect_language(const char *s, int len) { if (len < 5) return 1; - return memcmp(s, "%!PDF", 2); + return memcmp(s, "%!PDF", 5); } static const pl_interp_characteristics_t * @@ -368,6 +368,7 @@ pdf_impl_report_errors(pl_interp_implementation_t *impl, return 0; } +#if 0 /* * Get the allocator with which to allocate a device */ @@ -379,6 +380,7 @@ pdf_impl_get_device_memory(pl_interp_implementation_t *impl) return ctx->memory; } +#endif static int plist_value_get_int64(gs_param_typed_value *pvalue, int64_t *pint) { @@ -469,31 +471,6 @@ static int plist_value_get_bool(gs_param_typed_value *pvalue, bool *pbool) return_error(gs_error_typecheck); } -/* Get the Overprint value and translate it to a numeric value */ -static int -pdfi_get_overprint_param(pdf_context *ctx, gs_param_typed_value *pvalue) -{ - char *val = NULL; - int len; - int code; - - code = plist_value_get_string_or_name(ctx, pvalue, &val, &len); - if (code < 0) - return code; - - ctx->args.overprint_control = PDF_OVERPRINT_ENABLE; - if (val != NULL && !strncmp(val, "disable", len)) { - ctx->args.overprint_control = PDF_OVERPRINT_DISABLE; - } - if (val != NULL && !strncmp(val, "simulate", len)) { - ctx->args.overprint_control = PDF_OVERPRINT_SIMULATE; - } - - if (val) - gs_free_object(ctx->memory, val, "Transparency param"); - return code; -} - static int pdf_impl_set_param(pl_interp_implementation_t *impl, gs_param_list *plist) @@ -567,7 +544,7 @@ pdf_impl_set_param(pl_interp_implementation_t *impl, if (code < 0) return code; } - if (!strncmp(param, "NOCIDFALLBACK", 13)) { + if (!strncmp(param, "PDFNOCIDFALLBACK", 13)) { code = plist_value_get_bool(&pvalue, &ctx->args.nocidfallback); if (code < 0) return code; @@ -642,6 +619,11 @@ pdf_impl_set_param(pl_interp_implementation_t *impl, if (code < 0) return code; } + if (!strncmp(param, "PreserveMarkedContent", 21)) { + code = plist_value_get_bool(&pvalue, &ctx->args.preservemarkedcontent); + if (code < 0) + return code; + } if (!strncmp(param, "NoUserUnit", 10)) { code = plist_value_get_bool(&pvalue, &ctx->args.nouserunit); if (code < 0) @@ -667,11 +649,6 @@ pdf_impl_set_param(pl_interp_implementation_t *impl, if (code < 0) return code; } - if (!strncmp(param, "Overprint", 9)) { - code = pdfi_get_overprint_param(ctx, &pvalue); - if (code < 0) - return code; - } if (!strncmp(param, "UsePDFX3Profile", strlen("UsePDFX3Profile"))) { /* This is a weird one because it can be either a bool or an int. * If it's a bool=true, then it defaults to PDFX3Profile_num = 0 @@ -705,6 +682,31 @@ pdf_impl_set_param(pl_interp_implementation_t *impl, if (code < 0) return code; code = pdfi_add_paths_to_search_paths(ctx, (const char *)s, slen, true); + gs_free_object(ctx->memory, s, "FONTPATH param string"); + } + if (!strncmp(param, "FONTMAP", 7)) { + char *s = NULL; + int slen; + code = plist_value_get_string_or_name(ctx, &pvalue, &s, &slen); + if (code < 0) + return code; + code = pdfi_add_fontmapfiles(ctx, (const char *)s, slen); + gs_free_object(ctx->memory, s, "FONTMAP param string"); + } + if (!strncmp(param, "CIDSubstPath", 12)) { + code = plist_value_get_string_or_name(ctx, &pvalue, (char **)&ctx->args.cidsubstpath.data, (int *)&ctx->args.cidsubstpath.size); + if (code < 0) + return code; + } + if (!strncmp(param, "CIDSubstFont", 12)) { + code = plist_value_get_string_or_name(ctx, &pvalue, (char **)&ctx->args.cidsubstfont.data, (int *)&ctx->args.cidsubstfont.size); + if (code < 0) + return code; + } + if (!strncmp(param, "IgnoreToUnicode", 15)) { + code = plist_value_get_bool(&pvalue, &ctx->args.ignoretounicode); + if (code < 0) + return code; } } @@ -776,7 +778,7 @@ pl_interp_implementation_t pdf_implementation = { pdf_impl_characteristics, pdf_impl_allocate_interp_instance, - pdf_impl_get_device_memory, + NULL, /* pdf_impl_get_device_memory, */ pdf_impl_set_param, pdf_impl_add_path, pdf_impl_post_args_init, @@ -843,6 +845,7 @@ pdfi_install_halftone(pdf_context *ctx, gx_device *pdevice) if (gx_device_must_halftone(pdevice)) { + memset(&ht.rc, 0x00, sizeof(ht.rc)); ht.type = ht_type_threshold; ht.objtype = HT_OBJTYPE_DEFAULT; ht.params.threshold.width = width; |