diff options
author | 2022-09-21 14:18:08 +0100 | |
---|---|---|
committer | 2022-10-02 04:31:25 +0100 | |
commit | a529111f77ff46f4836fe7312e70953bc16587cf (patch) | |
tree | 9dc3924cb1a6ef3ef853b7bb45f735365e0b4e6d /pdf | |
parent | Import Ghostscript 9.56.1 (diff) | |
download | ghostscript-gpl-patches-ghostscript-10.tar.gz ghostscript-gpl-patches-ghostscript-10.tar.bz2 ghostscript-gpl-patches-ghostscript-10.zip |
Import Ghostscript 10.0ghostscript-10.0ghostscript-10
Signed-off-by: Sam James <sam@gentoo.org>
Diffstat (limited to 'pdf')
60 files changed, 6447 insertions, 3594 deletions
diff --git a/pdf/ghostpdf.c b/pdf/ghostpdf.c index 8d88b090..2dd4c63c 100644 --- a/pdf/ghostpdf.c +++ b/pdf/ghostpdf.c @@ -79,10 +79,15 @@ static int pdfi_output_metadata(pdf_context *ctx) { int code = 0; + if (ctx->filename != NULL) + dmprintf2(ctx->memory, "\n %s has %"PRIi64" ", ctx->filename, ctx->num_pages); + else + dmprintf1(ctx->memory, "\n File has %"PRIi64" ", ctx->num_pages); + if (ctx->num_pages > 1) - dmprintf2(ctx->memory, "\n %s has %"PRIi64" pages\n\n", ctx->filename, ctx->num_pages); + dmprintf(ctx->memory, "pages\n\n"); else - dmprintf2(ctx->memory, "\n %s has %"PRIi64" page.\n\n", ctx->filename, ctx->num_pages); + dmprintf(ctx->memory, "page.\n\n"); if (ctx->Info != NULL) { pdf_name *n = NULL; @@ -151,6 +156,7 @@ static int pdfi_output_metadata(pdf_context *ctx) pdfi_countdown(n); n = NULL; } + dmprintf(ctx->memory, "\n"); return code; } @@ -177,7 +183,7 @@ static int pdfi_dump_box(pdf_context *ctx, pdf_dict *page_dict, const char *Key) if (i != 0) dmprintf(ctx->memory, " "); if (code == 0) { - if (a->values[i]->type == PDF_INT) + if (pdfi_type_of(a->values[i]) == PDF_INT) dmprintf1(ctx->memory, "%"PRIi64"", ((pdf_num *)a->values[i])->value.i); else dmprintf1(ctx->memory, "%f", ((pdf_num *)a->values[i])->value.d); @@ -192,6 +198,90 @@ static int pdfi_dump_box(pdf_context *ctx, pdf_dict *page_dict, const char *Key) return code; } +static int dump_font(pdf_context *ctx, pdf_dict *font_dict, bool space_name) +{ + pdf_obj *obj = NULL; + char *str = NULL; + int len = 0, code = 0, i; + bool known = false, type0 = false; + + code = pdfi_dict_get_type(ctx, font_dict, "BaseFont", PDF_NAME, &obj); + if (code >= 0) { + code = pdfi_string_from_name(ctx, (pdf_name *)obj, &str, &len); + if (code >= 0) { + dmprintf1(ctx->memory, "%s", str); + if (len < 32 && space_name) { + for (i = 0; i < 32 - len;i++) + dmprintf(ctx->memory, " "); + } else + dmprintf(ctx->memory, " "); + (void)pdfi_free_string_from_name(ctx, str); + } + pdfi_countdown(obj); + obj = NULL; + } + + code = pdfi_dict_get_type(ctx, font_dict, "Subtype", PDF_NAME, &obj); + if (code >= 0) { + code = pdfi_string_from_name(ctx, (pdf_name *)obj, &str, &len); + if (code >= 0) { + dmprintf1(ctx->memory, "%s", str); + for (i = 0; i < 16 - len;i++) + dmprintf(ctx->memory, " "); + (void)pdfi_free_string_from_name(ctx, str); + } + if (pdfi_name_is((pdf_name *)obj, "Type0")) + type0 = true; + pdfi_countdown(obj); + obj = NULL; + } + + if (!type0) { + code = pdfi_dict_get_type(ctx, font_dict, "Embedded", PDF_BOOL, &obj); + if (code >= 0) { + if (obj == PDF_FALSE_OBJ) + dmprintf(ctx->memory, "Not embedded "); + else + dmprintf(ctx->memory, "Embedded "); + pdfi_countdown(obj); + obj = NULL; + } + else + dmprintf(ctx->memory, "Not embedded "); + } else + dmprintf(ctx->memory, " "); + + code = pdfi_dict_get_type(ctx, font_dict, "ToUnicode", PDF_BOOL, &obj); + if (code >= 0) { + if (obj == PDF_TRUE_OBJ) + dmprintf(ctx->memory, "Has ToUnicode "); + else + dmprintf(ctx->memory, "No ToUnicode "); + pdfi_countdown(obj); + obj = NULL; + } + else + dmprintf(ctx->memory, "No ToUnicode "); + + code = pdfi_dict_known(ctx, font_dict, "Descendants", &known); + if (code >= 0 && known) { + code = pdfi_dict_get_type(ctx, font_dict, "Descendants", PDF_ARRAY, &obj); + if (code >= 0) { + pdf_obj *desc = NULL; + + code = pdfi_array_get_type(ctx, (pdf_array *)obj, 0, PDF_DICT, &desc); + if (code >= 0) { + dmprintf(ctx->memory, "\n Descendants: ["); + (void)dump_font(ctx, (pdf_dict *)desc, false); + dmprintf(ctx->memory, "]"); + } + pdfi_countdown(obj); + obj = NULL; + } + } + return 0; +} + /* * This routine along with pdfi_output_metadtaa above, dumps certain kinds * of metadata from the PDF file, and from each page in the PDF file. It is @@ -202,12 +292,13 @@ static int pdfi_dump_box(pdf_context *ctx, pdf_dict *page_dict, const char *Key) * we always emit them, and the switches -dDumpFontsNeeded, -dDumpXML, * -dDumpFontsUsed and -dShowEmbeddedFonts are not implemented at all yet. */ -static int pdfi_output_page_info(pdf_context *ctx, uint64_t page_num) +int pdfi_output_page_info(pdf_context *ctx, uint64_t page_num) { int code; bool known = false; double f; pdf_dict *page_dict = NULL; + pdf_array *fonts_array = NULL, *spots_array = NULL; code = pdfi_page_get_dict(ctx, page_num, &page_dict); if (code < 0) @@ -271,7 +362,7 @@ static int pdfi_output_page_info(pdf_context *ctx, uint64_t page_num) return code; } - code = pdfi_check_page(ctx, page_dict, false); + code = pdfi_check_page(ctx, page_dict, &fonts_array, &spots_array, false); if (code < 0) { if (ctx->args.pdfstoponerror) return code; @@ -283,16 +374,62 @@ static int pdfi_output_page_info(pdf_context *ctx, uint64_t page_num) code = pdfi_dict_known(ctx, page_dict, "Annots", &known); if (code < 0) { if (code != gs_error_undefined && ctx->args.pdfstoponerror) - return code; + goto error; } else { if (known == true) dmprintf(ctx->memory, " Page contains Annotations"); + code = 0; + } + + if (spots_array != NULL) { + uint64_t index = 0; + pdf_name *spot = NULL; + char *str = NULL; + int len; + + dmprintf(ctx->memory, "\n Page Spot colors: \n"); + for (index = 0;index < pdfi_array_size(spots_array);index++) { + code = pdfi_array_get(ctx, spots_array, index, (pdf_obj **)&spot); + if (code >= 0) { + if (pdfi_type_of(spot) == PDF_NAME) { + code = pdfi_string_from_name(ctx, spot, &str, &len); + if (code >= 0) { + dmprintf1(ctx->memory, " '%s'\n", str); + (void)pdfi_free_string_from_name(ctx, str); + } + } + pdfi_countdown(spot); + spot = NULL; + } + } + code = 0; } + if (fonts_array != NULL && pdfi_array_size(fonts_array) != 0) { + uint64_t index = 0; + pdf_dict *font_dict = NULL; + + dmprintf(ctx->memory, "\n Fonts used: \n"); + for (index = 0;index < pdfi_array_size(fonts_array);index++) { + code = pdfi_array_get_type(ctx, fonts_array, index, PDF_DICT, (pdf_obj **)&font_dict); + if (code >= 0) { + dmprintf(ctx->memory, " "); + (void)dump_font(ctx, font_dict, true); + dmprintf(ctx->memory, "\n"); + pdfi_countdown(font_dict); + font_dict = NULL; + } + } + code = 0; + } + +error: + pdfi_countdown(fonts_array); + pdfi_countdown(spots_array); dmprintf(ctx->memory, "\n\n"); pdfi_countdown(page_dict); - return 0; + return code; } /* Error and warning string tables. There should be a string for each error and warning @@ -347,6 +484,8 @@ const char *gs_error_strings[] = { "unregistered", "invalidcontext", "invalidid", + "pdf_stackoverflow", + "circular reference" }; const char *gs_internal_error_strings[] = { @@ -358,16 +497,15 @@ const char *gs_internal_error_strings[] = { "exec stack underflow", "VMreclaim", "Need input", + "need file", "No defined error", "No defined error (2)", - "need file", "error info", "handled", - "circular reference" }; -#define LASTNORMALGSERROR gs_error_invalidid * -1 +#define LASTNORMALGSERROR gs_error_circular_reference * -1 #define FIRSTINTERNALERROR gs_error_hit_detected * -1 -#define LASTGSERROR gs_error_circular_reference * -1 +#define LASTGSERROR gs_error_handled * -1 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) { @@ -784,7 +922,7 @@ int pdfi_prep_collection(pdf_context *ctx, uint64_t *TotalFiles, char ***names_a if (code < 0) goto exit; - if (File->type == PDF_DICT) { + if (pdfi_type_of(File) == PDF_DICT) { if (pdfi_dict_knownget_type(ctx, (pdf_dict *)File, "EF", PDF_DICT, &EF)) { if (pdfi_dict_knownget_type(ctx, (pdf_dict *)EF, "F", PDF_STREAM, &F)) { pdf_dict *stream_dict = NULL; @@ -859,7 +997,7 @@ int pdfi_prep_collection(pdf_context *ctx, uint64_t *TotalFiles, char ***names_a /* Create an entry for the Description in the names array */ code = pdfi_array_get(ctx, FileNames, ix * 2, (pdf_obj **)&Name); if (code >= 0) { - if (Name->type == PDF_STRING) { + if (pdfi_type_of((pdf_obj *)Name) == PDF_STRING) { working_array[(index * 2) + 1] = (char *)gs_alloc_bytes(ctx->memory, Name->length + 3, "Collection file names array entry"); if (working_array[(index * 2) + 1] != NULL) { memset(working_array[(index * 2) + 1], 0x00, Name->length + 3); @@ -999,6 +1137,13 @@ int pdfi_process_pdf_file(pdf_context *ctx, char *filename) else code = pdfi_process(ctx); + /* Pdfmark_InitialPage is the offset for Page Dests in + * /Outlines and Link annotations. It is the count of pages + * processed so far. Update it by the number of pages in + * this file. + */ + ctx->Pdfmark_InitialPage += ctx->num_pages; + pdfi_close_pdf_file(ctx); return code; } @@ -1037,9 +1182,14 @@ static int pdfi_init_file(pdf_context *ctx) if (code < 0 && code != gs_error_undefined) goto exit; if (code == 0) { - code = pdfi_initialise_Decryption(ctx); - if (code < 0) - goto exit; + if (pdfi_type_of(o) == PDF_DICT) { + code = pdfi_initialise_Decryption(ctx); + if (code < 0) + goto exit; + } else { + if (pdfi_type_of(o) != PDF_NULL) + pdfi_set_error(ctx, code, NULL, E_PDF_BADENCRYPT, "pdfi_init_file", NULL); + } } } @@ -1144,6 +1294,7 @@ int pdfi_set_input_stream(pdf_context *ctx, stream *stm) } /* Determine file size */ + pdfi_seek(ctx, ctx->main_stream, 0, SEEK_SET); pdfi_seek(ctx, ctx->main_stream, 0, SEEK_END); ctx->main_stream_length = pdfi_tell(ctx->main_stream); Offset = BUF_SIZE; @@ -1268,8 +1419,15 @@ int pdfi_set_input_stream(pdf_context *ctx, stream *stm) */ if (last_lineend) { leftover = last_lineend - Buffer; - memmove(Buffer + bytes - leftover, last_lineend, leftover); - bytes -= leftover; + /* Ensure we don't try to copy more than half a buffer, because that will + * end up overrunning the buffer end. Since we are only doing this to + * ensure we don't drop a partial 'startxref' that's far more than enough. + */ + if (leftover < BUF_SIZE / 2) { + memmove(Buffer + bytes - leftover, last_lineend, leftover); + bytes -= leftover; + } else + leftover = 0; } else leftover = 0; } @@ -1757,6 +1915,15 @@ int pdfi_clear_context(pdf_context *ctx) dmprintf1(ctx->memory, "Normal object cache hit rate: %f\n", hit_rate); dmprintf1(ctx->memory, "Compressed object cache hit rate: %f\n", compressed_hit_rate); #endif + if (ctx->PathSegments != NULL) { + gs_free_object(ctx->memory, ctx->PathSegments, "pdfi_clear_context"); + ctx->PathSegments = NULL; + } + if (ctx->PathPts != NULL) { + gs_free_object(ctx->memory, ctx->PathPts, "pdfi_clear_context"); + ctx->PathPts = NULL; + } + if (ctx->args.PageList) { gs_free_object(ctx->memory, ctx->args.PageList, "pdfi_clear_context"); ctx->args.PageList = NULL; @@ -1786,14 +1953,19 @@ 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.cidfsubstpath.data != NULL) { + gs_free_object(ctx->memory, ctx->args.cidfsubstpath.data, "cidfsubstpath.data"); + ctx->args.cidfsubstpath.data = NULL; + } + + if (ctx->args.cidfsubstfont.data != NULL) { + gs_free_object(ctx->memory, ctx->args.cidfsubstfont.data, "cidfsubstfont.data"); + ctx->args.cidfsubstfont.data = NULL; } - if (ctx->args.cidsubstfont.data != NULL) { - gs_free_object(ctx->memory, ctx->args.cidsubstfont.data, "cidsubstpath.data"); - ctx->args.cidsubstfont.data = NULL; + if (ctx->args.defaultfont.data != NULL) { + gs_free_object(ctx->memory, ctx->args.defaultfont.data, "cidfsubstfont.data"); + ctx->args.defaultfont.data = NULL; } pdfi_free_cstring_array(ctx, &ctx->args.showannottypes); @@ -1923,6 +2095,8 @@ int pdfi_clear_context(pdf_context *ctx) ctx->pdffontmap = NULL; pdfi_countdown(ctx->pdfnativefontmap); ctx->pdfnativefontmap = NULL; + pdfi_countdown(ctx->pdf_substitute_fonts); + ctx->pdf_substitute_fonts = NULL; return 0; } diff --git a/pdf/ghostpdf.h b/pdf/ghostpdf.h index 49c91808..1c2ffe78 100644 --- a/pdf/ghostpdf.h +++ b/pdf/ghostpdf.h @@ -22,6 +22,12 @@ #define BUF_SIZE 2048 +/* Limit nesting of arrays and dictionaries. We don't want to allow this + * to be unbounded, because on exit we could end up exceeding the C execution stack + * if we get too deeply nested. + */ +#define MAX_NESTING_DEPTH 100 + #include "pdf_types.h" #if defined(MEMENTO) @@ -143,8 +149,11 @@ typedef struct cmd_args_s { bool QUIET; bool verbose_errors; bool verbose_warnings; - gs_string cidsubstpath; - gs_string cidsubstfont; + gs_string cidfsubstpath; + gs_string cidfsubstfont; + gs_string defaultfont; + bool defaultfont_is_name; + bool ignoretounicode; bool nonativefontmap; } cmd_args_t; @@ -321,6 +330,11 @@ typedef struct pdf_context_s /* Doing a high level form for pdfwrite (annotations) */ bool PreservePDFForm; + /* If processing multiple files, the number of pages to add to /Page Destinations + * when handling Outlines and Annotations. This is the number of pages in all + * files completely processed so far. + */ + int Pdfmark_InitialPage; /* Optional things from Root */ pdf_dict *OCProperties; @@ -339,6 +353,27 @@ typedef struct pdf_context_s gs_font_dir * font_dir; /* Obviously we need a graphics state */ gs_gstate *pgs; + + /* PDF really doesn't have a path in the graphics state. This is different to + * PostScript and has implications; changing the CTM partway through path + * construction affects path segments already accumulated. The path is + * unaffected by gsvae and grestore. Previously we've unwound any pending + * path and rerun it, this is causing problems so instead we'll do what + * Acrobat obviously does and build the path outside the graphics state + */ + /* We make allocations in chunks for the path to avoid lots of little + * allocations, but we need to know where the end of the current allocation + * is so that we can tell if we would overflow and increase it. + */ + char *PathSegments; + /* The current insertion point. */ + char *PathSegmentsCurrent; + /* The current limit of the block */ + char *PathSegmentsTop; + double *PathPts; + double *PathPtsCurrent; + double *PathPtsTop; + /* set up by pdf_impl_set_device, this is the 'high water mark' for * restoring back to when we close a PDF file. This ensures the device * is correctly set up for any subesquent file to be run. @@ -406,6 +441,12 @@ typedef struct pdf_context_s uint32_t loop_detection_entries; uint64_t *loop_detection; + /* A counter for nesting of arrays and dictionaries. We don't want to allow this + * to be unbounded, because on exit we could end up exceeding the C execution stack + * if we get too deeply nested. + */ + uint32_t object_nesting; + /* Used to set the 'parent' stream of a stream that gets created by dereferencing * We should not need this but badly fromed PDF files can use Resources defined in * an earlier (non-Page) stream object, and Acrobat handles this, so we need to. @@ -423,6 +464,7 @@ typedef struct pdf_context_s search_paths_t search_paths; pdf_dict *pdffontmap; pdf_dict *pdfnativefontmap; /* Explicit mappings take precedence, hence we need separate dictionaries */ + pdf_dict *pdf_substitute_fonts; pdf_dict *pdfcidfmap; /* These function pointers can be replaced by ones intended to replicate @@ -468,6 +510,7 @@ int pdfi_prep_collection(pdf_context *ctx, uint64_t *TotalFiles, char ***names_a int pdfi_close_pdf_file(pdf_context *ctx); 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); +int pdfi_output_page_info(pdf_context *ctx, uint64_t page_num); 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); diff --git a/pdf/pdf.mak b/pdf/pdf.mak index 7040a72e..118a0b7d 100644 --- a/pdf/pdf.mak +++ b/pdf/pdf.mak @@ -188,7 +188,7 @@ $(PDFOBJ)pdf_stack.$(OBJ): $(PDFSRC)pdf_stack.c $(PDFINCLUDES) $(PDF_MAK) $(MAKE $(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) + $(gscoord_h) $(gsutil_h) $(gscolor3_h) $(PDF_MAK) $(MAKEDIRS) $(gzpath_h) $(gspenum_h) $(PDFCCC) $(PDFSRC)pdf_gstate.c $(PDFO_)pdf_gstate.$(OBJ) $(PDFOBJ)pdf_colour.$(OBJ): $(PDFSRC)pdf_colour.c $(PDFINCLUDES) \ diff --git a/pdf/pdf_annot.c b/pdf/pdf_annot.c index 7b96be1c..83b79293 100644 --- a/pdf/pdf_annot.c +++ b/pdf/pdf_annot.c @@ -269,7 +269,7 @@ static int pdfi_annot_draw_AP(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP if (NormAP == NULL) return 0; - if (NormAP->type != PDF_STREAM) + if (pdfi_type_of(NormAP) != PDF_STREAM) return_error(gs_error_typecheck); code = pdfi_op_q(ctx); @@ -649,11 +649,55 @@ static int pdfi_form_get_inheritable(pdf_context *ctx, pdf_dict *field, const ch { int code = 0; pdf_dict *Parent = NULL; + bool known = false; /* Check this field */ code = pdfi_dict_knownget_type(ctx, field, Key, type, o); - if (code != 0) goto exit; + if (code != 0) goto exit1; + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + goto exit; + + /* Check for Parent. Do not store the dereferenced Parent back to the dictionary + * as this can cause circular references. + */ + code = pdfi_dict_known(ctx, field, "Parent", &known); + if (code >= 0 && known == true) + { + code = pdfi_dict_get_no_store_R(ctx, field, "Parent", (pdf_obj **)&Parent); + if (code < 0) + goto exit; + + if (pdfi_type_of(Parent) != PDF_DICT) { + if (pdfi_type_of(Parent) == PDF_INDIRECT) { + pdf_indirect_ref *o = (pdf_indirect_ref *)Parent; + + code = pdfi_dereference(ctx, o->ref_object_num, o->ref_generation_num, (pdf_obj **)&Parent); + pdfi_countdown(o); + goto exit; + } else { + code = gs_note_error(gs_error_typecheck); + goto exit; + } + } + if (Parent->object_num != 0) { + code = pdfi_loop_detector_add_object(ctx, Parent->object_num); + if (code < 0) + goto exit; + } + code = pdfi_form_get_inheritable(ctx, Parent, Key, type, o); + if (code <= 0) { + if (ctx->AcroForm) + code = pdfi_dict_knownget_type(ctx, ctx->AcroForm, Key, type, o); + } + } else { + /* No Parent, so check AcroForm, if any */ + if (ctx->AcroForm) + code = pdfi_dict_knownget_type(ctx, ctx->AcroForm, Key, type, o); + } + +#if 0 /* If not found, recursively check Parent, if any */ code = pdfi_dict_knownget_type(ctx, field, "Parent", PDF_DICT, (pdf_obj **)&Parent); if (code < 0) goto exit; @@ -665,8 +709,12 @@ static int pdfi_form_get_inheritable(pdf_context *ctx, pdf_dict *field, const ch if (ctx->AcroForm) code = pdfi_dict_knownget_type(ctx, ctx->AcroForm, Key, type, o); } +#endif - exit: +exit: + (void)pdfi_loop_detector_cleartomark(ctx); + +exit1: pdfi_countdown(Parent); return code; } @@ -1475,11 +1523,13 @@ static int pdfi_annot_draw_LE(pdf_context *ctx, pdf_dict *annot, double dx, dy; double angle; int code; + pdf_obj_type type; code = pdfi_dict_knownget(ctx, annot, "LE", (pdf_obj **)&LE); if (code <= 0) goto exit; - if (LE->type != PDF_ARRAY && LE->type != PDF_NAME) { + type = pdfi_type_of(LE); + if (type != PDF_ARRAY && type != PDF_NAME) { code = gs_note_error(gs_error_typecheck); goto exit; } @@ -1490,7 +1540,7 @@ static int pdfi_annot_draw_LE(pdf_context *ctx, pdf_dict *annot, if (code < 0) angle = 0; - if (LE->type == PDF_ARRAY) { + if (type == PDF_ARRAY) { code = pdfi_array_get_type(ctx, (pdf_array *)LE, 0, PDF_NAME, (pdf_obj **)&LE1); if (code < 0) goto exit; @@ -1554,34 +1604,35 @@ static int pdfi_annot_get_NormAP(pdf_context *ctx, pdf_dict *annot, pdf_obj **No /* Nothing found */ if (code == 0) goto exit; - if (baseAP->type == PDF_STREAM) { - /* Use baseAP for the AP */ - AP = (pdf_stream *)baseAP; - baseAP = NULL; - } else { - if (baseAP->type != PDF_DICT) { - code = gs_error_typecheck; - goto exit; - } - - code = pdfi_dict_knownget_type(ctx, annot, "AS", PDF_NAME, (pdf_obj **)&AS); - if (code < 0) goto exit; - if (code == 0) { - pdfi_set_warning(ctx, 0, NULL, W_PDF_ANNOT_AP_ERROR, "pdfi_annot_get_NormAP", "WARNING Annotation has non-stream AP but no AS. Don't know what to render"); - goto exit; - } + switch (pdfi_type_of(baseAP)) { + case PDF_STREAM: + /* Use baseAP for the AP */ + AP = (pdf_stream *)baseAP; + baseAP = NULL; + break; + case PDF_DICT: + code = pdfi_dict_knownget_type(ctx, annot, "AS", PDF_NAME, (pdf_obj **)&AS); + if (code < 0) goto exit; + if (code == 0) { + pdfi_set_warning(ctx, 0, NULL, W_PDF_ANNOT_AP_ERROR, "pdfi_annot_get_NormAP", "WARNING Annotation has non-stream AP but no AS. Don't know what to render"); + goto exit; + } - /* Lookup the AS in the NormAP and use that as the AP */ - code = pdfi_dict_get_by_key(ctx, (pdf_dict *)baseAP, AS, (pdf_obj **)&AP); - if (code < 0) { - /* Apparently this is not an error, just silently don't have an AP */ - code = 0; - goto exit; - } - if (AP->type != PDF_STREAM) { - code = gs_note_error(gs_error_typecheck); + /* Lookup the AS in the NormAP and use that as the AP */ + code = pdfi_dict_get_by_key(ctx, (pdf_dict *)baseAP, AS, (pdf_obj **)&AP); + if (code < 0) { + /* Apparently this is not an error, just silently don't have an AP */ + code = 0; + goto exit; + } + if (pdfi_type_of(AP) != PDF_STREAM) { + code = gs_note_error(gs_error_typecheck); + goto exit; + } + break; + default: + code = gs_error_typecheck; goto exit; - } } *NormAP = (pdf_obj *)AP; @@ -3630,9 +3681,9 @@ static int pdfi_annot_draw_Widget(pdf_context *ctx, pdf_dict *annot, pdf_obj *No { int code = 0; bool found_T = false; - bool found_TF = false; + bool found_FT = false, known = false; pdf_obj *T = NULL; - pdf_obj *TF = NULL; + pdf_obj *FT = NULL; pdf_dict *Parent = NULL; pdf_dict *currdict = NULL; @@ -3651,33 +3702,71 @@ static int pdfi_annot_draw_Widget(pdf_context *ctx, pdf_dict *annot, pdf_obj *No /* TODO: See top part of pdf_draw.ps/drawwidget * check for /FT and /T and stuff */ + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + goto exit; + currdict = annot; pdfi_countup(currdict); while (true) { + if (currdict->object_num != 0) { + code = pdfi_loop_detector_add_object(ctx, currdict->object_num); + if (code < 0) + break; + } + code = pdfi_dict_knownget(ctx, currdict, "T", &T); if (code < 0) goto exit; if (code > 0) { found_T = true; - break; + pdfi_countdown(T); + T = NULL; + if (found_FT) + break; } - code = pdfi_dict_knownget(ctx, currdict, "TF", &TF); + code = pdfi_dict_knownget(ctx, currdict, "FT", &FT); if (code < 0) goto exit; if (code > 0) { - found_TF = true; - break; + found_FT = true; + pdfi_countdown(FT); + FT = NULL; + if (found_T) + break; } - /* Check for Parent */ - code = pdfi_dict_knownget_type(ctx, currdict, "Parent", PDF_DICT, (pdf_obj **)&Parent); - if (code < 0) goto exit; - if (code == 0) + /* Check for Parent. Do not store the dereferenced Parent back to the dictionary + * as this can cause circular references. + */ + code = pdfi_dict_known(ctx, currdict, "Parent", &known); + if (code >= 0 && known == true) + { + code = pdfi_dict_get_no_store_R(ctx, currdict, "Parent", (pdf_obj **)&Parent); + if (code < 0) { + (void)pdfi_loop_detector_cleartomark(ctx); + goto exit; + } + if (pdfi_type_of(Parent) != PDF_DICT) { + if (pdfi_type_of(Parent) == PDF_INDIRECT) { + pdf_indirect_ref *o = (pdf_indirect_ref *)Parent; + + code = pdfi_dereference(ctx, o->ref_object_num, o->ref_generation_num, (pdf_obj **)&Parent); + pdfi_countdown(o); + if (code < 0) + break; + } else { + break; + } + } + pdfi_countdown(currdict); + currdict = Parent; + Parent = NULL; + } else break; - pdfi_countdown(currdict); - currdict = Parent; - pdfi_countup(currdict); } + (void)pdfi_loop_detector_cleartomark(ctx); + code = 0; - if (!found_T && !found_TF) { + if (!found_T || !found_FT) { *render_done = true; dmprintf(ctx->memory, "**** Warning: A Widget annotation dictionary lacks either the FT or T key.\n"); dmprintf(ctx->memory, " Acrobat ignores such annoataions, annotation will not be rendered.\n"); @@ -3696,8 +3785,6 @@ static int pdfi_annot_draw_Widget(pdf_context *ctx, pdf_dict *annot, pdf_obj *No *render_done = true; exit: - pdfi_countdown(T); - pdfi_countdown(TF); pdfi_countdown(Parent); pdfi_countdown(currdict); return code; @@ -3950,7 +4037,7 @@ static int pdfi_annot_preserve_modQP(pdf_context *ctx, pdf_dict *annot, pdf_name code = pdfi_dict_get(ctx, annot, "QuadPoints", (pdf_obj **)&QP); if (code < 0) goto exit; - if (QP->type != PDF_ARRAY) { + if (pdfi_type_of(QP) != PDF_ARRAY) { /* Invalid QuadPoints, just delete it because I dunno what to do... * TODO: Should flag a warning here */ @@ -4024,7 +4111,7 @@ static int pdfi_annot_preserve_modAP(pdf_context *ctx, pdf_dict *annot, pdf_name code = pdfi_dict_get(ctx, annot, "AP", (pdf_obj **)&AP); if (code < 0) goto exit; - if (AP->type != PDF_DICT) { + if (pdfi_type_of(AP) != PDF_DICT) { /* This is an invalid AP, we will flag and delete it below */ found_ap = false; goto exit; @@ -4037,14 +4124,14 @@ static int pdfi_annot_preserve_modAP(pdf_context *ctx, pdf_dict *annot, pdf_name if (code < 0) goto exit; /* Handle indirect object */ - if (Value->type != PDF_INDIRECT) + if (pdfi_type_of(Value) != PDF_INDIRECT) goto loop_continue; /* Dereference it */ code = pdfi_dereference(ctx, Value->ref_object_num, Value->ref_generation_num, &object); if (code < 0) goto exit; - if (object->type == PDF_STREAM) { + if (pdfi_type_of(object) == PDF_STREAM) { /* Get a form label */ code = pdfi_annot_preserve_nextformlabel(ctx, &labeldata, &labellen); if (code < 0) goto exit; diff --git a/pdf/pdf_array.c b/pdf/pdf_array.c index 5f269fe5..0cdb3365 100644 --- a/pdf/pdf_array.c +++ b/pdf/pdf_array.c @@ -20,6 +20,7 @@ #include "pdf_stack.h" #include "pdf_deref.h" #include "pdf_array.h" +#include "pdf_loop_detect.h" /* NOTE: I think this should take a pdf_context param, but it's not available where it's * called, would require some surgery. @@ -40,7 +41,6 @@ void pdfi_free_array(pdf_obj *o) int pdfi_array_alloc(pdf_context *ctx, uint64_t size, pdf_array **a) { int code, i; - pdf_obj *n = NULL; *a = NULL; code = pdfi_object_alloc(ctx, PDF_ARRAY, size, (pdf_obj **)a); @@ -50,22 +50,14 @@ int pdfi_array_alloc(pdf_context *ctx, uint64_t size, pdf_array **a) (*a)->size = size; if (size > 0) { - /* Make a null object */ - code = pdfi_object_alloc(ctx, PDF_NULL, 1, &n); - if (code < 0) { - pdfi_free_object((pdf_obj *)(*a)); - *a = NULL; - return code; - } - /* And start all the array entries pointing at that null object. + /* Start all the array entries pointing to null. * array_put will replace tehm. This ensures we always have a valid * object for every entry. pdfi_array_from_stack() doesn't do this * initialisation because we know how many obejcts there are in the array * and we have valid objects for each entry on the stack already created. */ for (i=0;i<size;i++){ - (*a)->values[i] = n; - pdfi_countup(n); + (*a)->values[i] = PDF_NULL_OBJ; } } return 0; @@ -118,6 +110,48 @@ int pdfi_array_from_stack(pdf_context *ctx, uint32_t indirect_num, uint32_t indi return code; } +int pdfi_array_fetch_recursing(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o, bool setref, bool cache) +{ + int code; + pdf_obj *obj; + + *o = NULL; + + if (pdfi_type_of(a) != PDF_ARRAY) + return_error(gs_error_typecheck); + + if (index >= a->size) + return_error(gs_error_rangecheck); + obj = a->values[index]; + + if (pdfi_type_of(obj) == PDF_INDIRECT) { + pdf_obj *o1 = NULL; + pdf_indirect_ref *r = (pdf_indirect_ref *)obj; + + 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; + + if (setref) + (void)pdfi_array_put(ctx, a, index, o1); + obj = o1; + } else { + if (ctx->loop_detection != NULL && (uintptr_t)obj > TOKEN__LAST_KEY && obj->object_num != 0) + if (pdfi_loop_detector_check_object(ctx, obj->object_num)) + return gs_note_error(gs_error_circular_reference); + pdfi_countup(obj); + } + + *o = obj; + return 0; +} + /* Fetch object from array, resolving indirect reference if needed * setref -- indicates whether to replace indirect ref with the object */ @@ -128,14 +162,14 @@ int pdfi_array_fetch(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o *o = NULL; - if (a->type != PDF_ARRAY) + if (pdfi_type_of(a) != PDF_ARRAY) return_error(gs_error_typecheck); if (index >= a->size) return_error(gs_error_rangecheck); obj = a->values[index]; - if (obj->type == PDF_INDIRECT) { + if (pdfi_type_of(obj) == PDF_INDIRECT) { pdf_obj *o1 = NULL; pdf_indirect_ref *r = (pdf_indirect_ref *)obj; @@ -166,7 +200,7 @@ int pdfi_array_fetch(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o */ int pdfi_array_get_no_deref(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o) { - if (a->type != PDF_ARRAY) + if (pdfi_type_of(a) != PDF_ARRAY) return_error(gs_error_typecheck); if (index >= a->size) @@ -201,7 +235,7 @@ int pdfi_array_get_type(pdf_context *ctx, pdf_array *a, uint64_t index, if (code < 0) return code; - if ((*o)->type != type) { + if (pdfi_type_of(*o) != type) { pdfi_countdown(*o); *o = NULL; return_error(gs_error_typecheck); @@ -212,34 +246,26 @@ int pdfi_array_get_type(pdf_context *ctx, pdf_array *a, uint64_t index, int pdfi_array_get_int(pdf_context *ctx, pdf_array *a, uint64_t index, int64_t *i) { int code; - pdf_num *n; + pdf_obj *n; - code = pdfi_array_get_type(ctx, a, index, PDF_INT, (pdf_obj **)&n); + code = pdfi_array_get(ctx, a, index, &n); if (code < 0) return code; - *i = n->value.i; + code = pdfi_obj_to_int(ctx, n, i); pdfi_countdown(n); - return 0; + return code; } -int pdfi_array_get_number(pdf_context *ctx, pdf_array *a, uint64_t index, double *f) +int pdfi_array_get_number(pdf_context *ctx, pdf_array *a, uint64_t index, double *d) { int code; - pdf_num *n; + pdf_obj *n; - code = pdfi_array_get(ctx, a, index, (pdf_obj **)&n); + code = pdfi_array_get(ctx, a, index, &n); if (code < 0) return code; - if (n->type == PDF_INT) - *f = (double)n->value.i; - else { - if (n->type == PDF_REAL) - *f = n->value.d; - else { - code = gs_note_error(gs_error_typecheck); - } - } + code = pdfi_obj_to_real(ctx, n, d); pdfi_countdown(n); return code; @@ -253,7 +279,7 @@ bool pdfi_array_known(pdf_context *ctx, pdf_array *a, pdf_obj *o, int *index) { int i; - if (a->type != PDF_ARRAY) + if (pdfi_type_of(a) != PDF_ARRAY) return_error(gs_error_typecheck); for (i=0; i < a->size; i++) { @@ -263,7 +289,7 @@ bool pdfi_array_known(pdf_context *ctx, pdf_array *a, pdf_obj *o, int *index) code = pdfi_array_fetch(ctx, a, i, &val, true, true); if (code < 0) continue; - if (val->object_num == o->object_num) { + if (pdf_object_num(val) == pdf_object_num(o)) { if (index != NULL) *index = i; pdfi_countdown(val); return true; @@ -275,7 +301,7 @@ bool pdfi_array_known(pdf_context *ctx, pdf_array *a, pdf_obj *o, int *index) int pdfi_array_put(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj *o) { - if (a->type != PDF_ARRAY) + if (pdfi_type_of(a) != PDF_ARRAY) return_error(gs_error_typecheck); if (index >= a->size) @@ -292,7 +318,7 @@ int pdfi_array_put_int(pdf_context *ctx, pdf_array *a, uint64_t index, int64_t v int code; pdf_num *obj; - if (a->type != PDF_ARRAY) + if (pdfi_type_of(a) != PDF_ARRAY) return_error(gs_error_typecheck); code = pdfi_object_alloc(ctx, PDF_INT, 0, (pdf_obj **)&obj); @@ -308,7 +334,7 @@ int pdfi_array_put_real(pdf_context *ctx, pdf_array *a, uint64_t index, double v int code; pdf_num *obj; - if (a->type != PDF_ARRAY) + if (pdfi_type_of(a) != PDF_ARRAY) return_error(gs_error_typecheck); code = pdfi_object_alloc(ctx, PDF_REAL, 0, (pdf_obj **)&obj); @@ -358,7 +384,7 @@ int pdfi_array_to_gs_rect(pdf_context *ctx, pdf_array *array, gs_rect *rect) rect->q.y = 1.0; /* Identity matrix if no array */ - if (array == NULL || array->type != PDF_ARRAY) { + if (array == NULL || pdfi_type_of(array) != PDF_ARRAY) { return 0; } if (pdfi_array_size(array) != 4) { @@ -451,7 +477,7 @@ int pdfi_array_to_gs_matrix(pdf_context *ctx, pdf_array *array, gs_matrix *mat) mat->ty = 0.0; /* Identity matrix if no array */ - if (array == NULL || array->type != PDF_ARRAY) { + if (array == NULL || pdfi_type_of(array) != PDF_ARRAY) { return 0; } if (pdfi_array_size(array) != 6) { diff --git a/pdf/pdf_array.h b/pdf/pdf_array.h index 37097aaa..a4eb7d35 100644 --- a/pdf/pdf_array.h +++ b/pdf/pdf_array.h @@ -55,4 +55,6 @@ int pdfi_array_to_gs_matrix(pdf_context *ctx, pdf_array *array, gs_matrix *matri int pdfi_array_to_num_array(pdf_context *ctx, pdf_array *array, double *out, int start, int size); void pdfi_bbox_transform(pdf_context *ctx, gs_rect *bbox, gs_matrix *matrix); +int pdfi_array_fetch_recursing(pdf_context *ctx, pdf_array *a, uint64_t index, pdf_obj **o, bool setref, bool cache); + #endif diff --git a/pdf/pdf_check.c b/pdf/pdf_check.c index 00933bdf..19a6d957 100644 --- a/pdf/pdf_check.c +++ b/pdf/pdf_check.c @@ -66,7 +66,7 @@ * stores all the objects (which we don't want to do because its wasteful) and checking * to see if its already tested a given resource for spots/transparency. * This is a temporary allocation, big enough to hold all the objects in the file (1 per bit) - * each time we have fully checked a resource we add it here, when checking a resoruce we + * each time we have fully checked a resource we add it here, when checking a resource we * first check this list to see if its already been checked, in which case we can skip * it. When done we release the memory. */ @@ -74,6 +74,7 @@ typedef struct { bool transparent; bool has_overprint; /* Does it have OP or op in an ExtGState? */ pdf_dict *spot_dict; + pdf_array *font_array; uint32_t size; byte *CheckedResources; } pdfi_check_tracker_t; @@ -84,6 +85,7 @@ static inline bool resource_is_checked(pdfi_check_tracker_t *tracker, pdf_obj *o { uint32_t byte_offset; byte bit_offset; + int object_num; if(tracker->CheckedResources == NULL) return 0; @@ -91,14 +93,15 @@ static inline bool resource_is_checked(pdfi_check_tracker_t *tracker, pdf_obj *o /* objects with object number 0 are directly defined, we can't * store those so just return immediately */ - if (o->object_num > 0 && (o->object_num >> 3) < tracker->size) { + object_num = pdf_object_num(o); + if (object_num > 0 && (object_num >> 3) < tracker->size) { /* CheckedResources is a byte array, each byte represents * 8 objects. So the object number / 8 is the byte offset * into the array, and then object number % 8 is the bit * within that byte that we want. */ - bit_offset = 0x01 << (o->object_num % 8); - byte_offset = o->object_num >> 3; + bit_offset = 0x01 << (object_num % 8); + byte_offset = object_num >> 3; /* If its already set, then return that. */ if (tracker->CheckedResources[byte_offset] & bit_offset) @@ -116,12 +119,13 @@ pdfi_check_free_tracker(pdf_context *ctx, pdfi_check_tracker_t *tracker) { gs_free_object(ctx->memory, tracker->CheckedResources, "pdfi_check_free_tracker(flags)"); pdfi_countdown(tracker->spot_dict); + pdfi_countdown(tracker->font_array); memset(tracker, 0, sizeof(*tracker)); return 0; } static int -pdfi_check_init_tracker(pdf_context *ctx, pdfi_check_tracker_t *tracker) +pdfi_check_init_tracker(pdf_context *ctx, pdfi_check_tracker_t *tracker, pdf_array **fonts_array, pdf_array **spot_array) { int code = 0; @@ -136,16 +140,25 @@ 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->pgs->device->icc_struct->overprint_control) == gs_overprint_control_simulate) { + (ctx->pgs->device->icc_struct->overprint_control) == gs_overprint_control_simulate || + spot_array != NULL) + { code = pdfi_dict_alloc(ctx, 32, &tracker->spot_dict); if (code < 0) goto cleanup; pdfi_countup(tracker->spot_dict); } + if (fonts_array != NULL) { + code = pdfi_array_alloc(ctx, 0, &tracker->font_array); + if (code < 0) + goto cleanup; + pdfi_countup(tracker->font_array); + } + return 0; - cleanup: +cleanup: pdfi_check_free_tracker(ctx, tracker); return code; } @@ -164,6 +177,9 @@ static int pdfi_check_ColorSpace_dict(pdf_context *ctx, pdf_dict *cspace_dict, if (resource_is_checked(tracker, (pdf_obj *)cspace_dict)) return 0; + if (pdfi_type_of(cspace_dict) != PDF_DICT) + return_error(gs_error_typecheck); + if (pdfi_dict_entries(cspace_dict) > 0) { code = pdfi_loop_detector_mark(ctx); /* Mark the start of the ColorSpace dictionary loop */ if (code < 0) @@ -197,7 +213,7 @@ static int pdfi_check_ColorSpace_dict(pdf_context *ctx, pdf_dict *cspace_dict, } code = pdfi_dict_next(ctx, cspace_dict, &Key, &Value, &index); - if (code == 0 && Value->type == PDF_ARRAY) + if (code == 0 && pdfi_type_of(Value) == PDF_ARRAY) break; pdfi_countdown(Key); Key = NULL; @@ -235,6 +251,9 @@ static int pdfi_check_Shading(pdf_context *ctx, pdf_obj *shading, if (code < 0) return code; + if (pdfi_type_of(shading_dict) != PDF_DICT) + return_error(gs_error_typecheck); + code = pdfi_dict_knownget(ctx, shading_dict, "ColorSpace", (pdf_obj **)&o); if (code > 0) { code = pdfi_check_ColorSpace_for_spots(ctx, o, shading_dict, page_dict, tracker->spot_dict); @@ -257,13 +276,16 @@ static int pdfi_check_Shading_dict(pdf_context *ctx, pdf_dict *shading_dict, if (resource_is_checked(tracker, (pdf_obj *)shading_dict)) return 0; + if (pdfi_type_of(shading_dict) != PDF_DICT) + return_error(gs_error_typecheck); + if (pdfi_dict_entries(shading_dict) > 0) { code = pdfi_loop_detector_mark(ctx); /* Mark the start of the Shading dictionary loop */ if (code < 0) return code; code = pdfi_dict_first(ctx, shading_dict, &Key, &Value, &index); - if (code < 0 || !(Value->type == PDF_DICT || Value->type == PDF_STREAM)) + if (code < 0 || !(pdfi_type_of(Value) == PDF_DICT || pdfi_type_of(Value) == PDF_STREAM)) goto error1; i = 1; @@ -290,7 +312,7 @@ static int pdfi_check_Shading_dict(pdf_context *ctx, pdf_dict *shading_dict, } code = pdfi_dict_next(ctx, shading_dict, &Key, &Value, &index); - if (code == 0 && Value->type == PDF_DICT) + if (code == 0 && pdfi_type_of(Value) == PDF_DICT) break; pdfi_countdown(Key); Key = NULL; @@ -326,6 +348,9 @@ static int pdfi_check_XObject(pdf_context *ctx, pdf_dict *xobject, pdf_dict *pag if (resource_is_checked(tracker, (pdf_obj *)xobject)) return 0; + if (pdfi_type_of(xobject) != PDF_DICT) + return_error(gs_error_typecheck); + code = pdfi_dict_get_type(ctx, xobject, "Subtype", PDF_NAME, (pdf_obj **)&n); if (code >= 0) { if (pdfi_name_is((const pdf_name *)n, "Image")) { @@ -417,6 +442,9 @@ static int pdfi_check_XObject_dict(pdf_context *ctx, pdf_dict *xobject_dict, pdf if (resource_is_checked(tracker, (pdf_obj *)xobject_dict)) return 0; + if (pdfi_type_of(xobject_dict) != PDF_DICT) + return_error(gs_error_typecheck); + if (pdfi_dict_entries(xobject_dict) > 0) { code = pdfi_loop_detector_mark(ctx); /* Mark the start of the XObject dictionary loop */ if (code < 0) @@ -425,7 +453,7 @@ static int pdfi_check_XObject_dict(pdf_context *ctx, pdf_dict *xobject_dict, pdf code = pdfi_dict_first(ctx, xobject_dict, &Key, &Value, &index); if (code < 0) goto error_exit; - if (Value->type != PDF_STREAM) + if (pdfi_type_of(Value) != PDF_STREAM) goto error_exit; i = 1; @@ -457,7 +485,7 @@ static int pdfi_check_XObject_dict(pdf_context *ctx, pdf_dict *xobject_dict, pdf } code = pdfi_dict_next(ctx, xobject_dict, &Key, &Value, &index); - if (code == 0 && Value->type == PDF_STREAM) + if (code == 0 && pdfi_type_of(Value) == PDF_STREAM) break; pdfi_countdown(Key); Key = NULL; @@ -490,6 +518,9 @@ static int pdfi_check_ExtGState(pdf_context *ctx, pdf_dict *extgstate_dict, pdf_ if (resource_is_checked(tracker, (pdf_obj *)extgstate_dict)) return 0; + if (pdfi_type_of(extgstate_dict) != PDF_DICT) + return_error(gs_error_typecheck); + if (pdfi_dict_entries(extgstate_dict) > 0) { /* See if /OP or /op is true */ code = pdfi_dict_get_bool(ctx, extgstate_dict, "OP", &overprint); @@ -502,14 +533,16 @@ static int pdfi_check_ExtGState(pdf_context *ctx, pdf_dict *extgstate_dict, pdf_ /* Check SMask */ code = pdfi_dict_knownget(ctx, extgstate_dict, "SMask", &o); if (code > 0) { - if (o->type == PDF_NAME) { - if (!pdfi_name_is((pdf_name *)o, "None")) { - pdfi_countdown(o); - tracker->transparent = true; - return 0; - } - } else { - if (o->type == PDF_DICT) { + switch (pdfi_type_of(o)) { + case PDF_NAME: + if (!pdfi_name_is((pdf_name *)o, "None")) { + pdfi_countdown(o); + tracker->transparent = true; + return 0; + } + break; + case PDF_DICT: + { pdf_obj *G = NULL; tracker->transparent = true; @@ -526,6 +559,8 @@ static int pdfi_check_ExtGState(pdf_context *ctx, pdf_dict *extgstate_dict, pdf_ pdfi_countdown(o); return code; } + default: + break; } } pdfi_countdown(o); @@ -577,6 +612,9 @@ static int pdfi_check_ExtGState_dict(pdf_context *ctx, pdf_dict *extgstate_dict, if (resource_is_checked(tracker, (pdf_obj *)extgstate_dict)) return 0; + if (pdfi_type_of(extgstate_dict) != PDF_DICT) + return_error(gs_error_typecheck); + if (pdfi_dict_entries(extgstate_dict) > 0) { code = pdfi_loop_detector_mark(ctx); /* Mark the start of the ColorSpace dictionary loop */ if (code < 0) @@ -611,7 +649,7 @@ static int pdfi_check_ExtGState_dict(pdf_context *ctx, pdf_dict *extgstate_dict, } code = pdfi_dict_next(ctx, extgstate_dict, &Key, &Value, &index); - if (code == 0 && Value->type == PDF_DICT) + if (code == 0 && pdfi_type_of(Value) == PDF_DICT) break; pdfi_countdown(Key); Key = NULL; @@ -644,6 +682,9 @@ static int pdfi_check_Pattern(pdf_context *ctx, pdf_dict *pattern, pdf_dict *pag if (resource_is_checked(tracker, (pdf_obj *)pattern)) return 0; + if (pdfi_type_of(pattern) != PDF_DICT) + return_error(gs_error_typecheck); + if (tracker->spot_dict != NULL) { code = pdfi_dict_knownget(ctx, pattern, "Shading", &o); if (code > 0) @@ -677,7 +718,7 @@ int pdfi_check_Pattern_transparency(pdf_context *ctx, pdf_dict *pattern, pdf_dic bool *transparent) { int code; - pdfi_check_tracker_t tracker = {0, 0, NULL, 0, NULL}; + pdfi_check_tracker_t tracker = {0, 0, NULL, NULL, 0, NULL}; /* NOTE: We use a "null" tracker that won't do any optimization to prevent * checking the same resource twice. @@ -708,6 +749,9 @@ static int pdfi_check_Pattern_dict(pdf_context *ctx, pdf_dict *pattern_dict, pdf if (resource_is_checked(tracker, (pdf_obj *)pattern_dict)) return 0; + if (pdfi_type_of(pattern_dict) != PDF_DICT) + return_error(gs_error_typecheck); + if (pdfi_dict_entries(pattern_dict) > 0) { code = pdfi_loop_detector_mark(ctx); /* Mark the start of the Pattern dictionary loop */ if (code < 0) @@ -717,7 +761,7 @@ static int pdfi_check_Pattern_dict(pdf_context *ctx, pdf_dict *pattern_dict, pdf if (code < 0) goto error1; - if(Value->type != PDF_DICT && Value->type != PDF_STREAM) + if (pdfi_type_of(Value) != PDF_DICT && pdfi_type_of(Value) != PDF_STREAM) goto transparency_exit; i = 1; @@ -748,7 +792,7 @@ static int pdfi_check_Pattern_dict(pdf_context *ctx, pdf_dict *pattern_dict, pdf } code = pdfi_dict_next(ctx, pattern_dict, &Key, &Value, &index); - if (code == 0 && (Value->type == PDF_DICT || Value->type == PDF_STREAM)) + if (code == 0 && (pdfi_type_of(Value) == PDF_DICT || pdfi_type_of(Value) == PDF_STREAM)) break; pdfi_countdown(Key); Key = NULL; @@ -771,7 +815,8 @@ error1: /* * This routine checks a Font dictionary to see if it contains any spot - * colour definitions, or transparency usage. + * colour definitions, or transparency usage. While we are here, if the tracker's font_array + * is not NULL, pick up the font information and store it in the array. */ static int pdfi_check_Font(pdf_context *ctx, pdf_dict *font, pdf_dict *page_dict, pdfi_check_tracker_t *tracker) @@ -782,22 +827,186 @@ static int pdfi_check_Font(pdf_context *ctx, pdf_dict *font, pdf_dict *page_dict if (resource_is_checked(tracker, (pdf_obj *)font)) return 0; - if (font->type != PDF_DICT) + if (pdfi_type_of(font) != PDF_DICT) return_error(gs_error_typecheck); - code = pdfi_dict_knownget_type(ctx, font, "Subtype", PDF_NAME, &o); - if (code > 0) { - if (pdfi_name_is((pdf_name *)o, "Type3")) { - pdfi_countdown(o); - o = NULL; + if (tracker->font_array != NULL) { + /* If we get to here this is a font we have not seen before. We need + * to make a new font array big enough to hold the existing entries +1 + * copy the existing entries to the new array and free the old array. + * Finally create a dictionary with all the font information we want + * and add it to the array. + */ + pdf_array *new_fonts = NULL; + int index = 0; + pdf_obj *array_obj = NULL; + pdf_dict *font_info_dict = NULL; - code = pdfi_dict_knownget_type(ctx, font, "Resources", PDF_DICT, &o); - if (code > 0) - (void)pdfi_check_Resources(ctx, (pdf_dict *)o, page_dict, tracker); + /* Let's start by gathering the information we need and storing it in a dictionary */ + code = pdfi_dict_alloc(ctx, 4, &font_info_dict); + if (code < 0) + return code; + pdfi_countup(font_info_dict); + + if (font->object_num != 0) { + pdf_num *int_obj = NULL; + + code = pdfi_object_alloc(ctx, PDF_INT, 0, (pdf_obj **)&int_obj); + if (code >= 0) { + pdfi_countup(int_obj); + int_obj->value.i = font->object_num; + code = pdfi_dict_put(ctx, font_info_dict, "ObjectNum", (pdf_obj *)int_obj); + pdfi_countdown(int_obj); + } + if (code < 0) { + pdfi_countdown(font_info_dict); + return code; + } + } + + code = pdfi_dict_get(ctx, font, "BaseFont", &array_obj); + if (code >= 0) { + code = pdfi_dict_put(ctx, font_info_dict, "BaseFont", array_obj); + if (code < 0) { + pdfi_countdown(array_obj); + pdfi_countdown(font_info_dict); + return code; + } + } + pdfi_countdown(array_obj); + array_obj = NULL; + + code = pdfi_dict_get(ctx, font, "ToUnicode", &array_obj); + if (code >= 0) + code = pdfi_dict_put(ctx, font_info_dict, "ToUnicode", PDF_TRUE_OBJ); + else + code = pdfi_dict_put(ctx, font_info_dict, "ToUnicode", PDF_FALSE_OBJ); + pdfi_countdown(array_obj); + array_obj = NULL; + if (code < 0) + return code; + + code = pdfi_dict_get(ctx, font, "FontDescriptor", &array_obj); + if (code >= 0) { + bool known = false; + + (void)pdfi_dict_known(ctx, (pdf_dict *)array_obj, "FontFile", &known); + if (!known) { + (void)pdfi_dict_known(ctx, (pdf_dict *)array_obj, "FontFile2", &known); + if (!known) { + (void)pdfi_dict_known(ctx, (pdf_dict *)array_obj, "FontFile3", &known); + } + } + + if (known >= 0) + code = pdfi_dict_put(ctx, font_info_dict, "Embedded", PDF_TRUE_OBJ); + else + code = pdfi_dict_put(ctx, font_info_dict, "Embedded", PDF_FALSE_OBJ); + } else + code = pdfi_dict_put(ctx, font_info_dict, "Embedded", PDF_FALSE_OBJ); + + pdfi_countdown(array_obj); + array_obj = NULL; + + if (code < 0) + return code; + + + code = pdfi_dict_knownget_type(ctx, font, "Subtype", PDF_NAME, &array_obj); + if (code >= 0) { + code = pdfi_dict_put(ctx, font_info_dict, "Subtype", array_obj); + if (code < 0) { + pdfi_countdown(array_obj); + pdfi_countdown(font_info_dict); + return code; + } + + if (pdfi_name_is((pdf_name *)array_obj, "Type3")) { + pdfi_countdown(o); + o = NULL; + + code = pdfi_dict_knownget_type(ctx, font, "Resources", PDF_DICT, &o); + if (code > 0) + (void)pdfi_check_Resources(ctx, (pdf_dict *)o, page_dict, tracker); + } + + if (pdfi_name_is((const pdf_name *)array_obj, "Type0")){ + pdf_array *descendants = NULL; + pdf_dict *desc_font = NULL; + + code = pdfi_dict_get(ctx, font, "DescendantFonts", (pdf_obj **)&descendants); + if (code >= 0) { + code = pdfi_array_get(ctx, descendants, 0, (pdf_obj **)&desc_font); + if (code >= 0){ + pdf_array *desc_array = NULL; + + code = pdfi_array_alloc(ctx, 0, &desc_array); + pdfi_countup(desc_array); + if (code >= 0) { + pdf_array *saved = tracker->font_array; + + tracker->font_array = desc_array; + (void)pdfi_check_Font(ctx, desc_font, page_dict, tracker); + (void)pdfi_dict_put(ctx, font_info_dict, "Descendants", (pdf_obj *)tracker->font_array); + pdfi_countdown((pdf_obj *)tracker->font_array); + tracker->font_array = saved; + } + pdfi_countdown(descendants); + pdfi_countdown(desc_font); + } + } + } + } + pdfi_countdown(array_obj); + array_obj = NULL; + + code = pdfi_array_alloc(ctx, pdfi_array_size(tracker->font_array) + 1, &new_fonts); + if (code < 0) { + pdfi_countdown(font_info_dict); + return code; + } + pdfi_countup(new_fonts); + + for (index = 0; index < pdfi_array_size(tracker->font_array); index++) { + code = pdfi_array_get(ctx, tracker->font_array, index, &array_obj); + if (code < 0) { + pdfi_countdown(font_info_dict); + pdfi_countdown(new_fonts); + return code; + } + code = pdfi_array_put(ctx, new_fonts, index, array_obj); + pdfi_countdown(array_obj); + if (code < 0) { + pdfi_countdown(font_info_dict); + pdfi_countdown(new_fonts); + return code; + } + } + code = pdfi_array_put(ctx, new_fonts, index, (pdf_obj *)font_info_dict); + if (code < 0) { + pdfi_countdown(font_info_dict); + pdfi_countdown(new_fonts); + return code; } + pdfi_countdown(font_info_dict); + pdfi_countdown(tracker->font_array); + tracker->font_array = new_fonts; + } else { + code = pdfi_dict_knownget_type(ctx, font, "Subtype", PDF_NAME, &o); + if (code > 0) { + if (pdfi_name_is((pdf_name *)o, "Type3")) { + pdfi_countdown(o); + o = NULL; + + code = pdfi_dict_knownget_type(ctx, font, "Resources", PDF_DICT, &o); + if (code > 0) + (void)pdfi_check_Resources(ctx, (pdf_dict *)o, page_dict, tracker); + } + } + + pdfi_countdown(o); + o = NULL; } - pdfi_countdown(o); - o = NULL; return 0; } @@ -815,6 +1024,9 @@ static int pdfi_check_Font_dict(pdf_context *ctx, pdf_dict *font_dict, pdf_dict if (resource_is_checked(tracker, (pdf_obj *)font_dict)) return 0; + if (pdfi_type_of(font_dict) != PDF_DICT) + return_error(gs_error_typecheck); + if (pdfi_dict_entries(font_dict) > 0) { code = pdfi_loop_detector_mark(ctx); /* Mark the start of the Font dictionary loop */ if (code < 0) @@ -846,7 +1058,7 @@ static int pdfi_check_Font_dict(pdf_context *ctx, pdf_dict *font_dict, pdf_dict } code = pdfi_dict_next(ctx, font_dict, &Key, &Value, &index); - if (code == 0 && Value->type == PDF_DICT) + if (code == 0 && pdfi_type_of(Value) == PDF_DICT) break; pdfi_countdown(Key); Key = NULL; @@ -875,6 +1087,9 @@ static int pdfi_check_Resources(pdf_context *ctx, pdf_dict *Resources_dict, if (resource_is_checked(tracker, (pdf_obj *)Resources_dict)) return 0; + if (pdfi_type_of(Resources_dict) != PDF_DICT) + return_error(gs_error_typecheck); + /* First up, check any colour spaces, for new spot colours. * We only do this if asked because its expensive. spot_dict being NULL * means we aren't interested in spot colours (not a DeviceN or Separation device) @@ -937,6 +1152,9 @@ static int pdfi_check_annot_for_transparency(pdf_context *ctx, pdf_dict *annot, if (resource_is_checked(tracker, (pdf_obj *)annot)) return 0; + if (pdfi_type_of(annot) != PDF_DICT) + return_error(gs_error_typecheck); + /* Check #1 Does the (Normal) Appearnce stream use any Resources which include transparency. * We check this first, because this also checks for spot colour spaces. Once we've done that we * can exit the checks as soon as we detect transparency. @@ -1035,6 +1253,9 @@ static int pdfi_check_Annots_for_transparency(pdf_context *ctx, pdf_array *annot if (resource_is_checked(tracker, (pdf_obj *)annots_array)) return 0; + if (pdfi_type_of(annots_array) != PDF_ARRAY) + return_error(gs_error_typecheck); + for (i=0; i < pdfi_array_size(annots_array); i++) { code = pdfi_array_get_type(ctx, annots_array, (uint64_t)i, PDF_DICT, (pdf_obj **)&annot); if (code >= 0) { @@ -1084,6 +1305,10 @@ static int pdfi_check_page_inner(pdf_context *ctx, pdf_dict *page_dict, tracker->transparent = false; + if (pdfi_type_of(page_dict) != PDF_DICT) + return_error(gs_error_typecheck); + + /* Check if the page dictionary has a page Group entry (for spots). * Page group should mean the page has transparency but we ignore it for the purposes * of transparency detection. See above. @@ -1104,7 +1329,7 @@ static int pdfi_check_page_inner(pdf_context *ctx, pdf_dict *page_dict, code = pdfi_dict_knownget_type(ctx, page_dict, "Resources", PDF_DICT, (pdf_obj **)&Resources); if (code > 0) code = pdfi_check_Resources(ctx, Resources, page_dict, tracker); - if ((code < 0 && ctx->args.pdfstoponerror) || (code == gs_error_stackoverflow)) + if ((code < 0 && ctx->args.pdfstoponerror) || (code == gs_error_pdf_stackoverflow)) goto exit; /* If we are drawing Annotations, check to see if the page uses any Annots */ @@ -1130,7 +1355,7 @@ static int pdfi_check_page_inner(pdf_context *ctx, pdf_dict *page_dict, * Sets ctx->page.has_transparency and ctx->page.num_spots * do_setup -- indicates whether to actually set up the device with the spot count. */ -int pdfi_check_page(pdf_context *ctx, pdf_dict *page_dict, bool do_setup) +int pdfi_check_page(pdf_context *ctx, pdf_dict *page_dict, pdf_array **fonts_array, pdf_array **spots_array, bool do_setup) { int code; int spots = 0; @@ -1145,7 +1370,7 @@ int pdfi_check_page(pdf_context *ctx, pdf_dict *page_dict, bool do_setup) * TODO: Should probably look into that.. */ pdfi_device_set_flags(ctx); - code = pdfi_check_init_tracker(ctx, &tracker); + code = pdfi_check_init_tracker(ctx, &tracker, fonts_array, spots_array); if (code < 0) goto exit; @@ -1191,7 +1416,7 @@ int pdfi_check_page(pdf_context *ctx, pdf_dict *page_dict, bool do_setup) code = pdfi_dict_first(ctx, tracker.spot_dict, (pdf_obj **)&Key, &Value, &index); while (code >= 0) { - if (Key->type == PDF_NAME) { + if (pdfi_type_of(Key) == PDF_NAME) { table[a].data = ((pdf_string *)Key)->data; table[a].size = ((pdf_string *)Key)->length; table[a++].persistent = false; @@ -1263,6 +1488,47 @@ int pdfi_check_page(pdf_context *ctx, pdf_dict *page_dict, bool do_setup) ctx->page.has_OP = false; exit: + if (fonts_array != NULL) { + *fonts_array = tracker.font_array; + pdfi_countup(*fonts_array); + } + + if (spots_array != NULL && tracker.spot_dict != NULL && pdfi_dict_entries(tracker.spot_dict) != 0) { + pdf_array *new_array = NULL; + pdf_name *Key = NULL; + pdf_obj *Value = NULL; + uint64_t index = 0, a_index = 0; + + index = pdfi_dict_entries(tracker.spot_dict); + + code = pdfi_array_alloc(ctx, index, &new_array); + if (code < 0) + goto error; + pdfi_countup(new_array); + + code = pdfi_dict_first(ctx, tracker.spot_dict, (pdf_obj **)&Key, &Value, &index); + while (code >= 0) + { + if (pdfi_type_of(Key) == PDF_NAME) { + code = pdfi_array_put(ctx, new_array, a_index++, (pdf_obj *)Key); + if (code < 0) { + pdfi_countdown(new_array); + pdfi_countdown(Key); + pdfi_countdown(Value); + goto error; + } + } + + pdfi_countdown(Key); + Key = NULL; + pdfi_countdown(Value); + Value = NULL; + code = pdfi_dict_next(ctx, tracker.spot_dict, (pdf_obj **)&Key, &Value, &index); + } + code = 0; + *spots_array = new_array; + } +error: (void)pdfi_check_free_tracker(ctx, &tracker); return code; } diff --git a/pdf/pdf_check.h b/pdf/pdf_check.h index eee5c82b..68619864 100644 --- a/pdf/pdf_check.h +++ b/pdf/pdf_check.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 @@ -16,7 +16,7 @@ #ifndef PDF_CHECK #define PDF_CHECK -int pdfi_check_page(pdf_context *ctx, pdf_dict *page_dict, bool do_setup); +int pdfi_check_page(pdf_context *ctx, pdf_dict *page_dict, pdf_array **fonts_array, pdf_array **spots_array, bool do_setup); int pdfi_check_Pattern_transparency(pdf_context *ctx, pdf_dict *pattern, pdf_dict *page_dict, bool *transparent); diff --git a/pdf/pdf_cmap.c b/pdf/pdf_cmap.c index d7198e24..792b8d1b 100644 --- a/pdf/pdf_cmap.c +++ b/pdf/pdf_cmap.c @@ -790,17 +790,20 @@ pdfi_read_cmap(pdf_context *ctx, pdf_obj *cmap, pdf_cmap **pcmap) pdf_ps_ctx_t cmap_ctx; pdfi_cmap->ctx = ctx; - if (cmap->type == PDF_NAME) { - gs_string cmname; - pdf_name *cmapn = (pdf_name *)cmap; - cmname.data = cmapn->data; - cmname.size = cmapn->length; - code = pdf_cmap_open_file(ctx, &cmname, &buf, &buflen); - if (code < 0) - goto error_out; - } - else { - if (cmap->type == PDF_STREAM) { + switch (pdfi_type_of(cmap)) { + case PDF_NAME: + { + gs_string cmname; + pdf_name *cmapn = (pdf_name *)cmap; + cmname.data = cmapn->data; + cmname.size = cmapn->length; + code = pdf_cmap_open_file(ctx, &cmname, &buf, &buflen); + if (code < 0) + goto error_out; + break; + } + case PDF_STREAM: + { pdf_obj *ucmap; pdf_cmap *upcmap = NULL; pdf_dict *cmap_dict = NULL; @@ -842,11 +845,11 @@ pdfi_read_cmap(pdf_context *ctx, pdf_obj *cmap, pdf_cmap **pcmap) if (code < 0) { goto error_out; } + break; } - else { - code = gs_note_error(gs_error_typecheck); - goto error_out; - } + default: + code = gs_note_error(gs_error_typecheck); + goto error_out; } pdfi_cmap->ctx = ctx; pdfi_cmap->buf = buf; diff --git a/pdf/pdf_colour.c b/pdf/pdf_colour.c index dcc3ebf0..0fad43a6 100644 --- a/pdf/pdf_colour.c +++ b/pdf/pdf_colour.c @@ -227,7 +227,7 @@ static int pdfi_check_for_spots_by_array(pdf_context *ctx, pdf_array *color_arra if (code < 0) goto exit; - if (a->type != PDF_ARRAY) { + if (pdfi_type_of(a) != PDF_ARRAY) { code = gs_note_error(gs_error_typecheck); goto exit; } @@ -256,15 +256,16 @@ int pdfi_check_ColorSpace_for_spots(pdf_context *ctx, pdf_obj *space, pdf_dict * if (code < 0) return code; - if (space->type == PDF_NAME) { - code = pdfi_check_for_spots_by_name(ctx, (pdf_name *)space, parent_dict, page_dict, spot_dict); - } else { - if (space->type == PDF_ARRAY) { + switch(pdfi_type_of(space)) { + case PDF_NAME: + code = pdfi_check_for_spots_by_name(ctx, (pdf_name *)space, parent_dict, page_dict, spot_dict); + break; + case PDF_ARRAY: code = pdfi_check_for_spots_by_array(ctx, (pdf_array *)space, parent_dict, page_dict, spot_dict); - } else { + break; + default: pdfi_loop_detector_cleartomark(ctx); return 0; - } } (void)pdfi_loop_detector_cleartomark(ctx); @@ -282,13 +283,15 @@ int pdfi_ri(pdf_context *ctx) if (pdfi_count_stack(ctx) < 1) return_error(gs_error_stackunderflow); - if (ctx->stack_top[-1]->type != PDF_NAME) { + if (pdfi_type_of(ctx->stack_top[-1]) != PDF_NAME) { pdfi_pop(ctx, 1); return_error(gs_error_typecheck); } n = (pdf_name *)ctx->stack_top[-1]; - code = pdfi_setrenderingintent(ctx, n); + pdfi_countup(n); pdfi_pop(ctx, 1); + code = pdfi_setrenderingintent(ctx, n); + pdfi_countdown(n); return code; } @@ -330,7 +333,7 @@ static void pdfi_cspace_free_callback(gs_memory_t * mem, void *cs) if (pfn) pdfi_free_function(ctx, pfn); } - if (o->type != PDF_CTX) { + if (pdfi_type_of(o) != PDF_CTX) { pdfi_countdown(o); pcs->interpreter_data = NULL; } @@ -439,84 +442,45 @@ int pdfi_gs_setcolorspace(pdf_context *ctx, gs_color_space *pcs) /* Start with the simple cases, where we set the colour space and colour in a single operation */ int pdfi_setgraystroke(pdf_context *ctx) { - pdf_num *n1; int code; double d1; - if (pdfi_count_stack(ctx) < 1) - return_error(gs_error_stackunderflow); + code = pdfi_destack_real(ctx, &d1); + if (code < 0) + return code; - n1 = (pdf_num *)ctx->stack_top[-1]; - if (n1->type == PDF_INT){ - d1 = (double)n1->value.i; - } else{ - if (n1->type == PDF_REAL) { - d1 = n1->value.d; - } else { - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); - } - } gs_swapcolors_quick(ctx->pgs); code = pdfi_gs_setgray(ctx, d1); gs_swapcolors_quick(ctx->pgs); - pdfi_pop(ctx, 1); + return code; } int pdfi_setgrayfill(pdf_context *ctx) { - pdf_num *n1; int code; double d1; - if (pdfi_count_stack(ctx) < 1) - return_error(gs_error_stackunderflow); + code = pdfi_destack_real(ctx, &d1); + if (code < 0) + return code; - n1 = (pdf_num *)ctx->stack_top[-1]; - if (n1->type == PDF_INT){ - d1 = (double)n1->value.i; - } else{ - if (n1->type == PDF_REAL) { - d1 = n1->value.d; - } else { - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); - } - } - code = pdfi_gs_setgray(ctx, d1); - pdfi_pop(ctx, 1); - return code; + return pdfi_gs_setgray(ctx, d1); } int pdfi_setrgbstroke(pdf_context *ctx) { - pdf_num *num; double Values[3]; - int i, code; + int code; - if (pdfi_count_stack(ctx) < 3) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } + code = pdfi_destack_reals(ctx, Values, 3); + if (code < 0) + return code; - for (i=0;i < 3;i++){ - num = (pdf_num *)ctx->stack_top[i - 3]; - if (num->type != PDF_INT) { - if(num->type != PDF_REAL) { - pdfi_pop(ctx, 3); - return_error(gs_error_typecheck); - } - else - Values[i] = num->value.d; - } else { - Values[i] = (double)num->value.i; - } - } gs_swapcolors_quick(ctx->pgs); code = pdfi_gs_setrgbcolor(ctx, Values[0], Values[1], Values[2]); gs_swapcolors_quick(ctx->pgs); - pdfi_pop(ctx, 3); + return code; } @@ -534,104 +498,57 @@ int pdfi_setrgbfill_array(pdf_context *ctx) return_error(gs_error_stackunderflow); array = (pdf_array *)ctx->stack_top[-1]; - if (array->type != PDF_ARRAY) { + pdfi_countup(array); + pdfi_pop(ctx, 1); + if (pdfi_type_of(array) != PDF_ARRAY) { code = gs_note_error(gs_error_typecheck); goto exit; } code = pdfi_setcolor_from_array(ctx, array); exit: - pdfi_pop(ctx, 1); + pdfi_countdown(array); return code; } int pdfi_setrgbfill(pdf_context *ctx) { - pdf_num *num; double Values[3]; - int i, code; + int code; - if (pdfi_count_stack(ctx) < 3) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } + code = pdfi_destack_reals(ctx, Values, 3); + if (code < 0) + return code; - for (i=0;i < 3;i++){ - num = (pdf_num *)ctx->stack_top[i - 3]; - if (num->type != PDF_INT) { - if(num->type != PDF_REAL) { - pdfi_pop(ctx, 3); - return_error(gs_error_typecheck); - } - else - Values[i] = num->value.d; - } else { - Values[i] = (double)num->value.i; - } - } - code = pdfi_gs_setrgbcolor(ctx, Values[0], Values[1], Values[2]); - pdfi_pop(ctx, 3); - return code; + return pdfi_gs_setrgbcolor(ctx, Values[0], Values[1], Values[2]); } int pdfi_setcmykstroke(pdf_context *ctx) { - pdf_num *num; double Values[4]; - int i, code; + int code; - if (pdfi_count_stack(ctx) < 4) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } + code = pdfi_destack_reals(ctx, Values, 4); + if (code < 0) + return code; - for (i=0;i < 4;i++){ - num = (pdf_num *)ctx->stack_top[i - 4]; - if (num->type != PDF_INT) { - if(num->type != PDF_REAL) { - pdfi_pop(ctx, 4); - return_error(gs_error_typecheck); - } - else - Values[i] = num->value.d; - } else { - Values[i] = (double)num->value.i; - } - } gs_swapcolors_quick(ctx->pgs); code = pdfi_gs_setcmykcolor(ctx, Values[0], Values[1], Values[2], Values[3]); gs_swapcolors_quick(ctx->pgs); - pdfi_pop(ctx, 4); + return code; } int pdfi_setcmykfill(pdf_context *ctx) { - pdf_num *num; double Values[4]; - int i, code; + int code; - if (pdfi_count_stack(ctx) < 4) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } + code = pdfi_destack_reals(ctx, Values, 4); + if (code < 0) + return code; - for (i=0;i < 4;i++){ - num = (pdf_num *)ctx->stack_top[i - 4]; - if (num->type != PDF_INT) { - if(num->type != PDF_REAL) { - pdfi_pop(ctx, 4); - return_error(gs_error_typecheck); - } - else - Values[i] = num->value.d; - } else { - Values[i] = (double)num->value.i; - } - } - code = pdfi_gs_setcmykcolor(ctx, Values[0], Values[1], Values[2], Values[3]); - pdfi_pop(ctx, 4); - return code; + return pdfi_gs_setcmykcolor(ctx, Values[0], Values[1], Values[2], Values[3]); } /* Do a setcolor using values in an array @@ -675,27 +592,22 @@ int pdfi_setcolor_from_array(pdf_context *ctx, pdf_array *array) static int pdfi_get_color_from_stack(pdf_context *ctx, gs_client_color *cc, int ncomps) { - int i; - pdf_num *n; + int i, code; if (pdfi_count_stack(ctx) < ncomps) { pdfi_clearstack(ctx); return_error(gs_error_stackunderflow); } - for (i=0;i<ncomps;i++){ - n = (pdf_num *)ctx->stack_top[i - ncomps]; - if (n->type == PDF_INT) { - cc->paint.values[i] = (float)n->value.i; - } else { - if (n->type == PDF_REAL) { - cc->paint.values[i] = n->value.d; - } else { - pdfi_clearstack(ctx); - return_error(gs_error_typecheck); - } + + for (i = 0; i < ncomps; i++) { + code = pdfi_obj_to_float(ctx, ctx->stack_top[i - ncomps], &cc->paint.values[i]); + if (code < 0) { + pdfi_clearstack(ctx); + return code; } } pdfi_pop(ctx, ncomps); + return 0; } @@ -780,14 +692,20 @@ pdfi_setcolorN(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, boo if (pcs->type == &gs_color_space_type_Pattern) is_pattern = true; if (is_pattern) { - if (ctx->stack_top[-1]->type != PDF_NAME) { + pdf_name *n = NULL; + + if (pdfi_type_of(ctx->stack_top[-1]) != PDF_NAME) { pdfi_clearstack(ctx); - code = gs_note_error(gs_error_syntaxerror); + code = gs_note_error(gs_error_typecheck); goto cleanupExit0; } - base_space = pcs->base_space; - code = pdfi_pattern_set(ctx, stream_dict, page_dict, (pdf_name *)ctx->stack_top[-1], &cc); + n = (pdf_name *)ctx->stack_top[-1]; + pdfi_countup(n); pdfi_pop(ctx, 1); + + base_space = pcs->base_space; + code = pdfi_pattern_set(ctx, stream_dict, page_dict, n, &cc); + pdfi_countdown(n); if (code < 0) { /* 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"); @@ -1125,14 +1043,19 @@ static int pdfi_create_iccbased(pdf_context *ctx, pdf_array *color_array, int in } code = pdfi_dict_knownget(ctx, dict, "Name", &Name); if (code > 0) { - if(Name->type == PDF_STRING || Name->type == PDF_NAME) { - cname = (char *)gs_alloc_bytes(ctx->memory, ((pdf_name *)Name)->length + 1, "pdfi_create_iccbased (profile name)"); - if (cname == NULL) { - code = gs_note_error(gs_error_VMerror); - goto done; - } - memset(cname, 0x00, ((pdf_name *)Name)->length + 1); - memcpy(cname, ((pdf_name *)Name)->data, ((pdf_name *)Name)->length); + switch (pdfi_type_of(Name)) { + case PDF_STRING: + case PDF_NAME: + cname = (char *)gs_alloc_bytes(ctx->memory, ((pdf_name *)Name)->length + 1, "pdfi_create_iccbased (profile name)"); + if (cname == NULL) { + code = gs_note_error(gs_error_VMerror); + goto done; + } + memset(cname, 0x00, ((pdf_name *)Name)->length + 1); + memcpy(cname, ((pdf_name *)Name)->data, ((pdf_name *)Name)->length); + break; + default: + break; } } if (code < 0) @@ -1146,7 +1069,7 @@ static int pdfi_create_iccbased(pdf_context *ctx, pdf_array *color_array, int in int i; if (pdfi_array_size(a) >= N * 2) { - for (i = 0; i < pdfi_array_size(a);i++) { + for (i = 0; i < N * 2;i++) { code = pdfi_array_get_number(ctx, a, i, &dbl); if (code < 0) { known = false; @@ -1218,7 +1141,7 @@ static int pdfi_create_iccbased(pdf_context *ctx, pdf_array *color_array, int in code = pdfi_dict_knownget(ctx, dict, "Alternate", &Alternate); if (code > 0) { /* The Alternate should be one of the device spaces, therefore a Name object. If its not, fallback to using /N */ - if (Alternate->type == PDF_NAME) + if (pdfi_type_of(Alternate) == PDF_NAME) code = pdfi_create_colorspace_by_name(ctx, (pdf_name *)Alternate, stream_dict, page_dict, ppcs, inline_image); pdfi_countdown(Alternate); @@ -1639,23 +1562,23 @@ static int pdfi_create_Separation(pdf_context *ctx, pdf_array *color_array, int if (code < 0) goto pdfi_separation_error; - if (o->type == PDF_NAME) { - NamedAlternate = (pdf_name *)o; - code = pdfi_create_colorspace_by_name(ctx, NamedAlternate, stream_dict, page_dict, &pcs_alt, inline_image); - if (code < 0) - goto pdfi_separation_error; - - } else { - if (o->type == PDF_ARRAY) { + switch (pdfi_type_of(o)) { + case PDF_NAME: + NamedAlternate = (pdf_name *)o; + code = pdfi_create_colorspace_by_name(ctx, NamedAlternate, stream_dict, page_dict, &pcs_alt, inline_image); + if (code < 0) + goto pdfi_separation_error; + break; + case 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) goto pdfi_separation_error; - } - else { + break; + default: + pdfi_countdown(o); code = gs_error_typecheck; goto pdfi_separation_error; - } } code = pdfi_array_get(ctx, color_array, index + 3, &transform); @@ -1814,14 +1737,14 @@ all_error: if (code < 0) goto pdfi_devicen_error; - if (o->type == PDF_NAME) { - NamedAlternate = (pdf_name *)o; - code = pdfi_create_colorspace_by_name(ctx, NamedAlternate, stream_dict, page_dict, &pcs_alt, inline_image); - if (code < 0) - goto pdfi_devicen_error; - - } else { - if (o->type == PDF_ARRAY) { + switch (pdfi_type_of(o)) { + case PDF_NAME: + NamedAlternate = (pdf_name *)o; + code = pdfi_create_colorspace_by_name(ctx, NamedAlternate, stream_dict, page_dict, &pcs_alt, inline_image); + if (code < 0) + goto pdfi_devicen_error; + break; + case 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) @@ -1830,12 +1753,11 @@ all_error: * paths count down ArrayAlternate. */ goto pdfi_devicen_error; - } - else { + break; + default: code = gs_error_typecheck; pdfi_countdown(o); goto pdfi_devicen_error; - } } /* Now the tint transform */ @@ -1889,21 +1811,24 @@ all_error: if (code == 0) { pcs->params.device_n.subtype = gs_devicen_DeviceN; } else { - if (subtype->type == PDF_NAME || subtype->type == PDF_STRING) { - if (memcmp(((pdf_name *)subtype)->data, "DeviceN", 7) == 0) { - pcs->params.device_n.subtype = gs_devicen_DeviceN; - } else { - if (memcmp(((pdf_name *)subtype)->data, "NChannel", 8) == 0) { - pcs->params.device_n.subtype = gs_devicen_NChannel; + switch (pdfi_type_of(subtype)) { + case PDF_NAME: + case PDF_STRING: + if (memcmp(((pdf_name *)subtype)->data, "DeviceN", 7) == 0) { + pcs->params.device_n.subtype = gs_devicen_DeviceN; } else { - pdfi_countdown(subtype); - goto pdfi_devicen_error; + if (memcmp(((pdf_name *)subtype)->data, "NChannel", 8) == 0) { + pcs->params.device_n.subtype = gs_devicen_NChannel; + } else { + pdfi_countdown(subtype); + goto pdfi_devicen_error; + } } - } - pdfi_countdown(subtype); - } else { - pdfi_countdown(subtype); - goto pdfi_devicen_error; + pdfi_countdown(subtype); + break; + default: + pdfi_countdown(subtype); + goto pdfi_devicen_error; } } @@ -1944,21 +1869,24 @@ all_error: goto pdfi_devicen_error; } - if (name->type == PDF_NAME || name->type == PDF_STRING) { - pcs->params.device_n.process_names[ix] = (char *)gs_alloc_bytes(pcs->params.device_n.mem->non_gc_memory, ((pdf_name *)name)->length + 1, "pdfi_devicen(Processnames)"); - if (pcs->params.device_n.process_names[ix] == NULL) { + switch (pdfi_type_of(name)) { + case PDF_NAME: + case PDF_STRING: + pcs->params.device_n.process_names[ix] = (char *)gs_alloc_bytes(pcs->params.device_n.mem->non_gc_memory, ((pdf_name *)name)->length + 1, "pdfi_devicen(Processnames)"); + if (pcs->params.device_n.process_names[ix] == NULL) { + pdfi_countdown(Components); + pdfi_countdown(name); + code = gs_error_VMerror; + goto pdfi_devicen_error; + } + memcpy(pcs->params.device_n.process_names[ix], ((pdf_name *)name)->data, ((pdf_name *)name)->length); + pcs->params.device_n.process_names[ix][((pdf_name *)name)->length] = 0x00; + pdfi_countdown(name); + break; + default: pdfi_countdown(Components); pdfi_countdown(name); - code = gs_error_VMerror; goto pdfi_devicen_error; - } - memcpy(pcs->params.device_n.process_names[ix], ((pdf_name *)name)->data, ((pdf_name *)name)->length); - pcs->params.device_n.process_names[ix][((pdf_name *)name)->length] = 0x00; - pdfi_countdown(name); - } else { - pdfi_countdown(Components); - pdfi_countdown(name); - goto pdfi_devicen_error; } } pdfi_countdown(Components); @@ -1979,17 +1907,26 @@ all_error: goto pdfi_devicen_error; do { - if (Space->type != PDF_STRING && Space->type != PDF_NAME && Space->type != PDF_ARRAY) { - pdfi_countdown(Space); - pdfi_countdown(Colorant); - code = gs_note_error(gs_error_typecheck); - goto pdfi_devicen_error; + switch (pdfi_type_of(Space)) { + case PDF_STRING: + case PDF_NAME: + case PDF_ARRAY: + break; + default: + pdfi_countdown(Space); + pdfi_countdown(Colorant); + code = gs_note_error(gs_error_typecheck); + goto pdfi_devicen_error; } - if (Colorant->type != PDF_STRING && Colorant->type != PDF_NAME) { - pdfi_countdown(Space); - pdfi_countdown(Colorant); - code = gs_note_error(gs_error_typecheck); - goto pdfi_devicen_error; + switch (pdfi_type_of(Colorant)) { + case PDF_STRING: + case PDF_NAME: + break; + default: + pdfi_countdown(Space); + pdfi_countdown(Colorant); + code = gs_note_error(gs_error_typecheck); + goto pdfi_devicen_error; } code = pdfi_create_colorspace(ctx, Space, stream_dict, page_dict, &colorant_space, inline_image); @@ -2115,11 +2052,14 @@ pdfi_create_indexed(pdf_context *ctx, pdf_array *color_array, int index, if (code < 0) goto exit; - if (lookup->type == PDF_STREAM) { + switch (pdfi_type_of(lookup)) { + case PDF_STREAM: code = pdfi_stream_to_buffer(ctx, (pdf_stream *)lookup, &Buffer, &lookup_length); if (code < 0) goto exit; - } else if (lookup->type == PDF_STRING) { + break; + case PDF_STRING: + { /* This is not legal, but Acrobat seems to accept it */ pdf_string *lookup_string = (pdf_string *)lookup; /* alias */ @@ -2131,7 +2071,9 @@ pdfi_create_indexed(pdf_context *ctx, pdf_array *color_array, int index, memcpy(Buffer, lookup_string->data, lookup_string->length); lookup_length = lookup_string->length; - } else { + break; + } + default: code = gs_note_error(gs_error_typecheck); goto exit; } @@ -2208,6 +2150,7 @@ static int pdfi_create_DeviceGray(pdf_context *ctx, gs_color_space **ppcs) } } else { code = pdfi_gs_setgray(ctx, 0); + pdfi_set_colour_callback(ctx->pgs->color[0].color_space, ctx, pdfi_cspace_free_callback); } return code; } @@ -2236,6 +2179,7 @@ static int pdfi_create_DeviceRGB(pdf_context *ctx, gs_color_space **ppcs) } } else { code = pdfi_gs_setrgbcolor(ctx, 0, 0, 0); + pdfi_set_colour_callback(ctx->pgs->color[0].color_space, ctx, pdfi_cspace_free_callback); } return code; } @@ -2264,6 +2208,7 @@ static int pdfi_create_DeviceCMYK(pdf_context *ctx, gs_color_space **ppcs) } } else { code = pdfi_gs_setcmykcolor(ctx, 0, 0, 0, 1); + pdfi_set_colour_callback(ctx->pgs->color[0].color_space, ctx, pdfi_cspace_free_callback); } return code; } @@ -2352,7 +2297,7 @@ pdfi_create_colorspace_by_array(pdf_context *ctx, pdf_array *color_array, int in if (code < 0) goto exit; - if (a->type != PDF_ARRAY) { + if (pdfi_type_of(a) != PDF_ARRAY) { code = gs_note_error(gs_error_typecheck); goto exit; } @@ -2416,11 +2361,15 @@ pdfi_create_colorspace_by_name(pdf_context *ctx, pdf_name *name, if (code < 0) return code; - if (ref_space->type == PDF_NAME) { + if (pdfi_type_of(ref_space) == 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); } + if (((pdf_name *)ref_space)->length <= 0) { + pdfi_countdown(ref_space); + return_error(gs_error_syntaxerror); + } } /* recursion */ @@ -2492,15 +2441,16 @@ int pdfi_create_colorspace(pdf_context *ctx, pdf_obj *space, pdf_dict *stream_di if (code < 0) return code; - if (space->type == PDF_NAME) { + switch (pdfi_type_of(space)) { + case PDF_NAME: code = pdfi_create_colorspace_by_name(ctx, (pdf_name *)space, stream_dict, page_dict, ppcs, inline_image); - } else { - if (space->type == PDF_ARRAY) { - code = pdfi_create_colorspace_by_array(ctx, (pdf_array *)space, 0, stream_dict, page_dict, ppcs, inline_image); - } else { - pdfi_loop_detector_cleartomark(ctx); - return_error(gs_error_typecheck); - } + break; + case PDF_ARRAY: + code = pdfi_create_colorspace_by_array(ctx, (pdf_array *)space, 0, stream_dict, page_dict, ppcs, inline_image); + break; + default: + pdfi_loop_detector_cleartomark(ctx); + return_error(gs_error_typecheck); } if (code >= 0 && ppcs && *ppcs) (void)(*ppcs)->type->install_cspace(*ppcs, ctx->pgs); @@ -2518,36 +2468,46 @@ int pdfi_setcolorspace(pdf_context *ctx, pdf_obj *space, pdf_dict *stream_dict, int pdfi_setstrokecolor_space(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) { int code; + pdf_obj *n = NULL; if (pdfi_count_stack(ctx) < 1) return_error(gs_error_stackunderflow); - if (ctx->stack_top[-1]->type != PDF_NAME) { + if (pdfi_type_of(ctx->stack_top[-1]) != PDF_NAME) { pdfi_pop(ctx, 1); - return_error(gs_error_stackunderflow); + return_error(gs_error_typecheck); } + n = ctx->stack_top[-1]; + pdfi_countup(n); + pdfi_pop(ctx, 1); + gs_swapcolors_quick(ctx->pgs); - code = pdfi_setcolorspace(ctx, ctx->stack_top[-1], stream_dict, page_dict); + code = pdfi_setcolorspace(ctx, n, stream_dict, page_dict); gs_swapcolors_quick(ctx->pgs); - pdfi_pop(ctx, 1); + pdfi_countdown(n); return code; } int pdfi_setfillcolor_space(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) { int code; + pdf_obj *n = NULL; if (pdfi_count_stack(ctx) < 1) return_error(gs_error_stackunderflow); - if (ctx->stack_top[-1]->type != PDF_NAME) { + if (pdfi_type_of(ctx->stack_top[-1]) != PDF_NAME) { pdfi_pop(ctx, 1); - return_error(gs_error_stackunderflow); + return_error(gs_error_typecheck); } - code = pdfi_setcolorspace(ctx, ctx->stack_top[-1], stream_dict, page_dict); + n = ctx->stack_top[-1]; + pdfi_countup(n); pdfi_pop(ctx, 1); + code = pdfi_setcolorspace(ctx, n, stream_dict, page_dict); + + pdfi_countdown(n); return code; } @@ -2790,7 +2750,7 @@ static int Check_Default_Space(pdf_context *ctx, pdf_obj *space, pdf_dict *sourc pdf_obj *ref_space = NULL; int code = 0; - if (space->type == PDF_NAME) + if (pdfi_type_of(space) == PDF_NAME) { if (pdfi_name_is((const pdf_name *)space, "DeviceGray")) return (num_components == 1 ? 0 : gs_error_rangecheck); @@ -2804,7 +2764,7 @@ static int Check_Default_Space(pdf_context *ctx, pdf_obj *space, pdf_dict *sourc if (code < 0) return code; - if (ref_space->type == PDF_NAME) { + if (pdfi_type_of(ref_space) == 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); @@ -2827,12 +2787,12 @@ static int Check_Default_Space(pdf_context *ctx, pdf_obj *space, pdf_dict *sourc space = ref_space; } - if (space->type == PDF_ARRAY) { + if (pdfi_type_of(space) == PDF_ARRAY) { code = pdfi_array_get(ctx, (pdf_array *)space, 0, &primary); if (code < 0) goto exit; - if (primary->type == PDF_NAME) { + if (pdfi_type_of(primary) == PDF_NAME) { if (pdfi_name_is((pdf_name *)primary, "Lab")) { code = gs_note_error(gs_error_typecheck); goto exit; diff --git a/pdf/pdf_colour.h b/pdf/pdf_colour.h index 263c3d9d..b279c2f8 100644 --- a/pdf/pdf_colour.h +++ b/pdf/pdf_colour.h @@ -27,7 +27,7 @@ static inline void pdfi_set_colourspace_name(pdf_context *ctx, gs_color_space *p { if (pcs->interpreter_data != NULL) { pdf_obj *o = (pdf_obj *)(pcs->interpreter_data); - if (o != NULL && o->type == PDF_NAME) { + if (o != NULL && pdfi_type_of(o) == PDF_NAME) { pdfi_countdown(o); pcs->interpreter_data = NULL; } @@ -53,7 +53,7 @@ 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) + if (o == NULL || pdfi_type_of(o) != PDF_NAME) return 0; if (pdfi_name_cmp(n, (pdf_name *)o) == 0) { diff --git a/pdf/pdf_deref.c b/pdf/pdf_deref.c index 91d77f63..d444e0c7 100644 --- a/pdf/pdf_deref.c +++ b/pdf/pdf_deref.c @@ -45,6 +45,9 @@ static int pdfi_add_to_cache(pdf_context *ctx, pdf_obj *o) { pdf_obj_cache_entry *entry; + if (o < PDF_TOKEN_AS_OBJ(TOKEN__LAST_KEY)) + return 0; + if (ctx->xref_table->xref[o->object_num].cache != NULL) { #if DEBUG_CACHE dmprintf1(ctx->memory, "Attempting to add object %d to cache when the object is already cached!\n", o->object_num); @@ -200,7 +203,6 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ { int code = 0; int64_t i; - pdf_keyword *keyword = NULL; pdf_dict *dict = NULL; gs_offset_t offset; pdf_stream *stream_obj = NULL; @@ -222,14 +224,15 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ return_error(gs_error_stackunderflow); dict = (pdf_dict *)ctx->stack_top[-1]; - dict->indirect_num = dict->object_num = objnum; - dict->indirect_gen = dict->generation_num = gen; - if (dict->type != PDF_DICT) { + if (pdfi_type_of(dict) != PDF_DICT) { pdfi_pop(ctx, 1); return_error(gs_error_syntaxerror); } + dict->indirect_num = dict->object_num = objnum; + dict->indirect_gen = dict->generation_num = gen; + /* Convert the dict into a stream */ code = pdfi_obj_dict_to_stream(ctx, dict, &stream_obj, true); if (code < 0) { @@ -258,12 +261,16 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ pdfi_countdown(stream_obj); /* get rid of extra ref */ return code; } - if (pdfi_loop_detector_check_object(ctx, stream_obj->object_num)) + if (pdfi_loop_detector_check_object(ctx, stream_obj->object_num)) { + pdfi_countdown(stream_obj); /* get rid of extra ref */ + pdfi_loop_detector_cleartomark(ctx); 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 */ + pdfi_loop_detector_cleartomark(ctx); return code; } @@ -305,8 +312,8 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ stream_obj->Length = 0; stream_obj->length_valid = false; - code = pdfi_read_token(ctx, ctx->main_stream, objnum, gen); - if (code < 0 || pdfi_count_stack(ctx) < 2) { + code = pdfi_read_bare_keyword(ctx, ctx->main_stream); + if (code == 0) { char extra_info[gp_file_name_sizeof]; gs_snprintf(extra_info, sizeof(extra_info), "Failed to find a valid object at end of stream object %u.\n", objnum); @@ -318,34 +325,27 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ 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]; + } else if (code < 0) { + char extra_info[gp_file_name_sizeof]; - 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_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 */ - stream_obj->Length = i; - stream_obj->length_valid = true; - } - } - pdfi_pop(ctx, 1); + 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 if (code != TOKEN_ENDSTREAM) { + char extra_info[gp_file_name_sizeof]; + + 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 */ + stream_obj->Length = i; + stream_obj->length_valid = true; } } /* If we failed to find a valid object, or the object wasn't a keyword, or the * keywrod wasn't 'endstream' then the Length is wrong. We need to have the correct * Length for streams if we have encrypted files, because we must install a - * SubFileDecode filter iwth a Length (EODString is incompatible with AES encryption) + * SubFileDecode filter with a Length (EODString is incompatible with AES encryption) * Rather than mess about checking for encryption, we'll choose to just correctly * calculate the Length of all streams. Although this takes time, it will only * happen for files which are invalid. @@ -393,7 +393,7 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ return 0; } - code = pdfi_read_token(ctx, ctx->main_stream, objnum, gen); + code = pdfi_read_bare_keyword(ctx, ctx->main_stream); if (code < 0) { pdfi_countdown(stream_obj); /* get rid of extra ref */ if (ctx->args.pdfstoponerror) @@ -406,14 +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 (code == 0) { pdfi_countdown(stream_obj); /* get rid of extra ref */ return_error(gs_error_stackunderflow); } - if (((pdf_obj *)ctx->stack_top[-1])->type != PDF_KEYWORD) { + if (code != TOKEN_ENDOBJ) { pdfi_countdown(stream_obj); /* get rid of extra ref */ - pdfi_pop(ctx, 1); if (ctx->args.pdfstoponerror) return_error(gs_error_typecheck); pdfi_set_error(ctx, 0, NULL, E_PDF_MISSINGENDOBJ, "pdfi_read_stream_object", NULL); @@ -424,12 +423,6 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ } 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); - return_error(gs_error_typecheck); - } - pdfi_pop(ctx, 1); return 0; } @@ -440,10 +433,12 @@ static int pdfi_read_stream_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_ */ int pdfi_read_bare_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_t stream_offset, uint32_t objnum, uint32_t gen) { - int code = 0; - pdf_keyword *keyword = NULL; + int code = 0, initial_depth = 0; + pdf_key keyword; gs_offset_t saved_offset[3]; + pdf_obj_type type; + initial_depth = pdfi_count_stack(ctx); saved_offset[0] = saved_offset[1] = saved_offset[2] = 0; code = pdfi_read_token(ctx, s, objnum, gen); @@ -468,13 +463,16 @@ 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); + type = pdfi_type_of(ctx->stack_top[-1]); + if (type == PDF_KEYWORD) + goto missing_endobj; + } while (type != PDF_FAST_KEYWORD); - keyword = ((pdf_keyword *)ctx->stack_top[-1]); - if (keyword->key == TOKEN_ENDOBJ) { + keyword = (pdf_key)(uintptr_t)(ctx->stack_top[-1]); + if (keyword == TOKEN_ENDOBJ) { pdf_obj *o; - if (pdfi_count_stack(ctx) < 2) { + if (pdfi_count_stack(ctx) - initial_depth < 2) { pdfi_clearstack(ctx); return_error(gs_error_stackunderflow); } @@ -483,21 +481,23 @@ int pdfi_read_bare_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_t stream_ pdfi_pop(ctx, 1); - o->indirect_num = o->object_num = objnum; - o->indirect_gen = o->generation_num = gen; + if (o >= PDF_TOKEN_AS_OBJ(TOKEN__LAST_KEY)) { + o->indirect_num = o->object_num = objnum; + o->indirect_gen = o->generation_num = gen; + } return code; } - if (keyword->key == TOKEN_STREAM) { + if (keyword == TOKEN_STREAM) { pdfi_pop(ctx, 1); return pdfi_read_stream_object(ctx, s, stream_offset, objnum, gen); } - if (keyword->key == TOKEN_OBJ) { + if (keyword == TOKEN_OBJ) { pdf_obj *o; pdfi_set_error(ctx, 0, NULL, E_PDF_MISSINGENDOBJ, "pdfi_read_bare_object", NULL); /* 4 for; the object we want, the object number, generation number and 'obj' keyword */ - if (pdfi_count_stack(ctx) < 4) + if (pdfi_count_stack(ctx) - initial_depth < 4) return_error(gs_error_stackunderflow); /* If we have that many objects, assume that we can throw away the x y obj and just use the remaining object */ @@ -505,28 +505,33 @@ int pdfi_read_bare_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_t stream_ pdfi_pop(ctx, 3); - o->indirect_num = o->object_num = objnum; - o->indirect_gen = o->generation_num = gen; + if (pdfi_type_of(o) != PDF_BOOL && pdfi_type_of(o) != PDF_NULL && pdfi_type_of(o) != PDF_FAST_KEYWORD) { + o->indirect_num = o->object_num = objnum; + o->indirect_gen = o->generation_num = gen; + } if (saved_offset[0] > 0) (void)pdfi_seek(ctx, s, saved_offset[0], SEEK_SET); return 0; } +missing_endobj: /* Assume that any other keyword means a missing 'endobj' */ if (!ctx->args.pdfstoponerror) { pdf_obj *o; pdfi_set_error(ctx, 0, NULL, E_PDF_MISSINGENDOBJ, "pdfi_read_bare_object", NULL); - if (pdfi_count_stack(ctx) < 2) + if (pdfi_count_stack(ctx) - initial_depth < 2) return_error(gs_error_stackunderflow); o = ctx->stack_top[-2]; pdfi_pop(ctx, 1); - o->indirect_num = o->object_num = objnum; - o->indirect_gen = o->generation_num = gen; + if (pdfi_type_of(o) != PDF_BOOL && pdfi_type_of(o) != PDF_NULL && pdfi_type_of(o) != PDF_FAST_KEYWORD) { + o->indirect_num = o->object_num = objnum; + o->indirect_gen = o->generation_num = gen; + } return code; } pdfi_pop(ctx, 2); @@ -535,60 +540,34 @@ int pdfi_read_bare_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_t stream_ static int pdfi_read_object(pdf_context *ctx, pdf_c_stream *s, gs_offset_t stream_offset) { - int code = 0, stack_size = pdfi_count_stack(ctx); - uint64_t objnum = 0, gen = 0; - pdf_keyword *keyword = NULL; + int code = 0; + int objnum = 0, gen = 0; /* An object consists of 'num gen obj' followed by a token, follwed by an endobj * A stream dictionary might have a 'stream' instead of an 'endobj', in which case we * want to deal with it specially by getting the Length, jumping to the end and checking * for an endobj. Or not, possibly, because it would be slow. */ - code = pdfi_read_token(ctx, s, 0, 0); + code = pdfi_read_bare_int(ctx, s, &objnum); 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) { - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); - } - objnum = ((pdf_num *)ctx->stack_top[-1])->value.i; - pdfi_pop(ctx, 1); - - code = pdfi_read_token(ctx, s, 0, 0); + code = pdfi_read_bare_int(ctx, s, &gen); 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) { - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); - } - gen = ((pdf_num *)ctx->stack_top[-1])->value.i; - pdfi_pop(ctx, 1); - - code = pdfi_read_token(ctx, s, 0, 0); + code = pdfi_read_bare_keyword(ctx, s); if (code < 0) return code; - if (stack_size >= pdfi_count_stack(ctx)) + if (code == 0) return gs_note_error(gs_error_ioerror); - if (((pdf_obj *)ctx->stack_top[-1])->type != PDF_KEYWORD) { - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); - } - keyword = ((pdf_keyword *)ctx->stack_top[-1]); - if (keyword->key != TOKEN_OBJ) { - pdfi_pop(ctx, 1); + if (code != TOKEN_OBJ) { return_error(gs_error_syntaxerror); } - pdfi_pop(ctx, 1); return pdfi_read_bare_object(ctx, s, stream_offset, objnum, gen); } @@ -602,13 +581,13 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p pdf_c_stream *SubFile_stream = NULL; pdf_c_stream *Object_stream = NULL; int i = 0, object_length = 0; - int64_t num_entries, found_object; - int64_t Length; + int64_t num_entries; + int found_object; + int64_t Length, First; gs_offset_t offset = 0; pdf_stream *compressed_object = NULL; pdf_dict *compressed_sdict = NULL; /* alias */ 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); @@ -637,7 +616,7 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p goto exit; } - if ((ctx->stack_top[-1])->type != PDF_STREAM) { + if (pdfi_type_of(ctx->stack_top[-1]) != PDF_STREAM) { pdfi_pop(ctx, 1); code = gs_note_error(gs_error_typecheck); goto exit; @@ -666,31 +645,70 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p if (code < 0) return code; + if (ctx->loop_detection != NULL) { + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + goto exit; + if (compressed_sdict->object_num != 0) { + if (pdfi_loop_detector_check_object(ctx, compressed_sdict->object_num)) { + code = gs_note_error(gs_error_circular_reference); + } else { + code = pdfi_loop_detector_add_object(ctx, compressed_sdict->object_num); + } + if (code < 0) { + (void)pdfi_loop_detector_cleartomark(ctx); + goto exit; + } + } + } /* Check its an ObjStm ! */ code = pdfi_dict_get_type(ctx, compressed_sdict, "Type", PDF_NAME, (pdf_obj **)&Type); - if (code < 0) + if (code < 0) { + if (ctx->loop_detection != NULL) + (void)pdfi_loop_detector_cleartomark(ctx); goto exit; + } if (!pdfi_name_is(Type, "ObjStm")){ + if (ctx->loop_detection != NULL) + (void)pdfi_loop_detector_cleartomark(ctx); code = gs_note_error(gs_error_syntaxerror); goto exit; } /* Need to check the /N entry to see if the object is actually in this stream! */ code = pdfi_dict_get_int(ctx, compressed_sdict, "N", &num_entries); - if (code < 0) + if (code < 0) { + if (ctx->loop_detection != NULL) + (void)pdfi_loop_detector_cleartomark(ctx); goto exit; + } if (num_entries < 0 || num_entries > ctx->xref_table->xref_size) { + if (ctx->loop_detection != NULL) + (void)pdfi_loop_detector_cleartomark(ctx); code = gs_note_error(gs_error_rangecheck); goto exit; } - code = pdfi_seek(ctx, ctx->main_stream, pdfi_stream_offset(ctx, compressed_object), SEEK_SET); - if (code < 0) + code = pdfi_dict_get_int(ctx, compressed_sdict, "Length", &Length); + if (code < 0) { + if (ctx->loop_detection != NULL) + (void)pdfi_loop_detector_cleartomark(ctx); goto exit; + } - code = pdfi_dict_get_int(ctx, compressed_sdict, "Length", &Length); + code = pdfi_dict_get_int(ctx, compressed_sdict, "First", &First); + if (code < 0) { + if (ctx->loop_detection != NULL) + (void)pdfi_loop_detector_cleartomark(ctx); + goto exit; + } + + if (ctx->loop_detection != NULL) + (void)pdfi_loop_detector_cleartomark(ctx); + + code = pdfi_seek(ctx, ctx->main_stream, pdfi_stream_offset(ctx, compressed_object), SEEK_SET); if (code < 0) goto exit; @@ -703,57 +721,86 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p goto exit; for (i=0;i < num_entries;i++) - { - 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); - pdfi_pop(ctx, 1); - goto exit; - } - found_object = ((pdf_num *)temp_obj)->value.i; - pdfi_pop(ctx, 1); - 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); + { + int new_offset; + code = pdfi_read_bare_int(ctx, compressed_stream, &found_object); + if (code < 0) + goto exit; + if (code == 0) { + code = gs_note_error(gs_error_syntaxerror); + goto exit; + } + code = pdfi_read_bare_int(ctx, compressed_stream, &new_offset); + if (code < 0) + goto exit; + if (code == 0) { + code = gs_note_error(gs_error_syntaxerror); + goto exit; + } + if (i == entry->u.compressed.object_index) { + if (found_object != obj) { + code = gs_note_error(gs_error_undefined); goto exit; } - if (i == entry->u.compressed.object_index) { - if (found_object != obj) { - pdfi_pop(ctx, 1); - code = gs_note_error(gs_error_undefined); - goto exit; - } - offset = ((pdf_num *)temp_obj)->value.i; - } - if (i == entry->u.compressed.object_index + 1) - object_length = ((pdf_num *)temp_obj)->value.i - offset; - pdfi_pop(ctx, 1); + offset = new_offset; + } + if (i == entry->u.compressed.object_index + 1) + object_length = new_offset - offset; + } + + /* Bug #705259 - The first object need not lie immediately after the initial + * table of object numbers and offsets. The start of the first object is given + * by the value of First. We don't know how many bytes we consumed getting to + * the end of the table, unfortunately, so we close the stream, rewind the main + * stream back to the beginning of the ObjStm, and then read and discard 'First' + * bytes in order to get to the start of the first object. Then we read the + * number of bytes required to get from there to the start of the object we + * actually want. + * If this ever looks like it's causing performance problems we could read the + * initial table above manually instead of using the existing code, and track + * how many bytes we'd read, which would avoid us having to tear down and + * rebuild the stream. + */ + if (compressed_stream) + pdfi_close_file(ctx, compressed_stream); + if (SubFile_stream) + pdfi_close_file(ctx, SubFile_stream); + + code = pdfi_seek(ctx, ctx->main_stream, pdfi_stream_offset(ctx, compressed_object), SEEK_SET); + if (code < 0) + goto exit; + + /* We already dereferenced this above, so we don't need the loop detection checking here */ + code = pdfi_dict_get_int(ctx, compressed_sdict, "Length", &Length); + if (code < 0) + goto exit; + + code = pdfi_apply_SubFileDecode_filter(ctx, Length, NULL, ctx->main_stream, &SubFile_stream, false); + if (code < 0) + goto exit; + + code = pdfi_filter(ctx, compressed_object, SubFile_stream, &compressed_stream, false); + if (code < 0) + goto exit; + + for (i=0;i < First;i++) + { + int c = pdfi_read_byte(ctx, compressed_stream); + if (c < 0) { + code = gs_note_error(gs_error_ioerror); + goto exit; } + } /* Skip to the offset of the object we want to read */ for (i=0;i < offset;i++) - { - int c = pdfi_read_byte(ctx, compressed_stream); - if (c < 0) { - code = gs_note_error(gs_error_ioerror); - goto exit; - } + { + int c = pdfi_read_byte(ctx, compressed_stream); + if (c < 0) { + code = gs_note_error(gs_error_ioerror); + goto exit; } + } /* If object_length is not 0, then we want to apply a SubFileDecode filter to limit * the number of bytes we read to the declared size of the object (difference between @@ -777,7 +824,7 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p 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) { + if (pdfi_type_of(ctx->stack_top[-1]) == PDF_ARRAY_MARK || pdfi_type_of(ctx->stack_top[-1]) == PDF_DICT_MARK) { int start_depth = pdfi_count_stack(ctx); /* Need to read all the elements from COS objects */ @@ -793,16 +840,18 @@ static int pdfi_deref_compressed(pdf_context *ctx, uint64_t obj, uint64_t gen, p code = gs_note_error(gs_error_ioerror); goto exit; } - }while ((ctx->stack_top[-1]->type != PDF_ARRAY && ctx->stack_top[-1]->type != PDF_DICT) || pdfi_count_stack(ctx) > start_depth); + } while ((pdfi_type_of(ctx->stack_top[-1]) != PDF_ARRAY && pdfi_type_of(ctx->stack_top[-1]) != PDF_DICT) || pdfi_count_stack(ctx) > start_depth); } *object = ctx->stack_top[-1]; /* For compressed objects we don't get a 'obj gen obj' sequence which is what sets * the object number for uncompressed objects. So we need to do that here. */ - (*object)->indirect_num = (*object)->object_num = obj; - (*object)->indirect_gen = (*object)->generation_num = gen; - pdfi_countup(*object); + if (*object >= PDF_TOKEN_AS_OBJ(TOKEN__LAST_KEY)) { + (*object)->indirect_num = (*object)->object_num = obj; + (*object)->indirect_gen = (*object)->generation_num = gen; + pdfi_countup(*object); + } pdfi_pop(ctx, 1); if (cache) { @@ -850,10 +899,15 @@ static int pdfi_dereference_main(pdf_context *ctx, uint64_t obj, uint64_t gen, p if(ctx->args.pdfstoponerror) return_error(gs_error_rangecheck); - code = pdfi_object_alloc(ctx, PDF_NULL, 0, object); - if (code == 0) - pdfi_countup(*object); - return code; + code = pdfi_repair_file(ctx); + if (code < 0) { + *object = PDF_NULL_OBJ; + return code; + } + if (obj >= ctx->xref_table->xref_size) { + *object = PDF_NULL_OBJ; + return_error(gs_error_rangecheck); + } } entry = &ctx->xref_table->xref[obj]; @@ -924,11 +978,8 @@ static int pdfi_dereference_main(pdf_context *ctx, uint64_t obj, uint64_t gen, p int code1 = 0; if (entry->free) { dmprintf2(ctx->memory, "Dereference of free object %"PRIu64", next object number as offset failed (code = %d), returning NULL object.\n", entry->object_num, code); - code = pdfi_object_alloc(ctx, PDF_NULL, 1, object); - if (code >= 0) { - pdfi_countup(*object); - goto free_obj; - } + *object = PDF_NULL_OBJ; + goto free_obj; } ctx->encryption.decrypt_strings = saved_decrypt_strings; (void)pdfi_seek(ctx, ctx->main_stream, saved_stream_offset, SEEK_SET); @@ -941,10 +992,22 @@ static int pdfi_dereference_main(pdf_context *ctx, uint64_t obj, uint64_t gen, p return code; } - if (pdfi_count_stack(ctx) > 0 && (ctx->stack_top[-1])->object_num == obj) { + if (pdfi_count_stack(ctx) > 0 && + (ctx->stack_top[-1] > PDF_TOKEN_AS_OBJ(TOKEN__LAST_KEY) && + (ctx->stack_top[-1])->object_num == obj)) { *object = ctx->stack_top[-1]; pdfi_countup(*object); pdfi_pop(ctx, 1); + if (pdfi_type_of(*object) == PDF_INDIRECT) { + pdf_indirect_ref *iref = (pdf_indirect_ref *)*object; + + if (iref->ref_object_num == obj) { + code = gs_note_error(gs_error_circular_reference); + pdfi_countdown(*object); + *object = NULL; + goto error; + } + } if (cache) { code = pdfi_add_to_cache(ctx, *object); if (code < 0) { @@ -956,10 +1019,8 @@ static int pdfi_dereference_main(pdf_context *ctx, uint64_t obj, uint64_t gen, p pdfi_pop(ctx, 1); if (entry->free) { dmprintf1(ctx->memory, "Dereference of free object %"PRIu64", next object number as offset failed, returning NULL object.\n", entry->object_num); - code = pdfi_object_alloc(ctx, PDF_NULL, 1, object); - if (code >= 0) - pdfi_countup(*object); - return code; + *object = PDF_NULL_OBJ; + return 0; } code = gs_note_error(gs_error_undefined); goto error; @@ -969,7 +1030,7 @@ free_obj: (void)pdfi_seek(ctx, ctx->main_stream, saved_stream_offset, SEEK_SET); } - if (ctx->loop_detection && (*object)->object_num != 0) { + if (ctx->loop_detection && pdf_object_num(*object) != 0) { code = pdfi_loop_detector_add_object(ctx, (*object)->object_num); if (code < 0) { ctx->encryption.decrypt_strings = saved_decrypt_strings; @@ -1052,11 +1113,12 @@ static int pdfi_resolve_indirect_array(pdf_context *ctx, pdf_obj *obj, bool recu code = 0; } else { if (code < 0) goto exit; - /* don't store the object if it's a stream (leave as a ref) */ - if (object->type != PDF_STREAM) - code = pdfi_array_put(ctx, array, index, object); if (recurse) code = pdfi_resolve_indirect_loop_detect(ctx, NULL, object, recurse); + if (code < 0) goto exit; + /* don't store the object if it's a stream (leave as a ref) */ + if (pdfi_type_of(object) != PDF_STREAM) + code = pdfi_array_put(ctx, array, index, object); } if (code < 0) goto exit; @@ -1107,7 +1169,7 @@ static int pdfi_resolve_indirect_dict(pdf_context *ctx, pdf_obj *obj, bool recur } else { if (code < 0) goto exit; /* don't store the object if it's a stream (leave as a ref) */ - if (Value->type != PDF_STREAM) + if (pdfi_type_of(Value) != PDF_STREAM) pdfi_dict_put_obj(ctx, dict, (pdf_obj *)Key, Value, true); if (recurse) code = pdfi_resolve_indirect_loop_detect(ctx, NULL, Value, recurse); @@ -1130,7 +1192,7 @@ int pdfi_resolve_indirect(pdf_context *ctx, pdf_obj *value, bool recurse) { int code = 0; - switch(value->type) { + switch(pdfi_type_of(value)) { case PDF_ARRAY: code = pdfi_resolve_indirect_array(ctx, value, recurse); break; @@ -1158,7 +1220,7 @@ int pdfi_resolve_indirect_loop_detect(pdf_context *ctx, pdf_obj *parent, pdf_obj if (code < 0) goto exit; } - if (value->object_num != 0) { + if (pdf_object_num(value) != 0) { if (pdfi_loop_detector_check_object(ctx, value->object_num)) { code = gs_note_error(gs_error_circular_reference); goto exit; diff --git a/pdf/pdf_dict.c b/pdf/pdf_dict.c index 34356dfe..217cb6db 100644 --- a/pdf/pdf_dict.c +++ b/pdf/pdf_dict.c @@ -140,7 +140,7 @@ int pdfi_dict_from_stack(pdf_context *ctx, uint32_t indirect_num, uint32_t indir i = (index / 2) - 1; /* 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) { + if (pdfi_type_of((pdf_obj *)ctx->stack_top[-2]) == PDF_NAME) { d->list[i].key = ctx->stack_top[-2]; pdfi_countup(d->list[i].key); #if DEBUG_DICT @@ -149,7 +149,7 @@ int pdfi_dict_from_stack(pdf_context *ctx, uint32_t indirect_num, uint32_t indir d->list[i].value = ctx->stack_top[-1]; pdfi_countup(d->list[i].value); } else { - if (convert_string_keys && ((pdf_obj *)ctx->stack_top[-2])->type == PDF_STRING) { + if (convert_string_keys && (pdfi_type_of((pdf_obj *)ctx->stack_top[-2]) == PDF_STRING)) { pdf_name *n; code = pdfi_dict_name_from_string(ctx, (pdf_string *)ctx->stack_top[-2], &n); if (code < 0) { @@ -266,7 +266,7 @@ static int pdfi_dict_find_unsorted(pdf_context *ctx, pdf_dict *d, const char *Ke for (i=0;i< d->entries;i++) { t = (pdf_name *)d->list[i].key; - if (t && t->type == PDF_NAME) { + if (t && pdfi_type_of(t) == PDF_NAME) { if (pdfi_name_is((pdf_name *)t, Key)) { return i; } @@ -315,14 +315,14 @@ int pdfi_dict_get_common(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj *o = NULL; - if (d->type != PDF_DICT) + if (pdfi_type_of(d) != PDF_DICT) return_error(gs_error_typecheck); index = pdfi_dict_find(ctx, d, Key, true); if (index < 0) return index; - if (d->list[index].value->type == PDF_INDIRECT) { + if (pdfi_type_of(d->list[index].value) == PDF_INDIRECT) { pdf_indirect_ref *r = (pdf_indirect_ref *)d->list[index].value; if (r->ref_object_num == d->object_num) @@ -340,8 +340,11 @@ int pdfi_dict_get_common(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj * 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) - { + if ((*o) < (pdf_obj *)(uintptr_t)(TOKEN__LAST_KEY)) { + /* "FAST" object, therefore can't be a problem. */ + pdfi_countdown(d->list[index].value); + d->list[index].value = *o; + } else if ((*o)->object_num == 0 || (*o)->object_num != d->object_num) { pdfi_countdown(d->list[index].value); d->list[index].value = *o; } else { @@ -364,7 +367,7 @@ int pdfi_dict_get_no_deref(pdf_context *ctx, pdf_dict *d, const pdf_name *Key, p *o = NULL; - if (d->type != PDF_DICT) + if (pdfi_type_of(d) != PDF_DICT) return_error(gs_error_typecheck); index = pdfi_dict_find_key(ctx, d, Key, true); @@ -386,14 +389,14 @@ int pdfi_dict_get_by_key(pdf_context *ctx, pdf_dict *d, const pdf_name *Key, pdf *o = NULL; - if (d->type != PDF_DICT) + if (pdfi_type_of(d) != PDF_DICT) return_error(gs_error_typecheck); index = pdfi_dict_find_key(ctx, d, Key, true); if (index < 0) return index; - if (d->list[index].value->type == PDF_INDIRECT) { + if (pdfi_type_of(d->list[index].value) == 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); @@ -414,14 +417,14 @@ int pdfi_dict_get_ref(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_indire *o = NULL; - if (d->type != PDF_DICT) + if (pdfi_type_of(d) != PDF_DICT) return_error(gs_error_typecheck); index = pdfi_dict_find(ctx, d, Key, true); if (index < 0) return index; - if (d->list[index].value->type == PDF_INDIRECT) { + if (pdfi_type_of(d->list[index].value) == PDF_INDIRECT) { *o = (pdf_indirect_ref *)d->list[index].value; pdfi_countup(*o); return 0; @@ -444,7 +447,7 @@ static int pdfi_dict_get_no_store_R_inner(pdf_context *ctx, pdf_dict *d, const c *o = NULL; - if (d->type != PDF_DICT) + if (pdfi_type_of(d) != PDF_DICT) return_error(gs_error_typecheck); if (strKey == NULL) @@ -455,7 +458,7 @@ static int pdfi_dict_get_no_store_R_inner(pdf_context *ctx, pdf_dict *d, const c if (index < 0) return index; - if (d->list[index].value->type == PDF_INDIRECT) { + if (pdfi_type_of(d->list[index].value) == 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); @@ -500,7 +503,7 @@ int pdfi_dict_get_type(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj_t if (code < 0) return code; - if ((*o)->type != type) { + if (pdfi_type_of(*o) != type) { pdfi_countdown(*o); *o = NULL; return_error(gs_error_typecheck); @@ -524,15 +527,14 @@ pdfi_dict_get_int2(pdf_context *ctx, pdf_dict *d, const char *Key1, int pdfi_dict_get_int(pdf_context *ctx, pdf_dict *d, const char *Key, int64_t *i) { int code; - pdf_num *n; + pdf_obj *n; - code = pdfi_dict_get_type(ctx, d, Key, PDF_INT, (pdf_obj **)&n); + code = pdfi_dict_get(ctx, d, Key, &n); if (code < 0) return code; - - *i = n->value.i; + code = pdfi_obj_to_int(ctx, n, i); pdfi_countdown(n); - return 0; + return code; } /* Get an int from dict, and if undefined, return provided default */ @@ -566,15 +568,24 @@ pdfi_dict_get_bool2(pdf_context *ctx, pdf_dict *d, const char *Key1, int pdfi_dict_get_bool(pdf_context *ctx, pdf_dict *d, const char *Key, bool *val) { int code; - pdf_bool *b; + pdf_obj *b; - code = pdfi_dict_get_type(ctx, d, Key, PDF_BOOL, (pdf_obj **)&b); + code = pdfi_dict_get(ctx, d, Key, &b); if (code < 0) return code; - *val = b->value; + if (b == PDF_TRUE_OBJ) { + *val = 1; + return 0; + } else if (b == PDF_FALSE_OBJ) { + *val = 0; + return 0; + } + pdfi_countdown(b); - return 0; + + *val = 0; /* Be consistent at least! */ + return_error(gs_error_typecheck); } int pdfi_dict_get_number2(pdf_context *ctx, pdf_dict *d, const char *Key1, const char *Key2, double *f) @@ -590,23 +601,15 @@ int pdfi_dict_get_number2(pdf_context *ctx, pdf_dict *d, const char *Key1, const int pdfi_dict_get_number(pdf_context *ctx, pdf_dict *d, const char *Key, double *f) { int code; - pdf_num *o; + pdf_obj *o; - code = pdfi_dict_get(ctx, d, Key, (pdf_obj **)&o); + code = pdfi_dict_get(ctx, d, Key, &o); if (code < 0) return code; - if (o->type == PDF_INT) { - *f = (double)(o->value.i); - } else { - if (o->type == PDF_REAL){ - *f = o->value.d; - } else { - pdfi_countdown(o); - return_error(gs_error_typecheck); - } - } + code = pdfi_obj_to_real(ctx, o, f); pdfi_countdown(o); - return 0; + + return code; } /* convenience functions for retrieving arrys, see shadings and functions */ @@ -627,7 +630,7 @@ int fill_domain_from_dict(pdf_context *ctx, float *parray, int size, pdf_dict *d code = pdfi_dict_get(ctx, dict, "Domain", (pdf_obj **)&a); if (code < 0) return code; - if (a->type != PDF_ARRAY) { + if (pdfi_type_of(a) != PDF_ARRAY) { pdfi_countdown(a); return_error(gs_error_typecheck); } @@ -659,7 +662,7 @@ int fill_float_array_from_dict(pdf_context *ctx, float *parray, int size, pdf_di code = pdfi_dict_get(ctx, dict, Key, (pdf_obj **)&a); if (code < 0) return code; - if (a->type != PDF_ARRAY) { + if (pdfi_type_of(a) != PDF_ARRAY) { code = gs_note_error(gs_error_typecheck); goto exit; } @@ -685,13 +688,13 @@ int fill_bool_array_from_dict(pdf_context *ctx, bool *parray, int size, pdf_dict { int code, i; pdf_array *a = NULL; - pdf_bool *o; + pdf_obj *o; uint64_t array_size; code = pdfi_dict_get(ctx, dict, Key, (pdf_obj **)&a); if (code < 0) return code; - if (a->type != PDF_ARRAY) { + if (pdfi_type_of(a) != PDF_ARRAY) { pdfi_countdown(a); return_error(gs_error_typecheck); } @@ -700,13 +703,20 @@ int fill_bool_array_from_dict(pdf_context *ctx, bool *parray, int size, pdf_dict return_error(gs_error_rangecheck); for (i=0;i< array_size;i++) { - code = pdfi_array_get_type(ctx, a, (uint64_t)i, PDF_BOOL, (pdf_obj **)&o); + code = pdfi_array_get(ctx, a, (uint64_t)i, (pdf_obj **)&o); if (code < 0) { pdfi_countdown(a); return_error(code); } - parray[i] = o->value; - pdfi_countdown(o); + if (o == PDF_TRUE_OBJ) { + parray[i] = 1; + } else if (o == PDF_FALSE_OBJ) { + parray[i] = 0; + } else { + pdfi_countdown(o); + pdfi_countdown(a); + return_error(gs_error_typecheck); + } } pdfi_countdown(a); return array_size; @@ -722,7 +732,7 @@ int fill_matrix_from_dict(pdf_context *ctx, float *parray, pdf_dict *dict) code = pdfi_dict_get(ctx, dict, "Matrix", (pdf_obj **)&a); if (code < 0) return code; - if (a->type != PDF_ARRAY) { + if (pdfi_type_of(a) != PDF_ARRAY) { pdfi_countdown(a); return_error(gs_error_typecheck); } @@ -758,7 +768,7 @@ int pdfi_make_float_array_from_dict(pdf_context *ctx, float **parray, pdf_dict * code = pdfi_dict_get(ctx, dict, Key, (pdf_obj **)&a); if (code < 0) return code; - if (a->type != PDF_ARRAY) { + if (pdfi_type_of(a) != PDF_ARRAY) { pdfi_countdown(a); return_error(gs_error_typecheck); } @@ -795,7 +805,7 @@ int pdfi_make_int_array_from_dict(pdf_context *ctx, int **parray, pdf_dict *dict code = pdfi_dict_get(ctx, dict, Key, (pdf_obj **)&a); if (code < 0) return code; - if (a->type != PDF_ARRAY) { + if (pdfi_type_of(a) != PDF_ARRAY) { pdfi_countdown(a); return_error(gs_error_typecheck); } @@ -828,10 +838,10 @@ int pdfi_dict_put_obj(pdf_context *ctx, pdf_dict *d, pdf_obj *Key, pdf_obj *valu int i; pdf_dict_entry *new_list; - if (d->type != PDF_DICT) + if (pdfi_type_of(d) != PDF_DICT) return_error(gs_error_typecheck); - if (Key->type != PDF_NAME) + if (pdfi_type_of(Key) != PDF_NAME) return_error(gs_error_typecheck); /* First, do we have a Key/value pair already ? */ @@ -883,6 +893,60 @@ int pdfi_dict_put_obj(pdf_context *ctx, pdf_dict *d, pdf_obj *Key, pdf_obj *valu return 0; } +/* + * Be very cautious using this routine; it does not check to see if a key already exists + * in a dictionary!. This is initially at least intended for use by the font code, to build + * a CharStrings dictionary. We do that by adding each glyph individually with a name + * created from a loop counter, so we know there cannot be any duplicates, and the time + * taken to check that each of 64K names was unique was quite significant. + * See bug #705534, the old PDF interpreter (nullpage, 72 dpi) runs this file in ~20 seconds + * pdfi runs it in around 40 seconds. With this change it runs in around 3 seconds. THis is, + * of course, an extreme example. + */ +int pdfi_dict_put_unchecked(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj *value) +{ + int i, code = 0; + pdf_dict_entry *new_list; + pdf_obj *key = NULL; + + code = pdfi_name_alloc(ctx, (byte *)Key, strlen(Key), &key); + if (code < 0) + return code; + pdfi_countup(key); + + /* 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->list[i].key == NULL) { + d->list[i].key = key; + d->list[i].value = value; + pdfi_countup(value); + d->entries++; + return 0; + } + } + } + + 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_list, d->list, d->size * sizeof(pdf_dict_entry)); + + gs_free_object(ctx->memory, d->list, "pdfi_dict_put key/value reallocation"); + + d->list = new_list; + + d->list[d->size].key = key; + d->list[d->size].value = value; + d->size++; + d->entries++; + pdfi_countup(value); + + return 0; +} + /* Put into dictionary with key as string */ int pdfi_dict_put(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj *value) { @@ -914,15 +978,9 @@ int pdfi_dict_put_int(pdf_context *ctx, pdf_dict *d, const char *key, int64_t va int pdfi_dict_put_bool(pdf_context *ctx, pdf_dict *d, const char *key, bool value) { - int code; - pdf_bool *obj = NULL; - - code = pdfi_object_alloc(ctx, PDF_BOOL, 0, (pdf_obj **)&obj); - if (code < 0) - return code; + pdf_obj *obj = (value ? PDF_TRUE_OBJ : PDF_FALSE_OBJ); - obj->value = value; - return pdfi_dict_put(ctx, d, key, (pdf_obj *)obj); + return pdfi_dict_put(ctx, d, key, obj); } int pdfi_dict_put_name(pdf_context *ctx, pdf_dict *d, const char *key, const char *name) @@ -957,7 +1015,7 @@ int pdfi_dict_known(pdf_context *ctx, pdf_dict *d, const char *Key, bool *known) { int i; - if (d->type != PDF_DICT) + if (pdfi_type_of(d) != PDF_DICT) return_error(gs_error_typecheck); *known = false; @@ -972,7 +1030,7 @@ int pdfi_dict_known_by_key(pdf_context *ctx, pdf_dict *d, pdf_name *Key, bool *k { int i; - if (d->type != PDF_DICT) + if (pdfi_type_of(d) != PDF_DICT) return_error(gs_error_typecheck); *known = false; @@ -1030,6 +1088,25 @@ int pdfi_dict_knownget_type(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_ return 1; } +int pdfi_dict_knownget_bool(pdf_context *ctx, pdf_dict *d, const char *Key, bool *b) +{ + bool known = false; + int code; + + code = pdfi_dict_known(ctx, d, Key, &known); + if (code < 0) + return code; + + if (known == false) + return 0; + + code = pdfi_dict_get_bool(ctx, d, Key, b); + if (code < 0) + return code; + + return 1; +} + /* Like pdfi_dict_knownget_type() but retrieves numbers (two possible types) */ int pdfi_dict_knownget_number(pdf_context *ctx, pdf_dict *d, const char *Key, double *f) @@ -1055,7 +1132,7 @@ int pdfi_dict_next(pdf_context *ctx, pdf_dict *d, pdf_obj **Key, pdf_obj **Value { int code; - if (d->type != PDF_DICT) + if (pdfi_type_of(d) != PDF_DICT) return_error(gs_error_typecheck); while (1) { @@ -1078,7 +1155,7 @@ int pdfi_dict_next(pdf_context *ctx, pdf_dict *d, pdf_obj **Key, pdf_obj **Value continue; } - if (d->list[*index].value->type == PDF_INDIRECT) { + if (pdfi_type_of(d->list[*index].value) == PDF_INDIRECT) { pdf_indirect_ref *r = (pdf_indirect_ref *)d->list[*index].value; pdf_obj *o; @@ -1113,7 +1190,7 @@ int pdfi_dict_key_next(pdf_context *ctx, pdf_dict *d, pdf_obj **Key, uint64_t *i { uint64_t *i = index; - if (d->type != PDF_DICT) + if (pdfi_type_of(d) != PDF_DICT) return_error(gs_error_typecheck); while (1) { @@ -1169,7 +1246,7 @@ int64_t pdfi_stream_length(pdf_context *ctx, pdf_stream *stream) int64_t Length = 0; int code; - if (stream->type != PDF_STREAM) + if (pdfi_type_of(stream) != PDF_STREAM) return 0; if (stream->length_valid) @@ -1195,14 +1272,14 @@ int64_t pdfi_stream_length(pdf_context *ctx, pdf_stream *stream) */ gs_offset_t pdfi_stream_offset(pdf_context *ctx, pdf_stream *stream) { - if (stream->type != PDF_STREAM) + if (pdfi_type_of(stream) != PDF_STREAM) return 0; return stream->stream_offset; } pdf_stream *pdfi_stream_parent(pdf_context *ctx, pdf_stream *stream) { - if (stream->type != PDF_STREAM) + if (pdfi_type_of(stream) != PDF_STREAM) return 0; return (pdf_stream *)stream->parent_obj; } @@ -1233,11 +1310,15 @@ void pdfi_clear_stream_parent(pdf_context *ctx, pdf_stream *stream) int pdfi_dict_from_obj(pdf_context *ctx, pdf_obj *obj, pdf_dict **dict) { *dict = NULL; - if (obj->type == PDF_DICT) - *dict = (pdf_dict *)obj; - else if (obj->type == PDF_STREAM) - *dict = ((pdf_stream *)obj)->stream_dict; - else - return_error(gs_error_typecheck); + switch (pdfi_type_of(obj)) { + case PDF_DICT: + *dict = (pdf_dict *)obj; + break; + case PDF_STREAM: + *dict = ((pdf_stream *)obj)->stream_dict; + break; + default: + return_error(gs_error_typecheck); + } return 0; } diff --git a/pdf/pdf_dict.h b/pdf/pdf_dict.h index c5a8743b..90cac6af 100644 --- a/pdf/pdf_dict.h +++ b/pdf/pdf_dict.h @@ -28,7 +28,7 @@ static inline int pdfi_dict_get(pdf_context *ctx, pdf_dict *d, const char *Key, 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); + return pdfi_dict_get_common(ctx, d, Key, o, false); } @@ -42,8 +42,10 @@ int pdfi_dict_known_by_key(pdf_context *ctx, pdf_dict *d, pdf_name *Key, bool *k 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_dict_knownget_bool(pdf_context *ctx, pdf_dict *d, const char *Key, bool *b); 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, bool replace); +int pdfi_dict_put_unchecked(pdf_context *ctx, pdf_dict *d, const char *Key, pdf_obj *value); 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); diff --git a/pdf/pdf_doc.c b/pdf/pdf_doc.c index be71dfcb..7febe60a 100644 --- a/pdf/pdf_doc.c +++ b/pdf/pdf_doc.c @@ -55,13 +55,13 @@ int pdfi_read_Root(pdf_context *ctx) } pdfi_countdown(d); - if (o1->type == PDF_INDIRECT) { + if (pdfi_type_of(o1) == PDF_INDIRECT) { code = pdfi_dereference(ctx, ((pdf_indirect_ref *)o1)->ref_object_num, ((pdf_indirect_ref *)o1)->ref_generation_num, &o); pdfi_countdown(o1); if (code < 0) return code; - if (o->type != PDF_DICT) { + if (pdfi_type_of(o) != PDF_DICT) { pdfi_countdown(o); return_error(gs_error_typecheck); } @@ -73,23 +73,40 @@ int pdfi_read_Root(pdf_context *ctx) } o1 = o; } else { - if (o1->type != PDF_DICT) { + if (pdfi_type_of(o1) != PDF_DICT) { pdfi_countdown(o1); - return_error(gs_error_typecheck); + if (ctx->Root == NULL) + return_error(gs_error_typecheck); + return 0; } } code = pdfi_dict_get_type(ctx, (pdf_dict *)o1, "Type", PDF_NAME, &o); if (code < 0) { - pdfi_countdown(o1); - return code; + bool known = false; + + pdfi_set_error(ctx, 0, NULL, E_PDF_MISSINGTYPE, "pdfi_read_Root", NULL); + + /* Missing the *required* /Type key! See if it has /Pages at least, if it does carry on */ + code = pdfi_dict_known(ctx, (pdf_dict *)o1, "Pages", &known); + if (code < 0 || known == false) { + pdfi_countdown(o1); + return code; + } } - if (pdfi_name_strcmp((pdf_name *)o, "Catalog") != 0){ + else { + if (pdfi_name_strcmp((pdf_name *)o, "Catalog") != 0){ + pdfi_countdown(o); + pdfi_countdown(o1); + /* If we repaired the file, we may already have spotted a potential Root dictionary + * so if the one we found here isn't valid, try the one we found when scanning + */ + if (ctx->Root == NULL) + return_error(gs_error_syntaxerror); + return 0; + } pdfi_countdown(o); - pdfi_countdown(o1); - return_error(gs_error_syntaxerror); } - pdfi_countdown(o); if (ctx->args.pdfdebug) dmprintf(ctx->memory, "\n"); @@ -101,6 +118,183 @@ int pdfi_read_Root(pdf_context *ctx) return 0; } +static int Info_check_dict(pdf_context *ctx, pdf_dict *d); + +static int Info_check_array(pdf_context *ctx, pdf_array *a) +{ + int code = 0, i = 0; + pdf_obj *array_obj = NULL; + + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + return code; + + for (i = 0;i < pdfi_array_size(a); i++) { + code = pdfi_array_fetch_recursing(ctx, a, i, &array_obj, true, true); + if (code < 0) + goto error; + + switch(pdfi_type_of(array_obj)) { + case PDF_DICT: + if (array_obj->object_num != 0) { + code = pdfi_loop_detector_add_object(ctx, array_obj->object_num); + if (code < 0) + goto error; + } + code = Info_check_dict(ctx, (pdf_dict *)array_obj); + if (code < 0) + goto error; + break; + case PDF_ARRAY: + if (array_obj->object_num != 0) { + code = pdfi_loop_detector_add_object(ctx, array_obj->object_num); + if (code < 0) + goto error; + } + code = Info_check_array(ctx, (pdf_array *)array_obj); + if (code < 0) + goto error; + break; + default: + break; + } + + pdfi_countdown(array_obj); + array_obj = NULL; + } +error: + pdfi_countdown(array_obj); + pdfi_loop_detector_cleartomark(ctx); + return code; +} + +static int Info_check_dict(pdf_context *ctx, pdf_dict *d) +{ + int code = 0; + uint64_t index = 0; + pdf_name *Key = NULL; + pdf_obj *Value = NULL; + + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + return code; + + code = pdfi_dict_first(ctx, d, (pdf_obj **)&Key, &Value, &index); + if (code < 0) { + if (code == gs_error_undefined) + code = 0; + goto error; + } + + while (code >= 0) { + switch(pdfi_type_of(Value)) { + case PDF_DICT: + if (Value->object_num != 0) { + code = pdfi_loop_detector_add_object(ctx, Value->object_num); + if (code < 0) + goto error; + } + code = Info_check_dict(ctx, (pdf_dict *)Value); + if (code < 0) + goto error; + break; + case PDF_ARRAY: + if (Value->object_num != 0) { + code = pdfi_loop_detector_add_object(ctx, Value->object_num); + if (code < 0) + goto error; + } + code = Info_check_array(ctx, (pdf_array *)Value); + if (code < 0) + goto error; + break; + default: + break; + } + pdfi_countdown(Key); + Key = NULL; + pdfi_countdown(Value); + Value = NULL; + + code = pdfi_dict_next(ctx, d, (pdf_obj **)&Key, &Value, &index); + if (code == gs_error_undefined) { + code = 0; + break; + } + } +error: + pdfi_countdown(Key); + pdfi_countdown(Value); + pdfi_loop_detector_cleartomark(ctx); + return code; +} + +static int pdfi_sanitize_Info_references(pdf_context *ctx, pdf_dict *Info) +{ + int code = 0; + uint64_t index = 0; + pdf_name *Key = NULL; + pdf_obj *Value = NULL; + +restart_scan: + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + return code; + + code = pdfi_dict_first(ctx, Info, (pdf_obj **)&Key, &Value, &index); + if (code == gs_error_undefined) { + code = 0; + goto error; + } + + while (code >= 0) { + switch(pdfi_type_of(Value)) { + case PDF_DICT: + code = Info_check_dict(ctx, (pdf_dict *)Value); + break; + case PDF_ARRAY: + code = Info_check_array(ctx, (pdf_array *)Value); + break; + default: + code = 0; + break; + } + pdfi_countdown(Value); + Value = NULL; + if (code < 0) { + code = pdfi_dict_delete_pair(ctx, Info, Key); + if (code < 0) + goto error; + pdfi_countdown(Key); + Key = NULL; + + pdfi_loop_detector_cleartomark(ctx); + goto restart_scan; + } + pdfi_countdown(Key); + Key = NULL; + + pdfi_loop_detector_cleartomark(ctx); + code = pdfi_loop_detector_mark(ctx); + if (code < 0) { + pdfi_countdown(Key); + pdfi_countdown(Value); + return code; + } + + code = pdfi_dict_next(ctx, Info, (pdf_obj **)&Key, &Value, &index); + if (code == gs_error_undefined) { + code = 0; + break; + } + } +error: + pdfi_countdown(Key); + pdfi_countdown(Value); + pdfi_loop_detector_cleartomark(ctx); + return code; +} + int pdfi_read_Info(pdf_context *ctx) { pdf_dict *Info; @@ -121,6 +315,20 @@ int pdfi_read_Info(pdf_context *ctx) if (ctx->args.pdfdebug) dmprintf(ctx->memory, "\n"); + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + goto error; + code = pdfi_loop_detector_add_object(ctx, Info->object_num); + if (code < 0) + goto error1; + + /* sanitize Info for circular references */ + code = pdfi_sanitize_Info_references(ctx, Info); + if (code < 0) + goto error1; + + (void)pdfi_loop_detector_cleartomark(ctx); + pdfi_device_set_flags(ctx); pdfi_pdfmark_write_docinfo(ctx, Info); @@ -129,12 +337,19 @@ int pdfi_read_Info(pdf_context *ctx) */ ctx->Info = Info; return 0; + +error1: + pdfi_loop_detector_cleartomark(ctx); +error: + pdfi_countdown(Info); + return code; } int pdfi_read_Pages(pdf_context *ctx) { pdf_obj *o, *o1; - int code; + pdf_array *a = NULL; + int code, pagecount = 0; double d; if (ctx->args.pdfdebug) @@ -144,15 +359,15 @@ int pdfi_read_Pages(pdf_context *ctx) if (code < 0) return code; - if (o1->type == PDF_INDIRECT) { + if (pdfi_type_of(o1) == PDF_INDIRECT) { code = pdfi_dereference(ctx, ((pdf_indirect_ref *)o1)->ref_object_num, ((pdf_indirect_ref *)o1)->ref_generation_num, &o); pdfi_countdown(o1); if (code < 0) return code; - if (o->type != PDF_DICT) { + if (pdfi_type_of(o) != PDF_DICT) { pdfi_countdown(o); - if (o->type == PDF_INDIRECT) + if (pdfi_type_of(o) == PDF_INDIRECT) pdfi_set_error(ctx, 0, NULL, E_PDF_BADPAGEDICT, "pdfi_read_Pages", (char *)"*** Error: Something is wrong with the Pages dictionary. Giving up."); else pdfi_set_error(ctx, 0, NULL, E_PDF_BADPAGEDICT, "pdfi_read_Pages", (char *)"*** Error: Something is wrong with the Pages dictionary. Giving up.\n Double indirect reference. Loop in Pages tree?"); @@ -166,7 +381,7 @@ int pdfi_read_Pages(pdf_context *ctx) } o1 = o; } else { - if (o1->type != PDF_DICT) { + if (pdfi_type_of(o1) != PDF_DICT) { pdfi_countdown(o1); return_error(gs_error_typecheck); } @@ -212,6 +427,86 @@ int pdfi_read_Pages(pdf_context *ctx) ctx->num_pages = (int)floor(d); } + /* A simple confidence check in the value of Count. We only do this because + * the OSS-fuzz tool keeps on coming up with files that time out because the + * initial Count is insanely huge, and we spend much time trying to find + * millions of pages which don't exist. + */ + code = pdfi_dict_knownget_type(ctx, (pdf_dict *)o1, "Kids", PDF_ARRAY, (pdf_obj **)&a); + if (code == 0) + code = gs_note_error(gs_error_undefined); + if (code < 0) { + pdfi_countdown(o1); + return code; + } + + /* Firstly check if the Kids array has enough nodes, in which case it's + * probably flat (the common case) + */ + if (a->size != ctx->num_pages) { + int i = 0; + pdf_obj *p = NULL, *p1 = NULL; + pdf_num *c = NULL; + + /* Either its not a flat tree, or the top node /Count is incorrect. + * Get each entry in the Kids array in turn and total the /Count of + * each node and add any leaf nodes. + */ + for (i=0;i < a->size; i++) { + code = pdfi_array_get(ctx, a, i, &p); + if (code < 0) + continue; + if (pdfi_type_of(p) != PDF_DICT) { + pdfi_countdown(p); + p = NULL; + continue; + } + code = pdfi_dict_knownget_type(ctx, (pdf_dict *)p, "Type", PDF_NAME, (pdf_obj **)&p1); + if (code <= 0) { + pdfi_countdown(p); + p = NULL; + continue; + } + if (pdfi_name_is((pdf_name *)p1, "Page")) { + pagecount++; + } else { + if (pdfi_name_is((pdf_name *)p1, "Pages")) { + code = pdfi_dict_knownget(ctx, (pdf_dict *)p, "Count", (pdf_obj **)&c); + if (code >= 0) { + if (pdfi_type_of(c) == PDF_INT) + pagecount += c->value.i; + if (pdfi_type_of(c) == PDF_REAL) + pagecount += (int)c->value.d; + pdfi_countdown(c); + c = NULL; + } + } + } + pdfi_countdown(p1); + p1 = NULL; + pdfi_countdown(p); + p = NULL; + } + } else + pagecount = a->size; + + pdfi_countdown(a); + + /* If the count of the top level of the tree doesn't match the /Count + * of the root node then something is wrong. We could abort right now + * and will if this continues to be a problem, but initially let's assume + * the count of the top level is correct and the root node /Count is wrong. + * This will allow us to recover if only the root /Count gets corrupted. + * In future we could also try validating the entire tree at this point, + * though I suspect that's pointless; if the tree is corrupted we aren't + * likely to get much that's usable from it. + */ + if (pagecount != ctx->num_pages) { + ctx->num_pages = pagecount; + code = pdfi_dict_put_int(ctx, (pdf_dict *)o1, "Count", ctx->num_pages); + pdfi_set_error(ctx, 0, NULL, E_PDF_BADPAGECOUNT, "pdfi_read_Pages", NULL); + } + /* We don't pdfi_countdown(o1) now, because we've transferred our * reference to the pointer in the pdf_context structure. */ @@ -278,12 +573,12 @@ static int pdfi_get_child(pdf_context *ctx, pdf_array *Kids, int i, pdf_dict **p if (code < 0) goto errorExit; - if (node->type != PDF_INDIRECT && node->type != PDF_DICT) { + if (pdfi_type_of(node) != PDF_INDIRECT && pdfi_type_of(node) != PDF_DICT) { code = gs_note_error(gs_error_typecheck); goto errorExit; } - if (node->type == PDF_INDIRECT) { + if (pdfi_type_of(node) == PDF_INDIRECT) { code = pdfi_dereference(ctx, node->ref_object_num, node->ref_generation_num, (pdf_obj **)&child); if (code < 0) { @@ -295,7 +590,7 @@ static int pdfi_get_child(pdf_context *ctx, pdf_array *Kids, int i, pdf_dict **p if (code < 0) goto errorExit; } - if (child->type != PDF_DICT) { + if (pdfi_type_of(child) != PDF_DICT) { code = gs_note_error(gs_error_typecheck); goto errorExit; } @@ -352,6 +647,11 @@ static int pdfi_get_child(pdf_context *ctx, pdf_array *Kids, int i, pdf_dict **p code = gs_note_error(gs_error_circular_reference); goto errorExit; } + if (node->object_num > 0) { + code = pdfi_loop_detector_add_object(ctx, node->object_num); + if (code < 0) + goto errorExit; + } } child = (pdf_dict *)node; pdfi_countup(child); @@ -423,6 +723,10 @@ int pdfi_get_page_dict(pdf_context *ctx, pdf_dict *d, uint64_t page_num, uint64_ return code; pdfi_countup(inheritable); + code = pdfi_loop_detector_mark(ctx); + if (code < 0) + return code; + /* if we are being passed any inherited values from our parent, copy them now */ if (inherited != NULL) { code = pdfi_dict_copy(ctx, inheritable, inherited); @@ -543,6 +847,7 @@ int pdfi_get_page_dict(pdf_context *ctx, pdf_dict *d, uint64_t page_num, uint64_ code = 1; exit: + pdfi_loop_detector_cleartomark(ctx); pdfi_countdown(inheritable); pdfi_countdown(Kids); pdfi_countdown(child); @@ -599,12 +904,15 @@ int pdfi_find_resource(pdf_context *ctx, unsigned char *Type, pdf_name *name, { pdf_dict *typedict = NULL; pdf_dict *Parent = NULL; + pdf_name *n = NULL; int code; *o = NULL; /* Check the provided dict, stream_dict can be NULL if we are trying to find a Default* ColorSpace */ if (dict != NULL) { + bool deref_parent = true; + code = pdfi_resource_knownget_typedict(ctx, Type, dict, &typedict); if (code < 0) goto exit; @@ -615,30 +923,40 @@ int pdfi_find_resource(pdf_context *ctx, unsigned char *Type, pdf_name *name, } /* 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; + /* If the current dictionary is a Page dictionary, do NOT dereference it's Parent, as that + * will be the Pages tree, and we will end up with circular references, causing a memory leak. + */ + if (pdfi_dict_knownget_type(ctx, dict, "Type", PDF_NAME, (pdf_obj **)&n) > 0) { + if (pdfi_name_is(n, "Page")) + deref_parent = false; + pdfi_countdown(n); + } - code = pdfi_loop_detector_add_object(ctx, dict->object_num); - if (code < 0) { + if (deref_parent) { + code = pdfi_dict_knownget_type(ctx, dict, "Parent", PDF_DICT, (pdf_obj **)&Parent); + if (code < 0) + goto exit; + if (code > 0) { + if (ctx->page.CurrentPageDict != NULL && 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); - return code; + if (code != gs_error_undefined) + goto exit; } - 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; } @@ -788,6 +1106,9 @@ static int pdfi_doc_mark_outline(pdf_context *ctx, pdf_dict *outline) pdf_dict *child = NULL; pdf_dict *Next = NULL; + if (outline == (pdf_dict *)PDF_NULL_OBJ) + return 0; + /* Mark the outline */ /* NOTE: I think the pdfmark for this needs to be written before the children * because I think pdfwrite relies on the order of things. @@ -803,7 +1124,7 @@ static int pdfi_doc_mark_outline(pdf_context *ctx, pdf_dict *outline) /* Handle any 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) { + if (code < 0 || pdfi_type_of(child) != PDF_DICT) { /* TODO: flag a warning? */ code = 0; goto exit; @@ -829,7 +1150,7 @@ static int pdfi_doc_mark_outline(pdf_context *ctx, pdf_dict *outline) code = 0; goto exit; } - if (code < 0 || Next->type != PDF_DICT) + if (code < 0 || pdfi_type_of(Next) != PDF_DICT) goto exit; pdfi_countdown(child); @@ -869,12 +1190,15 @@ static int pdfi_doc_Outlines(pdf_context *ctx) /* Handle any children (don't deref them, we don't want to leave them hanging around) */ code = pdfi_dict_get_no_store_R(ctx, Outlines, "First", (pdf_obj **)&outline); - if (code < 0 || outline->type != PDF_DICT) { + if (code < 0 || pdfi_type_of(outline) != PDF_DICT) { /* TODO: flag a warning? */ code = 0; goto exit; } + if (pdfi_type_of(outline) != PDF_DICT) + goto exit; /* Exit with no error? */ + if (outline->object_num != 0) { code = pdfi_loop_detector_add_object(ctx, outline->object_num); if (code < 0) @@ -900,7 +1224,7 @@ static int pdfi_doc_Outlines(pdf_context *ctx) code = 0; goto exit; } - if (code < 0 || outline->type != PDF_DICT) + if (code < 0 || pdfi_type_of(Next) != PDF_DICT) goto exit; pdfi_countdown(outline); diff --git a/pdf/pdf_errors.h b/pdf/pdf_errors.h index e52a6c74..d62d6acb 100644 --- a/pdf/pdf_errors.h +++ b/pdf/pdf_errors.h @@ -24,6 +24,7 @@ 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_PREV_NOT_XREF_STREAM, "The /Prev entry in an XrefStm dictionary did not point to an XrefStm"), 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"), @@ -52,5 +53,11 @@ 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)."), - +PARAM(E_PDF_GROUP_NO_CS, "Group attributes dictionary is missing /CS"), +PARAM(E_BAD_GROUP_DICT, "Error retrieving Group dictionary for a page or XObject"), +PARAM(E_BAD_HALFTONE, "Error setting a halftone"), +PARAM(E_PDF_BADENCRYPT, "Encrypt diciotnary not a dictionary"), +PARAM(E_PDF_MISSINGTYPE, "A dictionary is missing a required /Type key."), +PARAM(E_PDF_NESTEDTOODEEP, "Dictionaries/arrays nested too deeply"), +PARAM(E_PDF_BADPAGECOUNT, "page tree root node /Count did not match the actual number of pages in the tree."), #undef PARAM diff --git a/pdf/pdf_fapi.c b/pdf/pdf_fapi.c index 3e4420c4..7fb7c954 100644 --- a/pdf/pdf_fapi.c +++ b/pdf/pdf_fapi.c @@ -35,12 +35,14 @@ #include "pdf_dict.h" #include "pdf_array.h" #include "pdf_font.h" -#include "pdf_font.h" #include "gscencs.h" #include "gsagl.h" #include "gxfont1.h" /* for gs_font_type1_s */ #include "gscrypt1.h" /* for crypt_c1 */ +extern single_glyph_list_t SingleGlyphList[]; + + /* forward declarations for the pdfi_ff_stub definition */ static int pdfi_fapi_get_word(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index, unsigned short *ret); @@ -70,6 +72,9 @@ static int pdfi_fapi_serialize_tt_font(gs_fapi_font * ff, void *buf, int buf_size); static int +pdfi_fapi_retrieve_tt_font(gs_fapi_font * ff, void **buf, int *buf_size); + +static int pdfi_fapi_get_charstring(gs_fapi_font *ff, int index, byte *buf, ushort buf_length); static int @@ -130,6 +135,7 @@ static const gs_fapi_font pdfi_ff_stub = { pdfi_fapi_get_raw_subr, /* get_raw_subr */ pdfi_fapi_get_glyph, /* get_glyph */ pdfi_fapi_serialize_tt_font, /* serialize_tt_font */ + pdfi_fapi_retrieve_tt_font, /* retrieve_tt_font */ pdfi_fapi_get_charstring, /* get_charstring */ pdfi_fapi_get_charstring_name, /* get_charstring_name */ pdfi_get_glyphdirectory_data, /* get_GlyphDirectory_data_ptr */ @@ -248,7 +254,7 @@ pdfi_fapi_get_word(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index, uns { if (pfont->FontType == ft_encrypted2) { pdf_font_cff *pdffont2 = (pdf_font_cff *)pfont->client_data; - *ret = pdffont2->NumGlobalSubrs; + *ret = pdffont2->GlobalSubrs == NULL ? 0 : pdfi_array_size(pdffont2->GlobalSubrs); } else { *ret = 0; @@ -260,11 +266,11 @@ pdfi_fapi_get_word(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index, uns { if (pfont->FontType == ft_encrypted) { pdf_font_type1 *pdffont1 = (pdf_font_type1 *)pfont->client_data; - *ret = pdffont1->NumSubrs; + *ret = pdffont1->Subrs == NULL ? 0 : pdfi_array_size(pdffont1->Subrs); } else if (pfont->FontType == ft_encrypted2) { pdf_font_cff *pdffont2 = (pdf_font_cff *)pfont->client_data; - *ret = pdffont2->NumSubrs; + *ret = pdffont2->Subrs == NULL ? 0 : pdfi_array_size(pdffont2->Subrs); } else { *ret = 0; @@ -395,7 +401,7 @@ pdfi_fapi_get_word(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index, uns pdf_font_type1 *pdffont1 = (pdf_font_type1 *)pfont->client_data; if (pdffont1->blendfontbbox != NULL) { pdf_array *suba; - pdf_num *v; + double d; int ind, aind; ind = index % 4; aind = (index - ind) / 4; @@ -404,17 +410,13 @@ pdfi_fapi_get_word(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index, uns *ret = 0; break; } - code = pdfi_array_get(pdffont1->ctx, suba, ind, (pdf_obj **)&v); + code = pdfi_array_get_number(pdffont1->ctx, suba, ind, &d); pdfi_countdown(suba); if (code < 0) { *ret = 0; break; } - if (v->type == PDF_INT) - *ret = (unsigned short)v->value.i; - else - *ret = (unsigned short)v->value.d; - pdfi_countdown(v); + *ret = (unsigned short)d; } else { *ret = 0; @@ -447,8 +449,13 @@ pdfi_fapi_get_long(gs_fapi_font * ff, gs_fapi_font_feature var_id, int index, un pdf_font_type1 *pdffont1 = (pdf_font_type1 *)pfont->client_data; int i; *ret = 0; - for (i = 0; i < pdffont1->NumSubrs; i++) { - *ret += pdffont1->Subrs[i].size; + for (i = 0; i < (pdffont1->Subrs == NULL ? 0 : pdfi_array_size(pdffont1->Subrs)); i++) { + pdf_string *subr_str = NULL; + code = pdfi_array_get_type(pdffont1->ctx, pdffont1->Subrs, i, PDF_STRING, (pdf_obj **)&subr_str); + if (code >= 0) { + *ret += subr_str->length; + } + pdfi_countdown(subr_str); } } } @@ -520,7 +527,7 @@ pdfi_fapi_get_float(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index, fl { int array_index, subind; pdf_array *suba; - pdf_num *v; + double d; pdf_font_type1 *pdffont1 = (pdf_font_type1 *)pbfont->client_data; *ret = 0; @@ -543,19 +550,13 @@ pdfi_fapi_get_float(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index, fl break; } - code = pdfi_array_get(pdffont1->ctx, suba, subind, (pdf_obj**)&v); + code = pdfi_array_get_number(pdffont1->ctx, suba, subind, &d); pdfi_countdown(suba); if (code < 0) { code = 0; break; } - if (v->type == PDF_INT) { - *ret = (float)v->value.i; - } - else { - *ret = (float)v->value.d; - } - pdfi_countdown(v); + *ret = (float)d; } break; case gs_fapi_font_feature_BlendDesignMapArrayValue: @@ -580,15 +581,11 @@ pdfi_fapi_get_float(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index, fl pdfi doesn't, hence the multiplications by 64. */ if ((i * 64) + (j * 64) + k == index) { - pdf_num *n; - code = pdfi_array_get(pdffont1->ctx, suba, i, (pdf_obj **)&n); + double d; + code = pdfi_array_get_number(pdffont1->ctx, suba, i, &d); if (code < 0) continue; - if (n->type == PDF_INT) - *ret = (float)n->value.i; - else - *ret = (float)n->value.d; - pdfi_countdown(n); + *ret = (float)d; pdfi_countdown(subsuba); pdfi_countdown(suba); goto gotit; @@ -683,7 +680,7 @@ pdfi_fapi_get_gsubr(gs_fapi_font *ff, int index, byte *buf, int buf_length) int code = 0; if (pfont->FontType == ft_encrypted2) { pdf_font_cff *pdffont2 = (pdf_font_cff *)pfont->client_data; - if (index > pdffont2->NumGlobalSubrs) { + if (index > (pdffont2->GlobalSubrs == NULL ? 0 : pdfi_array_size(pdffont2->GlobalSubrs))) { code = gs_error_rangecheck; } else { @@ -692,7 +689,7 @@ pdfi_fapi_get_gsubr(gs_fapi_font *ff, int index, byte *buf, int buf_length) code = pdfi_array_get(pdffont2->ctx, pdffont2->GlobalSubrs, index, (pdf_obj **)&subrstring); if (code >= 0) { - if (subrstring->type == PDF_STRING) { + if (pdfi_type_of(subrstring) == PDF_STRING) { code = subrstring->length - leniv; if (buf && buf_length >= code) { if (ff->need_decrypt && pfont->data.lenIV >= 0) { @@ -724,27 +721,35 @@ pdfi_fapi_get_subr(gs_fapi_font *ff, int index, byte *buf, int buf_length) if (pfont->FontType == ft_encrypted) { pdf_font_type1 *pdffont1 = (pdf_font_type1 *)pfont->client_data; - if (index > pdffont1->NumSubrs) { + if (index > (pdffont1->Subrs == NULL ? 0 : pdfi_array_size(pdffont1->Subrs))) { code = gs_note_error(gs_error_rangecheck); } else { int leniv = (pfont->data.lenIV > 0 ? pfont->data.lenIV : 0); - if (pdffont1->Subrs[index].size > 0) { - code = pdffont1->Subrs[index].size - leniv; + pdf_string *subr_str = NULL; + + code = pdfi_array_get_type(pdffont1->ctx, pdffont1->Subrs, index, PDF_STRING, (pdf_obj **)&subr_str); + if (code >= 0) { + code = subr_str->length - leniv; if (buf && buf_length >= code) { if (ff->need_decrypt && pfont->data.lenIV >= 0) { - decode_bytes(buf, pdffont1->Subrs[index].data, code + leniv, pfont->data.lenIV); + decode_bytes(buf, subr_str->data, code + leniv, pfont->data.lenIV); } else { - memcpy(buf, pdffont1->Subrs[index].data, code); + memcpy(buf, subr_str->data, code); } } } + else { + /* Ignore invalid or missing subrs */ + code = 0; + } + pdfi_countdown(subr_str); } } else if (pfont->FontType == ft_encrypted2) { pdf_font_cff *pdffont2 = (pdf_font_cff *)pfont->client_data; - if (index > pdffont2->NumSubrs) { + if (index > (pdffont2->Subrs == NULL ? 0 : pdfi_array_size(pdffont2->Subrs))) { code = gs_error_rangecheck; } else { @@ -756,7 +761,7 @@ pdfi_fapi_get_subr(gs_fapi_font *ff, int index, byte *buf, int buf_length) else code = pdfi_array_get(pdffont2->ctx, pdffont2->Subrs, index, (pdf_obj **)&subrstring); if (code >= 0) { - if (subrstring->type == PDF_STRING) { + if (pdfi_type_of(subrstring) == PDF_STRING) { if (subrstring->length > 0) { code = subrstring->length - leniv; if (buf && buf_length >= code) { @@ -790,14 +795,23 @@ pdfi_fapi_get_raw_subr(gs_fapi_font *ff, int index, byte *buf, int buf_length) if (pfont->FontType == ft_encrypted) { pdf_font_type1 *pdffont1 = (pdf_font_type1 *)pfont->client_data; - if (index > pdffont1->NumSubrs) { + if (index > (pdffont1->Subrs == NULL ? 0 : pdfi_array_size(pdffont1->Subrs))) { code = gs_error_rangecheck; } else { - code = pdffont1->Subrs[index].size; - if (buf && buf_length >= code) { - memcpy(buf, pdffont1->Subrs[index].data, code); + pdf_string *subr_str = NULL; + code = pdfi_array_get_type(pdffont1->ctx, pdffont1->Subrs, index, PDF_STRING, (pdf_obj **)&subr_str); + if (code >= 0) { + code = subr_str->length; + if (buf && buf_length >= code) { + memcpy(buf, subr_str->data, code); + } + } + else { + /* Ignore missing or invalid subrs */ + code = 0; } + pdfi_countdown(subr_str); } } return code; @@ -815,8 +829,6 @@ pdfi_fapi_get_charstring_name(gs_fapi_font *ff, int index, byte *buf, ushort buf return 0; } -extern single_glyph_list_t SingleGlyphList[]; - static int pdfi_fapi_get_glyphname_or_cid(gs_text_enum_t *penum, gs_font_base * pbfont, gs_string * charstring, gs_string * name, gs_glyph ccode, gs_string * enc_char_name, @@ -835,8 +847,8 @@ pdfi_fapi_get_glyphname_or_cid(gs_text_enum_t *penum, gs_font_base * pbfont, gs_ if (pttfont->substitute == false) { gid = ccode; - if (pttfont->cidtogidmap.size > (ccode << 1) + 1) { - gid = pttfont->cidtogidmap.data[ccode << 1] << 8 | pttfont->cidtogidmap.data[(ccode << 1) + 1]; + if (pttfont->cidtogidmap != NULL && pttfont->cidtogidmap->length > (ccode << 1) + 1) { + gid = pttfont->cidtogidmap->data[ccode << 1] << 8 | pttfont->cidtogidmap->data[(ccode << 1) + 1]; } cr->client_char_code = ccode; cr->char_codes[0] = gid; @@ -933,8 +945,9 @@ pdfi_fapi_get_glyphname_or_cid(gs_text_enum_t *penum, gs_font_base * pbfont, gs_ code = (*ctx->get_glyph_name)((gs_font *)pbfont, ccode, &gname); if (code >= 0) { code = pdfi_name_alloc(ctx, (byte *) gname.data, gname.size, (pdf_obj **) &glyphname); - if (code >= 0) - pdfi_countup(glyphname); + if (code < 0) + return code; + pdfi_countup(glyphname); } if (code < 0) { @@ -942,10 +955,12 @@ pdfi_fapi_get_glyphname_or_cid(gs_text_enum_t *penum, gs_font_base * pbfont, gs_ return code; } 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 **)&charstr); + code = pdfi_map_glyph_name_via_agl(cfffont->CharStrings, glyphname, &charstr); + if (code < 0) + code = pdfi_dict_get(cfffont->ctx, cfffont->CharStrings, ".notdef", (pdf_obj **)&charstr); } + pdfi_countdown(glyphname); if (code < 0) return code; @@ -1121,7 +1136,6 @@ pdfi_fapi_get_glyph(gs_fapi_font * ff, gs_glyph char_code, byte * buf, int buf_l pdf_name *encn; int cstrlen = 0; - /* This should only get called for Postscript-type fonts */ if (ff->is_type1) { if (pbfont->FontType == ft_encrypted) { @@ -1137,14 +1151,18 @@ pdfi_fapi_get_glyph(gs_fapi_font * ff, gs_glyph char_code, byte * buf, int buf_l return code; pdfi_countup(glyphname); code = pdfi_dict_get_by_key(pdffont1->ctx, pdffont1->CharStrings, glyphname, (pdf_obj **)&charstring); - pdfi_countdown(glyphname); if (code < 0) { - code = pdfi_dict_get(pdffont1->ctx, pdffont1->CharStrings, ".notdef", (pdf_obj **)&charstring); + code = pdfi_map_glyph_name_via_agl(pdffont1->CharStrings, glyphname, &charstring); if (code < 0) { - code = gs_note_error(gs_error_invalidfont); - goto done; + code = pdfi_dict_get(pdffont1->ctx, pdffont1->CharStrings, ".notdef", (pdf_obj **)&charstring); + if (code < 0) { + pdfi_countdown(glyphname); + code = gs_note_error(gs_error_invalidfont); + goto done; + } } } + pdfi_countdown(glyphname); cstrlen = charstring->length - leniv; if (buf != NULL && cstrlen <= buf_length) { if (ff->need_decrypt && pfont1->data.lenIV >= 0) @@ -1261,7 +1279,16 @@ pdfi_fapi_get_glyph(gs_fapi_font * ff, gs_glyph char_code, byte * buf, int buf_l } } else { - code = gs_error_invalidaccess; + gs_font_type42 *pfont42 = (gs_font_type42 *)pbfont; + gs_glyph_data_t pgd; + + code = pfont42->data.get_outline(pfont42, char_code, &pgd); + cstrlen = pgd.bits.size; + if (code >= 0) { + if (buf && buf_length >= cstrlen) { + memcpy(buf, pgd.bits.data, cstrlen); + } + } } done: if (code < 0) @@ -1277,6 +1304,19 @@ pdfi_fapi_serialize_tt_font(gs_fapi_font * ff, void *buf, int buf_size) } static int +pdfi_fapi_retrieve_tt_font(gs_fapi_font * ff, void **buf, int *buf_size) +{ + gs_font_type42 *pfonttt = (gs_font_type42 *) ff->client_font_data; + pdf_font_truetype *pdfttf = (pdf_font_truetype *)pfonttt->client_data; + + *buf = pdfttf->sfnt->data; + *buf_size = pdfttf->sfnt->length; + + return 0; +} + + +static int pdfi_get_glyphdirectory_data(gs_fapi_font * ff, int char_code, const byte ** ptr) { @@ -1439,6 +1479,7 @@ pdfi_fapi_passfont(pdf_font *font, int subfont, char *fapi_request, char *fapi_id = NULL; int code = 0; gs_string fdata; + gs_string *fdatap = &fdata; gs_font_base *pbfont = (gs_font_base *)font->pfont; gs_fapi_font local_pdf_ff_stub = pdfi_ff_stub; gs_fapi_ttf_cmap_request symbolic_req[GS_FAPI_NUM_TTF_CMAP_REQ] = {{3, 0}, {1, 0}, {3, 1}, {3, 10}, {-1, -1}}; @@ -1450,16 +1491,15 @@ pdfi_fapi_passfont(pdf_font *font, int subfont, char *fapi_request, } if (font->pdfi_font_type == e_pdf_font_truetype) { - fdata.data = ((pdf_font_truetype *)font)->sfnt.data; - fdata.size = ((pdf_font_truetype *)font)->sfnt.size; + fdatap = NULL; } else if (font->pdfi_font_type == e_pdf_cidfont_type2) { - fdata.data = ((pdf_cidfont_type2 *)font)->sfnt.data; - fdata.size = ((pdf_cidfont_type2 *)font)->sfnt.size; + fdatap->data = ((pdf_cidfont_type2 *)font)->sfnt->data; + fdatap->size = ((pdf_cidfont_type2 *)font)->sfnt->length; } else { - fdata.data = font_data; - fdata.size = font_data_len; + fdatap->data = font_data; + fdatap->size = font_data_len; } if (font->pdfi_font_type == e_pdf_font_truetype) { @@ -1476,7 +1516,7 @@ pdfi_fapi_passfont(pdf_font *font, int subfont, char *fapi_request, (gs_font *)pbfont); code = - gs_fapi_passfont((gs_font *)pbfont, subfont, (char *)file_name, &fdata, + gs_fapi_passfont((gs_font *)pbfont, subfont, (char *)file_name, fdatap, (char *)fapi_request, NULL, (char **)&fapi_id, (gs_fapi_get_server_param_callback) pdfi_get_server_param); diff --git a/pdf/pdf_file.c b/pdf/pdf_file.c index 5698866e..7f06a046 100644 --- a/pdf/pdf_file.c +++ b/pdf/pdf_file.c @@ -334,7 +334,7 @@ static int pdfi_Flate_filter(pdf_context *ctx, pdf_dict *d, stream *source, stre (*new_stream)->strm = source; source = *new_stream; - if (d && d->type == PDF_DICT) { + if (d && pdfi_type_of(d) == PDF_DICT) { Flate_source = (*new_stream)->strm; code = pdfi_Predictor_filter(ctx, d, source, new_stream); if (code < 0) @@ -403,7 +403,7 @@ static int pdfi_LZW_filter(pdf_context *ctx, pdf_dict *d, stream *source, stream /* s_zlibD_template defined in base/szlibd.c */ s_LZW_set_defaults_inline(&lzs); - if (d && d->type == PDF_DICT) { + if (d && pdfi_type_of(d) == PDF_DICT) { code = pdfi_dict_get_int(ctx, d, "EarlyChange", &i); if (code < 0 && code != gs_error_undefined) return code; @@ -421,7 +421,7 @@ static int pdfi_LZW_filter(pdf_context *ctx, pdf_dict *d, stream *source, stream (*new_stream)->strm = source; source = *new_stream; - if (d && d->type == PDF_DICT) + if (d && pdfi_type_of(d) == PDF_DICT) pdfi_Predictor_filter(ctx, d, source, new_stream); return 0; } @@ -473,21 +473,25 @@ pdfi_JPX_filter(pdf_context *ctx, pdf_dict *dict, pdf_dict *decode, } if (dict && pdfi_dict_get(ctx, dict, "ColorSpace", &csobj) == 0) { /* parse the value */ - if (csobj->type == PDF_ARRAY) { + switch (pdfi_type_of(csobj)) { + case PDF_ARRAY: /* assume it's the first array element */ code = pdfi_array_get(ctx, (pdf_array *)csobj, (uint64_t)0, (pdf_obj **)&csname); if (code < 0) { pdfi_countdown(csobj); return code; } - } else if (csobj->type == PDF_NAME) { + break; + case PDF_NAME: /* use the name directly */ csname = (pdf_name *)csobj; csobj = NULL; /* To keep ref counting straight */ - } else { + break; + default: dmprintf(ctx->memory, "warning: JPX ColorSpace value is an unhandled type!\n"); + break; } - if (csname != NULL && csname->type == PDF_NAME) { + if (csname != NULL && pdfi_type_of(csname) == PDF_NAME) { /* request raw index values if the colorspace is /Indexed */ if (pdfi_name_is(csname, "Indexed")) state.colorspace = gs_jpx_cs_indexed; @@ -613,7 +617,7 @@ static int pdfi_DCT_filter(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *de return code; } - if (decode && decode->type == PDF_DICT) { + if (decode && pdfi_type_of(decode) == PDF_DICT) { /* TODO: Why is this here? 'i' never gets used? */ code = pdfi_dict_get_int(ctx, decode, "ColorTransform", &i); if (code < 0 && code != gs_error_undefined) @@ -674,7 +678,7 @@ static int pdfi_CCITTFax_filter(pdf_context *ctx, pdf_dict *d, stream *source, s s_CF_set_defaults_inline(&ss); - if (d && d->type == PDF_DICT) { + if (d && pdfi_type_of(d) == PDF_DICT) { code = pdfi_dict_get_int(ctx, d, "K", &i); if (code < 0 && code != gs_error_undefined) return code; @@ -922,12 +926,11 @@ int pdfi_filter_no_decryption(pdf_context *ctx, pdf_stream *stream_obj, goto exit; } - if (Filter->type != PDF_ARRAY && Filter->type != PDF_NAME) { + switch (pdfi_type_of(Filter)) { + default: code = gs_note_error(gs_error_typecheck); goto exit; - } - - if (Filter->type == PDF_NAME) { + case PDF_NAME: code = pdfi_dict_knownget(ctx, stream_dict, "DecodeParms", &decode); if (code == 0 && inline_image) code = pdfi_dict_knownget(ctx, stream_dict, "DP", &decode); @@ -940,7 +943,9 @@ int pdfi_filter_no_decryption(pdf_context *ctx, pdf_stream *stream_obj, goto exit; code = pdfi_alloc_stream(ctx, new_s, source->s, new_stream); - } else { + break; + case PDF_ARRAY: + { pdf_array *filter_array = (pdf_array *)Filter; code = pdfi_dict_knownget_type(ctx, stream_dict, "DecodeParms", PDF_ARRAY, (pdf_obj **)&DecodeParams); @@ -1003,11 +1008,15 @@ int pdfi_filter_no_decryption(pdf_context *ctx, pdf_stream *stream_obj, goto error; } } - if (decode && decode->type != PDF_NULL && decode->type != PDF_DICT) { + if (decode && decode != PDF_NULL_OBJ && pdfi_type_of(decode) != PDF_DICT) { pdfi_countdown(decode); decode = NULL; pdfi_set_warning(ctx, 0, NULL, W_PDF_STREAM_BAD_DECODEPARMS, "pdfi_filter_no_decryption", NULL); } + if (decode && decode == PDF_NULL_OBJ) { + pdfi_countdown(decode); + decode = NULL; + } code = pdfi_apply_filter(ctx, stream_dict, (pdf_name *)o, (pdf_dict *)decode, s, &new_s, inline_image); @@ -1022,6 +1031,7 @@ int pdfi_filter_no_decryption(pdf_context *ctx, pdf_stream *stream_obj, } code = pdfi_alloc_stream(ctx, s, source->s, new_stream); } + } exit: pdfi_countdown(o); @@ -1050,6 +1060,9 @@ int pdfi_filter(pdf_context *ctx, pdf_stream *stream_obj, pdf_c_stream *source, pdf_c_stream *crypt_stream = NULL, *SubFile_stream = NULL; pdf_string *StreamKey = NULL; pdf_dict *stream_dict = NULL; + pdf_obj *FileSpec = NULL; + pdf_stream *NewStream = NULL; + bool known = false; *new_stream = NULL; @@ -1057,6 +1070,98 @@ int pdfi_filter(pdf_context *ctx, pdf_stream *stream_obj, pdf_c_stream *source, if (code < 0) goto error; + /* Horrifyingly, any stream dictionary can contain a file specification, which means that + * instead of using the stream from the PDF file we must use an external file. + * So much for portability! + * Note: We must not do this for inline images as an inline image dictionary can + * contain the abbreviation /F for the Filter, and an inline image is never a + * separate stream, it is (obviously) contained in the current stream. + */ + if (!inline_image) { + code = pdfi_dict_known(ctx, stream_dict, "F", &known); + if (code >= 0 && known) { + pdf_obj *FS = NULL, *o = NULL; + pdf_dict *dict = NULL; + stream *gstream = NULL; + + code = pdfi_dict_get(ctx, stream_dict, "F", &FileSpec); + if (code < 0) + goto error; + if (pdfi_type_of(FileSpec) == PDF_DICT) { + /* We don't really support FileSpec dictionaries, partly because we + * don't really know which platform to use. If there is a /F string + * then we will use that, just as if we had been given a string in + * the first place. + */ + code = pdfi_dict_knownget(ctx, (pdf_dict *)FileSpec, "F", &FS); + if (code < 0) { + goto error; + } + pdfi_countdown(FileSpec); + FileSpec = FS; + FS = NULL; + } + if (pdfi_type_of(FileSpec) != PDF_STRING) { + code = gs_note_error(gs_error_typecheck); + goto error; + } + /* We should now have a string with the filename (or URL). We need + * to open the file and create a stream, if that succeeds. + */ + gstream = sfopen((const char *)((pdf_string *)FileSpec)->data, "r", ctx->memory); + if (gstream == NULL) { + emprintf1(ctx->memory, "Failed to open file %s\n", (const char *)((pdf_string *)FileSpec)->data); + code = gs_note_error(gs_error_ioerror); + goto error; + } + + source = (pdf_c_stream *)gs_alloc_bytes(ctx->memory, sizeof(pdf_c_stream), "external stream"); + if (source == NULL) { + code = gs_note_error(gs_error_VMerror); + goto error; + } + memset(source, 0x00, sizeof(pdf_c_stream)); + source->s = gstream; + + code = pdfi_object_alloc(ctx, PDF_STREAM, 0, (pdf_obj **)&NewStream); + if (code < 0) + goto error; + pdfi_countup(NewStream); + code = pdfi_dict_alloc(ctx, 32, &dict); + if (code < 0){ + pdfi_countdown(NewStream); + goto error; + } + pdfi_countup(dict); + NewStream->stream_dict = dict; + code = pdfi_dict_get(ctx, stream_dict, "FFilter", &o); + if (code >= 0) { + code = pdfi_dict_put(ctx, NewStream->stream_dict, "Filter", o); + if (code < 0) { + pdfi_countdown(NewStream); + goto error; + } + } + code = pdfi_dict_get(ctx, stream_dict, "FPredictor", &o); + if (code >= 0) { + code = pdfi_dict_put(ctx, NewStream->stream_dict, "Predictor", o); + if (code < 0) { + pdfi_countdown(NewStream); + goto error; + } + } + pdfi_countup(NewStream->stream_dict); + NewStream->stream_offset = 0; + NewStream->Length = 0; + NewStream->length_valid = 0; + NewStream->stream_written = 0; + NewStream->is_marking = 0; + NewStream->parent_obj = NULL; + stream_obj = NewStream; + stream_dict = NewStream->stream_dict; + } + } + /* If the file isn't encrypted, don't apply encryption. If this is an inline * image then its in a content stream and will already be decrypted, so don't * apply decryption again. @@ -1143,7 +1248,9 @@ int pdfi_filter(pdf_context *ctx, pdf_stream *stream_obj, pdf_c_stream *source, code = pdfi_filter_no_decryption(ctx, stream_obj, source, new_stream, inline_image); } error: + pdfi_countdown(NewStream); pdfi_countdown(StreamKey); + pdfi_countdown(FileSpec); return code; } diff --git a/pdf/pdf_fmap.c b/pdf/pdf_fmap.c index ee05af1c..00034594 100644 --- a/pdf/pdf_fmap.c +++ b/pdf/pdf_fmap.c @@ -728,11 +728,11 @@ static int pdfi_generate_native_fontmap(pdf_context *ctx) (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) { + if (pdfi_type_of(v) == 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) + if (code >= 0 && pdfi_type_of(n) == PDF_INT) find = n->value.i; else code = 0; @@ -758,11 +758,11 @@ static int pdfi_generate_native_fontmap(pdf_context *ctx) (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) { + if (pdfi_type_of(v) == 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) + if (code >= 0 && pdfi_type_of(n) == PDF_INT) find = n->value.i; else code = 0; @@ -814,48 +814,56 @@ pdf_fontmap_lookup_font(pdf_context *ctx, pdf_name *fname, pdf_obj **mapname, in 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) { + 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; + if (code >= 0) { + if (pdfi_type_of(record) == PDF_STRING) { + mname = record; + } + else if (pdfi_type_of(record) == PDF_DICT) { + int64_t i64; + code = pdfi_dict_get(ctx, (pdf_dict *)record, "Path", &mname); + if (code >= 0) + code = pdfi_dict_get_int(ctx, (pdf_dict *)record, "Index", &i64); + if (code < 0) { + pdfi_countdown(record); + return code; + } + *findex = (int)i64; /* Rangecheck? */ } - code = pdfi_dict_get(ctx, (pdf_dict *)record, "Index", (pdf_obj **)&ind); - if (code >= 0 && ind->type == PDF_INT) { - *findex = ind->value.i; + else { + pdfi_countdown(record); + code = gs_error_undefined; } } } + else { + code = gs_error_undefined; + } - if (mname != NULL && mname->type == PDF_STRING && pdfi_fmap_file_exists(ctx, (pdf_string *)mname)) { + if (code < 0) { + 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; + } + } + } + if (mname != NULL && pdfi_type_of(mname) == 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" */ + else if (mname != NULL && pdfi_type_of(mname) == PDF_NAME) { /* If we map to a name, we assume (for now) we have the font as a "built-in" */ *mapname = mname; code = 0; } @@ -881,9 +889,9 @@ pdf_fontmap_lookup_cidfont(pdf_context *ctx, pdf_dict *font_dict, pdf_name *name return code; } } - if (name == NULL || name->type != PDF_NAME) { + if (name == NULL || pdfi_type_of(name) != PDF_NAME) { code = pdfi_dict_get(ctx, font_dict, "BaseFont", &cidname); - if (code < 0 || cidname->type != PDF_NAME) { + if (code < 0 || pdfi_type_of(cidname) != PDF_NAME) { pdfi_countdown(cidname); return_error(gs_error_undefined); } @@ -910,19 +918,18 @@ pdf_fontmap_lookup_cidfont(pdf_context *ctx, pdf_dict *font_dict, pdf_name *name pdfi_countdown(mname); mname = mname2; } - if (mname->type == PDF_DICT) { + if (pdfi_type_of(mname) == 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; + int64_t i64; 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) { + if (code < 0 || pdfi_type_of(filetype) != PDF_NAME || filetype->length != 8 || memcmp(filetype->data, "TrueType", 8) != 0) { pdfi_countdown(filetype); pdfi_countdown(rec); return_error(gs_error_undefined); @@ -930,21 +937,21 @@ pdf_fontmap_lookup_cidfont(pdf_context *ctx, pdf_dict *font_dict, pdf_name *name pdfi_countdown(filetype); code = pdfi_dict_get(ctx, rec, "CSI", (pdf_obj **)&mcsi); - if (code < 0 || mcsi->type != PDF_ARRAY) { + if (code < 0 || pdfi_type_of(mcsi) != 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) { + if (code < 0 || pdfi_type_of(ocsi) != 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) { + if (code < 0 || pdfi_type_of(ord1) != PDF_STRING) { pdfi_countdown(ord1); pdfi_countdown(ocsi); pdfi_countdown(mcsi); @@ -952,7 +959,7 @@ pdf_fontmap_lookup_cidfont(pdf_context *ctx, pdf_dict *font_dict, pdf_name *name return_error(gs_error_undefined); } code = pdfi_array_get(ctx, mcsi, 0, (pdf_obj **)&ord2); - if (code < 0 || ord2->type != PDF_STRING) { + if (code < 0 || pdfi_type_of(ord2) != PDF_STRING) { pdfi_countdown(ord1); pdfi_countdown(ord2); pdfi_countdown(ocsi); @@ -970,43 +977,16 @@ pdf_fontmap_lookup_cidfont(pdf_context *ctx, pdf_dict *font_dict, pdf_name *name } 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)) { + if (code < 0 || pdfi_type_of(path) != 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 = pdfi_dict_get_int(ctx, rec, "Index", &i64); + *findex = (code < 0) ? 0 : (int)i64; /* rangecheck? */ code = 0; } diff --git a/pdf/pdf_font.c b/pdf/pdf_font.c index fa716056..28ebe96e 100644 --- a/pdf/pdf_font.c +++ b/pdf/pdf_font.c @@ -34,11 +34,14 @@ #include "pdf_font0.h" #include "pdf_fmap.h" #include "gscencs.h" /* For gs_c_known_encode and gs_c_glyph_name */ +#include "gsagl.h" #include "strmio.h" #include "stream.h" #include "gsstate.h" /* For gs_setPDFfontsize() */ +extern single_glyph_list_t SingleGlyphList[]; + static int pdfi_gs_setfont(pdf_context *ctx, gs_font *pfont) { int code = 0; @@ -78,7 +81,7 @@ bool pdfi_font_known_symbolic(pdf_obj *basefont) int i; pdf_name *nm = (pdf_name *)basefont; - if (basefont != NULL && basefont->type == PDF_NAME) { + if (basefont != NULL && pdfi_type_of(basefont) == PDF_NAME) { for (i = 0; known_symbolic_font_names[i].name != NULL; i++) { if (nm->length == known_symbolic_font_names[i].namelen && !strncmp((char *)nm->data, known_symbolic_font_names[i].name, nm->length)) { @@ -104,7 +107,7 @@ pdfi_font_match_glyph_widths(pdf_font *pdfont) /* For "best" results, restrict to what we *hope* are A-Z,a-z */ sindex = pdfont->FirstChar < 96 ? 96 : pdfont->FirstChar; - lindex = pdfont->LastChar > 122 ? 122 : pdfont->LastChar; + lindex = pdfont->LastChar > 122 ? 123 : pdfont->LastChar + 1; for (i = sindex; i < lindex; i++) { gs_glyph_info_t ginfo = {0}; @@ -156,8 +159,14 @@ static void pdfi_print_font_name(pdf_context *ctx, pdf_name *n) (void)outwrite(ctx->memory, (const char *)n->data, n->length); } +static void pdfi_print_font_string(pdf_context *ctx, pdf_string *s) +{ + if (ctx->args.QUIET != true) + (void)outwrite(ctx->memory, (const char *)s->data, s->length); +} + /* Print a null terminated string to stdout */ -static void pdfi_print_string(pdf_context *ctx, const char *str) +static void pdfi_print_cstring(pdf_context *ctx, const char *str) { if (ctx->args.QUIET != true) (void)outwrite(ctx->memory, str, strlen(str)); @@ -169,7 +178,7 @@ static void pdfi_print_string(pdf_context *ctx, const char *str) 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, int *findex) +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 = 0; char fontfname[gp_file_name_sizeof]; @@ -184,12 +193,12 @@ pdfi_open_CIDFont_substitute_file(pdf_context * ctx, pdf_dict *font_dict, pdf_di pdf_dict *csi = NULL; code = pdfi_dict_get(ctx, font_dict, "CIDSystemInfo", (pdf_obj **)&csi); - if (code >= 0 && csi->type == PDF_DICT) { + if (code >= 0 && pdfi_type_of(csi) == 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 + && pdfi_type_of(csi_reg) == PDF_STRING && pdfi_type_of(csi_ord) == 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); @@ -209,10 +218,10 @@ pdfi_open_CIDFont_substitute_file(pdf_context * ctx, pdf_dict *font_dict, pdf_di } pdfi_countdown(csi); - if (mname == NULL || mname->type != PDF_STRING) + if (mname == NULL || pdfi_type_of(mname) != PDF_STRING) code = pdf_fontmap_lookup_cidfont(ctx, font_dict, NULL, (pdf_obj **)&mname, findex); - if (code < 0 || mname->type != PDF_STRING) { + if (code < 0 || pdfi_type_of(mname) != PDF_STRING) { const char *fsprefix = "CIDFSubst/"; int fsprefixlen = strlen(fsprefix); const char *defcidfallack = "DroidSansFallback.ttf"; @@ -224,26 +233,26 @@ pdfi_open_CIDFont_substitute_file(pdf_context * ctx, pdf_dict *font_dict, pdf_di code = gs_note_error(gs_error_invalidfont); } else { - if (ctx->args.cidsubstpath.data == NULL) { + if (ctx->args.cidfsubstpath.data == NULL) { memcpy(fontfname, fsprefix, fsprefixlen); } else { - memcpy(fontfname, ctx->args.cidsubstpath.data, ctx->args.cidsubstpath.size); - fsprefixlen = ctx->args.cidsubstpath.size; + memcpy(fontfname, ctx->args.cidfsubstpath.data, ctx->args.cidfsubstpath.size); + fsprefixlen = ctx->args.cidfsubstpath.size; } - if (ctx->args.cidsubstfont.data == NULL) { + if (ctx->args.cidfsubstfont.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); + if (gp_getenv("CIDFSUBSTFONT", (char *)0, &len) < 0 && len + fsprefixlen + 1 < gp_file_name_sizeof) { + (void)gp_getenv("CIDFSUBSTFONT", (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; + memcpy(fontfname, ctx->args.cidfsubstfont.data, ctx->args.cidfsubstfont.size); + defcidfallacklen = ctx->args.cidfsubstfont.size; } fontfname[fsprefixlen + defcidfallacklen] = '\0'; @@ -253,12 +262,12 @@ pdfi_open_CIDFont_substitute_file(pdf_context * ctx, pdf_dict *font_dict, pdf_di } else { if (cidname) { - pdfi_print_string(ctx, "Loading CIDFont "); + pdfi_print_cstring(ctx, "Loading CIDFont "); pdfi_print_font_name(ctx, (pdf_name *)cidname); - pdfi_print_string(ctx, " substitute from "); + pdfi_print_cstring(ctx, " substitute from "); } else { - pdfi_print_string(ctx, "Loading nameless CIDFont from "); + pdfi_print_cstring(ctx, "Loading nameless CIDFont from "); } sfilename(s, &fname); if (fname.size < gp_file_name_sizeof) { @@ -268,8 +277,8 @@ pdfi_open_CIDFont_substitute_file(pdf_context * ctx, pdf_dict *font_dict, pdf_di else { strcpy(fontfname, "unnamed file"); } - pdfi_print_string(ctx, fontfname); - pdfi_print_string(ctx, "\n"); + pdfi_print_cstring(ctx, fontfname); + pdfi_print_cstring(ctx, "\n"); sfseek(s, 0, SEEK_END); @@ -287,19 +296,25 @@ pdfi_open_CIDFont_substitute_file(pdf_context * ctx, pdf_dict *font_dict, pdf_di } } else { - code = pdfi_open_resource_file(ctx, (const char *)mname->data, mname->length, &s); + if (mname->length + 1 > gp_file_name_sizeof) { + pdfi_countdown(mname); + return_error(gs_error_invalidfont); + } + memcpy(fontfname, mname->data, mname->length); + fontfname[mname->length] = '\0'; + code = pdfi_open_resource_file(ctx, (const char *)fontfname, 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_cstring(ctx, "Loading CIDFont "); pdfi_print_font_name(ctx, (pdf_name *)cidname); - pdfi_print_string(ctx, " (or substitute) from "); + pdfi_print_cstring(ctx, " (or substitute) from "); } else { - pdfi_print_string(ctx, "Loading nameless CIDFont from "); + pdfi_print_cstring(ctx, "Loading nameless CIDFont from "); } sfilename(s, &fname); if (fname.size < gp_file_name_sizeof) { @@ -309,8 +324,8 @@ pdfi_open_CIDFont_substitute_file(pdf_context * ctx, pdf_dict *font_dict, pdf_di else { strcpy(fontfname, "unnamed file"); } - pdfi_print_string(ctx, fontfname); - pdfi_print_string(ctx, "\n"); + pdfi_print_cstring(ctx, fontfname); + pdfi_print_cstring(ctx, "\n"); sfseek(s, 0, SEEK_END); *buflen = sftell(s); sfseek(s, 0, SEEK_SET); @@ -329,9 +344,11 @@ pdfi_open_CIDFont_substitute_file(pdf_context * ctx, pdf_dict *font_dict, pdf_di const char *fsprefix = "CIDFont/"; const int fsprefixlen = strlen(fsprefix); - if (cidname == NULL || cidname->type != PDF_NAME - || fsprefixlen + cidname->length >= gp_file_name_sizeof) + if (cidname == NULL || pdfi_type_of(cidname) != PDF_NAME + || fsprefixlen + cidname->length >= gp_file_name_sizeof) { + code = gs_note_error(gs_error_invalidfont); goto exit; + } memcpy(fontfname, fsprefix, fsprefixlen); memcpy(fontfname + fsprefixlen, cidname->data, cidname->length); @@ -428,64 +445,90 @@ static const char *pdfi_clean_font_name(const char *fontname) return NULL; } -static const char *pdfi_font_substitute_by_flags(unsigned int flags) +static int pdfi_font_substitute_by_flags(pdf_context *ctx, unsigned int flags, char **name, int *namelen) { bool fixed = ((flags & pdfi_font_flag_fixed) != 0); bool serif = ((flags & pdfi_font_flag_serif) != 0); bool italic = ((flags & pdfi_font_flag_italic) != 0); bool bold = ((flags & pdfi_font_flag_forcebold) != 0); + int code = 0; - if (fixed) { + if (ctx->args.defaultfont_is_name == true && ctx->args.defaultfont.size == 4 + && !memcmp(ctx->args.defaultfont.data, "None", 4)) { + *name = NULL; + *namelen = 0; + code = gs_error_invalidfont; + } + else if (ctx->args.defaultfont.data != NULL && ctx->args.defaultfont.size > 0) { + *name = (char *)ctx->args.defaultfont.data; + *namelen = ctx->args.defaultfont.size; + } + else if (fixed) { if (bold) { if (italic) { - return "Courier-BoldOblique"; + *name = (char *)pdfi_base_font_names[3][0]; + *namelen = strlen(*name); } else { - return "Courier-Bold"; + *name = (char *)pdfi_base_font_names[1][0]; + *namelen = strlen(*name); } } else { if (italic) { - return "Courier-Oblique"; + *name = (char *)pdfi_base_font_names[2][0]; + *namelen = strlen(*name); } else { - return "Courier"; + *name = (char *)pdfi_base_font_names[0][0]; + *namelen = strlen(*name); } } } else if (serif) { if (bold) { if (italic) { - return "Times-BoldItalic"; + *name = (char *)pdfi_base_font_names[11][0]; + *namelen = strlen(*name); } else { - return "Times-Bold"; + *name = (char *)pdfi_base_font_names[9][0]; + *namelen = strlen(*name); } } else { if (italic) { - return "Times-Italic"; + *name = (char *)pdfi_base_font_names[10][0]; + *namelen = strlen(*name); } else { - return "Times-Roman"; + *name = (char *)pdfi_base_font_names[8][0]; + *namelen = strlen(*name); } } } else { if (bold) { if (italic) { - return "Helvetica-BoldOblique"; + *name = (char *)pdfi_base_font_names[7][0]; + *namelen = strlen(*name); } else { - return "Helvetica-Bold"; + *name = (char *)pdfi_base_font_names[5][0]; + *namelen = strlen(*name); } } else { if (italic) { - return "Helvetica-Oblique"; + *name = (char *)pdfi_base_font_names[6][0]; + *namelen = strlen(*name); + } + else { + *name = (char *)pdfi_base_font_names[4][0]; + *namelen = strlen(*name); } } } - return "Helvetica"; /* Really shouldn't ever happen */ + return code; } enum { @@ -524,27 +567,112 @@ static int pdfi_fonttype_picker(byte *buf, int64_t buflen) #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, int *findex) +static int pdfi_copy_font(pdf_context *ctx, pdf_font *spdffont, pdf_dict *font_dict, pdf_font **tpdffont) +{ + int code; + if (pdfi_type_of(spdffont) != PDF_FONT) + return_error(gs_error_typecheck); + + switch(spdffont->pdfi_font_type) { + case e_pdf_font_type1: + code = pdfi_copy_type1_font(ctx, spdffont, font_dict, tpdffont); + break; + case e_pdf_font_cff: + code = pdfi_copy_cff_font(ctx, spdffont, font_dict, tpdffont); + break; + case e_pdf_font_truetype: + code = pdfi_copy_truetype_font(ctx, spdffont, font_dict, tpdffont); + break; + default: + return_error(gs_error_invalidfont); + } + return code; +} + +enum { + font_embedded = 0, + font_from_file = 1, + font_substitute = 2 +}; + +static int pdfi_load_font_buffer(pdf_context *ctx, byte *fbuf, int fbuflen, int fftype, pdf_name *Subtype, int findex, pdf_dict *stream_dict, pdf_dict *page_dict, pdf_dict *font_dict, pdf_font **ppdffont, bool cidfont) +{ + int code = gs_error_invalidfont; + if (fbuf != NULL) { + /* First, see if we can glean the type from the magic number */ + int sftype = pdfi_fonttype_picker(fbuf, fbuflen); + if (sftype == no_type_font) { + if (fftype != no_type_font) + sftype = fftype; + else { + /* 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; + else if (pdfi_name_is(Subtype, "TrueType")) + sftype = tt_font; + } + } + /* fbuf ownership passes to the font loader */ + switch (sftype) { + case type1_font: + code = pdfi_read_type1_font(ctx, (pdf_dict *)font_dict, stream_dict, page_dict, fbuf, fbuflen, ppdffont); + fbuf = NULL; + break; + case cff_font: + code = pdfi_read_cff_font(ctx, (pdf_dict *)font_dict, stream_dict, page_dict, fbuf, fbuflen, cidfont, ppdffont); + fbuf = NULL; + break; + case tt_font: + { + if (cidfont) + 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, findex, ppdffont); + fbuf = NULL; + } + break; + default: + gs_free_object(ctx->memory, fbuf, "pdfi_load_font_buffer(fbuf)"); + code = gs_note_error(gs_error_invalidfont); + } + } + return code; +} + +static int pdfi_load_font_file(pdf_context *ctx, int fftype, pdf_name *Subtype, pdf_dict *stream_dict, pdf_dict *page_dict, pdf_dict *font_dict, pdf_dict *fontdesc, bool substitute, pdf_font **ppdffont) { int code; char fontfname[gp_file_name_sizeof]; - pdf_obj *basefont = NULL, *mapname; + pdf_obj *basefont = NULL, *mapname = NULL; pdf_obj *fontname = NULL; stream *s; const char *fn; + int findex = 0; + byte *buf; + int buflen; + pdf_font *pdffont = NULL; + pdf_font *substpdffont = NULL; + bool f_retry = true; code = pdfi_dict_knownget_type(ctx, font_dict, "BaseFont", PDF_NAME, &basefont); - if (code < 0 || basefont == NULL || ((pdf_name *)basefont)->length == 0) - fallback = true; + if (substitute == false && (code < 0 || basefont == NULL || ((pdf_name *)basefont)->length == 0)) { + pdfi_countdown(basefont); + return_error(gs_error_invalidfont); + } - if (fallback == true) { - const char *fbname; + if (substitute == true) { + char *fbname; + int fbnamelen; int64_t flags = 0; if (fontdesc != NULL) { (void)pdfi_dict_get_int(ctx, fontdesc, "Flags", &flags); } - fbname = pdfi_font_substitute_by_flags((int)flags); + code = pdfi_font_substitute_by_flags(ctx, (int)flags, &fbname, &fbnamelen); + if (code < 0) + return code; + code = pdfi_name_alloc(ctx, (byte *)fbname, strlen(fbname), (pdf_obj **) &fontname); if (code < 0) return code; @@ -558,87 +686,166 @@ pdfi_open_font_substitute_file(pdf_context *ctx, pdf_dict *font_dict, pdf_dict * if (((pdf_name *)fontname)->length < gp_file_name_sizeof) { memcpy(fontfname, ((pdf_name *)fontname)->data, ((pdf_name *)fontname)->length); fontfname[((pdf_name *)fontname)->length] = '\0'; - fn = pdfi_clean_font_name(fontfname); - if (fn != NULL) { - pdfi_countdown(fontname); + pdfi_countdown(fontname); - code = pdfi_name_alloc(ctx, (byte *)fn, strlen(fn), (pdf_obj **) &fontname); - if (code < 0) - return code; - pdfi_countup(fontname); - } - } - code = pdf_fontmap_lookup_font(ctx, (pdf_name *) fontname, &mapname, findex); - if (code < 0) { - mapname = fontname; - pdfi_countup(mapname); - code = 0; + code = pdfi_name_alloc(ctx, (byte *)fontfname, strlen(fontfname), (pdf_obj **) &fontname); + if (code < 0) + return code; + pdfi_countup(fontname); } - 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'; + + do { + code = pdf_fontmap_lookup_font(ctx, (pdf_name *) fontname, &mapname, &findex); + if (code < 0) { + if (((pdf_name *)fontname)->length < gp_file_name_sizeof) { + memcpy(fontfname, ((pdf_name *)fontname)->data, ((pdf_name *)fontname)->length); + fontfname[((pdf_name *)fontname)->length] = '\0'; + fn = pdfi_clean_font_name(fontfname); + if (fn != NULL) { + pdfi_countdown(fontname); + + code = pdfi_name_alloc(ctx, (byte *)fn, strlen(fn), (pdf_obj **) &fontname); + if (code < 0) + return code; + pdfi_countup(fontname); + } + } + code = pdf_fontmap_lookup_font(ctx, (pdf_name *) fontname, &mapname, &findex); + if (code < 0) { + mapname = fontname; + pdfi_countup(mapname); + code = 0; + } + } + if (pdfi_type_of(mapname) == PDF_NAME || pdfi_type_of(mapname) == 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); } - } - else { - pdfi_countdown(mapname); - pdfi_countdown(fontname); - return_error(gs_error_invalidfileaccess); - } - code = pdfi_open_font_file(ctx, fontfname, strlen(fontfname), &s); + if (ctx->pdf_substitute_fonts != NULL) { + code = pdfi_dict_knownget_type(ctx, ctx->pdf_substitute_fonts, fontfname, PDF_FONT, (pdf_obj **)&pdffont); + if (code == 1 && pdffont->filename == NULL) { + pdfi_countdown(pdffont); + pdffont = NULL; + code = 0; + } + } + else + code = 0; + + if (code != 1) { + code = pdfi_open_font_file(ctx, fontfname, strlen(fontfname), &s); + if (code < 0 && f_retry && pdfi_type_of(mapname) == PDF_NAME) { + pdfi_countdown(fontname); + fontname = mapname; + mapname = NULL; + f_retry = false; + continue; + } + if (code >= 0) { + gs_const_string fname; + + 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"); + } + sfseek(s, 0, SEEK_END); + buflen = sftell(s); + sfseek(s, 0, SEEK_SET); + buf = gs_alloc_bytes(ctx->memory, buflen, "pdfi_open_t1_font_file(buf)"); + if (buf != NULL) { + sfread(buf, 1, buflen, s); + } + else { + code = gs_note_error(gs_error_VMerror); + } + sfclose(s); + /* On success, the buffer owership moves to the font object */ + code = pdfi_load_font_buffer(ctx, buf, buflen, no_type_font, NULL, findex, stream_dict, page_dict, NULL, &pdffont, false); + if (code < 0) { + gs_free_object(ctx->memory, buf, "pdfi_load_font_file"); + } + else { + pdffont->filename = NULL; + code = pdfi_object_alloc(ctx, PDF_STRING, strlen(fontfname) , (pdf_obj **)&pdffont->filename); + if (code >= 0) { + pdfi_countup(pdffont->filename); + memcpy(pdffont->filename->data, fontfname, strlen(fontfname)); + pdffont->filename->length = strlen(fontfname); + } + + if (ctx->pdf_substitute_fonts == NULL) { + code = pdfi_dict_alloc(ctx, 16, &ctx->pdf_substitute_fonts); + if (code >= 0) + pdfi_countup(ctx->pdf_substitute_fonts); + } + if (ctx->pdf_substitute_fonts != NULL) { + if (pdfi_type_of(mapname) == PDF_STRING) { + pdf_name *n = NULL; + pdf_string *mn = (pdf_string *)mapname; + + code = pdfi_name_alloc(ctx, mn->data, mn->length, (pdf_obj **)&n); + if (code >= 0) { + pdfi_countdown(mapname); + mapname = (pdf_obj *)n; + pdfi_countup(mapname); + code = 0; + } + } + else + code = 0; + + if (code == 0) + (void)pdfi_dict_put_obj(ctx, ctx->pdf_substitute_fonts, mapname, (pdf_obj *)pdffont, true); + code = 0; + } + } + } + } + break; + } while (1); + if (code >= 0) { - gs_const_string fname; if (basefont) { - pdfi_print_string(ctx, "Loading font "); + pdfi_print_cstring(ctx, "Loading font "); pdfi_print_font_name(ctx, (pdf_name *)basefont); - pdfi_print_string(ctx, " (or substitute) from "); - } - else { - 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'; - } - 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_t1_font_file(buf)"); - if (*buf != NULL) { - sfread(*buf, 1, *buflen, s); + pdfi_print_cstring(ctx, " (or substitute) from "); } else { - code = gs_note_error(gs_error_VMerror); + pdfi_print_cstring(ctx, "Loading nameless font from "); } - sfclose(s); + pdfi_print_font_string(ctx, pdffont->filename); + pdfi_print_cstring(ctx, "\n"); + + code = pdfi_copy_font(ctx, pdffont, font_dict, &substpdffont); + pdfi_countdown(pdffont); } + *ppdffont = substpdffont; + pdfi_countdown(basefont); pdfi_countdown(mapname); pdfi_countdown(fontname); return code; } -enum { - font_embedded = 0, - font_from_file = 1, - font_substitute = 2 -}; - int pdfi_load_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, pdf_dict *font_dict, gs_font **ppfont, bool cidfont) { int code; @@ -655,13 +862,19 @@ int pdfi_load_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, int findex = -1; code = pdfi_dict_get_type(ctx, font_dict, "Type", PDF_NAME, (pdf_obj **)&Type); - if (code < 0) - goto exit; - if (!pdfi_name_is(Type, "Font")){ - code = gs_note_error(gs_error_typecheck); - goto exit; + if (code < 0) { + pdfi_set_error(ctx, 0, NULL, E_PDF_MISSINGTYPE, "pdfi_load_font", NULL); + } + else { + if (!pdfi_name_is(Type, "Font")){ + code = gs_note_error(gs_error_typecheck); + goto exit; + } } code = pdfi_dict_get_type(ctx, font_dict, "Subtype", PDF_NAME, (pdf_obj **)&Subtype); + if (code < 0) { + pdfi_set_error(ctx, 0, NULL, E_PDF_NO_SUBTYPE, "pdfi_load_font", NULL); + } /* Beyond Type 0 and Type 3, there is no point trusting the Subtype key */ if (code >= 0 && pdfi_name_is(Subtype, "Type0")) { @@ -682,7 +895,7 @@ int pdfi_load_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, even if we don't */ code = pdfi_dict_get_type(ctx, font_dict, "FontDescriptor", PDF_DICT, (pdf_obj**)&fontdesc); - if (fontdesc != NULL && fontdesc->type == PDF_DICT) { + if (fontdesc != NULL && pdfi_type_of(fontdesc) == PDF_DICT) { code = pdfi_dict_get_type(ctx, (pdf_dict *) fontdesc, "FontFile", PDF_STREAM, (pdf_obj**)&fontfile); if (code >= 0) fftype = type1_font; @@ -692,7 +905,7 @@ int pdfi_load_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, } if (code < 0) { code = pdfi_dict_get_type(ctx, (pdf_dict *) fontdesc, "FontFile3", PDF_STREAM, (pdf_obj**)&fontfile); - if (fontfile != NULL) { + if (code >= 0 && fontfile != NULL) { code = pdfi_dict_get_type(ctx, fontfile->stream_dict, "Subtype", PDF_NAME, (pdf_obj **)&ffsubtype); if (code >= 0) { if (pdfi_name_is(ffsubtype, "Type1")) @@ -724,59 +937,27 @@ int pdfi_load_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, while (1) { if (fbuf != NULL) { - /* First, see if we can glean the type from the magic number */ - int sftype = pdfi_fonttype_picker(fbuf, fbuflen); - if (sftype == no_type_font) { - if (fftype != no_type_font) - sftype = fftype; - else { - /* 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; - else if (pdfi_name_is(Subtype, "TrueType")) - sftype = tt_font; - } - } - /* fbuf ownership passes to the font loader */ - switch (sftype) { - case type1_font: - code = pdfi_read_type1_font(ctx, (pdf_dict *)font_dict, stream_dict, page_dict, fbuf, fbuflen, &ppdffont); - fbuf = NULL; - break; - case cff_font: - code = pdfi_read_cff_font(ctx, (pdf_dict *)font_dict, stream_dict, page_dict, fbuf, fbuflen, cidfont, &ppdffont); - fbuf = NULL; - break; - case tt_font: - { - if (cidfont) - 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, findex, &ppdffont); - fbuf = NULL; - } - break; - default: - code = gs_note_error(gs_error_invalidfont); - } + /* fbuf overship passes to pdfi_load_font_buffer() */ + code = pdfi_load_font_buffer(ctx, fbuf, fbuflen, fftype, Subtype, findex, stream_dict, page_dict, font_dict, &ppdffont, cidfont); if (code < 0 && substitute == font_embedded) { - 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"); + if (ctx->args.pdfstoponerror == true) { + goto exit; + } + else { + char obj[129]; + pdfi_print_cstring(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_cstring(ctx, obj); + pdfi_print_cstring(ctx, "**** Attempting to load a substitute font.\n"); + } } } + else { + code = gs_error_invalidfont; + } if (code < 0 && code != gs_error_VMerror && substitute == font_embedded) { - /* Font not embedded, or embedded font not usable - use a substitute */ - if (fbuf != NULL) { - gs_free_object(ctx->memory, fbuf, "pdfi_load_font(fbuf)"); - } - substitute = font_from_file; if (cidfont == true) { @@ -790,14 +971,12 @@ 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, &findex); + code = pdfi_load_font_file(ctx, no_type_font, Subtype, stream_dict, page_dict, font_dict, fontdesc, false, &ppdffont); if (code < 0) { - code = pdfi_open_font_substitute_file(ctx, font_dict, fontdesc, true, &fbuf, &fbuflen, &findex); + code = pdfi_load_font_file(ctx, no_type_font, Subtype, stream_dict, page_dict, font_dict, fontdesc, true, &ppdffont); substitute |= font_substitute; } - - if (code < 0) - goto exit; + break; } continue; } @@ -834,17 +1013,18 @@ int pdfi_load_dict_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_ gs_font *pfont; pdf_font *pdfif; - if (font_dict->type == PDF_FONT) { - pdfi_countup(font_dict); - pfont = (gs_font *)((pdf_font *)font_dict)->pfont; - code = 0; - } - else { - if (font_dict->type != PDF_DICT) { + switch (pdfi_type_of(font_dict)) { + case PDF_FONT: + pdfi_countup(font_dict); + pfont = (gs_font *)((pdf_font *)font_dict)->pfont; + code = 0; + break; + case PDF_DICT: + code = pdfi_load_font(ctx, stream_dict, page_dict, font_dict, &pfont, false); + break; + default: code = gs_note_error(gs_error_typecheck); goto exit; - } - code = pdfi_load_font(ctx, stream_dict, page_dict, font_dict, &pfont, false); } if (code < 0) goto exit; @@ -869,7 +1049,7 @@ static int pdfi_load_resource_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_ int code; pdf_dict *font_dict = NULL; - if (fontname->type != PDF_NAME) { + if (pdfi_type_of(fontname) != PDF_NAME) { /* Passing empty string here should fall back to a default font */ return pdfi_font_set_internal_string(ctx, "", point_size); } @@ -922,6 +1102,8 @@ int pdfi_get_cidfont_glyph_metrics(gs_font *pfont, gs_glyph cid, double *widths, i = 0; while(1) { + pdf_obj_type type; + if (i + 1>= W->size) break; code = pdfi_array_get_type(pdffont->ctx, W, i, PDF_INT, (pdf_obj **)&c); if (code < 0) goto cleanup; @@ -929,7 +1111,9 @@ int pdfi_get_cidfont_glyph_metrics(gs_font *pfont, gs_glyph cid, double *widths, code = pdfi_array_get(pdffont->ctx, W, i + 1, &o); if (code < 0) goto cleanup; - if (o->type == PDF_INT) { + type = pdfi_type_of(o); + if (type == PDF_INT) { + double d; c2 = (pdf_num *)o; o = NULL; if (i + 2 >= W->size){ @@ -939,18 +1123,10 @@ int pdfi_get_cidfont_glyph_metrics(gs_font *pfont, gs_glyph cid, double *widths, break; } - code = pdfi_array_get(pdffont->ctx, W, i + 2, (pdf_obj **)&o); + code = pdfi_array_get_number(pdffont->ctx, W, i + 2, &d); if (code < 0) goto cleanup; - if (o->type != PDF_INT && o->type != PDF_REAL) { - code = gs_note_error(gs_error_typecheck); - goto cleanup; - } if (cid >= c->value.i && cid <= c2->value.i) { - if (o->type == PDF_INT) - widths[GLYPH_W0_WIDTH_INDEX] = (double)((pdf_num *)o)->value.i; - else - widths[GLYPH_W0_WIDTH_INDEX] = ((pdf_num *)o)->value.d; - + widths[GLYPH_W0_WIDTH_INDEX] = d; widths[GLYPH_W0_HEIGHT_INDEX] = 0.0; /* We countdown and NULL c, c2 and o after exit from the loop * in order to avoid doing so in the break statements @@ -961,27 +1137,17 @@ int pdfi_get_cidfont_glyph_metrics(gs_font *pfont, gs_glyph cid, double *widths, i += 3; pdfi_countdown(c2); pdfi_countdown(c); - pdfi_countdown(o); c = c2 = NULL; - o = NULL; continue; } } - else if (o->type == PDF_ARRAY) { + else if (type == PDF_ARRAY) { pdf_array *a = (pdf_array *)o; o = NULL; if (cid >= c->value.i && cid < c->value.i + a->size) { - code = pdfi_array_get(pdffont->ctx, a, cid - c->value.i, (pdf_obj **)&o); + code = pdfi_array_get_number(pdffont->ctx, a, cid - c->value.i, &widths[GLYPH_W0_WIDTH_INDEX]); if (code >= 0) { pdfi_countdown(a); - if (o->type == PDF_INT) - widths[GLYPH_W0_WIDTH_INDEX] = (double)((pdf_num *)o)->value.i; - else if (o->type == PDF_REAL) - widths[GLYPH_W0_WIDTH_INDEX] = ((pdf_num *)o)->value.d; - else { - code = gs_note_error(gs_error_typecheck); - goto cleanup; - } widths[GLYPH_W0_HEIGHT_INDEX] = 0.0; /* We countdown and NULL c, c2 and o on exit from the loop * in order to avoid doing so in the break statements @@ -991,8 +1157,6 @@ int pdfi_get_cidfont_glyph_metrics(gs_font *pfont, gs_glyph cid, double *widths, } pdfi_countdown(a); pdfi_countdown(c); - pdfi_countdown(o); - o = NULL; c = NULL; i += 2; continue; @@ -1016,78 +1180,42 @@ int pdfi_get_cidfont_glyph_metrics(gs_font *pfont, gs_glyph cid, double *widths, widths[GLYPH_W1_V_X_INDEX] = (widths[GLYPH_W0_WIDTH_INDEX] / 2.0); widths[GLYPH_W1_V_Y_INDEX] = 880.0; - if (DW2 != NULL && DW2->type == PDF_ARRAY + if (DW2 != NULL && pdfi_type_of(DW2) == PDF_ARRAY && DW2->size >= 2) { - pdf_num *w2_0 = NULL, *w2_1 = NULL; - - code = pdfi_array_get(pdffont->ctx, (pdf_array *)DW2, 0, (pdf_obj **)&w2_0); - if (code >= 0 && (w2_0->type == PDF_INT || w2_0->type == PDF_REAL)) { - code = pdfi_array_get(pdffont->ctx, (pdf_array *)DW2, 1, (pdf_obj **)&w2_1); - if (code >= 0 && (w2_1->type == PDF_INT || w2_1->type == PDF_REAL)) { - widths[GLYPH_W1_V_X_INDEX] = widths[GLYPH_W0_WIDTH_INDEX] / 2.0; - if (w2_0->type == PDF_INT) - widths[GLYPH_W1_V_Y_INDEX] = (double)w2_0->value.i; - else - widths[GLYPH_W1_V_Y_INDEX] = (double)w2_0->value.d; - - widths[GLYPH_W1_WIDTH_INDEX] = 0.0; - if (w2_1->type == PDF_INT) - widths[GLYPH_W1_HEIGHT_INDEX] = (double)w2_1->value.i; - else - widths[GLYPH_W1_HEIGHT_INDEX] = (double)w2_1->value.d; - } + code = pdfi_array_get_number(pdffont->ctx, (pdf_array *)DW2, 0, &widths[GLYPH_W1_V_Y_INDEX]); + if (code >= 0) + code = pdfi_array_get_number(pdffont->ctx, (pdf_array *)DW2, 1, &widths[GLYPH_W1_HEIGHT_INDEX]); + if (code >= 0) { + widths[GLYPH_W1_V_X_INDEX] = widths[GLYPH_W0_WIDTH_INDEX] / 2.0; + widths[GLYPH_W1_WIDTH_INDEX] = 0.0; } - pdfi_countdown(w2_0); - pdfi_countdown(w2_1); } - if (W2 != NULL && W2->type == PDF_ARRAY) { + if (W2 != NULL && pdfi_type_of(W2) == PDF_ARRAY) { i = 0; while(1) { + pdf_obj_type type; if (i + 1 >= W2->size) break; (void)pdfi_array_get(pdffont->ctx, W2, i, (pdf_obj **)&c); - if (c->type != PDF_INT) { + if (pdfi_type_of(c) != PDF_INT) { code = gs_note_error(gs_error_typecheck); goto cleanup; } code = pdfi_array_get(pdffont->ctx, W2, i + 1, (pdf_obj **)&o); if (code < 0) goto cleanup; - if (o->type == PDF_INT) { + type = pdfi_type_of(o); + if (type == PDF_INT) { if (cid >= c->value.i && cid <= ((pdf_num *)o)->value.i) { - pdf_num *w1y, *v1x, *v1y; if (i + 4 >= W2->size) { /* We countdown and NULL c, and o on exit from the function * so we don't need to do so in the break statements */ break; } - (void)pdfi_array_get(pdffont->ctx, W2, i + 1, (pdf_obj **)&w1y); - (void)pdfi_array_get(pdffont->ctx, W2, i + 1, (pdf_obj **)&v1x); - (void)pdfi_array_get(pdffont->ctx, W2, i + 1, (pdf_obj **)&v1y); - if (w1y != NULL && (w1y->type == PDF_INT || w1y->type == PDF_REAL) - && v1x != NULL && (v1x->type == PDF_INT || v1x->type == PDF_REAL) - && v1y != NULL && (v1y->type == PDF_INT || v1y->type == PDF_REAL)) { - widths[GLYPH_W1_WIDTH_INDEX] = 0; - if (w1y->type == PDF_INT) - widths[GLYPH_W1_HEIGHT_INDEX] = (double)w1y->value.i; - else - widths[GLYPH_W1_HEIGHT_INDEX] = w1y->value.d; - - if (v1x->type == PDF_INT) - widths[GLYPH_W1_V_X_INDEX] = (double)v1x->value.i; - else - widths[GLYPH_W1_V_X_INDEX] = v1x->value.d; - - if (v1y->type == PDF_INT) - widths[GLYPH_W1_V_Y_INDEX] = (double)v1y->value.i; - else - widths[GLYPH_W1_V_Y_INDEX] = v1y->value.d; - } - else - code = gs_note_error(gs_error_typecheck); - - pdfi_countdown(w1y); - pdfi_countdown(v1x); - pdfi_countdown(v1y); + code = pdfi_array_get_number(pdffont->ctx, W2, i + 1, &widths[GLYPH_W1_HEIGHT_INDEX]); + if (code < 0) goto cleanup; + code = pdfi_array_get_number(pdffont->ctx, W2, i + 1, &widths[GLYPH_W1_V_X_INDEX]); + if (code < 0) goto cleanup; + code = pdfi_array_get_number(pdffont->ctx, W2, i + 1, &widths[GLYPH_W1_V_Y_INDEX]); if (code < 0) goto cleanup; /* We countdown and NULL c, and o on exit from the function * so we don't need to do so in the break statements @@ -1096,43 +1224,26 @@ int pdfi_get_cidfont_glyph_metrics(gs_font *pfont, gs_glyph cid, double *widths, } i += 5; } - else if (o->type == PDF_ARRAY) { + else if (type == PDF_ARRAY) { pdf_array *a = (pdf_array *)o; int l = a->size - (a->size % 3); o = NULL; if (cid >= c->value.i && cid < c->value.i + (l / 3)) { - pdf_num *w1y = NULL, *v1x = NULL, *v1y = NULL; int index = (cid - c->value.i) * 3; - (void)pdfi_array_get(pdffont->ctx, a, index, (pdf_obj **)&w1y); - (void)pdfi_array_get(pdffont->ctx, a, index + 1, (pdf_obj **)&v1x); - (void)pdfi_array_get(pdffont->ctx, a, index + 2, (pdf_obj **)&v1y); - pdfi_countdown(a); - - if (w1y != NULL && (w1y->type == PDF_INT || w1y->type == PDF_REAL) - && v1x != NULL && (v1x->type == PDF_INT || v1x->type == PDF_REAL) - && v1y != NULL && (v1y->type == PDF_INT || v1y->type == PDF_REAL)) { - widths[GLYPH_W1_WIDTH_INDEX] = 0.0; - if (w1y->type == PDF_INT) - widths[GLYPH_W1_HEIGHT_INDEX] = (double)w1y->value.i; - else - widths[GLYPH_W1_HEIGHT_INDEX] = w1y->value.d; - - if (v1x->type == PDF_INT) - widths[GLYPH_W1_V_X_INDEX] = (double)v1x->value.i; - else - widths[GLYPH_W1_V_X_INDEX] = v1x->value.d; - - if (v1y->type == PDF_INT) - widths[GLYPH_W1_V_Y_INDEX] = (double)v1y->value.i; - else - widths[GLYPH_W1_V_Y_INDEX] = v1y->value.d; + code = pdfi_array_get_number(pdffont->ctx, a, index, &widths[GLYPH_W1_HEIGHT_INDEX]); + if (code < 0) { + pdfi_countdown(a); + goto cleanup; } - else - code = gs_note_error(gs_error_typecheck); - pdfi_countdown(w1y); - pdfi_countdown(v1x); - pdfi_countdown(v1y); + code = pdfi_array_get_number(pdffont->ctx, a, index + 1, &widths[GLYPH_W1_V_X_INDEX]); + if (code < 0) { + pdfi_countdown(a); + goto cleanup; + } + code = pdfi_array_get_number(pdffont->ctx, a, index + 2, &widths[GLYPH_W1_V_Y_INDEX]); + pdfi_countdown(a); if (code < 0) goto cleanup; + /* We countdown and NULL c, and o on exit from the function * so we don't need to do so in the break statements */ @@ -1176,11 +1287,11 @@ int pdfi_d0(pdf_context *ctx) goto d0_error; } - if (ctx->stack_top[-1]->type != PDF_INT && ctx->stack_top[-1]->type != PDF_REAL) { + if (pdfi_type_of(ctx->stack_top[-1]) != PDF_INT && pdfi_type_of(ctx->stack_top[-1]) != PDF_REAL) { code = gs_note_error(gs_error_typecheck); goto d0_error; } - if (ctx->stack_top[-2]->type != PDF_INT && ctx->stack_top[-2]->type != PDF_REAL) { + if (pdfi_type_of(ctx->stack_top[-2]) != PDF_INT && pdfi_type_of(ctx->stack_top[-2]) != PDF_REAL) { code = gs_note_error(gs_error_typecheck); goto d0_error; } @@ -1189,11 +1300,11 @@ int pdfi_d0(pdf_context *ctx) goto d0_error; } - if (ctx->stack_top[-1]->type == PDF_INT) + if (pdfi_type_of(ctx->stack_top[-1]) == PDF_INT) width[0] = (double)((pdf_num *)ctx->stack_top[-1])->value.i; else width[0] = ((pdf_num *)ctx->stack_top[-1])->value.d; - if (ctx->stack_top[-2]->type == PDF_INT) + if (pdfi_type_of(ctx->stack_top[-2]) == PDF_INT) width[1] = (double)((pdf_num *)ctx->stack_top[-1])->value.i; else width[1] = ((pdf_num *)ctx->stack_top[-1])->value.d; @@ -1245,7 +1356,7 @@ d0_error: int pdfi_d1(pdf_context *ctx) { - int code = 0, i, gsave_level; + int code = 0, gsave_level; double wbox[6]; if (ctx->text.inside_CharProc == false) @@ -1253,21 +1364,9 @@ int pdfi_d1(pdf_context *ctx) ctx->text.CharProc_d_type = pdf_type3_d1; - if (pdfi_count_stack(ctx) < 6) { - code = gs_note_error(gs_error_stackunderflow); + code = pdfi_destack_reals(ctx, wbox, 6); + if (code < 0) goto d1_error; - } - - for (i=-6;i < 0;i++) { - if (ctx->stack_top[i]->type != PDF_INT && ctx->stack_top[i]->type != PDF_REAL) { - code = gs_note_error(gs_error_typecheck); - goto d1_error; - } - if (ctx->stack_top[i]->type == PDF_INT) - wbox[i + 6] = (double)((pdf_num *)ctx->stack_top[i])->value.i; - else - wbox[i + 6] = ((pdf_num *)ctx->stack_top[i])->value.d; - } /* * We don't intend to retain this, instead we will use (effectively) xyshow to apply @@ -1291,7 +1390,6 @@ int pdfi_d1(pdf_context *ctx) if (code < 0) goto d1_error; - pdfi_pop(ctx, 6); return 0; d1_error: @@ -1319,16 +1417,9 @@ int pdfi_Tf(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) pdfi_pop(ctx, 2); /* Get the point_size */ - if (point_arg->type == PDF_INT) - point_size = (double)((pdf_num *)point_arg)->value.i; - else { - if (point_arg->type == PDF_REAL) - point_size = ((pdf_num *)point_arg)->value.d; - else { - code = gs_note_error(gs_error_typecheck); - goto exit0; - } - } + code = pdfi_obj_to_real(ctx, point_arg, &point_size); + if (code < 0) + goto exit0; code = pdfi_load_resource_font(ctx, stream_dict, page_dict, fontname, point_size); @@ -1378,7 +1469,7 @@ int pdfi_free_font(pdf_obj *font) static inline int pdfi_encoding_name_to_index(pdf_name *name) { int ind = gs_error_undefined; - if (name->type == PDF_NAME) { + if (pdfi_type_of(name) == PDF_NAME) { if (pdfi_name_is(name, "StandardEncoding")) { ind = ENCODING_INDEX_STANDARD; } else { @@ -1452,21 +1543,23 @@ int pdfi_create_Encoding(pdf_context *ctx, pdf_obj *pdf_Encoding, pdf_obj *font_ return code; pdfi_countup(*Encoding); - if (pdf_Encoding->type == PDF_NAME) { - code = pdfi_build_Encoding(ctx, (pdf_name *)pdf_Encoding, (pdf_array *)*Encoding); - if (code < 0) { - pdfi_countdown(*Encoding); - *Encoding = NULL; - return code; - } - } else { - if (pdf_Encoding->type == PDF_DICT) { + switch (pdfi_type_of(pdf_Encoding)) { + case PDF_NAME: + code = pdfi_build_Encoding(ctx, (pdf_name *)pdf_Encoding, (pdf_array *)*Encoding); + if (code < 0) { + pdfi_countdown(*Encoding); + *Encoding = NULL; + return code; + } + break; + case PDF_DICT: + { pdf_name *n = NULL; pdf_array *a = NULL; pdf_obj *o = NULL; int offset = 0; - if (font_Encoding != NULL && font_Encoding->type == PDF_ARRAY) { + if (font_Encoding != NULL && pdfi_type_of(font_Encoding) == PDF_ARRAY) { pdf_array *fenc = (pdf_array *)font_Encoding; for (i = 0; i < pdfi_array_size(fenc) && code >= 0; i++) { code = pdfi_array_get(ctx, fenc, (uint64_t)i, &o); @@ -1523,25 +1616,25 @@ int pdfi_create_Encoding(pdf_context *ctx, pdf_obj *pdf_Encoding, pdf_obj *font_ } for (i=0;i < pdfi_array_size(a);i++) { + pdf_obj_type type; code = pdfi_array_get(ctx, a, (uint64_t)i, &o); if (code < 0) break; - if (o->type == PDF_NAME) { + type = pdfi_type_of(o); + if (type == PDF_NAME) { if (offset < pdfi_array_size((pdf_array *)*Encoding)) code = pdfi_array_put(ctx, (pdf_array *)*Encoding, (uint64_t)offset, o); pdfi_countdown(o); offset++; if (code < 0) break; + } else if (type == PDF_INT) { + offset = ((pdf_num *)o)->value.i; + pdfi_countdown(o); } else { - if (o->type == PDF_INT) { - offset = ((pdf_num *)o)->value.i; - pdfi_countdown(o); - } else { - code = gs_note_error(gs_error_typecheck); - pdfi_countdown(o); - break; - } + code = gs_note_error(gs_error_typecheck); + pdfi_countdown(o); + break; } } pdfi_countdown(a); @@ -1550,11 +1643,12 @@ int pdfi_create_Encoding(pdf_context *ctx, pdf_obj *pdf_Encoding, pdf_obj *font_ *Encoding = NULL; return code; } - } else { + break; + } + default: pdfi_countdown(*Encoding); *Encoding = NULL; return gs_note_error(gs_error_typecheck); - } } return 0; } @@ -1575,6 +1669,10 @@ gs_glyph pdfi_encode_char(gs_font * pfont, gs_char chr, gs_glyph_space_t not_use pdf_name *GlyphName = NULL; code = pdfi_array_get(ctx, font->Encoding, (uint64_t)chr, (pdf_obj **)&GlyphName); if (code >= 0) { + if (pdfi_type_of(GlyphName) != PDF_NAME) + /* Can't signal an error, just return the 'not found' case */ + return g; + code = (*ctx->get_glyph_index)(pfont, (byte *)GlyphName->data, GlyphName->length, &nindex); pdfi_countdown(GlyphName); if (code >= 0) @@ -1595,9 +1693,9 @@ int pdfi_tounicode_char_to_unicode(pdf_context *ctx, pdf_cmap *tounicode, gs_gly 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) { + while (l == 0 && 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)) { + while (l == 0 && 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++) { @@ -1704,6 +1802,62 @@ static int pdfi_global_glyph_code(const gs_font *pfont, gs_const_string *gstr, g return code; } +int pdfi_map_glyph_name_via_agl(pdf_dict *cstrings, pdf_name *gname, pdf_string **cstring) +{ + single_glyph_list_t *sgl = (single_glyph_list_t *)&(SingleGlyphList); + int i, code, ucode = gs_error_undefined; + *cstring = NULL; + + if (gname->length == 7 && strncmp((char *)gname->data, "uni", 3) == 0) { + char u[5] = {0}; + memcpy(u, gname->data + 3, 4); + code = sscanf(u, "%x", &ucode); + if (code <= 0) + ucode = gs_error_undefined; + } + + if (ucode == gs_error_undefined) { + for (i = 0; sgl[i].Glyph != 0x00; i++) { + if (sgl[i].Glyph[0] == gname->data[0] + && strlen(sgl[i].Glyph) == gname->length + && !strncmp((char *)sgl[i].Glyph, (char *)gname->data, gname->length)) { + ucode = (int)sgl[i].Unicode; + break; + } + } + } + if (ucode > 0) { + for (i = 0; sgl[i].Glyph != 0x00; i++) { + if (sgl[i].Unicode == (unsigned short)ucode) { + pdf_string *s; + code = pdfi_dict_get((pdf_context *)cstrings->ctx, cstrings, (char *)sgl[i].Glyph, (pdf_obj **)&s); + if (code >= 0) { + *cstring = s; + break; + } + } + } + if (*cstring == NULL) { + char u[16] = {0}; + code = gs_snprintf(u, 16, "uni%04x", ucode); + if (code > 0) { + pdf_string *s; + code = pdfi_dict_get((pdf_context *)cstrings->ctx, cstrings, u, (pdf_obj **)&s); + if (code >= 0) { + *cstring = s; + } + } + } + } + + if (*cstring == NULL) + code = gs_note_error(gs_error_undefined); + else + code = 0; + + return code; +} + int pdfi_init_font_directory(pdf_context *ctx) { ctx->font_dir = gs_font_dir_alloc2(ctx->memory, ctx->memory); @@ -1763,6 +1917,95 @@ int pdfi_load_font_by_name_string(pdf_context *ctx, const byte *fontname, size_t return code; } + +int pdfi_font_create_widths(pdf_context *ctx, pdf_dict *fontdict, pdf_font *font, double scale) +{ + int code = 0; + pdf_obj *obj = NULL; + int i; + + font->Widths = NULL; + + if (font->FontDescriptor != NULL) { + code = pdfi_dict_knownget(ctx, font->FontDescriptor, "MissingWidth", &obj); + if (code > 0) { + if (pdfi_type_of(obj) == PDF_INT) { + font->MissingWidth = ((pdf_num *) obj)->value.i * scale; + } + else if (pdfi_type_of(obj) == PDF_REAL) { + font->MissingWidth = ((pdf_num *) obj)->value.d * scale; + } + else { + font->MissingWidth = 0; + } + pdfi_countdown(obj); + obj = NULL; + } + else { + font->MissingWidth = 0; + } + } + else { + font->MissingWidth = 1000.0 * scale; + } + + code = pdfi_dict_knownget_type(ctx, fontdict, "Widths", PDF_ARRAY, (pdf_obj **)&obj); + if (code > 0) { + if (pdfi_array_size((pdf_array *)obj) < font->LastChar - font->FirstChar + 1) { + code = gs_note_error(gs_error_rangecheck); + goto error; + } + + font->Widths = (double *)gs_alloc_bytes(OBJ_MEMORY(font), sizeof(double) * (font->LastChar - font->FirstChar + 1), "pdfi_font_create_widths(Widths)"); + if (font->Widths == NULL) { + code = gs_note_error(gs_error_VMerror); + goto error; + } + memset(font->Widths, 0x00, sizeof(double) * (font->LastChar - font->FirstChar + 1)); + for (i = 0; i < (font->LastChar - font->FirstChar + 1); i++) { + code = pdfi_array_get_number(ctx, (pdf_array *)obj, (uint64_t)i, &font->Widths[i]); + if (code < 0) + goto error; + font->Widths[i] *= scale; + } + } +error: + pdfi_countdown(obj); + if (code < 0) { + gs_free_object(OBJ_MEMORY(font), font->Widths, "pdfi_font_create_widths(Widths)"); + font->Widths = NULL; + } + return code; +} + +void pdfi_font_set_first_last_char(pdf_context *ctx, pdf_dict *fontdict, pdf_font *font) +{ + double f, l; + int code; + + if (fontdict == NULL) { + f = (double)0; + l = (double)255; + } + else { + code = pdfi_dict_get_number(ctx, fontdict, "FirstChar", &f); + if (code < 0 || f < 0 || f > 255) + f = (double)0; + + code = pdfi_dict_get_number(ctx, fontdict, "LastChar", &l); + if (code < 0 || l < 0 || l > 255) + l = (double)255; + } + if (f <= l) { + font->FirstChar = f; + font->LastChar = l; + } + else { + font->FirstChar = 0; + font->LastChar = 255; + } +} + /* Patch or create a new XUID based on the existing UID/XUID, a simple hash of the input file name and the font dictionary object number. This allows improved glyph cache efficiency, also ensures pdfwrite understands @@ -1776,10 +2019,10 @@ int pdfi_font_generate_pseudo_XUID(pdf_context *ctx, pdf_dict *fontdict, gs_font int i; uint32_t hash = 0; long *xvalues; - int xuidlen = 2; + int xuidlen = 3; sfilename(ctx->main_stream->s, &fn); - if (fn.size > 0 && fontdict->object_num != 0) { + if (fn.size > 0 && fontdict!= NULL && fontdict->object_num != 0) { for (i = 0; i < fn.size; i++) { hash = ((((hash & 0xf8000000) >> 27) ^ (hash << 5)) & 0x7ffffffff) ^ fn.data[i]; } @@ -1795,14 +2038,17 @@ int pdfi_font_generate_pseudo_XUID(pdf_context *ctx, pdf_dict *fontdict, gs_font } xvalues[0] = 1000000; /* "Private" value */ xvalues[1] = hash; + + xvalues[2] = ctx->device_state.HighLevelDevice ? pfont->id : 0; + if (uid_is_XUID(&pfont->UID)) { for (i = 0; i < uid_XUID_size(&pfont->UID); i++) { - xvalues[i + 2] = uid_XUID_values(&pfont->UID)[i]; + xvalues[i + 3] = uid_XUID_values(&pfont->UID)[i]; } uid_free(&pfont->UID, pfont->memory, "pdfi_font_generate_pseudo_XUID"); } else if (uid_is_valid(&pfont->UID)) - xvalues[2] = pfont->UID.id; + xvalues[3] = pfont->UID.id; uid_set_XUID(&pfont->UID, xvalues, xuidlen); } @@ -1817,7 +2063,7 @@ int pdfi_set_font_internal(pdf_context *ctx, pdf_obj *fontobj, double point_size int code; pdf_font *pdffont = (pdf_font *)fontobj; - if (pdffont->type != PDF_FONT || pdffont->pfont == NULL) + if (pdfi_type_of(pdffont) != PDF_FONT || pdffont->pfont == NULL) return_error(gs_error_invalidfont); code = gs_setPDFfontsize(ctx->pgs, point_size); @@ -1854,7 +2100,7 @@ int pdfi_font_set_internal_string(pdf_context *ctx, const char *fontname, double int pdfi_font_set_internal_name(pdf_context *ctx, pdf_name *fontname, double point_size) { - if (fontname->type != PDF_NAME) + if (pdfi_type_of(fontname) != PDF_NAME) return_error(gs_error_typecheck); else return pdfi_font_set_internal_inner(ctx, fontname->data, fontname->length, point_size); diff --git a/pdf/pdf_font.h b/pdf/pdf_font.h index da76aeb8..a18541c6 100644 --- a/pdf/pdf_font.h +++ b/pdf/pdf_font.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 @@ -57,6 +57,7 @@ static inline pdf_font *pdfi_get_current_pdf_font(pdf_context *ctx) return NULL; } +int pdfi_create_Widths(pdf_context *ctx, pdf_dict *font_dict, pdf_font *pdffont); int pdfi_create_Encoding(pdf_context *ctx, pdf_obj *pdf_Encoding, pdf_obj *font_Encoding, pdf_obj **Encoding); 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); @@ -72,6 +73,8 @@ int pdfi_fapi_passfont(pdf_font *font, int subfont, char *fapi_request, int pdfi_fapi_check_cmap_for_GID(gs_font *pfont, uint c, uint *g); +int pdfi_map_glyph_name_via_agl(pdf_dict *cstrings, pdf_name *gname, pdf_string **cstring); + int pdfi_init_font_directory(pdf_context *ctx); int pdfi_load_font(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict, pdf_dict *font_dict, gs_font **ppfont, bool cidfont); @@ -103,5 +106,7 @@ enum { }; int pdfi_get_cidfont_glyph_metrics(gs_font *pfont, gs_glyph cid, double *widths, bool vertical); +int pdfi_font_create_widths(pdf_context *ctx, pdf_dict *fontdict, pdf_font *font, double wscale); +void pdfi_font_set_first_last_char(pdf_context *ctx, pdf_dict *fontdict, pdf_font *font); int pdfi_font_generate_pseudo_XUID(pdf_context *ctx, pdf_dict *fontdict, gs_font_base *pfont); #endif diff --git a/pdf/pdf_font0.c b/pdf/pdf_font0.c index a322bfb4..76a94c8a 100644 --- a/pdf/pdf_font0.c +++ b/pdf/pdf_font0.c @@ -64,9 +64,21 @@ static void pdfi_font0_cid_subst_tables(const char *reg, const int reglen, const } static int -pdfi_font0_glyph_name(gs_font *font, gs_glyph index, gs_const_string *pstr) +pdfi_font0_glyph_name(gs_font *pfont, gs_glyph index, gs_const_string *pstr) { - return_error(gs_error_rangecheck); + int code; + pdf_font_type0 *pt0font = (pdf_font_type0 *)pfont->client_data; + char gnm[64]; + pdf_context *ctx = pt0font->ctx; + uint gindex = 0; + + gs_snprintf(gnm, 64, "%lu", (long)index); + code = (*ctx->get_glyph_index)((gs_font *)pfont, (byte *)gnm, strlen(gnm), &gindex); + if (code < 0) + return code; + code = (*ctx->get_glyph_name)(pfont, (gs_glyph)gindex, (gs_const_string *)pstr); + + return code; } static int @@ -80,7 +92,7 @@ pdfi_font0_map_glyph_to_unicode(gs_font *font, gs_glyph glyph, int ch, ushort *u pdfi_cid_subst_nwp_table_t *substnwp = pt0font->substnwp; code = pdfi_array_get(pt0font->ctx, pt0font->DescendantFonts, 0, (pdf_obj **)&decfont); - if (code < 0 || decfont->type != PDF_FONT) { + if (code < 0 || pdfi_type_of(decfont) != PDF_FONT) { pdfi_countdown(decfont); return gs_error_undefined; } @@ -189,7 +201,7 @@ int pdfi_read_type0_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream code = pdfi_dict_get(ctx, font_dict, "Encoding", &cmap); if (code < 0) goto error; - if (cmap->type == PDF_CMAP) { + if (pdfi_type_of(cmap) == PDF_CMAP) { pcmap = (pdf_cmap *)cmap; cmap = NULL; } @@ -203,7 +215,7 @@ int pdfi_read_type0_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream code = pdfi_dict_get(ctx, font_dict, "DescendantFonts", (pdf_obj **)&arr); if (code < 0) goto error; - if (arr->type != PDF_ARRAY || arr->size != 1) { + if (pdfi_type_of(arr) != PDF_ARRAY || arr->size != 1) { code = gs_note_error(gs_error_invalidfont); goto error; } @@ -211,31 +223,32 @@ int pdfi_read_type0_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream pdfi_countdown(arr); arr = NULL; if (code < 0) goto error; - if (decfontdict->type == PDF_FONT) { - descpfont = (pdf_font *)decfontdict; - decfontdict = descpfont->PDF_font; - pdfi_countup(decfontdict); - } - else { - if (decfontdict->type != PDF_DICT) { - code = gs_note_error(gs_error_invalidfont); - goto error; - } - code = pdfi_dict_get(ctx, (pdf_dict *)decfontdict, "Type", (pdf_obj **)&n); - if (code < 0) goto error; - if (n->type != PDF_NAME || n->length != 4 || memcmp(n->data, "Font", 4) != 0) { + switch (pdfi_type_of(decfontdict)) { + case PDF_FONT: + descpfont = (pdf_font *)decfontdict; + decfontdict = descpfont->PDF_font; + pdfi_countup(decfontdict); + break; + case PDF_DICT: + code = pdfi_dict_get(ctx, (pdf_dict *)decfontdict, "Type", (pdf_obj **)&n); + if (code < 0) goto error; + if (pdfi_type_of(n) != PDF_NAME || n->length != 4 || memcmp(n->data, "Font", 4) != 0) { + pdfi_countdown(n); + code = gs_note_error(gs_error_invalidfont); + goto error; + } pdfi_countdown(n); + break; + default: code = gs_note_error(gs_error_invalidfont); goto error; - } - pdfi_countdown(n); } #if 0 code = pdfi_dict_get(ctx, (pdf_dict *)decfontdict, "Subtype", (pdf_obj **)&n); if (code < 0) goto error; - if (n->type != PDF_NAME || n->length != 12 || memcmp(n->data, "CIDFontType", 11) != 0) { + if (pdfi_type_of(n) != PDF_NAME || n->length != 12 || memcmp(n->data, "CIDFontType", 11) != 0) { pdfi_countdown(n); code = gs_note_error(gs_error_invalidfont); goto error; @@ -255,13 +268,13 @@ int pdfi_read_type0_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream if (ctx->args.ignoretounicode != true) { code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tounicode); - if (code >= 0 && tounicode->type == PDF_STREAM) { + if (code >= 0 && pdfi_type_of(tounicode) == 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)) { + if (code < 0 || (tounicode != NULL && pdfi_type_of(tounicode) != PDF_CMAP)) { pdfi_countdown(tounicode); tounicode = NULL; code = 0; @@ -295,8 +308,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 && reg->type == PDF_STRING - && ord != NULL && ord->type == PDF_STRING) { + if (reg != NULL && pdfi_type_of(reg) == PDF_STRING + && ord != NULL && pdfi_type_of(ord) == PDF_STRING) { r = (char *)reg->data; rlen = reg->length; o = (char *)ord->data; @@ -352,6 +365,7 @@ int pdfi_read_type0_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream dmprintf2(ctx->memory, "Allocated object of type %c with UID %"PRIi64"\n", pdft0->type, pdft0->UID); #endif pdft0->refcnt = 1; + pdft0->filename = NULL; pdft0->object_num = font_dict->object_num; pdft0->generation_num = font_dict->generation_num; pdft0->indirect_num = font_dict->indirect_num; @@ -524,6 +538,8 @@ pdfi_free_font_type0(pdf_obj *font) pdfi_countdown(pdft0->Encoding); pdfi_countdown(pdft0->DescendantFonts); pdfi_countdown(pdft0->ToUnicode); + pdfi_countdown(pdft0->filename); + gs_free_object(OBJ_MEMORY(pdft0), pfont0->data.Encoding, "pdfi_free_font_type0(data.Encoding)"); /* We shouldn't need to free the fonts in the FDepVector, that should happen with DescendantFonts above. diff --git a/pdf/pdf_font1.c b/pdf/pdf_font1.c index 87ba858b..b350f449 100644 --- a/pdf/pdf_font1.c +++ b/pdf/pdf_font1.c @@ -117,6 +117,9 @@ pdfi_t1_glyph_data(gs_font_type1 *pfont, gs_glyph glyph, gs_glyph_data_t *pgd) if (code >= 0) { code = pdfi_dict_get_by_key(ctx, pdffont1->CharStrings, glyphname, (pdf_obj **)&charstring); + if (code < 0) { + code = pdfi_map_glyph_name_via_agl(pdffont1->CharStrings, glyphname, &charstring); + } if (code >= 0) gs_glyph_data_from_bytes(pgd, charstring->data, 0, charstring->length, NULL); } @@ -132,11 +135,17 @@ pdfi_t1_subr_data(gs_font_type1 *pfont, int index, bool global, gs_glyph_data_t int code = 0; pdf_font_type1 *pdffont1 = (pdf_font_type1 *) pfont->client_data; - if (global == true || index < 0 || index >= pdffont1->NumSubrs) { + if (global == true || index < 0 || index >= (pdffont1->Subrs == NULL ? 0 : pdfi_array_size(pdffont1->Subrs))) { code = gs_note_error(gs_error_rangecheck); } else { - gs_glyph_data_from_bytes(pgd, pdffont1->Subrs[index].data, 0, pdffont1->Subrs[index].size, NULL); + pdf_string *subr_str = NULL; + code = pdfi_array_get_type(pdffont1->ctx, pdffont1->Subrs, index, PDF_STRING, (pdf_obj **)&subr_str); + if (code >= 0) { + gs_glyph_data_from_bytes(pgd, subr_str->data, 0, subr_str->length, NULL); + } + /* decrementing is safe here, because the reference in the pdffont1->Subrs will persist */ + pdfi_countdown(subr_str); } return code; } @@ -168,11 +177,13 @@ pdfi_t1_seac_data(gs_font_type1 *pfont, int ccode, gs_glyph *pglyph, gs_const_st pdfi_countup(glyphname); code = pdfi_dict_get_by_key(ctx, pdffont1->CharStrings, glyphname, (pdf_obj **)&charstring); pdfi_countdown(glyphname); - if (code >= 0) - if (pgd != NULL) + if (code >= 0) { + if (pgd != NULL) { gs_glyph_data_from_bytes(pgd, charstring->data, 0, charstring->length, NULL); + } pdfi_countdown(charstring); } + } } return code; @@ -368,7 +379,7 @@ pdfi_t1_decode_pfb(pdf_context *ctx, byte *inbuf, int inlen, byte **outbuf, int *outbuf = NULL; *outlen = 0; - strm = push_pfb_filter(ctx->memory, inbuf, inbuf + inlen + 1); + strm = push_pfb_filter(ctx->memory, inbuf, inbuf + inlen); if (strm == NULL) { code = gs_note_error(gs_error_VMerror); } @@ -386,7 +397,7 @@ pdfi_t1_decode_pfb(pdf_context *ctx, byte *inbuf, int inlen, byte **outbuf, int } else { d = decodebuf; - strm = push_pfb_filter(ctx->memory, inbuf, inbuf + inlen + 1); + strm = push_pfb_filter(ctx->memory, inbuf, inbuf + inlen); while (1) { c = sgetc(strm); if (c < 0) @@ -501,6 +512,7 @@ int pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, byte *fbuf, int64_t fbuflen, pdf_font **ppdffont) { int code = 0; + double x_scale; pdf_obj *fontdesc = NULL; pdf_obj *basefont = NULL; pdf_obj *mapname = NULL; @@ -508,8 +520,10 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic pdf_font_type1 *t1f = NULL; pdf_obj *tounicode = NULL; ps_font_interp_private fpriv = { 0 }; + bool key_known; - (void)pdfi_dict_knownget_type(ctx, font_dict, "FontDescriptor", PDF_DICT, &fontdesc); + if (font_dict != NULL) + (void)pdfi_dict_knownget_type(ctx, font_dict, "FontDescriptor", PDF_DICT, &fontdesc); if (fbuf[0] == 128 && fbuf[1] == 1) { byte *decodebuf = NULL; @@ -530,12 +544,12 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic gs_free_object(ctx->memory, fbuf, "pdfi_read_type1_font"); /* If we have a full CharStrings dictionary, we probably have enough to make a font */ - if (code < 0 || (fpriv.u.t1.CharStrings == NULL || fpriv.u.t1.CharStrings->type != PDF_DICT - || fpriv.u.t1.CharStrings->entries == 0)) { + if (code < 0 || fpriv.u.t1.CharStrings == NULL || pdfi_type_of(fpriv.u.t1.CharStrings) != PDF_DICT + || fpriv.u.t1.CharStrings->entries == 0) { code = gs_note_error(gs_error_invalidfont); goto error; } - code = pdfi_alloc_t1_font(ctx, &t1f, font_dict->object_num); + code = pdfi_alloc_t1_font(ctx, &t1f, font_dict != NULL ? font_dict->object_num : 0); if (code >= 0) { gs_font_type1 *pfont1 = (gs_font_type1 *) t1f->pfont; @@ -555,22 +569,26 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic pfont1->PaintType = fpriv.gsu.gst1.PaintType; pfont1->StrokeWidth = fpriv.gsu.gst1.StrokeWidth; - 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; + if (font_dict != NULL) { + 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; - pdfi_countup(basefont); t1f->FontDescriptor = (pdf_dict *) fontdesc; pdfi_countup(fontdesc); t1f->Name = mapname; pdfi_countup(mapname); /* We want basefont, but we can live without it */ - (void)pdfi_dict_knownget_type(ctx, font_dict, "BaseFont", PDF_NAME, &basefont); + if (font_dict != NULL) { + (void)pdfi_dict_knownget_type(ctx, font_dict, "BaseFont", PDF_NAME, &basefont); + t1f->BaseFont = basefont; + pdfi_countup(basefont); + } t1f->descflags = 0; if (t1f->FontDescriptor != NULL) { @@ -588,15 +606,15 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic t1f->descflags |= 4; } - if (ctx->args.ignoretounicode != true) { + if (ctx->args.ignoretounicode != true && font_dict != NULL) { code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tounicode); - if (code >= 0 && tounicode->type == PDF_STREAM) { + if (code >= 0 && pdfi_type_of(tounicode) == 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)) { + if (code < 0 || (tounicode != NULL && pdfi_type_of(tounicode) != PDF_CMAP)) { pdfi_countdown(tounicode); tounicode = NULL; code = 0; @@ -608,74 +626,29 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic 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; - pdfi_countdown(tmp); - tmp = NULL; - } - else { - t1f->FirstChar = 0; - } - code = pdfi_dict_knownget_type(ctx, font_dict, "LastChar", PDF_INT, &tmp); - if (code == 1) { - t1f->LastChar = ((pdf_num *) tmp)->value.i; - pdfi_countdown(tmp); - tmp = NULL; - } - else { - t1f->LastChar = 255; - } + pdfi_font_set_first_last_char(ctx, font_dict, (pdf_font *)t1f); - t1f->fake_glyph_names = (gs_string *) gs_alloc_bytes(ctx->memory, t1f->LastChar * sizeof(gs_string), "pdfi_read_type1_font: fake_glyph_names"); - if (!t1f->fake_glyph_names) { - code = gs_note_error(gs_error_VMerror); - goto error; - } - memset(t1f->fake_glyph_names, 0x00, t1f->LastChar * sizeof(gs_string)); - - code = pdfi_dict_knownget_type(ctx, font_dict, "Widths", PDF_ARRAY, &tmp); - if (code > 0) { - int i; - double x_scale; - int num_chars = t1f->LastChar - t1f->FirstChar + 1; - - if (num_chars == pdfi_array_size((pdf_array *) tmp)) { - t1f->Widths = (double *)gs_alloc_bytes(ctx->memory, sizeof(double) * num_chars, "Type 1 font Widths array"); - if (t1f->Widths == NULL) { - code = gs_note_error(gs_error_VMerror); - goto error; - } - - /* Widths are defined assuming a 1000x1000 design grid, but we apply - * them in font space - so undo the 1000x1000 scaling, and apply - * the inverse of the font's x scaling - */ - x_scale = 0.001 / hypot(pfont1->FontMatrix.xx, pfont1->FontMatrix.xy); - - memset(t1f->Widths, 0x00, sizeof(double) * num_chars); - for (i = 0; i < num_chars; i++) { - code = pdfi_array_get_number(ctx, (pdf_array *) tmp, (uint64_t) i, &t1f->Widths[i]); - if (code < 0) - goto error; - t1f->Widths[i] *= x_scale; - } - } - else { - t1f->Widths = NULL; - } - } - pdfi_countdown(tmp); - tmp = NULL; + /* Widths are defined assuming a 1000x1000 design grid, but we apply + * them in font space - so undo the 1000x1000 scaling, and apply + * the inverse of the font's x scaling + */ + x_scale = 0.001 / hypot(pfont1->FontMatrix.xx, pfont1->FontMatrix.xy); + + /* ignore errors with widths... for now */ + if (font_dict != NULL) + (void)pdfi_font_create_widths(ctx, font_dict, (pdf_font*)t1f, x_scale); - code = pdfi_dict_knownget(ctx, font_dict, "Encoding", &tmp); + if (font_dict != NULL) + code = pdfi_dict_knownget(ctx, font_dict, "Encoding", &tmp); + else + code = gs_error_undefined; if (code == 1) { - if ((tmp->type == PDF_NAME || tmp->type == PDF_DICT) && (t1f->descflags & 4) == 0) { + if ((pdfi_type_of(tmp) == PDF_NAME || pdfi_type_of(tmp) == PDF_DICT) && (t1f->descflags & 4) == 0) { code = pdfi_create_Encoding(ctx, tmp, NULL, (pdf_obj **) & t1f->Encoding); if (code >= 0) code = 1; } - else if (tmp->type == PDF_DICT && (t1f->descflags & 4) != 0) { + else if (pdfi_type_of(tmp) == PDF_DICT && (t1f->descflags & 4) != 0) { code = pdfi_create_Encoding(ctx, tmp, (pdf_obj *)fpriv.u.t1.Encoding, (pdf_obj **) & t1f->Encoding); if (code >= 0) code = 1; @@ -685,15 +658,6 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic pdfi_countdown(tmp); tmp = NULL; if (code == 1) { - /* Since the underlying font stream can be shared between font descriptors, - and the font descriptors can be shared between font objects, if we change - the encoding, we can't share cached glyphs with other instances of this - underlying font, so invalidate the UniqueID/XUID so the glyph cache won't - try. - */ - if (uid_is_XUID(&t1f->pfont->UID)) - uid_free(&t1f->pfont->UID, t1f->pfont->memory, "pdfi_read_type1_font"); - uid_set_invalid(&t1f->pfont->UID); } } else { @@ -706,6 +670,15 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic t1f->Encoding = fpriv.u.t1.Encoding; pdfi_countup(t1f->Encoding); } + /* Since the underlying font stream can be shared between font descriptors, + and the font descriptors can be shared between font objects, if we change + the encoding, we can't share cached glyphs with other instances of this + underlying font, so invalidate the UniqueID/XUID so the glyph cache won't + try. + */ + if (uid_is_XUID(&t1f->pfont->UID)) + uid_free(&t1f->pfont->UID, t1f->pfont->memory, "pdfi_read_type1_font"); + uid_set_invalid(&t1f->pfont->UID); t1f->CharStrings = fpriv.u.t1.CharStrings; pdfi_countup(t1f->CharStrings); @@ -713,7 +686,11 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic t1f->Subrs = fpriv.u.t1.Subrs; fpriv.u.t1.Subrs = NULL; - t1f->NumSubrs = fpriv.u.t1.NumSubrs; + + code = pdfi_font_generate_pseudo_XUID(ctx, font_dict, t1f->pfont); + if (code < 0) { + goto error; + } t1f->blenddesignpositions = fpriv.u.t1.blenddesignpositions; pdfi_countup(t1f->blenddesignpositions); @@ -724,6 +701,18 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic t1f->blendaxistypes = fpriv.u.t1.blendaxistypes; pdfi_countup(t1f->blendaxistypes); + key_known = false; + if (t1f->FontDescriptor != NULL) { + code = pdfi_dict_known(ctx, t1f->FontDescriptor, "FontFile", &key_known); + if (code < 0 || key_known == false) { + code = pdfi_dict_known(ctx, t1f->FontDescriptor, "FontFile2", &key_known); + if (code < 0 || key_known == false) { + code = pdfi_dict_known(ctx, t1f->FontDescriptor, "FontFile3", &key_known); + } + } + } + t1f->pfont->is_resource = (key_known == false); + code = gs_definefont(ctx->font_dir, (gs_font *) t1f->pfont); if (code < 0) { goto error; @@ -753,20 +742,190 @@ pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dic pdfi_countdown(fpriv.u.t1.blenddesignmap); pdfi_countdown(fpriv.u.t1.blendfontbbox); pdfi_countdown(fpriv.u.t1.blendaxistypes); + pdfi_countdown(fpriv.u.t1.Subrs); if (fpriv.gsu.gst1.UID.xvalues != NULL) { gs_free_object(ctx->memory, fpriv.gsu.gst1.UID.xvalues, "pdfi_read_type1_font(xuid)"); } - if (fpriv.u.t1.Subrs) { - int i; - for (i = 0; i < fpriv.u.t1.NumSubrs; i++) { - gs_free_object(ctx->memory, fpriv.u.t1.Subrs[i].data, "Subrs[i]"); + if (code < 0) { + pdfi_countdown(t1f); + } + return code; +} + +int +pdfi_copy_type1_font(pdf_context *ctx, pdf_font *spdffont, pdf_dict *font_dict, pdf_font **tpdffont) +{ + int code = 0; + pdf_font_type1 *font = NULL; + gs_font_type1 *spfont1 = (gs_font_type1 *) spdffont->pfont; + gs_font_type1 *dpfont1; + gs_id t_id; + pdf_obj *tmp; + + if (font_dict == NULL) + return_error(gs_error_invalidfont); + + code = pdfi_alloc_t1_font(ctx, &font, font_dict->object_num); + if (code < 0) + return code; + dpfont1 = (gs_font_type1 *) font->pfont; + + t_id = dpfont1->id; + memcpy(dpfont1, spfont1, sizeof(gs_font_type1)); + dpfont1->id = t_id; + dpfont1->FAPI = NULL; + dpfont1->FAPI_font_data = NULL; + + memcpy(font, spdffont, sizeof(pdf_font_type1)); + font->pfont = (gs_font_base *)dpfont1; + font->refcnt = 1; + dpfont1->client_data = (void *)font; + font->filename = NULL; + + dpfont1->notify_list.memory = NULL; + dpfont1->notify_list.first = NULL; + gs_notify_init(&dpfont1->notify_list, dpfont1->memory); + + font->PDF_font = font_dict; + font->object_num = font_dict->object_num; + font->generation_num = font_dict->generation_num; + pdfi_countup(font->PDF_font); + + /* We want basefont and descriptor, but we can live without them */ + font->BaseFont = NULL; + code = pdfi_dict_knownget_type(ctx, font_dict, "BaseFont", PDF_NAME, &font->BaseFont); + if (code < 0) { + pdfi_countdown(font->BaseFont); + font->BaseFont = NULL; + } + font->FontDescriptor = NULL; + code = pdfi_dict_knownget_type(ctx, font_dict, "FontDescriptor", PDF_DICT, (pdf_obj **)&font->FontDescriptor); + if (code < 0) { + pdfi_countdown(font->FontDescriptor); + font->FontDescriptor = NULL; + } + + pdfi_countup(font->Name); + pdfi_countup(font->CharStrings); + pdfi_countup(font->blenddesignpositions); + pdfi_countup(font->blenddesignmap); + pdfi_countup(font->blendfontbbox); + pdfi_countup(font->blendaxistypes); + pdfi_countup(font->Subrs); + + if (font->BaseFont != NULL && ((pdf_name *)font->BaseFont)->length <= gs_font_name_max - 1) { + memcpy(dpfont1->key_name.chars, ((pdf_name *)font->BaseFont)->data, ((pdf_name *)font->BaseFont)->length); + dpfont1->key_name.size = ((pdf_name *)font->BaseFont)->length; + dpfont1->key_name.chars[dpfont1->key_name.size] = '\0'; + memcpy(dpfont1->font_name.chars, ((pdf_name *)font->BaseFont)->data, ((pdf_name *)font->BaseFont)->length); + dpfont1->font_name.size = ((pdf_name *)font->BaseFont)->length; + dpfont1->font_name.chars[dpfont1->font_name.size] = '\0'; + } + + font->Encoding = NULL; + font->ToUnicode = NULL; + font->Widths = NULL; + + pdfi_font_set_first_last_char(ctx, font_dict, (pdf_font *)font); + (void)pdfi_font_create_widths(ctx, font_dict, (pdf_font *)font, (double)(0.001 / hypot(dpfont1->FontMatrix.xx, dpfont1->FontMatrix.xy))); + + font->descflags = 0; + if (font->FontDescriptor != NULL) { + code = pdfi_dict_get_int(ctx, font->FontDescriptor, "Flags", &font->descflags); + if (code >= 0) { + /* If both the symbolic and non-symbolic flag are set, + believe that latter. + */ + if ((font->descflags & 32) != 0) + font->descflags = (font->descflags & ~4); } - gs_free_object(ctx->memory, fpriv.u.t1.Subrs, "Subrs"); } + + if (pdfi_font_known_symbolic(font->BaseFont)) { + font->descflags |= 4; + } + + tmp = NULL; + code = pdfi_dict_knownget(ctx, font_dict, "Encoding", &tmp); + if (code == 1) { + if ((pdfi_type_of(tmp) == PDF_NAME || pdfi_type_of(tmp) == PDF_DICT) && (font->descflags & 4) == 0) { + code = pdfi_create_Encoding(ctx, tmp, NULL, (pdf_obj **) & font->Encoding); + if (code >= 0) + code = 1; + } + else if (pdfi_type_of(tmp) == PDF_DICT && (font->descflags & 4) != 0) { + code = pdfi_create_Encoding(ctx, tmp, (pdf_obj *)spdffont->Encoding, (pdf_obj **) &font->Encoding); + if (code >= 0) + code = 1; + } + else + code = gs_error_undefined; + pdfi_countdown(tmp); + tmp = NULL; + } + else { + pdfi_countdown(tmp); + tmp = NULL; + code = 0; + } + + if (code <= 0) { + font->Encoding = spdffont->Encoding; + pdfi_countup(font->Encoding); + } + + /* Since various aspects of the font may differ (widths, encoding, etc) + we cannot reliably use the UniqueID/XUID for copied fonts. + */ + if (uid_is_XUID(&font->pfont->UID)) + uid_free(&font->pfont->UID, font->pfont->memory, "pdfi_read_type1_font"); + uid_set_invalid(&font->pfont->UID); + + code = pdfi_font_generate_pseudo_XUID(ctx, font_dict, font->pfont); if (code < 0) { - pdfi_countdown(t1f); + goto error; } + + if (ctx->args.ignoretounicode != true) { + code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tmp); + if (code >= 0 && pdfi_type_of(tmp) == PDF_STREAM) { + pdf_cmap *tu = NULL; + code = pdfi_read_cmap(ctx, tmp, &tu); + pdfi_countdown(tmp); + tmp = (pdf_obj *)tu; + } + if (code < 0 || (tmp != NULL && pdfi_type_of(tmp) != PDF_CMAP)) { + pdfi_countdown(tmp); + tmp = NULL; + code = 0; + } + } + else { + tmp = NULL; + } + font->ToUnicode = tmp; + + code = gs_definefont(ctx->font_dir, (gs_font *) font->pfont); + if (code < 0) { + goto error; + } + + code = pdfi_fapi_passfont((pdf_font *) font, 0, NULL, NULL, NULL, 0); + if (code < 0) { + goto error; + } + /* object_num can be zero if the dictionary was defined inline */ + if (font->object_num != 0) { + (void)replace_cache_entry(ctx, (pdf_obj *) font); + } + + *tpdffont = (pdf_font *)font; + +error: + if (code < 0) + pdfi_countdown(font); + return code; } @@ -774,7 +933,6 @@ int pdfi_free_font_type1(pdf_obj *font) { pdf_font_type1 *t1f = (pdf_font_type1 *) font; - int i; gs_free_object(OBJ_MEMORY(font), t1f->pfont, "Free Type 1 gs_font"); @@ -789,20 +947,9 @@ pdfi_free_font_type1(pdf_obj *font) pdfi_countdown(t1f->blenddesignmap); pdfi_countdown(t1f->blendfontbbox); pdfi_countdown(t1f->blendaxistypes); + pdfi_countdown(t1f->Subrs); + pdfi_countdown(t1f->filename); - if (t1f->fake_glyph_names != NULL) { - for (i = 0; i < t1f->LastChar; i++) { - if (t1f->fake_glyph_names[i].data != NULL) - gs_free_object(OBJ_MEMORY(font), t1f->fake_glyph_names[i].data, "Type 1 fake_glyph_name"); - } - gs_free_object(OBJ_MEMORY(font), t1f->fake_glyph_names, "Type 1 fake_glyph_names"); - } - if (t1f->NumSubrs > 0 && t1f->Subrs != NULL) { - for (i = 0; i < t1f->NumSubrs; i++) { - gs_free_object(OBJ_MEMORY(font), t1f->Subrs[i].data, "Type 1 Subr"); - } - gs_free_object(OBJ_MEMORY(font), t1f->Subrs, "Type 1 Subrs"); - } gs_free_object(OBJ_MEMORY(font), t1f->Widths, "Free Type 1 fontWidths"); gs_free_object(OBJ_MEMORY(font), t1f, "Free Type 1 font"); return 0; diff --git a/pdf/pdf_font1.h b/pdf/pdf_font1.h index 0385aafd..5fd41afa 100644 --- a/pdf/pdf_font1.h +++ b/pdf/pdf_font1.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 @@ -21,6 +21,8 @@ int pdfi_read_type1_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, byte *fbuf, int64_t fbuflen, pdf_font **ppdffont); int pdfi_free_font_type1(pdf_obj *font); +int pdfi_copy_type1_font(pdf_context *ctx, pdf_font *spdffont, pdf_dict *font_dict, pdf_font **tpdffont); + int pdfi_t1_global_glyph_code(const gs_font *pfont, gs_const_string *gstr, gs_glyph *pglyph); #endif diff --git a/pdf/pdf_font11.c b/pdf/pdf_font11.c index 09712f60..d01e13e2 100644 --- a/pdf/pdf_font11.c +++ b/pdf/pdf_font11.c @@ -36,12 +36,12 @@ static int pdfi_cidtype2_string_proc(gs_font_type42 * pfont, ulong offset, uint pdf_cidfont_type2 *ttfont = (pdf_cidfont_type2 *)pfont->client_data; int code = 0; - if (offset + length > ttfont->sfnt.size) { + if (offset + length > ttfont->sfnt->length) { *pdata = NULL; code = gs_note_error(gs_error_invalidfont); } else { - *pdata = ttfont->sfnt.data + offset; + *pdata = ttfont->sfnt->data + offset; } return code; } @@ -51,8 +51,8 @@ static int pdfi_cidtype2_CIDMap_proc(gs_font_cid2 *pfont, gs_glyph glyph) pdf_cidfont_type2 *pdffont11 = (pdf_cidfont_type2 *)pfont->client_data; uint gid = glyph - GS_MIN_CID_GLYPH; - if (pdffont11->cidtogidmap.size > (gid << 1) + 1) { - gid = pdffont11->cidtogidmap.data[gid << 1] << 8 | pdffont11->cidtogidmap.data[(gid << 1) + 1]; + if (pdffont11->cidtogidmap != NULL && pdffont11->cidtogidmap->length > (gid << 1) + 1) { + gid = pdffont11->cidtogidmap->data[gid << 1] << 8 | pdffont11->cidtogidmap->data[(gid << 1) + 1]; } return (int)gid; @@ -61,7 +61,7 @@ static int pdfi_cidtype2_CIDMap_proc(gs_font_cid2 *pfont, gs_glyph glyph) static uint pdfi_cidtype2_get_glyph_index(gs_font_type42 *pfont, gs_glyph glyph) { pdf_cidfont_type2 *pdffont11 = (pdf_cidfont_type2 *)pfont->client_data; - uint gid; + uint gid = 0; if (glyph < GS_MIN_CID_GLYPH) { gid = 0; @@ -69,13 +69,10 @@ static uint pdfi_cidtype2_get_glyph_index(gs_font_type42 *pfont, gs_glyph glyph) else { if (glyph < GS_MIN_GLYPH_INDEX) { gid = glyph - GS_MIN_CID_GLYPH; - if (pdffont11->cidtogidmap.size > 0) { - gid = pdffont11->cidtogidmap.data[gid << 1] << 8 | pdffont11->cidtogidmap.data[(gid << 1) + 1]; + if (pdffont11->cidtogidmap != NULL && pdffont11->cidtogidmap->length > (gid << 1) + 1) { + gid = pdffont11->cidtogidmap->data[gid << 1] << 8 | pdffont11->cidtogidmap->data[(gid << 1) + 1]; } } - else { - gid = (uint)(glyph - GS_MIN_GLYPH_INDEX); - } } return gid; @@ -151,15 +148,15 @@ pdfi_cidtype2_enumerate_glyph(gs_font *font, int *pindex, if (*pindex <= 0) *pindex = 0; - if (pdffont11->cidtogidmap.size > 0) { + if (pdffont11->cidtogidmap != NULL && pdffont11->cidtogidmap->length > 0) { do { - *pglyph = pdffont11->cidtogidmap.data[(*pindex) << 1] << 8 | pdffont11->cidtogidmap.data[((*pindex) << 1) + 1]; + *pglyph = pdffont11->cidtogidmap->data[(*pindex) << 1] << 8 | pdffont11->cidtogidmap->data[((*pindex) << 1) + 1]; (*pindex)++; if (*pglyph == 0 && *pindex == 1) /* notdef - special case */ break; - } while (*pglyph == 0 && ((*pindex) << 1) < pdffont11->cidtogidmap.size); + } while (*pglyph == 0 && ((*pindex) << 1) < pdffont11->cidtogidmap->length); - if (((*pindex) << 1) >= pdffont11->cidtogidmap.size) { + if (((*pindex) << 1) >= pdffont11->cidtogidmap->length) { *pindex = 0; } else { @@ -312,9 +309,16 @@ int pdfi_read_cidtype2_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str font->FontDescriptor = (pdf_dict *)fontdesc; fontdesc = NULL; - /* Ownership of buf is now part of the font and managed via its lifetime */ - font->sfnt.data = buf; - font->sfnt.size = buflen; + code = pdfi_object_alloc(ctx, PDF_BUFFER, 0, (pdf_obj **)&font->sfnt); + if (code < 0) { + goto error; + } + pdfi_countup(font->sfnt); + code = pdfi_buffer_set_data((pdf_obj *)font->sfnt, buf, buflen); + if (code < 0) { + goto error; + } + buf = NULL; /* Strictly speaking BaseFont is required, but we can continue without one */ code = pdfi_dict_knownget_type(ctx, font_dict, "BaseFont", PDF_NAME, (pdf_obj **)&obj); @@ -368,18 +372,26 @@ int pdfi_read_cidtype2_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str code = pdfi_dict_knownget(ctx, font_dict, "CIDToGIDMap", (pdf_obj **)&obj); if (code > 0) { - font->cidtogidmap.data = NULL; - font->cidtogidmap.size = 0; /* CIDToGIDMap can only be a stream or a name, and if it's a name it's only permitted to be "/Identity", so ignore it */ - if (obj->type == PDF_STREAM) { + if (pdfi_type_of(obj) == PDF_STREAM) { + byte *d; int64_t sz; - code = pdfi_stream_to_buffer(ctx, (pdf_stream *)obj, &(font->cidtogidmap.data), &sz); + + code = pdfi_object_alloc(ctx, PDF_BUFFER, 0, (pdf_obj **)&font->cidtogidmap); + if (code < 0) { + goto error; + } + pdfi_countup(font->cidtogidmap); + code = pdfi_stream_to_buffer(ctx, (pdf_stream *)obj, &d, &sz); + if (code < 0) { + goto error; + } + code = pdfi_buffer_set_data((pdf_obj *)font->cidtogidmap, d, (int32_t)sz); if (code < 0) { goto error; } - font->cidtogidmap.size = (uint)sz; } pdfi_countdown(obj); obj = NULL; @@ -427,20 +439,31 @@ int pdfi_read_cidtype2_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str pdfi_countdown(obj); obj = NULL; + code = gs_type42_font_init((gs_font_type42 *)font->pfont, 0); if (code < 0) { goto error; } + if (uid_is_XUID(&font->pfont->UID)) + uid_free(&font->pfont->UID, font->pfont->memory, "pdfi_read_type1_font"); + uid_set_invalid(&font->pfont->UID); + font->pfont->id = gs_next_ids(ctx->memory, 1); + + code = pdfi_font_generate_pseudo_XUID(ctx, font_dict, font->pfont); + if (code < 0) + goto error; + + font->orig_glyph_info = font->pfont->procs.glyph_info; font->pfont->procs.glyph_info = pdfi_cidtype2_glyph_info; font->pfont->procs.enumerate_glyph = pdfi_cidtype2_enumerate_glyph; - if (font->cidtogidmap.size > 0) { + if (font->cidtogidmap != NULL) { gs_font_cid2 *cid2 = (gs_font_cid2 *)font->pfont; - if (cid2->data.numGlyphs > font->cidtogidmap.size >> 1) + if (cid2->data.numGlyphs > font->cidtogidmap->length >> 1) cid2->cidata.common.CIDCount = cid2->data.numGlyphs; else { - cid2->cidata.common.CIDCount = font->cidtogidmap.size >> 1; + cid2->cidata.common.CIDCount = font->cidtogidmap->length >> 1; } cid2->cidata.common.MaxCID = cid2->cidata.common.CIDCount; } @@ -453,16 +476,12 @@ int pdfi_read_cidtype2_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str cid2->cidata.orig_procs.get_outline = cid2->data.get_outline; cid2->data.get_glyph_index = pdfi_cidtype2_get_glyph_index; - code = pdfi_font_generate_pseudo_XUID(ctx, font->PDF_font, font->pfont); - if (code < 0) - goto error; - code = gs_definefont(ctx->font_dir, (gs_font *)font->pfont); if (code < 0) { goto error; } - code = pdfi_fapi_passfont((pdf_font *)font, 0, NULL, NULL, font->sfnt.data, font->sfnt.size); + code = pdfi_fapi_passfont((pdf_font *)font, 0, NULL, NULL, font->sfnt->data, font->sfnt->length); if (code < 0) { goto error; } @@ -487,9 +506,9 @@ int pdfi_free_font_cidtype2(pdf_obj *font) gs_font_cid2 *pfont = (gs_font_cid2 *)pdfcidf->pfont; gs_free_object(OBJ_MEMORY(pdfcidf), pfont, "pdfi_free_font_cidtype2(pfont)"); - gs_free_object(OBJ_MEMORY(pdfcidf), pdfcidf->cidtogidmap.data, "pdfi_free_font_cidtype2(cidtogidmap.data)"); - gs_free_object(OBJ_MEMORY(pdfcidf), pdfcidf->sfnt.data, "pdfi_free_font_cidtype2(sfnt.data)"); + pdfi_countdown(pdfcidf->cidtogidmap); + pdfi_countdown(pdfcidf->sfnt); pdfi_countdown(pdfcidf->PDF_font); pdfi_countdown(pdfcidf->BaseFont); pdfi_countdown(pdfcidf->FontDescriptor); @@ -498,6 +517,7 @@ int pdfi_free_font_cidtype2(pdf_obj *font) pdfi_countdown(pdfcidf->W2); pdfi_countdown(pdfcidf->registry); pdfi_countdown(pdfcidf->ordering); + pdfi_countdown(pdfcidf->filename); 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 f4788845..43ebced3 100644 --- a/pdf/pdf_font1C.c +++ b/pdf/pdf_font1C.c @@ -60,7 +60,7 @@ typedef struct pdfi_cff_font_priv_s { pdf_array *W; pdf_array *DW2; pdf_array *W2; - gs_string cidtogidmap; + pdf_buffer *cidtogidmap; pdf_array *FDArray; /* The registry and ordering strings in gs_font_cid0_data are just references to strings assumed to be managed be managed by the interpreter - so we have to stash @@ -145,6 +145,9 @@ pdfi_cff_glyph_data(gs_font_type1 *pfont, gs_glyph glyph, gs_glyph_data_t *pgd) } if (code >= 0) { code = pdfi_dict_get_by_key(ctx, cfffont->CharStrings, glyphname, (pdf_obj **) &charstring); + if (code < 0) { + code = pdfi_map_glyph_name_via_agl(cfffont->CharStrings, glyphname, &charstring); + } if (code >= 0) gs_glyph_data_from_bytes(pgd, charstring->data, 0, charstring->length, NULL); } @@ -205,11 +208,13 @@ pdfi_cff_seac_data(gs_font_type1 *pfont, int ccode, gs_glyph *pglyph, gs_const_s pdfi_countup(glyphname); code = pdfi_dict_get_by_key(ctx, cfffont->CharStrings, glyphname, (pdf_obj **)&charstring); pdfi_countdown(glyphname); - if (code >= 0) - if (pgd != NULL) + if (code >= 0) { + if (pgd != NULL) { gs_glyph_data_from_bytes(pgd, charstring->data, 0, charstring->length, NULL); + } pdfi_countdown(charstring); } + } } return code; @@ -272,8 +277,13 @@ pdfi_cff_enumerate_glyph(gs_font *pfont, int *pindex, else if (pdffont->pdfi_font_type != e_pdf_cidfont_type0 && pdffont->Encoding != NULL) { unsigned int nindex; code = (*ctx->get_glyph_index)(pfont, key->data, key->length, &nindex); - if (code < 0) - *pglyph = GS_NO_GLYPH; + if (code < 0) { + code = (*ctx->get_glyph_index)(pfont, (byte *)".notdef", 7, &nindex); + if (code < 0) + *pglyph = GS_NO_GLYPH; + else + *pglyph = (gs_glyph)nindex; + } else *pglyph = (gs_glyph)nindex; } @@ -296,9 +306,9 @@ pdfi_cff_enumerate_glyph(gs_font *pfont, int *pindex, } if (l > 0) { pdf_cidfont_type0 *cffcidfont = (pdf_cidfont_type0 *) pdffont; - if (cffcidfont->cidtogidmap.size > 0) { - for (j = (cffcidfont->cidtogidmap.size >> 1) - 1; j >= 0; j--) { - if (val == (cffcidfont->cidtogidmap.data[j << 1] << 8 | cffcidfont->cidtogidmap.data[(j << 1) + 1])) { + if (cffcidfont->cidtogidmap != NULL && cffcidfont->cidtogidmap->length > 0) { + for (j = (cffcidfont->cidtogidmap->length >> 1) - 1; j >= 0; j--) { + if (val == (cffcidfont->cidtogidmap->data[j << 1] << 8 | cffcidfont->cidtogidmap->data[(j << 1) + 1])) { val = j; break; } @@ -427,8 +437,8 @@ pdfi_cff_cid_glyph_data(gs_font_base *pbfont, gs_glyph glyph, gs_glyph_data_t *p else gid = glyph - GS_MIN_CID_GLYPH; - if (pdffont9->cidtogidmap.size > (gid << 1) + 1) { - gid = pdffont9->cidtogidmap.data[gid << 1] << 8 | pdffont9->cidtogidmap.data[(gid << 1) + 1]; + if (pdffont9->cidtogidmap != NULL && pdffont9->cidtogidmap->length > (gid << 1) + 1) { + gid = pdffont9->cidtogidmap->data[gid << 1] << 8 | pdffont9->cidtogidmap->data[(gid << 1) + 1]; } l = gs_snprintf(nbuf, sizeof(nbuf), "%" PRId64, gid); @@ -814,7 +824,8 @@ pdfi_read_cff_dict(byte *p, byte *e, pdfi_gs_cff_font_priv *ptpriv, cff_font_off n = 0; while (p < e && code >= 0) { - b0 = *p++; + b0 = *p; + p++; switch (b0) { case 22: @@ -946,9 +957,10 @@ pdfi_read_cff_dict(byte *p, byte *e, pdfi_gs_cff_font_priv *ptpriv, cff_font_off 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; + int nlen = fnamestr->length > gs_font_name_max ? gs_font_name_max : fnamestr->length; + memcpy(ptpriv->font_name.chars, fnamestr->data, nlen); + memcpy(ptpriv->key_name.chars, fnamestr->data, nlen); + ptpriv->font_name.size = ptpriv->key_name.size = nlen; pdfi_countdown(fnamestr); } break; @@ -1698,7 +1710,6 @@ 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) { @@ -1715,11 +1726,7 @@ 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); + code = pdfi_array_put(ctx, font->GlobalSubrs, (uint64_t) i, PDF_NULL_OBJ); if (code < 0) { pdfi_countdown(font->GlobalSubrs); font->GlobalSubrs = NULL; @@ -1736,7 +1743,6 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) 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) { @@ -1751,11 +1757,7 @@ 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); + code = pdfi_array_put(ctx, font->Subrs, (uint64_t) i, PDF_NULL_OBJ); if (code < 0) { pdfi_countdown(font->Subrs); font->Subrs = NULL; @@ -1838,7 +1840,7 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) pdf_font_cff *pdffont = NULL; gs_font_type1 *pt1font; - pdfi_init_cff_font_priv(ctx, &fdptpriv, font->cffdata, (font->cffend - font->cffdata) + 1, true); + pdfi_init_cff_font_priv(ctx, &fdptpriv, font->cffdata, (font->cffend - font->cffdata), true); offsets.private_off = 0; @@ -1879,9 +1881,8 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) /* Check the subrs index */ pdffont->Subrs = NULL; - pdffont->subrs = fdptpriv.pdfcffpriv.subrs; - if (pdffont->subrs) { - p = pdfi_count_cff_index(pdffont->subrs, e, &pdffont->NumSubrs); + if (fdptpriv.pdfcffpriv.subrs) { + p = pdfi_count_cff_index(fdptpriv.pdfcffpriv.subrs, e, &pdffont->NumSubrs); if (!p) { pdffont->Subrs = NULL; pdffont->NumSubrs = 0; @@ -1897,7 +1898,7 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) for (j = 0; j < pdffont->NumSubrs; j++) { pdf_string *subrstr; - p = pdfi_find_cff_index(pdffont->subrs, e, j, &strp, &stre); + p = pdfi_find_cff_index(fdptpriv.pdfcffpriv.subrs, e, j, &strp, &stre); if (p) { code = pdfi_object_alloc(ctx, PDF_STRING, stre - strp, (pdf_obj **) &subrstr); if (code >= 0) { @@ -1975,7 +1976,7 @@ pdfi_read_cff(pdf_context *ctx, pdfi_gs_cff_font_priv *ptpriv) if (g > maxcid) maxcid = g; gs_snprintf(gkey, sizeof(gkey), "%d", g); - code = pdfi_dict_put(ctx, font->CharStrings, gkey, (pdf_obj *) charstr); + code = pdfi_dict_put_unchecked(ctx, font->CharStrings, gkey, (pdf_obj *) charstr); } if (maxcid > ptpriv->pdfcffpriv.cidcount - 1) ptpriv->pdfcffpriv.cidcount = maxcid + 1; @@ -2270,8 +2271,13 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, fbuflen = tlen; } - code = pdfi_dict_knownget_type(ctx, font_dict, "FontDescriptor", PDF_DICT, &fontdesc); - if (code < 0) { + if (font_dict != NULL) { + code = pdfi_dict_knownget_type(ctx, font_dict, "FontDescriptor", PDF_DICT, &fontdesc); + if (code < 0) { + fontdesc = NULL; + } + } + else { fontdesc = NULL; } @@ -2335,7 +2341,7 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, 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) { + if (code <= 0 || pdfi_type_of(suppl) != PDF_INT) { cffcid->supplement = cffpriv.pdfcffpriv.supplement; } else { @@ -2358,9 +2364,6 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, cffcid->PDF_font = font_dict; pdfi_countup(font_dict); - cffcid->cidtogidmap.data = NULL; - cffcid->cidtogidmap.size = 0; - pfont->client_data = cffcid; cffcid->object_num = font_dict->object_num; @@ -2384,23 +2387,35 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pfont->cidata.common.CIDCount = cffpriv.pdfcffpriv.cidcount; - cffcid->cidtogidmap.data = NULL; - cffcid->cidtogidmap.size = 0; + cffcid->cidtogidmap = NULL; code = pdfi_dict_knownget(ctx, font_dict, "CIDToGIDMap", (pdf_obj **) &obj); if (code > 0) { /* CIDToGIDMap can only be a stream or a name, and if it's a name it's only permitted to be "/Identity", so ignore it */ - int64_t size = 0; - if (obj->type == PDF_STREAM) { - code = pdfi_stream_to_buffer(ctx, (pdf_stream *) obj, &(cffcid->cidtogidmap.data), &size); + if (pdfi_type_of(obj) == PDF_STREAM) { + byte *d; + int64_t sz; + + code = pdfi_object_alloc(ctx, PDF_BUFFER, 0, (pdf_obj **)&cffcid->cidtogidmap); + if (code < 0) { + goto error; + } + pdfi_countup(cffcid->cidtogidmap); + code = pdfi_stream_to_buffer(ctx, (pdf_stream *)obj, &d, &sz); + if (code < 0) { + goto error; + } + code = pdfi_buffer_set_data((pdf_obj *)cffcid->cidtogidmap, d, (int32_t)sz); + if (code < 0) { + goto error; + } } pdfi_countdown(obj); obj = NULL; - cffcid->cidtogidmap.size = size; - if (size > 0) { - pfont->cidata.common.CIDCount = size >> 1; + if (cffcid->cidtogidmap != NULL && cffcid->cidtogidmap->length > 0) { + pfont->cidata.common.CIDCount = cffcid->cidtogidmap->length >> 1; } } pfont->cidata.common.MaxCID = pfont->cidata.common.CIDCount - 1; @@ -2438,10 +2453,10 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, else { cffcid->W2 = NULL; } - code = pdfi_font_generate_pseudo_XUID(ctx, font_dict, (gs_font_base *)cffcid->pfont); - if (code < 0) - uid_set_invalid(&cffcid->pfont->UID); - + if (uid_is_XUID(&cffcid->pfont->UID)) + uid_free(&cffcid->pfont->UID, cffcid->pfont->memory, "pdfi_read_type1_font"); + uid_set_invalid(&cffcid->pfont->UID); + cffcid->pfont->id = gs_next_ids(ctx->memory, 1); } else if (forcecid) { pdf_obj *obj; @@ -2581,22 +2596,34 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, cffcid->PDF_font = font_dict; pdfi_countup(font_dict); - cffcid->cidtogidmap.data = NULL; - cffcid->cidtogidmap.size = 0; + cffcid->cidtogidmap = NULL; code = pdfi_dict_knownget(ctx, font_dict, "CIDToGIDMap", (pdf_obj **) &obj); if (code > 0) { + byte *d; + int64_t sz; /* CIDToGIDMap can only be a stream or a name, and if it's a name it's only permitted to be "/Identity", so ignore it */ - int64_t size = 0; - if (obj->type == PDF_STREAM) { - code = pdfi_stream_to_buffer(ctx, (pdf_stream *) obj, &(cffcid->cidtogidmap.data), (int64_t *) &size); + if (pdfi_type_of(obj) == PDF_STREAM) { + code = pdfi_object_alloc(ctx, PDF_BUFFER, 0, (pdf_obj **)&cffcid->cidtogidmap); + if (code < 0) { + goto error; + } + pdfi_countup(cffcid->cidtogidmap); + code = pdfi_stream_to_buffer(ctx, (pdf_stream *)obj, &d, &sz); + if (code < 0) { + goto error; + } + code = pdfi_buffer_set_data((pdf_obj *)cffcid->cidtogidmap, d, (int32_t)sz); + if (code < 0) { + goto error; + } } pdfi_countdown(obj); obj = NULL; - cffcid->cidtogidmap.size = size; - if (size > 0) { - pfont->cidata.common.CIDCount = size >> 1; + + if (cffcid->cidtogidmap != NULL && cffcid->cidtogidmap->length > 0) { + pfont->cidata.common.CIDCount = cffcid->cidtogidmap->length >> 1; } } pfont->cidata.common.MaxCID = pfont->cidata.common.CIDCount - 1; @@ -2635,17 +2662,17 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, cffcid->W2 = NULL; } - code = pdfi_font_generate_pseudo_XUID(ctx, cffcid->PDF_font, ppdfont->pfont); - if (code < 0) - goto error; - + if (uid_is_XUID(&cffcid->pfont->UID)) + uid_free(&cffcid->pfont->UID, cffcid->pfont->memory, "pdfi_read_type1_font"); + uid_set_invalid(&cffcid->pfont->UID); + cffcid->pfont->id = gs_next_ids(ctx->memory, 1); } 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); + code = pdfi_alloc_cff_font(ctx, &cfffont, font_dict != NULL ? font_dict->object_num : 0, false); pfont = (gs_font_type1 *) cfffont->pfont; ppdfont = (pdf_font *) cfffont; @@ -2657,12 +2684,15 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pfont->procs.glyph_info = pdfi_cff_glyph_info; - cfffont->object_num = font_dict->object_num; - cfffont->generation_num = font_dict->generation_num; - cfffont->indirect_num = font_dict->indirect_num; - cfffont->indirect_gen = font_dict->indirect_gen; + if (font_dict) { + cfffont->object_num = font_dict->object_num; + cfffont->generation_num = font_dict->generation_num; + 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); + } - (void)pdfi_dict_knownget_type(ctx, font_dict, "BaseFont", PDF_NAME, &basefont); cfffont->BaseFont = basefont; cfffont->Name = basefont; pdfi_countup(basefont); @@ -2700,69 +2730,23 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, cfffont->descflags |= 4; } - code = pdfi_dict_knownget_type(ctx, font_dict, "FirstChar", PDF_INT, &tmp); - if (code == 1) { - cfffont->FirstChar = ((pdf_num *) tmp)->value.i; - pdfi_countdown(tmp); - tmp = NULL; - } - else { - cfffont->FirstChar = 0; - } - code = pdfi_dict_knownget_type(ctx, font_dict, "LastChar", PDF_INT, &tmp); - if (code == 1) { - cfffont->LastChar = ((pdf_num *) tmp)->value.i; - pdfi_countdown(tmp); - tmp = NULL; - } - else { - cfffont->LastChar = 255; - } + pdfi_font_set_first_last_char(ctx, font_dict, (pdf_font *)cfffont); - cfffont->fake_glyph_names = (gs_string *) gs_alloc_bytes(ctx->memory, cfffont->LastChar * sizeof(gs_string), "pdfi_read_cff_font: fake_glyph_names"); - if (!cfffont->fake_glyph_names) { - code = gs_note_error(gs_error_VMerror); - goto error; - } - memset(cfffont->fake_glyph_names, 0x00, cfffont->LastChar * sizeof(gs_string)); - code = pdfi_dict_knownget_type(ctx, font_dict, "Widths", PDF_ARRAY, &tmp); - if (code > 0) { - int i; - double x_scale; - int num_chars = cfffont->LastChar - cfffont->FirstChar + 1; - - if (num_chars != pdfi_array_size((pdf_array *) tmp)) { - pdfi_countdown(tmp); - code = gs_note_error(gs_error_rangecheck); - goto error; - } - - cfffont->Widths = (double *)gs_alloc_bytes(ctx->memory, sizeof(double) * num_chars, "Type 1C font Widths array"); - if (cfffont->Widths == NULL) { - code = gs_note_error(gs_error_VMerror); - goto error; - } - memset(cfffont->Widths, 0x00, sizeof(double) * num_chars); - - /* Widths are defined assuming a 1000x1000 design grid, but we apply - * them in font space - so undo the 1000x1000 scaling, and apply - * the inverse of the font's x scaling - */ - x_scale = 0.001 / hypot(pfont->FontMatrix.xx, pfont->FontMatrix.xy); - - for (i = 0; i < num_chars; i++) { - code = pdfi_array_get_number(ctx, (pdf_array *) tmp, (uint64_t) i, &cfffont->Widths[i]); - if (code < 0) - goto error; - cfffont->Widths[i] *= x_scale; - } + /* Widths are defined assuming a 1000x1000 design grid, but we apply + * them in font space - so undo the 1000x1000 scaling, and apply + * the inverse of the font's x scaling + */ + if (font_dict != NULL) { + /* ignore errors with widths... for now */ + (void)pdfi_font_create_widths(ctx, font_dict, (pdf_font*)cfffont, (double)(0.001 / hypot(pfont->FontMatrix.xx, pfont->FontMatrix.xy))); } - pdfi_countdown(tmp); - tmp = NULL; - code = pdfi_dict_knownget(ctx, font_dict, "Encoding", &tmp); + if (font_dict != NULL) + code = pdfi_dict_knownget(ctx, font_dict, "Encoding", &tmp); + else + code = gs_error_undefined; if (code == 1) { - if ((cfffont->descflags & 4) != 0 && tmp->type == PDF_DICT) { + if ((cfffont->descflags & 4) != 0 && pdfi_type_of(tmp) == PDF_DICT) { code = pdfi_create_Encoding(ctx, tmp, (pdf_obj *)cffpriv.pdfcffpriv.Encoding, (pdf_obj **) &cfffont->Encoding); if (code >= 0) { pdfi_countdown(cffpriv.pdfcffpriv.Encoding); @@ -2770,7 +2754,7 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, code = 1; } } - else if ((tmp->type == PDF_NAME || tmp->type == PDF_DICT)) { + else if ((pdfi_type_of(tmp) == PDF_NAME || pdfi_type_of(tmp) == PDF_DICT)) { code = pdfi_create_Encoding(ctx, tmp, NULL, (pdf_obj **) &cfffont->Encoding); if (code >= 0) { pdfi_countdown(cffpriv.pdfcffpriv.Encoding); @@ -2780,6 +2764,9 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, } else code = gs_error_undefined; + + if (code == 1) { + } pdfi_countdown(tmp); tmp = NULL; } @@ -2792,15 +2779,27 @@ pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, cfffont->Encoding = cffpriv.pdfcffpriv.Encoding; cffpriv.pdfcffpriv.Encoding = NULL; } - if (ctx->args.ignoretounicode != true) { + + /* Since the underlying font stream can be shared between font descriptors, + and the font descriptors can be shared between font objects, if we change + the encoding, we can't share cached glyphs with other instances of this + underlying font, so invalidate the UniqueID/XUID so the glyph cache won't + try. + */ + if (uid_is_XUID(&cfffont->pfont->UID)) + uid_free(&cfffont->pfont->UID, cfffont->pfont->memory, "pdfi_read_type1_font"); + uid_set_invalid(&cfffont->pfont->UID); + cfffont->pfont->id = gs_next_ids(ctx->memory, 1); + + if (ctx->args.ignoretounicode != true && font_dict != NULL) { code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tounicode); - if (code >= 0 && tounicode->type == PDF_STREAM) { + if (code >= 0 && pdfi_type_of(tounicode) == 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)) { + if (code < 0 || (tounicode != NULL && pdfi_type_of(tounicode) != PDF_CMAP)) { pdfi_countdown(tounicode); tounicode = NULL; code = 0; @@ -2831,6 +2830,11 @@ error: } } else { + code = pdfi_font_generate_pseudo_XUID(ctx, font_dict, ppdfont->pfont); + if (code < 0) { + goto error; + } + code = gs_definefont(ctx->font_dir, (gs_font *) ppdfont->pfont); if (code >= 0) @@ -2840,6 +2844,7 @@ 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; @@ -2898,6 +2903,168 @@ pdfi_read_type1C_font(pdf_context *ctx, pdf_dict *font_dict, } int +pdfi_copy_cff_font(pdf_context *ctx, pdf_font *spdffont, pdf_dict *font_dict, pdf_font **tpdffont) +{ + int code = 0; + pdf_font_cff *font = NULL; + gs_font_type1 *spfont1 = (gs_font_type1 *) spdffont->pfont; + gs_font_type1 *dpfont1; + gs_id t_id; + pdf_obj *tmp; + + if (font_dict == NULL) + return_error(gs_error_invalidfont); + + code = pdfi_alloc_cff_font(ctx, &font, font_dict->object_num, false); + if (code < 0) + return code; + dpfont1 = (gs_font_type1 *) font->pfont; + + t_id = dpfont1->id; + memcpy(dpfont1, spfont1, sizeof(gs_font_type1)); + dpfont1->id = t_id; + dpfont1->FAPI = NULL; + dpfont1->FAPI_font_data = NULL; + dpfont1->notify_list.memory = NULL; + dpfont1->notify_list.first = NULL; + gs_notify_init(&dpfont1->notify_list, dpfont1->memory); + + memcpy(font, spdffont, sizeof(pdf_font_type1)); + font->refcnt = 1; + font->pfont = (gs_font_base *)dpfont1; + dpfont1->client_data = (void *)font; + font->filename = NULL; + + font->PDF_font = font_dict; + font->object_num = font_dict->object_num; + font->generation_num = font_dict->generation_num; + pdfi_countup(font->PDF_font); + + /* We want basefont and descriptor, but we can live without them */ + font->BaseFont = NULL; + (void)pdfi_dict_knownget_type(ctx, font_dict, "BaseFont", PDF_NAME, &font->BaseFont); + font->FontDescriptor = NULL; + (void)pdfi_dict_knownget_type(ctx, font_dict, "FontDescriptor", PDF_DICT, (pdf_obj **)&font->FontDescriptor); + + pdfi_countup(font->Name); + pdfi_countup(font->CharStrings); + pdfi_countup(font->Subrs); + pdfi_countup(font->GlobalSubrs); + + if (font->BaseFont != NULL && ((pdf_name *)font->BaseFont)->length <= gs_font_name_max) { + memcpy(dpfont1->key_name.chars, ((pdf_name *)font->BaseFont)->data, ((pdf_name *)font->BaseFont)->length); + dpfont1->key_name.size = ((pdf_name *)font->BaseFont)->length; + memcpy(dpfont1->font_name.chars, ((pdf_name *)font->BaseFont)->data, ((pdf_name *)font->BaseFont)->length); + dpfont1->font_name.size = ((pdf_name *)font->BaseFont)->length; + } + + font->Encoding = NULL; + font->ToUnicode = NULL; + font->Widths = NULL; + + pdfi_font_set_first_last_char(ctx, font_dict, (pdf_font *)font); + (void)pdfi_font_create_widths(ctx, font_dict, (pdf_font *)font, (double)(0.001 / hypot(dpfont1->FontMatrix.xx, dpfont1->FontMatrix.xy))); + + font->descflags = 0; + if (font->FontDescriptor != NULL) { + code = pdfi_dict_get_int(ctx, font->FontDescriptor, "Flags", &font->descflags); + if (code >= 0) { + /* If both the symbolic and non-symbolic flag are set, + believe that latter. + */ + if ((font->descflags & 32) != 0) + font->descflags = (font->descflags & ~4); + } + } + + if (pdfi_font_known_symbolic(font->BaseFont)) { + font->descflags |= 4; + } + + + tmp = NULL; + code = pdfi_dict_knownget(ctx, font_dict, "Encoding", &tmp); + if (code == 1) { + if ((pdfi_type_of(tmp) == PDF_NAME || pdfi_type_of(tmp) == PDF_DICT) && (font->descflags & 4) == 0) { + code = pdfi_create_Encoding(ctx, tmp, NULL, (pdf_obj **) & font->Encoding); + if (code >= 0) + code = 1; + } + else if (pdfi_type_of(tmp) == PDF_DICT && (font->descflags & 4) != 0) { + code = pdfi_create_Encoding(ctx, tmp, (pdf_obj *)spdffont->Encoding, (pdf_obj **) &font->Encoding); + if (code >= 0) + code = 1; + } + else + code = gs_error_undefined; + pdfi_countdown(tmp); + tmp = NULL; + } + else { + pdfi_countdown(tmp); + tmp = NULL; + code = 0; + } + + if (code <= 0) { + font->Encoding = spdffont->Encoding; + pdfi_countup(font->Encoding); + } + + /* Since various aspects of the font may differ (widths, encoding, etc) + we cannot reliably use the UniqueID/XUID for copied fonts. + */ + if (uid_is_XUID(&font->pfont->UID)) + uid_free(&font->pfont->UID, font->pfont->memory, "pdfi_read_type1_font"); + uid_set_invalid(&font->pfont->UID); + + code = pdfi_font_generate_pseudo_XUID(ctx, font_dict, font->pfont); + if (code < 0) { + goto error; + } + + if (ctx->args.ignoretounicode != true) { + code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tmp); + if (code >= 0 && pdfi_type_of(tmp) == PDF_STREAM) { + pdf_cmap *tu = NULL; + code = pdfi_read_cmap(ctx, tmp, &tu); + pdfi_countdown(tmp); + tmp = (pdf_obj *)tu; + } + if (code < 0 || (tmp != NULL && pdfi_type_of(tmp) != PDF_CMAP)) { + pdfi_countdown(tmp); + tmp = NULL; + code = 0; + } + } + else { + tmp = NULL; + } + font->ToUnicode = tmp; + + code = gs_definefont(ctx->font_dir, (gs_font *) font->pfont); + if (code < 0) { + goto error; + } + + code = pdfi_fapi_passfont((pdf_font *) font, 0, NULL, NULL, NULL, 0); + if (code < 0) { + goto error; + } + /* object_num can be zero if the dictionary was defined inline */ + if (font->object_num != 0) { + (void)replace_cache_entry(ctx, (pdf_obj *) font); + } + + *tpdffont = (pdf_font *)font; + +error: + if (code < 0) + pdfi_countdown(font); + return code; +} + +int pdfi_free_font_cff(pdf_obj *font) { pdf_font_cff *pdfontcff = (pdf_font_cff *) font; @@ -2913,8 +3080,8 @@ pdfi_free_font_cff(pdf_obj *font) pdfi_countdown(pdfontcff->GlobalSubrs); pdfi_countdown(pdfontcff->Encoding); pdfi_countdown(pdfontcff->ToUnicode); + pdfi_countdown(pdfontcff->filename); - gs_free_object(OBJ_MEMORY(font), pdfontcff->fake_glyph_names, "Type 2 fake_glyph_names"); gs_free_object(OBJ_MEMORY(font), pdfontcff->Widths, "Type 2 fontWidths"); gs_free_object(OBJ_MEMORY(font), pdfontcff, "pdfi_free_font_cff(pbfont)"); @@ -2948,8 +3115,9 @@ pdfi_free_font_cidtype0(pdf_obj *font) pdfi_countdown(pdfont0->FDArray); pdfi_countdown(pdfont0->registry); pdfi_countdown(pdfont0->ordering); + pdfi_countdown(pdfont0->cidtogidmap); + pdfi_countdown(pdfont0->filename); - gs_free_object(OBJ_MEMORY(font), pdfont0->cidtogidmap.data, "pdfi_free_font_cff(cidtogidmap.data)"); gs_free_object(OBJ_MEMORY(font), pdfont0, "pdfi_free_font_cff(pbfont)"); return 0; diff --git a/pdf/pdf_font1C.h b/pdf/pdf_font1C.h index e02b6342..ea8c7084 100644 --- a/pdf/pdf_font1C.h +++ b/pdf/pdf_font1C.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 @@ -19,10 +19,11 @@ #define PDF_TYPE1C_FONT int pdfi_read_type1C_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, pdf_font **ppdffont); int pdfi_cff_global_glyph_code(const gs_font *pfont, gs_const_string *gstr, gs_glyph *pglyph); -int pdfi_free_font_cff(pdf_obj *font); int pdfi_read_cff_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, byte *pfbuf, int64_t fbuflen, bool forcecid, pdf_font **ppdffont); +int pdfi_copy_cff_font(pdf_context *ctx, pdf_font *spdffont, pdf_dict *font_dict, pdf_font **tpdffont); +int pdfi_free_font_cff(pdf_obj *font); int pdfi_free_font_cidtype0(pdf_obj *font); #endif diff --git a/pdf/pdf_font3.c b/pdf/pdf_font3.c index 7ecd3bff..708e7658 100644 --- a/pdf/pdf_font3.c +++ b/pdf/pdf_font3.c @@ -74,7 +74,7 @@ pdfi_type3_build_char(gs_show_enum * penum, gs_gstate * pgs, gs_font * pfont, } if (code < 0) goto build_char_error; - if (CharProc->type != PDF_STREAM) { + if (pdfi_type_of(CharProc) != PDF_STREAM) { code = gs_note_error(gs_error_typecheck); goto build_char_error; } @@ -223,6 +223,8 @@ int pdfi_free_font_type3(pdf_obj *font) pdfi_countdown(t3font->CharProcs); pdfi_countdown(t3font->Encoding); pdfi_countdown(t3font->ToUnicode); + pdfi_countdown(t3font->filename); /* Should never exist, but just in case */ + gs_free_object(OBJ_MEMORY(font), font, "Free type 3 font"); return 0; } @@ -230,10 +232,9 @@ int pdfi_free_font_type3(pdf_obj *font) int pdfi_read_type3_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream_dict, pdf_dict *page_dict, pdf_font **ppdffont) { - int code = 0, i, num_chars = 0; + int code = 0; pdf_font_type3 *font = NULL; pdf_obj *obj = NULL; - double f; pdf_obj *tounicode = NULL; *ppdffont = NULL; @@ -271,44 +272,15 @@ int pdfi_read_type3_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream if (code < 0) goto font3_error; - code = pdfi_dict_get_number(ctx, font_dict, "FirstChar", &f); - if (code < 0) - goto font3_error; - font->FirstChar = (int)f; - - code = pdfi_dict_get_number(ctx, font_dict, "LastChar", &f); - if (code < 0) - goto font3_error; - font->LastChar = (int)f; - num_chars = (font->LastChar - font->FirstChar) + 1; code = pdfi_dict_knownget_type(ctx, font_dict, "FontDescriptor", PDF_DICT, (pdf_obj **)&font->FontDescriptor); if (code < 0) goto font3_error; - code = pdfi_dict_knownget_type(ctx, font_dict, "Widths", PDF_ARRAY, (pdf_obj **)&obj); - if (code < 0) - goto font3_error; - if (code > 0) { - if (num_chars != pdfi_array_size((pdf_array *)obj)) { - code = gs_note_error(gs_error_rangecheck); - goto font3_error; - } + pdfi_font_set_first_last_char(ctx, font_dict, (pdf_font *)font); + /* ignore errors with widths... for now */ + (void)pdfi_font_create_widths(ctx, font_dict, (pdf_font*)font, 1.0); - font->Widths = (double *)gs_alloc_bytes(ctx->memory, sizeof(double) * num_chars, "type 3 font Widths array"); - if (font->Widths == NULL) { - code = gs_note_error(gs_error_VMerror); - goto font3_error; - } - memset(font->Widths, 0x00, sizeof(double) * num_chars); - for (i = 0; i < num_chars; i++) { - code = pdfi_array_get_number(ctx, (pdf_array *)obj, (uint64_t)i, &font->Widths[i]); - if (code < 0) - goto font3_error; - } - } - pdfi_countdown(obj); - obj = NULL; code = pdfi_dict_get(ctx, font_dict, "Encoding", &obj); if (code < 0) @@ -324,13 +296,13 @@ int pdfi_read_type3_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *stream if (ctx->args.ignoretounicode != true) { code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tounicode); - if (code >= 0 && tounicode->type == PDF_STREAM) { + if (code >= 0 && pdfi_type_of(tounicode) == 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)) { + if (code < 0 || (tounicode != NULL && pdfi_type_of(tounicode) != PDF_CMAP)) { pdfi_countdown(tounicode); tounicode = NULL; code = 0; diff --git a/pdf/pdf_fontTT.c b/pdf/pdf_fontTT.c index aec3ee7b..d7d43e62 100644 --- a/pdf/pdf_fontTT.c +++ b/pdf/pdf_fontTT.c @@ -44,12 +44,12 @@ pdfi_ttf_string_proc(gs_font_type42 * pfont, ulong offset, uint length, const by pdf_font_truetype *ttfont = (pdf_font_truetype *)pfont->client_data; int code = 0; - if ((uint64_t)offset + length > ttfont->sfnt.size) { + if ((uint64_t)offset + length > ttfont->sfnt->length) { *pdata = NULL; code = gs_note_error(gs_error_invalidfont); } else { - *pdata = ttfont->sfnt.data + offset; + *pdata = ttfont->sfnt->data + offset; } return code; } @@ -356,11 +356,10 @@ static int pdfi_set_type42_data_procs(gs_font_type42 *pfont) 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; + int code = 0, i; pdf_obj *fontdesc = NULL; pdf_obj *obj = NULL; pdf_obj *basefont = NULL; - double f; int64_t descflags; bool encoding_known = false; bool forced_symbolic = false; @@ -371,96 +370,71 @@ int pdfi_read_truetype_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str *ppdffont = NULL; - code = pdfi_dict_knownget_type(ctx, font_dict, "FontDescriptor", PDF_DICT, &fontdesc); - if (code <= 0) { - code = gs_note_error(gs_error_invalidfont); - goto error; - } + if (font_dict != NULL) + (void)pdfi_dict_knownget_type(ctx, font_dict, "FontDescriptor", PDF_DICT, &fontdesc); if ((code = pdfi_alloc_tt_font(ctx, &font, false)) < 0) { 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; + if (font_dict != NULL) { + 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; - code = pdfi_dict_get_number(ctx, font_dict, "FirstChar", &f); + pdfi_font_set_first_last_char(ctx, font_dict, (pdf_font *)font); + + code = pdfi_object_alloc(ctx, PDF_BUFFER, 0, (pdf_obj **)&font->sfnt); if (code < 0) { goto error; } - font->FirstChar = (int)f; - - code = pdfi_dict_get_number(ctx, font_dict, "LastChar", &f); + pdfi_countup(font->sfnt); + code = pdfi_buffer_set_data((pdf_obj *)font->sfnt, buf, buflen); if (code < 0) { goto error; } - font->LastChar = (int)f; - - num_chars = font->LastChar - font->FirstChar + 1; - - font->sfnt.data = buf; - font->sfnt.size = buflen; buf = NULL; /* Strictly speaking BaseFont is required, but we can continue without one */ - code = pdfi_dict_knownget_type(ctx, font_dict, "BaseFont", PDF_NAME, (pdf_obj **)&basefont); - if (code > 0) { - pdf_name *nobj = (pdf_name *)basefont; - int nlen = nobj->length > gs_font_name_max ? gs_font_name_max : nobj->length; - - memcpy(font->pfont->key_name.chars, nobj->data, nlen); - font->pfont->key_name.chars[nlen] = 0; - font->pfont->key_name.size = nlen; - memcpy(font->pfont->font_name.chars, nobj->data, nlen); - font->pfont->font_name.chars[nlen] = 0; - font->pfont->font_name.size = nlen; - pdfi_countdown(obj); - obj = NULL; + if (font_dict != NULL) { + code = pdfi_dict_knownget_type(ctx, font_dict, "BaseFont", PDF_NAME, (pdf_obj **)&basefont); + if (code > 0) { + pdf_name *nobj = (pdf_name *)basefont; + int nlen = nobj->length > gs_font_name_max ? gs_font_name_max : nobj->length; + + memcpy(font->pfont->key_name.chars, nobj->data, nlen); + font->pfont->key_name.chars[nlen] = 0; + font->pfont->key_name.size = nlen; + memcpy(font->pfont->font_name.chars, nobj->data, nlen); + font->pfont->font_name.chars[nlen] = 0; + font->pfont->font_name.size = nlen; + pdfi_countdown(obj); + obj = NULL; + } } font->BaseFont = basefont; basefont = NULL; font->PDF_font = font_dict; pdfi_countup(font_dict); - code = pdfi_dict_knownget_type(ctx, font_dict, "Widths", PDF_ARRAY, (pdf_obj **)&obj); - if (code < 0) - goto error; - if (code > 0) { - if (num_chars != pdfi_array_size((pdf_array *)obj)) { - code = gs_note_error(gs_error_rangecheck); - goto error; - } - - font->Widths = (double *)gs_alloc_bytes(ctx->memory, sizeof(double) * num_chars, "truetype font Widths array"); - if (font->Widths == NULL) { - code = gs_note_error(gs_error_VMerror); - goto error; - } - memset(font->Widths, 0x00, sizeof(double) * num_chars); - for (i = 0; i < num_chars; i++) { - code = pdfi_array_get_number(ctx, (pdf_array *)obj, (uint64_t)i, &font->Widths[i]); - if (code < 0) - goto error; - font->Widths[i] /= 1000; - } - } - pdfi_countdown(obj); - obj = NULL; + /* ignore errors with widths... for now */ + if (font_dict != NULL) + (void)pdfi_font_create_widths(ctx, font_dict, (pdf_font*)font, 0.001); - if (ctx->args.ignoretounicode != true) { + if (ctx->args.ignoretounicode != true && font_dict != NULL) { code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tounicode); - if (code >= 0 && tounicode->type == PDF_STREAM) { + if (code >= 0 && pdfi_type_of(tounicode) == 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)) { + if (code < 0 || (tounicode != NULL && pdfi_type_of(tounicode) != PDF_CMAP)) { pdfi_countdown(tounicode); tounicode = NULL; code = 0; @@ -472,11 +446,20 @@ int pdfi_read_truetype_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str font->ToUnicode = tounicode; tounicode = NULL; - code = pdfi_dict_get_int(ctx, font->FontDescriptor, "Flags", &descflags); - if (code < 0) + if (font->FontDescriptor != NULL) { + code = pdfi_dict_get_int(ctx, font->FontDescriptor, "Flags", &descflags); + if (code < 0) + descflags = 0; + } + else { descflags = 0; + } + + if (font_dict != NULL) + code = pdfi_dict_get(ctx, font_dict, "Encoding", &obj); + else + code = gs_error_undefined; - code = pdfi_dict_get(ctx, font_dict, "Encoding", &obj); if (code < 0) { static const char encstr[] = "WinAnsiEncoding"; code = pdfi_name_alloc(ctx, (byte *)encstr, strlen(encstr), (pdf_obj **)&obj); @@ -508,13 +491,6 @@ int pdfi_read_truetype_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str pdfi_countdown(obj); obj = NULL; - font->fake_glyph_names = (gs_string *)gs_alloc_bytes(OBJ_MEMORY(font), font->LastChar * sizeof(gs_string), "pdfi_read_truetype_font: fake_glyph_names"); - if (!font->fake_glyph_names) { - code = gs_note_error(gs_error_VMerror); - goto error; - } - memset(font->fake_glyph_names, 0x00, font->LastChar * sizeof(gs_string)); - code = gs_type42_font_init((gs_font_type42 *)font->pfont, 0); if (code < 0) { goto error; @@ -565,7 +541,9 @@ int pdfi_read_truetype_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str font->descflags = descflags; } else if (encoding_known == true) { - static const char encstr[] = "WinAnsiEncoding"; + static const char mrencstr[] = "MacRomanEncoding"; + static const char waencstr[] = "WinAnsiEncoding"; + const char *encstr = ((cmaps_available & CMAP_TABLE_31_PRESENT) == CMAP_TABLE_31_PRESENT) ? waencstr : mrencstr; font->descflags = descflags & ~4; code = pdfi_name_alloc(ctx, (byte *)encstr, strlen(encstr), (pdf_obj **)&obj); if (code >= 0) @@ -586,12 +564,30 @@ int pdfi_read_truetype_font(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *str font->descflags = descflags; } + if (uid_is_XUID(&font->pfont->UID)) + uid_free(&font->pfont->UID, font->pfont->memory, "pdfi_read_type1_font"); + uid_set_invalid(&font->pfont->UID); + + code = pdfi_font_generate_pseudo_XUID(ctx, font_dict, font->pfont); + if (code < 0) { + goto error; + } + + if ((font->descflags & 4) == 0) { + /* Horrid hacky solution */ + /* We don't want to draw the TTF notdef */ + gs_font_type42 *gst42 = ((gs_font_type42 *)font->pfont); + if (gst42->data.len_glyphs != NULL && gst42->data.len_glyphs[0] > 10) { + gst42->data.len_glyphs[0] = 0; + } + } + code = gs_definefont(ctx->font_dir, (gs_font *)font->pfont); if (code < 0) { goto error; } - code = pdfi_fapi_passfont((pdf_font *)font, 0, NULL, NULL, font->sfnt.data, font->sfnt.size); + code = pdfi_fapi_passfont((pdf_font *)font, 0, NULL, NULL, font->sfnt->data, font->sfnt->length); if (code < 0) { goto error; } @@ -613,30 +609,175 @@ error: return code; } +int +pdfi_copy_truetype_font(pdf_context *ctx, pdf_font *spdffont, pdf_dict *font_dict, pdf_font **tpdffont) +{ + int code = 0; + pdf_font_truetype *font = NULL; + gs_font_type42 *spfont1 = (gs_font_type42 *) spdffont->pfont; + gs_font_type42 *dpfont42; + gs_id t_id; + pdf_obj *tmp; + + if (font_dict == NULL) + return_error(gs_error_invalidfont); + + code = pdfi_alloc_tt_font(ctx, &font, font_dict->object_num); + if (code < 0) + return code; + dpfont42 = (gs_font_type42 *) font->pfont; + + t_id = dpfont42->id; + memcpy(dpfont42, spfont1, sizeof(gs_font_type42)); + dpfont42->id = t_id; + dpfont42->FAPI = NULL; + dpfont42->FAPI_font_data = NULL; + dpfont42->notify_list.memory = NULL; + dpfont42->notify_list.first = NULL; + gs_notify_init(&dpfont42->notify_list, dpfont42->memory); + + memcpy(font, spdffont, sizeof(pdf_font_truetype)); + font->refcnt = 1; + font->filename = NULL; + + font->pfont = (gs_font_base *)dpfont42; + dpfont42->client_data = (void *)font; + + font->PDF_font = font_dict; + font->object_num = font_dict->object_num; + font->generation_num = font_dict->generation_num; + pdfi_countup(font->PDF_font); + + /* We want basefont and descriptor, but we can live without them */ + font->BaseFont = NULL; + (void)pdfi_dict_knownget_type(ctx, font_dict, "BaseFont", PDF_NAME, &font->BaseFont); + font->FontDescriptor = NULL; + (void)pdfi_dict_knownget_type(ctx, font_dict, "FontDescriptor", PDF_DICT, (pdf_obj **)&font->FontDescriptor); + + pdfi_countup(font->sfnt); + + if (font->BaseFont != NULL && ((pdf_name *)font->BaseFont)->length <= gs_font_name_max) { + memcpy(dpfont42->key_name.chars, ((pdf_name *)font->BaseFont)->data, ((pdf_name *)font->BaseFont)->length); + dpfont42->key_name.size = ((pdf_name *)font->BaseFont)->length; + memcpy(dpfont42->font_name.chars, ((pdf_name *)font->BaseFont)->data, ((pdf_name *)font->BaseFont)->length); + dpfont42->font_name.size = ((pdf_name *)font->BaseFont)->length; + } + + font->Encoding = NULL; + font->ToUnicode = NULL; + font->Widths = NULL; + + pdfi_font_set_first_last_char(ctx, font_dict, (pdf_font *)font); + (void)pdfi_font_create_widths(ctx, font_dict, (pdf_font*)font, (double)0.001); + + font->descflags = 0; + if (font->FontDescriptor != NULL) { + code = pdfi_dict_get_int(ctx, font->FontDescriptor, "Flags", &font->descflags); + if (code >= 0) { + /* If both the symbolic and non-symbolic flag are set, + believe that latter. + */ + if ((font->descflags & 32) != 0) + font->descflags = (font->descflags & ~4); + } + } + + tmp = NULL; + code = pdfi_dict_knownget(ctx, font_dict, "Encoding", &tmp); + if (code == 1) { + if ((pdfi_type_of(tmp) == PDF_NAME || pdfi_type_of(tmp) == PDF_DICT) && (font->descflags & 4) == 0) { + code = pdfi_create_Encoding(ctx, tmp, NULL, (pdf_obj **) & font->Encoding); + } + else if (pdfi_type_of(tmp) == PDF_DICT && (font->descflags & 4) != 0) { + code = pdfi_create_Encoding(ctx, tmp, (pdf_obj *)spdffont->Encoding, (pdf_obj **) &font->Encoding); + } + pdfi_countdown(tmp); + tmp = NULL; + } + else { + pdfi_countdown(tmp); + tmp = NULL; + code = 0; + } + if (code < 0) { + goto error; + } + + /* Since various aspects of the font may differ (widths, encoding, etc) + we cannot reliably use the UniqueID/XUID for copied fonts. + */ + if (uid_is_XUID(&font->pfont->UID)) + uid_free(&font->pfont->UID, font->pfont->memory, "pdfi_read_type1_font"); + uid_set_invalid(&font->pfont->UID); + + code = pdfi_font_generate_pseudo_XUID(ctx, font_dict, font->pfont); + if (code < 0) { + goto error; + } + + if (code <= 0) { + font->Encoding = spdffont->Encoding; + pdfi_countup(font->Encoding); + } + + if (ctx->args.ignoretounicode != true) { + code = pdfi_dict_get(ctx, font_dict, "ToUnicode", (pdf_obj **)&tmp); + if (code >= 0 && pdfi_type_of(tmp) == PDF_STREAM) { + pdf_cmap *tu = NULL; + code = pdfi_read_cmap(ctx, tmp, &tu); + pdfi_countdown(tmp); + tmp = (pdf_obj *)tu; + } + if (code < 0 || (tmp != NULL && pdfi_type_of(tmp) != PDF_CMAP)) { + pdfi_countdown(tmp); + tmp = NULL; + code = 0; + } + } + else { + tmp = NULL; + } + font->ToUnicode = tmp; + code = gs_definefont(ctx->font_dir, (gs_font *) font->pfont); + if (code < 0) { + goto error; + } + + code = pdfi_fapi_passfont((pdf_font *) font, 0, NULL, NULL, NULL, 0); + if (code < 0) { + goto error; + } + /* object_num can be zero if the dictionary was defined inline */ + if (font->object_num != 0) { + (void)replace_cache_entry(ctx, (pdf_obj *) font); + } + + *tpdffont = (pdf_font *)font; + +error: + if (code < 0) + pdfi_countdown(font); + return code; +} + int pdfi_free_font_truetype(pdf_obj *font) { pdf_font_truetype *ttfont = (pdf_font_truetype *)font; - int i; + if (ttfont->pfont) gs_free_object(OBJ_MEMORY(ttfont), ttfont->pfont, "Free TrueType gs_font"); if (ttfont->Widths) gs_free_object(OBJ_MEMORY(ttfont), ttfont->Widths, "Free TrueType font Widths array"); - if (ttfont->fake_glyph_names != NULL) { - for (i = 0; i < ttfont->LastChar; i++) { - if (ttfont->fake_glyph_names[i].data != NULL) - gs_free_object(OBJ_MEMORY(ttfont), ttfont->fake_glyph_names[i].data, "Free TrueType fake_glyph_name"); - } - } - gs_free_object(OBJ_MEMORY(ttfont), ttfont->fake_glyph_names, "Free TrueType fake_glyph_names"); - gs_free_object(OBJ_MEMORY(ttfont), ttfont->sfnt.data, "Free TrueType font sfnt buffer"); - + pdfi_countdown(ttfont->sfnt); pdfi_countdown(ttfont->FontDescriptor); pdfi_countdown(ttfont->Encoding); pdfi_countdown(ttfont->BaseFont); pdfi_countdown(ttfont->PDF_font); pdfi_countdown(ttfont->ToUnicode); + pdfi_countdown(ttfont->filename); + 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 90e4086e..14932f66 100644 --- a/pdf/pdf_fontTT.h +++ b/pdf/pdf_fontTT.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 @@ -19,6 +19,7 @@ #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, int findex, pdf_font **ppdffont); +int pdfi_copy_truetype_font(pdf_context *ctx, pdf_font *spdffont, pdf_dict *font_dict, pdf_font **tpdffont); int pdfi_free_font_truetype(pdf_obj *font); #endif diff --git a/pdf/pdf_font_types.h b/pdf/pdf_font_types.h index f83a0851..f29088f5 100644 --- a/pdf/pdf_font_types.h +++ b/pdf/pdf_font_types.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 @@ -84,18 +84,18 @@ typedef enum pdf_font_type_e { pdf_dict *PDF_font; /* The original font dictionary from the PDF file */\ pdf_obj *BaseFont; /* Should be a name object, but best allow for strings as well */\ pdf_dict *FontDescriptor; /* For PDF up to 1.4 this may be absent for the base 14 */ \ - int64_t descflags - + int64_t descflags; \ + pdf_obj *ToUnicode; /* Name or stream (technically should be a stream, but we've seen Identity names */ \ + pdf_string *filename /* If we read this from disk, this is the file it came from */ #define pdf_font_common \ pdf_font_base;\ pdf_obj *Name; /* Should be a name object, but best allow for strings as well */\ unsigned int FirstChar; /* For PDF up to 1.4 this may be absent for the base 14 */\ unsigned int LastChar; /* For PDF up to 1.4 this may be absent for the base 14 */\ - double *Widths; /* For PDF up to 1.4 this may be absent for the base 14 */\ - pdf_array *Encoding; /* Array built from name or dictionary */\ - pdf_obj *ToUnicode; /* Name or stream (technically should be a stream, but we've seen Identity names */ \ - gs_string *fake_glyph_names /* For when we encounter a glyph not in the Encoding */ + double MissingWidth; \ + double *Widths; /* For PDF up to 1.4 this may be absent for the base 14 */\ + pdf_array *Encoding /* Array built from name or dictionary */\ /* The registry and ordering strings in gs_font_cid0_data are just references to @@ -112,7 +112,7 @@ typedef enum pdf_font_type_e { pdf_string *registry; \ pdf_string *ordering; \ int supplement; \ - gs_string cidtogidmap; \ + pdf_buffer *cidtogidmap; \ bool substitute; /* We need to know what a CIDFont is a substitute */ \ font_proc_glyph_info((*orig_glyph_info)) @@ -147,16 +147,14 @@ typedef struct pdf_font_type0_s { pdf_obj *Encoding; /* CMap */ pdf_array *DescendantFonts; /* A single element array specifying the CIDFont dictionary */ - pdf_obj *ToUnicode; /* Name or stream (technically shoudl be a stream, but we've seen Identity names */ pdfi_cid_decoding_t *decoding; /* Used when substituting a non-Identity CIDFont */ pdfi_cid_subst_nwp_table_t *substnwp; /* Also used for CIDFont substitions */ } pdf_font_type0; typedef struct pdf_font_type1_s { pdf_font_common; - gs_string *Subrs; + pdf_array *Subrs; pdf_dict *CharStrings; - int NumSubrs; /* Multiple Master Support - weightvector is stored in gs_font_type1 */ pdf_array *blenddesignpositions; pdf_array *blenddesignmap; @@ -171,11 +169,6 @@ typedef struct pdf_font_cff_s { pdf_array *GlobalSubrs; int NumGlobalSubrs; pdf_dict *CharStrings; - byte *cffdata; - byte *cffend; - byte *gsubrs; - byte *subrs; - byte *charstrings; int ncharstrings; } pdf_font_cff; @@ -197,7 +190,7 @@ typedef enum { typedef struct pdf_font_truetype_s { pdf_font_common; - gs_string sfnt; + pdf_buffer *sfnt; pdfi_truetype_cmap cmap; } pdf_font_truetype; @@ -212,11 +205,6 @@ typedef struct pdf_cidfont_type0_s { pdf_array *GlobalSubrs; int NumGlobalSubrs; pdf_dict *CharStrings; - byte *cffdata; - byte *cffend; - byte *gsubrs; - byte *subrs; - byte *charstrings; int ncharstrings; pdf_array *FDArray; int cidcount; @@ -225,7 +213,7 @@ typedef struct pdf_cidfont_type0_s { typedef struct pdf_cidfont_type2_s { pdf_cid_font_common; - gs_string sfnt; + pdf_buffer *sfnt; } pdf_cidfont_type2; #endif diff --git a/pdf/pdf_fontps.c b/pdf/pdf_fontps.c index 3ab399b9..5cfa1652 100644 --- a/pdf/pdf_fontps.c +++ b/pdf/pdf_fontps.c @@ -885,7 +885,7 @@ ps_font_def_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte *bufend) } } if (code >= 0) { - pdfi_array_put(s->pdfi_ctx, priv->u.t1.blenddesignpositions, i, (pdf_obj *)sa); + code = pdfi_array_put(s->pdfi_ctx, priv->u.t1.blenddesignpositions, i, (pdf_obj *)sa); } pdfi_countdown(sa); } @@ -1045,21 +1045,14 @@ ps_font_array_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte *bufend) !memcmp(s->cur[-1].val.name, PDF_PS_OPER_NAME_AND_LEN("Subrs"))) { if (s->cur[0].val.i > 0) { - if (priv->u.t1.Subrs != NULL) { - int i; - for (i = 0; i < priv->u.t1.NumSubrs; i++) { - gs_free_object(mem, priv->u.t1.Subrs[i].data, "ps_font_array_func(Subrs[i])"); - } - gs_free_object(mem, priv->u.t1.Subrs, "ps_font_array_func(Subrs)"); - } + pdfi_countdown(priv->u.t1.Subrs); - priv->u.t1.Subrs = (gs_string *) gs_alloc_bytes(mem, s->cur[0].val.i *sizeof(gs_string), "ps_font_array_func(Subrs)"); - if (priv->u.t1.Subrs == NULL) { - return_error(gs_error_VMerror); + code = pdfi_object_alloc(s->pdfi_ctx, PDF_ARRAY, (unsigned int)s->cur[0].val.i, (pdf_obj **)&priv->u.t1.Subrs); + if (code < 0) { + return code; } - memset(priv->u.t1.Subrs, 0x00, s->cur[0].val.i * sizeof(gs_string)); + pdfi_countup(priv->u.t1.Subrs); } - priv->u.t1.NumSubrs = s->cur[0].val.i; code = pdf_ps_stack_pop(s, 1); } else if (pdf_ps_obj_has_type(&s->cur[-1], PDF_PS_OBJ_NAME) && @@ -1162,14 +1155,18 @@ pdf_ps_RD_oper_func(gs_memory_t *mem, pdf_ps_ctx_t *s, byte *buf, byte *bufend) size = s->cur[0].val.i; buf++; if (buf + size < bufend) { - priv->u.t1.Subrs[inx].data = - gs_alloc_bytes(mem, size, "pdf_ps_RD_oper_func(subr string)"); - if (priv->u.t1.Subrs[inx].data == NULL) { - (void)pdf_ps_stack_pop(s, 2); - return_error(gs_error_VMerror); + pdf_string *subr_str; + + code = pdfi_object_alloc(s->pdfi_ctx, PDF_STRING, (unsigned int)size, (pdf_obj **)&subr_str); + if (code < 0) { + return code; } - memcpy(priv->u.t1.Subrs[inx].data, buf, size); - priv->u.t1.Subrs[inx].size = size; + memcpy(subr_str->data, buf, size); + pdfi_countup(subr_str); + code = pdfi_array_put(s->pdfi_ctx, priv->u.t1.Subrs, inx, (pdf_obj *)subr_str); + pdfi_countdown(subr_str); + if (code < 0) + return code; } } } @@ -1282,7 +1279,7 @@ pdfi_read_ps_font(pdf_context *ctx, pdf_dict *font_dict, byte *fbuf, int fbuflen and that can end up in a stackoverflow error, even though we have a complete font. Override it and let the Type 1 specific code decide for itself if it can use the font. */ - if (code == gs_error_stackoverflow) + if (code == gs_error_pdf_stackoverflow) code = 0; return code; diff --git a/pdf/pdf_fontps.h b/pdf/pdf_fontps.h index 835f0066..79ba2aef 100644 --- a/pdf/pdf_fontps.h +++ b/pdf/pdf_fontps.h @@ -208,7 +208,7 @@ static inline int pdf_ps_stack_push(pdf_ps_ctx_t *s) } s->cur++; if (pdf_ps_obj_has_type(s->cur, PDF_PS_OBJ_STACK_TOP)) - return_error(gs_error_stackoverflow); + return_error(gs_error_pdf_stackoverflow); if (pdf_ps_obj_has_type(s->cur, PDF_PS_OBJ_STACK_BOTTOM)) return_error(gs_error_stackunderflow); return 0; @@ -239,7 +239,7 @@ static inline int pdf_ps_stack_pop(pdf_ps_ctx_t *s, unsigned int n) pdf_ps_make_null(s->cur); s->cur--; if (pdf_ps_obj_has_type(s->cur, PDF_PS_OBJ_STACK_TOP)) - return_error(gs_error_stackoverflow); + return_error(gs_error_pdf_stackoverflow); if (pdf_ps_obj_has_type(s->cur, PDF_PS_OBJ_STACK_BOTTOM)) return_error(gs_error_stackunderflow); } diff --git a/pdf/pdf_func.c b/pdf/pdf_func.c index 9537152e..2aaf1eea 100644 --- a/pdf/pdf_func.c +++ b/pdf/pdf_func.c @@ -157,6 +157,15 @@ pdfi_parse_type4_func_stream(pdf_context *ctx, pdf_c_stream *function_stream, in if (c < 0) break; switch(c) { + case '%': + do { + c = pdfi_read_byte(ctx, function_stream); + if (c < 0) + break; + if (c == 0x0a || c == 0x0d) + break; + }while (1); + break; case 0x20: case 0x0a: case 0x0d: @@ -296,7 +305,7 @@ pdfi_build_function_4(pdf_context *ctx, gs_function_params_t * mnDR, params.ops.data = 0; /* in case of failure */ params.ops.size = 0; /* ditto */ - if (function_obj->type != PDF_STREAM) + if (pdfi_type_of(function_obj) != PDF_STREAM) return_error(gs_error_undefined); Length = pdfi_stream_length(ctx, (pdf_stream *)function_obj); @@ -378,7 +387,7 @@ pdfi_build_function_0(pdf_context *ctx, gs_function_params_t * mnDR, params.Size = params.array_step = params.stream_step = NULL; params.Order = 0; - if (function_obj->type != PDF_STREAM) + if (pdfi_type_of(function_obj) != PDF_STREAM) return_error(gs_error_undefined); code = pdfi_dict_from_obj(ctx, (pdf_obj *)function_obj, &function_dict); @@ -444,7 +453,7 @@ pdfi_build_function_0(pdf_context *ctx, gs_function_params_t * mnDR, code = pdfi_make_int_array_from_dict(ctx, (int **)¶ms.Size, function_dict, "Size"); if (code != params.m) { - if (code > 0) + if (code >= 0) code = gs_error_rangecheck; goto function_0_error; } @@ -560,6 +569,20 @@ pdfi_build_function_3(pdf_context *ctx, gs_function_params_t * mnDR, params.Functions = (const gs_function_t * const *)ptr; + code = pdfi_make_float_array_from_dict(ctx, (float **)¶ms.Bounds, function_dict, "Bounds"); + if (code < 0) + goto function_3_error; + + code = pdfi_make_float_array_from_dict(ctx, (float **)¶ms.Encode, function_dict, "Encode"); + if (code < 0) + goto function_3_error; + + if (code != 2 * params.k) { + code = gs_note_error(gs_error_rangecheck); + goto function_3_error; + } + code = 0; + for (i = 0; i < params.k; ++i) { pdf_obj * rsubfn = NULL; @@ -578,23 +601,12 @@ pdfi_build_function_3(pdf_context *ctx, gs_function_params_t * mnDR, if (code < 0) goto function_3_error; - code = pdfi_build_sub_function(ctx, &ptr[i], shading_domain, num_inputs, rsubfn, page_dict); + code = pdfi_build_sub_function(ctx, &ptr[i], ¶ms.Encode[i * 2], num_inputs, rsubfn, page_dict); pdfi_countdown(rsubfn); if (code < 0) goto function_3_error; } - code = pdfi_make_float_array_from_dict(ctx, (float **)¶ms.Bounds, function_dict, "Bounds"); - if (code < 0) - goto function_3_error; - - code = pdfi_make_float_array_from_dict(ctx, (float **)¶ms.Encode, function_dict, "Encode"); - if (code < 0) - goto function_3_error; - - if (code != 2 * params.k) - goto function_3_error; - if (params.Range == 0) params.n = params.Functions[0]->params.n; @@ -619,6 +631,7 @@ static int pdfi_build_sub_function(pdf_context *ctx, gs_function_t ** ppfn, cons int64_t Type; gs_function_params_t params; pdf_dict *stream_dict; + int obj_num; params.Range = params.Domain = NULL; @@ -626,10 +639,11 @@ static int pdfi_build_sub_function(pdf_context *ctx, gs_function_t ** ppfn, cons if (code < 0) return code; - if (stream_obj->object_num != 0) { - if (pdfi_loop_detector_check_object(ctx, stream_obj->object_num)) + obj_num = pdf_object_num(stream_obj); + if (obj_num != 0) { + if (pdfi_loop_detector_check_object(ctx, obj_num)) return gs_note_error(gs_error_circular_reference); - code = pdfi_loop_detector_add_object(ctx, stream_obj->object_num); + code = pdfi_loop_detector_add_object(ctx, obj_num); if (code < 0) goto sub_function_error; } diff --git a/pdf/pdf_gstate.c b/pdf/pdf_gstate.c index c1444b4b..5248bd40 100644 --- a/pdf/pdf_gstate.c +++ b/pdf/pdf_gstate.c @@ -44,6 +44,8 @@ #include "gscoord.h" /* For gs_concat() */ #include "gsutil.h" /* For gs_next_ids() */ #include "gscolor3.h" /* For gs_setsmoothness() */ +#include "gzpath.h" +#include "gspenum.h" static const char *blend_mode_names[] = { GS_BLEND_MODE_NAMES, 0 @@ -158,8 +160,7 @@ pdfi_gstate_set_client(pdf_context *ctx, gs_gstate *pgs) int pdfi_concat(pdf_context *ctx) { - int i, code; - pdf_num *num; + int code; double Values[6]; gs_matrix m; @@ -171,28 +172,18 @@ int pdfi_concat(pdf_context *ctx) if (ctx->text.BlockDepth != 0) pdfi_set_warning(ctx, 0, NULL, W_PDF_OPINVALIDINTEXT, "pdfi_concat", NULL); - for (i=0;i < 6;i++){ - num = (pdf_num *)ctx->stack_top[i - 6]; - if (num->type != PDF_INT) { - if(num->type != PDF_REAL) { - pdfi_pop(ctx, 6); - return_error(gs_error_typecheck); - } - else - Values[i] = num->value.d; - } else { - Values[i] = (double)num->value.i; - } - } + code = pdfi_destack_reals(ctx, Values, 6); + if (code < 0) + return code; + m.xx = (float)Values[0]; m.xy = (float)Values[1]; m.yx = (float)Values[2]; m.yy = (float)Values[3]; m.tx = (float)Values[4]; m.ty = (float)Values[5]; - code = gs_concat(ctx->pgs, (const gs_matrix *)&m); - pdfi_pop(ctx, 6); - return code; + + return gs_concat(ctx->pgs, (const gs_matrix *)&m); } int pdfi_op_q(pdf_context *ctx) @@ -216,7 +207,6 @@ int pdfi_op_q(pdf_context *ctx) int pdfi_op_Q(pdf_context *ctx) { int code = 0; - gx_path *ppath = NULL; #if DEBUG_GSAVE dbgmprintf(ctx->memory, "(doing Q)\n"); /* TODO: Spammy, delete me at some point */ @@ -232,30 +222,7 @@ int pdfi_op_Q(pdf_context *ctx) return code; } - /* Section 4.4.1 of the 3rd Edition PDF_Refrence Manual, p226 of the 1.7 version - * states that the current path is **NOT** part of the graphics state and is not - * saved and restored along with the other graphics state parameters. So here - * we need to indulge in some ugliness. We take a copy of the current path - * before we do a grestore, and below we assign the copy to the graphics state - * after the grestore, thus preserving it unchanged. This is still better than - * the 'PDF interpreter written in PostScript' method. - */ - ppath = gx_path_alloc_shared(ctx->pgs->path, ctx->memory, "temporary current path copy for Q"); - if (ppath == NULL) - return_error(gs_error_VMerror); - - code = pdfi_grestore(ctx); - - if (code >= 0) { - /* Put the path back, and make sure current point is properly set */ - code = gx_path_assign_preserve(ctx->pgs->path, ppath); - if (gx_path_position_valid(ctx->pgs->path)) - gx_setcurrentpoint_from_path(ctx->pgs, ctx->pgs->path); - } - - gx_path_free(ppath, "temporary current path copy for Q"); - - return code; + return pdfi_grestore(ctx); } /* We want pdfi_grestore() so we can track and warn of "too many Qs" @@ -296,86 +263,60 @@ int pdfi_gs_setgstate(gs_gstate * pgs, const gs_gstate * pfrom) int pdfi_setlinewidth(pdf_context *ctx) { int code; - pdf_num *n1; double d1; if (pdfi_count_stack(ctx) < 1) return_error(gs_error_stackunderflow); - n1 = (pdf_num *)ctx->stack_top[-1]; - if (n1->type == PDF_INT){ - d1 = (double)n1->value.i; - } else{ - if (n1->type == PDF_REAL) { - d1 = n1->value.d; - } else { - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); - } - } - code = gs_setlinewidth(ctx->pgs, d1); - pdfi_pop(ctx, 1); - return code; + code = pdfi_destack_real(ctx, &d1); + if (code < 0) + return code; + + return gs_setlinewidth(ctx->pgs, d1); } int pdfi_setlinejoin(pdf_context *ctx) { int code; - pdf_num *n1; + int64_t i; if (pdfi_count_stack(ctx) < 1) return_error(gs_error_stackunderflow); - n1 = (pdf_num *)ctx->stack_top[-1]; - if (n1->type == PDF_INT){ - code = gs_setlinejoin(ctx->pgs, n1->value.i); - } else { - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); - } - pdfi_pop(ctx, 1); - return code; + code = pdfi_destack_int(ctx, &i); + if (code < 0) + return code; + + return gs_setlinejoin(ctx->pgs, (int)i); } int pdfi_setlinecap(pdf_context *ctx) { int code; - pdf_num *n1; + int64_t i; if (pdfi_count_stack(ctx) < 1) return_error(gs_error_stackunderflow); - n1 = (pdf_num *)ctx->stack_top[-1]; - if (n1->type == PDF_INT){ - code = gs_setlinecap(ctx->pgs, n1->value.i); - } else { - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); - } - pdfi_pop(ctx, 1); - return code; + code = pdfi_destack_int(ctx, &i); + if (code < 0) + return code; + + return gs_setlinecap(ctx->pgs, i); } int pdfi_setflat(pdf_context *ctx) { int code; - pdf_num *n1; double d1; if (pdfi_count_stack(ctx) < 1) return_error(gs_error_stackunderflow); - n1 = (pdf_num *)ctx->stack_top[-1]; - if (n1->type == PDF_INT){ - d1 = (double)n1->value.i; - } else{ - if (n1->type == PDF_REAL) { - d1 = n1->value.d; - } else { - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); - } - } + code = pdfi_destack_real(ctx, &d1); + if (code < 0) + return code; + /* PDF spec says the value is 1-100, with 0 meaning "use the default" * But gs code (and now our code) forces the value to be <= 1 * This matches what Adobe and evince seem to do (see Bug 555657). @@ -384,9 +325,7 @@ int pdfi_setflat(pdf_context *ctx) */ if (d1 > 1.0) d1 = 1.0; - code = gs_setflat(ctx->pgs, d1); - pdfi_pop(ctx, 1); - return code; + return gs_setflat(ctx->pgs, d1); } int pdfi_setdash_impl(pdf_context *ctx, pdf_array *a, double phase_d) @@ -412,9 +351,9 @@ int pdfi_setdash_impl(pdf_context *ctx, pdf_array *a, double phase_d) gs_free_object(ctx->memory, dash_array, "error in setdash"); return code; } + int pdfi_setdash(pdf_context *ctx) { - pdf_num *phase; pdf_array *a; double phase_d; int code; @@ -424,52 +363,47 @@ int pdfi_setdash(pdf_context *ctx) return_error(gs_error_stackunderflow); } - phase = (pdf_num *)ctx->stack_top[-1]; - if (phase->type == PDF_INT){ - phase_d = (double)phase->value.i; - } else{ - if (phase->type == PDF_REAL) { - phase_d = phase->value.d; - } else { - pdfi_pop(ctx, 2); - return_error(gs_error_typecheck); - } + code = pdfi_destack_real(ctx, &phase_d); + if (code < 0) { + pdfi_pop(ctx, 1); + return code; } - a = (pdf_array *)ctx->stack_top[-2]; - if (a->type != PDF_ARRAY) { - pdfi_pop(ctx, 2); + a = (pdf_array *)ctx->stack_top[-1]; + pdfi_countup(a); + pdfi_pop(ctx, 1); + + if (pdfi_type_of(a) != PDF_ARRAY) { + pdfi_countdown(a); return_error(gs_error_typecheck); } code = pdfi_setdash_impl(ctx, a, phase_d); - pdfi_pop(ctx, 2); + pdfi_countdown(a); return code; } int pdfi_setmiterlimit(pdf_context *ctx) { int code; - pdf_num *n1; double d1; if (pdfi_count_stack(ctx) < 1) return_error(gs_error_stackunderflow); - n1 = (pdf_num *)ctx->stack_top[-1]; - if (n1->type == PDF_INT){ - d1 = (double)n1->value.i; - } else{ - if (n1->type == PDF_REAL) { - d1 = n1->value.d; - } else { - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); - } - } - code = gs_setmiterlimit(ctx->pgs, d1); - pdfi_pop(ctx, 1); - return code; + code = pdfi_destack_real(ctx, &d1); + if (code < 0) + return code; + + /* PostScript (and therefore the graphics library) impose a minimum + * value of 1.0 on miter limit. PDF does not specify a minimum, but less + * than 1 doesn't make a lot of sense. This code brought over from the old + * PDF interpreter which silently clamped the value to 1. + */ + if (d1 < 1.0) + d1 = 1.0; + + return gs_setmiterlimit(ctx->pgs, d1); } static int GS_LW(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict *page_dict) @@ -569,38 +503,36 @@ static int GS_RI(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict static int GS_OP(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict *page_dict) { - pdf_bool *b = NULL; + bool b; int code; bool known=false; - code = pdfi_dict_get_type(ctx, GS, "OP", PDF_BOOL, (pdf_obj **)&b); + code = pdfi_dict_get_bool(ctx, GS, "OP", &b); if (code < 0) return code; - gs_setstrokeoverprint(ctx->pgs, b->value); + gs_setstrokeoverprint(ctx->pgs, b); /* If op not in the dict, then also set it with OP * Because that's what gs does pdf_draw.ps/gsparamdict/OP */ code = pdfi_dict_known(ctx, GS, "op", &known); if (!known) - gs_setfilloverprint(ctx->pgs, b->value); + gs_setfilloverprint(ctx->pgs, b); - pdfi_countdown(b); return 0; } static int GS_op(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict *page_dict) { - pdf_bool *b; + bool b; int code; - code = pdfi_dict_get_type(ctx, GS, "op", PDF_BOOL, (pdf_obj **)&b); + code = pdfi_dict_get_bool(ctx, GS, "op", &b); if (code < 0) return code; - gs_setfilloverprint(ctx->pgs, b->value); - pdfi_countdown(b); + gs_setfilloverprint(ctx->pgs, b); return 0; } @@ -652,46 +584,45 @@ static int pdfi_set_blackgeneration(pdf_context *ctx, pdf_obj *obj, pdf_dict *pa int code = 0, i; gs_function_t *pfn; - if (obj->type == PDF_NAME) { - if (pdfi_name_is((const pdf_name *)obj, "Identity")) { - code = gs_setblackgeneration_remap(ctx->pgs, gs_identity_transfer, false); - goto exit; - } else { - if (!is_BG && pdfi_name_is((const pdf_name *)obj, "Default")) { + switch (pdfi_type_of(obj)) { + case PDF_NAME: + if (pdfi_name_is((const pdf_name *)obj, "Identity")) + code = gs_setblackgeneration_remap(ctx->pgs, gs_identity_transfer, false); + else if (!is_BG && pdfi_name_is((const pdf_name *)obj, "Default")) { code = gs_setblackgeneration_remap(ctx->pgs, ctx->page.DefaultBG.proc, false); memcpy(ctx->pgs->black_generation->values, ctx->page.DefaultBG.values, transfer_map_size * sizeof(frac)); - goto exit; - } else { + } else code = gs_note_error(gs_error_rangecheck); - goto exit; - } - } - } else { - if (obj->type != PDF_DICT && obj->type != PDF_STREAM) - return_error(gs_error_typecheck); + goto exit; - code = pdfi_build_function(ctx, &pfn, NULL, 1, obj, page_dict); - if (code < 0) - return code; + case PDF_DICT: + case PDF_STREAM: + code = pdfi_build_function(ctx, &pfn, NULL, 1, obj, page_dict); + if (code < 0) + return code; - gs_setblackgeneration_remap(ctx->pgs, gs_mapped_transfer, false); - for (i = 0; i < transfer_map_size; i++) { - float v, f; + gs_setblackgeneration_remap(ctx->pgs, gs_mapped_transfer, false); + for (i = 0; i < transfer_map_size; i++) { + float v, f; - f = (1.0f / (transfer_map_size - 1)) * i; + f = (1.0f / (transfer_map_size - 1)) * i; - code = gs_function_evaluate(pfn, (const float *)&f, &v); - if (code < 0) { - pdfi_free_function(ctx, pfn); - return code; + code = gs_function_evaluate(pfn, (const float *)&f, &v); + if (code < 0) { + pdfi_free_function(ctx, pfn); + return code; + } + + ctx->pgs->black_generation->values[i] = + (v < 0.0 ? float2frac(0.0) : + v >= 1.0 ? frac_1 : + float2frac(v)); } + code = pdfi_free_function(ctx, pfn); + break; - ctx->pgs->black_generation->values[i] = - (v < 0.0 ? float2frac(0.0) : - v >= 1.0 ? frac_1 : - float2frac(v)); - } - code = pdfi_free_function(ctx, pfn); + default: + return_error(gs_error_typecheck); } exit: return code; @@ -741,46 +672,52 @@ static int pdfi_set_undercolorremoval(pdf_context *ctx, pdf_obj *obj, pdf_dict * int code = 0, i; gs_function_t *pfn; - if (obj->type == PDF_NAME) { - if (pdfi_name_is((const pdf_name *)obj, "Identity")) { - code = gs_setundercolorremoval_remap(ctx->pgs, gs_identity_transfer, false); - goto exit; - } else { - if (!is_BG && pdfi_name_is((const pdf_name *)obj, "Default")) { + switch (pdfi_type_of(obj)) { + case PDF_NAME: + if (pdfi_name_is((const pdf_name *)obj, "Identity")) { + code = gs_setundercolorremoval_remap(ctx->pgs, gs_identity_transfer, false); + } else if (!is_BG && pdfi_name_is((const pdf_name *)obj, "Default")) { code = gs_setundercolorremoval_remap(ctx->pgs, ctx->page.DefaultUCR.proc, false); memcpy(ctx->pgs->undercolor_removal->values, ctx->page.DefaultUCR.values, transfer_map_size * sizeof(frac)); - goto exit; } else { code = gs_note_error(gs_error_rangecheck); - goto exit; } - } - } else { - if (obj->type != PDF_DICT && obj->type != PDF_STREAM) - return_error(gs_error_typecheck); + goto exit; - code = pdfi_build_function(ctx, &pfn, NULL, 1, obj, page_dict); - if (code < 0) - return code; + case PDF_DICT: + case PDF_STREAM: + code = pdfi_build_function(ctx, &pfn, NULL, 1, obj, page_dict); + if (code < 0) + return code; - gs_setundercolorremoval_remap(ctx->pgs, gs_mapped_transfer, false); - for (i = 0; i < transfer_map_size; i++) { - float v, f; + if (pfn->params.n == 1) { + gs_setundercolorremoval_remap(ctx->pgs, gs_mapped_transfer, false); + for (i = 0; i < transfer_map_size; i++) { + float v, f; - f = (1.0f / (transfer_map_size - 1)) * i; + f = (1.0f / (transfer_map_size - 1)) * i; - code = gs_function_evaluate(pfn, (const float *)&f, &v); - if (code < 0) { - pdfi_free_function(ctx, pfn); - return code; + code = gs_function_evaluate(pfn, (const float *)&f, &v); + if (code < 0) { + pdfi_free_function(ctx, pfn); + return code; + } + + ctx->pgs->undercolor_removal->values[i] = + (v < 0.0 ? float2frac(0.0) : + v >= 1.0 ? frac_1 : + float2frac(v)); + } + code = pdfi_free_function(ctx, pfn); + } + else { + (void)pdfi_free_function(ctx, pfn); + code = gs_note_error(gs_error_rangecheck); } + break; - ctx->pgs->undercolor_removal->values[i] = - (v < 0.0 ? float2frac(0.0) : - v >= 1.0 ? frac_1 : - float2frac(v)); - } - code = pdfi_free_function(ctx, pfn); + default: + return_error(gs_error_typecheck); } exit: return code; @@ -852,12 +789,12 @@ static int pdfi_set_all_transfers(pdf_context *ctx, pdf_array *a, pdf_dict *page code = pdfi_array_get(ctx, a, (uint64_t)i, &o); if (code < 0) goto exit; - if (o->type == PDF_NAME) { - if (pdfi_name_is((const pdf_name *)o, "Identity")) { - proc_types[i] = E_IDENTITY; - map_procs[i] = gs_identity_transfer; - } else { - if (!is_TR && pdfi_name_is((const pdf_name *)o, "Default")) { + switch (pdfi_type_of(o)) { + case PDF_NAME: + if (pdfi_name_is((const pdf_name *)o, "Identity")) { + proc_types[i] = E_IDENTITY; + map_procs[i] = gs_identity_transfer; + } else if (!is_TR && pdfi_name_is((const pdf_name *)o, "Default")) { proc_types[i] = E_DEFAULT; map_procs[i] = ctx->page.DefaultTransfers[i].proc; } else { @@ -865,9 +802,9 @@ static int pdfi_set_all_transfers(pdf_context *ctx, pdf_array *a, pdf_dict *page code = gs_note_error(gs_error_typecheck); goto exit; } - } - } else { - if (o->type == PDF_STREAM || o->type == PDF_DICT) { + break; + case PDF_STREAM: + case PDF_DICT: proc_types[i] = E_FUNCTION; map_procs[i] = gs_mapped_transfer; code = pdfi_build_function(ctx, &pfn[i], NULL, 1, o, page_dict); @@ -880,11 +817,11 @@ static int pdfi_set_all_transfers(pdf_context *ctx, pdf_array *a, pdf_dict *page code = gs_note_error(gs_error_rangecheck); goto exit; } - } else { + break; + default: pdfi_countdown(o); code = gs_note_error(gs_error_typecheck); goto exit; - } } pdfi_countdown(o); } @@ -954,7 +891,7 @@ static int pdfi_set_gray_transfer(pdf_context *ctx, pdf_obj *tr_obj, pdf_dict *p int code = 0, i; gs_function_t *pfn; - if (tr_obj->type != PDF_DICT && tr_obj->type != PDF_STREAM) + if (pdfi_type_of(tr_obj) != PDF_DICT && pdfi_type_of(tr_obj) != PDF_STREAM) return_error(gs_error_typecheck); code = pdfi_build_function(ctx, &pfn, NULL, 1, tr_obj, page_dict); @@ -990,7 +927,7 @@ static int pdfi_set_transfer(pdf_context *ctx, pdf_obj *obj, pdf_dict *page_dict { int code = 0; - if (obj->type == PDF_NAME) { + if (pdfi_type_of(obj) == PDF_NAME) { if (pdfi_name_is((const pdf_name *)obj, "Identity")) { code = gs_settransfer_remap(ctx->pgs, gs_identity_transfer, false); goto exit; @@ -1006,7 +943,7 @@ static int pdfi_set_transfer(pdf_context *ctx, pdf_obj *obj, pdf_dict *page_dict } } - if (obj->type == PDF_ARRAY) { + if (pdfi_type_of(obj) == PDF_ARRAY) { if (pdfi_array_size((pdf_array *)obj) != 4) { code = gs_note_error(gs_error_rangecheck); goto exit; @@ -1157,7 +1094,7 @@ error: static int build_type1_halftone(pdf_context *ctx, pdf_dict *halftone_dict, pdf_dict *page_dict, gx_ht_order *porder, gs_halftone_component *phtc, char *name, int len, int comp_num) { - int code; + int code, i; pdf_obj *obj = NULL, *transfer = NULL; double f, a; float values[2] = {0, 0}, domain[4] = {-1, 1, -1, 1}, out; @@ -1193,46 +1130,48 @@ static int build_type1_halftone(pdf_context *ctx, pdf_dict *halftone_dict, pdf_d } memset(order, 0x00, sizeof(gx_ht_order)); - if (obj->type == PDF_NAME) { - int i; - - if (pdfi_name_is((pdf_name *)obj, "Default")) { - i = 0; - } else { - for (i = 0; i < (sizeof(spot_table) / sizeof (char *)); i++){ - if (pdfi_name_is((pdf_name *)obj, spot_table[i])) - break; + switch (pdfi_type_of(obj)) { + case PDF_NAME: + if (pdfi_name_is((pdf_name *)obj, "Default")) { + i = 0; + } else { + for (i = 0; i < (sizeof(spot_table) / sizeof (char *)); i++) { + if (pdfi_name_is((pdf_name *)obj, spot_table[i])) + break; + } + if (i >= (sizeof(spot_table) / sizeof (char *))) + return gs_note_error(gs_error_rangecheck); } - if (i >= (sizeof(spot_table) / sizeof (char *))) - return gs_note_error(gs_error_rangecheck); - } - code = pdfi_build_halftone_function(ctx, &pfn, (byte *)spot_functions[i], strlen(spot_functions[i])); - if (code < 0) - goto error; - } else { - if (obj->type == PDF_DICT || obj->type == PDF_STREAM) { + code = pdfi_build_halftone_function(ctx, &pfn, (byte *)spot_functions[i], strlen(spot_functions[i])); + if (code < 0) + goto error; + break; + case PDF_DICT: + case PDF_STREAM: code = pdfi_build_function(ctx, &pfn, (const float *)domain, 2, obj, page_dict); if (code < 0) goto error; - } else { + break; + default: code = gs_note_error(gs_error_typecheck); goto error; - } } if (pdfi_dict_knownget(ctx, halftone_dict, "TransferFunction", &transfer) > 0) { - if (transfer->type == PDF_NAME) { - /* As far as I can tell, only /Identity is valid as a name, so we can just ignore - * names, if it's not Identity it would be an error (which we would ignore) and if - * it is, it has no effect. So what's the point ? - */ - } else { - if (transfer->type == PDF_STREAM) { + switch (pdfi_type_of(transfer)) { + case PDF_NAME: + /* As far as I can tell, only /Identity is valid as a name, so we can just ignore + * names, if it's not Identity it would be an error (which we would ignore) and if + * it is, it has no effect. So what's the point ? + */ + break; + case PDF_STREAM: pdfi_evaluate_transfer(ctx, transfer, page_dict, &pmap); - } else { + break; + default: /* should be an error, but we can just ignore it */ pdfi_set_warning(ctx, 0, NULL, W_PDF_TYPECHECK, "build_type1_halftone", NULL); - } + break; } } @@ -1557,7 +1496,7 @@ static int build_type5_halftone(pdf_context *ctx, pdf_dict *halftone_dict, pdf_d * members. */ do { - if (Key->type != PDF_NAME) { + if (pdfi_type_of(Key) != PDF_NAME) { code = gs_note_error(gs_error_typecheck); goto error; } @@ -1630,7 +1569,7 @@ static int build_type5_halftone(pdf_context *ctx, pdf_dict *halftone_dict, pdf_d */ ix = 1; do { - if (Key->type != PDF_NAME) { + if (pdfi_type_of(Key) != PDF_NAME) { code = gs_note_error(gs_error_typecheck); goto error; } @@ -1673,7 +1612,7 @@ static int build_type5_halftone(pdf_context *ctx, pdf_dict *halftone_dict, pdf_d goto error; break; case 6: - if (Value->type != PDF_STREAM) { + if (pdfi_type_of(Value) != PDF_STREAM) { code = gs_note_error(gs_error_typecheck); goto error; } @@ -1682,7 +1621,7 @@ static int build_type5_halftone(pdf_context *ctx, pdf_dict *halftone_dict, pdf_d goto error; break; case 10: - if (Value->type != PDF_STREAM) { + if (pdfi_type_of(Value) != PDF_STREAM) { code = gs_note_error(gs_error_typecheck); goto error; } @@ -1691,7 +1630,7 @@ static int build_type5_halftone(pdf_context *ctx, pdf_dict *halftone_dict, pdf_d goto error; break; case 16: - if (Value->type != PDF_STREAM) { + if (pdfi_type_of(Value) != PDF_STREAM) { code = gs_note_error(gs_error_typecheck); goto error; } @@ -1849,7 +1788,7 @@ static int pdfi_do_halftone(pdf_context *ctx, pdf_obj *halftone_obj, pdf_dict *p break; case 6: - if (halftone_obj->type != PDF_STREAM) + if (pdfi_type_of(halftone_obj) != PDF_STREAM) return_error(gs_error_typecheck); phtc = (gs_halftone_component *)gs_alloc_bytes(ctx->memory, sizeof(gs_halftone_component), "pdfi_do_halftone"); if (phtc == 0) { @@ -1870,22 +1809,24 @@ static int pdfi_do_halftone(pdf_context *ctx, pdf_obj *halftone_obj, pdf_dict *p /* Transfer function pdht->order->transfer */ if (pdfi_dict_knownget(ctx, ((pdf_stream *)halftone_obj)->stream_dict, "TransferFunction", &transfer) > 0) { - if (transfer->type == PDF_NAME) { - /* As far as I can tell, only /Identity is valid as a name, so we can just ignore - * names, if it's not Identity it would be an error (which we would ignore) and if - * it is, it has no effect. So what's the point ? - */ - } else { - if (transfer->type == PDF_STREAM) { + switch (pdfi_type_of(transfer)) { + case PDF_NAME: + /* As far as I can tell, only /Identity is valid as a name, so we can just ignore + * names, if it's not Identity it would be an error (which we would ignore) and if + * it is, it has no effect. So what's the point ? + */ + break; + case PDF_STREAM: /* If we get an error here, we can just ignore it, and not apply the transfer */ code = pdfi_evaluate_transfer(ctx, transfer, page_dict, &pmap); if (code >= 0) { pdht->order.transfer = pmap; } - } else { + break; + default: /* should be an error, but we can just ignore it */ pdfi_set_warning(ctx, 0, NULL, W_PDF_TYPECHECK, "do_halftone", NULL); - } + break; } pdfi_countdown(transfer); } @@ -1901,7 +1842,7 @@ static int pdfi_do_halftone(pdf_context *ctx, pdf_obj *halftone_obj, pdf_dict *p gx_unset_both_dev_colors(ctx->pgs); break; case 10: - if (halftone_obj->type != PDF_STREAM) + if (pdfi_type_of(halftone_obj) != PDF_STREAM) return_error(gs_error_typecheck); phtc = (gs_halftone_component *)gs_alloc_bytes(ctx->memory, sizeof(gs_halftone_component), "pdfi_do_halftone"); if (phtc == 0) { @@ -1922,22 +1863,24 @@ static int pdfi_do_halftone(pdf_context *ctx, pdf_obj *halftone_obj, pdf_dict *p /* Transfer function pdht->order->transfer */ if (pdfi_dict_knownget(ctx, ((pdf_stream *)halftone_obj)->stream_dict, "TransferFunction", &transfer) > 0) { - if (transfer->type == PDF_NAME) { - /* As far as I can tell, only /Identity is valid as a name, so we can just ignore - * names, if it's not Identity it would be an error (which we would ignore) and if - * it is, it has no effect. So what's the point ? - */ - } else { - if (transfer->type == PDF_STREAM) { + switch (pdfi_type_of(transfer)) { + case PDF_NAME: + /* As far as I can tell, only /Identity is valid as a name, so we can just ignore + * names, if it's not Identity it would be an error (which we would ignore) and if + * it is, it has no effect. So what's the point ? + */ + break; + case PDF_STREAM: /* If we get an error here, we can just ignore it, and not apply the transfer */ code = pdfi_evaluate_transfer(ctx, transfer, page_dict, &pmap); if (code >= 0) { pdht->order.transfer = pmap; } - } else { + break; + default: /* should be an error, but we can just ignore it */ pdfi_set_warning(ctx, 0, NULL, W_PDF_TYPECHECK, "do_halftone", NULL); - } + break; } pdfi_countdown(transfer); } @@ -1953,7 +1896,7 @@ static int pdfi_do_halftone(pdf_context *ctx, pdf_obj *halftone_obj, pdf_dict *p gx_unset_both_dev_colors(ctx->pgs); break; case 16: - if (halftone_obj->type != PDF_STREAM) + if (pdfi_type_of(halftone_obj) != PDF_STREAM) return_error(gs_error_typecheck); phtc = (gs_halftone_component *)gs_alloc_bytes(ctx->memory, sizeof(gs_halftone_component), "pdfi_do_halftone"); if (phtc == 0) { @@ -1974,22 +1917,24 @@ static int pdfi_do_halftone(pdf_context *ctx, pdf_obj *halftone_obj, pdf_dict *p /* Transfer function pdht->order->transfer */ if (pdfi_dict_knownget(ctx, ((pdf_stream *)halftone_obj)->stream_dict, "TransferFunction", &transfer) > 0) { - if (transfer->type == PDF_NAME) { - /* As far as I can tell, only /Identity is valid as a name, so we can just ignore - * names, if it's not Identity it would be an error (which we would ignore) and if - * it is, it has no effect. So what's the point ? - */ - } else { - if (transfer->type == PDF_STREAM) { + switch (pdfi_type_of(transfer)) { + case PDF_NAME: + /* As far as I can tell, only /Identity is valid as a name, so we can just ignore + * names, if it's not Identity it would be an error (which we would ignore) and if + * it is, it has no effect. So what's the point ? + */ + break; + case PDF_STREAM: /* If we get an error here, we can just ignore it, and not apply the transfer */ code = pdfi_evaluate_transfer(ctx, transfer, page_dict, &pmap); if (code >= 0) { pdht->order.transfer = pmap; } - } else { + break; + default: /* should be an error, but we can just ignore it */ pdfi_set_warning(ctx, 0, NULL, W_PDF_TYPECHECK, "do_halftone", NULL); - } + break; } pdfi_countdown(transfer); } @@ -2034,7 +1979,7 @@ static int GS_HT(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict return code; - if (obj->type == PDF_NAME) { + if (pdfi_type_of(obj) == PDF_NAME) { if (pdfi_name_is((const pdf_name *)obj, "Default")) { goto exit; } else { @@ -2044,6 +1989,10 @@ static int GS_HT(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict } else { code = pdfi_do_halftone(ctx, obj, page_dict); } + if (code < 0 && !ctx->args.pdfstoponerror) { + pdfi_set_error(ctx, code, NULL, E_BAD_HALFTONE, "GS_HT", "Halftone will be ignored"); + code = 0; + } exit: pdfi_countdown(obj); @@ -2078,33 +2027,51 @@ static int GS_SM(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict static int GS_SA(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict *page_dict) { - pdf_bool *b; + bool b; int code; - code = pdfi_dict_get_type(ctx, GS, "SA", PDF_BOOL, (pdf_obj **)&b); + code = pdfi_dict_get_bool(ctx, GS, "SA", &b); if (code < 0) return code; - code = gs_setstrokeadjust(ctx->pgs, b->value); - pdfi_countdown(b); - return 0; + return gs_setstrokeadjust(ctx->pgs, b); } static int GS_BM(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict *page_dict) { pdf_name *n; int code; - gs_blend_mode_t mode; + gs_blend_mode_t mode = 0; /* Start with /Normal */ - code = pdfi_dict_get_type(ctx, GS, "BM", PDF_NAME, (pdf_obj **)&n); + code = pdfi_dict_get(ctx, GS, "BM", (pdf_obj **)&n); if (code < 0) return code; - code = pdfi_get_blend_mode(ctx, n, &mode); - pdfi_countdown(n); - if (code == 0) + if (pdfi_type_of(n) == PDF_ARRAY) { + int i; + pdf_array *a = (pdf_array *)n; + + for (i=0;i < pdfi_array_size(a);i++){ + code = pdfi_array_get_type(ctx, a, i, PDF_NAME, (pdf_obj **)&n); + if (code < 0) + continue; + code = pdfi_get_blend_mode(ctx, n, &mode); + pdfi_countdown(n); + if (code == 0) + break; + } + pdfi_countdown(a); return gs_setblendmode(ctx->pgs, mode); - return_error(gs_error_undefined); + } + + if (pdfi_type_of(n) == PDF_NAME) { + code = pdfi_get_blend_mode(ctx, n, &mode); + pdfi_countdown(n); + if (code == 0) + return gs_setblendmode(ctx->pgs, mode); + return_error(gs_error_undefined); + } + return_error(gs_error_typecheck); } static int GS_SMask(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict *page_dict) @@ -2112,7 +2079,7 @@ static int GS_SMask(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_d pdf_obj *o = NULL; pdfi_int_gstate *igs = (pdfi_int_gstate *)ctx->pgs->client_data; int code; - pdf_bool *Processed = NULL; + bool Processed; if (ctx->page.has_transparency == false || ctx->args.notransparency == true) return 0; @@ -2121,40 +2088,51 @@ static int GS_SMask(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_d if (code < 0) return code; - if (o->type == PDF_NAME) { - pdf_name *n = (pdf_name *)o; + switch (pdfi_type_of(o)) { + case PDF_NAME: + { + pdf_name *n = (pdf_name *)o; - if (pdfi_name_is(n, "None")) { - if (igs->SMask) { - pdfi_gstate_smask_free(igs); - code = pdfi_trans_end_smask_notify(ctx); + if (pdfi_name_is(n, "None")) { + if (igs->SMask) { + pdfi_gstate_smask_free(igs); + code = pdfi_trans_end_smask_notify(ctx); + } + goto exit; } - goto exit; + code = pdfi_find_resource(ctx, (unsigned char *)"ExtGState", n, stream_dict, page_dict, &o); + pdfi_countdown(n); + if (code < 0) + return code; + break; } - code = pdfi_find_resource(ctx, (unsigned char *)"ExtGState", n, stream_dict, page_dict, &o); - pdfi_countdown(n); - if (code < 0) - return code; - } - if (o->type == PDF_DICT) { - code = pdfi_dict_knownget_type(ctx, (pdf_dict *)o, "Processed", PDF_BOOL, (pdf_obj **)&Processed); - /* Need to clear the Processed flag in the SMask if another value is set - * (even if it's the same SMask?) - * TODO: I think there is a better way to do this that doesn't require sticking this - * flag in the SMask dictionary. But for now, let's get correct behavior. - */ - if (code > 0 && Processed->value) - Processed->value = false; - if (igs->SMask) - pdfi_gstate_smask_free(igs); - /* We need to use the graphics state memory, in case we are running under Ghostscript. */ - pdfi_gstate_smask_install(igs, ctx->pgs->memory, (pdf_dict *)o, ctx->pgs); + case PDF_DICT: + { + code = pdfi_dict_knownget_bool(ctx, (pdf_dict *)o, "Processed", &Processed); + /* Need to clear the Processed flag in the SMask if another value is set + * (even if it's the same SMask?) + * TODO: I think there is a better way to do this that doesn't require sticking this + * flag in the SMask dictionary. But for now, let's get correct behavior. + */ + if (code > 0 && Processed) { + code = pdfi_dict_put_bool(ctx, (pdf_dict *)o, "Processed", false); + if (code < 0) + return code; + } + if (igs->SMask) + pdfi_gstate_smask_free(igs); + /* We need to use the graphics state memory, in case we are running under Ghostscript. */ + pdfi_gstate_smask_install(igs, ctx->pgs->memory, (pdf_dict *)o, ctx->pgs); + break; + } + + default: + break; } exit: pdfi_countdown(o); - pdfi_countdown(Processed); return 0; } @@ -2206,30 +2184,26 @@ static int GS_ca(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict static int GS_AIS(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict *page_dict) { - pdf_bool *b; + bool b; int code; - code = pdfi_dict_get_type(ctx, GS, "AIS", PDF_BOOL, (pdf_obj **)&b); + code = pdfi_dict_get_bool(ctx, GS, "AIS", &b); if (code < 0) return code; - code = gs_setalphaisshape(ctx->pgs, b->value); - pdfi_countdown(b); - return 0; + return gs_setalphaisshape(ctx->pgs, b); } static int GS_TK(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict *page_dict) { - pdf_bool *b; + bool b; int code; - code = pdfi_dict_get_type(ctx, GS, "TK", PDF_BOOL, (pdf_obj **)&b); + code = pdfi_dict_get_bool(ctx, GS, "TK", &b); if (code < 0) return code; - code = gs_settextknockout(ctx->pgs, b->value); - pdfi_countdown(b); - return 0; + return gs_settextknockout(ctx->pgs, b); } typedef int (*GS_proc)(pdf_context *ctx, pdf_dict *GS, pdf_dict *stream_dict, pdf_dict *page_dict); @@ -2293,7 +2267,7 @@ int pdfi_set_ExtGState(pdf_context *ctx, pdf_dict *stream_dict, int pdfi_setgstate(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) { - pdf_name *n; + pdf_name *n = NULL; pdf_obj *o = NULL; int code=0, code1 = 0; @@ -2306,19 +2280,20 @@ int pdfi_setgstate(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) goto setgstate_error; } n = (pdf_name *)ctx->stack_top[-1]; - if (n->type != PDF_NAME) { - pdfi_pop(ctx, 1); + pdfi_countup(n); + pdfi_pop(ctx, 1); + + if (pdfi_type_of(n) != PDF_NAME) { code = gs_note_error(gs_error_typecheck); goto setgstate_error; } code = pdfi_find_resource(ctx, (unsigned char *)"ExtGState", n, (pdf_dict *)stream_dict, page_dict, &o); - pdfi_pop(ctx, 1); if (code < 0) goto setgstate_error; - if (o->type != PDF_DICT) { + if (pdfi_type_of(o) != PDF_DICT) { code = gs_note_error(gs_error_typecheck); goto setgstate_error; } @@ -2329,6 +2304,7 @@ setgstate_error: code1 = pdfi_loop_detector_cleartomark(ctx); if (code == 0) code = code1; + pdfi_countdown(n); pdfi_countdown(o); return code; } diff --git a/pdf/pdf_image.c b/pdf/pdf_image.c index 554cc344..43b70f56 100644 --- a/pdf/pdf_image.c +++ b/pdf/pdf_image.c @@ -173,7 +173,7 @@ pdfi_find_alternate(pdf_context *ctx, pdf_obj *alt) int code; bool flag; - if (alt->type != PDF_ARRAY) + if (pdfi_type_of(alt) != PDF_ARRAY) return NULL; array = (pdf_array *)alt; @@ -528,6 +528,10 @@ pdfi_get_image_info(pdf_context *ctx, pdf_stream *image_obj, } info->BPC = 1; } + else if (info->BPC != 1 && info->BPC != 2 && info->BPC != 4 && info->BPC != 8 && info->BPC != 16) { + code = gs_note_error(gs_error_rangecheck); + goto errorExit; + } /* TODO: spec says if ImageMask is specified, and BPC is specified, then BPC must be 1 Should we flag an error if this is violated? */ @@ -541,11 +545,18 @@ pdfi_get_image_info(pdf_context *ctx, pdf_stream *image_obj, * GS implementation does. */ if (code != gs_error_undefined) { - pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_IMAGEDICT, "pdfi_get_image_info", NULL); + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_IMAGEDICT, "pdfi_get_image_info", NULL); if (ctx->args.pdfstoponwarning) goto errorExit; } } + if (info->Mask != NULL && (pdfi_type_of(info->Mask) != PDF_ARRAY && pdfi_type_of(info->Mask) != PDF_STREAM)) { + pdfi_countdown(info->Mask); + info->Mask = NULL; + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_IMAGEDICT, "pdfi_get_image_info", NULL); + if (ctx->args.pdfstoponwarning) + goto errorExit; + } /* Optional (apparently there is no abbreviation for "SMask"? */ code = pdfi_dict_get(ctx, image_dict, "SMask", &info->SMask); @@ -558,7 +569,7 @@ pdfi_get_image_info(pdf_context *ctx, pdf_stream *image_obj, code = 0; } } else { - if (info->SMask->type == PDF_NAME) { + if (pdfi_type_of(info->SMask) == PDF_NAME) { pdf_obj *o = NULL; code = pdfi_find_resource(ctx, (unsigned char *)"ExtGState", (pdf_name *)info->SMask, image_dict, page_dict, &o); @@ -568,9 +579,12 @@ pdfi_get_image_info(pdf_context *ctx, pdf_stream *image_obj, } } - if (info->SMask->type != PDF_STREAM){ + if (pdfi_type_of(info->SMask) != PDF_STREAM){ pdfi_countdown(info->SMask); info->SMask = NULL; + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_IMAGEDICT, "pdfi_get_image_info", NULL); + if (ctx->args.pdfstoponwarning) + goto errorExit; } } @@ -592,6 +606,13 @@ pdfi_get_image_info(pdf_context *ctx, pdf_stream *image_obj, if (code != gs_error_undefined) goto errorExit; } + if (info->ColorSpace != NULL && (pdfi_type_of(info->ColorSpace) != PDF_NAME && pdfi_type_of(info->ColorSpace) != PDF_ARRAY)) { + pdfi_countdown(info->ColorSpace); + info->ColorSpace = NULL; + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_IMAGEDICT, "pdfi_get_image_info", NULL); + if (ctx->args.pdfstoponwarning) + goto errorExit; + } /* Optional (default is to use from graphics state) */ /* (no abbreviation for inline) */ @@ -607,6 +628,13 @@ pdfi_get_image_info(pdf_context *ctx, pdf_stream *image_obj, if (code != gs_error_undefined) goto errorExit; } + if (info->Alternates != NULL && pdfi_type_of(info->Alternates) != PDF_ARRAY) { + pdfi_countdown(info->Alternates); + info->Alternates = NULL; + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_IMAGEDICT, "pdfi_get_image_info", NULL); + if (ctx->args.pdfstoponwarning) + goto errorExit; + } /* Optional (required in PDF1.0, obsolete, do we support?) */ code = pdfi_dict_get(ctx, image_dict, "Name", &info->Name); @@ -614,6 +642,13 @@ pdfi_get_image_info(pdf_context *ctx, pdf_stream *image_obj, if (code != gs_error_undefined) goto errorExit; } + if (info->Name != NULL && pdfi_type_of(info->Name) != PDF_NAME) { + pdfi_countdown(info->Name); + info->Name = NULL; + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_IMAGEDICT, "pdfi_get_image_info", NULL); + if (ctx->args.pdfstoponwarning) + goto errorExit; + } /* Required "if image is structural content item" */ /* TODO: Figure out what to do here */ @@ -629,6 +664,13 @@ pdfi_get_image_info(pdf_context *ctx, pdf_stream *image_obj, if (code != gs_error_undefined) goto errorExit; } + if (info->Decode != NULL && pdfi_type_of(info->Decode) != PDF_ARRAY) { + pdfi_countdown(info->Decode); + info->Decode = NULL; + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_IMAGEDICT, "pdfi_get_image_info", NULL); + if (ctx->args.pdfstoponwarning) + goto errorExit; + } /* Optional "Optional Content" */ code = pdfi_dict_get_type(ctx, image_dict, "OC", PDF_DICT, (pdf_obj **)&info->OC); @@ -643,10 +685,17 @@ pdfi_get_image_info(pdf_context *ctx, pdf_stream *image_obj, if (code != gs_error_undefined) goto errorExit; } + if (info->Filter != NULL && (pdfi_type_of(info->Filter) != PDF_NAME && pdfi_type_of(info->Filter) != PDF_ARRAY)) { + pdfi_countdown(info->Filter); + info->Filter = NULL; + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_IMAGEDICT, "pdfi_get_image_info", NULL); + if (ctx->args.pdfstoponwarning) + goto errorExit; + } /* Check and set JPXDecode flag for later */ info->is_JPXDecode = false; - if (info->Filter && info->Filter->type == PDF_NAME) { + if (info->Filter && pdfi_type_of(info->Filter) == PDF_NAME) { if (pdfi_name_is((pdf_name *)info->Filter, "JPXDecode")) info->is_JPXDecode = true; } @@ -657,6 +706,13 @@ pdfi_get_image_info(pdf_context *ctx, pdf_stream *image_obj, if (code != gs_error_undefined) goto errorExit; } + if (info->DecodeParms != NULL && (pdfi_type_of(info->DecodeParms) != PDF_DICT && pdfi_type_of(info->DecodeParms) != PDF_ARRAY)) { + pdfi_countdown(info->DecodeParms); + info->DecodeParms = NULL; + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_IMAGEDICT, "pdfi_get_image_info", NULL); + if (ctx->args.pdfstoponwarning) + goto errorExit; + } return 0; @@ -987,6 +1043,9 @@ pdfi_do_image_smask(pdf_context *ctx, pdf_c_stream *source, pdfi_image_info_t *i if (code < 0) return code; + if (pdfi_type_of(image_info->SMask) != PDF_STREAM) + return_error(gs_error_typecheck); + if (image_info->SMask->object_num != 0) { if (pdfi_loop_detector_check_object(ctx, image_info->SMask->object_num)) return gs_note_error(gs_error_circular_reference); @@ -1018,22 +1077,23 @@ pdfi_do_image_smask(pdf_context *ctx, pdf_c_stream *source, pdfi_image_info_t *i pdfi_seek(ctx, ctx->main_stream, pdfi_stream_offset(ctx, (pdf_stream *)image_info->SMask), SEEK_SET); - if (image_info->SMask->type == PDF_DICT) { - code = pdfi_obj_dict_to_stream(ctx, (pdf_dict *)image_info->SMask, &stream_obj, false); - if (code == 0) { - code = pdfi_do_image_or_form(ctx, image_info->stream_dict, - image_info->page_dict, (pdf_obj *)stream_obj); + switch (pdfi_type_of(image_info->SMask)) { + case PDF_DICT: + code = pdfi_obj_dict_to_stream(ctx, (pdf_dict *)image_info->SMask, &stream_obj, false); + if (code == 0) { + code = pdfi_do_image_or_form(ctx, image_info->stream_dict, + image_info->page_dict, (pdf_obj *)stream_obj); - pdfi_countdown(stream_obj); - } - } else { - if (image_info->SMask->type == PDF_STREAM) + pdfi_countdown(stream_obj); + } + break; + case PDF_STREAM: code = pdfi_do_image_or_form(ctx, image_info->stream_dict, image_info->page_dict, image_info->SMask); - else { + break; + default: code = gs_note_error(gs_error_typecheck); goto exit; - } } pdfi_seek(ctx, ctx->main_stream, savedoffset, SEEK_SET); @@ -1434,7 +1494,7 @@ pdfi_image_get_color(pdf_context *ctx, pdf_c_stream *source, pdfi_image_info_t * pcs, image_info->inline_image); if (code < 0) { dmprintf(ctx->memory, "WARNING: Image has unsupported ColorSpace "); - if (ColorSpace->type == PDF_NAME) { + if (pdfi_type_of(ColorSpace) == PDF_NAME) { pdf_name *name = (pdf_name *)ColorSpace; char str[100]; int length = name->length; @@ -1496,7 +1556,7 @@ pdfi_make_smask_dict(pdf_context *ctx, pdf_stream *image_stream, pdfi_image_info goto exit; } - if (image_stream->type != PDF_STREAM) { + if (pdfi_type_of(image_stream) != PDF_STREAM) { code = gs_note_error(gs_error_typecheck); goto exit; } @@ -1623,7 +1683,7 @@ pdfi_do_image(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *stream_dict, pdf_ memset(&smask_info, 0, sizeof(mask_info)); /* Make sure the image is a stream (which we will assume in later code) */ - if (image_stream->type != PDF_STREAM) + if (pdfi_type_of(image_stream) != PDF_STREAM) return_error(gs_error_typecheck); if (!inline_image) { @@ -1867,23 +1927,26 @@ pdfi_do_image(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *stream_dict, pdf_ /* Get the Mask data either as an array or a dict, if present */ if (image_info.Mask != NULL) { - if (image_info.Mask->type == PDF_ARRAY) { - mask_array = (pdf_array *)image_info.Mask; - } else if (image_info.Mask->type == PDF_STREAM) { - mask_stream = (pdf_stream *)image_info.Mask; - code = pdfi_get_image_info(ctx, mask_stream, page_dict, - stream_dict, inline_image, &mask_info); - if (code < 0) - goto cleanupExit; - } else { - pdfi_countdown(image_info.Mask); - image_info.Mask = NULL; - pdfi_set_warning(ctx, 0, NULL, W_PDF_MASK_ERROR, "pdfi_do_image", NULL); + switch (pdfi_type_of(image_info.Mask)) { + case PDF_ARRAY: + mask_array = (pdf_array *)image_info.Mask; + break; + case PDF_STREAM: + mask_stream = (pdf_stream *)image_info.Mask; + code = pdfi_get_image_info(ctx, mask_stream, page_dict, + stream_dict, inline_image, &mask_info); + if (code < 0) + goto cleanupExit; + break; + default: + pdfi_countdown(image_info.Mask); + image_info.Mask = NULL; + pdfi_set_warning(ctx, 0, NULL, W_PDF_MASK_ERROR, "pdfi_do_image", NULL); } } /* Get the SMask info if we will need it (Type 3x images) */ - if (image_info.SMask && image_info.SMask->type == PDF_STREAM && ctx->device_state.preserve_smask) { + if (image_info.SMask && pdfi_type_of(image_info.SMask) == PDF_STREAM && ctx->device_state.preserve_smask) { /* smask_dict non-NULL is used to flag a Type 3x image below */ smask_stream = (pdf_stream *)image_info.SMask; code = pdfi_get_image_info(ctx, smask_stream, page_dict, stream_dict, @@ -2305,7 +2368,7 @@ static int pdfi_form_stream_hack(pdf_context *ctx, pdf_dict *form_dict, pdf_stre *hacked_stream = NULL; - if (form_dict->type == PDF_STREAM) + if (pdfi_type_of(form_dict) == PDF_STREAM) return 0; if (!ctx->args.pdfstoponerror) { @@ -2325,7 +2388,7 @@ static int pdfi_form_stream_hack(pdf_context *ctx, pdf_dict *form_dict, pdf_stre pdfi_countup(d); do { code = pdfi_dict_knownget(ctx, d, "Parent", (pdf_obj **)&Parent); - if (code > 0) { + if (code > 0 && pdfi_type_of(Parent) == PDF_DICT) { if (Parent->object_num == stream_obj->object_num) { pdfi_countdown(d); pdfi_countdown(Parent); @@ -2386,7 +2449,7 @@ static int pdfi_do_form(pdf_context *ctx, pdf_dict *page_dict, pdf_stream *form_ #if DEBUG_IMAGES dbgmprintf(ctx->memory, "pdfi_do_form BEGIN\n"); #endif - if (form_obj->type != PDF_STREAM) { + if (pdfi_type_of(form_obj) != PDF_STREAM) { code = pdfi_form_stream_hack(ctx, (pdf_dict *)form_obj, &hacked_stream); if (code < 0) return code; @@ -2530,10 +2593,15 @@ int pdfi_do_image_or_form(pdf_context *ctx, pdf_dict *stream_dict, else goto exit; } + if (pdfi_type_of(n) != PDF_NAME) { + code = gs_note_error(gs_error_typecheck); + goto exit; + } + if (pdfi_name_is(n, "Image")) { gs_offset_t savedoffset; - if (xobject_obj->type != PDF_STREAM) { + if (pdfi_type_of(xobject_obj) != PDF_STREAM) { code = gs_note_error(gs_error_typecheck); goto exit; } @@ -2568,14 +2636,17 @@ int pdfi_Do(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) pdf_name *n = NULL; pdf_obj *o = NULL; pdf_dict *sdict = NULL; - bool known = false; + bool known = false, AddedParent = false; if (pdfi_count_stack(ctx) < 1) { code = gs_note_error(gs_error_stackunderflow); goto exit1; } n = (pdf_name *)ctx->stack_top[-1]; - if (n->type != PDF_NAME) { + pdfi_countup(n); + pdfi_pop(ctx, 1); + + if (pdfi_type_of(n) != PDF_NAME) { code = gs_note_error(gs_error_typecheck); goto exit1; } @@ -2590,7 +2661,7 @@ int pdfi_Do(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) if (code < 0) goto exit; - if (o->type != PDF_STREAM && o->type != PDF_DICT) { + if (pdfi_type_of(o) != PDF_STREAM && pdfi_type_of(o) != PDF_DICT) { code = gs_note_error(gs_error_typecheck); goto exit; } @@ -2610,9 +2681,11 @@ int pdfi_Do(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) code = pdfi_dict_put(ctx, sdict, "Parent", (pdf_obj *)stream_dict); if (code < 0) goto exit; + pdfi_countup(sdict); + AddedParent = true; } - code = pdfi_loop_detector_cleartomark(ctx); + (void)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. @@ -2627,15 +2700,21 @@ 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); + pdfi_countdown(n); pdfi_countdown(o); - pdfi_pop(ctx, 1); + if (AddedParent == true) { + if (code >= 0) + code = pdfi_dict_delete(ctx, sdict, "Parent"); + else + (void)pdfi_dict_delete(ctx, sdict, "Parent"); + pdfi_countdown(sdict); + } return code; 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(n); pdfi_countdown(o); - pdfi_pop(ctx, 1); return code; } diff --git a/pdf/pdf_int.c b/pdf/pdf_int.c index 9493922b..d32b3f7e 100644 --- a/pdf/pdf_int.c +++ b/pdf/pdf_int.c @@ -36,6 +36,7 @@ #include "pdf_trans.h" #include "pdf_optcontent.h" #include "pdf_sec.h" +#include <stdlib.h> #include "gsstate.h" /* for gs_gstate_free */ @@ -134,6 +135,7 @@ int pdfi_skip_eol(pdf_context *ctx, pdf_c_stream *s) return 0; if (c >= 0) pdfi_unread_byte(ctx, s, (byte)c); + pdfi_set_warning(ctx, 0, NULL, W_PDF_STREAM_BAD_KEYWORD, "pdfi_skip_eol", NULL); return 0; } @@ -162,11 +164,14 @@ static float acrobat_compatible_atof(char *s) } if (*s == '.') { + float MAX = (MAX_FLOAT-9)/10; float v = (float)i; float n = 0; float d = 1; ++s; - while (*s >= '0' && *s <= '9') { + /* Bug 705211: Ensure that we don't overflow n here - just ignore any + * trailing digits after this. This will be plenty accurate enough. */ + while (*s >= '0' && *s <= '9' && n <= MAX) { n = 10 * n + (*s - '0'); d = 10 * d; ++s; @@ -178,6 +183,83 @@ static float acrobat_compatible_atof(char *s) } } +int pdfi_read_bare_int(pdf_context *ctx, pdf_c_stream *s, int *parsed_int) +{ + int index = 0; + int int_val = 0; + int negative = 0; + +restart: + pdfi_skip_white(ctx, s); + + do { + int c = pdfi_read_byte(ctx, s); + if (c == EOFC) + break; + + if (c < 0) + return_error(gs_error_ioerror); + + if (iswhite(c)) { + break; + } else if (c == '%' && index == 0) { + pdfi_skip_comment(ctx, s); + goto restart; + } else if (isdelimiter(c)) { + pdfi_unread_byte(ctx, s, (byte)c); + break; + } + + if (c >= '0' && c <= '9') { + int_val = int_val*10 + c - '0'; + } else if (c == '.') { + goto error; + } else if (c == 'e' || c == 'E') { + pdfi_set_warning(ctx, 0, NULL, W_PDF_NUM_EXPONENT, "pdfi_read_num", NULL); + goto error; + } else if (c == '-') { + /* Any - sign not at the start of the string indicates a malformed number. */ + if (index != 0 || negative) { + pdfi_set_error(ctx, 0, NULL, E_PDF_MALFORMEDNUMBER, "pdfi_read_num", NULL); + if (ctx->args.pdfstoponerror) + return_error(gs_error_syntaxerror); + goto error; + } + negative = 1; + } else if (c == '+') { + if (index == 0) { + /* Just drop the + it's pointless, and it'll get in the way + * of our negation handling for floats. */ + continue; + } else { + pdfi_set_error(ctx, 0, NULL, E_PDF_MALFORMEDNUMBER, "pdfi_read_num", NULL); + if (ctx->args.pdfstoponerror) + return_error(gs_error_syntaxerror); + goto error; + } + } else { + if (index > 0) { + 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_byte(ctx, s, (byte)c); + goto error; + } + if (++index > 255) + return_error(gs_error_syntaxerror); + } while(1); + + *parsed_int = negative ? -int_val : int_val; + if (ctx->args.pdfdebug) + dmprintf1(ctx->memory, " %d", *parsed_int); + return (index > 0); + +error: + *parsed_int = 0; + return_error(gs_error_syntaxerror); +} + static int pdfi_read_num(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_num, uint32_t indirect_gen) { byte Buffer[256]; @@ -297,12 +379,29 @@ static int pdfi_read_num(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_nu 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 (has_exponent) { - float f; - if (sscanf((char *)Buffer, "%g", &f) == 1) { - num->value.d = f; - } else { + float f, exp; + char *p = strstr((const char *)Buffer, "e"); + + if (p == NULL) + p = strstr((const char *)Buffer, "E"); + + if (p == NULL) { 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 { + p++; + + if (sscanf((char *)p, "%g", &exp) != 1 || exp > 38) { + 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 (sscanf((char *)Buffer, "%g", &f) == 1) { + num->value.d = f; + } else { + 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); @@ -443,6 +542,22 @@ static int pdfi_read_hexstring(pdf_context *ctx, pdf_c_stream *s, uint32_t indir if (hex1 < 0) break; + if (hex1 == '>') { + /* PDF Reference 1.7 page 56: + * "If the final digit of a hexadecimal string is missing that is, + * if there is an odd number of digits the final digit is assumed to be 0." + */ + hex1 = 0x30; + if (!ishex(hex0) || !ishex(hex1)) { + code = gs_note_error(gs_error_syntaxerror); + goto exit; + } + Buffer[index] = (fromhex(hex0) << 4) + fromhex(hex1); + if (ctx->args.pdfdebug) + dmprintf1(ctx->memory, "%c", hex1); + break; + } + if (!ishex(hex0) || !ishex(hex1)) { code = gs_note_error(gs_error_syntaxerror); goto exit; @@ -674,7 +789,7 @@ int pdfi_read_dict(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_num, uin if (code == 0) return_error(gs_error_syntaxerror); - if (ctx->stack_top[-1]->type != PDF_DICT_MARK) + if (pdfi_type_of(ctx->stack_top[-1]) != PDF_DICT_MARK) return_error(gs_error_typecheck); depth = pdfi_count_stack(ctx); @@ -708,18 +823,22 @@ int pdfi_skip_comment(pdf_context *ctx, pdf_c_stream *s) return 0; } -/* This function is slightly misnamed, for some keywords we do - * indeed read the keyword and return a PDF_KEYWORD object, but - * for null, true, false and R we create an appropriate object - * of that type (PDF_NULL, PDF_BOOL or PDF_INDIRECT_REF) - * and return it instead. - */ -static int pdfi_read_keyword(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_num, uint32_t indirect_gen) +#define PARAM1(A) # A, +#define PARAM2(A,B) A, +static const char pdf_token_strings[][10] = { +#include "pdf_tokens.h" +}; + +#define nelems(A) (sizeof(A)/sizeof(A[0])) + +typedef int (*bsearch_comparator)(const void *, const void *); + +int pdfi_read_bare_keyword(pdf_context *ctx, pdf_c_stream *s) { byte Buffer[256]; - unsigned short index = 0; - int c, code; - pdf_keyword *keyword; + int index = 0; + int c; + void *t; pdfi_skip_white(ctx, s); @@ -739,46 +858,94 @@ static int pdfi_read_keyword(pdf_context *ctx, pdf_c_stream *s, uint32_t indirec if (index >= 255 || index == 0) { if (ctx->args.pdfstoponerror) return_error(gs_error_syntaxerror); - strcpy((char *)Buffer, "KEYWORD_TOO_LONG"); - index = 16; + return TOKEN_INVALID_KEY; } - /* NB The code below uses 'Buffer', not the data stored in keyword->data to compare strings */ Buffer[index] = 0x00; + t = bsearch((const void *)Buffer, + (const void *)pdf_token_strings[TOKEN_INVALID_KEY+1], + nelems(pdf_token_strings)-(TOKEN_INVALID_KEY+1), + sizeof(pdf_token_strings[0]), + (bsearch_comparator)&strcmp); + if (t == NULL) + return TOKEN_INVALID_KEY; - code = pdfi_object_alloc(ctx, PDF_KEYWORD, index, (pdf_obj **)&keyword); - if (code < 0) - return code; + if (ctx->args.pdfdebug) + dmprintf1(ctx->memory, " %s\n", Buffer); - memcpy(keyword->data, Buffer, index); - pdfi_countup(keyword); + return (((const char *)t) - pdf_token_strings[0]) / sizeof(pdf_token_strings[0]); +} - keyword->indirect_num = indirect_num; - keyword->indirect_gen = indirect_gen; +static pdf_key lookup_keyword(const byte *Buffer) +{ + void *t = bsearch((const void *)Buffer, + (const void *)pdf_token_strings[TOKEN_INVALID_KEY+1], + nelems(pdf_token_strings)-(TOKEN_INVALID_KEY+1), + sizeof(pdf_token_strings[0]), + (bsearch_comparator)&strcmp); + if (t == NULL) + return TOKEN_NOT_A_KEYWORD; + + return (pdf_key)((((const char *)t) - pdf_token_strings[0]) / + sizeof(pdf_token_strings[0])); +} - if (ctx->args.pdfdebug) - dmprintf1(ctx->memory, " %s\n", Buffer); +/* This function is slightly misnamed. We read 'keywords' from + * the stream (including null, true, false and R), and will usually + * return them directly as TOKENs cast to be pointers. In the event + * that we can't match what we parse to a known keyword, we'll + * instead return a PDF_KEYWORD object. In the even that we parse + * an 'R', we will return a PDF_INDIRECT object. + */ +static int pdfi_read_keyword(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_num, uint32_t indirect_gen) +{ + byte Buffer[256]; + unsigned short index = 0; + int c, code; + pdf_keyword *keyword; + pdf_key key; - switch(Buffer[0]) { - case 'K': - if (keyword->length == 16 && memcmp(keyword->data, "KEYWORD_TOO_LONG", 16) == 0) { - keyword->key = TOKEN_INVALID_KEY; - } + pdfi_skip_white(ctx, s); + + do { + c = pdfi_read_byte(ctx, s); + if (c < 0) + break; + + if (iswhite(c) || isdelimiter(c)) { + pdfi_unread_byte(ctx, s, (byte)c); break; - case 'R': - if (keyword->length == 1){ + } + Buffer[index] = (byte)c; + index++; + } while (index < 255); + + if (index >= 255 || index == 0) { + if (ctx->args.pdfstoponerror) + return_error(gs_error_syntaxerror); + key = (index >= 255 ? TOKEN_TOO_LONG : TOKEN_INVALID_KEY); + index = 0; + Buffer[0] = 0; + } else { + Buffer[index] = 0x00; + key = lookup_keyword(Buffer); + + if (ctx->args.pdfdebug) + dmprintf1(ctx->memory, " %s\n", Buffer); + + switch (key) { + case TOKEN_R: + { pdf_indirect_ref *o; uint64_t obj_num; uint32_t gen_num; - pdfi_countdown(keyword); - if(pdfi_count_stack(ctx) < 2) { pdfi_clearstack(ctx); return_error(gs_error_stackunderflow); } - if(((pdf_obj *)ctx->stack_top[-1])->type != PDF_INT || ((pdf_obj *)ctx->stack_top[-2])->type != PDF_INT) { + if(pdfi_type_of(ctx->stack_top[-1]) != PDF_INT || pdfi_type_of(ctx->stack_top[-2]) != PDF_INT) { pdfi_clearstack(ctx); return_error(gs_error_typecheck); } @@ -803,104 +970,43 @@ static int pdfi_read_keyword(pdf_context *ctx, pdf_c_stream *s, uint32_t indirec return code; } - break; - case 'e': - if (keyword->length == 9 && memcmp((const char *)Buffer, "endstream", 9) == 0) - keyword->key = TOKEN_ENDSTREAM; - else { - if (keyword->length == 6 && memcmp((const char *)Buffer, "endobj", 6) == 0) - keyword->key = TOKEN_ENDOBJ; - } - break; - case 'o': - if (keyword->length == 3 && memcmp((const char *)Buffer, "obj", 3) == 0) - keyword->key = TOKEN_OBJ; - break; - case 's': - if (keyword->length == 6 && memcmp((const char *)Buffer, "stream", 6) == 0){ - keyword->key = TOKEN_STREAM; + case TOKEN_NOT_A_KEYWORD: + /* Unexpected keyword found. We'll allocate an object for the buffer below. */ + break; + case TOKEN_STREAM: code = pdfi_skip_eol(ctx, s); - if (code < 0) { - pdfi_countdown(keyword); - return code; - } - } - else { - if (keyword->length == 9 && memcmp((const char *)Buffer, "startxref", 9) == 0) - keyword->key = TOKEN_STARTXREF; - } - break; - case 't': - if (keyword->length == 4 && memcmp((const char *)Buffer, "true", 4) == 0) { - pdf_bool *o; - - pdfi_countdown(keyword); - - code = pdfi_object_alloc(ctx, PDF_BOOL, 0, (pdf_obj **)&o); - if (code < 0) - return code; - - o->value = true; - o->indirect_num = indirect_num; - o->indirect_gen = indirect_gen; - - code = pdfi_push(ctx, (pdf_obj *)o); - if (code < 0) - pdfi_free_object((pdf_obj *)o); - return code; - } - else { - if (keyword->length == 7 && memcmp((const char *)Buffer, "trailer", 7) == 0) - keyword->key = TOKEN_TRAILER; - } - break; - case 'f': - if (keyword->length == 5 && memcmp((const char *)Buffer, "false", 5) == 0) - { - pdf_bool *o; - - pdfi_countdown(keyword); - - code = pdfi_object_alloc(ctx, PDF_BOOL, 0, (pdf_obj **)&o); if (code < 0) return code; + /* fallthrough */ + case TOKEN_TRUE: + case TOKEN_FALSE: + case TOKEN_null: + default: + /* This is the fast, common exit case. We just push the key + * onto the stack. No allocation required. No deallocation + * in the case of error. */ + return pdfi_push(ctx, (pdf_obj *)(intptr_t)key); + } + } - o->value = false; - o->indirect_num = indirect_num; - o->indirect_gen = indirect_gen; - - code = pdfi_push(ctx, (pdf_obj *)o); - if (code < 0) - pdfi_free_object((pdf_obj *)o); - return code; - } - break; - case 'n': - if (keyword->length == 4 && memcmp((const char *)Buffer, "null", 4) == 0){ - pdf_obj *o; + /* Unexpected keyword. We can't handle this with the fast no-allocation case. */ + code = pdfi_object_alloc(ctx, PDF_KEYWORD, index, (pdf_obj **)&keyword); + if (code < 0) + return code; - pdfi_countdown(keyword); + if (index) + memcpy(keyword->data, Buffer, index); - code = pdfi_object_alloc(ctx, PDF_NULL, 0, &o); - if (code < 0) - return code; - o->indirect_num = indirect_num; - o->indirect_gen = indirect_gen; + /* keyword->length set as part of allocation. */ + keyword->indirect_num = indirect_num; + keyword->indirect_gen = indirect_gen; - code = pdfi_push(ctx, o); - if (code < 0) - pdfi_free_object((pdf_obj *)o); - return code; - } - break; - case 'x': - if (keyword->length == 4 && memcmp((const char *)Buffer, "xref", 4) == 0) - keyword->key = TOKEN_XREF; - break; - } + if (ctx->args.pdfdebug) + dmprintf1(ctx->memory, " %s\n", Buffer); code = pdfi_push(ctx, (pdf_obj *)keyword); - pdfi_countdown(keyword); + if (code < 0) + pdfi_free_object((pdf_obj *)keyword); return code; } @@ -957,6 +1063,8 @@ rescan: c = pdfi_read_byte(ctx, s); } if (c == '<') { + if (ctx->object_nesting < MAX_NESTING_DEPTH) + ctx->object_nesting++; if (ctx->args.pdfdebug) dmprintf (ctx->memory, " <<\n"); code = pdfi_mark_stack(ctx, PDF_DICT_MARK); @@ -983,9 +1091,14 @@ rescan: if (c < 0) return (gs_error_ioerror); if (c == '>') { - code = pdfi_dict_from_stack(ctx, indirect_num, indirect_gen, false); - if (code < 0) - return code; + if (ctx->object_nesting > 0) + ctx->object_nesting--; + if (ctx->object_nesting < MAX_NESTING_DEPTH) { + code = pdfi_dict_from_stack(ctx, indirect_num, indirect_gen, false); + if (code < 0) + return code; + } else + pdfi_set_error(ctx, 0, NULL, E_PDF_NESTEDTOODEEP, "pdfi_read_token", NULL); return 1; } else { pdfi_unread_byte(ctx, s, (byte)c); @@ -999,6 +1112,8 @@ rescan: return 1; break; case '[': + if (ctx->object_nesting < MAX_NESTING_DEPTH) + ctx->object_nesting++; if (ctx->args.pdfdebug) dmprintf (ctx->memory, "["); code = pdfi_mark_stack(ctx, PDF_ARRAY_MARK); @@ -1007,9 +1122,14 @@ rescan: return 1; break; case ']': - code = pdfi_array_from_stack(ctx, indirect_num, indirect_gen); - if (code < 0) - return code; + if (ctx->object_nesting > 0) + ctx->object_nesting--; + if (ctx->object_nesting < MAX_NESTING_DEPTH) { + code = pdfi_array_from_stack(ctx, indirect_num, indirect_gen); + if (code < 0) + return code; + } else + pdfi_set_error(ctx, 0, NULL, E_PDF_NESTEDTOODEEP, "pdfi_read_token", NULL); break; case '{': if (ctx->args.pdfdebug) @@ -1080,56 +1200,63 @@ static char op_table_1[27][1] = { static int pdfi_interpret_stream_operator(pdf_context *ctx, pdf_c_stream *source, pdf_dict *stream_dict, pdf_dict *page_dict); +static int +make_keyword_obj(pdf_context *ctx, const byte *data, int length, pdf_keyword **pkey) +{ + byte Buffer[256]; + pdf_key key; + int code; + + memcpy(Buffer, data, length); + Buffer[length] = 0; + key = lookup_keyword(Buffer); + if (key != TOKEN_INVALID_KEY) { + /* The common case. We've found a real key, just cast the token to + * a pointer, and return that. */ + *pkey = (pdf_keyword *)PDF_TOKEN_AS_OBJ(key); + return 1; + } + /* We still haven't found a real keyword. Allocate a new object and + * return it. */ + code = pdfi_object_alloc(ctx, PDF_KEYWORD, length, (pdf_obj **)pkey); + if (code < 0) + return code; + if (length) + memcpy((*pkey)->data, Buffer, length); + pdfi_countup(*pkey); + + return 1; +} + static int search_table_3(pdf_context *ctx, unsigned char *str, pdf_keyword **key) { - int i, code = 0; + int i; for (i = 0; i < 5; i++) { - if (memcmp(str, op_table_3[i], 3) == 0) { - code = pdfi_object_alloc(ctx, PDF_KEYWORD, 3, (pdf_obj **)key); - if (code < 0) - return code; - memcpy((*key)->data, str, 3); - (*key)->key = TOKEN_NOT_A_KEYWORD; - pdfi_countup(*key); - return 1; - } + if (memcmp(str, op_table_3[i], 3) == 0) + return make_keyword_obj(ctx, str, 3, key); } return 0; } static int search_table_2(pdf_context *ctx, unsigned char *str, pdf_keyword **key) { - int i, code = 0; + int i; for (i = 0; i < 39; i++) { - if (memcmp(str, op_table_2[i], 2) == 0) { - code = pdfi_object_alloc(ctx, PDF_KEYWORD, 2, (pdf_obj **)key); - if (code < 0) - return code; - memcpy((*key)->data, str, 2); - (*key)->key = TOKEN_NOT_A_KEYWORD; - pdfi_countup(*key); - return 1; - } + if (memcmp(str, op_table_2[i], 2) == 0) + return make_keyword_obj(ctx, str, 2, key); } return 0; } static int search_table_1(pdf_context *ctx, unsigned char *str, pdf_keyword **key) { - int i, code = 0; + int 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) - return code; - memcpy((*key)->data, str, 1); - (*key)->key = TOKEN_NOT_A_KEYWORD; - pdfi_countup(*key); - return 1; - } + if (memcmp(str, op_table_1[i], 1) == 0) + return make_keyword_obj(ctx, str, 1, key); } return 0; } @@ -1138,45 +1265,41 @@ static int split_bogus_operator(pdf_context *ctx, pdf_c_stream *source, pdf_dict { int code = 0; pdf_keyword *keyword = (pdf_keyword *)ctx->stack_top[-1], *key1 = NULL, *key2 = NULL; + int length = keyword->length - 6; - if (keyword->length > 6) { + if (length > 0) { /* Longer than 2 3-character operators, we only allow for up to two * operators. Check to see if it includes an endstream or endobj. */ - if (memcmp(&keyword->data[keyword->length - 6], "endobj", 6) == 0) { - code = pdfi_object_alloc(ctx, PDF_KEYWORD, keyword->length - 6, (pdf_obj **)&key1); + if (memcmp(&keyword->data[length], "endobj", 6) == 0) { + /* Keyword is "<something>endobj". So make a keyword just from + * <something>, push that, execute it, then push endobj. */ + code = make_keyword_obj(ctx, keyword->data, length, &key1); if (code < 0) goto error_exit; - memcpy(key1->data, keyword->data, key1->length); pdfi_pop(ctx, 1); pdfi_push(ctx, (pdf_obj *)key1); + pdfi_countdown(key1); /* Drop the reference returned by make_keyword_obj. */ code = pdfi_interpret_stream_operator(ctx, source, stream_dict, page_dict); if (code < 0) goto error_exit; - code = pdfi_object_alloc(ctx, PDF_KEYWORD, 6, (pdf_obj **)&key1); - if (code < 0) - goto error_exit; - memcpy(key1->data, "endobj", 6); - key1->key = TOKEN_ENDOBJ; - pdfi_push(ctx, (pdf_obj *)key1); + pdfi_push(ctx, PDF_TOKEN_AS_OBJ(TOKEN_ENDOBJ)); return 0; } else { - if (keyword->length > 9 && memcmp(&keyword->data[keyword->length - 9], "endstream", 9) == 0) { - code = pdfi_object_alloc(ctx, PDF_KEYWORD, keyword->length - 9, (pdf_obj **)&key1); + length = keyword->length - 9; + if (length > 0 && memcmp(&keyword->data[length], "endstream", 9) == 0) { + /* Keyword is "<something>endstream". So make a keyword just from + * <something>, push that, execute it, then push endstream. */ + code = make_keyword_obj(ctx, keyword->data, length, &key1); if (code < 0) goto error_exit; - memcpy(key1->data, keyword->data, key1->length); pdfi_pop(ctx, 1); pdfi_push(ctx, (pdf_obj *)key1); + pdfi_countdown(key1); /* Drop the reference returned by make_keyword_obj. */ code = pdfi_interpret_stream_operator(ctx, source, stream_dict, page_dict); if (code < 0) goto error_exit; - code = pdfi_object_alloc(ctx, PDF_KEYWORD, 9, (pdf_obj **)&key1); - if (code < 0) - goto error_exit; - memcpy(key1->data, "endstream", 9); - key1->key = TOKEN_ENDSTREAM; - pdfi_push(ctx, (pdf_obj *)key1); + pdfi_push(ctx, PDF_TOKEN_AS_OBJ(TOKEN_ENDSTREAM)); return 0; } else { pdfi_clearstack(ctx); @@ -1191,15 +1314,15 @@ static int split_bogus_operator(pdf_context *ctx, pdf_c_stream *source, pdf_dict goto error_exit; if (code > 0) { - switch(keyword->length - 3) { + switch (keyword->length - 3) { case 1: - code = search_table_1(ctx, &keyword->data[key1->length], &key2); + code = search_table_1(ctx, &keyword->data[3], &key2); break; case 2: - code = search_table_1(ctx, &keyword->data[key1->length], &key2); + code = search_table_2(ctx, &keyword->data[3], &key2); break; case 3: - code = search_table_1(ctx, &keyword->data[key1->length], &key2); + code = search_table_3(ctx, &keyword->data[3], &key2); break; default: goto error_exit; @@ -1225,13 +1348,13 @@ static int split_bogus_operator(pdf_context *ctx, pdf_c_stream *source, pdf_dict if (code > 0) { switch(keyword->length - 2) { case 1: - code = search_table_1(ctx, &keyword->data[key1->length], &key2); + code = search_table_1(ctx, &keyword->data[2], &key2); break; case 2: - code = search_table_1(ctx, &keyword->data[key1->length], &key2); + code = search_table_2(ctx, &keyword->data[2], &key2); break; case 3: - code = search_table_1(ctx, &keyword->data[key1->length], &key2); + code = search_table_3(ctx, &keyword->data[2], &key2); break; default: goto error_exit; @@ -1255,13 +1378,13 @@ static int split_bogus_operator(pdf_context *ctx, pdf_c_stream *source, pdf_dict switch(keyword->length - 1) { case 1: - code = search_table_1(ctx, &keyword->data[key1->length], &key2); + code = search_table_1(ctx, &keyword->data[1], &key2); break; case 2: - code = search_table_1(ctx, &keyword->data[key1->length], &key2); + code = search_table_2(ctx, &keyword->data[1], &key2); break; case 3: - code = search_table_1(ctx, &keyword->data[key1->length], &key2); + code = search_table_3(ctx, &keyword->data[1], &key2); break; default: goto error_exit; @@ -1289,354 +1412,353 @@ error_exit: return code; } -#define K1(a) (a) -#define K2(a, b) ((a << 8) + b) -#define K3(a, b, c) ((a << 16) + (b << 8) + c) - static int pdfi_interpret_stream_operator(pdf_context *ctx, pdf_c_stream *source, pdf_dict *stream_dict, pdf_dict *page_dict) { - pdf_keyword *keyword = (pdf_keyword *)ctx->stack_top[-1]; - uint32_t op = 0; - int i, code = 0; + pdf_obj *keyword = ctx->stack_top[-1]; + int code = 0; - if (keyword->length > 3) { - /* This means we either have a corrupted or illegal operator. The most - * usual corruption is two concatented operators (eg QBT instead of Q BT) - * I plan to tackle this by trying to see if I can make two or more operators - * out of the mangled one. Note this will also be done below in the 'default' - * case where we don't recognise a keyword with 3 or fewer characters. - */ - code = split_bogus_operator(ctx, source, stream_dict, page_dict); - if (code < 0) - return code; - if (pdfi_count_stack(ctx) > 0) { - keyword = (pdf_keyword *)ctx->stack_top[-1]; - if (keyword->key != TOKEN_NOT_A_KEYWORD) - return REPAIRED_KEYWORD; - } else - return 0; - } else { - for (i=0;i < keyword->length;i++) { - op = (op << 8) + keyword->data[i]; - } - switch(op) { - case K1('b'): /* closepath, fill, stroke */ + if (keyword < PDF_TOKEN_AS_OBJ(TOKEN__LAST_KEY)) + { + switch((uintptr_t)keyword) { + case TOKEN_b: /* closepath, fill, stroke */ pdfi_pop(ctx, 1); code = pdfi_b(ctx); break; - case K1('B'): /* fill, stroke */ + case TOKEN_B: /* fill, stroke */ pdfi_pop(ctx, 1); code = pdfi_B(ctx); break; - case K2('b','*'): /* closepath, eofill, stroke */ + case TOKEN_bstar: /* closepath, eofill, stroke */ pdfi_pop(ctx, 1); code = pdfi_b_star(ctx); break; - case K2('B','*'): /* eofill, stroke */ + case TOKEN_Bstar: /* eofill, stroke */ pdfi_pop(ctx, 1); code = pdfi_B_star(ctx); break; - case K2('B','I'): /* begin inline image */ + case TOKEN_BI: /* begin inline image */ pdfi_pop(ctx, 1); code = pdfi_BI(ctx); break; - case K3('B','D','C'): /* begin marked content sequence with property list */ + case TOKEN_BDC: /* begin marked content sequence with property list */ pdfi_pop(ctx, 1); code = pdfi_op_BDC(ctx, stream_dict, page_dict); break; - case K3('B','M','C'): /* begin marked content sequence */ + case TOKEN_BMC: /* begin marked content sequence */ pdfi_pop(ctx, 1); code = pdfi_op_BMC(ctx); break; - case K2('B','T'): /* begin text */ + case TOKEN_BT: /* begin text */ pdfi_pop(ctx, 1); code = pdfi_BT(ctx); break; - case K2('B','X'): /* begin compatibility section */ + case TOKEN_BX: /* begin compatibility section */ pdfi_pop(ctx, 1); break; - case K1('c'): /* curveto */ + case TOKEN_c: /* curveto */ pdfi_pop(ctx, 1); code = pdfi_curveto(ctx); break; - case K2('c','m'): /* concat */ + case TOKEN_cm: /* concat */ pdfi_pop(ctx, 1); code = pdfi_concat(ctx); break; - case K2('C','S'): /* set stroke colour space */ + case TOKEN_CS: /* set stroke colour space */ pdfi_pop(ctx, 1); code = pdfi_setstrokecolor_space(ctx, stream_dict, page_dict); break; - case K2('c','s'): /* set non-stroke colour space */ + case TOKEN_cs: /* set non-stroke colour space */ pdfi_pop(ctx, 1); code = pdfi_setfillcolor_space(ctx, stream_dict, page_dict); break; - break; - case K1('d'): /* set dash params */ + case TOKEN_d: /* set dash params */ pdfi_pop(ctx, 1); code = pdfi_setdash(ctx); break; - case K2('d','0'): /* set type 3 font glyph width */ + case TOKEN_d0: /* set type 3 font glyph width */ pdfi_pop(ctx, 1); code = pdfi_d0(ctx); break; - case K2('d','1'): /* set type 3 font glyph width and bounding box */ + case TOKEN_d1: /* set type 3 font glyph width and bounding box */ pdfi_pop(ctx, 1); code = pdfi_d1(ctx); break; - case K2('D','o'): /* invoke named XObject */ + case TOKEN_Do: /* invoke named XObject */ pdfi_pop(ctx, 1); code = pdfi_Do(ctx, stream_dict, page_dict); break; - case K2('D','P'): /* define marked content point with property list */ + case TOKEN_DP: /* define marked content point with property list */ pdfi_pop(ctx, 1); code = pdfi_op_DP(ctx, stream_dict, page_dict); break; - case K2('E','I'): /* end inline image */ + case TOKEN_EI: /* end inline image */ pdfi_pop(ctx, 1); code = pdfi_EI(ctx); break; - case K2('E','T'): /* end text */ + case TOKEN_ET: /* end text */ pdfi_pop(ctx, 1); code = pdfi_ET(ctx); break; - case K3('E','M','C'): /* end marked content sequence */ + case TOKEN_EMC: /* end marked content sequence */ pdfi_pop(ctx, 1); code = pdfi_op_EMC(ctx); break; - case K2('E','X'): /* end compatibility section */ + case TOKEN_EX: /* end compatibility section */ pdfi_pop(ctx, 1); break; - case K1('f'): /* fill */ + case TOKEN_f: /* fill */ pdfi_pop(ctx, 1); code = pdfi_fill(ctx); break; - case K1('F'): /* fill (obselete operator) */ + case TOKEN_F: /* fill (obselete operator) */ pdfi_pop(ctx, 1); code = pdfi_fill(ctx); break; - case K2('f','*'): /* eofill */ + case TOKEN_fstar: /* eofill */ pdfi_pop(ctx, 1); code = pdfi_eofill(ctx); break; - case K1('G'): /* setgray for stroke */ + case TOKEN_G: /* setgray for stroke */ pdfi_pop(ctx, 1); code = pdfi_setgraystroke(ctx); break; - case K1('g'): /* setgray for non-stroke */ + case TOKEN_g: /* setgray for non-stroke */ pdfi_pop(ctx, 1); code = pdfi_setgrayfill(ctx); break; - case K2('g','s'): /* set graphics state from dictionary */ + case TOKEN_gs: /* set graphics state from dictionary */ pdfi_pop(ctx, 1); code = pdfi_setgstate(ctx, stream_dict, page_dict); break; - case K1('h'): /* closepath */ + case TOKEN_h: /* closepath */ pdfi_pop(ctx, 1); code = pdfi_closepath(ctx); break; - case K1('i'): /* setflat */ + case TOKEN_i: /* setflat */ pdfi_pop(ctx, 1); code = pdfi_setflat(ctx); break; - case K2('I','D'): /* begin inline image data */ + case TOKEN_ID: /* begin inline image data */ pdfi_pop(ctx, 1); code = pdfi_ID(ctx, stream_dict, page_dict, source); break; - case K1('j'): /* setlinejoin */ + case TOKEN_j: /* setlinejoin */ pdfi_pop(ctx, 1); code = pdfi_setlinejoin(ctx); break; - case K1('J'): /* setlinecap */ + case TOKEN_J: /* setlinecap */ pdfi_pop(ctx, 1); code = pdfi_setlinecap(ctx); break; - case K1('K'): /* setcmyk for non-stroke */ + case TOKEN_K: /* setcmyk for non-stroke */ pdfi_pop(ctx, 1); code = pdfi_setcmykstroke(ctx); break; - case K1('k'): /* setcmyk for non-stroke */ + case TOKEN_k: /* setcmyk for non-stroke */ pdfi_pop(ctx, 1); code = pdfi_setcmykfill(ctx); break; - case K1('l'): /* lineto */ + case TOKEN_l: /* lineto */ pdfi_pop(ctx, 1); code = pdfi_lineto(ctx); break; - case K1('m'): /* moveto */ + case TOKEN_m: /* moveto */ pdfi_pop(ctx, 1); code = pdfi_moveto(ctx); break; - case K1('M'): /* setmiterlimit */ + case TOKEN_M: /* setmiterlimit */ pdfi_pop(ctx, 1); code = pdfi_setmiterlimit(ctx); break; - case K2('M','P'): /* define marked content point */ + case TOKEN_MP: /* define marked content point */ pdfi_pop(ctx, 1); code = pdfi_op_MP(ctx); break; - case K1('n'): /* newpath */ + case TOKEN_n: /* newpath */ pdfi_pop(ctx, 1); code = pdfi_newpath(ctx); break; - case K1('q'): /* gsave */ + case TOKEN_q: /* gsave */ pdfi_pop(ctx, 1); code = pdfi_op_q(ctx); break; - case K1('Q'): /* grestore */ + case TOKEN_Q: /* grestore */ pdfi_pop(ctx, 1); code = pdfi_op_Q(ctx); break; - case K1('r'): /* non-standard set rgb colour for non-stroke */ + case TOKEN_r: /* non-standard set rgb colour for non-stroke */ pdfi_pop(ctx, 1); code = pdfi_setrgbfill_array(ctx); break; - case K2('r','e'): /* append rectangle */ + case TOKEN_re: /* append rectangle */ pdfi_pop(ctx, 1); code = pdfi_rectpath(ctx); break; - case K2('R','G'): /* set rgb colour for stroke */ + case TOKEN_RG: /* set rgb colour for stroke */ pdfi_pop(ctx, 1); code = pdfi_setrgbstroke(ctx); break; - case K2('r','g'): /* set rgb colour for non-stroke */ + case TOKEN_rg: /* set rgb colour for non-stroke */ pdfi_pop(ctx, 1); code = pdfi_setrgbfill(ctx); break; - case K2('r','i'): /* set rendering intent */ + case TOKEN_ri: /* set rendering intent */ pdfi_pop(ctx, 1); code = pdfi_ri(ctx); break; - case K1('s'): /* closepath, stroke */ + case TOKEN_s: /* closepath, stroke */ pdfi_pop(ctx, 1); code = pdfi_closepath_stroke(ctx); break; - case K1('S'): /* stroke */ + case TOKEN_S: /* stroke */ pdfi_pop(ctx, 1); code = pdfi_stroke(ctx); break; - case K2('S','C'): /* set colour for stroke */ + case TOKEN_SC: /* set colour for stroke */ pdfi_pop(ctx, 1); code = pdfi_setstrokecolor(ctx); break; - case K2('s','c'): /* set colour for non-stroke */ + case TOKEN_sc: /* set colour for non-stroke */ pdfi_pop(ctx, 1); code = pdfi_setfillcolor(ctx); break; - case K3('S','C','N'): /* set special colour for stroke */ + case TOKEN_SCN: /* set special colour for stroke */ pdfi_pop(ctx, 1); code = pdfi_setcolorN(ctx, stream_dict, page_dict, false); break; - case K3('s','c','n'): /* set special colour for non-stroke */ + case TOKEN_scn: /* set special colour for non-stroke */ pdfi_pop(ctx, 1); code = pdfi_setcolorN(ctx, stream_dict, page_dict, true); break; - case K2('s','h'): /* fill with sahding pattern */ + case TOKEN_sh: /* fill with sahding pattern */ pdfi_pop(ctx, 1); code = pdfi_shading(ctx, stream_dict, page_dict); break; - case K2('T','*'): /* Move to start of next text line */ + case TOKEN_Tstar: /* Move to start of next text line */ pdfi_pop(ctx, 1); code = pdfi_T_star(ctx); break; - case K2('T','c'): /* set character spacing */ + case TOKEN_Tc: /* set character spacing */ pdfi_pop(ctx, 1); code = pdfi_Tc(ctx); break; - case K2('T','d'): /* move text position */ + case TOKEN_Td: /* move text position */ pdfi_pop(ctx, 1); code = pdfi_Td(ctx); break; - case K2('T','D'): /* Move text position, set leading */ + case TOKEN_TD: /* Move text position, set leading */ pdfi_pop(ctx, 1); code = pdfi_TD(ctx); break; - case K2('T','f'): /* set font and size */ + case TOKEN_Tf: /* set font and size */ pdfi_pop(ctx, 1); code = pdfi_Tf(ctx, stream_dict, page_dict); break; - case K2('T','j'): /* show text */ + case TOKEN_Tj: /* show text */ pdfi_pop(ctx, 1); code = pdfi_Tj(ctx); break; - case K2('T','J'): /* show text with individual glyph positioning */ + case TOKEN_TJ: /* show text with individual glyph positioning */ pdfi_pop(ctx, 1); code = pdfi_TJ(ctx); break; - case K2('T','L'): /* set text leading */ + case TOKEN_TL: /* set text leading */ pdfi_pop(ctx, 1); code = pdfi_TL(ctx); break; - case K2('T','m'): /* set text matrix */ + case TOKEN_Tm: /* set text matrix */ pdfi_pop(ctx, 1); code = pdfi_Tm(ctx); break; - case K2('T','r'): /* set text rendering mode */ + case TOKEN_Tr: /* set text rendering mode */ pdfi_pop(ctx, 1); code = pdfi_Tr(ctx); break; - case K2('T','s'): /* set text rise */ + case TOKEN_Ts: /* set text rise */ pdfi_pop(ctx, 1); code = pdfi_Ts(ctx); break; - case K2('T','w'): /* set word spacing */ + case TOKEN_Tw: /* set word spacing */ pdfi_pop(ctx, 1); code = pdfi_Tw(ctx); break; - case K2('T','z'): /* set text matrix */ + case TOKEN_Tz: /* set text matrix */ pdfi_pop(ctx, 1); code = pdfi_Tz(ctx); break; - case K1('v'): /* append curve (initial point replicated) */ + case TOKEN_v: /* append curve (initial point replicated) */ pdfi_pop(ctx, 1); code = pdfi_v_curveto(ctx); break; - case K1('w'): /* setlinewidth */ + case TOKEN_w: /* setlinewidth */ pdfi_pop(ctx, 1); code = pdfi_setlinewidth(ctx); break; - case K1('W'): /* clip */ + case TOKEN_W: /* clip */ pdfi_pop(ctx, 1); ctx->clip_active = true; ctx->do_eoclip = false; break; - case K2('W','*'): /* eoclip */ + case TOKEN_Wstar: /* eoclip */ pdfi_pop(ctx, 1); ctx->clip_active = true; ctx->do_eoclip = true; break; - case K1('y'): /* append curve (final point replicated) */ + case TOKEN_y: /* append curve (final point replicated) */ pdfi_pop(ctx, 1); code = pdfi_y_curveto(ctx); break; - case K1('\''): /* move to next line and show text */ + case TOKEN_APOSTROPHE: /* move to next line and show text */ pdfi_pop(ctx, 1); code = pdfi_singlequote(ctx); break; - case K1('"'): /* set word and character spacing, move to next line, show text */ + case TOKEN_QUOTE: /* set word and character spacing, move to next line, show text */ pdfi_pop(ctx, 1); code = pdfi_doublequote(ctx); break; default: - code = split_bogus_operator(ctx, source, stream_dict, page_dict); - if (code < 0) - return code; - if (pdfi_count_stack(ctx) > 0) { - keyword = (pdf_keyword *)ctx->stack_top[-1]; - if (keyword->key != TOKEN_NOT_A_KEYWORD) - return REPAIRED_KEYWORD; - } + /* Shouldn't we return an error here? Original code didn't seem to. */ break; - } + } + /* We use a return value of 1 to indicate a repaired keyword (a pair of operators + * was concatenated, and we split them up). We must not return a value > 0 from here + * to avoid tripping that test. + */ + if (code > 0) + code = 0; + return code; + } else if (((pdf_keyword *)keyword)->length > 3) { + if (ctx->args.pdfdebug) { + char Buffer[1024]; + int length; + + if (((pdf_keyword *)keyword)->length > 1023) + length = 1023; + else + length = ((pdf_keyword *)keyword)->length; + + memcpy(Buffer, ((pdf_keyword *)keyword)->data, length); + Buffer[length] = 0x00; + dmprintf1(ctx->memory, " %s\n", Buffer); + } + + /* This means we either have a corrupted or illegal operator. The most + * usual corruption is two concatented operators (eg QBT instead of Q BT) + * I plan to tackle this by trying to see if I can make two or more operators + * out of the mangled one. Note this will also be done below in the 'default' + * case where we don't recognise a keyword with 3 or fewer characters. + */ + code = split_bogus_operator(ctx, source, stream_dict, page_dict); + if (code < 0) + return code; + if (pdfi_count_stack(ctx) > 0) { + keyword = ctx->stack_top[-1]; + if (keyword != PDF_TOKEN_AS_OBJ(TOKEN_NOT_A_KEYWORD)) + return REPAIRED_KEYWORD; + } } - /* We use a return value of 1 to indicate a repaired keyword (a pair of operators - * was concatenated, and we split them up). We must not return a value > 0 from here - * to avoid tripping that test. - */ - if (code > 0) - code = 0; - return code; + return 0; } void local_save_stream_state(pdf_context *ctx, stream_save *local_save) @@ -1699,10 +1821,9 @@ int pdfi_run_context(pdf_context *ctx, pdf_stream *stream_obj, gs_color_space *PageDefaultRGB = ctx->page.DefaultRGB_cs; gs_color_space *PageDefaultCMYK = ctx->page.DefaultCMYK_cs; - /* increment their reference counts because we took a new reference to each */ - rc_increment(ctx->page.DefaultGray_cs); - rc_increment(ctx->page.DefaultRGB_cs); - rc_increment(ctx->page.DefaultCMYK_cs); + ctx->page.DefaultGray_cs = NULL; + ctx->page.DefaultRGB_cs = NULL; + ctx->page.DefaultCMYK_cs = NULL; #if DEBUG_CONTEXT dbgmprintf(ctx->memory, "pdfi_run_context BEGIN\n"); @@ -1714,6 +1835,20 @@ int pdfi_run_context(pdf_context *ctx, pdf_stream *stream_obj, if (code < 0) goto exit; + /* If no Default* space found, try using the Page level ones (if any) */ + if (ctx->page.DefaultGray_cs == NULL) { + ctx->page.DefaultGray_cs = PageDefaultGray; + rc_increment(PageDefaultGray); + } + if (ctx->page.DefaultRGB_cs == NULL) { + ctx->page.DefaultRGB_cs = PageDefaultRGB; + rc_increment(PageDefaultRGB); + } + if (ctx->page.DefaultCMYK_cs == NULL) { + ctx->page.DefaultCMYK_cs = PageDefaultCMYK; + rc_increment(PageDefaultCMYK); + } + code = pdfi_copy_DefaultQState(ctx, &DefaultQState); if (code < 0) goto exit; @@ -1743,6 +1878,7 @@ exit: ctx->page.DefaultGray_cs = PageDefaultGray; ctx->page.DefaultRGB_cs = PageDefaultRGB; ctx->page.DefaultCMYK_cs = PageDefaultCMYK; + #if DEBUG_CONTEXT dbgmprintf(ctx->memory, "pdfi_run_context END\n"); #endif @@ -1900,6 +2036,7 @@ pdfi_interpret_content_stream(pdf_context *ctx, pdf_c_stream *content_stream, pdf_c_stream *stream; pdf_keyword *keyword; pdf_stream *s = ctx->current_stream; + pdf_obj_type type; /* 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 @@ -1908,9 +2045,18 @@ pdfi_interpret_content_stream(pdf_context *ctx, pdf_c_stream *content_stream, * 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) { + while (s != NULL && pdfi_type_of(s) == PDF_STREAM) { if (s->object_num > 0) { if (s->object_num == stream_obj->object_num) { + pdf_dict *d = NULL; + bool known = false; + + code = pdfi_dict_from_obj(ctx, (pdf_obj *)stream_obj, &d); + if (code >= 0) { + code = pdfi_dict_known(ctx, d, "Parent", &known); + if (code >= 0 && known) + (void)pdfi_dict_delete(ctx, d, "Parent"); + } pdfi_set_error(ctx, 0, NULL, E_PDF_CIRCULARREF, "pdfi_interpret_content_stream", "Aborting stream"); return_error(gs_error_circular_reference); } @@ -1952,11 +2098,12 @@ pdfi_interpret_content_stream(pdf_context *ctx, pdf_c_stream *content_stream, break; } - if (ctx->stack_top[-1]->type == PDF_KEYWORD) { repaired_keyword: + type = pdfi_type_of(ctx->stack_top[-1]); + if (type == PDF_FAST_KEYWORD) { keyword = (pdf_keyword *)ctx->stack_top[-1]; - switch(keyword->key) { + switch((uintptr_t)keyword) { case TOKEN_ENDSTREAM: pdfi_pop(ctx,1); goto exit; @@ -1968,35 +2115,39 @@ repaired_keyword: code = gs_note_error(gs_error_syntaxerror); goto exit; break; - case TOKEN_NOT_A_KEYWORD: - { - pdf_dict *stream_dict = NULL; - - code = pdfi_dict_from_obj(ctx, (pdf_obj *)stream_obj, &stream_dict); - if (code < 0) - goto exit; - - code = pdfi_interpret_stream_operator(ctx, stream, stream_dict, page_dict); - if (code == REPAIRED_KEYWORD) - goto repaired_keyword; - - if (code < 0) { - pdfi_set_error(ctx, code, NULL, E_PDF_TOKENERROR, "pdf_interpret_content_stream", NULL); - if (ctx->args.pdfstoponerror) { - pdfi_clearstack(ctx); - goto exit; - } - } - } - break; case TOKEN_INVALID_KEY: pdfi_set_error(ctx, 0, NULL, E_PDF_KEYWORDTOOLONG, "pdfi_interpret_content_stream", NULL); pdfi_clearstack(ctx); break; - default: + case TOKEN_TOO_LONG: pdfi_set_error(ctx, 0, NULL, E_PDF_MISSINGENDSTREAM, "pdfi_interpret_content_stream", NULL); pdfi_clearstack(ctx); break; + default: + goto execute; + } + } + else if (type == PDF_KEYWORD) + { +execute: + { + pdf_dict *stream_dict = NULL; + + code = pdfi_dict_from_obj(ctx, (pdf_obj *)stream_obj, &stream_dict); + if (code < 0) + goto exit; + + code = pdfi_interpret_stream_operator(ctx, stream, stream_dict, page_dict); + if (code == REPAIRED_KEYWORD) + goto repaired_keyword; + + if (code < 0) { + pdfi_set_error(ctx, code, NULL, E_PDF_TOKENERROR, "pdf_interpret_content_stream", NULL); + if (ctx->args.pdfstoponerror) { + pdfi_clearstack(ctx); + goto exit; + } + } } } if(stream->eof == true) diff --git a/pdf/pdf_int.h b/pdf/pdf_int.h index c841fc9f..4cf1dcb6 100644 --- a/pdf/pdf_int.h +++ b/pdf/pdf_int.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 @@ -29,6 +29,9 @@ int pdfi_name_alloc(pdf_context *ctx, byte *key, uint32_t size, pdf_obj **o); int pdfi_read_dict(pdf_context *ctx, pdf_c_stream *s, uint32_t indirect_num, uint32_t indirect_gen); +int pdfi_read_bare_int(pdf_context *ctx, pdf_c_stream *s, int *parsed_int); +int pdfi_read_bare_keyword(pdf_context *ctx, pdf_c_stream *s); + void local_save_stream_state(pdf_context *ctx, stream_save *local_save); void local_restore_stream_state(pdf_context *ctx, stream_save *local_save); void cleanup_context_interpretation(pdf_context *ctx, stream_save *local_save); diff --git a/pdf/pdf_mark.c b/pdf/pdf_mark.c index d625e34a..27b72d8c 100644 --- a/pdf/pdf_mark.c +++ b/pdf/pdf_mark.c @@ -50,7 +50,7 @@ static int pdfi_pdfmark_setparam_pair(pdf_context *ctx, pdf_name *Key, pdf_obj * int code = 0; /* Handle the Key */ - if (Key->type != PDF_NAME) { + if (pdfi_type_of(Key) != PDF_NAME) { code = gs_note_error(gs_error_typecheck); goto exit; } @@ -391,14 +391,20 @@ static int pdfi_pdfmark_add_Page_View(pdf_context *ctx, pdf_dict *link_dict, pdf code = pdfi_array_get_no_store_R(ctx, dest_array, 0, (pdf_obj **)&page_dict); if (code < 0) goto exit; - if (page_dict->type != PDF_DICT) { - code = gs_note_error(gs_error_typecheck); - goto exit; + if(pdfi_type_of(page_dict) == PDF_INT) { + page_num = ((pdf_num *)page_dict)->value.i; + } else { + if (pdfi_type_of(page_dict) != PDF_DICT) { + code = gs_note_error(gs_error_typecheck); + goto exit; + } + + /* Find out which page number this is */ + code = pdfi_page_get_number(ctx, page_dict, &page_num); + if (code < 0) goto exit; } - /* Find out which page number this is */ - code = pdfi_page_get_number(ctx, page_dict, &page_num); - if (code < 0) goto exit; + page_num += ctx->Pdfmark_InitialPage; /* Add /Page key to the link_dict * Of course pdfwrite is numbering its pages starting at 1, because... of course :( @@ -457,12 +463,12 @@ static int pdfi_pdfmark_handle_dest_names(pdf_context *ctx, pdf_dict *link_dict, /* Note: in current implementation, PDF_STRING and PDF_NAME have all the same * fields, but just in case that changes I treat them separately here. */ - if (name->type == PDF_STRING && dest->type == PDF_STRING) { + if (pdfi_type_of(name) == PDF_STRING && pdfi_type_of(dest) == PDF_STRING) { if (!pdfi_string_cmp((pdf_string *)name, (pdf_string *)dest)) { found = true; break; } - } else if (name->type == PDF_NAME && dest->type == PDF_NAME) { + } else if (pdfi_type_of(name) == PDF_NAME && pdfi_type_of(dest) == PDF_NAME) { if (!pdfi_name_cmp((pdf_name *)name, (pdf_name *)dest)) { found = true; break; @@ -482,18 +488,19 @@ static int pdfi_pdfmark_handle_dest_names(pdf_context *ctx, pdf_dict *link_dict, code = pdfi_array_get(ctx, Names, i+1, (pdf_obj **)&D_dict); 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) { + switch (pdfi_type_of(D_dict)) { + case 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; + break; + case PDF_ARRAY: dest_array = (pdf_array *)D_dict; D_dict = NULL; - } else { + break; + default: code = gs_note_error(gs_error_typecheck); goto exit; - } } /* Process the dest_array to replace with /Page /View */ @@ -512,7 +519,7 @@ static int pdfi_pdfmark_handle_dest_names(pdf_context *ctx, pdf_dict *link_dict, */ int pdfi_pdfmark_modDest(pdf_context *ctx, pdf_dict *link_dict) { - int code = 0; + int code = 0, code1 = 0; pdf_dict *Dests = NULL; pdf_obj *Dest = NULL; bool delete_Dest = true; @@ -529,14 +536,13 @@ int pdfi_pdfmark_modDest(pdf_context *ctx, pdf_dict *link_dict) code = pdfi_dict_knownget_type(ctx, ctx->Root, "Names", PDF_DICT, (pdf_obj **)&Names_dict); if (code < 0) goto exit; - switch (Dest->type) { + switch (pdfi_type_of(Dest)) { case PDF_ARRAY: code = pdfi_pdfmark_add_Page_View(ctx, link_dict, (pdf_array *)Dest); if (code < 0) goto exit; break; case PDF_NAME: - case PDF_STRING: - if (Dest->type == PDF_NAME && Dests != NULL) { + if (Dests != NULL) { /* Case where it's a name to look up in Contents(Root) /Dests */ code = pdfi_dict_get_by_key(ctx, Dests, (const pdf_name *)Dest, (pdf_obj **)&dest_array); if (code == gs_error_undefined) { @@ -545,13 +551,17 @@ int pdfi_pdfmark_modDest(pdf_context *ctx, pdf_dict *link_dict) goto exit; } if (code < 0) goto exit; - if (dest_array->type != PDF_ARRAY) { + if (pdfi_type_of(dest_array) != PDF_ARRAY) { code = gs_note_error(gs_error_typecheck); goto exit; } code = pdfi_pdfmark_add_Page_View(ctx, link_dict, dest_array); if (code < 0) goto exit; - } else if (Names_dict != NULL) { + break; + } + /* fallthrough */ + case PDF_STRING: + if (Names_dict != NULL) { /* Looking in Catalog(Root) for /Names<</Dests<</Names [name dict array]>>>> */ code = pdfi_dict_knownget_type(ctx, Names_dict, "Dests", PDF_DICT, (pdf_obj **)&Dests); if (code < 0) goto exit; @@ -580,8 +590,9 @@ int pdfi_pdfmark_modDest(pdf_context *ctx, pdf_dict *link_dict) exit: if (delete_Dest) { /* Delete the Dest key */ - code = pdfi_dict_delete(ctx, link_dict, "Dest"); - if (code < 0) goto exit; + code1 = pdfi_dict_delete(ctx, link_dict, "Dest"); + if (code1 < 0 && code >= 0) + code = code1; } pdfi_countdown(Dest); pdfi_countdown(Dests); @@ -591,6 +602,233 @@ int pdfi_pdfmark_modDest(pdf_context *ctx, pdf_dict *link_dict) return code; } +static int pdfi_check_limits(pdf_context *ctx, pdf_dict *node, char *str, int len) +{ + int code = 0, min, i, len2 = 0; + pdf_array *Limits = NULL; + pdf_string *Str = NULL; + char *str2 = NULL; + + code = pdfi_dict_get_type(ctx, node, "Limits", PDF_ARRAY, (pdf_obj **)&Limits); + if (code < 0) + goto error; + + if (pdfi_array_size(Limits) != 2) { + /* Limits are not valid, just ignore them. The calling code will then check + * the Names array. + */ + pdfi_set_warning(ctx, 0, NULL, PDF_W_BAD_TREE_LIMITS, "pdfi_get_name_from_node", 0); + goto error; + } + + code = pdfi_array_get_type(ctx, Limits, 0, PDF_STRING, (pdf_obj **)&Str); + if (code < 0) + goto error; + + if (pdfi_type_of(Str) == PDF_NAME) { + code = pdfi_string_from_name(ctx, (pdf_name *)Str, &str2, &len2); + if (code < 0) + return code; + } else { + len2 = ((pdf_string *)Str)->length; + str2 = (char *)gs_alloc_bytes(ctx->memory, len2 + 1, "pdfi_get_named_dest"); + if (str2 == NULL) { + code = gs_note_error(gs_error_VMerror); + goto error; + } + memcpy(str2, ((pdf_string *)Str)->data, len2); + str2[len2] = 0; + } + + pdfi_countdown(Str); + Str = NULL; + + min = len; + if (len2 < min) + min = len2; + + for (i=0;i< min;i++) { + if (str[i] < str2[i]) { + code = gs_note_error(gs_error_undefined); + goto error; + } + if (str[i] != str2[i]) + break; + } + if (i > min && len2 < Str->length) { + code = gs_note_error(gs_error_undefined); + goto error; + } + gs_free_object(ctx->memory, str2, "pdfi_get_named_dest"); + str2 = NULL; + + code = pdfi_array_get_type(ctx, Limits, 1, PDF_STRING, (pdf_obj **)&Str); + if (code < 0) + goto error; + + if (pdfi_type_of(Str) == PDF_NAME) { + code = pdfi_string_from_name(ctx, (pdf_name *)Str, &str2, &len2); + if (code < 0) + return code; + } else { + len2 = ((pdf_string *)Str)->length; + str2 = (char *)gs_alloc_bytes(ctx->memory, len2 + 1, "pdfi_get_named_dest"); + if (str2 == NULL) { + code = gs_note_error(gs_error_VMerror); + goto error; + } + memcpy(str2, ((pdf_string *)Str)->data, len2); + str2[len2] = 0; + } + + pdfi_countdown(Str); + Str = NULL; + + min = len; + if (len2 < min) + min = len2; + + for (i=0;i< min;i++) { + if (str[i] > str2[i]) { + code = gs_note_error(gs_error_undefined); + goto error; + } + if (str[i] != str2[i]) + break; + } + + if (i > min && len > len2) + code = gs_note_error(gs_error_undefined); + +error: + gs_free_object(ctx->memory, str2, "pdfi_get_named_dest"); + pdfi_countdown(Str); + pdfi_countdown(Limits); + return code; +} + +static int pdfi_get_name_from_node(pdf_context *ctx, pdf_dict *node, char *str, pdf_obj **Name) +{ + int i = 0, len = strlen(str), code = 0; + pdf_string *StrKey = NULL; + pdf_array *NamesArray = NULL; + pdf_dict *Kid = NULL; + bool known; + + code = pdfi_dict_known(ctx, node, "Names", &known); + if (code < 0) + goto error; + + if (known) { + code = pdfi_dict_known(ctx, node, "Limits", &known); + if (code < 0) + goto error; + + if (!known) { + /* No Limits array (a required entry), so just assume that the + * string is in this node and check all the Names anyway + */ + pdfi_set_warning(ctx, 0, NULL, PDF_W_NO_TREE_LIMITS, "pdfi_get_name_from_node", 0); + } else { + code = pdfi_check_limits(ctx, node, str, len); + if (code < 0) + goto error; + } + + code = pdfi_dict_get_type(ctx, node, "Names", PDF_ARRAY, (pdf_obj **)&NamesArray); + if (code < 0) + goto error; + + if (pdfi_array_size(NamesArray) & 1) + pdfi_set_warning(ctx, 0, NULL, PDF_W_NAMES_ARRAY_SIZE, "pdfi_get_name_from_node", 0); + + for (i = 0;i < pdfi_array_size(NamesArray) / 2; i++) { + code = pdfi_array_get_type(ctx, NamesArray, i * 2, PDF_STRING, (pdf_obj **)&StrKey); + if (code < 0) + goto error; + + if (StrKey->length == len && strncmp((const char *)StrKey->data, str, len) == 0) { + code = pdfi_array_get(ctx, NamesArray, (i * 2) + 1, (pdf_obj **)Name); + goto error; + } + pdfi_countdown(StrKey); + StrKey = NULL; + } + pdfi_countdown(NamesArray); + NamesArray = NULL; + } + + /* Either no Names array (initial node) or not in array */ + code = pdfi_dict_get_type(ctx, node, "Kids", PDF_ARRAY, (pdf_obj **)&NamesArray); + if (code < 0) + goto error; + + for (i = 0;i < pdfi_array_size(NamesArray); i++) { + code = pdfi_array_get_type(ctx, NamesArray, i, PDF_DICT, (pdf_obj **)&Kid); + if (code < 0) + goto error; + + code = pdfi_get_name_from_node(ctx, Kid, str, Name); + pdfi_countdown(Kid); + Kid = NULL; + if (code == 0) + break; + + if (code < 0) { + if (code == gs_error_undefined) + continue; + goto error; + } + } + +error: + pdfi_countdown(Kid); + pdfi_countdown(StrKey); + pdfi_countdown(NamesArray); + return code; +} + +static int pdfi_get_named_dest(pdf_context *ctx, pdf_obj *Named, pdf_obj **Dest) +{ + int code = 0, len = 0; + pdf_dict *Names = NULL, *Dests = NULL; + char *str = NULL; + + code = pdfi_dict_get_type(ctx, ctx->Root, "Names", PDF_DICT, (pdf_obj **)&Names); + if (code < 0) + goto error; + + code = pdfi_dict_get_type(ctx, Names, "Dests", PDF_DICT, (pdf_obj **)&Dests); + if (code < 0) + goto error; + + if (pdfi_type_of(Named) == PDF_NAME) { + code = pdfi_string_from_name(ctx, (pdf_name *)Named, &str, &len); + if (code < 0) + return code; + } else { + len = ((pdf_string *)Named)->length; + str = (char *)gs_alloc_bytes(ctx->memory, len + 1, "pdfi_get_named_dest"); + if (str == NULL) { + code = gs_note_error(gs_error_VMerror); + goto error; + } + memcpy(str, ((pdf_string *)Named)->data, len); + str[len] = 0; + } + + code = pdfi_get_name_from_node(ctx, Dests, str, Dest); + +error: + if (pdfi_type_of(Named) == PDF_NAME) + (void)pdfi_free_string_from_name(ctx, str); + else + gs_free_object(ctx->memory, str, "pdfi_get_named_dest"); + pdfi_countdown(Names); + pdfi_countdown(Dests); + return code; +} + /* Special handling for "A" in Link annotations and Outlines * Will delete A if handled and if A_key is provided. */ @@ -607,7 +845,7 @@ int pdfi_pdfmark_modA(pdf_context *ctx, pdf_dict *dict) code = pdfi_dict_get_no_store_R(ctx, dict, "A", (pdf_obj **)&A_dict); if (code < 0) goto exit; - if (A_dict->type != PDF_DICT) { + if (pdfi_type_of(A_dict) != PDF_DICT) { /* Invalid AP, just delete it because I dunno what to do... * TODO: Should flag a warning here */ @@ -631,19 +869,33 @@ int pdfi_pdfmark_modA(pdf_context *ctx, pdf_dict *dict) if (code <= 0) goto exit; /* We only handle GoTo for now */ if (pdfi_name_is(S_name, "GoTo")) { - code = pdfi_dict_knownget_type(ctx, A_dict, "D", PDF_ARRAY, (pdf_obj **)&D_array); - if (code == 0) goto exit; - if (code < 0) { - if (code == gs_error_typecheck) { - /* TODO: Are there other cases to handle? - * Sample tests_private/pdf/sumatra/recursive_action_destinations.pdf - * has a recursive destination that has an indirect ref here. We return a - * typecheck and that causes us to omit the whole thing, but is that - * really the best treatment? - */ + code = pdfi_dict_knownget(ctx, A_dict, "D", (pdf_obj **)&D_array); + if (code <= 0) + goto exit; + if (pdfi_type_of(D_array) == PDF_STRING || pdfi_type_of(D_array) == PDF_NAME) + { + pdf_obj *Dest = NULL; + + code = pdfi_get_named_dest(ctx, (pdf_obj *)D_array, &Dest); + if (code < 0) + goto exit; + pdfi_countdown(D_array); + D_array = NULL; + if (pdfi_type_of(Dest) != PDF_DICT) { + pdfi_countdown(Dest); + code = gs_note_error(gs_error_typecheck); + goto exit; } + code = pdfi_dict_knownget(ctx, (pdf_dict *)Dest, "D", (pdf_obj **)&D_array); + pdfi_countdown(Dest); + if (code <= 0) + goto exit; + } + if (pdfi_type_of(D_array) != PDF_ARRAY) { + code = gs_note_error(gs_error_typecheck); goto exit; } + /* Process the D array to replace with /Page /View */ code = pdfi_pdfmark_add_Page_View(ctx, dict, D_array); if (code < 0) goto exit; @@ -1043,7 +1295,7 @@ void pdfi_pdfmark_write_boxes(pdf_context *ctx, pdf_dict *page_dict) pdf_array *new_array = NULL; /* Box is present in page dicitonayr, check it's an array */ - if (o->type != PDF_ARRAY) { + if (pdfi_type_of(o) != PDF_ARRAY) { pdfi_countdown(o); continue; } diff --git a/pdf/pdf_misc.c b/pdf/pdf_misc.c index 11c5dde5..e027baec 100644 --- a/pdf/pdf_misc.c +++ b/pdf/pdf_misc.c @@ -149,7 +149,7 @@ int pdfi_setrenderingintent(pdf_context *ctx, pdf_name *n) int pdfi_string_from_name(pdf_context *ctx, pdf_name *n, char **str, int *len) { - if (n->type != PDF_NAME) + if (pdfi_type_of(n) != PDF_NAME) return gs_note_error(gs_error_typecheck); *str = NULL; @@ -166,6 +166,13 @@ int pdfi_string_from_name(pdf_context *ctx, pdf_name *n, char **str, int *len) return 0; } +int pdfi_free_string_from_name(pdf_context *ctx, char *str) +{ + if (str != NULL) + gs_free_object(ctx->memory, str, "pdfi_free_string_from_name"); + return 0; +} + void normalize_rectangle(double *d) { double d1[4]; diff --git a/pdf/pdf_misc.h b/pdf/pdf_misc.h index 8642b088..a879ec2b 100644 --- a/pdf/pdf_misc.h +++ b/pdf/pdf_misc.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 @@ -23,6 +23,7 @@ bool pdfi_name_is(const pdf_name *n, const char *s); int pdfi_name_cmp(const pdf_name *n1, const pdf_name *n2); int pdfi_string_cmp(const pdf_string *n1, const pdf_string *n2); int pdfi_string_from_name(pdf_context *ctx, pdf_name *n, char **str, int *len); +int pdfi_free_string_from_name(pdf_context *ctx, char *str); gs_color_space_index pdfi_get_color_space_index(pdf_context *ctx, const gs_color_space *pcs); gs_color_space_index pdfi_currentcolorspace(pdf_context *ctx, int index); diff --git a/pdf/pdf_obj.c b/pdf/pdf_obj.c index 58bd59b0..aae9401b 100644 --- a/pdf/pdf_obj.c +++ b/pdf/pdf_obj.c @@ -36,12 +36,12 @@ int pdfi_object_alloc(pdf_context *ctx, pdf_obj_type type, unsigned int size, pdf_obj **obj) { int bytes = 0; + int code = 0; switch(type) { case PDF_ARRAY_MARK: case PDF_DICT_MARK: case PDF_PROC_MARK: - case PDF_NULL: bytes = sizeof(pdf_obj); break; case PDF_INT: @@ -50,7 +50,10 @@ 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) + size - sizeof(PDF_NAME_DECLARED_LENGTH); + bytes = sizeof(pdf_string) + size - PDF_NAME_DECLARED_LENGTH; + break; + case PDF_BUFFER: + bytes = sizeof(pdf_buffer); break; case PDF_ARRAY: bytes = sizeof(pdf_array); @@ -61,11 +64,8 @@ int pdfi_object_alloc(pdf_context *ctx, pdf_obj_type type, unsigned int size, pd case PDF_INDIRECT: bytes = sizeof(pdf_indirect_ref); break; - case PDF_BOOL: - bytes = sizeof(pdf_bool); - break; case PDF_KEYWORD: - bytes = sizeof(pdf_keyword) + size - sizeof(PDF_NAME_DECLARED_LENGTH); + bytes = sizeof(pdf_keyword) + size - 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. @@ -76,23 +76,34 @@ int pdfi_object_alloc(pdf_context *ctx, pdf_obj_type type, unsigned int size, pd case PDF_STREAM: bytes = sizeof(pdf_stream); break; + case PDF_NULL: + case PDF_BOOL: default: - return_error(gs_error_typecheck); + code = gs_note_error(gs_error_typecheck); + goto error_out; } *obj = (pdf_obj *)gs_alloc_bytes(ctx->memory, bytes, "pdfi_object_alloc"); - if (*obj == NULL) - return_error(gs_error_VMerror); + if (*obj == NULL) { + code = gs_note_error(gs_error_VMerror); + goto error_out; + } memset(*obj, 0x00, bytes); (*obj)->ctx = ctx; (*obj)->type = type; switch(type) { +/* PDF_NULL and PDF_BOOL are now handled as special (not allocated) data types + and we will return an error in the switch above if we get a call to allocate + one of these. Having the cases isn't harmful but Coverity complains of dead + code, so commenting these out to silence Coverity while preserving the old + semantics to indicate what's happening. case PDF_NULL: + case PDF_BOOL: */ + case PDF_INT: case PDF_REAL: case PDF_INDIRECT: - case PDF_BOOL: case PDF_ARRAY_MARK: case PDF_DICT_MARK: case PDF_PROC_MARK: @@ -102,6 +113,24 @@ int pdfi_object_alloc(pdf_context *ctx, pdf_obj_type type, unsigned int size, pd case PDF_NAME: ((pdf_string *)*obj)->length = size; break; + case PDF_BUFFER: + { + pdf_buffer *b = (pdf_buffer *)*obj; + /* NOTE: size can be 0 if the caller wants to allocate the data area itself + */ + if (size > 0) { + b->data = gs_alloc_bytes(ctx->memory, size, "pdfi_object_alloc"); + if (b->data == NULL) { + code = gs_note_error(gs_error_VMerror); + goto error_out; + } + } + else { + b->data = NULL; + } + b->length = size; + } + break; case PDF_ARRAY: { pdf_obj **values = NULL; @@ -110,10 +139,8 @@ int pdfi_object_alloc(pdf_context *ctx, pdf_obj_type type, unsigned int size, pd if (size > 0) { values = (pdf_obj **)gs_alloc_bytes(ctx->memory, size * sizeof(pdf_obj *), "pdfi_object_alloc"); if (values == NULL) { - gs_free_object(ctx->memory, *obj, "pdfi_object_alloc"); - gs_free_object(ctx->memory, values, "pdfi_object_alloc"); - *obj = NULL; - return_error(gs_error_VMerror); + code = gs_note_error(gs_error_VMerror); + goto error_out; } ((pdf_array *)*obj)->values = values; memset(((pdf_array *)*obj)->values, 0x00, size * sizeof(pdf_obj *)); @@ -128,9 +155,8 @@ int pdfi_object_alloc(pdf_context *ctx, pdf_obj_type type, unsigned int size, pd if (size > 0) { 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"); - *obj = NULL; - return_error(gs_error_VMerror); + code = gs_note_error(gs_error_VMerror); + goto error_out; } ((pdf_dict *)*obj)->list = entries; memset(((pdf_dict *)*obj)->list, 0x00, size * sizeof(pdf_dict_entry)); @@ -150,6 +176,10 @@ int pdfi_object_alloc(pdf_context *ctx, pdf_obj_type type, unsigned int size, pd dmprintf2(ctx->memory, "Allocated object of type %c with UID %"PRIi64"\n", (*obj)->type, (*obj)->UID); #endif return 0; +error_out: + gs_free_object(ctx->memory, *obj, "pdfi_object_alloc"); + *obj = NULL; + return code; } /* Create a PDF number object from a numeric value. Attempts to create @@ -217,25 +247,36 @@ static void pdfi_free_stream(pdf_obj *o) gs_free_object(OBJ_MEMORY(o), o, "pdfi_free_stream"); } +static void pdfi_free_buffer(pdf_obj *o) +{ + pdf_buffer *b = (pdf_buffer *)o; + + gs_free_object(OBJ_MEMORY(b), b->data, "pdfi_free_buffer(data)"); + gs_free_object(OBJ_MEMORY(o), o, "pdfi_free_buffer"); +} + void pdfi_free_object(pdf_obj *o) { if (o == NULL) return; + if ((intptr_t)o < (intptr_t)TOKEN__LAST_KEY) + return; switch(o->type) { case PDF_ARRAY_MARK: case PDF_DICT_MARK: case PDF_PROC_MARK: - case PDF_NULL: case PDF_INT: case PDF_REAL: case PDF_INDIRECT: - case PDF_BOOL: gs_free_object(OBJ_MEMORY(o), o, "pdf interpreter object refcount to 0"); break; case PDF_STRING: case PDF_NAME: pdfi_free_namestring(o); break; + case PDF_BUFFER: + pdfi_free_buffer(o); + break; case PDF_ARRAY: pdfi_free_array(o); break; @@ -257,8 +298,12 @@ void pdfi_free_object(pdf_obj *o) case PDF_CMAP: pdfi_free_cmap(o); break; + case PDF_BOOL: + case PDF_NULL: + dbgmprintf(OBJ_MEMORY(o), "!!! Attempting to free non-allocated object type !!!\n"); + break; default: - dbgmprintf(OBJ_MEMORY(o), "!!! Attempting to free unknown obect type !!!\n"); + dbgmprintf(OBJ_MEMORY(o), "!!! Attempting to free unknown object type !!!\n"); break; } } @@ -274,7 +319,7 @@ int pdfi_obj_dict_to_stream(pdf_context *ctx, pdf_dict *dict, pdf_stream **strea int code = 0; pdf_stream *new_stream = NULL; - if (dict->type != PDF_DICT) + if (pdfi_type_of(dict) != PDF_DICT) return_error(gs_error_typecheck); code = pdfi_object_alloc(ctx, PDF_STREAM, 0, (pdf_obj **)&new_stream); @@ -445,7 +490,7 @@ int pdfi_obj_get_label(pdf_context *ctx, pdf_obj *obj, char **label) goto exit; } - if (obj->type == PDF_INDIRECT) + if (pdfi_type_of(obj) == PDF_INDIRECT) snprintf(string, length, template, ref->ref_object_num, ref->ref_generation_num); else snprintf(string, length, template, obj->object_num, obj->generation_num); @@ -566,10 +611,10 @@ static int pdfi_obj_indirect_str(pdf_context *ctx, pdf_obj *obj, byte **data, in if (code < 0 && code != gs_error_circular_reference) goto exit; if (code == 0) { - if (object->type == PDF_STREAM) { + if (pdfi_type_of(object) == PDF_STREAM) { code = pdfi_pdfmark_stream(ctx, (pdf_stream *)object); if (code < 0) goto exit; - } else if (object->type == PDF_DICT) { + } else if (pdfi_type_of(object) == PDF_DICT) { code = pdfi_pdfmark_dict(ctx, (pdf_dict *)object); if (code < 0) goto exit; } else { @@ -596,13 +641,12 @@ static int pdfi_obj_bool_str(pdf_context *ctx, pdf_obj *obj, byte **data, int *l { int code = 0; int size = 5; - pdf_bool *bool = (pdf_bool *)obj; char *buf; buf = (char *)gs_alloc_bytes(ctx->memory, size, "pdfi_obj_bool_str(data)"); if (buf == NULL) return_error(gs_error_VMerror); - if (bool->value) { + if (obj == PDF_TRUE_OBJ) { memcpy(buf, (byte *)"true", 4); *len = 4; } else { @@ -665,6 +709,8 @@ static int pdfi_obj_string_str(pdf_context *ctx, pdf_obj *obj, byte **data, int * can have special characters. So I will handle the minimum that seems needed for that. */ switch (*ptr) { + case 0x0a: + case 0x0d: case '(': case ')': case '\\': @@ -694,6 +740,16 @@ static int pdfi_obj_string_str(pdf_context *ctx, pdf_obj *obj, byte **data, int bufptr = buf + 1; for (i=0,ptr=string->data;i<string_len;i++) { switch (*ptr) { + case 0x0d: + *bufptr++ = '\\'; + *bufptr++ = 'r'; + ptr++; + continue; + case 0x0a: + *bufptr++ = '\\'; + *bufptr++ = 'n'; + ptr++; + continue; case '(': case ')': case '\\': @@ -920,6 +976,28 @@ static int pdfi_obj_dict_str(pdf_context *ctx, pdf_obj *obj, byte **data, int *l return code; } +#define PARAM1(A) # A, +#define PARAM2(A,B) A, +static const char pdf_token_strings[][10] = { +#include "pdf_tokens.h" +}; + +static int pdfi_obj_fast_keyword_str(pdf_context *ctx, pdf_obj *obj, byte **data, int *len) +{ + int code = 0; + const char *s = pdf_token_strings[(uintptr_t)obj]; + int size = (int)strlen(s) + 1; + byte *buf; + + buf = gs_alloc_bytes(ctx->memory, size, "pdfi_obj_name_str(data)"); + if (buf == NULL) + return_error(gs_error_VMerror); + memcpy(buf, s, size); + *data = buf; + *len = size; + return code; +} + obj_str_dispatch_t obj_str_dispatch[] = { {PDF_NAME, pdfi_obj_name_str}, {PDF_ARRAY, pdfi_obj_array_str}, @@ -931,6 +1009,7 @@ obj_str_dispatch_t obj_str_dispatch[] = { {PDF_STREAM, pdfi_obj_stream_str}, {PDF_INDIRECT, pdfi_obj_indirect_str}, {PDF_NULL, pdfi_obj_null_str}, + {PDF_FAST_KEYWORD, pdfi_obj_fast_keyword_str}, {0, NULL} }; @@ -940,11 +1019,13 @@ int pdfi_obj_to_string(pdf_context *ctx, pdf_obj *obj, byte **data, int *len) { obj_str_dispatch_t *dispatch_ptr; int code = 0; + pdf_obj_type type; *data = NULL; *len = 0; + type = pdfi_type_of(obj); for (dispatch_ptr = obj_str_dispatch; dispatch_ptr->func; dispatch_ptr ++) { - if (obj->type == dispatch_ptr->type) { + if (type == dispatch_ptr->type) { code = dispatch_ptr->func(ctx, obj, data, len); goto exit; } diff --git a/pdf/pdf_obj.h b/pdf/pdf_obj.h index 99c9a178..eb512db9 100644 --- a/pdf/pdf_obj.h +++ b/pdf/pdf_obj.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 @@ -25,4 +25,90 @@ int pdfi_obj_charstr_to_name(pdf_context *ctx, const char *charstr, pdf_name **n int pdfi_obj_get_label(pdf_context *ctx, pdf_obj *obj, char **label); int pdfi_num_alloc(pdf_context *ctx, double d, pdf_num **num); +static inline int +pdfi_obj_to_real(pdf_context *ctx, pdf_obj *obj, double *d) +{ + pdf_num *num = (pdf_num *)obj; + + switch (pdfi_type_of(num)) { + case PDF_INT: + *d = (double)num->value.i; + break; + case PDF_REAL: + *d = num->value.d; + break; + default: + return_error(gs_error_typecheck); + } + + return 0; +} + +static inline int +pdfi_obj_to_float(pdf_context *ctx, pdf_obj *obj, float *f) +{ + pdf_num *num = (pdf_num *)obj; + + switch (pdfi_type_of(num)) { + case PDF_INT: + *f = (float)num->value.i; + break; + case PDF_REAL: + *f = (float)num->value.d; + break; + default: + return_error(gs_error_typecheck); + } + + return 0; +} + +static inline int +pdfi_obj_to_int(pdf_context *ctx, pdf_obj *obj, int64_t *i) +{ + pdf_num *num = (pdf_num *)obj; + int64_t tmp; + + switch (pdfi_type_of(num)) { + case PDF_INT: + *i = num->value.i; + break; + case PDF_REAL: + /* We shouldn't be given a real here. We will grudgingly accept + * (with a warning) an int given as a real, but will error out + * otherwise. If we find a case where we need to accept reals + * as ints, we'll do a new version of this function called something + * like pdfi_obj_real_as_int what will just cast it down. */ + tmp = (int64_t)num->value.d; + if ((double)tmp != num->value.d) { + return_error(gs_error_typecheck); + } + pdfi_set_warning(ctx, 0, NULL, W_PDF_INT_AS_REAL, "pdfi_obj_to_int", NULL); + *i = tmp; + break; + default: + return_error(gs_error_typecheck); + } + + return 0; +} + +/* NOTE: the buffer object takes ownership of "data" */ +static inline int +pdfi_buffer_set_data(pdf_obj *o, byte *data, int32_t length) +{ + pdf_buffer *b = (pdf_buffer *)o; + if (pdfi_type_of(b) != PDF_BUFFER) { + return_error(gs_error_typecheck); + } + + if (b->data) { + gs_free_object(OBJ_MEMORY(b), b->data, "pdfi_buffer_set_data(data)"); + } + b->data = data; + b->length = length; + return 0; +} + + #endif diff --git a/pdf/pdf_optcontent.c b/pdf/pdf_optcontent.c index 484d72c9..288ee231 100644 --- a/pdf/pdf_optcontent.c +++ b/pdf/pdf_optcontent.c @@ -167,8 +167,8 @@ pdfi_oc_check_OCMD_array(pdf_context *ctx, pdf_array *array, ocmd_p_type type) code = pdfi_array_get(ctx, array, i, &val); if (code < 0) continue; - if (val->type != PDF_DICT) { - dmprintf1(ctx->memory, "WARNING: OCMD array contains item type %d, expected PDF_DICT or PDF_NULL\n", val->type); + if (pdfi_type_of(val) != PDF_DICT) { + dmprintf1(ctx->memory, "WARNING: OCMD array contains item type %d, expected PDF_DICT or PDF_NULL\n", pdfi_type_of(val)); pdfi_countdown(val); val = NULL; continue; @@ -235,9 +235,9 @@ pdfi_oc_check_OCMD(pdf_context *ctx, pdf_dict *ocdict) code = pdfi_dict_knownget(ctx, ocdict, "OCGs", &obj); if (code <= 0) goto cleanup; - if (obj->type == PDF_ARRAY) { + if (pdfi_type_of(obj) == PDF_ARRAY) { OCGs_array = (pdf_array *)obj; - } else if (obj->type == PDF_DICT) { + } else if (pdfi_type_of(obj) == PDF_DICT) { OCGs_dict = (pdf_dict *)obj; } else { goto cleanup; @@ -367,7 +367,7 @@ static int pdfi_oc_levels_set(pdf_context *ctx, pdfi_oc_levels_t *levels, uint64 byte *new = NULL; uint64_t newmax; - if (index > levels->max_flags) { + if (index > levels->max_flags - 1) { /* Expand the flags buffer */ newmax = levels->max_flags + NUM_CONTENT_LEVELS; if (index > newmax) @@ -390,7 +390,7 @@ static int pdfi_oc_levels_set(pdf_context *ctx, pdfi_oc_levels_t *levels, uint64 static int pdfi_oc_levels_clear(pdf_context *ctx, pdfi_oc_levels_t *levels, uint64_t index) { - if (index > levels->max_flags) + if (index > levels->max_flags - 1) return -1; if (levels->flags[index] != 0) levels->num_off --; @@ -446,16 +446,19 @@ int pdfi_op_MP(pdf_context *ctx) goto exit; o = ctx->stack_top[-1]; - if (o->type != PDF_NAME) { - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); + pdfi_countup(o); + pdfi_pop(ctx, 1); + + if (pdfi_type_of(o) != PDF_NAME) { + code = gs_note_error(gs_error_typecheck); + goto exit; } code = pdfi_pdfmark_from_objarray(ctx, &o, 1, NULL, "MP"); ctx->BMClevel ++; exit: - pdfi_pop(ctx, 1); + pdfi_countdown(o); return code; } @@ -463,17 +466,20 @@ 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; + pdf_obj **objarray = NULL, *o = 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) + if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) { + pdfi_pop(ctx, 2); /* pop args */ goto exit; + } - if ((ctx->stack_top[-2])->type != PDF_NAME) { + if (pdfi_type_of(ctx->stack_top[-2]) != PDF_NAME) { + pdfi_pop(ctx, 2); /* pop args */ code = gs_note_error(gs_error_typecheck); goto exit; } @@ -485,30 +491,38 @@ int pdfi_op_DP(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) } objarray[0] = ctx->stack_top[-2]; + pdfi_countup(objarray[0]); + o = ctx->stack_top[-2]; + pdfi_countup(o); + pdfi_pop(ctx, 2); /* pop args */ - 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) { + switch (pdfi_type_of(o)) { + case PDF_NAME: + code = pdfi_find_resource(ctx, (unsigned char *)"Properties", (pdf_name *)o, stream_dict, page_dict, (pdf_obj **)&properties); + if(code < 0) + goto exit; + if (pdfi_type_of(properties) != PDF_DICT) { + code = gs_note_error(gs_error_typecheck); + goto exit; + } + objarray[1] = (pdf_obj *)properties; + break; + case PDF_DICT: + objarray[1] = o; + break; + default: 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) + if (objarray != NULL) { + pdfi_countdown(objarray[0]); gs_free_object(ctx->memory, objarray, "free pdfi_op_DP"); - pdfi_pop(ctx, 2); /* pop args */ + } + pdfi_countdown(o); pdfi_countdown(properties); return code; } @@ -525,13 +539,18 @@ int pdfi_op_BMC(pdf_context *ctx) if (pdfi_count_stack(ctx) < 1) return_error(gs_error_stackunderflow); - if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) + if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) { + pdfi_pop(ctx, 1); goto exit; + } o = ctx->stack_top[-1]; - if (o->type != PDF_NAME) { - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); + pdfi_countup(o); + pdfi_pop(ctx, 1); + + if (pdfi_type_of(o) != PDF_NAME) { + code = gs_note_error(gs_error_typecheck); + goto exit; } ctx->BDCWasOC = false; @@ -539,7 +558,7 @@ int pdfi_op_BMC(pdf_context *ctx) ctx->BMClevel ++; exit: - pdfi_pop(ctx, 1); + pdfi_countdown(o); return code; } @@ -551,7 +570,7 @@ 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; + pdf_obj **objarray = NULL, *o = NULL;; /* This will also prevent us writing out an EMC if the BDC is in any way invalid */ ctx->BDCWasOC = true; @@ -564,7 +583,12 @@ int pdfi_op_BDC(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) ctx->BMClevel ++; tag = (pdf_name *)ctx->stack_top[-2]; - if (tag->type != PDF_NAME) + pdfi_countup(tag); + o = ctx->stack_top[-1]; + pdfi_countup(o); + pdfi_pop(ctx, 2); + + if (pdfi_type_of(tag) != PDF_NAME) goto exit; if (!pdfi_name_is(tag, "OC")) { @@ -578,23 +602,25 @@ int pdfi_op_BDC(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) 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) { + objarray[0] = (pdf_obj *)tag; + + switch (pdfi_type_of(o)) { + case PDF_NAME: + code = pdfi_find_resource(ctx, (unsigned char *)"Properties", (pdf_name *)o, stream_dict, page_dict, (pdf_obj **)&oc_dict); + if(code < 0) + goto exit; + if (pdfi_type_of(oc_dict) != PDF_DICT) { + code = gs_note_error(gs_error_typecheck); + goto exit; + } + objarray[1] = (pdf_obj *)oc_dict; + break; + case PDF_DICT: + objarray[1] = o; + break; + default: 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"); @@ -605,8 +631,8 @@ int pdfi_op_BDC(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) /* 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 (properties->type != PDF_NAME) + properties = (pdf_name *)o; + if (pdfi_type_of(properties) != PDF_NAME) goto exit; /* If it's a name, look it up in Properties */ @@ -614,7 +640,7 @@ int pdfi_op_BDC(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) (pdf_dict *)stream_dict, page_dict, (pdf_obj **)&oc_dict); if (code != 0) goto exit; - if (oc_dict->type != PDF_DICT) + if (pdfi_type_of(oc_dict) != PDF_DICT) goto exit; /* Now we have an OC dict, see if it's visible */ @@ -625,7 +651,8 @@ int pdfi_op_BDC(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) exit: if (objarray != NULL) gs_free_object(ctx->memory, objarray, "free pdfi_op_BDC"); - pdfi_pop(ctx, 2); /* pop args */ + pdfi_countdown(o); + pdfi_countdown(tag); pdfi_countdown(oc_dict); return code; } diff --git a/pdf/pdf_page.c b/pdf/pdf_page.c index f2389fea..b413b68d 100644 --- a/pdf/pdf_page.c +++ b/pdf/pdf_page.c @@ -56,7 +56,7 @@ static int pdfi_process_page_contents(pdf_context *ctx, pdf_dict *page_dict) if (code < 0) return code; - if (o->type == PDF_INDIRECT) { + if (pdfi_type_of(o) == PDF_INDIRECT) { if (((pdf_indirect_ref *)o)->ref_object_num == page_dict->object_num) return_error(gs_error_circular_reference); @@ -77,7 +77,7 @@ static int pdfi_process_page_contents(pdf_context *ctx, pdf_dict *page_dict) } ctx->encryption.decrypt_strings = false; - if (o->type == PDF_ARRAY) { + if (pdfi_type_of(o) == PDF_ARRAY) { pdf_array *a = (pdf_array *)o; for (i=0;i < pdfi_array_size(a); i++) { @@ -85,13 +85,13 @@ static int pdfi_process_page_contents(pdf_context *ctx, pdf_dict *page_dict) code = pdfi_array_get_no_deref(ctx, a, i, (pdf_obj **)&r); if (code < 0) goto page_error; - if (r->type == PDF_STREAM) { + if (pdfi_type_of (r) == PDF_STREAM) { code = pdfi_interpret_content_stream(ctx, NULL, (pdf_stream *)r, page_dict); pdfi_countdown(r); if (code < 0) goto page_error; } else { - if (r->type != PDF_INDIRECT) { + if (pdfi_type_of(r) != PDF_INDIRECT) { pdfi_countdown(r); code = gs_note_error(gs_error_typecheck); goto page_error; @@ -108,7 +108,7 @@ static int pdfi_process_page_contents(pdf_context *ctx, pdf_dict *page_dict) code = 0; goto page_error; } - if (o1->type != PDF_STREAM) { + if (pdfi_type_of(o1) != PDF_STREAM) { pdfi_countdown(o1); code = gs_note_error(gs_error_typecheck); goto page_error; @@ -123,7 +123,7 @@ static int pdfi_process_page_contents(pdf_context *ctx, pdf_dict *page_dict) } } } else { - if (o->type == PDF_STREAM) { + if (pdfi_type_of(o) == PDF_STREAM) { code = pdfi_interpret_content_stream(ctx, NULL, (pdf_stream *)o, page_dict); } else { pdfi_countdown(o); @@ -295,14 +295,23 @@ static int pdfi_set_media_size(pdf_context *ctx, pdf_dict *page_dict) if (a == NULL) { code = pdfi_dict_get_type(ctx, page_dict, "CropBox", PDF_ARRAY, (pdf_obj **)&a); if (code >= 0 && pdfi_array_size(a) >= 4) { + pdf_obj *box_obj = NULL; + for (i=0;i<4;i++) { - code = pdfi_array_get_number(ctx, a, i, &d_crop[i]); - d_crop[i] *= userunit; + code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj); + if (code >= 0) { + code = pdfi_obj_to_real(ctx, box_obj, &d_crop[i]); + pdfi_countdown(box_obj); + } + if (code < 0) + break; } pdfi_countdown(a); - normalize_rectangle(d_crop); - memcpy(ctx->page.Crop, d_crop, 4 * sizeof(double)); - do_crop = true; + if (code >= 0) { + normalize_rectangle(d_crop); + memcpy(ctx->page.Crop, d_crop, 4 * sizeof(double)); + do_crop = true; + } } a = default_media; } @@ -313,7 +322,20 @@ static int pdfi_set_media_size(pdf_context *ctx, pdf_dict *page_dict) ctx->page.UserUnit = userunit; for (i=0;i<4;i++) { - code = pdfi_array_get_number(ctx, a, i, &d[i]); + pdf_obj *box_obj = NULL; + + code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj); + if (code >= 0) { + code = pdfi_obj_to_real(ctx, box_obj, &d[i]); + pdfi_countdown(box_obj); + } + + if (code < 0) { + pdfi_countdown(a); + pdfi_set_warning(ctx, code, NULL, W_PDF_BAD_MEDIABOX, "pdfi_get_media_size", NULL); + code = gs_erasepage(ctx->pgs); + return 0; + } d[i] *= userunit; } pdfi_countdown(a); @@ -458,26 +480,21 @@ static void pdfi_setup_transfers(pdf_context *ctx) } } -static int store_box(pdf_context *ctx, float *box, pdf_array *a) -{ - double f; - int code = 0, i; - - for (i=0;i < 4;i++) { - code = pdfi_array_get_number(ctx, a, (uint64_t)i, &f); - if (code < 0) - return code; - box[i] = (float)f; - } - return 0; -} - -int pdfi_page_info(pdf_context *ctx, uint64_t page_num, pdf_info_t *info) +/* Return a dictionary containing information about the page. Basic information is that + * required to render the page; if extended is true then additionally contains an + * array of spot ink names and an array of dictionaries each of which contains + * information about a font used on the page. THis is normally only used for tools + * like pdf_info.ps + */ +int pdfi_page_info(pdf_context *ctx, uint64_t page_num, pdf_dict **info, bool extended) { - int code = 0; - pdf_dict *page_dict = NULL; + int code = 0, i=0; + pdf_dict *page_dict = NULL, *info_dict = NULL; + pdf_array *fonts_array = NULL, *spots_array = NULL; pdf_array *a = NULL; - double dbl = 0.0; + pdf_obj *o = NULL; + bool known = false; + double dummy; code = pdfi_page_get_dict(ctx, page_num, &page_dict); if (code < 0) @@ -488,79 +505,202 @@ int pdfi_page_info(pdf_context *ctx, uint64_t page_num, pdf_info_t *info) goto done; } - code = pdfi_check_page(ctx, page_dict, false); + code = pdfi_dict_alloc(ctx, 6, &info_dict); if (code < 0) goto done; - info->boxes = BOX_NONE; + pdfi_countup(info_dict); + + if (extended) + code = pdfi_check_page(ctx, page_dict, &fonts_array, &spots_array, false); + else + code = pdfi_check_page(ctx, page_dict, NULL, NULL, false); + if (code < 0) + goto done; + + if (spots_array != NULL) { + code = pdfi_dict_put(ctx, info_dict, "Spots", (pdf_obj *)spots_array); + if (code < 0) + goto done; + pdfi_countdown(spots_array); + } + + if (fonts_array != NULL) { + code = pdfi_dict_put(ctx, info_dict, "Fonts", (pdf_obj *)fonts_array); + if (code < 0) + goto done; + pdfi_countdown(fonts_array); + } + code = pdfi_dict_get_type(ctx, page_dict, "MediaBox", PDF_ARRAY, (pdf_obj **)&a); if (code < 0) pdfi_set_warning(ctx, code, NULL, W_PDF_BAD_MEDIABOX, "pdfi_page_info", NULL); if (code >= 0) { - code = store_box(ctx, (float *)&info->MediaBox, a); + pdf_obj *box_obj = NULL; + + for (i = 0;i < pdfi_array_size(a); i++) { + code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj); + if (code >= 0) { + code = pdfi_obj_to_real(ctx, box_obj, &dummy); + pdfi_countdown(box_obj); + } + if (code < 0) { + pdfi_set_warning(ctx, code, NULL, W_PDF_BAD_MEDIABOX, "pdfi_page_info", NULL); + goto done; + } + } + + code = pdfi_dict_put(ctx, info_dict, "MediaBox", (pdf_obj *)a); if (code < 0) goto done; - info->boxes |= MEDIA_BOX; pdfi_countdown(a); a = NULL; } code = pdfi_dict_get_type(ctx, page_dict, "ArtBox", PDF_ARRAY, (pdf_obj **)&a); if (code >= 0) { - code = store_box(ctx, (float *)&info->ArtBox, a); - if (code < 0) - goto done; - info->boxes |= ART_BOX; + pdf_obj *box_obj = NULL; + + for (i = 0;i < pdfi_array_size(a); i++) { + code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj); + if (code >= 0) { + code = pdfi_obj_to_real(ctx, box_obj, &dummy); + pdfi_countdown(box_obj); + } + if (code < 0) + break; + } + if (code >= 0) { + code = pdfi_dict_put(ctx, info_dict, "ArtBox", (pdf_obj *)a); + if (code < 0) + goto done; + } pdfi_countdown(a); a = NULL; } code = pdfi_dict_get_type(ctx, page_dict, "CropBox", PDF_ARRAY, (pdf_obj **)&a); if (code >= 0) { - code = store_box(ctx, (float *)&info->CropBox, a); - if (code < 0) - goto done; - info->boxes |= CROP_BOX; + pdf_obj *box_obj = NULL; + + for (i = 0;i < pdfi_array_size(a); i++) { + code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj); + if (code >= 0) { + code = pdfi_obj_to_real(ctx, box_obj, &dummy); + pdfi_countdown(box_obj); + } + if (code < 0) + break; + } + if (code >= 0) { + code = pdfi_dict_put(ctx, info_dict, "CropBox", (pdf_obj *)a); + if (code < 0) + goto done; + } pdfi_countdown(a); a = NULL; } code = pdfi_dict_get_type(ctx, page_dict, "TrimBox", PDF_ARRAY, (pdf_obj **)&a); if (code >= 0) { - code = store_box(ctx, (float *)&info->TrimBox, a); - if (code < 0) - goto done; - info->boxes |= TRIM_BOX; + pdf_obj *box_obj = NULL; + + for (i = 0;i < pdfi_array_size(a); i++) { + code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj); + if (code >= 0) { + code = pdfi_obj_to_real(ctx, box_obj, &dummy); + pdfi_countdown(box_obj); + } + if (code < 0) + break; + } + if (code >= 0) { + code = pdfi_dict_put(ctx, info_dict, "TrimBox", (pdf_obj *)a); + if (code < 0) + goto done; + } pdfi_countdown(a); a = NULL; } code = pdfi_dict_get_type(ctx, page_dict, "BleedBox", PDF_ARRAY, (pdf_obj **)&a); if (code >= 0) { - code = store_box(ctx, (float *)&info->BleedBox, a); - if (code < 0) - goto done; - info->boxes |= BLEED_BOX; + pdf_obj *box_obj = NULL; + + for (i = 0;i < pdfi_array_size(a); i++) { + code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj); + if (code >= 0) { + code = pdfi_obj_to_real(ctx, box_obj, &dummy); + pdfi_countdown(box_obj); + } + if (code < 0) + break; + } + if (code >= 0) { + code = pdfi_dict_put(ctx, info_dict, "BleedBox", (pdf_obj *)a); + if (code < 0) + goto done; + } pdfi_countdown(a); a = NULL; } code = 0; - dbl = info->Rotate = 0; - code = pdfi_dict_get_number(ctx, page_dict, "Rotate", &dbl); - code = 0; - info->Rotate = dbl; + code = pdfi_dict_get(ctx, page_dict, "Rotate", &o); + if (code >= 0) { + if (pdfi_type_of(o) == PDF_INT || pdfi_type_of(o) == PDF_REAL) { + code = pdfi_dict_put(ctx, info_dict, "Rotate", o); + if (code < 0) + goto done; + } + pdfi_countdown(o); + } - dbl = info->UserUnit = 1; - code = pdfi_dict_get_number(ctx, page_dict, "UserUnit", &dbl); - code = 0; - info->UserUnit = dbl; + code = pdfi_dict_get(ctx, page_dict, "UserUnit", &o); + if (code >= 0) { + if (pdfi_type_of(o) == PDF_INT || pdfi_type_of(o) == PDF_REAL) { + code = pdfi_dict_put(ctx, info_dict, "UserUnit", o); + if (code < 0) + goto done; + } + pdfi_countdown(o); + } + + if (ctx->page.has_transparency) + code = pdfi_dict_put(ctx, info_dict, "UsesTransparency", PDF_TRUE_OBJ); + else + code = pdfi_dict_put(ctx, info_dict, "UsesTransparency", PDF_FALSE_OBJ); + if (code < 0) + goto done; + + code = pdfi_dict_known(ctx, page_dict, "Annots", &known); + if (code >= 0 && known) + code = pdfi_dict_put(ctx, info_dict, "Annots", PDF_TRUE_OBJ); + else + code = pdfi_dict_put(ctx, info_dict, "Annots", PDF_FALSE_OBJ); + if (code < 0) + goto done; - info->HasTransparency = ctx->page.has_transparency; - info->NumSpots = ctx->page.num_spots; + code = pdfi_object_alloc(ctx, PDF_INT, 0, &o); + if (code >= 0) { + pdfi_countup(o); + ((pdf_num *)o)->value.i = ctx->page.num_spots; + code = pdfi_dict_put(ctx, info_dict, "NumSpots", o); + pdfi_countdown(o); + o = NULL; + if (code < 0) + goto done; + } done: + if (code < 0) { + pdfi_countdown(info_dict); + info_dict = NULL; + *info = NULL; + } else + *info = info_dict; + pdfi_countdown(a); pdfi_countdown(page_dict); return code; @@ -585,7 +725,7 @@ int pdfi_page_get_dict(pdf_context *ctx, uint64_t page_num, pdf_dict **dict) code = pdfi_dict_get(ctx, ctx->Root, "Pages", &o); if (code < 0) goto page_error; - if (o->type != PDF_DICT) { + if (pdfi_type_of(o) != PDF_DICT) { code = gs_note_error(gs_error_typecheck); goto page_error; } @@ -663,14 +803,35 @@ int pdfi_page_get_number(pdf_context *ctx, pdf_dict *target_dict, uint64_t *page static void release_page_DefaultSpaces(pdf_context *ctx) { if (ctx->page.DefaultGray_cs != NULL) { + if (ctx->page.DefaultGray_cs->interpreter_data != NULL) { + pdf_obj *o = (pdf_obj *)(ctx->page.DefaultGray_cs->interpreter_data); + if (o != NULL && pdfi_type_of(o) == PDF_NAME) { + pdfi_countdown(o); + ctx->page.DefaultGray_cs->interpreter_data = NULL; + } + } rc_decrement(ctx->page.DefaultGray_cs, "pdfi_page_render"); ctx->page.DefaultGray_cs = NULL; } if (ctx->page.DefaultRGB_cs != NULL) { + if (ctx->page.DefaultRGB_cs->interpreter_data != NULL) { + pdf_obj *o = (pdf_obj *)(ctx->page.DefaultRGB_cs->interpreter_data); + if (o != NULL && pdfi_type_of(o) == PDF_NAME) { + pdfi_countdown(o); + ctx->page.DefaultRGB_cs->interpreter_data = NULL; + } + } rc_decrement(ctx->page.DefaultRGB_cs, "pdfi_page_render"); ctx->page.DefaultRGB_cs = NULL; } if (ctx->page.DefaultCMYK_cs != NULL) { + if (ctx->page.DefaultCMYK_cs->interpreter_data != NULL) { + pdf_obj *o = (pdf_obj *)(ctx->page.DefaultCMYK_cs->interpreter_data); + if (o != NULL && pdfi_type_of(o) == PDF_NAME) { + pdfi_countdown(o); + ctx->page.DefaultCMYK_cs->interpreter_data = NULL; + } + } rc_decrement(ctx->page.DefaultCMYK_cs, "pdfi_page_render"); ctx->page.DefaultCMYK_cs = NULL; } @@ -684,6 +845,12 @@ static int setup_page_DefaultSpaces(pdf_context *ctx, pdf_dict *page_dict) return(pdfi_setup_DefaultSpaces(ctx, page_dict)); } +static bool +pdfi_pattern_purge_all_proc(gx_color_tile * ctile, void *proc_data) +{ + return true; +} + int pdfi_page_render(pdf_context *ctx, uint64_t page_num, bool init_graphics) { int code, code1=0; @@ -714,7 +881,7 @@ int pdfi_page_render(pdf_context *ctx, uint64_t page_num, bool init_graphics) pdfi_device_set_flags(ctx); - code = pdfi_check_page(ctx, page_dict, init_graphics); + code = pdfi_check_page(ctx, page_dict, NULL, NULL, init_graphics); if (code < 0) goto exit3; @@ -729,8 +896,12 @@ 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); + /* Ignore errors retrieving the Group dictionary, we will just ignore it. This allows us + * to handle files such as Bug #705206 where the Group dictionary is a free object in a + * compressed object stream. + */ if (code < 0) - goto exit3; + pdfi_set_error(ctx, 0, NULL, E_BAD_GROUP_DICT, "pdfi_page_render", NULL); if (group_dict != NULL) page_group_known = true; @@ -864,7 +1035,12 @@ exit3: release_page_DefaultSpaces(ctx); - if (code == 0 || (!ctx->args.pdfstoponerror && code != gs_error_stackoverflow)) + /* Flush any pattern tiles. We don't want to (potentially) return to PostScript + * with any pattern tiles referencing our objects, in case the garbager runs. + */ + gx_pattern_cache_winnow(gstate_pattern_cache(ctx->pgs), pdfi_pattern_purge_all_proc, NULL); + + if (code == 0 || (!ctx->args.pdfstoponerror && code != gs_error_pdf_stackoverflow)) if (!page_dict_error && ctx->finish_page != NULL) code = ctx->finish_page(ctx); return code; diff --git a/pdf/pdf_page.h b/pdf/pdf_page.h index b5fd3e2a..156f3794 100644 --- a/pdf/pdf_page.h +++ b/pdf/pdf_page.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 @@ -18,30 +18,8 @@ #ifndef PDF_PAGE_OPERATORS #define PDF_PAGE_OPERATORS -typedef enum pdfi_box_enum_e { - BOX_NONE = 0, - MEDIA_BOX = 1, - CROP_BOX = 2, - TRIM_BOX = 4, - ART_BOX = 8, - BLEED_BOX = 16 -}pdfi_box_enum; - -typedef struct { - bool HasTransparency; - int NumSpots; - pdfi_box_enum boxes; - float MediaBox[4]; - float CropBox[4]; - float ArtBox[4]; - float BleedBox[4]; - float TrimBox[4]; - float Rotate; - float UserUnit; -} pdf_info_t; - int pdfi_page_render(pdf_context *ctx, uint64_t page_num, bool init_graphics); -int pdfi_page_info(pdf_context *ctx, uint64_t page_num, pdf_info_t *info); +int pdfi_page_info(pdf_context *ctx, uint64_t page_num, pdf_dict **info_dict, bool extended); int pdfi_page_graphics_begin(pdf_context *ctx); int pdfi_page_get_dict(pdf_context *ctx, uint64_t page_num, pdf_dict **dict); int pdfi_page_get_number(pdf_context *ctx, pdf_dict *target_dict, uint64_t *page_num); diff --git a/pdf/pdf_path.c b/pdf/pdf_path.c index a9724def..56a59b07 100644 --- a/pdf/pdf_path.c +++ b/pdf/pdf_path.c @@ -26,88 +26,227 @@ #include "gspath.h" /* For gs_moveto() and friends */ #include "gspaint.h" /* For gs_fill() and friends */ -int pdfi_moveto (pdf_context *ctx) +typedef enum path_segment_e { + pdfi_moveto_seg, + pdfi_lineto_seg, + pdfi_curveto_seg, + pdfi_re_seg, + pdfi_v_curveto_seg, + pdfi_y_curveto_seg, + pdfi_closepath_seg +} pdfi_path_segment; + +static int StorePathSegment(pdf_context *ctx, pdfi_path_segment segment, double *pts) { - pdf_num *n1, *n2; - int code; - double x, y; + int size = 0; + + switch (segment) + { + case pdfi_moveto_seg: + case pdfi_lineto_seg: + size = 2; + break; + case pdfi_re_seg: + case pdfi_v_curveto_seg: + case pdfi_y_curveto_seg: + size = 4; + break; + case pdfi_curveto_seg: + size = 6; + break; + case pdfi_closepath_seg: + break; + default: + return_error(gs_error_undefined); + break; + } + if (ctx->PathSegments == NULL) { + ctx->PathSegments = (char *)gs_alloc_bytes(ctx->memory, 1024, "StorePathSegment"); + if (ctx->PathSegments == NULL) + return_error(gs_error_VMerror); + ctx->PathSegmentsCurrent = ctx->PathSegments; + ctx->PathSegmentsTop = ctx->PathSegments + 1024; + } + if (ctx->PathSegmentsCurrent == ctx->PathSegmentsTop) { + char *new_accumulator = NULL; + uint64_t old_size; + + old_size = ctx->PathSegmentsCurrent - ctx->PathSegments; + new_accumulator = (char *)gs_alloc_bytes(ctx->memory, old_size + 1024, "StorePathSegment"); + if (new_accumulator == NULL) + return_error(gs_error_VMerror); + memcpy(new_accumulator, ctx->PathSegments, old_size); + ctx->PathSegmentsCurrent = new_accumulator + old_size; + gs_free_object(ctx->memory, ctx->PathSegments, "StorePathSegment"); + ctx->PathSegments = new_accumulator; + ctx->PathSegmentsTop = ctx->PathSegments + old_size + 1024; + } - if (ctx->text.BlockDepth != 0) - pdfi_set_warning(ctx, 0, NULL, W_PDF_OPINVALIDINTEXT, "pdfi_moveto", NULL); + if (ctx->PathPts == NULL) { + ctx->PathPts = (double *)gs_alloc_bytes(ctx->memory, 4096, "StorePathSegment"); + if (ctx->PathPts == NULL) + return_error(gs_error_VMerror); + ctx->PathPtsCurrent = ctx->PathPts; + ctx->PathPtsTop = ctx->PathPts + (4096 / sizeof(double)); + } + if (ctx->PathPtsCurrent + size > ctx->PathPtsTop) { + double *new_accumulator = NULL; + uint64_t old_size; + + old_size = (char *)ctx->PathPtsCurrent - (char *)ctx->PathPts; + new_accumulator = (double *)gs_alloc_bytes(ctx->memory, old_size + 4096, "StorePathSegment"); + if (new_accumulator == NULL) + return_error(gs_error_VMerror); + memcpy(new_accumulator, ctx->PathPts, old_size); + ctx->PathPtsCurrent = new_accumulator + (old_size / sizeof(double)); + gs_free_object(ctx->memory, ctx->PathPts, "StorePathSegment"); + ctx->PathPts = new_accumulator; + ctx->PathPtsTop = ctx->PathPts + ((old_size + 4096) / sizeof(double)); + } - if (pdfi_count_stack(ctx) < 2) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); + *(ctx->PathSegmentsCurrent++) = (char)segment; + switch (segment) + { + case pdfi_moveto_seg: + case pdfi_lineto_seg: + memcpy(ctx->PathPtsCurrent, pts, 2 * sizeof(double)); + ctx->PathPtsCurrent += 2; + break; + case pdfi_re_seg: + case pdfi_v_curveto_seg: + case pdfi_y_curveto_seg: + memcpy(ctx->PathPtsCurrent, pts, 4 * sizeof(double)); + ctx->PathPtsCurrent += 4; + break; + case pdfi_curveto_seg: + memcpy(ctx->PathPtsCurrent, pts, 6 * sizeof(double)); + ctx->PathPtsCurrent += 6; + break; + case pdfi_closepath_seg: + break; } + return 0; +} - n1 = (pdf_num *)ctx->stack_top[-1]; - n2 = (pdf_num *)ctx->stack_top[-2]; - if (n1->type == PDF_INT){ - y = (double)n1->value.i; - } else{ - if (n1->type == PDF_REAL) { - y = n1->value.d; - } else { - pdfi_pop(ctx, 2); - return_error(gs_error_typecheck); - } +static int ApplyStoredPath(pdf_context *ctx) +{ + int code = 0; + char *op = NULL; + double *dpts = NULL; + + if (ctx->PathSegments == NULL) + return 0; + + if (ctx->PathPts == NULL) { + code = gs_note_error(gs_error_unknownerror); + goto error; + } + + if (ctx->pgs->current_point_valid) { + code = gs_newpath(ctx->pgs); + if (code < 0) + goto error; } - if (n2->type == PDF_INT){ - x = (double)n2->value.i; - } else{ - if (n2->type == PDF_REAL) { - x = n2->value.d; - } else { - pdfi_pop(ctx, 2); - return_error(gs_error_typecheck); + + op = ctx->PathSegments; + dpts = ctx->PathPts; + + while (op < ctx->PathSegmentsCurrent) { + if (dpts > ctx->PathPtsCurrent) { + code = gs_note_error(gs_error_unknownerror); + goto error; } + + switch(*op++) { + case pdfi_moveto_seg: + code = gs_moveto(ctx->pgs, dpts[0], dpts[1]); + dpts+= 2; + break; + case pdfi_lineto_seg: + code = gs_lineto(ctx->pgs, dpts[0], dpts[1]); + dpts+= 2; + break; + case pdfi_re_seg: + code = gs_moveto(ctx->pgs, dpts[0], dpts[1]); + if (code >= 0) { + code = gs_rlineto(ctx->pgs, dpts[2], 0); + if (code >= 0) { + code = gs_rlineto(ctx->pgs, 0, dpts[3]); + if (code >= 0) { + code = gs_rlineto(ctx->pgs, -dpts[2], 0); + if (code >= 0) + code = gs_closepath(ctx->pgs); + } + } + } + dpts+= 4; + break; + case pdfi_v_curveto_seg: + { + gs_point pt; + + code = gs_currentpoint(ctx->pgs, &pt); + if (code >= 0) { + code = gs_curveto(ctx->pgs, pt.x, pt.y, dpts[0], dpts[1], dpts[2], dpts[3]); + dpts+= 4; + } + } + break; + case pdfi_y_curveto_seg: + code = gs_curveto(ctx->pgs, dpts[0], dpts[1], dpts[2], dpts[3], dpts[2], dpts[3]); + dpts+= 4; + break; + case pdfi_curveto_seg: + code = gs_curveto(ctx->pgs, dpts[0], dpts[1], dpts[2], dpts[3], dpts[4], dpts[5]); + dpts+= 6; + break; + case pdfi_closepath_seg: + code = gs_closepath(ctx->pgs); + break; + default: + code = gs_note_error(gs_error_rangecheck); + break; + } + if (code < 0) + break; } - code = gs_moveto(ctx->pgs, x, y); - pdfi_pop(ctx, 2); +error: + gs_free_object(ctx->memory, ctx->PathSegments, "ApplyStoredPath"); + ctx->PathSegmentsTop = ctx->PathSegmentsCurrent = ctx->PathSegments = NULL; + gs_free_object(ctx->memory, ctx->PathPts, "ApplyStoredPath"); + ctx->PathPtsTop = ctx->PathPtsCurrent = ctx->PathPts = NULL; return code; } +int pdfi_moveto (pdf_context *ctx) +{ + int code; + double xy[2]; + + if (ctx->text.BlockDepth != 0) + pdfi_set_warning(ctx, 0, NULL, W_PDF_OPINVALIDINTEXT, "pdfi_moveto", NULL); + + code = pdfi_destack_reals(ctx, xy, 2); + if (code < 0) + return code; + + return StorePathSegment(ctx, pdfi_moveto_seg, (double *)&xy); +} + int pdfi_lineto (pdf_context *ctx) { - pdf_num *n1, *n2; int code; - double x, y; + double xy[2]; if (ctx->text.BlockDepth != 0) pdfi_set_warning(ctx, 0, NULL, W_PDF_OPINVALIDINTEXT, "pdfi_lineto", NULL); - if (pdfi_count_stack(ctx) < 2) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } - - n1 = (pdf_num *)ctx->stack_top[-1]; - n2 = (pdf_num *)ctx->stack_top[-2]; - if (n1->type == PDF_INT){ - y = (double)n1->value.i; - } else{ - if (n1->type == PDF_REAL) { - y = n1->value.d; - } else { - pdfi_pop(ctx, 2); - return_error(gs_error_typecheck); - } - } - if (n2->type == PDF_INT){ - x = (double)n2->value.i; - } else{ - if (n2->type == PDF_REAL) { - x = n2->value.d; - } else { - pdfi_pop(ctx, 2); - return_error(gs_error_typecheck); - } - } + code = pdfi_destack_reals(ctx, xy, 2); + if (code < 0) + return code; - code = gs_lineto(ctx->pgs, x, y); - pdfi_pop(ctx, 2); - return code; + return StorePathSegment(ctx, pdfi_lineto_seg, (double *)&xy); } static int pdfi_fill_inner(pdf_context *ctx, bool use_eofill) @@ -121,6 +260,10 @@ static int pdfi_fill_inner(pdf_context *ctx, bool use_eofill) if (pdfi_oc_is_off(ctx)) goto exit; + code = ApplyStoredPath(ctx); + if (code < 0) + return code; + 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 @@ -171,8 +314,9 @@ int pdfi_stroke(pdf_context *ctx) if (pdfi_oc_is_off(ctx)) goto exit; -/* code = pdfi_gsave(ctx); - if (code < 0) goto exit;*/ + code = ApplyStoredPath(ctx); + if (code < 0) + return code; gs_swapcolors_quick(ctx->pgs); code = pdfi_trans_setup(ctx, &state, NULL, TRANSPARENCY_Caller_Stroke); @@ -190,9 +334,6 @@ int pdfi_stroke(pdf_context *ctx) } gs_swapcolors_quick(ctx->pgs); -/* code1 = pdfi_grestore(ctx); - if (code == 0) code = code1;*/ - exit: code1 = pdfi_newpath(ctx); if (code == 0) code = code1; @@ -207,127 +348,64 @@ int pdfi_closepath_stroke(pdf_context *ctx) if (ctx->text.BlockDepth != 0) pdfi_set_warning(ctx, 0, NULL, W_PDF_OPINVALIDINTEXT, "pdfi_closepath_stroke", NULL); - code = gs_closepath(ctx->pgs); - if (code == 0) - code = pdfi_stroke(ctx); - return code; + code = StorePathSegment(ctx, pdfi_closepath_seg, NULL); + if (code < 0) + return code; + + return pdfi_stroke(ctx); } int pdfi_curveto(pdf_context *ctx) { - int i, code; - pdf_num *num; + int code; double Values[6]; - if (pdfi_count_stack(ctx) < 6) { - pdfi_clearstack(ctx); - pdfi_set_error(ctx, 0, NULL, E_PDF_STACKUNDERFLOWERROR, "pdfi_curveto", NULL); - return_error(gs_error_stackunderflow); - } - - for (i=0;i < 6;i++){ - num = (pdf_num *)ctx->stack_top[i - 6]; - if (num->type != PDF_INT) { - if(num->type != PDF_REAL) { - pdfi_pop(ctx, 6); - return_error(gs_error_typecheck); - } - else - Values[i] = num->value.d; - } else { - Values[i] = (double)num->value.i; - } - } + code = pdfi_destack_reals(ctx, Values, 6); + if (code < 0) + return code; if (ctx->text.BlockDepth != 0) pdfi_set_warning(ctx, 0, NULL, W_PDF_OPINVALIDINTEXT, "pdfi_curveto", NULL); - code = gs_curveto(ctx->pgs, Values[0], Values[1], Values[2], Values[3], Values[4], Values[5]); - pdfi_pop(ctx, 6); - return code; + return StorePathSegment(ctx, pdfi_curveto_seg, (double *)&Values); } int pdfi_v_curveto(pdf_context *ctx) { - int i, code; - pdf_num *num; + int code; double Values[4]; - gs_point pt; - if (pdfi_count_stack(ctx) < 4) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } - - for (i=0;i < 4;i++){ - num = (pdf_num *)ctx->stack_top[i - 4]; - if (num->type != PDF_INT) { - if(num->type != PDF_REAL) { - pdfi_pop(ctx, 4); - return_error(gs_error_typecheck); - } - else - Values[i] = num->value.d; - } else { - Values[i] = (double)num->value.i; - } - } + code = pdfi_destack_reals(ctx, Values, 4); + if (code < 0) + return code; if (ctx->text.BlockDepth != 0) pdfi_set_warning(ctx, 0, NULL, W_PDF_OPINVALIDINTEXT, "pdfi_v_curveto", NULL); - code = gs_currentpoint(ctx->pgs, &pt); - if (code < 0) { - pdfi_pop(ctx, 4); - return code; - } - - code = gs_curveto(ctx->pgs, pt.x, pt.y, Values[0], Values[1], Values[2], Values[3]); - pdfi_pop(ctx, 4); - return code; + return StorePathSegment(ctx, pdfi_v_curveto_seg, (double *)&Values); } int pdfi_y_curveto(pdf_context *ctx) { - int i, code; - pdf_num *num; + int code; double Values[4]; - if (pdfi_count_stack(ctx) < 4) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } - - for (i=0;i < 4;i++){ - num = (pdf_num *)ctx->stack_top[i - 4]; - if (num->type != PDF_INT) { - if(num->type != PDF_REAL) { - pdfi_pop(ctx, 4); - return_error(gs_error_typecheck); - } - else - Values[i] = num->value.d; - } else { - Values[i] = (double)num->value.i; - } - } + code = pdfi_destack_reals(ctx, Values, 4); + if (code < 0) + return code; if (ctx->text.BlockDepth != 0) pdfi_set_warning(ctx, 0, NULL, W_PDF_OPINVALIDINTEXT, "pdfi_y_curveto", NULL); - code = gs_curveto(ctx->pgs, Values[0], Values[1], Values[2], Values[3], Values[2], Values[3]); - pdfi_pop(ctx, 4); - return code; + return StorePathSegment(ctx, pdfi_y_curveto_seg, (double *)&Values); } int pdfi_closepath(pdf_context *ctx) { - int code = gs_closepath(ctx->pgs); - if (ctx->text.BlockDepth != 0) pdfi_set_warning(ctx, 0, NULL, W_PDF_OPINVALIDINTEXT, "pdfi_closepath", NULL); - return code; + return StorePathSegment(ctx, pdfi_closepath_seg, NULL); } int pdfi_newpath(pdf_context *ctx) @@ -335,8 +413,13 @@ int pdfi_newpath(pdf_context *ctx) int code = 0, code1; /* This code is to deal with the wacky W and W* operators */ - if (ctx->pgs->current_point_valid) { - if (ctx->clip_active) { + if (ctx->clip_active) { + if (ctx->PathSegments != NULL) { + code = ApplyStoredPath(ctx); + if (code < 0) + return code; + } + if (ctx->pgs->current_point_valid) { if (ctx->do_eoclip) code = gs_eoclip(ctx->pgs); else @@ -345,6 +428,13 @@ int pdfi_newpath(pdf_context *ctx) } ctx->clip_active = false; + if (ctx->PathSegments != NULL){ + gs_free_object(ctx->memory, ctx->PathSegments, "ApplyStoredPath"); + ctx->PathSegmentsTop = ctx->PathSegmentsCurrent = ctx->PathSegments = NULL; + gs_free_object(ctx->memory, ctx->PathPts, "ApplyStoredPath"); + ctx->PathPtsTop = ctx->PathPtsCurrent = ctx->PathPts = NULL; + } + code1 = gs_newpath(ctx->pgs); if (code == 0) code = code1; @@ -361,10 +451,11 @@ int pdfi_b(pdf_context *ctx) if (ctx->text.BlockDepth != 0) pdfi_set_warning(ctx, 0, NULL, W_PDF_OPINVALIDINTEXT, "pdfi_b", NULL); - code = gs_closepath(ctx->pgs); - if (code >= 0) - code = pdfi_B(ctx); - return code; + code = StorePathSegment(ctx, pdfi_closepath_seg, NULL); + if (code < 0) + return code; + + return pdfi_B(ctx); } int pdfi_b_star(pdf_context *ctx) @@ -374,10 +465,11 @@ int pdfi_b_star(pdf_context *ctx) if (ctx->text.BlockDepth != 0) pdfi_set_warning(ctx, 0, NULL, W_PDF_OPINVALIDINTEXT, "pdfi_b_star", NULL); - code = gs_closepath(ctx->pgs); - if (code >= 0) - code = pdfi_B_star(ctx); - return code; + code = StorePathSegment(ctx, pdfi_closepath_seg, NULL); + if (code < 0) + return code; + + return pdfi_B_star(ctx); } /* common code for B and B* */ @@ -392,6 +484,10 @@ static int pdfi_B_inner(pdf_context *ctx, bool use_eofill) if (pdfi_oc_is_off(ctx)) goto exit; + code = ApplyStoredPath(ctx); + if (code < 0) + return code; + code = pdfi_trans_setup(ctx, &state, NULL, TRANSPARENCY_Caller_FillStroke); if (code == 0) { code = pdfi_gsave(ctx); @@ -448,45 +544,15 @@ int pdfi_eoclip(pdf_context *ctx) int pdfi_rectpath(pdf_context *ctx) { - int i, code; - pdf_num *num; + int code; double Values[4]; - if (pdfi_count_stack(ctx) < 4) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } - - for (i=0;i < 4;i++){ - num = (pdf_num *)ctx->stack_top[i - 4]; - if (num->type != PDF_INT) { - if(num->type != PDF_REAL) { - pdfi_pop(ctx, 4); - return_error(gs_error_typecheck); - } - else - Values[i] = num->value.d; - } else { - Values[i] = (double)num->value.i; - } - } + code = pdfi_destack_reals(ctx, Values, 4); + if (code < 0) + return code; if (ctx->text.BlockDepth != 0) pdfi_set_warning(ctx, 0, NULL, W_PDF_OPINVALIDINTEXT, "pdfi_rectpath", NULL); - code = gs_moveto(ctx->pgs, Values[0], Values[1]); - if (code == 0) { - code = gs_rlineto(ctx->pgs, Values[2], 0); - if (code == 0){ - code = gs_rlineto(ctx->pgs, 0, Values[3]); - if (code == 0) { - code = gs_rlineto(ctx->pgs, -Values[2], 0); - if (code == 0){ - code = gs_closepath(ctx->pgs); - } - } - } - } - pdfi_pop(ctx, 4); - return code; + return StorePathSegment(ctx, pdfi_re_seg, (double *)&Values); } diff --git a/pdf/pdf_pattern.c b/pdf/pdf_pattern.c index 57a16449..b4bd0375 100644 --- a/pdf/pdf_pattern.c +++ b/pdf/pdf_pattern.c @@ -114,35 +114,12 @@ static void pdfi_free_pattern_context(pdf_pattern_context_t *context) gs_free_object(context->ctx->memory, context, "Free pattern context"); } -static bool -pdfi_pattern_purge_proc(gx_color_tile * ctile, void *proc_data) -{ - if (ctile->id == *((gx_bitmap_id *)proc_data)) - return true; - return false; -} - void pdfi_pattern_cleanup(gs_memory_t * mem, void *p) { gs_pattern1_instance_t *pinst = (gs_pattern1_instance_t *)p; - pdf_pattern_context_t *context; - gx_color_tile *pctile = NULL; - - context = (pdf_pattern_context_t *)pinst->client_data; - /* If are being called from Ghostscript, the clist pattern accumulator device (in - the tile cache) *can* outlast outlast our pattern instance, so if the pattern - instance is being freed, also remove the entry from the cache - */ - if (context != NULL && context->ctx != NULL && context->ctx->pgs != NULL && - 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)); - } - - if (context != NULL) { - pdfi_free_pattern_context(context); + if (pinst->client_data != NULL) { + pdfi_free_pattern_context((pdf_pattern_context_t *)pinst->client_data); pinst->client_data = NULL; pinst->notify_free = NULL; } @@ -418,7 +395,7 @@ pdfi_setpattern_type1(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_di gs_pattern1_init(&templat); /* Must be a stream */ - if (stream->type != PDF_STREAM) { + if (pdfi_type_of(stream) != PDF_STREAM) { code = gs_note_error(gs_error_typecheck); goto exit; } @@ -525,6 +502,28 @@ pdfi_setpattern_type1(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_di cc->pattern->client_data = context; cc->pattern->notify_free = pdfi_pattern_cleanup; + { + unsigned long hash = 5381; + unsigned int i; + const char *str = (const char *)&ctx->pgs->ctm; + + gs_pattern1_instance_t *pinst = (gs_pattern1_instance_t *)cc->pattern; + + + for (i = 0; i < 4 * sizeof(float); i++) + hash = ((hash << 5) + hash) + str[i]; /* hash * 33 + c */ + + str = (const char *)&pdict->object_num; + for (i = 0; i < sizeof(uint32_t); i++) + hash = ((hash << 5) + hash) + str[i]; /* hash * 33 + c */ + + hash = ((hash << 5) + hash) + ctx->pgs->device->color_info.num_components; /* hash * 33 + c */ + + /* Include num_components for case where we have softmask and non-softmask + fills with the same tile. We may need two tiles for this if there is a + change in color space for the transparency group. */ + pinst->id = hash; + } context = NULL; code = pdfi_grestore(ctx); diff --git a/pdf/pdf_repair.c b/pdf/pdf_repair.c index 2c2badf2..22865fe6 100644 --- a/pdf/pdf_repair.c +++ b/pdf/pdf_repair.c @@ -25,14 +25,14 @@ #include "pdf_misc.h" #include "pdf_repair.h" -static int pdfi_repair_add_object(pdf_context *ctx, uint64_t obj, uint64_t gen, gs_offset_t offset) +static int pdfi_repair_add_object(pdf_context *ctx, int64_t obj, int64_t gen, gs_offset_t offset) { /* Although we can handle object numbers larger than this, on some systems (32-bit Windows) * memset is limited to a (signed!) integer for the size of memory to clear. We could deal * with this by clearing the memory in blocks, but really, this is almost certainly a * corrupted file or something. */ - if (obj >= 0x7ffffff / sizeof(xref_entry)) + if (obj >= 0x7ffffff / sizeof(xref_entry) || obj < 1 || gen < 0 || offset < 0) return_error(gs_error_rangecheck); if (ctx->xref_table == NULL) { @@ -85,7 +85,7 @@ int pdfi_repair_file(pdf_context *ctx) { int code = 0; gs_offset_t offset, saved_offset; - uint64_t object_num = 0, generation_num = 0; + int64_t object_num = 0, generation_num = 0; int i; gs_offset_t outer_saved_offset[3]; @@ -163,18 +163,18 @@ int pdfi_repair_file(pdf_context *ctx) goto exit; } if (pdfi_count_stack(ctx) > 0) { - if (ctx->stack_top[-1]->type == PDF_KEYWORD) { - pdf_keyword *k = (pdf_keyword *)ctx->stack_top[-1]; + if (pdfi_type_of(ctx->stack_top[-1]) == PDF_FAST_KEYWORD) { + pdf_obj *k = ctx->stack_top[-1]; pdf_num *n; - if (k->key == TOKEN_OBJ) { + if (k == PDF_TOKEN_AS_OBJ(TOKEN_OBJ)) { gs_offset_t saved_offset[3]; offset = outer_saved_offset[0]; saved_offset[0] = saved_offset[1] = saved_offset[2] = 0; - if (pdfi_count_stack(ctx) < 3 || ctx->stack_top[-2]->type != PDF_INT || ctx->stack_top[-2]->type != PDF_INT) { + if (pdfi_count_stack(ctx) < 3 || pdfi_type_of(ctx->stack_top[-3]) != PDF_INT || pdfi_type_of(ctx->stack_top[-2]) != PDF_INT) { pdfi_clearstack(ctx); continue; } @@ -199,15 +199,15 @@ int pdfi_repair_file(pdf_context *ctx) if (code == 0 && ctx->main_stream->eof) break; - if (ctx->stack_top[-1]->type == PDF_KEYWORD){ - pdf_keyword *k = (pdf_keyword *)ctx->stack_top[-1]; + if (pdfi_type_of(ctx->stack_top[-1]) == PDF_FAST_KEYWORD) { + pdf_obj *k = ctx->stack_top[-1]; - if (k->key == TOKEN_OBJ) { + if (k == PDF_TOKEN_AS_OBJ(TOKEN_OBJ)) { /* Found obj while looking for endobj, store the existing 'obj' * and start afresh. */ code = pdfi_repair_add_object(ctx, object_num, generation_num, offset); - if (pdfi_count_stack(ctx) < 3 || ctx->stack_top[-2]->type != PDF_INT || ctx->stack_top[-2]->type != PDF_INT) { + if (pdfi_count_stack(ctx) < 3 || pdfi_type_of(ctx->stack_top[-3]) != PDF_INT || pdfi_type_of(ctx->stack_top[-2]) != PDF_INT) { pdfi_clearstack(ctx); break; } @@ -220,14 +220,14 @@ int pdfi_repair_file(pdf_context *ctx) continue; } - if (k->key == TOKEN_ENDOBJ) { + if (k == PDF_TOKEN_AS_OBJ(TOKEN_ENDOBJ)) { code = pdfi_repair_add_object(ctx, object_num, generation_num, offset); if (code < 0) goto exit; pdfi_clearstack(ctx); break; } else { - if (k->key == TOKEN_STREAM) { + if (k == PDF_TOKEN_AS_OBJ(TOKEN_STREAM)) { static const char test[] = "endstream"; int index = 0; @@ -245,27 +245,16 @@ int pdfi_repair_file(pdf_context *ctx) index = 0; } while (index < 9); do { - code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); - if (code < 0) { - if (code != gs_error_VMerror && code != gs_error_ioerror) - continue; + code = pdfi_read_bare_keyword(ctx, ctx->main_stream); + if (code == gs_error_VMerror || code == gs_error_ioerror) goto exit; + if (code == TOKEN_ENDOBJ || code == TOKEN_INVALID_KEY) { + code = pdfi_repair_add_object(ctx, object_num, generation_num, offset); + if (code == gs_error_VMerror || code == gs_error_ioerror) + goto exit; + break; } - 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; - } - } - } - }while(ctx->main_stream->eof == false); + } while(ctx->main_stream->eof == false); pdfi_clearstack(ctx); break; @@ -277,10 +266,10 @@ int pdfi_repair_file(pdf_context *ctx) } while(1); break; } else { - if (k->key == TOKEN_ENDOBJ) { + if (k == PDF_TOKEN_AS_OBJ(TOKEN_ENDOBJ)) { pdfi_clearstack(ctx); } else - if (k->key == TOKEN_STARTXREF) { + if (k == PDF_TOKEN_AS_OBJ(TOKEN_STARTXREF)) { code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); if (code < 0 && code != gs_error_VMerror && code != gs_error_ioerror) continue; @@ -288,9 +277,9 @@ int pdfi_repair_file(pdf_context *ctx) goto exit; pdfi_clearstack(ctx); } else { - if (k->key == TOKEN_TRAILER) { + if (k == PDF_TOKEN_AS_OBJ(TOKEN_TRAILER)) { code = pdfi_read_bare_object(ctx, ctx->main_stream, 0, 0, 0); - if (code == 0 && pdfi_count_stack(ctx) > 0 && ctx->stack_top[-1]->type == PDF_DICT) { + if (code == 0 && pdfi_count_stack(ctx) > 0 && pdfi_type_of(ctx->stack_top[-1]) == PDF_DICT) { if (ctx->Trailer) { pdf_dict *d = (pdf_dict *)ctx->stack_top[-1]; bool known = false; @@ -319,7 +308,7 @@ int pdfi_repair_file(pdf_context *ctx) goto exit; } } - if (pdfi_count_stack(ctx) > 0 && ctx->stack_top[-1]->type != PDF_INT) + if (pdfi_count_stack(ctx) > 0 && pdfi_type_of(ctx->stack_top[-1]) != PDF_INT) pdfi_clearstack(ctx); } } while (ctx->main_stream->eof == false); @@ -338,33 +327,44 @@ int pdfi_repair_file(pdf_context *ctx) if (ctx->xref_table->xref[i].object_num != 0) { /* At this stage, all the objects we've found must be uncompressed */ if (ctx->xref_table->xref[i].u.uncompressed.offset > ctx->main_stream_length) { - code = gs_note_error(gs_error_rangecheck); - goto exit; + /* This can only happen if we had read an xref table before we tried to repair + * the file, and the table has entries we didn't find in the file. So + * mark the entry as free, and offset of 0, and just carry on. + */ + ctx->xref_table->xref[i].free = 1; + ctx->xref_table->xref[i].u.uncompressed.offset = 0; + continue; } pdfi_seek(ctx, ctx->main_stream, ctx->xref_table->xref[i].u.uncompressed.offset, SEEK_SET); do { code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); - if (ctx->main_stream->eof == true || (code < 0 && code != gs_error_ioerror && code != gs_error_VMerror)) + if (ctx->main_stream->eof == true || (code < 0 && code != gs_error_ioerror && code != gs_error_VMerror)) { + /* object offset is beyond EOF or object is broken (possibly due to multiple xref + * errors) ignore the error and carry on, if the object gets used then we will + * error out at that point. + */ + code = 0; break; + } if (code < 0) goto exit; - if (ctx->stack_top[-1]->type == PDF_KEYWORD) { - pdf_keyword *k = (pdf_keyword *)ctx->stack_top[-1]; + if (pdfi_type_of(ctx->stack_top[-1]) == PDF_FAST_KEYWORD) { + pdf_obj *k = ctx->stack_top[-1]; - if (k->key == TOKEN_OBJ){ + if (k == PDF_TOKEN_AS_OBJ(TOKEN_OBJ)) { continue; } - if (k->key == TOKEN_ENDOBJ) { + if (k == PDF_TOKEN_AS_OBJ(TOKEN_ENDOBJ)) { if (pdfi_count_stack(ctx) > 1) { - if (ctx->stack_top[-2]->type == PDF_DICT) { + if (pdfi_type_of(ctx->stack_top[-2]) == PDF_DICT) { pdf_dict *d = (pdf_dict *)ctx->stack_top[-2]; pdf_obj *o = NULL; code = pdfi_dict_knownget_type(ctx, d, "Type", PDF_NAME, &o); if (code < 0) { pdfi_clearstack(ctx); - goto exit; + continue; } if (code > 0) { pdf_name *n = (pdf_name *)o; @@ -381,7 +381,7 @@ int pdfi_repair_file(pdf_context *ctx) pdfi_clearstack(ctx); break; } - if (k->key == TOKEN_STREAM) { + if (k == PDF_TOKEN_AS_OBJ(TOKEN_STREAM)) { pdf_dict *d; pdf_name *n = NULL; @@ -390,7 +390,7 @@ int pdfi_repair_file(pdf_context *ctx) break;; } d = (pdf_dict *)ctx->stack_top[-2]; - if (d->type != PDF_DICT) { + if (pdfi_type_of(d) != PDF_DICT) { pdfi_clearstack(ctx); break;; } @@ -403,10 +403,10 @@ int pdfi_repair_file(pdf_context *ctx) } if (code > 0) { if (pdfi_name_is(n, "ObjStm")) { - int64_t N, obj_num, offset; + int64_t N; + int obj_num, offset; int j; pdf_c_stream *compressed_stream; - pdf_obj *o; pdf_stream *stream; offset = pdfi_unread_tell(ctx); @@ -422,36 +422,28 @@ int pdfi_repair_file(pdf_context *ctx) code = pdfi_dict_get_int(ctx, d, "N", &N); if (code == 0) { for (j=0;j < N; j++) { - code = pdfi_read_token(ctx, compressed_stream, 0, 0); - if (code == 0) + code = pdfi_read_bare_int(ctx, compressed_stream, &obj_num); + 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) { - o = ctx->stack_top[-1]; - if (((pdf_obj *)o)->type == PDF_INT) { - offset = ((pdf_num *)o)->value.i; - if (obj_num < 1) { - pdfi_close_file(ctx, compressed_stream); - pdfi_clearstack(ctx); - code = gs_note_error(gs_error_rangecheck); - goto exit; - } - 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; - } - } + else { + code = pdfi_read_bare_int(ctx, compressed_stream, &offset); + if (code > 0) { + if (obj_num < 1) { + pdfi_close_file(ctx, compressed_stream); + pdfi_countdown(n); + pdfi_clearstack(ctx); + code = gs_note_error(gs_error_rangecheck); + goto exit; + } + 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; } } } @@ -461,6 +453,7 @@ int pdfi_repair_file(pdf_context *ctx) } if (code < 0) { if (ctx->args.pdfstoponerror || code == gs_error_VMerror) { + pdfi_countdown(n); pdfi_clearstack(ctx); goto exit; } @@ -477,6 +470,8 @@ int pdfi_repair_file(pdf_context *ctx) } exit: + if (code > 0) + code = 0; pdfi_seek(ctx, ctx->main_stream, saved_offset, SEEK_SET); ctx->main_stream->eof = false; return code; diff --git a/pdf/pdf_sec.c b/pdf/pdf_sec.c index fa7131f8..999fa713 100644 --- a/pdf/pdf_sec.c +++ b/pdf/pdf_sec.c @@ -993,6 +993,7 @@ static int pdfi_read_Encrypt_dict(pdf_context *ctx, int *KeyLen) pdf_dict *CF_dict = NULL, *StdCF_dict = NULL; pdf_dict *d = NULL, *d1 = NULL; pdf_obj *o = NULL; + bool b; pdf_string *s = NULL; int64_t i64; double f; @@ -1039,30 +1040,40 @@ static int pdfi_read_Encrypt_dict(pdf_context *ctx, int *KeyLen) pdfi_countdown(o); o = NULL; - code = pdfi_dict_knownget_number(ctx, d, "Length", &f); + *KeyLen = 0; + ctx->encryption.V = -1; + + code = pdfi_dict_get_int(ctx, d, "R", &i64); if (code < 0) goto done; + ctx->encryption.R = (int)i64; - if (code > 0) - *KeyLen = (int)f; - else - *KeyLen = 0; - - code = pdfi_dict_get_int(ctx, d, "V", &i64); + /* V is required for PDF 2.0 but only strongly recommended for earlier versions */ + code = pdfi_dict_known(ctx, d, "V", &b); if (code < 0) goto done; - if (i64 < 1 || i64 > 5) { - code = gs_error_rangecheck; - goto done; - } + if (b) { + code = pdfi_dict_get_int(ctx, d, "V", &i64); + if (code < 0) + goto done; - ctx->encryption.V = (int)i64; + if (i64 < 1 || i64 > 5) { + code = gs_error_rangecheck; + goto done; + } - code = pdfi_dict_get_int(ctx, d, "R", &i64); - if (code < 0) - goto done; - ctx->encryption.R = (int)i64; + ctx->encryption.V = (int)i64; + + if (ctx->encryption.V == 2 || ctx->encryption.V == 3) { + code = pdfi_dict_knownget_number(ctx, d, "Length", &f); + if (code < 0) + goto done; + + if (code > 0) + *KeyLen = (int)f; + } + } code = pdfi_dict_get_int(ctx, d, "P", &i64); if (code < 0) @@ -1109,12 +1120,12 @@ static int pdfi_read_Encrypt_dict(pdf_context *ctx, int *KeyLen) pdfi_countdown(s); s = NULL; - code = pdfi_dict_knownget_type(ctx, d, "EncryptMetadata", PDF_BOOL, &o); + code = pdfi_dict_knownget_bool(ctx, d, "EncryptMetadata", &b); if (code < 0) goto done; if (code > 0) { - ctx->encryption.EncryptMetadata = ((pdf_bool *)o)->value; - pdfi_countdown(o); + ctx->encryption.EncryptMetadata = b; + code = 0; } else ctx->encryption.EncryptMetadata = true; @@ -1368,13 +1379,19 @@ int pdfi_initialise_Decryption(pdf_context *ctx) switch(ctx->encryption.R) { case 2: /* Set up the defaults if not already set */ + /* R of 2 means V < 2 which is either algorithm 3.1 with a 40-bit key + * or an undocumented and unsupported algorithm. + */ + if (ctx->encryption.V >= 0) { + if (ctx->encryption.V == 0) { + code = gs_note_error(gs_error_undefined); + goto done; + } + } /* Revision 2 is always 40-bit RC4 */ - if (KeyLen != 0 && (KeyLen < 40 || KeyLen > 128 || KeyLen % 8 != 0)) { + if (KeyLen != 0 && KeyLen != 40) 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; + KeyLen = 40; if (ctx->encryption.StmF == CRYPT_NONE) ctx->encryption.StmF = CRYPT_V1; if (ctx->encryption.StrF == CRYPT_NONE) @@ -1383,10 +1400,17 @@ int pdfi_initialise_Decryption(pdf_context *ctx) break; case 3: /* Set up the defaults if not already set */ - /* Revision 3 is always 128-bit RC4 */ - if (KeyLen != 0 && KeyLen != 128) + if (ctx->encryption.V >= 0) { + if (ctx->encryption.V == 3) { + code = gs_note_error(gs_error_undefined); + goto done; + } + } + /* Revision 3 *may* be more than 40 bits of RC4 */ + if (KeyLen != 0 && (KeyLen < 40 || KeyLen > 128 || KeyLen % 8 != 0)) { pdfi_set_warning(ctx, 0, NULL, W_PDF_INVALID_DECRYPT_LEN, "pdfi_initialise_Decryption", NULL); - KeyLen = 128; + KeyLen = 128; + } if (ctx->encryption.StmF == CRYPT_NONE) ctx->encryption.StmF = CRYPT_V2; if (ctx->encryption.StrF == CRYPT_NONE) diff --git a/pdf/pdf_shading.c b/pdf/pdf_shading.c index c4e93e88..5cd347f7 100644 --- a/pdf/pdf_shading.c +++ b/pdf/pdf_shading.c @@ -55,13 +55,13 @@ static int pdfi_build_shading_function(pdf_context *ctx, gs_function_t **ppfn, c if (code < 0) goto build_shading_function_error; - if (o->type != PDF_DICT && o->type != PDF_STREAM) { + if (pdfi_type_of(o) != PDF_DICT && pdfi_type_of(o) != PDF_STREAM) { uint size; pdf_obj *rsubfn; gs_function_t **Functions; int64_t i; - if (o->type != PDF_ARRAY) { + if (pdfi_type_of(o) != PDF_ARRAY) { code = gs_error_typecheck; goto build_shading_function_error; } @@ -78,7 +78,7 @@ static int pdfi_build_shading_function(pdf_context *ctx, gs_function_t **ppfn, c for (i = 0; i < size; ++i) { code = pdfi_array_get(ctx, (pdf_array *)o, i, &rsubfn); if (code == 0) { - if (rsubfn->type != PDF_DICT && rsubfn->type != PDF_STREAM) + if (pdfi_type_of(rsubfn) != PDF_DICT && pdfi_type_of(rsubfn) != PDF_STREAM) code = gs_note_error(gs_error_typecheck); } if (code < 0) { @@ -133,7 +133,7 @@ static int pdfi_shading1(pdf_context *ctx, gs_shading_params_t *pcommon, static const float default_Domain[4] = {0, 1, 0, 1}; pdf_dict *shading_dict; - if (Shading->type != PDF_DICT) + if (pdfi_type_of(Shading) != PDF_DICT) return_error(gs_error_typecheck); shading_dict = (pdf_dict *)Shading; @@ -179,7 +179,7 @@ static int pdfi_shading2(pdf_context *ctx, gs_shading_params_t *pcommon, int code, i; pdf_dict *shading_dict; - if (Shading->type != PDF_DICT) + if (pdfi_type_of(Shading) != PDF_DICT) return_error(gs_error_typecheck); shading_dict = (pdf_dict *)Shading; @@ -231,7 +231,7 @@ static int pdfi_shading3(pdf_context *ctx, gs_shading_params_t *pcommon, int code, i; pdf_dict *shading_dict; - if (Shading->type != PDF_DICT) + if (pdfi_type_of(Shading) != PDF_DICT) return_error(gs_error_typecheck); shading_dict = (pdf_dict *)Shading; @@ -285,7 +285,7 @@ static int pdfi_build_mesh_shading(pdf_context *ctx, gs_shading_mesh_params_t *p int64_t i; pdf_dict *shading_dict; - if (Shading->type != PDF_STREAM) + if (pdfi_type_of(Shading) != PDF_STREAM) return_error(gs_error_typecheck); code = pdfi_dict_from_obj(ctx, Shading, &shading_dict); @@ -634,6 +634,7 @@ static int get_shading_common(pdf_context *ctx, pdf_dict *shading_dict, gs_shadi get_shading_common_error: pdfi_countdown((pdf_obj *)a); gs_free_object(ctx->memory, params->Background, "Background (common_shading_error)"); + params->Background = NULL; return code; } @@ -861,17 +862,27 @@ int pdfi_shading(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) if (ctx->text.BlockDepth != 0) pdfi_set_warning(ctx, 0, NULL, W_PDF_OPINVALIDINTEXT, "pdfi_shading", NULL); - if (pdfi_oc_is_off(ctx)) + if (pdfi_oc_is_off(ctx)) { + pdfi_pop(ctx, 1); return 0; + } + + savedoffset = pdfi_tell(ctx->main_stream); n = (pdf_name *)ctx->stack_top[-1]; - if (n->type != PDF_NAME) - return_error(gs_error_typecheck); + pdfi_countup(n); + pdfi_pop(ctx, 1); + + if (pdfi_type_of(n) != PDF_NAME) { + code = gs_note_error(gs_error_typecheck); + goto exit1; + } - savedoffset = pdfi_tell(ctx->main_stream); code = pdfi_loop_detector_mark(ctx); - if (code < 0) + if (code < 0) { + pdfi_countdown(n); return code; + } code = pdfi_op_q(ctx); if (code < 0) @@ -882,7 +893,7 @@ int pdfi_shading(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) if (code < 0) goto exit2; - if (Shading->type != PDF_DICT && Shading->type != PDF_STREAM) { + if (pdfi_type_of(Shading) != PDF_DICT && pdfi_type_of(Shading) != PDF_STREAM) { code = gs_note_error(gs_error_typecheck); goto exit2; } @@ -929,7 +940,7 @@ int pdfi_shading(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) if (code == 0) code = code1; exit1: - pdfi_pop(ctx, 1); + pdfi_countdown(n); (void)pdfi_loop_detector_cleartomark(ctx); pdfi_seek(ctx, ctx->main_stream, savedoffset, SEEK_SET); return code; diff --git a/pdf/pdf_stack.c b/pdf/pdf_stack.c index d2644fbb..12dae267 100644 --- a/pdf/pdf_stack.c +++ b/pdf/pdf_stack.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 @@ -49,7 +49,7 @@ int pdfi_push(pdf_context *ctx, pdf_obj *o) if (ctx->stack_top >= ctx->stack_limit) { if (ctx->stack_size >= MAX_STACK_SIZE) - return_error(gs_error_stackoverflow); + return_error(gs_error_pdf_stackoverflow); new_stack = (pdf_obj **)gs_alloc_bytes(ctx->memory, (ctx->stack_size + INITIAL_STACK_SIZE) * sizeof (pdf_obj *), "pdfi_push_increase_interp_stack"); if (new_stack == NULL) @@ -106,7 +106,7 @@ int pdfi_count_to_mark(pdf_context *ctx, uint64_t *count) *count = 0; while (&ctx->stack_top[index] >= save_bot) { - if (o->type == PDF_ARRAY_MARK || o->type == PDF_DICT_MARK) + if (pdfi_type_of(o) == PDF_ARRAY_MARK || pdfi_type_of(o) == PDF_DICT_MARK) return 0; o = ctx->stack_top[--index]; (*count)++; @@ -124,3 +124,105 @@ int pdfi_clear_to_mark(pdf_context *ctx) return code; return pdfi_pop(ctx, count + 1); } + +int +pdfi_destack_real(pdf_context *ctx, double *d) +{ + int code; + + if (pdfi_count_stack(ctx) < 1) + return_error(gs_error_stackunderflow); + + code = pdfi_obj_to_real(ctx, ctx->stack_top[-1], d); + if (code < 0) { + pdfi_clearstack(ctx); + return code; + } + pdfi_pop(ctx, 1); + + return 0; +} + +int +pdfi_destack_reals(pdf_context *ctx, double *d, int n) +{ + int i, code; + + if (pdfi_count_stack(ctx) < n) { + pdfi_clearstack(ctx); + return_error(gs_error_stackunderflow); + } + + for (i = 0; i < n; i++) { + code = pdfi_obj_to_real(ctx, ctx->stack_top[i-n], &d[i]); + if (code < 0) { + pdfi_clearstack(ctx); + return code; + } + } + pdfi_pop(ctx, n); + + return 0; +} + +int +pdfi_destack_floats(pdf_context *ctx, float *d, int n) +{ + int i, code; + + if (pdfi_count_stack(ctx) < n) { + pdfi_clearstack(ctx); + return_error(gs_error_stackunderflow); + } + + for (i = 0; i < n; i++) { + code = pdfi_obj_to_float(ctx, ctx->stack_top[i-n], &d[i]); + if (code < 0) { + pdfi_clearstack(ctx); + return code; + } + } + pdfi_pop(ctx, n); + + return 0; +} + +int +pdfi_destack_int(pdf_context *ctx, int64_t *i) +{ + int code; + + if (pdfi_count_stack(ctx) < 1) + return_error(gs_error_stackunderflow); + + code = pdfi_obj_to_int(ctx, ctx->stack_top[-1], i); + if (code < 0) { + pdfi_clearstack(ctx); + return code; + } + pdfi_pop(ctx, 1); + + return 0; +} + +int +pdfi_destack_ints(pdf_context *ctx, int64_t *i64, int n) +{ + int i, code; + + if (pdfi_count_stack(ctx) < n) { + pdfi_clearstack(ctx); + return_error(gs_error_stackunderflow); + } + + for (i = 0; i < n; i++) { + code = pdfi_obj_to_int(ctx, ctx->stack_top[i-n], &i64[i]); + if (code < 0) { + pdfi_clearstack(ctx); + return code; + } + } + pdfi_pop(ctx, n); + + return 0; +} diff --git a/pdf/pdf_stack.h b/pdf/pdf_stack.h index 982cfab1..2f7d8640 100644 --- a/pdf/pdf_stack.h +++ b/pdf/pdf_stack.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 @@ -29,60 +29,75 @@ int pdfi_mark_stack(pdf_context *ctx, pdf_obj_type type); void pdfi_clearstack(pdf_context *ctx); int pdfi_count_to_mark(pdf_context *ctx, uint64_t *count); int pdfi_clear_to_mark(pdf_context *ctx); +int pdfi_destack_real(pdf_context *ctx, double *d); +int pdfi_destack_reals(pdf_context *ctx, double *d, int n); +int pdfi_destack_floats(pdf_context *ctx, float *d, int n); +int pdfi_destack_int(pdf_context *ctx, int64_t *i); +int pdfi_destack_ints(pdf_context *ctx, int64_t *i, int n); static inline void pdfi_countup_impl(pdf_obj *o) { - if (o != NULL) { - o->refcnt++; + if ((uintptr_t)o < TOKEN__LAST_KEY) + { #if REFCNT_DEBUG - dmprintf3(OBJ_MEMORY(o), "Incrementing reference count of object %d, UID %lu, to %d\n", o->object_num, o->UID, o->refcnt); + if (o == NULL) + dprintf("Incrementing reference count of NULL pointer\n"); #endif + return; } + o->refcnt++; #if REFCNT_DEBUG - else { - dprintf("Incrementing reference count of NULL pointer\n"); - } + dmprintf3(OBJ_MEMORY(o), "Incrementing reference count of object %d, UID %lu, to %d\n", o->object_num, o->UID, o->refcnt); #endif } static inline void pdfi_countdown_impl(pdf_obj *o) { - if (o != NULL) { +#if defined(DEBUG) || REFCNT_DEBUG + pdf_context *ctx; +#endif + + /* A 'low' pointer value indicates a type that is not an + * actual object (typically keyword). This includes the + * NULL case. Nothing to free in that case. */ + if ((uintptr_t)o < TOKEN__LAST_KEY) + return; + +#if defined(DEBUG) || REFCNT_DEBUG + ctx = (pdf_context *)o->ctx; +#endif #ifdef DEBUG - pdf_context *ctx1 = (pdf_context *)o->ctx; - if (o->refcnt == 0) - emprintf(OBJ_MEMORY(o), "Decrementing object with refcount at 0!\n"); + if (o->refcnt == 0) + emprintf(OBJ_MEMORY(o), "Decrementing object with refcount at 0!\n"); #endif - o->refcnt--; + o->refcnt--; #if REFCNT_DEBUG - dmprintf3(OBJ_MEMORY(o), "Decrementing reference count of object %d, UID %lu, to %d\n", o->object_num, o->UID, o->refcnt); + dmprintf3(OBJ_MEMORY(o), "Decrementing reference count of object %d, UID %lu, to %d\n", o->object_num, o->UID, o->refcnt); #endif - if (o->refcnt == 0) { + if (o->refcnt != 0) + return; #if REFCNT_DEBUG - pdf_context *ctx = (pdf_context *)o->ctx; - if (ctx != NULL && ctx->cache_entries != 0) { - pdf_obj_cache_entry *entry = ctx->cache_LRU, *next; - - while(entry) { - next = entry->next; - if (entry->o->object_num != 0 && entry->o->object_num == o->object_num) - dmprintf2(OBJ_MEMORY(o), "Freeing object %d, UID %lu, but there is still a cache entry!\n", o->object_num, o->UID); - entry = next; - } - } - dmprintf2(OBJ_MEMORY(o), "Freeing object %d, UID %lu\n", o->object_num, o->UID); + if (ctx != NULL && ctx->cache_entries != 0) { + pdf_obj_cache_entry *entry = ctx->cache_LRU, *next; + + while(entry) { + next = entry->next; + if (entry->o->object_num != 0 && entry->o->object_num == o->object_num) + dmprintf2(OBJ_MEMORY(o), "Freeing object %d, UID %lu, but there is still a cache entry!\n", o->object_num, o->UID); + entry = next; + } + } + dmprintf2(OBJ_MEMORY(o), "Freeing object %d, UID %lu\n", o->object_num, o->UID); #endif #ifdef DEBUG - if (ctx1->xref_table != NULL && o->object_num > 0 && - o->object_num < ctx1->xref_table->xref_size && - ctx1->xref_table->xref[o->object_num].cache != NULL && - ctx1->xref_table->xref[o->object_num].cache->o == o) { - dmprintf1(OBJ_MEMORY(o), "Freeing object %d while it is still in the object cache!\n", o->object_num); - } -#endif - pdfi_free_object(o); - } + if (ctx->xref_table != NULL && o->object_num > 0 && + o->object_num < ctx->xref_table->xref_size && + ctx->xref_table->xref[o->object_num].cache != NULL && + ctx->xref_table->xref[o->object_num].cache->o == o) { + dmprintf1(OBJ_MEMORY(o), "Freeing object %d while it is still in the object cache!\n", o->object_num); } +#endif + pdfi_free_object(o); } /* These two macros are present simply to add a cast to the generic object type, so that diff --git a/pdf/pdf_text.c b/pdf/pdf_text.c index e22f0c0e..dfc35201 100644 --- a/pdf/pdf_text.c +++ b/pdf/pdf_text.c @@ -188,65 +188,30 @@ static int pdfi_set_Tc(pdf_context *ctx, double Tc) int pdfi_Tc(pdf_context *ctx) { - int code = 0; - pdf_num *n = NULL; - - if (pdfi_count_stack(ctx) < 1) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } + int code; + double d; - n = (pdf_num *)ctx->stack_top[-1]; + code = pdfi_destack_real(ctx, &d); + if (code < 0) + return code; - if (n->type == PDF_INT) - code = pdfi_set_Tc(ctx, (double)n->value.i); - else { - if (n->type == PDF_REAL) - code = pdfi_set_Tc(ctx, n->value.d); - else - code = gs_note_error(gs_error_typecheck); - } - pdfi_pop(ctx, 1); - return code; + return pdfi_set_Tc(ctx, d); } int pdfi_Td(pdf_context *ctx) { int code; - pdf_num *Tx = NULL, *Ty = NULL; + double Txy[2]; gs_matrix m, mat; - if (pdfi_count_stack(ctx) < 2) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } + code = pdfi_destack_reals(ctx, Txy, 2); + if (code < 0) + return code; gs_make_identity(&m); - Ty = (pdf_num *)ctx->stack_top[-1]; - Tx = (pdf_num *)ctx->stack_top[-2]; - - if (Tx->type == PDF_INT) { - m.tx = (float)Tx->value.i; - } else { - if (Tx->type == PDF_REAL) { - m.tx = (float)Tx->value.d; - } else { - code = gs_note_error(gs_error_typecheck); - goto Td_error; - } - } - - if (Ty->type == PDF_INT) { - m.ty = (float)Ty->value.i; - } else { - if (Ty->type == PDF_REAL) { - m.ty = (float)Ty->value.d; - } else { - code = gs_note_error(gs_error_typecheck); - goto Td_error; - } - } + m.tx = Txy[0]; + m.ty = Txy[1]; if (ctx->text.BlockDepth == 0) { pdfi_set_warning(ctx, 0, NULL, W_PDF_TEXTOPNOBT, "pdfi_Td", NULL); @@ -254,70 +219,38 @@ int pdfi_Td(pdf_context *ctx) gs_make_identity(&mat); code = gs_settextmatrix(ctx->pgs, &mat); if (code < 0) - goto Td_error; + return code; code = gs_settextlinematrix(ctx->pgs, &mat); if (code < 0) - goto Td_error; + return code; } code = gs_matrix_multiply(&m, &ctx->pgs->textlinematrix, &mat); if (code < 0) - goto Td_error; + return code; code = gs_settextmatrix(ctx->pgs, (gs_matrix *)&mat); if (code < 0) - goto Td_error; - - code = gs_settextlinematrix(ctx->pgs, (gs_matrix *)&mat); - if (code < 0) - goto Td_error; - - pdfi_pop(ctx, 2); - return code; + return code; -Td_error: - pdfi_pop(ctx, 2); - return code; + return gs_settextlinematrix(ctx->pgs, (gs_matrix *)&mat); } int pdfi_TD(pdf_context *ctx) { int code; - pdf_num *Tx = NULL, *Ty = NULL; + double Txy[2]; gs_matrix m, mat; - if (pdfi_count_stack(ctx) < 2) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } - gs_make_identity(&m); - Ty = (pdf_num *)ctx->stack_top[-1]; - Tx = (pdf_num *)ctx->stack_top[-2]; - - if (Tx->type == PDF_INT) { - m.tx = (float)Tx->value.i; - } else { - if (Tx->type == PDF_REAL) { - m.tx = (float)Tx->value.d; - } else { - code = gs_note_error(gs_error_typecheck); - goto TD_error; - } - } + code = pdfi_destack_reals(ctx, Txy, 2); + if (code < 0) + return code; - if (Ty->type == PDF_INT) { - m.ty = (float)Ty->value.i; - } else { - if (Ty->type == PDF_REAL) { - m.ty = (float)Ty->value.d; - } else { - code = gs_note_error(gs_error_typecheck); - goto TD_error; - } - } + m.tx = Txy[0]; + m.ty = Txy[1]; if (ctx->text.BlockDepth == 0) { pdfi_set_warning(ctx, 0, NULL, W_PDF_TEXTOPNOBT, "pdfi_TD", NULL); @@ -325,35 +258,26 @@ int pdfi_TD(pdf_context *ctx) gs_make_identity(&mat); code = gs_settextmatrix(ctx->pgs, &mat); if (code < 0) - goto TD_error; + return code; code = gs_settextlinematrix(ctx->pgs, &mat); if (code < 0) - goto TD_error; + return code; } code = pdfi_set_TL(ctx, m.ty * 1.0f); if (code < 0) - goto TD_error; + return code; code = gs_matrix_multiply(&m, &ctx->pgs->textlinematrix, &mat); if (code < 0) - goto TD_error; + return code; code = gs_settextmatrix(ctx->pgs, (gs_matrix *)&mat); if (code < 0) - goto TD_error; - - code = gs_settextlinematrix(ctx->pgs, (gs_matrix *)&mat); - if (code < 0) - goto TD_error; - - pdfi_pop(ctx, 2); - return code; + return code; -TD_error: - pdfi_pop(ctx, 2); - return code; + return gs_settextlinematrix(ctx->pgs, (gs_matrix *)&mat); } /* This routine sets up most of the text params structure. In particular it @@ -436,7 +360,7 @@ static int pdfi_show_set_params(pdf_context *ctx, pdf_string *s, gs_text_params_ for (i = 0;i < s->length; i++) { /* Get the width (in unscaled text units) */ if (s->data[i] < current_font->FirstChar || s->data[i] > current_font->LastChar) - width = 0; + width = current_font->MissingWidth; else width = current_font->Widths[s->data[i] - current_font->FirstChar]; /* And convert the width into an appropriate value for the current environment */ @@ -1083,8 +1007,10 @@ int pdfi_Tj(pdf_context *ctx) goto exit; s = (pdf_string *)ctx->stack_top[-1]; - if (s->type != PDF_STRING) + if (pdfi_type_of(s) != PDF_STRING) { + pdfi_pop(ctx, 1); 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 @@ -1198,7 +1124,7 @@ int pdfi_TJ(pdf_context *ctx) goto exit; a = (pdf_array *)ctx->stack_top[-1]; - if (a->type != PDF_ARRAY) { + if (pdfi_type_of(a) != PDF_ARRAY) { pdfi_pop(ctx, 1); return gs_note_error(gs_error_typecheck); } @@ -1260,29 +1186,17 @@ int pdfi_TJ(pdf_context *ctx) if (code < 0) goto TJ_error; - if (o->type == PDF_INT) { - dx = (double)((pdf_num *)o)->value.i / -1000; + if (pdfi_type_of(o) == PDF_STRING) + code = pdfi_show(ctx, (pdf_string *)o); + else { + code = pdfi_obj_to_real(ctx, o, &dx); + if (code < 0) + goto TJ_error; + dx /= -1000; if (current_font->pfont && current_font->pfont->WMode == 0) code = gs_rmoveto(ctx->pgs, dx, 0); else code = gs_rmoveto(ctx->pgs, 0, dx); - if (code < 0) - goto TJ_error; - } else { - if (o->type == PDF_REAL) { - dx = ((pdf_num *)o)->value.d / -1000; - if (current_font->pfont && current_font->pfont->WMode == 0) - code = gs_rmoveto(ctx->pgs, dx, 0); - else - code = gs_rmoveto(ctx->pgs, 0, dx); - if (code < 0) - goto TJ_error; - } else { - if (o->type == PDF_STRING) - code = pdfi_show(ctx, (pdf_string *)o); - else - code = gs_note_error(gs_error_typecheck); - } } pdfi_countdown(o); o = NULL; @@ -1326,53 +1240,25 @@ static int pdfi_set_TL(pdf_context *ctx, double TL) int pdfi_TL(pdf_context *ctx) { - int code = 0; - pdf_num *n = NULL; - - if (pdfi_count_stack(ctx) < 1) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } + int code; + double d; - n = (pdf_num *)ctx->stack_top[-1]; + code = pdfi_destack_real(ctx, &d); + if (code < 0) + return code; - if (n->type == PDF_INT) - code = pdfi_set_TL(ctx, (double)(n->value.i * -1)); - else { - if (n->type == PDF_REAL) - code = pdfi_set_TL(ctx, n->value.d * -1.0); - else - code = gs_note_error(gs_error_typecheck); - } - pdfi_pop(ctx, 1); - return code; + return pdfi_set_TL(ctx, -d); } int pdfi_Tm(pdf_context *ctx) { - int code = 0, i; + int code; float m[6]; - pdf_num *n = NULL; gs_matrix mat; - if (pdfi_count_stack(ctx) < 6) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } - for (i = 1;i < 7;i++) { - n = (pdf_num *)ctx->stack_top[-1 * i]; - if (n->type == PDF_INT) - m[6 - i] = (float)n->value.i; - else { - if (n->type == PDF_REAL) - m[6 - i] = (float)n->value.d; - else { - pdfi_pop(ctx, 6); - return_error(gs_error_typecheck); - } - } - } - pdfi_pop(ctx, 6); + code = pdfi_destack_floats(ctx, m, 6); + if (code < 0) + return code; if (ctx->text.BlockDepth == 0) { pdfi_set_warning(ctx, 0, NULL, W_PDF_TEXTOPNOBT, "pdfi_Tm", NULL); @@ -1394,91 +1280,72 @@ int pdfi_Tm(pdf_context *ctx) if (code < 0) return code; - code = gs_settextlinematrix(ctx->pgs, (gs_matrix *)&m); - - return code; + return gs_settextlinematrix(ctx->pgs, (gs_matrix *)&m); } int pdfi_Tr(pdf_context *ctx) { - int code = 0, mode = 0; - pdf_num *n = NULL; - - if (pdfi_count_stack(ctx) < 1) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } - - n = (pdf_num *)ctx->stack_top[-1]; + int code; + int64_t mode; - if (n->type == PDF_INT) - mode = n->value.i; - else { - if (n->type == PDF_REAL) - mode = (int)n->value.d; - else { - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); - } - } - pdfi_pop(ctx, 1); + code = pdfi_destack_int(ctx, &mode); + if (code < 0) + return code; if (mode < 0 || mode > 7) - code = gs_note_error(gs_error_rangecheck); - else { + return_error(gs_error_rangecheck); + /* See comment regarding text rendering modes involving clip in pdfi_BT() above. * The commented out code here will be needed when we enhance pdfwrite so that * we don't need to do the clip separately. */ -/* if (!ctx->device_state.preserve_tr_mode) {*/ - gs_point initial_point; +/* + if (ctx->device_state.preserve_tr_mode) { + gs_settextrenderingmode(ctx->pgs, mode); + } else +*/ + { + gs_point initial_point; - /* Detect attempts to switch from a clipping mode to a non-clipping - * mode, this is defined as invalid in the spec. + /* Detect attempts to switch from a clipping mode to a non-clipping + * mode, this is defined as invalid in the spec. + */ + if (gs_currenttextrenderingmode(ctx->pgs) > 3 && mode < 4 && ctx->text.BlockDepth != 0) + pdfi_set_warning(ctx, 0, NULL, W_PDF_BADTRSWITCH, "pdfi_Tr", NULL); + + if (gs_currenttextrenderingmode(ctx->pgs) < 4 && mode >= 4 && ctx->text.BlockDepth != 0) { + /* If we are switching from a non-clip text rendering mode to a + * mode involving a cip, and we are already inside a text block, + * put a gsave in place so that we can accumulate a path for + * clipping without disturbing any existing path in the + * graphics state. */ - if (gs_currenttextrenderingmode(ctx->pgs) > 3 && mode < 4 && ctx->text.BlockDepth != 0) - pdfi_set_warning(ctx, 0, NULL, W_PDF_BADTRSWITCH, "pdfi_Tr", NULL); - - if (gs_currenttextrenderingmode(ctx->pgs) < 4 && mode >= 4 && ctx->text.BlockDepth != 0) { - /* If we are switching from a non-clip text rendering mode to a - * mode involving a cip, and we are already inside a text block, - * put a gsave in place so that we can accumulate a path for - * clipping without disturbing any existing path in the - * graphics state. - */ - gs_settextrenderingmode(ctx->pgs, mode); - pdfi_gsave(ctx); - /* Capture the current position */ - code = gs_currentpoint(ctx->pgs, &initial_point); - /* Start a new path (so our clip doesn't include any - * already extant path in the graphics state) - */ - gs_newpath(ctx->pgs); - gs_moveto(ctx->pgs, initial_point.x, initial_point.y); - } - else { - if (gs_currenttextrenderingmode(ctx->pgs) >= 4 && mode < 4 && ctx->text.BlockDepth != 0) { - /* If we are switching from a clipping mode to a non-clipping - * mode then behave as if we had an implicit ET to flush the - * accumulated text to a clip, then set the text rendering mode - * to the non-clip mode, and perform an implicit BT. - */ - code = pdfi_ET(ctx); - if (code < 0) - return code; - gs_settextrenderingmode(ctx->pgs, mode); - code = pdfi_BT(ctx); - if (code < 0) - return code; - } - else - gs_settextrenderingmode(ctx->pgs, mode); - } -/* } + gs_settextrenderingmode(ctx->pgs, mode); + pdfi_gsave(ctx); + /* Capture the current position */ + code = gs_currentpoint(ctx->pgs, &initial_point); + /* Start a new path (so our clip doesn't include any + * already extant path in the graphics state) + */ + gs_newpath(ctx->pgs); + gs_moveto(ctx->pgs, initial_point.x, initial_point.y); + } else if (gs_currenttextrenderingmode(ctx->pgs) >= 4 && mode < 4 && ctx->text.BlockDepth != 0) { + /* If we are switching from a clipping mode to a non-clipping + * mode then behave as if we had an implicit ET to flush the + * accumulated text to a clip, then set the text rendering mode + * to the non-clip mode, and perform an implicit BT. + */ + code = pdfi_ET(ctx); + if (code < 0) + return code; + gs_settextrenderingmode(ctx->pgs, mode); + code = pdfi_BT(ctx); + if (code < 0) + return code; + } else - gs_settextrenderingmode(ctx->pgs, mode);*/ + gs_settextrenderingmode(ctx->pgs, mode); } - return code; } @@ -1489,26 +1356,14 @@ static int pdfi_set_Ts(pdf_context *ctx, double Ts) int pdfi_Ts(pdf_context *ctx) { - int code = 0; - pdf_num *n = NULL; - - if (pdfi_count_stack(ctx) < 1) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } + int code; + double d; - n = (pdf_num *)ctx->stack_top[-1]; + code = pdfi_destack_real(ctx, &d); + if (code < 0) + return code; - if (n->type == PDF_INT) - code = pdfi_set_Ts(ctx, (double)n->value.i); - else { - if (n->type == PDF_REAL) - code = pdfi_set_Ts(ctx, n->value.d); - else - code = gs_note_error(gs_error_typecheck); - } - pdfi_pop(ctx, 1); - return code; + return pdfi_set_Ts(ctx, d); } static int pdfi_set_Tw(pdf_context *ctx, double Tw) @@ -1518,50 +1373,26 @@ static int pdfi_set_Tw(pdf_context *ctx, double Tw) int pdfi_Tw(pdf_context *ctx) { - int code = 0; - pdf_num *n = NULL; - - if (pdfi_count_stack(ctx) < 1) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } + int code; + double d; - n = (pdf_num *)ctx->stack_top[-1]; + code = pdfi_destack_real(ctx, &d); + if (code < 0) + return code; - if (n->type == PDF_INT) - code = pdfi_set_Tw(ctx, (double)n->value.i); - else { - if (n->type == PDF_REAL) - code = pdfi_set_Tw(ctx, n->value.d); - else - code = gs_note_error(gs_error_typecheck); - } - pdfi_pop(ctx, 1); - return code; + return pdfi_set_Tw(ctx, d); } int pdfi_Tz(pdf_context *ctx) { - int code = 0; - pdf_num *n = NULL; - - if (pdfi_count_stack(ctx) < 1) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } + int code; + double d; - n = (pdf_num *)ctx->stack_top[-1]; + code = pdfi_destack_real(ctx, &d); + if (code < 0) + return code; - if (n->type == PDF_INT) - code = gs_settexthscaling(ctx->pgs, (double)n->value.i); - else { - if (n->type == PDF_REAL) - code = gs_settexthscaling(ctx->pgs, n->value.d); - else - code = gs_note_error(gs_error_typecheck); - } - pdfi_pop(ctx, 1); - return code; + return gs_settexthscaling(ctx->pgs, d); } int pdfi_singlequote(pdf_context *ctx) @@ -1572,11 +1403,6 @@ int pdfi_singlequote(pdf_context *ctx) pdfi_set_warning(ctx, 0, NULL, W_PDF_TEXTOPNOBT, "pdfi_singlequote", NULL); } - if (pdfi_count_stack(ctx) < 1) { - pdfi_clearstack(ctx); - return_error(gs_error_stackunderflow); - } - code = pdfi_T_star(ctx); if (code < 0) return code; @@ -1587,8 +1413,7 @@ int pdfi_singlequote(pdf_context *ctx) int pdfi_doublequote(pdf_context *ctx) { int code; - pdf_string *s; - pdf_num *Tw, *Tc; + double Tw, Tc; if (ctx->text.BlockDepth == 0) { pdfi_set_warning(ctx, 0, NULL, W_PDF_TEXTOPNOBT, "pdfi_T_doublequote", NULL); @@ -1599,38 +1424,35 @@ int pdfi_doublequote(pdf_context *ctx) return_error(gs_error_stackunderflow); } - s = (pdf_string *)ctx->stack_top[-1]; - Tc = (pdf_num *)ctx->stack_top[-2]; - Tw = (pdf_num *)ctx->stack_top[-3]; - if (s->type != PDF_STRING || (Tc->type != PDF_INT && Tc->type != PDF_REAL) || - (Tw->type != PDF_INT && Tw->type != PDF_REAL)) { + if (pdfi_type_of(ctx->stack_top[-1]) != PDF_STRING) { pdfi_pop(ctx, 3); return gs_note_error(gs_error_typecheck); } - if (Tc->type == PDF_INT) - code = pdfi_set_Tc(ctx, (double)Tc->value.i); - else - code = pdfi_set_Tc(ctx, Tc->value.d); - if (code < 0) { - pdfi_pop(ctx, 3); - return code; - } + code = pdfi_obj_to_real(ctx, ctx->stack_top[-2], &Tc); + if (code < 0) + goto error; + code = pdfi_set_Tc(ctx, Tc); + if (code < 0) + goto error; - if (Tw->type == PDF_INT) - code = pdfi_set_Tw(ctx, (double)Tw->value.i); - else - code = pdfi_set_Tw(ctx, Tw->value.d); - if (code < 0) { - pdfi_pop(ctx, 3); - return code; - } + code = pdfi_obj_to_real(ctx, ctx->stack_top[-3], &Tw); + if (code < 0) + goto error; + code = pdfi_set_Tw(ctx, Tw); + if (code < 0) + goto error; code = pdfi_T_star(ctx); if (code < 0) - return code; + goto error; code = pdfi_Tj(ctx); + /* Tj pops one off the stack for us, leaving us 2 to go. */ + pdfi_pop(ctx, 2); + return code; + +error: pdfi_pop(ctx, 3); return code; } diff --git a/pdf/pdf_tokens.h b/pdf/pdf_tokens.h new file mode 100644 index 00000000..e9fe833d --- /dev/null +++ b/pdf/pdf_tokens.h @@ -0,0 +1,116 @@ +/* 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 PARAM1 +#define PARAMCAT(A,B) A ## B +#define PARAM1(A) PARAMCAT(TOKEN_, A), +#define PARAM2(A,B) PARAMCAT(TOKEN_, B), +#endif + +// Errors +PARAM2("", NOT_A_KEYWORD) +// Insert new error tokens here. +PARAM2("", TOO_LONG) +PARAM2("", INVALID_KEY) +// Real tokens start here, at TOKEN_INVALID_KEY+1 +PARAM2("\"", QUOTE) +PARAM2("'", APOSTROPHE) +PARAM1(B) +PARAM2("B*", Bstar) +PARAM1(BDC) +PARAM1(BI) +PARAM1(BMC) +PARAM1(BT) +PARAM1(BX) +PARAM1(CS) +PARAM1(DP) +PARAM1(Do) +PARAM1(EI) +PARAM1(EMC) +PARAM1(ET) +PARAM1(EX) +PARAM1(F) +PARAM1(G) +PARAM1(ID) +PARAM1(J) +PARAM1(K) +PARAM1(M) +PARAM1(MP) +PARAM1(Q) +PARAM1(R) +PARAM1(RG) +PARAM1(S) +PARAM1(SC) +PARAM1(SCN) +PARAM2("T*", Tstar) +PARAM1(TD) +PARAM1(TJ) +PARAM1(TL) +PARAM1(Tc) +PARAM1(Td) +PARAM1(Tf) +PARAM1(Tj) +PARAM1(Tm) +PARAM1(Tr) +PARAM1(Ts) +PARAM1(Tw) +PARAM1(Tz) +PARAM1(W) +PARAM2("W*", Wstar) +PARAM1(b) +PARAM2("b*", bstar) +PARAM1(c) +PARAM1(cm) +PARAM1(cs) +PARAM1(d) +PARAM1(d0) +PARAM1(d1) +PARAM2("endobj", ENDOBJ) +PARAM2("endstream", ENDSTREAM) +PARAM1(f) +PARAM2("f*", fstar) +PARAM2("false", FALSE) +PARAM1(g) +PARAM1(gs) +PARAM1(h) +PARAM1(i) +PARAM1(j) +PARAM1(k) +PARAM1(l) +PARAM1(m) +PARAM1(n) +PARAM1(null) +PARAM2("obj", OBJ) +PARAM1(q) +PARAM1(r) +PARAM1(re) +PARAM1(rg) +PARAM1(ri) +PARAM1(s) +PARAM1(sc) +PARAM1(scn) +PARAM1(sh) +PARAM2("startxref", STARTXREF) +PARAM2("stream", STREAM) +PARAM2("trailer", TRAILER) +PARAM2("true", TRUE) +PARAM1(v) +PARAM1(w) +PARAM2("xref", XREF) +PARAM1(y) + +#undef PARAM1 +#undef PARAM2 +#undef PARAMCAT diff --git a/pdf/pdf_trans.c b/pdf/pdf_trans.c index df0194d7..fecb74c9 100644 --- a/pdf/pdf_trans.c +++ b/pdf/pdf_trans.c @@ -103,7 +103,7 @@ static int pdfi_trans_set_mask(pdf_context *ctx, pdfi_int_gstate *igs, int color double f; gs_matrix save_matrix, GroupMat, group_Matrix; gs_transparency_mask_subtype_t subtype = TRANSPARENCY_MASK_Luminosity; - pdf_bool *Processed = NULL; + bool Processed, ProcessedKnown = 0; bool save_OverrideICC = gs_currentoverrideicc(ctx->pgs); #if DEBUG_TRANSPARENCY @@ -114,28 +114,26 @@ static int pdfi_trans_set_mask(pdf_context *ctx, pdfi_int_gstate *igs, int color /* Following the logic of the ps code, cram a /Processed key in the SMask dict to * track whether it's already been processed. */ - code = pdfi_dict_knownget_type(ctx, SMask, "Processed", PDF_BOOL, (pdf_obj **)&Processed); - if (code > 0 && Processed->value) { + code = pdfi_dict_knownget_bool(ctx, SMask, "Processed", &Processed); + if (code > 0) { + if (Processed) { #if DEBUG_TRANSPARENCY - dbgmprintf(ctx->memory, "SMask already built, skipping\n"); + dbgmprintf(ctx->memory, "SMask already built, skipping\n"); #endif - goto exit; + code = 0; + goto exit; + } + ProcessedKnown = 1; } gs_setoverrideicc(ctx->pgs, true); /* If /Processed not in the dict, put it there */ if (code == 0) { - /* the cleanup at end of this routine assumes Processed has a ref */ - code = pdfi_object_alloc(ctx, PDF_BOOL, 0, (pdf_obj **)&Processed); - if (code < 0) - goto exit; - Processed->value = false; - /* pdfi_object_alloc() doesn't grab a ref */ - pdfi_countup(Processed); - code = pdfi_dict_put(ctx, SMask, "Processed", (pdf_obj *)Processed); + code = pdfi_dict_put_bool(ctx, SMask, "Processed", false); if (code < 0) goto exit; + ProcessedKnown = 1; } /* See pdf1.7 pg 553 (pain in the butt to find this!) */ @@ -172,21 +170,25 @@ static int pdfi_trans_set_mask(pdf_context *ctx, pdfi_int_gstate *igs, int color /* TR is transfer function (Optional) */ code = pdfi_dict_knownget(ctx, SMask, "TR", (pdf_obj **)&TR); if (code > 0) { - if (TR->type == PDF_DICT || TR->type == PDF_STREAM) { - 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"); - } - } else { - dmprintf(ctx->memory, "WARNING: Ignoring invalid TR in SMask\n"); + switch (pdfi_type_of(TR)) { + case PDF_DICT: + case PDF_STREAM: + 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"); + } + break; + case PDF_NAME: + if (!pdfi_name_is((pdf_name *)TR, "Identity")) { + dmprintf(ctx->memory, "WARNING: Unknown TR in SMask\n"); + } + break; + default: + dmprintf(ctx->memory, "WARNING: Ignoring invalid TR in SMask\n"); } } @@ -251,8 +253,14 @@ static int pdfi_trans_set_mask(pdf_context *ctx, pdfi_int_gstate *igs, int color if (code > 0) { /* TODO: Stuff with colorspace, see .execmaskgroup */ code = pdfi_dict_knownget(ctx, Group, "CS", &CS); - if (code < 0) - goto exit; + if (code < 0) { + code = pdfi_dict_knownget(ctx, Group, "ColorSpace", &CS); + if (code < 0) { + pdfi_set_error(ctx, 0, NULL, E_PDF_GROUP_NO_CS, "pdfi_trans_set_mask", (char *)"*** Defaulting to currrent colour space"); + goto exit; + } + pdfi_set_warning(ctx, 0, NULL, W_PDF_GROUP_HAS_COLORSPACE, "pdfi_trans_set_mask", NULL); + } if (code > 0) { code = pdfi_create_colorspace(ctx, CS, (pdf_dict *)ctx->main_stream, ctx->page.CurrentPageDict, &pcs, false); @@ -284,6 +292,9 @@ static int pdfi_trans_set_mask(pdf_context *ctx, pdfi_int_gstate *igs, int color } params.Background_components = pdfi_array_size(BC); + if (gs_color_space_num_components(params.ColorSpace) != params.Background_components) + pdfi_set_warning(ctx, 0, NULL, W_PDF_GROUP_BAD_BC, "pdfi_trans_set_mask", NULL); + /* TODO: Not sure how to handle this... recheck PS code (pdf_draw.ps/gssmask) */ /* This should be "currentgray" for the color that we put in params.ColorSpace, * It looks super-convoluted to actually get this value. Really? @@ -300,7 +311,7 @@ static int pdfi_trans_set_mask(pdf_context *ctx, pdfi_int_gstate *igs, int color code = pdfi_form_execgroup(ctx, ctx->page.CurrentPageDict, G_stream, igs->GroupGState, NULL, &group_Matrix); code1 = gs_end_transparency_mask(ctx->pgs, colorindex); - if (code != 0) + if (code == 0) code = code1; /* Put back the matrix (we couldn't just rely on gsave/grestore for whatever reason, @@ -309,8 +320,12 @@ static int pdfi_trans_set_mask(pdf_context *ctx, pdfi_int_gstate *igs, int color gs_setmatrix(ctx->pgs, &save_matrix); /* Set Processed flag */ - if (code == 0 && Processed) - Processed->value = true; + if (code == 0 && ProcessedKnown) + { + code = pdfi_dict_put_bool(ctx, SMask, "Processed", true); + if (code < 0) + goto exit; + } } else { /* take action on a non-/Mask entry. What does this mean ? What do we need to do */ dmprintf(ctx->memory, "Warning: Type is not /Mask, entry ignored in pdfi_set_trans_mask\n"); @@ -332,7 +347,6 @@ static int pdfi_trans_set_mask(pdf_context *ctx, pdfi_int_gstate *igs, int color pdfi_countdown(BBox); pdfi_countdown(Matrix); pdfi_countdown(CS); - pdfi_countdown(Processed); #if DEBUG_TRANSPARENCY dbgmprintf(ctx->memory, "pdfi_trans_set_mask (.execmaskgroup) END\n"); #endif @@ -393,7 +407,7 @@ static int pdfi_transparency_group_common(pdf_context *ctx, pdf_dict *page_dict, /* Didn't find a /CS key, try again using /ColorSpace */ code = pdfi_dict_knownget(ctx, group_dict, "ColorSpace", &CS); } - if (code > 0 && CS->type != PDF_NULL) { + if (code > 0 && pdfi_type_of(CS) != PDF_NULL) { code = pdfi_setcolorspace(ctx, CS, group_dict, page_dict); if (code < 0) goto exit; @@ -865,6 +879,7 @@ int pdfi_trans_teardown(pdf_context *ctx, pdfi_trans_state_t *state) int pdfi_trans_set_params(pdf_context *ctx) { + int code = 0; pdfi_int_gstate *igs = (pdfi_int_gstate *)ctx->pgs->client_data; gs_transparency_channel_selector_t csel; @@ -874,9 +889,9 @@ int pdfi_trans_set_params(pdf_context *ctx) else csel = TRANSPARENCY_CHANNEL_Opacity; if (igs->SMask) { - pdfi_trans_set_mask(ctx, igs, csel); + code = pdfi_trans_set_mask(ctx, igs, csel); } } - return 0; + return code; } diff --git a/pdf/pdf_types.h b/pdf/pdf_types.h index b8213e0b..6f78dde7 100644 --- a/pdf/pdf_types.h +++ b/pdf/pdf_types.h @@ -47,6 +47,7 @@ typedef enum pdf_obj_type_e { PDF_INDIRECT = 'R', PDF_BOOL = 'b', PDF_KEYWORD = 'K', + PDF_FAST_KEYWORD = 'k', PDF_FONT = 'F', PDF_STREAM = 'S', /* The following aren't PDF object types, but are objects we either want to @@ -57,6 +58,7 @@ typedef enum pdf_obj_type_e { PDF_DICT_MARK = '<', PDF_PROC_MARK = '{', PDF_CMAP = 'C', + PDF_BUFFER = 'B', /* 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 @@ -115,11 +117,6 @@ typedef struct pdf_obj_s { pdf_obj_common; } pdf_obj; -typedef struct pdf_bool_s { - pdf_obj_common; - bool value; -} pdf_bool; - typedef struct pdf_num_s { pdf_obj_common; union { @@ -141,22 +138,29 @@ typedef struct pdf_name_s { unsigned char data[PDF_NAME_DECLARED_LENGTH]; } pdf_name; +/* For storing arbitrary byte arrays where the length may be + greater than PDF_NAME_DECLARED_LENGTH - prevents static + alalysis tools complaining if we just used pdf_string + */ +typedef struct pdf_buffer_s { + pdf_obj_common; + uint32_t length; + unsigned char *data; +} pdf_buffer; + typedef enum pdf_key_e { - TOKEN_NOT_A_KEYWORD, - TOKEN_OBJ, - TOKEN_ENDOBJ, - TOKEN_STREAM, - TOKEN_ENDSTREAM, - TOKEN_XREF, - TOKEN_STARTXREF, - TOKEN_TRAILER, - TOKEN_INVALID_KEY, +#include "pdf_tokens.h" + TOKEN__LAST_KEY, } pdf_key; +#define PDF_NULL_OBJ ((pdf_obj *)(uintptr_t)TOKEN_null) +#define PDF_TRUE_OBJ ((pdf_obj *)(uintptr_t)TOKEN_TRUE) +#define PDF_FALSE_OBJ ((pdf_obj *)(uintptr_t)TOKEN_FALSE) +#define PDF_TOKEN_AS_OBJ(token) ((pdf_obj *)(uintptr_t)(token)) + typedef struct pdf_keyword_s { pdf_obj_common; uint32_t length; - pdf_key key; unsigned char data[PDF_NAME_DECLARED_LENGTH]; } pdf_keyword; @@ -251,4 +255,29 @@ typedef struct pdf_c_stream_s { char unget_buffer[UNREAD_BUFFER_SIZE]; } pdf_c_stream; +#ifndef inline +#define inline __inline +#endif /* inline */ + +#define pdfi_type_of(A) pdfi_type_of_imp((pdf_obj *)A) + +static inline pdf_obj_type pdfi_type_of_imp(pdf_obj *obj) +{ + if ((uintptr_t)obj > TOKEN__LAST_KEY) + return obj->type; + else if ((uintptr_t)obj == TOKEN_TRUE || (uintptr_t)obj == TOKEN_FALSE) + return PDF_BOOL; + else if ((uintptr_t)obj == TOKEN_null) + return PDF_NULL; + else + return PDF_FAST_KEYWORD; +} + +static inline int pdf_object_num(pdf_obj *obj) +{ + if ((uintptr_t)obj > TOKEN__LAST_KEY) + return obj->object_num; + return 0; +} + #endif diff --git a/pdf/pdf_warnings.h b/pdf/pdf_warnings.h index 21b2403f..82928bef 100644 --- a/pdf/pdf_warnings.h +++ b/pdf/pdf_warnings.h @@ -18,6 +18,9 @@ #endif PARAM(W_PDF_NOWARNING, "no warning"), PARAM(W_PDF_BAD_XREF_SIZE, "incorrect xref size"), +PARAM(W_PDF_BAD_XREF_ENTRY_SIZE, "xref entry not exactly 20 bytes"), +PARAM(W_PDF_BAD_XREF_ENTRY_NO_EOL, "xref entry not terminated with EOL"), +PARAM(W_PDF_BAD_XREF_ENTRY_FORMAT, "xref entry not valid format"), 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"), @@ -43,6 +46,7 @@ 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_STREAM_BAD_KEYWORD, "A stream keyword was not terminated with a linefeed (0x0A)"), 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"), @@ -58,5 +62,10 @@ PARAM(W_PDF_CA_OUTOFRANGE, "CA or ca value not in range 0.0 to 1.0, cla 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"), - +PARAM(W_PDF_GROUP_HAS_COLORSPACE, "Group attributes dictionary has /ColorSpace instead of /CS"), +PARAM(W_PDF_GROUP_BAD_BC, "Group attributes dictionary /BC differs in number of components from the colour space"), +PARAM(W_PDF_INT_AS_REAL, "found real number when expecting int"), +PARAM(PDF_W_NO_TREE_LIMITS, "Name tree node missing required Limits entry"), +PARAM(PDF_W_BAD_TREE_LIMITS, "Name tree node Limits array does not have 2 entries"), +PARAM(PDF_W_NAMES_ARRAY_SIZE, "Name tree Names array size not a mulitple of 2"), #undef PARAM diff --git a/pdf/pdf_xref.c b/pdf/pdf_xref.c index 7e611130..3f3d8008 100644 --- a/pdf/pdf_xref.c +++ b/pdf/pdf_xref.c @@ -141,7 +141,7 @@ static int read_xref_stream_entries(pdf_context *ctx, pdf_c_stream *s, uint64_t /* Forward definition */ static int read_xref(pdf_context *ctx, pdf_c_stream *s); /* These two routines are recursive.... */ -static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s); +static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s, int obj_num); static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pdf_c_stream *s) { @@ -153,9 +153,10 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd int64_t size; int64_t num; int64_t W[3]; + int objnum; bool known = false; - if (stream_obj->type != PDF_STREAM) + if (pdfi_type_of(stream_obj) != PDF_STREAM) return_error(gs_error_typecheck); code = pdfi_dict_from_obj(ctx, (pdf_obj *)stream_obj, &sdict); @@ -307,7 +308,7 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd return code; } } else { - int64_t start, end; + int64_t start, size; if (code < 0) { pdfi_close_file(ctx, XRefStrm); @@ -334,7 +335,7 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd return code; } - code = pdfi_array_get_int(ctx, a, (uint64_t)i+1, &end); + code = pdfi_array_get_int(ctx, a, (uint64_t)i+1, &size); if (code < 0) { pdfi_countdown(a); pdfi_close_file(ctx, XRefStrm); @@ -343,8 +344,11 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd return code; } - if (start + end >= ctx->xref_table->xref_size) { - code = resize_xref(ctx, start + end); + if (size < 1) + continue; + + if (start + size >= ctx->xref_table->xref_size) { + code = resize_xref(ctx, start + size); if (code < 0) { pdfi_countdown(a); pdfi_close_file(ctx, XRefStrm); @@ -354,7 +358,7 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd } } - code = read_xref_stream_entries(ctx, XRefStrm, start, start + end - 1, (uint64_t *)W); + code = read_xref_stream_entries(ctx, XRefStrm, start, start + size - 1, (uint64_t *)W); if (code < 0) { pdfi_countdown(a); pdfi_close_file(ctx, XRefStrm); @@ -391,145 +395,106 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd pdfi_seek(ctx, s, num, SEEK_SET); - code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); + code = pdfi_read_bare_int(ctx, ctx->main_stream, &objnum); + if (code == 1) + return pdfi_read_xref_stream_dict(ctx, s, objnum); + + code = pdfi_read_bare_keyword(ctx, ctx->main_stream); 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 */ - pdfi_pop(ctx, 1); - return(read_xref(ctx, ctx->main_stream)); - } else - code = pdfi_read_xref_stream_dict(ctx, s); - - return code; + if (code == TOKEN_XREF) { + pdfi_set_error(ctx, 0, NULL, E_PDF_PREV_NOT_XREF_STREAM, "pdfi_process_xref_stream", NULL); + if (!ctx->args.pdfstoponerror) + /* Read old-style xref table */ + return(read_xref(ctx, ctx->main_stream)); + } + return_error(gs_error_syntaxerror); } -static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s) +static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s, int obj_num) { int code; + int gen_num; if (ctx->args.pdfdebug) dmprintf(ctx->memory, "\n%% Reading PDF 1.5+ xref stream\n"); - if (((pdf_obj *)ctx->stack_top[-1])->type == PDF_INT) { - /* 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) - return(pdfi_repair_file(ctx)); + /* We have the obj_num. Lets try for obj_num gen obj as a XRef stream */ + code = pdfi_read_bare_int(ctx, ctx->main_stream, &gen_num); + if (code <= 0) + return(pdfi_repair_file(ctx)); - if (((pdf_obj *)ctx->stack_top[-1])->type != PDF_INT) { - /* Second element is not an integer, not a valid xref */ - pdfi_pop(ctx, 1); - return(pdfi_repair_file(ctx)); - } + /* Try to read 'obj' */ + code = pdfi_read_bare_keyword(ctx, ctx->main_stream); + if (code < 0) + return code; + if (code == 0) + return_error(gs_error_syntaxerror); - code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); - if (code < 0) { - pdfi_pop(ctx, 1); - return code; - } - if (code == 0) { - pdfi_pop(ctx, 1); - return_error(gs_error_syntaxerror); - } + /* Third element must be obj, or it's not a valid xref */ + if (code != TOKEN_OBJ) + return(pdfi_repair_file(ctx)); - if (((pdf_obj *)ctx->stack_top[-1])->type != PDF_KEYWORD) { - /* Second element is not an integer, not a valid xref */ - pdfi_pop(ctx, 2); - return(pdfi_repair_file(ctx)); - } else { - int obj_num, gen_num; + do { + code = pdfi_read_token(ctx, ctx->main_stream, obj_num, gen_num); + if (code <= 0) + return pdfi_repair_file(ctx); - pdf_keyword *keyword = (pdf_keyword *)ctx->stack_top[-1]; + if (pdfi_count_stack(ctx) >= 2 && pdfi_type_of(ctx->stack_top[-1]) == PDF_FAST_KEYWORD) { + uintptr_t keyword = (uintptr_t)ctx->stack_top[-1]; + if (keyword == TOKEN_STREAM) { + pdf_dict *dict; + pdf_stream *sdict = NULL; + int64_t Length; - if (keyword->key != TOKEN_OBJ) { - pdfi_pop(ctx, 3); - return(pdfi_repair_file(ctx)); - } - /* pop the 'obj', generation and object numbers */ - pdfi_pop(ctx, 1); - gen_num = ((pdf_num *)ctx->stack_top[-1])->value.i; - pdfi_pop(ctx, 1); - obj_num = ((pdf_num *)ctx->stack_top[-1])->value.i; - pdfi_pop(ctx, 1); + /* Remove the 'stream' token from the stack, should leave a dictionary object on the stack */ + pdfi_pop(ctx, 1); + if (pdfi_type_of(ctx->stack_top[-1]) != PDF_DICT) { + return pdfi_repair_file(ctx); + } + dict = (pdf_dict *)ctx->stack_top[-1]; - do { - code = pdfi_read_token(ctx, ctx->main_stream, obj_num, gen_num); - if (code <= 0) + /* Convert the dict into a stream (sdict comes back with at least one ref) */ + code = pdfi_obj_dict_to_stream(ctx, dict, &sdict, true); + /* Pop off the dict */ + pdfi_pop(ctx, 1); + if (code < 0) { + /* TODO: should I return code instead of trying to repair? + * Normally the above routine should not fail so something is + * probably seriously fubar. + */ return pdfi_repair_file(ctx); + } + dict = NULL; + + /* Init the stuff for the stream */ + sdict->stream_offset = pdfi_unread_tell(ctx); + sdict->object_num = obj_num; + sdict->generation_num = gen_num; + + code = pdfi_dict_get_int(ctx, sdict->stream_dict, "Length", &Length); + if (code < 0) { + /* TODO: Not positive this will actually have a length -- just use 0 */ + pdfi_set_error_var(ctx, 0, NULL, E_PDF_BADSTREAM, "pdfi_read_xref_stream_dict", "Xref Stream object %u missing mandatory keyword /Length\n", obj_num); + code = 0; + Length = 0; + } + sdict->Length = Length; + sdict->length_valid = true; - 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; - pdf_stream *sdict = NULL; - int64_t Length; - - /* Remove the 'stream' token from the stack, should leave a dictionary object on the stack */ - pdfi_pop(ctx, 1); - if (((pdf_obj *)ctx->stack_top[-1])->type != PDF_DICT) { - pdfi_pop(ctx, 1); - return pdfi_repair_file(ctx); - } - dict = (pdf_dict *)ctx->stack_top[-1]; - - /* Convert the dict into a stream (sdict comes back with at least one ref) */ - code = pdfi_obj_dict_to_stream(ctx, dict, &sdict, true); - if (code < 0) { - pdfi_pop(ctx, 1); - /* TODO: should I return code instead of trying to repair? - * Normally the above routine should not fail so something is - * probably seriously fubar. - */ - return pdfi_repair_file(ctx); - } - /* Pop off the dict */ - pdfi_pop(ctx, 1); - dict = NULL; - - /* Init the stuff for the stream */ - sdict->stream_offset = pdfi_unread_tell(ctx); - sdict->object_num = obj_num; - sdict->generation_num = gen_num; - - code = pdfi_dict_get_int(ctx, sdict->stream_dict, "Length", &Length); - if (code < 0) { - /* TODO: Not positive this will actually have a length -- just use 0 */ - char extra_info[gp_file_name_sizeof]; - - 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; - } - sdict->Length = Length; - sdict->length_valid = true; - - code = pdfi_process_xref_stream(ctx, sdict, ctx->main_stream); - if (code < 0) { - pdfi_countdown(sdict); - return (pdfi_repair_file(ctx)); - } - pdfi_countdown(sdict); - break; - } - if (keyword->key == TOKEN_ENDOBJ) { - /* Something went wrong, this is not a stream dictionary */ - pdfi_pop(ctx, 3); - return(pdfi_repair_file(ctx)); - break; - } + code = pdfi_process_xref_stream(ctx, sdict, ctx->main_stream); + if (code < 0) { + pdfi_countdown(sdict); + return (pdfi_repair_file(ctx)); } - } while(1); + pdfi_countdown(sdict); + break; + } else if (keyword == TOKEN_ENDOBJ) { + /* Something went wrong, this is not a stream dictionary */ + return(pdfi_repair_file(ctx)); + } } - } else { - /* Not an 'xref' and not an integer, so not a valid xref */ - return(pdfi_repair_file(ctx)); - } + } while(1); return 0; } @@ -676,8 +641,8 @@ static int write_offset(byte *B, gs_offset_t o, unsigned int g, unsigned char fr static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *section_start, uint64_t *section_size) { int code = 0, i, j; - pdf_obj *o = NULL; - uint64_t start = 0, size = 0; + int start = 0; + int size = 0; int64_t bytes = 0; char Buffer[21]; @@ -686,57 +651,37 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio if (ctx->args.pdfdebug) dmprintf(ctx->memory, "\n%% Reading xref section\n"); - code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); - - if (code < 0) - return code; - - if (pdfi_count_stack(ctx) < 1) - return_error(gs_error_stackunderflow); - - o = ctx->stack_top[-1]; - if (o->type == PDF_KEYWORD) - return 0; + code = pdfi_read_bare_int(ctx, ctx->main_stream, &start); + if (code < 0) { + /* Not an int, might be a keyword */ + code = pdfi_read_bare_keyword(ctx, ctx->main_stream); + if (code < 0) + return code; - if (o->type != PDF_INT) { - /* element is not an integer, not a valid xref */ - pdfi_pop(ctx, 1); - return_error(gs_error_typecheck); + if (code != TOKEN_TRAILER) { + /* element is not an integer, and not a keyword - not a valid xref */ + return_error(gs_error_typecheck); + } + return 1; } - if (((pdf_num *)o)->value.i < 0) { - pdfi_pop(ctx, 1); + if (start < 0) return_error(gs_error_rangecheck); - } - *section_start = start = ((pdf_num *)o)->value.i; + *section_start = start; - code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); - if (code < 0) { - pdfi_pop(ctx, 1); + code = pdfi_read_bare_int(ctx, ctx->main_stream, &size); + if (code < 0) return code; - } - if (code == 0) { - pdfi_pop(ctx, 1); + if (code == 0) return_error(gs_error_syntaxerror); - } - - o = ctx->stack_top[-1]; - if (o->type != PDF_INT) { - /* element is not an integer, not a valid xref */ - 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); + if (size < 0) return_error(gs_error_rangecheck); - } - *section_size = size = ((pdf_num *)o)->value.i; - pdfi_pop(ctx, 2); + *section_size = size; if (ctx->args.pdfdebug) dmprintf2(ctx->memory, "\n%% Section starts at %d and has %d entries\n", (unsigned int) start, (unsigned int)size); @@ -784,9 +729,12 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio if (bytes < 20) return_error(gs_error_ioerror); j = 19; + if ((Buffer[19] != 0x0a && Buffer[19] != 0x0d) || (Buffer[18] != 0x0d && Buffer[18] != 0x0a && Buffer[18] != 0x20)) + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_XREF_ENTRY_SIZE, "read_xref_section", NULL); while (Buffer[j] != 0x0D && Buffer[j] != 0x0A) { pdfi_unread_byte(ctx, s, (byte)Buffer[j]); if (--j < 0) { + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_XREF_ENTRY_NO_EOL, "read_xref_section", NULL); dmprintf(ctx->memory, "Invalid xref entry, line terminator missing.\n"); code = read_xref_entry_slow(ctx, s, &off, &gen, &free); if (code < 0) @@ -803,6 +751,7 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio continue; if (sscanf(Buffer, "%"PRIdOFFSET" %d %c", &entry->u.uncompressed.offset, &entry->u.uncompressed.generation_num, &free) != 3) { + pdfi_set_warning(ctx, 0, NULL, W_PDF_BAD_XREF_ENTRY_FORMAT, "read_xref_section", NULL); 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); @@ -827,16 +776,17 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio static int read_xref(pdf_context *ctx, pdf_c_stream *s) { int code = 0; - pdf_obj **o = NULL; - pdf_keyword *k; pdf_dict *d = NULL; uint64_t size = 0, max_obj = 0; int64_t num; + int obj_num; + + if (ctx->repaired) + return 0; do { uint64_t section_start, section_size; - o = ctx->stack_top; code = read_xref_section(ctx, s, §ion_start, §ion_size); if (code < 0) return code; @@ -844,23 +794,15 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) 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]; - if(k->type != PDF_KEYWORD || k->key != TOKEN_TRAILER) - return_error(gs_error_syntaxerror); - else { - pdfi_pop(ctx, 1); - break; - } - } - } while (1); + /* code == 1 => read_xref_section ended with a trailer. */ + } while (code != 1); code = pdfi_read_dict(ctx, ctx->main_stream, 0, 0); if (code < 0) return code; d = (pdf_dict *)ctx->stack_top[-1]; - if (d->type != PDF_DICT) { + if (pdfi_type_of(d) != PDF_DICT) { pdfi_pop(ctx, 1); return_error(gs_error_typecheck); } @@ -950,21 +892,25 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) } } - pdfi_loop_detector_mark(ctx); + code = pdfi_loop_detector_mark(ctx); + if (code < 0) { + pdfi_pop(ctx, 1); + return code; + } /* Because of the way the code works when we read a file which is a pure * xref stream file, we need to read the first integer of 'x y obj' * because the xref stream decoding code expects that to be on the stack. */ pdfi_seek(ctx, s, num, SEEK_SET); - code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); + code = pdfi_read_bare_int(ctx, ctx->main_stream, &obj_num); if (code < 0) { pdfi_loop_detector_cleartomark(ctx); pdfi_pop(ctx, 1); return code; } - code = pdfi_read_xref_stream_dict(ctx, ctx->main_stream); + code = pdfi_read_xref_stream_dict(ctx, ctx->main_stream, obj_num); if (code < 0) { pdfi_loop_detector_cleartomark(ctx); pdfi_pop(ctx, 1); @@ -1017,13 +963,16 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) if (code < 0) return code; - code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); - if (code < 0) - return(code); - if (code == 0) + if (!ctx->repaired) { + code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); + if (code < 0) + return(code); + if (code == 0) return_error(gs_error_syntaxerror); + } else + return 0; - if (((pdf_obj *)ctx->stack_top[-1])->type == PDF_KEYWORD && ((pdf_keyword *)ctx->stack_top[-1])->key == TOKEN_XREF) { + if ((intptr_t)(ctx->stack_top[-1]) == (intptr_t)TOKEN_XREF) { /* Read old-style xref table */ pdfi_pop(ctx, 1); return(read_xref(ctx, ctx->main_stream)); @@ -1036,61 +985,51 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) int pdfi_read_xref(pdf_context *ctx) { int code = 0; - bool do_repair = false; + int obj_num; code = pdfi_loop_detector_mark(ctx); if (code < 0) return code; - if (ctx->startxref != 0) { - code = pdfi_loop_detector_add_object(ctx, ctx->startxref); - if (code < 0) - goto exit; + if (ctx->startxref == 0) + goto repair; - if (ctx->args.pdfdebug) - dmprintf(ctx->memory, "%% Trying to read 'xref' token for xref table, or 'int int obj' for an xref stream\n"); + code = pdfi_loop_detector_add_object(ctx, ctx->startxref); + if (code < 0) + goto exit; - if (ctx->startxref > ctx->main_stream_length - 5) { - pdfi_set_error(ctx, 0, NULL, E_PDF_BADSTARTXREF, "pdfi_read_xref", (char *)"startxref offset is beyond end of file"); - do_repair = true; - goto exit; - } + if (ctx->args.pdfdebug) + dmprintf(ctx->memory, "%% Trying to read 'xref' token for xref table, or 'int int obj' for an xref stream\n"); + if (ctx->startxref > ctx->main_stream_length - 5) { + pdfi_set_error(ctx, 0, NULL, E_PDF_BADSTARTXREF, "pdfi_read_xref", (char *)"startxref offset is beyond end of file"); + goto repair; + } + if (ctx->startxref < 0) { + pdfi_set_error(ctx, 0, NULL, E_PDF_BADSTARTXREF, "pdfi_read_xref", (char *)"startxref offset is before start of file"); + goto repair; + } - /* Read the xref(s) */ - pdfi_seek(ctx, ctx->main_stream, ctx->startxref, SEEK_SET); + /* Read the xref(s) */ + pdfi_seek(ctx, ctx->main_stream, ctx->startxref, SEEK_SET); - code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); - if (code < 0) { + /* If it starts with an int, it's an xref stream dict */ + code = pdfi_read_bare_int(ctx, ctx->main_stream, &obj_num); + if (code == 1) { + code = pdfi_read_xref_stream_dict(ctx, ctx->main_stream, obj_num); + if (code < 0) + goto repair; + } else { + /* If not, it had better start 'xref', and be an old-style xref table */ + code = pdfi_read_bare_keyword(ctx, ctx->main_stream); + if (code != TOKEN_XREF) { pdfi_set_error(ctx, 0, NULL, E_PDF_BADSTARTXREF, "pdfi_read_xref", (char *)"Failed to read any token at the startxref location"); - do_repair = true; - goto exit; - } - - if (pdfi_count_stack(ctx) < 1) { - code = gs_note_error(gs_error_undefined); - goto exit; + goto repair; } - if (((pdf_obj *)ctx->stack_top[-1])->type == PDF_KEYWORD && ((pdf_keyword *)ctx->stack_top[-1])->key == TOKEN_XREF) { - /* Read old-style xref table */ - pdfi_pop(ctx, 1); - code = read_xref(ctx, ctx->main_stream); - if (code < 0) { - do_repair = true; - goto exit; - } - } else { - code = pdfi_read_xref_stream_dict(ctx, ctx->main_stream); - if (code < 0){ - do_repair = true; - goto exit; - } - } - } else { - /* Attempt to repair PDF file */ - do_repair = true; - goto exit; + code = read_xref(ctx, ctx->main_stream); + if (code < 0) + goto repair; } if(ctx->args.pdfdebug && ctx->xref_table) { @@ -1159,11 +1098,15 @@ int pdfi_read_xref(pdf_context *ctx) exit: (void)pdfi_loop_detector_cleartomark(ctx); - if (do_repair) - return(pdfi_repair_file(ctx)); if (code < 0) return code; return 0; + +repair: + (void)pdfi_loop_detector_cleartomark(ctx); + if (!ctx->repaired) + return(pdfi_repair_file(ctx)); + return 0; } diff --git a/pdf/pdftop.c b/pdf/pdftop.c index 5e147cdd..9f5cfdf4 100644 --- a/pdf/pdftop.c +++ b/pdf/pdftop.c @@ -60,9 +60,9 @@ extern const char gp_file_name_list_separator; static int pdf_detect_language(const char *s, int len) { - if (len < 5) - return 1; - return memcmp(s, "%!PDF", 5); + if (len >= 5 && memcmp(s, "%!PDF", 5) == 0) + return 100; + return 0; } static const pl_interp_characteristics_t * @@ -393,7 +393,7 @@ static int plist_value_get_int64(gs_param_typed_value *pvalue, int64_t *pint) /* Get the value for a string or a name (null terminated) */ static int plist_value_get_string_or_name(pdf_context *ctx, gs_param_typed_value *pvalue, - char **pstr, int *plen) + char **pstr, int *plen, bool *is_name) { const byte *data; uint size; @@ -401,9 +401,11 @@ static int plist_value_get_string_or_name(pdf_context *ctx, gs_param_typed_value if (pvalue->type == gs_param_type_string) { data = pvalue->value.s.data; size = pvalue->value.s.size; + *is_name = false; } else if (pvalue->type == gs_param_type_name) { data = pvalue->value.n.data; size = pvalue->value.n.size; + *is_name = true; } else { return_error(gs_error_typecheck); } @@ -481,6 +483,7 @@ pdf_impl_set_param(pl_interp_implementation_t *impl, gs_param_key_t key; int code; int len; + bool discard_isname; param_init_enumerator(&enumerator); if ((code = param_get_next_key(plist, &enumerator, &key)) == 0) { @@ -645,7 +648,7 @@ pdf_impl_set_param(pl_interp_implementation_t *impl, return code; } if (!strncmp(param, "PDFPassword", 11)) { - code = plist_value_get_string_or_name(ctx, &pvalue, &ctx->encryption.Password , &ctx->encryption.PasswordLen); + code = plist_value_get_string_or_name(ctx, &pvalue, &ctx->encryption.Password , &ctx->encryption.PasswordLen, &discard_isname); if (code < 0) return code; } @@ -671,14 +674,14 @@ pdf_impl_set_param(pl_interp_implementation_t *impl, return code; } if (!strncmp(param, "UseOutputIntent", strlen("UseOutputIntent"))) { - code = plist_value_get_string_or_name(ctx, &pvalue, &ctx->args.UseOutputIntent, &len); + code = plist_value_get_string_or_name(ctx, &pvalue, &ctx->args.UseOutputIntent, &len, &discard_isname); if (code < 0) return code; } if (!strncmp(param, "FONTPATH", 11)) { char *s = NULL; int slen; - code = plist_value_get_string_or_name(ctx, &pvalue, &s , &slen); + code = plist_value_get_string_or_name(ctx, &pvalue, &s , &slen, &discard_isname); if (code < 0) return code; code = pdfi_add_paths_to_search_paths(ctx, (const char *)s, slen, true); @@ -687,19 +690,24 @@ pdf_impl_set_param(pl_interp_implementation_t *impl, if (!strncmp(param, "FONTMAP", 7)) { char *s = NULL; int slen; - code = plist_value_get_string_or_name(ctx, &pvalue, &s, &slen); + code = plist_value_get_string_or_name(ctx, &pvalue, &s, &slen, &discard_isname); 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); + code = plist_value_get_string_or_name(ctx, &pvalue, (char **)&ctx->args.cidfsubstpath.data, (int *)&ctx->args.cidfsubstpath.size, &discard_isname); 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); + code = plist_value_get_string_or_name(ctx, &pvalue, (char **)&ctx->args.cidfsubstfont.data, (int *)&ctx->args.cidfsubstfont.size, &discard_isname); + if (code < 0) + return code; + } + if (!strncmp(param, "SUBSTFONT", 12)) { + code = plist_value_get_string_or_name(ctx, &pvalue, (char **)&ctx->args.defaultfont.data, (int *)&ctx->args.defaultfont.size, &ctx->args.defaultfont_is_name); if (code < 0) return code; } |