[gs-commits] ghostpdl branch, pdfi, updated. jbig2dec-0.14-2214-g6d2cc1a

[email protected] (Nancy Durgin)
Newsgroups gmane.comp.printing.ghostscript.cvs
Message-ID <[email protected]>
The ghostpdl branch, pdfi has been updated
       via  6d2cc1ab2064254e6734f013908bdb9b5a73dca8 (commit)
       via  e0ffeda75b00f7d864cdfb785cdded5baa88c849 (commit)
      from  f86f9fef1d8e43847342faa49a77d3560b9b14e4 (commit)

----------------------------------------------------------------------
commit 6d2cc1ab2064254e6734f013908bdb9b5a73dca8
Author: Nancy Durgin <[email protected]>
Date:   Wed Sep 18 09:34:14 2019 -0700

    Clean up some optcontext stuff
    
    -- wasn't calling the routine to free it
    -- cleaned up the implementation, fixed bug in untested code

diff --git a/pdf/ghostpdf.c b/pdf/ghostpdf.c
index bb7aeeb..442455f 100644
--- a/pdf/ghostpdf.c
+++ b/pdf/ghostpdf.c
@@ -883,6 +883,7 @@ int pdfi_free_context(gs_memory_t *pmem, pdf_context *ctx)
     }
 
     pdfi_free_DefaultQState(ctx);
+    pdfi_oc_free(ctx);
 
     if (ctx->pdfi_param_list.head != NULL)
         gs_c_param_list_release(&ctx->pdfi_param_list);
diff --git a/pdf/pdf_optcontent.c b/pdf/pdf_optcontent.c
index 61f3461..70bb0c5 100644
--- a/pdf/pdf_optcontent.c
+++ b/pdf/pdf_optcontent.c
@@ -310,7 +310,7 @@ pdfi_oc_is_ocg_visible(pdf_context *ctx, pdf_dict *ocdict)
     return is_visible;
 }
 
-#define INIT_CONTENT_LEVELS 100
+#define NUM_CONTENT_LEVELS 100
 typedef struct {
     byte *flags;
     uint64_t num_off;
@@ -329,16 +329,16 @@ static int pdfi_oc_levels_init(pdf_context *ctx, pdfi_oc_levels_t **levels)
     if (!new)
         return_error(gs_error_VMerror);
 
-    data = (byte *)gs_alloc_bytes(ctx->memory, INIT_CONTENT_LEVELS, "pdfi_oc_levels_init (data)");
+    data = (byte *)gs_alloc_bytes(ctx->memory, NUM_CONTENT_LEVELS, "pdfi_oc_levels_init (data)");
     if (!data) {
         gs_free_object(ctx->memory, new, "pdfi_oc_levels_init (levels (error))");
         return_error(gs_error_VMerror);
     }
-    memset(data, 0, INIT_CONTENT_LEVELS);
+    memset(data, 0, NUM_CONTENT_LEVELS);
 
     new->flags = data;
     new->num_off = 0;
-    new->max_flags = INIT_CONTENT_LEVELS;
+    new->max_flags = NUM_CONTENT_LEVELS;
     *levels = new;
 
     return 0;
@@ -359,7 +359,7 @@ static int pdfi_oc_levels_set(pdf_context *ctx, pdfi_oc_levels_t *levels, uint64
 
     if (index > levels->max_flags) {
         /* Expand the flags buffer */
-        newmax = levels->max_flags + INIT_CONTENT_LEVELS;
+        newmax = levels->max_flags + NUM_CONTENT_LEVELS;
         if (index > newmax)
             return_error(gs_error_Fatal); /* shouldn't happen */
         new = gs_alloc_bytes(ctx->memory, newmax, "pdfi_oc_levels_set (new data)");
@@ -369,6 +369,7 @@ static int pdfi_oc_levels_set(pdf_context *ctx, pdfi_oc_levels_t *levels, uint64
         memcpy(new, levels->flags, levels->max_flags);
         gs_free_object(ctx->memory, levels->flags, "pdfi_oc_levels_set (old data)");
         levels->flags = new;
+        levels->max_flags += NUM_CONTENT_LEVELS;
     }
 
     if (levels->flags[index] == 0)
@@ -414,6 +415,15 @@ int pdfi_oc_init(pdf_context *ctx)
     return 0;
 }
 
+int pdfi_oc_free(pdf_context *ctx)
+{
+    int code;
+
+    code = pdfi_oc_levels_free(ctx, (pdfi_oc_levels_t *)ctx->OFFlevels);
+    ctx->OFFlevels = NULL;
+    return code;
+}
+
 /* begin marked content sequence */
 /* TODO: Incomplete implementation, it is ignoring the argument */
 int pdfi_op_BMC(pdf_context *ctx)
diff --git a/pdf/pdf_optcontent.h b/pdf/pdf_optcontent.h
index 71d5366..61c9d69 100644
--- a/pdf/pdf_optcontent.h
+++ b/pdf/pdf_optcontent.h
@@ -19,6 +19,7 @@
 bool pdfi_oc_is_ocg_visible(pdf_context *ctx, pdf_dict *ocdict);
 
 int pdfi_oc_init(pdf_context *ctx);
+int pdfi_oc_free(pdf_context *ctx);
 bool pdfi_oc_is_off(pdf_context *ctx);
 int pdfi_op_BMC(pdf_context *ctx);
 int pdfi_op_BDC(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict);

----------------------------------------------------------------------
commit e0ffeda75b00f7d864cdfb785cdded5baa88c849
Author: Nancy Durgin <[email protected]>
Date:   Wed Sep 18 09:09:54 2019 -0700

    Create pdf_check.c and move a bunch of code out of ghostpdf.c
    
    Move some page code to pdf_page.c
    Move the transparency and spot checking code to pdf_check.c
    
    No functional changes, just moving code around.

diff --git a/pdf/ghostpdf.c b/pdf/ghostpdf.c
index 49a0dff..bb7aeeb 100644
--- a/pdf/ghostpdf.c
+++ b/pdf/ghostpdf.c
@@ -31,1141 +31,8 @@
 #include "pdf_colour.h"
 #include "pdf_font.h"
 #include "pdf_text.h"
-#include "pdf_optcontent.h"
-
-/* This routine is slightly misnamed, as it also checks ColorSpaces for spot colours.
- * This is done at the page level, so we maintain a dictionary of the spot colours
- * encountered so far, which we consult before adding any new ones.
- */
-static int pdfi_check_Resources_for_transparency(pdf_context *ctx, pdf_dict *Resources_dict, pdf_dict *page_dict, bool *transparent, int *num_spots);
-
-/* For performance and resource reasons we do not want to install the transparency blending
- * compositor unless we need it. Similarly, if a device handles spot colours it can minimise
- * memory usage if it knows ahead of time how many spot colours there will be.
- *
- * The PDF interpreter written in PostScript performed these as two separate tasks, on opening
- * a PDF file it would count spot colour usage and then for each page it would chek if the page
- * used any transparency. The code below is used to check for both transparency and spot colours.
- * If the int pointer to num_spots is NULL then we aren't interested in spot colours (not supported
- * by the device), if the int pointer to transparent is NULL then we aren't interested in transparency
- * (-dNOTRANSPARENCY is set).
- *
- * Currenlty the code is run when we open the PDF file, the existence of transparency on any given
- * page is recorded in a bit array for later use. If it turns out that checking every page when we
- * open the file is a performance burden then we could do it on a per-page basis instead. NB if
- * the device supports spot colours then we do need to scan the file before starting any page.
- *
- * The technique is fairly straight-forward, we start with each page, and open its Resources
- * dictionary, we then check by type each possible resource. Some resources (eg Pattern, XObject)
- * can themselves contain Resources, in which case we recursively check that dictionary. Note; not
- * all Resource types need to be checked for both transparency and spot colours, some types can
- * only contain one or the other.
- *
- * Routines with the name pdfi_check_xxx_dict are intended to check a Resource dictionary entry, this
- * will be a dictioanry of names and values, where the values are objects of the given Resource type.
- *
- */
-
-/*
- * Check the Resources dictionary ColorSpace entry. pdfi_check_ColorSpace_for_spots is defined
- * in pdf_colour.c
- */
-static int pdfi_check_ColorSpace_dict(pdf_context *ctx, pdf_dict *cspace_dict, pdf_dict *page_dict, int *num_spots)
-{
-    int code, i, index;
-    pdf_obj *Key = NULL, *Value = NULL;
-
-    if (pdfi_dict_entries(cspace_dict) > 0) {
-        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the ColorSpace dictionary loop */
-        if (code < 0)
-            return code;
-
-        code = pdfi_dict_first(ctx, cspace_dict, &Key, &Value, (void *)&index);
-        if (code < 0)
-            goto error1;
-
-        i = 1;
-        do {
-            code = pdfi_check_ColorSpace_for_spots(ctx, Value, cspace_dict, page_dict, num_spots);
-            if (code < 0)
-                goto error2;
-
-            pdfi_countdown(Key);
-            Key = NULL;
-            pdfi_countdown(Value);
-            Value = NULL;
-
-            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */
-
-            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Shading dictionary loop */
-            if (code < 0)
-                goto error1;
-
-            do {
-                if (i++ >= pdfi_dict_entries(cspace_dict)) {
-                    code = 0;
-                    goto transparency_exit;
-                }
-
-                code = pdfi_dict_next(ctx, cspace_dict, &Key, &Value, (void *)&index);
-                if (code == 0 && Value->type == PDF_ARRAY)
-                    break;
-                pdfi_countdown(Key);
-                Key = NULL;
-                pdfi_countdown(Value);
-                Value = NULL;
-            } while(1);
-        }while (1);
-    }
-    return 0;
-
-transparency_exit:
-error2:
-    pdfi_countdown(Key);
-    pdfi_countdown(Value);
-
-error1:
-    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
-    return code;
-}
-
-/*
- * Process an individual Shading dictionary to see if it contains a ColorSpace with a spot colour
- */
-static int pdfi_check_Shading(pdf_context *ctx, pdf_dict *shading, pdf_dict *page_dict, int *num_spots)
-{
-    int code;
-    pdf_obj *o = NULL;
-
-    code = pdfi_dict_knownget(ctx, shading, "ColorSpace", (pdf_obj **)&o);
-    if (code > 0) {
-        code = pdfi_check_ColorSpace_for_spots(ctx, o, shading, page_dict, num_spots);
-        pdfi_countdown(o);
-        return code;
-    }
-    return 0;
-}
-
-/*
- * Check the Resources dictionary Shading entry.
- */
-static int pdfi_check_Shading_dict(pdf_context *ctx, pdf_dict *shading_dict, pdf_dict *page_dict, int *num_spots)
-{
-    int code, i, index;
-    pdf_obj *Key = NULL, *Value = NULL;
-
-    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, (void *)&index);
-        if (code < 0 || Value->type != PDF_DICT)
-            goto error1;
-
-        i = 1;
-        do {
-            code = pdfi_check_Shading(ctx, (pdf_dict *)Value, page_dict, num_spots);
-            if (code < 0)
-                goto error2;
-
-            pdfi_countdown(Key);
-            Key = NULL;
-            pdfi_countdown(Value);
-            Value = NULL;
-
-            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */
-
-            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Shading dictionary loop */
-            if (code < 0)
-                goto error1;
-
-            do {
-                if (i++ >= pdfi_dict_entries(shading_dict)) {
-                    code = 0;
-                    goto transparency_exit;
-                }
-
-                code = pdfi_dict_next(ctx, shading_dict, &Key, &Value, (void *)&index);
-                if (code == 0 && Value->type == PDF_DICT)
-                    break;
-                pdfi_countdown(Key);
-                Key = NULL;
-                pdfi_countdown(Value);
-                Value = NULL;
-            } while(1);
-        }while (1);
-    }
-    return 0;
-
-transparency_exit:
-error2:
-    pdfi_countdown(Key);
-    pdfi_countdown(Value);
-
-error1:
-    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
-    return code;
-}
-
-/*
- * This routine checks an XObject to see if it contains any spot
- * colour definitions, or transparency usage.
- */
-static int pdfi_check_XObject(pdf_context *ctx, pdf_dict *xobject, pdf_dict *page_dict, bool *transparent, int *num_spots)
-{
-    int code = 0;
-    pdf_name *n = NULL;
-    bool known = false;
-    double f;
-
-    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")) {
-            pdf_obj *CS = NULL;
-
-            pdfi_countdown(n);
-            n = NULL;
-            code = pdfi_dict_known(xobject, "SMask", &known);
-            if (code >= 0) {
-                if (known == true) {
-                    *transparent = true;
-                    if (num_spots == NULL)
-                        goto transparency_exit;
-                }
-                code = pdfi_dict_knownget_number(ctx, xobject, "SMaskInData", &f);
-                if (code > 0) {
-                    code = 0;
-                    if (f != 0.0)
-                        *transparent = true;
-                    if (num_spots == NULL)
-                        goto transparency_exit;
-                }
-                /* Check the image dictionary for a ColorSpace entry, if we are checking spot names */
-                if (num_spots) {
-                    code = pdfi_dict_knownget(ctx, xobject, "ColorSpace", (pdf_obj **)&CS);
-                    if (code > 0) {
-                        /* We don't care if there's an error here, it'll be picked up if we use the ColorSpace later */
-                        (void)pdfi_check_ColorSpace_for_spots(ctx, CS, xobject, page_dict, num_spots);
-                        pdfi_countdown(CS);
-                    }
-                }
-            }
-        } else {
-            if (pdfi_name_is((const pdf_name *)n, "Form")) {
-                pdf_dict *group_dict = NULL, *resource_dict = NULL;
-                pdf_obj *CS = NULL;
-
-                pdfi_countdown(n);
-                code = pdfi_dict_knownget_type(ctx, xobject, "Group", PDF_DICT, (pdf_obj **)&group_dict);
-                if (code > 0) {
-                    *transparent = true;
-                    if (num_spots == NULL) {
-                        pdfi_countdown(group_dict);
-                        goto transparency_exit;
-                    }
-
-                    /* Start a new loop detector group to avoid this being detected in the Resources check below */
-                    code = pdfi_loop_detector_mark(ctx); /* Mark the start of the XObject dictionary loop */
-                    if (code > 0) {
-                        code = pdfi_dict_knownget(ctx, group_dict, "CS", &CS);
-                        if (code > 0)
-                            /* We don't care if there's an error here, it'll be picked up if we use the ColorSpace later */
-                            (void)pdfi_check_ColorSpace_for_spots(ctx, CS, group_dict, page_dict, num_spots);
-                    }
-                    pdfi_countdown(group_dict);
-                    pdfi_countdown(CS);
-                    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the XObject dictionary loop */
-                }
-
-                code = pdfi_dict_knownget_type(ctx, xobject, "Resources", PDF_DICT, (pdf_obj **)&resource_dict);
-                if (code > 0) {
-                    code = pdfi_check_Resources_for_transparency(ctx, resource_dict, page_dict, transparent, num_spots);
-                    pdfi_countdown(resource_dict);
-                    if (code < 0)
-                        goto transparency_exit;
-                }
-            } else
-                pdfi_countdown(n);
-        }
-    }
-
-    return 0;
-
-transparency_exit:
-    return code;
-}
-
-/*
- * Check the Resources dictionary XObject entry.
- */
-static int pdfi_check_XObject_dict(pdf_context *ctx, pdf_dict *xobject_dict, pdf_dict *page_dict, bool *transparent, int *num_spots)
-{
-    int code, i, index;
-    pdf_obj *Key = NULL, *Value = NULL; //, *o = NULL;
-
-    if (pdfi_dict_entries(xobject_dict) > 0) {
-        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the XObject dictionary loop */
-        if (code < 0)
-            return code;
-
-        code = pdfi_dict_first(ctx, xobject_dict, &Key, &Value, (void *)&index);
-        if (code < 0 || Value->type != PDF_DICT)
-            goto error1;
-
-        i = 1;
-        do {
-            code = pdfi_check_XObject(ctx, (pdf_dict *)Value, page_dict, transparent, num_spots);
-            if (code < 0)
-                goto error2;
-
-            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the XObject dictionary loop */
-
-            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the XObject dictionary loop */
-            if (code < 0)
-                goto error1;
-
-            pdfi_countdown(Key);
-            Key = NULL;
-            pdfi_countdown(Value);
-            Value = NULL;
-
-            do {
-                if (i++ >= pdfi_dict_entries(xobject_dict)) {
-                    code = 0;
-                    goto transparency_exit;
-                }
-
-                code = pdfi_dict_next(ctx, xobject_dict, &Key, &Value, (void *)&index);
-                if (code == 0 && Value->type == PDF_DICT)
-                    break;
-                pdfi_countdown(Key);
-                Key = NULL;
-                pdfi_countdown(Value);
-                Value = NULL;
-            } while(1);
-        }while(1);
-    }
-    return 0;
-
-transparency_exit:
-error2:
-    pdfi_countdown(Key);
-    pdfi_countdown(Value);
-
-error1:
-    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
-    return code;
-}
-
-/*
- * This routine checks an ExtGState dictionary to see if it contains any spot
- * colour definitions, or transparency usage.
- */
-static int pdfi_check_ExtGState(pdf_context *ctx, pdf_dict *extgstate_dict, pdf_dict *page_dict, bool *transparent, int *num_spots)
-{
-    int code;
-    pdf_obj *o = NULL;
-    double f;
-
-    if (pdfi_dict_entries(extgstate_dict) > 0) {
-        /* Check SMask first, because if we test spot colours first we can exit as
-         * soon as we detect transparency.
-         */
-        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);
-                    *transparent = true;
-                    return 0;
-                }
-            } else {
-                if (o->type == PDF_DICT) {
-                    pdf_obj *G = NULL;
-
-                    *transparent = true;
-
-                    if (num_spots != NULL) {
-                        /* Check if the SMask has a /G (Group) */
-                        code = pdfi_dict_knownget(ctx, (pdf_dict *)o, "G", &G);
-                        if (code > 0) {
-                            code = pdfi_check_XObject(ctx, (pdf_dict *)G, page_dict, transparent, num_spots);
-                            pdfi_countdown(G);
-                        }
-                    }
-                    pdfi_countdown(o);
-                    return code;
-                }
-            }
-        }
-        pdfi_countdown(o);
-        o = NULL;
-
-        code = pdfi_dict_knownget_type(ctx, extgstate_dict, "BM", PDF_NAME, &o);
-        if (code > 0) {
-            if (!pdfi_name_is((pdf_name *)o, "Normal")) {
-                if (!pdfi_name_is((pdf_name *)o, "Compatible")) {
-                    pdfi_countdown(o);
-                    *transparent = true;
-                    return 0;
-                }
-            }
-        }
-        pdfi_countdown(o);
-        o = NULL;
-
-        code = pdfi_dict_knownget_number(ctx, extgstate_dict, "CA", &f);
-        if (code > 0) {
-            if (f != 1.0) {
-                *transparent = true;
-                return 0;
-            }
-        }
-
-        code = pdfi_dict_knownget_number(ctx, extgstate_dict, "ca", &f);
-        if (code > 0) {
-            if (f != 1.0) {
-                *transparent = true;
-                return 0;
-            }
-        }
-    }
-    return 0;
-}
-
-/*
- * Check the Resources dictionary ExtGState entry.
- */
-static int pdfi_check_ExtGState_dict(pdf_context *ctx, pdf_dict *extgstate_dict, pdf_dict *page_dict, bool *transparent, int *num_spots)
-{
-    int code, i, index;
-    pdf_obj *Key = NULL, *Value = NULL;
-
-    if (pdfi_dict_entries(extgstate_dict) > 0) {
-        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the ColorSpace dictionary loop */
-        if (code < 0)
-            return code;
-
-        code = pdfi_dict_first(ctx, extgstate_dict, &Key, &Value, (void *)&index);
-        if (code < 0)
-            goto error1;
-
-        i = 1;
-        do {
-
-            (void)pdfi_check_ExtGState(ctx, (pdf_dict *)Value, page_dict, transparent, num_spots);
-            if (*transparent == true && num_spots == NULL)
-                goto transparency_exit;
-
-            pdfi_countdown(Key);
-            Key = NULL;
-            pdfi_countdown(Value);
-            Value = NULL;
-
-            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the ExtGState dictionary loop */
-
-            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the ExtGState dictionary loop */
-            if (code < 0)
-                goto error1;
-
-            do {
-                if (i++ >= pdfi_dict_entries(extgstate_dict)) {
-                    code = 0;
-                    goto transparency_exit;
-                }
-
-                code = pdfi_dict_next(ctx, extgstate_dict, &Key, &Value, (void *)&index);
-                if (code == 0 && Value->type == PDF_DICT)
-                    break;
-                pdfi_countdown(Key);
-                Key = NULL;
-                pdfi_countdown(Value);
-                Value = NULL;
-            } while(1);
-        }while (1);
-    }
-    return 0;
-
-transparency_exit:
-    pdfi_countdown(Key);
-    pdfi_countdown(Value);
-
-error1:
-    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
-    return code;
-}
-
-/*
- * This routine checks a Pattern dictionary to see if it contains any spot
- * colour definitions, or transparency usage.
- */
-int pdfi_check_Pattern(pdf_context *ctx, pdf_dict *pattern, pdf_dict *page_dict, bool *transparent, int *num_spots)
-{
-    int code = 0;
-    pdf_obj *o = NULL;
-
-    if (num_spots != NULL) {
-        code = pdfi_dict_knownget_type(ctx, pattern, "Shading", PDF_DICT, &o);
-        if (code > 0)
-            (void)pdfi_check_Shading(ctx, (pdf_dict *)o, page_dict, num_spots);
-        pdfi_countdown(o);
-        o = NULL;
-    }
-
-    code = pdfi_dict_knownget_type(ctx, pattern, "Resources", PDF_DICT, &o);
-    if (code > 0)
-        (void)pdfi_check_Resources_for_transparency(ctx, (pdf_dict *)o, page_dict, transparent, num_spots);
-    pdfi_countdown(o);
-    o = NULL;
-    if (*transparent == true && num_spots == NULL)
-        goto transparency_exit;
-
-    code = pdfi_dict_knownget_type(ctx, pattern, "ExtGState", PDF_DICT, &o);
-    if (code > 0)
-        (void)pdfi_check_ExtGState(ctx, (pdf_dict *)o, page_dict, transparent, num_spots);
-    pdfi_countdown(o);
-    o = NULL;
-
-transparency_exit:
-    return 0;
-}
-
-/*
- * Check the Resources dictionary Pattern entry.
- */
-static int pdfi_check_Pattern_dict(pdf_context *ctx, pdf_dict *pattern_dict, pdf_dict *page_dict, bool *transparent, int *num_spots)
-{
-    int code, i, index;
-    pdf_obj *Key = NULL, *Value = NULL;
-
-    if (pdfi_dict_entries(pattern_dict) > 0) {
-        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the Pattern dictionary loop */
-        if (code < 0)
-            return code;
-
-        code = pdfi_dict_first(ctx, pattern_dict, &Key, &Value, (void *)&index);
-        if (code < 0)
-            goto error1;
-
-        if (Value->type != PDF_DICT)
-            goto transparency_exit;
-
-        i = 1;
-        do {
-            code = pdfi_check_Pattern(ctx, (pdf_dict *)Value, page_dict, transparent, num_spots);
-            if (code < 0)
-                goto transparency_exit;
-
-            pdfi_countdown(Key);
-            Key = NULL;
-            pdfi_countdown(Value);
-            Value = NULL;
-            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */
-
-            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Shading dictionary loop */
-            if (code < 0)
-                goto error1;
-
-            do {
-                if (i++ >= pdfi_dict_entries(pattern_dict)) {
-                    code = 0;
-                    goto transparency_exit;
-                }
-
-                code = pdfi_dict_next(ctx, pattern_dict, &Key, &Value, (void *)&index);
-                if (code == 0 && Value->type == PDF_DICT)
-                    break;
-                pdfi_countdown(Key);
-                Key = NULL;
-                pdfi_countdown(Value);
-                Value = NULL;
-            } while(1);
-        }while (1);
-    }
-    return 0;
-
-transparency_exit:
-    pdfi_countdown(Key);
-    pdfi_countdown(Value);
-
-error1:
-    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
-    return code;
-}
-
-/*
- * This routine checks a Font dictionary to see if it contains any spot
- * colour definitions, or transparency usage.
- */
-static int pdfi_check_Font(pdf_context *ctx, pdf_dict *font, pdf_dict *page_dict, bool *transparent, int *num_spots)
-{
-    int code = 0;
-    pdf_obj *o = NULL;
-
-    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_for_transparency(ctx, (pdf_dict *)o, page_dict, transparent, num_spots);
-        }
-    }
-    pdfi_countdown(o);
-    o = NULL;
-
-    return 0;
-}
-
-/*
- * Check the Resources dictionary Font entry.
- */
-static int pdfi_check_Font_dict(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *page_dict, bool *transparent, int *num_spots)
-{
-    int code, i, index;
-    pdf_obj *Key = NULL, *Value = NULL;
-
-    if (pdfi_dict_entries(font_dict) > 0) {
-        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the Font dictionary loop */
-        if (code < 0)
-            return code;
-
-        code = pdfi_dict_first(ctx, font_dict, &Key, &Value, (void *)&index);
-        if (code < 0)
-            goto error1;
-
-        i = 1;
-        do {
-            code = pdfi_check_Font(ctx, (pdf_dict *)Value, page_dict, transparent, num_spots);
-
-            pdfi_countdown(Key);
-            Key = NULL;
-            pdfi_countdown(Value);
-            Value = NULL;
-
-            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Font dictionary loop */
-
-            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Font dictionary loop */
-            if (code < 0)
-                goto error1;
-
-            do {
-                if (i++ >= pdfi_dict_entries(font_dict)) {
-                    code = 0;
-                    goto transparency_exit;
-                }
-
-                code = pdfi_dict_next(ctx, font_dict, &Key, &Value, (void *)&index);
-                if (code == 0 && Value->type == PDF_DICT)
-                    break;
-                pdfi_countdown(Key);
-                Key = NULL;
-                pdfi_countdown(Value);
-                Value = NULL;
-            } while(1);
-        }while (1);
-    }
-    return 0;
-
-transparency_exit:
-    pdfi_countdown(Key);
-    pdfi_countdown(Value);
-
-error1:
-    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
-    return code;
-}
-
-static int pdfi_check_Resources_for_transparency(pdf_context *ctx, pdf_dict *Resources_dict, pdf_dict *page_dict, bool *transparent, int *num_spots)
-{
-    int code;
-    pdf_obj *d = NULL;
-
-    /* First up, check any colour spaces, for new spot colours.
-     * We only do this if asked because its expensive. num_spots being NULL
-     * means we aren't interested in spot colours (not a DeviceN or Separation device)
-     */
-    if (num_spots != NULL) {
-        code = pdfi_dict_knownget_type(ctx, Resources_dict, "ColorSpace", PDF_DICT, &d);
-        if (code > 0)
-            (void)pdfi_check_ColorSpace_dict(ctx, (pdf_dict *)d, page_dict, num_spots);
-
-        pdfi_countdown(d);
-        d = NULL;
-
-        code = pdfi_dict_knownget_type(ctx, Resources_dict, "Shading", PDF_DICT, &d);
-        if (code > 0)
-            (void)pdfi_check_Shading_dict(ctx, (pdf_dict *)d, page_dict, num_spots);
-        pdfi_countdown(d);
-        d = NULL;
-    }
-
-    code = pdfi_dict_knownget_type(ctx, Resources_dict, "XObject", PDF_DICT, &d);
-    if (code > 0)
-        (void)pdfi_check_XObject_dict(ctx, (pdf_dict *)d, page_dict, transparent, num_spots);
-    pdfi_countdown(d);
-    d = NULL;
-
-    code = pdfi_dict_knownget_type(ctx, Resources_dict, "Pattern", PDF_DICT, &d);
-    if (code > 0)
-        (void)pdfi_check_Pattern_dict(ctx, (pdf_dict *)d, page_dict, transparent, num_spots);
-    pdfi_countdown(d);
-    d = NULL;
-
-    code = pdfi_dict_knownget_type(ctx, Resources_dict, "Font", PDF_DICT, &d);
-    if (code > 0)
-        (void)pdfi_check_Font_dict(ctx, (pdf_dict *)d, page_dict, transparent, num_spots);
-    /* From this point onwards, if we detect transparency (or have already detected it) we
-     * can exit, we have already counted up any spot colours.
-     */
-    pdfi_countdown(d);
-    d = NULL;
-
-    code = pdfi_dict_knownget_type(ctx, Resources_dict, "ExtGState", PDF_DICT, &d);
-    if (code > 0)
-        (void)pdfi_check_ExtGState_dict(ctx, (pdf_dict *)d, page_dict, transparent, num_spots);
-    pdfi_countdown(d);
-    d = NULL;
-
-    return 0;
-}
-
-static int pdfi_check_annot_for_transparency(pdf_context *ctx, pdf_dict *annot, pdf_dict *page_dict, bool *transparent, int *num_spots)
-{
-    int code;
-    pdf_name *n;
-    pdf_dict *ap = NULL, *N = NULL, *Resources = NULL;
-    double f;
-
-    /* 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.
-     */
-    code = pdfi_dict_knownget_type(ctx, annot, "AP", PDF_DICT, (pdf_obj **)&ap);
-    if (code > 0)
-    {
-        code = pdfi_dict_knownget_type(ctx, ap, "N", PDF_DICT, (pdf_obj **)&N);
-        if (code > 0) {
-            code = pdfi_dict_knownget_type(ctx, N, "Resources", PDF_DICT, (pdf_obj **)&Resources);
-            if (code > 0)
-                code = pdfi_check_Resources_for_transparency(ctx, (pdf_dict *)Resources, page_dict, transparent, num_spots);
-        }
-    }
-    pdfi_countdown(ap);
-    pdfi_countdown(N);
-    pdfi_countdown(Resources);
-    if (code < 0)
-        return code;
-    /* We've checked the Resources, and nothing else in an annotation can define spot colours, so
-     * if we detected transparency in the Resources we need not check further.
-     */
-    if (*transparent == true)
-        return 0;
-
-    code = pdfi_dict_get_type(ctx, annot, "Subtype", PDF_NAME, (pdf_obj **)&n);
-    if (code < 0) {
-        if (ctx->pdfstoponerror)
-            return code;
-    } else {
-        /* Check #2, Highlight annotations are always preformed with transparency */
-        if (pdfi_name_is((const pdf_name *)n, "Highlight")) {
-            pdfi_countdown(n);
-            *transparent = true;
-            return 0;
-        }
-        pdfi_countdown(n);
-        n = NULL;
-
-        /* Check #3 Blend Mode (BM) not being 'Normal' or 'Compatible' */
-        code = pdfi_dict_knownget_type(ctx, annot, "BM", PDF_NAME, (pdf_obj **)&n);
-        if (code > 0) {
-            if (!pdfi_name_is((const pdf_name *)n, "Normal")) {
-                if (!pdfi_name_is((const pdf_name *)n, "Compatible")) {
-                    pdfi_countdown(n);
-                    *transparent = true;
-                    return 0;
-                }
-            }
-            code = 0;
-        }
-        pdfi_countdown(n);
-        if (code < 0)
-            return code;
-
-        /* Check #4 stroke constant alpha (CA) is not 1 (100% opaque) */
-        code = pdfi_dict_knownget_number(ctx, annot, "CA", &f);
-        if (code > 0) {
-            if (f != 1.0) {
-                *transparent = true;
-                return 0;
-            }
-        }
-        if (code < 0)
-            return code;
-
-        /* Check #5 non-stroke constant alpha (ca) is not 1 (100% opaque) */
-        code = pdfi_dict_knownget_number(ctx, annot, "ca", &f);
-        if (code > 0) {
-            if (f != 1.0) {
-                *transparent = true;
-                return 0;
-            }
-        }
-        if (code < 0)
-            return code;
-    }
-
-    return 0;
-}
-
-static int pdfi_check_Annots_for_transparency(pdf_context *ctx, pdf_array *annots_array, pdf_dict *page_dict, bool *transparent, int *num_spots)
-{
-    int i, code = 0;
-    pdf_dict *annot = NULL;
-
-    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) {
-            code = pdfi_check_annot_for_transparency(ctx, annot, page_dict, transparent, num_spots);
-            if (code < 0 && ctx->pdfstoponerror)
-                goto exit;
-
-            /* If we've found transparency, and don't need to continue checkign for spot colours
-             * just exit as fast as possible.
-             */
-            if (*transparent == true && num_spots == NULL)
-                goto exit;
-
-            pdfi_countdown(annot);
-            annot = NULL;
-        }
-    }
-exit:
-    pdfi_countdown(annot);
-    return code;
-}
-
-/* From the original PDF interpreter written in PostScript:
- * Note: we deliberately don't check to see whether a Group is defined,
- * because Adobe Illustrator 10 (and possibly other applications) define
- * a page-level group whether transparency is actually used or not.
- * Ignoring the presence of Group is justified because, in the absence
- * of any other transparency features, they have no effect.
- */
-static int pdfi_check_page_transparency(pdf_context *ctx, pdf_dict *page_dict, bool *transparent, int *spots)
-{
-    int code;
-    pdf_obj *d = NULL;
-    int num_spots = 0;
-    pdf_dict *group_dict = NULL;
-
-    /* Note that we don't reset 'spots' to 0 because these accumulate across pages */
-    *transparent = false;
-
-    ctx->spot_capable_device = false;
-    gs_c_param_list_read(&ctx->pdfi_param_list);
-    code = param_read_int((gs_param_list *)&ctx->pdfi_param_list, "PageSpotColors", &ctx->spot_capable_device);
-    if (code < 0)
-        return code;
-    if (code > 0)
-        ctx->spot_capable_device = 0;
-    else
-        ctx->spot_capable_device = 1;
-
-    /* Check if the page dictionary has a page Group entry */
-    code = pdfi_dict_knownget_type(ctx, page_dict, "Group", PDF_DICT, (pdf_obj **)&group_dict);
-    if (code > 0) {
-        pdf_obj *CS = NULL;
-
-        /* Page group means the page is transparent but we ignore it for the purposes
-         * of transparency detection. See above.
-         */
-        if (ctx->spot_capable_device) {
-            /* Check the Group to see if it has a ColorSpace (CS) and if it
-             * does whether it has any spot colours */
-            code = pdfi_dict_knownget(ctx, group_dict, "CS", &CS);
-            if (code > 0) {
-                code = pdfi_check_ColorSpace_for_spots(ctx, CS, group_dict, page_dict, spots);
-                if (code < 0 && ctx->pdfstoponerror) {
-                    pdfi_countdown(CS);
-                    pdfi_countdown(group_dict);
-                    pdfi_countdown(ctx->SpotNames);
-                    return code;
-                }
-                pdfi_countdown(CS);
-            }
-        }
-        pdfi_countdown(group_dict);
-    }
-
-    /* Now check any Resources dictionary in the Page dictionary */
-    code = pdfi_dict_get_type(ctx, page_dict, "Resources", PDF_DICT, &d);
-    if (code >= 0) {
-        if (ctx->spot_capable_device)
-            code = pdfi_check_Resources_for_transparency(ctx, (pdf_dict *)d, page_dict, transparent, &num_spots);
-        else
-            code = pdfi_check_Resources_for_transparency(ctx, (pdf_dict *)d, page_dict, transparent, NULL);
-        pdfi_countdown(d);
-        if (code < 0)
-            return code;
-    }
-    if (code < 0 && code != gs_error_undefined && ctx->pdfstoponerror)
-        return code;
-
-    /* If we are drawing Annotations, check to see if the page uses any Annots */
-    if (ctx->showannots) {
-        code = pdfi_dict_get_type(ctx, page_dict, "Annots", PDF_ARRAY, &d);
-        if (code >= 0) {
-            if (ctx->spot_capable_device)
-                code = pdfi_check_Annots_for_transparency(ctx, (pdf_array *)d, page_dict, transparent, &num_spots);
-            else
-                code = pdfi_check_Annots_for_transparency(ctx, (pdf_array *)d, page_dict, transparent, NULL);
-            pdfi_countdown(d);
-            if (code < 0)
-                return code;
-        }
-        if (code < 0 && code != gs_error_undefined && ctx->pdfstoponerror)
-            return code;
-    }
-
-    if (ctx->spot_capable_device)
-        *spots += num_spots;
-    else
-        *spots = 0;
-
-    return 0;
-}
-
-static int pdfi_check_transparency_spots(pdf_context *ctx)
-{
-    int code = 0;
-
-    /* Loop round all the pages looking for spot colours and transparency. We only check
-     * for spot colours if the device is capable of spot colours (the code checks). We
-     * only store the transparency setting if NOTRANSPARENCY is not set
-     */
-    if (ctx->num_pages) {
-        pdf_dict *page_dict = NULL;
-        uint64_t page_offset = 0;
-        bool uses_transparency = false;
-        int spots = 0;
-        uint64_t ix;
-
-        int bytes = (int)ceil((float)ctx->num_pages / 8.0f);
-        ctx->PageTransparencyArray = (char *)gs_alloc_bytes(ctx->memory, bytes, "pdfi_process_file, allocate page transparency tracking array");
-        memset(ctx->PageTransparencyArray, 0x00, bytes);
-
-        code = pdfi_alloc_object(ctx, PDF_DICT, 32, (pdf_obj **)&ctx->SpotNames);
-        if (code < 0)
-            goto exit;
-
-        pdfi_countup(ctx->SpotNames);
-
-        for (ix=0;ix < ctx->num_pages;ix++) {
-            if (ctx->pdfdebug)
-                dmprintf1(ctx->memory, "%% Checking Page %"PRIi64" for transparency and spot plates\n", ix + 1);
-
-            uses_transparency = false;
-
-            /* Get the page dictionary */
-            code = pdfi_loop_detector_mark(ctx);
-            if (code < 0)
-                goto exit;
-
-            code = pdfi_loop_detector_add_object(ctx, ctx->Pages->object_num);
-            if (code < 0) {
-                pdfi_loop_detector_cleartomark(ctx);
-                goto exit;
-            }
-
-            page_offset = 0;
-            code = pdfi_get_page_dict(ctx, ctx->Pages, ix, &page_offset, &page_dict, NULL);
-            pdfi_loop_detector_cleartomark(ctx);
-            if (code < 0) {
-                if (code == gs_error_VMerror || ctx->pdfstoponerror)
-                    goto exit;
-                return 0;
-            }
-
-            if (code > 0) {
-                /* This can happen if the number of declared pages (/Count) is larger than the number of pages in the Pages array
-                 * We'll ignore that for the purposes of transparency checking. If the user tries to render this
-                 * page it'll fail in the page rendering code.
-                 */
-                pdfi_countdown(page_dict);
-                page_dict = NULL;
-                break;
-            }
-
-            /* Check the page dictionary for spots and transparency */
-            code = pdfi_check_page_transparency(ctx, page_dict, &uses_transparency, &spots);
-            if (code < 0) {
-                pdfi_countdown(ctx->SpotNames);
-                pdfi_countdown(page_dict);
-                ctx->SpotNames = NULL;
-                goto exit;
-            }
-            if (uses_transparency && !ctx->notransparency) {
-                uint64_t index = ix >> 3;
-                char value = 0x80 >> (ix % 8);
-
-                ctx->PageTransparencyArray[index] |= value;
-            }
-            pdfi_countdown(page_dict);
-            page_dict = NULL;
-        }
-
-        pdfi_countdown(ctx->SpotNames);
-        ctx->SpotNames = NULL;
-
-        /* If there are spot colours (and by inference, the device renders spot plates) then
-         * send the number of Spots to the device, so it can setup correctly.
-         */
-        if (spots > 0) {
-            gs_c_param_list_write(&ctx->pdfi_param_list, ctx->memory);
-            param_write_int((gs_param_list *)&ctx->pdfi_param_list, "PageSpotColors", &spots);
-            gs_c_param_list_read(&ctx->pdfi_param_list);
-            code = gs_putdeviceparams(ctx->pgs->device, (gs_param_list *)&ctx->pdfi_param_list);
-            if (code > 0) {
-                /* The device was closed, we need to reopen it */
-                code = gs_setdevice_no_erase(ctx->pgs, ctx->pgs->device);
-                if (code < 0) {
-                    if (uses_transparency)
-                        (void)gs_abort_pdf14trans_device(ctx->pgs);
-                    pdfi_countdown(ctx->SpotNames);
-                    pdfi_countdown(page_dict);
-                    goto exit;
-                }
-                gs_erasepage(ctx->pgs);
-            }
-        }
-    }
-exit:
-    return code;
-}
-
-static int pdfi_set_media_size(pdf_context *ctx, pdf_dict *page_dict)
-{
-    gs_c_param_list list;
-    gs_param_float_array fa;
-    pdf_array *a = NULL, *default_media = NULL;
-    float fv[2];
-    double d[4];
-    int code;
-    uint64_t i;
-    int64_t rotate = 0;
-
-    gs_c_param_list_write(&list, ctx->memory);
-
-    code = pdfi_dict_get_type(ctx, page_dict, "MediaBox", PDF_ARRAY, (pdf_obj **)&default_media);
-    if (code < 0) {
-        code = gs_erasepage(ctx->pgs);
-        return 0;
-    }
-
-    if (ctx->usecropbox) {
-        if (a != NULL)
-            pdfi_countdown(a);
-        (void)pdfi_dict_get_type(ctx, page_dict, "CropBox", PDF_ARRAY, (pdf_obj **)&a);
-    }
-    if (ctx->useartbox) {
-        if (a != NULL)
-            pdfi_countdown(a);
-        (void)pdfi_dict_get_type(ctx, page_dict, "ArtBox", PDF_ARRAY, (pdf_obj **)&a);
-    }
-    if (ctx->usebleedbox) {
-        if (a != NULL)
-            pdfi_countdown(a);
-        (void)pdfi_dict_get_type(ctx, page_dict, "BleedBox", PDF_ARRAY, (pdf_obj **)&a);
-    }
-    if (ctx->usetrimbox) {
-        if (a != NULL)
-            pdfi_countdown(a);
-        (void)pdfi_dict_get_type(ctx, page_dict, "MediaBox", PDF_ARRAY, (pdf_obj **)&a);
-    }
-    if (a == NULL)
-        a = default_media;
-
-    for (i=0;i<4;i++) {
-        code = pdfi_array_get_number(ctx, a, i, &d[i]);
-    }
-    pdfi_countdown(a);
-
-    normalize_rectangle(d);
-    memcpy(ctx->PageSize, d, 4 * sizeof(double));
-
-    code = pdfi_dict_get_int(ctx, page_dict, "Rotate", &rotate);
-
-    rotate = rotate % 360;
-
-    switch(rotate) {
-        default:
-        case 0:
-        case 360:
-        case -180:
-        case 180:
-            fv[0] = (float)(d[2] - d[0]);
-            fv[1] = (float)(d[3] - d[1]);
-            break;
-        case -90:
-        case 90:
-        case -270:
-        case 270:
-            fv[1] = (float)(d[2] - d[0]);
-            fv[0] = (float)(d[3] - d[1]);
-            break;
-    }
-
-    fa.persistent = false;
-    fa.data = fv;
-    fa.size = 2;
-
-    code = param_write_float_array((gs_param_list *)&list, ".MediaSize", &fa);
-    if (code >= 0)
-    {
-        gx_device *dev = gs_currentdevice(ctx->pgs);
-
-        gs_c_param_list_read(&list);
-        code = gs_putdeviceparams(dev, (gs_param_list *)&list);
-        if (code < 0) {
-            gs_c_param_list_release(&list);
-            return code;
-        }
-    }
-    gs_c_param_list_release(&list);
-
-    gs_initgraphics(ctx->pgs);
-
-    switch(rotate) {
-        case 0:
-            break;
-        case -270:
-        case 90:
-            gs_translate(ctx->pgs, 0, fv[1]);
-            gs_rotate(ctx->pgs, -90);
-            break;
-        case -180:
-        case 180:
-            gs_translate(ctx->pgs, fv[0], fv[1]);
-            gs_rotate(ctx->pgs, 180);
-            break;
-        case -90:
-        case 270:
-            gs_translate(ctx->pgs, fv[0], 0);
-            gs_rotate(ctx->pgs, 90);
-            break;
-        default:
-            break;
-    }
-
-    gs_translate(ctx->pgs, d[0] * -1, d[1] * -1);
-
-    code = gs_erasepage(ctx->pgs);
-    return 0;
-}
+#include "pdf_page.h"
+#include "pdf_check.h"
 
 /*
  * Convenience routine to check if a given string exists in a dictionary
@@ -1429,299 +296,6 @@ static int pdfi_output_page_info(pdf_context *ctx, uint64_t page_num)
     return 0;
 }
 
-static int pdfi_process_page_contents(pdf_context *ctx, pdf_dict *page_dict)
-{
-    int i, code = 0;
-    pdf_obj *o, *o1;
-
-    code = pdfi_dict_get(ctx, page_dict, "Contents", &o);
-    if (code == gs_error_undefined)
-        /* Don't throw an error if there are no contents, just render nothing.... */
-        return 0;
-    if (code < 0)
-        return code;
-
-    if (o->type == PDF_INDIRECT) {
-        if (((pdf_indirect_ref *)o)->ref_object_num == page_dict->object_num)
-            return_error(gs_error_circular_reference);
-
-        code = pdfi_dereference(ctx, ((pdf_indirect_ref *)o)->ref_object_num, ((pdf_indirect_ref *)o)->ref_generation_num, &o1);
-        pdfi_countdown(o);
-        if (code < 0) {
-            if (code == gs_error_VMerror)
-                return code;
-            return 0;
-        }
-        o = o1;
-    }
-
-    code = pdfi_gsave(ctx);
-    if (code < 0) {
-        pdfi_countdown(o);
-        return code;
-    }
-
-    if (o->type == PDF_ARRAY) {
-        pdf_array *a = (pdf_array *)o;
-
-        for (i=0;i < pdfi_array_size(a); i++) {
-            pdf_indirect_ref *r;
-            code = pdfi_array_get_no_deref(ctx, a, i, (pdf_obj **)&r);
-            if (code < 0)
-                goto page_error;
-            if (r->type == PDF_DICT) {
-                code = pdfi_interpret_content_stream(ctx, (pdf_dict *)r, page_dict);
-                pdfi_countdown(r);
-                if (code < 0)
-                    goto page_error;
-            } else {
-                if (r->type != PDF_INDIRECT) {
-                        pdfi_countdown(r);
-                        code = gs_note_error(gs_error_typecheck);
-                        goto page_error;
-                } else {
-                    if (r->ref_object_num == page_dict->object_num) {
-                        pdfi_countdown(r);
-                        code = gs_note_error(gs_error_circular_reference);
-                        goto page_error;
-                    }
-                    code = pdfi_dereference(ctx, r->ref_object_num, r->ref_generation_num, &o1);
-                    pdfi_countdown(r);
-                    if (code < 0) {
-                        if (code != gs_error_VMerror || ctx->pdfstoponerror == false)
-                            code = 0;
-                        goto page_error;
-                    }
-                    if (o1->type != PDF_DICT) {
-                        pdfi_countdown(o1);
-                        code = gs_note_error(gs_error_typecheck);
-                        goto page_error;
-                    }
-                    code = pdfi_interpret_content_stream(ctx, (pdf_dict *)o1, page_dict);
-                    pdfi_countdown(o1);
-                    if (code < 0) {
-                        if (code == gs_error_VMerror || ctx->pdfstoponerror == true)
-                            goto page_error;
-                    }
-                }
-            }
-        }
-    } else {
-        if (o->type == PDF_DICT) {
-            code = pdfi_interpret_content_stream(ctx, (pdf_dict *)o, page_dict);
-        } else {
-            pdfi_countdown(o);
-            return_error(gs_error_typecheck);
-        }
-    }
-page_error:
-    pdfi_grestore(ctx);
-    pdfi_countdown(o);
-    return code;
-}
-
-static int pdfi_render_page(pdf_context *ctx, uint64_t page_num)
-{
-    int code, code1=0;
-    uint64_t page_offset = 0;
-    pdf_dict *page_dict = NULL;
-    bool uses_transparency = false;
-    bool page_group_known = false;
-    int page_index = page_num >> 3;
-    char page_bit = 0x80 >> (page_num % 8);
-    stream_save local_entry_save;
-    pdf_dict *group_dict = NULL;
-
-    if (page_num > ctx->num_pages)
-        return_error(gs_error_rangecheck);
-
-    if (ctx->pdfdebug)
-        dmprintf1(ctx->memory, "%% Processing Page %"PRIi64" content stream\n", page_num + 1);
-
-    code = pdfi_loop_detector_mark(ctx);
-    if (code < 0)
-        return code;
-
-    code = pdfi_loop_detector_add_object(ctx, ctx->Pages->object_num);
-    if (code < 0) {
-        pdfi_loop_detector_cleartomark(ctx);
-        goto exit2;
-    }
-
-    code = pdfi_get_page_dict(ctx, ctx->Pages, page_num, &page_offset, &page_dict, NULL);
-    pdfi_loop_detector_cleartomark(ctx);
-    if (code < 0) {
-        if (code == gs_error_VMerror || ctx->pdfstoponerror)
-            goto exit2;
-        code = 0;
-        goto exit2;
-    }
-
-    if (code > 0) {
-        code = gs_note_error(gs_error_unknownerror);
-        goto exit2;
-    }
-
-    code = pdfi_set_media_size(ctx, page_dict);
-    if (code < 0) {
-        goto exit2;
-    }
-
-    code = pdfi_dict_knownget_type(ctx, page_dict, "Group", PDF_DICT, (pdf_obj **)&group_dict);
-    if (code < 0)
-        goto exit2;
-    if (group_dict != NULL)
-        page_group_known = true;
-
-    pdfi_countdown(ctx->CurrentPageDict);
-    ctx->CurrentPageDict = page_dict;
-    pdfi_countup(ctx->CurrentPageDict);
-
-    code = gs_setstrokeconstantalpha(ctx->pgs, 1.0);
-    code = gs_setfillconstantalpha(ctx->pgs, 1.0);
-    code = gs_setalphaisshape(ctx->pgs, 0);
-    code = gs_setblendmode(ctx->pgs, BLEND_MODE_Compatible);
-    code = gs_settextknockout(ctx->pgs, true);
-    code = gs_settextspacing(ctx->pgs, (double)0.0);
-    code = gs_settextleading(ctx->pgs, (double)0.0);
-    gs_settextrenderingmode(ctx->pgs, 0);
-    code = gs_setwordspacing(ctx->pgs, (double)0.0);
-    code = gs_settexthscaling(ctx->pgs, (double)100.0);
-    ctx->TextBlockDepth = 0;
-
-    if (ctx->pgs->set_transfer.red == 0x00) {
-        ctx->DefaultTransfers[0].proc = gs_identity_transfer;
-        memset(ctx->DefaultTransfers[0].values, 0x00, transfer_map_size * sizeof(frac));
-    } else {
-        ctx->DefaultTransfers[0].proc = ctx->pgs->set_transfer.red->proc;
-        memcpy(ctx->DefaultTransfers[0].values, ctx->pgs->set_transfer.red->values, transfer_map_size * sizeof(frac));
-    }
-    if (ctx->pgs->set_transfer.green == 0x00) {
-        ctx->DefaultTransfers[1].proc = gs_identity_transfer;
-        memset(ctx->DefaultTransfers[1].values, 0x00, transfer_map_size * sizeof(frac));
-    } else {
-        ctx->DefaultTransfers[1].proc = ctx->pgs->set_transfer.green->proc;
-        memcpy(ctx->DefaultTransfers[1].values, ctx->pgs->set_transfer.green->values, transfer_map_size * sizeof(frac));
-    }
-    if (ctx->pgs->set_transfer.blue == 0x00) {
-        ctx->DefaultTransfers[2].proc = gs_identity_transfer;
-        memset(ctx->DefaultTransfers[2].values, 0x00, transfer_map_size * sizeof(frac));
-    } else {
-        ctx->DefaultTransfers[2].proc = ctx->pgs->set_transfer.blue->proc;
-        memcpy(ctx->DefaultTransfers[2].values, ctx->pgs->set_transfer.blue->values, transfer_map_size * sizeof(frac));
-    }
-    if (ctx->pgs->set_transfer.gray == 0x00) {
-        ctx->DefaultTransfers[3].proc = gs_identity_transfer;
-        memset(ctx->DefaultTransfers[3].values, 0x00, transfer_map_size * sizeof(frac));
-    } else {
-        ctx->DefaultTransfers[3].proc = ctx->pgs->set_transfer.gray->proc;
-        memcpy(ctx->DefaultTransfers[3].values, ctx->pgs->set_transfer.gray->values, transfer_map_size * sizeof(frac));
-    }
-    if (ctx->pgs->black_generation == 0x00) {
-        ctx->DefaultBG.proc = gs_identity_transfer;
-        memset(ctx->DefaultBG.values, 0x00, transfer_map_size * sizeof(frac));
-    } else {
-        ctx->DefaultBG.proc = ctx->pgs->black_generation->proc;
-        memcpy(ctx->DefaultBG.values, ctx->pgs->black_generation->values, transfer_map_size * sizeof(frac));
-    }
-    if (ctx->pgs->undercolor_removal == 0x00) {
-        ctx->DefaultUCR.proc = gs_identity_transfer;
-        memset(ctx->DefaultUCR.values, 0x00, transfer_map_size * sizeof(frac));
-    } else {
-        ctx->DefaultUCR.proc = ctx->pgs->undercolor_removal->proc;
-        memcpy(ctx->DefaultUCR.values, ctx->pgs->undercolor_removal->values, transfer_map_size * sizeof(frac));
-    }
-    dbgmprintf1(ctx->memory, "Current page transparency setting is %d\n", ctx->PageTransparencyArray[page_index] & page_bit ? 1 : 0);
-
-    /* Force NOTRANSPARENCY here if required, until we can get it working... */
-    ctx->page_has_transparency = ctx->PageTransparencyArray[page_index] & page_bit ? 1 : 0;
-
-    /* Set whether device needs OP support
-     * This needs to be before transparency device is pushed, if applicable
-     */
-    pdfi_trans_set_needs_OP(ctx);
-    pdfi_oc_init(ctx);
-
-    pdfi_gsave(ctx);
-
-    if (ctx->page_has_transparency) {
-        if (code >= 0) {
-            /* We don't retain the PDF14 device */
-            code = gs_push_pdf14trans_device(ctx->pgs, false, false);
-            if (code >= 0) {
-                if (page_group_known) {
-                    code = pdfi_trans_begin_page_group(ctx, page_dict, group_dict);
-                    /* If setting the page group failed for some reason, abandon the page group, but continue with the page */
-                    if (code < 0)
-                        page_group_known = false;
-                }
-            } else {
-                /* Couldn't push the transparency compositor.
-                 * This is probably fatal, but attempt to recover by abandoning transparency
-                 */
-                ctx->page_has_transparency = uses_transparency = false;
-            }
-        } else {
-            /* Couldn't gsave round the transparency compositor.
-             * This is probably fatal, but attempt to recover by abandoning transparency
-             */
-            ctx->page_has_transparency = uses_transparency = false;
-        }
-    }
-
-    /* Init a base_pgs graphics state for Patterns
-     * (this has to be after transparency device pushed, if applicable)
-     */
-    pdfi_set_DefaultQState(ctx, ctx->pgs);
-
-    /* Save the current stream state, for later cleanup, in a local variable */
-    local_save_stream_state(ctx, &local_entry_save);
-    initialise_stream_save(ctx);
-
-    code = pdfi_process_page_contents(ctx, page_dict);
-
-    /* Put our state back the way it was before we ran the contents
-     * and check if the stream had problems
-     */
-#ifdef PROBE_STREAMS
-    if (ctx->pgs->level > ctx->current_stream_save.gsave_level ||
-        pdfi_count_stack(ctx) > ctx->current_stream_save.stack_count)
-        code = ((pdf_context *)0)->first_page;
-#endif
-
-    cleanup_context_interpretation(ctx, &local_entry_save);
-    local_restore_stream_state(ctx, &local_entry_save);
-
-    if (ctx->page_has_transparency && page_group_known) {
-        code1 = pdfi_trans_end_group(ctx);
-    }
-
-    pdfi_countdown(ctx->CurrentPageDict);
-    ctx->CurrentPageDict = NULL;
-
-    if (ctx->page_has_transparency) {
-        if (code1 < 0) {
-            (void)gs_abort_pdf14trans_device(ctx->pgs);
-            goto exit1;
-        }
-
-        code = gs_pop_pdf14trans_device(ctx->pgs, false);
-        if (code < 0) {
-            goto exit1;
-        }
-    }
-
- exit1:
-    pdfi_grestore(ctx);
- exit2:
-    pdfi_countdown(page_dict);
-    pdfi_countdown(group_dict);
-
-    if (code == 0)
-        code = pl_finish_page(ctx->memory->gs_lib_ctx->top_of_system, ctx->pgs, 1, true);
-    return code;
-}
-
 static void
 pdfi_report_errors(pdf_context *ctx)
 {
@@ -1976,7 +550,7 @@ read_root:
         if (ctx->pdfinfo)
             code = pdfi_output_page_info(ctx, i);
         else
-            code = pdfi_render_page(ctx, i);
+            code = pdfi_page_render(ctx, i);
 
         if (code < 0 && ctx->pdfstoponerror)
             goto exit;
diff --git a/pdf/ghostpdf.h b/pdf/ghostpdf.h
index d8e2651..ac90c60 100644
--- a/pdf/ghostpdf.h
+++ b/pdf/ghostpdf.h
@@ -343,7 +343,4 @@ int pdfi_open_pdf_file(pdf_context *ctx, char *filename);
 int pdfi_process_pdf_file(pdf_context *ctx, char *filename);
 int pdfi_close_pdf_file(pdf_context *ctx);
 
-/* Transparency stuff, should be moved */
-int pdfi_check_Pattern(pdf_context *ctx, pdf_dict *pattern, pdf_dict *page_dict, bool *transparent, int *num_spots);
-
 #endif
diff --git a/pdf/pdf.mak b/pdf/pdf.mak
index a9d0082..b758ca2 100644
--- a/pdf/pdf.mak
+++ b/pdf/pdf.mak
@@ -131,6 +131,9 @@ $(PDFOBJ)pdf_misc.$(OBJ): $(PDFSRC)pdf_misc.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDI
 $(PDFOBJ)pdf_optcontent.$(OBJ): $(PDFSRC)pdf_optcontent.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS)
 	$(PDFCCC) $(PDFSRC)pdf_optcontent.c $(PDFO_)pdf_optcontent.$(OBJ)
 
+$(PDFOBJ)pdf_check.$(OBJ): $(PDFSRC)pdf_check.c $(PDFINCLUDES) $(PDF_MAK) $(MAKEDIRS)
+	$(PDFCCC) $(PDFSRC)pdf_check.c $(PDFO_)pdf_check.$(OBJ)
+
 $(PDFGEN)pdfimpl.c: $(PLSRC)plimpl.c $(PDF_MAK) $(MAKEDIRS)
 	$(CP_) $(PLSRC)plimpl.c $(PDFGEN)pdfimpl.c
 
@@ -178,6 +181,7 @@ PDF_OBJS=\
     $(PDFOBJ)pdf_device.$(OBJ)\
     $(PDFOBJ)pdf_misc.$(OBJ)\
     $(PDFOBJ)pdf_optcontent.$(OBJ)\
+    $(PDFOBJ)pdf_check.$(OBJ)\
 
 
 # NB - note this is a bit squirrely.  Right now the pjl interpreter is
diff --git a/pdf/pdf_check.c b/pdf/pdf_check.c
new file mode 100644
index 0000000..ea6bfb1
--- /dev/null
+++ b/pdf/pdf_check.c
@@ -0,0 +1,1043 @@
+/* Copyright (C) 2001-2019 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.
+*/
+
+/* Checks for transparency and spots for the PDF interpreter */
+
+#include "pdf_int.h"
+#include "pdf_stack.h"
+#include "pdf_page.h"
+#include "pdf_file.h"
+#include "pdf_dict.h"
+#include "pdf_array.h"
+#include "pdf_loop_detect.h"
+#include "pdf_colour.h"
+#include "pdf_trans.h"
+#include "pdf_gstate.h"
+#include "pdf_misc.h"
+#include "pdf_check.h"
+
+/* This routine is slightly misnamed, as it also checks ColorSpaces for spot colours.
+ * This is done at the page level, so we maintain a dictionary of the spot colours
+ * encountered so far, which we consult before adding any new ones.
+ */
+static int pdfi_check_Resources_for_transparency(pdf_context *ctx, pdf_dict *Resources_dict, pdf_dict *page_dict, bool *transparent, int *num_spots);
+
+/* For performance and resource reasons we do not want to install the transparency blending
+ * compositor unless we need it. Similarly, if a device handles spot colours it can minimise
+ * memory usage if it knows ahead of time how many spot colours there will be.
+ *
+ * The PDF interpreter written in PostScript performed these as two separate tasks, on opening
+ * a PDF file it would count spot colour usage and then for each page it would chek if the page
+ * used any transparency. The code below is used to check for both transparency and spot colours.
+ * If the int pointer to num_spots is NULL then we aren't interested in spot colours (not supported
+ * by the device), if the int pointer to transparent is NULL then we aren't interested in transparency
+ * (-dNOTRANSPARENCY is set).
+ *
+ * Currenlty the code is run when we open the PDF file, the existence of transparency on any given
+ * page is recorded in a bit array for later use. If it turns out that checking every page when we
+ * open the file is a performance burden then we could do it on a per-page basis instead. NB if
+ * the device supports spot colours then we do need to scan the file before starting any page.
+ *
+ * The technique is fairly straight-forward, we start with each page, and open its Resources
+ * dictionary, we then check by type each possible resource. Some resources (eg Pattern, XObject)
+ * can themselves contain Resources, in which case we recursively check that dictionary. Note; not
+ * all Resource types need to be checked for both transparency and spot colours, some types can
+ * only contain one or the other.
+ *
+ * Routines with the name pdfi_check_xxx_dict are intended to check a Resource dictionary entry, this
+ * will be a dictioanry of names and values, where the values are objects of the given Resource type.
+ *
+ */
+
+/*
+ * Check the Resources dictionary ColorSpace entry. pdfi_check_ColorSpace_for_spots is defined
+ * in pdf_colour.c
+ */
+static int pdfi_check_ColorSpace_dict(pdf_context *ctx, pdf_dict *cspace_dict, pdf_dict *page_dict, int *num_spots)
+{
+    int code, i, index;
+    pdf_obj *Key = NULL, *Value = NULL;
+
+    if (pdfi_dict_entries(cspace_dict) > 0) {
+        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the ColorSpace dictionary loop */
+        if (code < 0)
+            return code;
+
+        code = pdfi_dict_first(ctx, cspace_dict, &Key, &Value, (void *)&index);
+        if (code < 0)
+            goto error1;
+
+        i = 1;
+        do {
+            code = pdfi_check_ColorSpace_for_spots(ctx, Value, cspace_dict, page_dict, num_spots);
+            if (code < 0)
+                goto error2;
+
+            pdfi_countdown(Key);
+            Key = NULL;
+            pdfi_countdown(Value);
+            Value = NULL;
+
+            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */
+
+            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Shading dictionary loop */
+            if (code < 0)
+                goto error1;
+
+            do {
+                if (i++ >= pdfi_dict_entries(cspace_dict)) {
+                    code = 0;
+                    goto transparency_exit;
+                }
+
+                code = pdfi_dict_next(ctx, cspace_dict, &Key, &Value, (void *)&index);
+                if (code == 0 && Value->type == PDF_ARRAY)
+                    break;
+                pdfi_countdown(Key);
+                Key = NULL;
+                pdfi_countdown(Value);
+                Value = NULL;
+            } while(1);
+        }while (1);
+    }
+    return 0;
+
+transparency_exit:
+error2:
+    pdfi_countdown(Key);
+    pdfi_countdown(Value);
+
+error1:
+    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
+    return code;
+}
+
+/*
+ * Process an individual Shading dictionary to see if it contains a ColorSpace with a spot colour
+ */
+static int pdfi_check_Shading(pdf_context *ctx, pdf_dict *shading, pdf_dict *page_dict, int *num_spots)
+{
+    int code;
+    pdf_obj *o = NULL;
+
+    code = pdfi_dict_knownget(ctx, shading, "ColorSpace", (pdf_obj **)&o);
+    if (code > 0) {
+        code = pdfi_check_ColorSpace_for_spots(ctx, o, shading, page_dict, num_spots);
+        pdfi_countdown(o);
+        return code;
+    }
+    return 0;
+}
+
+/*
+ * Check the Resources dictionary Shading entry.
+ */
+static int pdfi_check_Shading_dict(pdf_context *ctx, pdf_dict *shading_dict, pdf_dict *page_dict, int *num_spots)
+{
+    int code, i, index;
+    pdf_obj *Key = NULL, *Value = NULL;
+
+    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, (void *)&index);
+        if (code < 0 || Value->type != PDF_DICT)
+            goto error1;
+
+        i = 1;
+        do {
+            code = pdfi_check_Shading(ctx, (pdf_dict *)Value, page_dict, num_spots);
+            if (code < 0)
+                goto error2;
+
+            pdfi_countdown(Key);
+            Key = NULL;
+            pdfi_countdown(Value);
+            Value = NULL;
+
+            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */
+
+            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Shading dictionary loop */
+            if (code < 0)
+                goto error1;
+
+            do {
+                if (i++ >= pdfi_dict_entries(shading_dict)) {
+                    code = 0;
+                    goto transparency_exit;
+                }
+
+                code = pdfi_dict_next(ctx, shading_dict, &Key, &Value, (void *)&index);
+                if (code == 0 && Value->type == PDF_DICT)
+                    break;
+                pdfi_countdown(Key);
+                Key = NULL;
+                pdfi_countdown(Value);
+                Value = NULL;
+            } while(1);
+        }while (1);
+    }
+    return 0;
+
+transparency_exit:
+error2:
+    pdfi_countdown(Key);
+    pdfi_countdown(Value);
+
+error1:
+    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
+    return code;
+}
+
+/*
+ * This routine checks an XObject to see if it contains any spot
+ * colour definitions, or transparency usage.
+ */
+static int pdfi_check_XObject(pdf_context *ctx, pdf_dict *xobject, pdf_dict *page_dict, bool *transparent, int *num_spots)
+{
+    int code = 0;
+    pdf_name *n = NULL;
+    bool known = false;
+    double f;
+
+    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")) {
+            pdf_obj *CS = NULL;
+
+            pdfi_countdown(n);
+            n = NULL;
+            code = pdfi_dict_known(xobject, "SMask", &known);
+            if (code >= 0) {
+                if (known == true) {
+                    *transparent = true;
+                    if (num_spots == NULL)
+                        goto transparency_exit;
+                }
+                code = pdfi_dict_knownget_number(ctx, xobject, "SMaskInData", &f);
+                if (code > 0) {
+                    code = 0;
+                    if (f != 0.0)
+                        *transparent = true;
+                    if (num_spots == NULL)
+                        goto transparency_exit;
+                }
+                /* Check the image dictionary for a ColorSpace entry, if we are checking spot names */
+                if (num_spots) {
+                    code = pdfi_dict_knownget(ctx, xobject, "ColorSpace", (pdf_obj **)&CS);
+                    if (code > 0) {
+                        /* We don't care if there's an error here, it'll be picked up if we use the ColorSpace later */
+                        (void)pdfi_check_ColorSpace_for_spots(ctx, CS, xobject, page_dict, num_spots);
+                        pdfi_countdown(CS);
+                    }
+                }
+            }
+        } else {
+            if (pdfi_name_is((const pdf_name *)n, "Form")) {
+                pdf_dict *group_dict = NULL, *resource_dict = NULL;
+                pdf_obj *CS = NULL;
+
+                pdfi_countdown(n);
+                code = pdfi_dict_knownget_type(ctx, xobject, "Group", PDF_DICT, (pdf_obj **)&group_dict);
+                if (code > 0) {
+                    *transparent = true;
+                    if (num_spots == NULL) {
+                        pdfi_countdown(group_dict);
+                        goto transparency_exit;
+                    }
+
+                    /* Start a new loop detector group to avoid this being detected in the Resources check below */
+                    code = pdfi_loop_detector_mark(ctx); /* Mark the start of the XObject dictionary loop */
+                    if (code > 0) {
+                        code = pdfi_dict_knownget(ctx, group_dict, "CS", &CS);
+                        if (code > 0)
+                            /* We don't care if there's an error here, it'll be picked up if we use the ColorSpace later */
+                            (void)pdfi_check_ColorSpace_for_spots(ctx, CS, group_dict, page_dict, num_spots);
+                    }
+                    pdfi_countdown(group_dict);
+                    pdfi_countdown(CS);
+                    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the XObject dictionary loop */
+                }
+
+                code = pdfi_dict_knownget_type(ctx, xobject, "Resources", PDF_DICT, (pdf_obj **)&resource_dict);
+                if (code > 0) {
+                    code = pdfi_check_Resources_for_transparency(ctx, resource_dict, page_dict, transparent, num_spots);
+                    pdfi_countdown(resource_dict);
+                    if (code < 0)
+                        goto transparency_exit;
+                }
+            } else
+                pdfi_countdown(n);
+        }
+    }
+
+    return 0;
+
+transparency_exit:
+    return code;
+}
+
+/*
+ * Check the Resources dictionary XObject entry.
+ */
+static int pdfi_check_XObject_dict(pdf_context *ctx, pdf_dict *xobject_dict, pdf_dict *page_dict, bool *transparent, int *num_spots)
+{
+    int code, i, index;
+    pdf_obj *Key = NULL, *Value = NULL; //, *o = NULL;
+
+    if (pdfi_dict_entries(xobject_dict) > 0) {
+        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the XObject dictionary loop */
+        if (code < 0)
+            return code;
+
+        code = pdfi_dict_first(ctx, xobject_dict, &Key, &Value, (void *)&index);
+        if (code < 0 || Value->type != PDF_DICT)
+            goto error1;
+
+        i = 1;
+        do {
+            code = pdfi_check_XObject(ctx, (pdf_dict *)Value, page_dict, transparent, num_spots);
+            if (code < 0)
+                goto error2;
+
+            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the XObject dictionary loop */
+
+            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the XObject dictionary loop */
+            if (code < 0)
+                goto error1;
+
+            pdfi_countdown(Key);
+            Key = NULL;
+            pdfi_countdown(Value);
+            Value = NULL;
+
+            do {
+                if (i++ >= pdfi_dict_entries(xobject_dict)) {
+                    code = 0;
+                    goto transparency_exit;
+                }
+
+                code = pdfi_dict_next(ctx, xobject_dict, &Key, &Value, (void *)&index);
+                if (code == 0 && Value->type == PDF_DICT)
+                    break;
+                pdfi_countdown(Key);
+                Key = NULL;
+                pdfi_countdown(Value);
+                Value = NULL;
+            } while(1);
+        }while(1);
+    }
+    return 0;
+
+transparency_exit:
+error2:
+    pdfi_countdown(Key);
+    pdfi_countdown(Value);
+
+error1:
+    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
+    return code;
+}
+
+/*
+ * This routine checks an ExtGState dictionary to see if it contains any spot
+ * colour definitions, or transparency usage.
+ */
+static int pdfi_check_ExtGState(pdf_context *ctx, pdf_dict *extgstate_dict, pdf_dict *page_dict, bool *transparent, int *num_spots)
+{
+    int code;
+    pdf_obj *o = NULL;
+    double f;
+
+    if (pdfi_dict_entries(extgstate_dict) > 0) {
+        /* Check SMask first, because if we test spot colours first we can exit as
+         * soon as we detect transparency.
+         */
+        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);
+                    *transparent = true;
+                    return 0;
+                }
+            } else {
+                if (o->type == PDF_DICT) {
+                    pdf_obj *G = NULL;
+
+                    *transparent = true;
+
+                    if (num_spots != NULL) {
+                        /* Check if the SMask has a /G (Group) */
+                        code = pdfi_dict_knownget(ctx, (pdf_dict *)o, "G", &G);
+                        if (code > 0) {
+                            code = pdfi_check_XObject(ctx, (pdf_dict *)G, page_dict, transparent, num_spots);
+                            pdfi_countdown(G);
+                        }
+                    }
+                    pdfi_countdown(o);
+                    return code;
+                }
+            }
+        }
+        pdfi_countdown(o);
+        o = NULL;
+
+        code = pdfi_dict_knownget_type(ctx, extgstate_dict, "BM", PDF_NAME, &o);
+        if (code > 0) {
+            if (!pdfi_name_is((pdf_name *)o, "Normal")) {
+                if (!pdfi_name_is((pdf_name *)o, "Compatible")) {
+                    pdfi_countdown(o);
+                    *transparent = true;
+                    return 0;
+                }
+            }
+        }
+        pdfi_countdown(o);
+        o = NULL;
+
+        code = pdfi_dict_knownget_number(ctx, extgstate_dict, "CA", &f);
+        if (code > 0) {
+            if (f != 1.0) {
+                *transparent = true;
+                return 0;
+            }
+        }
+
+        code = pdfi_dict_knownget_number(ctx, extgstate_dict, "ca", &f);
+        if (code > 0) {
+            if (f != 1.0) {
+                *transparent = true;
+                return 0;
+            }
+        }
+    }
+    return 0;
+}
+
+/*
+ * Check the Resources dictionary ExtGState entry.
+ */
+static int pdfi_check_ExtGState_dict(pdf_context *ctx, pdf_dict *extgstate_dict, pdf_dict *page_dict, bool *transparent, int *num_spots)
+{
+    int code, i, index;
+    pdf_obj *Key = NULL, *Value = NULL;
+
+    if (pdfi_dict_entries(extgstate_dict) > 0) {
+        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the ColorSpace dictionary loop */
+        if (code < 0)
+            return code;
+
+        code = pdfi_dict_first(ctx, extgstate_dict, &Key, &Value, (void *)&index);
+        if (code < 0)
+            goto error1;
+
+        i = 1;
+        do {
+
+            (void)pdfi_check_ExtGState(ctx, (pdf_dict *)Value, page_dict, transparent, num_spots);
+            if (*transparent == true && num_spots == NULL)
+                goto transparency_exit;
+
+            pdfi_countdown(Key);
+            Key = NULL;
+            pdfi_countdown(Value);
+            Value = NULL;
+
+            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the ExtGState dictionary loop */
+
+            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the ExtGState dictionary loop */
+            if (code < 0)
+                goto error1;
+
+            do {
+                if (i++ >= pdfi_dict_entries(extgstate_dict)) {
+                    code = 0;
+                    goto transparency_exit;
+                }
+
+                code = pdfi_dict_next(ctx, extgstate_dict, &Key, &Value, (void *)&index);
+                if (code == 0 && Value->type == PDF_DICT)
+                    break;
+                pdfi_countdown(Key);
+                Key = NULL;
+                pdfi_countdown(Value);
+                Value = NULL;
+            } while(1);
+        }while (1);
+    }
+    return 0;
+
+transparency_exit:
+    pdfi_countdown(Key);
+    pdfi_countdown(Value);
+
+error1:
+    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
+    return code;
+}
+
+/*
+ * This routine checks a Pattern dictionary to see if it contains any spot
+ * colour definitions, or transparency usage.
+ */
+int pdfi_check_Pattern(pdf_context *ctx, pdf_dict *pattern, pdf_dict *page_dict, bool *transparent, int *num_spots)
+{
+    int code = 0;
+    pdf_obj *o = NULL;
+
+    if (num_spots != NULL) {
+        code = pdfi_dict_knownget_type(ctx, pattern, "Shading", PDF_DICT, &o);
+        if (code > 0)
+            (void)pdfi_check_Shading(ctx, (pdf_dict *)o, page_dict, num_spots);
+        pdfi_countdown(o);
+        o = NULL;
+    }
+
+    code = pdfi_dict_knownget_type(ctx, pattern, "Resources", PDF_DICT, &o);
+    if (code > 0)
+        (void)pdfi_check_Resources_for_transparency(ctx, (pdf_dict *)o, page_dict, transparent, num_spots);
+    pdfi_countdown(o);
+    o = NULL;
+    if (*transparent == true && num_spots == NULL)
+        goto transparency_exit;
+
+    code = pdfi_dict_knownget_type(ctx, pattern, "ExtGState", PDF_DICT, &o);
+    if (code > 0)
+        (void)pdfi_check_ExtGState(ctx, (pdf_dict *)o, page_dict, transparent, num_spots);
+    pdfi_countdown(o);
+    o = NULL;
+
+transparency_exit:
+    return 0;
+}
+
+/*
+ * Check the Resources dictionary Pattern entry.
+ */
+static int pdfi_check_Pattern_dict(pdf_context *ctx, pdf_dict *pattern_dict, pdf_dict *page_dict, bool *transparent, int *num_spots)
+{
+    int code, i, index;
+    pdf_obj *Key = NULL, *Value = NULL;
+
+    if (pdfi_dict_entries(pattern_dict) > 0) {
+        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the Pattern dictionary loop */
+        if (code < 0)
+            return code;
+
+        code = pdfi_dict_first(ctx, pattern_dict, &Key, &Value, (void *)&index);
+        if (code < 0)
+            goto error1;
+
+        if (Value->type != PDF_DICT)
+            goto transparency_exit;
+
+        i = 1;
+        do {
+            code = pdfi_check_Pattern(ctx, (pdf_dict *)Value, page_dict, transparent, num_spots);
+            if (code < 0)
+                goto transparency_exit;
+
+            pdfi_countdown(Key);
+            Key = NULL;
+            pdfi_countdown(Value);
+            Value = NULL;
+            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */
+
+            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Shading dictionary loop */
+            if (code < 0)
+                goto error1;
+
+            do {
+                if (i++ >= pdfi_dict_entries(pattern_dict)) {
+                    code = 0;
+                    goto transparency_exit;
+                }
+
+                code = pdfi_dict_next(ctx, pattern_dict, &Key, &Value, (void *)&index);
+                if (code == 0 && Value->type == PDF_DICT)
+                    break;
+                pdfi_countdown(Key);
+                Key = NULL;
+                pdfi_countdown(Value);
+                Value = NULL;
+            } while(1);
+        }while (1);
+    }
+    return 0;
+
+transparency_exit:
+    pdfi_countdown(Key);
+    pdfi_countdown(Value);
+
+error1:
+    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
+    return code;
+}
+
+/*
+ * This routine checks a Font dictionary to see if it contains any spot
+ * colour definitions, or transparency usage.
+ */
+static int pdfi_check_Font(pdf_context *ctx, pdf_dict *font, pdf_dict *page_dict, bool *transparent, int *num_spots)
+{
+    int code = 0;
+    pdf_obj *o = NULL;
+
+    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_for_transparency(ctx, (pdf_dict *)o, page_dict, transparent, num_spots);
+        }
+    }
+    pdfi_countdown(o);
+    o = NULL;
+
+    return 0;
+}
+
+/*
+ * Check the Resources dictionary Font entry.
+ */
+static int pdfi_check_Font_dict(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *page_dict, bool *transparent, int *num_spots)
+{
+    int code, i, index;
+    pdf_obj *Key = NULL, *Value = NULL;
+
+    if (pdfi_dict_entries(font_dict) > 0) {
+        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the Font dictionary loop */
+        if (code < 0)
+            return code;
+
+        code = pdfi_dict_first(ctx, font_dict, &Key, &Value, (void *)&index);
+        if (code < 0)
+            goto error1;
+
+        i = 1;
+        do {
+            code = pdfi_check_Font(ctx, (pdf_dict *)Value, page_dict, transparent, num_spots);
+
+            pdfi_countdown(Key);
+            Key = NULL;
+            pdfi_countdown(Value);
+            Value = NULL;
+
+            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Font dictionary loop */
+
+            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Font dictionary loop */
+            if (code < 0)
+                goto error1;
+
+            do {
+                if (i++ >= pdfi_dict_entries(font_dict)) {
+                    code = 0;
+                    goto transparency_exit;
+                }
+
+                code = pdfi_dict_next(ctx, font_dict, &Key, &Value, (void *)&index);
+                if (code == 0 && Value->type == PDF_DICT)
+                    break;
+                pdfi_countdown(Key);
+                Key = NULL;
+                pdfi_countdown(Value);
+                Value = NULL;
+            } while(1);
+        }while (1);
+    }
+    return 0;
+
+transparency_exit:
+    pdfi_countdown(Key);
+    pdfi_countdown(Value);
+
+error1:
+    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
+    return code;
+}
+
+static int pdfi_check_Resources_for_transparency(pdf_context *ctx, pdf_dict *Resources_dict, pdf_dict *page_dict, bool *transparent, int *num_spots)
+{
+    int code;
+    pdf_obj *d = NULL;
+
+    /* First up, check any colour spaces, for new spot colours.
+     * We only do this if asked because its expensive. num_spots being NULL
+     * means we aren't interested in spot colours (not a DeviceN or Separation device)
+     */
+    if (num_spots != NULL) {
+        code = pdfi_dict_knownget_type(ctx, Resources_dict, "ColorSpace", PDF_DICT, &d);
+        if (code > 0)
+            (void)pdfi_check_ColorSpace_dict(ctx, (pdf_dict *)d, page_dict, num_spots);
+
+        pdfi_countdown(d);
+        d = NULL;
+
+        code = pdfi_dict_knownget_type(ctx, Resources_dict, "Shading", PDF_DICT, &d);
+        if (code > 0)
+            (void)pdfi_check_Shading_dict(ctx, (pdf_dict *)d, page_dict, num_spots);
+        pdfi_countdown(d);
+        d = NULL;
+    }
+
+    code = pdfi_dict_knownget_type(ctx, Resources_dict, "XObject", PDF_DICT, &d);
+    if (code > 0)
+        (void)pdfi_check_XObject_dict(ctx, (pdf_dict *)d, page_dict, transparent, num_spots);
+    pdfi_countdown(d);
+    d = NULL;
+
+    code = pdfi_dict_knownget_type(ctx, Resources_dict, "Pattern", PDF_DICT, &d);
+    if (code > 0)
+        (void)pdfi_check_Pattern_dict(ctx, (pdf_dict *)d, page_dict, transparent, num_spots);
+    pdfi_countdown(d);
+    d = NULL;
+
+    code = pdfi_dict_knownget_type(ctx, Resources_dict, "Font", PDF_DICT, &d);
+    if (code > 0)
+        (void)pdfi_check_Font_dict(ctx, (pdf_dict *)d, page_dict, transparent, num_spots);
+    /* From this point onwards, if we detect transparency (or have already detected it) we
+     * can exit, we have already counted up any spot colours.
+     */
+    pdfi_countdown(d);
+    d = NULL;
+
+    code = pdfi_dict_knownget_type(ctx, Resources_dict, "ExtGState", PDF_DICT, &d);
+    if (code > 0)
+        (void)pdfi_check_ExtGState_dict(ctx, (pdf_dict *)d, page_dict, transparent, num_spots);
+    pdfi_countdown(d);
+    d = NULL;
+
+    return 0;
+}
+
+static int pdfi_check_annot_for_transparency(pdf_context *ctx, pdf_dict *annot, pdf_dict *page_dict, bool *transparent, int *num_spots)
+{
+    int code;
+    pdf_name *n;
+    pdf_dict *ap = NULL, *N = NULL, *Resources = NULL;
+    double f;
+
+    /* 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.
+     */
+    code = pdfi_dict_knownget_type(ctx, annot, "AP", PDF_DICT, (pdf_obj **)&ap);
+    if (code > 0)
+    {
+        code = pdfi_dict_knownget_type(ctx, ap, "N", PDF_DICT, (pdf_obj **)&N);
+        if (code > 0) {
+            code = pdfi_dict_knownget_type(ctx, N, "Resources", PDF_DICT, (pdf_obj **)&Resources);
+            if (code > 0)
+                code = pdfi_check_Resources_for_transparency(ctx, (pdf_dict *)Resources, page_dict, transparent, num_spots);
+        }
+    }
+    pdfi_countdown(ap);
+    pdfi_countdown(N);
+    pdfi_countdown(Resources);
+    if (code < 0)
+        return code;
+    /* We've checked the Resources, and nothing else in an annotation can define spot colours, so
+     * if we detected transparency in the Resources we need not check further.
+     */
+    if (*transparent == true)
+        return 0;
+
+    code = pdfi_dict_get_type(ctx, annot, "Subtype", PDF_NAME, (pdf_obj **)&n);
+    if (code < 0) {
+        if (ctx->pdfstoponerror)
+            return code;
+    } else {
+        /* Check #2, Highlight annotations are always preformed with transparency */
+        if (pdfi_name_is((const pdf_name *)n, "Highlight")) {
+            pdfi_countdown(n);
+            *transparent = true;
+            return 0;
+        }
+        pdfi_countdown(n);
+        n = NULL;
+
+        /* Check #3 Blend Mode (BM) not being 'Normal' or 'Compatible' */
+        code = pdfi_dict_knownget_type(ctx, annot, "BM", PDF_NAME, (pdf_obj **)&n);
+        if (code > 0) {
+            if (!pdfi_name_is((const pdf_name *)n, "Normal")) {
+                if (!pdfi_name_is((const pdf_name *)n, "Compatible")) {
+                    pdfi_countdown(n);
+                    *transparent = true;
+                    return 0;
+                }
+            }
+            code = 0;
+        }
+        pdfi_countdown(n);
+        if (code < 0)
+            return code;
+
+        /* Check #4 stroke constant alpha (CA) is not 1 (100% opaque) */
+        code = pdfi_dict_knownget_number(ctx, annot, "CA", &f);
+        if (code > 0) {
+            if (f != 1.0) {
+                *transparent = true;
+                return 0;
+            }
+        }
+        if (code < 0)
+            return code;
+
+        /* Check #5 non-stroke constant alpha (ca) is not 1 (100% opaque) */
+        code = pdfi_dict_knownget_number(ctx, annot, "ca", &f);
+        if (code > 0) {
+            if (f != 1.0) {
+                *transparent = true;
+                return 0;
+            }
+        }
+        if (code < 0)
+            return code;
+    }
+
+    return 0;
+}
+
+static int pdfi_check_Annots_for_transparency(pdf_context *ctx, pdf_array *annots_array, pdf_dict *page_dict, bool *transparent, int *num_spots)
+{
+    int i, code = 0;
+    pdf_dict *annot = NULL;
+
+    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) {
+            code = pdfi_check_annot_for_transparency(ctx, annot, page_dict, transparent, num_spots);
+            if (code < 0 && ctx->pdfstoponerror)
+                goto exit;
+
+            /* If we've found transparency, and don't need to continue checkign for spot colours
+             * just exit as fast as possible.
+             */
+            if (*transparent == true && num_spots == NULL)
+                goto exit;
+
+            pdfi_countdown(annot);
+            annot = NULL;
+        }
+    }
+exit:
+    pdfi_countdown(annot);
+    return code;
+}
+
+int pdfi_check_transparency_spots(pdf_context *ctx)
+{
+    int code = 0;
+
+    /* Loop round all the pages looking for spot colours and transparency. We only check
+     * for spot colours if the device is capable of spot colours (the code checks). We
+     * only store the transparency setting if NOTRANSPARENCY is not set
+     */
+    if (ctx->num_pages) {
+        pdf_dict *page_dict = NULL;
+        uint64_t page_offset = 0;
+        bool uses_transparency = false;
+        int spots = 0;
+        uint64_t ix;
+
+        int bytes = (int)ceil((float)ctx->num_pages / 8.0f);
+        ctx->PageTransparencyArray = (char *)gs_alloc_bytes(ctx->memory, bytes, "pdfi_process_file, allocate page transparency tracking array");
+        memset(ctx->PageTransparencyArray, 0x00, bytes);
+
+        code = pdfi_alloc_object(ctx, PDF_DICT, 32, (pdf_obj **)&ctx->SpotNames);
+        if (code < 0)
+            goto exit;
+
+        pdfi_countup(ctx->SpotNames);
+
+        for (ix=0;ix < ctx->num_pages;ix++) {
+            if (ctx->pdfdebug)
+                dmprintf1(ctx->memory, "%% Checking Page %"PRIi64" for transparency and spot plates\n", ix + 1);
+
+            uses_transparency = false;
+
+            /* Get the page dictionary */
+            code = pdfi_loop_detector_mark(ctx);
+            if (code < 0)
+                goto exit;
+
+            code = pdfi_loop_detector_add_object(ctx, ctx->Pages->object_num);
+            if (code < 0) {
+                pdfi_loop_detector_cleartomark(ctx);
+                goto exit;
+            }
+
+            page_offset = 0;
+            code = pdfi_get_page_dict(ctx, ctx->Pages, ix, &page_offset, &page_dict, NULL);
+            pdfi_loop_detector_cleartomark(ctx);
+            if (code < 0) {
+                if (code == gs_error_VMerror || ctx->pdfstoponerror)
+                    goto exit;
+                return 0;
+            }
+
+            if (code > 0) {
+                /* This can happen if the number of declared pages (/Count) is larger than the number of pages in the Pages array
+                 * We'll ignore that for the purposes of transparency checking. If the user tries to render this
+                 * page it'll fail in the page rendering code.
+                 */
+                pdfi_countdown(page_dict);
+                page_dict = NULL;
+                break;
+            }
+
+            /* Check the page dictionary for spots and transparency */
+            code = pdfi_check_page_transparency(ctx, page_dict, &uses_transparency, &spots);
+            if (code < 0) {
+                pdfi_countdown(ctx->SpotNames);
+                pdfi_countdown(page_dict);
+                ctx->SpotNames = NULL;
+                goto exit;
+            }
+            if (uses_transparency && !ctx->notransparency) {
+                uint64_t index = ix >> 3;
+                char value = 0x80 >> (ix % 8);
+
+                ctx->PageTransparencyArray[index] |= value;
+            }
+            pdfi_countdown(page_dict);
+            page_dict = NULL;
+        }
+
+        pdfi_countdown(ctx->SpotNames);
+        ctx->SpotNames = NULL;
+
+        /* If there are spot colours (and by inference, the device renders spot plates) then
+         * send the number of Spots to the device, so it can setup correctly.
+         */
+        if (spots > 0) {
+            gs_c_param_list_write(&ctx->pdfi_param_list, ctx->memory);
+            param_write_int((gs_param_list *)&ctx->pdfi_param_list, "PageSpotColors", &spots);
+            gs_c_param_list_read(&ctx->pdfi_param_list);
+            code = gs_putdeviceparams(ctx->pgs->device, (gs_param_list *)&ctx->pdfi_param_list);
+            if (code > 0) {
+                /* The device was closed, we need to reopen it */
+                code = gs_setdevice_no_erase(ctx->pgs, ctx->pgs->device);
+                if (code < 0) {
+                    if (uses_transparency)
+                        (void)gs_abort_pdf14trans_device(ctx->pgs);
+                    pdfi_countdown(ctx->SpotNames);
+                    pdfi_countdown(page_dict);
+                    goto exit;
+                }
+                gs_erasepage(ctx->pgs);
+            }
+        }
+    }
+exit:
+    return code;
+}
+
+/* From the original PDF interpreter written in PostScript:
+ * Note: we deliberately don't check to see whether a Group is defined,
+ * because Adobe Illustrator 10 (and possibly other applications) define
+ * a page-level group whether transparency is actually used or not.
+ * Ignoring the presence of Group is justified because, in the absence
+ * of any other transparency features, they have no effect.
+ */
+int pdfi_check_page_transparency(pdf_context *ctx, pdf_dict *page_dict, bool *transparent, int *spots)
+{
+    int code;
+    pdf_obj *d = NULL;
+    int num_spots = 0;
+    pdf_dict *group_dict = NULL;
+
+    /* Note that we don't reset 'spots' to 0 because these accumulate across pages */
+    *transparent = false;
+
+    ctx->spot_capable_device = false;
+    gs_c_param_list_read(&ctx->pdfi_param_list);
+    code = param_read_int((gs_param_list *)&ctx->pdfi_param_list, "PageSpotColors", &ctx->spot_capable_device);
+    if (code < 0)
+        return code;
+    if (code > 0)
+        ctx->spot_capable_device = 0;
+    else
+        ctx->spot_capable_device = 1;
+
+    /* Check if the page dictionary has a page Group entry */
+    code = pdfi_dict_knownget_type(ctx, page_dict, "Group", PDF_DICT, (pdf_obj **)&group_dict);
+    if (code > 0) {
+        pdf_obj *CS = NULL;
+
+        /* Page group means the page is transparent but we ignore it for the purposes
+         * of transparency detection. See above.
+         */
+        if (ctx->spot_capable_device) {
+            /* Check the Group to see if it has a ColorSpace (CS) and if it
+             * does whether it has any spot colours */
+            code = pdfi_dict_knownget(ctx, group_dict, "CS", &CS);
+            if (code > 0) {
+                code = pdfi_check_ColorSpace_for_spots(ctx, CS, group_dict, page_dict, spots);
+                if (code < 0 && ctx->pdfstoponerror) {
+                    pdfi_countdown(CS);
+                    pdfi_countdown(group_dict);
+                    pdfi_countdown(ctx->SpotNames);
+                    return code;
+                }
+                pdfi_countdown(CS);
+            }
+        }
+        pdfi_countdown(group_dict);
+    }
+
+    /* Now check any Resources dictionary in the Page dictionary */
+    code = pdfi_dict_get_type(ctx, page_dict, "Resources", PDF_DICT, &d);
+    if (code >= 0) {
+        if (ctx->spot_capable_device)
+            code = pdfi_check_Resources_for_transparency(ctx, (pdf_dict *)d, page_dict, transparent, &num_spots);
+        else
+            code = pdfi_check_Resources_for_transparency(ctx, (pdf_dict *)d, page_dict, transparent, NULL);
+        pdfi_countdown(d);
+        if (code < 0)
+            return code;
+    }
+    if (code < 0 && code != gs_error_undefined && ctx->pdfstoponerror)
+        return code;
+
+    /* If we are drawing Annotations, check to see if the page uses any Annots */
+    if (ctx->showannots) {
+        code = pdfi_dict_get_type(ctx, page_dict, "Annots", PDF_ARRAY, &d);
+        if (code >= 0) {
+            if (ctx->spot_capable_device)
+                code = pdfi_check_Annots_for_transparency(ctx, (pdf_array *)d, page_dict, transparent, &num_spots);
+            else
+                code = pdfi_check_Annots_for_transparency(ctx, (pdf_array *)d, page_dict, transparent, NULL);
+            pdfi_countdown(d);
+            if (code < 0)
+                return code;
+        }
+        if (code < 0 && code != gs_error_undefined && ctx->pdfstoponerror)
+            return code;
+    }
+
+    if (ctx->spot_capable_device)
+        *spots += num_spots;
+    else
+        *spots = 0;
+
+    return 0;
+}
diff --git a/pdf/pdf_page.h b/pdf/pdf_check.h
similarity index 59%
copy from pdf/pdf_page.h
copy to pdf/pdf_check.h
index c1213e3..7d67447 100644
--- a/pdf/pdf_page.h
+++ b/pdf/pdf_check.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2018 Artifex Software, Inc.
+/* Copyright (C) 2001-2019 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -13,11 +13,12 @@
    CA 94945, U.S.A., +1(415)492-9861, for further information.
 */
 
-/* Page-level operations for the PDF interpreter */
+#ifndef PDF_CHECK
+#define PDF_CHECK
 
-#ifndef PDF_PAGE_OPERATORS
-#define PDF_PAGE_OPERATORS
+int pdfi_check_transparency_spots(pdf_context *ctx);
+int pdfi_check_page_transparency(pdf_context *ctx, pdf_dict *page_dict, bool *transparent, int *spots);
 
-/* NOTE: used to have OC stuff here, moved to pdf_optcontent.h */
+int pdfi_check_Pattern(pdf_context *ctx, pdf_dict *pattern, pdf_dict *page_dict, bool *transparent, int *num_spots);
 
 #endif
diff --git a/pdf/pdf_page.c b/pdf/pdf_page.c
index b8aef1a..d0216f8 100644
--- a/pdf/pdf_page.c
+++ b/pdf/pdf_page.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2018 Artifex Software, Inc.
+/* Copyright (C) 2001-2019 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -16,6 +16,7 @@
 /* Page-level operations for the PDF interpreter */
 
 #include "pdf_int.h"
+#include "plmain.h"
 #include "pdf_stack.h"
 #include "pdf_page.h"
 #include "pdf_file.h"
@@ -24,13 +25,419 @@
 #include "pdf_loop_detect.h"
 #include "pdf_colour.h"
 #include "pdf_trans.h"
+#include "pdf_gstate.h"
 #include "pdf_misc.h"
+#include "pdf_optcontent.h"
 
-#include "gsiparm4.h"
-#include "gsiparm3.h"
+static int pdfi_process_page_contents(pdf_context *ctx, pdf_dict *page_dict)
+{
+    int i, code = 0;
+    pdf_obj *o, *o1;
 
-#include "gstrans.h"
+    code = pdfi_dict_get(ctx, page_dict, "Contents", &o);
+    if (code == gs_error_undefined)
+        /* Don't throw an error if there are no contents, just render nothing.... */
+        return 0;
+    if (code < 0)
+        return code;
 
-/* NOTE: I used to have the OC stuff here, moved it to pdf_optcontent.c
- * Maybe this file will have other things at some point, else it could be deleted.
- */
+    if (o->type == PDF_INDIRECT) {
+        if (((pdf_indirect_ref *)o)->ref_object_num == page_dict->object_num)
+            return_error(gs_error_circular_reference);
+
+        code = pdfi_dereference(ctx, ((pdf_indirect_ref *)o)->ref_object_num, ((pdf_indirect_ref *)o)->ref_generation_num, &o1);
+        pdfi_countdown(o);
+        if (code < 0) {
+            if (code == gs_error_VMerror)
+                return code;
+            return 0;
+        }
+        o = o1;
+    }
+
+    code = pdfi_gsave(ctx);
+    if (code < 0) {
+        pdfi_countdown(o);
+        return code;
+    }
+
+    if (o->type == PDF_ARRAY) {
+        pdf_array *a = (pdf_array *)o;
+
+        for (i=0;i < pdfi_array_size(a); i++) {
+            pdf_indirect_ref *r;
+            code = pdfi_array_get_no_deref(ctx, a, i, (pdf_obj **)&r);
+            if (code < 0)
+                goto page_error;
+            if (r->type == PDF_DICT) {
+                code = pdfi_interpret_content_stream(ctx, (pdf_dict *)r, page_dict);
+                pdfi_countdown(r);
+                if (code < 0)
+                    goto page_error;
+            } else {
+                if (r->type != PDF_INDIRECT) {
+                        pdfi_countdown(r);
+                        code = gs_note_error(gs_error_typecheck);
+                        goto page_error;
+                } else {
+                    if (r->ref_object_num == page_dict->object_num) {
+                        pdfi_countdown(r);
+                        code = gs_note_error(gs_error_circular_reference);
+                        goto page_error;
+                    }
+                    code = pdfi_dereference(ctx, r->ref_object_num, r->ref_generation_num, &o1);
+                    pdfi_countdown(r);
+                    if (code < 0) {
+                        if (code != gs_error_VMerror || ctx->pdfstoponerror == false)
+                            code = 0;
+                        goto page_error;
+                    }
+                    if (o1->type != PDF_DICT) {
+                        pdfi_countdown(o1);
+                        code = gs_note_error(gs_error_typecheck);
+                        goto page_error;
+                    }
+                    code = pdfi_interpret_content_stream(ctx, (pdf_dict *)o1, page_dict);
+                    pdfi_countdown(o1);
+                    if (code < 0) {
+                        if (code == gs_error_VMerror || ctx->pdfstoponerror == true)
+                            goto page_error;
+                    }
+                }
+            }
+        }
+    } else {
+        if (o->type == PDF_DICT) {
+            code = pdfi_interpret_content_stream(ctx, (pdf_dict *)o, page_dict);
+        } else {
+            pdfi_countdown(o);
+            return_error(gs_error_typecheck);
+        }
+    }
+page_error:
+    pdfi_grestore(ctx);
+    pdfi_countdown(o);
+    return code;
+}
+
+static int pdfi_set_media_size(pdf_context *ctx, pdf_dict *page_dict)
+{
+    gs_c_param_list list;
+    gs_param_float_array fa;
+    pdf_array *a = NULL, *default_media = NULL;
+    float fv[2];
+    double d[4];
+    int code;
+    uint64_t i;
+    int64_t rotate = 0;
+
+    gs_c_param_list_write(&list, ctx->memory);
+
+    code = pdfi_dict_get_type(ctx, page_dict, "MediaBox", PDF_ARRAY, (pdf_obj **)&default_media);
+    if (code < 0) {
+        code = gs_erasepage(ctx->pgs);
+        return 0;
+    }
+
+    if (ctx->usecropbox) {
+        if (a != NULL)
+            pdfi_countdown(a);
+        (void)pdfi_dict_get_type(ctx, page_dict, "CropBox", PDF_ARRAY, (pdf_obj **)&a);
+    }
+    if (ctx->useartbox) {
+        if (a != NULL)
+            pdfi_countdown(a);
+        (void)pdfi_dict_get_type(ctx, page_dict, "ArtBox", PDF_ARRAY, (pdf_obj **)&a);
+    }
+    if (ctx->usebleedbox) {
+        if (a != NULL)
+            pdfi_countdown(a);
+        (void)pdfi_dict_get_type(ctx, page_dict, "BleedBox", PDF_ARRAY, (pdf_obj **)&a);
+    }
+    if (ctx->usetrimbox) {
+        if (a != NULL)
+            pdfi_countdown(a);
+        (void)pdfi_dict_get_type(ctx, page_dict, "MediaBox", PDF_ARRAY, (pdf_obj **)&a);
+    }
+    if (a == NULL)
+        a = default_media;
+
+    for (i=0;i<4;i++) {
+        code = pdfi_array_get_number(ctx, a, i, &d[i]);
+    }
+    pdfi_countdown(a);
+
+    normalize_rectangle(d);
+    memcpy(ctx->PageSize, d, 4 * sizeof(double));
+
+    code = pdfi_dict_get_int(ctx, page_dict, "Rotate", &rotate);
+
+    rotate = rotate % 360;
+
+    switch(rotate) {
+        default:
+        case 0:
+        case 360:
+        case -180:
+        case 180:
+            fv[0] = (float)(d[2] - d[0]);
+            fv[1] = (float)(d[3] - d[1]);
+            break;
+        case -90:
+        case 90:
+        case -270:
+        case 270:
+            fv[1] = (float)(d[2] - d[0]);
+            fv[0] = (float)(d[3] - d[1]);
+            break;
+    }
+
+    fa.persistent = false;
+    fa.data = fv;
+    fa.size = 2;
+
+    code = param_write_float_array((gs_param_list *)&list, ".MediaSize", &fa);
+    if (code >= 0)
+    {
+        gx_device *dev = gs_currentdevice(ctx->pgs);
+
+        gs_c_param_list_read(&list);
+        code = gs_putdeviceparams(dev, (gs_param_list *)&list);
+        if (code < 0) {
+            gs_c_param_list_release(&list);
+            return code;
+        }
+    }
+    gs_c_param_list_release(&list);
+
+    gs_initgraphics(ctx->pgs);
+
+    switch(rotate) {
+        case 0:
+            break;
+        case -270:
+        case 90:
+            gs_translate(ctx->pgs, 0, fv[1]);
+            gs_rotate(ctx->pgs, -90);
+            break;
+        case -180:
+        case 180:
+            gs_translate(ctx->pgs, fv[0], fv[1]);
+            gs_rotate(ctx->pgs, 180);
+            break;
+        case -90:
+        case 270:
+            gs_translate(ctx->pgs, fv[0], 0);
+            gs_rotate(ctx->pgs, 90);
+            break;
+        default:
+            break;
+    }
+
+    gs_translate(ctx->pgs, d[0] * -1, d[1] * -1);
+
+    code = gs_erasepage(ctx->pgs);
+    return 0;
+}
+
+int pdfi_page_render(pdf_context *ctx, uint64_t page_num)
+{
+    int code, code1=0;
+    uint64_t page_offset = 0;
+    pdf_dict *page_dict = NULL;
+    bool uses_transparency = false;
+    bool page_group_known = false;
+    int page_index = page_num >> 3;
+    char page_bit = 0x80 >> (page_num % 8);
+    stream_save local_entry_save;
+    pdf_dict *group_dict = NULL;
+
+    if (page_num > ctx->num_pages)
+        return_error(gs_error_rangecheck);
+
+    if (ctx->pdfdebug)
+        dmprintf1(ctx->memory, "%% Processing Page %"PRIi64" content stream\n", page_num + 1);
+
+    code = pdfi_loop_detector_mark(ctx);
+    if (code < 0)
+        return code;
+
+    code = pdfi_loop_detector_add_object(ctx, ctx->Pages->object_num);
+    if (code < 0) {
+        pdfi_loop_detector_cleartomark(ctx);
+        goto exit2;
+    }
+
+    code = pdfi_get_page_dict(ctx, ctx->Pages, page_num, &page_offset, &page_dict, NULL);
+    pdfi_loop_detector_cleartomark(ctx);
+    if (code < 0) {
+        if (code == gs_error_VMerror || ctx->pdfstoponerror)
+            goto exit2;
+        code = 0;
+        goto exit2;
+    }
+
+    if (code > 0) {
+        code = gs_note_error(gs_error_unknownerror);
+        goto exit2;
+    }
+
+    code = pdfi_set_media_size(ctx, page_dict);
+    if (code < 0) {
+        goto exit2;
+    }
+
+    code = pdfi_dict_knownget_type(ctx, page_dict, "Group", PDF_DICT, (pdf_obj **)&group_dict);
+    if (code < 0)
+        goto exit2;
+    if (group_dict != NULL)
+        page_group_known = true;
+
+    pdfi_countdown(ctx->CurrentPageDict);
+    ctx->CurrentPageDict = page_dict;
+    pdfi_countup(ctx->CurrentPageDict);
+
+    code = gs_setstrokeconstantalpha(ctx->pgs, 1.0);
+    code = gs_setfillconstantalpha(ctx->pgs, 1.0);
+    code = gs_setalphaisshape(ctx->pgs, 0);
+    code = gs_setblendmode(ctx->pgs, BLEND_MODE_Compatible);
+    code = gs_settextknockout(ctx->pgs, true);
+    code = gs_settextspacing(ctx->pgs, (double)0.0);
+    code = gs_settextleading(ctx->pgs, (double)0.0);
+    gs_settextrenderingmode(ctx->pgs, 0);
+    code = gs_setwordspacing(ctx->pgs, (double)0.0);
+    code = gs_settexthscaling(ctx->pgs, (double)100.0);
+    ctx->TextBlockDepth = 0;
+
+    if (ctx->pgs->set_transfer.red == 0x00) {
+        ctx->DefaultTransfers[0].proc = gs_identity_transfer;
+        memset(ctx->DefaultTransfers[0].values, 0x00, transfer_map_size * sizeof(frac));
+    } else {
+        ctx->DefaultTransfers[0].proc = ctx->pgs->set_transfer.red->proc;
+        memcpy(ctx->DefaultTransfers[0].values, ctx->pgs->set_transfer.red->values, transfer_map_size * sizeof(frac));
+    }
+    if (ctx->pgs->set_transfer.green == 0x00) {
+        ctx->DefaultTransfers[1].proc = gs_identity_transfer;
+        memset(ctx->DefaultTransfers[1].values, 0x00, transfer_map_size * sizeof(frac));
+    } else {
+        ctx->DefaultTransfers[1].proc = ctx->pgs->set_transfer.green->proc;
+        memcpy(ctx->DefaultTransfers[1].values, ctx->pgs->set_transfer.green->values, transfer_map_size * sizeof(frac));
+    }
+    if (ctx->pgs->set_transfer.blue == 0x00) {
+        ctx->DefaultTransfers[2].proc = gs_identity_transfer;
+        memset(ctx->DefaultTransfers[2].values, 0x00, transfer_map_size * sizeof(frac));
+    } else {
+        ctx->DefaultTransfers[2].proc = ctx->pgs->set_transfer.blue->proc;
+        memcpy(ctx->DefaultTransfers[2].values, ctx->pgs->set_transfer.blue->values, transfer_map_size * sizeof(frac));
+    }
+    if (ctx->pgs->set_transfer.gray == 0x00) {
+        ctx->DefaultTransfers[3].proc = gs_identity_transfer;
+        memset(ctx->DefaultTransfers[3].values, 0x00, transfer_map_size * sizeof(frac));
+    } else {
+        ctx->DefaultTransfers[3].proc = ctx->pgs->set_transfer.gray->proc;
+        memcpy(ctx->DefaultTransfers[3].values, ctx->pgs->set_transfer.gray->values, transfer_map_size * sizeof(frac));
+    }
+    if (ctx->pgs->black_generation == 0x00) {
+        ctx->DefaultBG.proc = gs_identity_transfer;
+        memset(ctx->DefaultBG.values, 0x00, transfer_map_size * sizeof(frac));
+    } else {
+        ctx->DefaultBG.proc = ctx->pgs->black_generation->proc;
+        memcpy(ctx->DefaultBG.values, ctx->pgs->black_generation->values, transfer_map_size * sizeof(frac));
+    }
+    if (ctx->pgs->undercolor_removal == 0x00) {
+        ctx->DefaultUCR.proc = gs_identity_transfer;
+        memset(ctx->DefaultUCR.values, 0x00, transfer_map_size * sizeof(frac));
+    } else {
+        ctx->DefaultUCR.proc = ctx->pgs->undercolor_removal->proc;
+        memcpy(ctx->DefaultUCR.values, ctx->pgs->undercolor_removal->values, transfer_map_size * sizeof(frac));
+    }
+    dbgmprintf1(ctx->memory, "Current page transparency setting is %d\n", ctx->PageTransparencyArray[page_index] & page_bit ? 1 : 0);
+
+    /* Force NOTRANSPARENCY here if required, until we can get it working... */
+    ctx->page_has_transparency = ctx->PageTransparencyArray[page_index] & page_bit ? 1 : 0;
+
+    /* Set whether device needs OP support
+     * This needs to be before transparency device is pushed, if applicable
+     */
+    pdfi_trans_set_needs_OP(ctx);
+    pdfi_oc_init(ctx);
+
+    pdfi_gsave(ctx);
+
+    if (ctx->page_has_transparency) {
+        if (code >= 0) {
+            /* We don't retain the PDF14 device */
+            code = gs_push_pdf14trans_device(ctx->pgs, false, false);
+            if (code >= 0) {
+                if (page_group_known) {
+                    code = pdfi_trans_begin_page_group(ctx, page_dict, group_dict);
+                    /* If setting the page group failed for some reason, abandon the page group, but continue with the page */
+                    if (code < 0)
+                        page_group_known = false;
+                }
+            } else {
+                /* Couldn't push the transparency compositor.
+                 * This is probably fatal, but attempt to recover by abandoning transparency
+                 */
+                ctx->page_has_transparency = uses_transparency = false;
+            }
+        } else {
+            /* Couldn't gsave round the transparency compositor.
+             * This is probably fatal, but attempt to recover by abandoning transparency
+             */
+            ctx->page_has_transparency = uses_transparency = false;
+        }
+    }
+
+    /* Init a base_pgs graphics state for Patterns
+     * (this has to be after transparency device pushed, if applicable)
+     */
+    pdfi_set_DefaultQState(ctx, ctx->pgs);
+
+    /* Save the current stream state, for later cleanup, in a local variable */
+    local_save_stream_state(ctx, &local_entry_save);
+    initialise_stream_save(ctx);
+
+    code = pdfi_process_page_contents(ctx, page_dict);
+
+    /* Put our state back the way it was before we ran the contents
+     * and check if the stream had problems
+     */
+#ifdef PROBE_STREAMS
+    if (ctx->pgs->level > ctx->current_stream_save.gsave_level ||
+        pdfi_count_stack(ctx) > ctx->current_stream_save.stack_count)
+        code = ((pdf_context *)0)->first_page;
+#endif
+
+    cleanup_context_interpretation(ctx, &local_entry_save);
+    local_restore_stream_state(ctx, &local_entry_save);
+
+    if (ctx->page_has_transparency && page_group_known) {
+        code1 = pdfi_trans_end_group(ctx);
+    }
+
+    pdfi_countdown(ctx->CurrentPageDict);
+    ctx->CurrentPageDict = NULL;
+
+    if (ctx->page_has_transparency) {
+        if (code1 < 0) {
+            (void)gs_abort_pdf14trans_device(ctx->pgs);
+            goto exit1;
+        }
+
+        code = gs_pop_pdf14trans_device(ctx->pgs, false);
+        if (code < 0) {
+            goto exit1;
+        }
+    }
+
+ exit1:
+    pdfi_grestore(ctx);
+ exit2:
+    pdfi_countdown(page_dict);
+    pdfi_countdown(group_dict);
+
+    if (code == 0)
+        code = pl_finish_page(ctx->memory->gs_lib_ctx->top_of_system, ctx->pgs, 1, true);
+    return code;
+}
diff --git a/pdf/pdf_page.h b/pdf/pdf_page.h
index c1213e3..17ab3af 100644
--- a/pdf/pdf_page.h
+++ b/pdf/pdf_page.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2018 Artifex Software, Inc.
+/* Copyright (C) 2001-2019 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -18,6 +18,6 @@
 #ifndef PDF_PAGE_OPERATORS
 #define PDF_PAGE_OPERATORS
 
-/* NOTE: used to have OC stuff here, moved to pdf_optcontent.h */
+int pdfi_page_render(pdf_context *ctx, uint64_t page_num);
 
 #endif
diff --git a/pdf/pdf_pattern.c b/pdf/pdf_pattern.c
index 00d396b..a6f965c 100644
--- a/pdf/pdf_pattern.c
+++ b/pdf/pdf_pattern.c
@@ -31,6 +31,7 @@
 #include "pdf_loop_detect.h"
 #include "pdf_func.h"
 #include "pdf_shading.h"
+#include "pdf_check.h"
 #include "gscsepr.h"
 #include "stream.h"
 #include "strmio.h"


Summary of changes:
 pdf/ghostpdf.c                   | 1433 +-------------------------------------
 pdf/ghostpdf.h                   |    3 -
 pdf/pdf.mak                      |    4 +
 pdf/pdf_check.c                  | 1043 +++++++++++++++++++++++++++
 base/gsstrl.h => pdf/pdf_check.h |   12 +-
 pdf/pdf_optcontent.c             |   20 +-
 pdf/pdf_optcontent.h             |    1 +
 pdf/pdf_page.c                   |  421 ++++++++++-
 pdf/pdf_page.h                   |    4 +-
 pdf/pdf_pattern.c                |    1 +
 10 files changed, 1489 insertions(+), 1453 deletions(-)
 create mode 100644 pdf/pdf_check.c
 copy base/gsstrl.h => pdf/pdf_check.h (65%)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.