diff -pruN 9.55.0~dfsg-3/base/fapibstm.c 9.56.1~dfsg-1/base/fapibstm.c
--- 9.55.0~dfsg-3/base/fapibstm.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/fapibstm.c	2022-04-04 13:46:22.000000000 +0000
@@ -346,6 +346,8 @@ static const FAPI_server If0 = {
     {0},
     0,
     false,
+    false,
+    {1, 0, 0, 1, 0, 0},
     1,
     {1, 0, 0, 1, 0, 0},
     ensure_open,
diff -pruN 9.55.0~dfsg-3/base/fapi_ft.c 9.56.1~dfsg-1/base/fapi_ft.c
--- 9.55.0~dfsg-3/base/fapi_ft.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/fapi_ft.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -59,6 +59,7 @@
 #include FT_TRUETYPE_TABLES_H
 #include FT_MULTIPLE_MASTERS_H
 #include FT_TYPE1_TABLES_H
+#include FT_SIZES_H
 
 /* Note: structure definitions here start with FF_, which stands for 'FAPI FreeType". */
 
@@ -642,14 +643,15 @@ load_glyph(gs_fapi_server * a_server, gs
            data. (NOTE: if those do not match the original font's metrics, again, the hinting
            can be distorted)
          */
-        if (a_char_ref->metrics_type == gs_fapi_metrics_replace && !a_fapi_font->is_mtx_skipped)
-            face->ft_inc_int->object->metrics_type = gs_fapi_metrics_replace_width;
-        else
+        if (a_char_ref->metrics_type == gs_fapi_metrics_replace && !a_fapi_font->is_mtx_skipped) {
+            face->ft_inc_int->object->glyph_metrics_index = 0xFFFFFFFF;
+            delta.x = FT_MulFix(a_char_ref->sb_x >> 16, ft_face->size->metrics.x_scale);
+            delta.y = FT_MulFix(a_char_ref->sb_y >> 16, ft_face->size->metrics.y_scale);
+            FT_Vector_Transform( &delta, &face->ft_transform);
+        }
+        else {
             face->ft_inc_int->object->metrics_type = a_char_ref->metrics_type;
-
-        delta.x = FT_MulFix(a_char_ref->sb_x >> 16, ft_face->size->metrics.x_scale);
-        delta.y = FT_MulFix(a_char_ref->sb_y >> 16, ft_face->size->metrics.y_scale);
-        FT_Vector_Transform( &delta, &face->ft_transform);
+        }
     }
     else if (face->ft_inc_int)
         /* Make sure we don't leave this set to the last value, as we may then use inappropriate metrics values */
@@ -705,6 +707,20 @@ load_glyph(gs_fapi_server * a_server, gs
 
     if (ft_error == FT_Err_Out_Of_Memory
         || ft_error == FT_Err_Array_Too_Large) {
+        /* An out of memory error can leave the FT TTF hinting context in a partially initialized state.
+           Meaning bad things can happen if we try to render another glyph using the same context.
+           Ideally this would be handled by FT internally, but that means some implications for supporting
+           out of spec fonts, and performance.
+           By destroying, recreating and resetting the size, it invalidates the (possibly corrupt) hinting
+           context, and ensures a fresh start in any subsequent call.
+         */
+        FT_Size ftsize = NULL;
+        FT_Done_Size(ft_face->size);
+        FT_New_Size(face->ft_face, &ftsize);
+        FT_Activate_Size(ftsize);
+        ft_error = FT_Set_Char_Size(face->ft_face, face->width, face->height, face->horz_res, face->vert_res);
+        if (ft_error != 0) return_error(gs_error_invalidfont);
+
         return (gs_error_VMerror);
     }
 
@@ -809,6 +825,10 @@ load_glyph(gs_fapi_server * a_server, gs
                 FT_Render_Mode mode = FT_RENDER_MODE_MONO;
 
                 ft_error = FT_Render_Glyph(ft_face->glyph, mode);
+                if (ft_error != 0) {
+                    (*a_glyph) = NULL;
+                    return (gs_error_VMerror);
+                }
             }
             else {
                 (*a_glyph) = NULL;
@@ -1072,8 +1092,8 @@ transform_decompose(FT_Matrix * a_transf
      * scalex/y we calculate will be >64 after rounding.
      */
 
-    if (scalex < 10.0) {
-        fact = 10.016 / scalex;
+    if (scalex < 1.0) {
+        fact = 1.016 / scalex;
         scalex = scalex * fact;
         scaley = scaley * fact;
     }
@@ -1330,6 +1350,50 @@ gs_fapi_ft_get_scaled_font(gs_fapi_serve
                 return_error(gs_error_VMerror);
             }
             a_font->server_font_data = face;
+
+            if (!a_font->is_type1) {
+                for (i = 0; i < GS_FAPI_NUM_TTF_CMAP_REQ && !cmap; i++) {
+                    if (a_font->ttf_cmap_req[i].platform_id > 0) {
+                        for (j = 0; j < face->ft_face->num_charmaps; j++) {
+                            if (FT_Get_CMap_Format(face->ft_face->charmaps[j]) >= 0
+                             && face->ft_face->charmaps[j]->platform_id == a_font->ttf_cmap_req[i].platform_id
+                             && face->ft_face->charmaps[j]->encoding_id == a_font->ttf_cmap_req[i].encoding_id) {
+
+                                cmap = face->ft_face->charmaps[j];
+                                break;
+                            }
+                        }
+                    }
+                    else {
+                        break;
+                    }
+                }
+                if (cmap) {
+                    (void)FT_Set_Charmap(face->ft_face, cmap);
+                }
+                else if (a_font->full_font_buf != NULL || a_font->font_file_path != NULL) {
+                    /* If we've passed a complete TTF to Freetype, but *haven't* requested a
+                     * specific cmap table above, try to use a Unicode one
+                     * If that doesn't work, just leave the default in place.
+                     */
+                    (void)FT_Select_Charmap(face->ft_face, ft_encoding_unicode);
+                }
+                /* For PDF, we have to know which cmap table actually was selected */
+                if (face->ft_face->charmap != NULL) {
+                    a_font->ttf_cmap_selected.platform_id = face->ft_face->charmap->platform_id;
+                    a_font->ttf_cmap_selected.encoding_id = face->ft_face->charmap->encoding_id;
+                }
+                else {
+                    /* Just in case */
+                    a_font->ttf_cmap_selected.platform_id = -1;
+                    a_font->ttf_cmap_selected.encoding_id = -1;
+                }
+            }
+            else {
+                /* Just in case */
+                a_font->ttf_cmap_selected.platform_id = -1;
+                a_font->ttf_cmap_selected.encoding_id = -1;
+            }
         }
         else
             a_font->server_font_data = NULL;
@@ -1378,49 +1442,6 @@ gs_fapi_ft_get_scaled_font(gs_fapi_serve
 
         FT_Set_Transform(face->ft_face, &face->ft_transform, NULL);
 
-        if (!a_font->is_type1) {
-            for (i = 0; i < GS_FAPI_NUM_TTF_CMAP_REQ && !cmap; i++) {
-                if (a_font->ttf_cmap_req[i].platform_id > 0) {
-                    for (j = 0; j < face->ft_face->num_charmaps; j++) {
-                        if (FT_Get_CMap_Format(face->ft_face->charmaps[j]) >= 0
-                         && face->ft_face->charmaps[j]->platform_id == a_font->ttf_cmap_req[i].platform_id
-                         && face->ft_face->charmaps[j]->encoding_id == a_font->ttf_cmap_req[i].encoding_id) {
-
-                            cmap = face->ft_face->charmaps[j];
-                            break;
-                        }
-                    }
-                }
-                else {
-                    break;
-                }
-            }
-            if (cmap) {
-                (void)FT_Set_Charmap(face->ft_face, cmap);
-            }
-            else if (a_font->full_font_buf != NULL || a_font->font_file_path != NULL) {
-                /* If we've passed a complete TTF to Freetype, but *haven't* requested a
-                 * specific cmap table above, try to use a Unicode one
-                 * If that doesn't work, just leave the default in place.
-                 */
-                (void)FT_Select_Charmap(face->ft_face, ft_encoding_unicode);
-            }
-            /* For PDF, we have to know which cmap table actually was selected */
-            if (face->ft_face->charmap != NULL) {
-                a_font->ttf_cmap_selected.platform_id = face->ft_face->charmap->platform_id;
-                a_font->ttf_cmap_selected.encoding_id = face->ft_face->charmap->encoding_id;
-            }
-            else {
-                /* Just in case */
-                a_font->ttf_cmap_selected.platform_id = -1;
-                a_font->ttf_cmap_selected.encoding_id = -1;
-            }
-        }
-        else {
-            /* Just in case */
-            a_font->ttf_cmap_selected.platform_id = -1;
-            a_font->ttf_cmap_selected.encoding_id = -1;
-        }
     }
 
     /* dpf("gs_fapi_ft_get_scaled_font return %d\n", a_font->server_font_data ? 0 : -1); */
@@ -1856,6 +1877,8 @@ static const gs_fapi_server freetypeserv
     {0},
     0,
     false,
+    false,
+    {1, 0, 0, 1, 0, 0},
     1,
     {1, 0, 0, 1, 0, 0},
     gs_fapi_ft_ensure_open,
diff -pruN 9.55.0~dfsg-3/base/fapiufst.c 9.56.1~dfsg-1/base/fapiufst.c
--- 9.55.0~dfsg-3/base/fapiufst.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/fapiufst.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -712,9 +712,10 @@ pack_long(LPUB8 * p, UL32 v)
 }
 
 static inline void
-pack_float(LPUB8 * p, float v)
+pack_float(LPUB8 * p, LPUB8 pe, float v)
 {
-    gs_sprintf((char *)(*p), "%f", v);
+    int plen = (int)(((char *)(*p)) - ((char *)(pe)));
+    gs_snprintf((char *)(*p), plen, "%f", v);
     *p += strlen((const char *)*p) + 1;
 }
 
@@ -770,7 +771,7 @@ pack_pseo_word_array(fapi_ufst_server *
 }
 
 static int
-pack_pseo_fhdr(fapi_ufst_server * r, gs_fapi_font * ff, UB8 * p)
+pack_pseo_fhdr(fapi_ufst_server * r, gs_fapi_font * ff, UB8 * p, UB8 * pe)
 {
     ushort j, n, skip = 0;
     int code;
@@ -784,7 +785,7 @@ pack_pseo_fhdr(fapi_ufst_server * r, gs_
         code = ff->get_float(ff, gs_fapi_font_feature_FontMatrix, j, &f);
         if (code < 0)
             return code;
-        pack_float(&p, f);
+        pack_float(&p, pe, f);
     }
     while (((uint64_t) p) & 0x03)       /* align to QUADWORD */
         PACK_ZERO(p);
@@ -1165,7 +1166,7 @@ ufst_make_font_data(fapi_ufst_server * r
         if (ff->is_type1) {
             LPUB8 fontdata = (LPUB8) h + PCLETTOFONTHDRSIZE;
 
-            code = pack_pseo_fhdr(r, ff, fontdata);
+            code = pack_pseo_fhdr(r, ff, fontdata, (LPUB8)(buf + area_length));
             if (code < 0)
                 return code;
         }
@@ -2246,6 +2247,8 @@ static const gs_fapi_server ufstserver =
     {0},
     0,
     false,
+    false,
+    {1, 0, 0, 1, 0, 0},
     1,
     {1, 0, 0, 1, 0, 0},
     gs_fapi_ufst_ensure_open,
diff -pruN 9.55.0~dfsg-3/base/freetype.mak 9.56.1~dfsg-1/base/freetype.mak
--- 9.55.0~dfsg-3/base/freetype.mak	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/freetype.mak	2022-04-04 13:46:22.000000000 +0000
@@ -43,7 +43,6 @@ FT_MAK=$(GLSRC)freetype.mak $(TOP_MAKEFI
 
 # file complements for each component
 ft_autofit=\
-	$(FTOBJ)afangles.$(OBJ) \
 	$(FTOBJ)afcjk.$(OBJ) \
 	$(FTOBJ)afdummy.$(OBJ) \
 	$(FTOBJ)afglobal.$(OBJ) \
@@ -52,7 +51,6 @@ ft_autofit=\
 	$(FTOBJ)aflatin.$(OBJ) \
 	$(FTOBJ)afloader.$(OBJ) \
 	$(FTOBJ)afmodule.$(OBJ) \
-	$(FTOBJ)afwarp.$(OBJ) \
 	$(FTOBJ)afblue.$(OBJ) \
 	$(FTOBJ)afranges.$(OBJ) \
 	$(FTOBJ)afshaper.$(OBJ)
@@ -210,6 +208,12 @@ ft_type42=\
 
 ft_winfonts=$(FTOBJ)winfnt.$(OBJ)
 
+ft_sdf=\
+	$(FTOBJ)ftbsdf.$(OBJ) \
+	$(FTOBJ)ftsdf.$(OBJ) \
+	$(FTOBJ)ftsdfcommon.$(OBJ) \
+	$(FTOBJ)ftsdfrend.$(OBJ) \
+
 # instantiate the requested build option (shared or compiled in)
 $(FTGEN)freetype.dev : $(FTGEN)freetype_$(SHARE_FT).dev $(FT_MAK) $(GENFTCONFH) $(MAKEDIRS)
 	$(CP_) $(FTGEN)freetype_$(SHARE_FT).dev $(FTGEN)freetype.dev
@@ -224,7 +228,7 @@ $(FTGEN)freetype_0.dev : $(FT_MAK) $(ECH
     $(ft_autofit) $(ft_base) $(ft_bdf) $(ft_cache) $(ft_cff) $(ft_cid) \
     $(ft_gzip) $(ft_lzw) $(ft_pcf) $(ft_pfr) $(ft_psaux) $(ft_pshinter) \
     $(ft_psnames) $(ft_raster) $(ft_smooth) $(ft_sfnt) $(ft_truetype) \
-    $(ft_type1) $(ft_type42) $(ft_winfonts) $(GENFTCONFH) $(MAKEDIRS)
+    $(ft_type1) $(ft_type42) $(ft_winfonts) $(ft_sdf) $(GENFTCONFH) $(MAKEDIRS)
 	$(SETMOD) $(FTGEN)freetype_0 $(ft_autofit)
 	$(ADDMOD) $(FTGEN)freetype_0 $(ft_base)
 	$(ADDMOD) $(FTGEN)freetype_0 $(ft_bdf)
@@ -245,6 +249,7 @@ $(FTGEN)freetype_0.dev : $(FT_MAK) $(ECH
 	$(ADDMOD) $(FTGEN)freetype_0 $(ft_type1)
 	$(ADDMOD) $(FTGEN)freetype_0 $(ft_type42)
 	$(ADDMOD) $(FTGEN)freetype_0 $(ft_winfonts)
+	$(ADDMOD) $(FTGEN)freetype_0 $(ft_sdf)
 
 
 # custom build rules for each source file
@@ -647,3 +652,15 @@ $(FTOBJ)t42drivr.$(OBJ) : $(FTSRC)type42
 
 $(FTOBJ)winfnt.$(OBJ) : $(FTSRC)winfonts$(D)winfnt.c $(FT_MAK) $(GENFTCONFH) $(MAKEDIRS)
 	$(FTCC) $(FTO_)winfnt.$(OBJ) $(C_) $(FTSRC)winfonts$(D)winfnt.c
+
+$(FTOBJ)ftbsdf.$(OBJ) : $(FTSRC)sdf$(D)ftbsdf.c $(FT_MAK) $(GENFTCONFH) $(MAKEDIRS)
+	$(FTCC) $(FTO_)ftbsdf.$(OBJ) $(C_) $(FTSRC)sdf$(D)ftbsdf.c
+
+$(FTOBJ)ftsdf.$(OBJ) : $(FTSRC)sdf$(D)ftsdf.c $(FT_MAK) $(GENFTCONFH) $(MAKEDIRS)
+	$(FTCC) $(FTO_)ftsdf.$(OBJ) $(C_) $(FTSRC)sdf$(D)ftsdf.c
+
+$(FTOBJ)ftsdfcommon.$(OBJ) : $(FTSRC)sdf$(D)ftsdfcommon.c $(FT_MAK) $(GENFTCONFH) $(MAKEDIRS)
+	$(FTCC) $(FTO_)ftsdfcommon.$(OBJ) $(C_) $(FTSRC)sdf$(D)ftsdfcommon.c
+
+$(FTOBJ)ftsdfrend.$(OBJ) : $(FTSRC)sdf$(D)ftsdfrend.c $(FT_MAK) $(GENFTCONFH) $(MAKEDIRS)
+	$(FTCC) $(FTO_)ftsdfrend.$(OBJ) $(C_) $(FTSRC)sdf$(D)ftsdfrend.c
diff -pruN 9.55.0~dfsg-3/base/gdebug.h 9.56.1~dfsg-1/base/gdebug.h
--- 9.55.0~dfsg-3/base/gdebug.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdebug.h	2022-04-04 13:46:22.000000000 +0000
@@ -94,7 +94,7 @@ bool gs_debug_c(int /*char */ );
 extern gp_file *gs_debug_out;
 
 /* Debugging printout macros. */
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#if defined(DEBUG)
 #  define if_debug0(c,s)\
     BEGIN if (gs_debug_c(c)) dlprintf(s); END
 #  define if_debug1(c,s,a1)\
@@ -272,9 +272,7 @@ void debug_dump_bytes(const gs_memory_t
 void debug_dump_bitmap(const gs_memory_t *mem,
                        const byte * from, uint raster, uint height,
                        const char *msg);
-#ifndef GS_THREADSAFE
 void debug_print_string_hex_nomem(const byte * str, uint len);
-#endif
 void debug_print_string_hex(const gs_memory_t *mem, const byte * str, uint len);
 
 #endif /* gdebug_INCLUDED */
diff -pruN 9.55.0~dfsg-3/base/gdevdbit.c 9.56.1~dfsg-1/base/gdevdbit.c
--- 9.55.0~dfsg-3/base/gdevdbit.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevdbit.c	2022-04-04 13:46:22.000000000 +0000
@@ -379,7 +379,7 @@ gx_default_copy_alpha(gx_device * dev, c
             int l_xprev = x;
             gs_get_bits_params_t params;
 
-            params.options = (GB_ALIGN_ANY |
+            params.options = (GB_ALIGN_STANDARD |
                               (GB_RETURN_COPY | GB_RETURN_POINTER) |
                               GB_OFFSET_0 |
                               GB_RASTER_STANDARD | GB_PACKING_CHUNKY |
diff -pruN 9.55.0~dfsg-3/base/gdevdevn.c 9.56.1~dfsg-1/base/gdevdevn.c
--- 9.55.0~dfsg-3/base/gdevdevn.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevdevn.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -642,7 +642,7 @@ devn_put_params(gx_device * pdev, gs_par
             pdev->color_info.num_components = (num_order)
                 ? num_order
                 : (page_spot_colors >= 0)
-                    ? npcmcolors + num_spot + page_spot_colors
+                    ? npcmcolors + page_spot_colors
                     : pdev->color_info.max_components;
 
             if (pdev->color_info.num_components >
@@ -1185,11 +1185,11 @@ gx_devn_prn_ret_devn_params_const(const
  *  Device proc for updating the equivalent CMYK color for spot colors.
  */
 int
-gx_devn_prn_update_spot_equivalent_colors(gx_device *dev, const gs_gstate * pgs)
+gx_devn_prn_update_spot_equivalent_colors(gx_device *dev, const gs_gstate * pgs, const gs_color_space *pcs)
 {
     gx_devn_prn_device *pdev = (gx_devn_prn_device *)dev;
 
-    return update_spot_equivalent_cmyk_colors(dev, pgs, &pdev->devn_params,
+    return update_spot_equivalent_cmyk_colors(dev, pgs, pcs, &pdev->devn_params,
                                               &pdev->equiv_cmyk_colors);
 }
 
@@ -1373,7 +1373,7 @@ spotcmyk_print_page(gx_device_printer *
 
     /* Open the output files for the spot colors */
     for(i = 0; i < nspot; i++) {
-        gs_sprintf(spotname, "%ss%d", pdevn->fname, i);
+        gs_snprintf(spotname, gp_file_name_sizeof, "%ss%d", pdevn->fname, i);
         code = gs_add_control_path(pdev->memory, gs_permit_file_writing, spotname);
         if (code < 0)
             goto prn_done;
@@ -1418,7 +1418,7 @@ spotcmyk_print_page(gx_device_printer *
             goto prn_done;
     }
     for(i = 0; i < nspot; i++) {
-        gs_sprintf(spotname, "%ss%d", pdevn->fname, i);
+        gs_snprintf(spotname, gp_file_name_sizeof, "%ss%d", pdevn->fname, i);
         code = devn_write_pcx_file(pdev, spotname, 1, bpc, linelength[i]);
         if (code < 0)
             goto prn_done;
@@ -1765,7 +1765,7 @@ devn_write_pcx_file(gx_device_printer *
         code = gs_note_error(gs_error_invalidfileaccess);
         goto done;
     }
-    gs_sprintf(outname, "%s.pcx", filename);
+    gs_snprintf(outname, gp_file_name_sizeof, "%s.pcx", filename);
     code = gs_add_control_path(pdev->memory, gs_permit_file_writing, outname);
     if (code < 0)
         goto done;
diff -pruN 9.55.0~dfsg-3/base/gdevdflt.c 9.56.1~dfsg-1/base/gdevdflt.c
--- 9.55.0~dfsg-3/base/gdevdflt.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevdflt.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -376,6 +376,15 @@ set_linear_color_bits_mask_shift(gx_devi
 /* Determine if a number is a power of two.  Works only for integers. */
 #define is_power_of_two(x) ((((x) - 1) & (x)) == 0)
 
+/* A brutish way to check if we are a HT device */
+bool
+device_is_contone(gx_device* pdev)
+{
+    if ((float)pdev->color_info.depth / (float)pdev->color_info.num_components >= 8)
+        return true;
+    return false;
+}
+
 /*
  * This routine attempts to determine if a device's encode_color procedure
  * produces gx_color_index values which are 'separable'.  A 'separable' value
@@ -962,6 +971,8 @@ gx_default_dev_spec_op(gx_device *pdev,
         case gxdso_supports_alpha:
         case gxdso_pdf14_sep_device:
         case gxdso_supports_pattern_transparency:
+        case gxdso_overprintsim_state:
+        case gxdso_skip_icc_component_validation:
             return 0;
         case gxdso_pattern_shfill_doesnt_need_path:
             return (dev_proc(pdev, fill_path) == gx_default_fill_path);
@@ -1027,7 +1038,7 @@ gx_default_include_color_space(gx_device
  * src/gsequivc.c.
  */
 int
-gx_default_update_spot_equivalent_colors(gx_device *pdev, const gs_gstate * pgs)
+gx_default_update_spot_equivalent_colors(gx_device *pdev, const gs_gstate * pgs, const gs_color_space *pcs)
 {
     return 0;
 }
diff -pruN 9.55.0~dfsg-3/base/gdevdrop.c 9.56.1~dfsg-1/base/gdevdrop.c
--- 9.55.0~dfsg-3/base/gdevdrop.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevdrop.c	2022-04-04 13:46:22.000000000 +0000
@@ -1028,6 +1028,130 @@ mem_transform_pixel_region_render_portra
     return template_mem_transform_pixel_region_render_portrait(dev, state, buffer, data_x, cmapper, pgs, state->spp);
 }
 
+static inline int
+template_mem_transform_pixel_region_render_portrait_planar(gx_device *dev, mem_transform_pixel_region_state_t *state, const unsigned char **buffer, int data_x, gx_cmapper_t *cmapper, const gs_gstate *pgs, int spp)
+{
+    gx_device_memory *mdev = (gx_device_memory *)dev;
+    gx_dda_fixed_point pnext;
+    int vci, vdi;
+    int irun;			/* int x/rrun */
+    int w = state->w;
+    int h = state->h;
+    const byte *data = buffer[0] + data_x * spp;
+    const byte *bufend = NULL;
+    const byte *run;
+    int k;
+    gx_color_value *conc = &cmapper->conc[0];
+    gx_cmapper_fn *mapper = cmapper->set_color;
+    int minx, maxx;
+
+    if (h == 0)
+        return 0;
+
+    /* Clip on y */
+    get_portrait_y_extent(state, &vci, &vdi);
+    if (vci < state->clip.p.y)
+        vdi += vci - state->clip.p.y, vci = state->clip.p.y;
+    if (vci+vdi > state->clip.q.y)
+        vdi = state->clip.q.y - vci;
+    if (vdi <= 0)
+        return 0;
+
+    pnext = state->pixels;
+    dda_translate(pnext.x,  (-fixed_epsilon));
+    irun = fixed2int_var_rounded(dda_current(pnext.x));
+    if_debug5m('b', dev->memory, "[b]y=%d data_x=%d w=%d xt=%f yt=%f\n",
+               vci, data_x, w, fixed2float(dda_current(pnext.x)), fixed2float(dda_current(pnext.y)));
+
+    minx = state->clip.p.x;
+    maxx = state->clip.q.x;
+    bufend = data + w * spp;
+    while (data < bufend) {
+        /* Find the length of the next run. It will either end when we hit
+         * the end of the source data, or when the pixel data differs. */
+        run = data + spp;
+        while (1) {
+            dda_next(pnext.x);
+            if (run >= bufend)
+                break;
+            if (memcmp(run, data, spp))
+                break;
+            run += spp;
+        }
+        /* So we have a run of pixels from data to run that are all the same. */
+        /* This needs to be sped up */
+        for (k = 0; k < spp; k++) {
+            conc[k] = gx_color_value_from_byte(data[k]);
+        }
+        mapper(cmapper);
+        /* Fill the region between irun and fixed2int_var_rounded(pnext.x) */
+        {
+            int xi = irun;
+            int wi = (irun = fixed2int_var_rounded(dda_current(pnext.x))) - xi;
+
+            if (wi < 0)
+                xi += wi, wi = -wi;
+
+            if (xi < minx)
+                wi += xi - minx, xi = minx;
+            if (xi+wi > maxx)
+                wi = maxx - xi;
+            if (wi > 0) {
+                /* assert(color_is_pure(&cmapper->devc)); */
+                gx_color_index color = cmapper->devc.colors.pure;
+                for (k = 0; k < spp; k++) {
+                    unsigned char c = (color>>mdev->planes[k].shift) & ((1<<mdev->planes[k].depth)-1);
+                    for (h = 0; h < vdi; h++) {
+                        byte *out = mdev->line_ptrs[vci + h + k*mdev->height] + xi;
+                        memset(out, c, wi);
+                    }
+                }
+            }
+        }
+        data = run;
+    }
+    return 0;
+}
+
+static int
+mem_transform_pixel_region_render_portrait_1p(gx_device *dev, mem_transform_pixel_region_state_t *state, const unsigned char **buffer, int data_x, gx_cmapper_t *cmapper, const gs_gstate *pgs)
+{
+    return template_mem_transform_pixel_region_render_portrait_planar(dev, state, buffer, data_x, cmapper, pgs, 1);
+}
+
+static int
+mem_transform_pixel_region_render_portrait_3p(gx_device *dev, mem_transform_pixel_region_state_t *state, const unsigned char **buffer, int data_x, gx_cmapper_t *cmapper, const gs_gstate *pgs)
+{
+    return template_mem_transform_pixel_region_render_portrait_planar(dev, state, buffer, data_x, cmapper, pgs, 3);
+}
+
+static int
+mem_transform_pixel_region_render_portrait_4p(gx_device *dev, mem_transform_pixel_region_state_t *state, const unsigned char **buffer, int data_x, gx_cmapper_t *cmapper, const gs_gstate *pgs)
+{
+    return template_mem_transform_pixel_region_render_portrait_planar(dev, state, buffer, data_x, cmapper, pgs, 4);
+}
+
+static int
+mem_transform_pixel_region_render_portrait_np(gx_device *dev, mem_transform_pixel_region_state_t *state, const unsigned char **buffer, int data_x, gx_cmapper_t *cmapper, const gs_gstate *pgs)
+{
+    return template_mem_transform_pixel_region_render_portrait_planar(dev, state, buffer, data_x, cmapper, pgs, state->spp);
+}
+
+static int
+mem_transform_pixel_region_render_portrait_planar(gx_device *dev, mem_transform_pixel_region_state_t *state, const unsigned char **buffer, int data_x, gx_cmapper_t *cmapper, const gs_gstate *pgs)
+{
+    switch(state->spp) {
+    case 1:
+        return mem_transform_pixel_region_render_portrait_1p(dev, state, buffer, data_x, cmapper, pgs);
+    case 3:
+        return mem_transform_pixel_region_render_portrait_3p(dev, state, buffer, data_x, cmapper, pgs);
+    case 4:
+        return mem_transform_pixel_region_render_portrait_4p(dev, state, buffer, data_x, cmapper, pgs);
+    default:
+        return mem_transform_pixel_region_render_portrait_np(dev, state, buffer, data_x, cmapper, pgs);
+    }
+}
+
 static int
 mem_transform_pixel_region_render_portrait(gx_device *dev, mem_transform_pixel_region_state_t *state, const unsigned char **buffer, int data_x, gx_cmapper_t *cmapper, const gs_gstate *pgs)
 {
@@ -1564,12 +1688,138 @@ mem_transform_pixel_region_render_landsc
     }
 }
 
+static inline int
+template_mem_transform_pixel_region_render_landscape_planar(gx_device *dev, mem_transform_pixel_region_state_t *state, const unsigned char **buffer, int data_x, gx_cmapper_t *cmapper, const gs_gstate *pgs, int spp)
+{
+    gx_device_memory *mdev = (gx_device_memory *)dev;
+    gx_dda_fixed_point pnext;
+    int vci, vdi;
+    int irun;			/* int x/rrun */
+    int w = state->w;
+    int h = state->h;
+    const byte *data = buffer[0] + data_x * spp;
+    const byte *bufend = NULL;
+    const byte *run;
+    int k;
+    gx_color_value *conc = &cmapper->conc[0];
+    gx_cmapper_fn *mapper = cmapper->set_color;
+    byte *out;
+    int miny, maxy;
+
+    if (h == 0)
+        return 0;
+
+    /* Clip on x */
+    get_landscape_x_extent(state, &vci, &vdi);
+    if (vci < state->clip.p.x)
+        vdi += vci - state->clip.p.x, vci = state->clip.p.x;
+    if (vci+vdi > state->clip.q.x)
+        vdi = state->clip.q.x - vci;
+    if (vdi <= 0)
+        return 0;
+
+    pnext = state->pixels;
+    dda_translate(pnext.x,  (-fixed_epsilon));
+    irun = fixed2int_var_rounded(dda_current(pnext.y));
+    if_debug5m('b', dev->memory, "[b]y=%d data_x=%d w=%d xt=%f yt=%f\n",
+               vci, data_x, w, fixed2float(dda_current(pnext.x)), fixed2float(dda_current(pnext.y)));
+
+    miny = state->clip.p.y;
+    maxy = state->clip.q.y;
+    bufend = data + w * spp;
+    while (data < bufend) {
+        /* Find the length of the next run. It will either end when we hit
+         * the end of the source data, or when the pixel data differs. */
+        run = data + spp;
+        while (1) {
+            dda_next(pnext.y);
+            if (run >= bufend)
+                break;
+            if (memcmp(run, data, spp))
+                break;
+            run += spp;
+        }
+        /* So we have a run of pixels from data to run that are all the same. */
+        /* This needs to be sped up */
+        for (k = 0; k < spp; k++) {
+            conc[k] = gx_color_value_from_byte(data[k]);
+        }
+        mapper(cmapper);
+        /* Fill the region between irun and fixed2int_var_rounded(pnext.y) */
+        {              /* 90 degree rotated rectangle */
+            int yi = irun;
+            int hi = (irun = fixed2int_var_rounded(dda_current(pnext.y))) - yi;
+
+            if (hi < 0)
+                yi += hi, hi = -hi;
+
+            if (yi < miny)
+                hi += yi - miny, yi = miny;
+            if (yi+hi > maxy)
+                hi = maxy - yi;
+            if (hi > 0) {
+                /* assert(color_is_pure(&cmapper->devc)); */
+                gx_color_index color = cmapper->devc.colors.pure;
+                for (k = 0; k < spp; k++) {
+                    unsigned char c = (color>>mdev->planes[k].shift) & ((1<<mdev->planes[k].depth)-1);
+                    for (h = 0; h < hi; h++) {
+                        out = mdev->line_ptrs[yi + h + k * mdev->height] + vci;
+                        memset(out, c, vdi);
+                    }
+                }
+            }
+        }
+        data = run;
+    }
+    return 1;
+}
+
+static int
+mem_transform_pixel_region_render_landscape_1p(gx_device *dev, mem_transform_pixel_region_state_t *state, const unsigned char **buffer, int data_x, gx_cmapper_t *cmapper, const gs_gstate *pgs)
+{
+    return template_mem_transform_pixel_region_render_landscape_planar(dev, state, buffer, data_x, cmapper, pgs, 1);
+}
+
+static int
+mem_transform_pixel_region_render_landscape_3p(gx_device *dev, mem_transform_pixel_region_state_t *state, const unsigned char **buffer, int data_x, gx_cmapper_t *cmapper, const gs_gstate *pgs)
+{
+    return template_mem_transform_pixel_region_render_landscape_planar(dev, state, buffer, data_x, cmapper, pgs, 3);
+}
+
+static int
+mem_transform_pixel_region_render_landscape_4p(gx_device *dev, mem_transform_pixel_region_state_t *state, const unsigned char **buffer, int data_x, gx_cmapper_t *cmapper, const gs_gstate *pgs)
+{
+    return template_mem_transform_pixel_region_render_landscape_planar(dev, state, buffer, data_x, cmapper, pgs, 4);
+}
+
+static int
+mem_transform_pixel_region_render_landscape_np(gx_device *dev, mem_transform_pixel_region_state_t *state, const unsigned char **buffer, int data_x, gx_cmapper_t *cmapper, const gs_gstate *pgs)
+{
+    return template_mem_transform_pixel_region_render_landscape_planar(dev, state, buffer, data_x, cmapper, pgs, state->spp);
+}
+
+static int
+mem_transform_pixel_region_render_landscape_planar(gx_device *dev, mem_transform_pixel_region_state_t *state, const unsigned char **buffer, int data_x, gx_cmapper_t *cmapper, const gs_gstate *pgs)
+{
+    switch (state->spp) {
+    case 1:
+        return mem_transform_pixel_region_render_landscape_1p(dev, state, buffer, data_x, cmapper, pgs);
+    case 3:
+        return mem_transform_pixel_region_render_landscape_3p(dev, state, buffer, data_x, cmapper, pgs);
+    case 4:
+        return mem_transform_pixel_region_render_landscape_4p(dev, state, buffer, data_x, cmapper, pgs);
+    default:
+        return mem_transform_pixel_region_render_landscape_np(dev, state, buffer, data_x, cmapper, pgs);
+    }
+}
+
 static int
 mem_transform_pixel_region_begin(gx_device *dev, int w, int h, int spp,
                       const gx_dda_fixed_point *pixels, const gx_dda_fixed_point *rows,
                       const gs_int_rect *clip, transform_pixel_region_posture posture,
                       mem_transform_pixel_region_state_t **statep)
 {
+    gx_device_memory *mdev = (gx_device_memory *)dev;
     mem_transform_pixel_region_state_t *state;
     gs_memory_t *mem = dev->memory->non_gc_memory;
     *statep = state = (mem_transform_pixel_region_state_t *)gs_alloc_bytes(mem, sizeof(mem_transform_pixel_region_state_t), "mem_transform_pixel_region_state_t");
@@ -1599,7 +1849,9 @@ mem_transform_pixel_region_begin(gx_devi
     if (state->posture == transform_pixel_region_portrait) {
 #ifdef WITH_CAL
         int factor;
-        if (pixels->x.step.dQ == fixed_1*8 && pixels->x.step.dR == 0 && rows->y.step.dQ == fixed_1*8 && rows->y.step.dR == 0) {
+        if (mdev->is_planar) {
+            goto planar;
+        } else if (pixels->x.step.dQ == fixed_1*8 && pixels->x.step.dR == 0 && rows->y.step.dQ == fixed_1*8 && rows->y.step.dR == 0) {
             state->render = mem_transform_pixel_region_render_portrait_1to8;
             factor = 8;
             goto use_doubler;
@@ -1634,11 +1886,18 @@ mem_transform_pixel_region_begin(gx_devi
         } else
 no_cal:
 #endif
-        if (pixels->x.step.dQ == fixed_1 && pixels->x.step.dR == 0)
+        if (mdev->is_planar)
+#ifdef WITH_CAL
+planar:
+#endif
+            state->render = mem_transform_pixel_region_render_portrait_planar;
+        else if (pixels->x.step.dQ == fixed_1 && pixels->x.step.dR == 0)
             state->render = mem_transform_pixel_region_render_portrait_1to1;
         else
             state->render = mem_transform_pixel_region_render_portrait;
-    } else
+    } else if (mdev->is_planar)
+        state->render = mem_transform_pixel_region_render_landscape_planar;
+    else
         state->render = mem_transform_pixel_region_render_landscape;
 
     return 0;
diff -pruN 9.55.0~dfsg-3/base/gdevm1.c 9.56.1~dfsg-1/base/gdevm1.c
--- 9.55.0~dfsg-3/base/gdevm1.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevm1.c	2022-04-04 13:46:22.000000000 +0000
@@ -293,10 +293,10 @@ mem_mono_strip_copy_rop2_dev(gx_device *
                 int sx = sourcex;
                 int dx = x;
                 int w = width;
-                const byte *trow = textures->data + (ty % textures->rep_height) * traster;
+                const byte *trow = textures->data + imod(ty, textures->rep_height) * traster;
                 int xoff = x_offset(phase_x, ty, textures);
                 int nw;
-                int tx = (dx + xoff) % textures->rep_width;
+                int tx = imod(dx + xoff, textures->rep_width);
 
                 /* Loop over (horizontal) copies of the tile. */
                 for (; w > 0; sx += nw, dx += nw, w -= nw, tx = 0) {
@@ -322,10 +322,10 @@ mem_mono_strip_copy_rop2_dev(gx_device *
         for (; line_count-- > 0; drow += draster, ++ty) {
             int dx = x;
             int w = width;
-            const byte *trow = textures->data + (ty % textures->rep_height) * traster;
+            const byte *trow = textures->data + imod(ty, textures->rep_height) * traster;
             int xoff = x_offset(phase_x, ty, textures);
             int nw;
-            int tx = (dx + xoff) % textures->rep_width;
+            int tx = imod(dx + xoff, textures->rep_width);
 
             /* Loop over (horizontal) copies of the tile. */
             for (; w > 0; dx += nw, w -= nw, tx = 0) {
@@ -371,10 +371,10 @@ mem_mono_strip_copy_rop2_dev(gx_device *
             int sx = sourcex;
             int dx = x;
             int w = width;
-            const byte *trow = textures->data + (ty % textures->rep_height) * traster;
+            const byte *trow = textures->data + imod(ty, textures->rep_height) * traster;
             int xoff = x_offset(phase_x, ty, textures);
             int nw;
-            int tx = (dx + xoff) % textures->rep_width;
+            int tx = imod(dx + xoff, textures->rep_width);
 
             /* Loop over (horizontal) copies of the tile. */
             for (; w > 0; sx += nw, dx += nw, w -= nw, tx = 0) {
@@ -907,7 +907,7 @@ int tx, int y, int tw, int th, gx_color_
     fit_fill(dev, tx, y, tw, th);
     invert = (uint)(-(int) color0);
     source_raster = tiles->raster;
-    source_data = tiles->data + ((y + py) % tiles->rep_height) * source_raster;
+    source_data = tiles->data + (imod(y + py, tiles->rep_height) * source_raster;
     tile_bits_size = tiles->size.y * source_raster;
     end = tiles->data + tile_bits_size;
 #undef END_Y_LOOP
@@ -925,7 +925,7 @@ int tx, int y, int tw, int th, gx_color_
      * have source_x = 0.
      */
     {
-        int source_x = (x + px) % tiles->rep_width;
+        int source_x = imod(x + px, tiles->rep_width;
 
         w = tiles->size.x - source_x;
         bptr = source_data + ((source_x & ~chunk_align_bit_mask) >> 3);
diff -pruN 9.55.0~dfsg-3/base/gdevm24.c 9.56.1~dfsg-1/base/gdevm24.c
--- 9.55.0~dfsg-3/base/gdevm24.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevm24.c	2022-04-04 13:46:22.000000000 +0000
@@ -33,7 +33,9 @@
 /*#define USE_MEMCPY*/
 
 /* Define debugging statistics. */
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+/* #define COLLECT_STATS_MEM24 */
+
+#ifdef COLLECT_STATS_MEM24
 struct stats_mem24_s {
     long
         fill, fwide, fgray[101], fsetc, fcolor[101], fnarrow[5],
@@ -124,7 +126,7 @@ mem_true24_fill_rectangle(gx_device * de
      */
     fit_fill_xywh(dev, x, y, w, h);
     INCR(fill);
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_MEM24
     stats_mem24.ftotal += w;
 #endif
     if (w >= 5) {
@@ -206,7 +208,7 @@ mem_true24_fill_rectangle(gx_device * de
                 INCR(fsetc);
                 set_color24_cache(color, r, g, b);
             }
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_MEM24
             {
                 int ci;
                 for (ci = 0; ci < prev_count; ++ci)
diff -pruN 9.55.0~dfsg-3/base/gdevm40.c 9.56.1~dfsg-1/base/gdevm40.c
--- 9.55.0~dfsg-3/base/gdevm40.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevm40.c	2022-04-04 13:46:22.000000000 +0000
@@ -21,7 +21,9 @@
 #include "gdevmem.h"		/* private definitions */
 
 /* Define debugging statistics. */
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+/* #define COLLECT_STATS_MEM40 */
+
+#ifdef COLLECT_STATS_MEM40
 struct stats_mem40_s {
     long
         fill, fwide, fgray[101], fsetc, fcolor[101], fnarrow[5],
@@ -116,7 +118,7 @@ mem_true40_fill_rectangle(gx_device * de
      */
     fit_fill_xywh(dev, x, y, w, h);
     INCR(fill);
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_MEM40
     stats_mem40.ftotal += w;
 #endif
     if (w >= 5) {
@@ -146,7 +148,7 @@ mem_true40_fill_rectangle(gx_device * de
                 INCR(fsetc);
                 set_color40_cache(color, a, b, c, d, e);
             }
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_MEM40
             {
                 int ci;
                 for (ci = 0; ci < prev_count; ++ci)
diff -pruN 9.55.0~dfsg-3/base/gdevm48.c 9.56.1~dfsg-1/base/gdevm48.c
--- 9.55.0~dfsg-3/base/gdevm48.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevm48.c	2022-04-04 13:46:22.000000000 +0000
@@ -21,7 +21,9 @@
 #include "gdevmem.h"		/* private definitions */
 
 /* Define debugging statistics. */
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+/* #define COLLECT_STATS_MEM48 */
+
+#ifdef COLLECT_STATS_MEM48
 struct stats_mem48_s {
     long
         fill, fwide, fgray[101], fsetc, fcolor[101], fnarrow[5],
@@ -113,7 +115,7 @@ mem_true48_fill_rectangle(gx_device * de
      */
     fit_fill_xywh(dev, x, y, w, h);
     INCR(fill);
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_MEM48
     stats_mem48.ftotal += w;
 #endif
     if (w >= 5) {
@@ -141,7 +143,7 @@ mem_true48_fill_rectangle(gx_device * de
                 INCR(fsetc);
                 set_color48_cache(color, a, b, c, d, e, f);
             }
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_MEM48
             {
                 int ci;
                 for (ci = 0; ci < prev_count; ++ci)
diff -pruN 9.55.0~dfsg-3/base/gdevm56.c 9.56.1~dfsg-1/base/gdevm56.c
--- 9.55.0~dfsg-3/base/gdevm56.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevm56.c	2022-04-04 13:46:22.000000000 +0000
@@ -21,7 +21,9 @@
 #include "gdevmem.h"		/* private definitions */
 
 /* Define debugging statistics. */
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+/* #define COLLECT_STATS_MEM56 */
+
+#ifdef COLLECT_STATS_MEM56
 struct stats_mem56_s {
     long
         fill, fwide, fgray[101], fsetc, fcolor[101], fnarrow[5],
@@ -123,7 +125,7 @@ mem_true56_fill_rectangle(gx_device * de
      */
     fit_fill_xywh(dev, x, y, w, h);
     INCR(fill);
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_MEM56
     stats_mem56.ftotal += w;
 #endif
     if (w >= 5) {
@@ -155,7 +157,7 @@ mem_true56_fill_rectangle(gx_device * de
                 INCR(fsetc);
                 set_color56_cache(color, a, b, c, d, e, f, g);
             }
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_MEM56
             {
                 int ci;
                 for (ci = 0; ci < prev_count; ++ci)
diff -pruN 9.55.0~dfsg-3/base/gdevm64.c 9.56.1~dfsg-1/base/gdevm64.c
--- 9.55.0~dfsg-3/base/gdevm64.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevm64.c	2022-04-04 13:46:22.000000000 +0000
@@ -21,7 +21,9 @@
 #include "gdevmem.h"		/* private definitions */
 
 /* Define debugging statistics. */
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+/* #define COLLECT_STATS_MEM64 */
+
+#ifdef COLLECT_STATS_MEM64
 struct stats_mem64_s {
     long
         fill, fwide, fgray[101], fsetc, fcolor[101], fnarrow[5],
@@ -104,7 +106,7 @@ mem_true64_fill_rectangle(gx_device * de
      */
     fit_fill_xywh(dev, x, y, w, h);
     INCR(fill);
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_MEM64
     stats_mem64.ftotal += w;
 #endif
     if (h <= 0)
@@ -112,7 +114,7 @@ mem_true64_fill_rectangle(gx_device * de
     if (w >= 5) {
         INCR(fwide);
         setup_rect(dest);
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_MEM64
         {
             int ci;
             for (ci = 0; ci < prev_count; ++ci)
diff -pruN 9.55.0~dfsg-3/base/gdevmem.c 9.56.1~dfsg-1/base/gdevmem.c
--- 9.55.0~dfsg-3/base/gdevmem.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevmem.c	2022-04-04 13:46:22.000000000 +0000
@@ -375,13 +375,10 @@ gdev_mem_bits_size(const gx_device_memor
     ulong size;
     int pi;
 
-    if (dev->is_planar)
-    {
-        int has_tags = device_encodes_tags((const gx_device *)dev);
-        num_planes = dev->color_info.num_components + has_tags;
+    if (dev->is_planar) {
+        num_planes = dev->color_info.num_components;
         planes = dev->planes;
-    }
-    else
+    } else
         planes = &plane1, plane1.depth = dev->color_info.depth, num_planes = 1;
     for (size = 0, pi = 0; pi < num_planes; ++pi)
         size += bitmap_raster_pad_align(width * planes[pi].depth, dev->pad, dev->log2_align_mod);
@@ -397,10 +394,9 @@ gdev_mem_bits_size(const gx_device_memor
 ulong
 gdev_mem_line_ptrs_size(const gx_device_memory * dev, int width, int height)
 {
-    int has_tags = device_encodes_tags((const gx_device *)dev);
     int num_planes = 1;
     if (dev->is_planar)
-        num_planes = dev->color_info.num_components + has_tags;
+        num_planes = dev->color_info.num_components;
     return (ulong)height * sizeof(byte *) * num_planes;
 }
 int
diff -pruN 9.55.0~dfsg-3/base/gdevmpla.c 9.56.1~dfsg-1/base/gdevmpla.c
--- 9.55.0~dfsg-3/base/gdevmpla.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevmpla.c	2022-04-04 13:46:22.000000000 +0000
@@ -238,10 +238,12 @@ typedef struct mem_save_params_s {
    mdev->base = msp.base,\
    mdev->line_ptrs = msp.line_ptrs)
 
+/* Note tag_offset == 0 if there is no tag plane. Tags always follow the
+   color data, but not neccessarily right after. */
 static int
 put_image_copy_planes(gx_device * dev, const byte **base_ptr, int sourcex,
                       int sraster, gx_bitmap_id id,
-                      int x, int y, int w, int h)
+                      int x, int y, int w, int h, int tag_offset)
 {
     gx_device_memory * const mdev = (gx_device_memory *)dev;
     int plane_depth;
@@ -249,11 +251,16 @@ put_image_copy_planes(gx_device * dev, c
     const gdev_mem_functions *fns;
     int code = 0;
     uchar plane;
+    const byte *base;
+    int last_plane = mdev->color_info.num_components - 1;
 
     MEM_SAVE_PARAMS(mdev, save);
     for (plane = 0; plane < mdev->color_info.num_components; plane++)
     {
-        const byte *base = base_ptr[plane];
+        if (tag_offset && plane == last_plane)
+            base = base_ptr[tag_offset];
+        else
+            base = base_ptr[plane];
         plane_depth = mdev->planes[plane].depth;
         fns = gdev_mem_functions_for_bits(plane_depth);
         if (base == NULL) {
@@ -286,7 +293,8 @@ mem_planar_put_image(gx_device *pdev, gx
 
     put_image_copy_planes(pdev, buffers, 0, row_stride,
                           gx_no_bitmap_id, xstart, ystart,
-                          width, height);
+                          width, height, tag_plane_index);
+
     /* we used all of the data */
     return height;
 }
@@ -1918,7 +1926,7 @@ mem_planar_strip_copy_rop2(gx_device * d
             return gs_note_error(gs_error_VMerror);
         }
         for (j = 0; j < mdev->color_info.num_components; j++) {
-            sbuf = (byte *)sdata + j * sraster;
+            sbuf = (byte *)sdata + j * sraster * planar_height;
             for (i = height; i > 0; i--) {
                 *line_ptrs++ = sbuf;
                 sbuf += sraster;
@@ -1926,7 +1934,7 @@ mem_planar_strip_copy_rop2(gx_device * d
         }
         line_ptrs -= height * mdev->color_info.num_components;
         planar_to_chunky(mdev, sourcex, 0, width, height,
-                         0, chunky_sraster, buf, line_ptrs, planar_height);
+                         0, chunky_sraster, buf, line_ptrs, height);
         gs_free_object(mdev->memory, line_ptrs, "mem_planar_strip_copy_rop(line_ptrs)");
         code = mem_planar_strip_copy_rop2(dev, buf, 0, chunky_sraster,
                                           id, scolors, textures, tcolors,
diff -pruN 9.55.0~dfsg-3/base/gdevmx.c 9.56.1~dfsg-1/base/gdevmx.c
--- 9.55.0~dfsg-3/base/gdevmx.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevmx.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -25,4 +25,4 @@
 
 /* The device descriptor. */
 const gx_device_memory mem_x_device =
-    mem_device("imagex", 256, 0, NULL);
+    mem_device("imagex", 256, 0, mem_initialize_device_procs);
diff -pruN 9.55.0~dfsg-3/base/gdevnfwd.c 9.56.1~dfsg-1/base/gdevnfwd.c
--- 9.55.0~dfsg-3/base/gdevnfwd.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevnfwd.c	2022-04-04 13:46:22.000000000 +0000
@@ -809,14 +809,14 @@ gx_forward_fill_linear_color_triangle(gx
 }
 
 int
-gx_forward_update_spot_equivalent_colors(gx_device *dev, const gs_gstate * pgs)
+gx_forward_update_spot_equivalent_colors(gx_device *dev, const gs_gstate * pgs, const gs_color_space *pcs)
 {
     gx_device_forward * const fdev = (gx_device_forward *)dev;
     gx_device *tdev = fdev->target;
     int code = 0;
 
     if (tdev != NULL)
-        code = dev_proc(tdev, update_spot_equivalent_colors)(tdev, pgs);
+        code = dev_proc(tdev, update_spot_equivalent_colors)(tdev, pgs, pcs);
     return code;
 }
 
diff -pruN 9.55.0~dfsg-3/base/gdevp14.c 9.56.1~dfsg-1/base/gdevp14.c
--- 9.55.0~dfsg-3/base/gdevp14.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevp14.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -926,7 +926,7 @@ template_transform_color_buffer(gs_gstat
     rendering_params.graphics_type_tag = GS_IMAGE_TAG;
     rendering_params.override_icc = false;
     rendering_params.preserve_black = gsBKPRESNOTSPECIFIED;
-    rendering_params.rendering_intent = gsPERCEPTUAL;
+    rendering_params.rendering_intent = gsRELATIVECOLORIMETRIC;  /* Use relative intent */
     rendering_params.cmm = gsCMM_DEFAULT;
     icc_link = gsicc_get_link_profile(pgs, dev, src_profile, des_profile,
         &rendering_params, pgs->memory, false);
@@ -1893,7 +1893,9 @@ static void pdf14_free_mask_stack(pdf14_
         pdf14_mask_t *curr_mask = mask_stack;
         pdf14_mask_t *old_mask;
         while (curr_mask != NULL) {
-            rc_decrement(curr_mask->rc_mask, "pdf14_free_mask_stack");
+            /* Force to decrement until free */
+            while (curr_mask->rc_mask != NULL)
+                rc_decrement(curr_mask->rc_mask, "pdf14_free_mask_stack");
             old_mask = curr_mask;
             curr_mask = curr_mask->previous;
             gs_free_object(old_mask->memory, old_mask, "pdf14_free_mask_stack");
@@ -2064,7 +2066,7 @@ pdf14_pop_transparency_mask(pdf14_ctx *c
                     rendering_params.graphics_type_tag = GS_IMAGE_TAG;
                     rendering_params.override_icc = false;
                     rendering_params.preserve_black = gsBKPRESNOTSPECIFIED;
-                    rendering_params.rendering_intent = gsPERCEPTUAL;
+                    rendering_params.rendering_intent = gsRELATIVECOLORIMETRIC;
                     rendering_params.cmm = gsCMM_DEFAULT;
                     icc_link = gsicc_get_link_profile(pgs, dev, des_profile,
                         src_profile, &rendering_params, pgs->memory, false);
@@ -2514,6 +2516,7 @@ pdf14_put_image(gx_device * dev, gs_gsta
     int i;
     int alpha_offset, tag_offset;
     const byte* buf_ptrs[GS_CLIENT_COLOR_MAX_COMPONENTS];
+    int rendering_intent_saved;
 
     /* Nothing was ever drawn. */
     if (buf == NULL)
@@ -2565,7 +2568,7 @@ pdf14_put_image(gx_device * dev, gs_gsta
 
     /* Check if we have a color conversion issue */
     des_profile = dev_target_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE];
-    if (pdev->using_blend_cs || !gsicc_profiles_equal(des_profile, src_profile))
+    if (!gsicc_profiles_equal(des_profile, src_profile))
         color_mismatch = true;
 
     /* Check if target supports alpha */
@@ -2720,11 +2723,16 @@ pdf14_put_image(gx_device * dev, gs_gsta
     ctm_only_writable(pgs).yy = (float)height;
     ctm_only_writable(pgs).tx = (float)rect.p.x;
     ctm_only_writable(pgs).ty = (float)rect.p.y;
+    /* Make sure that the relative colorimetric rendering intent is
+       used for this image. */
+    rendering_intent_saved = pgs->renderingintent;
+    pgs->renderingintent = gsRELATIVECOLORIMETRIC;
     code = dev_proc(target, begin_typed_image) (target,
                                                 pgs, NULL,
                                                 (gs_image_common_t *)&image,
                                                 NULL, NULL, NULL,
                                                 pgs->memory, &info);
+    pgs->renderingintent = rendering_intent_saved;
     if (code < 0) {
         rc_decrement_only_cs(pcs, "pdf14_put_image");
         return code;
@@ -3178,13 +3186,15 @@ pdf14_put_blended_image_cmykspot(gx_devi
     const byte* buf_ptrs[GS_CLIENT_COLOR_MAX_COMPONENTS];
     int alpha_offset = num_comp;
     int tag_offset = has_tags ? num_comp + 1 : 0;
-    bool keep_native = pdev->overprint_sim && pdev->devn_params.page_spot_colors > 0;
     gs_color_space *pcs;
     gs_image1_t image;
     gx_image_enum_common_t *info;
     gx_image_plane_t planes[GS_IMAGE_MAX_COMPONENTS];
     pdf14_buf *cm_result = NULL;
     bool did_alloc;
+    bool target_sep_device = dev_proc(target, dev_spec_op)(target, gxdso_supports_devn, NULL, 0);
+    bool has_spots = pdev->ctx->num_spots > 0;
+    bool blend_spots = !target_sep_device && has_spots;
 
     /* Check if group color space is CMYK based */
     code = dev_proc(target, get_profile)(target, &dev_target_profile);
@@ -3201,10 +3211,10 @@ pdf14_put_blended_image_cmykspot(gx_devi
         src_profile = pdf14dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE];
     }
 
-    /* If we have spot colors and are doing overprint simulation and the source
-       space is not CMYK due to a blending color space being used, then convert
-       base colors to CMYK so that we can properly blend the spot colors */
-    if (keep_native && src_profile->data_cs != gsCMYK) {
+    /* If the target device does not support spot colors and we have spot colors
+       here due to overprint simulation (blend_spots == true), then we will need to convert the base
+       colors to CMYK if it is RGB or Gray so tha we can blend in the spot colors */
+    if (blend_spots && src_profile->data_cs != gsCMYK) {
 
         cm_result = pdf14_transform_color_buffer_no_matte(pgs, pdev->ctx, (gx_device *)dev, buf,
             buf->data, src_profile, pgs->icc_manager->default_cmyk, 0, 0, buf->rect.q.x,
@@ -3223,7 +3233,7 @@ pdf14_put_blended_image_cmykspot(gx_devi
 #if RAW_DUMP
         buf_ptr = buf->data + (rect.p.y - buf->rect.p.y) * buf->rowstride + ((rect.p.x - buf->rect.p.x) << deep);
         dump_raw_buffer(target->memory, height, width, buf->n_planes, planestride, rowstride,
-            "post_to_cmyk_for_spot_blend", buf_ptr, deep);
+            "convertbase_to_cmyk_for_spot_blend", buf_ptr, deep);
         global_index++;
 #endif
     }
@@ -3235,7 +3245,7 @@ pdf14_put_blended_image_cmykspot(gx_devi
 
     /* Check if we have a color conversion issue */
     des_profile = dev_target_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE];
-    if (pdev->using_blend_cs || !gsicc_profiles_equal(des_profile, src_profile))
+    if (!gsicc_profiles_equal(des_profile, src_profile))
         color_mismatch = true;
 
     /* Check if target supports alpha */
@@ -3322,10 +3332,12 @@ pdf14_put_blended_image_cmykspot(gx_devi
         global_index++;
 #endif
 
-        /* Delay the baking to big endian if we have to do spots to CMYK still.  We will take
-           care of the conversion at that point */
         if (color_mismatch && (src_profile->data_cs == gsRGB || src_profile->data_cs == gsGRAY)) {
             if (deep) {
+            /* In this case, we are NOT going to bring the spots into the CMYK
+               equivalent colors, since otherwise src_profile would be CMYK based.  So
+               16 bit data will be converted now from native endian to big endian during
+               the blending process */
                 pdf14_blend_image_mixed_buffer16(buf_ptr, width, height, rowstride,
                     planestride, num_comp, src_profile->num_comps);
             } else {
@@ -3334,6 +3346,12 @@ pdf14_put_blended_image_cmykspot(gx_devi
             }
         } else {
             if (deep) {
+            /* In this case, if blend_spots == true, we will shortly be bringing
+               the spot colors to CMYK equivalent colors. It is at that time that
+               we will convert from native endian to big endian. In all other
+               cases this blending will due to conversion from native to BE */
+                bool keep_native = (blend_spots == true);
+
                 gx_blend_image_buffer16(buf_ptr, width, height, rowstride,
                     planestride, num_comp, bg, keep_native);
             } else {
@@ -3348,9 +3366,13 @@ pdf14_put_blended_image_cmykspot(gx_devi
         global_index++;
 #endif
 
-        /* If doing simulated overprint, Bring the spot color channels into
-           CMYK. Data is planar and 16 bit data is still in native format. */
-        if (pdev->overprint_sim && pdev->devn_params.page_spot_colors > 0) {
+        /* If doing simulated overprint and we are not going to a sep device and
+           we have spot colors, then bring the spot color channels into CMYK
+           (We should have already converted our base color space to CMYK if it was RGB or gray).
+           At this point, data is planar and 16 bit data is still in native format. It is
+           here that 16 bit data will be converted to BE. Otherwise it will have been converted
+           above during the alpha blend operation. */
+        if (blend_spots) {
             cmyk_composite_map cmyk_map[GX_DEVICE_MAX_SEPARATIONS];  /* Fracs */
 
             /* In the clist case, we need to get equiv spots out of the
@@ -3455,10 +3477,10 @@ pdf14_put_blended_image_cmykspot(gx_devi
     }
 
     /* Sep devices all support put_image (tiffsep and psdcmyk)
-       as well as those devices that support alpha (pngalpha)
-       If we are here, then we are doing an overprint simulation
-       on some other device. Image data is aleady blended and in
-       device color space. */
+       as well as those devices that support alpha (pngalpha,
+       png16malpha). If we are here, then we are doing an
+       overprint simulation on some other device. Image data
+       is aleady blended and in device color space. */
     code = gs_cspace_build_ICC(&pcs, NULL, pgs->memory);
     if (code < 0)
         return code;
@@ -3787,6 +3809,9 @@ pdf14_output_page(gx_device * dev, int n
  * parameters related to page size and resolution, but not any of the
  * color-related parameters, as the pdf14 device retains its own color
  * handling. This routine is parallel to gx_device_copy_params().
+ * Note that it DOES copy the devn_params since these are required to
+ * keep agreement with colorant name->number mapping, and don't change
+ * with the pdf14 color handling.
  */
 static	void
 gs_pdf14_device_copy_params(gx_device *dev, const gx_device *target)
@@ -3794,6 +3819,7 @@ gs_pdf14_device_copy_params(gx_device *d
     cmm_dev_profile_t *profile_targ;
     cmm_dev_profile_t *profile_dev14;
     pdf14_device *pdev = (pdf14_device*) dev;
+    cmm_profile_t *blend_profile = NULL;
     int k;
 
     COPY_PARAM(width);
@@ -3809,11 +3835,7 @@ gs_pdf14_device_copy_params(gx_device *d
     COPY_PARAM(graphics_type_tag);
     COPY_PARAM(interpolate_control);
     memcpy(&(dev->space_params), &(target->space_params), sizeof(gdev_space_params));
-    /* The PDF14 device copies only the default profile not the text etc.
-       TODO: MJV.  It has to make its own device structure but
-       can grab a copy of the profile.  This allows swapping of profiles
-       in the PDF14 device without messing up the target device profile.
-       Also if the device is using a blend color space it will grab that too */
+
     if (dev->icc_struct == NULL) {
         dev->icc_struct = gsicc_new_device_profile_array(dev);
         profile_dev14 = dev->icc_struct;
@@ -3838,17 +3860,30 @@ gs_pdf14_device_copy_params(gx_device *d
         dev->icc_struct->usefastcolor = profile_targ->usefastcolor;
         dev->icc_struct->blacktext = profile_targ->blacktext;
 
-        if (pdev->using_blend_cs) {
-            /* Swap the device profile and the blend profile. */
-            gsicc_adjust_profile_rc(profile_targ->device_profile[GS_DEFAULT_DEVICE_PROFILE],
-                                    1, "gs_pdf14_device_copy_params");
-            gsicc_adjust_profile_rc(profile_targ->blend_profile, 1, "gs_pdf14_device_copy_params");
-            gsicc_adjust_profile_rc(profile_dev14->device_profile[GS_DEFAULT_DEVICE_PROFILE],
-                                    -1, "gs_pdf14_device_copy_params");
-            gsicc_adjust_profile_rc(profile_dev14->blend_profile, -1, "gs_pdf14_device_copy_params");
-            profile_dev14->blend_profile = profile_targ->device_profile[GS_DEFAULT_DEVICE_PROFILE];
-            profile_dev14->device_profile[GS_DEFAULT_DEVICE_PROFILE] = profile_targ->blend_profile;
+        switch (pdev->blend_cs_state) {
+            case PDF14_BLEND_CS_UNSPECIFIED:
+            case PDF14_BLEND_CS_TARGET_CIELAB:
+                /* PDF14_BLEND_CS_TARGET_CIELAB handled
+                   during the device push, when we have
+                   access to the pgs */
+                break;
+            case PDF14_BLEND_CS_OUTPUTINTENT:
+                blend_profile = profile_targ->oi_profile;
+                break;
+            case PDF14_BLEND_CS_SPECIFIED:
+                blend_profile = profile_targ->blend_profile;
+                break;
+            default:
+                break;
+        }
+
+        if (blend_profile != NULL) {
+            /* Set the device profile to the blend profile. Note only default profile is set */
+            gsicc_adjust_profile_rc(blend_profile, 1, "gs_pdf14_device_copy_params");
+            gsicc_adjust_profile_rc(profile_dev14->device_profile[GS_DEFAULT_DEVICE_PROFILE], -1, "gs_pdf14_device_copy_params");
+            profile_dev14->device_profile[GS_DEFAULT_DEVICE_PROFILE] = blend_profile;
         }
+
         profile_dev14->overprint_control = profile_targ->overprint_control;
     }
 #undef COPY_ARRAY_PARAM
@@ -5542,24 +5577,56 @@ pdf14_disable_device(gx_device * dev)
  */
 static	pdf14_default_colorspace_t
 pdf14_determine_default_blend_cs(gx_device * pdev, bool use_pdf14_accum,
-                                 bool *using_blend_cs)
+                                 pdf14_blend_cs_t *blend_cs_state)
 {
     /* If a blend color space was specified, then go ahead and use that to
        define the default color space for the blend modes.  Only Gray, RGB
        or CMYK blend color spaces are allowed.  Note we do not allow this
        setting if we are dealing with a separation device. */
     cmm_dev_profile_t *dev_profile;
+    cmm_profile_t *blend_profile = NULL;
+    pdf14_blend_cs_t temp_cs_state = PDF14_BLEND_CS_UNSPECIFIED;
     int code = dev_proc(pdev, get_profile)(pdev, &dev_profile);
     bool valid_blend_cs = false;
-    *using_blend_cs = false;
 
-    /* Make sure any specified blend color space is valid along with other cond */
-    if (code == 0 && dev_profile->blend_profile != NULL && !use_pdf14_accum) {
-        if (!dev_profile->blend_profile->isdevlink &&
-            !dev_profile->blend_profile->islab &&
-            (dev_profile->blend_profile->data_cs == gsGRAY ||
-             dev_profile->blend_profile->data_cs == gsRGB ||
-             dev_profile->blend_profile->data_cs == gsCMYK)) {
+    *blend_cs_state = PDF14_BLEND_CS_UNSPECIFIED;
+
+    /* Are we using a blend color space or the output intent color space? Also
+       is there a conflict in the settings. i.e. has someone set a blend color
+       space and tried to use the output intent with simulate overprint setting.
+    */
+    if (dev_profile->overprint_control == gs_overprint_control_simulate &&
+        dev_profile->oi_profile != NULL &&
+        !gsicc_profiles_equal(dev_profile->oi_profile, dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE])) {
+        /* If blend profile is also set, throw a warning about output intent not being used. We have
+           possible conflicting command line settings and we will err on using the blend profile
+           if one was specified. */
+        if (dev_profile->blend_profile != NULL &&
+            !gsicc_profiles_equal(dev_profile->blend_profile, dev_profile->oi_profile)) {
+            blend_profile = dev_profile->blend_profile;
+            temp_cs_state = PDF14_BLEND_CS_SPECIFIED;
+            emprintf(pdev->memory, "Warning: OI profile not used for blending CS\n");
+        } else {
+            /* All good, use the output intent profile as we have one
+               and are doing simulate overprint with a different device
+               profile set. */
+            blend_profile = dev_profile->oi_profile;
+            temp_cs_state = PDF14_BLEND_CS_OUTPUTINTENT;
+        }
+    } else if (dev_profile->blend_profile != NULL &&
+               !gsicc_profiles_equal(dev_profile->blend_profile, dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE])) {
+        /* Blend profile is different than device profile */
+        blend_profile = dev_profile->blend_profile;
+        temp_cs_state = PDF14_BLEND_CS_SPECIFIED;
+    }
+
+    /* Make sure any blend color space is valid along with other cond */
+    if (code == 0 && blend_profile != NULL && !use_pdf14_accum) {
+        if (!blend_profile->isdevlink &&
+            !blend_profile->islab &&
+            (blend_profile->data_cs == gsGRAY ||
+             blend_profile->data_cs == gsRGB ||
+             blend_profile->data_cs == gsCMYK)) {
             /* Also, do not allow the use of the blend space when we are pushing
                a pattern pdf14 device.  Those should inherit from the parent */
             if (!(gx_device_is_pattern_clist(pdev) ||
@@ -5579,8 +5646,8 @@ pdf14_determine_default_blend_cs(gx_devi
         * and 3 colorants for DeviceRGB.
         */
         if (valid_blend_cs) {
-            *using_blend_cs = true;
-            switch (dev_profile->blend_profile->num_comps) {
+            *blend_cs_state = temp_cs_state;
+            switch (blend_profile->num_comps) {
             case 1:
                 return PDF14_DeviceGray;
             case 3:
@@ -5626,8 +5693,8 @@ pdf14_determine_default_blend_cs(gx_devi
         if (num_cmyk_used == 4 && pdev->color_info.num_components == 4
             && pdev->color_info.max_components == 4) {
             if (valid_blend_cs) {
-                *using_blend_cs = true;
-                switch (dev_profile->blend_profile->num_comps) {
+                *blend_cs_state = temp_cs_state;
+                switch (blend_profile->num_comps) {
                 case 1:
                     return PDF14_DeviceGray;
                 case 3:
@@ -5650,6 +5717,8 @@ pdf14_determine_default_blend_cs(gx_devi
         /*
          * Otherewise we use a CMYK plus spot colors for blending.
          */
+        if (valid_blend_cs)
+            *blend_cs_state = temp_cs_state;
         return PDF14_DeviceCMYKspot;
     }
 }
@@ -5667,16 +5736,16 @@ get_pdf14_device_proto(gx_device       *
                  const gs_pdf14trans_t *pdf14pct,
                        bool             use_pdf14_accum)
 {
-    bool using_blend_cs;
+    pdf14_blend_cs_t blend_cs_state;
     pdf14_default_colorspace_t dev_cs =
                 pdf14_determine_default_blend_cs(dev, use_pdf14_accum,
-                                                 &using_blend_cs);
+                                                 &blend_cs_state);
     bool deep = device_is_deep(dev);
     int num_spots = pdf14pct->params.num_spot_colors;
 
     /* overprint overide */
-    if (pdf14pct->params.overprint_sim_push) {
-        using_blend_cs = false;
+    if (pdf14pct->params.overprint_sim_push &&
+        blend_cs_state == PDF14_BLEND_CS_UNSPECIFIED) {
         if (pdf14pct->params.num_spot_colors_int > 0) {
             dev_cs = PDF14_DeviceCMYKspot;
             num_spots = pdf14pct->params.num_spot_colors_int;
@@ -5720,7 +5789,6 @@ get_pdf14_device_proto(gx_device       *
              * for the page.
              */
             if (num_spots >= 0) {
-                pdevproto->devn_params.page_spot_colors = num_spots;
                 pdevproto->color_info.num_components =
                     pdevproto->devn_params.num_std_colorant_names + num_spots;
                 if (pdevproto->color_info.num_components > GS_CLIENT_COLOR_MAX_COMPONENTS)
@@ -5750,7 +5818,7 @@ get_pdf14_device_proto(gx_device       *
             return_error(gs_error_rangecheck);
     }
     pdevproto->initialize_device_procs((gx_device *)pdevproto);
-    pdevproto->using_blend_cs = using_blend_cs;
+    pdevproto->blend_cs_state = blend_cs_state;
     pdevproto->overprint_sim = pdf14pct->params.overprint_sim_push;
     return 0;
 }
@@ -5761,9 +5829,9 @@ get_pdf14_device_proto(gx_device       *
 bool
 pdf14_ok_to_optimize(gx_device *dev)
 {
-    bool using_blend_cs;
+    pdf14_blend_cs_t blend_cs_state;
     pdf14_default_colorspace_t pdf14_cs =
-        pdf14_determine_default_blend_cs(dev, false, &using_blend_cs);
+        pdf14_determine_default_blend_cs(dev, false, &blend_cs_state);
     gsicc_colorbuffer_t dev_icc_cs;
     bool ok = false;
     int tag_depth = device_encodes_tags(dev) ? 8 : 0;
@@ -5848,6 +5916,7 @@ pdf14_recreate_device(gs_memory_t *mem,
         pdev->is_planar = true;
     else
         pdev->is_planar = target->is_planar;
+    pdev->interpolate_threshold = dev_proc(target, dev_spec_op)(target, gxdso_interpolate_threshold, NULL, 0);
 
     pdev->procs = dev_proto.procs;
     if (deep) {
@@ -5955,7 +6024,7 @@ gx_update_pdf14_compositor(gx_device * p
             break;
         case PDF14_BEGIN_TRANS_MASK:
             code = gx_begin_transparency_mask(pgs, pdev, &params);
-            if (code >= 0)
+            if (code >= 0 && params.subtype != TRANSPARENCY_MASK_None)
                 p14dev->in_smask_construction++;
             break;
         case PDF14_END_TRANS_MASK:
@@ -6843,6 +6912,7 @@ pdf14_pop_color_model(gx_device* dev, pd
         pdev->blend_procs = group_color->blend_procs;
         pdev->ctx->additive = group_color->isadditive;
         pdev->pdf14_procs = group_color->unpack_procs;
+        pdev->color_info.opmsupported = GX_CINFO_OPMSUPPORTED_UNKNOWN;
         pdev->color_info.depth = group_color->depth;
         pdev->color_info.max_color = group_color->max_color;
         pdev->color_info.max_gray = group_color->max_gray;
@@ -7087,6 +7157,7 @@ pdf14_push_color_model(gx_device *dev, g
     group_color->polarity = pdev->color_info.polarity = new_polarity;
     group_color->num_components = pdev->color_info.num_components = new_num_comps;
     group_color->isadditive = pdev->ctx->additive = new_additive;
+    pdev->color_info.opmsupported = GX_CINFO_OPMSUPPORTED_UNKNOWN;
     group_color->unpack_procs = pdev->pdf14_procs = new_14procs;
     pdev->color_info.depth = new_num_comps * (8<<deep);
     memset(&(pdev->color_info.comp_bits), 0, GX_DEVICE_COLOR_MAX_COMPONENTS);
@@ -7158,6 +7229,9 @@ pdf14_clist_push_color_model(gx_device *
     new_group_color = gs_alloc_struct(dev->memory->stable_memory, pdf14_group_color_t,
         &st_pdf14_clr, "pdf14_clist_push_color_model");
 
+    if (new_group_color == NULL)
+        return_error(gs_error_VMerror);
+
     /* Link to old one */
     new_group_color->previous = pdev->color_model_stack;
 
@@ -7378,6 +7452,7 @@ pdf14_clist_push_color_model(gx_device *
     memcpy(&(pdev->color_info.comp_bits), comp_bits, new_num_comps);
     memcpy(&(pdev->color_info.comp_shift), comp_shift, new_num_comps);
     pdev->color_info.comp_shift[new_num_comps] = new_depth - 8;	/* in case we has_tags is set */
+    pdev->color_info.opmsupported = GX_CINFO_OPMSUPPORTED_UNKNOWN;
 
     /* If we have a compressed color codec, and we are doing a soft mask
        push operation then go ahead and update the color encode and
@@ -7388,6 +7463,7 @@ pdf14_clist_push_color_model(gx_device *
        we add it in to catch for future use. */
     cldev->clist_color_info.depth = pdev->color_info.depth;
     cldev->clist_color_info.polarity = pdev->color_info.polarity;
+    cldev->clist_color_info.opmsupported = GX_CINFO_OPMSUPPORTED_UNKNOWN;
     cldev->clist_color_info.num_components = pdev->color_info.num_components;
     cldev->clist_color_info.max_color = pdev->color_info.max_color;
     cldev->clist_color_info.max_gray = pdev->color_info.max_gray;
@@ -7415,7 +7491,7 @@ pdf14_clist_pop_color_model(gx_device *d
     gx_device_clist_writer * cldev = (gx_device_clist_writer *)pdev->pclist_device;
 
     if (group_color == NULL)
-        return_error(gs_error_unknownerror);  /* Unmatched group pop */
+        return_error(gs_error_Fatal);  /* Unmatched group pop */
 
     if_debug0m('v', pdev->memory, "[v]pdf14_clist_pop_color_model\n");
     /* The color procs are always pushed.  Simply restore them. */
@@ -7431,6 +7507,7 @@ pdf14_clist_pop_color_model(gx_device *d
         set_dev_proc(pdev, get_color_mapping_procs, group_color->group_color_mapping_procs);
         set_dev_proc(pdev, get_color_comp_index, group_color->group_color_comp_index);
         pdev->color_info.polarity = group_color->polarity;
+        pdev->color_info.opmsupported = GX_CINFO_OPMSUPPORTED_UNKNOWN;
         pdev->color_info.depth = group_color->depth;
         pdev->color_info.num_components = group_color->num_components;
         pdev->blend_procs = group_color->blend_procs;
@@ -7443,11 +7520,13 @@ pdf14_clist_pop_color_model(gx_device *d
                             GX_DEVICE_COLOR_MAX_COMPONENTS);
         memcpy(&(pdev->color_info.comp_shift),&(group_color->comp_shift),
                             GX_DEVICE_COLOR_MAX_COMPONENTS);
+
         /* clist writer fill rect has no access to gs_gstate */
         /* and it forwards the target device.  this information */
         /* is passed along to use in this case */
         cldev->clist_color_info.depth = pdev->color_info.depth;
         cldev->clist_color_info.polarity = pdev->color_info.polarity;
+        cldev->clist_color_info.opmsupported = GX_CINFO_OPMSUPPORTED_UNKNOWN;
         cldev->clist_color_info.num_components = pdev->color_info.num_components;
         cldev->clist_color_info.max_color = pdev->color_info.max_color;
         cldev->clist_color_info.max_gray = pdev->color_info.max_gray;
@@ -7616,6 +7695,7 @@ pdf14_end_transparency_mask(gx_device *d
             set_dev_proc(pdev, get_color_mapping_procs, group_color->group_color_mapping_procs);
             set_dev_proc(pdev, get_color_comp_index, group_color->group_color_comp_index);
             pdev->color_info.polarity = group_color->polarity;
+            pdev->color_info.opmsupported = GX_CINFO_OPMSUPPORTED_UNKNOWN;
             pdev->color_info.num_components = group_color->num_components;
             pdev->color_info.depth = group_color->depth;
             pdev->blend_procs = group_color->blend_procs;
@@ -7681,6 +7761,12 @@ do_mark_fill_rectangle_ko_simple(gx_devi
     gx_color_index comps;
     bool has_backdrop = buf->backdrop != NULL;
 
+    /* If we are going out to a CMYK or CMYK + spots pdf14 device (i.e.
+       subtractive) and we are doing overprint with drawn_comps == 0
+       then this is a no-operation */
+    if (overprint && drawn_comps == 0 && !buf->group_color_info->isadditive)
+        return 0;
+
     if (buf->data == NULL)
         return 0;
 #if 0
@@ -7700,6 +7786,9 @@ do_mark_fill_rectangle_ko_simple(gx_devi
      * color spaces.
      */
     if (devn) {
+        if (has_tags) {
+            curr_tag = pdc->tag;
+        }
         if (additive) {
             for (j = 0; j < num_comp; j++) {
                 src[j] = ((pdc->colors.devn.values[j]) >> shift & mask);
@@ -7709,8 +7798,15 @@ do_mark_fill_rectangle_ko_simple(gx_devi
                 src[j] = 255 - ((pdc->colors.devn.values[j]) >> shift & mask);
             }
         }
-    } else
+    } else {
+        if (has_tags) {
+            curr_tag = (color >> (num_comp * 8)) & 0xff;
+        }
         pdev->pdf14_procs->unpack_color(num_comp, color, pdev, src);
+    }
+
+    if (!has_tags)
+        tag_off = 0;
 
     src_alpha = src[num_comp] = (byte)floor (255 * pdev->alpha + 0.5);
     if (has_shape) {
@@ -7718,11 +7814,7 @@ do_mark_fill_rectangle_ko_simple(gx_devi
     } else {
         shape_off = 0;
     }
-    if (has_tags) {
-        curr_tag = (color >> (num_comp*8)) & 0xff;
-    } else {
-        tag_off = 0;
-    }
+
     if (!has_alpha_g)
         alpha_g_off = 0;
     src_alpha = 255 - src_alpha;
@@ -7818,8 +7910,6 @@ do_mark_fill_rectangle_ko_simple(gx_devi
                 dst_ptr[num_comp * planestride] = dst[num_comp];
             }
             if (tag_off) {
-                /* FIXME: As we are knocking out, possibly, we should be
-                 * always overwriting tag values here? */
                 /* If src alpha is 100% then set to curr_tag, else or */
                 /* other than Normal BM, we always OR */
                 if (src[num_comp] == 255 && tag_blend) {
@@ -7892,6 +7982,12 @@ do_mark_fill_rectangle_ko_simple16(gx_de
     gx_color_index comps;
     bool has_backdrop = buf->backdrop != NULL;
 
+    /* If we are going out to a CMYK or CMYK + spots pdf14 device (i.e.
+       subtractive) and we are doing overprint with drawn_comps == 0
+       then this is a no-operation */
+    if (overprint && drawn_comps == 0 && !buf->group_color_info->isadditive)
+        return 0;
+
     if (buf->data == NULL)
         return 0;
 #if 0
@@ -7911,6 +8007,9 @@ do_mark_fill_rectangle_ko_simple16(gx_de
      * color spaces.
      */
     if (devn) {
+        if (has_tags) {
+            curr_tag = pdc->tag;
+        }
         if (additive) {
             for (j = 0; j < num_comp; j++) {
                 src[j] = pdc->colors.devn.values[j];
@@ -7920,8 +8019,12 @@ do_mark_fill_rectangle_ko_simple16(gx_de
                 src[j] = 65535 - pdc->colors.devn.values[j];
             }
         }
-    } else
+    } else {
+        if (has_tags) {
+            curr_tag = (color >> (num_comp * 16)) & 0xff;
+        }
         pdev->pdf14_procs->unpack_color16(num_comp, color, pdev, src);
+    }
 
     src_alpha = src[num_comp] = (uint16_t)floor (65535 * pdev->alpha + 0.5);
     if (has_shape) {
@@ -7929,11 +8032,11 @@ do_mark_fill_rectangle_ko_simple16(gx_de
     } else {
         shape_off = 0;
     }
-    if (has_tags) {
-        curr_tag = (color >> (num_comp*16)) & 0xff;
-    } else {
+
+    if (!has_tags) {
         tag_off = 0;
     }
+
     if (!has_alpha_g)
         alpha_g_off = 0;
     src_alpha = 65535 - src_alpha;
@@ -8509,8 +8612,20 @@ pdf14_dev_spec_op(gx_device *pdev, int d
         rc_decrement_only(tdev, "pdf14_dev_spec_op");
         return 0;
     }
+    if (dev_spec_op == gxdso_interpolate_threshold)
+        return p14dev->interpolate_threshold;
 
-     return dev_proc(p14dev->target, dev_spec_op)(p14dev->target, dev_spec_op, data, size);
+    if (dev_spec_op == gxdso_overprintsim_state) {
+        unsigned char *data_uchar = (unsigned char *) data;
+        data_uchar[0] = (unsigned char) p14dev->overprint_sim;
+        if (p14dev->ctx != NULL)
+            data_uchar[1] = (unsigned char)p14dev->ctx->num_spots; /* pdf14 page device */
+        else
+            data_uchar[1] = (unsigned char)p14dev->devn_params.page_spot_colors;  /* pdf14 clist device */
+        return 1;
+    }
+
+    return dev_proc(p14dev->target, dev_spec_op)(p14dev->target, dev_spec_op, data, size);
 }
 
 /* Needed to set color monitoring in the target device's profile */
@@ -8596,6 +8711,7 @@ gs_pdf14_device_push(gs_memory_t *mem, g
         p14dev->is_planar = true;
     else
         p14dev->is_planar = target->is_planar;
+    p14dev->interpolate_threshold = dev_proc(target, dev_spec_op)(target, gxdso_interpolate_threshold, NULL, 0);
 
     p14dev->alpha = 1.0;
     p14dev->shape = 1.0;
@@ -8618,7 +8734,9 @@ gs_pdf14_device_push(gs_memory_t *mem, g
            we will convert from RGB to CIELAB.  Need to check that we have a
            default profile, which will not be the case if we are coming from the clist reader */
         if ((icc_profile->data_cs == gsCIELAB || icc_profile->islab)
-            && pgs->icc_manager->default_rgb != NULL && !p14dev->using_blend_cs) {
+            && pgs->icc_manager->default_rgb != NULL &&
+            p14dev->blend_cs_state == PDF14_BLEND_CS_UNSPECIFIED) {
+            p14dev->blend_cs_state = PDF14_BLEND_CS_TARGET_CIELAB;
             gsicc_adjust_profile_rc(pgs->icc_manager->default_rgb, 1, "gs_pdf14_device_push");
             gsicc_adjust_profile_rc(p14dev->icc_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE],
                 -1, "gs_pdf14_device_push");
@@ -8637,7 +8755,7 @@ gs_pdf14_device_push(gs_memory_t *mem, g
 
     /* The number of color planes should not exceed that of the target.
        Unless we are using a blend CS */
-    if (!(p14dev->using_blend_cs || p14dev->overprint_sim)) {
+    if (!(p14dev->blend_cs_state != PDF14_BLEND_CS_UNSPECIFIED || p14dev->overprint_sim)) {
         if (p14dev->color_info.num_components > target->color_info.num_components)
             p14dev->color_info.num_components = target->color_info.num_components;
         if (p14dev->color_info.max_components > target->color_info.max_components)
@@ -8660,7 +8778,14 @@ gs_pdf14_device_push(gs_memory_t *mem, g
         p14dev->color_info.comp_shift[p14dev->color_info.num_components] = p14dev->color_info.depth;
         p14dev->color_info.depth += 8;
     }
-
+    /* if the device has separations already defined (by SeparationOrderNames) */
+    /* we need to copy them (allocating new names) so the colorants are in the */
+    /* same order as the target device.                                        */
+    if (dev_proc(target, dev_spec_op)(target, gxdso_supports_devn, NULL, 0)) {
+        code = devn_copy_params(target, (gx_device *)p14dev);
+        if (code < 0)
+            return code;
+    }
     /* by definition pdf14_encode _is_ standard */
     p14dev->color_info.separable_and_linear = GX_CINFO_SEP_LIN_STANDARD;
     gx_device_fill_in_procs((gx_device *)p14dev);
@@ -8742,7 +8867,14 @@ gs_pdf14_device_push(gs_memory_t *mem, g
 
         new_target->PageHandlerPushed = true;
         new_target->ObjectHandlerPushed = true;
-
+        /* if the device has separations already defined (by SeparationOrderNames) */
+        /* we need to copy them (allocating new names) so the colorants are in the */
+        /* same order as the target device.                                        */
+        if (dev_proc(target, dev_spec_op)(target, gxdso_supports_devn, NULL, 0)) {
+            code = devn_copy_params(target, (gx_device *)pdev);
+            if (code < 0)
+                return code;
+        }
         /* UsePlanarBuffer is true in case this is CMYKspot */
         if ((code = gdev_prn_open_planar(new_target, UsePlanarBuffer)) < 0 ||
              !PRINTER_IS_CLIST((gx_device_printer *)new_target)) {
@@ -9302,9 +9434,9 @@ c_pdf14trans_create_default_compositor(c
                 code = 1;
             break;
         default:
-	    /* No other compositor actions are allowed if this isn't a pdf14 compositor */
+            /* No other compositor actions are allowed if this isn't a pdf14 compositor */
             *pp14dev = NULL;
-	    return_error(gs_error_unregistered);
+            return_error(gs_error_unregistered);
     }
     return code;
 }
@@ -9647,7 +9779,7 @@ pdf14_clist_init_procs(gx_device *dev,
     set_dev_proc(dev, decode_color, pdf14_decode_color);
     set_dev_proc(dev, fill_rectangle_hl_color, gx_forward_fill_rectangle_hl_color);
     set_dev_proc(dev, update_spot_equivalent_colors, gx_forward_update_spot_equivalent_colors);
-    set_dev_proc(dev, ret_devn_params, gx_forward_ret_devn_params);
+    set_dev_proc(dev, ret_devn_params, pdf14_ret_devn_params);
     set_dev_proc(dev, fillpage, gx_forward_fillpage);
     set_dev_proc(dev, push_transparency_state, pdf14_push_transparency_state);
     set_dev_proc(dev, pop_transparency_state, pdf14_pop_transparency_state);
@@ -9813,17 +9945,17 @@ get_pdf14_clist_device_proto(gx_device
                        const gs_pdf14trans_t    *pdf14pct,
                              bool                use_pdf14_accum)
 {
-    bool using_blend_cs;
+    pdf14_blend_cs_t blend_cs_state;
     pdf14_default_colorspace_t dev_cs =
                 pdf14_determine_default_blend_cs(dev, use_pdf14_accum,
-                                                 &using_blend_cs);
+                                                 &blend_cs_state);
     bool has_tags = device_encodes_tags(dev);
     bool deep = device_is_deep(dev);
     int num_spots = pdf14pct->params.num_spot_colors;
 
     /* overprint overide */
-    if (pdf14pct->params.overprint_sim_push) {
-        using_blend_cs = false;
+    if (pdf14pct->params.overprint_sim_push &&
+        blend_cs_state == PDF14_BLEND_CS_UNSPECIFIED) {
         if (pdf14pct->params.num_spot_colors_int > 0) {
             dev_cs = PDF14_DeviceCMYKspot;
             num_spots = pdf14pct->params.num_spot_colors_int;
@@ -9924,7 +10056,7 @@ get_pdf14_clist_device_proto(gx_device
             return_error(gs_error_rangecheck);
     }
     pdevproto->overprint_sim = pdf14pct->params.overprint_sim_push;
-    pdevproto->using_blend_cs = using_blend_cs;
+    pdevproto->blend_cs_state = blend_cs_state;
     return 0;
 }
 
@@ -9962,7 +10094,7 @@ pdf14_create_clist_device(gs_memory_t *m
 
     /* If we are not using a blending color space, the number of color planes
        should not exceed that of the target */
-    if (!(pdev->using_blend_cs || pdev->overprint_sim)) {
+    if (!(pdev->blend_cs_state != PDF14_BLEND_CS_UNSPECIFIED || pdev->overprint_sim)) {
         if (pdev->color_info.num_components > target->color_info.num_components)
             pdev->color_info.num_components = target->color_info.num_components;
         if (pdev->color_info.max_components > target->color_info.max_components)
@@ -9976,6 +10108,7 @@ pdf14_create_clist_device(gs_memory_t *m
         pdev->is_planar = true;
     else
         pdev->is_planar = target->is_planar;
+    pdev->interpolate_threshold = dev_proc(target, dev_spec_op)(target, gxdso_interpolate_threshold, NULL, 0);
 
     pdev->op_state = pgs->is_fill_color ? PDF14_OP_STATE_FILL : PDF14_OP_STATE_NONE;
 
@@ -10029,7 +10162,8 @@ pdf14_create_clist_device(gs_memory_t *m
            proper blending.  During put_image we will convert from RGB to
            CIELAB */
         if ((target_profile->data_cs == gsCIELAB || target_profile->islab) &&
-            !pdev->using_blend_cs) {
+            pdev->blend_cs_state == PDF14_BLEND_CS_UNSPECIFIED) {
+            pdev->blend_cs_state = PDF14_BLEND_CS_TARGET_CIELAB;
             rc_assign(pdev->icc_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE],
                 pgs->icc_manager->default_rgb, "pdf14_create_clist_device");
         }
@@ -10043,7 +10177,14 @@ pdf14_create_clist_device(gs_memory_t *m
         pdev->target_support_devn = pdev->icc_struct->supports_devn;
         pdev->icc_struct->supports_devn = true;  /* Reset when pdf14 device is disabled */
     }
-
+    /* if the device has separations already defined (by SeparationOrderNames) */
+    /* we need to copy them (allocating new names) so the colorants are in the */
+    /* same order as the target device.                                        */
+    if (dev_proc(target, dev_spec_op)(target, gxdso_supports_devn, NULL, 0)) {
+        code = devn_copy_params(target, (gx_device *)pdev);
+        if (code < 0)
+            return code;
+    }
     pdev->my_encode_color = dev_proc(pdev, encode_color);
     pdev->my_decode_color = dev_proc(pdev, decode_color);
     pdev->my_get_color_mapping_procs = dev_proc(pdev, get_color_mapping_procs);
@@ -10114,6 +10255,7 @@ pdf14_recreate_clist_device(gs_memory_t
         pdev->is_planar = true;
     else
         pdev->is_planar = target->is_planar;
+    pdev->interpolate_threshold = dev_proc(target, dev_spec_op)(target, gxdso_interpolate_threshold, NULL, 0);
 
     pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN_STANDARD;
     gx_device_fill_in_procs((gx_device *)pdev);
@@ -10214,33 +10356,31 @@ pdf14_accum_get_color_mapping_procs(cons
  *  Device proc for updating the equivalent CMYK color for spot colors.
  */
 static int
-pdf14_accum_update_spot_equivalent_colors(gx_device * dev, const gs_gstate * pgs)
+pdf14_accum_update_spot_equivalent_colors(gx_device * dev, const gs_gstate * pgs, const gs_color_space *pcs)
 {
     gx_device_pdf14_accum *pdev = (gx_device_pdf14_accum *)dev;
     gx_device *tdev = ((pdf14_device *)(pdev->save_p14dev))->target;
-    int code = update_spot_equivalent_cmyk_colors(dev, pgs, &pdev->devn_params,
+    int code = update_spot_equivalent_cmyk_colors(dev, pgs, pcs, &pdev->devn_params,
                                               &pdev->equiv_cmyk_colors);
 
     if (code >= 0 && tdev != NULL)
-        code = dev_proc(tdev, update_spot_equivalent_colors)(tdev, pgs);
+        code = dev_proc(tdev, update_spot_equivalent_colors)(tdev, pgs, pcs);
     return code;
 }
 
 /* Used when doing overprint simulation and have spot colors */
 static int
-pdf14_update_spot_equivalent_colors(gx_device *dev, const gs_gstate *pgs)
+pdf14_update_spot_equivalent_colors(gx_device *dev, const gs_gstate *pgs, const gs_color_space *pcs)
 {
     pdf14_device *pdev = (pdf14_device *)dev;
-    const gs_color_space *pcs;
     int code;
 
     /* Make sure we are not All or None */
-    pcs = gs_currentcolorspace_inline(pgs);
     if (pcs != NULL && pcs->type->index == gs_color_space_index_Separation &&
         pcs->params.separation.sep_type != SEP_OTHER)
             return 0;
 
-    code = update_spot_equivalent_cmyk_colors(dev, pgs, &pdev->devn_params,
+    code = update_spot_equivalent_cmyk_colors(dev, pgs, pcs, &pdev->devn_params,
         &pdev->op_pequiv_cmyk_colors);
     return code;
 }
@@ -10272,7 +10412,7 @@ put_param_pdf14_spot_names(gx_device * p
                 char buff[20];
                 byte * sep_name;
 
-                gs_sprintf(buff, "PDF14SpotName_%d", i);
+                gs_snprintf(buff, sizeof(buff), "PDF14SpotName_%d", i);
                 code = param_read_string(plist, buff, &str);
                 switch (code) {
                     default:
@@ -10808,7 +10948,8 @@ pdf14_clist_update_params(pdf14_clist_de
        do a compositor action */
     if (changed != 0) {
         code = gs_create_pdf14trans(&pct_new, &params, pgs->memory);
-        if (code < 0) return code;
+        if (code < 0)
+            return code;
         code = dev_proc(pdev->target, composite)
                     (pdev->target, &pcdev, pct_new, (gs_gstate *)pgs, pgs->memory, NULL);
         gs_free_object(pgs->memory, pct_new, "pdf14_clist_update_params");
@@ -10843,6 +10984,9 @@ pdf14_clist_fill_path(gx_device	*dev, co
     if (code < 0)
         return code;
 
+    if (dev->color_info.separable_and_linear == GX_CINFO_UNKNOWN_SEP_LIN)
+        check_device_separable(dev);
+
     gsicc_extract_profile(GS_UNKNOWN_TAG, fwd_profile, &icc_profile_fwd,
                           &render_cond);
     gsicc_extract_profile(GS_UNKNOWN_TAG, dev_profile, &icc_profile_dev,
@@ -10865,7 +11009,7 @@ pdf14_clist_fill_path(gx_device	*dev, co
        this, so we have to pass on the clist_writer device to enable proper
        mapping to the transparency group color space. */
 
-    if (pdcolor != NULL && gx_dc_is_pattern2_color(pdcolor)) {
+    if (gx_dc_is_pattern2_color(pdcolor)) {
         /* Non-idempotent blends require a transparency
          * group to be pushed because shadings might
          * paint several pixels twice. */
@@ -10880,10 +11024,15 @@ pdf14_clist_fill_path(gx_device	*dev, co
     }
     if (push_group) {
         gs_fixed_rect box;
-        if (pcpath)
+        gs_fixed_rect dev_bbox;
+
+        if (pcpath) {
             gx_cpath_outer_box(pcpath, &box);
-        else
+            (*dev_proc(dev, get_clipping_box)) (dev, &dev_bbox);
+            rect_intersect(box, dev_bbox);
+        } else
             (*dev_proc(dev, get_clipping_box)) (dev, &box);
+
         if (ppath) {
             gs_fixed_rect path_box;
 
@@ -10897,6 +11046,15 @@ pdf14_clist_fill_path(gx_device	*dev, co
             if (box.q.y > path_box.q.y)
                 box.q.y = path_box.q.y;
         }
+
+        if (box.p.y >= box.q.y || box.p.x >= box.q.x) {
+            /* No need to do anything */
+            if (pinst != NULL) {
+                pinst->saved->trans_device = NULL;
+            }
+            return 0;
+        }
+
         /* Group alpha set from fill value. push_shfill_group does reset to 1.0 */
         code = push_shfill_group(pdev, &new_pgs, &box);
     } else
@@ -10904,7 +11062,10 @@ pdf14_clist_fill_path(gx_device	*dev, co
     if (code >= 0) {
         new_pgs.trans_device = dev;
         new_pgs.has_transparency = true;
-        code = gx_forward_fill_path(dev, &new_pgs, ppath, params, pdcolor, pcpath);
+        if (gx_dc_is_pattern2_color(pdcolor))
+            code = gx_default_fill_path_shading_or_pattern(dev, &new_pgs, ppath, params, pdcolor, pcpath);
+        else
+            code = gx_forward_fill_path(dev, &new_pgs, ppath, params, pdcolor, pcpath);
         new_pgs.trans_device = NULL;
         new_pgs.has_transparency = false;
     }
@@ -10948,7 +11109,7 @@ pdf14_clist_stroke_path(gx_device *dev,
        different color space, then we need to get the proper device information
        passed along so that we use the correct color procs and colorinfo about
        the transparency device and not the final target device */
-    if (pdcolor != NULL && gx_dc_is_pattern2_color(pdcolor)) {
+    if (gx_dc_is_pattern2_color(pdcolor)) {
         /* Non-idempotent blends require a transparency
          * group to be pushed because shadings might
          * paint several pixels twice. */
@@ -11013,7 +11174,10 @@ pdf14_clist_stroke_path(gx_device *dev,
     if (code >= 0) {
         new_pgs.trans_device = dev;
         new_pgs.has_transparency = true;
-        code = gx_forward_stroke_path(dev, &new_pgs, ppath, params, pdcolor, pcpath);
+        if (gx_dc_is_pattern2_color(pdcolor))
+            code = gx_default_stroke_path_shading_or_pattern(dev, &new_pgs, ppath, params, pdcolor, pcpath);
+        else
+            code = gx_forward_stroke_path(dev, &new_pgs, ppath, params, pdcolor, pcpath);
         new_pgs.trans_device = NULL;
         new_pgs.has_transparency = false;
     }
@@ -11238,8 +11402,8 @@ pdf14_clist_fill_stroke_path(gx_device	*
        We will need to break up the fill stroke now and do
        the appropriate group pushes and set up. */
 
-    if ((pdevc_fill != NULL && gx_dc_is_pattern2_color(pdevc_fill)) ||
-        (pdevc_stroke != NULL && gx_dc_is_pattern2_color(pdevc_stroke))) {
+    if (gx_dc_is_pattern2_color(pdevc_fill) ||
+        gx_dc_is_pattern2_color(pdevc_stroke)) {
         return pdf14_clist_fill_stroke_path_pattern_setup(dev, pgs, ppath,
             params_fill, pdevc_fill, params_stroke, pdevc_stroke, pcpath);
     }
@@ -11399,7 +11563,8 @@ pdf14_clist_begin_typed_image(gx_device
                     bbox_in.q.y = pim->Height;
                     code = gs_bbox_transform_inverse(&bbox_in, &(pim->ImageMatrix),
                                                      &bbox_out);
-                    if (code < 0) return code;
+                    if (code < 0)
+                        return code;
                     /* Set up a compositor action for pushing the group */
                     if_debug0m('v', pgs->memory, "[v]Pushing special trans group for image\n");
                     tgp.Isolated = true;
@@ -11637,8 +11802,12 @@ c_pdf14trans_clist_read_update(gs_compos
                           &render_cond);
 
     /* If we are using the blending color space, then be sure to use that. */
-    if (p14dev->using_blend_cs && dev_profile->blend_profile != NULL)
+    if (p14dev->blend_cs_state == PDF14_BLEND_CS_SPECIFIED &&
+        dev_profile->blend_profile != NULL)
         cl_icc_profile = dev_profile->blend_profile;
+    else if (p14dev->blend_cs_state == PDF14_BLEND_CS_OUTPUTINTENT &&
+        dev_profile->oi_profile != NULL)
+        cl_icc_profile = dev_profile->oi_profile;
 
     dev_proc(p14dev, get_profile)((gx_device *)p14dev,  &dev_profile);
     gsicc_extract_profile(GS_UNKNOWN_TAG, dev_profile, &p14_icc_profile,
@@ -12187,6 +12356,7 @@ pdf14_device_finalize(const gs_memory_t
 {
     gx_device * const dev = (gx_device *)vptr;
     pdf14_device * pdev = (pdf14_device *)dev;
+    int k;
 
     pdf14_cleanup_group_color_profiles (pdev);
 
@@ -12198,6 +12368,21 @@ pdf14_device_finalize(const gs_memory_t
     while (pdev->color_model_stack) {
         pdf14_pop_group_color(dev, NULL);
     }
+
+    for (k = 0; k < pdev->devn_params.separations.num_separations; k++) {
+        if (pdev->devn_params.separations.names[k].data) {
+            gs_free_object(pdev->memory->stable_memory, pdev->devn_params.separations.names[k].data, "pdf14_device_finalize");
+            pdev->devn_params.separations.names[k].data = NULL;
+        }
+    }
+
+    for (k = 0; k < pdev->devn_params.pdf14_separations.num_separations; k++) {
+        if (pdev->devn_params.pdf14_separations.names[k].data) {
+            gs_free_object(pdev->memory->stable_memory, pdev->devn_params.pdf14_separations.names[k].data, "pdf14_device_finalize");
+            pdev->devn_params.pdf14_separations.names[k].data = NULL;
+        }
+    }
+
     gx_device_finalize(cmem, vptr);
 }
 
diff -pruN 9.55.0~dfsg-3/base/gdevp14.h 9.56.1~dfsg-1/base/gdevp14.h
--- 9.55.0~dfsg-3/base/gdevp14.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevp14.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -40,6 +40,13 @@ typedef enum {
     PDF14_OP_STATE_STROKE = 2,
 } PDF14_OP_FS_STATE;
 
+typedef enum {
+    PDF14_BLEND_CS_UNSPECIFIED = 0,
+    PDF14_BLEND_CS_TARGET_CIELAB,
+    PDF14_BLEND_CS_OUTPUTINTENT,
+    PDF14_BLEND_CS_SPECIFIED
+} pdf14_blend_cs_t;
+
 /*
  * This structure contains procedures for processing routine which differ
  * between the different blending color spaces.
@@ -100,7 +107,7 @@ struct pdf14_mask_s {
 /* A structure to hold information about the group color related
  * procs and other information. These may change depending upon
  * if the blending space is different than the base space.
- * The structure is a list that is updated upo every transparency
+ * The structure is a list that is updated upon every transparency
  * group push and pop */
 
 typedef struct pdf14_group_color_s pdf14_group_color_t;
@@ -232,7 +239,7 @@ typedef struct pdf14_device_s {
     gx_device * pclist_device;
     bool free_devicen;              /* Used to avoid freeing a deviceN parameter from target clist device */
     bool sep_device;
-    bool using_blend_cs;
+    pdf14_blend_cs_t blend_cs_state;
     bool overprint_sim;
     bool target_support_devn;
 
@@ -264,6 +271,7 @@ typedef struct pdf14_device_s {
     const gx_color_map_procs *(*save_get_cmap_procs)(const gs_gstate *,
                                                      const gx_device *);
     gx_device_color_info saved_target_color_info;
+    int interpolate_threshold;
     dev_proc_encode_color(*saved_target_encode_color);
     dev_proc_decode_color(*saved_target_decode_color);
     dev_proc_get_color_mapping_procs(*saved_target_get_color_mapping_procs);
diff -pruN 9.55.0~dfsg-3/base/gdevprn.c 9.56.1~dfsg-1/base/gdevprn.c
--- 9.55.0~dfsg-3/base/gdevprn.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevprn.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -278,6 +278,7 @@ gdev_prn_allocate(gx_device *pdev, gdev_
     bool save_is_command_list = false; /* Quiet compiler */
     bool size_ok = 0;
     int ecode = 0;
+    int code;
     int pass;
     gs_memory_t *buffer_memory =
         (ppdev->buffer_memory == 0 ? pdev->memory->non_gc_memory :
@@ -455,6 +456,7 @@ gdev_prn_allocate(gx_device *pdev, gdev_
                 gs_free_object(buffer_memory, base, "printer buffer");
                 pdev->procs = ppdev->orig_procs;
                 ppdev->orig_procs.open_device = 0;	/* prevent uninit'd restore of procs */
+                gs_free_object(pdev->memory->non_gc_memory, ppdev->bg_print, "prn bg_print");
                 return_error(code);
             }
         }
@@ -492,14 +494,19 @@ gdev_prn_allocate(gx_device *pdev, gdev_
 #undef COPY_PROC
         /* If using a command list, already opened the device. */
         if (is_command_list)
-            return ecode;
+            code = ecode;
         else
-            return (*dev_proc(pdev, open_device))(pdev);
+            /* If this open_device fails, do we need to free everything? */
+            code = (*dev_proc(pdev, open_device))(pdev);
     } else {
         pdev->procs = ppdev->orig_procs;
         ppdev->orig_procs.open_device = 0;	/* prevent uninit'd restore of procs */
-        return ecode;
+        code = ecode;
     }
+    if (code < 0) {
+          gs_free_object(pdev->memory->non_gc_memory, ppdev->bg_print, "prn bg_print");
+    }
+    return code;
 }
 
 int
@@ -580,8 +587,9 @@ gdev_prn_get_param(gx_device *dev, char
     }
     if (strcmp(Param, "BandListStorage") == 0) {
         gs_param_string bls;
+        gs_lib_ctx_core_t *core = dev->memory->gs_lib_ctx->core;
         /* Force the default to 'memory' if clist file I/O is not included in this build */
-        if (clist_io_procs_file_global == NULL)
+        if (core->clist_io_procs_file == NULL)
             ppdev->BLS_force_memory = true;
         if (ppdev->BLS_force_memory) {
             bls.data = (byte *)"memory";
@@ -629,6 +637,7 @@ gdev_prn_get_params(gx_device * pdev, gs
     gs_param_string bls;
     gs_param_string saved_pages;
     bool pageneutralcolor = false;
+    gs_lib_ctx_core_t *core = pdev->memory->gs_lib_ctx->core;
 
     if (pdev->icc_struct != NULL)
         pageneutralcolor = pdev->icc_struct->pageneutralcolor;
@@ -646,7 +655,7 @@ gdev_prn_get_params(gx_device * pdev, gs
         return code;
 
     /* Force the default to 'memory' if clist file I/O is not included in this build */
-    if (clist_io_procs_file_global == NULL)
+    if (core->clist_io_procs_file == NULL)
         ppdev->BLS_force_memory = true;
     if (ppdev->BLS_force_memory) {
         bls.data = (byte *)"memory";
@@ -709,6 +718,7 @@ gdev_prn_put_params(gx_device * pdev, gs
     gs_param_dict mdict;
     gs_param_string saved_pages;
     bool pageneutralcolor = false;
+    gs_lib_ctx_core_t *core = ppdev->memory->gs_lib_ctx->core;
 
     memset(&saved_pages, 0, sizeof(gs_param_string));
     save_sp = ppdev->space_params;
@@ -751,7 +761,7 @@ gdev_prn_put_params(gx_device * pdev, gs
         case 0:
             /* Only accept 'file' if the file procs are include in the build */
             if ((bls.size > 1) && (bls.data[0] == 'm' ||
-                 (clist_io_procs_file_global != NULL && bls.data[0] == 'f')))
+                 (core->clist_io_procs_file != NULL && bls.data[0] == 'f')))
                 break;
             /* fall through */
         default:
@@ -1184,11 +1194,18 @@ gx_render_plane_init(gx_render_plane_t *
     int num_planes = dev->color_info.num_components;
     int plane_depth = dev->color_info.depth / num_planes;
 
-    if (index < 0 || index >= num_planes)
+    if (index < -1 || index >= num_planes)
         return_error(gs_error_rangecheck);
     render_plane->index = index;
-    render_plane->depth = plane_depth;
-    render_plane->shift = plane_depth * (num_planes - 1 - index);
+    if (index == -1) {
+        /* No plane, chunky results required. */
+        render_plane->depth = dev->color_info.depth;
+        render_plane->shift = 0;
+    } else {
+        /* A single plane */
+        render_plane->depth = plane_depth;
+        render_plane->shift = plane_depth * (num_planes - 1 - index);
+    }
     return 0;
 }
 
@@ -1414,8 +1431,16 @@ gx_default_create_buf_device(gx_device *
 #endif
         gx_device_fill_in_procs((gx_device *)mdev);
     } else {
-        gs_make_mem_device(mdev, mdproto, mem, (color_usage == NULL ? 1 : 0),
-                           target);
+        gs_devn_params* pdevn_params;
+
+        gs_make_mem_device(mdev, mdproto, mem, (color_usage == NULL ? 1 : 0), target);
+        /* mem devices may need to refer to the target's devn_params struct */
+        /* if the device has separations already defined (by SeparationOrderNames), we   */
+        /* need to use them so the colorants are in the same order as the target device. */
+        pdevn_params = dev_proc(target, ret_devn_params)(target);
+        if (pdevn_params != NULL) {
+            mdev->procs.ret_devn_params = gx_forward_ret_devn_params;
+        }
     }
     mdev->width = target->width;
     mdev->band_y = y;
diff -pruN 9.55.0~dfsg-3/base/gdevsclass.c 9.56.1~dfsg-1/base/gdevsclass.c
--- 9.55.0~dfsg-3/base/gdevsclass.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gdevsclass.c	2022-04-04 13:46:22.000000000 +0000
@@ -596,10 +596,10 @@ int default_subclass_fill_linear_color_t
     return gx_default_fill_linear_color_triangle(dev, fa, p0, p1, p2, c0, c1, c2);
 }
 
-int default_subclass_update_spot_equivalent_colors(gx_device *dev, const gs_gstate * pgs)
+int default_subclass_update_spot_equivalent_colors(gx_device *dev, const gs_gstate * pgs, const gs_color_space *pcs)
 {
     if (dev->child)
-        return dev_proc(dev->child, update_spot_equivalent_colors)(dev->child, pgs);
+        return dev_proc(dev->child, update_spot_equivalent_colors)(dev->child, pgs, pcs);
 
     return 0;
 }
diff -pruN 9.55.0~dfsg-3/base/globals.h 9.56.1~dfsg-1/base/globals.h
--- 9.55.0~dfsg-3/base/globals.h	1970-01-01 00:00:00.000000000 +0000
+++ 9.56.1~dfsg-1/base/globals.h	2022-04-04 13:46:22.000000000 +0000
@@ -0,0 +1,41 @@
+/* Copyright (C) 2001-2021 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.
+*/
+
+
+/* This routine abstracts the platform specific ways to get some
+ * global state shared between different instances of the gs libary.
+ * There is special effort required to ensure that multiple instances
+ * do not have race conditions in the setup of such global state. Not
+ * all platforms support this, so on such platforms, no global state
+ * is supported. */
+#include "std.h"
+#include "gslibctx.h"
+
+/* Interface to platform-specific global variable routines. */
+
+#ifndef gs_globals_INCLUDED
+#  define gs_globals_INCLUDED
+
+struct gs_globals
+{
+	int non_threadsafe_count;
+};
+
+void gs_globals_init(gs_globals *globals);
+
+void gp_global_lock(gs_globals *globals);
+void gp_global_unlock(gs_globals *globals);
+
+#endif /* gs_globals_INCLUDED */
diff -pruN 9.55.0~dfsg-3/base/gp.h 9.56.1~dfsg-1/base/gp.h
--- 9.55.0~dfsg-3/base/gp.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gp.h	2022-04-04 13:46:22.000000000 +0000
@@ -23,6 +23,8 @@
 #include "stat_.h"
 #include "gstypes.h"
 #include "gscdefs.h"		/* for gs_serialnumber */
+#include "globals.h"
+
 /*
  * This file defines the interface to ***ALL*** platform-specific routines,
  * with the exception of the thread/synchronization interface (gpsync.h)
@@ -704,4 +706,17 @@ gp_validate_path(const gs_memory_t *mem,
                  const char        *path,
                  const char        *mode);
 
+/*
+ * Fetch a pointer to the globals structure. This is cleverly synchronised
+ * so that the first call to this function is guaranteed to return a safely
+ * (and atomically) initialised structure.
+ *
+ * Platforms that can't perform this synchronisation just return NULL.
+ */
+struct gs_globals *gp_get_globals(void);
+
+void gp_set_debug_mem_ptr(gs_memory_t *mem);
+
+gs_memory_t *gp_get_debug_mem_ptr(void);
+
 #endif /* gp_INCLUDED */
diff -pruN 9.55.0~dfsg-3/base/gp_mspol.c 9.56.1~dfsg-1/base/gp_mspol.c
--- 9.55.0~dfsg-3/base/gp_mspol.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gp_mspol.c	2022-04-04 13:46:22.000000000 +0000
@@ -32,10 +32,9 @@
 int
 gp_check_interrupts(const gs_memory_t *mem)
 {
-#ifndef GS_THREADSAFE
+#ifdef DEBUG
     if(mem == NULL) {
-        /* MAJOR HACK will NOT work in multithreaded environment */
-        mem = gs_lib_ctx_get_non_gc_memory_t();
+        mem = gp_get_debug_mem_ptr();
     }
 #endif
     if (mem && mem->gs_lib_ctx && mem->gs_lib_ctx->core->poll_fn)
diff -pruN 9.55.0~dfsg-3/base/gp_mswin.c 9.56.1~dfsg-1/base/gp_mswin.c
--- 9.55.0~dfsg-3/base/gp_mswin.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gp_mswin.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -60,7 +60,9 @@
 #include "winrtsup.h"
 
 /* Library routines not declared in a standard header */
+#if _MSC_VER < 1400 /* Earlier than VS2005 */
 extern char *getenv(const char *);
+#endif
 
 /* limits */
 #define MAXSTR 255
@@ -298,7 +300,7 @@ get_queues(void)
         char buf[256];
 
         free(enumbuffer);
-        gs_sprintf(buf, "EnumPrinters() failed, error code = %d", GetLastError());
+        gs_snprintf(buf, sizeof(buf), "EnumPrinters() failed, error code = %d", GetLastError());
         MessageBox((HWND) NULL, buf, szAppName, MB_OK | MB_ICONSTOP);
         return NULL;
     }
@@ -401,7 +403,7 @@ gp_printfile_win32(const gs_memory_t *me
     if (!gp_OpenPrinter(port, &printer)) {
         char buf[256];
 
-        gs_sprintf(buf, "OpenPrinter() failed for \042%s\042, error code = %d", port, GetLastError());
+        gs_snprintf(buf, sizeof(buf), "OpenPrinter() failed for \042%s\042, error code = %d", port, GetLastError());
         MessageBox((HWND) NULL, buf, szAppName, MB_OK | MB_ICONSTOP);
         free(buffer);
         return FALSE;
@@ -414,7 +416,7 @@ gp_printfile_win32(const gs_memory_t *me
     if (!StartDocPrinter(printer, 1, (LPBYTE) & di)) {
         char buf[256];
 
-        gs_sprintf(buf, "StartDocPrinter() failed, error code = %d", GetLastError());
+        gs_snprintf(buf, sizeof(buf), "StartDocPrinter() failed, error code = %d", GetLastError());
         MessageBox((HWND) NULL, buf, szAppName, MB_OK | MB_ICONSTOP);
         AbortPrinter(printer);
         free(buffer);
@@ -435,7 +437,7 @@ gp_printfile_win32(const gs_memory_t *me
     if (!EndDocPrinter(printer)) {
         char buf[256];
 
-        gs_sprintf(buf, "EndDocPrinter() failed, error code = %d", GetLastError());
+        gs_snprintf(buf, sizeof(buf), "EndDocPrinter() failed, error code = %d", GetLastError());
         MessageBox((HWND) NULL, buf, szAppName, MB_OK | MB_ICONSTOP);
         AbortPrinter(printer);
         return FALSE;
@@ -443,7 +445,7 @@ gp_printfile_win32(const gs_memory_t *me
     if (!ClosePrinter(printer)) {
         char buf[256];
 
-        gs_sprintf(buf, "ClosePrinter() failed, error code = %d", GetLastError());
+        gs_snprintf(buf, sizeof(buf), "ClosePrinter() failed, error code = %d", GetLastError());
         MessageBox((HWND) NULL, buf, szAppName, MB_OK | MB_ICONSTOP);
         return FALSE;
     }
diff -pruN 9.55.0~dfsg-3/base/gp_nsync.c 9.56.1~dfsg-1/base/gp_nsync.c
--- 9.55.0~dfsg-3/base/gp_nsync.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gp_nsync.c	2022-04-04 13:46:22.000000000 +0000
@@ -18,6 +18,8 @@
 #include "std.h"
 #include "gserrors.h"
 #include "gpsync.h"
+#include "gp.h"
+#include "globals.h"
 
 /* ------- Synchronization primitives -------- */
 
@@ -118,3 +120,26 @@ void
 gp_thread_finish(gp_thread_id thread)
 {
 }
+
+/* No threading -> no globals */
+gs_globals *gp_get_globals(void)
+{
+    return NULL;
+}
+
+void gp_global_lock(gs_globals *globals)
+{
+}
+
+void gp_global_unlock(gs_globals *globals)
+{
+}
+
+void gp_set_debug_mem_ptr(gs_memory_t *mem)
+{
+}
+
+gs_memory_t *gp_get_debug_mem_ptr(void)
+{
+    return NULL;
+}
diff -pruN 9.55.0~dfsg-3/base/gp_psync.c 9.56.1~dfsg-1/base/gp_psync.c
--- 9.55.0~dfsg-3/base/gp_psync.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gp_psync.c	2022-04-04 13:46:22.000000000 +0000
@@ -23,6 +23,8 @@
 #include "gserrors.h"
 #include "gpsync.h"
 #include "assert_.h"
+#include "gp.h"
+#include "globals.h"
 /*
  * Thanks to Larry Jones <larry.jones@sdrc.com> for this revision of
  * Aladdin's original code into a form that depends only on POSIX APIs.
@@ -37,6 +39,65 @@
 #  define PTHREAD_CREATE_DETACHED 1
 #endif
 
+static struct
+{
+    pthread_once_t once;
+    pthread_mutex_t mutex;
+    gs_globals globals;
+#ifdef DEBUG
+    pthread_key_t tlsKey;
+#endif
+} GhostscriptGlobals = { PTHREAD_ONCE_INIT, PTHREAD_MUTEX_INITIALIZER };
+
+static void init_globals(void)
+{
+    if (pthread_mutex_init(&GhostscriptGlobals.mutex, NULL))
+        exit(1);
+#ifdef DEBUG
+    if (pthread_key_create(&GhostscriptGlobals.tlsKey, NULL))
+        exit(1);
+#endif
+    gs_globals_init(&GhostscriptGlobals.globals);
+}
+
+gs_globals *gp_get_globals(void)
+{
+    if (pthread_once(&GhostscriptGlobals.once, init_globals))
+        return NULL;
+
+    return &GhostscriptGlobals.globals;
+}
+
+void gp_global_lock(gs_globals *globals)
+{
+    if (globals == NULL)
+        return;
+    pthread_mutex_lock(&GhostscriptGlobals.mutex);
+}
+
+void gp_global_unlock(gs_globals *globals)
+{
+    if (globals == NULL)
+        return;
+    pthread_mutex_unlock(&GhostscriptGlobals.mutex);
+}
+
+void gp_set_debug_mem_ptr(gs_memory_t *mem)
+{
+#ifdef DEBUG
+    pthread_setspecific(GhostscriptGlobals.tlsKey, mem);
+#endif
+}
+
+gs_memory_t *gp_get_debug_mem_ptr(void)
+{
+#ifdef DEBUG
+    return (gs_memory_t *)pthread_getspecific(GhostscriptGlobals.tlsKey);
+#else
+    return NULL;
+#endif
+}
+
 /* ------- Synchronization primitives -------- */
 
 /* Semaphore supports wait/signal semantics */
@@ -320,6 +381,9 @@ gp_monitor_leave(gp_monitor * mona)
 typedef struct gp_thread_creation_closure_s {
     gp_thread_creation_callback_t proc;  /* actual start procedure */
     void *proc_data;			/* closure data for proc */
+#ifdef DEBUG
+    gs_memory_t *mem;
+#endif
 } gp_thread_creation_closure_t;
 
 /* Wrapper procedure called to start the new thread. */
@@ -330,6 +394,9 @@ gp_thread_begin_wrapper(void *thread_dat
 
     closure = *(gp_thread_creation_closure_t *)thread_data;
     free(thread_data);
+#ifdef DEBUG
+    pthread_setspecific(GhostscriptGlobals.tlsKey, closure.mem);
+#endif
     DISCARD(closure.proc(closure.proc_data));
     return NULL;		/* return value is ignored */
 }
@@ -355,6 +422,9 @@ gp_create_thread(gp_thread_creation_call
         return_error(gs_error_VMerror);
     closure->proc = proc;
     closure->proc_data = proc_data;
+#ifdef DEBUG
+    closure->mem = pthread_getspecific(GhostscriptGlobals.tlsKey);
+#endif
     pthread_attr_init(&attr);
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
     code = pthread_create(&ignore_thread, &attr, gp_thread_begin_wrapper,
@@ -380,6 +450,9 @@ gp_thread_start(gp_thread_creation_callb
         return_error(gs_error_VMerror);
     closure->proc = proc;
     closure->proc_data = proc_data;
+#ifdef DEBUG
+    closure->mem = pthread_getspecific(GhostscriptGlobals.tlsKey);
+#endif
     pthread_attr_init(&attr);
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
     code = pthread_create(&new_thread, &attr, gp_thread_begin_wrapper,
diff -pruN 9.55.0~dfsg-3/base/gp_strdl.c 9.56.1~dfsg-1/base/gp_strdl.c
--- 9.55.0~dfsg-3/base/gp_strdl.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gp_strdl.c	2022-04-04 13:46:22.000000000 +0000
@@ -21,7 +21,7 @@
 #include "gp.h"
 
 int
-gp_readline_init(void **preadline_data, gs_memory_t * mem) /* lgtm [cpp/useless-expression] */
+gp_readline_init(void **preadline_data, gs_memory_t * mem)
 {
     return 0;
 }
diff -pruN 9.55.0~dfsg-3/base/gp_wsync.c 9.56.1~dfsg-1/base/gp_wsync.c
--- 9.55.0~dfsg-3/base/gp_wsync.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gp_wsync.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -20,8 +20,135 @@
 #include "malloc_.h"
 #include "gserrors.h"
 #include "gpsync.h"
+#include "gp.h"
+#include "globals.h"
 #include <process.h>
 
+/* We have 2 possible implementations of the routines to initialise
+ * globals. One uses the InitOnceExecuteOnce facility present in
+ * windows versions >= Vista, the other uses a mutex and
+ * InterlockedCompareExchangePointer and works on versions >=
+ * XP. Accordingly, we use the XP-capable version. The other version
+ * is retained for reference. */
+#define XP_COMPATIBLE_INIT
+
+/* Whatever happens, if we are compiling for a version < Vista
+ * we MUST use the XP version. */
+#if _WIN32_WINNT < 0x600
+#ifndef XP_COMPATIBLE_INIT
+#define XP_COMPATIBLE_INIT
+#endif
+#endif
+
+#ifndef INIT_ONCE_STATIC_INIT
+#define INIT_ONCE_STATIC_INIT { 0 }
+#endif
+
+static struct
+{
+#ifdef XP_COMPATIBLE_INIT
+    HANDLE once_mutex;
+    int inited;
+#else
+    INIT_ONCE once;
+#endif
+    CRITICAL_SECTION lock;
+    gs_globals globals;
+#ifdef DEBUG
+    DWORD tlsIndex;
+#endif
+} GhostscriptGlobals = { INIT_ONCE_STATIC_INIT };
+
+static BOOL CALLBACK init_globals(
+#ifndef XP_COMPATIBLE_INIT
+                                  PINIT_ONCE InitOnce,
+                                  PVOID Parameter,
+#endif
+                                  PVOID *lpContext)
+{
+#ifdef METRO
+    InitializeCriticalSectionEx(&GhostscriptGlobals.lock, 0, 0);	/* returns no status */
+#else
+    InitializeCriticalSection(&GhostscriptGlobals.lock);	/* returns no status */
+#endif
+#ifdef DEBUG
+    GhostscriptGlobals.tlsIndex = TlsAlloc();
+#endif
+    gs_globals_init(&GhostscriptGlobals.globals);
+    return TRUE;
+}
+
+gs_globals *gp_get_globals(void)
+{
+    PVOID lpContext;
+#ifdef XP_COMPATIBLE_INIT
+    /* Prior to Windows Vista, we don't have InitOnceExecuteOnce
+     * capability, so we have to fudge it. Windows XP provides
+     * InterlockedCompareExchangePointer, so we can use that.
+     * We don't care about anything earlier than XP. */
+
+    /* If we haven't got a mutex yet...*/
+    if (GhostscriptGlobals.once_mutex == NULL) {
+        /* Make one */
+        HANDLE p = CreateMutex(NULL, FALSE, NULL);
+        /* Now atomically swap that one into the structure. */
+        if (InterlockedCompareExchangePointer((PVOID *)&GhostscriptGlobals.once_mutex, (PVOID)p, NULL) != NULL) {
+            /* If there was one there already, ditch ours and just use the one that was there already. */
+            CloseHandle(p);
+        }
+    }
+    WaitForSingleObject(GhostscriptGlobals.once_mutex, INFINITE);
+    if (GhostscriptGlobals.inited == 0) {
+        init_globals(&lpContext);
+        GhostscriptGlobals.inited = 1;
+    }
+    ReleaseMutex(GhostscriptGlobals.once_mutex);
+#else
+    BOOL status = InitOnceExecuteOnce(&GhostscriptGlobals.once,
+                                      init_globals,
+                                      NULL,
+                                      &lpContext);
+    if (status == FALSE)
+        return NULL;
+#endif
+
+    return &GhostscriptGlobals.globals;
+}
+
+
+void gp_global_lock(gs_globals *globals)
+{
+    if (globals == NULL)
+        return;
+    EnterCriticalSection(&GhostscriptGlobals.lock);
+}
+
+void gp_global_unlock(gs_globals *globals)
+{
+    if (globals == NULL)
+        return;
+    LeaveCriticalSection(&GhostscriptGlobals.lock);
+}
+
+void gp_set_debug_mem_ptr(gs_memory_t *mem)
+{
+#ifdef DEBUG
+    if (GhostscriptGlobals.tlsIndex != TLS_OUT_OF_INDEXES)
+        TlsSetValue(GhostscriptGlobals.tlsIndex, mem);
+#endif
+}
+
+gs_memory_t *gp_get_debug_mem_ptr(void)
+{
+#ifdef DEBUG
+    if (GhostscriptGlobals.tlsIndex == TLS_OUT_OF_INDEXES)
+        return NULL;
+    return (gs_memory_t *)TlsGetValue(GhostscriptGlobals.tlsIndex);
+#else
+    return NULL;
+#endif
+}
+
 /* It seems that both Borland and Watcom *should* be able to cope with the
  * new style threading using _beginthreadex/_endthreadex. I am unable to test
  * this properly however, and the tests I have done lead me to believe it
@@ -187,6 +314,7 @@ gp_monitor_leave(
 typedef struct gp_thread_creation_closure_s {
     gp_thread_creation_callback_t function;	/* function to start */
     void *data;			/* magic data to pass to thread */
+    gs_memory_t *mem;
 } gp_thread_creation_closure;
 
 /* Origin of new threads started by gp_create_thread */
@@ -199,6 +327,10 @@ gp_thread_begin_wrapper(
 
     closure = *(gp_thread_creation_closure *)thread_data;
     free(thread_data);
+#ifdef DEBUG
+    if (GhostscriptGlobals.tlsIndex != TLS_OUT_OF_INDEXES)
+        TlsSetValue(GhostscriptGlobals.tlsIndex, closure.mem);
+#endif
     (*closure.function)(closure.data);
     _endthread();
 }
@@ -218,6 +350,10 @@ gp_create_thread(
         return_error(gs_error_VMerror);
     closure->function = function;
     closure->data = data;
+#ifdef DEBUG
+    if (GhostscriptGlobals.tlsIndex != TLS_OUT_OF_INDEXES)
+        closure->mem = TlsGetValue(GhostscriptGlobals.tlsIndex);
+#endif
 
     /*
      * Start thread_wrapper.  The Watcom _beginthread returns (int)(-1) if
@@ -246,6 +382,10 @@ gp_thread_start_wrapper(void *thread_dat
 
     closure = *(gp_thread_creation_closure *)thread_data;
     free(thread_data);
+#ifdef DEBUG
+    if (GhostscriptGlobals.tlsIndex != TLS_OUT_OF_INDEXES)
+        TlsSetValue(GhostscriptGlobals.tlsIndex, closure.mem);
+#endif
     (*closure.function)(closure.data);
     _endthreadex(0);
     return 0;
@@ -268,6 +408,10 @@ int gp_thread_start(gp_thread_creation_c
         return_error(gs_error_VMerror);
     closure->function = function;
     closure->data = data;
+#ifdef DEBUG
+    if (GhostscriptGlobals.tlsIndex != TLS_OUT_OF_INDEXES)
+        closure->mem = TlsGetValue(GhostscriptGlobals.tlsIndex);
+#endif
     hThread = (HANDLE)_beginthreadex(NULL, 0, &gp_thread_start_wrapper,
                                      closure, 0, &threadID);
     if (hThread == (HANDLE)0)
diff -pruN 9.55.0~dfsg-3/base/gsalloc.c 9.56.1~dfsg-1/base/gsalloc.c
--- 9.55.0~dfsg-3/base/gsalloc.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsalloc.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -25,20 +25,6 @@
 #include "stream.h"		/* for clearing stream list */
 #include "malloc_.h" /* For MEMENTO */
 
-#if GS_USE_MEMORY_HEADER_ID
-gs_id hdr_id = 0;
-#ifdef DEBUG
-/**** BIG WARNING: Calling this could be catastrophic if "ptr" does not point
- **** to a GS "struct" allocation.
- ****/
-gs_id get_mem_hdr_id (void *ptr)
-{
-    return (*((hdr_id_t *)((byte *)ptr) - HDR_ID_OFFSET));
-}
-#endif
-#endif
-
-
 /*
  * Define whether to try consolidating space before adding a new clump.
  * The default is not to do this, because it is computationally
@@ -1118,7 +1104,6 @@ gs_memory_set_vm_reclaim(gs_ref_memory_t
                 *pfl = *(obj_header_t **)ptr;\
                 ptr[-1].o_size = (obj_size_t)size;\
                 ptr[-1].o_type = pstype;\
-                ASSIGN_HDR_ID(ptr);\
                 /* If debugging, clear the block in an attempt to */\
                 /* track down uninitialized data errors. */\
                 gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
@@ -1127,7 +1112,6 @@ gs_memory_set_vm_reclaim(gs_ref_memory_t
         else if (size > max_freelist_size &&\
                  (ptr = large_freelist_alloc(imem, size)) != 0)\
         {	ptr[-1].o_type = pstype;\
-                ASSIGN_HDR_ID(ptr);\
                 /* If debugging, clear the block in an attempt to */\
                 /* track down uninitialized data errors. */\
                 gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
@@ -1144,7 +1128,6 @@ gs_memory_set_vm_reclaim(gs_ref_memory_t
                 ptr->o_size = (obj_size_t)size;\
                 ptr->o_type = pstype;\
                 ptr++;\
-                ASSIGN_HDR_ID(ptr);\
                 /* If debugging, clear the block in an attempt to */\
                 /* track down uninitialized data errors. */\
                 gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
@@ -1184,7 +1167,7 @@ i_alloc_bytes(gs_memory_t * mem, size_t
 #if IGC_PTR_STABILITY_CHECK
         obj[-1].d.o.space_id = imem->space_id;
 #endif
-    return (byte *) obj;
+    return (byte *)Memento_label(obj, cname);
 }
 static byte *
 i_alloc_bytes_immovable(gs_memory_t * mem, size_t ssize, client_name_t cname)
@@ -1206,7 +1189,7 @@ i_alloc_bytes_immovable(gs_memory_t * me
     if (obj == 0)
         return 0;
     alloc_trace("|+b.", imem, cname, NULL, size, obj);
-    return (byte *) obj;
+    return (byte *)Memento_label(obj, cname);
 }
 static void *
 i_alloc_struct(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
@@ -1239,7 +1222,7 @@ i_alloc_struct(gs_memory_t * mem, gs_mem
 #if IGC_PTR_STABILITY_CHECK
         obj[-1].d.o.space_id = imem->space_id;
 #endif
-    return obj;
+    return Memento_label(obj, cname);
 }
 static void *
 i_alloc_struct_immovable(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
@@ -1257,7 +1240,7 @@ i_alloc_struct_immovable(gs_memory_t * m
     ALLOC_CHECK_SIZE(mem,pstype);
     obj = alloc_obj(imem, size, pstype, ALLOC_IMMOVABLE | ALLOC_DIRECT, cname);
     alloc_trace("|+<.", imem, cname, pstype, size, obj);
-    return obj;
+    return Memento_label(obj, cname);
 }
 
 static inline bool
@@ -1307,7 +1290,7 @@ i_alloc_byte_array(gs_memory_t * mem, si
                alloc_trace_space(imem), client_name_string(cname),
                num_elements * elt_size,
                num_elements, elt_size, (intptr_t)obj);
-    return (byte *) obj;
+    return (byte *)Memento_label(obj, cname);
 }
 static byte *
 i_alloc_byte_array_immovable(gs_memory_t * mem, size_t num_elements,
@@ -1334,7 +1317,7 @@ i_alloc_byte_array_immovable(gs_memory_t
                alloc_trace_space(imem), client_name_string(cname),
                num_elements * elt_size,
                num_elements, elt_size, (intptr_t)obj);
-    return (byte *) obj;
+    return (byte *)Memento_label(obj, cname);
 }
 static void *
 i_alloc_struct_array(gs_memory_t * mem, size_t num_elements,
@@ -1368,7 +1351,7 @@ i_alloc_struct_array(gs_memory_t * mem,
                struct_type_name_string(pstype),
                num_elements * pstype->ssize,
                num_elements, pstype->ssize, (intptr_t)obj);
-    return (char *)obj;
+    return (char *)Memento_label(obj, cname);
 }
 static void *
 i_alloc_struct_array_immovable(gs_memory_t * mem, size_t num_elements,
@@ -1395,7 +1378,7 @@ i_alloc_struct_array_immovable(gs_memory
                struct_type_name_string(pstype),
                num_elements * pstype->ssize,
                num_elements, pstype->ssize, (intptr_t)obj);
-    return (char *)obj;
+    return (char *)Memento_label(obj, cname);
 }
 static void *
 i_resize_object(gs_memory_t * mem, void *obj, size_t new_num_elements,
@@ -1440,7 +1423,7 @@ i_resize_object(gs_memory_t * mem, void
                    client_name_string(cname),
                    struct_type_name_string(pstype),
                    old_size, new_size, (intptr_t)obj);
-        return new_obj;
+        return Memento_label(new_obj, cname);
     }
     /* Punt. */
     new_obj = gs_alloc_struct_array(mem, new_num_elements, void,
@@ -1449,7 +1432,7 @@ i_resize_object(gs_memory_t * mem, void
         return 0;
     memcpy(new_obj, obj, min(old_size, new_size));
     gs_free_object(mem, obj, cname);
-    return new_obj;
+    return Memento_label(new_obj, cname);
 }
 static void
 i_free_object(gs_memory_t * mem, void *ptr, client_name_t cname)
@@ -1624,11 +1607,6 @@ i_alloc_string(gs_memory_t * mem, size_t
      */
     clump_t *cp = clump_splay_walk_init_mid(&sw, imem->cc);
 
-    if (nbytes + (size_t)HDR_ID_OFFSET < nbytes)
-        return NULL;
-
-    nbytes += HDR_ID_OFFSET;
-
 #ifdef MEMENTO
     if (Memento_failThisEvent())
         return NULL;
@@ -1645,8 +1623,6 @@ top:
                    (intptr_t)(imem->cc->ctop - nbytes));
         str = imem->cc->ctop -= nbytes;
         gs_alloc_fill(str, gs_alloc_fill_alloc, nbytes);
-        str += HDR_ID_OFFSET;
-        ASSIGN_HDR_ID(str);
         return str;
     }
     /* Try the next clump. */
@@ -1686,8 +1662,6 @@ i_alloc_string_immovable(gs_memory_t * m
     size_t asize;
     clump_t *cp;
 
-    nbytes += HDR_ID_OFFSET;
-
 #ifdef MEMENTO
     if (Memento_failThisEvent())
         return NULL;
@@ -1705,10 +1679,8 @@ i_alloc_string_immovable(gs_memory_t * m
                alloc_trace_space(imem), client_name_string(cname), nbytes,
                (intptr_t)str);
     gs_alloc_fill(str, gs_alloc_fill_alloc, nbytes);
-    str += HDR_ID_OFFSET;
-    ASSIGN_HDR_ID(str);
 
-    return str;
+    return Memento_label(str, cname);
 }
 
 static byte *
@@ -1721,10 +1693,6 @@ i_resize_string(gs_memory_t * mem, byte
     if (old_num == new_num)	/* same size returns the same string */
         return data;
 
-    data -= HDR_ID_OFFSET;
-    old_num += HDR_ID_OFFSET;
-    new_num += HDR_ID_OFFSET;
-
     if ( imem->cc && data == imem->cc->ctop &&	/* bottom-most string */
         (new_num < old_num ||
          imem->cc->ctop - imem->cc->cbot > new_num - old_num)
@@ -1744,8 +1712,6 @@ i_resize_string(gs_memory_t * mem, byte
         else
             gs_alloc_fill(data, gs_alloc_fill_free, old_num - new_num);
 #endif
-        ptr += HDR_ID_OFFSET;
-        ASSIGN_HDR_ID(ptr);
     } else
         if (new_num < old_num) {
             /* trim the string and create a free space hole */
@@ -1756,13 +1722,7 @@ i_resize_string(gs_memory_t * mem, byte
             if_debug5m('A', mem, "[a%d:<> ]%s(%"PRIuSIZE"->%"PRIuSIZE") "PRI_INTPTR"\n",
                        alloc_trace_space(imem), client_name_string(cname),
                        old_num, new_num, (intptr_t)ptr);
-            ptr += HDR_ID_OFFSET;
-            ASSIGN_HDR_ID(ptr);
         } else {			/* Punt. */
-            data += HDR_ID_OFFSET;
-            old_num -= HDR_ID_OFFSET;
-            new_num -= HDR_ID_OFFSET;
-
             ptr = gs_alloc_string(mem, new_num, cname);
             if (ptr == 0)
                 return 0;
@@ -1780,8 +1740,6 @@ i_free_string(gs_memory_t * mem, byte *
     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
 
     if (data) {
-        data -= HDR_ID_OFFSET;
-        nbytes += HDR_ID_OFFSET;
         if (imem->cc && data == imem->cc->ctop) {
             if_debug4m('A', mem, "[a%d:-> ]%s(%"PRIuSIZE") "PRI_INTPTR"\n",
                        alloc_trace_space(imem), client_name_string(cname), nbytes,
@@ -2073,9 +2031,8 @@ done:
         ptr->d.o.space_id = mem->space_id;
 #   endif
     ptr++;
-    ASSIGN_HDR_ID(ptr);
     gs_alloc_fill(ptr, gs_alloc_fill_alloc, lsize);
-    return ptr;
+    return Memento_label(ptr, cname);
 }
 
 /*
@@ -2770,7 +2727,7 @@ debug_dump_contents(const gs_memory_t *m
             }
             continue;
         }
-        gs_sprintf(label, PRI_INTPTR":", (intptr_t)block);
+        gs_snprintf(label, sizeof(label), PRI_INTPTR":", (intptr_t)block);
         debug_indent(mem, indent);
         dmputs(mem, label);
         for (i = 0; i < block_size; ++i) {
diff -pruN 9.55.0~dfsg-3/base/gsargs.c 9.56.1~dfsg-1/base/gsargs.c
--- 9.55.0~dfsg-3/base/gsargs.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsargs.c	2022-04-04 13:46:22.000000000 +0000
@@ -334,10 +334,31 @@ arg_next(arg_list * pal, const char **ar
                     errprintf(errmem, "Command too long: %s\n", cstr);
                     return_error(gs_error_Fatal);
                 }
-                /* Allow quotes to protect whitespace. */
-                /* (special cases have already been handled and don't reach this point) */
-                if (c == '"')
-                    in_quote = !in_quote;
+                /* Now, some (slightly hairy) code to allow quotes to protect whitespace.
+                 * We only allow for double-quote quoting within @files, as a) command-
+                 * line args passed via argv are zero terminated so we should have no
+                 * confusion with whitespace, and b) callers using the command line will
+                 * have to have carefully quoted double-quotes to make them survive the
+                 * shell anyway! */
+                if (c == '"' && pal->depth > 0) {
+                    if (i == 0 && !in_quote)
+                        in_quote = true;
+                    else if (in_quote) {
+                        /* Need to check the next char to see if we're closing at the end */
+                        c = get_codepoint(pal, pas);
+                        if (c > 0 && c < 256 && isspace(c)) {
+                            /* Reading from an @file, we've hit a space char. That's good, this
+                             * was a close quote. */
+                            cstr[i] = 0;
+                            break;
+                        }
+                        /* Not a close quote, just a literal quote. */
+                        i += codepoint_to_utf8(&cstr[i], '"');
+                        eol = false;
+                        continue; /* Jump to the start of the loop without reading another char. */
+                    } else
+                        i += codepoint_to_utf8(&cstr[i], c);
+                }
                 else
                     i += codepoint_to_utf8(&cstr[i], c);
                 eol = is_eol(c);
diff -pruN 9.55.0~dfsg-3/base/gscdefs.h 9.56.1~dfsg-1/base/gscdefs.h
--- 9.55.0~dfsg-3/base/gscdefs.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gscdefs.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -27,7 +27,7 @@
 
 #ifndef GS_COPYRIGHT
 #  define GS_COPYRIGHT\
-        "Copyright (C) 2021 Artifex Software, Inc.  All rights reserved."
+        "Copyright (C) 2022 Artifex Software, Inc.  All rights reserved."
 #endif
 
 #ifndef GS_PRODUCTFAMILY
diff -pruN 9.55.0~dfsg-3/base/gscdevn.c 9.56.1~dfsg-1/base/gscdevn.c
--- 9.55.0~dfsg-3/base/gscdevn.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gscdevn.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -747,7 +747,7 @@ gx_install_DeviceN(gs_color_space * pcs,
     if (code >= 0) {
         if (dev_proc(pgs->device, update_spot_equivalent_colors))
             code = dev_proc(pgs->device, update_spot_equivalent_colors)
-                                                        (pgs->device, pgs);
+                                                        (pgs->device, pgs, pcs);
     }
     return code;
 }
@@ -774,8 +774,7 @@ gx_set_overprint_DeviceN(const gs_color_
         else
             return gx_set_no_overprint(pgs);
     } else {
-        gs_overprint_params_t   params;
-
+        gs_overprint_params_t   params = { 0 };
 
         params.retain_any_comps = (pgs->overprint && pgs->is_fill_color) ||
                                   (pgs->stroke_overprint && !pgs->is_fill_color);
diff -pruN 9.55.0~dfsg-3/base/gscedata.c 9.56.1~dfsg-1/base/gscedata.c
--- 9.55.0~dfsg-3/base/gscedata.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gscedata.c	2022-04-04 13:46:22.000000000 +0000
@@ -12,7 +12,6 @@
    Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
    CA 94945, U.S.A., +1(415)492-9861, for further information.
 */
-
 /*
  * This file contains substantial parts of toolbin/encs2c.ps,
  * which generated the remainder of the file mechanically from
@@ -23,6 +22,9 @@
  * This source file is maintained manually under source code control,
  * however its content should be regenerated by using encs2c.ps
  * if changes are required.
+ * You should not manually alter this file! If you regenerate it using
+ * encs2c.ps you must regenerate all 4 files; base/gscedata.[c|h]
+ * and devices/vector/gdevpdtv.[c|h]
  */
 
 #include "stdpre.h"
diff -pruN 9.55.0~dfsg-3/base/gscedata.h 9.56.1~dfsg-1/base/gscedata.h
--- 9.55.0~dfsg-3/base/gscedata.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gscedata.h	2022-04-04 13:46:22.000000000 +0000
@@ -12,7 +12,6 @@
    Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
    CA 94945, U.S.A., +1(415)492-9861, for further information.
 */
-
 /*
  * This file contains substantial parts of toolbin/encs2c.ps,
  * which generated the remainder of the file mechanically from
@@ -23,13 +22,14 @@
  * This source file is maintained manually under source code control,
  * however its content should be regenerated by using encs2c.ps
  * if changes are required.
+ * You should not manually alter this file! If you regenerate it using
+ * encs2c.ps you must regenerate all 4 files; base/gscedata.[c|h]
+ * and devices/vector/gdevpdtv.[c|h]
  */
 
 #ifndef gscedata_INCLUDED
 #  define gscedata_INCLUDED
 
-#include "stdpre.h"
-
 #define NUM_LEN_BITS 5
 
 #define N(len,offset) (((offset) << NUM_LEN_BITS) + (len))
diff -pruN 9.55.0~dfsg-3/base/gscoord.c 9.56.1~dfsg-1/base/gscoord.c
--- 9.55.0~dfsg-3/base/gscoord.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gscoord.c	2022-04-04 13:46:22.000000000 +0000
@@ -618,7 +618,6 @@ gx_matrix_to_fixed_coeff(const gs_matrix
     SET_C(yx);
     SET_C(yy);
 #undef SET_C
-#ifndef GS_THREADSAFE
 #ifdef DEBUG
     if (gs_debug_c('x')) {
         dlprintf6("[x]ctm: [%6g %6g %6g %6g %6g %6g]\n",
@@ -628,7 +627,6 @@ gx_matrix_to_fixed_coeff(const gs_matrix
                   pfc->shift);
     }
 #endif
-#endif
     pfc->max_bits = max_bits;
     return 0;
 }
diff -pruN 9.55.0~dfsg-3/base/gscpixel.c 9.56.1~dfsg-1/base/gscpixel.c
--- 9.55.0~dfsg-3/base/gscpixel.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gscpixel.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -110,7 +110,7 @@ gx_remap_concrete_DevicePixel(const gs_c
 static int
 gx_set_overprint_DevicePixel(const gs_color_space * pcs, gs_gstate * pgs)
 {
-    gs_overprint_params_t   params;
+    gs_overprint_params_t params = { 0 };
 
     params.retain_any_comps = false;
     params.effective_opm = pgs->color[0].effective_opm = 0;
diff -pruN 9.55.0~dfsg-3/base/gscsepr.c 9.56.1~dfsg-1/base/gscsepr.c
--- 9.55.0~dfsg-3/base/gscsepr.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gscsepr.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -172,7 +172,7 @@ gx_install_Separation(gs_color_space * p
          */
         if (dev_proc(pgs->device, update_spot_equivalent_colors))
            code = dev_proc(pgs->device, update_spot_equivalent_colors)
-                                                        (pgs->device, pgs);
+                                                        (pgs->device, pgs, pcs);
     }
     return code;
 }
@@ -186,7 +186,7 @@ gx_set_overprint_Separation(const gs_col
     if (pcmap->use_alt_cspace)
         return gx_set_no_overprint(pgs);
     else {
-        gs_overprint_params_t   params;
+        gs_overprint_params_t params = { 0 };
 
         params.retain_any_comps = (((pgs->overprint && pgs->is_fill_color) ||
                                    (pgs->stroke_overprint && !pgs->is_fill_color)) &&
diff -pruN 9.55.0~dfsg-3/base/gsdcolor.h 9.56.1~dfsg-1/base/gsdcolor.h
--- 9.55.0~dfsg-3/base/gsdcolor.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsdcolor.h	2022-04-04 13:46:22.000000000 +0000
@@ -376,7 +376,6 @@ struct gx_device_color_saved_s {
         } devn;
         struct _pattern {
             gs_id id;
-            gs_int_point phase;
         }		pattern;
         struct _pattern2 {
             gs_id id;
diff -pruN 9.55.0~dfsg-3/base/gsdevice.c 9.56.1~dfsg-1/base/gsdevice.c
--- 9.55.0~dfsg-3/base/gsdevice.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsdevice.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -820,12 +820,8 @@ gx_device_raster(const gx_device * dev,
 
         /* depth can be <= num_components if planar and MEM_SET_PARAMS has changed it */
         if (depth <= num_components || bpc >= 8) {
-            /* tag plane requires at least 8 bits (per component as well as tags) */
-            int has_tags = bpc >= 8 ? device_encodes_tags(dev): 0;
-
-            bits /= (num_components + has_tags);
-        }
-        else {
+            bits /= num_components;
+        } else {
             /* depth is original depth, not the plane_depth since it is > num_components */
             bits /= (depth / bpc);
         }
@@ -1206,13 +1202,15 @@ int gx_device_delete_output_file(const g
     const char *fmt;
     char *pfname = (char *)gs_alloc_bytes(dev->memory, gp_file_name_sizeof, "gx_device_delete_output_file(pfname)");
     int code;
+    size_t len;
 
     if (pfname == NULL) {
         code = gs_note_error(gs_error_VMerror);
         goto done;
     }
 
-    code = gx_parse_output_file_name(&parsed, &fmt, fname, strlen(fname),
+    len = strlen(fname);
+    code = gx_parse_output_file_name(&parsed, &fmt, fname, len,
                                          dev->memory);
     if (code < 0) {
         goto done;
@@ -1227,11 +1225,11 @@ int gx_device_delete_output_file(const g
         while (*fmt != 'l' && *fmt != '%')
             --fmt;
         if (*fmt == 'l')
-            gs_sprintf(pfname, parsed.fname, count1);
+            gs_snprintf(pfname, len, parsed.fname, count1);
         else
-            gs_sprintf(pfname, parsed.fname, (int)count1);
+            gs_snprintf(pfname, len, parsed.fname, (int)count1);
     } else if (parsed.len && strchr(parsed.fname, '%'))	/* filename with "%%" but no "%nnd" */
-        gs_sprintf(pfname, parsed.fname);
+        gs_snprintf(pfname, len, parsed.fname);
     else
         pfname[0] = 0; /* 0 to use "fname", not "pfname" */
     if (pfname[0]) {
@@ -1304,11 +1302,11 @@ gx_device_open_output_file(const gx_devi
         while (*fmt != 'l' && *fmt != '%')
             --fmt;
         if (*fmt == 'l')
-            gs_sprintf(pfname, parsed.fname, count1);
+            gs_snprintf(pfname, gp_file_name_sizeof, parsed.fname, count1);
         else
-            gs_sprintf(pfname, parsed.fname, (int)count1);
+            gs_snprintf(pfname, gp_file_name_sizeof, parsed.fname, (int)count1);
     } else if (parsed.len && strchr(parsed.fname, '%'))	/* filename with "%%" but no "%nnd" */
-        gs_sprintf(pfname, parsed.fname);
+        gs_snprintf(pfname, gp_file_name_sizeof, parsed.fname);
     else
         pfname[0] = 0; /* 0 to use "fname", not "pfname" */
     if (pfname[0]) {
@@ -1435,3 +1433,25 @@ gdev_space_params_cmp(const gdev_space_p
 
   return(0);
 }
+
+static void
+release_nts_lock(gx_device *dev)
+{
+    (void)gs_lib_ctx_nts_adjust(dev->memory, -1);
+}
+
+int gx_init_non_threadsafe_device(gx_device *dev)
+{
+    int code;
+
+    if (dev == NULL || dev->finalize != NULL)
+        return gs_error_unknownerror;
+
+    code = gs_lib_ctx_nts_adjust(dev->memory, 1);
+    if (code < 0)
+        return code;
+
+    dev->finalize = release_nts_lock;
+
+    return 0;
+}
diff -pruN 9.55.0~dfsg-3/base/gsdparam.c 9.56.1~dfsg-1/base/gsdparam.c
--- 9.55.0~dfsg-3/base/gsdparam.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsdparam.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -921,7 +921,7 @@ gdev_write_input_media(int index, gs_par
     int code;
     gs_param_string as;
 
-    gs_sprintf(key, "%d", index);
+    gs_snprintf(key, sizeof(key), "%d", index);
     mdict.size = 4;
     code = param_begin_write_dict(pdict->list, key, &mdict, false);
     if (code < 0)
@@ -1009,7 +1009,7 @@ gdev_write_output_media(int index, gs_pa
     gs_param_dict mdict;
     int code;
 
-    gs_sprintf(key, "%d", index);
+    gs_snprintf(key, sizeof(key), "%d", index);
     mdict.size = 4;
     code = param_begin_write_dict(pdict->list, key, &mdict, false);
     if (code < 0)
diff -pruN 9.55.0~dfsg-3/base/gsequivc.c 9.56.1~dfsg-1/base/gsequivc.c
--- 9.55.0~dfsg-3/base/gsequivc.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsequivc.c	2022-04-04 13:46:22.000000000 +0000
@@ -283,10 +283,10 @@ static bool check_all_colors_known(int n
 
 /* If possible, update the equivalent CMYK color for a spot color */
 int
-update_spot_equivalent_cmyk_colors(gx_device * pdev, const gs_gstate * pgs,
+update_spot_equivalent_cmyk_colors(gx_device * pdev, const gs_gstate * pgs, const gs_color_space * pcs_in,
     gs_devn_params * pdevn_params, equivalent_cmyk_color_params * pparams)
 {
-    const gs_color_space * pcs;
+    const gs_color_space * pcs = pcs_in;
     cmm_dev_profile_t *dev_profile;
     int code;
 
@@ -303,12 +303,16 @@ update_spot_equivalent_cmyk_colors(gx_de
         pparams->all_color_info_valid = true;
         return 0;
     }
+
+    /* If the caller apssed in NULL for the colour space, use the current colour space */
+    if (pcs_in == NULL)
+        pcs = gs_currentcolorspace_inline(pgs);
+
     /*
      * Verify that the given color space is a Separation or a DeviceN color
      * space.  If so then when check if the color space contains a separation
      * color for which we need a CMYK equivalent.
      */
-    pcs = gs_currentcolorspace_inline(pgs);
     if (pcs != NULL) {
         if (pcs->type->index == gs_color_space_index_Separation) {
             update_Separation_spot_equivalent_cmyk_colors(pdev, pgs, pcs,
diff -pruN 9.55.0~dfsg-3/base/gsequivc.h 9.56.1~dfsg-1/base/gsequivc.h
--- 9.55.0~dfsg-3/base/gsequivc.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsequivc.h	2022-04-04 13:46:22.000000000 +0000
@@ -61,7 +61,8 @@ typedef struct equivalent_cmyk_color_par
  * If possible, update the equivalent CMYK color for spot colors.
  */
 int update_spot_equivalent_cmyk_colors(gx_device * pdev,
-                const gs_gstate * pgs, gs_devn_params * pdevn_params,
+                const gs_gstate * pgs, const gs_color_space * pcs,
+                gs_devn_params * pdevn_params,
                 equivalent_cmyk_color_params * pparams);
 
 /*
diff -pruN 9.55.0~dfsg-3/base/gserrors.h 9.56.1~dfsg-1/base/gserrors.h
--- 9.55.0~dfsg-3/base/gserrors.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gserrors.h	2022-04-04 13:46:22.000000000 +0000
@@ -129,16 +129,11 @@ enum gs_error_type {
 typedef enum gs_error_type gs_error_t;
 
 int gs_log_error(int, const char *, int);
-#if !defined(DEBUG) || defined(GS_THREADSAFE)
+#if !defined(DEBUG)
 #  define gs_log_error(err, file, line) (err)
 #endif
-#ifdef GS_THREADSAFE
-#define gs_note_error(err) (err)
-#define return_error(err) return (err)
-#else
 #define gs_note_error(err) gs_log_error(err, __FILE__, __LINE__)
 #define return_error(err) return gs_note_error(err)
-#endif
 
 #if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L
 #  if defined(__GNUC__) && __GNUC__ >= 2
@@ -168,105 +163,6 @@ int gs_log_error(int, const char *, int)
 
 const char *gs_errstr(int code);
 
-#ifdef GS_THREADSAFE
-/* In threadsafe builds, we just swallow errors unreported */
-#define gs_throw_code(code) \
-    (code)
-
-#define gs_throw(code, fmt) \
-    (code)
-#define gs_throw1(code, fmt, arg1) \
-    (code)
-#define gs_throw2(code, fmt, arg1, arg2) \
-    (code)
-#define gs_throw3(code, fmt, arg1, arg2, arg3) \
-    (code)
-#define gs_throw4(code, fmt, arg1, arg2, arg3, arg4) \
-    (code)
-#define gs_throw5(code, fmt, arg1, arg2, arg3, arg4, arg5) \
-    (code)
-#define gs_throw6(code, fmt, arg1, arg2, arg3, arg4, arg5, arg6) \
-    (code)
-#define gs_throw7(code, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
-    (code)
-#define gs_throw8(code, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
-    (code)
-#define gs_throw9(code, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
-    (code)
-
-/* Bubble the code up the stack
-*/
-#define gs_rethrow_code(code) \
-    (code)
-
-#define gs_rethrow(code, fmt) \
-    (code)
-#define gs_rethrow1(code, fmt, arg1) \
-    (code)
-#define gs_rethrow2(code, fmt, arg1, arg2) \
-    (code)
-#define gs_rethrow3(code, fmt, arg1, arg2, arg3) \
-    (code)
-#define gs_rethrow4(code, fmt, arg1, arg2, arg3, arg4) \
-    (code)
-#define gs_rethrow5(code, fmt, arg1, arg2, arg3, arg4, arg5) \
-    (code)
-#define gs_rethrow6(code, fmt, arg1, arg2, arg3, arg4, arg5, arg6) \
-    (code)
-#define gs_rethrow7(code, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
-    (code)
-#define gs_rethrow8(code, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
-    (code)
-#define gs_rethrow9(code, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
-    (code)
-
-/* This will cause trouble, as it implies you are fixing an error
- * the system will spew messages
- */
-#define gs_catch(code, fmt) \
-    (code)
-#define gs_catch1(code, fmt, arg1) \
-    (code)
-#define gs_catch2(code, fmt, arg1, arg2) \
-    (code)
-#define gs_catch3(code, fmt, arg1, arg2, arg3) \
-    (code)
-#define gs_catch4(code, fmt, arg1, arg2, arg3, arg4) \
-    (code)
-#define gs_catch5(code, fmt, arg1, arg2, arg3, arg4, arg5) \
-    (code)
-#define gs_catch6(code, fmt, arg1, arg2, arg3, arg4, arg5, arg6) \
-    (code)
-#define gs_catch7(code, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
-    (code)
-#define gs_catch8(code, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
-    (code)
-#define gs_catch9(code, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
-    (code)
-
-/* gs_warn is a printf
- */
-#define gs_warn(fmt) \
-    DO_NOTHING
-#define gs_warn1(fmt, arg1) \
-    DO_NOTHING
-#define gs_warn2(fmt, arg1, arg2) \
-    DO_NOTHING
-#define gs_warn3(fmt, arg1, arg2, arg3) \
-    DO_NOTHING
-#define gs_warn4(fmt, arg1, arg2, arg3, arg4) \
-    DO_NOTHING
-#define gs_warn5(fmt, arg1, arg2, arg3, arg4, arg5) \
-    DO_NOTHING
-#define gs_warn6(fmt, arg1, arg2, arg3, arg4, arg5, arg6) \
-    DO_NOTHING
-#define gs_warn7(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
-    DO_NOTHING
-#define gs_warn8(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
-    DO_NOTHING
-#define gs_warn9(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
-    DO_NOTHING
-#else
 int gs_throw_imp(const char *func, const char *file, int line,
         int op, int code, const char *fmt, ...) __printflike(6, 7);
 
@@ -368,7 +264,6 @@ int gs_throw_imp(const char *func, const
     (void)gs_throw_imp(__func__, __FILE__, __LINE__, 3, 0, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
 #define gs_warn9(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
     (void)gs_throw_imp(__func__, __FILE__, __LINE__, 3, 0, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
-#endif
 
 /* just in case you don't know 0 means no error
  * other return codes are errors.
diff -pruN 9.55.0~dfsg-3/base/gsfcmap1.c 9.56.1~dfsg-1/base/gsfcmap1.c
--- 9.55.0~dfsg-3/base/gsfcmap1.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsfcmap1.c	2022-04-04 13:46:22.000000000 +0000
@@ -23,6 +23,7 @@
 #include "gsstruct.h"
 #include "gsutil.h"		/* for gs_next_ids */
 #include "gxfcmap1.h"
+#include "gp.h"
 
 /* Get a big-endian integer. */
 static inline ulong
@@ -77,22 +78,24 @@ public_st_cmap_lookup_range_element();
  * multi-dimensional range comparator
  */
 
-#ifndef GS_THREADSAFE
 #ifdef DEBUG
 static void
 print_msg_str_in_range(const byte *str,
                        const byte *key_lo, const byte *key_hi,
                        int key_size)
 {
-    debug_print_string_hex_nomem(str, key_size);
-    dlprintf(" in ");
-    debug_print_string_hex_nomem(key_lo, key_size);
-    dlprintf(" - ");
-    debug_print_string_hex_nomem(key_hi, key_size);
-    dlprintf("\n");
+    gs_memory_t *mem = gp_get_debug_mem_ptr();
+
+    if (mem == NULL)
+        return;
+    debug_print_string_hex(mem, str, key_size);
+    dmlprintf(mem, " in ");
+    debug_print_string_hex(mem, key_lo, key_size);
+    dmlprintf(mem, " - ");
+    debug_print_string_hex(mem, key_hi, key_size);
+    dmlprintf(mem, "\n");
 }
 #endif
-#endif
 
 static int
 gs_cmap_get_shortest_chr(const gx_code_map_t * pcmap, uint *pfidx)
@@ -128,14 +131,12 @@ gs_multidim_CID_offset(const byte *key_s
     int i;	/* index for current dimension */
     int CID_offset = 0;
 
-#ifndef GS_THREADSAFE
 #ifdef DEBUG
     if (gs_debug_c('J')) {
         dlprintf("[J]gmCo()         calc CID_offset for 0x");
         print_msg_str_in_range(key_str, key_lo, key_hi, key_size);
     }
 #endif
-#endif
 
     for (i = 0; i < key_size; i++)
         CID_offset = CID_offset * (key_hi[i] - key_lo[i] + 1) +
@@ -183,7 +184,6 @@ code_map_decode_next_multidim_regime(con
 
     *pchr = '\0';
 
-#ifndef GS_THREADSAFE
 #ifdef DEBUG
     if (gs_debug_c('J')) {
         dlprintf("[J]CMDNmr() is called: str=(");
@@ -192,7 +192,6 @@ code_map_decode_next_multidim_regime(con
                   (intptr_t)str, ssize, pcmap->num_lookup);
     }
 #endif
-#endif
 
     for (i = pcmap->num_lookup - 1; i >= 0; --i) {
         /* main loop - scan the map passed via pcmap */
@@ -220,7 +219,6 @@ code_map_decode_next_multidim_regime(con
             if (0 == j)			/* no match, skip to next i */
                 continue;
             else if (j < pre_size) {	/* not exact, partial match */
-#ifndef GS_THREADSAFE
 #ifdef DEBUG
                 if (gs_debug_c('J')) {
                     dlprintf("[J]CMDNmr() partial match with prefix:");
@@ -228,7 +226,6 @@ code_map_decode_next_multidim_regime(con
                                                 prefix, pre_size);
                 }
 #endif
-#endif
                 if (pm_maxlen < j) {
                     pm_maxlen = chr_size;
                     pm_chr = bytes2int(str, chr_size);
@@ -238,14 +235,12 @@ code_map_decode_next_multidim_regime(con
                 continue ; /* no need to check key, skip to next i */
             }
 
-#ifndef GS_THREADSAFE
 #ifdef DEBUG
             if (gs_debug_c('J')) {
                 dlprintf("[J]CMDNmr()   full match with prefix:");
                 print_msg_str_in_range(str, prefix, prefix, pre_size);
             }
 #endif
-#endif
         } /* if (0 < pre_size) */
 
         /* full match in prefix. check key */
@@ -266,14 +261,12 @@ code_map_decode_next_multidim_regime(con
 
             for (k = 0; k < pclr->num_entries; ++k, key += step) {
 
-#ifndef GS_THREADSAFE
 #ifdef DEBUG
                 if_debug0('j', "[j]CMDNmr()     check key:");
                 if (gs_debug_c('j'))
                     print_msg_str_in_range(str + pre_size,
                         key, key + step - key_size, key_size) ;
 #endif
-#endif
 
                 for (l = 0; l < key_size; l++) {
                     byte c = str[l + pre_size];
@@ -302,7 +295,6 @@ code_map_decode_next_multidim_regime(con
             *pfidx = pclr->font_index;
             pvalue = pclr->values.data + k * pclr->value_size;
 
-#ifndef GS_THREADSAFE
 #ifdef DEBUG
             if (gs_debug_c('J')) {
                 dlprintf("[J]CMDNmr()     full matched pvalue=(");
@@ -310,7 +302,6 @@ code_map_decode_next_multidim_regime(con
                 dlprintf(")\n");
             }
 #endif
-#endif
 
             switch (pclr->value_type) {
             case CODE_VALUE_CID:
@@ -343,7 +334,6 @@ code_map_decode_next_multidim_regime(con
     *pfidx = pm_fidx;
     *pglyph = GS_NO_GLYPH;
 
-#ifndef GS_THREADSAFE
 #ifdef DEBUG
     if (gs_debug_c('J')) {
         dlprintf("[J]CMDNmr()     no full match, use partial match for (");
@@ -351,7 +341,6 @@ code_map_decode_next_multidim_regime(con
         dlprintf(")\n");
     }
 #endif
-#endif
 
     return 0;
 }
@@ -434,7 +423,6 @@ gs_cmap_adobe1_decode_next(const gs_cmap
             *pglyph = GS_MIN_CID_GLYPH;	/* CID = 0, this is CMap fallback */
             *pindex = save_index + chr_size_shortest;
             *pchr = '\0';
-#ifndef GS_THREADSAFE
 #ifdef DEBUG
             if (gs_debug_c('J')) {
                 const byte *str = pstr->data + save_index;
@@ -444,21 +432,18 @@ gs_cmap_adobe1_decode_next(const gs_cmap
                 dlprintf(")\n");
             }
 #endif
-#endif
             return 0; /* should return some error for fallback .notdef? */
         }
         else {
             /* Undecodable string is shorter than the shortest character,
              * return 'GS_NO_GLYPH' and update index to end-of-string
              */
-#ifndef GS_THREADSAFE
 #ifdef DEBUG
             if (gs_debug_c('J')) {
                 dlprintf2("[J]GCDN() left data in buffer (%d) is shorter than shortest defined character (%d)\n",
                   ssize, chr_size_shortest);
             }
 #endif
-#endif
             *pglyph = GS_NO_GLYPH;
             *pindex += ssize;
             return 0;			/* fixme: should return a code != 0 if caller needs to know */
diff -pruN 9.55.0~dfsg-3/base/gsfcmap.c 9.56.1~dfsg-1/base/gsfcmap.c
--- 9.55.0~dfsg-3/base/gsfcmap.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsfcmap.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -568,7 +568,7 @@ gs_cmap_ToUnicode_alloc(gs_memory_t *mem
         char sid[10], *pref = "aux-";
         int sid_len, pref_len = strlen(pref);
 
-        gs_sprintf(sid, "%d", id);
+        gs_snprintf(sid, sizeof(sid), "%d", id);
         sid_len = strlen(sid);
         name_len = pref_len + sid_len;
         cmap_name = gs_alloc_string(mem, name_len, "gs_cmap_ToUnicode_alloc");
diff -pruN 9.55.0~dfsg-3/base/gsfont.c 9.56.1~dfsg-1/base/gsfont.c
--- 9.55.0~dfsg-3/base/gsfont.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsfont.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -189,6 +189,14 @@ gs_font_finalize(const gs_memory_t *cmem
             prev->next = next;
     } else if (ppfirst != 0 && *ppfirst == pfont)
         *ppfirst = next;
+
+    if (pfont->FontType != ft_composite) {
+        gs_font_base *pbfont = (gs_font_base *)pfont;
+        if (uid_is_XUID(&pbfont->UID)) {
+            uid_free(&pbfont->UID, pbfont->memory, "gs_font_finalize");
+        }
+    }
+
     gs_notify_release(&pfont->notify_list);
 }
 static
@@ -234,8 +242,8 @@ gs_font_dir_alloc2(gs_memory_t * struct_
         pdir = gs_font_dir_alloc2_limits(struct_mem, bits_mem,
                                          smax_SMALL, bmax_SMALL, mmax_SMALL,
                                          cmax_SMALL, blimit_SMALL);
-    if (pdir == 0)
-        return 0;
+    if (pdir == NULL)
+        return NULL;
     pdir->ccache.mark_glyph = cc_no_mark_glyph;
     pdir->ccache.mark_glyph_data = 0;
     return pdir;
@@ -249,16 +257,15 @@ gs_font_dir_alloc2_limits(gs_memory_t *
                         "font_dir_alloc(dir)");
     int code;
 
-    if (pdir == 0)
-        return 0;
+    if (pdir == NULL)
+        return NULL;
     memset(pdir, 0, sizeof(*pdir));
+    pdir->memory = struct_mem;
     code = gx_char_cache_alloc(struct_mem, bits_mem, pdir,
                                bmax, mmax, cmax, upper);
     if (code < 0) {
-        gs_free_object(struct_mem, pdir->ccache.table, "font_dir_alloc(chars)");
-        gs_free_object(struct_mem, pdir->fmcache.mdata, "font_dir_alloc(mdata)");
         gs_free_object(struct_mem, pdir, "font_dir_alloc(dir)");
-        return 0;
+        return NULL;
     }
     pdir->orig_fonts = 0;
     pdir->scaled_fonts = 0;
@@ -267,7 +274,6 @@ gs_font_dir_alloc2_limits(gs_memory_t *
     pdir->align_to_pixels = false;
     pdir->glyph_to_unicode_table = NULL;
     pdir->grid_fit_tt = 1;
-    pdir->memory = struct_mem;
     pdir->tti = 0;
     pdir->ttm = 0;
     pdir->san = 0;
@@ -314,6 +320,13 @@ gs_font_dir_finalize(const gs_memory_t *
     }
     pdir->ccache.chunks = NULL;
 }
+void
+gs_font_dir_free(gs_font_dir *dir)
+{
+    if (dir == NULL)
+        return;
+    gs_free_object(dir->memory, dir, "gs_font_dir_free");
+}
 
 /* Allocate and minimally initialize a font. */
 gs_font *
diff -pruN 9.55.0~dfsg-3/base/gsfont.h 9.56.1~dfsg-1/base/gsfont.h
--- 9.55.0~dfsg-3/base/gsfont.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsfont.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -43,6 +43,7 @@ gs_font_dir *gs_font_dir_alloc2_limits(g
                                        gs_memory_t * bits_mem,
                                        uint smax, uint bmax, uint mmax,
                                        uint cmax, uint upper);
+void gs_font_dir_free(gs_font_dir *dir);
 
 /* Backward compatibility */
 #define gs_font_dir_alloc(mem) gs_font_dir_alloc2(mem, mem)
diff -pruN 9.55.0~dfsg-3/base/gshsb.c 9.56.1~dfsg-1/base/gshsb.c
--- 9.55.0~dfsg-3/base/gshsb.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gshsb.c	2022-04-04 13:46:22.000000000 +0000
@@ -150,7 +150,6 @@ color_hsb_to_rgb(double hue, double satu
         rgb[0] = frac2float(R);
         rgb[1] = frac2float(G);
         rgb[2] = frac2float(B);
-#ifndef GS_THREADSAFE
 #ifdef DEBUG
         if (gs_debug_c('c')) {
             dlprintf7("[c]hsb(%g,%g,%g)->VSFI(%ld,%d,%ld,%d)->\n",
@@ -159,6 +158,5 @@ color_hsb_to_rgb(double hue, double satu
                       R, G, B, rgb[0], rgb[1], rgb[2]);
         }
 #endif
-#endif
     }
 }
diff -pruN 9.55.0~dfsg-3/base/gsht.c 9.56.1~dfsg-1/base/gsht.c
--- 9.55.0~dfsg-3/base/gsht.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsht.c	2022-04-04 13:46:22.000000000 +0000
@@ -26,7 +26,6 @@
 #include "gxarith.h"            /* for igcd */
 #include "gzstate.h"
 #include "gxdevice.h"           /* for gzht.h */
-#include "gxdevsop.h"
 #include "gzht.h"
 #include "gxfmap.h"             /* For effective transfer usage in threshold */
 #include "gp.h"
@@ -390,7 +389,7 @@ gx_ht_alloc_threshold_order(gx_ht_order
 {
     gx_ht_order order;
 
-    unsigned long num_bits = bitmap_raster(width) * 8 * height;
+    unsigned long num_bits = bitmap_raster(width) * (unsigned long)8 * height;
     const gx_ht_order_procs_t *procs;
     int code;
 
@@ -460,7 +459,6 @@ gx_sort_ht_order(gx_ht_bit * recs, uint
     for (i = 0; i < N; i++)
         recs[i].offset = i;
     qsort((void *)recs, N, sizeof(*recs), compare_samples);
-#ifndef GS_THREADSAFE
 #ifdef DEBUG
     if (gs_debug_c('H')) {
         uint i;
@@ -471,7 +469,6 @@ gx_sort_ht_order(gx_ht_bit * recs, uint
                       i, recs[i].offset, recs[i].mask);
     }
 #endif
-#endif
 }
 
 /*
@@ -646,53 +643,53 @@ int
 gs_color_name_component_number(gx_device * dev, const char * pname,
                                 int name_size, int halftonetype)
 {
-    int num_colorant = -1;	/* initialize to "unknown" */
-    int color_component_type = NO_COMP_NAME_TYPE_HT;
-    bool devn = dev_proc(dev, dev_spec_op)(dev, gxdso_supports_devn, NULL, 0);
+    int num_colorant;
 
-#define check_colorant_name(dev, name, component_type) \
-    ((*dev_proc(dev, get_color_comp_index)) (dev, name, strlen(name), component_type))
+#define check_colorant_name(dev, name) \
+    ((*dev_proc(dev, get_color_comp_index)) (dev, name, strlen(name), NO_COMP_NAME_TYPE_HT))
 
-#define check_colorant_name_length(dev, name, length, component_type) \
-    ((*dev_proc(dev, get_color_comp_index)) (dev, name, length, component_type))
+#define check_colorant_name_length(dev, name, length) \
+    ((*dev_proc(dev, get_color_comp_index)) (dev, name, length, NO_COMP_NAME_TYPE_HT))
 
 #define check_name(str, pname, length) \
     ((strlen(str) == length) && (strncmp(pname, str, length) == 0))
 
     /*
+     * Check if this is a device colorant.
+     */
+    num_colorant = check_colorant_name_length(dev, pname, name_size);
+    if (num_colorant >= 0) {
+        /*
+         * The device will return GX_DEVICE_COLOR_MAX_COMPONENTS if the
+         * colorant is logically present in the device but not being used
+         * because a SeparationOrder parameter is specified.  Since we are
+         * using this value to indicate 'Default', we use -1 to indicate
+         * that the colorant is not really being used.
+         */
+        if (num_colorant == GX_DEVICE_COLOR_MAX_COMPONENTS)
+            num_colorant = -1;
+        return num_colorant;
+    }
+
+    /*
      * Check if this is the default component
      */
     if (check_name("Default", pname, name_size))
         return GX_DEVICE_COLOR_MAX_COMPONENTS;
 
-    if (check_cmyk_color_model_comps(dev))
-        color_component_type = SEPARATION_NAME;		/* allow separations to be added */
-
-    /*
-     * Check if this is a device colorant.
-     */
     /* Halftones set by setcolorscreen, and (we think) */
-    /* Type 2, 4, and 5 halftones, are supposed to work */
+    /* Type 2 and Type 4 halftones, are supposed to work */
     /* for both RGB and CMYK, so we need a special check here. */
     if (halftonetype == ht_type_colorscreen ||
-        halftonetype == ht_type_multiple_colorscreen ||
-        (halftonetype == ht_type_multiple && devn)) {
-        /* Note that Red, Green, Blue and/or Gray can be added using setcolorspace */
-        /* we just don't automatically add it as a result of sethalftone           */
-        /* The NO_COMP_NAME_TYPE_HT won't add colorants                            */
+        halftonetype == ht_type_multiple_colorscreen) {
         if (check_name("Red", pname, name_size))
-            num_colorant = check_colorant_name(dev, "Cyan", NO_COMP_NAME_TYPE_HT);
+            num_colorant = check_colorant_name(dev, "Cyan");
         else if (check_name("Green", pname, name_size))
-            num_colorant = check_colorant_name(dev, "Magenta", NO_COMP_NAME_TYPE_HT);
+            num_colorant = check_colorant_name(dev, "Magenta");
         else if (check_name("Blue", pname, name_size))
-            num_colorant = check_colorant_name(dev, "Yellow", NO_COMP_NAME_TYPE_HT);
+            num_colorant = check_colorant_name(dev, "Yellow");
         else if (check_name("Gray", pname, name_size))
-            num_colorant = check_colorant_name(dev, "Black", NO_COMP_NAME_TYPE_HT);
-    }
-    if (num_colorant < 0)
-        num_colorant = check_colorant_name_length(dev, pname, name_size, color_component_type);
-
-    if (num_colorant >= 0) {
+            num_colorant = check_colorant_name(dev, "Black");
         /*
          * The device will return GX_DEVICE_COLOR_MAX_COMPONENTS if the
          * colorant is logically present in the device but not being used
@@ -702,12 +699,12 @@ gs_color_name_component_number(gx_device
          */
         if (num_colorant == GX_DEVICE_COLOR_MAX_COMPONENTS)
             num_colorant = -1;
-        return num_colorant;
-    }
+
 #undef check_colorant_name
 #undef check_colorant_name_length
 #undef check_name
 
+    }
     return num_colorant;
 }
 
@@ -1306,7 +1303,10 @@ gx_gstate_set_effective_xfer(gs_gstate *
         }
     }
 
-    if (pdht) { /* might not be initialized yet */
+    /* HT may not be initialized yet.  Only do if the target is a halftone device.
+       Per the spec, the HT is a self-contained description of a halftoning process.
+       We don't use any xfer function from the HT if we are not halftoning */
+    if (pdht && !device_is_contone(pgs->device)) {
 
         /* Since the transfer function is pickled into the threshold array (if any)*/
         /*  we need to free it so it can be reconstructed with the current transfer */
diff -pruN 9.55.0~dfsg-3/base/gshtscr.c 9.56.1~dfsg-1/base/gshtscr.c
--- 9.55.0~dfsg-3/base/gshtscr.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gshtscr.c	2022-04-04 13:46:22.000000000 +0000
@@ -602,7 +602,7 @@ gs_screen_next(gs_screen_enum * penum, d
     if (value < -1.0 || value > 1.0)
         return_error(gs_error_rangecheck);
     sample = (ht_sample_t) ((value + 1) * max_ht_sample);
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#if defined(DEBUG)
     if (gs_debug_c('H')) {
         gs_point pt;
 
diff -pruN 9.55.0~dfsg-3/base/gsicc_cache.c 9.56.1~dfsg-1/base/gsicc_cache.c
--- 9.55.0~dfsg-3/base/gsicc_cache.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsicc_cache.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -47,6 +47,7 @@
          *  of links.
          */
 #define ICC_CACHE_MAXLINKS (MAX_THREADS*2)	/* allow up to two active links per thread */
+#define ICC_CACHE_NOT_VALID_COUNT 20  /* This should not really occur. If it does we need to take a closer look */
 
 /* Static prototypes */
 
@@ -108,20 +109,21 @@ gsicc_cache_new(gs_memory_t *memory)
     result->num_links = 0;
     result->cache_full = false;
     result->memory = memory->stable_memory;
+    result->full_wait = NULL; /* Required so finaliser can work when result freed. */
+    rc_init_free(result, memory->stable_memory, 1, rc_gsicc_link_cache_free);
     result->lock = gx_monitor_label(gx_monitor_alloc(memory->stable_memory),
                                     "gsicc_cache_new");
     if (result->lock == NULL) {
-        gs_free_object(memory->stable_memory, result, "gsicc_cache_new");
+        rc_decrement(result, "gsicc_cache_new");
         return(NULL);
     }
     result->full_wait = gx_semaphore_label(gx_semaphore_alloc(memory->stable_memory),
                                     "gsicc_cache_new");
     if (result->full_wait == NULL) {
-        gx_monitor_free(result->lock);
-        gs_free_object(memory->stable_memory, result, "gsicc_cache_new");
+        /* Don't free result->lock, as the finaliser for result does that! */
+        rc_decrement(result, "gsicc_cache_new");
         return(NULL);
     }
-    rc_init_free(result, memory->stable_memory, 1, rc_gsicc_link_cache_free);
     if_debug2m(gs_debug_flag_icc, memory,
                "[icc] Allocating link cache = "PRI_INTPTR" memory = "PRI_INTPTR"\n",
 	       (intptr_t)result, (intptr_t)result->memory);
@@ -179,15 +181,16 @@ gsicc_alloc_link_dev(gs_memory_t *memory
     gsicc_link_t *result;
     int cms_flags = 0;
 
-    result = (gsicc_link_t*) gs_malloc(memory->stable_memory, 1,
+    memory = memory->non_gc_memory;
+    result = (gsicc_link_t*)gs_alloc_byte_array(memory, 1,
         sizeof(gsicc_link_t), "gsicc_alloc_link_dev");
 
     if (result == NULL)
         return NULL;
-    result->lock = gx_monitor_label(gx_monitor_alloc(memory->stable_memory),
+    result->lock = gx_monitor_label(gx_monitor_alloc(memory),
         "gsicc_link_new");
     if (result->lock == NULL) {
-        gs_free_object(memory->stable_memory, result, "gsicc_alloc_link(lock)");
+        gs_free_object(memory, result, "gsicc_alloc_link(lock)");
         return NULL;
     }
     gx_monitor_enter(result->lock);
@@ -212,35 +215,35 @@ gsicc_alloc_link_dev(gs_memory_t *memory
     result->includes_devlink = 0;
     result->is_identity = false;
     result->valid = true;
-    result->memory = memory->stable_memory;
+    result->memory = memory;
 
     if_debug1m('^', result->memory, "[^]icclink "PRI_INTPTR" init = 1\n",
                (intptr_t)result);
 
     if (src_profile->profile_handle == NULL) {
         src_profile->profile_handle = gsicc_get_profile_handle_buffer(
-            src_profile->buffer, src_profile->buffer_size, memory->stable_memory);
+            src_profile->buffer, src_profile->buffer_size, memory);
     }
 
     if (des_profile->profile_handle == NULL) {
         des_profile->profile_handle = gsicc_get_profile_handle_buffer(
-            des_profile->buffer, des_profile->buffer_size, memory->stable_memory);
+            des_profile->buffer, des_profile->buffer_size, memory);
     }
 
     /* Check for problems.. */
     if (src_profile->profile_handle == 0 || des_profile->profile_handle == 0) {
-        gs_free_object(memory->stable_memory, result, "gsicc_alloc_link_dev");
+        gs_free_object(memory, result, "gsicc_alloc_link_dev");
         return NULL;
     }
 
     /* [0] is chunky, littleendian, noalpha, 16-in, 16-out */
     result->link_handle = gscms_get_link(src_profile->profile_handle,
         des_profile->profile_handle, rendering_params, cms_flags,
-        memory->stable_memory);
+        memory);
 
     /* Check for problems.. */
     if (result->link_handle == NULL) {
-        gs_free_object(memory->stable_memory, result, "gsicc_alloc_link_dev");
+        gs_free_object(memory, result, "gsicc_alloc_link_dev");
         return NULL;
     }
 
@@ -258,10 +261,12 @@ gsicc_alloc_link_dev(gs_memory_t *memory
 
 /* And the related release of the link */
 void
-gsicc_free_link_dev(gs_memory_t *memory, gsicc_link_t *link)
+gsicc_free_link_dev(gsicc_link_t *link)
 {
-    gs_memory_t *nongc_mem = memory->non_gc_memory;
-    gs_free_object(nongc_mem, link, "gsicc_free_link_dev");
+    if (link == NULL)
+        return;
+    link->procs.free_link(link);
+    gs_free_object(link->memory, link, "gsicc_free_link_dev");
 }
 
 static gsicc_link_t *
@@ -527,6 +532,7 @@ gsicc_findcachelink(gsicc_hashlink_t has
 {
     gsicc_link_t *curr, *prev;
     int64_t hashcode = hash.link_hashcode;
+    int cache_loop = 0;
 
     /* Look through the cache for the hashcode */
     gx_monitor_enter(icc_link_cache->lock);
@@ -554,18 +560,25 @@ gsicc_findcachelink(gsicc_hashlink_t has
                        "icclink", (intptr_t)curr, curr->ref_count);
             while (curr->valid == false) {
                 gx_monitor_leave(icc_link_cache->lock); /* exit to let other threads run briefly */
+                if (cache_loop > ICC_CACHE_NOT_VALID_COUNT) {
+                    /* Clearly something is wrong.  Return NULL.
+                       File a bug report. */
+                    emprintf(curr->memory, "Reached maximum invalid counts \n");
+                    return NULL;
+                }
+                cache_loop++;
                 gx_monitor_enter(curr->lock);			/* wait until we can acquire the lock */
                 gx_monitor_leave(curr->lock);			/* it _should be valid now */
                 /* If it is still not valid, but we were able to lock, it means that the thread	*/
-                /* that was building it failed to be able to complete building it		*/
-                /* this is probably a fatal error. MV ???					*/
+                /* that was building it failed to be able to complete building it.  Try this only
+                   a limited number of times before we bail. */
                 if (curr->valid == false) {
-		  emprintf1(curr->memory, "link "PRI_INTPTR" lock released, but still not valid.\n", (intptr_t)curr);	/* Breakpoint here */
+		            emprintf1(curr->memory, "link "PRI_INTPTR" lock released, but still not valid.\n", (intptr_t)curr);	/* Breakpoint here */
                 }
                 gx_monitor_enter(icc_link_cache->lock);	/* re-enter to loop and check */
             }
             gx_monitor_leave(icc_link_cache->lock);
-            return(curr);	/* success */
+            return curr;	/* success */
         }
         prev = curr;
         curr = curr->next;
@@ -958,7 +971,7 @@ gsicc_get_link_profile(const gs_gstate *
     gsicc_manager_t *icc_manager = pgs->icc_manager;
     gsicc_link_cache_t *icc_link_cache = pgs->icc_link_cache;
     gs_memory_t *cache_mem = pgs->icc_link_cache->memory;
-    gcmmhprofile_t *cms_input_profile;
+    gcmmhprofile_t *cms_input_profile = NULL;
     gcmmhprofile_t *cms_output_profile = NULL;
     gcmmhprofile_t *cms_proof_profile = NULL;
     gcmmhprofile_t *cms_devlink_profile = NULL;
@@ -1034,6 +1047,7 @@ gsicc_get_link_profile(const gs_gstate *
             gsicc_get_profile_handle_clist(gs_input_profile,
                                            gs_input_profile->memory);
         gs_input_profile->profile_handle = cms_input_profile;
+
         /* It is possible that we are not using color management
            due to a setting forced from srcgtag object (the None option)
            which has made its way though the clist in the clist imaging
@@ -1042,7 +1056,6 @@ gsicc_get_link_profile(const gs_gstate *
            We also have the Replace option.  */
         if (gs_input_profile->rend_is_valid &&
             gs_input_profile->rend_cond.cmm == gsCMM_NONE) {
-
             if (gs_input_profile->data_cs == gsRGB) {
                 link = gsicc_nocm_get_link(pgs, dev, 3);
             } else {
@@ -1074,7 +1087,7 @@ gsicc_get_link_profile(const gs_gstate *
                                include_devicelink))
         return link;
     if (link == NULL)
-        return NULL;		/* error, couldn't allocate a link */
+        return NULL;		/* error, couldn't allocate a link.  Nothing to cleanup */
 
     /* Here the link was new and the contents have valid=false and we	*/
     /* own the lock for the link_profile. Build the profile, set valid	*/
@@ -1095,20 +1108,25 @@ gsicc_get_link_profile(const gs_gstate *
                 gsicc_get_profile_handle_buffer(gs_input_profile->buffer,
                                                 gs_input_profile->buffer_size,
                                                 memory);
-            if (cms_input_profile == NULL)
-                return NULL;
+            if (cms_input_profile == NULL) {
+                link->ref_count--;
+                goto icc_link_error;
+            }
+
             gs_input_profile->profile_handle = cms_input_profile;
             /* This *must* be a default profile that was not set up at start-up/
                However it could be one from the icc creator code which does not
                do an initialization at the time of creation from CalRGB etc. */
             code = gsicc_initialize_default_profile(gs_input_profile);
-            if (code < 0) return NULL;
+            if (code < 0) {
+                link->ref_count--;
+                goto icc_link_error;
+            }
         } else {
             /* Cant create the link.  No profile present,
-               nor any defaults to use for this.  Really
-               need to throw an error for this case. */
-            gsicc_remove_link(link, cache_mem);
-            return NULL;
+               nor any defaults to use for this. */
+            link->ref_count--;
+            goto icc_link_error;
         }
     }
     /* No need to worry about an output profile handle if our source is a
@@ -1125,7 +1143,10 @@ gsicc_get_link_profile(const gs_gstate *
             gs_output_profile->profile_handle = cms_output_profile;
             /* This *must* be a default profile that was not set up at start-up */
             code = gsicc_initialize_default_profile(gs_output_profile);
-            if (code < 0) return NULL;
+            if (code < 0)  {
+                link->ref_count--;
+                goto icc_link_error;
+            }
         } else {
               /* See if we have a clist device pointer. */
             if ( gs_output_profile->dev != NULL ) {
@@ -1137,10 +1158,9 @@ gsicc_get_link_profile(const gs_gstate *
                 gs_output_profile->profile_handle = cms_output_profile;
             } else {
                 /* Cant create the link.  No profile present,
-                   nor any defaults to use for this.  Really
-                   need to throw an error for this case. */
-                gsicc_remove_link(link, cache_mem);
-                return NULL;
+                   nor any defaults to use for this. */
+                link->ref_count--;
+                goto icc_link_error;
             }
         }
     }
@@ -1157,8 +1177,8 @@ gsicc_get_link_profile(const gs_gstate *
                     gx_monitor_enter(proof_profile->lock);
             } else {
                 /* Cant create the link */
-                gsicc_remove_link(link, cache_mem);
-                return NULL;
+                link->ref_count--;
+                goto icc_link_error;
             }
         }
     }
@@ -1175,8 +1195,8 @@ gsicc_get_link_profile(const gs_gstate *
                     gx_monitor_enter(devlink_profile->lock);
             } else {
                 /* Cant create the link */
-                gsicc_remove_link(link, cache_mem);
-                return NULL;
+                link->ref_count--;
+                goto icc_link_error;
             }
         }
     }
@@ -1203,9 +1223,9 @@ gsicc_get_link_profile(const gs_gstate *
                                           pgs->icc_manager,
                                           pgs->icc_manager->memory->stable_memory);
             if (icc_manager->graytok_profile == NULL) {
-                /* Cant create the link */	/* FIXME: clean up allocations and locksso far ??? */
-                gsicc_remove_link(link, cache_mem);
-                return NULL;
+                /* Cant create the link */
+                link->ref_count--;
+                goto icc_link_error;
             }
         }
         if (icc_manager->smask_profiles == NULL) {
@@ -1278,10 +1298,18 @@ gsicc_get_link_profile(const gs_gstate *
             gx_semaphore_signal(icc_link_cache->full_wait);	/* let a waiting thread run */
         }
         gx_monitor_leave(link->lock);
-        gsicc_remove_link(link, cache_mem);
-        return NULL;
+        goto icc_link_error;
     }
     return link;
+
+icc_link_error:
+    /* Something went very wrong. Clean up so that the cache
+       does not maintain an invalid entry.  Any other allocations
+       (e.g. profile handles) would get freed when the profiles
+        are freed */
+    gsicc_remove_link(link, cache_mem);
+
+    return NULL;
 }
 
 /* The following is used to transform a named color value at a particular tint
diff -pruN 9.55.0~dfsg-3/base/gsicc_cache.h 9.56.1~dfsg-1/base/gsicc_cache.h
--- 9.55.0~dfsg-3/base/gsicc_cache.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsicc_cache.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -66,5 +66,5 @@ bool gsicc_support_named_color(const gs_
 int  gsicc_get_device_profile_comps(const cmm_dev_profile_t *dev_profile);
 gsicc_link_t * gsicc_alloc_link_dev(gs_memory_t *memory, cmm_profile_t *src_profile,
     cmm_profile_t *des_profile, gsicc_rendering_param_t *rendering_params);
-void gsicc_free_link_dev(gs_memory_t *memory, gsicc_link_t *link);
+void gsicc_free_link_dev(gsicc_link_t *link);
 #endif
diff -pruN 9.55.0~dfsg-3/base/gsicc_cms.h 9.56.1~dfsg-1/base/gsicc_cms.h
--- 9.55.0~dfsg-3/base/gsicc_cms.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsicc_cms.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -76,8 +76,8 @@ gcmmhlink_t gscms_get_link_proof_devlink
                                          gsicc_rendering_param_t *rendering_params,
                                          bool src_dev_link, int cmm_flags,
                                          gs_memory_t *memory);
-int gscms_create(gs_memory_t *memory);
-void gscms_destroy(gs_memory_t *memory);
+void *gscms_create(gs_memory_t *memory);
+void gscms_destroy(void *);
 void gscms_release_link(gsicc_link_t *icclink);
 void gscms_release_profile(void *profile, gs_memory_t *memory);
 int gscms_transform_named_color(gsicc_link_t *icclink,  float tint_value,
diff -pruN 9.55.0~dfsg-3/base/gsicc_create.c 9.56.1~dfsg-1/base/gsicc_create.c
--- 9.55.0~dfsg-3/base/gsicc_create.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsicc_create.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -345,13 +345,14 @@ gsicc_create_clut(const gs_color_space *
        Uniformly from min range to max range */
     ptr_short = clut->data_short;
     for (i = 0; i < num_points; i++) {
-        if (num_components == 1) {
+        switch (num_components) {
+        case 1:
             /* Get the input vector value */
             fltptr = input_samples[0];
             index = i%table_size;
             cc.paint.values[0] = fltptr[index];
-        }
-        if (num_components == 3) {
+            break;
+        case 3:
             /* The first channel varies least rapidly in the ICC table */
             fltptr = input_samples[2];
             index = i%table_size;
@@ -363,8 +364,8 @@ gsicc_create_clut(const gs_color_space *
             index = (unsigned int) floor((float) i/(float) (table_size*
                                                         table_size))%table_size;
             cc.paint.values[0] = fltptr[index];
-        }
-        if (num_components == 4) {
+            break;
+        case 4:
             /* The first channel varies least rapidly in the ICC table */
             fltptr = input_samples[3];
             index = i%table_size;
@@ -380,6 +381,9 @@ gsicc_create_clut(const gs_color_space *
             index = (unsigned int) floor((float) i/(float) (table_size*
                                         table_size*table_size))%table_size;
             cc.paint.values[0] = fltptr[index];
+            break;
+        default:
+            return_error(gs_error_rangecheck); /* Should never happen */
         }
         /* These special concretizations functions do not go through
            the ICC mapping like the procs associated with the color space */
@@ -461,7 +465,7 @@ save_profile(const gs_memory_t *mem, uns
     char full_file_name[50];
     gp_file *fid;
 
-    gs_sprintf(full_file_name,"%d)Profile_%s.icc",icc_debug_index,filename);
+    gs_snprintf(full_file_name,sizeof(full_file_name),"%d)Profile_%s.icc",icc_debug_index,filename);
     fid = gp_fopen(mem, full_file_name,"wb");
     gp_fwrite(buffer,sizeof(unsigned char),buffer_size,fid);
     gp_fclose(fid);
@@ -1525,6 +1529,7 @@ create_lutAtoBprofile(unsigned char **pp
         return gs_throw(gs_error_VMerror, "Allocation of ICC cam failed");
     }
     gsicc_create_compute_cam(lutatobparts->white_point, &(d50), cam);
+    gs_free_object(memory, lutatobparts->cam, "create_lutAtoBprofile");
     lutatobparts->cam = cam;
     get_D50(temp_XYZ); /* See Appendix D6 in spec */
     add_xyzdata(curr_ptr, temp_XYZ);
diff -pruN 9.55.0~dfsg-3/base/gsicc_lcms2.c 9.56.1~dfsg-1/base/gsicc_lcms2.c
--- 9.55.0~dfsg-3/base/gsicc_lcms2.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsicc_lcms2.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -462,7 +462,7 @@ int
 gscms_transform_color(gx_device *dev, gsicc_link_t *icclink, void *inputcolor,
                              void *outputcolor, int num_bytes)
 {
-    return gscms_transformm_color_const(dev, icclink, inputcolor, outputcolor, num_bytes);
+    return gscms_transform_color_const(dev, icclink, inputcolor, outputcolor, num_bytes);
 }
 
 int
@@ -731,7 +731,7 @@ gscms_get_link_proof_devlink(gcmmhprofil
 }
 
 /* Do any initialization if needed to the CMS */
-int
+void *
 gscms_create(gs_memory_t *memory)
 {
     cmsContext ctx;
@@ -739,27 +739,26 @@ gscms_create(gs_memory_t *memory)
     /* Set our own error handling function */
     ctx = cmsCreateContext((void *)&gs_cms_memhandler, memory);
     if (ctx == NULL)
-        return_error(gs_error_VMerror);
+        return NULL;
 
 #ifdef USE_LCMS2_LOCKING
     cmsPluginTHR(ctx, (void *)&gs_cms_mutexhandler);
 #endif
 
     cmsSetLogErrorHandlerTHR(ctx, gscms_error);
-    gs_lib_ctx_set_cms_context(memory, ctx);
-    return 0;
+
+    return ctx;
 }
 
 /* Do any clean up when done with the CMS if needed */
 void
-gscms_destroy(gs_memory_t *memory)
+gscms_destroy(void *cmsContext_)
 {
-    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
+    cmsContext ctx = (cmsContext)cmsContext_;
     if (ctx == NULL)
         return;
 
     cmsDeleteContext(ctx);
-    gs_lib_ctx_set_cms_context(memory, NULL);
 }
 
 /* Have the CMS release the link */
diff -pruN 9.55.0~dfsg-3/base/gsicc_lcms2mt.c 9.56.1~dfsg-1/base/gsicc_lcms2mt.c
--- 9.55.0~dfsg-3/base/gsicc_lcms2mt.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsicc_lcms2mt.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -877,7 +877,7 @@ gscms_get_link_proof_devlink(gcmmhprofil
 }
 
 /* Do any initialization if needed to the CMS */
-int
+void *
 gscms_create(gs_memory_t *memory)
 {
     cmsContext ctx;
@@ -885,7 +885,7 @@ gscms_create(gs_memory_t *memory)
     /* Set our own error handling function */
     ctx = cmsCreateContext((void *)&gs_cms_memhandler, memory);
     if (ctx == NULL)
-        return_error(gs_error_VMerror);
+        return NULL;
 
 #ifdef USE_LCMS2_LOCKING
     cmsPlugin(ctx, (void *)&gs_cms_mutexhandler);
@@ -897,20 +897,19 @@ gscms_create(gs_memory_t *memory)
 #endif
 
     cmsSetLogErrorHandler(ctx, gscms_error);
-    gs_lib_ctx_set_cms_context(memory, ctx);
-    return 0;
+
+    return ctx;
 }
 
 /* Do any clean up when done with the CMS if needed */
 void
-gscms_destroy(gs_memory_t *memory)
+gscms_destroy(void *cmsContext_)
 {
-    cmsContext ctx = gs_lib_ctx_get_cms_context(memory);
+    cmsContext ctx = (cmsContext)cmsContext_;
     if (ctx == NULL)
         return;
 
     cmsDeleteContext(ctx);
-    gs_lib_ctx_set_cms_context(memory, NULL);
 }
 
 /* Have the CMS release the link */
diff -pruN 9.55.0~dfsg-3/base/gsicc_manage.c 9.56.1~dfsg-1/base/gsicc_manage.c
--- 9.55.0~dfsg-3/base/gsicc_manage.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsicc_manage.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -1546,7 +1546,7 @@ gsicc_set_devicen_equiv_colors(gx_device
         return gs_throw(gs_error_VMerror, "Insufficient memory for devn equiv colors");
     pcspace->cmm_icc_profile_data = profile;
     temp_state.color[0].color_space = pcspace;
-    return dev_proc(dev, update_spot_equivalent_colors)(dev, &temp_state);
+    return dev_proc(dev, update_spot_equivalent_colors)(dev, &temp_state, pcspace);
 }
 
 #define DEFAULT_ICC_PROCESS "Cyan, Magenta, Yellow, Black,"
@@ -1604,13 +1604,13 @@ gsicc_set_device_profile_colorants(gx_de
                                                "gsicc_set_device_profile_colorants");
             if (name_str == NULL)
                 return gs_throw(gs_error_VMerror, "Insufficient memory for colorant name");
-            gs_sprintf(name_str, DEFAULT_ICC_PROCESS);
+            gs_snprintf(name_str, total_len+1, DEFAULT_ICC_PROCESS);
             for (kk = 0; kk < num_comps-5; kk++) {
-                gs_sprintf(temp_str,"ICC_COLOR_%d,",kk);
+                gs_snprintf(temp_str,sizeof(temp_str),"ICC_COLOR_%d,",kk);
                 strcat(name_str,temp_str);
             }
             /* Last one no comma */
-            gs_sprintf(temp_str,"ICC_COLOR_%d",kk);
+            gs_snprintf(temp_str,sizeof(temp_str),"ICC_COLOR_%d",kk);
             strcat(name_str,temp_str);
         }
         str_len = strlen(name_str);
@@ -1940,129 +1940,151 @@ gsicc_set_device_profile(gx_device * pde
 {
     cmm_profile_t *icc_profile;
     stream *str;
-    int code = 0;
+    int code;
 
-    /* This is slightly silly, we have a device method for 'get_profile' we really ought to
-     * have one for 'set_profile' as well. In its absence, make sure we are setting the profile
-     * of the bootm level device.
-     */
-    while(pdev->child)
-        pdev = pdev->child;
+    if (file_name == NULL)
+        return 0;
 
     /* Check if device has a profile for this slot. Note that we already
        decremented for any profile that we might be replacing
        in gsicc_init_device_profile_struct */
-    if (file_name != NULL) {
-        /* Silent on failure if this is an output intent profile that
-         * could not be found.  Bug 695042.  Multi-threaded rendering
-         * set up will try to find the file for the profile during the set
-         * up via put/get params. but one does not exist.  The OI profile
-         * will be cloned after the put/get params */
-        if (strncmp(file_name, OI_PROFILE, strlen(OI_PROFILE)) == 0)
-            return -1;
+    /* Silent on failure if this is an output intent profile that
+     * could not be found.  Bug 695042.  Multi-threaded rendering
+     * set up will try to find the file for the profile during the set
+     * up via put/get params. but one does not exist.  The OI profile
+     * will be cloned after the put/get params */
+    if (strncmp(file_name, OI_PROFILE, strlen(OI_PROFILE)) == 0)
+        return -1;
 
-        code = gsicc_open_search(file_name, strlen(file_name), mem,
-                                 mem->gs_lib_ctx->profiledir,
-                                 mem->gs_lib_ctx->profiledir_len, &str);
-        if (code < 0)
-            return code;
-        if (str != NULL) {
-            icc_profile =
-                gsicc_profile_new(str, mem, file_name, strlen(file_name));
-            code = sfclose(str);
-            if (icc_profile == NULL)
-                return gs_throw(gs_error_VMerror, "Creation of ICC profile failed");
-            if (pro_enum < gsPROOFPROFILE) {
-                if_debug1m(gs_debug_flag_icc, mem,
-                           "[icc] Setting device profile %d\n", pro_enum);
-                pdev->icc_struct->device_profile[pro_enum] = icc_profile;
-            } else {
-                /* The proof, link or post render profile. Output intent
-                   profile is set in zicc.c */
-                if (pro_enum == gsPROOFPROFILE) {
-                    if_debug0m(gs_debug_flag_icc, mem, "[icc] Setting proof profile\n");
-                    pdev->icc_struct->proof_profile = icc_profile;
-                } else if (pro_enum ==  gsLINKPROFILE) {
-                    if_debug0m(gs_debug_flag_icc, mem, "[icc] Setting link profile\n");
-                    pdev->icc_struct->link_profile = icc_profile;
-                } else if (pro_enum == gsPRPROFILE) {
-                    if_debug0m(gs_debug_flag_icc, mem, "[icc] Setting postrender profile\n");
-                    pdev->icc_struct->postren_profile = icc_profile;
-                } else {
-                    if_debug0m(gs_debug_flag_icc, mem, "[icc] Setting blend profile\n");
-                    pdev->icc_struct->blend_profile = icc_profile;
-                }
-            }
+    code = gsicc_open_search(file_name, strlen(file_name), mem,
+                             mem->gs_lib_ctx->profiledir,
+                             mem->gs_lib_ctx->profiledir_len, &str);
+    if (code < 0)
+        return code;
+    if (str == NULL)
+        return gs_rethrow(-1, "cannot find device profile");
 
-            /* Get the profile handle */
-            icc_profile->profile_handle =
+    icc_profile =
+            gsicc_profile_new(str, mem, file_name, strlen(file_name));
+    code = sfclose(str);
+    if (icc_profile == NULL)
+        return gs_throw(gs_error_VMerror, "Creation of ICC profile failed");
+
+    /* Get the profile handle */
+    icc_profile->profile_handle =
                 gsicc_get_profile_handle_buffer(icc_profile->buffer,
                                                 icc_profile->buffer_size,
                                                 mem);
-            if (icc_profile->profile_handle == NULL)
-                return_error(gs_error_unknownerror);
+    if (icc_profile->profile_handle == NULL) {
+        rc_decrement(icc_profile, "gsicc_set_device_profile");
+        return_error(gs_error_unknownerror);
+    }
 
-            /* Compute the hash code of the profile. Everything in the
-               ICC manager will have it's hash code precomputed */
-            gsicc_get_icc_buff_hash(icc_profile->buffer,
-                                    &(icc_profile->hashcode),
-                                    icc_profile->buffer_size);
-            icc_profile->hash_is_valid = true;
+    /* Compute the hash code of the profile. Everything in the
+       ICC manager will have it's hash code precomputed */
+    gsicc_get_icc_buff_hash(icc_profile->buffer,
+                            &(icc_profile->hashcode),
+                            icc_profile->buffer_size);
+    icc_profile->hash_is_valid = true;
 
-            /* Get the number of channels in the output profile */
-            icc_profile->num_comps =
+    /* Get the number of channels in the output profile */
+    icc_profile->num_comps =
                 gscms_get_input_channel_count(icc_profile->profile_handle,
-                    icc_profile->memory);
-            if_debug1m(gs_debug_flag_icc, mem, "[icc] Profile has %d components\n",
-                       icc_profile->num_comps);
-            icc_profile->num_comps_out =
+                                              icc_profile->memory);
+    if_debug1m(gs_debug_flag_icc, mem, "[icc] Profile has %d components\n",
+               icc_profile->num_comps);
+    icc_profile->num_comps_out =
                 gscms_get_output_channel_count(icc_profile->profile_handle,
-                    icc_profile->memory);
-            icc_profile->data_cs =
+                                               icc_profile->memory);
+    icc_profile->data_cs =
                 gscms_get_profile_data_space(icc_profile->profile_handle,
-                    icc_profile->memory);
+                                             icc_profile->memory);
 
-            /* Check that everything is OK with regard to the number of
-               components. */
-            if (gsicc_verify_device_profiles(pdev) < 0)
-                return gs_rethrow(-1, "Error in device profiles");
-
-            /* We need to know if this is one of the "default" profiles or
-               if someone has externally set it.  The reason is that if there
-               is an output intent in the file, and someone wants to use the
-               output intent our handling of the output intent profile is
-               different depending upon if someone specified a particular
-               output profile */
-            switch (icc_profile->num_comps) {
-                case 1:
-                    if (strncmp(icc_profile->name, DEFAULT_GRAY_ICC,
-                    strlen(icc_profile->name)) == 0) {
-                        icc_profile->default_match = DEFAULT_GRAY;
-                    }
-                    break;
-                case 3:
-                    if (strncmp(icc_profile->name, DEFAULT_RGB_ICC,
-                    strlen(icc_profile->name)) == 0) {
-                        icc_profile->default_match = DEFAULT_RGB;
-                    }
-                    break;
-                case 4:
-                    if (strncmp(icc_profile->name, DEFAULT_CMYK_ICC,
-                    strlen(icc_profile->name)) == 0) {
-                        icc_profile->default_match = DEFAULT_CMYK;
-                    }
-                    break;
-                default:
-                    /* NCLR Profile.  Set up default colorant names */
-                    code = gsicc_set_device_profile_colorants(pdev, NULL);
-                    break;
+    /* We need to know if this is one of the "default" profiles or
+       if someone has externally set it.  The reason is that if there
+       is an output intent in the file, and someone wants to use the
+       output intent our handling of the output intent profile is
+       different depending upon if someone specified a particular
+       output profile */
+    switch (icc_profile->num_comps) {
+        case 1:
+            if (strncmp(icc_profile->name, DEFAULT_GRAY_ICC,
+                        strlen(icc_profile->name)) == 0) {
+                icc_profile->default_match = DEFAULT_GRAY;
             }
-            if_debug1m(gs_debug_flag_icc, mem, "[icc] Profile data CS is %d\n",
-                       icc_profile->data_cs);
-        } else
-            return gs_rethrow(-1, "cannot find device profile");
+            break;
+        case 3:
+            if (strncmp(icc_profile->name, DEFAULT_RGB_ICC,
+                        strlen(icc_profile->name)) == 0) {
+                icc_profile->default_match = DEFAULT_RGB;
+            }
+            break;
+        case 4:
+            if (strncmp(icc_profile->name, DEFAULT_CMYK_ICC,
+                        strlen(icc_profile->name)) == 0) {
+                icc_profile->default_match = DEFAULT_CMYK;
+            }
+            break;
     }
-    return code;
+
+    if_debug1m(gs_debug_flag_icc, mem, "[icc] Profile data CS is %d\n",
+               icc_profile->data_cs);
+
+    /* This is slightly silly, we have a device method for 'get_profile' we really ought to
+     * have one for 'set_profile' as well. In its absence, make sure we are setting the profile
+     * of the bottom level device.
+     */
+    while(pdev->child)
+        pdev = pdev->child;
+
+    switch (pro_enum)
+    {
+        case gsDEFAULTPROFILE:
+        case gsGRAPHICPROFILE:
+        case gsIMAGEPROFILE:
+        case gsTEXTPROFILE:
+            if_debug1m(gs_debug_flag_icc, mem,
+                       "[icc] Setting device profile %d\n", pro_enum);
+            pdev->icc_struct->device_profile[pro_enum] = icc_profile;
+            break;
+        case gsPROOFPROFILE:
+            if_debug0m(gs_debug_flag_icc, mem, "[icc] Setting proof profile\n");
+            pdev->icc_struct->proof_profile = icc_profile;
+            break;
+        case gsLINKPROFILE:
+            if_debug0m(gs_debug_flag_icc, mem, "[icc] Setting link profile\n");
+            pdev->icc_struct->link_profile = icc_profile;
+            break;
+        case gsPRPROFILE:
+            if_debug0m(gs_debug_flag_icc, mem, "[icc] Setting postrender profile\n");
+            pdev->icc_struct->postren_profile = icc_profile;
+            break;
+        case gsBLENDPROFILE:
+            if_debug0m(gs_debug_flag_icc, mem, "[icc] Setting blend profile\n");
+            pdev->icc_struct->blend_profile = icc_profile;
+            break;
+        default:
+        case gsOIPROFILE:
+            /* This never happens as output intent profile is set in zicc.c */
+            rc_decrement(icc_profile, "gsicc_set_device_profile");
+            return_error(gs_error_unknownerror);
+    }
+
+    /* Check that everything is OK with regard to the number of
+       components. */
+    if (gsicc_verify_device_profiles(pdev) < 0)
+        return gs_rethrow(-1, "Error in device profiles");
+
+    if (icc_profile->num_comps != 1 &&
+        icc_profile->num_comps != 3 &&
+        icc_profile->num_comps != 4) {
+        /* NCLR Profile.  Set up default colorant names */
+        code = gsicc_set_device_profile_colorants(pdev, NULL);
+        if (code < 0)
+            return code;
+    }
+
+    return 0;
 }
 
 /* Set the icc profile in the gs_color_space object */
@@ -2545,7 +2567,7 @@ gsicc_get_profile_handle_clist(cmm_profi
         position = gsicc_search_icc_table(pcrdev->icc_table,
                                           picc_profile->hashcode, &size);
         if ( position < 0 )
-            return 0;  /* Not found. */
+            return NULL;  /* Not found. */
 
         /* Get the ICC buffer.  We really want to avoid this transfer.
            I need to write  an interface to the CMM to do this through
@@ -2556,10 +2578,14 @@ gsicc_get_profile_handle_clist(cmm_profi
         buffer_ptr = gs_alloc_bytes(memory->non_gc_memory, profile_size,
                                             "gsicc_get_profile_handle_clist");
         if (buffer_ptr == NULL)
-            return 0;
+            return NULL;
         clist_read_chunk(pcrdev, position + GSICC_SERIALIZED_SIZE,
             profile_size, (unsigned char *) buffer_ptr);
         profile_handle = gscms_get_profile_handle_mem(buffer_ptr, profile_size, memory->non_gc_memory);
+        if (profile_handle == NULL) {
+            gs_free_object(memory->non_gc_memory, buffer_ptr, "gsicc_get_profile_handle_clist");
+            return NULL;
+        }
         /* We also need to get some of the serialized information */
         clist_read_chunk(pcrdev, position, GSICC_SERIALIZED_SIZE,
                         (unsigned char *) (&profile_header));
@@ -2583,7 +2609,7 @@ gsicc_get_profile_handle_clist(cmm_profi
         gs_free_object(memory->non_gc_memory, buffer_ptr, "gsicc_get_profile_handle_clist");
         return profile_handle;
      }
-     return 0;
+     return NULL;
 }
 
 gcmmhprofile_t
@@ -2867,7 +2893,7 @@ dump_icc_buffer(const gs_memory_t *mem,
     char full_file_name[50];
     gp_file *fid;
 
-    gs_sprintf(full_file_name,"%d)%s_debug.icc",global_icc_index,filename);
+    gs_snprintf(full_file_name,sizeof(full_file_name),"%d)%s_debug.icc",global_icc_index,filename);
     fid = gp_fopen(mem, full_file_name,"wb");
     gp_fwrite(Buffer,sizeof(unsigned char),buffersize,fid);
     gp_fclose(fid);
diff -pruN 9.55.0~dfsg-3/base/gsicc_profilecache.c 9.56.1~dfsg-1/base/gsicc_profilecache.c
--- 9.55.0~dfsg-3/base/gsicc_profilecache.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsicc_profilecache.c	2022-04-04 13:46:22.000000000 +0000
@@ -74,7 +74,7 @@ rc_gsicc_profile_cache_free(gs_memory_t
     while (curr != NULL ){
         next = curr->next;
         rc_decrement(curr->color_space, "rc_gsicc_profile_cache_free");
-        gs_free_object(mem->stable_memory, curr,
+        gs_free_object(profile_cache->memory->stable_memory, curr,
                        "rc_gsicc_profile_cache_free");
         profile_cache->num_entries--;
         curr = next;
@@ -84,7 +84,7 @@ rc_gsicc_profile_cache_free(gs_memory_t
         emprintf1(mem,"gsicc_profile_cache_free, num_entries is %d (should be 0).\n",
                   profile_cache->num_entries);
 #endif
-    gs_free_object(mem->stable_memory, profile_cache,
+    gs_free_object(profile_cache->memory->stable_memory, profile_cache,
                    "rc_gsicc_profile_cache_free");
 }
 
@@ -93,7 +93,7 @@ gsicc_add_cs(gs_gstate * pgs, gs_color_s
 {
     gsicc_profile_entry_t *result;
     gsicc_profile_cache_t *profile_cache = pgs->icc_profile_cache;
-    gs_memory_t *memory =  pgs->memory;
+    gs_memory_t *memory =  profile_cache->memory;
 
     if (dictkey == 0)
         return;
diff -pruN 9.55.0~dfsg-3/base/gsiodisk.c 9.56.1~dfsg-1/base/gsiodisk.c
--- 9.55.0~dfsg-3/base/gsiodisk.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsiodisk.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -491,7 +491,7 @@ MapFileOpen(gs_memory_t *mem, const char
     fullname = (char *)gs_alloc_bytes(mem, totlen, "MapFileOpen(fullname)");
     if (fullname) {
 
-        gs_sprintf(fullname, "%s%s", rootpath, filename);
+        gs_snprintf(fullname, totlen, "%s%s", rootpath, filename);
         f = gp_fopen(fullname, attributes);
 
         gs_free_object(mem, fullname , "MapFileOpen(fullname)");
@@ -600,7 +600,7 @@ MapFileUnlink(gs_memory_t *mem, const ch
         return;
     fullname = (char *)gs_alloc_bytes(mem, totlen, "MapFileUnlink(fullname)");
     if (fullname) {
-        gs_sprintf(fullname, "%s%s", rootpath, filename);
+        gs_snprintf(fullname, totlen, "%s%s", rootpath, filename);
 
         unlink(fullname);
 
@@ -631,8 +631,8 @@ MapFileRename(gs_memory_t *mem, const ch
     newfullname = (char *)gs_alloc_bytes(mem, ntotlen, "MapFileRename(newfullname)");
 
     if (oldfullname && newfullname) {
-        gs_sprintf(oldfullname, "%s%s", rootpath, oldfilename);
-        gs_sprintf(newfullname, "%s%s", rootpath, newfilename);
+        gs_snprintf(oldfullname, ototlen, "%s%s", rootpath, oldfilename);
+        gs_snprintf(newfullname, ntotlen, "%s%s", rootpath, newfilename);
         rename(oldfullname, newfullname);
     }
 
@@ -813,7 +813,7 @@ map_file_name_get(gs_memory_t *mem, cons
     if (d != -1) {
         /* 20 characters are enough for even a 64 bit integer */
         if ((strlen(root_name) + 20) < gp_file_name_sizeof) {
-            gs_sprintf(osname, "%s%d", root_name, d);
+            gs_snprintf(osname, gp_file_name_sizeof, "%s%d", root_name, d);
             return true;
         }
     }
diff -pruN 9.55.0~dfsg-3/base/gslibctx.c 9.56.1~dfsg-1/base/gslibctx.c
--- 9.55.0~dfsg-3/base/gslibctx.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gslibctx.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -13,6 +13,27 @@
    CA 94945, U.S.A., +1(415)492-9861, for further information.
 */
 
+/*
+        Some notes on the structure here:
+
+        At the top level, we have 'instances' of Ghostscript (or GPL).
+        Here, 'instance' is short for 'instance of the Ghostscript API'.
+        Each instance is returned by 'gsapi_new_instance'. Every new
+        instance gets a unique gs_lib_ctx_core_t. Each instance can be
+        called from any number of threads, but only from one thread at
+        a time!
+
+        Each instance of Ghostscript owns one or more interpreters.
+        Each interpreter gets a unique gs_lib_ctx_t, that shares the
+        instance's gs_lib_ctx_core_t.
+
+        Each interpreter (by which we include the graphics library
+        called by that interpreter) can make multiple gs_memory_t's.
+        Certainly, every simultaneous (rendering) thread created by by
+        the interpreter will get a unique gs_memory_t. These
+        gs_memory_t's share the same gs_lib_ctx_t (and hence the same
+        gs_lib_ctx_core_t).
+*/
 
 
 /* library context functionality for ghostscript
@@ -32,6 +53,7 @@
 #include "cal.h"
 #endif
 #include "gsargs.h"
+#include "globals.h"
 
 /* Include the extern for the device list. */
 extern_gs_lib_device_list();
@@ -47,16 +69,6 @@ gs_lib_ctx_get_real_stdio(FILE **in, FIL
 #include "gslibctx.h"
 #include "gsmemory.h"
 
-#ifndef GS_THREADSAFE
-static gs_memory_t *mem_err_print = NULL;
-
-gs_memory_t *
-gs_lib_ctx_get_non_gc_memory_t()
-{
-    return mem_err_print ? mem_err_print : NULL;
-}
-#endif
-
 /*  This sets the directory to prepend to the ICC profile names specified for
     defaultgray, defaultrgb, defaultcmyk, proofing, linking, named color and device */
 int
@@ -204,7 +216,9 @@ fs_file_open_printer(const gs_memory_t *
         *file = NULL;
         return gs_error_invalidfileaccess;
     }
-    gp_setmode_binary_impl(f, binary_mode);
+    /* The lgtm comment below is required because on some platforms that function
+     * does nothing. */
+    gp_setmode_binary_impl(f, binary_mode); /* lgtm [cpp/useless-expression] */
 
     return 0;
 }
@@ -242,14 +256,19 @@ static cal_allocators cal_allocs =
 int gs_lib_ctx_init(gs_lib_ctx_t *ctx, gs_memory_t *mem)
 {
     gs_lib_ctx_t *pio = NULL;
+    gs_globals *globals;
 
     /* Check the non gc allocator is being passed in */
     if (mem == 0 || mem != mem->non_gc_memory)
         return_error(gs_error_Fatal);
 
-#ifndef GS_THREADSAFE
-    mem_err_print = mem;
-#endif
+    /* Get globals here, earlier than it seems we might need it
+     * because a side effect of this is ensuring that the thread
+     * local storage for the malloc pointer is set up. */
+    globals = gp_get_globals();
+
+    /* Now it's safe to set this. */
+    gp_set_debug_mem_ptr(mem);
 
     if (mem->gs_lib_ctx) /* one time initialization */
         return 0;
@@ -278,6 +297,7 @@ int gs_lib_ctx_init(gs_lib_ctx_t *ctx, g
             return -1;
         }
         memset(pio->core, 0, sizeof(*pio->core));
+        pio->core->globals = globals;
         pio->core->fs = (gs_fs_list_t *)gs_alloc_bytes_immovable(mem,
                                                                  sizeof(gs_fs_list_t),
                                                                  "gs_lib_ctx_init(gs_fs_list_t)");
@@ -305,15 +325,8 @@ int gs_lib_ctx_init(gs_lib_ctx_t *ctx, g
         pio->core->fs->next   = NULL;
 
         pio->core->monitor = gx_monitor_alloc(mem);
-        if (pio->core->monitor == NULL) {
-#ifdef WITH_CAL
-            cal_fin(pio->core->cal_ctx, mem);
-#endif
-            gs_free_object(mem, pio->core->fs, "gs_lib_ctx_init");
-            gs_free_object(mem, pio->core, "gs_lib_ctx_init");
-            gs_free_object(mem, pio, "gs_lib_ctx_init");
-            return -1;
-        }
+        if (pio->core->monitor == NULL)
+            goto core_create_failed;
         pio->core->refs = 1;
         pio->core->memory = mem;
 
@@ -324,6 +337,19 @@ int gs_lib_ctx_init(gs_lib_ctx_t *ctx, g
         pio->core->gs_next_id = 5; /* Cloned contexts share the state */
         /* Set scanconverter to 1 (default) */
         pio->core->scanconverter = GS_SCANCONVERTER_DEFAULT;
+        /* Initialise the underlying CMS. */
+        pio->core->cms_context = gscms_create(mem);
+        if (pio->core->cms_context == NULL) {
+            gx_monitor_free((gx_monitor_t *)(pio->core->monitor));
+core_create_failed:
+#ifdef WITH_CAL
+            cal_fin(pio->core->cal_ctx, mem);
+#endif
+            gs_free_object(mem, pio->core->fs, "gs_lib_ctx_init");
+            gs_free_object(mem, pio->core, "gs_lib_ctx_init");
+            gs_free_object(mem, pio, "gs_lib_ctx_init");
+            return -1;
+        }
     }
 
     /* Now set the non zero/false/NULL things */
@@ -342,10 +368,6 @@ int gs_lib_ctx_init(gs_lib_ctx_t *ctx, g
                                            strlen(gs_dev_defaults)) < 0)
         goto Failure;
 
-    /* Initialise the underlying CMS. */
-    if (gscms_create(mem))
-        goto Failure;
-
     /* Initialise any lock required for the jpx codec */
     if (sjpxd_create(mem))
         goto Failure;
@@ -397,7 +419,6 @@ void gs_lib_ctx_fin(gs_memory_t *mem)
     ctx_mem = ctx->memory;
 
     sjpxd_destroy(mem);
-    gscms_destroy(ctx_mem);
     gs_free_object(ctx_mem, ctx->profiledir,
         "gs_lib_ctx_fin");
 
@@ -408,14 +429,11 @@ void gs_lib_ctx_fin(gs_memory_t *mem)
     gs_free_object(ctx_mem, ctx->io_device_table_root, "gs_lib_ctx_fin");
     gs_free_object(ctx_mem, ctx->font_dir_root, "gs_lib_ctx_fin");
 
-#ifndef GS_THREADSAFE
-    mem_err_print = NULL;
-#endif
-
     gx_monitor_enter((gx_monitor_t *)(ctx->core->monitor));
     refs = --ctx->core->refs;
     gx_monitor_leave((gx_monitor_t *)(ctx->core->monitor));
     if (refs == 0) {
+        gscms_destroy(ctx->core->cms_context);
         gx_monitor_free((gx_monitor_t *)(ctx->core->monitor));
 #ifdef WITH_CAL
         cal_fin(ctx->core->cal_ctx, ctx->core->memory);
@@ -461,14 +479,7 @@ void *gs_lib_ctx_get_cms_context( const
 {
     if (mem == NULL)
         return NULL;
-    return mem->gs_lib_ctx->cms_context;
-}
-
-void gs_lib_ctx_set_cms_context( const gs_memory_t *mem, void *cms_context )
-{
-    if (mem == NULL)
-        return;
-    mem->gs_lib_ctx->cms_context = cms_context;
+    return mem->gs_lib_ctx->core->cms_context;
 }
 
 int gs_lib_ctx_get_act_on_uel( const gs_memory_t *mem )
@@ -503,13 +514,6 @@ int outwrite(const gs_memory_t *mem, con
     return code;
 }
 
-#ifndef GS_THREADSAFE
-int errwrite_nomem(const char *str, int len)
-{
-    return errwrite(mem_err_print, str, len);
-}
-#endif
-
 int errwrite(const gs_memory_t *mem, const char *str, int len)
 {
     int code;
@@ -518,13 +522,11 @@ int errwrite(const gs_memory_t *mem, con
     if (len == 0)
         return 0;
     if (mem == NULL) {
-#ifdef GS_THREADSAFE
-        return 0;
-#else
-        mem = mem_err_print;
+#ifdef DEBUG
+        mem = gp_get_debug_mem_ptr();
         if (mem == NULL)
-            return 0;
 #endif
+            return 0;
     }
     ctx = mem->gs_lib_ctx;
     if (ctx == NULL)
@@ -553,13 +555,6 @@ void outflush(const gs_memory_t *mem)
         fflush(core->fstdout);
 }
 
-#ifndef GS_THREADSAFE
-void errflush_nomem(void)
-{
-    errflush(mem_err_print);
-}
-#endif
-
 void errflush(const gs_memory_t *mem)
 {
     if (!mem->gs_lib_ctx->core->stderr_fn)
@@ -1339,3 +1334,39 @@ int gs_lib_ctx_callout(gs_memory_t *mem,
     }
     return -1;
 }
+
+int gs_lib_ctx_nts_adjust(gs_memory_t *mem, int adjust)
+{
+    gs_lib_ctx_core_t *core;
+    int ret = 0;
+    gs_globals *globals;
+
+    if (adjust == 0)
+        return 0;
+
+    if (mem == NULL || mem->gs_lib_ctx == NULL || mem->gs_lib_ctx->core == NULL)
+        return_error(gs_error_unknownerror);
+
+    core = mem->gs_lib_ctx->core;
+    globals = core->globals;
+    if (globals == NULL)
+        return 0; /* No globals means just once instance. Adjustment is pointless. */
+
+    gp_global_lock(globals);
+    if (adjust > 0 && globals->non_threadsafe_count != 0)
+        ret = gs_error_unknownerror; /* We already have one non threadsafe device running. */
+    else if (adjust < 0 && globals->non_threadsafe_count == 0)
+        ret = gs_error_unknownerror; /* This indicates something has gone very wrong! */
+    else
+        globals->non_threadsafe_count += adjust;
+    gp_global_unlock(globals);
+
+    if (ret)
+        ret = gs_note_error(ret);
+    return ret;
+}
+
+void gs_globals_init(gs_globals *globals)
+{
+    memset(globals, 0, sizeof(*globals));
+}
diff -pruN 9.55.0~dfsg-3/base/gslibctx.h 9.56.1~dfsg-1/base/gslibctx.h
--- 9.55.0~dfsg-3/base/gslibctx.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gslibctx.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -94,6 +94,8 @@ typedef struct gs_callout_list_s {
     void *handle;
 } gs_callout_list_t;
 
+typedef struct gs_globals gs_globals;
+
 typedef struct {
     void *monitor;
     int refs;
@@ -131,12 +133,21 @@ typedef struct {
      * all builds. */
     void *cal_ctx;
 
+    void *cms_context;  /* Opaque context pointer from underlying CMS in use */
+
     gs_callout_list_t *callouts;
 
     /* Stashed args */
     int arg_max;
     int argc;
     char **argv;
+
+    /* clist io procs pointers. Indirected through here to allow
+     * easy build time selection. */
+    const void *clist_io_procs_memory;
+    const void *clist_io_procs_file;
+
+    gs_globals *globals;
 } gs_lib_ctx_core_t;
 
 typedef struct gs_lib_ctx_s
@@ -176,7 +187,6 @@ typedef struct gs_lib_ctx_s
      * and one in the device */
     char *profiledir;               /* Directory used in searching for ICC profiles */
     int profiledir_len;             /* length of directory name (allows for Unicode) */
-    void *cms_context;  /* Opaque context pointer from underlying CMS in use */
     gs_fapi_server **fapi_servers;
     char *default_device_list;
     int gcsignal;
@@ -206,21 +216,13 @@ void gs_lib_ctx_fin( gs_memory_t *mem );
 gs_lib_ctx_t *gs_lib_ctx_get_interp_instance( const gs_memory_t *mem );
 
 void *gs_lib_ctx_get_cms_context( const gs_memory_t *mem );
-void gs_lib_ctx_set_cms_context( const gs_memory_t *mem, void *cms_context );
 int gs_lib_ctx_get_act_on_uel( const gs_memory_t *mem );
 
 int gs_lib_ctx_register_callout(gs_memory_t *mem, gs_callout_fn, void *arg);
 void gs_lib_ctx_deregister_callout(gs_memory_t *mem, gs_callout_fn, void *arg);
 int gs_lib_ctx_callout(gs_memory_t *mem, const char *dev_name,
                        int id, int size, void *data);
-
-
-#ifndef GS_THREADSAFE
-/* HACK to get at non garbage collection memory pointer
- *
- */
-gs_memory_t * gs_lib_ctx_get_non_gc_memory_t(void);
-#endif
+int gs_lib_ctx_nts_adjust(gs_memory_t *mem, int adjust);
 
 int gs_lib_ctx_set_icc_directory(const gs_memory_t *mem_gc, const char* pname,
                                  int dir_namelen);
diff -pruN 9.55.0~dfsg-3/base/gs.mak 9.56.1~dfsg-1/base/gs.mak
--- 9.55.0~dfsg-3/base/gs.mak	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gs.mak	2022-04-04 13:46:22.000000000 +0000
@@ -533,13 +533,14 @@ gpdl_tr=$(GLGENDIR)$(D)gpdl.tr
 igpdl_tr=$(GLGENDIR)$(D)igpdl.tr
 gpdlld_tr=$(GLGENDIR)$(D)gpdlld.tr
 $(gpdl_tr): $(GS_MAK) $(GLSRCDIR)$(D)version.mak $(GENCONF_XE) $(ECHOGS_XE) $(ld_tr) $(devs_tr) $(XPS_DEVS_ALL) \
-            $(devs_tr) $(PSI_DEVS_ALL) $(PCL_FEATURE_DEVS) $(XPS_FEATURE_DEVS) $(GLGENDIR)$(D)libcore.dev $(MAKEDIRS)
+		$(devs_tr) $(PSI_DEVS_ALL) $(PCL_FEATURE_DEVS) $(XPS_FEATURE_DEVS) $(PDF_FEATURE_DEVS) \
+		$(GLGENDIR)$(D)libcore.dev $(MAKEDIRS)
 	$(EXP)$(ECHOGS_XE) -w $(igpdl_tr) - -include $(PSI_FEATURE_DEVS)
 	$(EXP)$(GENCONF_XE) $(igpdl_tr) -h $(iconfxx_h) $(CONFILES) $(CONFLDTR) $(gpdlld_tr)
 	$(EXP)$(ECHOGS_XE) -w $(iconfig_h) -R $(iconfxx_h)
 	$(EXP)$(ECHOGS_XE) -w $(gpdl_tr) -R $(devs_tr)
 	$(EXP)$(ECHOGS_XE) -a $(gpdl_tr) -R $(igpdl_tr)
-	$(EXP)$(ECHOGS_XE) -a $(gpdl_tr) - -include $(PCL_FEATURE_DEVS) $(XPS_FEATURE_DEVS)
+	$(EXP)$(ECHOGS_XE) -a $(gpdl_tr) - -include $(PCL_FEATURE_DEVS) $(XPS_FEATURE_DEVS) $(PDF_FEATURE_DEVS)
 	$(EXP)$(GENCONF_XE) $(gpdl_tr) -h $(GLGENDIR)$(D)unused2.h $(CONFILES) $(CONFLDTR) $(gpdlld_tr)
 
 $(gconfxx_h) : $(ld_tr)
diff -pruN 9.55.0~dfsg-3/base/gsmalloc.c 9.56.1~dfsg-1/base/gsmalloc.c
--- 9.55.0~dfsg-3/base/gsmalloc.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsmalloc.c	2022-04-04 13:46:22.000000000 +0000
@@ -24,6 +24,7 @@
 #include "gsstruct.h"		/* for st_bytes */
 #include "gsmalloc.h"
 #include "gsmemret.h"		/* retrying wrapper */
+#include "gp.h"
 
 /* ------ Heap allocator ------ */
 
diff -pruN 9.55.0~dfsg-3/base/gsmchunk.c 9.56.1~dfsg-1/base/gsmchunk.c
--- 9.55.0~dfsg-3/base/gsmchunk.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsmchunk.c	2022-04-04 13:46:22.000000000 +0000
@@ -967,7 +967,7 @@ chunk_obj_alloc(gs_memory_t *mem, size_t
     gs_memory_chunk_dump_memory(cmem);
 #endif
 
-    return (byte *)(obj) + SIZEOF_ROUND_ALIGN(chunk_obj_node_t);
+    return (byte *)Memento_label((byte *)(obj) + SIZEOF_ROUND_ALIGN(chunk_obj_node_t), cname);
 }
 
 static byte *
diff -pruN 9.55.0~dfsg-3/base/gsmisc.c 9.56.1~dfsg-1/base/gsmisc.c
--- 9.55.0~dfsg-3/base/gsmisc.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsmisc.c	2022-04-04 13:46:22.000000000 +0000
@@ -47,6 +47,7 @@ orig_sqrt(double x)
 #include "gxfixed.h"
 #include "stdint_.h"
 #include "stdio_.h"
+#include "gp.h"
 
 /* ------ Redirected stdout and stderr  ------ */
 
@@ -73,25 +74,26 @@ int outprintf(const gs_memory_t *mem, co
     return count;
 }
 
-#ifndef GS_THREADSAFE
 int errprintf_nomem(const char *fmt, ...)
 {
     int count;
     char buf[PRINTF_BUF_LENGTH];
     va_list args;
+    gs_memory_t *mem = gp_get_debug_mem_ptr();
 
+    if (mem == NULL)
+        return 0;
     va_start(args, fmt);
     count = vsnprintf(buf, sizeof(buf), fmt, args);
     if (count < 0 || count >= sizeof(buf))  { /* MSVC || C99*/
-        errwrite_nomem(buf, sizeof(buf) - 1);
-        errwrite_nomem(msg_truncated, sizeof(msg_truncated) - 1);
+        errwrite(mem, buf, sizeof(buf) - 1);
+        errwrite(mem, msg_truncated, sizeof(msg_truncated) - 1);
     } else {
-        errwrite_nomem(buf, count);
+        errwrite(mem, buf, count);
     }
     va_end(args);
     return count;
 }
-#endif
 
 int errprintf(const gs_memory_t *mem, const char *fmt, ...)
 {
@@ -159,11 +161,12 @@ dprintf_file_tail(const char *file)
         --tail;
     return tail;
 }
-#ifndef GS_THREADSAFE
 void
 dflush(void)
 {
-    errflush_nomem();
+    gs_memory_t *mem = gp_get_debug_mem_ptr();
+    if (mem)
+        errflush(mem);
 }
 #if __LINE__                    /* compiler provides it */
 void
@@ -207,7 +210,6 @@ eprintf_program_ident(const char *progra
         epf(": ");
     }
 }
-#endif
 #if __LINE__                    /* compiler provides it */
 void
 dmprintf_file_and_line(const gs_memory_t *mem,const char *file, int line)
@@ -303,12 +305,15 @@ gs_return_check_interrupt(const gs_memor
     }
 }
 
-#ifndef GS_THREADSAFE
 int gs_throw_imp(const char *func, const char *file, int line, int op, int code, const char *fmt, ...)
 {
     char msg[1024];
     va_list ap;
     int count;
+    gs_memory_t *mem = gp_get_debug_mem_ptr();
+
+    if (mem == NULL)
+        return code;
 
     va_start(ap, fmt);
     count = vsnprintf(msg, sizeof(msg), fmt, ap);
@@ -325,26 +330,25 @@ int gs_throw_imp(const char *func, const
 
     /* throw */
     if (op == 0)
-        errprintf_nomem("+ %s:%d: %s(): %s\n", file, line, func, msg);
+        errprintf(mem, "+ %s:%d: %s(): %s\n", file, line, func, msg);
 
     /* rethrow */
     if (op == 1)
-        errprintf_nomem("| %s:%d: %s(): %s\n", file, line, func, msg);
+        errprintf(mem, "| %s:%d: %s(): %s\n", file, line, func, msg);
 
     /* catch */
     if (op == 2)
-        errprintf_nomem("- %s:%d: %s(): %s\n", file, line, func, msg);
+        errprintf(mem, "- %s:%d: %s(): %s\n", file, line, func, msg);
 
     /* warn */
     if (op == 3)
-        errprintf_nomem("  %s:%d: %s(): %s\n", file, line, func, msg);
+        errprintf(mem, "  %s:%d: %s(): %s\n", file, line, func, msg);
 
     if (count < 0 || count >= sizeof(msg))  { /* MSVC || C99 */
-        errwrite_nomem(msg_truncated, sizeof(msg_truncated) - 1);
+        errwrite(mem, msg_truncated, sizeof(msg_truncated) - 1);
     }
     return code;
 }
-#endif
 
 const char *gs_errstr(int code)
 {
@@ -542,7 +546,7 @@ gs_realloc(void *old_ptr, size_t old_siz
 
 /* ------ Debugging support ------ */
 
-#ifndef GS_THREADSAFE
+#ifdef DEBUG
 /* Print a string in hexdump format. */
 void
 debug_print_string_hex_nomem(const byte * chrs, uint len)
@@ -615,7 +619,7 @@ debug_print_string_hex(const gs_memory_t
  * first_arg is the first argument of the procedure into which this code
  * is patched.
  */
-#ifndef GS_THREADSAFE
+#ifdef DEBUG
 #define BACKTRACE(first_arg)\
   BEGIN\
     ulong *fp_ = (ulong *)&first_arg - 2;\
@@ -935,7 +939,7 @@ fixed_mult_quo(fixed signed_A, fixed B,
 double
 gs_sqrt(double x, const char *file, int line)
 {
-#ifndef GS_THREADSAFE
+#ifdef DEBUG
     if (gs_debug_c('~')) {
         dprintf3("[~]sqrt(%g) at %s:%d\n", x, (const char *)file, line);
         dflush();
diff -pruN 9.55.0~dfsg-3/base/gspaint.c 9.56.1~dfsg-1/base/gspaint.c
--- 9.55.0~dfsg-3/base/gspaint.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gspaint.c	2022-04-04 13:46:22.000000000 +0000
@@ -220,11 +220,11 @@ alpha_buffer_init(gs_gstate * pgs, fixed
     ibox.q.x = fixed2int_ceiling(bbox.q.x + extra_x) + 1;
     ibox.q.y = fixed2int_ceiling(bbox.q.y + extra_y) + 1;
     (void)dev_proc(dev, dev_spec_op)(dev, gxdso_restrict_bbox, &ibox, sizeof(ibox));
+    if ((ibox.q.y <= ibox.p.y) || (ibox.q.x <= ibox.p.x))
+        return 2;
     width = (ibox.q.x - ibox.p.x) << log2_scale.x;
     raster = bitmap_raster(width);
     band_space = raster << log2_scale.y;
-    if (ibox.q.y <= ibox.p.y)
-        return 2;
     height2 = (ibox.q.y - ibox.p.y);
     height = (abuf_nominal / band_space);
     if (height == 0)
diff -pruN 9.55.0~dfsg-3/base/gsparaml.c 9.56.1~dfsg-1/base/gsparaml.c
--- 9.55.0~dfsg-3/base/gsparaml.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsparaml.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -834,20 +834,20 @@ int_array_to_string(gs_param_int_array i
 
     out_string(out, "[");
     for (i = 0; i < ia.size; i++) {
-        gs_sprintf(text, "%d", ia.data[i]);
+        gs_snprintf(text, sizeof(text), "%d", ia.data[i]);
         out_string(out, text);
     }
     out_string(out, "]");
 }
 
 static void
-print_float(char *text, float f)
+print_float(char text[32], float f)
 {
     /* We attempt to tidy up %f's somewhat unpredictable output
      * here, so rather than printing 0.10000000 we print 0.1 */
     char *p = text;
     int frac = 0;
-    gs_sprintf(text, "%f", f);
+    gs_snprintf(text, 32, "%f", f);
     /* Find the terminator, or 'e' to spot exponent mode. */
     while (*p && *p != 'e' && *p != 'E') {
         if (*p == '.')
@@ -970,28 +970,28 @@ to_string(gs_param_list *plist, gs_param
     case gs_param_type_int:
     {
         char text[32];
-        gs_sprintf(text, "%d", pvalue.value.i);
+        gs_snprintf(text, sizeof(text), "%d", pvalue.value.i);
         out_string(out, text);
         break;
     }
     case gs_param_type_i64:
     {
         char text[32];
-        gs_sprintf(text, "%"PRId64, pvalue.value.i64);
+        gs_snprintf(text, sizeof(text), "%"PRId64, pvalue.value.i64);
         out_string(out, text);
         break;
     }
     case gs_param_type_long:
     {
         char text[32];
-        gs_sprintf(text, "%ld", pvalue.value.l);
+        gs_snprintf(text, sizeof(text), "%ld", pvalue.value.l);
         out_string(out, text);
         break;
     }
     case gs_param_type_size_t:
     {
         char text[32];
-        gs_sprintf(text, "%"PRIdSIZE, pvalue.value.z);
+        gs_snprintf(text, sizeof(text), "%"PRIdSIZE, pvalue.value.z);
         out_string(out, text);
         break;
     }
diff -pruN 9.55.0~dfsg-3/base/gspcolor.c 9.56.1~dfsg-1/base/gspcolor.c
--- 9.55.0~dfsg-3/base/gspcolor.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gspcolor.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -281,7 +281,7 @@ gx_install_Pattern(gs_color_space * pcs,
 static int
 gx_set_overprint_Pattern(const gs_color_space * pcs, gs_gstate * pgs)
 {
-    gs_overprint_params_t params;
+    gs_overprint_params_t params = { 0 };
 
     if (!pgs->overprint) {
         params.retain_any_comps = false;
diff -pruN 9.55.0~dfsg-3/base/gspmdrv.c 9.56.1~dfsg-1/base/gspmdrv.c
--- 9.55.0~dfsg-3/base/gspmdrv.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gspmdrv.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -345,11 +345,11 @@ write_profile(void)
 {
     char profile[64];
 
-    gs_sprintf(profile, "%d %d", option.img_origin.x, option.img_origin.y);
+    gs_snprintf(profile, sizeof(profile), "%d %d", option.img_origin.x, option.img_origin.y);
     PrfWriteProfileString(HINI_USERPROFILE, section, "Origin", profile);
-    gs_sprintf(profile, "%d %d", option.img_size.x, option.img_size.y);
+    gs_snprintf(profile, sizeof(profile), "%d %d", option.img_size.x, option.img_size.y);
     PrfWriteProfileString(HINI_USERPROFILE, section, "Size", profile);
-    gs_sprintf(profile, "%d", option.img_max);
+    gs_snprintf(profile, sizeof(profile), "%d", option.img_max);
     PrfWriteProfileString(HINI_USERPROFILE, section, "Maximized", profile);
 }
 
@@ -489,26 +489,26 @@ init_display(int argc, char *argv[])
     find_hwnd_gs(argv[2]);
 
     if (!rc) {
-        gs_sprintf(name, SHARED_NAME, argv[2]);
+        gs_snprintf(name, sizeof(name), SHARED_NAME, argv[2]);
         rc = DosGetNamedSharedMem((PVOID *) & bitmap.pbmi, name, PAG_READ | PAG_WRITE);
         if (rc) {
-            gs_sprintf(buf, "Failed to open: bmp shared memory \"%s\" rc = %d", argv[0], rc);
+            gs_snprintf(buf, sizeof(buf), "Failed to open: bmp shared memory \"%s\" rc = %d", argv[0], rc);
             error_message(buf);
         }
     }
     if (!rc) {
-        gs_sprintf(name, SYNC_NAME, argv[2]);
+        gs_snprintf(name, sizeof(name), SYNC_NAME, argv[2]);
         rc = DosOpenEventSem(name, &update_event_sem);
         if (rc) {
-            gs_sprintf(buf, "Failed to open: update event semaphore \"%s\" rc = %d", argv[1], rc);
+            gs_snprintf(buf, sizeof(buf), "Failed to open: update event semaphore \"%s\" rc = %d", argv[1], rc);
             error_message(buf);
         }
     }
     if (!rc) {
-        gs_sprintf(name, MUTEX_NAME, argv[2]);
+        gs_snprintf(name, sizeof(name), MUTEX_NAME, argv[2]);
         rc = DosOpenMutexSem(name, &bmp_mutex_sem);
         if (rc) {
-            gs_sprintf(buf, "Failed to open: bmp mutex semaphore \"%s\" rc = %d", argv[1], rc);
+            gs_snprintf(buf, sizeof(buf), "Failed to open: bmp mutex semaphore \"%s\" rc = %d", argv[1], rc);
             error_message(buf);
         }
     }
@@ -535,19 +535,19 @@ init_bitmap(int argc, char *argv[])
     if ((rc = DosOpen(argv[2], &hf, &action, 0, FILE_NORMAL, FILE_OPEN,
                       OPEN_ACCESS_READONLY | OPEN_SHARE_DENYREADWRITE, 0))
         != (APIRET) 0) {
-        gs_sprintf(buf, "Error opening: %s", argv[2]);
+        gs_snprintf(buf, sizeof(buf), "Error opening: %s", argv[2]);
         error_message(buf);
         return rc;
     }
     rc = DosSetFilePtr(hf, 0, FILE_END, &length);
     if (rc) {
-        gs_sprintf(buf, "failed seeking to EOF: error = %d", rc);
+        gs_snprintf(buf, sizeof(buf), "failed seeking to EOF: error = %d", rc);
         error_message(buf);
         return rc;
     }
     rc = DosSetFilePtr(hf, 0, FILE_BEGIN, &count);
     if (rc) {
-        gs_sprintf(buf, "failed seeking to BOF: error = %d", rc);
+        gs_snprintf(buf, sizeof(buf), "failed seeking to BOF: error = %d", rc);
         error_message(buf);
         return rc;
     };
@@ -555,14 +555,14 @@ init_bitmap(int argc, char *argv[])
     /* allocate memory for bitmap */
     if ((rc = DosAllocMem((PPVOID) & bbuffer, length, PAG_READ | PAG_WRITE | PAG_COMMIT))
         != (APIRET) 0) {
-        gs_sprintf(buf, "failed allocating memory");
+        gs_snprintf(buf, sizeof(buf), "failed allocating memory");
         error_message(buf);
         return rc;
     }
     rc = DosRead(hf, bbuffer, length, &count);
     DosClose(hf);
     if (rc) {
-        gs_sprintf(buf, "failed reading bitmap, error = %u, count = %u", rc, count);
+        gs_snprintf(buf, sizeof(buf), "failed reading bitmap, error = %u, count = %u", rc, count);
         error_message(buf);
         return rc;
     }
@@ -573,7 +573,7 @@ init_bitmap(int argc, char *argv[])
     scan_bitmap(&bitmap);
     bitmap.valid = TRUE;
 
-    gs_sprintf(buf, "bitmap width = %d, height = %d", bitmap.width, bitmap.height);
+    gs_snprintf(buf, sizeof(buf), "bitmap width = %d, height = %d", bitmap.width, bitmap.height);
     message_box(buf, 0);
     return rc;
 }
@@ -805,7 +805,7 @@ make_bitmap(BMAP * pbm, ULONG left, ULON
             char buf[256];
 
             eid = WinGetLastError(hab);
-            gs_sprintf(buf, "make_bitmap: GpiDrawBits rc = %08x, eid = %08x", rc, eid);
+            gs_snprintf(buf, sizeof(buf), "make_bitmap: GpiDrawBits rc = %08x, eid = %08x", rc, eid);
             message_box(buf, 0);
         }
     }
diff -pruN 9.55.0~dfsg-3/base/gsptype1.c 9.56.1~dfsg-1/base/gsptype1.c
--- 9.55.0~dfsg-3/base/gsptype1.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsptype1.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -415,6 +415,7 @@ gs_pattern1_make_pattern(gs_client_color
     return 0;
   fsaved:gs_gstate_free(saved);
     gs_free_object(mem, pinst, "gs_makepattern");
+    pcc->pattern = NULL; /* We've just freed the memory this points to */
     return code;
 }
 
@@ -616,10 +617,34 @@ compute_inst_matrix(gs_pattern1_instance
         return code;
 
     /* The stepping matrix : */
-    xx = pinst->templat.XStep * saved->ctm.xx;
-    xy = pinst->templat.XStep * saved->ctm.xy;
-    yx = pinst->templat.YStep * saved->ctm.yx;
-    yy = pinst->templat.YStep * saved->ctm.yy;
+    /* We do not want to overflow the maths here. Since xx etc are all floats
+     * then the multiplication will definitely fit into a double, and we can
+     * check to ensure that the result still fits into a float without
+     * overflowing at any point.
+     */
+    {
+        double double_mult = 0.0;
+
+        double_mult = (double)pinst->templat.XStep * (double)saved->ctm.xx;
+        if (double_mult < -MAX_FLOAT || double_mult > MAX_FLOAT)
+            return_error(gs_error_rangecheck);
+        xx = (float)double_mult;
+
+        double_mult = (double)pinst->templat.XStep * (double)saved->ctm.xy;
+        if (double_mult < -MAX_FLOAT || double_mult > MAX_FLOAT)
+            return_error(gs_error_rangecheck);
+        xy = double_mult;
+
+        double_mult = (double)pinst->templat.YStep * (double)saved->ctm.yx;
+        if (double_mult < -MAX_FLOAT || double_mult > MAX_FLOAT)
+            return_error(gs_error_rangecheck);
+        yx = double_mult;
+
+        double_mult = (double)pinst->templat.YStep * (double)saved->ctm.yy;
+        if (double_mult < -MAX_FLOAT || double_mult > MAX_FLOAT)
+            return_error(gs_error_rangecheck);
+        yy = double_mult;
+    }
 
     /* Adjust the stepping matrix so all coefficients are >= 0. */
     if (xx == 0 || yy == 0) { /* We know that both xy and yx are non-zero. */
@@ -1474,13 +1499,13 @@ gx_dc_pattern_save_dc(
     psdc->type = pdevc->type;
     if (pdevc->ccolor_valid) {
         psdc->colors.pattern.id = pdevc->ccolor.pattern->pattern_id;
-        psdc->colors.pattern.phase = pdevc->phase;
+        psdc->phase = pdevc->phase;
     } else {
         /* The client color has been changed to a non-pattern color,
            but device color has not been created yet.
          */
         psdc->colors.pattern.id = gs_no_id;
-        psdc->colors.pattern.phase.x = psdc->colors.pattern.phase.y = 0;
+        psdc->phase.x = psdc->phase.y = 0;
     }
 }
 
@@ -1695,16 +1720,30 @@ typedef struct tile_trans_clist_info_s {
     int height;
 } tile_trans_clist_info_t;
 
+#define serialized_tile_common \
+    gs_id id;\
+    int size_b, size_c;\
+    gs_matrix step_matrix;\
+    gs_rect bbox;\
+    int flags
+
 typedef struct gx_dc_serialized_tile_s {
-    gs_id id;
-    int size_b, size_c;
-    gs_int_point size;
-    gs_matrix step_matrix;
-    gs_rect bbox;
-    int flags;
-    gs_blend_mode_t blending_mode;	/* in case tile has transparency */
+    serialized_tile_common;
 } gx_dc_serialized_tile_t;
 
+#define serialized_tile_trans \
+    serialized_tile_common;\
+    gs_blend_mode_t blending_mode
+
+typedef struct gx_dc_serialized_trans_tile_s {
+    serialized_tile_trans;
+} gx_dc_serialized_trans_tile_t;
+
+typedef struct gx_dc_serialized_pattern_tile_s {
+    serialized_tile_trans;
+    gs_int_point size;
+} gx_dc_serialized_pattern_tile_t;
+
 enum {
     TILE_IS_LOCKED   = (int)0x80000000,
     TILE_HAS_OVERLAP = 0x40000000,
@@ -1742,8 +1781,6 @@ gx_dc_pattern_write_raster(gx_color_tile
 #endif
 
         buf.id = ptile->id;
-        buf.size.x = 0; /* fixme: don't write with raster patterns. */
-        buf.size.y = 0; /* fixme: don't write with raster patterns. */
         buf.size_b = size_b;
         buf.size_c = size_c;
         buf.step_matrix = ptile->step_matrix;
@@ -1773,7 +1810,7 @@ gx_dc_pattern_write_raster(gx_color_tile
         dp += sizeof(buf1);
         offset1 += sizeof(buf1);
     }
-    if (offset1 <= sizeof(gx_dc_serialized_tile_t) + size_b) {
+    if (offset1 < sizeof(gx_dc_serialized_tile_t) + size_b) {
         int l = min((size_b - sizeof(gx_strip_bitmap)) - (offset1 - sizeof(gx_dc_serialized_tile_t) -  sizeof(gx_strip_bitmap)), left);
 
         memcpy(dp, ptile->tbits.data + (offset1 - sizeof(gx_dc_serialized_tile_t) -  sizeof(gx_strip_bitmap)), l);
@@ -1785,7 +1822,7 @@ gx_dc_pattern_write_raster(gx_color_tile
         return 0;
     if (size_c == 0)
         return 0;
-    if (offset1 <= sizeof(gx_dc_serialized_tile_t) + size_b + sizeof(gx_strip_bitmap)) {
+    if (offset1 < sizeof(gx_dc_serialized_tile_t) + size_b + sizeof(gx_strip_bitmap)) {
         gx_strip_bitmap buf;
 
         if (left < sizeof(buf))
@@ -1797,7 +1834,7 @@ gx_dc_pattern_write_raster(gx_color_tile
         dp += sizeof(buf);
         offset1 += sizeof(buf);
     }
-    if (offset1 <= sizeof(gx_dc_serialized_tile_t) + size_b + size_c) {
+    if (offset1 < sizeof(gx_dc_serialized_tile_t) + size_b + size_c) {
         int l = min(size_c - sizeof(gx_strip_bitmap), left);
 
         memcpy(dp, ptile->tmask.data + (offset1 - sizeof(gx_dc_serialized_tile_t) - size_b - sizeof(gx_strip_bitmap)), l);
@@ -1818,7 +1855,7 @@ gx_dc_pattern_trans_write_raster(gx_colo
     int64_t offset1 = offset;
     unsigned char *ptr;
 
-    size_h = sizeof(gx_dc_serialized_tile_t) + sizeof(tile_trans_clist_info_t);
+    size_h = sizeof(gx_dc_serialized_trans_tile_t) + sizeof(tile_trans_clist_info_t);
 
     /* Everything that we need to handle the transparent tile */
 
@@ -1832,12 +1869,10 @@ gx_dc_pattern_trans_write_raster(gx_colo
         return 0;
     }
     if (offset1 == 0) { /* Serialize tile parameters: */
-        gx_dc_serialized_tile_t buf;
+        gx_dc_serialized_trans_tile_t buf;
         tile_trans_clist_info_t trans_info;
 
         buf.id = ptile->id;
-        buf.size.x = 0; /* fixme: don't write with raster patterns. */
-        buf.size.y = 0; /* fixme: don't write with raster patterns. */
         buf.size_b = size - size_h;
         buf.size_c = 0;
         buf.flags = ptile->depth
@@ -1884,9 +1919,9 @@ gx_dc_pattern_trans_write_raster(gx_colo
      * plane if this buffer has_tags. */
 
     /* check if we have written it all */
-    if (offset1 <= size) {
+    if (offset1 < size) {
         /* Get the most that we can write */
-        int u = min(size, left);
+        int u = min(size - offset1, left);
 
         /* copy that amount */
         ptr = ptile->ttrans->transbytes;
@@ -1952,11 +1987,11 @@ gx_dc_pattern_write(
     if (size_c < 0)
         return_error(gs_error_unregistered);
     if (data == NULL) {
-        *psize = sizeof(gx_dc_serialized_tile_t) + size_b + size_c;
+        *psize = sizeof(gx_dc_serialized_pattern_tile_t) + size_b + size_c;
         return 0;
     }
     if (offset1 == 0) { /* Serialize tile parameters: */
-        gx_dc_serialized_tile_t buf;
+        gx_dc_serialized_pattern_tile_t buf;
 
         buf.id = ptile->id;
         buf.size.x = ptile->cdev->common.width;
@@ -1977,14 +2012,14 @@ gx_dc_pattern_write(
             /* For a while we require the client to provide enough buffer size. */
             return_error(gs_error_unregistered); /* Must not happen. */
         }
-        memcpy(dp, &buf, sizeof(gx_dc_serialized_tile_t));
+        memcpy(dp, &buf, sizeof(gx_dc_serialized_pattern_tile_t));
         left -= sizeof(buf);
         dp += sizeof(buf);
         offset1 += sizeof(buf);
     }
-    if (offset1 <= sizeof(gx_dc_serialized_tile_t) + size_b) {
-        l = min(left, size_b - (offset1 - sizeof(gx_dc_serialized_tile_t)));
-        code = clist_get_data(ptile->cdev, 0, offset1 - sizeof(gx_dc_serialized_tile_t), dp, l);
+    if (offset1 < sizeof(gx_dc_serialized_pattern_tile_t) + size_b) {
+        l = min(left, size_b - (offset1 - sizeof(gx_dc_serialized_pattern_tile_t)));
+        code = clist_get_data(ptile->cdev, 0, offset1 - sizeof(gx_dc_serialized_pattern_tile_t), dp, l);
         if (code < 0)
             return code;
         left -= l;
@@ -1992,8 +2027,8 @@ gx_dc_pattern_write(
         dp += l;
     }
     if (left > 0) {
-        l = min(left, size_c - (offset1 - sizeof(gx_dc_serialized_tile_t) - size_b));
-        code = clist_get_data(ptile->cdev, 1, offset1 - sizeof(gx_dc_serialized_tile_t) - size_b, dp, l);
+        l = min(left, size_c - (offset1 - sizeof(gx_dc_serialized_pattern_tile_t) - size_b));
+        code = clist_get_data(ptile->cdev, 1, offset1 - sizeof(gx_dc_serialized_pattern_tile_t) - size_b, dp, l);
         if (code < 0)
             return code;
     }
@@ -2027,7 +2062,7 @@ gx_dc_pattern_read_raster(gx_color_tile
         size_c = ptile->tmask.data != NULL ? gs_object_size(mem, ptile->tmask.data) + sizeof(gx_strip_bitmap) : 0;
     }
     /* Read tbits : */
-    if (offset1 <= sizeof(gx_dc_serialized_tile_t) + sizeof(gx_strip_bitmap)) {
+    if (offset1 < sizeof(gx_dc_serialized_tile_t) + sizeof(gx_strip_bitmap)) {
         int l = min(sizeof(gx_strip_bitmap), left);
         byte *save = ptile->tbits.data;
 
@@ -2039,7 +2074,7 @@ gx_dc_pattern_read_raster(gx_color_tile
     }
     if (left == 0)
         return size;    /* we've consumed it all */
-    if (offset1 <= sizeof(gx_dc_serialized_tile_t) + size_b) {
+    if (offset1 < sizeof(gx_dc_serialized_tile_t) + size_b) {
         int l = min(sizeof(gx_dc_serialized_tile_t) + size_b - offset1, left);
 
         memcpy(ptile->tbits.data +
@@ -2051,7 +2086,7 @@ gx_dc_pattern_read_raster(gx_color_tile
     if (left == 0 || size_c == 0)
         return size - left;
     /* Read tmask : */
-    if (offset1 <= sizeof(gx_dc_serialized_tile_t) + size_b + sizeof(gx_strip_bitmap)) {
+    if (offset1 < sizeof(gx_dc_serialized_tile_t) + size_b + sizeof(gx_strip_bitmap)) {
         int l = min(sizeof(gx_dc_serialized_tile_t) + size_b + sizeof(gx_strip_bitmap) - offset1, left);
         byte *save = ptile->tmask.data;
 
@@ -2063,7 +2098,7 @@ gx_dc_pattern_read_raster(gx_color_tile
     }
     if (left == 0)
         return size;
-    if (offset1 <= sizeof(gx_dc_serialized_tile_t) + size_b + size_c) {
+    if (offset1 < sizeof(gx_dc_serialized_tile_t) + size_b + size_c) {
         int l = min(sizeof(gx_dc_serialized_tile_t) + size_b + size_c - offset1, left);
 
         memcpy(ptile->tmask.data +
@@ -2096,12 +2131,12 @@ gx_dc_pattern_read_trans_buff(gx_color_t
                 return_error(gs_error_VMerror);
     }
     /* Read transparency buffer */
-    if (offset1 <= sizeof(gx_dc_serialized_tile_t) + sizeof(tile_trans_clist_info_t) + data_size ) {
+    if (offset1 < sizeof(gx_dc_serialized_trans_tile_t) + sizeof(tile_trans_clist_info_t) + data_size ) {
 
-        int u = min(data_size, left);
+        int u = min(data_size - (offset1 - sizeof(gx_dc_serialized_trans_tile_t) - sizeof(tile_trans_clist_info_t)), left);
         byte *save = trans_pat->transbytes;
 
-        memcpy( trans_pat->transbytes + offset1 - sizeof(gx_dc_serialized_tile_t) -
+        memcpy( trans_pat->transbytes + offset1 - sizeof(gx_dc_serialized_trans_tile_t) -
                                     sizeof(tile_trans_clist_info_t), dp, u);
         trans_pat->transbytes = save;
         left -= u;
@@ -2118,9 +2153,11 @@ gx_dc_pattern_read(
     int64_t                    offset,
     const byte *            data,
     uint                    size,
-    gs_memory_t *           mem )
+    gs_memory_t *           mem,
+    int                     x0,
+    int                     y0)
 {
-    gx_dc_serialized_tile_t buf;
+    gx_dc_serialized_pattern_tile_t buf;
     int size_b, size_c = -1;
     const byte *dp = data;
     int left = size;
@@ -2130,6 +2167,7 @@ gx_dc_pattern_read(
     tile_trans_clist_info_t trans_info = { { { 0 } } };
     int cache_space_needed;
     bool deep = device_is_deep(dev);
+    size_t buf_read;
 
     if (offset == 0) {
         pdevc->mask.id = gx_no_bitmap_id;
@@ -2155,14 +2193,25 @@ gx_dc_pattern_read(
             /* For a while we require the client to provide enough buffer size. */
             return_error(gs_error_unregistered); /* Must not happen. */
         }
-        memcpy(&buf, dp, sizeof(buf));
-        dp += sizeof(buf);
-        left -= sizeof(buf);
-        offset1 += sizeof(buf);
+        memcpy(&buf, dp, sizeof(gx_dc_serialized_tile_t));
+        dp += sizeof(gx_dc_serialized_tile_t);
+        buf_read = sizeof(gx_dc_serialized_tile_t);
+        if (buf.flags & TILE_USES_TRANSP) {
+            memcpy(((char *)&buf)+sizeof(gx_dc_serialized_tile_t), dp, sizeof(gx_dc_serialized_trans_tile_t) - sizeof(gx_dc_serialized_tile_t));
+            dp += sizeof(gx_dc_serialized_trans_tile_t) - sizeof(gx_dc_serialized_tile_t);
+            buf_read = sizeof(gx_dc_serialized_trans_tile_t);
+        }
+        if (buf.flags & TILE_IS_CLIST) {
+            memcpy(((char *)&buf) + sizeof(gx_dc_serialized_trans_tile_t), dp, sizeof(gx_dc_serialized_pattern_tile_t) - sizeof(gx_dc_serialized_trans_tile_t));
+            dp += sizeof(gx_dc_serialized_pattern_tile_t) - sizeof(gx_dc_serialized_trans_tile_t);
+            buf_read = sizeof(gx_dc_serialized_pattern_tile_t);
+        }
+        left -= buf_read;
+        offset1 += buf_read;
 
         if ((buf.flags & TILE_USES_TRANSP) && !(buf.flags & TILE_IS_CLIST)){
 
-            if (sizeof(buf) + sizeof(tile_trans_clist_info_t) > size) {
+            if (buf_read + sizeof(tile_trans_clist_info_t) > size) {
                 return_error(gs_error_unregistered); /* Must not happen. */
             }
 
@@ -2243,13 +2292,13 @@ gx_dc_pattern_read(
                 code = gx_dc_pattern_read_trans_buff(ptile, offset1, dp, left, mem);
                 if (code < 0)
                     return code;
-                return code + sizeof(buf)+sizeof(trans_info);
+                return code + buf_read + sizeof(trans_info);
 
             } else {
-                code = gx_dc_pattern_read_raster(ptile, &buf, offset1, dp, left, mem);
+                code = gx_dc_pattern_read_raster(ptile, (gx_dc_serialized_tile_t *)&buf, offset1, dp, left, mem);
                 if (code < 0)
                     return code;
-                return code + sizeof(buf);
+                return code + buf_read;
             }
 
         }
@@ -2292,7 +2341,7 @@ gx_dc_pattern_read(
 
         size_b = ptile->tbits.size.x;
     }
-    if (offset1 <= sizeof(buf) + size_b) {
+    if (offset1 < sizeof(buf) + size_b) {
         l = min(left, size_b - (offset1 - sizeof(buf)));
         code = clist_put_data(ptile->cdev, 0, offset1 - sizeof(buf), dp, l);
         if (code < 0)
diff -pruN 9.55.0~dfsg-3/base/gsptype2.c 9.56.1~dfsg-1/base/gsptype2.c
--- 9.55.0~dfsg-3/base/gsptype2.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsptype2.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -222,7 +222,7 @@ gs_pattern2_set_color(const gs_client_co
 
 /* Fill a rectangle with a PatternType 2 color. */
 /* WARNING: This function doesn't account the shading BBox
-   to allow the clipent to optimize the clipping
+   to allow the client to optimize the clipping
    with changing the order of clip paths and rects.
    The client must clip with the shading BBox before calling this function. */
 static int
diff -pruN 9.55.0~dfsg-3/base/gsrefct.h 9.56.1~dfsg-1/base/gsrefct.h
--- 9.55.0~dfsg-3/base/gsrefct.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsrefct.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -116,6 +116,7 @@ rc_free_proc(rc_free_struct_only);
   BEGIN\
     (vp)->rc.ref_count++;\
     IF_RC_DEBUG(rc_trace_increment(vp, &(vp)->rc));\
+    (void)Memento_adjustRef((vp), 1);\
   END
 #define rc_increment(vp)\
   BEGIN\
diff -pruN 9.55.0~dfsg-3/base/gsstate.c 9.56.1~dfsg-1/base/gsstate.c
--- 9.55.0~dfsg-3/base/gsstate.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gsstate.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -1290,12 +1290,12 @@ gstate_alloc(gs_memory_t * mem, client_n
     gs_gstate *pgs =
         gs_alloc_struct(mem, gs_gstate, &st_gs_gstate, cname);
 
-    if (pgs == 0)
-        return 0;
+    if (pgs == NULL)
+        return NULL;
     memset(pgs, 0x00, sizeof(gs_gstate));
     if (gstate_alloc_parts(pgs, pfrom, mem, cname) < 0) {
         gs_free_object(mem, pgs, cname);
-        return 0;
+        return NULL;
     }
     pgs->memory = mem;
     return pgs;
@@ -1333,7 +1333,7 @@ gstate_clone_core(const gs_gstate
         if (pdata == NULL ||
             gstate_copy_client_data(pfrom, pdata, pfrom->client_data,
                                     reason) < 0)
-            goto fail;
+            goto failEarly;
     }
     /* Copy the dash and dash pattern if necessary. */
     clone_data->dash = gs_currentlineparams_inline(pfrom)->dash;
@@ -1368,9 +1368,10 @@ gstate_clone_core(const gs_gstate
     return pgs;
 
   fail:
+    gs_free_object(mem, clone_data->dash.pattern, cname);
     if (pdata != NULL)
         (*pfrom->client_procs.free) (pdata, mem, pgs);
-    gs_free_object(mem, clone_data->dash.pattern, cname);
+  failEarly:
     gstate_free_parts(pgs, mem, cname);
     gs_free_object(mem, pgs, cname);
 
diff -pruN 9.55.0~dfsg-3/base/gstext.c 9.56.1~dfsg-1/base/gstext.c
--- 9.55.0~dfsg-3/base/gstext.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gstext.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -533,7 +533,7 @@ gs_xyshow_begin(gs_gstate * pgs, const b
                 const float *x_widths, const float *y_widths,
                 uint widths_size, gs_memory_t * mem, gs_text_enum_t ** ppte)
 {
-    gs_text_params_t text;
+    gs_text_params_t text = {0};
     uint widths_needed;
     int code;
 
diff -pruN 9.55.0~dfsg-3/base/gstrans.c 9.56.1~dfsg-1/base/gstrans.c
--- 9.55.0~dfsg-3/base/gstrans.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gstrans.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -31,6 +31,7 @@
 #include "gxclist.h"
 #include "gsicc_manage.h"
 #include "gsicc_cache.h"
+#include "gxdevsop.h"
 
 /* ------ Transparency-related graphics state elements ------ */
 
@@ -789,6 +790,7 @@ gs_push_pdf14trans_device(gs_gstate * pg
     gsicc_rendering_param_t render_cond;
     int code;
     cmm_dev_profile_t *dev_profile;
+    unsigned char pattern_opsim_setting[2];
 
     code = dev_proc(pgs->device, get_profile)(pgs->device,  &dev_profile);
     if (code < 0)
@@ -805,10 +807,18 @@ gs_push_pdf14trans_device(gs_gstate * pg
     params.num_spot_colors = get_num_pdf14_spot_colors(pgs);
     params.is_pattern = is_pattern;
 
-    /* Information related to overprint simulation */
-    params.num_spot_colors_int = spot_color_count;
-    if (depth < 0)
-        params.overprint_sim_push = true;
+    /* If pattern, get overprint simulation information from
+       the pattern accumulators target device */
+    if (is_pattern && dev_proc(pgs->device, dev_spec_op)(pgs->device, gxdso_overprintsim_state, &pattern_opsim_setting, sizeof(pattern_opsim_setting))) {
+        /* Use the target device setting */
+        params.overprint_sim_push = pattern_opsim_setting[0];
+        params.num_spot_colors_int = pattern_opsim_setting[1];
+    } else {
+        /* Use information from interpreter */
+        params.num_spot_colors_int = spot_color_count;
+        if (depth < 0)
+            params.overprint_sim_push = true;
+    }
 
     /* If we have an NCLR ICC profile, the extra spot colorants do not
        get included in the transparency buffers. This is also true
diff -pruN 9.55.0~dfsg-3/base/gxacpath.c 9.56.1~dfsg-1/base/gxacpath.c
--- 9.55.0~dfsg-3/base/gxacpath.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxacpath.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -256,10 +256,10 @@ accum_open_device(register gx_device * d
     gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
 
     gx_clip_list_init(&adev->list);
-    adev->bbox.p.x = adev->bbox.p.y = max_int;
-    adev->bbox.q.x = adev->bbox.q.y = min_int;
-    adev->clip_box.p.x = adev->clip_box.p.y = min_int;
-    adev->clip_box.q.x = adev->clip_box.q.y = max_int;
+    adev->bbox.p.x = adev->bbox.p.y = fixed2int(max_fixed);
+    adev->bbox.q.x = adev->bbox.q.y = fixed2int(min_fixed);
+    adev->clip_box.p.x = adev->clip_box.p.y = fixed2int(min_fixed);
+    adev->clip_box.q.x = adev->clip_box.q.y = fixed2int(max_fixed);
     return 0;
 }
 
diff -pruN 9.55.0~dfsg-3/base/gxbcache.c 9.56.1~dfsg-1/base/gxbcache.c
--- 9.55.0~dfsg-3/base/gxbcache.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxbcache.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -146,3 +146,35 @@ gx_bits_cache_free(gx_bits_cache * bc, g
     cbh->size = size;		/* gs_alloc_fill may have overwritten */
     cb_head_set_free(cbh);
 }
+
+#ifdef DEBUG
+/* A useful bit of code to dump the contents of the bitmap cache. Not
+ * currently called. Current position is indicated with a '>'. */
+void
+gx_bits_cache_dump(gx_bits_cache * bc)
+{
+    gx_bits_cache_chunk *bck = bc->chunks;
+    gx_bits_cache_chunk *first = bck;
+
+    dlprintf2("%d entries making %d bytes\n", bc->csize, bc->bsize);
+
+    do {
+        gx_cached_bits_head *cbh;
+        uint pos = 0;
+
+        dlprintf2(" chunk of %d bytes (%d allocated)\n", bck->size, bck->allocated);
+
+        cbh = (gx_cached_bits_head *)bck->data;
+        while (pos != bck->size) {
+            dlprintf3(" %csize=%d depth=%d\n",
+                      pos == bc->cnext && bck == bc->chunks ? '>' : ' ',
+                      cbh->size, cbh->depth);
+            pos += cbh->size;
+            cbh = (gx_cached_bits_head *)(((byte *)cbh) + cbh->size);
+        }
+        bck = bck->next;
+    } while (bck != first);
+
+    bck=bck;
+}
+#endif
diff -pruN 9.55.0~dfsg-3/base/gxbcache.h 9.56.1~dfsg-1/base/gxbcache.h
--- 9.55.0~dfsg-3/base/gxbcache.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxbcache.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -47,8 +47,6 @@ typedef struct gx_cached_bits_head_s {
         ushort width, height, shift;\
         ushort raster;\
         gx_bitmap_id id
-/* Define aliases for head members. */
-#define cb_depth head.depth
 /* Define aliases for common members formerly in the head. */
 #define cb_raster raster
 typedef struct gx_cached_bits_s {
@@ -118,4 +116,9 @@ void gx_bits_cache_shorten(gx_bits_cache
 void gx_bits_cache_free(gx_bits_cache *, gx_cached_bits_head *,
                         gx_bits_cache_chunk *);
 
+#ifdef DEBUG
+/* Debug only function to print the contents of the cache. */
+void gx_bits_cache_dump(gx_bits_cache * bc);
+#endif
+
 #endif /* gxbcache_INCLUDED */
diff -pruN 9.55.0~dfsg-3/base/gxblend1.c 9.56.1~dfsg-1/base/gxblend1.c
--- 9.55.0~dfsg-3/base/gxblend1.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxblend1.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -242,7 +242,7 @@ pdf14_preserve_backdrop_cm(pdf14_buf *bu
         rendering_params.graphics_type_tag = GS_IMAGE_TAG;
         rendering_params.override_icc = false;
         rendering_params.preserve_black = gsBKPRESNOTSPECIFIED;
-        rendering_params.rendering_intent = gsPERCEPTUAL;
+        rendering_params.rendering_intent = gsRELATIVECOLORIMETRIC;
         rendering_params.cmm = gsCMM_DEFAULT;
         /* Request the ICC link for the transform that we will need to use */
         icc_link = gsicc_get_link_profile(pgs, dev, tos_profile, group_profile,
@@ -682,7 +682,7 @@ dump_planar_rgba(gs_memory_t *mem, const
     info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
 
     /* add comment */
-    gs_sprintf(software_text, "%s %d.%02d", gs_product,
+    gs_snprintf(software_text, sizeof(software_text), "%s %d.%02d", gs_product,
             (int)(gs_revision / 100), (int)(gs_revision % 100));
     text_png.compression = -1;	/* uncompressed */
     text_png.key = (char *)software_key;	/* not const, unfortunately */
diff -pruN 9.55.0~dfsg-3/base/gxblend.c 9.56.1~dfsg-1/base/gxblend.c
--- 9.55.0~dfsg-3/base/gxblend.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxblend.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -1345,10 +1345,8 @@ art_blend_pixel_8_inline(byte *gs_restri
                 break;
             }
         default:
-#ifndef GS_THREADSAFE
             dlprintf1("art_blend_pixel_8: blend mode %d not implemented\n",
                       blend_mode);
-#endif
             memcpy(dst, src, n_chan);
             break;
     }
@@ -1556,10 +1554,8 @@ art_blend_pixel_16_inline(uint16_t *gs_r
                 break;
             }
         default:
-#ifndef GS_THREADSAFE
             dlprintf1("art_blend_pixel_16: blend mode %d not implemented\n",
                       blend_mode);
-#endif
             memcpy(dst, src, n_chan*2);
             break;
     }
@@ -2565,6 +2561,12 @@ art_pdf_composite_knockout_8(byte *gs_re
         byte blend[ART_MAX_CHAN];
         byte a_b, a_s;
         unsigned int a_r;
+        /* Using a volatile variable set to 0 here works around
+           a gcc (10.3.0) compiler optimiser bug that appears to
+           drop the "if (a_r != 0)" test below, meaning we can end
+           up dividing by a_r when it is zero.
+         */
+        volatile const unsigned int vzero = 0;
         int src_scale;
         int c_b, c_s;
 
@@ -2575,7 +2577,7 @@ art_pdf_composite_knockout_8(byte *gs_re
         tmp = (0xff - a_b) * (0xff - a_s) + 0x80;
         a_r = 0xff - (((tmp >> 8) + tmp) >> 8);
 
-        if (a_r != 0) {
+        if (a_r != vzero) {
             /* Compute a_s / a_r in 16.16 format */
             src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
 
@@ -2646,6 +2648,12 @@ art_pdf_composite_knockout_16(uint16_t *
         uint16_t blend[ART_MAX_CHAN];
         uint16_t a_b, a_s;
         unsigned int a_r;
+        /* Using a volatile variable set to 0 here works around
+           a gcc (10.3.0) compiler optimiser bug that appears to
+           drop the "if (a_r != 0)" test below, meaning we can end
+           up dividing by a_r when it is zero.
+         */
+        volatile const unsigned int vzero = 0;
         int src_scale;
         int c_b, c_s;
 
@@ -2656,7 +2664,7 @@ art_pdf_composite_knockout_16(uint16_t *
         tmp = (0xffff - a_b) * (0xffff - a_s) + 0x8000;
         a_r = 0xffff - (((tmp >> 16) + tmp) >> 16);
 
-        if (a_r != 0) {
+        if (a_r != vzero) {
             /* Compute a_s / a_r in 16.16 format */
             src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
 
@@ -2706,7 +2714,7 @@ do_dump_raw_buffer(const gs_memory_t *me
     if ((n_chan == 2) || (n_chan == 3)) {
         int x;
         dlprintf2("%02d)%s.pam\n",global_index,filename);dflush();
-        gs_sprintf(full_file_name,"%02d)%s.pam",global_index,filename);
+        gs_snprintf(full_file_name,sizeof(full_file_name),"%02d)%s.pam",global_index,filename);
         fid = gp_fopen(mem,full_file_name,"wb");
         fprintf(fid, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 2\nMAXVAL %d\nTUPLTYPE GRAYSCALE_ALPHA\nENDHDR\n",
                 width, num_rows, deep ? 65535 : 255);
@@ -2727,7 +2735,7 @@ do_dump_raw_buffer(const gs_memory_t *me
         gp_fclose(fid);
         if (n_chan == 3) {
             dlprintf2("%02d)%s_shape.pgm\n",global_index,filename);dflush();
-            gs_sprintf(full_file_name,"%02d)%s_shape.pgm",global_index,filename);
+            gs_snprintf(full_file_name,sizeof(full_file_name),"%02d)%s_shape.pgm",global_index,filename);
             fid = gp_fopen(mem,full_file_name,"wb");
             fprintf(fid, "P5\n%d %d %d\n",
                     width, num_rows, deep ? 65535 : 255);
@@ -2749,7 +2757,7 @@ do_dump_raw_buffer(const gs_memory_t *me
     if ((n_chan == 4) || (n_chan == 5) || (n_chan == 6)) {
         int x;
         dprintf2("%02d)%s.pam\n",global_index,filename);dflush();
-        gs_sprintf(full_file_name,"%02d)%s.pam",global_index,filename);
+        gs_snprintf(full_file_name,sizeof(full_file_name),"%02d)%s.pam",global_index,filename);
         fid = gp_fopen(mem,full_file_name,"wb");
         fprintf(fid, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL %d\nTUPLTYPE RGB_ALPHA\nENDHDR\n",
                 width, num_rows, deep ? 65535 : 255);
@@ -2769,7 +2777,7 @@ do_dump_raw_buffer(const gs_memory_t *me
         }
         gp_fclose(fid);
         if (n_chan > 4) {
-            gs_sprintf(full_file_name,"%02d)%s_shape.pgm",global_index,filename);
+            gs_snprintf(full_file_name,sizeof(full_file_name),"%02d)%s_shape.pgm",global_index,filename);
             fid = gp_fopen(mem,full_file_name,"wb");
             fprintf(fid, "P5\n%d %d %d\n",
                     width, num_rows, deep ? 65535 : 255);
@@ -2788,7 +2796,7 @@ do_dump_raw_buffer(const gs_memory_t *me
             gp_fclose(fid);
         }
         if (n_chan == 6) {
-            gs_sprintf(full_file_name,"%02d)%s_tags.pgm",global_index,filename);
+            gs_snprintf(full_file_name,sizeof(full_file_name),"%02d)%s_tags.pgm",global_index,filename);
             fid = gp_fopen(mem, full_file_name,"wb");
             fprintf(fid, "P5\n%d %d 255\n", width, num_rows);
             if (deep) {
@@ -2807,7 +2815,7 @@ do_dump_raw_buffer(const gs_memory_t *me
 #endif
     max_bands = ( n_chan < 57 ? n_chan : 56);   /* Photoshop handles at most 56 bands */
     dlprintf6("%02d)%s_%dx%dx%dx%d.raw\n",global_index,filename,width,num_rows,deep ? 16 : 8,max_bands);dflush();
-    gs_sprintf(full_file_name,"%02d)%s_%dx%dx%dx%d.raw",global_index,filename,width,num_rows,deep ? 16 : 8,max_bands);
+    gs_snprintf(full_file_name,sizeof(full_file_name),"%02d)%s_%dx%dx%dx%d.raw",global_index,filename,width,num_rows,deep ? 16 : 8,max_bands);
     fid = gp_fopen(mem, full_file_name,"wb");
 
     if (be && deep) {
@@ -5131,6 +5139,12 @@ do_mark_fill_rectangle(gx_device * dev,
     int first_blend_spot = num_comp;
     pdf14_mark_fill_rect_fn fn;
 
+    /* If we are going out to a CMYK or CMYK + spots pdf14 device (i.e.
+       subtractive) and we are doing overprint with drawn_comps == 0
+       then this is a no-operation */
+    if (overprint && drawn_comps == 0 && !buf->group_color_info->isadditive)
+        return 0;
+
     /* This is a fix to handle the odd case where overprint is active
        but drawn comps is zero due to the colorants that are present
        in the sep or devicen color space.  For example, if the color
@@ -5767,6 +5781,12 @@ do_mark_fill_rectangle16(gx_device * dev
     int first_blend_spot = num_comp;
     pdf14_mark_fill_rect16_fn fn;
 
+   /* If we are going out to a CMYK or CMYK + spots pdf14 device (i.e.
+   subtractive) and we are doing overprint with drawn_comps == 0
+   then this is a no-operation */
+    if (overprint && drawn_comps == 0 && !buf->group_color_info->isadditive)
+        return 0;
+
   /* This is a fix to handle the odd case where overprint is active
    but drawn comps is zero due to the colorants that are present
    in the sep or devicen color space.  For example, if the color
diff -pruN 9.55.0~dfsg-3/base/gxccman.c 9.56.1~dfsg-1/base/gxccman.c
--- 9.55.0~dfsg-3/base/gxccman.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxccman.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -473,14 +473,10 @@ gs_purge_fm_pair(gs_font_dir * dir, cach
  * (multiplication by 1 << pscale->{x,y}.)
  * The depth parameter is the final number of alpha bits;
  * depth <= x scale * y scale.
- * If dev == NULL, this is an xfont-only entry.
- * If dev != NULL, set up the memory device(s); in this case, if dev2 is
- * not NULL, dev should be an alpha-buffer device with dev2 (an alpha
- * device) as target.
  */
 int
-gx_alloc_char_bits(gs_font_dir * dir, gx_device_memory * dev,
-                   gx_device_memory * dev2, ushort iwidth, ushort iheight,
+gx_alloc_char_bits(gs_font_dir * dir, gx_device_memory * pdev,
+                   ushort iwidth, ushort iheight,
                    const gs_log2_scale_point * pscale, int depth, cached_char **pcc)
 {
     int log2_xscale = pscale->x;
@@ -493,23 +489,12 @@ gx_alloc_char_bits(gs_font_dir * dir, gx
 #endif
     uint iraster;
     cached_char *cc;
-    gx_device_memory mdev;
-    gx_device_memory *pdev = dev;
-    gx_device_memory *pdev2;
     float HWResolution0 = 72, HWResolution1 = 72;  /* default for dev == NULL */
     int code;
 
     *pcc = 0;
-    if (dev == NULL) {
-        mdev.memory = 0;
-        mdev.target = 0;
-        pdev = &mdev;
-    } else {
-        HWResolution0 = dev->HWResolution[0];
-        HWResolution1 = dev->HWResolution[1];
-    }
-
-    pdev2 = (dev2 == 0 ? pdev : dev2);
+    HWResolution0 = pdev->HWResolution[0];
+    HWResolution1 = pdev->HWResolution[1];
 
     /* Compute the scaled-down bitmap size, and test against */
     /* the maximum cachable character size. */
@@ -522,7 +507,7 @@ gx_alloc_char_bits(gs_font_dir * dir, gx
         return 0;		/* too big */
     }
     /* Compute the actual bitmap size(s) and allocate the bits. */
-    if (dev2 == 0) {
+    {
         /*
          * Render to a full (possibly oversampled) bitmap; compress
          * (if needed) when done.
@@ -545,21 +530,6 @@ gx_alloc_char_bits(gs_font_dir * dir, gx
         gdev_mem_bitmap_size(pdev, &isize);	/* Assume less than max_ulong */
         pdev->HWResolution[0] = HWResolution0;
         pdev->HWResolution[1] = HWResolution1;
-    } else {
-        /* We reckon this code has never actually been run since 2002,
-         * as the conditions set up in gx_compute_text_oversampling
-         * preclude this function ever being called in a way that
-         * will cause this else clause to be executed. */
-        static int THIS_NEVER_HAPPENS = 0;
-
-        if (THIS_NEVER_HAPPENS == 0) {
-            /* Just put the warning out once */
-            dmlprintf(dev2->memory,
-                      "Unexpected code path in gx_alloc_char_bits taken!\n"
-                      "Please contact the Ghostscript developers.\n");
-            THIS_NEVER_HAPPENS = 1;
-        }
-        return_error(gs_error_unknownerror);
     }
     icdsize = isize + sizeof_cached_char;
     code = alloc_char(dir, icdsize, &cc);
@@ -568,7 +538,7 @@ gx_alloc_char_bits(gs_font_dir * dir, gx
     *pcc = cc;
     if (cc == 0)
         return 0;
-    if_debug4m('k', dev->memory, "[k]adding char "PRI_INTPTR":%u(%u,%u)\n",
+    if_debug4m('k', pdev->memory, "[k]adding char "PRI_INTPTR":%u(%u,%u)\n",
                (intptr_t)cc, (uint)icdsize, iwidth, iheight);
 
     /* Fill in the entry. */
@@ -578,35 +548,17 @@ gx_alloc_char_bits(gs_font_dir * dir, gx
     /* Set the width and height to those of the device. */
     /* Note that if we are oversampling without an alpha buffer. */
     /* these are not the final unscaled dimensions. */
-    cc->width = pdev2->width;
-    cc->height = pdev2->height;
+    cc->width = pdev->width;
+    cc->height = pdev->height;
     cc->shift = 0;
-    cc_set_raster(cc, gdev_mem_raster(pdev2));
+    cc_set_raster(cc, gdev_mem_raster(pdev));
     cc_set_pair_only(cc, 0);	/* not linked in yet */
     cc->id = gx_no_bitmap_id;
     cc->subpix_origin.x = cc->subpix_origin.y = 0;
     cc->linked = false;
 
     /* Open the cache device(s). */
-
-#ifndef ENABLE_IMPOSSIBLE_ALPHA_CODE
-    if (dev2) {			/* The second device is an alpha device that targets */
-        /* the real storage for the character. */
-        byte *bits = cc_bits(cc);
-        ulong bsize;
-
-        gdev_mem_bitmap_size(dev2, &bsize);
-        memset(bits, 0, bsize);
-        dev2->base = bits;
-        (*dev_proc(dev2, open_device)) ((gx_device *) dev2);
-        dev->base = bits + bsize;
-        (*dev_proc(dev, open_device)) ((gx_device *) dev);
-    } else if (dev)
-        gx_open_cache_device(dev, cc);
-#else /* ENABLE_IMPOSSIBLE_ALPHA_CODE */
-    if (dev)
-        gx_open_cache_device(dev, cc);
-#endif /* ENABLE_IMPOSSIBLE_ALPHA_CODE */
+    gx_open_cache_device(pdev, cc);
 
     return 0;
 }
diff -pruN 9.55.0~dfsg-3/base/gxchar.c 9.56.1~dfsg-1/base/gxchar.c
--- 9.55.0~dfsg-3/base/gxchar.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxchar.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -153,8 +153,10 @@ gx_default_text_begin(gx_device * dev, g
         penum->can_cache = 1; break;
     }
     code = show_state_setup(penum);
-    if (code < 0)
+    if (code < 0) {
+        gs_free_object(mem, penum, "gx_default_text_begin");
         return code;
+    }
     penum->show_gstate =
         (propagate_charpath && (pgs->in_charpath != 0) ?
          pgs->show_gstate : pgs);
@@ -576,18 +578,8 @@ set_cache_device(gs_show_enum * penum, g
             if (code < 0)
                 return code;
         }
-        /*
-         * If we're oversampling (i.e., the temporary bitmap is
-         * larger than the final monobit or alpha array) and the
-         * temporary bitmap is large, use incremental conversion
-         * from oversampled bitmap strips to alpha values instead of
-         * full oversampling with compression at the end.
-         */
         code = gx_alloc_char_bits(dir, penum->dev_cache,
-                                (iwidth > MAX_CCACHE_TEMP_BITMAP_BITS / iheight &&
-                                 log2_scale.x + log2_scale.y > alpha_bits ?
-                                 penum->dev_cache2 : NULL),
-                                iwidth, iheight, &log2_scale, depth, &cc);
+                                  iwidth, iheight, &log2_scale, depth, &cc);
         if (code < 0)
             return code;
 
@@ -1552,8 +1544,8 @@ show_cache_setup(gs_show_enum * penum)
          * The structure is full of garbage so must not call the
          * finalize method but still need to free the structure
          */
-        gs_set_object_type(mem, dev2, NULL);
-        gs_set_object_type(mem, dev, NULL);
+        gs_set_object_type(mem, dev2, &st_bytes);
+        gs_set_object_type(mem, dev, &st_bytes);
         gs_free_object(mem, dev2, "show_cache_setup(dev_cache2)");
         gs_free_object(mem, dev, "show_cache_setup(dev_cache)");
         return_error(gs_error_VMerror);
diff -pruN 9.55.0~dfsg-3/base/gxchar.h 9.56.1~dfsg-1/base/gxchar.h
--- 9.55.0~dfsg-3/base/gxchar.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxchar.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -36,7 +36,7 @@ struct gs_show_enum_s {
 /* Get the current character code. */
 int gx_current_char(const gs_text_enum_t * pte);
 
-int  gx_alloc_char_bits(gs_font_dir *, gx_device_memory *, gx_device_memory *, ushort, ushort, const gs_log2_scale_point *, int, cached_char **);
+int  gx_alloc_char_bits(gs_font_dir *, gx_device_memory *, ushort, ushort, const gs_log2_scale_point *, int, cached_char **);
 void gx_open_cache_device(gx_device_memory *, cached_char *);
 void gx_free_cached_char(gs_font_dir *, cached_char *);
 int  gx_add_cached_char(gs_font_dir *, gx_device_memory *, cached_char *, cached_fm_pair *, const gs_log2_scale_point *);
diff -pruN 9.55.0~dfsg-3/base/gxcht.c 9.56.1~dfsg-1/base/gxcht.c
--- 9.55.0~dfsg-3/base/gxcht.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxcht.c	2022-04-04 13:46:22.000000000 +0000
@@ -129,8 +129,12 @@ gx_dc_ht_colored_equal(const gx_device_c
  * The halftone is never transmitted as part of a device color, so there
  * is no flag for it.
  */
-static const int   dc_ht_colored_has_base = 0x01;
-static const int   dc_ht_colored_has_level = 0x02;
+enum {
+   dc_ht_colored_has_base = 0x01,
+   dc_ht_colored_has_level = 0x02,
+   dc_ht_colored_has_phase_x = 0x04,
+   dc_ht_colored_has_phase_y = 0x08,
+};
 
 /*
  * Serialize a device color that uses a traditional colored halftone.
@@ -219,7 +223,7 @@ gx_dc_ht_colored_write(
     }
 
     plane_mask = pdevc->colors.colored.plane_mask;
-    if ( psdc == 0                                                          ||
+    if ( psdc == NULL                                                       ||
          memcmp( pdevc->colors.colored.c_level,
                  psdc->colors.colored.c_level,
                  num_comps * sizeof(pdevc->colors.colored.c_level[0]) ) != 0  ) {
@@ -243,6 +247,11 @@ gx_dc_ht_colored_write(
         }
     }
 
+    if (psdc == NULL || psdc->phase.x != pdevc->phase.x)
+        flag_bits |= dc_ht_colored_has_phase_x, req_size += enc_u_sizew(pdevc->phase.x);
+    if (psdc == NULL || psdc->phase.y != pdevc->phase.y)
+        flag_bits |= dc_ht_colored_has_phase_y, req_size += enc_u_sizew(pdevc->phase.y);
+
     /* see if there is anything to do */
     if (flag_bits == 0) {
         *psize = 0;
@@ -299,6 +308,13 @@ gx_dc_ht_colored_write(
         }
     }
 
+    if ((flag_bits & dc_ht_colored_has_phase_x) != 0) {
+        enc_u_putw(pdevc->phase.x, pdata);
+    }
+    if ((flag_bits & dc_ht_colored_has_phase_x) != 0) {
+        enc_u_putw(pdevc->phase.y, pdata);
+    }
+
     *psize = pdata - pdata0;
     return 0;
 }
@@ -336,13 +352,15 @@ gx_dc_ht_colored_write(
 static int
 gx_dc_ht_colored_read(
     gx_device_color *       pdevc,
-    const gs_gstate * pgs,
+    const gs_gstate *       pgs,
     const gx_device_color * prior_devc,
     const gx_device *       dev,
-    int64_t		    offset,
+    int64_t                 offset,
     const byte *            pdata,
     uint                    size,
-    gs_memory_t *           mem )       /* ignored */
+    gs_memory_t *           mem,        /* ignored */
+    int                     x0,
+    int                     y0)
 {
     gx_device_color         devc;
     int                     num_comps = dev->color_info.num_components;
@@ -430,12 +448,14 @@ gx_dc_ht_colored_read(
         size -= pdata - pdata_start;
     }
 
-    /* set the phase as required (select value is arbitrary) */
-    color_set_phase_mod( &devc,
-                         pgs->screen_phase[0].x,
-                         pgs->screen_phase[0].y,
-                         pgs->dev_ht[HT_OBJTYPE_DEFAULT]->lcm_width,
-                         pgs->dev_ht[HT_OBJTYPE_DEFAULT]->lcm_height );
+    if ((flag_bits & dc_ht_colored_has_phase_x) != 0) {
+        enc_u_getw(devc.phase.x, pdata);
+        devc.phase.x += x0;
+    }
+    if ((flag_bits & dc_ht_colored_has_phase_y) != 0) {
+        enc_u_getw(devc.phase.y, pdata);
+        devc.phase.y += y0;
+    }
 
     /* everything looks OK */
     *pdevc = devc;
diff -pruN 9.55.0~dfsg-3/base/gxclbits.c 9.56.1~dfsg-1/base/gxclbits.c
--- 9.55.0~dfsg-3/base/gxclbits.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclbits.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -486,21 +486,19 @@ clist_add_tile(gx_device_clist_writer *
     uint size_bytes = raster * tiles->size.y * tiles->num_planes;
     uint tsize =
     sizeof(tile_slot) + cldev->tile_band_mask_size + size_bytes;
-    gx_cached_bits_head *slot_head;
-
-#define slot ((tile_slot *)slot_head)
+    tile_slot *slot;
 
     if (cldev->bits.csize == cldev->tile_max_count) {	/* Don't let the hash table get too full: delete an entry. */
         /* Since gx_bits_cache_alloc returns an entry to delete when */
         /* it fails, just force it to fail. */
         gx_bits_cache_alloc(&cldev->bits, (ulong) cldev->cache_chunk->size,
-                            &slot_head);
-        if (slot_head == 0) {	/* Wrap around and retry. */
+                            (gx_cached_bits_head **)&slot);
+        if (slot == NULL) {	/* Wrap around and retry. */
             cldev->bits.cnext = 0;
             gx_bits_cache_alloc(&cldev->bits, (ulong) cldev->cache_chunk->size,
-                                &slot_head);
+                                (gx_cached_bits_head **)&slot);
 #ifdef DEBUG
-            if (slot_head == 0) {
+            if (slot == NULL) {
                 lprintf("No entry to delete!\n");
                 return_error(gs_error_Fatal);
             }
@@ -509,8 +507,8 @@ clist_add_tile(gx_device_clist_writer *
         clist_delete_tile(cldev, slot);
     }
     /* Allocate the space for the new entry, deleting entries as needed. */
-    while (gx_bits_cache_alloc(&cldev->bits, (ulong) tsize, &slot_head) < 0) {
-        if (slot_head == 0) {	/* Wrap around. */
+    while (gx_bits_cache_alloc(&cldev->bits, (ulong) tsize, (gx_cached_bits_head **)&slot) < 0) {
+        if (slot == NULL) {	/* Wrap around. */
             if (cldev->bits.cnext == 0) {	/* Too big to fit.  We should probably detect this */
                 /* sooner, since if we get here, we've cleared the */
                 /* cache. */
@@ -521,8 +519,8 @@ clist_add_tile(gx_device_clist_writer *
             clist_delete_tile(cldev, slot);
     }
     /* Fill in the entry. */
-    slot->cb_depth = depth;
-    slot->cb_raster = raster;
+    slot->head.depth = depth;
+    slot->raster = raster;
     slot->width = tiles->rep_width;
     slot->height = tiles->rep_height;
     slot->shift = slot->rep_shift = tiles->rep_shift;
@@ -549,7 +547,7 @@ clist_add_tile(gx_device_clist_writer *
 #endif
         slot->index = loc.index;
         cldev->tile_table[loc.index].offset =
-            (byte *) slot_head - cldev->data;
+            (byte *) slot - cldev->data;
         if_debug2m('L', cldev->memory, "[L]adding index=%u, offset=%lu\n",
                    loc.index, cldev->tile_table[loc.index].offset);
     }
@@ -701,7 +699,7 @@ clist_change_tile(gx_device_clist_writer
                 code = cmd_put_bits(cldev, pcls, ts_bits(cldev, loc.tile),
                                     tiles->rep_width * pdepth,
                                     tiles->rep_height * tiles->num_planes,
-                                    loc.tile->cb_raster, rsize,
+                                    loc.tile->raster, rsize,
                                     allow_large_bitmap |
                                         (cldev->tile_params.size.x > tiles->rep_width ?
                                              decompress_elsewhere | decompress_spread :
@@ -711,6 +709,8 @@ clist_change_tile(gx_device_clist_writer
                 if (code < 0)
                     return code;
                 if (extra) {	/* Write the tile parameters before writing the bits. */
+                    if_debug1m('L', cldev->memory,
+                               "[L] fake end_run: really set_tile_size[%d]\n", extra);
                     cmd_store_tile_params(dp, &cldev->tile_params, depth,
                                           extra, for_pattern, cldev->memory);
                     dp += extra;
@@ -721,6 +721,8 @@ clist_change_tile(gx_device_clist_writer
                     if (band_index > cldev->tile_known_max)
                         cldev->tile_known_max = band_index;
                 }
+                if_debug1m('L', cldev->memory,
+                           "[L] fake end_run: really set_tile_bits[%d]\n", csize-extra);
                 *dp = cmd_count_op(cmd_opv_set_tile_bits, csize - extra, cldev->memory);
                 dp++;
                 dp = cmd_put_w(loc.index, dp);
@@ -762,68 +764,78 @@ clist_change_bits(gx_device_clist_writer
 {
     tile_loc loc;
     int code;
+    uint band_index = pcls - cldev->states;
+    byte bmask = 1 << (band_index & 7);
+    byte *bptr;
+
+    while (!clist_find_bits(cldev, tiles->id, &loc)) {
+        /* The tile is not in the cache. */
+        code = clist_add_tile(cldev, tiles, tiles->raster, depth);
+        if (code < 0)
+            return code;
+    }
 
-   top:if (clist_find_bits(cldev, tiles->id, &loc)) {	/* The bitmap is in the cache.  Check whether this band */
-        /* knows about it. */
-        uint band_index = pcls - cldev->states;
-        byte *bptr = ts_mask(loc.tile) + (band_index >> 3);
-        byte bmask = 1 << (band_index & 7);
-
-        if (*bptr & bmask) {	/* Already known.  Just set the index. */
-            if (pcls->tile_index == loc.index)
-                return 0;
-            cmd_put_tile_index(cldev, pcls, loc.index);
-        } else {		/* Not known yet.  Output the bits. */
-            /* Note that the offset we write is the one used by */
-            /* the reading phase, not the writing phase. */
-            ulong offset = (byte *) loc.tile - cldev->cache_chunk->data;
-            uint rsize = 2 + cmd_size_w(loc.tile->width) +
-            cmd_size_w(loc.tile->height) + cmd_size_w(loc.index) +
-            cmd_size_w(offset);
-            byte *dp;
-            uint csize;
-            gx_clist_state *bit_pcls = pcls;
-            int code;
-            int pdepth = depth;
-
-            if (tiles->num_planes != 1)
-                    pdepth /= loc.tile->num_planes;
-            if (loc.tile->num_bands == CHAR_ALL_BANDS_COUNT)
-                bit_pcls = NULL;
-            /* put the bits, but don't restrict to a single buffer */
-            code = cmd_put_bits(cldev, bit_pcls, ts_bits(cldev, loc.tile),
-                                loc.tile->width * pdepth,
-                                loc.tile->height * loc.tile->num_planes, loc.tile->cb_raster,
-                                rsize,
-                                decompress_elsewhere |
-                                    (cldev->target->BLS_force_memory ? (1 << cmd_compress_cfe) : 0),
-                                &dp, &csize);
+    /* The bitmap is in the cache.  Check whether this band */
+    /* knows about it. */
+    bptr = ts_mask(loc.tile) + (band_index >> 3);
+
+    if (*bptr & bmask) {	/* Already known.  Just set the index. */
+        if (pcls->tile_index == loc.index)
+            return 0;
+        cmd_put_tile_index(cldev, pcls, loc.index);
+    } else {		/* Not known yet.  Output the bits. */
+        /* Note that the offset we write is the one used by */
+        /* the reading phase, not the writing phase. */
+        ulong offset = (byte *) loc.tile - cldev->cache_chunk->data;
+        uint rsize = 2 + cmd_size_w(loc.tile->width) +
+                     cmd_size_w(loc.tile->height) +
+                     (loc.tile->num_planes > 1 ? 1 : 0) +
+                     cmd_size_w(loc.index) +
+                     cmd_size_w(offset);
+        byte *dp;
+        uint csize;
+        gx_clist_state *bit_pcls = pcls;
+        int pdepth = depth;
+
+        if (tiles->num_planes != 1)
+            pdepth /= loc.tile->num_planes;
+        if (loc.tile->num_bands == CHAR_ALL_BANDS_COUNT)
+            bit_pcls = NULL;
+        /* put the bits, but don't restrict to a single buffer */
+        code = cmd_put_bits(cldev, bit_pcls, ts_bits(cldev, loc.tile),
+                            loc.tile->width * pdepth,
+                            loc.tile->height * loc.tile->num_planes, loc.tile->raster,
+                            rsize,
+                            decompress_elsewhere |
+                                (cldev->target->BLS_force_memory ? (1 << cmd_compress_cfe) : 0),
+                            &dp, &csize);
 
-            if (code < 0)
-                return code;
-            *dp = cmd_count_op(cmd_opv_set_bits, csize, cldev->memory);
-            dp[1] = (depth << 2) + code;
-            dp += 2;
-            dp = cmd_put_w(loc.tile->width, dp);
-            dp = cmd_put_w(loc.tile->height, dp);
-            dp = cmd_put_w(loc.index, dp);
-            cmd_put_w(offset, dp);
-            if (bit_pcls == NULL) {
-                memset(ts_mask(loc.tile), 0xff,
-                       cldev->tile_band_mask_size);
-                loc.tile->num_bands = cldev->nbands;
-            } else {
-                *bptr |= bmask;
-                loc.tile->num_bands++;
-            }
+        if (code < 0)
+            return code;
+        if_debug1m('L', cldev->memory,
+                   "[L] fake end_run: really set_bits[%d]\n", csize);
+        *dp = cmd_count_op(loc.tile->num_planes > 1 ? cmd_opv_set_bits_planar : cmd_opv_set_bits,
+                           csize, cldev->memory);
+        dp[1] = (depth << 2) + code;
+        dp += 2;
+        dp = cmd_put_w(loc.tile->width, dp);
+        dp = cmd_put_w(loc.tile->height, dp);
+        if (loc.tile->num_planes > 1)
+            *dp++ = loc.tile->num_planes;
+        dp = cmd_put_w(loc.index, dp);
+        cmd_put_w(offset, dp);
+        if_debug7m('L', cldev->memory, " compress=%d depth=%d size=(%d,%d) planes=%d index=%d offset=%d\n",
+                   code, depth, loc.tile->width, loc.tile->height, loc.tile->num_planes, loc.index, offset);
+        if (bit_pcls == NULL) {
+            memset(ts_mask(loc.tile), 0xff,
+                   cldev->tile_band_mask_size);
+            loc.tile->num_bands = cldev->nbands;
+        } else {
+            *bptr |= bmask;
+            loc.tile->num_bands++;
         }
-        pcls->tile_index = loc.index;
-        pcls->tile_id = loc.tile->id;
-        return 0;
     }
-    /* The tile is not in the cache. */
-    code = clist_add_tile(cldev, tiles, tiles->raster, depth);
-    if (code < 0)
-        return code;
-    goto top;
+    pcls->tile_index = loc.index;
+    pcls->tile_id = loc.tile->id;
+    return 0;
 }
diff -pruN 9.55.0~dfsg-3/base/gxcldev.h 9.56.1~dfsg-1/base/gxcldev.h
--- 9.55.0~dfsg-3/base/gxcldev.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxcldev.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -73,49 +73,159 @@ int cmd_write_pseudo_band(gx_device_clis
  *         and/or previous operands.
  */
 typedef enum {
-    cmd_op_misc = 0x00,		/* (see below) */
-    cmd_opv_end_run = 0x00,	/* (nothing) */
-    cmd_opv_set_tile_size = 0x01,   /* rs?(1)nry?(1)nrx?(1)depth(5, encoded), */
-                                /* rep_width#, rep_height#, */
-                                /* [, nreps_x#][, nreps_y #] */
-                                /* [, rep_shift#] */
-    cmd_opv_set_tile_phase = 0x02,	/* x#, y# */
-    cmd_opv_set_tile_bits = 0x03,	/* index#, offset#, <bits> */
-    cmd_opv_set_bits = 0x04,	/* depth*4+compress, width#, height#, */
-                                /* index#, offset#, <bits> */
-    cmd_opv_set_tile_color = 0x05,	/* (nothing; next set/delta_color */
-                                /* refers to tile) */
-    cmd_opv_set_misc = 0x06,
-#define cmd_set_misc_lop (0 << 6)	/* 00: lop_lsb(6), lop_msb# */
-#define cmd_set_misc_data_x (1 << 6)	/* 01: more(1)dx_lsb(5)[, dx_msb#] */
-#define cmd_set_misc_map (2 << 6)	/* 10: contents(2)map_index(4) */
-    /* [, n x frac] */
-#define cmd_set_misc_halftone (3 << 6)	/* 11: type(6), num_comp# */
-    cmd_opv_enable_lop = 0x07,	/* (nothing) */
-    cmd_opv_disable_lop = 0x08,	/* (nothing) */
-    cmd_opv_end_page = 0x0b,	/* (nothing) */
-    cmd_opv_delta_color0 = 0x0c,	/* See cmd_put_color in gxclutil.c */
-    cmd_opv_delta_color1 = 0x0d,	/* <<same as color0>> */
-    cmd_opv_set_copy_color = 0x0e,	/* (nothing) */
-    cmd_opv_set_copy_alpha = 0x0f,	/* (nothing) */
-    cmd_op_set_color0 = 0x10,	/* +n = number of low order zero bytes | */
-#define cmd_no_color_index 15	/* +15 = transparent - "no color" */
-    cmd_op_set_color1 = 0x20,	/* <<same as color0>> */
-    cmd_op_fill_rect = 0x30,	/* +dy2dh2, x#, w# | +0, rect# */
-    cmd_op_fill_rect_short = 0x40,	/* +dh, dx, dw | +0, rect_short */
-    cmd_op_fill_rect_tiny = 0x50,	/* +dw+0, rect_tiny | +dw+8 */
-    cmd_op_tile_rect = 0x60,	/* +dy2dh2, x#, w# | +0, rect# */
-    cmd_op_tile_rect_short = 0x70,	/* +dh, dx, dw | +0, rect_short */
-    cmd_op_tile_rect_tiny = 0x80,	/* +dw+0, rect_tiny | +dw+8 */
-    cmd_op_copy_mono_planes = 0x90,	/* +compress, plane_height, x#, y#, (w+data_x)#, */
-                                        /* h#, <bits> | */
-#define cmd_copy_use_tile 8             /* +8 (use tile), x#, y# | */
-    cmd_op_copy_color_alpha = 0xa0,	/* (same as copy_mono, except: */
-                                /* if color, ignore ht_color; */
-                                /* if alpha & !use_tile, depth is */
-                                /*   first operand) */
-    cmd_op_delta_tile_index = 0xb0,	/* +delta+8 */
-    cmd_op_set_tile_index = 0xc0	/* +index[11:8], index[7:0] */
+    cmd_op_misc              = 0x00, /* (see below) */
+    cmd_opv_end_run          = 0x00, /* (nothing) */
+    cmd_opv_set_tile_size    = 0x01, /* rs?(1)nry?(1)nrx?(1)depth(5, encoded), */
+                                     /* rep_width#, rep_height#, */
+                                     /* [, nreps_x#][, nreps_y #] */
+                                     /* [, rep_shift#] */
+    cmd_opv_set_tile_phase   = 0x02, /* x#, y# */
+    cmd_opv_set_tile_bits    = 0x03, /* index#, offset#, <bits> */
+    cmd_opv_set_bits         = 0x04, /* depth*4+compress, width#, height#, */
+                                     /* index#, offset#, <bits> */
+    cmd_opv_set_tile_color   = 0x05, /* (nothing; next set/delta_color */
+                                     /* refers to tile) */
+    cmd_opv_set_misc         = 0x06,
+#define cmd_set_misc_lop      (0 << 6) /* 00: lop_lsb(6), lop_msb# */
+#define cmd_set_misc_data_x   (1 << 6) /* 01: more(1)dx_lsb(5)[, dx_msb#] */
+#define cmd_set_misc_map      (2 << 6) /* 10: contents(2)map_index(4) */
+                                       /* [, n x frac] */
+#define cmd_set_misc_halftone (3 << 6) /* 11: type(6), num_comp# */
+    cmd_opv_enable_lop       = 0x07, /* (nothing) */
+    cmd_opv_disable_lop      = 0x08, /* (nothing) */
+    cmd_opv_set_screen_phaseT= 0x09, /* x#, y# */
+    cmd_opv_set_screen_phaseS= 0x0a, /* x#, y# */
+    cmd_opv_end_page         = 0x0b, /* (nothing) */
+    cmd_opv_delta_color0     = 0x0c, /* See cmd_put_color in gxclutil.c */
+    cmd_opv_delta_color1     = 0x0d, /* <<same as color0>> */
+    cmd_opv_set_copy_color   = 0x0e, /* (nothing) */
+    cmd_opv_set_copy_alpha   = 0x0f, /* (nothing) */
+    cmd_op_set_color0        = 0x10, /* +n = number of low order zero bytes | */
+#define cmd_no_color_index 15        /* +15 = transparent - "no color" */
+    cmd_op_set_color1        = 0x20, /* <<same as color0>> */
+    cmd_op_fill_rect         = 0x30, /* +dy2dh2, x#, w# | +0, rect# */
+    cmd_op_fill_rect_short   = 0x40, /* +dh, dx, dw | +0, rect_short */
+    cmd_op_fill_rect_tiny    = 0x50, /* +dw+0, rect_tiny | +dw+8 */
+    cmd_op_tile_rect         = 0x60, /* +dy2dh2, x#, w# | +0, rect# */
+    cmd_op_tile_rect_short   = 0x70, /* +dh, dx, dw | +0, rect_short */
+    cmd_op_tile_rect_tiny    = 0x80, /* +dw+0, rect_tiny | +dw+8 */
+    cmd_op_copy_mono_planes  = 0x90, /* +compress, plane_height, x#, y#, (w+data_x)#, */
+                                     /* h#, <bits> | */
+#define cmd_copy_use_tile 8          /* +8 (use tile), x#, y# | */
+    cmd_op_copy_color_alpha  = 0xa0, /* (same as copy_mono, except: */
+                                     /* if color, ignore ht_color; */
+                                     /* if alpha & !use_tile, depth is */
+                                     /*   first operand) */
+    cmd_op_delta_tile_index  = 0xb0, /* +delta+8 */
+    cmd_op_set_tile_index    = 0xc0, /* +index[11:8], index[7:0] */
+    cmd_op_misc2             = 0xd0, /* (see below) */
+    cmd_opv_set_bits_planar  = 0xd0, /* depth*4+compress, width#, height#, */
+                                     /* num_planes, index#, offset#, <bits> */
+    cmd_op_fill_rect_hl      = 0xd1, /* rect fill with devn color */
+    cmd_opv_set_fill_adjust  = 0xd2, /* adjust_x/y(fixed) */
+    cmd_opv_set_ctm          = 0xd3, /* [per sput/sget_matrix] */
+    cmd_opv_set_color_space  = 0xd4, /* base(4)Indexed?(2)0(2) */
+                                     /* [, hival#, table|map] */
+    /*
+     * cmd_opv_set_misc2_value is followed by a mask (a variable-length
+     * integer), and then by parameter values for the parameters selected
+     * by the mask.  See gxclpath.h for the "known" mask values.
+     */
+    /* cap_join:      0(2)cap(3)join(3) */
+    /* cj_ac_sa:      0(3)curve_join+1(3)acc.curves(1)stroke_adj(1) */
+    /* flatness:      (float) */
+    /* line width:    (float) */
+    /* miter limit:   (float) */
+    /* op_bm_tk:      blend mode(5)text knockout(1)o.p.mode(1)o.p.(1) */
+    /* segment notes: (byte) */
+    /* opacity/shape: alpha(float)mask(TBD) */
+    /* alpha:         <<verbatim copy from gs_gstate>> */
+    cmd_opv_set_misc2        = 0xd5, /* mask#, selected parameters */
+    cmd_opv_set_dash         = 0xd6, /* adapt(1)abs.dot(1)n(6), dot */
+                                     /* length(float), offset(float), */
+                                     /* n x (float) */
+    cmd_opv_enable_clip      = 0xd7, /* (nothing) */
+    cmd_opv_disable_clip     = 0xd8, /* (nothing) */
+    cmd_opv_begin_clip       = 0xd9, /* fill_adjust.x#, fill_adjust.y# */
+    cmd_opv_end_clip         = 0xda, /* (nothing) */
+    cmd_opv_begin_image_rect = 0xdb, /* same as begin_image, followed by */
+                                     /* x0#, w-x1#, y0#, h-y1# */
+    cmd_opv_begin_image      = 0xdc, /* image_type_table index, */
+                                     /* [per image type] */
+    cmd_opv_image_data       = 0xdd, /* height# (premature EOD if 0), */
+                                     /* raster#, <data> */
+    cmd_opv_image_plane_data = 0xde, /* height# (premature EOD if 0), */
+                                     /* flags# (0 = same raster & data_x, */
+                                     /* 1 = new raster & data_x, lsb first), */
+                                     /* [raster#, [data_x#,]]* <data> */
+    cmd_opv_extend           = 0xdf, /* command, varies (see gx_cmd_ext_op below) */
+
+
+#define cmd_misc2_op_name_strings\
+  "set_bits_planar", "fill_hl_color", \
+  "set_fill_adjust", "set_ctm",\
+  "set_color_space", "set_misc2", "set_dash", "enable_clip",\
+  "disable_clip", "begin_clip", "end_clip", "begin_image_rect",\
+  "begin_image", "image_data", "image_plane_data", "extended"
+
+    cmd_op_segment           = 0xe0, /* (see below) */
+    cmd_opv_rmoveto          = 0xe0, /* dx%, dy% */
+    cmd_opv_rlineto          = 0xe1, /* dx%, dy% */
+    cmd_opv_hlineto          = 0xe2, /* dx% */
+    cmd_opv_vlineto          = 0xe3, /* dy% */
+    cmd_opv_rmlineto         = 0xe4, /* dx1%,dy1%, dx2%,dy2% */
+    cmd_opv_rm2lineto        = 0xe5, /* dx1%,dy1%, dx2%,dy2%, dx3%,dy3% */
+    cmd_opv_rm3lineto        = 0xe6, /* dx1%,dy1%, dx2%,dy2%, dx3%,dy3%, */
+                                     /* [-dx2,-dy2 implicit] */
+    cmd_opv_rrcurveto        = 0xe7, /* dx1%,dy1%, dx2%,dy2%, dx3%,dy3% */
+    cmd_opv_min_curveto = cmd_opv_rrcurveto,
+    cmd_opv_hvcurveto        = 0xe8, /* dx1%, dx2%,dy2%, dy3% */
+    cmd_opv_vhcurveto        = 0xe9, /* dy1%, dx2%,dy2%, dx3% */
+    cmd_opv_nrcurveto        = 0xea, /* dx2%,dy2%, dx3%,dy3% */
+    cmd_opv_rncurveto        = 0xeb, /* dx1%,dy1%, dx2%,dy2% */
+    cmd_opv_vqcurveto        = 0xec, /* dy1%, dx2%[,dy2=dx2 with sign */
+                                     /* of dy1, dx3=dy1 with sign of dx2] */
+    cmd_opv_hqcurveto        = 0xed, /* dx1%, [dx2=dy2 with sign */
+                                     /* of dx1,]%dy2, [dy3=dx1 with sign */
+                                     /* of dy2] */
+    cmd_opv_scurveto         = 0xee, /* all implicit: previous op must have been */
+                                     /* *curveto with one or more of dx/y1/3 = 0. */
+                                     /* If h*: -dx3,dy3, -dx2,dy2, -dx1,dy1. */
+                                     /* If v*: dx3,-dy3, dx2,-dy2, dx1,-dy1. */
+    cmd_opv_max_curveto = cmd_opv_scurveto,
+    cmd_opv_closepath        = 0xef, /* (nothing) */
+
+#define cmd_segment_op_name_strings\
+  "rmoveto", "rlineto", "hlineto", "vlineto",\
+  "rmlineto", "rm2lineto", "rm3lineto", "rrcurveto",\
+  "hvcurveto", "vhcurveto", "nrcurveto", "rncurveto",\
+  "vqcurveto", "hqcurveto", "scurveto", "closepath"
+
+    cmd_op_path              = 0xf0, /* (see below) */
+    cmd_opv_fill             = 0xf0,
+    cmd_opv_rgapto           = 0xf1, /* dx%, dy% */
+    /* UNUSED 0xf2 */
+    cmd_opv_eofill           = 0xf3,
+    cmd_opv_fill_stroke      = 0xf4,
+    cmd_opv_eofill_stroke    = 0xf5,
+    cmd_opv_stroke           = 0xf6,
+    /* UNUSED 0xf7 */
+    /* UNUSED 0xf8 */
+    cmd_opv_polyfill         = 0xf9,
+    /* UNUSED 0xfa */
+    /* UNUSED 0xfb */
+    cmd_opv_fill_trapezoid   = 0xfc
+    /* UNUSED 0xfd */
+    /* UNUSED 0xfe */
+    /* UNUSED 0xff */
+
+#define cmd_path_op_name_strings\
+  "fill", "rgapto", "?f2?", "eofill",\
+  "fill_stroke", "eofill_stroke", "stroke", "?f7?",\
+  "?f8?", "polyfill", "?fa?", "?fb?",\
+  "fill_trapezoid", "?fd?", "?fe?", "?ff?"
+
+/* unused cmd_op values: 0xf2, 0xf7, 0xf8, 0xfa, 0xfb, 0xfd, 0xfe, 0xff */
 } gx_cmd_op;
 
 #define cmd_op_name_strings\
@@ -127,7 +237,7 @@ typedef enum {
 #define cmd_misc_op_name_strings\
   "end_run", "set_tile_size", "set_tile_phase", "set_tile_bits",\
   "set_bits", "set_tile_color", "set_misc", "enable_lop",\
-  "disable_lop", "invalid", "invalid", "end_page",\
+  "disable_lop", "set_screen_phaseT", "set_screen_phaseS", "end_page",\
   "delta2_color0", "delta2_color1", "set_copy_color", "set_copy_alpha",
 
 #ifdef DEBUG
@@ -262,6 +372,7 @@ struct gx_clist_state_s {
    ((tile_slot *)(cldev->data + offset_temp))->id == (tid))
     gs_id pattern_id;		/* the last stored pattern id. */
     gs_int_point tile_phase;	/* most recent tile phase */
+    gs_int_point screen_phase[2];	/* most recent screen phase */
     gx_color_index tile_colors[2];	/* most recent tile colors */
     gx_device_color tile_color_devn[2];  /* devn tile colors */
     gx_cmd_rect rect;		/* most recent rectangle */
@@ -291,7 +402,7 @@ struct gx_clist_state_s {
          { gx_no_color_index, gx_no_color_index },\
         { gx_dc_type_none },\
         0, gx_no_bitmap_id, gs_no_id,\
-         { 0, 0 }, { gx_no_color_index, gx_no_color_index },\
+         { 0, 0 }, { {0, 0}, {0, 0}}, { gx_no_color_index, gx_no_color_index },\
         { {NULL}, {NULL} },\
          { 0, 0, 0, 0 }, lop_default, 0, 0, 0, 0, initial_known,\
         { 0, 0 }, /* cmd_list */\
@@ -349,13 +460,17 @@ dev_proc_get_bits_rectangle(clist_get_bi
 int cmd_put_params(gx_device_clist_writer *, gs_param_list *);
 
 /* Conditionally keep command statistics. */
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+/* #define COLLECT_STATS_CLIST */
+
+#ifdef COLLECT_STATS_CLIST
 int cmd_count_op(int op, uint size, const gs_memory_t *mem);
+int cmd_count_extended_op(int op, uint size, const gs_memory_t *mem);
 void cmd_uncount_op(int op, uint size);
 void cmd_print_stats(const gs_memory_t *);
 #  define cmd_count_add1(v) (v++)
 #else
 #  define cmd_count_op(op, size, mem) (op)
+#  define cmd_count_extended_op(op, size, mem) (op)
 #  define cmd_uncount_op(op, size) DO_NOTHING
 #  define cmd_count_add1(v) DO_NOTHING
 #endif
@@ -364,36 +479,105 @@ void cmd_print_stats(const gs_memory_t *
 /* and allocate space for its data. */
 byte *cmd_put_list_op(gx_device_clist_writer * cldev, cmd_list * pcl, uint size);
 
+/* Add a extended op command to the appropriate band list, */
+/* and allocate space for its data. */
+byte *cmd_put_list_extended_op(gx_device_clist_writer * cldev, cmd_list * pcl, int op, uint size);
+
 /* Request a space in the buffer.
    Writes out the buffer if necessary.
    Returns the size of available space. */
 int cmd_get_buffer_space(gx_device_clist_writer * cldev, gx_clist_state * pcls, uint size);
 
 #ifdef DEBUG
+void clist_debug_op(gs_memory_t *mem, const unsigned char *op_ptr);
 byte *cmd_put_op(gx_device_clist_writer * cldev, gx_clist_state * pcls, uint size);
 #else
+#define clist_debug_op(mem, op) do { } while (0)
 #  define cmd_put_op(cldev, pcls, size)\
      cmd_put_list_op(cldev, &(pcls)->list, size)
+#  define cmd_put_extended_op(cldev, pcls, op, size)\
+     cmd_put_list_extended_op(cldev, &(pcls)->list, op, size)
 #endif
 /* Call cmd_put_op and update stats if no error occurs. */
-#define set_cmd_put_op(dp, cldev, pcls, op, csize)\
-  ( (*dp = cmd_put_op(cldev, pcls, csize)) == NULL ?\
-      (cldev)->error_code :\
-    (**dp = cmd_count_op(op, csize, cldev->memory), 0) )
+static inline int
+set_cmd_put_op(byte **dp, gx_device_clist_writer * cldev,
+               gx_clist_state * pcls, int op, uint csize)
+{
+    *dp = cmd_put_op(cldev, pcls, csize);
+
+    if (*dp == NULL)
+        return (cldev)->error_code;
+    **dp = cmd_count_op(op, csize, cldev->memory);
+
+    if (gs_debug_c('L')) {
+        clist_debug_op(cldev->memory, *dp);
+        dmlprintf1(cldev->memory, "[%u]\n", csize);
+    }
+
+    return 0;
+}
+/* Call cmd_put_extended_op and update stats if no error occurs. */
+static inline int
+set_cmd_put_extended_op(byte **dp, gx_device_clist_writer * cldev,
+                        gx_clist_state * pcls, int op, uint csize)
+{
+    *dp = cmd_put_op(cldev, pcls, csize);
+
+    if (*dp == NULL)
+        return (cldev)->error_code;
+    **dp = cmd_opv_extend;
+    (*dp)[1] = cmd_count_extended_op(op, csize, cldev->memory);
+
+    if (gs_debug_c('L')) {
+        clist_debug_op(cldev->memory, *dp);
+        dmlprintf1(cldev->memory, "[%u]\n", csize);
+    }
+
+    return 0;
+}
 
 /* Add a command for all bands or a range of bands. */
 byte *cmd_put_range_op(gx_device_clist_writer * cldev, int band_min,
                        int band_max, uint size);
 
-#define cmd_put_all_op(cldev, size)\
-  cmd_put_range_op(cldev, 0, (cldev)->nbands - 1, size)
 /* Call cmd_put_all/range_op and update stats if no error occurs. */
-#define set_cmd_put_range_op(dp, cldev, op, bmin, bmax, csize)\
-  ( (*dp = cmd_put_range_op(cldev, bmin, bmax, csize)) == NULL ?\
-      (cldev)->error_code :\
-    (**dp = cmd_count_op(op, csize, (cldev)->memory), 0) )
+static inline int
+set_cmd_put_range_op(byte **dp, gx_device_clist_writer * cldev,
+                     int op, int bmin, int bmax, uint csize)
+{
+    *dp = cmd_put_range_op(cldev, bmin, bmax, csize);
+    if (*dp == NULL)
+        return (cldev)->error_code;
+    **dp = cmd_count_op(op, csize, (cldev)->memory);
+
+    if (gs_debug_c('L')) {
+        clist_debug_op(cldev->memory, *dp);
+        dmlprintf1(cldev->memory, "[%u]\n", csize);
+    }
+
+    return 0;
+}
 #define set_cmd_put_all_op(dp, cldev, op, csize)\
   set_cmd_put_range_op(dp, cldev, op, 0, (cldev)->nbands - 1, csize)
+static inline int
+set_cmd_put_range_extended_op(byte **dp, gx_device_clist_writer * cldev,
+                     int op, int bmin, int bmax, uint csize)
+{
+    *dp = cmd_put_range_op(cldev, bmin, bmax, csize);
+    if (*dp == NULL)
+        return (cldev)->error_code;
+    **dp = cmd_opv_extend;
+    (*dp)[1] = cmd_count_extended_op(op, csize, (cldev)->memory);
+
+    if (gs_debug_c('L')) {
+        clist_debug_op(cldev->memory, *dp);
+        dmlprintf1(cldev->memory, "[%u]\n", csize);
+    }
+
+    return 0;
+}
+#define set_cmd_put_all_extended_op(dp, cldev, op, csize)\
+  set_cmd_put_range_extended_op(dp, cldev, op, 0, (cldev)->nbands - 1, csize)
 
 /* Shorten the last allocated command. */
 /* Note that this does not adjust the statistics. */
@@ -475,6 +659,13 @@ cmd_set_tile_phase_generic(gx_device_cli
                    int px, int py, bool all_bands);
 int cmd_set_tile_phase(gx_device_clist_writer *cldev, gx_clist_state * pcls,
                        int px, int py);
+/* Put out a command to set the screen phase. */
+int
+cmd_set_screen_phase_generic(gx_device_clist_writer * cldev, gx_clist_state * pcls,
+                             int px, int py, gs_color_select_t color_select, bool all_bands);
+int
+cmd_set_screen_phase(gx_device_clist_writer * cldev, gx_clist_state * pcls,
+                     int px, int py, gs_color_select_t color_select);
 
 /* Enable or disable the logical operation. */
 int cmd_put_enable_lop(gx_device_clist_writer *, gx_clist_state *, int);
@@ -679,7 +870,10 @@ int clist_playback_file_bands(clist_play
                           int band_first, int band_last, int x0, int y0);
 #ifdef DEBUG
 int64_t clist_file_offset(const stream_state *st, uint buffer_offset);
-int top_up_offset_map(stream_state * st, const byte *buf, const byte *ptr, const byte *end);
+void top_up_offset_map(stream_state * st, const byte *buf, const byte *ptr, const byte *end);
+void offset_map_next_data_out_of_band(stream_state *st);
+void clist_debug_op(gs_memory_t *mem, const unsigned char *op_ptr);
+void adjust_offset_map_for_skipped_data(stream_state *st, uint buffer_offset, uint skipped);
 #endif
 
 int clist_writer_push_no_cropping(gx_device_clist_writer *cdev);
diff -pruN 9.55.0~dfsg-3/base/gxclfile.c 9.56.1~dfsg-1/base/gxclfile.c
--- 9.55.0~dfsg-3/base/gxclfile.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclfile.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -234,7 +234,7 @@ typedef struct
 static void
 file_to_fake_path(clist_file_ptr file, char fname[gp_file_name_sizeof])
 {
-    gs_sprintf(fname, ENC_FILE_STR, file);
+    gs_snprintf(fname, gp_file_name_sizeof, ENC_FILE_STR, file);
 }
 
 static clist_file_ptr
@@ -532,7 +532,10 @@ clist_fseek(clist_file_ptr cf, int64_t o
         res = gp_fseek(ifile->f, offset, mode);
     }
     /* NB: if gp_can_share_fdesc, we don't actually seek */
-    if (res >= 0) {
+    /* The following lgtm tag is required because on some platforms
+     * !gp_can_share_fdesc() is always true, so the value of res is
+     * known. On other platforms though, this is NOT true. */
+    if (res >= 0) { /* lgtm [cpp/constant-comparison] */
         /* Update the ifile->pos */
         switch (mode) {
             case SEEK_SET:
@@ -566,9 +569,10 @@ init_proc(gs_gxclfile_init);
 int
 gs_gxclfile_init(gs_memory_t *mem)
 {
+    gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core;
 #ifdef PACIFY_VALGRIND
-    VALGRIND_HG_DISABLE_CHECKING(&clist_io_procs_file_global, sizeof(clist_io_procs_file_global));
+    VALGRIND_HG_DISABLE_CHECKING(&core->clist_io_procs_file, sizeof(core->clist_io_procs_file));
 #endif
-    clist_io_procs_file_global = &clist_io_procs_file;
+    core->clist_io_procs_file = &clist_io_procs_file;
     return 0;
 }
diff -pruN 9.55.0~dfsg-3/base/gxclimag.c 9.56.1~dfsg-1/base/gxclimag.c
--- 9.55.0~dfsg-3/base/gxclimag.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclimag.c	2022-04-04 13:46:22.000000000 +0000
@@ -303,6 +303,7 @@ typedef struct clist_image_enum_s {
     int bits_per_plane;         /* bits per pixel per plane */
     gs_matrix matrix;           /* image space -> device space */
     bool uses_color;
+    bool masked;
     clist_color_space_t color_space;
     int ymin, ymax;
     gx_color_usage_t color_usage;
@@ -514,6 +515,7 @@ clist_begin_typed_image(gx_device * dev,
 #endif
     pie->memory = mem;
     pie->buffer = NULL;
+    pie->masked = masked;
     *pinfo = (gx_image_enum_common_t *) pie;
     /* num_planes and plane_depths[] are set later, */
     /* by gx_image_enum_common_init. */
@@ -1129,9 +1131,9 @@ clist_image_plane_data(gx_image_enum_com
                 code = cmd_update_lop(cdev, re.pcls, lop);
             if (code < 0)
                 return code;
+            /* Does the result of this image depend upon the current color in the
+             * graphics state? If so, we need to send it. */
             if (pie->uses_color) {
-                gs_int_point color_phase;
-
                 /* We want to write the color taking into account the entire image so */
                 /* we set re.rect_nbands from pie->ymin and pie->ymax so that we will */
                 /* make the decision to write 'all_bands' the same for the whole image */
@@ -1143,13 +1145,37 @@ clist_image_plane_data(gx_image_enum_com
                                              &re, devn_not_tile_fill);
                 if (code < 0)
                     return code;
-                /* see if phase informaiton must be inserted in the command list */
-                /* if so, go ahead and do it for all_bands */
-                if ( pie->dcolor.type->get_phase(&pie->dcolor, &color_phase) &&
-                     (color_phase.x != re.pcls->tile_phase.x ||
-                      color_phase.y != re.pcls->tile_phase.y ) &&
-                     (code = cmd_set_tile_phase_generic(cdev, re.pcls,
-                                                        color_phase.x, color_phase.y, true)) < 0  )
+                if (!pie->masked) {
+                    /* In PS and PDF, masked == uses_color. In PCL, due to rops, we can
+                     * have a non-imagemask image that relies on the current graphics
+                     * color. C303.BIN page 20 has an example of this. Normally the above
+                     * call the cmd_put_drawing_color will have sent through the halftone
+                     * phase, but we can be in the situation where the current drawing
+                     * color is pure (so no phase is sent), but the colors in the image
+                     * are not (so a phase must be sent). Accordingly, we catch that
+                     * here. */
+                    if (pie->pgs->screen_phase[gs_color_select_texture].x != re.pcls->screen_phase[gs_color_select_texture].x ||
+                        pie->pgs->screen_phase[gs_color_select_texture].y != re.pcls->screen_phase[gs_color_select_texture].y) {
+                        code = cmd_set_screen_phase_generic(cdev, re.pcls,
+                                                            pie->pgs->screen_phase[gs_color_select_texture].x,
+                                                            pie->pgs->screen_phase[gs_color_select_texture].y,
+                                                            gs_color_select_texture, true);
+                        if (code < 0)
+                            return code;
+                    }
+                    if (pie->pgs->screen_phase[gs_color_select_source].x != re.pcls->screen_phase[gs_color_select_source].x ||
+                        pie->pgs->screen_phase[gs_color_select_source].y != re.pcls->screen_phase[gs_color_select_source].y) {
+                        code = cmd_set_screen_phase_generic(cdev, re.pcls,
+                                                            pie->pgs->screen_phase[gs_color_select_source].x,
+                                                            pie->pgs->screen_phase[gs_color_select_source].y,
+                                                            gs_color_select_source, true);
+                        if (code < 0)
+                            return code;
+                    }
+                }
+            } else if (0 != re.pcls->tile_phase.x || 0 != re.pcls->tile_phase.y) {
+                code = cmd_set_tile_phase(cdev, re.pcls, 0, 0);
+                if (code < 0)
                     return code;
             }
             if (entire_box.p.x != 0 || entire_box.p.y != 0 ||
@@ -1367,15 +1393,14 @@ clist_composite(gx_device * dev,
     if (cropping_op == ALLBANDS) {
         /* overprint applies to all bands */
         size_dummy = size;
-        code = set_cmd_put_all_op(& dp,
+        code = set_cmd_put_all_extended_op(& dp,
                                    (gx_device_clist_writer *)dev,
-                                   cmd_opv_extend,
+                                   cmd_opv_ext_composite,
                                    size );
         if (code < 0)
             return code;
 
-        /* insert the command and compositor identifier */
-        dp[1] = cmd_opv_ext_composite;
+        /* insert the compositor identifier */
         dp[2] = pcte->type->comp_id;
 
         /* serialize the remainder of the compositor */
@@ -1413,10 +1438,9 @@ clist_composite(gx_device * dev,
         RECT_ENUM_INIT(re, temp_cropping_min, temp_cropping_max - temp_cropping_min);
         do {
             RECT_STEP_INIT(re);
-            code = set_cmd_put_op(&dp, cdev, re.pcls, cmd_opv_extend, size);
+            code = set_cmd_put_extended_op(&dp, cdev, re.pcls, cmd_opv_ext_composite, size);
             if (code >= 0) {
                 size_dummy = size;
-                dp[1] = cmd_opv_ext_composite;
                 dp[2] = pcte->type->comp_id;
                 code = pcte->type->procs.write(pcte, dp + 3, &size_dummy, cdev);
             }
@@ -1502,9 +1526,8 @@ cmd_put_halftone(gx_device_clist_writer
     req_size = 2 + enc_u_sizew(ht_size);
 
     /* output the "put halftone" command */
-    if ((code = set_cmd_put_all_op(&dp, cldev, cmd_opv_extend, req_size)) < 0)
+    if ((code = set_cmd_put_all_extended_op(&dp, cldev, cmd_opv_ext_put_halftone, req_size)) < 0)
         return code;
-    dp[1] = cmd_opv_ext_put_halftone;
     dp += 2;
     enc_u_putw(ht_size, dp);
 
@@ -1518,11 +1541,10 @@ cmd_put_halftone(gx_device_clist_writer
     } else {
         /* send the only segment command */
         req_size += ht_size;
-        code = set_cmd_put_all_op(&dp, cldev, cmd_opv_extend, req_size);
+        code = set_cmd_put_all_extended_op(&dp, cldev, cmd_opv_ext_put_ht_seg, req_size);
         if (code < 0)
             return code;
         dp0 = dp;
-        dp[1] = cmd_opv_ext_put_ht_seg;
         dp += 2;
         enc_u_putw(ht_size, dp);
         pht_buff = dp;
@@ -1559,9 +1581,8 @@ cmd_put_halftone(gx_device_clist_writer
             seg_size = ( ht_size > cbuf_ht_seg_max_size ? cbuf_ht_seg_max_size
                                                         : ht_size );
             tmp_size = 2 + enc_u_sizew(seg_size) + seg_size;
-            code = set_cmd_put_all_op(&dp, cldev, cmd_opv_extend, tmp_size);
+            code = set_cmd_put_all_extended_op(&dp, cldev, cmd_opv_ext_put_ht_seg, tmp_size);
             if (code >= 0) {
-                dp[1] = cmd_opv_ext_put_ht_seg;
                 dp += 2;
                 enc_u_putw(seg_size, dp);
                 memcpy(dp, pbuff, seg_size);
@@ -1587,8 +1608,8 @@ cmd_put_color_mapping(gx_device_clist_wr
     int code;
     const gx_device_halftone *pdht = gx_select_dev_ht(pgs);
 
-    /* Put out the halftone, if present. */
-    if (pdht && pdht->id != cldev->device_halftone_id) {
+    /* Put out the halftone, if present, and target is not contone. */
+    if (pdht && pdht->id != cldev->device_halftone_id && !device_is_contone(cldev->target)) {
         code = cmd_put_halftone(cldev, pdht);
         if (code < 0)
             return code;
diff -pruN 9.55.0~dfsg-3/base/gxclio.h 9.56.1~dfsg-1/base/gxclio.h
--- 9.55.0~dfsg-3/base/gxclio.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclio.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -92,7 +92,4 @@ struct clist_io_procs_s {
 
 typedef struct clist_io_procs_s clist_io_procs_t;
 
-extern const clist_io_procs_t *clist_io_procs_file_global;
-extern const clist_io_procs_t *clist_io_procs_memory_global;
-
 #endif /* gxclio_INCLUDED */
diff -pruN 9.55.0~dfsg-3/base/gxclip2.c 9.56.1~dfsg-1/base/gxclip2.c
--- 9.55.0~dfsg-3/base/gxclip2.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclip2.c	2022-04-04 13:46:22.000000000 +0000
@@ -253,12 +253,12 @@ tile_clip_copy_mono(gx_device * dev,
   } END
 #define FOR_RUNS(data_row, tx1, tx, ty)\
         const byte *data_row = data;\
-        int cy = (y + cdev->phase.y) % cdev->tiles.rep_height;\
+        int cy = imod(y + cdev->phase.y, cdev->tiles.rep_height);\
         const byte *tile_row = cdev->tiles.data + cy * cdev->tiles.raster;\
         int ty;\
 \
         for ( ty = y; ty < y + h; ty++, data_row += raster ) {\
-          int cx = (x + x_offset(ty, cdev)) % cdev->tiles.rep_width;\
+          int cx = imod(x + x_offset(ty, cdev), cdev->tiles.rep_width);\
           const byte *tp = tile_row + (cx >> 3);\
           byte tbit = 0x80 >> (cx & 7);\
           int tx;\
diff -pruN 9.55.0~dfsg-3/base/gxclip.c 9.56.1~dfsg-1/base/gxclip.c
--- 9.55.0~dfsg-3/base/gxclip.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclip.c	2022-04-04 13:46:22.000000000 +0000
@@ -202,7 +202,9 @@ gx_make_clip_device_in_heap(gx_device_cl
     (void)(*dev_proc(dev, open_device)) ((gx_device *)dev);
 }
 /* Define debugging statistics for the clipping loops. */
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+/* #define COLLECT_STATS_CLIP */
+
+#ifdef COLLECT_STATS_CLIP
 struct stats_clip_s {
     long
          loops, out, in_y, in, in1, down, up, x, no_x;
@@ -234,7 +236,7 @@ clip_enumerate_rest(gx_device_clip * rde
     int yc;
     int code;
 
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_CLIP
     if (INCR(loops) % clip_interval == 0 && gs_debug_c('q')) {
         dmprintf5(rdev->memory,
                   "[q]loops=%ld out=%ld in_y=%ld in=%ld in1=%ld\n",
diff -pruN 9.55.0~dfsg-3/base/gxclist.c 9.56.1~dfsg-1/base/gxclist.c
--- 9.55.0~dfsg-3/base/gxclist.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclist.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -178,26 +178,25 @@ clist_initialize_device_procs(gx_device
 
 /*------------------- Choose the implementation -----------------------
 
-   For choosing the clist i/o implementation by makefile options
-   we define global variables, which are initialized with
-   file/memory io procs when they are included into the build.
- */
-const clist_io_procs_t *clist_io_procs_file_global = NULL;
-const clist_io_procs_t *clist_io_procs_memory_global = NULL;
-
+   For choosing the clist i/o implementation by makefile options we
+   define global variables (in gs_lib_ctx_core_t), which are
+   initialized with file/memory io procs when they are included into
+   the build.
+*/
 void
 clist_init_io_procs(gx_device_clist *pclist_dev, bool in_memory)
 {
+    gs_lib_ctx_core_t *core = pclist_dev->common.memory->gs_lib_ctx->core;
 #ifdef PACIFY_VALGRIND
-    VALGRIND_HG_DISABLE_CHECKING(&clist_io_procs_file_global, sizeof(clist_io_procs_file_global));
-    VALGRIND_HG_DISABLE_CHECKING(&clist_io_procs_memory_global, sizeof(clist_io_procs_memory_global));
+    VALGRIND_HG_DISABLE_CHECKING(&core->clist_io_procs_file, sizeof(core->clist_io_procs_file));
+    VALGRIND_HG_DISABLE_CHECKING(&core->clist_io_procs_memory, sizeof(core->clist_io_procs_memory));
 #endif
-    /* if clist_io_procs_file_global is NULL, then BAND_LIST_STORAGE=memory */
+    /* if core->clist_io_procs_file is NULL, then BAND_LIST_STORAGE=memory */
     /* was specified in the build, and "file" is not available */
-    if (in_memory || clist_io_procs_file_global == NULL)
-        pclist_dev->common.page_info.io_procs = clist_io_procs_memory_global;
+    if (in_memory || core->clist_io_procs_file == NULL)
+        pclist_dev->common.page_info.io_procs = core->clist_io_procs_memory;
     else
-        pclist_dev->common.page_info.io_procs = clist_io_procs_file_global;
+        pclist_dev->common.page_info.io_procs = core->clist_io_procs_file;
 }
 
 /* ------ Define the command set and syntax ------ */
@@ -517,6 +516,8 @@ clist_reset(gx_device * dev)
     cdev->band_range_list->head = cdev->band_range_list->tail = 0;
     cdev->band_range_min = 0;
     cdev->band_range_max = nbands - 1;
+    if_debug2m('L', cdev->memory, "[L]Resetting: Band range(%d,%d)\n",
+               cdev->band_range_min, cdev->band_range_max);
     {
         int band;
         gx_clist_state *states = cdev->states;
@@ -845,6 +846,8 @@ clist_end_page(gx_device_clist_writer *
             memset(&cb, 0, sizeof(cb)); /* Zero the block, including any padding */
             cb.band_min = cb.band_max = cmd_band_end;
             cb.pos = (cldev->page_cfile == 0 ? 0 : cldev->page_info.io_procs->ftell(cldev->page_cfile));
+            if_debug3m('l', cldev->memory, "[l]writing end for bands (%d,%d) at %"PRId64"\n",
+                       cb.band_min, cb.band_max, cb.pos);
             code = cldev->page_info.io_procs->fwrite_chars(&cb, sizeof(cb), cldev->page_bfile);
             if (code > 0)
                 code = 0;
@@ -900,7 +903,7 @@ gx_color_index2usage(gx_device *dev, gx_
 
     for (i = 0; i < dev->color_info.num_components; i++) {
         if (color & dev->color_info.comp_mask[i])
-            bits |= (1<<i);
+            bits |= (((gx_color_usage_bits)1) << i);
     }
     return bits;
 }
diff -pruN 9.55.0~dfsg-3/base/gxclmem.c 9.56.1~dfsg-1/base/gxclmem.c
--- 9.55.0~dfsg-3/base/gxclmem.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclmem.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -400,7 +400,7 @@ memfile_fopen(char fname[gp_file_name_si
 
     /* Return the address of this memfile as a string for use in future clist_fopen calls */
     fname[0] = 0xff;        /* a flag that this is a memfile name */
-    gs_sprintf(fname+1, "%p", f);
+    gs_snprintf(fname+1, gp_file_name_sizeof-1, "%p", f);
 
 #ifdef DEBUG
         tot_compressed = 0;
@@ -1007,11 +1007,12 @@ memfile_fread_chars(void *data, uint len
 {
     char *str = (char *)data;
     MEMFILE *f = (MEMFILE *) cf;
-    uint count = len, num_read, move_count;
+    uint count = len, move_count;
+    int64_t num_read;
 
     num_read = f->log_length - f->log_curr_pos;
-    if (count > num_read)
-        count = num_read;
+    if ((int64_t)count > num_read)
+        count = (int)num_read;
     num_read = count;
 
     while (count) {
@@ -1266,9 +1267,10 @@ init_proc(gs_gxclmem_init);
 int
 gs_gxclmem_init(gs_memory_t *mem)
 {
+    gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core;
 #ifdef PACIFY_VALGRIND
-    VALGRIND_HG_DISABLE_CHECKING(&clist_io_procs_memory_global, sizeof(clist_io_procs_memory_global));
+    VALGRIND_HG_DISABLE_CHECKING(&core->clist_io_procs_memory, sizeof(core->clist_io_procs_memory));
 #endif
-    clist_io_procs_memory_global = &clist_io_procs_memory;
+    core->clist_io_procs_memory = &clist_io_procs_memory;
     return 0;
 }
diff -pruN 9.55.0~dfsg-3/base/gxclpage.c 9.56.1~dfsg-1/base/gxclpage.c
--- 9.55.0~dfsg-3/base/gxclpage.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclpage.c	2022-04-04 13:46:22.000000000 +0000
@@ -781,7 +781,7 @@ gx_saved_pages_list_print(gx_device_prin
                             break;
                         curr_elem = curr_elem->prev;
                         /* Below is not needed since we never print reverse even/odd */
-                        if (page_skip < -1)
+                        if (page_skip < -1) /* lgtm [cpp/constant-comparison] */
                             curr_elem = curr_elem->prev;
                     }
                     if (curr_elem == NULL) {
diff -pruN 9.55.0~dfsg-3/base/gxclpath.c 9.56.1~dfsg-1/base/gxclpath.c
--- 9.55.0~dfsg-3/base/gxclpath.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclpath.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -37,7 +37,7 @@
 #include "gxdevsop.h"
 
 /* Statistics */
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_CLIST
 ulong stats_cmd_diffs[5];
 #endif
 
@@ -175,15 +175,17 @@ cmd_put_drawing_color(gx_device_clist_wr
     left = dc_size;
 
     CMD_CHECK_LAST_OP_BLOCK_DEFINED(cldev);
-    /* see if phase informaiton must be inserted in the command list */
-    if ( pdcolor->type->get_phase(pdcolor, &color_phase) &&
-         (color_phase.x != pcls->tile_phase.x ||
-          color_phase.y != pcls->tile_phase.y || all_bands)        &&
-         (code = cmd_set_tile_phase_generic( cldev,
-                                     pcls,
-                                     color_phase.x,
-                                     color_phase.y, all_bands)) < 0  )
-        return code;
+    /* see if phase information must be inserted in the command list */
+    if (pdcolor->type->get_phase(pdcolor, &color_phase) &&
+        (color_phase.x != pcls->screen_phase[gs_color_select_texture].x ||
+         color_phase.y != pcls->screen_phase[gs_color_select_texture].y || all_bands)) {
+        /* Devc phase is the reverse of screen phase! */
+        code = cmd_set_screen_phase_generic(cldev, pcls,
+                                            -color_phase.x, -color_phase.y,
+                                            gs_color_select_texture, all_bands);
+        if (code < 0)
+            return code;
+    }
 
     CMD_CHECK_LAST_OP_BLOCK_DEFINED(cldev);
     if (is_pattern) {
@@ -200,6 +202,7 @@ cmd_put_drawing_color(gx_device_clist_wr
     }
 
     do {
+        int extop;
         prefix_size = 2 + 1 + (offset > 0 ? enc_u_sizew(offset) : 0);
         req_size = left + prefix_size + enc_u_sizew(left);
         CMD_CHECK_LAST_OP_BLOCK_DEFINED(cldev);
@@ -212,29 +215,29 @@ cmd_put_drawing_color(gx_device_clist_wr
         if (req_size_final > buffer_space)
             return_error(gs_error_unregistered); /* Must not happen. */
         CMD_CHECK_LAST_OP_BLOCK_DEFINED(cldev);
-        if (all_bands)
-            code = set_cmd_put_all_op(&dp, cldev, cmd_opv_extend, req_size_final);
-        else
-            code = set_cmd_put_op(&dp, cldev, pcls, cmd_opv_extend, req_size_final);
-        if (code < 0)
-            return code;
-        dp0 = dp;
         switch (devn_type) {
             case devn_not_tile_fill:
-                dp[1] = cmd_opv_ext_put_fill_dcolor;
+                extop = cmd_opv_ext_put_fill_dcolor;
                 break;
             case devn_not_tile_stroke:
-                dp[1] = cmd_opv_ext_put_stroke_dcolor;
+                extop = cmd_opv_ext_put_stroke_dcolor;
                 break;
             case devn_tile0:
-                dp[1] = cmd_opv_ext_put_tile_devn_color0;
+                extop = cmd_opv_ext_put_tile_devn_color0;
                 break;
             case devn_tile1:
-                dp[1] = cmd_opv_ext_put_tile_devn_color1;
+                extop = cmd_opv_ext_put_tile_devn_color1;
                 break;
             default:
-                dp[1] = cmd_opv_ext_put_fill_dcolor;
+                extop = cmd_opv_ext_put_fill_dcolor;
         }
+        if (all_bands)
+            code = set_cmd_put_all_extended_op(&dp, cldev, extop, req_size_final);
+        else
+            code = set_cmd_put_extended_op(&dp, cldev, pcls, extop, req_size_final);
+        if (code < 0)
+            return code;
+        dp0 = dp;
         dp += 2;
         *dp++ = di | (offset > 0 ? 0x80 : 0);
         if (offset > 0)
@@ -597,56 +600,99 @@ cmd_write_unknown(gx_device_clist_writer
         int ymin = (pcls - cldev->states) * band_height;
         int ymax = min(ymin + band_height, cldev->height);
         gs_fixed_rect box;
-        bool punt_to_outer_box = false;
         int code;
+        int fill_adjust_size;
+        enum {
+            write_path_as_rect = 0,
+            write_path_as_rects = 1,
+            write_path_as_outer_box = 2,
+            write_path_as_path = 3
+        } method;
+
+        /* We are going to begin_clip followed by the fill_adjust to use.
+         * In order to know what fill_adjust to use, we need to know whether
+         * we are going to send the clip through based upon its actual
+         * 'path' entry, or whether we are going to send it based upon its
+         * rectangle list representation. Accordingly, we have to do the
+         * logic to figure out how we are going to send it now. */
+        if (pcpath->path_valid) {
+            if (gx_path_is_rectangle(&pcpath->path, &box) &&
+                fixed_is_int(box.p.x | box.p.y | box.q.x | box.q.y))
+                method = write_path_as_rect;
+            else if ( !(cldev->disable_mask & clist_disable_complex_clip) )
+                method = write_path_as_path;
+            else
+                method = write_path_as_outer_box;
+        } else {
+            const gx_clip_list *list = gx_cpath_list(pcpath);
+            const gx_clip_rect *prect = list->head;
 
-        code = set_cmd_put_op(&dp, cldev, pcls, cmd_opv_begin_clip, 1);
+            if (prect != NULL &&
+                cldev->disable_mask & clist_disable_complex_clip)
+                method = write_path_as_outer_box;
+            else
+                method = write_path_as_rects;
+        }
+
+        /* And thus how large the fill_adjust values will be. */
+        if (method == write_path_as_path)
+            fill_adjust_size = cmd_size2w(pcpath->path_fill_adjust.x,
+                                          pcpath->path_fill_adjust.y);
+        else
+            fill_adjust_size = cmd_size2w(0, 0);
+
+        /* Send the 'begin_clip' with the fill_adjust values. */
+        code = set_cmd_put_op(&dp, cldev, pcls, cmd_opv_begin_clip, 1+fill_adjust_size);
         if (code < 0)
             return code;
-        if (pcpath->path_valid) {
-            if (gx_path_is_rectangle(&pcpath->path, &box) &&
-                fixed_is_int(box.p.x | box.p.y | box.q.x | box.q.y)
-                ) {
-                /* Write the path as a rectangle. */
-                code = cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
-                                          fixed2int_var(box.p.x),
-                                          fixed2int_var(box.p.y),
-                                          fixed2int(box.q.x - box.p.x),
-                                          fixed2int(box.q.y - box.p.y));
-            } else if ( !(cldev->disable_mask & clist_disable_complex_clip) ) {
-                /* Write the path. */
-                code = cmd_put_path(cldev, pcls, &pcpath->path,
-                                    int2fixed(ymin - 1),
-                                    int2fixed(ymax + 1),
-                                    (byte)(pcpath->rule == gx_rule_even_odd ?
-                                     cmd_opv_eofill : cmd_opv_fill),
-                                    true, sn_not_first);
-            } else {
-                  /* Complex paths disabled: write outer box as clip */
-                  punt_to_outer_box = true;
-            }
-        } else {		/* Write out the rectangles. */
+        dp++;
+        if (method == write_path_as_path)
+            cmd_put2w(pcpath->path_fill_adjust.x,
+                      pcpath->path_fill_adjust.y, &dp);
+        else
+            cmd_put2w(0, 0, &dp);
+
+        /* Then send the actual clip path representation. */
+        switch (method)
+        {
+        case write_path_as_rect:
+            /* Write the path as a rectangle. */
+            code = cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
+                                      fixed2int_var(box.p.x),
+                                      fixed2int_var(box.p.y),
+                                      fixed2int(box.q.x - box.p.x),
+                                      fixed2int(box.q.y - box.p.y));
+            break;
+        case write_path_as_path:
+            /* Write the path. */
+            code = cmd_put_path(cldev, pcls, &pcpath->path,
+                                int2fixed(ymin - 1),
+                                int2fixed(ymax + 1),
+                                (byte)(pcpath->rule == gx_rule_even_odd ?
+                                 cmd_opv_eofill : cmd_opv_fill),
+                                true, sn_not_first);
+            break;
+        case write_path_as_rects:
+        {
+            /* Write out the rectangles. */
             const gx_clip_list *list = gx_cpath_list(pcpath);
             const gx_clip_rect *prect = list->head;
 
             if (prect == 0)
                 prect = &list->single;
-            else if (cldev->disable_mask & clist_disable_complex_clip)
-                punt_to_outer_box = true;
-            if (!punt_to_outer_box) {
-                for (; prect != 0 && code >= 0; prect = prect->next)
-                    if (prect->xmax > prect->xmin &&
-                        prect->ymin < ymax && prect->ymax > ymin
-                        ) {
-                        code =
-                            cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
-                                               prect->xmin, prect->ymin,
-                                               prect->xmax - prect->xmin,
-                                       prect->ymax - prect->ymin);
-                    }
+            for (; prect != 0 && code >= 0; prect = prect->next) {
+                if (prect->xmax > prect->xmin &&
+                    prect->ymin < ymax && prect->ymax > ymin) {
+                    code = cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
+                                              prect->xmin, prect->ymin,
+                                              prect->xmax - prect->xmin,
+                                              prect->ymax - prect->ymin);
+                }
             }
+            break;
         }
-        if (punt_to_outer_box) {
+        default:
+        {
             /* Clip is complex, but disabled. Write out the outer box */
             gs_fixed_rect box;
 
@@ -659,6 +705,9 @@ cmd_write_unknown(gx_device_clist_writer
                                       fixed2int_ceiling(box.q.x - box.p.x),
                                       fixed2int_ceiling(box.q.y - box.p.y));
         }
+        }
+
+        /* And now we can send 'end_clip' so the reader can finalise everything. */
         {
             int end_code =
                 set_cmd_put_op(&dp, cldev, pcls, cmd_opv_end_clip, 1);
@@ -715,10 +764,10 @@ cmd_write_unknown(gx_device_clist_writer
         } else {
             code = set_cmd_put_op(&dp, cldev, pcls, cmd_opv_set_color_space,
                 2 + sizeof(clist_icc_color_t));
-            memcpy(dp + 2, &(cldev->color_space.icc_info),
-                   sizeof(clist_icc_color_t));
             if (code < 0)
                 return code;
+            memcpy(dp + 2, &(cldev->color_space.icc_info),
+                   sizeof(clist_icc_color_t));
         }
         dp[1] = cldev->color_space.byte1;
         pcls->known |= color_space_known;
@@ -1204,8 +1253,17 @@ clist_stroke_path(gx_device * dev, const
                 return code;
         if (code < 0) {
             /* Something went wrong, use the default implementation. */
-            return gx_default_stroke_path(dev, pgs, ppath, params, pdcolor,
+            cdev->cropping_saved = false;
+            code = gx_default_stroke_path(dev, pgs, ppath, params, pdcolor,
                                           pcpath);
+            if (cdev->cropping_saved) {
+                cdev->cropping_min = cdev->save_cropping_min;
+                cdev->cropping_max = cdev->save_cropping_max;
+                if_debug2m('v', cdev->memory,
+                           "[v] clist_stroke_path: restore cropping_min=%d croping_max=%d\n",
+                           cdev->save_cropping_min, cdev->save_cropping_max);
+            }
+            return code;
         }
         re.pcls->color_usage.slow_rop |= slow_rop;
         CMD_CHECK_LAST_OP_BLOCK_DEFINED(cdev);
diff -pruN 9.55.0~dfsg-3/base/gxclpath.h 9.56.1~dfsg-1/base/gxclpath.h
--- 9.55.0~dfsg-3/base/gxclpath.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclpath.h	2022-04-04 13:46:22.000000000 +0000
@@ -28,27 +28,27 @@
  * various miscellaneous parameters (pcls->known).  The first N flags match
  * the mask parameter for cmd_set_misc2 below.
  */
-#define cap_join_known		(1<<0)
-#define cj_ac_sa_known		(1<<1)
-#define flatness_known		(1<<2)
-#define line_width_known	(1<<3)
-#define miter_limit_known	(1<<4)
-#define op_bm_tk_known		(1<<5)
+#define cap_join_known          (1<<0)
+#define cj_ac_sa_known          (1<<1)
+#define flatness_known          (1<<2)
+#define line_width_known        (1<<3)
+#define miter_limit_known       (1<<4)
+#define op_bm_tk_known          (1<<5)
 /* segment_notes must fit in the first byte (i.e. be less than 1<<7). */
-#define segment_notes_known	(1<<6) /* not used in pcls->known */
+#define segment_notes_known     (1<<6) /* not used in pcls->known */
 /* (flags beyond this point require an extra byte) */
-#define ais_known	(1<<7)
-#define stroke_alpha_known	(1<<8)
-#define fill_alpha_known		(1<<9)
-#define misc2_all_known		((1<<10)-1)
+#define ais_known               (1<<7)
+#define stroke_alpha_known      (1<<8)
+#define fill_alpha_known        (1<<9)
+#define misc2_all_known         ((1<<10)-1)
 /* End of misc2 flags. */
 /* The following bits don't get passed in misc2, so are only limited by sizeof uint */
-#define fill_adjust_known	(1<<10)
-#define ctm_known		(1<<11)
-#define dash_known		(1<<12)
-#define clip_path_known		(1<<13)
-#define STROKE_ALL_KNOWN	((1<<14)-1)
-#define color_space_known	(1<<14)
+#define fill_adjust_known       (1<<10)
+#define ctm_known               (1<<11)
+#define dash_known              (1<<12)
+#define clip_path_known         (1<<13)
+#define STROKE_ALL_KNOWN        ((1<<14)-1)
+#define color_space_known       (1<<14)
 /*#define all_known             ((1<<15)-1) */
 
 /* Define the drawing color types for distinguishing different */
@@ -59,108 +59,6 @@ typedef enum {
     cmd_dc_type_color = 2
 } cmd_dc_type;
 
-/* Extend the command set.  See gxcldev.h for more information. */
-typedef enum {
-    cmd_op_misc2 = 0xd0,	/* (see below) */
-    cmd_op_fill_rect_hl = 0xd1,  /* rect fill with devn color */
-    cmd_opv_set_fill_adjust = 0xd2,	/* adjust_x/y(fixed) */
-    cmd_opv_set_ctm = 0xd3,	/* [per sput/sget_matrix] */
-    cmd_opv_set_color_space = 0xd4,	/* base(4)Indexed?(2)0(2) */
-                                /* [, hival#, table|map] */
-    /*
-     * cmd_opv_set_misc2_value is followed by a mask (a variable-length
-     * integer), and then by parameter values for the parameters selected
-     * by the mask.  See above for the "known" mask values.
-     */
-    /* cap_join: 0(2)cap(3)join(3) */
-    /* cj_ac_sa: 0(3)curve_join+1(3)acc.curves(1)stroke_adj(1) */
-    /* flatness: (float) */
-    /* line width: (float) */
-    /* miter limit: (float) */
-    /* op_bm_tk: blend mode(5)text knockout(1)o.p.mode(1)o.p.(1) */
-    /* segment notes: (byte) */
-    /* opacity/shape: alpha(float)mask(TBD) */
-    /* alpha: <<verbatim copy from gs_gstate>> */
-    cmd_opv_set_misc2 = 0xd5,	/* mask#, selected parameters */
-    cmd_opv_set_dash = 0xd6,	/* adapt(1)abs.dot(1)n(6), dot */
-                                /* length(float), offset(float), */
-                                /* n x (float) */
-    cmd_opv_enable_clip = 0xd7,	/* (nothing) */
-    cmd_opv_disable_clip = 0xd8,	/* (nothing) */
-    cmd_opv_begin_clip = 0xd9,	/* (nothing) */
-    cmd_opv_end_clip = 0xda,	/* (nothing) */
-    cmd_opv_begin_image_rect = 0xdb, /* same as begin_image, followed by */
-                                /* x0#, w-x1#, y0#, h-y1# */
-    cmd_opv_begin_image = 0xdc,	/* image_type_table index, */
-                                /* [per image type] */
-    cmd_opv_image_data = 0xdd,	/* height# (premature EOD if 0), */
-                                /* raster#, <data> */
-    cmd_opv_image_plane_data = 0xde, /* height# (premature EOD if 0), */
-                                /* flags# (0 = same raster & data_x, */
-                                /* 1 = new raster & data_x, lsb first), */
-                                /* [raster#, [data_x#,]]* <data> */
-    cmd_opv_extend = 0xdf,	/* command, varies (see gx_cmd_ext_op below) */
-
-
-#define cmd_misc2_op_name_strings\
-  "?d0?", "fill_hl_color", \
-  "set_fill_adjust", "set_ctm",\
-  "set_color_space", "set_misc2", "set_dash", "enable_clip",\
-  "disable_clip", "begin_clip", "end_clip", "begin_image_rect",\
-  "begin_image", "image_data", "image_plane_data", "extended"
-
-    cmd_op_segment = 0xe0,	/* (see below) */
-    cmd_opv_rmoveto = 0xe0,	/* dx%, dy% */
-    cmd_opv_rlineto = 0xe1,	/* dx%, dy% */
-    cmd_opv_hlineto = 0xe2,	/* dx% */
-    cmd_opv_vlineto = 0xe3,	/* dy% */
-    cmd_opv_rmlineto = 0xe4,	/* dx1%,dy1%, dx2%,dy2% */
-    cmd_opv_rm2lineto = 0xe5,	/* dx1%,dy1%, dx2%,dy2%, dx3%,dy3% */
-    cmd_opv_rm3lineto = 0xe6,	/* dx1%,dy1%, dx2%,dy2%, dx3%,dy3%, */
-                                /* [-dx2,-dy2 implicit] */
-    cmd_opv_rrcurveto = 0xe7,	/* dx1%,dy1%, dx2%,dy2%, dx3%,dy3% */
-    cmd_opv_min_curveto = cmd_opv_rrcurveto,
-    cmd_opv_hvcurveto = 0xe8,	/* dx1%, dx2%,dy2%, dy3% */
-    cmd_opv_vhcurveto = 0xe9,	/* dy1%, dx2%,dy2%, dx3% */
-    cmd_opv_nrcurveto = 0xea,	/* dx2%,dy2%, dx3%,dy3% */
-    cmd_opv_rncurveto = 0xeb,	/* dx1%,dy1%, dx2%,dy2% */
-    cmd_opv_vqcurveto = 0xec,	/* dy1%, dx2%[,dy2=dx2 with sign */
-                                /* of dy1, dx3=dy1 with sign of dx2] */
-    cmd_opv_hqcurveto = 0xed,	/* dx1%, [dx2=dy2 with sign */
-                                /* of dx1,]%dy2, [dy3=dx1 with sign */
-                                /* of dy2] */
-    cmd_opv_scurveto = 0xee,	/* all implicit: previous op must have been */
-                                /* *curveto with one or more of dx/y1/3 = 0. */
-                                /* If h*: -dx3,dy3, -dx2,dy2, -dx1,dy1. */
-                                /* If v*: dx3,-dy3, dx2,-dy2, dx1,-dy1. */
-    cmd_opv_max_curveto = cmd_opv_scurveto,
-    cmd_opv_closepath = 0xef,	/* (nothing) */
-
-#define cmd_segment_op_name_strings\
-  "rmoveto", "rlineto", "hlineto", "vlineto",\
-  "rmlineto", "rm2lineto", "rm3lineto", "rrcurveto",\
-  "hvcurveto", "vhcurveto", "nrcurveto", "rncurveto",\
-  "vqcurveto", "hqcurveto", "scurveto", "closepath"
-
-    cmd_op_path = 0xf0,		/* (see below) */
-    cmd_opv_fill = 0xf0,
-    cmd_opv_rgapto = 0xf1, 	/* dx%, dy% */
-    cmd_opv_eofill = 0xf3,
-    cmd_opv_fill_stroke = 0xf4,
-    cmd_opv_eofill_stroke = 0xf5,
-    cmd_opv_stroke = 0xf6,
-    cmd_opv_polyfill = 0xf9,
-    cmd_opv_fill_trapezoid = 0xfc
-
-#define cmd_path_op_name_strings\
-  "fill", "rgapto", "?f2?", "eofill",\
-  "fill_stroke", "eofill_stroke", "stroke", "?f7?",\
-  "?f8?", "polyfill", "?fa?", "?fb?",\
-  "fill_trapezoid", "?fd?", "?fe?", "?ff?"
-
-/* unused cmd_op values: 0xd0, 0xf2, 0xf7, 0xf8, 0xfa, 0xfb, 0xfd, 0xfe, 0xff */
-} gx_cmd_xop;
-
 /* This is usd for cmd_opv_ext_put_drawing_color so that we know if it
    is assocated with a tile or not and for fill or stroke color */
 typedef enum {
@@ -174,23 +72,40 @@ typedef enum {
  * is the second byte of a command whose first byte is cmd_opv_extend.
  */
 typedef enum {
-    cmd_opv_ext_put_params = 0x00,           /* serialized parameter list */
-    cmd_opv_ext_composite = 0x01,    /* compositor id,
+    cmd_opv_ext_put_params           = 0x00, /* serialized parameter list */
+    cmd_opv_ext_composite            = 0x01, /* compositor id,
                                               * serialized compositor */
-    cmd_opv_ext_put_halftone = 0x02,         /* length of entire halftone */
-    cmd_opv_ext_put_ht_seg = 0x03,           /* segment length,
+    cmd_opv_ext_put_halftone         = 0x02, /* length of entire halftone */
+    cmd_opv_ext_put_ht_seg           = 0x03, /* segment length,
                                               * halftone segment data */
-    cmd_opv_ext_put_fill_dcolor = 0x04,      /* length, color type id,
+    cmd_opv_ext_put_fill_dcolor      = 0x04, /* length, color type id,
                                               * serialized color */
-    cmd_opv_ext_put_stroke_dcolor = 0x05,    /* length, color type id,
+    cmd_opv_ext_put_stroke_dcolor    = 0x05, /* length, color type id,
                                               * serialized color */
-    cmd_opv_ext_tile_rect_hl = 0x06,         /* Uses devn colors in tiling fill */
+    cmd_opv_ext_tile_rect_hl         = 0x06, /* Uses devn colors in tiling fill */
     cmd_opv_ext_put_tile_devn_color0 = 0x07, /* Devn color0 for tile filling */
     cmd_opv_ext_put_tile_devn_color1 = 0x08, /* Devn color1 for tile filling */
-    cmd_opv_ext_set_color_is_devn = 0x09,    /* Used for overload of copy_color_alpha */
-    cmd_opv_ext_unset_color_is_devn = 0x0a   /* Used for overload of copy_color_alpha */
+    cmd_opv_ext_set_color_is_devn    = 0x09, /* Used for overload of copy_color_alpha */
+    cmd_opv_ext_unset_color_is_devn  = 0x0a  /* Used for overload of copy_color_alpha */
 } gx_cmd_ext_op;
 
+#ifdef DEBUG
+#define cmd_extend_op_name_strings \
+  "put_params",\
+  "composite",\
+  "put_halftone",\
+  "put_ht_seg",\
+  "put_fill_dcolor",\
+  "put_stroke_dcolor",\
+  "tile_rect_hl",\
+  "put_tile_devn_color0",\
+  "put_tile_devn_color1",\
+  "set_color_is_devn",\
+  "unset_color_is_devn"
+
+extern const char *cmd_extend_op_names[256];
+#endif
+
 #define cmd_segment_op_num_operands_values\
   2, 2, 1, 1, 4, 6, 6, 6, 4, 4, 4, 4, 2, 2, 0, 0
 
diff -pruN 9.55.0~dfsg-3/base/gxclrast.c 9.56.1~dfsg-1/base/gxclrast.c
--- 9.55.0~dfsg-3/base/gxclrast.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclrast.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -149,6 +149,27 @@ set_cb_end(command_buf_t *pcb, const byt
                                         /**** limit and should check 'end'    ****/
 }
 
+static inline void
+advance_buffer(command_buf_t *pcb, const byte *cbp)
+{
+#ifdef DEBUG
+    stream_state *st = pcb->s->state;
+
+    top_up_offset_map(st, pcb->data, cbp, pcb->end);
+#endif
+    memmove(pcb->data, cbp, pcb->end - cbp);
+}
+
+static inline void
+next_is_skip(command_buf_t *pcb)
+{
+#ifdef DEBUG
+    stream_state *st = pcb->s->state;
+
+    offset_map_next_data_out_of_band(st);
+#endif
+}
+
 /* Read more data into a command buffer. */
 static int
 top_up_cbuf(command_buf_t *pcb, const byte **pcbp)
@@ -156,12 +177,9 @@ top_up_cbuf(command_buf_t *pcb, const by
     uint nread;
     const byte *cbp = *pcbp;
     byte *cb_top = pcb->data + (pcb->end - cbp);
-#   ifdef DEBUG
-    stream_state *st = pcb->s->state;
-#   endif
 
-    if (pcb->end - cbp >= pcb->size) {
-        errprintf(pcb->s->memory, "Clist I/O error: cbp past end of buffer\n");
+    if (cbp < pcb->data || cbp > pcb->end) {
+        errprintf(pcb->s->memory, "Clist I/O error: cbp outside of buffer\n");
         return (gs_error_ioerror);
     }
 
@@ -170,15 +188,7 @@ top_up_cbuf(command_buf_t *pcb, const by
         pcb->end_status = pcb->s->end_status;
         return 0;
     }
-#   ifdef DEBUG
-    {
-        int code = top_up_offset_map(st, pcb->data, cbp, pcb->end);
-
-        if (code < 0)
-            return code;
-    }
-#   endif
-    memmove(pcb->data, cbp, pcb->end - cbp);
+    advance_buffer(pcb, cbp);
     nread = pcb->end - cb_top;
     pcb->end_status = sgets(pcb->s, cb_top, nread, &nread);
     if ( nread == 0 ) {
@@ -199,6 +209,8 @@ top_up_cbuf(command_buf_t *pcb, const by
 }
 
 /* Read data from the command buffer and stream. */
+/* From the command_buffer pcb, read rsize bytes to ptr, starting from cbp.
+ * Return the new value for pcb->ptr. */
 static const byte *
 cmd_read_data(command_buf_t *pcb, byte *ptr, uint rsize, const byte *cbp)
 {
@@ -211,11 +223,16 @@ cmd_read_data(command_buf_t *pcb, byte *
 
         memmove(ptr, cbp, cleft);
         sgets(pcb->s, ptr + cleft, rleft, &rleft);
+#ifdef DEBUG
+        {
+            stream_state *st = pcb->s->state;
+
+            adjust_offset_map_for_skipped_data(st, (uint)(pcb->end - pcb->data), rleft);
+        }
+#endif
         return pcb->end;
     }
 }
-#define cmd_read(ptr, rsize, cbp)\
-  cbp = cmd_read_data(&cbuf, ptr, rsize, cbp)
 
 /* Read a fixed-size value from the command buffer. */
 static inline const byte *
@@ -472,11 +489,29 @@ read_set_misc_map(byte cb, command_buf_t
     return 0;
 }
 
+#ifdef DEBUG
+void clist_debug_op(gs_memory_t *mem, const unsigned char *cbp)
+{
+    unsigned char op = *cbp++;
+    const char *const *sub = cmd_sub_op_names[op >> 4];
+    if (op == cmd_opv_extend) {
+        unsigned char op2 = *cbp;
+        if (cmd_extend_op_names[op2])
+            dmlprintf1(mem, " %s", cmd_extend_op_names[op2]);
+        else
+            dmlprintf1(mem, " ?0x%02x?", (int)op2);
+    } else if (sub)
+        dmlprintf1(mem, " %s", sub[op & 0xf]);
+    else
+        dmlprintf2(mem, " %s %d", cmd_op_names[op >> 4], op & 0xf);
+}
+#endif
+
 int
-clist_playback_band(clist_playback_action playback_action,
+clist_playback_band(clist_playback_action playback_action, /* lgtm [cpp/use-of-goto] */
                     gx_device_clist_reader *cdev, stream *s,
                     gx_device *target, int x0, int y0,
-                    gs_memory_t * mem) /* lgtm [cpp/use-of-goto] */
+                    gs_memory_t * mem)
 {
     byte *cbuf_storage;
     command_buf_t cbuf;
@@ -495,7 +530,7 @@ clist_playback_band(clist_playback_actio
     tile_slot *state_slot;
     gx_strip_bitmap state_tile; /* parameters for reading tiles */
     tile_slot tile_bits;        /* parameters of current tile */
-    gs_int_point tile_phase, color_phase;
+    gs_int_point tile_phase;
     gx_path path;
     bool in_path;
     gs_fixed_point ppos;
@@ -590,8 +625,8 @@ in:                             /* Initi
     state_tile.shift = state_tile.rep_shift = 0;
     state_tile.size.x = state_tile.size.y = 0;
     state_tile.num_planes = 1;
-    tile_phase.x = color_phase.x = x0;
-    tile_phase.y = color_phase.y = y0;
+    tile_phase.x = x0;
+    tile_phase.y = y0;
     gx_path_init_local(&path, mem);
     in_path = false;
     /*
@@ -624,8 +659,10 @@ in:                             /* Initi
         goto out;
     }
     code = pcs->type->install_cspace(pcs, &gs_gstate);
-    if (code < 0)
+    if (code < 0) {
+        rc_decrement(pcs, "clist_playback_band");
         goto out;
+    }
     gs_gstate.color[0].color_space = pcs; /* we already have one ref */
     gs_gstate.color[1].color_space = pcs;
     rc_increment_cs(pcs); /* increment for second ref */
@@ -710,14 +747,10 @@ in:                             /* Initi
         op = *cbp++;
 #ifdef DEBUG
         if (gs_debug_c('L')) {
-            const char *const *sub = cmd_sub_op_names[op >> 4];
             long offset = (long)clist_file_offset(st, cbp - 1 - cbuf.data);
 
-            if (sub)
-                dmlprintf1(mem, "[L]%s", sub[op & 0xf]);
-            else
-                dmlprintf2(mem, "[L]%s %d", cmd_op_names[op >> 4], op & 0xf);
-            dmlprintf1(mem, "(offset=%ld):", offset);
+            dmlprintf1(mem, "[L] %ld:", offset);
+            clist_debug_op(mem, cbp-1);
         }
 #endif
         switch (op >> 4) {
@@ -740,7 +773,29 @@ in:                             /* Initi
                         if_debug2m('L', mem, " (%d,%d)\n",
                                    state.tile_phase.x,
                                    state.tile_phase.y);
-                        goto set_phase;
+                        goto set_tile_phase;
+                    case cmd_opv_set_screen_phaseT:
+                        cmd_getw(state.screen_phase[gs_color_select_texture].x, cbp);
+                        cmd_getw(state.screen_phase[gs_color_select_texture].y, cbp);
+                        if_debug2m('L', mem, " (%d,%d)\n",
+                                   state.screen_phase[0].x,
+                                   state.screen_phase[gs_color_select_texture].y);
+                        gx_gstate_setscreenphase(&gs_gstate,
+                                                 state.screen_phase[gs_color_select_texture].x - x0,
+                                                 state.screen_phase[gs_color_select_texture].y - y0,
+                                                 gs_color_select_texture);
+                        continue;
+                    case cmd_opv_set_screen_phaseS:
+                        cmd_getw(state.screen_phase[gs_color_select_source].x, cbp);
+                        cmd_getw(state.screen_phase[gs_color_select_source].y, cbp);
+                        if_debug2m('L', mem, " (%d,%d)\n",
+                                   state.screen_phase[gs_color_select_source].x,
+                                   state.screen_phase[gs_color_select_source].y);
+                        gx_gstate_setscreenphase(&gs_gstate,
+                                                 state.screen_phase[gs_color_select_source].x - x0,
+                                                 state.screen_phase[gs_color_select_source].y - y0,
+                                                 gs_color_select_source);
+                        continue;
                     case cmd_opv_set_tile_bits:
                         bits = tile_bits;
                         compress = 0;
@@ -754,18 +809,22 @@ in:                             /* Initi
                             goto out;
                         goto stp;
                     case cmd_opv_set_bits:
+do_opv_set_bits:
                         compress = *cbp & 3;
-                        bits.cb_depth = *cbp++ >> 2;
+                        bits.head.depth = *cbp++ >> 2;
                         cmd_getw(bits.width, cbp);
                         cmd_getw(bits.height, cbp);
-                        if_debug4m('L', mem, " compress=%d depth=%d size=(%d,%d)",
-                                   compress, bits.cb_depth,
-                                   bits.width, bits.height);
-                        bits.cb_raster =
-                            bitmap_raster(bits.width * bits.cb_depth);
+                        if (op == cmd_opv_set_bits_planar)
+                            cmd_getw(bits.num_planes, cbp);
+                        else
+                            bits.num_planes = 1;
+                        if_debug5m('L', mem, " compress=%d depth=%d size=(%d,%d) planes=%d",
+                                   compress, bits.head.depth,
+                                   bits.width, bits.height, bits.num_planes);
+                        bits.raster =
+                            bitmap_raster(bits.width * bits.head.depth);
                         bits.x_reps = bits.y_reps = 1;
                         bits.shift = bits.rep_shift = 0;
-                        bits.num_planes = 1;
                         goto stb;
                     case cmd_opv_set_tile_color:
                         set_colors = state.tile_colors;
@@ -1023,12 +1082,12 @@ in:                             /* Initi
                         goto out;
                     }
 #endif
-                    depth = state_slot->cb_depth;
+                    depth = state_slot->head.depth;
                     state.rect.width = state_slot->width;
                     state.rect.height = state_slot->height;
                     if (state.rect.y + state.rect.height > cdev->height)
                         state.rect.height = cdev->height - state.rect.y;	/* clamp as writer did */
-                    raster = state_slot->cb_raster;
+                    raster = state_slot->raster;
                     source = (byte *) (state_slot + 1);
                 } else {        /* Read width, height, bits. */
                     /* depth was set already. */
@@ -1100,12 +1159,7 @@ in:                             /* Initi
                             if (cleft < bytes  && !cbuf.end_status) {
                                 uint nread = cbuf_size - cleft;
 
-#                               ifdef DEBUG
-                                    code = top_up_offset_map(st, cbuf.data, cbp, cbuf.end);
-                                    if (code < 0)
-                                        goto top_up_failed;
-#                               endif
-                                memmove(cbuf.data, cbp, cleft);
+                                advance_buffer(&cbuf, cbp);
                                 cbuf.end_status = sgets(s, cbuf.data + cleft, nread, &nread);
                                 set_cb_end(&cbuf, cbuf.data + cleft + nread);
                                 cbp = cbuf.data;
@@ -1153,7 +1207,7 @@ in:                             /* Initi
                                 source = data_bits;
                         } else {
                             /* Never used for planar data */
-                            cmd_read(cbuf.data, bytes, cbp);
+                            cbp = cmd_read_data(&cbuf, cbuf.data, bytes, cbp);
                             source = cbuf.data;
                         }
                     }
@@ -1182,7 +1236,7 @@ in:                             /* Initi
                 state_tile.data = (byte *) (state_slot + 1);
               stp:state_tile.size.x = state_slot->width;
                 state_tile.size.y = state_slot->height;
-                state_tile.raster = state_slot->cb_raster;
+                state_tile.raster = state_slot->raster;
                 state_tile.rep_width = state_tile.size.x /
                     state_slot->x_reps;
                 state_tile.rep_height = state_tile.size.y /
@@ -1191,28 +1245,10 @@ in:                             /* Initi
                 state_tile.shift = state_slot->shift;
                 state_tile.id = state_slot->id;
                 state_tile.num_planes = state_slot->num_planes;
-set_phase:      /*
-                 * state.tile_phase is overloaded according to the command
-                 * to which it will apply:
-                 *      For fill_path, stroke_path, fill_triangle/p'gram,
-                 * fill_mask, and (mask or CombineWithColor) images,
-                 * it is pdcolor->phase.  For these operations, we
-                 * precompute the color_phase values.
-                 *      For strip_tile_rectangle and strip_copy_rop,
-                 * it is the phase arguments of the call, used with
-                 * state_tile.  For these operations, we precompute the
-                 * tile_phase values.
-                 *
-                 * Note that control may get here before one or both of
-                 * state_tile or dev_ht has been set.
-                 */
+set_tile_phase:
                 if (state_tile.size.x)
                     tile_phase.x =
                         (state.tile_phase.x + x0) % state_tile.size.x;
-                if (gs_gstate.dev_ht[HT_OBJTYPE_DEFAULT] && gs_gstate.dev_ht[HT_OBJTYPE_DEFAULT]->lcm_width)
-                    color_phase.x =
-                        (state.tile_phase.x + x0) %
-                        gs_gstate.dev_ht[HT_OBJTYPE_DEFAULT]->lcm_width;
                 /*
                  * The true tile height for shifted tiles is not
                  * size.y: see gxbitmap.h for the computation.
@@ -1224,23 +1260,16 @@ set_phase:      /*
                         full_height = state_tile.size.y;
                     else
                         full_height = state_tile.rep_height *
-                            (state_tile.rep_width /
-                             igcd(state_tile.rep_shift,
-                                  state_tile.rep_width));
-                    tile_phase.y =
-                        (state.tile_phase.y + y0) % full_height;
+                                    (state_tile.rep_width /
+                                     igcd(state_tile.rep_shift,
+                                          state_tile.rep_width));
+                    tile_phase.y = (state.tile_phase.y + y0) % full_height;
                 }
-                if (gs_gstate.dev_ht[HT_OBJTYPE_DEFAULT] && gs_gstate.dev_ht[HT_OBJTYPE_DEFAULT]->lcm_height)
-                    color_phase.y =
-                        (state.tile_phase.y + y0) %
-                        gs_gstate.dev_ht[HT_OBJTYPE_DEFAULT]->lcm_height;
-                gx_gstate_setscreenphase(&gs_gstate,
-                                         -(state.tile_phase.x + x0),
-                                         -(state.tile_phase.y + y0),
-                                         gs_color_select_all);
                 continue;
             case cmd_op_misc2 >> 4:
                 switch (op) {
+                    case cmd_opv_set_bits_planar:
+                        goto do_opv_set_bits;
                     case cmd_opv_set_fill_adjust:
                         cmd_get_value(gs_gstate.fill_adjust.x, cbp);
                         cmd_get_value(gs_gstate.fill_adjust.y, cbp);
@@ -1333,10 +1362,8 @@ set_phase:      /*
                         clip_save.dcolor = fill_color;
                         clip_save.fa_save.x = gs_gstate.fill_adjust.x;
                         clip_save.fa_save.y = gs_gstate.fill_adjust.y;
-                        /* clip_path should match fill_path, i.e., with fill_adjust applied	*/
-                        /* If we get here with the fill_adjust = [0, 0], set it to [0.5, 0.5]i	*/
-                        if (clip_save.fa_save.x == 0 || clip_save.fa_save.y == 0)
-                            gs_gstate.fill_adjust.x = gs_gstate.fill_adjust.y = fixed_half;
+                        cmd_getw(gs_gstate.fill_adjust.x, cbp);
+                        cmd_getw(gs_gstate.fill_adjust.y, cbp);
                         /* temporarily set a solid color */
                         color_set_pure(&fill_color, (gx_color_index)1);
                         state.lop_enabled = false;
@@ -1481,7 +1508,7 @@ ibegin:                 if_debug0m('L',
                                         if (code < 0)
                                             goto top_up_failed;
                                     }
-                                    cmd_getw(planes[plane].raster, cbp)                                ;
+                                    cmd_getw(planes[plane].raster, cbp);
                                     if ((raster1 = planes[plane].raster) != 0)
                                         cmd_getw(data_x, cbp);
                                 } else {
@@ -1550,6 +1577,8 @@ idata:                  data_size = 0;
                             } else
                                 rdata = cbuf.data;
                             memmove(rdata, cbp, cleft);
+                            if (data_on_heap)
+                                next_is_skip(&cbuf);
                             if (sgets(s, rdata + cleft, rleft, &rleft) < 0) {
                                 code = gs_note_error(gs_error_unregistered); /* Must not happen. */
                                 goto out;
@@ -1637,6 +1666,15 @@ idata:                  data_size = 0;
                                         if (code < 0)
                                             goto out;
                                     }
+#ifdef DEBUG
+                                    if (gs_debug_c('L')) {
+                                       long offset = (long)clist_file_offset(st, cbp - cbuf.data);
+
+                                       dmlprintf1(mem, "[L]  %ld:", offset);
+                                       clist_debug_op(mem, cbp);
+                                       dmlprintf(mem, "\n");
+                                    }
+#endif
                                     if (cbp[0] == cmd_opv_extend && cbp[1] == cmd_opv_ext_composite) {
                                         gs_composite_t *pcomp, *pcomp_opening;
                                         gs_compositor_closing_state closing_state;
@@ -1869,7 +1907,7 @@ idata:                  data_size = 0;
                                            see gx_dc_null_read.*/
                                         code = pdct->read(pdcolor, &gs_gstate,
                                                           pdcolor, tdev, offset,
-                                                          cbp, 0, mem);
+                                                          cbp, 0, mem, x0, y0);
                                         if (code < 0)
                                             goto out;
                                     }
@@ -1882,7 +1920,7 @@ idata:                  data_size = 0;
                                         l = min(left, cbuf.end - cbp);
                                         code = pdct->read(pdcolor, &gs_gstate,
                                                           pdcolor, tdev, offset,
-                                                          cbp, l, mem);
+                                                          cbp, l, mem, x0, y0);
                                         if (code < 0)
                                             goto out;
                                         l = code;
@@ -2457,7 +2495,7 @@ read_set_tile_size(command_buf_t *pcb, t
     uint pdepth;
     byte bd = *cbp++;
 
-    bits->cb_depth = cmd_code_to_depth(bd);
+    bits->head.depth = cmd_code_to_depth(bd);
     if (for_pattern)
         cmd_getw(bits->id, cbp);
     cmd_getw(rep_width, cbp);
@@ -2485,16 +2523,16 @@ read_set_tile_size(command_buf_t *pcb, t
     else
         bits->num_planes = 1;
     if_debug7('L', " depth=%d size=(%d,%d), rep_size=(%d,%d), rep_shift=%d, num_planes=%d\n",
-              bits->cb_depth, bits->width,
+              bits->head.depth, bits->width,
               bits->height, rep_width,
               rep_height, bits->rep_shift, bits->num_planes);
     bits->shift =
         (bits->rep_shift == 0 ? 0 :
          (bits->rep_shift * (bits->height / rep_height)) % rep_width);
-    pdepth = bits->cb_depth;
+    pdepth = bits->head.depth;
     if (bits->num_planes != 1)
         pdepth /= bits->num_planes;
-    bits->cb_raster = bitmap_raster(bits->width * pdepth);
+    bits->raster = bitmap_raster(bits->width * pdepth);
     pcb->ptr = cbp;
     return 0;
 }
@@ -2515,7 +2553,7 @@ read_set_bits(command_buf_t *pcb, tile_s
     uint bytes;
     byte *data;
     tile_slot *slot;
-    uint depth = bits->cb_depth;
+    uint depth = bits->head.depth;
 
     if (bits->num_planes != 1)
         depth /= bits->num_planes;
@@ -2560,19 +2598,8 @@ read_set_bits(command_buf_t *pcb, tile_s
 
         if (cleft < bytes && !pcb->end_status) {
             uint nread = cbuf_size - cleft;
-#   ifdef DEBUG
-            stream_state *st = pcb->s->state;
-#   endif
-
-#           ifdef DEBUG
-            {
-                int code = top_up_offset_map(st, pcb->data, cbp, pcb->end);
 
-                if (code < 0)
-                    return code;
-            }
-#           endif
-            memmove(pcb->data, cbp, cleft);
+            advance_buffer(pcb, cbp);
             pcb->end_status = sgets(pcb->s, pcb->data + cleft, nread, &nread);
             set_cb_end(pcb, pcb->data + cleft + nread);
             cbp = pcb->data;
@@ -2608,26 +2635,26 @@ read_set_bits(command_buf_t *pcb, tile_s
             return_error(gs_error_unregistered);
         }
         cbp = r.ptr + 1;
-    } else if (rep_height * bits->num_planes > 1 && width_bytes != bits->cb_raster) {
+    } else if (rep_height * bits->num_planes > 1 && width_bytes != bits->raster) {
         cbp = cmd_read_short_bits(pcb, data, bytes,
                                   width_bytes, rep_height * bits->num_planes,
-                                  bits->cb_raster, cbp);
+                                  bits->raster, cbp);
     } else {
         cbp = cmd_read_data(pcb, data, bytes, cbp);
     }
     if (bits->width > rep_width)
         bits_replicate_horizontally(data,
                                     rep_width * depth, rep_height * bits->num_planes,
-                                    bits->cb_raster,
+                                    bits->raster,
                                     bits->width * depth,
-                                    bits->cb_raster);
+                                    bits->raster);
     if (bits->height > rep_height)
         bits_replicate_vertically(data,
-                                  rep_height, bits->cb_raster,
+                                  rep_height, bits->raster,
                                   bits->height);
 #ifdef DEBUG
     if (gs_debug_c('L'))
-        cmd_print_bits(mem, data, bits->width, bits->height, bits->cb_raster);
+        cmd_print_bits(mem, data, bits->width, bits->height, bits->raster);
 #endif
     pcb->ptr = cbp;
     return 0;
@@ -2724,17 +2751,18 @@ read_set_misc2(command_buf_t *pcb, gs_gs
     const byte *cbp = pcb->ptr;
     uint mask, cb;
 
+    if_debug0m('L', pgs->memory, "\n");
     cmd_getw(mask, cbp);
     if (mask & cap_join_known) {
         cb = *cbp++;
         pgs->line_params.start_cap = (gs_line_cap)((cb >> 3) & 7);
         pgs->line_params.join = (gs_line_join)(cb & 7);
-        if_debug2m('L', pgs->memory, " start_cap=%d join=%d\n",
+        if_debug2m('L', pgs->memory, "[L]      start_cap=%d join=%d\n",
                    pgs->line_params.start_cap, pgs->line_params.join);
         cb = *cbp++;
         pgs->line_params.end_cap = (gs_line_cap)((cb >> 3) & 7);
         pgs->line_params.dash_cap = (gs_line_cap)(cb & 7);
-        if_debug2m('L', pgs->memory, "end_cap=%d dash_cap=%d\n",
+        if_debug2m('L', pgs->memory, "[L]      end_cap=%d dash_cap=%d\n",
                    pgs->line_params.end_cap, pgs->line_params.dash_cap);
     }
     if (mask & cj_ac_sa_known) {
@@ -2742,26 +2770,26 @@ read_set_misc2(command_buf_t *pcb, gs_gs
         pgs->line_params.curve_join = ((cb >> 2) & 7) - 1;
         pgs->accurate_curves = (cb & 2) != 0;
         pgs->stroke_adjust = cb & 1;
-        if_debug3m('L', pgs->memory, " CJ=%d AC=%d SA=%d\n",
+        if_debug3m('L', pgs->memory, "[L]      CJ=%d AC=%d SA=%d\n",
                    pgs->line_params.curve_join, pgs->accurate_curves,
                    pgs->stroke_adjust);
     }
     if (mask & flatness_known) {
         cmd_get_value(pgs->flatness, cbp);
-        if_debug1m('L', pgs->memory, " flatness=%g\n", pgs->flatness);
+        if_debug1m('L', pgs->memory, "[L]      flatness=%g\n", pgs->flatness);
     }
     if (mask & line_width_known) {
         float width;
 
         cmd_get_value(width, cbp);
-        if_debug1m('L', pgs->memory, " line_width=%g\n", width);
+        if_debug1m('L', pgs->memory, "[L]      line_width=%g\n", width);
         gx_set_line_width(&pgs->line_params, width);
     }
     if (mask & miter_limit_known) {
         float limit;
 
         cmd_get_value(limit, cbp);
-        if_debug1m('L', pgs->memory, " miter_limit=%g\n", limit);
+        if_debug1m('L', pgs->memory, "[L]      miter_limit=%g\n", limit);
         gx_set_miter_limit(&pgs->line_params, limit);
     }
     if (mask & op_bm_tk_known) {
@@ -2775,26 +2803,26 @@ read_set_misc2(command_buf_t *pcb, gs_gs
         pgs->overprint = cb & 1;
         cb = *cbp++;
         pgs->renderingintent = cb;
-        if_debug6m('L', pgs->memory, " BM=%d TK=%d OPM=%d OP=%d op=%d RI=%d\n",
+        if_debug6m('L', pgs->memory, "[L]      BM=%d TK=%d OPM=%d OP=%d op=%d RI=%d\n",
                    pgs->blend_mode, pgs->text_knockout, pgs->overprint_mode,
                    pgs->stroke_overprint, pgs->overprint, pgs->renderingintent);
     }
     if (mask & segment_notes_known) {
         cb = *cbp++;
         *pnotes = (segment_notes)(cb & 0x3f);
-        if_debug1m('L', pgs->memory, " notes=%d\n", *pnotes);
+        if_debug1m('L', pgs->memory, "[L]      notes=%d\n", *pnotes);
     }
     if (mask & ais_known) {
         cmd_get_value(pgs->alphaisshape, cbp);
-        if_debug1m('L', pgs->memory, " alphaisshape=%d\n", pgs->alphaisshape);
+        if_debug1m('L', pgs->memory, "[L]      alphaisshape=%d\n", pgs->alphaisshape);
     }
     if (mask & stroke_alpha_known) {
         cmd_get_value(pgs->strokeconstantalpha, cbp);
-        if_debug1m('L', pgs->memory, " strokeconstantalpha=%g\n", pgs->strokeconstantalpha);
+        if_debug1m('L', pgs->memory, "[L]      strokeconstantalpha=%g\n", pgs->strokeconstantalpha);
     }
     if (mask & fill_alpha_known) {
         cmd_get_value(pgs->fillconstantalpha, cbp);
-        if_debug1m('L', pgs->memory, " fillconstantalpha=%u\n", (uint)(pgs->fillconstantalpha));
+        if_debug1m('L', pgs->memory, "[L]      fillconstantalpha=%u\n", (uint)(pgs->fillconstantalpha));
     }
     pcb->ptr = cbp;
     return 0;
@@ -2985,6 +3013,7 @@ read_put_params(command_buf_t *pcb, gs_g
         cleft = pcb->end - cbp;
         rleft = param_length - cleft;
         memmove(param_buf, cbp, cleft);
+        next_is_skip(pcb);
         pcb->end_status = sgets(pcb->s, param_buf + cleft, rleft, &rleft);
         cbp = pcb->end;  /* force refill */
     }
diff -pruN 9.55.0~dfsg-3/base/gxclread.c 9.56.1~dfsg-1/base/gxclread.c
--- 9.55.0~dfsg-3/base/gxclread.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclread.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -41,6 +41,8 @@
 #include "stream.h"
 #include "strimpl.h"
 
+/* #define EXTRA_OFFSET_MAP_DEBUGGING */
+
 /* forward decl */
 private_st_clist_icctable_entry();
 private_st_clist_icctable();
@@ -72,8 +74,10 @@ typedef struct stream_band_read_state_s
 #ifdef DEBUG
     bool skip_first;
     cbuf_offset_map_elem *offset_map;
+    int bytes_skipped;
     int offset_map_length;
     int offset_map_max_length;
+    int skip_next;
 #endif
 } stream_band_read_state;
 
@@ -104,15 +108,18 @@ s_band_read_init_offset_map(gx_device_cl
         if (ss->offset_map == NULL)
             return_error(gs_error_VMerror);
         ss->offset_map[0].buffered = 0;
+        ss->bytes_skipped = 0;
         crdev->offset_map = ss->offset_map; /* Prevent collecting it as garbage.
                                             Debugged with ppmraw -r300 014-09.ps . */
     } else {
         ss->offset_map_length = 0;
         ss->offset_map_max_length = 0;
         ss->offset_map = NULL;
+        ss->bytes_skipped = 0;
         crdev->offset_map = NULL;
     }
     ss->skip_first = true;
+    ss->skip_next = 0;
     return 0;
 }
 
@@ -141,15 +148,40 @@ s_band_read_process(stream_state * st, s
     int status = 1;
     uint count;
     const clist_io_procs_t *io_procs = ss->page_info.io_procs;
+    int64_t pos;
 
+    /* left = number of bytes unread in the current command. */
+    /* count = number of bytes we have room in our buffer for. */
     while ((count = wlimit - q) != 0) {
-        if (left) {		/* Read more data for the current run. */
+        int bmin, bmax;
+        /* If there is more data to be read in the current command, then pull that in. */
+        if (left) {
             if (count > left)
                 count = left;
-#	    ifdef DEBUG
-                if (gs_debug_c('L'))
+#ifdef DEBUG
+            if (gs_debug_c('L')) {
+                if (ss->skip_next) {
+                    /* This buffer fill is NOT going into the normal buffer. */
+                    ss->skip_next = 0;
+                    ss->bytes_skipped += count;
+#ifdef EXTRA_OFFSET_MAP_DEBUGGING
+                    if (ss->offset_map_length != 1) {
+                        dmlprintf(ss->local_memory, "offset_map: confused!\n");
+                        exit(1);
+                    }
+#endif
+                } else {
+#ifdef EXTRA_OFFSET_MAP_DEBUGGING
+                    if (ss->offset_map[ss->offset_map_length - 1].buffered + count > cbuf_size*2) {
+                        dmlprintf2(ss->local_memory, "Invalid update to buffered. %d %d\n",
+                                   ss->offset_map[ss->offset_map_length - 1].buffered, count);
+                        exit(1);
+                    }
+#endif
                     ss->offset_map[ss->offset_map_length - 1].buffered += count;
-#	    endif
+                }
+            }
+#endif
             io_procs->fread_chars(q + 1, count, cfile);
             if (io_procs->ferror_code(cfile) < 0) {
                 status = ERRC;
@@ -160,47 +192,51 @@ s_band_read_process(stream_state * st, s
             process_interrupts(ss->local_memory);
             continue;
         }
-rb:
-        /*
-         * Scan for the next run for the current bands (or a band range
-         * that includes a current band).
-         */
-        if (ss->b_this.band_min == cmd_band_end &&
-            io_procs->ftell(bfile) == ss->page_bfile_end_pos
-            ) {
-            status = EOFC;
-            break;
-        } {
-            int bmin = ss->b_this.band_min;
-            int bmax = ss->b_this.band_max;
-            int64_t pos = ss->b_this.pos;
+        /* The current command is over. So find the next command in the bfile
+         * that applies to the current band(s) and read that in. */
+        do {
             int nread;
+            /* If we hit eof, end! */
+            /* Could this test be moved into the nread < sizeof() test below? */
+            if (ss->b_this.band_min == cmd_band_end &&
+                io_procs->ftell(bfile) == ss->page_bfile_end_pos) {
+                pw->ptr = q;
+                ss->left = left;
+                return EOFC;
+            }
 
+            /* Read the next cmd_block from the bfile. Each cmd_block contains
+             * the bands to use, and the file position of the END of the data.
+             * We therefore want to read the data from the file position given
+             * in the PREVIOUS record onwards, and compare to the band min/max
+             * given there too. */
+            bmin = ss->b_this.band_min;
+            bmax = ss->b_this.band_max;
+            pos = ss->b_this.pos; /* Record where our data starts! */
             nread = io_procs->fread_chars(&ss->b_this, sizeof(ss->b_this), bfile);
             if (nread < sizeof(ss->b_this)) {
                 DISCARD(gs_note_error(gs_error_unregistered)); /* Must not happen. */
                 return ERRC;
             }
-            if (!(ss->band_last >= bmin && ss->band_first <= bmax))
-                goto rb;
-            io_procs->fseek(cfile, pos, SEEK_SET, ss->page_cfname);
-            left = (uint) (ss->b_this.pos - pos);
-#	    ifdef DEBUG
-            if (left > 0  && gs_debug_c('L')) {
-                if (ss->offset_map_length >= ss->offset_map_max_length) {
-                    DISCARD(gs_note_error(gs_error_unregistered)); /* Must not happen. */
-                    return ERRC;
-                }
-                ss->offset_map[ss->offset_map_length].file_offset = pos;
-                ss->offset_map[ss->offset_map_length].buffered = 0;
-                ss->offset_map_length++;
+        } while (ss->band_last < bmin || ss->band_first > bmax);
+        /* So let's set up to read the actual command data from cfile. Seek... */
+        io_procs->fseek(cfile, pos, SEEK_SET, ss->page_cfname);
+        left = (uint) (ss->b_this.pos - pos);
+#ifdef DEBUG
+        if (left > 0  && gs_debug_c('L')) {
+            if (ss->offset_map_length >= ss->offset_map_max_length) {
+                DISCARD(gs_note_error(gs_error_unregistered)); /* Must not happen. */
+                return ERRC;
             }
-#	    endif
-            if_debug5m('l', ss->local_memory,
-                      "[l]reading for bands (%d,%d) at bfile %"PRId64", cfile %"PRId64", length %u\n",
-                      bmin, bmax,
-                      (io_procs->ftell(bfile) - sizeof(ss->b_this)), (int64_t)pos, left);
+            ss->offset_map[ss->offset_map_length].file_offset = pos;
+            ss->offset_map[ss->offset_map_length].buffered = 0;
+            ss->offset_map_length++;
         }
+#endif
+        if_debug5m('l', ss->local_memory,
+                   "[l]reading for bands (%d,%d) at bfile %"PRId64", cfile %"PRId64", length %u\n",
+                   bmin, bmax,
+                   (io_procs->ftell(bfile) - sizeof(ss->b_this)), (int64_t)pos, left);
     }
     pw->ptr = q;
     ss->left = left;
@@ -213,11 +249,43 @@ static const stream_template s_band_read
 };
 
 #ifdef DEBUG
+/* In DEBUG builds, we maintain an "offset_map" within stream_band_read_state,
+ * that allows us to relate offsets within the buffer, to offsets within the
+ * cfile.
+ *
+ * At any given point, for stream_band_read_state *ss:
+ *    There are n = ss->offset_map_length records in the table.
+ *    offset = 0;
+ *    for (i = 0; i < n; i++)
+ *       // Offset 'offset' in the buffer corresponds to ss->offset_map[i].file_offset in the file.
+ *       offset += ss->offset_map[i].buffered
+ *
+ * As we pull data from the stream, we keep file_offset and buffered up to date. Note that
+ * there are 2 cbuf_size sized buffers in play here. The cmd_buffer has one cbuf_size sized
+ * buffer in it. Data is pulled into that from the stream, which has another cbuf_sized
+ * buffer into it. Accordingly, 'buffered' should never be > 2*cbuf_size = 8192.
+ *
+ * Sometimes we will pull data out of the stream, bypassing the cmd_buffer's buffer. In this
+ * case, we 'skip' data, and record the number of bytes skipped in ss->bytes_skipped. This
+ * should only ever happen when we have already advanced as much as possible (i.e. when the
+ * current offset is in the first record).
+ */
+
+/* Given buffer_offset (an offset within the buffer), return the number of the offset_map
+ * record that contains it. Also fill poffset0 in with the offset of the start of that
+ * record within the buffer. (NOTE, depending on how much of the record has already been
+ * read, some bytes may already have been lost). */
 static int
 buffer_segment_index(const stream_band_read_state *ss, uint buffer_offset, uint *poffset0)
 {
     uint i, offset0, offset = 0;
 
+#ifdef EXTRA_OFFSET_MAP_DEBUGGING
+    dmlprintf1(ss->local_memory, "buffer_segment_index: buffer_offset=%d\n", buffer_offset);
+    for (i = 0; i < ss->offset_map_length; i++) {
+        dmlprintf3(ss->local_memory, " offset_map[%d].file_offset=%"PRId64" buffered=%d\n", i, ss->offset_map[i].file_offset, ss->offset_map[i].buffered);
+    }
+#endif
     for (i = 0; i < ss->offset_map_length; i++) {
         offset0 = offset;
         offset += ss->offset_map[i].buffered;
@@ -226,10 +294,17 @@ buffer_segment_index(const stream_band_r
             return i;
         }
     }
+#ifdef EXTRA_OFFSET_MAP_DEBUGGING
+    dmlprintf1(ss->local_memory, "buffer_segment_index fail: buffer_offset=%d not found\n", buffer_offset);
+    exit(1);
+#else
     (void)gs_note_error(gs_error_unregistered); /* Must not happen. */
+#endif
     return -1;
 }
 
+/* Map from a buffer offset, to the offset of the corresponding byte in the
+ * cfile. */
 int64_t
 clist_file_offset(const stream_state * st, uint buffer_offset)
 {
@@ -237,12 +312,10 @@ clist_file_offset(const stream_state * s
     uint offset0;
     int i = buffer_segment_index(ss, buffer_offset, &offset0);
 
-    if (i < 0)
-        return -1;
     return ss->offset_map[i].file_offset + (uint)(buffer_offset - offset0);
 }
 
-int
+void
 top_up_offset_map(stream_state * st, const byte *buf, const byte *ptr, const byte *end)
 {
     /* NOTE: The clist data are buffered in the clist reader buffer and in the
@@ -250,32 +323,75 @@ top_up_offset_map(stream_state * st, con
        from s_band_read_process, offset_map corresponds the union of the 2 buffers.
      */
     stream_band_read_state *const ss = (stream_band_read_state *) st;
+    uint buffer_offset, offset0, consumed;
+    int i;
 
-    if (!gs_debug_c('L')) {
-        return 0;
-    } else if (ss->skip_first) {
+#ifdef EXTRA_OFFSET_MAP_DEBUGGING
+    if (ptr < buf || end < ptr || end < buf || end > buf + cbuf_size)
+    {
+        dmlprintf3(ss->local_memory, "Invalid pointers for top_up_offset_map: buf=%p ptr=%p end=%p\n", buf, ptr, end);
+    }
+#endif
+
+    if (!gs_debug_c('L'))
+        return;
+    if (ss->skip_first) {
         /* Work around the trick with initializing the buffer pointer with the buffer end. */
         ss->skip_first = false;
-        return 0;
-    } else if (ptr == buf)
-        return 0;
-    else {
-        uint buffer_offset = ptr - buf;
-        uint offset0, consumed;
-        int i = buffer_segment_index(ss, buffer_offset, &offset0);
-
-        if (i < 0)
-            return_error(gs_error_unregistered); /* Must not happen. */
-        consumed = buffer_offset - offset0;
-        ss->offset_map[i].buffered -= consumed;
-        ss->offset_map[i].file_offset += consumed;
-        if (i) {
-            memmove(ss->offset_map, ss->offset_map + i,
+        return;
+    }
+    if (ptr == buf)
+        return;
+
+    /* We know that buf <= ptr <= end <= buf+4096, so uint is quite enough! */
+    buffer_offset = ptr - buf;
+    i = buffer_segment_index(ss, buffer_offset, &offset0);
+
+    consumed = buffer_offset - offset0;
+#ifdef EXTRA_OFFSET_MAP_DEBUGGING
+    dmlprintf3(ss->local_memory, "offset_map: dump %d entries + %d bytes + %d skipped bytes\n", i, consumed, ss->bytes_skipped);
+    if (ss->offset_map[i].buffered < consumed) {
+        dmlprintf2(ss->local_memory, "Invalid update to buffered. B %d %d\n", ss->offset_map[i].buffered, consumed);
+        exit(1);
+    }
+#endif
+    ss->offset_map[i].buffered -= consumed;
+    ss->offset_map[i].file_offset += consumed;
+    ss->bytes_skipped = 0;
+    if (i) {
+        memmove(ss->offset_map, ss->offset_map + i,
                 (ss->offset_map_length - i) * sizeof(*ss->offset_map));
-            ss->offset_map_length -= i;
-        }
+        ss->offset_map_length -= i;
     }
-    return 0;
+}
+
+/* This function is called when data is copied from the stream out into a separate
+ * buffer without going through the usual clist buffers. Essentially data for the
+ * id we are reading at buffer_offset within the buffer is skipped. */
+void adjust_offset_map_for_skipped_data(stream_state *st, uint buffer_offset, uint skipped)
+{
+    uint offset0;
+    stream_band_read_state *const ss = (stream_band_read_state *) st;
+    int i;
+
+    if (!gs_debug_c('L'))
+        return;
+
+    i = buffer_segment_index(ss, buffer_offset, &offset0);
+
+    ss->offset_map[i].buffered -= skipped;
+    ss->offset_map[i].file_offset += skipped;
+}
+
+void
+offset_map_next_data_out_of_band(stream_state *st)
+{
+    stream_band_read_state *const ss = (stream_band_read_state *) st;
+
+    if (!gs_debug_c('L'))
+        return;
+
+    ss->skip_next = 1;
 }
 #endif /* DEBUG */
 
@@ -838,6 +954,7 @@ clist_render_rectangle(gx_device_clist *
             page_info.tile_cache_size = ppage->page->tile_cache_size;
             page_info.bfile_end_pos = ppage->page->bfile_end_pos;
             page_info.band_params = ppage->page->band_params;
+            page_info.line_ptrs_offset = 0;
             pinfo = &page_info;
 
             /*
diff -pruN 9.55.0~dfsg-3/base/gxclrect.c 9.56.1~dfsg-1/base/gxclrect.c
--- 9.55.0~dfsg-3/base/gxclrect.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclrect.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -60,8 +60,7 @@ cmd_write_rect_hl_cmd(gx_device_clist_wr
     cmd_set_rect(pcls->rect);
     if (extended_command) {
         rcsize = 2 + cmd_size_rect(&pcls->rect);
-        code = set_cmd_put_op(&dp, cldev, pcls, cmd_opv_extend, rcsize);
-        dp[1] = op;
+        code = set_cmd_put_extended_op(&dp, cldev, pcls, op, rcsize);
         dp += 2;
     } else {
         rcsize = 1 + cmd_size_rect(&pcls->rect);
@@ -70,7 +69,7 @@ cmd_write_rect_hl_cmd(gx_device_clist_wr
     }
     if (code < 0)
         return code;
-    if_debug5m('L', cldev->memory, "rect hl r%d:%d,%d,%d,%d\n",
+    if_debug5m('L', cldev->memory, "[L]  rect hl r%d:%d,%d,%d,%d\n",
                rcsize - 1, pcls->rect.x, pcls->rect.y,
                pcls->rect.width, pcls->rect.height);
     cmd_put_rect(&pcls->rect, dp);
@@ -158,7 +157,7 @@ cmd_write_rect_cmd(gx_device_clist_write
         code = set_cmd_put_op(&dp, cldev, pcls, op, rcsize);
         if (code < 0)
             return code;
-        if_debug5m('L', cldev->memory, "    r%d:%d,%d,%d,%d\n",
+        if_debug5m('L', cldev->memory, "[L]  r%d:%d,%d,%d,%d\n",
                    rcsize - 1, dx, dwidth, dy, dheight);
         cmd_put_rect(&pcls->rect, dp + 1);
     }
@@ -891,9 +890,21 @@ copy:{
         }
         op += compress;
         if (dx) {
+            if_debug0m('L', dev->memory,
+                       "[L] fake end_run: really set_misc_data_x[2]\n");
             *dp++ = cmd_count_op(cmd_opv_set_misc, 2, dev->memory);
             *dp++ = cmd_set_misc_data_x + dx;
         }
+#ifdef DEBUG
+        if (gs_debug_c('L')) {
+            const char *const *sub = cmd_sub_op_names[op >> 4];
+
+            if (sub)
+                dmlprintf1(dev->memory, "[L] fake end_run: really %s\n", sub[op & 0xf]);
+            else
+                dmlprintf2(dev->memory, "[L] fake end_run: really %s %d\n", cmd_op_names[op >> 4], op & 0xf);
+        }
+#endif
         *dp++ = cmd_count_op(op, csize, dev->memory);
         /* Store the plane_height */
         cmd_putw(0, &dp);
@@ -1013,9 +1024,21 @@ clist_copy_planes(gx_device * dev,
          * cmd_put_bits fill the buffer up. */
         dp2 = dp;
         if (dx) {
+            if_debug0m('L', cdev->memory,
+                       "[L] fake end_run: really set_misc_data_x[2]\n");
             *dp2++ = cmd_count_op(cmd_opv_set_misc, 2, cdev->memory);
             *dp2++ = cmd_set_misc_data_x + dx;
         }
+#ifdef DEBUG
+        if (gs_debug_c('L')) {
+            const char *const *sub = cmd_sub_op_names[(op+code) >> 4];
+
+            if (sub)
+                dmlprintf1(cdev->memory, "[L] fake end_run: really %s\n", sub[(op+code) & 0xf]);
+            else
+                dmlprintf2(cdev->memory, "[L] fake end_run: really %s %d\n", cmd_op_names[(op+code) >> 4], (op+code) & 0xf);
+        }
+#endif
         *dp2++ = cmd_count_op(op + code, csize, cdev->memory);
         cmd_putw(plane_height, &dp2);
         cmd_put2w(rx, re.y, &dp2);
@@ -1145,9 +1168,21 @@ copy:{
             }
             op += compress;
             if (dx) {
+                if_debug0m('L', dev->memory,
+                           "[L] fake end_run: really set_misc_data_x[2]\n");
                 *dp++ = cmd_count_op(cmd_opv_set_misc, 2, dev->memory);
                 *dp++ = cmd_set_misc_data_x + dx;
             }
+#ifdef DEBUG
+            if (gs_debug_c('L')) {
+                const char *const *sub = cmd_sub_op_names[op >> 4];
+
+                if (sub)
+                    dmlprintf1(dev->memory, "[L] fake end_run: really %s\n", sub[op & 0xf]);
+                else
+                    dmlprintf2(dev->memory, "[L] fake end_run: really %s %d\n", cmd_op_names[op >> 4], op & 0xf);
+            }
+#endif
             *dp++ = cmd_count_op(op, csize, dev->memory);
             cmd_put2w(rx, re.y, &dp);
             cmd_put2w(w1, re.height, &dp);
@@ -1222,8 +1257,7 @@ clist_copy_alpha_hl_color(gx_device * de
         if (!re.pcls->color_is_devn) {
             byte *dp;
 
-            code = set_cmd_put_op(&dp, cdev, re.pcls, cmd_opv_extend, 2);
-            dp[1] = cmd_opv_ext_set_color_is_devn;
+            code = set_cmd_put_extended_op(&dp, cdev, re.pcls, cmd_opv_ext_set_color_is_devn, 2);
             dp += 2;
             if (code < 0)
                   return code;
@@ -1275,9 +1309,21 @@ copy:{
             }
             op += compress;
             if (dx) {
+                if_debug0m('L', dev->memory,
+                           "[L] fake end_run: really set_misc_data_x[2]\n");
                 *dp++ = cmd_count_op(cmd_opv_set_misc, 2, dev->memory);
                 *dp++ = cmd_set_misc_data_x + dx;
             }
+#ifdef DEBUG
+            if (gs_debug_c('L')) {
+                const char *const *sub = cmd_sub_op_names[op >> 4];
+
+                if (sub)
+                    dmlprintf1(dev->memory, "[L] fake end_run: really %s\n", sub[op & 0xf]);
+                else
+                    dmlprintf2(dev->memory, "[L] fake end_run: really %s %d\n", cmd_op_names[op >> 4], op & 0xf);
+            }
+#endif
             *dp++ = cmd_count_op(op, csize, dev->memory);
             *dp++ = depth;
             cmd_put2w(rx, re.y, &dp);
@@ -1350,12 +1396,10 @@ clist_copy_alpha(gx_device * dev, const
         if (re.pcls->color_is_devn) {
             byte *dp;
 
-            code = set_cmd_put_op(&dp, cdev, re.pcls, cmd_opv_extend, 1);
-            if (code >= 0)
-                code = set_cmd_put_op(&dp, cdev, re.pcls,
-                                      cmd_opv_ext_unset_color_is_devn, 1);
+            code = set_cmd_put_extended_op(&dp, cdev, re.pcls, cmd_opv_ext_unset_color_is_devn, 1);
             if (code < 0)
                 return code;
+            dp++;
             re.pcls->color_is_alpha = 1;
         }
         if (color != re.pcls->colors[1]) {
@@ -1407,9 +1451,21 @@ copy:{
             }
             op += compress;
             if (dx) {
+                if_debug0m('L', dev->memory,
+                           "[L] fake end_run: really set_misc_data_x[2]\n");
                 *dp++ = cmd_count_op(cmd_opv_set_misc, 2, dev->memory);
                 *dp++ = cmd_set_misc_data_x + dx;
             }
+#ifdef DEBUG
+            if (gs_debug_c('L')) {
+                const char *const *sub = cmd_sub_op_names[op >> 4];
+
+                if (sub)
+                    dmlprintf1(dev->memory, "[L] fake end_run: really %s\n", sub[op & 0xf]);
+                else
+                    dmlprintf2(dev->memory, "[L] fake end_run: really %s %d\n", cmd_op_names[op >> 4], op & 0xf);
+            }
+#endif
             *dp++ = cmd_count_op(op, csize, dev->memory);
             *dp++ = depth;
             cmd_put2w(rx, re.y, &dp);
diff -pruN 9.55.0~dfsg-3/base/gxclthrd.c 9.56.1~dfsg-1/base/gxclthrd.c
--- 9.55.0~dfsg-3/base/gxclthrd.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclthrd.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -995,7 +995,7 @@ clist_process_page(gx_device *dev, gx_pr
             return code;
     }
 
-    render_plane.index = -1;
+    gx_render_plane_init(&render_plane, dev, -1);
     for (y = 0; y < dev->height; y += lines_rasterized)
     {
         line_count = band_height;
diff -pruN 9.55.0~dfsg-3/base/gxclutil.c 9.56.1~dfsg-1/base/gxclutil.c
--- 9.55.0~dfsg-3/base/gxclutil.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxclutil.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -48,10 +48,13 @@ const char *const *const cmd_sub_op_name
  0, 0, 0, 0,
  0, cmd_misc2_op_names, cmd_segment_op_names, cmd_path_op_names
 };
-#ifndef GS_THREADSAFE
+const char *cmd_extend_op_names[256] =
+{cmd_extend_op_name_strings};
+
+#ifdef COLLECT_STATS_CLIST
 struct stats_cmd_s {
-    ulong op_counts[256];
-    ulong op_sizes[256];
+    ulong op_counts[512];
+    ulong op_sizes[512];
     ulong tile_reset, tile_found, tile_added;
     ulong same_band, other_band;
 } stats_cmd;
@@ -73,6 +76,25 @@ cmd_count_op(int op, uint size,const gs_
     }
     return op;
 }
+int
+cmd_count_extended_op(int op, uint size,const gs_memory_t *mem)
+{
+    stats_cmd.op_counts[cmd_opv_extend]++;
+    stats_cmd.op_sizes[cmd_opv_extend] += size;
+    stats_cmd.op_counts[256+op]++;
+    stats_cmd.op_sizes[256+op] += size;
+    if (gs_debug_c('L')) {
+        const char *ext = cmd_extend_op_names[op];
+
+        if (ext)
+            dmlprintf2(mem, ", %s(%u)\n", ext, size);
+        else
+            dmlprintf2(mem, ", ?0x%02x?(%u)\n", op,
+                      size);
+        dmflush(mem);
+    }
+    return op;
+}
 void
 cmd_uncount_op(int op, uint size)
 {
@@ -83,7 +105,7 @@ cmd_uncount_op(int op, uint size)
 #endif
 
 /* Print statistics. */
-#if defined(DEBUG_CLIST_STATS) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_CLIST
 void
 cmd_print_stats(const gs_memory_t *mem)
 {
@@ -125,6 +147,20 @@ cmd_print_stats(const gs_memory_t *mem)
         }
         dmputs(mem, "\n");
     }
+    for (ci = 0x100; ci < 0x200; ci ++) {
+        const char *ext = cmd_extend_op_names[ci-0x100];
+
+        if (ext != NULL) {
+            dmprintf3(mem, "[l] %s (%lu,%lu)\n",
+                         ext,
+                         stats_cmd.op_counts[ci], stats_cmd.op_sizes[ci]);
+        } else if (stats_cmd.op_counts[ci] || stats_cmd.op_sizes[ci]) {
+            dmprintf3(mem, "[l] ?0x%02x? (%lu,%lu)\n",
+                      ci-0x100,
+                      stats_cmd.op_counts[ci], stats_cmd.op_sizes[ci]);
+        }
+        dmputs(mem, "\n");
+    }
 }
 #endif /* DEBUG */
 
@@ -173,7 +209,7 @@ cmd_write_band(gx_device_clist_writer *
         clist_file_ptr cfile = cldev->page_cfile;
         clist_file_ptr bfile = cldev->page_bfile;
         cmd_block cb;
-        byte end = cmd_count_op(cmd_end, 1, cldev->memory);
+        byte end;
 
         if (cfile == 0 || bfile == 0)
             return_error(gs_error_ioerror);
@@ -195,12 +231,14 @@ cmd_write_band(gx_device_clist_writer *
                     return_error(gs_error_Fatal);
                 }
 #endif
-                if_debug2m('L', cldev->memory, "[L]Wrote cmd id=%ld at %"PRId64"\n",
+                if_debug2m('L', cldev->memory, "[L] cmd id=%ld at %"PRId64"\n",
                            cp->id, cldev->page_info.io_procs->ftell(cfile));
                 cldev->page_info.io_procs->fwrite_chars(cp + 1, cp->size, cfile);
             }
             pcl->head = pcl->tail = 0;
         }
+        if_debug0m('L', cldev->memory, "[L] adding terminator\n");
+        end  = cmd_count_op(cmd_end, 1, cldev->memory);
         cldev->page_info.io_procs->fwrite_chars(&end, 1, cfile);
         process_interrupts(cldev->memory);
         code_b = cldev->page_info.io_procs->ferror_code(bfile);
@@ -287,7 +325,7 @@ cmd_write_buffer(gx_device_clist_writer
     VALGRIND_MAKE_MEM_UNDEFINED(cldev->cbuf, cldev->cend - cldev->cbuf);
 #endif
     cldev->ccl = 0;
-#if defined(DEBUG_CLIST_STATS) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_CLIST
     if (gs_debug_c('l'))
         cmd_print_stats(cldev->memory);
 #endif
@@ -308,8 +346,10 @@ cmd_put_list_op(gx_device_clist_writer *
     CMD_CHECK_LAST_OP_BLOCK_DEFINED(cldev);
 
     if (size + cmd_headroom > cldev->cend - dp) {
-        if ((cldev->error_code =
-             cmd_write_buffer(cldev, cmd_opv_end_run)) != 0 ||
+        cldev->error_code = cmd_write_buffer(cldev, cmd_opv_end_run);
+        /* error_code can come back as +ve as a warning that memory
+         * is getting tight. Don't fail on that. */
+        if (cldev->error_code < 0 ||
             (size + cmd_headroom > cldev->cend - cldev->cnext)) {
             if (cldev->error_code == 0)
                 cldev->error_code = gs_error_VMerror;
@@ -326,7 +366,7 @@ cmd_put_list_op(gx_device_clist_writer *
             lprintf1("cmd_put_list_op error at "PRI_INTPTR"\n", (intptr_t)pcl->tail);
         }
 #endif
-        if_debug2m('L', cldev->memory, ", to id=%ld , offset=%ld",
+        if_debug2m('L', cldev->memory, "[L] id:%ld+%ld",
                    pcl->tail->id, (long)pcl->tail->size);
         pcl->tail->size += size;
     } else {
@@ -335,6 +375,21 @@ cmd_put_list_op(gx_device_clist_writer *
         cmd_prefix *cp = (cmd_prefix *)
             (dp + ((cldev->cbuf - dp) & (ARCH_ALIGN_PTR_MOD - 1)));
 
+        cp->id = cldev->ins_count++;
+#ifdef DEBUG
+        if (gs_debug_c('L'))
+        {
+            if (pcl == cldev->band_range_list)
+                dmlprintf2(cldev->memory, "[L]Change to bands=(%d->%d)", cldev->band_range_min, cldev->band_range_max);
+            else
+                dmlprintf1(cldev->memory, "[L]Change to band=%d",
+                           (int)(((intptr_t)pcl-(intptr_t)&cldev->states->list)/sizeof(*cldev->states)));
+
+            dmlprintf2(cldev->memory, ", align=%d\n[L] id:%ld+0",
+                       (int)((char *)cp-(char *)dp), cp->id);
+        }
+#endif
+
         cmd_count_add1(stats_cmd.other_band);
         dp = (byte *) (cp + 1);
         if (pcl->tail != 0) {
@@ -352,14 +407,28 @@ cmd_put_list_op(gx_device_clist_writer *
         pcl->tail = cp;
         cldev->ccl = pcl;
         cp->size = size;
-        cp->id = cldev->ins_count;
-        if_debug1m('L', cldev->memory, ", id=%ld ", cldev->ins_count);
-        cldev->ins_count++;
     }
     cldev->cnext = dp + size;
     return dp;
 }
 
+byte *
+cmd_put_list_extended_op(gx_device_clist_writer *cldev, cmd_list *pcl, int op, uint size)
+{
+    byte *dp = cmd_put_list_op(cldev, pcl, size);
+
+    if (dp) {
+        dp[1] = op;
+
+        if (gs_debug_c('L')) {
+            clist_debug_op(cldev->memory, dp);
+            dmlprintf1(cldev->memory, "[%u]\n", size);
+        }
+    }
+
+    return dp;
+}
+
 /* Request a space in the buffer.
    Writes out the buffer if necessary.
    Returns the size of available space. */
@@ -369,6 +438,8 @@ cmd_get_buffer_space(gx_device_clist_wri
     CMD_CHECK_LAST_OP_BLOCK_DEFINED(cldev);
 
     if (size + cmd_headroom > cldev->cend - cldev->cnext) {
+        /* error_code can come back as +ve as a warning that memory
+         * is getting tight. Don't fail on that. */
         cldev->error_code = cmd_write_buffer(cldev, cmd_opv_end_run);
         if (cldev->error_code < 0) {
             return cldev->error_code;
@@ -381,9 +452,6 @@ cmd_get_buffer_space(gx_device_clist_wri
 byte *
 cmd_put_op(gx_device_clist_writer * cldev, gx_clist_state * pcls, uint size)
 {
-    if_debug3m('L', cldev->memory, "[L]band %d: size=%u, left=%u",
-               (int)(pcls - cldev->states),
-               size, 0);
     return cmd_put_list_op(cldev, &pcls->list, size);
 }
 #endif
@@ -395,18 +463,21 @@ cmd_put_range_op(gx_device_clist_writer
 {
     CMD_CHECK_LAST_OP_BLOCK_DEFINED(cldev);
 
-    if_debug4m('L', cldev->memory, "[L]band range(%d,%d): size=%u, left=%u",
-               band_min, band_max, size, 0);
     if (cldev->ccl != 0 &&
         (cldev->ccl != cldev->band_range_list ||
          band_min != cldev->band_range_min ||
          band_max != cldev->band_range_max)
         ) {
-        if ((cldev->error_code = cmd_write_buffer(cldev, cmd_opv_end_run)) != 0) {
-            return 0;
+        cldev->error_code = cmd_write_buffer(cldev, cmd_opv_end_run);
+        /* error_code can come back as +ve as a warning that memory
+         * is getting tight. Don't fail on that. */
+        if (cldev->error_code < 0) {
+            return NULL;
         }
         cldev->band_range_min = band_min;
         cldev->band_range_max = band_max;
+        if_debug2m('L', cldev->memory, "[L]Band range(%d,%d)\n",
+                   band_min, band_max);
     }
     return cmd_put_list_op(cldev, cldev->band_range_list, size);
 }
@@ -693,6 +764,36 @@ cmd_set_tile_phase(gx_device_clist_write
     return cmd_set_tile_phase_generic(cldev, pcls, px, py, false);
 }
 
+int
+cmd_set_screen_phase_generic(gx_device_clist_writer * cldev, gx_clist_state * pcls,
+                             int px, int py, gs_color_select_t color_select,
+                             bool all_bands)
+{
+    int pcsize;
+    byte *dp;
+    int code;
+
+    pcsize = 1 + cmd_size2w(px, py);
+    if (all_bands)
+        code = set_cmd_put_all_op(&dp, cldev, (byte)cmd_opv_set_screen_phaseT + color_select, pcsize);
+    else
+        code = set_cmd_put_op(&dp, cldev, pcls, (byte)cmd_opv_set_screen_phaseT + color_select, pcsize);
+    if (code < 0)
+        return code;
+    ++dp;
+    pcls->screen_phase[color_select].x = px;
+    pcls->screen_phase[color_select].y = py;
+    cmd_putxy(pcls->screen_phase[color_select], &dp);
+    return 0;
+}
+
+int
+cmd_set_screen_phase(gx_device_clist_writer * cldev, gx_clist_state * pcls,
+                     int px, int py, gs_color_select_t color_select)
+{
+    return cmd_set_screen_phase_generic(cldev, pcls, px, py, color_select, false);
+}
+
 /* Write a command to enable or disable the logical operation. */
 int
 cmd_put_enable_lop(gx_device_clist_writer * cldev, gx_clist_state * pcls,
@@ -777,14 +878,13 @@ cmd_put_params(gx_device_clist_writer *c
         gs_param_list_serialize(param_list, local_buf, sizeof(local_buf));
     if (param_length > 0) {
         /* Get cmd buffer space for serialized */
-        code = set_cmd_put_all_op(&dp, cldev, cmd_opv_extend,
+        code = set_cmd_put_all_extended_op(&dp, cldev, cmd_opv_ext_put_params,
                                   2 + sizeof(unsigned) + param_length);
         if (code < 0)
             return code;
 
         /* write param list to cmd list: needs to all fit in cmd buffer */
         if_debug1m('l', cldev->memory, "[l]put_params, length=%d\n", param_length);
-        dp[1] = cmd_opv_ext_put_params;
         dp += 2;
         memcpy(dp, &param_length, sizeof(unsigned));
         dp += sizeof(unsigned);
@@ -869,3 +969,203 @@ cmd_read_matrix(gs_matrix * pmat, const
     sget_matrix(&s, pmat);
     return cbp + stell(&s);
 }
+
+/*
+Some notes on understanding the output of -ZL.
+
+The examples here are given from:
+  gs -o out.png -r96 -sDEVICE=png16m -dBandHeight=20 -dMaxBitmap=1000 -ZL examples/tiger.eps
+
+Not every line in that output is explained here!
+
+When writing a command list, we gather up a list of 'commands' into the
+clist (cfile). We then have a series of indexes that says which of these
+commands is needed for each band (bfile).
+
+So, while writing, we can be writing for 1 band, or for a range of bands
+at any given time. Commands that follow one another for the same band
+(or range of bands) will be crammed together into a single command block.
+These command blocks are each given an id for debugging purposes. When
+the set of bands for which we are writing changes, the id changes.
+
+Somewhere towards the top of the output (i.e. within a
+hundred lines or so) you should see:
+
+  [L]Resetting: Band range(0,56)
+
+So, we are writing some commands that will apply to bands 0 to 56.
+
+  [L] id:0+0, put_fill_dcolor(13)
+  [L] id:0+13, fill_rect 0(5)
+
+So, for id 0, at 0 bytes offset, we first have a put_fill_dcolor command
+that takes 13 bytes. Then, still in id 0, at 13 bytes offset, we have
+a fill_rect that takes 5 bytes.
+
+Then we change the band:
+
+  [L]Change to band=0, align=6
+
+When we change the band, we change to a new command block, and the id
+changes - so you'll see the subsequent entries listed with id 1.
+Subsequent command blocks are aligned, so you'll see some alignment
+(padding) bytes used - here 6 bytes.
+
+  [L] id:1+0, set_misc2(6)
+  [L] id:1+6, begin_clip(1)
+  [L] id:1+7, fill_rect 0(7)
+
+Here we see various commands, each for id 1, at the expected offsets
+given their respective sizes. Then we get some debugging from elsewhere
+in the clist system.
+
+[L]  r6:0,793,0,1123
+
+This indicates details about the fill_rect (in particular the way
+the fill_rect is encoded, and the parameters it uses). Such lines can
+be differentiated fairly easily from the command block writing code
+as they do not start with 'id:'.
+
+We continue with more commands:
+
+  [L] id:1+14, end_clip(1)
+  [L] id:1+15, put_fill_dcolor(13)
+  [L]  rmoveto:0: 0 0
+  [L] id:1+28, rmoveto(5)
+  [L]  rlineto:0: 0 1123
+  [L] id:1+33, vlineto(4)
+  [L]  rlineto:0: 793 0
+  [L] id:1+37, hlineto(3)
+  [L]  rlineto:0: 0 -1123
+  [L] id:1+40, vlineto(4)
+  [L]  closepath:0:
+  [L] id:1+44, closepath(1)
+  [L] id:1+45, fill(1)
+
+Here we note a couple of things. The clist command encoding system
+works by first reserving the required number of bytes for a command,
+then filling in those bytes. Because lots of parameters vary in length
+according to their particular value, we often have to do a lot of the
+encoding work twice; once to count how many bytes we need to reserve
+and then once to fill in the block.
+
+The command buffer debug lines (i.e. the ones starting 'id:') are output
+at the point the buffer is reserved. Other debug lines for the same
+command can happen either before or after these lines. So the 'r6' line
+happened after the command reservation that it corresponded to, whereas
+the 'rmoveto' (and others) above happen before the command reservation.
+This can be confusing.
+
+Another confusing thing is that the commands can appear to change. The
+non-command block debug above mentions 4 rlineto's, but these all
+appear in the command list as vlineto or hlineto. This is because
+the command block queueing attempts to be smart and to simplify the
+sequence of commands. This can mean pulling a command into a previous
+one, or (as in this case) realising that a simpler encoding can be
+used.
+
+And we continue...
+
+  [L]Change to band=1, align=2
+  [L] id:2+0, set_misc2(6)
+  [L] id:2+6, begin_clip(1)
+
+After a while, we move to an output phase where things are actually
+written to the file. These come in groups like:
+
+  [l]writing for bands (0,56) at 0
+  [L] cmd id=0 at 0
+  [L] adding terminator, end_run(1)
+
+So this is writing out a note that bands 0 to 56 should execute the following
+id's. We then write out the id's in question (id 0, goes into cfile at offset 0).
+This is then terminated by a single byte 'end_run' marker.
+
+This repeats, with the file offsets increasing as we go. Some cases have more
+than one id, for instance:
+
+  [l]writing for bands (7,7) at 640
+  [L] cmd id=8 at 640
+  [L] cmd id=194 at 685
+  [L] cmd id=215 at 785
+  [L] cmd id=712 at 928
+  [L] cmd id=720 at 969
+  [L] cmd id=726 at 986
+  [L] cmd id=732 at 1016
+  [L] cmd id=809 at 1046
+  [L] cmd id=817 at 1185
+  [L] cmd id=822 at 1258
+  [L] adding terminator, end_page(1)
+
+So, by matching up the id's in this section, together with their offsets,
+we can find out what command was written there.
+
+For instance, suppose we hit a problem when reading the cfile at offset 1029.
+We can look to see that this is id=732 + 13 bytes. We can look back in the
+output to where id:732 was being output, and we see:
+
+  [L] id:732+13, rmoveto(5)
+
+Most clist bugs tend to involve the reader and writer disagreeing on how
+many bytes a given command should be and getting out of step. By looking at
+where the writer puts stuff, and the reader is trying to read stuff, we can
+hopefully spot this.
+
+The writing phase ends with:
+
+  [l]writing pseudo band 57 cb pos 92521
+  [l]writing 1824 bytes into cfile at 92521
+  [l]writing end for bands (-1,-1) at 94345
+
+FIXME: Explain the pseudo band.
+
+The next section of the logging shows the reader reading. For each band
+in turn, we'll see a section where we announce what band we are
+rendering:
+
+  [l]rendering bands (0,0)
+
+Then we will read through the different band records that were output
+above.
+
+  [l]reading for bands (0,0) at bfile 0, cfile 0, length 0
+  [l]reading for bands (0,56) at bfile 16, cfile 0, length 19
+  [l]reading for bands (0,0) at bfile 32, cfile 19, length 47
+
+If we look back, we can see that the first of these corresponded to an
+empty record. The second of these corresponded to the write of
+"cmd id=0 at 0", and the third corresponds to the write of
+"cmd id=1 at 19".
+
+When these records have been read in, we actually execute the data. Each
+line gives the offset from which the command was read (which allows us
+to track it back to what it *should* be in the case of a mismatch),
+and is followed by the command name, and a selection of its parameters:
+
+  [L] 0: put_fill_dcolor cmd_opv_ext_put_drawing_color
+  [L] 13: fill_rect 0 x=0 y=0 w=0 h=0
+  [L] 18: end_run
+  [L] 19: set_misc2
+  [L]      CJ=-1 AC=1 SA=1
+  [L]      BM=0 TK=1 OPM=0 OP=0 op=0 RI=1
+  [L] 25: begin_clip
+  [L] 26: fill_rect 0 x=0 y=0 w=793 h=1123
+  [L] 33: end_clip
+  [L] 34: put_fill_dcolor cmd_opv_ext_put_drawing_color
+  [L] 47: rmoveto (0,0) 0 0
+  [L] 52: vlineto 1123
+  [L] 56: hlineto 793
+  [L] 59: vlineto -1123
+  [L] 63: closepath
+  [L] 64: fill
+  [L] 65: end_page
+
+Then we repeat gathering the data for the next band:
+
+  [l]rendering bands (1,1)
+  [l]reading for bands (0,56) at bfile 16, cfile 0, length 19
+  [l]reading for bands (1,1) at bfile 48, cfile 66, length 46
+
+and so on.
+
+*/
diff -pruN 9.55.0~dfsg-3/base/gxcmap.c 9.56.1~dfsg-1/base/gxcmap.c
--- 9.55.0~dfsg-3/base/gxcmap.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxcmap.c	2022-04-04 13:46:22.000000000 +0000
@@ -934,6 +934,7 @@ cmap_gray_halftoned(frac gray, gx_device
 
     /* apply the transfer function(s); convert to color values */
     if (pgs->effective_transfer_non_identity_count == 0) {
+        /* No transfer function to apply */
     } else if (dev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE)
         for (i = 0; i < ncomps; i++)
             cm_comps[i] = gx_map_color_frac(pgs,
@@ -2377,6 +2378,7 @@ cmap_transfer(gx_color_value *pconc, con
 
     /* apply the transfer function(s) */
     if (pgs->effective_transfer_non_identity_count == 0) {
+        /* No transfer function to apply */
     } else if (dev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE)
         for (i = 0; i < ncomps; i++)
             pconc[i] = frac2cv(gx_map_color_frac(pgs,
diff -pruN 9.55.0~dfsg-3/base/gxcpath.c 9.56.1~dfsg-1/base/gxcpath.c
--- 9.55.0~dfsg-3/base/gxcpath.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxcpath.c	2022-04-04 13:46:22.000000000 +0000
@@ -132,6 +132,8 @@ cpath_init_rectangle(gx_clip_path * pcpa
     gx_clip_list_from_rectangle(&pcpath->rect_list->list, pbox);
     pcpath->inner_box = *pbox;
     pcpath->path_valid = false;
+    pcpath->path_fill_adjust.x = 0;
+    pcpath->path_fill_adjust.y = 0;
     pcpath->path.bbox = *pbox;
     gx_cpath_set_outer_box(pcpath);
     pcpath->id = gs_next_ids(pcpath->path.memory, 1);	/* path changed => change id */
@@ -151,6 +153,7 @@ cpath_share_own_contents(gx_clip_path *
 {
     pcpath->inner_box = shared->inner_box;
     pcpath->path_valid = shared->path_valid;
+    pcpath->path_fill_adjust = shared->path_fill_adjust;
     pcpath->outer_box = shared->outer_box;
     pcpath->id = shared->id;
     pcpath->cached = NULL;
@@ -484,6 +487,8 @@ gx_cpath_to_path(gx_clip_path * pcpath,
         if (code < 0)
             return code;
         pcpath->path_valid = true;
+        pcpath->path_fill_adjust.x = 0;
+        pcpath->path_fill_adjust.y = 0;
     }
     return gx_path_assign_preserve(ppath, &pcpath->path);
 }
@@ -705,6 +710,7 @@ gx_cpath_intersect_with_params(gx_clip_p
             /* The path is valid; otherwise, defer constructing it. */
             gx_path_assign_preserve(&pcpath->path, ppath);
             pcpath->path_valid = true;
+            pcpath->path_fill_adjust = params != NULL ? params->adjust : pgs->fill_adjust;
         }
     } else {
         /* New clip path is nontrivial.  Intersect the slow way. */
@@ -737,6 +743,7 @@ gx_cpath_intersect_with_params(gx_clip_p
         if (path_valid) {
             gx_path_assign_preserve(&pcpath->path, ppath_orig);
             pcpath->path_valid = true;
+            pcpath->path_fill_adjust = params != NULL ? params->adjust : pgs->fill_adjust;
             pcpath->rule = rule;
         } else {
             code = gx_cpath_path_list_new(pcpath->path.memory, NULL, rule,
diff -pruN 9.55.0~dfsg-3/base/gxdcolor.c 9.56.1~dfsg-1/base/gxdcolor.c
--- 9.55.0~dfsg-3/base/gxdcolor.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxdcolor.c	2022-04-04 13:46:22.000000000 +0000
@@ -318,13 +318,15 @@ gx_dc_no_write(
 static int
 gx_dc_no_read(
     gx_device_color *       pdevc,
-    const gs_gstate        * pgs,                /* ignored */
+    const gs_gstate       * pgs,                /* ignored */
     const gx_device_color * prior_devc,         /* ignored */
     const gx_device *       dev,                /* ignored */
     int64_t		    offset,             /* ignored */
     const byte *            pdata,              /* ignored */
     uint                    size,               /* ignored */
-    gs_memory_t *           mem )               /* ignored */
+    gs_memory_t *           mem,                /* ignored */
+    int                     x0,                 /* ignored */
+    int                     y0)                 /* ignored */
 {
     pdevc->type = gx_dc_type_none;
     return 0;
@@ -345,13 +347,15 @@ gx_dc_cannot_write(
 int
 gx_dc_cannot_read(
     gx_device_color *       pdevc,
-    const gs_gstate  *       pgs,                /* ignored */
+    const gs_gstate *       pgs,                /* ignored */
     const gx_device_color * prior_devc,         /* ignored */
     const gx_device *       dev,                /* ignored */
     int64_t		    offset,             /* ignored */
     const byte *            pdata,              /* ignored */
     uint                    size,               /* ignored */
-    gs_memory_t *           mem )               /* ignored */
+    gs_memory_t *           mem,                /* ignored */
+    int                     x0,                 /* ignored */
+    int                     y0)                 /* ignored */
 {
     return_error(gs_error_unknownerror);
 }
@@ -401,13 +405,15 @@ gx_dc_null_equal(const gx_device_color *
 static int
 gx_dc_null_read(
     gx_device_color *       pdevc,
-    const gs_gstate  *       pgs,                /* ignored */
+    const gs_gstate *       pgs,                /* ignored */
     const gx_device_color * prior_devc,         /* ignored */
     const gx_device *       dev,                /* ignored */
     int64_t		    offset,             /* ignored */
     const byte *            pdata,              /* ignored */
     uint                    size,               /* ignored */
-    gs_memory_t *           mem )               /* ignored */
+    gs_memory_t *           mem,                /* ignored */
+    int                     x0,                 /* ignored */
+    int                     y0)                 /* ignored */
 {
     pdevc->type = gx_dc_type_null;
     return 0;
@@ -768,13 +774,15 @@ gx_devn_read_color(
 static int
 gx_dc_devn_read(
     gx_device_color *       pdevc,
-    const gs_gstate  *       pgs,                /* ignored */
+    const gs_gstate *       pgs,                /* ignored */
     const gx_device_color * prior_devc,         /* ignored */
     const gx_device *       dev,
     int64_t                 offset,             /* ignored */
     const byte *            pdata,
     uint                    size,
-    gs_memory_t *           mem )               /* ignored */
+    gs_memory_t *           mem,                /* ignored */
+    int                     x0,                 /* ignored */
+    int                     y0)                 /* ignored */
 {
     pdevc->type = gx_dc_type_devn;
     return gx_devn_read_color(&(pdevc->colors.devn.values[0]), &(pdevc->tag),
@@ -977,13 +985,15 @@ gx_dc_pure_write(
 static int
 gx_dc_pure_read(
     gx_device_color *       pdevc,
-    const gs_gstate        * pgs,                /* ignored */
+    const gs_gstate       * pgs,                /* ignored */
     const gx_device_color * prior_devc,         /* ignored */
     const gx_device *       dev,
     int64_t		    offset,             /* ignored */
     const byte *            pdata,
     uint                    size,
-    gs_memory_t *           mem )               /* ignored */
+    gs_memory_t *           mem,                /* ignored */
+    int                     x0,                 /* ignored */
+    int                     y0)                 /* ignored */
 {
     pdevc->type = gx_dc_type_pure;
     return gx_dc_read_color(&pdevc->colors.pure, dev, pdata, size);
diff -pruN 9.55.0~dfsg-3/base/gxdcolor.h 9.56.1~dfsg-1/base/gxdcolor.h
--- 9.55.0~dfsg-3/base/gxdcolor.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxdcolor.h	2022-04-04 13:46:22.000000000 +0000
@@ -214,7 +214,7 @@ struct gx_device_color_type_s {
 #define dev_color_proc_read(proc)\
   int proc(gx_device_color *pdevc, const gs_gstate * pgs,\
     const gx_device_color *prior_devc, const gx_device * dev, int64_t offset,\
-    const byte *data, uint size, gs_memory_t *mem)
+    const byte *data, uint size, gs_memory_t *mem, int x0, int y0)
                         dev_color_proc_read((*read));
 
     /*
diff -pruN 9.55.0~dfsg-3/base/gxdevcli.h 9.56.1~dfsg-1/base/gxdevcli.h
--- 9.55.0~dfsg-3/base/gxdevcli.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxdevcli.h	2022-04-04 13:46:22.000000000 +0000
@@ -1295,7 +1295,7 @@ typedef struct gs_fill_attributes_s {
  * start of src/gsequivc.c.
  */
 #define dev_t_proc_update_spot_equivalent_colors(proc, dev_t)\
-  int proc(dev_t *dev, const gs_gstate * pgs)
+  int proc(dev_t *dev, const gs_gstate * pgs, const gs_color_space *pcs)
 #define dev_proc_update_spot_equivalent_colors(proc)\
   dev_t_proc_update_spot_equivalent_colors(proc, gx_device)
 
diff -pruN 9.55.0~dfsg-3/base/gxdevice.h 9.56.1~dfsg-1/base/gxdevice.h
--- 9.55.0~dfsg-3/base/gxdevice.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxdevice.h	2022-04-04 13:46:22.000000000 +0000
@@ -278,7 +278,9 @@ dev_proc_get_alpha_bits(gx_default_get_a
 dev_proc_copy_alpha(gx_no_copy_alpha);  /* gives error */
 dev_proc_copy_alpha(gx_default_copy_alpha);
 dev_proc_fill_path(gx_default_fill_path);
+dev_proc_fill_path(gx_default_fill_path_shading_or_pattern);
 dev_proc_stroke_path(gx_default_stroke_path);
+dev_proc_stroke_path(gx_default_stroke_path_shading_or_pattern);
 dev_proc_fill_mask(gx_default_fill_mask);
 dev_proc_fill_trapezoid(gx_default_fill_trapezoid);
 dev_proc_fill_parallelogram(gx_default_fill_parallelogram);
@@ -434,6 +436,10 @@ void gx_device_forward_color_procs(gx_de
  */
 void check_device_separable(gx_device * dev);
 /*
+ * Is this a contone device?
+ */
+bool device_is_contone(gx_device* pdev);
+/*
  * Check if the device's encode_color routine uses a pdf14 compatible
  * encoding.  For more info see the routine's header.
  */
@@ -650,9 +656,6 @@ int gdev_begin_output_media(gs_param_lis
 int gdev_write_output_media(int index, gs_param_dict * pdict,
                             const gdev_output_media_t * pom);
 
-/* Can be called from set user params */
-void gx_default_put_icc_dir(gs_param_string *icc_pro, gx_device * dev);
-
 int gdev_end_output_media(gs_param_list * mlist, gs_param_dict * pdict);
 
 void gx_device_request_leadingedge(gx_device *dev, int le_req);
@@ -679,4 +682,7 @@ int gx_subclass_composite(gx_device *dev
     gs_gstate *pgs, gs_memory_t *memory, gx_device *cdev);
 void gx_subclass_fill_in_page_procs(gx_device *dev);
 
+int gx_init_non_threadsafe_device(gx_device *dev);
+
+
 #endif /* gxdevice_INCLUDED */
diff -pruN 9.55.0~dfsg-3/base/gxdevsop.h 9.56.1~dfsg-1/base/gxdevsop.h
--- 9.55.0~dfsg-3/base/gxdevsop.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxdevsop.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -442,6 +442,14 @@ enum {
     /* Determine if a given device is a null device. Returns 1 if it is. */
     gxdso_is_null_device,
 
+    /* Get information about pdf14 device overprint simulation state
+     *     data = unsigned char[2], [0] is overprint_sim_push [1] is num_spot_colors_int
+     *     size = sizeof(unsigned char[2])
+     * Returns 1 if returned values are valid
+     * 0 otherwise.
+     */
+    gxdso_overprintsim_state,
+
     /* Add new gxdso_ keys above this. */
     gxdso_pattern__LAST
 };
diff -pruN 9.55.0~dfsg-3/base/gxdhtserial.c 9.56.1~dfsg-1/base/gxdhtserial.c
--- 9.55.0~dfsg-3/base/gxdhtserial.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxdhtserial.c	2022-04-04 13:46:22.000000000 +0000
@@ -110,35 +110,30 @@ gx_ht_read_tf(
     --size;
     tf_type = (gx_ht_tf_type_t)*data++;
 
-    /* if no transfer function, exit now */
-    if (tf_type == gx_ht_tf_none) {
-        *ppmap = 0;
+    /* If no transfer function or identity set to NULL */
+    if (tf_type == gx_ht_tf_none || tf_type == gx_ht_tf_identity) {
+        *ppmap = NULL;
         return 1;
     }
 
-    /* allocate a transfer map */
+    /* If something strange then exit. Likely clist issue */
+    if (tf_type != gx_ht_tf_complete || size < sizeof(pmap->values))
+        return_error(gs_error_rangecheck);
+
+    /* Otherwise we have a real map. Allocate a transfer map */
     rc_alloc_struct_1( pmap,
                        gx_transfer_map,
                        &st_transfer_map,
                        mem,
                        return_error(gs_error_VMerror),
                        "gx_ht_read_tf" );
-
     pmap->id = gs_next_ids(mem, 1);
     pmap->closure.proc = 0;
     pmap->closure.data = 0;
-    if (tf_type == gx_ht_tf_identity) {
-        gx_set_identity_transfer(pmap);
-        return 1;
-    } else if (tf_type == gx_ht_tf_complete && size >= sizeof(pmap->values)) {
-        memcpy(pmap->values, data, sizeof(pmap->values));
-        pmap->proc = gs_mapped_transfer;
-        *ppmap = pmap;
-        return 1 + sizeof(pmap->values);
-    } else {
-        rc_decrement(pmap, "gx_ht_read_tf");
-        return_error(gs_error_rangecheck);
-    }
+    memcpy(pmap->values, data, sizeof(pmap->values));
+    pmap->proc = gs_mapped_transfer;
+    *ppmap = pmap;
+    return 1 + sizeof(pmap->values);
 }
 
 /*
diff -pruN 9.55.0~dfsg-3/base/gxdownscale.c 9.56.1~dfsg-1/base/gxdownscale.c
--- 9.55.0~dfsg-3/base/gxdownscale.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxdownscale.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -19,6 +19,7 @@
 #include "string_.h"
 #include "gdevprn.h"
 #include "assert_.h"
+#include "gsicc_cache.h"
 
 #ifdef WITH_CAL
 #include "cal_ets.h"
@@ -2046,11 +2047,17 @@ getbits_planar_line(gx_downscale_liner *
 
     code = (*dev_proc(liner->dev, get_bits_rectangle))(liner->dev, &rect, &params2);
 
-    /* get_bits_rectangle doesn't like doing planar copies, only return
-     * pointers. This is a problem for us, so fudge it here. */
-    for (i = 0; i < liner->num_comps; i++)
-        if (params->data[i] != params2.data[1])
-            memcpy(params->data[i], params2.data[i], n);
+    /* If our caller can't accept a pointer, we need to do some work. */
+    if (params->options & GB_RETURN_POINTER) {
+        for (i = 0; i < liner->num_comps; i++)
+            params->data[i] = params2.data[i];
+    } else {
+        /* get_bits_rectangle doesn't like doing planar copies, only return
+         * pointers. This is a problem for us, so fudge it here. */
+        for (i = 0; i < liner->num_comps; i++)
+            if (params->data[i] != params2.data[i])
+                memcpy(params->data[i], params2.data[i], n);
+    }
 
     return code;
 }
@@ -2268,23 +2275,25 @@ int gx_downscaler_init_planar_cm(gx_down
     ds->factor            = factor;
     ds->num_planes        = num_comps;
     ds->src_bpc           = src_bpc;
+    ds->dst_bpc           = dst_bpc;
     ds->scaled_data       = NULL;
     ds->scaled_span       = bitmap_raster((dst_bpc*dev->width*upfactor + downfactor-1)/downfactor);
     ds->apply_cm          = apply_cm;
     ds->apply_cm_arg      = apply_cm_arg;
-    ds->early_cm          = dst_bpc < src_bpc;
+    ds->early_cm          = dst_bpc < src_bpc || (dst_bpc == src_bpc && post_cm_num_comps < num_comps);
     ds->post_cm_num_comps = post_cm_num_comps;
     ds->do_skew_detection = params->do_skew_detection;
 
     if (apply_cm) {
-        for (i = 0; i < post_cm_num_comps; i++) {
-            ds->post_cm[i] = gs_alloc_bytes(dev->memory,
-                                            (size_t)post_span * downfactor,
-                                            "gx_downscaler(planar_data)");
-            if (ds->post_cm[i] == NULL) {
-                code = gs_note_error(gs_error_VMerror);
-                goto cleanup;
-            }
+        ds->post_cm[0] = gs_alloc_bytes(dev->memory,
+                                        (size_t)post_span * downfactor * post_cm_num_comps,
+                                        "gx_downscaler(planar_data)");
+        if (ds->post_cm[0] == NULL) {
+            code = gs_note_error(gs_error_VMerror);
+            goto cleanup;
+        }
+        for (i = 1; i < post_cm_num_comps; i++) {
+            ds->post_cm[i] = ds->post_cm[i-1] + (size_t)post_span * downfactor;
         }
     }
 
@@ -2307,14 +2316,11 @@ int gx_downscaler_init_planar_cm(gx_down
 
     memcpy(&ds->params, gb_params, sizeof(*gb_params));
     ds->params.raster = span;
-    for (i = 0; i < num_comps; i++) {
-        ds->pre_cm[i] = gs_alloc_bytes(dev->memory,
-                                       (size_t)span * downfactor,
-                                       "gx_downscaler(planar_data)");
-        if (ds->pre_cm[i] == NULL) {
-            code = gs_note_error(gs_error_VMerror);
-            goto cleanup;
-        }
+    ds->pre_cm[0] = gs_alloc_bytes(dev->memory,
+                                   (size_t)span * downfactor * num_comps,
+                                   "gx_downscaler(planar_data)");
+    for (i = 1; i < num_comps; i++) {
+        ds->pre_cm[i] = ds->pre_cm[i-1] + (size_t)span * downfactor;
     }
 
 #ifdef WITH_CAL
@@ -2462,7 +2468,9 @@ int gx_downscaler_init_planar_cm(gx_down
         code = gs_note_error(gs_error_rangecheck);
         goto cleanup;
     } else if (dst_bpc == 1) {
-        if (mfs > 1)
+        if (src_bpc == dst_bpc)
+            core = NULL;
+        else if (mfs > 1)
             core = &down_core_mfs;
         else if (factor == 4)
             core = &down_core_4;
@@ -2913,19 +2921,15 @@ gx_downscaler_init_cm_halftone(gx_downsc
 
 void gx_downscaler_fin(gx_downscaler_t *ds)
 {
-    int plane;
-
     if (ds->dev == NULL)
         return;
 
-    for (plane=0; plane < GS_CLIENT_COLOR_MAX_COMPONENTS; plane++) {
-        gs_free_object(ds->dev->memory, ds->pre_cm[plane],
-                       "gx_downscaler(planar_data)");
-        gs_free_object(ds->dev->memory, ds->post_cm[plane],
-                       "gx_downscaler(planar_data)");
-        ds->pre_cm[plane] = NULL;
-        ds->post_cm[plane] = NULL;
-    }
+    gs_free_object(ds->dev->memory, ds->pre_cm[0],
+                   "gx_downscaler(planar_data)");
+    gs_free_object(ds->dev->memory, ds->post_cm[0],
+                   "gx_downscaler(planar_data)");
+    ds->pre_cm[0] = NULL;
+    ds->post_cm[0] = NULL;
     ds->num_planes = 0;
 
     gs_free_object(ds->dev->memory, ds->mfs_data, "gx_downscaler(mfs)");
@@ -3018,11 +3022,14 @@ int gx_downscaler_get_bits_rectangle(gx_
     int                   subrow;
     int                   copy = (ds->dev->width * ds->src_bpc + 7)>>3;
     int                   i, j, n;
+    int                   num_planes_to_downscale;
 
     n = ds->dev->width;
     if (ds->dev->color_info.depth > ds->dev->color_info.num_components*8+8)
        n *= 2;
 
+    n = (n*ds->src_bpc+7)/8;
+
     gx_downscaler_decode_factor(factor, &upfactor, &downfactor);
 
     subrow = row % upfactor;
@@ -3059,11 +3066,10 @@ int gx_downscaler_get_bits_rectangle(gx_
                 params->options &= ~GB_RETURN_POINTER;
                 buffer = saved.data;
             } else
-                buffer = ds->pre_cm;
-            code = ds->apply_cm(ds->apply_cm_arg, params->data, buffer, ds->dev->width, rect.q.y - rect.p.y, params->raster);
-            if ((saved.options & GB_RETURN_COPY) == 0)
-                for (i = 0; i < ds->num_planes; i++)
-                    params->data[i] = buffer[i];
+                buffer = ds->post_cm;
+            code = ds->apply_cm(ds->apply_cm_arg, buffer, params->data, ds->dev->width, rect.q.y - rect.p.y, params->raster);
+            for (i = 0; i < ds->post_cm_num_comps; i++)
+                params->data[i] = buffer[i];
         }
         return code;
     }
@@ -3103,42 +3109,56 @@ int gx_downscaler_get_bits_rectangle(gx_
         for (j = 0; j < ds->num_planes; j++)
             memcpy(ds->pre_cm[j] + i*ds->span, ds->pre_cm[j] + (i-1)*ds->span, copy);
 
+    /* All the data is now in ds->pre_cm. Update params2.data so that this points to
+     * it. From here on in, we will keep params2.data pointing to whereever the
+     * latest processed version of the data is. */
     for (j = 0; j < ds->num_planes; j++)
         params2.data[j] = ds->pre_cm[j];
 
+    num_planes_to_downscale = ds->num_planes;
     if (ds->early_cm && ds->apply_cm) {
-        code = ds->apply_cm(ds->apply_cm_arg, ds->params.data, ds->post_cm, ds->dev->width, downfactor, params->raster);
+        code = ds->apply_cm(ds->apply_cm_arg, ds->post_cm, params2.data, ds->dev->width, downfactor, ds->span);
         if (code < 0)
             return code;
-        for (j = 0; j < ds->num_planes; j++)
+        for (j = 0; j < ds->post_cm_num_comps; j++)
             params2.data[j] = ds->post_cm[j];
+        num_planes_to_downscale = ds->post_cm_num_comps;
     }
 
     if (upfactor > 1) {
         /* Downscale the block of lines into our output buffer */
-        for (plane=0; plane < ds->num_planes; plane++) {
+        for (plane=0; plane < num_planes_to_downscale; plane++) {
             byte *scaled = ds->scaled_data + upfactor * plane * ds->scaled_span;
             (ds->down_core)(ds, scaled, params2.data[plane], row, plane, params2.raster);
-            params->data[plane] = scaled;
+            params2.data[plane] = scaled;
         }
     } else if (ds->down_core != NULL) {
         /* Downscale direct into output buffer */
-        for (plane=0; plane < ds->num_planes; plane++)
+        for (plane=0; plane < num_planes_to_downscale; plane++) {
             (ds->down_core)(ds, params->data[plane], params2.data[plane], row, plane, params2.raster);
+            params2.data[plane] = params->data[plane];
+        }
     } else {
         /* Copy into output buffer */
         /* No color management can be required here */
         assert(!ds->early_cm || ds->apply_cm == NULL);
-        for (plane=0; plane < ds->num_planes; plane++)
+        for (plane=0; plane < num_planes_to_downscale; plane++) {
             memcpy(params->data[plane], params2.data[plane], params2.raster);
+            params2.data[plane] = params->data[plane];
+        }
     }
 
     if (!ds->early_cm && ds->apply_cm) {
-        code = ds->apply_cm(ds->apply_cm_arg, ds->params.data, params2.data, ds->width, 1, params->raster);
+        code = ds->apply_cm(ds->apply_cm_arg, params->data, params2.data, ds->width, 1, params->raster);
         if (code < 0)
             return code;
+        for (plane=0; plane < num_planes_to_downscale; plane++)
+            params2.data[plane] = params->data[plane];
     }
 
+    for (plane=0; plane < num_planes_to_downscale; plane++)
+        params->data[plane] = params2.data[plane];
+
     return code;
 }
 
@@ -3566,3 +3586,71 @@ void ets_free(void *malloc_arg, void *p)
 
     gs_free_object((gs_memory_t *)malloc_arg, p, "ets_malloc");
 }
+
+int gx_downscaler_create_post_render_link(gx_device *dev, gsicc_link_t **link)
+{
+    cmm_dev_profile_t *profile_struct;
+    gsicc_rendering_param_t rendering_params;
+    int code = dev_proc(dev, get_profile)(dev, &profile_struct);
+    if (code < 0)
+        return_error(gs_error_undefined);
+
+    *link = NULL;
+    if (profile_struct->postren_profile == NULL)
+        return 0;
+
+    rendering_params.black_point_comp = gsBLACKPTCOMP_ON;
+    rendering_params.graphics_type_tag = GS_UNKNOWN_TAG;
+    rendering_params.override_icc = false;
+    rendering_params.preserve_black = gsBLACKPRESERVE_OFF;
+    rendering_params.rendering_intent = gsRELATIVECOLORIMETRIC;
+    rendering_params.cmm = gsCMM_DEFAULT;
+    *link = gsicc_alloc_link_dev(dev->memory,
+                                 profile_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE],
+                                 profile_struct->postren_profile,
+                                 &rendering_params);
+    if (*link == NULL)
+        return_error(gs_error_VMerror);
+
+    /* If it is identity, release it now and set link to NULL */
+    if ((*link)->is_identity) {
+        gsicc_free_link_dev(*link);
+        *link = NULL;
+    }
+    return 0;
+}
+
+int gx_downscaler_create_icc_link(gx_device *dev, gsicc_link_t **link, cmm_profile_t *icc_profile)
+{
+    gsicc_rendering_param_t rendering_params;
+    cmm_dev_profile_t *profile_struct;
+    int code = dev_proc(dev, get_profile)(dev, &profile_struct);
+
+    *link = NULL;
+
+    if (code < 0)
+        return code;
+
+    if (icc_profile == NULL)
+        return 0; /* Should be an error, maybe? */
+
+    rendering_params.black_point_comp = gsBLACKPTCOMP_ON;
+    rendering_params.graphics_type_tag = GS_UNKNOWN_TAG;
+    rendering_params.override_icc = false;
+    rendering_params.preserve_black = gsBLACKPRESERVE_OFF;
+    rendering_params.rendering_intent = gsRELATIVECOLORIMETRIC;
+    rendering_params.cmm = gsCMM_DEFAULT;
+    *link = gsicc_alloc_link_dev(dev->memory,
+                                 profile_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE],
+                                 icc_profile,
+                                 &rendering_params);
+    if (*link == NULL)
+        return_error(gs_error_VMerror);
+
+    /* If it is identity, release it now and set link to NULL */
+    if ((*link)->is_identity) {
+        gsicc_free_link_dev(*link);
+        *link = NULL;
+    }
+    return 0;
+}
diff -pruN 9.55.0~dfsg-3/base/gxdownscale.h 9.56.1~dfsg-1/base/gxdownscale.h
--- 9.55.0~dfsg-3/base/gxdownscale.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxdownscale.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -248,4 +248,20 @@ int gx_downscaler_write_params(gs_param_
                                gx_downscaler_params *params,
                                int                   features);
 
+/* A helper function for creating the post render ICC link.
+ * This maps from the device space to the space given by the
+ * post render profile entry in the device icc struct.
+ * This should be destroyed using gsicc_free_link_dev.*/
+int gx_downscaler_create_post_render_link(gx_device     *dev,
+                                          gsicc_link_t **link);
+
+/* A helper function for creating an ICC link. This maps
+ * from the device space to the space given by the
+ * supplied ICC profile.
+ * This should be destroyed using gsicc_free_link_dev.*/
+int
+gx_downscaler_create_icc_link(gx_device      *dev,
+                              gsicc_link_t  **link,
+                              cmm_profile_t  *profile);
+
 #endif
diff -pruN 9.55.0~dfsg-3/base/gxfapi.c 9.56.1~dfsg-1/base/gxfapi.c
--- 9.55.0~dfsg-3/base/gxfapi.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxfapi.c	2022-04-04 13:46:22.000000000 +0000
@@ -56,6 +56,7 @@ gs_fapi_renderer_retcode(gs_memory_t *me
 
 typedef struct gs_fapi_outline_handler_s
 {
+    gs_fapi_server *fserver;
     struct gx_path_s *path;
     fixed x0;
     fixed y0;
@@ -98,29 +99,33 @@ add_move(gs_fapi_path *I, int64_t x, int
 {
     gs_fapi_outline_handler *olh = (gs_fapi_outline_handler *) I->olh;
 
-    x = import_shift(x, I->shift) + olh->x0;
-    y = -import_shift(y, I->shift) + olh->y0;
-
-    if (x > (int64_t) max_fixed) {
-        x = (int64_t) max_fixed;
-    }
-    else if (x < (int64_t) min_fixed) {
-        x = (int64_t) min_fixed;
-    }
-
-    if (y > (int64_t) max_fixed) {
-        y = (int64_t) max_fixed;
-    }
-    else if (y < (int64_t) min_fixed) {
-        y = (int64_t) min_fixed;
+    x = import_shift(x, I->shift);
+    y = -import_shift(y, I->shift);
+    if (olh->fserver->transform_outline) {
+        gs_point pt;
+        I->gs_error = gs_distance_transform((double)fixed2float((float)x), (double)fixed2float((float)y), &olh->fserver->outline_mat, &pt);
+        if (I->gs_error < 0)
+            return I->gs_error;
+        x = float2fixed(pt.x);
+        y = float2fixed(pt.y);
+    }
+    x += olh->x0;
+    y += olh->y0;
+
+    if (x > (int64_t) max_coord_fixed || x < (int64_t) min_coord_fixed
+     || y > (int64_t) max_coord_fixed || y < (int64_t) min_coord_fixed) {
+         I->gs_error = gs_error_undefinedresult;
     }
+    else {
 
-    if (olh->need_close && olh->close_path)
-        if ((I->gs_error = add_closepath(I)) < 0)
-            return (I->gs_error);
-    olh->need_close = false;
-    I->gs_error = gx_path_add_point(olh->path, (fixed) x, (fixed) y);
+        if (olh->need_close && olh->close_path)
+            if ((I->gs_error = add_closepath(I)) < 0)
+                return (I->gs_error);
+        olh->need_close = false;
 
+/*        dprintf2("%f %f moveto\n", fixed2float(x), fixed2float(y)); */
+        I->gs_error = gx_path_add_point(olh->path, (fixed) x, (fixed) y);
+    }
     return (I->gs_error);
 }
 
@@ -129,24 +134,29 @@ add_line(gs_fapi_path *I, int64_t x, int
 {
     gs_fapi_outline_handler *olh = (gs_fapi_outline_handler *) I->olh;
 
-    x = import_shift(x, I->shift) + olh->x0;
-    y = -import_shift(y, I->shift) + olh->y0;
-    if (x > (int64_t) max_fixed) {
-        x = (int64_t) max_fixed;
-    }
-    else if (x < (int64_t) min_fixed) {
-        x = (int64_t) min_fixed;
+    x = import_shift(x, I->shift);
+    y = -import_shift(y, I->shift);
+    if (olh->fserver->transform_outline) {
+        gs_point pt;
+        I->gs_error = gs_distance_transform((double)fixed2float((float)x), (double)fixed2float((float)y), &olh->fserver->outline_mat, &pt);
+        if (I->gs_error < 0)
+            return I->gs_error;
+        x = float2fixed(pt.x);
+        y = float2fixed(pt.y);
+    }
+    x += olh->x0;
+    y += olh->y0;
+
+    if (x > (int64_t) max_coord_fixed || x < (int64_t) min_coord_fixed
+     || y > (int64_t) max_coord_fixed || y < (int64_t) min_coord_fixed) {
+         I->gs_error = gs_error_undefinedresult;
     }
+    else {
+        olh->need_close = true;
 
-    if (y > (int64_t) max_fixed) {
-        y = (int64_t) max_fixed;
+/*        dprintf2("%f %f lineto\n", fixed2float(x), fixed2float(y)); */
+        I->gs_error = gx_path_add_line_notes(olh->path, (fixed) x, (fixed) y, 0);
     }
-    else if (y < (int64_t) min_fixed) {
-        y = (int64_t) min_fixed;
-    }
-
-    olh->need_close = true;
-    I->gs_error = gx_path_add_line_notes(olh->path, (fixed) x, (fixed) y, 0);
     return (I->gs_error);
 }
 
@@ -156,57 +166,53 @@ add_curve(gs_fapi_path *I, int64_t x0, i
 {
     gs_fapi_outline_handler *olh = (gs_fapi_outline_handler *) I->olh;
 
-    x0 = import_shift(x0, I->shift) + olh->x0;
-    y0 = -import_shift(y0, I->shift) + olh->y0;
-    x1 = import_shift(x1, I->shift) + olh->x0;
-    y1 = -import_shift(y1, I->shift) + olh->y0;
-    x2 = import_shift(x2, I->shift) + olh->x0;
-    y2 = -import_shift(y2, I->shift) + olh->y0;
-
-    if (x0 > (int64_t) max_fixed) {
-        x0 = (int64_t) max_fixed;
-    }
-    else if (x0 < (int64_t) min_fixed) {
-        x0 = (int64_t) min_fixed;
-    }
+    x0 = import_shift(x0, I->shift);
+    y0 = -import_shift(y0, I->shift);
+    x1 = import_shift(x1, I->shift);
+    y1 = -import_shift(y1, I->shift);
+    x2 = import_shift(x2, I->shift);
+    y2 = -import_shift(y2, I->shift);
 
-    if (y0 > (int64_t) max_fixed) {
-        y0 = (int64_t) max_fixed;
-    }
-    else if (y0 < (int64_t) min_fixed) {
-        y0 = (int64_t) min_fixed;
-    }
-    if (x1 > (int64_t) max_fixed) {
-        x1 = (int64_t) max_fixed;
-    }
-    else if (x1 < (int64_t) min_fixed) {
-        x1 = (int64_t) min_fixed;
-    }
-
-    if (y1 > (int64_t) max_fixed) {
-        y1 = (int64_t) max_fixed;
-    }
-    else if (y1 < (int64_t) min_fixed) {
-        y1 = (int64_t) min_fixed;
-    }
-    if (x2 > (int64_t) max_fixed) {
-        x2 = (int64_t) max_fixed;
-    }
-    else if (x2 < (int64_t) min_fixed) {
-        x2 = (int64_t) min_fixed;
+    if (olh->fserver->transform_outline) {
+        gs_point pt;
+        I->gs_error = gs_distance_transform((double)fixed2float((float)x0), (double)fixed2float((float)y0), &olh->fserver->outline_mat, &pt);
+        if (I->gs_error < 0)
+            return I->gs_error;
+        x0 = float2fixed(pt.x);
+        y0 = float2fixed(pt.y);
+        I->gs_error = gs_distance_transform((double)fixed2float((float)x1), (double)fixed2float((float)y1), &olh->fserver->outline_mat, &pt);
+        if (I->gs_error < 0)
+            return I->gs_error;
+        x1 = float2fixed(pt.x);
+        y1 = float2fixed(pt.y);
+        I->gs_error = gs_distance_transform((double)fixed2float((float)x2), (double)fixed2float((float)y2), &olh->fserver->outline_mat, &pt);
+        if (I->gs_error < 0)
+            return I->gs_error;
+        x2 = float2fixed(pt.x);
+        y2 = float2fixed(pt.y);
+    }
+    x0 += olh->x0;
+    y0 += olh->y0;
+    x1 += olh->x0;
+    y1 += olh->y0;
+    x2 += olh->x0;
+    y2 += olh->y0;
+
+    if (x0 > (int64_t) max_coord_fixed || x0 < (int64_t) min_coord_fixed
+     || y0 > (int64_t) max_coord_fixed || y0 < (int64_t) min_coord_fixed
+     || x1 > (int64_t) max_coord_fixed || x1 < (int64_t) min_coord_fixed
+     || y1 > (int64_t) max_coord_fixed || y1 < (int64_t) min_coord_fixed
+     || x2 > (int64_t) max_coord_fixed || x2 < (int64_t) min_coord_fixed
+     || y2 > (int64_t) max_coord_fixed || y2 < (int64_t) min_coord_fixed)
+    {
+        I->gs_error = gs_error_undefinedresult;
     }
+    else {
+        olh->need_close = true;
 
-    if (y2 > (int64_t) max_fixed) {
-        y2 = (int64_t) max_fixed;
-    }
-    else if (y2 < (int64_t) min_fixed) {
-        y2 = (int64_t) min_fixed;
+/*        dprintf6("%f %f %f %f %f %f curveto\n", fixed2float(x0), fixed2float(y0), fixed2float(x1), fixed2float(y1), fixed2float(x2), fixed2float(y2));*/
+        I->gs_error = gx_path_add_curve_notes(olh->path, (fixed) x0, (fixed) y0, (fixed) x1, (fixed) y1, (fixed) x2, (fixed) y2, 0);
     }
-
-    olh->need_close = true;
-    I->gs_error =
-        gx_path_add_curve_notes(olh->path, (fixed) x0, (fixed) y0, (fixed) x1,
-                                (fixed) y1, (fixed) x2, (fixed) y2, 0);
     return (I->gs_error);
 }
 
@@ -567,10 +573,14 @@ outline_char(gs_memory_t *mem, gs_fapi_s
 {
     gs_fapi_path path_interface = path_interface_stub;
     gs_fapi_outline_handler olh;
-    int code;
+    int code = 0;
     gs_gstate *pgs = penum_s->pgs;
+    struct gx_path_s path1;
+
+    (void)gx_path_init_local(&path1, mem);
 
-    olh.path = path;
+    olh.fserver = I;
+    olh.path = &path1;
     olh.x0 = pgs->ctm.tx_fixed - float2fixed(penum_s->fapi_glyph_shift.x);
     olh.y0 = pgs->ctm.ty_fixed - float2fixed(penum_s->fapi_glyph_shift.y);
     olh.close_path = close_path;
@@ -582,15 +592,22 @@ outline_char(gs_memory_t *mem, gs_fapi_s
                                   I->get_char_outline(I,
                                                       &path_interface))) < 0
         || path_interface.gs_error != 0) {
-        if (path_interface.gs_error != 0)
-            return path_interface.gs_error;
-        else
-            return code;
+        if (path_interface.gs_error != 0) {
+            code = path_interface.gs_error;
+            goto done;
+        }
+        else {
+            goto done;
+        }
     }
     if (olh.need_close && olh.close_path)
         if ((code = add_closepath(&path_interface)) < 0)
-            return code;
-    return 0;
+            goto done;
+    code = gx_path_copy(&path1, path);
+done:
+    code = code >= 0 || code == gs_error_undefinedresult ? 0 : code;
+    gx_path_free(&path1, "outline_char");
+    return code;
 }
 
 static void
@@ -1234,6 +1251,7 @@ gs_fapi_do_char(gs_font *pfont, gs_gstat
         return 0;
 
     I->use_outline = false;
+    I->transform_outline = false;
 
     if (penum == 0)
         return_error(gs_error_undefined);
@@ -1302,7 +1320,7 @@ gs_fapi_do_char(gs_font *pfont, gs_gstat
     }
 
    scale = 1 << I->frac_shift;
-  retry_oversampling:
+retry_oversampling:
     if (I->face.font_id != pbfont->id ||
         !MTX_EQ((&I->face.ctm), ctm) ||
         I->face.log2_scale.x != log2_scale.x ||
@@ -1316,10 +1334,9 @@ gs_fapi_do_char(gs_font *pfont, gs_gstat
         , {1, 1}
         , true
         };
-        gs_matrix scale_mat, scale_ctm;
 
+        gs_matrix lctm, scale_mat, scale_ctm;
         I->face.font_id = pbfont->id;
-        I->face.ctm = *ctm;
         I->face.log2_scale = log2_scale;
         I->face.align_to_pixels = align_to_pixels;
         I->face.HWResolution[0] = dev->HWResolution[0];
@@ -1332,7 +1349,9 @@ gs_fapi_do_char(gs_font *pfont, gs_gstat
         /* We apply the entire transform to the glyph (that is ctm x FontMatrix)
          * at render time.
          */
-
+        lctm = *ctm;
+retry_scaling:
+        I->face.ctm = lctm;
         memset(&scale_ctm, 0x00, sizeof(gs_matrix));
         scale_ctm.xx = dev->HWResolution[0] / 72;
         scale_ctm.yy = dev->HWResolution[1] / 72;
@@ -1340,7 +1359,7 @@ gs_fapi_do_char(gs_font *pfont, gs_gstat
         if ((code = gs_matrix_invert((const gs_matrix *)&scale_ctm, &scale_ctm)) < 0)
             return code;
 
-        if ((code = gs_matrix_multiply(ctm, &scale_ctm, &scale_mat)) < 0)  /* scale_mat ==  CTM - resolution scaling */
+        if ((code = gs_matrix_multiply(&lctm, &scale_ctm, &scale_mat)) < 0)  /* scale_mat ==  CTM - resolution scaling */
             return code;
 
         if ((code = I->get_fontmatrix(I, &scale_ctm)) < 0)
@@ -1352,18 +1371,38 @@ gs_fapi_do_char(gs_font *pfont, gs_gstat
         if ((code = gs_matrix_multiply(&scale_mat, &scale_ctm, &scale_mat)) < 0)  /* scale_mat ==  CTM - resolution scaling - FontMatrix scaling */
             return code;
 
-        font_scale.matrix[0] =
-            (fracint) (scale_mat.xx * FontMatrix_div * scale + 0.5);
-        font_scale.matrix[1] =
-            -(fracint) (scale_mat.xy * FontMatrix_div * scale + 0.5);
-        font_scale.matrix[2] =
-            (fracint) (scale_mat.yx * FontMatrix_div * scale + 0.5);
-        font_scale.matrix[3] =
-            -(fracint) (scale_mat.yy * FontMatrix_div * scale + 0.5);
-        font_scale.matrix[4] =
-            (fracint) (scale_mat.tx * FontMatrix_div * scale + 0.5);
-        font_scale.matrix[5] =
-            (fracint) (scale_mat.ty * FontMatrix_div * scale + 0.5);
+        if (((int64_t)(scale_mat.xx * FontMatrix_div * scale + 0.5)) != ((int32_t)(scale_mat.xx * FontMatrix_div * scale + 0.5))
+        ||  ((int64_t)(scale_mat.xy * FontMatrix_div * scale + 0.5)) != ((int32_t)(scale_mat.xy * FontMatrix_div * scale + 0.5))
+        ||  ((int64_t)(scale_mat.yx * FontMatrix_div * scale + 0.5)) != ((int32_t)(scale_mat.yx * FontMatrix_div * scale + 0.5))
+        ||  ((int64_t)(scale_mat.yy * FontMatrix_div * scale + 0.5)) != ((int32_t)(scale_mat.yy * FontMatrix_div * scale + 0.5))) {
+            /* Overflow
+               If the scaling is large enough to overflow the 16.16 representation, we forcibly produce an outline
+               unscaled except an arbitrary "midrange" scale (chosen to avoid under/overflow issues). And
+               then scale the points as we create the Ghostscript path outline_char().
+               If the glyph is this large, we're really not worried about hinting or dropout detection etc.
+             */
+
+            memset(&lctm, 0x00, sizeof(gs_matrix));
+            lctm.xx = 256.0;
+            lctm.yy = 256.0;
+            I->transform_outline = true;
+            I->use_outline = true;
+            if ((code = gs_matrix_invert((const gs_matrix *)&lctm, &scale_ctm)) < 0)
+                 return code;
+            if ((code = gs_matrix_multiply(ctm, &scale_ctm, &scale_mat)) < 0)  /* scale_mat ==  CTM - resolution scaling */
+                return code;
+
+            I->outline_mat = scale_mat;
+            goto retry_scaling;
+        }
+        else {
+            font_scale.matrix[0] = (fracint) (scale_mat.xx * FontMatrix_div * scale + 0.5);
+            font_scale.matrix[1] = -(fracint) (scale_mat.xy * FontMatrix_div * scale + 0.5);
+            font_scale.matrix[2] = (fracint) (scale_mat.yx * FontMatrix_div * scale + 0.5);
+            font_scale.matrix[3] = -(fracint) (scale_mat.yy * FontMatrix_div * scale + 0.5);
+            font_scale.matrix[4] = (fracint) (scale_mat.tx * FontMatrix_div * scale + 0.5);
+            font_scale.matrix[5] = (fracint) (scale_mat.ty * FontMatrix_div * scale + 0.5);
+        }
 
         /* Note: the ctm mapping here is upside down. */
         font_scale.HWResolution[0] =
@@ -1380,6 +1419,7 @@ gs_fapi_do_char(gs_font *pfont, gs_gstat
                       (double)font_scale.matrix[3]) == 0.0)) {
 
             /* If the matrix is degenerate, force a scale to 1 unit. */
+            memset(&font_scale.matrix, 0x00, sizeof(font_scale.matrix));
             if (!font_scale.matrix[0])
                 font_scale.matrix[0] = 1;
             if (!font_scale.matrix[3])
diff -pruN 9.55.0~dfsg-3/base/gxfapi.h 9.56.1~dfsg-1/base/gxfapi.h
--- 9.55.0~dfsg-3/base/gxfapi.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxfapi.h	2022-04-04 13:46:22.000000000 +0000
@@ -329,6 +329,8 @@ struct gs_fapi_server_s
     gs_fapi_font ff;
     int max_bitmap;
     bool use_outline;
+    bool transform_outline;
+    gs_matrix outline_mat;
     uint grid_fit;
     gs_matrix initial_FontMatrix;       /* Font Matrix at the time the font is defined */
     /* Used to use the stored 'OrigFont' entry but */
diff -pruN 9.55.0~dfsg-3/base/gxfcache.h 9.56.1~dfsg-1/base/gxfcache.h
--- 9.55.0~dfsg-3/base/gxfcache.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxfcache.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -126,8 +126,8 @@ struct cached_char_s {
     /* gx_cached_bits_common includes depth. */
 
     gx_cached_bits_common;	/* (must be first) */
-#define cc_depth(cc) ((cc)->cb_depth)
-#define cc_set_depth(cc, d) ((cc)->cb_depth = (d))
+#define cc_depth(cc) ((cc)->head.depth)
+#define cc_set_depth(cc, d) ((cc)->head.depth = (d))
     cached_fm_pair *pair;
     bool linked;
 #define cc_pair(cc) ((cc)->pair)
diff -pruN 9.55.0~dfsg-3/base/gxfill.c 9.56.1~dfsg-1/base/gxfill.c
--- 9.55.0~dfsg-3/base/gxfill.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxfill.c	2022-04-04 13:46:22.000000000 +0000
@@ -80,7 +80,7 @@
 #define TRY_TO_EXTEND_TRAP 0
 #endif
 
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_FILL
 /* Define the statistics structure instance. */
 stats_fill_t stats_fill;
 #endif
@@ -525,7 +525,7 @@ gx_general_fill_path(gx_device * pdev, c
     free_line_list(&lst);
     if (pfpath != ppath)        /* had to flatten */
         gx_path_free(pfpath, "gx_general_fill_path");
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_FILL
     if (gs_debug_c('f')) {
         dmlputs(ppath->memory,
                 "[f]  # alloc    up  down horiz step slowx  iter  find  band bstep bfill\n");
@@ -567,103 +567,110 @@ pass_shading_area_through_clip_path_devi
     return gx_default_fill_path(pdev, pgs, ppath, params, pdevc, pcpath);
 }
 
-/*
- * Fill a path.  This is the default implementation of the driver
- * fill_path procedure.
- */
+/*  Optimization for shading and halftone fill :
+    The general filling algorithm subdivides the fill region into
+    trapezoid or rectangle subregions and then paints each subregion
+    with given color. If the color is complex, it also needs to be subdivided
+    into constant color rectangles. In the worst case it gives
+    a multiple of numbers of rectangles, which may be too slow.
+    A faster processing may be obtained with installing a clipper
+    device with the filling path, and then render the complex color
+    through it. The speeding up happens due to the clipper device
+    is optimised for fast scans through neighbour clipping rectangles.
+*/
 int
-gx_default_fill_path(gx_device * pdev, const gs_gstate * pgs,
-                     gx_path * ppath, const gx_fill_params * params,
-                 const gx_device_color * pdevc, const gx_clip_path * pcpath)
+gx_default_fill_path_shading_or_pattern(gx_device * pdev, const gs_gstate * pgs,
+                                        gx_path * ppath, const gx_fill_params * params,
+                                        const gx_device_color * pdevc, const gx_clip_path * pcpath)
 {
     int code = 0;
+    /*  We need a single clipping path here, because shadings and
+        halftones don't take 2 paths. Compute the clipping path intersection.
+    */
+    gx_clip_path cpath_intersection, cpath_with_shading_bbox;
+    const gx_clip_path *pcpath1, *pcpath2;
+    gs_gstate *pgs_noconst = (gs_gstate *)pgs; /* Break const. */
 
-    if (gx_dc_is_pattern2_color(pdevc)
-        || pdevc->type == &gx_dc_type_data_ht_colored
-        || (gx_dc_is_pattern1_color(pdevc) && gx_pattern_tile_is_clist(pdevc->colors.pattern.p_tile))
-        ) {
-        /*  Optimization for shading and halftone fill :
-            The general filling algorithm subdivides the fill region into
-            trapezoid or rectangle subregions and then paints each subregion
-            with given color. If the color is complex, it also needs to be subdivided
-            into constant color rectangles. In the worst case it gives
-            a multiple of numbers of rectangles, which may be too slow.
-            A faster processing may be obtained with installing a clipper
-            device with the filling path, and then render the complex color
-            through it. The speeding up happens due to the clipper device
-            is optimised for fast scans through neighbour clipping rectangles.
-        */
-        /*  We need a single clipping path here, because shadings and
-            halftones don't take 2 paths. Compute the clipping path intersection.
-        */
-        gx_clip_path cpath_intersection, cpath_with_shading_bbox;
-        const gx_clip_path *pcpath1, *pcpath2;
-        gs_gstate *pgs_noconst = (gs_gstate *)pgs; /* Break const. */
-
-        if (ppath != NULL) {
-            code = gx_cpath_init_local_shared_nested(&cpath_intersection, pcpath, pdev->memory, 1);
-            if (code < 0)
-                return code;
-            if (pcpath == NULL) {
-                gs_fixed_rect clip_box1;
+    if (ppath != NULL) {
+        code = gx_cpath_init_local_shared_nested(&cpath_intersection, pcpath, pdev->memory, 1);
+        if (code < 0)
+            return code;
+        if (pcpath == NULL) {
+            gs_fixed_rect clip_box1;
 
-                (*dev_proc(pdev, get_clipping_box)) (pdev, &clip_box1);
-                code = gx_cpath_from_rectangle(&cpath_intersection, &clip_box1);
-            }
-            if (code >= 0)
-                code = gx_cpath_intersect_with_params(&cpath_intersection, ppath, params->rule,
-                        pgs_noconst, params);
-            pcpath1 = &cpath_intersection;
-        } else
-            pcpath1 = pcpath;
-        pcpath2 = pcpath1;
+            (*dev_proc(pdev, get_clipping_box)) (pdev, &clip_box1);
+            code = gx_cpath_from_rectangle(&cpath_intersection, &clip_box1);
+        }
         if (code >= 0)
-            code = gx_dc_pattern2_clip_with_bbox(pdevc, pdev, &cpath_with_shading_bbox, &pcpath1);
-        /* Do fill : */
-        if (code >= 0) {
-            gs_fixed_rect clip_box;
-            gs_int_rect cb;
-            const gx_rop_source_t *rs = NULL;
-            gx_device *dev;
-            gx_device_clip cdev;
-
-            gx_cpath_outer_box(pcpath1, &clip_box);
-            cb.p.x = fixed2int_pixround(clip_box.p.x);
-            cb.p.y = fixed2int_pixround(clip_box.p.y);
-            cb.q.x = fixed2int_pixround(clip_box.q.x);
-            cb.q.y = fixed2int_pixround(clip_box.q.y);
-            if (gx_dc_is_pattern2_color(pdevc) &&
-                    (*dev_proc(pdev, dev_spec_op))(pdev,
+            code = gx_cpath_intersect_with_params(&cpath_intersection, ppath, params->rule,
+                                                  pgs_noconst, params);
+        pcpath1 = &cpath_intersection;
+    } else
+        pcpath1 = pcpath;
+    pcpath2 = pcpath1;
+    if (code >= 0)
+        code = gx_dc_pattern2_clip_with_bbox(pdevc, pdev, &cpath_with_shading_bbox, &pcpath1);
+    /* Do fill : */
+    if (code >= 0) {
+        gs_fixed_rect clip_box;
+        gs_int_rect cb;
+        const gx_rop_source_t *rs = NULL;
+        gx_device *dev;
+        gx_device_clip cdev;
+
+        gx_cpath_outer_box(pcpath1, &clip_box);
+        cb.p.x = fixed2int_pixround(clip_box.p.x);
+        cb.p.y = fixed2int_pixround(clip_box.p.y);
+        cb.q.x = fixed2int_pixround(clip_box.q.x);
+        cb.q.y = fixed2int_pixround(clip_box.q.y);
+        if (gx_dc_is_pattern2_color(pdevc) &&
+                (*dev_proc(pdev, dev_spec_op))(pdev,
                          gxdso_pattern_handles_clip_path, NULL, 0) > 0) {
-                /* A special interaction with clist writer device :
-                   pass the intersected clipping path. It uses an unusual call to
-                   fill_path with NULL device color. */
-                code = (*dev_proc(pdev, fill_path))(pdev, pgs, ppath, params, NULL, pcpath1);
-                dev = pdev;
-            } else {
-                gx_make_clip_device_on_stack(&cdev, pcpath1, pdev);
-                dev = (gx_device *)&cdev;
-                if ((*dev_proc(pdev, dev_spec_op))(pdev,
+            /* A special interaction with clist writer device :
+               pass the intersected clipping path. It uses an unusual call to
+               fill_path with NULL device color. */
+            code = (*dev_proc(pdev, fill_path))(pdev, pgs, ppath, params, NULL, pcpath1);
+            dev = pdev;
+        } else {
+            gx_make_clip_device_on_stack(&cdev, pcpath1, pdev);
+            dev = (gx_device *)&cdev;
+            if ((*dev_proc(pdev, dev_spec_op))(pdev,
                         gxdso_pattern_shading_area, NULL, 0) > 0)
-                    set_dev_proc(&cdev, fill_path, pass_shading_area_through_clip_path_device);
-                code = 0;
-            }
-            if (code >= 0)
-                code = pdevc->type->fill_rectangle(pdevc,
+                set_dev_proc(&cdev, fill_path, pass_shading_area_through_clip_path_device);
+            code = 0;
+        }
+        if (code >= 0)
+            code = pdevc->type->fill_rectangle(pdevc,
                         cb.p.x, cb.p.y, cb.q.x - cb.p.x, cb.q.y - cb.p.y,
                         dev, pgs->log_op, rs);
-        }
-        if (ppath != NULL)
-            gx_cpath_free(&cpath_intersection, "shading_fill_cpath_intersection");
-        if (pcpath1 != pcpath2)
-            gx_cpath_free(&cpath_with_shading_bbox, "shading_fill_cpath_intersection");
-    } else {
-        code = gx_general_fill_path(pdev, pgs, ppath, params, pdevc, pcpath);
     }
+    if (ppath != NULL)
+        gx_cpath_free(&cpath_intersection, "shading_fill_cpath_intersection");
+    if (pcpath1 != pcpath2)
+        gx_cpath_free(&cpath_with_shading_bbox, "shading_fill_cpath_intersection");
+
     return code;
 }
 
 /*
+ * Fill a path.  This is the default implementation of the driver
+ * fill_path procedure.
+ */
+int
+gx_default_fill_path(gx_device * pdev, const gs_gstate * pgs,
+                     gx_path * ppath, const gx_fill_params * params,
+                 const gx_device_color * pdevc, const gx_clip_path * pcpath)
+{
+    if (gx_dc_is_pattern2_color(pdevc) ||
+        pdevc->type == &gx_dc_type_data_ht_colored ||
+        (gx_dc_is_pattern1_color(pdevc) &&
+         gx_pattern_tile_is_clist(pdevc->colors.pattern.p_tile)))
+        return gx_default_fill_path_shading_or_pattern(pdev, pgs, ppath, params, pdevc, pcpath);
+    else
+        return gx_general_fill_path(pdev, pgs, ppath, params, pdevc, pcpath);
+}
+
+/*
  * Fill/Stroke a path.  This is the default implementation of the driver
  * fill_path procedure.
  */
diff -pruN 9.55.0~dfsg-3/base/gxfill.h 9.56.1~dfsg-1/base/gxfill.h
--- 9.55.0~dfsg-3/base/gxfill.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxfill.h	2022-04-04 13:46:22.000000000 +0000
@@ -155,7 +155,9 @@ struct line_list_s {
 
 /* ---------------- Statistics ---------------- */
 
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+/* #define COLLECT_STATS_FILL */
+
+#ifdef COLLECT_STATS_FILL
 struct stats_fill_s {
     long
         fill, fill_alloc, y_up, y_down, horiz, x_step, slow_x, iter, find_y,
diff -pruN 9.55.0~dfsg-3/base/gxhldevc.c 9.56.1~dfsg-1/base/gxhldevc.c
--- 9.55.0~dfsg-3/base/gxhldevc.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxhldevc.c	2022-04-04 13:46:22.000000000 +0000
@@ -117,9 +117,7 @@ bool gx_hld_saved_color_equal(const gx_h
     /* early bailout for pattern comparison */
     if (gx_dc_is_pattern1_color((gx_device_color *)(&psc1->saved_dev_color.type))) {
 
-        if (psc1->saved_dev_color.colors.pattern.id != psc2->saved_dev_color.colors.pattern.id
-         || psc1->saved_dev_color.colors.pattern.phase.x != psc2->saved_dev_color.colors.pattern.phase.x
-         || psc1->saved_dev_color.colors.pattern.phase.y != psc2->saved_dev_color.colors.pattern.phase.y)
+        if (psc1->saved_dev_color.colors.pattern.id != psc2->saved_dev_color.colors.pattern.id)
             return(false);
         else
             return true;
diff -pruN 9.55.0~dfsg-3/base/gxht.c 9.56.1~dfsg-1/base/gxht.c
--- 9.55.0~dfsg-3/base/gxht.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxht.c	2022-04-04 13:46:22.000000000 +0000
@@ -352,10 +352,14 @@ gx_dc_ht_binary_equal(const gx_device_co
  * The binary halftone tile is never transmitted as part of the string
  * representation, so there is also no flag bit for it.
  */
-static const int   dc_ht_binary_has_color0 = 0x01;
-static const int   dc_ht_binary_has_color1 = 0x02;
-static const int   dc_ht_binary_has_level = 0x04;
-static const int   dc_ht_binary_has_index = 0x08;
+enum {
+    dc_ht_binary_has_color0 = 0x01,
+    dc_ht_binary_has_color1 = 0x02,
+    dc_ht_binary_has_level = 0x04,
+    dc_ht_binary_has_index = 0x08,
+    dc_ht_binary_has_phase_x = 0x10,
+    dc_ht_binary_has_phase_y = 0x20,
+};
 
 /*
  * Serialize a binany halftone device color.
@@ -421,7 +425,7 @@ gx_dc_ht_binary_write(
                                  &tmp_size );
         req_size += tmp_size;
     }
-    if ( psdc == 0                                                      ||
+    if ( psdc == NULL ||
          pdevc->colors.binary.color[1] != psdc->colors.binary.b_color[1]  ) {
         flag_bits |= dc_ht_binary_has_color1;
         tmp_size = 0;
@@ -432,18 +436,30 @@ gx_dc_ht_binary_write(
         req_size += tmp_size;
     }
 
-    if ( psdc == 0                                                  ||
+    if ( psdc == NULL ||
          pdevc->colors.binary.b_level != psdc->colors.binary.b_level  ) {
         flag_bits |= dc_ht_binary_has_level;
         req_size += enc_u_sizew(pdevc->colors.binary.b_level);
     }
 
-    if ( psdc == 0                                                  ||
+    if ( psdc == NULL ||
          pdevc->colors.binary.b_index != psdc->colors.binary.b_index  ) {
         flag_bits |= dc_ht_binary_has_index;
         req_size += 1;
     }
 
+    if ( psdc == NULL ||
+         pdevc->phase.x != psdc->phase.x ) {
+        flag_bits |= dc_ht_binary_has_phase_x;
+        req_size += enc_u_sizew(pdevc->phase.x);
+    }
+
+    if ( psdc == NULL ||
+         pdevc->phase.y != psdc->phase.y ) {
+        flag_bits |= dc_ht_binary_has_phase_y;
+        req_size += enc_u_sizew(pdevc->phase.y);
+    }
+
     /* check if there is anything to be done */
     if (flag_bits == 0) {
         *psize = 0;
@@ -484,6 +500,10 @@ gx_dc_ht_binary_write(
         enc_u_putw(pdevc->colors.binary.b_level, pdata);
     if ((flag_bits & dc_ht_binary_has_index) != 0)
         *pdata++ = pdevc->colors.binary.b_index;
+    if ((flag_bits & dc_ht_binary_has_phase_x) != 0)
+        enc_u_putw(pdevc->phase.x, pdata);
+    if ((flag_bits & dc_ht_binary_has_phase_y) != 0)
+        enc_u_putw(pdevc->phase.y, pdata);
 
     *psize = pdata - pdata0;
     return 0;
@@ -528,7 +548,9 @@ gx_dc_ht_binary_read(
     int64_t		    offset,
     const byte *            pdata,
     uint                    size,
-    gs_memory_t *           mem )       /* ignored */
+    gs_memory_t *           mem,        /* ignored */
+    int                     x0,
+    int                     y0)
 {
     gx_device_color         devc;
     const byte *            pdata0 = pdata;
@@ -547,7 +569,7 @@ gx_dc_ht_binary_read(
     /* the halftone is always taken from the gs_gstate */
     devc.colors.binary.b_ht = pgs->dev_ht[HT_OBJTYPE_DEFAULT];
 
-    /* cache is not porvided until the device color is used */
+    /* cache is not provided until the device color is used */
     devc.colors.binary.b_tile = 0;
 
     /* verify the minimum amount of information */
@@ -578,7 +600,7 @@ gx_dc_ht_binary_read(
         pdata += code;
     }
     if ((flag_bits & dc_ht_binary_has_level) != 0) {
-        const byte *    pdata_start = pdata;
+        const byte *pdata_start = pdata;
 
         if (size < 1)
             return_error(gs_error_rangecheck);
@@ -591,15 +613,24 @@ gx_dc_ht_binary_read(
         --size;
         devc.colors.binary.b_index = *pdata++;
     }
+    if ((flag_bits & dc_ht_binary_has_phase_x) != 0) {
+        const byte *pdata_start = pdata;
+
+        if (size < 1)
+            return_error(gs_error_rangecheck);
+        enc_u_getw(devc.phase.x, pdata);
+        devc.phase.x += x0;
+        size -= pdata - pdata_start;
+    }
+    if ((flag_bits & dc_ht_binary_has_phase_y) != 0) {
+        const byte *pdata_start = pdata;
 
-    if (pgs->dev_ht[HT_OBJTYPE_DEFAULT] == NULL)
-        return_error(gs_error_unregistered); /* Must not happen. */
-    /* set the phase as required (select value is arbitrary) */
-    color_set_phase_mod( &devc,
-                         pgs->screen_phase[0].x,
-                         pgs->screen_phase[0].y,
-                         pgs->dev_ht[HT_OBJTYPE_DEFAULT]->lcm_width,
-                         pgs->dev_ht[HT_OBJTYPE_DEFAULT]->lcm_height );
+        if (size < 1)
+            return_error(gs_error_rangecheck);
+        enc_u_getw(devc.phase.y, pdata);
+        devc.phase.y += y0;
+        size -= pdata - pdata_start;
+    }
 
     /* everything looks good */
     *pdevc = devc;
diff -pruN 9.55.0~dfsg-3/base/gxht_thresh.c 9.56.1~dfsg-1/base/gxht_thresh.c
--- 9.55.0~dfsg-3/base/gxht_thresh.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxht_thresh.c	2022-04-04 13:46:22.000000000 +0000
@@ -1017,6 +1017,8 @@ gxht_thresh_planes(gx_image_enum *penum,
                         dx = penum->ht_landscape.xstart;
                     }
                     dx = (dx + penum->pgs->screen_phase[0].x) % thresh_width;
+                    if (dx < 0)
+                        dx += thresh_width;
                     dy = (penum->ht_landscape.y_pos -
                               penum->pgs->screen_phase[0].y) % thresh_height;
                     if (dy < 0)
diff -pruN 9.55.0~dfsg-3/base/gxicolor.c 9.56.1~dfsg-1/base/gxicolor.c
--- 9.55.0~dfsg-3/base/gxicolor.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxicolor.c	2022-04-04 13:46:22.000000000 +0000
@@ -698,11 +698,11 @@ image_render_color_thresh(gx_image_enum
                 xrun += penum->x_extent.x;
             vdi = penum->hci;
             contone_stride = penum->line_size;
-            offset_threshold = (- (((long)(penum->thresh_buffer)) +
-                                      penum->ht_offset_bits)) & 15;
+            offset_threshold = (- (((int)(intptr_t)(penum->thresh_buffer)) +
+                                   penum->ht_offset_bits)) & 15;
             for (k = 0; k < spp_out; k ++) {
-                offset_contone[k]   = (- (((long)(penum->line)) +
-                                          (long)contone_stride * k +
+                offset_contone[k]   = (- (((int)(intptr_t)(penum->line)) +
+                                          contone_stride * k +
                                           penum->ht_offset_bits)) & 15;
             }
             data_length = dest_width;
@@ -725,10 +725,10 @@ image_render_color_thresh(gx_image_enum
             xrun = dda_current(dda_ht);            /* really yrun, but just used here for landscape */
             dest_height = gxht_dda_length(&dda_ht, src_size);
             data_length = dest_height;
-            offset_threshold = (-(long)(penum->thresh_buffer)) & 15;
+            offset_threshold = (-(int)(intptr_t)(penum->thresh_buffer)) & 15;
             for (k = 0; k < spp_out; k ++) {
-                offset_contone[k]   = (- ((long)(penum->line) +
-                                          (long)contone_stride * k)) & 15;
+                offset_contone[k]   = (- ((int)(intptr_t)(penum->line) +
+                                          contone_stride * k)) & 15;
             }
             /* In the landscaped case, we want to accumulate multiple columns
                of data before sending to the device.  We want to have a full
diff -pruN 9.55.0~dfsg-3/base/gxifast.c 9.56.1~dfsg-1/base/gxifast.c
--- 9.55.0~dfsg-3/base/gxifast.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxifast.c	2022-04-04 13:46:22.000000000 +0000
@@ -39,9 +39,7 @@
 #include "valgrind.h"
 
 /* Conditionally include statistics code. */
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
-#  define STATS
-#endif
+/* #define COLLECT_STATS_IFAST */
 
 /* ------ Strategy procedure ------ */
 
@@ -195,7 +193,7 @@ image_render_skip(gx_image_enum * penum,
  * other bits in those bytes are set to zero (i.e., the value of the
  * 'zero' argument).
  */
-#ifdef STATS
+#ifdef COLLECT_STATS_IFAST
 struct stats_image_fast_s {
     long
          calls, all0s, all1s, runs, lbit0, byte00, byte01, byte02, byte03,
diff -pruN 9.55.0~dfsg-3/base/gximage1.c 9.56.1~dfsg-1/base/gximage1.c
--- 9.55.0~dfsg-3/base/gximage1.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gximage1.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -96,6 +96,7 @@ gx_begin_image1(gx_device * dev,
     if (code >= 0)
         *pinfo = (gx_image_enum_common_t *)penum;
     else {
+        /* penum is freed in by gx_image_enum_begin */
         *pinfo = NULL;
     }
     return code;
diff -pruN 9.55.0~dfsg-3/base/gximage3.c 9.56.1~dfsg-1/base/gximage3.c
--- 9.55.0~dfsg-3/base/gximage3.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gximage3.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -146,10 +146,11 @@ make_mcde_default(gx_device *dev, const
         return_error(gs_error_VMerror);
     bits.data = mdev->base;
     bits.raster = mdev->raster;
-    bits.size.x = mdev->width;
-    bits.size.y = mdev->height;
+    bits.size.x = bits.rep_width = mdev->width;
+    bits.size.y = bits.rep_height = mdev->height;
     bits.id = gx_no_bitmap_id;
     bits.num_planes = 1;
+    bits.rep_shift = bits.shift = 0;
     code = gx_mask_clip_initialize(mcdev, &gs_mask_clip_device,
                                    (const gx_bitmap *)&bits, dev,
                                    origin->x, origin->y, mem);
@@ -208,7 +209,8 @@ gx_begin_image3_generic(gx_device * dev,
     int code;
 
     /* Validate the parameters. */
-    if (pim->Height <= 0 || pim->MaskDict.Height <= 0)
+    if (pim->Width <= 0 || pim->MaskDict.Width <= 0 ||
+        pim->Height <= 0 || pim->MaskDict.Height <= 0)
         return_error(gs_error_rangecheck);
     switch (pim->InterleaveType) {
         default:
diff -pruN 9.55.0~dfsg-3/base/gximage4.c 9.56.1~dfsg-1/base/gximage4.c
--- 9.55.0~dfsg-3/base/gximage4.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gximage4.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -109,7 +109,7 @@ gx_begin_image4(gx_device * dev,
     if (code >= 0)
         *pinfo = (gx_image_enum_common_t *)penum;
     else {
-        gs_free_object(mem, penum, "gx_begin_image4");
+        /* penum is freed in by gx_image_enum_begin */
         *pinfo = NULL;
     }
     return code;
diff -pruN 9.55.0~dfsg-3/base/gximono.c 9.56.1~dfsg-1/base/gximono.c
--- 9.55.0~dfsg-3/base/gximono.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gximono.c	2022-04-04 13:46:22.000000000 +0000
@@ -1071,12 +1071,12 @@ image_render_mono_ht(gx_image_enum * pen
                 xrun += penum->x_extent.x;
             vdi = penum->hci;
             contone_stride = penum->line_size;
-            offset_threshold = (- (((long)(penum->thresh_buffer)) +
-                                      penum->ht_offset_bits)) & 15;
+            offset_threshold = (- (((int)(intptr_t)(penum->thresh_buffer)) +
+                                   penum->ht_offset_bits)) & 15;
             for (k = 0; k < spp_out; k ++) {
-                offset_contone[k]   = (- (((long)(penum->line)) +
-                                          (long)contone_stride * k +
-                                          penum->ht_offset_bits)) & 15;
+                offset_contone[k]   = (- (((int)(intptr_t)(penum->line)) +
+                                           contone_stride * k +
+                                           penum->ht_offset_bits)) & 15;
             }
             data_length = dest_width;
             dest_height = fixed2int_var_rounded(any_abs(penum->y_extent.y));
@@ -1098,10 +1098,10 @@ image_render_mono_ht(gx_image_enum * pen
             xrun = dda_current(dda_ht);            /* really yrun, but just used here for landscape */
             dest_height = gxht_dda_length(&dda_ht, src_size);
             data_length = dest_height;
-            offset_threshold = (-(long)(penum->thresh_buffer)) & 15;
+            offset_threshold = (-(int)(intptr_t)(penum->thresh_buffer)) & 15;
             for (k = 0; k < spp_out; k ++) {
-                offset_contone[k] = (- ((long)(penum->line) +
-                                        (long)contone_stride * k)) & 15;
+                offset_contone[k] = (- ((int)(intptr_t)(penum->line) +
+                                        contone_stride * k)) & 15;
             }
             /* In the landscaped case, we want to accumulate multiple columns
                of data before sending to the device.  We want to have a full
diff -pruN 9.55.0~dfsg-3/base/gxobj.h 9.56.1~dfsg-1/base/gxobj.h
--- 9.55.0~dfsg-3/base/gxobj.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxobj.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -27,37 +27,6 @@
 #  define IGC_PTR_STABILITY_CHECK 0
 #endif
 
-#ifndef GS_USE_MEMORY_HEADER_ID
-#define GS_USE_MEMORY_HEADER_ID 1
-#endif
-
-#if GS_USE_MEMORY_HEADER_ID
-
-  typedef gs_id hdr_id_t;
-
-  extern hdr_id_t hdr_id;
-
-# define HDR_ID_OFFSET (sizeof(obj_header_t) - offset_of(obj_header_t, d.o.hdr_id))
-
-# ifdef DEBUG
-
-# define ASSIGN_HDR_ID(obj) (*(hdr_id_t *)(((byte *)obj) - HDR_ID_OFFSET)) = hdr_id++
-
-  gs_id get_mem_hdr_id (void *ptr);
-
-# else /* DEBUG */
-
-#  define ASSIGN_HDR_ID(obj_hdr)
-
-# endif /* DEBUG */
-
-#else
-
-# define ASSIGN_HDR_ID(obj_hdr)
-# define HDR_ID_OFFSET 0
-
-#endif /* GS_USE_MEMORY_HEADER_ID */
-
 /* ================ Objects ================ */
 
 /*
@@ -130,10 +99,6 @@ typedef struct obj_header_data_s {
 #   if IGC_PTR_STABILITY_CHECK
     unsigned space_id:3; /* r_space_bits + 1 bit for "instability". */
 #   endif
-
-#   if GS_USE_MEMORY_HEADER_ID
-    hdr_id_t hdr_id; /* should be last, to save wasting space in the "strings" case. Makes object easier to trace thru GC */
-#   endif
 } obj_header_data_t;
 
 /*
diff -pruN 9.55.0~dfsg-3/base/gxp1fill.c 9.56.1~dfsg-1/base/gxp1fill.c
--- 9.55.0~dfsg-3/base/gxp1fill.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxp1fill.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -33,6 +33,7 @@
 #include "gxblend.h"
 #include "gsicc_cache.h"
 #include "gxdevsop.h"
+#include <limits.h>             /* For INT_MAX etc */
 
 #include "gdevp14.h"
 
@@ -212,6 +213,10 @@ tile_by_steps(tile_fill_state_t * ptfs,
             int xoff, yoff;
 
             if_debug4m('T', mem, "[T]i=%d j=%d x,y=(%d,%d)", i, j, x, y);
+            if (x == INT_MIN || y == INT_MIN) {
+                if_debug0m('T', mem, " underflow!\n");
+                continue;
+            }
             if (x < x0)
                 xoff = x0 - x, x = x0, w -= xoff;
             else
diff -pruN 9.55.0~dfsg-3/base/gxpath.c 9.56.1~dfsg-1/base/gxpath.c
--- 9.55.0~dfsg-3/base/gxpath.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxpath.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -440,9 +440,12 @@ gx_path_new(gx_path * ppath)
         int code = path_alloc_segments(&ppath->segments, ppath->memory,
                                        "gx_path_new");
 
-        rc_decrement(psegs, "gx_path_new");
-        if (code < 0)
+        if (code < 0) {
+            /* Leave the path in a valid state, despite the error */
+            ppath->segments = psegs;
             return code;
+        }
+        rc_decrement(psegs, "gx_path_new");
     } else {
         rc_free_path_segments_local(psegs->rc.memory, psegs, "gx_path_new");
     }
@@ -1026,7 +1029,7 @@ gx_print_segment(const gs_memory_t *mem,
     double py = fixed2float(pseg->pt.y);
     char out[80];
 
-    gs_sprintf(out, PRI_INTPTR "<"PRI_INTPTR","PRI_INTPTR">:%u",
+    gs_snprintf(out, sizeof(out), PRI_INTPTR "<"PRI_INTPTR","PRI_INTPTR">:%u",
                (intptr_t)pseg, (intptr_t)pseg->prev, (intptr_t)pseg->next, pseg->notes);
     switch (pseg->type) {
         case s_start:{
diff -pruN 9.55.0~dfsg-3/base/gxpcmap.c 9.56.1~dfsg-1/base/gxpcmap.c
--- 9.55.0~dfsg-3/base/gxpcmap.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxpcmap.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -133,6 +133,7 @@ pattern_accum_initialize_device_procs(gx
     set_dev_proc(dev, strip_tile_rect_devn, gx_default_strip_tile_rect_devn);
     set_dev_proc(dev, transform_pixel_region, gx_default_transform_pixel_region);
     set_dev_proc(dev, fill_stroke_path, gx_default_fill_stroke_path);
+    set_dev_proc(dev, copy_alpha_hl_color, gx_default_copy_alpha_hl_color);
 }
 
 static const gx_device_pattern_accum gs_pattern_accum_device =
@@ -648,8 +649,10 @@ blank_unmasked_bits(gx_device * mask,
 
     if (p->options & GB_PACKING_CHUNKY)
     {
-        if ((depth & 7) != 0 || depth > 64)
-            return_error(gs_error_rangecheck);
+        if ((depth & 7) != 0 || depth > 64) {
+            code = gs_note_error(gs_error_rangecheck);
+            goto fail;
+        }
         ptr = p->data[0];
         depth >>= 3;
         raster -= w*depth;
@@ -673,7 +676,7 @@ blank_unmasked_bits(gx_device * mask,
             for (x = 0; x < w; x++)
             {
                 int xx = x+x0;
-                if (((mine[xx>>3]>>(x&7)) & 1) == 0) {
+                if (((mine[xx>>3]<<(x&7)) & 128) == 0) {
                     switch (depth)
                     {
                     case 8:
@@ -704,8 +707,10 @@ blank_unmasked_bits(gx_device * mask,
         /* Planar, only handle 8 or 16 bits */
         int bytes_per_component = (depth/num_comps) >> 3;
 
-        if (depth/num_comps != 8 && depth/num_comps != 16)
-            return_error(gs_error_rangecheck);
+        if (depth/num_comps != 8 && depth/num_comps != 16) {
+            code = gs_note_error(gs_error_rangecheck);
+            goto fail;
+        }
         for (y = 0; y < h; y++)
         {
             int c;
@@ -1286,10 +1291,10 @@ dump_raw_pattern(int height, int width,
     is_planar = mdev->is_planar;
     max_bands = ( n_chan < 57 ? n_chan : 56);   /* Photoshop handles at most 56 bands */
     if (is_planar) {
-        gs_sprintf(full_file_name, "%d)PATTERN_PLANE_%dx%dx%d.raw", global_pat_index,
+        gs_snprintf(full_file_name, sizeof(full_file_name), "%d)PATTERN_PLANE_%dx%dx%d.raw", global_pat_index,
                 mdev->raster, height, max_bands);
     } else {
-        gs_sprintf(full_file_name, "%d)PATTERN_CHUNK_%dx%dx%d.raw", global_pat_index,
+        gs_snprintf(full_file_name, sizeof(full_file_name), "%d)PATTERN_CHUNK_%dx%dx%d.raw", global_pat_index,
                 width, height, max_bands);
     }
     fid = gp_fopen(memory,full_file_name,"wb");
@@ -1486,7 +1491,7 @@ gx_pattern_load(gx_device_color * pdc, c
         goto fail;
     if (pinst->templat.uses_transparency) {
         if_debug0m('v', mem, "gx_pattern_load: pushing the pdf14 compositor device into this graphics state\n");
-        if ((code = gs_push_pdf14trans_device(saved, true, false, 0, 0)) < 0)   /* FIXME: do we need spot_color_count ??? */
+        if ((code = gs_push_pdf14trans_device(saved, true, false, 0, 0)) < 0)   /* spot_color_count taken from pdf14 target values */
             return code;
         saved->device->is_open = true;
     } else {
@@ -1672,6 +1677,26 @@ pattern_accum_dev_spec_op(gx_device *dev
             return param_write_bool(plist, "NoInterpolateImagemasks", &bool_true);
         }
     }
+    /* Bug 704670.  Pattern accumulator should not allow whatever targets
+       lie beneath it to do any bbox adjustments. If we are here, the
+       pattern accumulator is actually drawing into a buffer
+       and it is not accumulating into a clist device. In this case, if it
+       was a pattern clist, we would be going to the special op for the clist
+       device of the pattern, which will have the proper extent and adjust
+       the bbox.  Here we just need to clip to the buffer into which we are drawing */
+    if (dso == gxdso_restrict_bbox) {
+        gs_int_rect* ibox = (gs_int_rect*)data;
+
+        if (ibox->p.y < 0)
+            ibox->p.y = 0;
+        if (ibox->q.y > padev->height)
+            ibox->q.y = padev->height;
+        if (ibox->p.x < 0)
+            ibox->p.x = 0;
+        if (ibox->q.x > padev->width)
+            ibox->q.x = padev->width;
+        return 0;
+    }
 
     return dev_proc(target, dev_spec_op)(target, dso, data, size);
 }
diff -pruN 9.55.0~dfsg-3/base/gxpflat.c 9.56.1~dfsg-1/base/gxpflat.c
--- 9.55.0~dfsg-3/base/gxpflat.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxpflat.c	2022-04-04 13:46:22.000000000 +0000
@@ -203,8 +203,7 @@ gx_flattened_iterator__init(gx_flattened
         return false;
     self->curve = true;
     self->k = k;
-#ifndef GS_THREADSAFE
-#   ifdef DEBUG
+#ifdef DEBUG
         if (gs_debug_c('3')) {
             dlprintf4("[3]x0=%f y0=%f x1=%f y1=%f\n",
                       fixed2float(self->x0), fixed2float(self->y0),
@@ -213,7 +212,6 @@ gx_flattened_iterator__init(gx_flattened
                       fixed2float(x2), fixed2float(y2),
                       fixed2float(self->x3), fixed2float(self->y3), self->k);
         }
-#   endif
 #endif
     if (k == -1) {
         /* A special hook for gx_subdivide_curve_rec.
@@ -333,7 +331,7 @@ gx_flattened_iterator__init_line(gx_flat
     return true;
 }
 
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef DEBUG
 static inline void
 gx_flattened_iterator__print_state(gx_flattened_iterator *self)
 {
@@ -418,7 +416,7 @@ gx_flattened_iterator__next(gx_flattened
         --self->i;
         if (self->i == 0)
             goto last; /* don't bother with last accum */
-#	if defined(DEBUG) && !defined(GS_THREADSAFE)
+#	ifdef DEBUG
             gx_flattened_iterator__print_state(self);
 #	endif
 #	define accum(i, r, di, dr, rmask)\
@@ -490,7 +488,7 @@ gx_flattened_iterator__prev(gx_flattened
     }
     gx_flattened_iterator__unaccum(self);
     self->i++;
-#   if defined(DEBUG) && !defined(GS_THREADSAFE)
+#   ifdef DEBUG
     if_debug5('3', "[3]%s x=%g, y=%g x=%ld y=%ld\n",
               (((self->x ^ self->lx1) | (self->y ^ self->ly1)) & float2fixed(-0.5) ?
                "add" : "skip"),
diff -pruN 9.55.0~dfsg-3/base/gxshade4.c 9.56.1~dfsg-1/base/gxshade4.c
--- 9.55.0~dfsg-3/base/gxshade4.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxshade4.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -123,7 +123,8 @@ gs_shading_FfGt_fill_rectangle(const gs_
     while ((flag = shade_next_flag(&cs, num_bits)) >= 0) {
         switch (flag) {
             default:
-                return_error(gs_error_rangecheck);
+                code = gs_note_error(gs_error_rangecheck);
+                goto error;
             case 0:
                 if ((code = Gt_next_vertex(pshm, &cs, &va, ca)) < 0 ||
                     (code = shade_next_flag(&cs, num_bits)) < 0 ||
@@ -150,6 +151,7 @@ v2:		if ((code = Gt_next_vertex(pshm, &c
         }
         cs.align(&cs, 8); /* Debugged with 12-14O.PS page 2. */
     }
+error:
     release_colors(&pfs, pfs.color_stack, 3);
     if (pfs.icclink != NULL) gsicc_release_link(pfs.icclink);
     if (term_patch_fill_state(&pfs))
diff -pruN 9.55.0~dfsg-3/base/gxstroke.c 9.56.1~dfsg-1/base/gxstroke.c
--- 9.55.0~dfsg-3/base/gxstroke.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxstroke.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -22,6 +22,7 @@
 #include "gserrors.h"
 #include "gsdcolor.h"
 #include "gsptype1.h"
+#include "gsptype2.h"
 #include "gxfixed.h"
 #include "gxfarith.h"
 #include "gxmatrix.h"
@@ -35,6 +36,7 @@
 #include "gzcpath.h"
 #include "gxpaint.h"
 #include "gsstate.h"            /* for gs_currentcpsimode */
+#include "gzacpath.h"
 
 /* RJW: There appears to be a difference in the xps and postscript models
  * (at least in as far as Microsofts implementation of xps and Acrobats of
@@ -329,15 +331,85 @@ static int cap_points(gs_line_cap, const
                        gs_fixed_point * /*[3] */ );
 static int join_under_pie(gx_path *, pl_ptr, pl_ptr, bool);
 
+int
+gx_default_stroke_path_shading_or_pattern(gx_device        * pdev,
+                                    const gs_gstate        * pgs_orig,
+                                          gx_path          * ppath,
+                                    const gx_stroke_params * params,
+                                    const gx_drawing_color * pdevc,
+                                    const gx_clip_path     * pcpath)
+{
+    gs_gstate *pgs = (gs_gstate *)pgs_orig; /* Nasty cast away const! */
+    gs_logical_operation_t save_lop = gs_current_logical_op_inline(pgs);
+    gx_device_cpath_accum adev;
+    gx_device_color devc;
+    gx_clip_path stroke_as_clip_path;
+    int code;
+
+    /* We want to make a image of the stroke as a clip path, so
+     * create an empty structure on the stack. */
+    code = gx_cpath_init_local_shared_nested(&stroke_as_clip_path, NULL, pdev->memory, 1);
+    if (code < 0)
+        return code;
+    /* Now we make an accumulator device that will fill that out. */
+    gx_cpath_accum_begin(&adev, stroke_as_clip_path.path.memory, false);
+    set_nonclient_dev_color(&devc, 0);	/* arbitrary, but not transparent */
+    gs_set_logical_op_inline(pgs, lop_default);
+    /* Stroke the path to the accumulator. */
+    code = gx_stroke_path_only(ppath, NULL, (gx_device *)&adev, pgs, params,
+                               &devc, pcpath);
+    /* Now extract the accumulated path into stroke_as_clip_path. */
+    if (code < 0 || (code = gx_cpath_accum_end(&adev, &stroke_as_clip_path)) < 0)
+        gx_cpath_accum_discard(&adev);
+    gs_set_logical_op_inline(pgs, save_lop);
+    if (code >= 0)
+    {
+        /* Now, fill a rectangle with the original color through that
+         * clip path. */
+        gs_fixed_rect clip_box, shading_box;
+        gs_int_rect cb;
+        gx_device_clip cdev;
+
+        gx_cpath_outer_box(&stroke_as_clip_path, &clip_box);
+        /* This is horrid. If the pdevc is a shading color, then the
+         * fill_rectangle routine requires us to have intersected it
+         * with the shading rectangle first. If we don't do this,
+         * ps3fts/470-01.ps goes wrong. */
+        if (gx_dc_is_pattern2_color(pdevc) &&
+            gx_dc_pattern2_get_bbox(pdevc, &shading_box) > 0)
+        {
+            rect_intersect(clip_box, shading_box);
+        }
+        cb.p.x = fixed2int_pixround(clip_box.p.x);
+        cb.p.y = fixed2int_pixround(clip_box.p.y);
+        cb.q.x = fixed2int_pixround(clip_box.q.x);
+        cb.q.y = fixed2int_pixround(clip_box.q.y);
+        gx_make_clip_device_on_stack(&cdev, &stroke_as_clip_path, pdev);
+        code = pdevc->type->fill_rectangle(pdevc,
+                        cb.p.x, cb.p.y, cb.q.x - cb.p.x, cb.q.y - cb.p.y,
+                        (gx_device *)&cdev, pgs->log_op, NULL);
+    }
+    gx_cpath_free(&stroke_as_clip_path, "gx_default_stroke_path_shading_or_pattern");
+
+    return code;
+}
+
 /* Define the default implementation of the device stroke_path procedure. */
 int
 gx_default_stroke_path(gx_device * dev, const gs_gstate * pgs,
                        gx_path * ppath, const gx_stroke_params * params,
-                       const gx_drawing_color * pdcolor,
+                       const gx_drawing_color * pdevc,
                        const gx_clip_path * pcpath)
 {
-    return gx_stroke_path_only(ppath, (gx_path *) 0, dev, pgs, params,
-                               pdcolor, pcpath);
+    if (gx_dc_is_pattern2_color(pdevc) ||
+        pdevc->type == &gx_dc_type_data_ht_colored ||
+        (gx_dc_is_pattern1_color(pdevc) &&
+         gx_pattern_tile_is_clist(pdevc->colors.pattern.p_tile)))
+        return gx_default_stroke_path_shading_or_pattern(dev, pgs, ppath, params,
+                                                         pdevc, pcpath);
+    else
+        return gx_stroke_path_only(ppath, (gx_path *) 0, dev, pgs, params,
+                                   pdevc, pcpath);
 }
 
 /* Fill a partial stroked path.  Free variables: */
@@ -422,9 +494,13 @@ gx_join_path_and_reverse(gx_path * path,
  * what is wanted.
  */
 static int
-gx_stroke_path_only_aux(gx_path * ppath, gx_path * to_path, gx_device * pdev,
-               const gs_gstate * pgs, const gx_stroke_params * params,
-                 const gx_device_color * pdevc, const gx_clip_path * pcpath)
+gx_stroke_path_only_aux(gx_path          *ppath, /* lgtm[cpp/use-of-goto] */
+                        gx_path          *to_path,
+                        gx_device        *pdev,
+                  const gs_gstate        *pgs,
+                  const gx_stroke_params *params,
+                  const gx_device_color  *pdevc,
+                  const gx_clip_path     *pcpath)
 {
     bool CPSI_mode = gs_currentcpsimode(pgs->memory);
     bool traditional = CPSI_mode | params->traditional;
@@ -1397,7 +1473,7 @@ line_intersect(
     double f1;
     double max_result = any_abs(denom) * (double)max_fixed;
 
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef DEBUG
     if (gs_debug_c('O')) {
         dlprintf4("[o]Intersect %f,%f(%f/%f)",
                   fixed2float(pp1->x), fixed2float(pp1->y),
@@ -1740,7 +1816,7 @@ check_miter(const gx_line_params * pgs_l
          * we actually have to do the test backwards.
          */
         ccw0 = v1 * u2 < v2 * u1;
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef DEBUG
         {
             double a1 = atan2(u1, v1), a2 = atan2(u2, v2), dif = a1 - a2;
 
@@ -1763,7 +1839,7 @@ check_miter(const gx_line_params * pgs_l
      */
     if (!ccw0)          /* have plp - nplp, want vice versa */
         num = -num;
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef DEBUG
     if (gs_debug_c('O')) {
         dlprintf4("[o]Miter check: u1/v1=%f/%f, u2/v2=%f/%f,\n",
                   u1, v1, u2, v2);
@@ -2511,7 +2587,7 @@ compute_caps(pl_ptr plp)
     plp->o.ce.x = plp->o.p.x - wx2, plp->o.ce.y = plp->o.p.y - wy2;
     plp->e.co.x = plp->e.p.x - wx2, plp->e.co.y = plp->e.p.y - wy2;
     plp->e.ce.x = plp->e.p.x + wx2, plp->e.ce.y = plp->e.p.y + wy2;
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef DEBUG
     if (gs_debug_c('O')) {
         dlprintf4("[o]Stroke o=(%f,%f) e=(%f,%f)\n",
                   fixed2float(plp->o.p.x), fixed2float(plp->o.p.y),
diff -pruN 9.55.0~dfsg-3/base/gxtype1.c 9.56.1~dfsg-1/base/gxtype1.c
--- 9.55.0~dfsg-3/base/gxtype1.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gxtype1.c	2022-04-04 13:46:22.000000000 +0000
@@ -346,7 +346,7 @@ type1_cis_get_metrics(const gs_type1_sta
  * This is exported only for the benefit of font copying.
  */
 int
-gs_type1_piece_codes(/*const*/ gs_font_type1 *pfont,
+gs_type1_piece_codes(/*const*/ gs_font_type1 *pfont, /* lgtm[cpp/use-of-goto] */
                      const gs_glyph_data_t *pgd, gs_char *chars)
 {
     gs_type1_data *const pdata = &pfont->data;
diff -pruN 9.55.0~dfsg-3/base/gzcpath.h 9.56.1~dfsg-1/base/gzcpath.h
--- 9.55.0~dfsg-3/base/gzcpath.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/gzcpath.h	2022-04-04 13:46:22.000000000 +0000
@@ -79,6 +79,9 @@ struct gx_clip_path_s {
     gs_id id;
     /* The last rectangle we accessed while using this clip_path */
     gx_clip_rect *cached;
+    /* The fill adjust to be used if the path is valid. If the
+     * path is not valid, always use 0,0. */
+    gs_fixed_point path_fill_adjust;
 };
 
 extern_st(st_clip_path);
diff -pruN 9.55.0~dfsg-3/base/lcups.mak 9.56.1~dfsg-1/base/lcups.mak
--- 9.55.0~dfsg-3/base/lcups.mak	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/lcups.mak	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2021 Artifex Software, Inc.
+# Copyright (C) 2001-2022 Artifex Software, Inc.
 # All Rights Reserved.
 #
 # This software is provided AS-IS with no warranty, either express or
@@ -16,21 +16,21 @@
 # makefile for libcups as part of the monolithic gs build.
 #
 # Users of this makefile must define the following:
-#	LCUPSSRCDIR    - the source directory
+#	LIBCUPSSRCDIR    - the source directory
 #	LCUPSGENDIR    - the generated intermediate file directory
 #	LCUPSOBJDIR    - the object file directory
 #	SHARE_LCUPS - 0 to compile in libcups, 1 to link a shared library
 #	LCUPS_LIBS  - if SHARE_CUPS=1, the link options for the shared library
 
 # (Rename directories.)
-LIBCUPSSRC=$(LCUPSSRCDIR)$(D)libs$(D)cups$(D)
+LIBCUPSSRC=$(LIBCUPSSRCDIR)$(D)libs$(D)cups$(D)
 LIBCUPSGEN=$(LCUPSGENDIR)$(D)
 LIBCUPSOBJ=$(LCUPSOBJDIR)$(D)
 LCUPSO_=$(O_)$(LIBCUPSOBJ)
 
 # NB: we can't use the normal $(CC_) here because msvccmd.mak
 # adds /Za which conflicts with the cups source.
-LCUPS_CC=$(CUPS_CC) $(CUPSCFLAGS) $(I_)$(LIBCUPSSRC) $(I_)$(LIBCUPSGEN)$(D)cups $(I_)$(LCUPSSRCDIR)$(D)libs
+LCUPS_CC=$(CUPS_CC) $(CUPSCFLAGS) $(I_)$(LIBCUPSSRC)$(_I) $(I_)$(LIBCUPSGEN)$(D)cups$(_I) $(I_)$(LIBCUPSSRCDIR)$(D)libs$(_I)
 
 # Define the name of this makefile.
 LCUPS_MAK=$(GLSRC)lcups.mak $(TOP_MAKEFILES)
@@ -106,7 +106,7 @@ LIBCUPS_DEPS	=	\
 libcups.clean : libcups.config-clean libcups.clean-not-config-clean
 
 libcups.clean-not-config-clean :
-	$(EXP)$(ECHOGS_XE) $(LCUPSSRCDIR) $(LCUPSOBJDIR)
+	$(EXP)$(ECHOGS_XE) $(LIBCUPSSRCDIR) $(LCUPSOBJDIR)
 	$(RM_) $(LIBCUPSOBJ)*.$(OBJ)
 
 libcups.config-clean :
@@ -135,8 +135,8 @@ $(LIBCUPSGEN)lcups_0.dev : $(ECHOGS_XE)
 # explicit rules for building the source files
 # for simplicity we have every source file depend on all headers
 
-$(LIBCUPSGEN)$(D)cups$(D)config.h : $(LCUPSSRCDIR)$(D)libs$(D)config$(LCUPSBUILDTYPE).h $(LIBCUPS_DEPS)
-	$(CP_) $(LCUPSSRCDIR)$(D)libs$(D)config$(LCUPSBUILDTYPE).h $(LIBCUPSGEN)$(D)cups$(D)config.h
+$(LIBCUPSGEN)$(D)cups$(D)config.h : $(LIBCUPSSRCDIR)$(D)libs$(D)config$(LCUPSBUILDTYPE).h $(LIBCUPS_DEPS)
+	$(CP_) $(LIBCUPSSRCDIR)$(D)libs$(D)config$(LCUPSBUILDTYPE).h $(LIBCUPSGEN)$(D)cups$(D)config.h
 
 $(LIBCUPSOBJ)adminutil.$(OBJ) : $(LIBCUPSSRC)adminutil.c $(LIBCUPSGEN)$(D)cups$(D)config.h $(LIBCUPS_DEPS)
 	$(LCUPS_CC) $(LCUPSO_)adminutil.$(OBJ) $(C_) $(LIBCUPSSRC)adminutil.c
diff -pruN 9.55.0~dfsg-3/base/lib.mak 9.56.1~dfsg-1/base/lib.mak
--- 9.55.0~dfsg-3/base/lib.mak	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/lib.mak	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2021 Artifex Software, Inc.
+# Copyright (C) 2001-2022 Artifex Software, Inc.
 # All Rights Reserved.
 #
 # This software is provided AS-IS with no warranty, either express or
@@ -101,6 +101,7 @@ srdline_h=$(GLSRC)srdline.h
 gpgetenv_h=$(GLSRC)gpgetenv.h
 gpmisc_h=$(GLSRC)gpmisc.h
 gp_h=$(GLSRC)gp.h
+globals_h=$(GLSRC)globals.h
 gpcheck_h=$(GLSRC)gpcheck.h
 gpsync_h=$(GLSRC)gpsync.h
 
@@ -239,7 +240,7 @@ $(GLOBJ)gsalloc.$(OBJ) : $(GLSRC)gsalloc
 	$(GLCC) $(GLO_)gsalloc.$(OBJ) $(C_) $(GLSRC)gsalloc.c
 
 $(GLOBJ)gsmalloc.$(OBJ) : $(GLSRC)gsmalloc.c $(malloc__h)\
- $(gdebug_h)\
+ $(gdebug_h) $(gp_h) \
  $(gserrors_h)\
  $(gsmalloc_h) $(gsmdebug_h) $(gsmemret_h)\
  $(gsmemory_h) $(gsstruct_h) $(gstypes_h) $(LIB_MAK) $(MAKEDIRS)
@@ -349,7 +350,7 @@ $(GLOBJ)gsargs.$(OBJ) : $(GLSRC)gsargs.c
 $(GLOBJ)gsmisc.$(OBJ) : $(GLSRC)gsmisc.c $(AK) $(gx_h) $(gserrors_h)\
  $(vmsmath_h) $(std_h) $(ctype__h) $(malloc__h) $(math__h) $(memory__h)\
  $(string__h) $(gpcheck_h) $(gxfarith_h) $(gxfixed_h) $(stdint__h) $(stdio__h)\
- $(gdbflags_h) $(LIB_MAK) $(MAKEDIRS)
+ $(gdbflags_h) $(gp_h) $(LIB_MAK) $(MAKEDIRS)
 	$(GLCC) $(GLO_)gsmisc.$(OBJ) $(C_) $(GLSRC)gsmisc.c
 
 $(AUX)gsmisc.$(OBJ) : $(GLSRC)gsmisc.c $(AK) $(gx_h) $(gpmisc_h) $(gserrors_h)\
@@ -358,9 +359,9 @@ $(AUX)gsmisc.$(OBJ) : $(GLSRC)gsmisc.c $
  $(gdbflags_h) $(LIB_MAK) $(MAKEDIRS)
 	$(GLCCAUX) $(C_) $(AUXO_)gsmisc.$(OBJ) $(GLSRC)gsmisc.c
 
-$(GLOBJ)gslibctx_1.$(OBJ) : $(GLSRC)gslibctx.c  $(AK) $(gp_h) $(gpmisc_h) $(gsmemory_h)\
-  $(gslibctx_h) $(stdio__h) $(string__h) $(gsicc_manage_h) $(gserrors_h)\
-  $(gscdefs_h) $(gsstruct_h)
+$(GLOBJ)gslibctx_1.$(OBJ) : $(GLSRC)gslibctx.c  $(AK) $(gp_h) $(gpmisc_h) \
+  $(gsmemory_h) $(gslibctx_h) $(stdio__h) $(string__h) $(gsicc_manage_h) \
+  $(gserrors_h) $(gscdefs_h) $(gsstruct_h) $(globals_h)
 	$(GLCC) $(D_)WITH_CAL$(_D) $(I_)$(CALSRCDIR)$(_I) $(GLO_)gslibctx_1.$(OBJ) $(C_) $(GLSRC)gslibctx.c
 
 $(GLOBJ)gslibctx_0.$(OBJ) : $(GLSRC)gslibctx.c  $(AK) $(gp_h) $(gpmisc_h) $(gsmemory_h)\
@@ -912,7 +913,7 @@ $(GLOBJ)gxscanc.$(OBJ) : $(GLSRC)gxscanc
 
 $(GLOBJ)gxstroke.$(OBJ) : $(GLSRC)gxstroke.c $(AK) $(gx_h)\
  $(gserrors_h) $(math__h) $(gpcheck_h) $(gsstate_h)\
- $(gscoord_h) $(gsdcolor_h) $(gsdevice_h) $(gsptype1_h)\
+ $(gscoord_h) $(gsdcolor_h) $(gsdevice_h) $(gsptype1_h) $(gsptype2_h)\
  $(gxdevice_h) $(gxfarith_h) $(gxfixed_h)\
  $(gxhttile_h) $(gxgstate_h) $(gxmatrix_h) $(gxpaint_h)\
  $(gzcpath_h) $(gzline_h) $(gzpath_h) $(LIB_MAK) $(MAKEDIRS)
@@ -1012,7 +1013,7 @@ $(GLOBJ)gsgcache.$(OBJ) : $(GLSRC)gsgcac
 	$(GLCC) $(GLO_)gsgcache.$(OBJ) $(C_) $(GLSRC)gsgcache.c
 
 $(GLOBJ)gsht.$(OBJ) : $(GLSRC)gsht.c $(AK) $(gx_h) $(gserrors_h)\
- $(memory__h) $(string__h) $(gsstruct_h) $(gsutil_h) $(gxarith_h) $(gxdevsop_h)\
+ $(memory__h) $(string__h) $(gsstruct_h) $(gsutil_h) $(gxarith_h)\
  $(gxdevice_h) $(gzht_h) $(gzstate_h) $(gxfmap_h) $(gp_h) $(LIB_MAK) $(MAKEDIRS)
 	$(GLCC) $(GLO_)gsht.$(OBJ) $(C_) $(GLSRC)gsht.c
 
@@ -1403,12 +1404,12 @@ downscale_=$(GLOBJ)gxdownscale.$(OBJ) $(
 
 $(GLOBJ)gxdownscale_0.$(OBJ) : $(GLSRC)gxdownscale.c $(AK) $(string__h)\
  $(gxdownscale_h) $(gserrors_h) $(gdevprn_h) $(assert__h) $(ets_h)\
- $(LIB_MAK) $(MAKEDIRS)
+ $(gsicc_cache_h) $(LIB_MAK) $(MAKEDIRS)
 	$(GLCC) $(GLO_)gxdownscale_0.$(OBJ) $(C_) $(GLSRC)gxdownscale.c
 
 $(GLOBJ)gxdownscale_1.$(OBJ) : $(GLSRC)gxdownscale.c $(AK) $(string__h)\
  $(gxdownscale_h) $(gserrors_h) $(gdevprn_h) $(assert__h) $(ets_h)\
- $(LIB_MAK) $(MAKEDIRS)
+ $(gsicc_cache_h) $(LIB_MAK) $(MAKEDIRS)
 	$(GLCC) $(D_)WITH_CAL$(_D) $(I_)$(CALSRCDIR)$(_I) $(GLO_)gxdownscale_1.$(OBJ) $(C_) $(GLSRC)gxdownscale.c
 
 $(GLOBJ)gxdownscale.$(OBJ) : $(GLOBJ)gxdownscale_$(WITH_CAL).$(OBJ) $(AK) $(gp_h)
@@ -3726,7 +3727,7 @@ $(GLD)nosync.dev : $(LIB_MAK) $(ECHOGS_X
 	$(SETMOD) $(GLD)nosync $(nosync_)
 
 $(GLOBJ)gp_nsync.$(OBJ) : $(GLSRC)gp_nsync.c $(AK) $(std_h)\
- $(gpsync_h) $(gserrors_h) $(LIB_MAK) $(MAKEDIRS)
+ $(gpsync_h) $(gp_h) $(globals_h) $(gserrors_h) $(LIB_MAK) $(MAKEDIRS)
 	$(GLCC) $(GLO_)gp_nsync.$(OBJ) $(C_) $(GLSRC)gp_nsync.c
 
 # POSIX pthreads-based implementation.
@@ -3736,8 +3737,8 @@ $(GLD)posync.dev : $(LIB_MAK) $(ECHOGS_X
 	$(ADDMOD) $(GLD)posync -replace $(GLD)nosync
 
 $(GLOBJ)gp_psync.$(OBJ) : $(GLSRC)gp_psync.c $(AK) $(malloc__h) $(string__h) \
- $(std_h) $(gpsync_h) $(gserrors_h) $(assert__h) $(unistd__h) $(LIB_MAK)\
- $(MAKEDIRS)
+ $(std_h) $(gpsync_h) $(gserrors_h) $(assert__h) $(unistd__h) $(globals_h) \
+ $(gp_h) $(LIB_MAK) $(MAKEDIRS)
 	$(GLCC) $(GLO_)gp_psync.$(OBJ) $(C_) $(GLSRC)gp_psync.c
 
 # Other stuff.
@@ -3975,6 +3976,7 @@ $(GLSRC)gp.h:$(GLSRC)stat_.h
 $(GLSRC)gp.h:$(GLSRC)gsstype.h
 $(GLSRC)gp.h:$(GLSRC)gsmemory.h
 $(GLSRC)gp.h:$(GLSRC)gpgetenv.h
+$(GLSRC)gp.h:$(GLSRC)globals.h
 $(GLSRC)gp.h:$(GLSRC)gscdefs.h
 $(GLSRC)gp.h:$(GLSRC)gslibctx.h
 $(GLSRC)gp.h:$(GLSRC)stdio_.h
@@ -4075,6 +4077,8 @@ $(GLSRC)gsgc.h:$(GLSRC)stdint_.h
 $(GLSRC)gsgc.h:$(GLSRC)std.h
 $(GLSRC)gsgc.h:$(GLSRC)stdpre.h
 $(GLSRC)gsgc.h:$(GLGEN)arch.h
+$(GLSRC)globals.h:$(GLSRC)std.h
+$(GLSRC)globals.h:$(GLSRC)gslibctx.h
 $(GLSRC)gsmalloc.h:$(GLSRC)gxsync.h
 $(GLSRC)gsmalloc.h:$(GLSRC)gpsync.h
 $(GLSRC)gsmalloc.h:$(GLSRC)gsmemory.h
diff -pruN 9.55.0~dfsg-3/base/memento.c 9.56.1~dfsg-1/base/memento.c
--- 9.55.0~dfsg-3/base/memento.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/memento.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009-2021 Artifex Software, Inc.
+/* Copyright (C) 2009-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -13,12 +13,27 @@
 
 /* Inspired by Fortify by Simon P Bullen. */
 
+/* Set the following if we want to do a build specifically for memory
+ * squeezing. We sacrifice some features for speed. */
+/* #define MEMENTO_SQUEEZEBUILD */
+
 /* Set the following if you're only looking for leaks, not memory overwrites
  * to speed the operation */
 /* #define MEMENTO_LEAKONLY */
 
+/* Unset the following if you don't want the speed/memory hit of tracking references. */
+#ifndef MEMENTO_SQUEEZEBUILD
+#define MEMENTO_TRACKREFS
+#endif
+
 /* Set the following to keep extra details about the history of blocks */
+#ifndef MEMENTO_SQUEEZEBUILD
 #define MEMENTO_DETAILS
+#endif
+
+/* Set what volume of memory blocks we keep around after it's been freed
+ * to check for overwrites. */
+#define MEMENTO_FREELIST_MAX 0x2000000
 
 /* Don't keep blocks around if they'd mean losing more than a quarter of
  * the freelist. */
@@ -80,10 +95,12 @@ int atexit(void (*)(void));
 #define FMTZ "%llu"
 #define FMTZ_CAST _int64
 #define FMTP "0x%p"
+typedef unsigned _int64 mem_uint64_t;
 #else
 #define FMTZ "%zu"
 #define FMTZ_CAST size_t
 #define FMTP "%p"
+typedef long long mem_uint64_t;
 #endif
 
 #define UB(x) ((intptr_t)((x) & 0xFF))
@@ -213,16 +230,22 @@ windows_fprintf(FILE *file, const char *
 #define MEMENTO_HAS_FORK
 #endif
 
+#if defined(_DLL) && defined(_MSC_VER)
+#define MEMENTO_CRT_SPEC __declspec(dllimport)
+#else
+#define MEMENTO_CRT_SPEC
+#endif
+
 /* Define the underlying allocators, just in case */
-void *MEMENTO_UNDERLYING_MALLOC(size_t);
-void MEMENTO_UNDERLYING_FREE(void *);
-void *MEMENTO_UNDERLYING_REALLOC(void *,size_t);
-void *MEMENTO_UNDERLYING_CALLOC(size_t,size_t);
+MEMENTO_CRT_SPEC void *MEMENTO_UNDERLYING_MALLOC(size_t);
+MEMENTO_CRT_SPEC void MEMENTO_UNDERLYING_FREE(void *);
+MEMENTO_CRT_SPEC void *MEMENTO_UNDERLYING_REALLOC(void *,size_t);
+MEMENTO_CRT_SPEC void *MEMENTO_UNDERLYING_CALLOC(size_t,size_t);
 
 /* And some other standard functions we use. We don't include the header
  * files, just in case they pull in unexpected others. */
-int atoi(const char *);
-char *getenv(const char *);
+MEMENTO_CRT_SPEC int atoi(const char *);
+MEMENTO_CRT_SPEC char *getenv(const char *);
 
 /* How far to search for pointers in each block when calculating nestings */
 /* mupdf needs at least 34000ish (sizeof(fz_shade))/ */
@@ -274,7 +297,9 @@ enum {
     Memento_Flag_BreakOnRealloc = 8,
     Memento_Flag_Freed = 16,
     Memento_Flag_KnownLeak = 32,
-    Memento_Flag_Reported = 64
+    Memento_Flag_Reported = 64,
+    Memento_Flag_LastPhase = 0x80000000,
+    Memento_Flag_PhaseMask = 0xFFFF0000
 };
 
 enum {
@@ -335,15 +360,24 @@ static const char *eventType[] =
 #define MEMENTO_SIBLING_MAGIC ((Memento_BlkHeader *)('n' | ('t' << 8) | ('0' << 16) | ('!' << 24)))
 
 #ifdef MEMENTO_DETAILS
+typedef struct Memento_hashedST Memento_hashedST;
+
+struct Memento_hashedST
+{
+    Memento_hashedST *next;
+    MEMENTO_UINT32    hash;
+    int               count;
+    void             *trace[1];
+};
+
 typedef struct Memento_BlkDetails Memento_BlkDetails;
 
 struct Memento_BlkDetails
 {
     Memento_BlkDetails *next;
     char                type;
-    char                count;
     int                 sequence;
-    void               *stack[1];
+    Memento_hashedST   *trace;
 };
 #endif /* MEMENTO_DETAILS */
 
@@ -355,10 +389,17 @@ struct Memento_BlkHeader
     int                  sequence;
     int                  lastCheckedOK;
     int                  flags;
+
+    const char          *label;
+
+    /* Blocks are held in a linked list for LRU */
     Memento_BlkHeader   *next;
     Memento_BlkHeader   *prev; /* Reused as 'parent' when printing nested list */
 
-    const char          *label;
+    /* Blocks are held in a splay tree for position. */
+    Memento_BlkHeader   *parent;
+    Memento_BlkHeader   *left;
+    Memento_BlkHeader   *right;
 
     /* Entries for nesting display calculations. Set to magic
      * values at all other time.  */
@@ -380,6 +421,7 @@ typedef struct Memento_Blocks
 {
     Memento_BlkHeader *head;
     Memento_BlkHeader *tail;
+    Memento_BlkHeader *top;
 } Memento_Blocks;
 
 /* What sort of Mutex should we use? */
@@ -453,13 +495,14 @@ static struct {
     int            nextPattern;
     int            patternBit;
     int            leaking;
+    int            showDetailedBlocks;
     int            hideMultipleReallocs;
     int            abortOnLeak;
     int            abortOnCorruption;
     size_t         maxMemory;
     size_t         alloc;
     size_t         peakAlloc;
-    size_t         totalAlloc;
+    mem_uint64_t   totalAlloc;
     size_t         numMallocs;
     size_t         numFrees;
     size_t         numReallocs;
@@ -467,6 +510,14 @@ static struct {
     Memento_range *squeezes;
     int            squeezes_num;
     int            squeezes_pos;
+    int            phasing;
+    int            verbose;
+    int            verboseNewlineSuppressed;
+    void          *lastVerbosePtr;
+#ifdef MEMENTO_DETAILS
+    Memento_hashedST *stacktraces[256];
+    int            hashCollisions;
+#endif
 } memento;
 
 #define MEMENTO_EXTRASIZE (sizeof(Memento_BlkHeader) + Memento_PostSize)
@@ -844,9 +895,12 @@ static int Memento_getStacktrace(void **
 static void Memento_showStacktrace(void **stack, int numberOfFrames)
 {
     MY_IMAGEHLP_LINE line;
-    int i;
+    int i, j;
     char symbol_buffer[sizeof(MY_SYMBOL_INFO) + 1024 + 1];
     MY_SYMBOL_INFO *symbol = (MY_SYMBOL_INFO *)symbol_buffer;
+    bool ok;
+    int prefix = 1; /* Ignore a prefix of 'unknowns' */
+    int suppressed = 0; /* How many unknowns we have suppressed. */
 
     symbol->MaxNameLen = 1024;
     symbol->SizeOfStruct = sizeof(MY_SYMBOL_INFO);
@@ -855,9 +909,18 @@ static void Memento_showStacktrace(void
     {
         DWORD64 dwDisplacement64;
         DWORD dwDisplacement;
-        Memento_SymFromAddr(Memento_process, (DWORD64)(stack[i]), &dwDisplacement64, symbol);
-        Memento_SymGetLineFromAddr(Memento_process, (DWORD_NATIVESIZED)(stack[i]), &dwDisplacement, &line);
-        fprintf(stderr, "    %s in %s:%d\n", symbol->Name, line.FileName, line.LineNumber);
+        ok = Memento_SymFromAddr(Memento_process, (DWORD64)(stack[i]), &dwDisplacement64, symbol);
+        if (ok == 1)
+            ok = Memento_SymGetLineFromAddr(Memento_process, (DWORD_NATIVESIZED)(stack[i]), &dwDisplacement, &line);
+        if (ok == 1) {
+            for (j = 0; j < suppressed; j++)
+                fprintf(stderr, "    unknown\n");
+            suppressed = 0;
+            fprintf(stderr, "    %s in %s:%d\n", symbol->Name, line.FileName, line.LineNumber);
+            prefix = 0;
+        } else if (prefix == 0) {
+            suppressed++;
+        }
     }
 }
 #elif defined(MEMENTO_STACKTRACE_METHOD) && MEMENTO_STACKTRACE_METHOD == 3
@@ -955,15 +1018,33 @@ static void Memento_showStacktrace(void
 #endif /* MEMENTO_STACKTRACE_METHOD */
 
 #ifdef MEMENTO_DETAILS
-static void Memento_storeDetails(Memento_BlkHeader *head, int type)
+static MEMENTO_UINT32
+hashStackTrace(void **stack, int count)
 {
-    void *stack[MEMENTO_BACKTRACE_MAX];
-    Memento_BlkDetails *details;
-    int count;
-    int skip;
+    int i;
+    MEMENTO_UINT32 hash = 0;
 
-    if (head == NULL)
-        return;
+    count *= sizeof(void *)/sizeof(unsigned int);
+    for (i = 0; i < count; i++)
+        hash = (hash>>5) ^ (hash<<27) ^ ((unsigned int *)stack)[i];
+
+    return hash;
+}
+
+static Memento_hashedST oom_hashed_st =
+{
+    NULL, /* next */
+    0,    /* hash */
+    0,    /* count */
+    {NULL}/* trace[0] */
+};
+
+static Memento_hashedST *Memento_getHashedStacktrace(void)
+{
+    void *stack[MEMENTO_BACKTRACE_MAX];
+    MEMENTO_UINT32 hash;
+    int count, skip;
+    Memento_hashedST **h;
 
 #ifdef MEMENTO_STACKTRACE_METHOD
     count = Memento_getStacktrace(stack, &skip);
@@ -972,17 +1053,60 @@ static void Memento_storeDetails(Memento
     count = 0;
 #endif
 
-    details = MEMENTO_UNDERLYING_MALLOC(sizeof(*details) + (count-1) * sizeof(void *));
-    if (details == NULL)
+    count -= skip;
+    hash = hashStackTrace(&stack[skip], count);
+    while (1) {
+        h = &memento.stacktraces[hash & 0xff];
+        while (*h) {
+            if ((*h)->hash == hash)
+                break;
+            h = &(*h)->next;
+        }
+        if ((*h) == NULL)
+            break; /* No match, fall through to make a new one. */
+        if (count == (*h)->count &&
+            memcmp((*h)->trace, &stack[skip], sizeof(void *) * count) == 0)
+            return (*h); /* We match! Reuse this one. */
+        /* Hash collision. */
+        hash++;
+        memento.hashCollisions++;
+    }
+
+    (*h) = MEMENTO_UNDERLYING_MALLOC(sizeof(Memento_hashedST) + sizeof(void *) * (count-1));
+    if (*h == NULL)
+        return &oom_hashed_st;
+
+    (*h)->next = NULL;
+    (*h)->hash = hash;
+    (*h)->count = count;
+    memcpy(&(*h)->trace[0], &stack[skip], count * sizeof(void *));
+
+    return *h;
+}
+
+static void Memento_showHashedStacktrace(Memento_hashedST *trace)
+{
+    if (trace == NULL)
         return;
 
-    if (count)
-        memcpy(&details->stack, &stack[skip], count * sizeof(void *));
+    Memento_showStacktrace(&trace->trace[0], trace->count);
+}
+
+static void Memento_storeDetails(Memento_BlkHeader *head, int type, Memento_hashedST *st)
+{
+    Memento_BlkDetails *details;
+
+    if (head == NULL)
+        return;
+
+    details = MEMENTO_UNDERLYING_MALLOC(sizeof(*details));
+    if (details == NULL)
+        return;
 
     details->type = (char)type;
-    details->count = (char)count;
     details->sequence = memento.sequence;
     details->next = NULL;
+    details->trace = st;
     VALGRIND_MAKE_MEM_DEFINED(&head->details_tail, sizeof(head->details_tail));
     *head->details_tail = details;
     head->details_tail = &details->next;
@@ -990,15 +1114,19 @@ static void Memento_storeDetails(Memento
 }
 #endif
 
-void (Memento_bt)(void)
+void Memento_showHash(MEMENTO_UINT32 hash)
 {
-#ifdef MEMENTO_STACKTRACE_METHOD
-    void *stack[MEMENTO_BACKTRACE_MAX];
-    int count;
-    int skip;
+#ifdef MEMENTO_DETAILS
+    Memento_hashedST *h;
 
-    count = Memento_getStacktrace(stack, &skip);
-    Memento_showStacktrace(&stack[skip-2], count-skip+2);
+    h = memento.stacktraces[hash & 0xff];
+    while (h) {
+        if (h->hash == hash)
+            break;
+        h = h->next;
+    }
+
+    Memento_showHashedStacktrace(h);
 #endif
 }
 
@@ -1014,6 +1142,11 @@ static void Memento_bt_internal(int skip
 #endif
 }
 
+void (Memento_bt)(void)
+{
+    Memento_bt_internal(0);
+}
+
 static int Memento_checkAllMemoryLocked(void);
 
 void Memento_breakpoint(void)
@@ -1041,12 +1174,271 @@ do { MEMENTO_DO_UNLOCK(); } while (0)
 #define Memento_breakpointLocked() \
 do { MEMENTO_UNLOCK(); Memento_breakpoint(); MEMENTO_LOCK(); } while (0)
 
+/* Move the given node to the root of the tree, by
+ * performing a series of the following rotations.
+ * The key observation here is that all these
+ * rotations preserve the ordering of the tree, and
+ * result in 'x' getting higher.
+ *
+ * Case 1:   z          x           Case 1b:   z                   x
+ *          # #        # #                    # #                 # #
+ *         y   D      A   y                  A   y               y   D
+ *        # #     =>     # #                    # #     =>      # #
+ *       x   C          B   z                  B   x           z   C
+ *      # #                # #                    # #         # #
+ *     A   B              C   D                  C   D       A   B
+ *
+ * Case 2:   z             x        Case 2b:   z                  x
+ *          # #          ## ##                # #               ## ##
+ *         y   D        y     z              A   y             z     y
+ *        # #     =>   # #   # #                # #     =>    # #   # #
+ *       A   x        A   B C   D              x   D         A   B C   D
+ *          # #                               # #
+ *         B   C                             B   C
+ *
+ * Case 3:   y          x           Case 3b:  y                  x
+ *          # #        # #                   # #                # #
+ *         x   C  =>  A   y                 A   x       =>     y   C
+ *        # #            # #                   # #            # #
+ *       A   B          B   C                 B   C          A   B
+ */
+static void
+move_to_root(Memento_BlkHeader *x)
+{
+    Memento_BlkHeader *y, *z;
+
+    if (x == NULL)
+        return;
+
+    VALGRIND_MAKE_MEM_DEFINED(x, sizeof(*x));
+    while ((y = x->parent) != NULL) {
+        VALGRIND_MAKE_MEM_DEFINED(y, sizeof(*y));
+        if ((z = y->parent) != NULL) {
+            VALGRIND_MAKE_MEM_DEFINED(z, sizeof(*z));
+            x->parent = z->parent;
+            if (x->parent) {
+                VALGRIND_MAKE_MEM_DEFINED(x->parent, sizeof(*x->parent));
+                if (x->parent->left == z)
+                    x->parent->left = x;
+                else
+                    x->parent->right  = x;
+                VALGRIND_MAKE_MEM_NOACCESS(x->parent, sizeof(*x->parent));
+            }
+            y->parent = x;
+            /* Case 1, 1b, 2 or 2b */
+            if (y->left == x) {
+                /* Case 1 or 2b */
+                if (z->left == y) {
+                    /* Case 1 */
+                    y->left = x->right;
+                    if (y->left) {
+                        VALGRIND_MAKE_MEM_DEFINED(y->left, sizeof(*y->left));
+                        y->left->parent = y;
+                        VALGRIND_MAKE_MEM_NOACCESS(y->left, sizeof(*y->left));
+                    }
+                    z->left = y->right;
+                    if (z->left) {
+                        VALGRIND_MAKE_MEM_DEFINED(z->left, sizeof(*z->left));
+                        z->left->parent = z;
+                        VALGRIND_MAKE_MEM_NOACCESS(z->left, sizeof(*z->left));
+                    }
+                    y->right = z;
+                    z->parent = y;
+                } else {
+                    /* Case 2b */
+                    z->right = x->left;
+                    if (z->right) {
+                        VALGRIND_MAKE_MEM_DEFINED(z->right, sizeof(*z->right));
+                        z->right->parent = z;
+                        VALGRIND_MAKE_MEM_NOACCESS(z->right, sizeof(*z->right));
+                    }
+                    y->left = x->right;
+                    if (y->left) {
+                        VALGRIND_MAKE_MEM_DEFINED(y->left, sizeof(*y->left));
+                        y->left->parent = y;
+                        VALGRIND_MAKE_MEM_NOACCESS(y->left, sizeof(*y->left));
+                    }
+                    x->left = z;
+                    z->parent = x;
+                }
+                x->right      = y;
+            } else {
+                /* Case 2 or 1b */
+                if (z->left == y) {
+                    /* Case 2 */
+                    y->right = x->left;
+                    if (y->right) {
+                        VALGRIND_MAKE_MEM_DEFINED(y->right, sizeof(*y->right));
+                        y->right->parent = y;
+                        VALGRIND_MAKE_MEM_NOACCESS(y->right, sizeof(*y->right));
+                    }
+                    z->left = x->right;
+                    if (z->left) {
+                        VALGRIND_MAKE_MEM_DEFINED(z->left, sizeof(*z->left));
+                        z->left->parent = z;
+                        VALGRIND_MAKE_MEM_NOACCESS(z->left, sizeof(*z->left));
+                    }
+                    x->right = z;
+                    z->parent = x;
+                } else {
+                    /* Case 1b */
+                    z->right = y->left;
+                    if (z->right) {
+                        VALGRIND_MAKE_MEM_DEFINED(z->right, sizeof(*z->right));
+                        z->right->parent = z;
+                        VALGRIND_MAKE_MEM_NOACCESS(z->right, sizeof(*z->right));
+                    }
+                    y->right = x->left;
+                    if (y->right) {
+                        VALGRIND_MAKE_MEM_DEFINED(y->right, sizeof(*y->right));
+                        y->right->parent = y;
+                        VALGRIND_MAKE_MEM_NOACCESS(y->right, sizeof(*y->right));
+                    }
+                    y->left = z;
+                    z->parent = y;
+                }
+                x->left = y;
+            }
+            VALGRIND_MAKE_MEM_NOACCESS(z, sizeof(*z));
+        } else {
+            /* Case 3 or 3b */
+            x->parent = NULL;
+            y->parent = x;
+            if (y->left == x) {
+                /* Case 3 */
+                y->left = x->right;
+                if (y->left) {
+                    VALGRIND_MAKE_MEM_DEFINED(y->left, sizeof(*y->left));
+                    y->left->parent = y;
+                    VALGRIND_MAKE_MEM_NOACCESS(y->left, sizeof(*y->left));
+                }
+                x->right = y;
+            } else {
+                /* Case 3b */
+                y->right = x->left;
+                if (y->right) {
+                    VALGRIND_MAKE_MEM_DEFINED(y->right, sizeof(*y->right));
+                    y->right->parent = y;
+                    VALGRIND_MAKE_MEM_NOACCESS(y->right, sizeof(*y->right));
+                }
+                x->left = y;
+            }
+        }
+        VALGRIND_MAKE_MEM_NOACCESS(y, sizeof(*y));
+    }
+    VALGRIND_MAKE_MEM_NOACCESS(x, sizeof(*x));
+}
+
+static void Memento_removeBlockSplay(Memento_Blocks    *blks,
+                                     Memento_BlkHeader *b)
+{
+    Memento_BlkHeader *replacement;
+
+    VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
+    if (b->left == NULL)
+    {
+        /* At most one child - easy */
+        replacement = b->right;
+    }
+    else if (b->right == NULL)
+    {
+        /* Strictly one child - easy */
+        replacement = b->left;
+    }
+    else
+    {
+        /* 2 Children - tricky */
+        /* Find in-order predecessor to b */
+        replacement = b->left;
+        VALGRIND_MAKE_MEM_DEFINED(replacement, sizeof(*replacement));
+        while (replacement->right)
+        {
+            Memento_BlkHeader *o = replacement;
+            replacement = o->right;
+            VALGRIND_MAKE_MEM_DEFINED(replacement, sizeof(*replacement));
+            VALGRIND_MAKE_MEM_NOACCESS(o, sizeof(*o));
+        }
+        /* Remove replacement - easy as just one child */
+        (void)Memento_removeBlockSplay(NULL, replacement);
+        /* Replace b with replacement */
+        VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
+        if (b->left) {
+            VALGRIND_MAKE_MEM_DEFINED(b->left, sizeof(*b->left));
+            b->left->parent = replacement;
+            VALGRIND_MAKE_MEM_NOACCESS(b->left, sizeof(*b->left));
+        }
+        VALGRIND_MAKE_MEM_DEFINED(b->right, sizeof(*b->right));
+        b->right->parent = replacement;
+        VALGRIND_MAKE_MEM_NOACCESS(b->right, sizeof(*b->right));
+        VALGRIND_MAKE_MEM_DEFINED(replacement, sizeof(*replacement));
+        replacement->left = b->left;
+        replacement->right = b->right;
+    }
+    if (b->parent)
+    {
+        VALGRIND_MAKE_MEM_DEFINED(b->parent, sizeof(*b->parent));
+        if (b->parent->left == b)
+            b->parent->left = replacement;
+        else
+            b->parent->right = replacement;
+            VALGRIND_MAKE_MEM_NOACCESS(b->parent, sizeof(*b->parent));
+    } else {
+        VALGRIND_MAKE_MEM_DEFINED(&blks->top, sizeof(blks->top));
+        blks->top = replacement;
+        VALGRIND_MAKE_MEM_NOACCESS(&blks->top, sizeof(blks->top));
+    }
+    if (replacement)
+    {
+        VALGRIND_MAKE_MEM_DEFINED(replacement, sizeof(*replacement));
+        replacement->parent = b->parent;
+        VALGRIND_MAKE_MEM_NOACCESS(replacement, sizeof(*replacement));
+    }
+    VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(*b));
+}
+
+static void Memento_addBlockSplay(Memento_Blocks    *blks,
+                                  Memento_BlkHeader *b)
+{
+    Memento_BlkHeader *parent = NULL;
+    Memento_BlkHeader **n = &blks->top;
+
+    /* Walk down, looking for a place to put b. */
+    VALGRIND_MAKE_MEM_DEFINED(&blks->top, sizeof(blks->top));
+    while (*n != NULL) {
+        Memento_BlkHeader *o = parent;
+        VALGRIND_MAKE_MEM_DEFINED(*n, sizeof(**n));
+        parent = *n;
+        if (o) VALGRIND_MAKE_MEM_NOACCESS(o, sizeof(*o));
+        VALGRIND_MAKE_MEM_DEFINED(parent, sizeof(*parent));
+        if (b < parent)
+            n = &parent->left;
+        else
+            n = &parent->right;
+    }
+    /* Place b */
+    *n = b;
+    if (parent) VALGRIND_MAKE_MEM_NOACCESS(parent, sizeof(*parent));
+    b->parent = parent;
+    b->left = NULL;
+    b->right = NULL;
+    /* Now perform the splay magic */
+    move_to_root(b);
+    /* This always leaves b at the top. */
+    blks->top = b;
+    VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
+    b->parent = NULL;
+    VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(*b));
+    VALGRIND_MAKE_MEM_NOACCESS(&blks->top, sizeof(blks->top));
+}
+
 static void Memento_addBlockHead(Memento_Blocks    *blks,
                                  Memento_BlkHeader *b,
                                  int                type)
 {
+    Memento_addBlockSplay(blks, b);
     if (blks->tail == NULL)
         blks->tail = b;
+    VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
     b->next    = blks->head;
     b->prev    = NULL;
     if (blks->head)
@@ -1064,7 +1456,7 @@ static void Memento_addBlockHead(Memento
     if (type == 0) { /* malloc */
         VALGRIND_MAKE_MEM_UNDEFINED(MEMBLK_TOBLK(b), b->rawsize);
     } else if (type == 1) { /* free */
-        VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_TOBLK(b), b->rawsize);
+      VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_TOBLK(b), b->rawsize);
     }
     VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader));
 }
@@ -1073,9 +1465,11 @@ static void Memento_addBlockTail(Memento
                                  Memento_BlkHeader *b,
                                  int                type)
 {
+    Memento_addBlockSplay(blks, b);
     VALGRIND_MAKE_MEM_DEFINED(&blks->tail, sizeof(Memento_BlkHeader *));
     if (blks->head == NULL)
         blks->head = b;
+    VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
     b->prev = blks->tail;
     b->next = NULL;
     if (blks->tail) {
@@ -1092,7 +1486,7 @@ static void Memento_addBlockTail(Memento
     if (type == 0) { /* malloc */
         VALGRIND_MAKE_MEM_UNDEFINED(MEMBLK_TOBLK(b), b->rawsize);
     } else if (type == 1) { /* free */
-        VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_TOBLK(b), b->rawsize);
+      VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_TOBLK(b), b->rawsize);
     }
     VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader));
     VALGRIND_MAKE_MEM_NOACCESS(&blks->tail, sizeof(Memento_BlkHeader *));
@@ -1220,6 +1614,7 @@ mismatch:
 static void Memento_removeBlock(Memento_Blocks    *blks,
                                 Memento_BlkHeader *b)
 {
+    Memento_removeBlockSplay(blks, b);
     VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
     if (b->next) {
         VALGRIND_MAKE_MEM_DEFINED(&b->next->prev, sizeof(b->next->prev));
@@ -1235,6 +1630,7 @@ static void Memento_removeBlock(Memento_
         blks->tail = b->prev;
     if (blks->head == b)
         blks->head = b->next;
+    VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(*b));
 }
 
 static void free_block(Memento_BlkHeader *head)
@@ -1265,6 +1661,8 @@ static int Memento_Internal_makeSpace(si
         VALGRIND_MAKE_MEM_DEFINED(head, sizeof(*head));
         memento.free.head = head->next;
         memento.freeListSize -= MEMBLK_SIZE(head->rawsize);
+        Memento_removeBlockSplay(&memento.free, head);
+        VALGRIND_MAKE_MEM_DEFINED(head, sizeof(*head));
         free_block(head);
     }
     /* Make sure we haven't just completely emptied the free list */
@@ -1297,6 +1695,47 @@ static int Memento_appBlocks(Memento_Blo
     return 0;
 }
 
+static Memento_BlkHeader *
+find_enclosing_block(Memento_Blocks *blks,
+                     void *addr,
+                     int *flags)
+{
+    Memento_BlkHeader *blk;
+
+    VALGRIND_MAKE_MEM_DEFINED(&blks->top, sizeof(blks->top));
+    blk = blks->top;
+    while (blk)
+    {
+        Memento_BlkHeader *oblk = blk;
+        char *blkstart;
+        char *blkend;
+        VALGRIND_MAKE_MEM_DEFINED(blk, sizeof(Memento_BlkHeader));
+        if (addr < (void *)oblk) {
+            blk = blk->left;
+            VALGRIND_MAKE_MEM_UNDEFINED(oblk, sizeof(Memento_BlkHeader));
+            continue;
+        }
+        blkstart = (char *)MEMBLK_TOBLK(blk);
+        blkend = &blkstart[blk->rawsize];
+        if (addr >= (void *)(blkend + Memento_PostSize)) {
+            blk = blk->right;
+            VALGRIND_MAKE_MEM_UNDEFINED(oblk, sizeof(Memento_BlkHeader));
+            continue;
+        }
+        if (flags) {
+            if (((void *)blkstart <= addr) && (addr < (void *)blkend))
+                *flags = 1;
+            else if (((void *)blk <= addr) && (addr < (void *)blkstart))
+                *flags = 2;
+            else
+                *flags = 3;
+        }
+        VALGRIND_MAKE_MEM_UNDEFINED(oblk, sizeof(Memento_BlkHeader));
+        return blk;
+    }
+    return NULL;
+}
+
 #ifndef MEMENTO_LEAKONLY
 /* Distrustful - check the block is a real one */
 static int Memento_appBlockUser(Memento_Blocks    *blks,
@@ -1305,25 +1744,18 @@ static int Memento_appBlockUser(Memento_
                                 void              *arg,
                                 Memento_BlkHeader *b)
 {
-    Memento_BlkHeader *head = blks->head;
-    Memento_BlkHeader *next;
-    int                result;
-    while (head && head != b) {
-        VALGRIND_MAKE_MEM_DEFINED(head, sizeof(Memento_BlkHeader));
-        next = head->next;
-       VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(head), Memento_PostSize);
-        head = next;
-    }
-    if (head == b) {
-        VALGRIND_MAKE_MEM_DEFINED(head, sizeof(Memento_BlkHeader));
-        VALGRIND_MAKE_MEM_DEFINED(MEMBLK_TOBLK(head),
-                                  head->rawsize + Memento_PostSize);
-        result = app(head, arg);
-        VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(head), Memento_PostSize);
-        VALGRIND_MAKE_MEM_NOACCESS(head, sizeof(Memento_BlkHeader));
-        return result;
-    }
-    return 0;
+    int result;
+    Memento_BlkHeader *head = find_enclosing_block(blks, b, NULL);
+    if (head == NULL)
+        return 0;
+
+    VALGRIND_MAKE_MEM_DEFINED(head, sizeof(Memento_BlkHeader));
+    VALGRIND_MAKE_MEM_DEFINED(MEMBLK_TOBLK(head),
+                              head->rawsize + Memento_PostSize);
+    result = app(head, arg);
+    VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(head), Memento_PostSize);
+    VALGRIND_MAKE_MEM_NOACCESS(head, sizeof(Memento_BlkHeader));
+    return result;
 }
 
 static int Memento_appBlock(Memento_Blocks    *blks,
@@ -1574,6 +2006,91 @@ void Memento_listNewBlocks(void)
     MEMENTO_UNLOCK();
 }
 
+typedef struct
+{
+    size_t counts[2];
+    unsigned int phase;
+} phased_t;
+
+static int Memento_listPhasedBlock(Memento_BlkHeader *b,
+                                   void              *arg)
+{
+    phased_t *phase = (phased_t *)arg;
+    if ((b->flags & phase->phase) == 0)
+        return 0;
+    b->flags = (b->flags & ~Memento_Flag_PhaseMask) | ((b->flags >> 1) & Memento_Flag_PhaseMask);
+    return Memento_listBlock(b, arg);
+}
+
+static int Memento_listNewPhasedBlock(Memento_BlkHeader *b,
+                                      void              *arg)
+{
+    if ((b->flags & Memento_Flag_PhaseMask) != 0)
+        return 0;
+    b->flags |= Memento_Flag_LastPhase;
+    return Memento_listBlock(b, arg);
+}
+
+static int Memento_startPhasing(Memento_BlkHeader *b,
+                                void              *arg)
+{
+    b->flags |= *(int *)arg;
+    return 0;
+}
+
+/* On the first call to this, we mark all blocks with the
+ * lowest 'phase' bit, (call this phase m) so they will
+ * never be reported.
+ *
+ * On subsequent calls, we:
+ *   for (n = m-1; n > 0; n--)
+ *     report all blocks in phase n, moving them to n+1.
+ *   report all new blocks, and place them in phase 0.
+ *
+ * The upshot of this is that if you call Memento_listPhasedBlocks()
+ * at a given point in the code, then you can watch for how long blocks
+ * live between each time the code reaches that point.
+ *
+ * This is basically like Memento_listNewBlocks(), but allows for
+ * the fact that sometimes blocks are freed just after the call.
+ */
+void Memento_listPhasedBlocks(void)
+{
+    phased_t phase;
+    int num = 0;
+    MEMENTO_LOCK();
+    phase.phase = Memento_Flag_LastPhase;
+    while ((phase.phase>>1) & Memento_Flag_PhaseMask)
+        num++, phase.phase >>= 1;
+    if (memento.phasing == 0)
+    {
+        fprintf(stderr, "Commencing Phasing:\n");
+        memento.phasing = 1;
+        Memento_appBlocks(&memento.used, Memento_startPhasing, &phase.phase);
+    } else {
+        phase.phase <<= 1;
+        do
+        {
+            phase.counts[0] = 0;
+            phase.counts[1] = 0;
+            fprintf(stderr, "Blocks allocated and still extant: In phase %d (%x):\n", num, phase.phase);
+            Memento_appBlocks(&memento.used, Memento_listPhasedBlock, &phase);
+            fprintf(stderr, "  Total number of blocks = "FMTZ"\n", (FMTZ_CAST)phase.counts[0]);
+            fprintf(stderr, "  Total size of blocks = "FMTZ"\n", (FMTZ_CAST)phase.counts[1]);
+            phase.phase = phase.phase<<1;
+            num--;
+        }
+        while (phase.phase != 0);
+        phase.counts[0] = 0;
+        phase.counts[1] = 0;
+        fprintf(stderr, "Blocks allocated and still extant: In phase 0:\n", num, phase.phase);
+        Memento_appBlocks(&memento.used, Memento_listNewPhasedBlock, &phase);
+        fprintf(stderr, "  Total number of blocks = "FMTZ"\n", (FMTZ_CAST)phase.counts[0]);
+        fprintf(stderr, "  Total size of blocks = "FMTZ"\n", (FMTZ_CAST)phase.counts[1]);
+    }
+    MEMENTO_UNLOCK();
+}
+
 static void Memento_endStats(void)
 {
     fprintf(stderr, "Total memory malloced = "FMTZ" bytes\n", (FMTZ_CAST)memento.totalAlloc);
@@ -1582,6 +2099,10 @@ static void Memento_endStats(void)
             (FMTZ_CAST)memento.numFrees, (FMTZ_CAST)memento.numReallocs);
     fprintf(stderr, "Average allocation size "FMTZ" bytes\n", (FMTZ_CAST)
             (memento.numMallocs != 0 ? memento.totalAlloc/memento.numMallocs: 0));
+#ifdef MEMENTO_DETAILS
+    if (memento.hashCollisions)
+        fprintf(stderr, "%d hash collisions\n", memento.hashCollisions);
+#endif
 }
 
 void Memento_stats(void)
@@ -1614,7 +2135,14 @@ static int showInfo(Memento_BlkHeader *b
             continue;
         }
         fprintf(stderr, "  Event %d (%s)\n", details->sequence, eventType[(int)details->type]);
-        Memento_showStacktrace(details->stack, details->count);
+        Memento_showHashedStacktrace(details->trace);
+        if (memento.showDetailedBlocks > 0) {
+            memento.showDetailedBlocks -= 1;
+            if (memento.showDetailedBlocks == 0) {
+                fprintf(stderr, "Stopping display of block details because memento.showDetailedBlocks is now zero.\n");
+                return 1;
+            }
+        }
     }
     return 0;
 }
@@ -1630,22 +2158,16 @@ void Memento_listBlockInfo(void)
 #endif
 }
 
-#ifdef MEMENTO_DETAILS
-static int
-showBlockInfo(Memento_BlkHeader *b, void *arg)
-{
-    if (arg < MEMBLK_TOBLK(b) || (void *)MEMBLK_POSTPTR(b) <= arg)
-        return 0;
-    return showInfo(b, NULL);
-}
-#endif
-
 void Memento_blockInfo(void *p)
 {
 #ifdef MEMENTO_DETAILS
+    Memento_BlkHeader *blk;
     MEMENTO_LOCK();
-    Memento_appBlocks(&memento.used, showBlockInfo, p);
-    Memento_appBlocks(&memento.free, showBlockInfo, p);
+    blk = find_enclosing_block(&memento.used, p, NULL);
+    if (blk == NULL)
+        blk = find_enclosing_block(&memento.free, p, NULL);
+    if (blk)
+        showInfo(blk, NULL);
     MEMENTO_UNLOCK();
 #endif
 }
@@ -1670,6 +2192,7 @@ static int Memento_nonLeakBlocksLeaked(v
 
 void Memento_fin(void)
 {
+    int leaked = 0;
     Memento_checkAllMemory();
     if (!memento.segv)
     {
@@ -1677,17 +2200,20 @@ void Memento_fin(void)
         if (Memento_nonLeakBlocksLeaked()) {
             Memento_listBlocks();
 #ifdef MEMENTO_DETAILS
-            fprintf(stderr, "\n");
-            Memento_listBlockInfo();
+            if (memento.showDetailedBlocks) {
+                fprintf(stderr, "\n");
+                Memento_listBlockInfo();
+            }
 #endif
             Memento_breakpoint();
+            leaked = 1;
         }
     }
     if (memento.squeezing) {
         if (memento.pattern == 0)
-            fprintf(stderr, "Memory squeezing @ %d complete%s\n", memento.squeezeAt, memento.segv ? " (with SEGV)" : "");
+            fprintf(stderr, "Memory squeezing @ %d complete%s\n", memento.squeezeAt, memento.segv ? " (with SEGV)" : (leaked ? " (with leaks)" : ""));
         else
-            fprintf(stderr, "Memory squeezing @ %d (%d) complete%s\n", memento.squeezeAt, memento.pattern, memento.segv ? " (with SEGV)" : "");
+            fprintf(stderr, "Memory squeezing @ %d (%d) complete%s\n", memento.squeezeAt, memento.pattern, memento.segv ? " (with SEGV)" : (leaked ? " (with leaks)" : ""));
     } else if (memento.segv) {
         fprintf(stderr, "Memento completed (with SEGV)\n");
     }
@@ -1879,6 +2405,14 @@ static int Memento_add_squeezes(const ch
     return e;
 }
 
+#if defined(_WIN32) || defined(_WIN64)
+static int Memento_fin_win(void)
+{
+    Memento_fin();
+    return 0;
+}
+#endif
+
 static void Memento_init(void)
 {
     char *env;
@@ -1914,6 +2448,9 @@ static void Memento_init(void)
     env = getenv("MEMENTO_PATTERN");
     memento.pattern = (env ? atoi(env) : 0);
 
+    env = getenv("MEMENTO_SHOW_DETAILED_BLOCKS");
+    memento.showDetailedBlocks = (env ? atoi(env) : -1);
+
     env = getenv("MEMENTO_HIDE_MULTIPLE_REALLOCS");
     memento.hideMultipleReallocs = (env ? atoi(env) : 0);
 
@@ -1937,7 +2474,19 @@ static void Memento_init(void)
     env = getenv("MEMENTO_MAXMEMORY");
     memento.maxMemory = (env ? atoi(env) : 0);
 
+    env = getenv("MEMENTO_VERBOSE");
+    memento.verbose = (env ? atoi(env) : 0);
+
+    /* For Windows, we can _onexit rather than atexit. This is because
+     * _onexit registered handlers are called when the DLL that they are
+     * in is freed, rather than on complete closedown. This gives us a
+     * higher chance of seeing Memento_fin called in a state when the
+     * stack backtracing mechanism can still work. */
+#if defined(_WIN32) || defined(_WIN64)
+    _onexit(Memento_fin_win);
+#else
     atexit(Memento_fin);
+#endif
 
     Memento_initMutex(&memento.mutex);
 
@@ -1946,54 +2495,16 @@ static void Memento_init(void)
     Memento_breakpoint();
 }
 
-typedef struct findBlkData {
-    void              *addr;
-    Memento_BlkHeader *blk;
-    int                flags;
-} findBlkData;
-
-static int Memento_containsAddr(Memento_BlkHeader *b,
-                                void *arg)
-{
-    findBlkData *data = (findBlkData *)arg;
-    char *blkend = &((char *)MEMBLK_TOBLK(b))[b->rawsize];
-    if ((MEMBLK_TOBLK(b) <= data->addr) &&
-        ((void *)blkend > data->addr)) {
-        data->blk = b;
-        data->flags = 1;
-        return 1;
-    }
-    if (((void *)b <= data->addr) &&
-        (MEMBLK_TOBLK(b) > data->addr)) {
-        data->blk = b;
-        data->flags = 2;
-        return 1;
-    }
-    if (((void *)blkend <= data->addr) &&
-        ((void *)(blkend + Memento_PostSize) > data->addr)) {
-        data->blk = b;
-        data->flags = 3;
-        return 1;
-    }
-    return 0;
-}
-
 static void Memento_infoLocked(void *addr)
 {
 #ifdef MEMENTO_DETAILS
-    findBlkData data;
+    Memento_BlkHeader *blk;
 
-    data.addr  = addr;
-    data.blk   = NULL;
-    data.flags = 0;
-    Memento_appBlocks(&memento.used, Memento_containsAddr, &data);
-    if (data.blk != NULL)
-        showInfo(data.blk, NULL);
-    data.blk   = NULL;
-    data.flags = 0;
-    Memento_appBlocks(&memento.free, Memento_containsAddr, &data);
-    if (data.blk != NULL)
-        showInfo(data.blk, NULL);
+    blk = find_enclosing_block(&memento.used, addr, NULL);
+    if (blk == NULL)
+        blk = find_enclosing_block(&memento.free, addr, NULL);
+    if (blk != NULL)
+        showInfo(blk, NULL);
 #else
     printf("Memento not compiled with details support\n");
 #endif
@@ -2167,7 +2678,6 @@ static void Memento_startFailing(void)
         memento.failing = 1;
         memento.failAt = memento.sequence;
         memento.nextFailAt = memento.sequence+1;
-        memento.pattern = 0;
         memento.patternBit = 0;
         signal(SIGSEGV, Memento_signal);
         signal(SIGABRT, Memento_signal);
@@ -2234,19 +2744,13 @@ static void *safe_find_block(void *ptr)
     VALGRIND_MAKE_MEM_DEFINED(&block->sibling, sizeof(block->sibling));
     valid = (block->child == MEMENTO_CHILD_MAGIC &&
              block->sibling == MEMENTO_SIBLING_MAGIC);
-    VALGRIND_MAKE_MEM_NOACCESS(&block->child, sizeof(block->child));
-    VALGRIND_MAKE_MEM_NOACCESS(&block->sibling, sizeof(block->sibling));
+    if (valid) {
+        VALGRIND_MAKE_MEM_NOACCESS(&block->child, sizeof(block->child));
+        VALGRIND_MAKE_MEM_NOACCESS(&block->sibling, sizeof(block->sibling));
+    }
     if (!valid)
     {
-        findBlkData data;
-
-        data.addr  = ptr;
-        data.blk   = NULL;
-        data.flags = 0;
-        Memento_appBlocks(&memento.used, Memento_containsAddr, &data);
-        if (data.blk == NULL)
-            return NULL;
-        block = data.blk;
+        block = find_enclosing_block(&memento.used, ptr, NULL);
     }
     return block;
 }
@@ -2266,6 +2770,13 @@ void *Memento_label(void *ptr, const cha
         VALGRIND_MAKE_MEM_NOACCESS(&block->label, sizeof(block->label));
     }
     MEMENTO_UNLOCK();
+
+    if (memento.verboseNewlineSuppressed) {
+        if (memento.lastVerbosePtr == block) {
+            fprintf(stderr, " (%s)", label);
+        }
+    }
+
     return ptr;
 }
 
@@ -2347,12 +2858,13 @@ int Memento_failThisEvent(void)
     return ret;
 }
 
-static void *do_malloc(size_t s, int eventType)
+static void *do_malloc(size_t s, int et)
 {
     Memento_BlkHeader *memblk;
     size_t             smem = MEMBLK_SIZE(s);
-
-    (void)eventType;
+#ifdef MEMENTO_DETAILS
+    Memento_hashedST  *st;
+#endif
 
     if (Memento_failThisEventLocked()) {
         errno = ENOMEM;
@@ -2369,9 +2881,42 @@ static void *do_malloc(size_t s, int eve
         return NULL;
     }
 
+#ifdef MEMENTO_DETAILS
+    st = Memento_getHashedStacktrace();
+#endif
+
     memblk = MEMENTO_UNDERLYING_MALLOC(smem);
-    if (memblk == NULL)
+    if (memblk == NULL) {
+        switch (memento.verbose) {
+        default:
+            if (memento.verboseNewlineSuppressed)
+                fprintf(stderr, "\n");
+            fprintf(stderr, "%s failed (size="FMTZ",num=%d",
+                    eventType[et], (FMTZ_CAST)s, memento.sequence);
+#ifdef MEMENTO_DETAILS
+            fprintf(stderr, ",st=%x", st->hash);
+#endif
+            fprintf(stderr, ")");
+            memento.verboseNewlineSuppressed = 1;
+            memento.lastVerbosePtr = memblk;
+            break;
+        case 2:
+            if (memento.verboseNewlineSuppressed)
+                fprintf(stderr, "\n");
+            fprintf(stderr, "%s failed (size="FMTZ"",
+                    eventType[et], (FMTZ_CAST)s);
+#ifdef MEMENTO_DETAILS
+            fprintf(stderr, ",st=%x", st->hash);
+#endif
+            fprintf(stderr, ")");
+            memento.verboseNewlineSuppressed = 1;
+            memento.lastVerbosePtr = memblk;
+            break;
+        case 0:
+            break;
+        }
         return NULL;
+    }
 
     memento.alloc      += s;
     memento.totalAlloc += s;
@@ -2390,13 +2935,48 @@ static void *do_malloc(size_t s, int eve
 #ifdef MEMENTO_DETAILS
     memblk->details       = NULL;
     memblk->details_tail  = &memblk->details;
-    Memento_storeDetails(memblk, eventType);
+    Memento_storeDetails(memblk, et, st);
 #endif /* MEMENTO_DETAILS */
     Memento_addBlockHead(&memento.used, memblk, 0);
 
     if (memento.leaking > 0)
         memblk->flags |= Memento_Flag_KnownLeak;
 
+    switch (memento.verbose) {
+    default:
+        if (memento.verboseNewlineSuppressed)
+            fprintf(stderr, "\n");
+        fprintf(stderr, "%s "FMTP":(size="FMTZ",num=%d",
+                eventType[et],
+                MEMBLK_TOBLK(memblk), (FMTZ_CAST)memblk->rawsize, memblk->sequence);
+#ifdef MEMENTO_DETAILS
+        fprintf(stderr, ",st=%x", st->hash);
+#endif
+        if (memblk->label)
+            fprintf(stderr, ") (%s", memblk->label);
+        fprintf(stderr, ")");
+        memento.verboseNewlineSuppressed = 1;
+        memento.lastVerbosePtr = memblk;
+        break;
+    case 2:
+        if (memento.verboseNewlineSuppressed)
+            fprintf(stderr, "\n");
+        fprintf(stderr, "%s (size="FMTZ"",
+                eventType[et],
+                (FMTZ_CAST)memblk->rawsize);
+#ifdef MEMENTO_DETAILS
+        fprintf(stderr, ",st=%x", st->hash);
+#endif
+        if (memblk->label)
+            fprintf(stderr, ") (%s", memblk->label);
+        fprintf(stderr, ")");
+        memento.verboseNewlineSuppressed = 1;
+        memento.lastVerbosePtr = memblk;
+        break;
+    case 0:
+        break;
+    }
+
     return MEMBLK_TOBLK(memblk);
 }
 
@@ -2507,12 +3087,15 @@ void *Memento_calloc(size_t n, size_t s)
     return block;
 }
 
+#ifdef MEMENTO_TRACKREFS
 static void do_reference(Memento_BlkHeader *blk, int event)
 {
 #ifdef MEMENTO_DETAILS
-    Memento_storeDetails(blk, event);
+    Memento_hashedST *st = Memento_getHashedStacktrace();
+    Memento_storeDetails(blk, event, st);
 #endif /* MEMENTO_DETAILS */
 }
+#endif
 
 static int checkPointerOrNullLocked(void *blk)
 {
@@ -2661,6 +3244,7 @@ int Memento_checkIntPointerOrNull(void *
     return ret;
 }
 
+#ifdef MEMENTO_TRACKREFS
 static void *do_takeRef(void *blk)
 {
     do_reference(safe_find_block(blk), Memento_EventType_takeRef);
@@ -2674,8 +3258,23 @@ static void *do_takeRefAndUnlock(void *b
     return blk;
 }
 
+static void *do_dropRef(void *blk)
+{
+    do_reference(safe_find_block(blk), Memento_EventType_dropRef);
+    return blk;
+}
+
+static void *do_dropRefAndUnlock(void *blk)
+{
+    do_reference(safe_find_block(blk), Memento_EventType_dropRef);
+    MEMENTO_UNLOCK();
+    return blk;
+}
+#endif
+
 void *Memento_takeByteRef(void *blk)
 {
+#ifdef MEMENTO_TRACKREFS
     if (!memento.inited)
         Memento_init();
 
@@ -2690,10 +3289,14 @@ void *Memento_takeByteRef(void *blk)
     (void)checkBytePointerOrNullLocked(blk);
 
     return do_takeRefAndUnlock(blk);
+#else
+    return blk;
+#endif
 }
 
 void *Memento_takeShortRef(void *blk)
 {
+#ifdef MEMENTO_TRACKREFS
     if (!memento.inited)
         Memento_init();
 
@@ -2708,10 +3311,14 @@ void *Memento_takeShortRef(void *blk)
     (void)checkShortPointerOrNullLocked(blk);
 
     return do_takeRefAndUnlock(blk);
+#else
+    return blk;
+#endif
 }
 
 void *Memento_takeIntRef(void *blk)
 {
+#ifdef MEMENTO_TRACKREFS
     if (!memento.inited)
         Memento_init();
 
@@ -2726,10 +3333,14 @@ void *Memento_takeIntRef(void *blk)
     (void)checkIntPointerOrNullLocked(blk);
 
     return do_takeRefAndUnlock(blk);
+#else
+    return blk;
+#endif
 }
 
 void *Memento_takeRef(void *blk)
 {
+#ifdef MEMENTO_TRACKREFS
     if (!memento.inited)
         Memento_init();
 
@@ -2742,23 +3353,14 @@ void *Memento_takeRef(void *blk)
     }
 
     return do_takeRefAndUnlock(blk);
-}
-
-static void *do_dropRef(void *blk)
-{
-    do_reference(safe_find_block(blk), Memento_EventType_dropRef);
-    return blk;
-}
-
-static void *do_dropRefAndUnlock(void *blk)
-{
-    do_reference(safe_find_block(blk), Memento_EventType_dropRef);
-    MEMENTO_UNLOCK();
+#else
     return blk;
+#endif
 }
 
 void *Memento_dropByteRef(void *blk)
 {
+#ifdef MEMENTO_TRACKREFS
     if (!memento.inited)
         Memento_init();
 
@@ -2773,10 +3375,14 @@ void *Memento_dropByteRef(void *blk)
     checkBytePointerOrNullLocked(blk);
 
     return do_dropRefAndUnlock(blk);
+#else
+    return blk;
+#endif
 }
 
 void *Memento_dropShortRef(void *blk)
 {
+#ifdef MEMENTO_TRACKREFS
     if (!memento.inited)
         Memento_init();
 
@@ -2791,10 +3397,14 @@ void *Memento_dropShortRef(void *blk)
     checkShortPointerOrNullLocked(blk);
 
     return do_dropRefAndUnlock(blk);
+#else
+    return blk;
+#endif
 }
 
 void *Memento_dropIntRef(void *blk)
 {
+#ifdef MEMENTO_TRACKREFS
     if (!memento.inited)
         Memento_init();
 
@@ -2809,10 +3419,14 @@ void *Memento_dropIntRef(void *blk)
     checkIntPointerOrNullLocked(blk);
 
     return do_dropRefAndUnlock(blk);
+#else
+    return blk;
+#endif
 }
 
 void *Memento_dropRef(void *blk)
 {
+#ifdef MEMENTO_TRACKREFS
     if (!memento.inited)
         Memento_init();
 
@@ -2825,10 +3439,14 @@ void *Memento_dropRef(void *blk)
     }
 
     return do_dropRefAndUnlock(blk);
+#else
+    return blk;
+#endif
 }
 
 void *Memento_adjustRef(void *blk, int adjust)
 {
+#ifdef MEMENTO_TRACKREFS
     if (!memento.inited)
         Memento_init();
 
@@ -2852,11 +3470,13 @@ void *Memento_adjustRef(void *blk, int a
     }
 
     MEMENTO_UNLOCK();
+#endif
     return blk;
- }
+}
 
 void *Memento_reference(void *blk)
 {
+#ifdef MEMENTO_TRACKREFS
     if (!blk)
         return NULL;
 
@@ -2866,6 +3486,7 @@ void *Memento_reference(void *blk)
     MEMENTO_LOCK();
     do_reference(safe_find_block(blk), Memento_EventType_reference);
     MEMENTO_UNLOCK();
+#endif
     return blk;
 }
 
@@ -2958,12 +3579,13 @@ static int checkBlock(Memento_BlkHeader
     return 0;
 }
 
-static void do_free(void *blk, int eventType)
+static void do_free(void *blk, int et)
 {
     Memento_BlkHeader *memblk;
     int ret;
-
-    (void)eventType;
+#ifdef MEMENTO_DETAILS
+    Memento_hashedST *st;
+#endif
 
     if (Memento_event()) Memento_breakpointLocked();
 
@@ -2985,7 +3607,46 @@ static void do_free(void *blk, int event
     }
 
 #ifdef MEMENTO_DETAILS
-    Memento_storeDetails(memblk, eventType);
+    st = Memento_getHashedStacktrace();
+#endif
+
+    switch (memento.verbose) {
+    default:
+        if (memento.verboseNewlineSuppressed) {
+            fprintf(stderr, "\n");
+            memento.verboseNewlineSuppressed = 0;
+        }
+        fprintf(stderr, "%s "FMTP":(size="FMTZ",num=%d",
+                eventType[et],
+                MEMBLK_TOBLK(memblk), (FMTZ_CAST)memblk->rawsize, memblk->sequence);
+#ifdef MEMENTO_DETAILS
+        fprintf(stderr, ",hash=%x", st->hash);
+#endif
+        if (memblk->label)
+            fprintf(stderr, ") (%s", memblk->label);
+        fprintf(stderr, ")\n");
+        break;
+    case 2:
+        if (memento.verboseNewlineSuppressed) {
+            fprintf(stderr, "\n");
+            memento.verboseNewlineSuppressed = 0;
+        }
+        fprintf(stderr, "%s (size="FMTZ,
+                eventType[et],
+                (FMTZ_CAST)memblk->rawsize);
+#ifdef MEMENTO_DETAILS
+        fprintf(stderr, ",hash=%x", st->hash);
+#endif
+        if (memblk->label)
+            fprintf(stderr, ") (%s", memblk->label);
+        fprintf(stderr, ")\n");
+        break;
+    case 0:
+        break;
+    }
+
+#ifdef MEMENTO_DETAILS
+    Memento_storeDetails(memblk, et, st);
 #endif
 
     VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk));
@@ -3027,16 +3688,55 @@ static void *do_realloc(void *blk, size_
     Memento_BlkHeader *memblk, *newmemblk;
     size_t             newsizemem;
     int                flags, ret;
+    size_t             oldsize;
+#ifdef MEMENTO_DETAILS
+    Memento_hashedST *st;
+#endif
 
     if (Memento_failThisEventLocked()) {
         errno = ENOMEM;
         return NULL;
     }
 
+#ifdef MEMENTO_DETAILS
+    st = Memento_getHashedStacktrace();
+#endif
+
     memblk     = MEMBLK_FROMBLK(blk);
     VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk));
     ret = checkBlock(memblk, "realloc");
     if (ret) {
+        switch (memento.verbose) {
+        default:
+            if (memento.verboseNewlineSuppressed) {
+                fprintf(stderr, "\n");
+                memento.verboseNewlineSuppressed = 0;
+            }
+            fprintf(stderr, "%s bad block "FMTP":(size=?=>"FMTZ", num=?, now=%d",
+                eventType[type],
+                MEMBLK_TOBLK(memblk),
+                (FMTZ_CAST)newsize, memento.sequence);
+#ifdef MEMENTO_DETAILS
+            fprintf(stderr, ",hash=%x", st->hash);
+#endif
+            fprintf(stderr, ")\n");
+            break;
+        case 2:
+            if (memento.verboseNewlineSuppressed) {
+                fprintf(stderr, "\n");
+                memento.verboseNewlineSuppressed = 0;
+            }
+            fprintf(stderr, "%s bad block (size=?=>"FMTZ,
+                eventType[type],
+                (FMTZ_CAST)newsize);
+#ifdef MEMENTO_DETAILS
+            fprintf(stderr, ",hash=%x", st->hash);
+#endif
+            fprintf(stderr, ")\n");
+            break;
+        case 0:
+            break;
+        }
         if (ret == 2)
             Memento_breakpoint();
         errno = ENOMEM;
@@ -3044,7 +3744,7 @@ static void *do_realloc(void *blk, size_
     }
 
 #ifdef MEMENTO_DETAILS
-    Memento_storeDetails(memblk, type);
+    Memento_storeDetails(memblk, type, st);
 #endif
 
     VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk));
@@ -3052,7 +3752,42 @@ static void *do_realloc(void *blk, size_
         Memento_breakpointLocked();
 
     VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk));
-    if (memento.maxMemory != 0 && memento.alloc - memblk->rawsize + newsize > memento.maxMemory) {
+    oldsize = memblk->rawsize;
+    if (memento.maxMemory != 0 && memento.alloc - oldsize + newsize > memento.maxMemory) {
+        switch (memento.verbose) {
+        default:
+            if (memento.verboseNewlineSuppressed) {
+                fprintf(stderr, "\n");
+                memento.verboseNewlineSuppressed = 0;
+            }
+            fprintf(stderr, "%s failing (memory limit exceeded) "FMTP":(size="FMTZ"=>"FMTZ", num=%d, now=%d",
+                    eventType[type], MEMBLK_TOBLK(memblk),
+                    (FMTZ_CAST)oldsize, (FMTZ_CAST)newsize,
+                    memblk->sequence, memento.sequence);
+#ifdef MEMENTO_DETAILS
+            fprintf(stderr, ",hash=%x", st->hash);
+#endif
+            if (memblk->label)
+                fprintf(stderr, ") (%s", memblk->label);
+            fprintf(stderr, ")\n");
+            break;
+        case 2:
+            if (memento.verboseNewlineSuppressed) {
+                fprintf(stderr, "\n");
+                memento.verboseNewlineSuppressed = 0;
+            }
+            fprintf(stderr, "%s failing (memory limit exceeded) (size="FMTZ"=>"FMTZ,
+                    eventType[type], (FMTZ_CAST)oldsize, (FMTZ_CAST)newsize);
+#ifdef MEMENTO_DETAILS
+            fprintf(stderr, ",hash=%x", st->hash);
+#endif
+            if (memblk->label)
+                fprintf(stderr, ") (%s", memblk->label);
+            fprintf(stderr, ")\n");
+            break;
+        case 0:
+            break;
+        }
         errno = ENOMEM;
         return NULL;
     }
@@ -3064,6 +3799,41 @@ static void *do_realloc(void *blk, size_
     newmemblk  = MEMENTO_UNDERLYING_REALLOC(memblk, newsizemem);
     if (newmemblk == NULL)
     {
+        switch (memento.verbose) {
+        default:
+            if (memento.verboseNewlineSuppressed) {
+                fprintf(stderr, "\n");
+                memento.verboseNewlineSuppressed = 0;
+            }
+            fprintf(stderr, "%s failed "FMTP":(size="FMTZ"=>"FMTZ", num=%d, now=%d",
+                    eventType[type], MEMBLK_TOBLK(memblk),
+                    (FMTZ_CAST)oldsize, (FMTZ_CAST)newsize,
+                    memblk->sequence, memento.sequence);
+#ifdef MEMENTO_DETAILS
+            fprintf(stderr, ",hash=%x", st->hash);
+#endif
+            if (memblk->label)
+                fprintf(stderr, ") (%s", newmemblk->label);
+            fprintf(stderr, ")\n");
+            break;
+        case 2:
+            if (memento.verboseNewlineSuppressed) {
+                fprintf(stderr, "\n");
+                memento.verboseNewlineSuppressed = 0;
+            }
+            fprintf(stderr, "%s failed (size="FMTZ"=>"FMTZ,
+                    eventType[type],
+                    (FMTZ_CAST)oldsize, (FMTZ_CAST)newsize);
+#ifdef MEMENTO_DETAILS
+            fprintf(stderr, ",hash=%x", st->hash);
+#endif
+            if (memblk->label)
+                fprintf(stderr, ") (%s", newmemblk->label);
+            fprintf(stderr, ")\n");
+            break;
+        case 0:
+            break;
+        }
         Memento_addBlockHead(&memento.used, memblk, 2);
         return NULL;
     }
@@ -3091,6 +3861,44 @@ static void *do_realloc(void *blk, size_
     memset(MEMBLK_POSTPTR(newmemblk), MEMENTO_POSTFILL, Memento_PostSize);
     VALGRIND_MAKE_MEM_UNDEFINED(MEMBLK_POSTPTR(newmemblk), Memento_PostSize);
 #endif
+
+    switch (memento.verbose) {
+    default:
+        if (memento.verboseNewlineSuppressed) {
+            fprintf(stderr, "\n");
+            memento.verboseNewlineSuppressed = 0;
+        }
+        fprintf(stderr, "%s "FMTP"=>"FMTP":(size="FMTZ"=>"FMTZ", num=%d, now=%d",
+                eventType[type],
+                MEMBLK_TOBLK(memblk), MEMBLK_TOBLK(newmemblk),
+                (FMTZ_CAST)oldsize, (FMTZ_CAST)newsize,
+                newmemblk->sequence, memento.sequence);
+#ifdef MEMENTO_DETAILS
+        fprintf(stderr, ",hash=%x", st->hash);
+#endif
+        if (memblk->label)
+            fprintf(stderr, ") (%s", newmemblk->label);
+        fprintf(stderr, ")\n");
+        break;
+    case 2:
+        if (memento.verboseNewlineSuppressed) {
+            fprintf(stderr, "\n");
+            memento.verboseNewlineSuppressed = 0;
+        }
+        fprintf(stderr, "%s (size="FMTZ"=>"FMTZ,
+                eventType[type],
+                (FMTZ_CAST)oldsize, (FMTZ_CAST)newsize);
+#ifdef MEMENTO_DETAILS
+        fprintf(stderr, ",hash=%x", st->hash);
+#endif
+        if (memblk->label)
+            fprintf(stderr, ") (%s", newmemblk->label);
+        fprintf(stderr, ")\n");
+        break;
+    case 0:
+        break;
+    }
+
     Memento_addBlockHead(&memento.used, newmemblk, 2);
     return MEMBLK_TOBLK(newmemblk);
 }
@@ -3162,6 +3970,10 @@ static int Memento_Internal_checkAllAllo
         fprintf(stderr, "corrupted.\n    "
                 "Block last checked OK at allocation %d. Now %d.\n",
                 memblk->lastCheckedOK, memento.sequence);
+        if (memento.abortOnCorruption) {
+            fprintf(stderr, "*** memblk corrupted, calling abort()\n");
+            abort();
+        }
         data->preCorrupt  = 0;
         data->postCorrupt = 0;
         data->freeCorrupt = 0;
@@ -3252,8 +4064,8 @@ int Memento_checkAllMemory(void)
         Memento_breakpoint();
         return 1;
     }
-    return 0;
 #endif
+    return 0;
 }
 
 int Memento_setParanoia(int i)
@@ -3293,33 +4105,27 @@ int Memento_check(void)
 
 int Memento_find(void *a)
 {
-    findBlkData data;
+    Memento_BlkHeader *blk;
     int s;
+    int flags;
 
     MEMENTO_LOCK();
-    data.addr  = a;
-    data.blk   = NULL;
-    data.flags = 0;
-    Memento_appBlocks(&memento.used, Memento_containsAddr, &data);
-    if (data.blk != NULL) {
+    blk = find_enclosing_block(&memento.used, a, &flags);
+    if (blk != NULL) {
         fprintf(stderr, "Address "FMTP" is in %sallocated block ",
-                data.addr,
-                (data.flags == 1 ? "" : (data.flags == 2 ?
-                                         "preguard of " : "postguard of ")));
-        s = showBlock(data.blk, ' ');
+                a,
+                (flags == 1 ? "" : (flags == 2 ? "preguard of " : "postguard of ")));
+        s = showBlock(blk, ' ');
         fprintf(stderr, "\n");
         MEMENTO_UNLOCK();
         return s;
     }
-    data.blk   = NULL;
-    data.flags = 0;
-    Memento_appBlocks(&memento.free, Memento_containsAddr, &data);
-    if (data.blk != NULL) {
+    blk = find_enclosing_block(&memento.free, a, &flags);
+    if (blk != NULL) {
         fprintf(stderr, "Address "FMTP" is in %sfreed block ",
-                data.addr,
-                (data.flags == 1 ? "" : (data.flags == 2 ?
-                                         "preguard of " : "postguard of ")));
-        s = showBlock(data.blk, ' ');
+                a,
+                (flags == 1 ? "" : (flags == 2 ? "preguard of " : "postguard of ")));
+        s = showBlock(blk, ' ');
         fprintf(stderr, "\n");
         MEMENTO_UNLOCK();
         return s;
@@ -3330,35 +4136,29 @@ int Memento_find(void *a)
 
 void Memento_breakOnFree(void *a)
 {
-    findBlkData data;
+    Memento_BlkHeader *blk;
+    int flags;
 
     MEMENTO_LOCK();
-    data.addr  = a;
-    data.blk   = NULL;
-    data.flags = 0;
-    Memento_appBlocks(&memento.used, Memento_containsAddr, &data);
-    if (data.blk != NULL) {
+    blk = find_enclosing_block(&memento.used, a, &flags);
+    if (blk != NULL) {
         fprintf(stderr, "Will stop when address "FMTP" (in %sallocated block ",
-                data.addr,
-                (data.flags == 1 ? "" : (data.flags == 2 ?
-                                         "preguard of " : "postguard of ")));
-        showBlock(data.blk, ' ');
+                a,
+                (flags == 1 ? "" : (flags == 2 ? "preguard of " : "postguard of ")));
+        showBlock(blk, ' ');
         fprintf(stderr, ") is freed\n");
-        VALGRIND_MAKE_MEM_DEFINED(data.blk, sizeof(Memento_BlkHeader));
-        data.blk->flags |= Memento_Flag_BreakOnFree;
-        VALGRIND_MAKE_MEM_NOACCESS(data.blk, sizeof(Memento_BlkHeader));
+        VALGRIND_MAKE_MEM_DEFINED(blk, sizeof(Memento_BlkHeader));
+        blk->flags |= Memento_Flag_BreakOnFree;
+        VALGRIND_MAKE_MEM_NOACCESS(blk, sizeof(Memento_BlkHeader));
         MEMENTO_UNLOCK();
         return;
     }
-    data.blk   = NULL;
-    data.flags = 0;
-    Memento_appBlocks(&memento.free, Memento_containsAddr, &data);
-    if (data.blk != NULL) {
+    blk = find_enclosing_block(&memento.free, a, &flags);
+    if (blk != NULL) {
         fprintf(stderr, "Can't stop on free; address "FMTP" is in %sfreed block ",
-                data.addr,
-                (data.flags == 1 ? "" : (data.flags == 2 ?
-                                         "preguard of " : "postguard of ")));
-        showBlock(data.blk, ' ');
+                a,
+                (flags == 1 ? "" : (flags == 2 ? "preguard of " : "postguard of ")));
+        showBlock(blk, ' ');
         fprintf(stderr, "\n");
         MEMENTO_UNLOCK();
         return;
@@ -3369,35 +4169,29 @@ void Memento_breakOnFree(void *a)
 
 void Memento_breakOnRealloc(void *a)
 {
-    findBlkData data;
+    Memento_BlkHeader *blk;
+    int flags;
 
     MEMENTO_LOCK();
-    data.addr  = a;
-    data.blk   = NULL;
-    data.flags = 0;
-    Memento_appBlocks(&memento.used, Memento_containsAddr, &data);
-    if (data.blk != NULL) {
+    blk = find_enclosing_block(&memento.used, a, &flags);
+    if (blk != NULL) {
         fprintf(stderr, "Will stop when address "FMTP" (in %sallocated block ",
-                data.addr,
-                (data.flags == 1 ? "" : (data.flags == 2 ?
-                                         "preguard of " : "postguard of ")));
-        showBlock(data.blk, ' ');
+                a,
+                (flags == 1 ? "" : (flags == 2 ? "preguard of " : "postguard of ")));
+        showBlock(blk, ' ');
         fprintf(stderr, ") is freed (or realloced)\n");
-        VALGRIND_MAKE_MEM_DEFINED(data.blk, sizeof(Memento_BlkHeader));
-        data.blk->flags |= Memento_Flag_BreakOnFree | Memento_Flag_BreakOnRealloc;
-        VALGRIND_MAKE_MEM_NOACCESS(data.blk, sizeof(Memento_BlkHeader));
+        VALGRIND_MAKE_MEM_DEFINED(blk, sizeof(Memento_BlkHeader));
+        blk->flags |= Memento_Flag_BreakOnFree | Memento_Flag_BreakOnRealloc;
+        VALGRIND_MAKE_MEM_NOACCESS(blk, sizeof(Memento_BlkHeader));
         MEMENTO_UNLOCK();
         return;
     }
-    data.blk   = NULL;
-    data.flags = 0;
-    Memento_appBlocks(&memento.free, Memento_containsAddr, &data);
-    if (data.blk != NULL) {
+    blk = find_enclosing_block(&memento.free, a, &flags);
+    if (blk != NULL) {
         fprintf(stderr, "Can't stop on free/realloc; address "FMTP" is in %sfreed block ",
-                data.addr,
-                (data.flags == 1 ? "" : (data.flags == 2 ?
-                                         "preguard of " : "postguard of ")));
-        showBlock(data.blk, ' ');
+                a,
+                (flags == 1 ? "" : (flags == 2 ? "preguard of " : "postguard of ")));
+        showBlock(blk, ' ');
         fprintf(stderr, "\n");
         MEMENTO_UNLOCK();
         return;
@@ -3436,6 +4230,12 @@ int Memento_squeezing(void)
     return memento.squeezing;
 }
 
+int Memento_setVerbose(int x)
+{
+    memento.verbose = x;
+    return x;
+}
+
 #endif /* MEMENTO_CPP_EXTRAS_ONLY */
 
 #ifdef __cplusplus
@@ -3672,6 +4472,10 @@ void (Memento_listNewBlocks)(void)
 {
 }
 
+void (Memento_listPhasedBlocks)(void)
+{
+}
+
 size_t (Memento_setMax)(size_t max)
 {
     return 0;
@@ -3711,4 +4515,13 @@ int (Memento_squeezing)(void)
     return 0;
 }
 
+int (Memento_setVerbose)(int x)
+{
+    return x;
+}
+
+void Memento_showHash(unsigned int hash)
+{
+}
+
 #endif
diff -pruN 9.55.0~dfsg-3/base/memento.h 9.56.1~dfsg-1/base/memento.h
--- 9.55.0~dfsg-3/base/memento.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/memento.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009-2021 Artifex Software, Inc.
+/* Copyright (C) 2009-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -167,7 +167,7 @@
  *    it's really easy:
  *       git clone git://github.com/ianlancetaylor/libbacktrace
  *       cd libbacktrace
- *       ./configure
+ *       ./configure --enable-shared
  *       make
  *
  *    This leaves the build .so as .libs/libbacktrace.so
@@ -184,8 +184,11 @@
 
 #ifndef MEMENTO_H
 
+/* Include all these first, so our definitions below do
+ * not conflict with them. */
 #include <stdlib.h>
 #include <stdarg.h>
+#include <string.h>
 
 #define MEMENTO_H
 
@@ -211,8 +214,6 @@
 #define MEMENTO_ALLOCFILL 0xa8
 #define MEMENTO_FREEFILL  0xa9
 
-#define MEMENTO_FREELIST_MAX 0x2000000
-
 int Memento_checkBlock(void *);
 int Memento_checkAllMemory(void);
 int Memento_check(void);
@@ -229,10 +230,12 @@ int Memento_failAt(int);
 int Memento_failThisEvent(void);
 void Memento_listBlocks(void);
 void Memento_listNewBlocks(void);
+void Memento_listPhasedBlocks(void);
 size_t Memento_setMax(size_t);
 void Memento_stats(void);
 void *Memento_label(void *, const char *);
 void Memento_tick(void);
+int Memento_setVerbose(int);
 
 void *Memento_malloc(size_t s);
 void *Memento_realloc(void *, size_t s);
@@ -276,6 +279,8 @@ void Memento_fin(void);
 
 void Memento_bt(void);
 
+void Memento_showHash(unsigned int hash);
+
 #ifdef MEMENTO
 
 #ifndef COMPILING_MEMENTO_C
@@ -317,6 +322,7 @@ void Memento_bt(void);
 #define Memento_failThisEvent()            0
 #define Memento_listBlocks()               do {} while (0)
 #define Memento_listNewBlocks()            do {} while (0)
+#define Memento_listPhasedBlocks()         do {} while (0)
 #define Memento_setMax(A)                  0
 #define Memento_stats()                    do {} while (0)
 #define Memento_label(A,B)                 (A)
@@ -345,6 +351,7 @@ void Memento_bt(void);
 #define Memento_bt()                       do {} while (0)
 #define Memento_sequence()                 (0)
 #define Memento_squeezing()                (0)
+#define Memento_setVerbose(A)              (A)
 
 #endif /* MEMENTO */
 
diff -pruN 9.55.0~dfsg-3/base/mkromfs.c 9.56.1~dfsg-1/base/mkromfs.c
--- 9.55.0~dfsg-3/base/mkromfs.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/mkromfs.c	2022-04-04 13:46:22.000000000 +0000
@@ -248,36 +248,43 @@ int errprintf_nomem(const char *fmt, ...
     return count;
 }
 
-#ifndef GS_THREADSAFE
 #if __LINE__                    /* compiler provides it */
 void
 lprintf_file_and_line(const char *file, int line)
 {
-    errprintf(NULL, "%s(%d): ", file, line);
+    errprintf_nomem("%s(%d): ", file, line);
 }
 #else
 void
 lprintf_file_only(FILE * f, const char *file)
 {
-    errprintf(NULL, "%s(?): ", file);
+    errprintf_nomem("%s(?): ", file);
 }
 #endif
 
+gs_memory_t *gp_get_debug_mem_ptr(void)
+{
+    return NULL;
+}
+
 void
 eprintf_program_ident(const char *program_name,
                       long revision_number)
 {
+    gs_memory_t *mem = gp_get_debug_mem_ptr();
+
+    if (mem == NULL)
+        return;
     if (program_name) {
-        errprintf(NULL, (revision_number ? "%s " : "%s"), program_name);
+        errprintf(mem, (revision_number ? "%s " : "%s"), program_name);
         if (revision_number) {
             int fpart = revision_number % 100;
 
-            errprintf(NULL, "%d.%02d", (int)(revision_number / 100), fpart);
+            errprintf(mem, "%d.%02d", (int)(revision_number / 100), fpart);
         }
-        errprintf(NULL, ": ");
+        errprintf(mem, ": ");
     }
 }
-#endif
 
 void
 emprintf_program_ident(const gs_memory_t *mem,
diff -pruN 9.55.0~dfsg-3/base/msvccmd.mak 9.56.1~dfsg-1/base/msvccmd.mak
--- 9.55.0~dfsg-3/base/msvccmd.mak	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/msvccmd.mak	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2021 Artifex Software, Inc.
+# Copyright (C) 2001-2022 Artifex Software, Inc.
 # All Rights Reserved.
 #
 # This software is provided AS-IS with no warranty, either express or
@@ -173,18 +173,26 @@ LCT=/DEBUG /INCREMENTAL:YES
 COMPILE_FULL_OPTIMIZED=    # no optimization when debugging
 COMPILE_WITH_FRAMES=    # no optimization when debugging
 COMPILE_WITHOUT_FRAMES=    # no optimization when debugging
+!if $(MAKEDLL)
+CMT=/MDd
+!else
 CMT=/MTd
+!endif
 !else
-!if $(DEBUGSYM)==0
+!if "$(DEBUGSYM)"=="0"
 CT=
 LCT=
+!if $(MAKEDLL)
+CMT=/MD
+!else
 CMT=/MT
+!endif
 COMPILE_WITHOUT_FRAMES=/Oy
 !else
 # Assume that DEBUGSYM != 0 implies a PROFILE build
 CT=/Zi /Fd$(GLOBJDIR)\ $(NULL) $(CDCC) $(CPCH)
 LCT=/DEBUG /PROFILE /OPT:REF /OPT:ICF
-CMT=/MTd
+CMT=/MD
 # Do not disable frame pointers in profile builds.
 COMPILE_WITHOUT_FRAMES=/Oy-
 !endif
@@ -234,8 +242,6 @@ COMPILE_FOR_DLL=
 COMPILE_FOR_EXE=
 COMPILE_FOR_CONSOLE_EXE=
 
-# The /MT is for multi-threading.  We would like to make this an option,
-# but it's too much work right now.
 GENOPT=$(CP) $(CD) $(CT) $(CS) $(WARNOPT) $(VC8WARN) /nologo $(CMT)
 
 CCFLAGS=$(PLATOPT) $(FPFLAGS) $(CPFLAGS) $(CFLAGS) $(XCFLAGS) $(MSINCFLAGS) $(SBRFLAGS)
diff -pruN 9.55.0~dfsg-3/base/msvclib.mak 9.56.1~dfsg-1/base/msvclib.mak
--- 9.55.0~dfsg-3/base/msvclib.mak	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/msvclib.mak	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2021 Artifex Software, Inc.
+# Copyright (C) 2001-2022 Artifex Software, Inc.
 # All Rights Reserved.
 #
 # This software is provided AS-IS with no warranty, either express or
@@ -547,6 +547,10 @@ MSVC_VERSION=14
 # VS2017 or VS2019 (Toolset v141)
 MSVC_VERSION=15
 !endif
+!if "$(_NMAKE_VER)" == "14.16.27043.0"
+# VS2017 or VS2019 (Toolset v141)
+MSVC_VERSION=15
+!endif
 !if "$(_NMAKE_VER)" == "14.24.28314.0"
 # VS2019 (Toolset v142)
 MSVC_VERSION=16
@@ -635,6 +639,31 @@ MS_TOOLSET_VERSION=14.29.30037
 # VS2019 (Toolset v142)
 MSVC_VERSION=16
 MS_TOOLSET_VERSION=14.29.30133
+!endif
+!if "$(_NMAKE_VER)" == "14.29.30136.0"
+# VS2019 (Toolset v142)
+MSVC_VERSION=16
+MS_TOOLSET_VERSION=14.29.30133
+!endif
+!if "$(_NMAKE_VER)" == "14.29.30137.0"
+# VS2019 (Toolset v142)
+MSVC_VERSION=16
+MS_TOOLSET_VERSION=14.29.30133
+!endif
+!if "$(_NMAKE_VER)" == "14.29.30139.0"
+# VS2019 (Toolset v142)
+MSVC_VERSION=16
+MS_TOOLSET_VERSION=14.29.30133
+!endif
+!if "$(_NMAKE_VER)" == "14.29.30140.0"
+# VS2019 (Toolset v142)
+MSVC_VERSION=16
+MS_TOOLSET_VERSION=14.29.30133
+!endif
+!if "$(_NMAKE_VER)" == "14.29.30141.0"
+# VS2019 (Toolset v142)
+MSVC_VERSION=16
+MS_TOOLSET_VERSION=14.29.30133
 !endif
 !endif
 
diff -pruN 9.55.0~dfsg-3/base/openvms.mak 9.56.1~dfsg-1/base/openvms.mak
--- 9.55.0~dfsg-3/base/openvms.mak	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/openvms.mak	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2021 Artifex Software, Inc.
+# Copyright (C) 2001-2022 Artifex Software, Inc.
 # All Rights Reserved.
 #
 # This software is provided AS-IS with no warranty, either express or
@@ -232,7 +232,7 @@ DEVICE_DEVS9=$(DD)pbm.dev $(DD)pbmraw.de
 DEVICE_DEVS10=$(DD)tiffcrle.dev $(DD)tiffg3.dev $(DD)tiffg32d.dev $(DD)tiffg4.dev $(DD)tifflzw.dev $(DD)tiffpack.dev
 DEVICE_DEVS11=$(DD)tiff12nc.dev $(DD)tiff24nc.dev $(DD)tiffgray.dev $(DD)tiff32nc.dev $(DD)tiffsep.dev $(DD)tiffsep1.dev $(DD)tiffscaled.dev $(DD)tiffscaled8.dev $(DD)tiffscaled24.dev $(DD)tiffscaled32.dev
 DEVICE_DEVS12=$(DD)bit.dev $(DD)bitrgb.dev $(DD)bitcmyk.dev
-DEVICE_DEVS13=$(DD)pngmono.dev $(DD)pngmonod.dev $(DD)pnggray.dev $(DD)png16.dev $(DD)png256.dev $(DD)png16m.dev $(DD)pngalpha.dev
+DEVICE_DEVS13=$(DD)pngmono.dev $(DD)pngmonod.dev $(DD)pnggray.dev $(DD)png16.dev $(DD)png256.dev $(DD)png16m.dev $(DD)pngalpha.dev $(DD)png16malpha.dev
 DEVICE_DEVS14=$(DD)jpeg.dev $(DD)jpeggray.dev $(DD)jpegcmyk.dev
 DEVICE_DEVS15=$(DD)pdfwrite.dev $(DD)ps2write.dev $(DD)eps2write.dev $(DD)txtwrite.dev $(DD)pxlmono.dev $(DD)pxlcolor.dev
 DEVICE_DEVS16=$(DD)bbox.dev $(DD)inkcov.dev $(DD)ink_cov.dev
diff -pruN 9.55.0~dfsg-3/base/openvms.mmk 9.56.1~dfsg-1/base/openvms.mmk
--- 9.55.0~dfsg-3/base/openvms.mmk	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/openvms.mmk	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2021 Artifex Software, Inc.
+# Copyright (C) 2001-2022 Artifex Software, Inc.
 # All Rights Reserved.
 #
 # This software is provided AS-IS with no warranty, either express or
@@ -244,7 +244,7 @@ DEVICE_DEVS9=$(DD)pbm.dev $(DD)pbmraw.de
 DEVICE_DEVS10=$(DD)tiffcrle.dev $(DD)tiffg3.dev $(DD)tiffg32d.dev $(DD)tiffg4.dev $(DD)tifflzw.dev $(DD)tiffpack.dev
 DEVICE_DEVS11=$(DD)tiff12nc.dev $(DD)tiff24nc.dev
 DEVICE_DEVS12=$(DD)bit.dev $(DD)bitrgb.dev $(DD)bitcmyk.dev
-DEVICE_DEVS13=$(DD)pngmono.dev $(DD)pnggray.dev $(DD)png16.dev $(DD)png256.dev $(DD)png16m.dev $(DD)pngalpha.dev
+DEVICE_DEVS13=$(DD)pngmono.dev $(DD)pnggray.dev $(DD)png16.dev $(DD)png256.dev $(DD)png16m.dev $(DD)pngalpha.dev $(DD)png16malpha.dev
 DEVICE_DEVS14=$(DD)jpeg.dev $(DD)jpeggray.dev
 DEVICE_DEVS15=$(DD)pdfwrite.dev $(DD)eps2write.dev $(DD)pxlmono.dev $(DD)pxlcolor.dev
 DEVICE_DEVS16=$(DD)bbox.dev
diff -pruN 9.55.0~dfsg-3/base/sa85d.c 9.56.1~dfsg-1/base/sa85d.c
--- 9.55.0~dfsg-3/base/sa85d.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/sa85d.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -156,7 +156,10 @@ s_A85D_process(stream_state * st, stream
                 }
             }
           finish:
-            p += i;		/* advance to the '>' */
+            if (p + i <= rlimit)
+                p += i;		/* advance to the '>' */
+            else
+                p = rlimit; /* Can happen if the '>' is missing */
             pw->ptr = q;
             status = a85d_finish(ccount, word, pw);
             q = pw->ptr;
diff -pruN 9.55.0~dfsg-3/base/scfe.c 9.56.1~dfsg-1/base/scfe.c
--- 9.55.0~dfsg-3/base/scfe.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/scfe.c	2022-04-04 13:46:22.000000000 +0000
@@ -25,9 +25,9 @@
 /* ------ Macros and support routines ------ */
 
 /* Statistics */
+/* #define COLLECT_STATS_SCFE */
 
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
-
+#ifdef COLLECT_STATS_SCFE
 typedef struct stats_runs_s {
     ulong termination[64];
     ulong make_up[41];
@@ -52,7 +52,7 @@ print_run_stats(const gs_memory_t *mem,
     dmprintf1(mem, " total=%lu\n", total);
 }
 
-#else /* !DEBUG || defined(GS_THREADSAFE) */
+#else /* !defined(COLLECT_STATS_SCFE) */
 
 #define COUNT_RUN(cnt, i) DO_NOTHING
 
@@ -82,10 +82,9 @@ cf_put_long_run(stream_CFE_state * ss, b
     hce_declare_state;
     cfe_run rr;
 
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_SCFE
     stats_runs_t *pstats =
     (prt == &cf_white_runs ? &stats_white_runs : &stats_black_runs);
-
 #endif
 
     hce_load_state();
@@ -374,11 +373,12 @@ s_CFE_process(stream_state * st, stream_
                status, ss->read_count, ss->write_count,
                (intptr_t) pr->ptr, (int)(rlimit - pr->ptr), (intptr_t) rlimit,
                (intptr_t) pw->ptr, (int)(wlimit - pw->ptr), (intptr_t) wlimit);
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef DEBUG
     if (pr->ptr > rlimit || pw->ptr > wlimit) {
         lprintf("Pointer overrun!\n");
         status = ERRC;
     }
+#ifdef COLLECT_STATS_SCFE
     if (gs_debug_c('w') && status == 1) {
         dmlputs(ss->memory, "[w]white runs:");
         print_run_stats(ss->memory, &stats_white_runs);
@@ -386,6 +386,7 @@ s_CFE_process(stream_state * st, stream_
         print_run_stats(ss->memory, &stats_black_runs);
     }
 #endif
+#endif
     return status;
 }
 
diff -pruN 9.55.0~dfsg-3/base/scommon.h 9.56.1~dfsg-1/base/scommon.h
--- 9.55.0~dfsg-3/base/scommon.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/scommon.h	2022-04-04 13:46:22.000000000 +0000
@@ -34,15 +34,6 @@
  */
 typedef struct stream_s stream;
 
-/* We really want our offset type to be 64 bit for large file support
- * but this allows a particular port to specficy a prefered data type
- */
-#ifdef GS_OFFSET_T
-typedef GS_OFFSET_T gs_offset_t;
-#else
-typedef int64_t gs_offset_t;
-#endif
-
 /*
  * A stream_state records the state specific to a given variety of stream.
  * The buffer processing function of a stream maintains this state.
diff -pruN 9.55.0~dfsg-3/base/sdcparam.c 9.56.1~dfsg-1/base/sdcparam.c
--- 9.55.0~dfsg-3/base/sdcparam.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/sdcparam.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -201,7 +201,7 @@ s_DCT_get_quantization_tables(gs_param_l
         gs_param_string str;
         gs_param_float_array fa;
 
-        gs_sprintf(key, "%d", i);
+        gs_snprintf(key, sizeof(key), "%d", i);
         if (QFactor == 1.0) {
             code = quant_param_string(&str, DCTSIZE2,
                             table_ptrs[comp_info[i].quant_tbl_no]->quantval,
@@ -490,7 +490,7 @@ s_DCT_put_quantization_tables(gs_param_l
         char istr[5];		/* i converted to string key */
         UINT16 values[DCTSIZE2];
 
-        gs_sprintf(istr, "%d", i);
+        gs_snprintf(istr, sizeof(istr), "%d", i);
         code = quant_params(quant_tables.list, istr, DCTSIZE2, values,
                             pdct->QFactor);
         if (code < 0)
@@ -582,7 +582,7 @@ s_DCT_put_huffman_tables(gs_param_list *
         UINT8 counts[16], values[256];
 
         /* Collect the Huffman parameters. */
-        gs_sprintf(istr, "%d", i);
+        gs_snprintf(istr, sizeof(istr), "%d", i);
         code = s_DCT_byte_params(huff_tables.list, istr, 0, 16, counts);
         if (code < 0)
             return code;
diff -pruN 9.55.0~dfsg-3/base/sfxcommon.c 9.56.1~dfsg-1/base/sfxcommon.c
--- 9.55.0~dfsg-3/base/sfxcommon.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/sfxcommon.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -183,6 +183,9 @@ file_prepare_stream(const char *fname, u
     byte *buffer;
     register stream *s;
 
+    if (strlen(file_access) > 2)
+        return_error(gs_error_invalidfileaccess);
+
     /* Open the file, always in binary mode. */
     strcpy(fmode, file_access);
     strcat(fmode, gp_fmode_binary_suffix);
diff -pruN 9.55.0~dfsg-3/base/shc.h 9.56.1~dfsg-1/base/shc.h
--- 9.55.0~dfsg-3/base/shc.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/shc.h	2022-04-04 13:46:22.000000000 +0000
@@ -105,7 +105,7 @@ typedef struct hce_table_s {
  * that q does not exceed pw->limit.
  */
 
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#if defined(DEBUG)
 #  define hc_print_value(code, clen)\
     (gs_debug_c('W') ?\
      (dlprintf2("[W]0x%x,%d\n", code, clen), 0) : 0)
diff -pruN 9.55.0~dfsg-3/base/sjbig2.c 9.56.1~dfsg-1/base/sjbig2.c
--- 9.55.0~dfsg-3/base/sjbig2.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/sjbig2.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -63,7 +63,7 @@ s_jbig2decode_error(void *callback_data,
         default: type = "unknown message:"; break;;
     }
     if (seg_idx == JBIG2_UNKNOWN_SEGMENT_NUMBER) segment[0] = '\0';
-    else gs_sprintf(segment, "(segment 0x%02x)", seg_idx);
+    else gs_snprintf(segment, sizeof(segment), "(segment 0x%02x)", seg_idx);
 
     if (error_data)
     {
diff -pruN 9.55.0~dfsg-3/base/sjpx_openjpeg.c 9.56.1~dfsg-1/base/sjpx_openjpeg.c
--- 9.55.0~dfsg-3/base/sjpx_openjpeg.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/sjpx_openjpeg.c	2022-04-04 13:46:22.000000000 +0000
@@ -715,7 +715,10 @@ s_opjd_process(stream_state * ss, stream
         locked = 1;
 
         code = s_opjd_accumulate_input(state, pr);
-        if (code < 0) return code;
+        if (code < 0) {
+            (void)opj_unlock(ss->memory);
+            return code;
+        }
 
         if (state->codec == NULL) {
             /* state->sb.size is non-zero after successful
diff -pruN 9.55.0~dfsg-3/base/spprint.c 9.56.1~dfsg-1/base/spprint.c
--- 9.55.0~dfsg-3/base/spprint.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/spprint.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -83,7 +83,7 @@ pprintd1(stream * s, const char *format,
     if (*fp == 0 || fp[1] != 'd')	/* shouldn't happen! */
         lprintf1("Bad format in pprintd1: %s\n", format);
 #endif
-    gs_sprintf(str, "%d", v);
+    gs_snprintf(str, sizeof(str), "%d", v);
     pputs_short(s, str);
     return pprintf_scan(s, fp + 2);
 }
@@ -115,12 +115,12 @@ pprintg1(stream * s, const char *format,
     if (*fp == 0 || fp[1] != 'g')	/* shouldn't happen! */
         lprintf1("Bad format in pprintg: %s\n", format);
 #endif
-    gs_sprintf(str, "%f", 1.5);
+    gs_snprintf(str, sizeof(str), "%f", 1.5);
     dot = str[1]; /* locale-dependent */
-    gs_sprintf(str, "%g", v);
+    gs_snprintf(str, sizeof(str), "%g", v);
     if (strchr(str, 'e')) {
         /* Bad news.  Try again using f-format. */
-        gs_sprintf(str, (fabs(v) > 1 ? "%1.1f" : "%1.8f"), v);
+        gs_snprintf(str, sizeof(str), (fabs(v) > 1 ? "%1.1f" : "%1.8f"), v);
     }
     /* Juggling locales isn't thread-safe. Posix me harder. */
     if (dot != '.') {
@@ -165,7 +165,7 @@ pprintld1(stream *s, const char *format,
     if (*fp == 0 || fp[1] != 'l' || fp[2] != 'd')	/* shouldn't happen! */
         lprintf1("Bad format in pprintld: %s\n", format);
 #endif
-    gs_sprintf(str, "%ld", v);
+    gs_snprintf(str, sizeof(str), "%ld", v);
     pputs_short(s, str);
     return pprintf_scan(s, fp + 3);
 }
@@ -197,7 +197,7 @@ pprintzd1(stream *s, const char *format,
     if (i != z)
         lprintf1("Bad format in pprintzd: %s\n", format);
 #endif
-    gs_sprintf(str, "%"PRIdSIZE, v);
+    gs_snprintf(str, sizeof(str), "%"PRIdSIZE, v);
     pputs_short(s, str);
     return pprintf_scan(s, fp + z);
 }
@@ -229,7 +229,7 @@ pprinti64d1(stream *s, const char *forma
     if (i != z)
         lprintf1("Bad format in pprinti64d: %s\n", format);
 #endif
-    gs_sprintf(str, "%"PRId64, v);
+    gs_snprintf(str, sizeof(str), "%"PRId64, v);
     pputs_short(s, str);
     return pprintf_scan(s, fp + z);
 }
diff -pruN 9.55.0~dfsg-3/base/std.h 9.56.1~dfsg-1/base/std.h
--- 9.55.0~dfsg-3/base/std.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/std.h	2022-04-04 13:46:22.000000000 +0000
@@ -128,8 +128,8 @@ typedef ulong bits32;
  *
  * If you do not have a gs_memory_t * to hand, then you may call dprintf (and
  * family) and eprintf(and family) insteads. Be aware that these functions
- * compile away to nothing in GS_THREADSAFE builds, as they will not work in
- * multithreaded environments.
+ * compile away to nothing in non-DEBUG builds (and even in some DEBUG
+ * environments), as they rely on platform specific threading tricks.
  *
  * Since we all stdout/stderr output must go via outprintf/errprintf,
  * we have to define dputc and dputs in terms of errprintf also.
@@ -156,10 +156,8 @@ typedef struct gs_memory_s gs_memory_t;
 #define dpfm errprintf
 #define epfm errprintf
 
-#ifndef GS_THREADSAFE
 #define dpf errprintf_nomem
 #define epf errprintf_nomem
-#endif /* GS_THREADSAFE */
 
 /* To allow stdout and stderr to be redirected, all stdout goes
  * though outwrite and all stderr goes through errwrite.
@@ -170,14 +168,6 @@ int errwrite(const gs_memory_t *mem, con
 void outflush(const gs_memory_t *mem);
 void errflush(const gs_memory_t *mem);
 
-#ifndef GS_THREADSAFE
-/* As a temporary measure, we allow forms of errwrite/errflush that do not
- * need to be given a memory pointer. Any uses of this (largely the debugging
- * system) will fail with multithreaded usage. */
-int errwrite_nomem(const char *str, int len);
-void errflush_nomem(void);
-#endif /* GS_THREADSAFE */
-
 /* Formatted output to outwrite and errwrite.
  * The maximum string length is 1023 characters.
  */
@@ -193,77 +183,19 @@ void errflush_nomem(void);
 #  endif
 int outprintf(const gs_memory_t *mem, const char *fmt, ...) __printflike(2, 3);
 int errprintf(const gs_memory_t *mem, const char *fmt, ...) __printflike(2, 3);
-#ifndef GS_THREADSAFE
 int errprintf_nomem(const char *fmt, ...) __printflike(1, 2);
-#endif
 #else
 int outprintf();
 int errprintf();
-#ifndef GS_THREADSAFE
 int errprintf_nomem();
 #endif
-#endif
 
-/* Print the program line # for debugging - NON THREADSAFE VERSIONS */
-#ifdef GS_THREADSAFE
-#define dflush() DO_NOTHING
-#define dputc(chr) DO_NOTHING
-#define dlputc(chr) DO_NOTHING
-#define dputs(str) DO_NOTHING
-#define dlputs(str) DO_NOTHING
-#define dprintf(str)\
-  DO_NOTHING
-#define dlprintf(str)\
-  DO_NOTHING
-#define dprintf1(str,arg1)\
-  DO_NOTHING
-#define dlprintf1(str,arg1)\
-  DO_NOTHING
-#define dprintf2(str,arg1,arg2)\
-  DO_NOTHING
-#define dlprintf2(str,arg1,arg2)\
-  DO_NOTHING
-#define dprintf3(str,arg1,arg2,arg3)\
-  DO_NOTHING
-#define dlprintf3(str,arg1,arg2,arg3)\
-  DO_NOTHING
-#define dprintf4(str,arg1,arg2,arg3,arg4)\
-  DO_NOTHING
-#define dlprintf4(str,arg1,arg2,arg3,arg4)\
-  DO_NOTHING
-#define dprintf5(str,arg1,arg2,arg3,arg4,arg5)\
-  DO_NOTHING
-#define dlprintf5(str,arg1,arg2,arg3,arg4,arg5)\
-  DO_NOTHING
-#define dprintf6(str,arg1,arg2,arg3,arg4,arg5,arg6)\
-  DO_NOTHING
-#define dlprintf6(str,arg1,arg2,arg3,arg4,arg5,arg6)\
-  DO_NOTHING
-#define dprintf7(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7)\
-  DO_NOTHING
-#define dlprintf7(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7)\
-  DO_NOTHING
-#define dprintf8(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)\
-  DO_NOTHING
-#define dlprintf8(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)\
-  DO_NOTHING
-#define dprintf9(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9)\
-  DO_NOTHING
-#define dlprintf9(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9)\
-  DO_NOTHING
-#define dprintf10(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10)\
-  DO_NOTHING
-#define dlprintf10(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10)\
-  DO_NOTHING
-#define dprintf11(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11)\
-  DO_NOTHING
-#define dlprintf11(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11)\
-  DO_NOTHING
-#define dprintf12(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12)\
-  DO_NOTHING
-#define dlprintf12(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12)\
-  DO_NOTHING
-#else
+/* Print the program line # for debugging */
+/* These versions do not take a memory pointer, and hence have to
+ * appeal to platform specific methods to get one from thread local
+ * storage. These are likely to be much slower than the other versions,
+ * and, on some platforms at least, may do nothing. Avoid these if
+ * possible. */
 #if __LINE__                    /* compiler provides it */
 void dprintf_file_and_line(const char *, int);
 #  define _dpl dprintf_file_and_line(__FILE__, __LINE__),
@@ -329,7 +261,6 @@ void dflush(void);              /* flush
   dpf(str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12)
 #define dlprintf12(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12)\
   (_dpl dprintf12(str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12))
-#endif /* GS_THREADSAFE */
 
 /* Print the program line # for debugging. */
 #if __LINE__                    /* compiler provides it */
@@ -400,11 +331,6 @@ void dmprintf_file_only(const gs_memory_
 
 
 void printf_program_ident(const gs_memory_t *mem, const char *program_name, long revision_number);
-#ifdef GS_THREADSAFE
-#define eprintf_program_ident(NAME,NUM) DO_NOTHING
-#else
-void eprintf_program_ident(const char *program_name, long revision_number);
-#endif /* GS_THREADSAFE */
 void emprintf_program_ident(const gs_memory_t *mem,
                             const char *program_name,
                             long revision_number);
@@ -412,30 +338,12 @@ const char *gs_program_family_name(void)
 const char *gs_program_name(void);
 long gs_revision_number(void);
 
-#ifdef GS_THREADSAFE
-#define eprintf(str)\
-  DO_NOTHING
-#define eprintf1(str,arg1)\
-  DO_NOTHING
-#define eprintf2(str,arg1,arg2)\
-  DO_NOTHING
-#define eprintf3(str,arg1,arg2,arg3)\
-  DO_NOTHING
-#define eprintf4(str,arg1,arg2,arg3,arg4)\
-  DO_NOTHING
-#define eprintf5(str,arg1,arg2,arg3,arg4,arg5)\
-  DO_NOTHING
-#define eprintf6(str,arg1,arg2,arg3,arg4,arg5,arg6)\
-  DO_NOTHING
-#define eprintf7(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7)\
-  DO_NOTHING
-#define eprintf8(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)\
-  DO_NOTHING
-#define eprintf9(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9)\
-  DO_NOTHING
-#define eprintf10(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10)\
-  DO_NOTHING
-#else
+/* These versions do not take a memory pointer, and hence have to
+ * appeal to platform specific methods to get one from thread local
+ * storage. These are likely to be much slower than the other versions,
+ * and, on some platforms at least, may do nothing. Avoid these if
+ * possible. */
+void eprintf_program_ident(const char *program_name, long revision_number);
 #define _epi eprintf_program_ident(gs_program_name(), gs_revision_number()),
 
 #define eprintf(str)\
@@ -460,7 +368,6 @@ long gs_revision_number(void);
   (_epi epf(str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9))
 #define eprintf10(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10)\
   (_epi epf(str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10))
-#endif /* GS_THREADSAFE */
 
 #define _epim(mem) emprintf_program_ident(mem, gs_program_name(), gs_revision_number()),
 
@@ -487,30 +394,6 @@ long gs_revision_number(void);
 #define emprintf10(mem, str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10)\
   (_epim(mem) epfm(mem, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10))
 
-#ifdef GS_THREADSAFE
-#define lprintf(str)\
-  DO_NOTHING
-#define lprintf1(str,arg1)\
-  DO_NOTHING
-#define lprintf2(str,arg1,arg2)\
-  DO_NOTHING
-#define lprintf3(str,arg1,arg2,arg3)\
-  DO_NOTHING
-#define lprintf4(str,arg1,arg2,arg3,arg4)\
-  DO_NOTHING
-#define lprintf5(str,arg1,arg2,arg3,arg4,arg5)\
-  DO_NOTHING
-#define lprintf6(str,arg1,arg2,arg3,arg4,arg5,arg6)\
-  DO_NOTHING
-#define lprintf7(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7)\
-  DO_NOTHING
-#define lprintf8(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)\
-  DO_NOTHING
-#define lprintf9(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9)\
-  DO_NOTHING
-#define lprintf10(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10)\
-  DO_NOTHING
-#else
 #if __LINE__			/* compiler provides it */
 void lprintf_file_and_line(const char *, int);
 #  define _epl _epi lprintf_file_and_line(__FILE__, __LINE__),
@@ -541,7 +424,6 @@ void lprintf_file_only(const char *);
   (_epl epf(str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9))
 #define lprintf10(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10)\
   (_epl epf(str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10))
-#endif /* GS_THREADSAFE */
 
 #if __LINE__			/* compiler provides it */
 void mlprintf_file_and_line(const gs_memory_t *,const char *, int);
diff -pruN 9.55.0~dfsg-3/base/stdint_.h 9.56.1~dfsg-1/base/stdint_.h
--- 9.55.0~dfsg-3/base/stdint_.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/stdint_.h	2022-04-04 13:46:22.000000000 +0000
@@ -27,6 +27,7 @@
 /* Define some stdint.h types. The jbig2dec headers and ttf bytecode
  * interpreter require these and they're generally useful to have around
  * now that there's a standard.
+
  */
 
 /* Some systems are guaranteed to have stdint.h
@@ -127,6 +128,24 @@ typedef unsigned long long uint64_t;
 #  define STDINT_TYPES_DEFINED
 #endif /* STDINT_TYPES_DEFINED */
 
+
+/* We really want our offset type to be 64 bit for large file support
+ * but this allows a particular port to specficy a prefered data type size
+ */
+#ifdef ARCH_SIZEOF_GS_OFFSET_T
+# if ARCH_SIZEOF_GS_OFFSET_T == 8
+typedef int64_t gs_offset_t;
+# elif ARCH_SIZEOF_GS_OFFSET_T == 4
+typedef int32_t gs_offset_t;
+# else
+UNSUPPORTED
+# endif
+#else
+# define ARCH_SIZEOF_GS_OFFSET_T 8
+typedef int64_t gs_offset_t;
+#endif
+
+
 #if defined(HAVE_INTTYPES_H) && HAVE_INTTYPES_H == 1
 # include <inttypes.h>
 #else
@@ -248,4 +267,10 @@ typedef unsigned long long uint64_t;
 #  define PRI_INTPTR "0x%" PRIx64
 # endif
 
+# if ARCH_SIZEOF_GS_OFFSET_T == 4
+#  define PRIdOFFSET PRId32
+# else
+#  define PRIdOFFSET PRId64
+# endif
+
 #endif /* stdint__INCLUDED */
diff -pruN 9.55.0~dfsg-3/base/stdio_.h 9.56.1~dfsg-1/base/stdio_.h
--- 9.55.0~dfsg-3/base/stdio_.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/stdio_.h	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -35,7 +35,7 @@
 #    define unlink(fname) delete(fname)
 #  endif
 #else
-#if !defined(const)
+#if !defined(const) && !defined(_WIN32) && !defined(_WIN64)
 /*
  * Other systems may or may not declare unlink in stdio.h;
  * if they do, the declaration will be compatible with this one, as long
diff -pruN 9.55.0~dfsg-3/base/ttinterp.c 9.56.1~dfsg-1/base/ttinterp.c
--- 9.55.0~dfsg-3/base/ttinterp.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/ttinterp.c	2022-04-04 13:46:22.000000000 +0000
@@ -88,7 +88,9 @@
 #  define DBG_PRINT4(fmt, a, b, c, d)
 #endif
 
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+/* #define COLLECT_STATS_TTINTERP */
+
+#ifdef COLLECT_STATS_TTINTERP
 static int nInstrCount=0;
 #endif
 
@@ -4952,7 +4954,7 @@ static int nInstrCount=0;
     Int          A;
     PDefRecord   WITH;
     PCallRecord  WITH1;
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_TTINTERP
     bool bFirst;
 #endif
     bool dbg_prt = (DBG_PRT_FUN != NULL);
@@ -5002,7 +5004,7 @@ static int nInstrCount=0;
         CUR.error = Result;
         goto _LExit;
     }
-#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_TTINTERP
     bFirst = true;
 #endif
     do
@@ -5037,7 +5039,7 @@ static int nInstrCount=0;
       CUR.step_ins = TRUE;
       CUR.error    = TT_Err_Ok;
 
-#     if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_TTINTERP
         DBG_PRINT3("\n%%n=%5d IP=%5d OP=%s            ", nInstrCount, CUR.IP, Instruct_Dispatch[CUR.opcode].sName);
         /*
         { for(int i=0;i<CUR.top;i++)
@@ -5050,11 +5052,11 @@ static int nInstrCount=0;
           memcpy(save_cx, CUR.pts.cur_x, sizeof(CUR.pts.cur_x[0]) * CUR.pts.n_points);
           memcpy(save_cy, CUR.pts.cur_y, sizeof(CUR.pts.cur_y[0]) * CUR.pts.n_points);
         }
-#     endif
+#endif
 
       Instruct_Dispatch[CUR.opcode].p( EXEC_ARGS &CUR.stack[CUR.args] );
 
-#     if defined(DEBUG) && !defined(GS_THREADSAFE)
+#ifdef COLLECT_STATS_TTINTERP
       if (save_ox != NULL) {
         F26Dot6 *pp[4], *qq[4];
         const char *ss[] = {"org.x", "org.y", "cur.x", "cur.y"};
@@ -5084,7 +5086,7 @@ static int nInstrCount=0;
         nInstrCount++;
         bFirst=FALSE;
       }
-#     endif
+#endif
 
       DBG_PAINT
 
diff -pruN 9.55.0~dfsg-3/base/unixansi.mak 9.56.1~dfsg-1/base/unixansi.mak
--- 9.55.0~dfsg-3/base/unixansi.mak	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/unixansi.mak	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2021 Artifex Software, Inc.
+# Copyright (C) 2001-2022 Artifex Software, Inc.
 # All Rights Reserved.
 #
 # This software is provided AS-IS with no warranty, either express or
@@ -353,16 +353,16 @@ DEVICE_DEVS9=$(DD)pbm.dev $(DD)pbmraw.de
 DEVICE_DEVS10=$(DD)tiffcrle.dev $(DD)tiffg3.dev $(DD)tiffg32d.dev $(DD)tiffg4.dev $(DD)tifflzw.dev $(DD)tiffpack.dev
 DEVICE_DEVS11=$(DD)tiff12nc.dev $(DD)tiff24nc.dev $(DD)tiffgray.dev $(DD)tiff32nc.dev $(DD)tiffsep.dev $(DD)tiffsep1.dev $(DD)tiffscaled.dev $(DD)tiffscaled8.dev $(DD)tiffscaled24.dev $(DD)tiffscaled32.dev $(DD)tiffscaled4.dev
 DEVICE_DEVS12=$(DD)bit.dev $(DD)bitrgb.dev $(DD)bitcmyk.dev
-DEVICE_DEVS13=$(DD)pngmono.dev $(DD)pngmonod.dev $(DD)pnggray.dev $(DD)png16.dev $(DD)png256.dev $(DD)png16m.dev $(DD)pngalpha.dev
+DEVICE_DEVS13=$(DD)pngmono.dev $(DD)pngmonod.dev $(DD)pnggray.dev $(DD)png16.dev $(DD)png256.dev $(DD)png16m.dev $(DD)pngalpha.dev $(DD)png16malpha.dev
 DEVICE_DEVS14=$(DD)jpeg.dev $(DD)jpeggray.dev $(DD)jpegcmyk.dev
 DEVICE_DEVS15=$(DD)pdfwrite.dev $(DD)ps2write.dev $(DD)eps2write.dev $(DD)txtwrite.dev $(DD)pxlmono.dev $(DD)pxlcolor.dev
-DEVICE_DEVS16=$(DD)bbox.dev $(DD)inkcov.dev $(DD)ink_cov.dev $(DD)pdfimage8.dev $(DD)pdfimage24.dev $(DD)pdfimage32.dev $(DD)PCLm.dev
+DEVICE_DEVS16=$(DD)bbox.dev $(DD)inkcov.dev $(DD)ink_cov.dev $(DD)pdfimage8.dev $(DD)pdfimage24.dev $(DD)pdfimage32.dev $(DD)PCLm.dev $(DD)PCLm8.dev
 # Overflow from DEVS9
 DEVICE_DEVS17=$(DD)pnm.dev $(DD)pnmraw.dev $(DD)ppm.dev $(DD)ppmraw.dev $(DD)pkm.dev $(DD)pkmraw.dev $(DD)pksm.dev $(DD)pksmraw.dev $(DD)pamcmyk32.dev
 DEVICE_DEVS18=
 DEVICE_DEVS19=
 DEVICE_DEVS20=
-DEVICE_DEVS21=$(DD)spotcmyk.dev $(DD)devicen.dev $(DD)xcf.dev $(DD)bmpsep1.dev $(DD)bmpsep8.dev $(DD)bmp16m.dev $(DD)bmp32b.dev $(DD)psdcmyk.dev $(DD)psdrgb.dev $(DD)pamcmyk32.dev $(DD)psdcmykog.dev $(DD)fpng.dev  $(DD)psdcmyk16.dev $(DD)psdrgb16.dev
+DEVICE_DEVS21=$(DD)spotcmyk.dev $(DD)devicen.dev $(DD)xcf.dev $(DD)bmpsep1.dev $(DD)bmpsep8.dev $(DD)bmp16m.dev $(DD)bmp32b.dev $(DD)psdcmyk.dev $(DD)psdrgb.dev $(DD)pamcmyk32.dev $(DD)psdcmykog.dev $(DD)fpng.dev  $(DD)psdcmyk16.dev $(DD)psdrgb16.dev $(DD)psdcmyktags16.dev $(DD)psdcmyktags.dev
 
 # ---------------------------- End of options --------------------------- #
 
diff -pruN 9.55.0~dfsg-3/base/unix-dll.mak 9.56.1~dfsg-1/base/unix-dll.mak
--- 9.55.0~dfsg-3/base/unix-dll.mak	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/unix-dll.mak	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2021 Artifex Software, Inc.
+# Copyright (C) 2001-2022 Artifex Software, Inc.
 # All Rights Reserved.
 #
 # This software is provided AS-IS with no warranty, either express or
@@ -193,12 +193,16 @@ gpdl-so-links-subtarget: $(GPDL_SO) $(UN
 	$(NO_OP)
 
 # Build the small Ghostscript loaders, with Gtk+ and without
-$(GSSOC_XE): gs-so-links-subtarget $(PSSRC)dxmainc.c $(UNIX_DLL_MAK) $(MAKEDIRS)
+$(GLO)dxmainc.$(OBJ): $(PSSRC)dxmainc.c $(UNIX_DLL_MAK) $(MAKEDIRS)
 	$(GLCC) $(GLO_)dxmainc.$(OBJ) $(C_) $(PSSRC)dxmainc.c
+
+$(GLO)dxmain.$(OBJ): $(PSSRC)dxmain.c $(UNIX_DLL_MAK) $(MAKEDIRS)
+	$(GLCC) $(SOC_CFLAGS) $(GLO_)dxmain.$(OBJ) $(C_) $(PSSRC)dxmain.c
+
+$(GSSOC_XE): gs-so-links-subtarget $(GLO)dxmainc.$(OBJ) $(UNIX_DLL_MAK) $(MAKEDIRS)
 	$(GLCC) -L$(BINDIR) $(LDFLAGS) $(O_) $(GSSOC_XE) $(GLOBJ)dxmainc.$(OBJ) -l$(GS_SO_BASE)
 
-$(GSSOX_XE): gs-so-links-subtarget $(PSSRC)$(SOC_LOADER).c $(UNIX_DLL_MAK) $(MAKEDIRS)
-	$(GLCC) $(SOC_CFLAGS) $(GLO_)$(SOC_LOADER).$(OBJ) $(C_) $(PSSRC)$(SOC_LOADER).c
+$(GSSOX_XE): gs-so-links-subtarget $(GLO)$(SOC_LOADER).$(OBJ) $(UNIX_DLL_MAK) $(MAKEDIRS)
 	$(GLCC) -L$(BINDIR) $(LDFLAGS) $(O_) $(GSSOX_XE) $(GLOBJ)$(SOC_LOADER).$(OBJ) -l$(GS_SO_BASE) $(SOC_LIBS)
 
 $(PCLSOC_XE): gpcl6-so-links-subtarget $(UNIX_DLL_MAK) $(PLOBJ)$(REALMAIN_SRC).$(OBJ) $(MAKEDIRS)
diff -pruN 9.55.0~dfsg-3/base/unix-gcc.mak 9.56.1~dfsg-1/base/unix-gcc.mak
--- 9.55.0~dfsg-3/base/unix-gcc.mak	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/unix-gcc.mak	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2021 Artifex Software, Inc.
+# Copyright (C) 2001-2022 Artifex Software, Inc.
 # All Rights Reserved.
 #
 # This software is provided AS-IS with no warranty, either express or
@@ -328,6 +328,7 @@ CUPSPDFTORASTER=0
 SHARE_LCUPS=1
 LCUPS_NAME=cups
 LCUPSSRCDIR=./cups
+LIBCUPSSRCDIR=./cups
 LCUPSBUILDTYPE=
 CUPS_CC=$(CC)
 
@@ -555,12 +556,12 @@ DISPLAY_DEV=$(DD)bbox.dev
 # devs.mak and dcontrib.mak for the list of available devices.
 # DEVICE_DEVS=$(DISPLAY_DEV) $(DD)x11.dev $(DD)x11_.dev $(DD)x11alpha.dev $(DD)x11alt_.dev $(DD)x11cmyk.dev $(DD)x11cmyk2.dev $(DD)x11cmyk4.dev $(DD)x11cmyk8.dev $(DD)x11gray2.dev $(DD)x11gray4.dev $(DD)x11mono.dev $(DD)x11rg16x.dev $(DD)x11rg32x.dev
 DEVICE_DEVS=$(DISPLAY_DEV)
-DEVICE_DEVS1=$(DD)bit.dev $(DD)bitcmyk.dev $(DD)bitrgb.dev $(DD)bitrgbtags.dev $(DD)bmp16.dev $(DD)bmp16m.dev $(DD)bmp256.dev $(DD)bmp32b.dev $(DD)bmpgray.dev $(DD)bmpmono.dev $(DD)bmpsep1.dev $(DD)bmpsep8.dev $(DD)ccr.dev $(DD)cif.dev $(DD)devicen.dev $(DD)eps2write.dev $(DD)fpng.dev $(DD)inferno.dev $(DD)ink_cov.dev $(DD)inkcov.dev $(DD)jpeg.dev $(DD)jpegcmyk.dev $(DD)jpeggray.dev $(DD)mgr4.dev $(DD)mgr8.dev $(DD)mgrgray2.dev $(DD)mgrgray4.dev $(DD)mgrgray8.dev $(DD)mgrmono.dev $(DD)miff24.dev $(DD)pam.dev $(DD)pamcmyk32.dev $(DD)pamcmyk4.dev $(DD)pbm.dev $(DD)pbmraw.dev $(DD)pcx16.dev $(DD)pcx24b.dev $(DD)pcx256.dev $(DD)pcxcmyk.dev $(DD)pcxgray.dev $(DD)pcxmono.dev $(DD)pdfwrite.dev $(DD)pgm.dev $(DD)pgmraw.dev $(DD)pgnm.dev $(DD)pgnmraw.dev $(DD)pkm.dev $(DD)pkmraw.dev $(DD)pksm.dev $(DD)pksmraw.dev $(DD)plan.dev $(DD)plan9bm.dev $(DD)planc.dev $(DD)plang.dev $(DD)plank.dev $(DD)planm.dev $(DD)plank.dev $(DD)plib.dev $(DD)plibc.dev $(DD)plibg.dev $(DD)plibk.dev $(DD)plibm.dev $(DD)pnm.dev $(DD)pnmraw.dev $(DD)ppm.dev $(DD)ppmraw.dev $(DD)ps2write.dev $(DD)psdcmyk.dev $(DD)psdcmykog.dev $(DD)psdf.dev $(DD)psdrgb.dev $(DD)spotcmyk.dev $(DD)txtwrite.dev $(DD)xcf.dev $(DD)psdcmyk16.dev $(DD)psdrgb16.dev
+DEVICE_DEVS1=$(DD)bit.dev $(DD)bitcmyk.dev $(DD)bitrgb.dev $(DD)bitrgbtags.dev $(DD)bmp16.dev $(DD)bmp16m.dev $(DD)bmp256.dev $(DD)bmp32b.dev $(DD)bmpgray.dev $(DD)bmpmono.dev $(DD)bmpsep1.dev $(DD)bmpsep8.dev $(DD)ccr.dev $(DD)cif.dev $(DD)devicen.dev $(DD)eps2write.dev $(DD)fpng.dev $(DD)inferno.dev $(DD)ink_cov.dev $(DD)inkcov.dev $(DD)jpeg.dev $(DD)jpegcmyk.dev $(DD)jpeggray.dev $(DD)mgr4.dev $(DD)mgr8.dev $(DD)mgrgray2.dev $(DD)mgrgray4.dev $(DD)mgrgray8.dev $(DD)mgrmono.dev $(DD)miff24.dev $(DD)pam.dev $(DD)pamcmyk32.dev $(DD)pamcmyk4.dev $(DD)pbm.dev $(DD)pbmraw.dev $(DD)pcx16.dev $(DD)pcx24b.dev $(DD)pcx256.dev $(DD)pcxcmyk.dev $(DD)pcxgray.dev $(DD)pcxmono.dev $(DD)pdfwrite.dev $(DD)pgm.dev $(DD)pgmraw.dev $(DD)pgnm.dev $(DD)pgnmraw.dev $(DD)pkm.dev $(DD)pkmraw.dev $(DD)pksm.dev $(DD)pksmraw.dev $(DD)plan.dev $(DD)plan9bm.dev $(DD)planc.dev $(DD)plang.dev $(DD)plank.dev $(DD)planm.dev $(DD)plank.dev $(DD)plib.dev $(DD)plibc.dev $(DD)plibg.dev $(DD)plibk.dev $(DD)plibm.dev $(DD)pnm.dev $(DD)pnmraw.dev $(DD)ppm.dev $(DD)ppmraw.dev $(DD)ps2write.dev $(DD)psdcmyk.dev $(DD)psdcmykog.dev $(DD)psdf.dev $(DD)psdrgb.dev $(DD)spotcmyk.dev $(DD)txtwrite.dev $(DD)xcf.dev $(DD)psdcmyk16.dev $(DD)psdrgb16.dev $(DD)psdcmyktags.dev $(DD)psdcmyktags16.dev
 DEVICE_DEVS2=$(DD)ap3250.dev $(DD)atx23.dev $(DD)atx24.dev $(DD)atx38.dev $(DD)bj10e.dev $(DD)bj200.dev $(DD)bjc600.dev $(DD)bjc800.dev $(DD)cdeskjet.dev $(DD)cdj500.dev $(DD)cdj550.dev $(DD)cdjcolor.dev $(DD)cdjmono.dev $(DD)cljet5.dev $(DD)cljet5c.dev $(DD)cljet5pr.dev $(DD)coslw2p.dev $(DD)coslwxl.dev $(DD)declj250.dev $(DD)deskjet.dev $(DD)dj505j.dev $(DD)djet500.dev $(DD)djet500c.dev $(DD)dnj650c.dev $(DD)eps9high.dev $(DD)eps9mid.dev $(DD)epson.dev $(DD)epsonc.dev $(DD)escp.dev $(DD)fs600.dev $(DD)hl7x0.dev $(DD)ibmpro.dev $(DD)imagen.dev $(DD)itk24i.dev $(DD)itk38.dev $(DD)jetp3852.dev $(DD)laserjet.dev $(DD)lbp8.dev $(DD)lips3.dev $(DD)lj250.dev $(DD)lj3100sw.dev $(DD)lj4dith.dev $(DD)lj4dithp.dev $(DD)lj5gray.dev $(DD)lj5mono.dev $(DD)ljet2p.dev $(DD)ljet3.dev $(DD)ljet3d.dev $(DD)ljet4.dev $(DD)ljet4d.dev $(DD)ljet4pjl.dev $(DD)ljetplus.dev $(DD)lp2563.dev $(DD)lp8000.dev $(DD)lq850.dev $(DD)lxm5700m.dev $(DD)m8510.dev $(DD)necp6.dev $(DD)oce9050.dev $(DD)oki182.dev $(DD)okiibm.dev $(DD)paintjet.dev $(DD)photoex.dev $(DD)picty180.dev $(DD)pj.dev $(DD)pjetxl.dev $(DD)pjxl.dev $(DD)pjxl300.dev $(DD)pxlcolor.dev $(DD)pxlmono.dev $(DD)r4081.dev $(DD)rinkj.dev $(DD)sj48.dev $(DD)st800.dev $(DD)stcolor.dev $(DD)t4693d2.dev $(DD)t4693d4.dev $(DD)t4693d8.dev $(DD)tek4696.dev $(DD)uniprint.dev
 DEVICE_DEVS3=
 DEVICE_DEVS4=$(DD)ijs.dev
 DEVICE_DEVS5=
-DEVICE_DEVS6=$(DD)png16.dev $(DD)png16m.dev $(DD)png256.dev $(DD)png48.dev $(DD)pngalpha.dev $(DD)pnggray.dev $(DD)pngmono.dev
+DEVICE_DEVS6=$(DD)png16.dev $(DD)png16m.dev $(DD)png256.dev $(DD)png48.dev $(DD)pngalpha.dev $(DD)png16malpha.dev $(DD)pnggray.dev $(DD)pngmono.dev
 DEVICE_DEVS7=
 DEVICE_DEVS8=
 DEVICE_DEVS9=
diff -pruN 9.55.0~dfsg-3/base/unixlink.mak 9.56.1~dfsg-1/base/unixlink.mak
--- 9.55.0~dfsg-3/base/unixlink.mak	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/unixlink.mak	2022-04-04 13:46:22.000000000 +0000
@@ -122,7 +122,7 @@ $(GXPS_A_XE): $(GXPS_A) $(REALMAIN_OBJ)
 
 libgpdl_tr=$(GLOBJ)libgpdl.tr
 GPDL_A=$(BINDIR)$(D)$(GPDL).a
-$(GPDL_A): $(GPDL_PSI_TOP_OBJS) $(PCL_PXL_TOP_OBJS) $(PSI_TOP_OBJ) $(XPS_TOP_OBJ) $(MAIN_OBJ) \
+$(GPDL_A): $(GPDL_PSI_TOP_OBJS) $(PCL_PXL_TOP_OBJS) $(PSI_TOP_OBJ) $(XPS_TOP_OBJ) $(PDF_TOP_OBJ) $(MAIN_OBJ) \
          $(XOBJS) $(GLOBJDIR)/pdlromfs$(COMPILE_INITS).$(OBJ) \
          $(GLOBJDIR)/pdlromfs$(COMPILE_INITS)c0.$(OBJ) \
          $(GLOBJDIR)/pdlromfs$(COMPILE_INITS)c1.$(OBJ) \
@@ -133,7 +133,7 @@ $(GPDL_A): $(GPDL_PSI_TOP_OBJS) $(PCL_PX
          $(UNIXLINK_MAK)
 	rm -f $(GPDL_A)
 	$(ECHOGS_XE) -w $(libgpdl_tr) -n - $(AR) $(ARFLAGS) $(GPDL_A)
-	$(ECHOGS_XE) -a $(libgpdl_tr) -n -s $(GPDL_PSI_TOP_OBJS) $(PCL_PXL_TOP_OBJS) $(PSI_TOP_OBJ) $(XPS_TOP_OBJ) $(XOBJS) -s
+	$(ECHOGS_XE) -a $(libgpdl_tr) -n -s $(GPDL_PSI_TOP_OBJS) $(PCL_PXL_TOP_OBJS) $(PSI_TOP_OBJ) $(XPS_TOP_OBJ) $(PDF_TOP_OBJ) $(XOBJS) -s
 	$(ECHOGS_XE) -a $(libgpdl_tr) -n -s $(GLOBJDIR)/pdlromfs$(COMPILE_INITS).$(OBJ) -s
 	$(ECHOGS_XE) -a $(libgpdl_tr) -n -s $(GLOBJDIR)/pdlromfs$(COMPILE_INITS)c0.$(OBJ) -s
 	$(ECHOGS_XE) -a $(libgpdl_tr) -n -s $(GLOBJDIR)/pdlromfs$(COMPILE_INITS)c1.$(OBJ) -s
@@ -250,15 +250,15 @@ $(GPDF_XE): $(ld_tr) $(pdf_tr) $(REALMAI
 
 gpdlldt_tr=$(PSOBJ)gpdlldt.tr
 $(GPDL_XE): $(ld_tr) $(gpdl_tr) $(INT_ARCHIVE_ALL) $(REALMAIN_OBJ) $(MAIN_OBJ) \
-             $(GPDL_PSI_TOP_OBJS) $(PCL_PXL_TOP_OBJS) $(PSI_TOP_OBJ) $(XPS_TOP_OBJ) \
-             $(XOBJS) $(GLOBJDIR)/pdlromfs$(COMPILE_INITS).$(OBJ) \
-	     $(GLOBJDIR)/pdlromfs$(COMPILE_INITS)c0.$(OBJ) \
-	     $(GLOBJDIR)/pdlromfs$(COMPILE_INITS)c1.$(OBJ) \
-	     $(GLOBJDIR)/pdlromfs$(COMPILE_INITS)c2.$(OBJ) \
-	     $(GLOBJDIR)/pdlromfs$(COMPILE_INITS)c3.$(OBJ) \
-             $(PSINT_ARCHIVE_ALL) $(UNIXLINK_MAK)
+		$(GPDL_PSI_TOP_OBJS) $(PCL_PXL_TOP_OBJS) $(PSI_TOP_OBJ) $(XPS_TOP_OBJ) $(PDF_TOP_OBJ) \
+		$(XOBJS) $(GLOBJDIR)/pdlromfs$(COMPILE_INITS).$(OBJ) \
+		$(GLOBJDIR)/pdlromfs$(COMPILE_INITS)c0.$(OBJ) \
+		$(GLOBJDIR)/pdlromfs$(COMPILE_INITS)c1.$(OBJ) \
+		$(GLOBJDIR)/pdlromfs$(COMPILE_INITS)c2.$(OBJ) \
+		$(GLOBJDIR)/pdlromfs$(COMPILE_INITS)c3.$(OBJ) \
+		$(PSINT_ARCHIVE_ALL) $(UNIXLINK_MAK)
 	$(ECHOGS_XE) -w $(gpdlldt_tr) -n - $(CCLD) $(PDL_LDFLAGS) $(XLIBDIRS) -o $(GPDL_XE)
-	$(ECHOGS_XE) -a $(gpdlldt_tr) -n -s $(GPDL_PSI_TOP_OBJS) $(PCL_PXL_TOP_OBJS) $(PSI_TOP_OBJ) $(XPS_TOP_OBJ) $(XOBJS) -s
+	$(ECHOGS_XE) -a $(gpdlldt_tr) -n -s $(GPDL_PSI_TOP_OBJS) $(PCL_PXL_TOP_OBJS) $(PDF_TOP_OBJ) $(PSI_TOP_OBJ) $(XPS_TOP_OBJ) $(XOBJS) -s
 	cat $(gpdlld_tr) >> $(gpdlldt_tr)
 	$(ECHOGS_XE) -a $(gpdlldt_tr) -n -s - $(GLOBJDIR)/pdlromfs$(COMPILE_INITS).$(OBJ)
 	$(ECHOGS_XE) -a $(gpdlldt_tr) -n -s - $(GLOBJDIR)/pdlromfs$(COMPILE_INITS)c0.$(OBJ)
diff -pruN 9.55.0~dfsg-3/base/version.mak 9.56.1~dfsg-1/base/version.mak
--- 9.55.0~dfsg-3/base/version.mak	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/version.mak	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-#  Copyright (C) 2001-2021 Artifex Software, Inc.
+#  Copyright (C) 2001-2022 Artifex Software, Inc.
 #  All Rights Reserved.
 #
 #  This software is provided AS-IS with no warranty, either express or
@@ -14,10 +14,10 @@
 
 # Major, minor and patch version numbers.
 GS_VERSION_MAJOR=9
-GS_VERSION_MINOR=55
-GS_VERSION_PATCH=0
+GS_VERSION_MINOR=56
+GS_VERSION_PATCH=1
 # Revision date: year x 10000 + month x 100 + day.
-GS_REVISIONDATE=20210927
+GS_REVISIONDATE=20220404
 # Derived values
 GS_VERSION=$(GS_VERSION_MAJOR)$(GS_VERSION_MINOR)$(GS_VERSION_PATCH)
 GS_DOT_VERSION=$(GS_VERSION_MAJOR).$(GS_VERSION_MINOR).$(GS_VERSION_PATCH)
diff -pruN 9.55.0~dfsg-3/base/winplat.mak 9.56.1~dfsg-1/base/winplat.mak
--- 9.55.0~dfsg-3/base/winplat.mak	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/winplat.mak	2022-04-04 13:46:22.000000000 +0000
@@ -67,7 +67,7 @@ $(GLD)winsync.dev : $(WINPLAT_MAK) $(ECH
 
 $(GLOBJ)gp_wsync.$(OBJ): $(GLSRC)gp_wsync.c $(AK)\
  $(dos__h) $(malloc__h) $(stdio__h) $(string__h) $(windows__h)\
- $(gp_h) $(gsmemory_h) $(gstypes_h) $(WINPLAT_MAK)
+ $(gp_h) $(gsmemory_h) $(gstypes_h) $(globals_h) $(WINPLAT_MAK)
 	$(GLCCWIN) $(GLO_)gp_wsync.$(OBJ) $(C_) $(GLSRC)gp_wsync.c
 
 # The XPS printer
diff -pruN 9.55.0~dfsg-3/base/write_t1.c 9.56.1~dfsg-1/base/write_t1.c
--- 9.55.0~dfsg-3/base/write_t1.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/write_t1.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -659,7 +659,7 @@ write_main_dictionary(gs_fapi_font * a_f
 
             entries++;
 
-        gs_sprintf(Buffer, "/FontInfo %d dict dup begin\n", entries);
+        gs_snprintf(Buffer, sizeof(Buffer), "/FontInfo %d dict dup begin\n", entries);
         WRF_wstring(a_fapi_font->memory, a_output, Buffer);
         code = a_fapi_font->get_word(a_fapi_font,
                                   gs_fapi_font_feature_BlendAxisTypes_count,
@@ -704,7 +704,7 @@ write_main_dictionary(gs_fapi_font * a_f
                     if (code < 0)
                         return code;
 
-                    gs_sprintf(Buffer, "%f ", x1);
+                    gs_snprintf(Buffer, sizeof(Buffer), "%f ", x1);
                     WRF_wstring(a_fapi_font->memory, a_output, Buffer);
                 }
                 WRF_wstring(a_fapi_font->memory, a_output, "]");
@@ -734,14 +734,14 @@ write_main_dictionary(gs_fapi_font * a_f
                     if (code < 0)
                         return code;
 
-                    gs_sprintf(Buffer, "%f ", x1);
+                    gs_snprintf(Buffer, sizeof(Buffer), "%f ", x1);
                     WRF_wstring(a_fapi_font->memory, a_output, Buffer);
                     code = a_fapi_font->get_float(a_fapi_font,
                                                 gs_fapi_font_feature_BlendDesignPositionsArrayValue,
                                                 i * 64 + j * 64 + 1, &x1);
                     if (code < 0)
                         return code;
-                    gs_sprintf(Buffer, "%f ", x1);
+                    gs_snprintf(Buffer, sizeof(Buffer), "%f ", x1);
                     WRF_wstring(a_fapi_font->memory, a_output, Buffer);
                     WRF_wstring(a_fapi_font->memory, a_output, "]");
                 }
@@ -791,7 +791,7 @@ write_main_dictionary(gs_fapi_font * a_f
             if (code < 0)
                 return code;
 
-            gs_sprintf(Buffer, "%f ", x1);
+            gs_snprintf(Buffer, sizeof(Buffer), "%f ", x1);
             WRF_wstring(a_fapi_font->memory, a_output, Buffer);
         }
         WRF_wstring(a_fapi_font->memory, a_output, "] def\n");
@@ -821,6 +821,7 @@ write_main_dictionary(gs_fapi_font * a_f
         WRF_wstring(a_fapi_font->memory, a_output, " } def\n");
         WRF_wstring(a_fapi_font->memory, a_output, "/Private 14 dict def\n");
         WRF_wstring(a_fapi_font->memory, a_output, "end def\n");
+#undef TEMP_BUF_LEN
     }
     WRF_wstring(a_fapi_font->memory, a_output, "currentdict end\ncurrentfile eexec\n");
 
diff -pruN 9.55.0~dfsg-3/base/write_t2.c 9.56.1~dfsg-1/base/write_t2.c
--- 9.55.0~dfsg-3/base/write_t2.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/base/write_t2.c	2022-04-04 13:46:22.000000000 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2021 Artifex Software, Inc.
+/* Copyright (C) 2001-2022 Artifex Software, Inc.
    All Rights Reserved.
 
    This software is provided AS-IS with no warranty, either express or
@@ -70,7 +70,7 @@ write_type2_float(gs_fapi_font * a_fapi_
     int high = true;
     char c = 0;
 
-    gs_sprintf(buffer, "%f", a_float);
+    gs_snprintf(buffer, sizeof(buffer), "%f", a_float);
     WRF_wbyte(a_fapi_font->memory, a_output, 30);
     for (;;) {
         char n = 0;
diff -pruN 9.55.0~dfsg-3/configure 9.56.1~dfsg-1/configure
--- 9.55.0~dfsg-3/configure	2021-09-27 07:44:02.000000000 +0000
+++ 9.56.1~dfsg-1/configure	2022-04-04 13:48:48.000000000 +0000
@@ -782,6 +782,7 @@ LCUPSINCLUDE
 LCUPSBUILDTYPE
 SHARELCUPSI
 SHARELCUPS
+LIB_CUPS_DIR
 CUPS_DIR
 CUPSINSTALL
 CUPSDATA
@@ -938,7 +939,6 @@ enable_contrib
 with_arch_h
 with_sanitizer
 enable_sse2
-enable_threadsafe
 with_large_color_index
 enable_threading
 with_tesseract
@@ -1630,8 +1630,6 @@ Optional Features:
 
   --disable-contrib       Do not include contributed drivers
   --disable-sse2          Do not use sse2 instrinsics
-  --enable-threadsafe     enable a thread safe build - NOTE: this will exclude
-                          non-threadsafe devices
   --disable-threading     disable support for multithreaded rendering
   --disable-fontconfig    Do not use fontconfig to list system fonts
   --disable-dbus          Do not use dbus to communicate with external
@@ -3089,17 +3087,6 @@ ARCH_AUTOCONF_HEADER=arch-config/arch_au
 ARCH_AUTOCONF_HEADER_PROTO=arch/arch_autoconf.h.in
 
 
-NTS_DEVS='opvp oprp'
-NTS_DEVS="$NTS_DEVS uniprint"
-NTS_DEVS="$NTS_DEVS x11 x11alpha"
-NTS_DEVS="$NTS_DEVS pcl3 hpdjplus hpdjportable hpdj310 hpdj320 hpdj340 hpdj400 hpdj500 hpdj500c hpdj510 hpdj520 hpdj540 hpdj550c hpdj560c hpdj600 hpdj660c hpdj670c hpdj680c hpdj690c hpdj850c hpdj855c hpdj870c hpdj890c hpdj1120c"
-
-# We use this to store a list of devices we have excluded
-# and emit a warning on completion.
-NTS_EXCLUDES=""
-
-
-
 
 
 
@@ -4648,21 +4635,21 @@ CFLAGS_LARGEFILE=""
 
 case $host in
         *-linux*|*-gnu)
-        if test $GCC = yes; then
+        if test x"$GCC" = x"yes"; then
             CC_OPT_FLAGS_TO_TRY="-O2 -DNDEBUG"
             CC_DBG_FLAGS_TO_TRY="-gdwarf-2 -g3 -O0"
             CC_VISIBILITY_FLAGS_TO_TRY="-fvisibility=hidden"
         fi
         ;;
         *bsd*)
-        if test $GCC = yes; then
+        if test x"$GCC" = x"yes"; then
             CC_OPT_FLAGS_TO_TRY="-O2 -DNDEBUG"
             CC_DBG_FLAGS_TO_TRY="-gdwarf-2 -g3 -O0"
             CC_VISIBILITY_FLAGS_TO_TRY="-fvisibility=hidden"
         fi
         ;;
         *-darwin*)
-        if test $GCC = yes; then
+        if test x"$GCC" = x"yes"; then
             CC_OPT_FLAGS_TO_TRY="-O2 -DNDEBUG"
             CC_DBG_FLAGS_TO_TRY="-gdwarf-2 -g3 -O0"
             CC_VISIBILITY_FLAGS_TO_TRY="-fvisibility=hidden"
@@ -4670,7 +4657,7 @@ case $host in
         SET_DT_SONAME=""
         ;;
         *-mingw*|*-msys*)
-        if test $GCC = yes; then
+        if test x"$GCC" = x"yes"; then
             CC_OPT_FLAGS_TO_TRY="-O2 -DNDEBUG"
             CC_DBG_FLAGS_TO_TRY="-gdwarf-2 -g3 -O0"
             CC_VISIBILITY_FLAGS_TO_TRY="-fvisibility=hidden"
@@ -4684,7 +4671,7 @@ case $host in
         if test $GCC = no; then
             SET_DT_SONAME="-h "
         fi
-        if test $GCC = yes; then
+        if test x"$GCC" = x"yes"; then
             CC_OPT_FLAGS_TO_TRY="-O2 -DNDEBUG"
             CC_DBG_FLAGS_TO_TRY="-gdwarf-2 -g3 -O0"
             CC_VISIBILITY_FLAGS_TO_TRY="-fvisibility=hidden"
@@ -4693,7 +4680,7 @@ case $host in
         fi
         ;;
         *-aix*)
-        if test $GCC = yes; then
+        if test x"$GCC" = x"yes"; then
             CC_OPT_FLAGS_TO_TRY="-O2 -DNDEBUG"
             CC_DBG_FLAGS_TO_TRY="-gdwarf-2 -g3 -O0"
             SET_DT_SONAME="so"
@@ -4705,7 +4692,7 @@ esac
 
 
 
-if test $GCC = yes; then
+if test x"$GCC" = x"yes"; then
     cflags_to_try="-Wall -Wstrict-prototypes -Wundef \
 -Wmissing-declarations -Wmissing-prototypes -Wwrite-strings \
 -fno-strict-aliasing -Werror=declaration-after-statement \
@@ -5533,15 +5520,6 @@ fi
 CFLAGS=$save_cflags
 
 
-# Check whether --enable-threadsafe was given.
-if test "${enable_threadsafe+set}" = set; then :
-  enableval=$enable_threadsafe;
-fi
-
-if test x$enable_threadsafe = xyes ; then
-    CFLAGS="-DGS_THREADSAFE"
-fi
-
 
 ac_header_dirent=no
 for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
@@ -8352,14 +8330,14 @@ $as_echo "yes" >&6; }
                 # Building on PPC with gcc, disable libpng's PPC specific
                 # optimizations: we don't include the source file for it
                 # and it only applies to reading, which we don't use.
-                if test $GCC = yes; then
+                if test x"$GCC" = x"yes"; then
                   CFLAGS="$CFLAGS -DPNG_POWERPC_VSX_OPT=0"
                 fi
             ;;
             *arm64*|*aarch64*)
                 # Building on arm64 with gcc, disable libpng's neon
                 # optimizations.
-                if test $GCC = yes; then
+                if test x"$GCC" = x"yes"; then
                   CFLAGS="$CFLAGS -DPNG_ARM_NEON_OPT=0"
                 fi
             ;;
@@ -8998,6 +8976,7 @@ CUPSDATA=""
 CUPSVERSION="0"
 CUPSPDFTORASTER="0"
 CUPS_DIR=""
+LIB_CUPS_DIR="src"
 
 cups_hard_fail=0
 
@@ -9242,6 +9221,13 @@ done
                   CUPSDEV="$CUPSDEV pwgraster"
                 fi
 
+                # appleraster support arrived in cups 2.2.2, so with
+                # API version 2.2 we do not necessarily have it, but
+                # 2.3 has it for sure
+                if test "$CUPSAPIVERSION" ">" "2.2" ; then
+                  CUPSDEV="$CUPSDEV appleraster urf"
+                fi
+
                 CUPSVERSION="`$CUPSCONFIG --version`"
 
                 LCUPSINCLUDE="include \$(GLSRCDIR)/lcups.mak"
@@ -9309,6 +9295,7 @@ done
             LCUPSINCLUDE="include \$(GLSRCDIR)/lcups.mak"
             LCUPSIINCLUDE="include \$(GLSRCDIR)/lcupsi.mak"
             CUPSDEV="cups pwgraster"
+            LIB_CUPS_DIR=$CUPS_DIR
         fi
     fi
 fi
@@ -9334,6 +9321,7 @@ fi
 
 
 
+
 # Check whether --with-ijs was given.
 if test "${with_ijs+set}" = set; then :
   withval=$with_ijs;
@@ -9648,7 +9636,7 @@ done
 JPX_SSE_CFLAGS=""
 
 if test "x$HAVE_SSE2" = "x" ; then
-  if test $GCC = yes; then
+  if test x"$GCC" = x"yes"; then
     JPX_SSE_CFLAGS="-U__SSE__"
   fi
 fi
@@ -11159,7 +11147,7 @@ JPEG_DEVS='jpeg jpeggray jpegcmyk'
 
 PCX_DEVS='pcxmono pcxgray pcx16 pcx256 pcx24b pcxcmyk'
 PBM_DEVS='pbm pbmraw pgm pgmraw pgnm pgnmraw pnm pnmraw ppm ppmraw pkm pkmraw pksm pksmraw pam pamcmyk4 pamcmyk32 plan plang planm planc plank'
-PS_DEVS='psdf psdcmyk psdrgb psdcmyk16 psdrgb16 pdfwrite ps2write eps2write bbox txtwrite inkcov ink_cov psdcmykog fpng pdfimage8 pdfimage24 pdfimage32 PCLm'
+PS_DEVS='psdf psdcmyk psdrgb psdcmyk16 psdrgb16 psdcmyktags psdcmyktags16 pdfwrite ps2write eps2write bbox txtwrite inkcov ink_cov psdcmykog fpng pdfimage8 pdfimage24 pdfimage32 PCLm PCLm8'
 
 # Handle --with-extract-dir=EXTRACT_DIR - build extract library and docxwrite
 # device.
@@ -11348,51 +11336,6 @@ $as_echo "$as_me: WARNING: Unable to inc
         esac
 done
 
-if test x$enable_threadsafe = xyes; then
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$P_DEVS0{@:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    P_DEVS0=`echo $P_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$F_DEVS0{@:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    F_DEVS0=`echo $F_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$CUPS_DEVS0{@:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    CUPS_DEVS0=`echo $CUPS_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$XPS_DEVS0{@:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    XPS_DEVS0=`echo $XPS_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$IJS_DEVS0{@:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    IJS_DEVS0=`echo $IJS_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$PNG_DEVS0{@:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    PNG_DEVS0=`echo $PNG_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$X11_DEVS0{@:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    X11_DEVS0=`echo $X11_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$JBIG2_DEVS{@:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    JBIG2_DEVS=`echo $JBIG2_DEVS | sed "s/\b$ntsdev\b//g"`
-  done
-
-  NTS_EXCLUDES=`echo "$NTS_EXCLUDES" | tr " " "\n" | sort | uniq | tr "\n" " "`
-fi # x$enable_threadsafe = xyes
-
 noncontribmakefiles=`find $srcdir -name '*.mak' -print | grep -v '/contrib/'`
 
 # No need to include opvp/oprp driver without iconv/libiconv.
@@ -11496,7 +11439,7 @@ if test "${enable_hidden_visibility+set}
 fi
 
 
-if test x$hide_symbols = xyes -a $GCC = yes; then
+if test x$hide_symbols = xyes -a x"$GCC" = x"yes"; then
     attr_default="__attribute__((visibility(\\\"default\\\")))"
     attr_hidden="__attribute__((visibility(\\\"hidden\\\")))"
 fi
@@ -11509,7 +11452,7 @@ case $host in
       XPS_DYNAMIC_LDFLAGS="-shared -Wl,\$(LD_SET_DT_SONAME)\$(LDFLAGS_SO_PREFIX)\$(XPS_SONAME_MAJOR)"
       PDL_DYNAMIC_LDFLAGS="-shared -Wl,\$(LD_SET_DT_SONAME)\$(LDFLAGS_SO_PREFIX)\$(GPDL_SONAME_MAJOR)"
       PDF_DYNAMIC_LDFLAGS="-shared -Wl,\$(LD_SET_DT_SONAME)\$(LDFLAGS_SO_PREFIX)\$(PDF_SONAME_MAJOR)"
-      if test $GCC = yes; then
+      if test x"$GCC" = x"yes"; then
         # GCC high level flag
         DYNAMIC_LIBS="-rdynamic"
       else
@@ -11596,7 +11539,7 @@ case $host in
       SO_LIB_EXT=".dylib"
     ;;
     *-sun*|*-solaris*)
-      if test $GCC = yes; then
+      if test x"$GCC" = x"yes"; then
         DYNAMIC_CFLAGS="-fPIC $DYNAMIC_CFLAGS"
       else
         DYNAMIC_CFLAGS="-KPIC $DYNAMIC_CFLAGS"
@@ -11616,7 +11559,7 @@ case $host in
       SO_LIB_EXT=".so"
     ;;
     *-aix*)
-      if test $GCC = yes; then
+      if test x"$GCC" = x"yes"; then
         DYNAMIC_CFLAGS="-fPIC $DYNAMIC_CFLAGS"
         GCFLAGS="-Wl,-brtl -D_LARGE_FILES $GCFLAGS"
         GS_DYNAMIC_LDFLAGS="-shared -Wl,-brtl,-G -fPIC"
@@ -11638,7 +11581,7 @@ case $host in
 esac
 
 if test x$hide_symbols = xyes ; then
-  if test $GCC = yes; then
+  if test x"$GCC" = x"yes"; then
     DYNAMIC_CFLAGS="$DYNAMIC_CFLAGS -fvisibility=hidden"
   fi
   DYNAMIC_CFLAGS="$DYNAMIC_CFLAGS -DGSDLLEXPORT=\"$attr_default\""
@@ -12797,7 +12740,7 @@ fi
 # NOTE: Strict aliasing can cause some parts
 #       of Ghostscript to malfunction.
 # --------------------------------------------------
-if test $GCC = yes; then
+if test x"$GCC" = x"yes"; then
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to explicitly disable strict aliasing" >&5
 $as_echo_n "checking whether to explicitly disable strict aliasing... " >&6; }
   CFLAGS_backup="$CFLAGS"
@@ -13628,12 +13571,6 @@ $as_echo "$as_me: WARNING: Using \"nativ
 $as_echo "$as_me: WARNING: Support for this will be removed in a future release" >&2;}
 fi
 
-if test "x$NTS_EXCLUDES" != "x" ; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Due to the --enable-threadsafe option, the following devices have been excluded because they are not threadsafe:" >&5
-$as_echo "$as_me: WARNING: Due to the --enable-threadsafe option, the following devices have been excluded because they are not threadsafe:" >&2;}
-   echo "$NTS_EXCLUDES"
-fi
-
 if test x"$OCR_DEVS_WARNING_LINE1" != x"" ; then
     echo ""
     { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $OCR_DEVS_WARNING_LINE1" >&5
diff -pruN 9.55.0~dfsg-3/configure.ac 9.56.1~dfsg-1/configure.ac
--- 9.55.0~dfsg-3/configure.ac	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/configure.ac	2022-04-04 13:46:22.000000000 +0000
@@ -49,21 +49,6 @@ ARCH_AUTOCONF_HEADER=arch-config/arch_au
 ARCH_AUTOCONF_HEADER_PROTO=arch/arch_autoconf.h.in
 
 dnl --------------------------------------------------
-dnl List of non-threadsafe devices
-dnl defined at the top of the file for ease of access
-dnl --------------------------------------------------
-
-NTS_DEVS='opvp oprp'
-NTS_DEVS="$NTS_DEVS uniprint"
-NTS_DEVS="$NTS_DEVS x11 x11alpha"
-NTS_DEVS="$NTS_DEVS pcl3 hpdjplus hpdjportable hpdj310 hpdj320 hpdj340 hpdj400 hpdj500 hpdj500c hpdj510 hpdj520 hpdj540 hpdj550c hpdj560c hpdj600 hpdj660c hpdj670c hpdj680c hpdj690c hpdj850c hpdj855c hpdj870c hpdj890c hpdj1120c"
-
-# We use this to store a list of devices we have excluded
-# and emit a warning on completion.
-NTS_EXCLUDES=""
-
-
-dnl --------------------------------------------------
 dnl Local utilities
 dnl --------------------------------------------------
 
@@ -285,21 +270,21 @@ CFLAGS_LARGEFILE=""
 
 case $host in
         *-linux*|*-gnu)
-        if test $GCC = yes; then
+        if test x"$GCC" = x"yes"; then
             CC_OPT_FLAGS_TO_TRY="-O2 -DNDEBUG"
             CC_DBG_FLAGS_TO_TRY="-gdwarf-2 -g3 -O0"
             CC_VISIBILITY_FLAGS_TO_TRY="-fvisibility=hidden"
         fi
         ;;
         *bsd*)
-        if test $GCC = yes; then
+        if test x"$GCC" = x"yes"; then
             CC_OPT_FLAGS_TO_TRY="-O2 -DNDEBUG"
             CC_DBG_FLAGS_TO_TRY="-gdwarf-2 -g3 -O0"
             CC_VISIBILITY_FLAGS_TO_TRY="-fvisibility=hidden"
         fi
         ;;
         *-darwin*)
-        if test $GCC = yes; then
+        if test x"$GCC" = x"yes"; then
             CC_OPT_FLAGS_TO_TRY="-O2 -DNDEBUG"
             CC_DBG_FLAGS_TO_TRY="-gdwarf-2 -g3 -O0"
             CC_VISIBILITY_FLAGS_TO_TRY="-fvisibility=hidden"
@@ -307,7 +292,7 @@ case $host in
         SET_DT_SONAME=""
         ;;
         *-mingw*|*-msys*)
-        if test $GCC = yes; then
+        if test x"$GCC" = x"yes"; then
             CC_OPT_FLAGS_TO_TRY="-O2 -DNDEBUG"
             CC_DBG_FLAGS_TO_TRY="-gdwarf-2 -g3 -O0"
             CC_VISIBILITY_FLAGS_TO_TRY="-fvisibility=hidden"
@@ -321,7 +306,7 @@ case $host in
         if test $GCC = no; then
             SET_DT_SONAME="-h "
         fi
-        if test $GCC = yes; then
+        if test x"$GCC" = x"yes"; then
             CC_OPT_FLAGS_TO_TRY="-O2 -DNDEBUG"
             CC_DBG_FLAGS_TO_TRY="-gdwarf-2 -g3 -O0"
             CC_VISIBILITY_FLAGS_TO_TRY="-fvisibility=hidden"
@@ -330,7 +315,7 @@ case $host in
         fi
         ;;
         *-aix*)
-        if test $GCC = yes; then
+        if test x"$GCC" = x"yes"; then
             CC_OPT_FLAGS_TO_TRY="-O2 -DNDEBUG"
             CC_DBG_FLAGS_TO_TRY="-gdwarf-2 -g3 -O0"
             SET_DT_SONAME="so"
@@ -342,7 +327,7 @@ esac
 AC_SUBST(SET_DT_SONAME)
 
 
-if test $GCC = yes; then
+if test x"$GCC" = x"yes"; then
     cflags_to_try="-Wall -Wstrict-prototypes -Wundef \
 -Wmissing-declarations -Wmissing-prototypes -Wwrite-strings \
 -fno-strict-aliasing -Werror=declaration-after-statement \
@@ -563,15 +548,6 @@ CFLAGS=$save_cflags
 
 
 dnl --------------------------------------------------
-dnl Enable thread safe build
-dnl --------------------------------------------------
-AC_ARG_ENABLE([threadsafe], AS_HELP_STRING([--enable-threadsafe],
-    [enable a thread safe build - NOTE: this will exclude non-threadsafe devices]))
-if test x$enable_threadsafe = xyes ; then
-    CFLAGS="-DGS_THREADSAFE"
-fi
-
-dnl --------------------------------------------------
 dnl Check for headers
 dnl --------------------------------------------------
 
@@ -1476,14 +1452,14 @@ else
                 # Building on PPC with gcc, disable libpng's PPC specific
                 # optimizations: we don't include the source file for it
                 # and it only applies to reading, which we don't use.
-                if test $GCC = yes; then
+                if test x"$GCC" = x"yes"; then
                   CFLAGS="$CFLAGS -DPNG_POWERPC_VSX_OPT=0"
                 fi
             ;;
             *arm64*|*aarch64*)
                 # Building on arm64 with gcc, disable libpng's neon
                 # optimizations.
-                if test $GCC = yes; then
+                if test x"$GCC" = x"yes"; then
                   CFLAGS="$CFLAGS -DPNG_ARM_NEON_OPT=0"
                 fi
             ;;
@@ -1787,6 +1763,7 @@ CUPSDATA=""
 CUPSVERSION="0"
 CUPSPDFTORASTER="0"
 CUPS_DIR=""
+LIB_CUPS_DIR="src"
 
 cups_hard_fail=0
 
@@ -1866,6 +1843,13 @@ if ( test -f $srcdir/cups/gdevcups.c );
                   CUPSDEV="$CUPSDEV pwgraster"
                 fi
 
+                # appleraster support arrived in cups 2.2.2, so with
+                # API version 2.2 we do not necessarily have it, but
+                # 2.3 has it for sure
+                if test "$CUPSAPIVERSION" ">" "2.2" ; then
+                  CUPSDEV="$CUPSDEV appleraster urf"
+                fi
+
                 CUPSVERSION="`$CUPSCONFIG --version`"
 
                 LCUPSINCLUDE="include \$(GLSRCDIR)/lcups.mak"
@@ -1892,6 +1876,7 @@ if ( test -f $srcdir/cups/gdevcups.c );
             LCUPSINCLUDE="include \$(GLSRCDIR)/lcups.mak"
             LCUPSIINCLUDE="include \$(GLSRCDIR)/lcupsi.mak"
             CUPSDEV="cups pwgraster"
+            LIB_CUPS_DIR=$CUPS_DIR
         fi
     fi
 fi
@@ -1906,6 +1891,7 @@ AC_SUBST(CUPSSERVERROOT)
 AC_SUBST(CUPSDATA)
 AC_SUBST(CUPSINSTALL)
 AC_SUBST(CUPS_DIR)
+AC_SUBST(LIB_CUPS_DIR)
 
 AC_SUBST(SHARELCUPS)
 AC_SUBST(SHARELCUPSI)
@@ -2094,7 +2080,7 @@ AC_CHECK_FUNCS([fseeko], [CFLAGS_OPJ_HAV
 JPX_SSE_CFLAGS=""
 
 if test "x$HAVE_SSE2" = "x" ; then
-  if test $GCC = yes; then
+  if test x"$GCC" = x"yes"; then
     JPX_SSE_CFLAGS="-U__SSE__"
   fi
 fi
@@ -2632,7 +2618,7 @@ JPEG_DEVS='jpeg jpeggray jpegcmyk'
 
 PCX_DEVS='pcxmono pcxgray pcx16 pcx256 pcx24b pcxcmyk'
 PBM_DEVS='pbm pbmraw pgm pgmraw pgnm pgnmraw pnm pnmraw ppm ppmraw pkm pkmraw pksm pksmraw pam pamcmyk4 pamcmyk32 plan plang planm planc plank'
-PS_DEVS='psdf psdcmyk psdrgb psdcmyk16 psdrgb16 pdfwrite ps2write eps2write bbox txtwrite inkcov ink_cov psdcmykog fpng pdfimage8 pdfimage24 pdfimage32 PCLm'
+PS_DEVS='psdf psdcmyk psdrgb psdcmyk16 psdrgb16 psdcmyktags psdcmyktags16 pdfwrite ps2write eps2write bbox txtwrite inkcov ink_cov psdcmykog fpng pdfimage8 pdfimage24 pdfimage32 PCLm PCLm8'
 
 # Handle --with-extract-dir=EXTRACT_DIR - build extract library and docxwrite
 # device.
@@ -2805,51 +2791,6 @@ while test -n "$drivers"; do
         esac
 done
 
-if test x$enable_threadsafe = xyes; then
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$P_DEVS0{[@]:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    P_DEVS0=`echo $P_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$F_DEVS0{[@]:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    F_DEVS0=`echo $F_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$CUPS_DEVS0{[@]:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    CUPS_DEVS0=`echo $CUPS_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$XPS_DEVS0{[@]:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    XPS_DEVS0=`echo $XPS_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$IJS_DEVS0{[@]:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    IJS_DEVS0=`echo $IJS_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$PNG_DEVS0{[@]:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    PNG_DEVS0=`echo $PNG_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$X11_DEVS0{[@]:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    X11_DEVS0=`echo $X11_DEVS0 | sed "s/\b$ntsdev\b//g"`
-  done
-
-  for ntsdev in $NTS_DEVS ; do
-    NTS_EXCLUDES="$(echo \"$JBIG2_DEVS{[@]:0}\" | grep -o $ntsdev) $NTS_EXCLUDES"
-    JBIG2_DEVS=`echo $JBIG2_DEVS | sed "s/\b$ntsdev\b//g"`
-  done
-
-  NTS_EXCLUDES=`echo "$NTS_EXCLUDES" | tr " " "\n" | sort | uniq | tr "\n" " "`
-fi # x$enable_threadsafe = xyes
-
 noncontribmakefiles=`find $srcdir -name '*.mak' -print | grep -v '/contrib/'`
 
 # No need to include opvp/oprp driver without iconv/libiconv.
@@ -2952,7 +2893,7 @@ AC_ARG_ENABLE([hidden-visibility],
         [hide all shared library symbols which are not part of its public API]),
     [hide_symbols=yes])
 
-if test x$hide_symbols = xyes -a $GCC = yes; then
+if test x$hide_symbols = xyes -a x"$GCC" = x"yes"; then
     attr_default="__attribute__((visibility(\\\"default\\\")))"
     attr_hidden="__attribute__((visibility(\\\"hidden\\\")))"
 fi
@@ -2965,7 +2906,7 @@ case $host in
       XPS_DYNAMIC_LDFLAGS="-shared -Wl,\$(LD_SET_DT_SONAME)\$(LDFLAGS_SO_PREFIX)\$(XPS_SONAME_MAJOR)"
       PDL_DYNAMIC_LDFLAGS="-shared -Wl,\$(LD_SET_DT_SONAME)\$(LDFLAGS_SO_PREFIX)\$(GPDL_SONAME_MAJOR)"
       PDF_DYNAMIC_LDFLAGS="-shared -Wl,\$(LD_SET_DT_SONAME)\$(LDFLAGS_SO_PREFIX)\$(PDF_SONAME_MAJOR)"
-      if test $GCC = yes; then
+      if test x"$GCC" = x"yes"; then
         # GCC high level flag
         DYNAMIC_LIBS="-rdynamic"
       else
@@ -3052,7 +2993,7 @@ case $host in
       SO_LIB_EXT=".dylib"
     ;;
     *-sun*|*-solaris*)
-      if test $GCC = yes; then
+      if test x"$GCC" = x"yes"; then
         DYNAMIC_CFLAGS="-fPIC $DYNAMIC_CFLAGS"
       else
         DYNAMIC_CFLAGS="-KPIC $DYNAMIC_CFLAGS"
@@ -3072,7 +3013,7 @@ case $host in
       SO_LIB_EXT=".so"
     ;;
     *-aix*)
-      if test $GCC = yes; then
+      if test x"$GCC" = x"yes"; then
         DYNAMIC_CFLAGS="-fPIC $DYNAMIC_CFLAGS"
         GCFLAGS="-Wl,-brtl -D_LARGE_FILES $GCFLAGS"
         GS_DYNAMIC_LDFLAGS="-shared -Wl,-brtl,-G -fPIC"
@@ -3094,7 +3035,7 @@ case $host in
 esac
 
 if test x$hide_symbols = xyes ; then
-  if test $GCC = yes; then
+  if test x"$GCC" = x"yes"; then
     DYNAMIC_CFLAGS="$DYNAMIC_CFLAGS -fvisibility=hidden"
   fi
   DYNAMIC_CFLAGS="$DYNAMIC_CFLAGS -DGSDLLEXPORT=\"$attr_default\""
@@ -3559,7 +3500,7 @@ AC_SUBST(VERSIONED_PATH)
 # NOTE: Strict aliasing can cause some parts
 #       of Ghostscript to malfunction.
 # --------------------------------------------------
-if test $GCC = yes; then
+if test x"$GCC" = x"yes"; then
   AC_MSG_CHECKING([whether to explicitly disable strict aliasing])
   CFLAGS_backup="$CFLAGS"
   CFLAGSAUX_backup="$CFLAGSAUX"
@@ -3762,11 +3703,6 @@ if test "x$AFS" = "x1"; then
    AC_MSG_WARN([Support for this will be removed in a future release])
 fi
 
-if test "x$NTS_EXCLUDES" != "x" ; then
-   AC_MSG_WARN([Due to the --enable-threadsafe option, the following devices have been excluded because they are not threadsafe:])
-   echo "$NTS_EXCLUDES"
-fi
-
 if test x"$OCR_DEVS_WARNING_LINE1" != x"" ; then
     echo ""
     AC_MSG_WARN([$OCR_DEVS_WARNING_LINE1])
diff -pruN 9.55.0~dfsg-3/contrib/eplaser/gdevescv.c 9.56.1~dfsg-1/contrib/eplaser/gdevescv.c
--- 9.55.0~dfsg-3/contrib/eplaser/gdevescv.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/eplaser/gdevescv.c	2022-04-04 13:46:22.000000000 +0000
@@ -473,7 +473,7 @@ escv_vector_dopath(gx_device_vector * vd
       y = fixed2float(vs[1]) / scale.y;
 
       /* ¥µ¥Ö¥Ñ¥¹³«»ÏÌ¿Îá p1 */
-      (void)gs_sprintf(obuf, ESC_GS "0;%d;%dmvpG", (int)x, (int)y);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "0;%d;%dmvpG", (int)x, (int)y);
       lputs(s, obuf);
 
       if (first)
@@ -484,13 +484,13 @@ escv_vector_dopath(gx_device_vector * vd
       cnt = 1;
       for (pseg = cenum.pseg; pseg != 0 && pseg->type == s_line; cnt++, pseg = pseg->next);
 
-      (void)gs_sprintf(obuf, ESC_GS "0;%d", cnt);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "0;%d", cnt);
       lputs(s, obuf);
 
       do {
-        (void)gs_sprintf(obuf, ";%d;%d",
-                         (int)(fixed2float(vs[0]) / scale.x),
-                         (int)(fixed2float(vs[1]) / scale.y));
+        (void)gs_snprintf(obuf, sizeof(obuf), ";%d;%d",
+                          (int)(fixed2float(vs[0]) / scale.x),
+                          (int)(fixed2float(vs[1]) / scale.y));
         lputs(s, obuf);
 
         pe_op = gx_path_enum_next(&cenum, (gs_fixed_point *) vs);
@@ -505,11 +505,11 @@ escv_vector_dopath(gx_device_vector * vd
     case gs_pe_curveto:
       cnt = 1;
       for (pseg = cenum.pseg; pseg != 0 && pseg->type == s_curve; cnt++, pseg = pseg->next);
-      (void)gs_sprintf(obuf, ESC_GS "0;%d", cnt * 3);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "0;%d", cnt * 3);
       lputs(s, obuf);
 
       do {
-        (void)gs_sprintf(obuf, ";%d;%d;%d;%d;%d;%d",
+        (void)gs_snprintf(obuf, sizeof(obuf), ";%d;%d;%d;%d;%d;%d",
                     (int)(fixed2float(vs[0]) / scale.x), (int)(fixed2float(vs[1]) / scale.y),
                     (int)(fixed2float(vs[2]) / scale.x), (int)(fixed2float(vs[3]) / scale.y),
                     (int)(fixed2float(vs[4]) / scale.x), (int)(fixed2float(vs[5]) / scale.y));
@@ -564,7 +564,7 @@ escv_vector_dorect(gx_device_vector * vd
 
   scale = vdev->scale;
 
-  (void)gs_sprintf(obuf, ESC_GS "0;%d;%d;%d;%d;0;0rrpG",
+  (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "0;%d;%d;%d;%d;0;0rrpG",
                 (int)(fixed2float(x0) / scale.x),
                 (int)(fixed2float(y0) / scale.y),
                 (int)(fixed2float(x1) / scale.x),
@@ -1057,7 +1057,7 @@ escv_beginpage(gx_device_vector * vdev)
           lputs(s, " PU=15");
         }
       } else if (pdev->cassetFeed) {
-        (void)gs_sprintf(ebuf, " PU=%d", pdev->cassetFeed);
+        (void)gs_snprintf(ebuf, sizeof(ebuf), " PU=%d", pdev->cassetFeed);
         lputs(s, ebuf);
       } else {
         lputs(s, " PU=AU");
@@ -1087,14 +1087,14 @@ escv_beginpage(gx_device_vector * vdev)
 
       /* lp8000c not have QT */
       if (strcmp(pdev->dname, "lp8000c") == 0) {
-        (void)gs_sprintf(ebuf, " QT=1 CO=%d", pdev->NumCopies);
+        (void)gs_snprintf(ebuf, sizeof(ebuf), " QT=1 CO=%d", pdev->NumCopies);
       } else {
         if (pdev->Collate) {
           /* CO is 1, when set QT */
-          (void)gs_sprintf(ebuf, " QT=%d CO=1", pdev->NumCopies);
+          (void)gs_snprintf(ebuf, sizeof(ebuf), " QT=%d CO=1", pdev->NumCopies);
         } else {
           /* QT is 1, when not specified QT */
-          (void)gs_sprintf(ebuf, " QT=1 CO=%d", pdev->NumCopies);
+          (void)gs_snprintf(ebuf, sizeof(ebuf), " QT=1 CO=%d", pdev->NumCopies);
         }
       }
       lputs(s, ebuf);
@@ -1103,7 +1103,7 @@ escv_beginpage(gx_device_vector * vdev)
     }
 
     if (pdev->toner_density) {
-      (void)gs_sprintf(ebuf, " DL=%d", pdev->toner_density);
+      (void)gs_snprintf(ebuf, sizeof(ebuf), " DL=%d", pdev->toner_density);
       lputs(s, ebuf);
     }
 
@@ -1248,7 +1248,7 @@ escv_setlinewidth(gx_device_vector * vde
   /* ESC/Page ¤Ç¤ÏÀþÉý¡¿½ªÃ¼¡¿ÀÜ¹çÉô¤ÎÀßÄê¤Ï£±¤Ä¤Î¥³¥Þ¥ó¥É¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤¿¤áÊÝ»ý¤·¤Æ¤ª¤¯¡£ */
   pdev -> lwidth = width;
 
-  (void)gs_sprintf(obuf, ESC_GS "%d;%d;%dlwG",
+  (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "%d;%d;%dlwG",
                 (int)(pdev -> lwidth),
                 (int)(pdev -> cap),
                 (int)(pdev -> join));
@@ -1269,7 +1269,7 @@ escv_setlinecap(gx_device_vector * vdev,
 
   if (pdev -> cap >= 3) return -1;
 
-  (void)gs_sprintf(obuf, ESC_GS "%d;%d;%dlwG",
+  (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "%d;%d;%dlwG",
                 (int)(pdev -> lwidth),
                 (int)(pdev -> cap),
                 (int)(pdev -> join));
@@ -1300,7 +1300,7 @@ escv_setlinejoin(gx_device_vector * vdev
     return -1;
   }
 
-  (void)gs_sprintf(obuf, ESC_GS "%d;%d;%dlwG",
+  (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "%d;%d;%dlwG",
                 (int)(pdev -> lwidth),
                 (int)(pdev -> cap),
                 (int)(pdev -> join));
@@ -1322,14 +1322,14 @@ escv_setmiterlimit(gx_device_vector * vd
   if (pdev -> join != 3) {
     /* ¶¯À©Åª¤ËÀÜ¹çÉô»ØÄê¤ò¹Ô¤¦ */
     pdev -> join = 3;
-    (void)gs_sprintf(obuf, ESC_GS "%d;%d;%dlwG",
+    (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "%d;%d;%dlwG",
                   (int)(pdev -> lwidth),
                   (int)(pdev -> cap),
                   (int)(pdev -> join));
     lputs(s, obuf);
   }
 
-  (void)gs_sprintf(obuf, ESC_GS "1;%dmlG", (int)limit);
+  (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "1;%dmlG", (int)limit);
   lputs(s, obuf);
 
   return 0;
@@ -1357,7 +1357,7 @@ escv_setfillcolor(gx_device_vector * vde
 
   if( 0 == pdev->colormode ) { /* ESC/Page (Monochrome) */
 
-    (void)gs_sprintf(obuf, /*ESC_GS "1owE"*/ ESC_GS "0;0;100spE" ESC_GS "1;0;%ldccE" ,color);
+    (void)gs_snprintf(obuf, sizeof(obuf), /*ESC_GS "1owE"*/ ESC_GS "0;0;100spE" ESC_GS "1;0;%ldccE" ,color);
     lputs(s, obuf);
 
     if (vdev->x_pixels_per_inch == 1200) {
@@ -1371,7 +1371,7 @@ escv_setfillcolor(gx_device_vector * vde
   } else {			/* ESC/Page-Color */
 
     /* ¥Ñ¥¿¡¼¥ó£Ï£Î»ØÄê¡¿¥½¥ê¥Ã¥É¥Ñ¥¿¡¼¥ó»ØÄê */
-    (void)gs_sprintf(obuf, ESC_GS "1;2;3;%d;%d;%dfpE",
+    (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "1;2;3;%d;%d;%dfpE",
                   (unsigned char)(color >> 16 & 0xff),
                   (unsigned char)(color >> 8 & 0xff),
                   (unsigned char)(color & 0xff));
@@ -1399,7 +1399,7 @@ escv_setstrokecolor(gx_device_vector * v
 
     pdev->current_color = color;
 
-    (void)gs_sprintf(obuf, /*ESC_GS "1owE"*/ ESC_GS "0;0;100spE" ESC_GS "1;1;%ldccE" , color);
+    (void)gs_snprintf(obuf, sizeof(obuf), /*ESC_GS "1owE"*/ ESC_GS "0;0;100spE" ESC_GS "1;1;%ldccE" , color);
     lputs(s, obuf);
 
     if (vdev->x_pixels_per_inch == 1200) {
@@ -1416,7 +1416,7 @@ escv_setstrokecolor(gx_device_vector * v
 
       pdev->current_color = color;
       /* ¥Ñ¥¿¡¼¥ó£Ï£Î¿§»ØÄê¡¿¥½¥ê¥Ã¥É¥Ñ¥¿¡¼¥ó»ØÄê */
-      (void)gs_sprintf(obuf, ESC_GS "1;2;3;%d;%d;%dfpE" ESC_GS "2;2;1;0;0cpE",
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "1;2;3;%d;%d;%dfpE" ESC_GS "2;2;1;0;0cpE",
                     (unsigned char)(color >> 16 & 0xff),
                     (unsigned char)(color >> 8 & 0xff),
                     (unsigned char)(color & 0xff));
@@ -1447,7 +1447,7 @@ escv_setdash(gx_device_vector * vdev, co
 
   if (count) {
     if (count == 1) {
-      (void)gs_sprintf(obuf, ESC_GS "1;%d;%ddlG", (int) pattern[0], (int) pattern[0]);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "1;%d;%ddlG", (int) pattern[0], (int) pattern[0]);
       lputs(s, obuf);
     } else {
       /* pattern ¤Ë£°¤¬¤¢¤Ã¤¿¾ì¹ç¤ÏÉÁ²èÉÔ²Ä¤È¤·¤ÆÊÖµÑ */
@@ -1457,7 +1457,7 @@ escv_setdash(gx_device_vector * vdev, co
 
       lputs(s, ESC_GS "1");
       for (i = 0; i < count; ++i) {
-        (void)gs_sprintf(obuf, ";%d", (int) pattern[i]);
+        (void)gs_snprintf(obuf, sizeof(obuf), ";%d", (int) pattern[i]);
         lputs(s, obuf);
       }
       lputs(s, "dlG");
@@ -1507,7 +1507,7 @@ escv_moveto(gx_device_vector * vdev,
   char	obuf[64];
 
   /* ¥µ¥Ö¥Ñ¥¹³«»ÏÌ¿Îá */
-  (void)gs_sprintf(obuf, ESC_GS "0;%d;%dmvpG", (int)x1, (int)y1);
+  (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "0;%d;%dmvpG", (int)x1, (int)y1);
   lputs(s, obuf);
 
   return 0;
@@ -1521,7 +1521,7 @@ escv_lineto(gx_device_vector * vdev,
   gx_device_escv *pdev = (gx_device_escv *) vdev;
   char	obuf[64];
 
-  (void)gs_sprintf(obuf, ESC_GS "0;1;%d;%dlnpG", (int)x1, (int)y1);
+  (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "0;1;%d;%dlnpG", (int)x1, (int)y1);
   lputs(s, obuf);
   pdev->ispath = 1;
 
@@ -1538,7 +1538,7 @@ escv_curveto(gx_device_vector * vdev, do
   char	obuf[128];
 
   /* ¥Ù¥¸¥§¶ÊÀþ */
-  (void)gs_sprintf(obuf, ESC_GS "0;3;%d;%d;%d;%d;%d;%dbzpG",
+  (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "0;3;%d;%d;%d;%d;%d;%dbzpG",
                 (int)x1, (int)y1, (int)x2, (int)y2, (int)x3, (int)y3);
   lputs(s, obuf);
   pdev->ispath = 1;
@@ -2071,7 +2071,7 @@ escv_copy_mono(gx_device * dev, const by
       if( 0 == pdev->colormode ) { /* ESC/Page (Monochrome) */
 
         /*	    lputs(s, ESC_GS "1owE");*/
-        (void)gs_sprintf(obuf, ESC_GS "1;1;%ldccE", c_color);
+        (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "1;1;%ldccE", c_color);
         lputs(s, obuf);
 
         if (vdev->x_pixels_per_inch == 1200) {
@@ -2156,7 +2156,7 @@ escv_copy_mono(gx_device * dev, const by
   } else {			/* ESC/Page-Color */
 
     /* ¥Ñ¥¿¡¼¥ó£Ï£Î»ØÄê¡¿¥½¥ê¥Ã¥É¥Ñ¥¿¡¼¥ó»ØÄê */
-    (void)gs_sprintf(obuf, ESC_GS "1;2;3;%d;%d;%dfpE",
+    (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "1;2;3;%d;%d;%dfpE",
                   (unsigned char)(c_color >> 16 & 0xff),
                   (unsigned char)(c_color >> 8 & 0xff),
                   (unsigned char)(c_color & 0xff));
@@ -2276,7 +2276,7 @@ escv_fill_mask(gx_device * dev,
     if (!gx_dc_is_pure(pdcolor)) return_error(gs_error_rangecheck);
     pdev->current_color = color;
 
-    (void)gs_sprintf(obuf, ESC_GS "0;0;100spE" ESC_GS "1;1;%ldccE" ,color);
+    (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "0;0;100spE" ESC_GS "1;1;%ldccE" ,color);
     lputs(s, obuf);
 
     if (vdev->x_pixels_per_inch == 1200) {
@@ -2316,7 +2316,7 @@ escv_fill_mask(gx_device * dev,
           memcpy(buf + i * width_bytes, data + (data_x >> 3) + i * raster, width_bytes);
         }
 
-        (void)gs_sprintf(obuf, ESC_GS "%d;%d;%d;%d;0db{F", num_bytes, (int)(id & VCACHE), w, h);
+        (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "%d;%d;%d;%d;0db{F", num_bytes, (int)(id & VCACHE), w, h);
         lputs(s, obuf);
         put_bytes(s, buf, num_bytes);
 
@@ -2324,9 +2324,9 @@ escv_fill_mask(gx_device * dev,
         pdev -> id_cache[id & VCACHE] = id;
       }
 
-      (void)gs_sprintf(obuf, ESC_GS "%dX" ESC_GS "%dY", x, y);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "%dX" ESC_GS "%dY", x, y);
       lputs(s, obuf);
-      (void)gs_sprintf(obuf, ESC_GS "%lddbF", id & VCACHE);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "%lddbF", id & VCACHE);
       lputs(s, obuf);
 
       return 0;
@@ -2511,7 +2511,7 @@ fallback:
         gx_color_index color = gx_dc_pure_color(pdcolor);
 
         /*	    lputs(s, ESC_GS "1owE");*/
-        (void)gs_sprintf(obuf, ESC_GS "1;1;%ldccE", color);
+        (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "1;1;%ldccE", color);
         lputs(s, obuf);
 
         if (vdev->x_pixels_per_inch == 1200) {
@@ -2803,7 +2803,7 @@ static void escv_write_begin(gx_device *
 
   if( 0 == pdev->colormode ) { /* ESC/Page (Monochrome) */
 
-    (void)gs_sprintf(obuf, ESC_GS "%dX" ESC_GS "%dY", x, y);
+    (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "%dX" ESC_GS "%dY", x, y);
     lputs(s, obuf);
 
     comp = 10;
@@ -2811,34 +2811,34 @@ static void escv_write_begin(gx_device *
     if (bits == 1) {
       if (strcmp(pdev->dname, "lp1800") == 0 ||
           strcmp(pdev->dname, "lp9600") == 0) {
-        (void)gs_sprintf(obuf, ESC_GS "0bcI");
+        (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "0bcI");
       }else{
-        (void)gs_sprintf(obuf, ESC_GS "5;%d;%d;%d;%d;%dsrI",  sw, sh, dw, dh, roll);
+        (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "5;%d;%d;%d;%d;%dsrI",  sw, sh, dw, dh, roll);
       }
     } else if (bits == 4) {
       if (pdev -> c4map) {
         pdev -> c4map = FALSE;
       }
-      (void)gs_sprintf(obuf, ESC_GS "1;1;1;0;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "1;1;1;0;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
     } else if (bits == 8) {
       if (pdev -> c8map) {
         pdev -> c8map = FALSE;
       }
-      (void)gs_sprintf(obuf, ESC_GS "1;1;1;0;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "1;1;1;0;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
     } else {
       /* 24 bit */
-      (void)gs_sprintf(obuf, ESC_GS "1;1;1;0;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "1;1;1;0;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
     }
 
   } else {			/* ESC/Page-Color */
 
-    (void)gs_sprintf(obuf, ESC_GS "%dX" ESC_GS "%dY", x, y);
+    (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "%dX" ESC_GS "%dY", x, y);
     lputs(s, obuf);
 
     comp = 0;
 
     if (bits == 1) {
-      (void)gs_sprintf(obuf, ESC_GS "2;201;1;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "2;201;1;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
     } else if (bits == 4) {
       if (pdev -> c4map) {
         /* ¥«¥é¡¼¥Þ¥Ã¥×ÅÐÏ¿ */
@@ -2854,7 +2854,7 @@ static void escv_write_begin(gx_device *
         gs_free_object(vdev->memory, tmp, "escv_write_begin(tmp4)");
         pdev -> c4map = FALSE;
       }
-      (void)gs_sprintf(obuf, ESC_GS "2;203;2;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "2;203;2;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
     } else if (bits == 8) {
       if (pdev -> c8map) {
         /* ¥«¥é¡¼¥Þ¥Ã¥×ÅÐÏ¿ */
@@ -2870,10 +2870,10 @@ static void escv_write_begin(gx_device *
         gs_free_object(vdev->memory, tmp, "escv_write_begin(tmp)");
         pdev -> c8map = FALSE;
       }
-      (void)gs_sprintf(obuf, ESC_GS "2;204;4;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "2;204;4;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
     } else {
       /* 24 bit */
-      (void)gs_sprintf(obuf, ESC_GS "2;102;0;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "2;102;0;%d;%d;%d;%d;%d;%dscrI", comp, sw, sh, dw, dh, roll);
     }
 
   }	/* ESC/Page-Color */
@@ -2940,12 +2940,12 @@ static void escv_write_data(gx_device *d
     if(bits == 1){
       if (strcmp(pdev->dname, "lp1800") == 0 || \
           strcmp(pdev->dname, "lp9600") == 0) {
-        (void)gs_sprintf(obuf, ESC_GS "%d;1;%d;%d;0db{I", bsize, w, ras);
+        (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "%d;1;%d;%d;0db{I", bsize, w, ras);
       }else{
-        (void)gs_sprintf(obuf, ESC_GS "%d;%du{I", bsize, ras);
+        (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "%d;%du{I", bsize, ras);
       }
     }else{
-      (void)gs_sprintf(obuf, ESC_GS "%d;%dcu{I", bsize, ras);
+      (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "%d;%dcu{I", bsize, ras);
     }
     lputs(s, obuf);
 
@@ -2972,7 +2972,7 @@ static void escv_write_data(gx_device *d
       buf = tmps;
     }
 
-    (void)gs_sprintf(obuf, ESC_GS "%d;%dcu{I", bsize, ras);
+    (void)gs_snprintf(obuf, sizeof(obuf), ESC_GS "%d;%dcu{I", bsize, ras);
     lputs(s, obuf);
     put_bytes(s, buf, bsize);
 
diff -pruN 9.55.0~dfsg-3/contrib/gdevcd8.c 9.56.1~dfsg-1/contrib/gdevcd8.c
--- 9.55.0~dfsg-3/contrib/gdevcd8.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/gdevcd8.c	2022-04-04 13:46:22.000000000 +0000
@@ -2461,7 +2461,7 @@ do_gcr(int bytecount, byte * inbyte, con
       if ((*cyan > 0) && (*magenta > 0) && (*yellow > 0))
       {
         char output[255];
-        gs_sprintf(output, "%3d %3d %3d %3d - ", *cyan, *magenta, *yellow, *black);
+        gs_snprintf(output, sizeof(output), "%3d %3d %3d %3d - ", *cyan, *magenta, *yellow, *black);
         debug_print_string(output, strlen(output));
       }
 #endif /* 0 */
@@ -2509,7 +2509,7 @@ do_gcr(int bytecount, byte * inbyte, con
         if (ucr > 0)
         {
           char output[255];
-          gs_sprintf(output, "%3d %3d %3d %3d - %5d\n", *cyan, *magenta, *yellow, *black, ucr);
+          gs_snprintf(output, sizeof(output), "%3d %3d %3d %3d - %5d\n", *cyan, *magenta, *yellow, *black, ucr);
           debug_print_string(output, strlen(output));
         }
 #endif /* 0 */
diff -pruN 9.55.0~dfsg-3/contrib/gdevdj9.c 9.56.1~dfsg-1/contrib/gdevdj9.c
--- 9.55.0~dfsg-3/contrib/gdevdj9.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/gdevdj9.c	2022-04-04 13:46:22.000000000 +0000
@@ -2663,7 +2663,7 @@ cdj970_write_header(gx_device * pdev, gp
 
     memset(startbuffer, 0, 1260);
 
-    gs_sprintf(&(startbuffer[600]),
+    gs_snprintf(&(startbuffer[600]), sizeof(startbuffer)- 600,
                "\033E\033%%-12345X@PJL JOB NAME = \"GHOST BY RENE HARSCH\"\n@PJL ENTER LANGUAGE=PCL3GUI\n");
 
     gp_fwrite(startbuffer, sizeof(char), 678, prn_stream);
diff -pruN 9.55.0~dfsg-3/contrib/gdevhl12.c 9.56.1~dfsg-1/contrib/gdevhl12.c
--- 9.55.0~dfsg-3/contrib/gdevhl12.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/gdevhl12.c	2022-04-04 13:46:22.000000000 +0000
@@ -670,7 +670,7 @@ hl1250_print_page_copies(gx_device_print
            (-120, 0) compared to the one in the ljet4 driver (-180, 36)
            (X, Y coordinates here are specified in 1/720-inch units).  */
 
-        gs_sprintf(page_init, "\033&l-120U\033*r0F\033&u%dD%s", y_dpi, tray_pcl);
+        gs_snprintf(page_init, sizeof(page_init), "\033&l-120U\033*r0F\033&u%dD%s", y_dpi, tray_pcl);
         return dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
                                             y_dpi, PCL_LJ4_FEATURES,
                                             page_init, page_init, false);
diff -pruN 9.55.0~dfsg-3/contrib/gdevxes.c 9.56.1~dfsg-1/contrib/gdevxes.c
--- 9.55.0~dfsg-3/contrib/gdevxes.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/gdevxes.c	2022-04-04 13:46:22.000000000 +0000
@@ -180,7 +180,7 @@ sixel_print_page(gx_device_printer *pdev
               if ( tmp[l] == last ) {
                 count++;
                 if (count==32767) {
-                  run[gs_sprintf(run, "%d", count)]='\0';
+                  run[gs_snprintf(run, sizeof(run), "%d", count)]='\0';
                   for (t=run; *t; t++) gp_fputc( *t, prn_stream );
                   gp_fputc( last, prn_stream );
                   last = '\0';
@@ -193,7 +193,7 @@ sixel_print_page(gx_device_printer *pdev
                     case 0: break;
                     case 1: gp_fputc( last, prn_stream );
                             break;
-                    default:run[gs_sprintf(run, "%d", count)]='\0';
+                    default:run[gs_snprintf(run, sizeof(run), "%d", count)]='\0';
                             for (t=run; *t; t++) gp_fputc( *t, prn_stream );
                             gp_fputc( last, prn_stream );
                             break;
@@ -210,7 +210,7 @@ sixel_print_page(gx_device_printer *pdev
       case 0: break;
       case 1: gp_fputc( last, prn_stream );
               break;
-      default:run[gs_sprintf(run, "%d", count)]='\0';
+      default:run[gs_snprintf(run, sizeof(run), "%d", count)]='\0';
               for (t=run; *t; t++) gp_fputc( *t, prn_stream );
               gp_fputc( last, prn_stream );
               break;
diff -pruN 9.55.0~dfsg-3/contrib/japanese/gdevfmlbp.c 9.56.1~dfsg-1/contrib/japanese/gdevfmlbp.c
--- 9.55.0~dfsg-3/contrib/japanese/gdevfmlbp.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/japanese/gdevfmlbp.c	2022-04-04 13:46:22.000000000 +0000
@@ -108,7 +108,7 @@ static char can_inits[] ={ ESC, 'c',
 /* Get the paper size code, based on width and height. */
 /* modified from gdevpcl.c, gdevmjc.c and gdevnpdl.c. */
 static char *
-gdev_fmlbp_paper_size(gx_device_printer *dev, char *paper)
+gdev_fmlbp_paper_size(gx_device_printer *dev, char paper[16])
 {
   int    landscape = 0;	/* portrait */
   float height_inches = dev->height / dev->y_pixels_per_inch;
@@ -120,7 +120,7 @@ gdev_fmlbp_paper_size(gx_device_printer
     height_inches = t;
     landscape = 1;
   }
-  gs_sprintf(paper, "%s;%d",
+  gs_snprintf(paper, 16, "%s;%d",
     (height_inches >= 15.9 ? PAPER_SIZE_A3 :
      height_inches >= 11.8 ?
      (width_inches >=  9.2 ? PAPER_SIZE_B4 : PAPER_SIZE_LEGAL) :
@@ -144,7 +144,7 @@ static void goto_xy(gp_file *prn_stream,
 
     gp_fputc(CEX,prn_stream);
     gp_fputc('"',prn_stream);
-    gs_sprintf((char *)buff,"%d",x);
+    gs_snprintf((char *)buff,sizeof(buff),"%d",x);
     while (*p)
       {
         if (!*(p+1)) gp_fputc((*p)+0x30,prn_stream);
@@ -154,7 +154,7 @@ static void goto_xy(gp_file *prn_stream,
       }
 
     p=buff;
-    gs_sprintf((char *)buff,"%d",y);
+    gs_snprintf((char *)buff,sizeof(buff),"%d",y);
     while (*p)
       {
         if (!*(p+1)) gp_fputc((*p)+0x40,prn_stream);
diff -pruN 9.55.0~dfsg-3/contrib/japanese/gdevfmpr.c 9.56.1~dfsg-1/contrib/japanese/gdevfmpr.c
--- 9.55.0~dfsg-3/contrib/japanese/gdevfmpr.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/japanese/gdevfmpr.c	2022-04-04 13:46:22.000000000 +0000
@@ -184,13 +184,13 @@ fmpr_print_page(gx_device_printer *pdev,
     }
     out_beg -= (out_beg - out) % bytes_per_column;
 
-    gs_sprintf(prn_buf, "\033[%da",
+    gs_snprintf(prn_buf, sizeof(prn_buf), "\033[%da",
             (out_beg - out) / bytes_per_column);
     prn_puts(pdev, prn_buf);
 
     /* Dot graphics */
     size = out_end - out_beg + 1;
-    gs_sprintf(prn_buf, "\033Q%d W", size / bytes_per_column);
+    gs_snprintf(prn_buf, sizeof(prn_buf), "\033Q%d W", size / bytes_per_column);
     prn_puts(pdev, prn_buf);
     prn_write(pdev, (const char *)out_beg, size);
 
diff -pruN 9.55.0~dfsg-3/contrib/japanese/gdevj100.c 9.56.1~dfsg-1/contrib/japanese/gdevj100.c
--- 9.55.0~dfsg-3/contrib/japanese/gdevj100.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/japanese/gdevj100.c	2022-04-04 13:46:22.000000000 +0000
@@ -128,12 +128,12 @@ jj100_print_page(gx_device_printer *pdev
 
                 /* Vertical tab to the appropriate position. */
                 while(skip > 15) {
-                        gs_sprintf(prn_buf, "\037%c", 16 + 15);
+                        gs_snprintf(prn_buf, sizeof(prn_buf), "\037%c", 16 + 15);
                         gp_fputs(prn_buf, pdev->file);
                         skip -= 15;
                 }
                 if(skip > 0) {
-                        gs_sprintf(prn_buf, "\037%c", 16 + skip);
+                        gs_snprintf(prn_buf, sizeof(prn_buf), "\037%c", 16 + skip);
                         gp_fputs(prn_buf, pdev->file);
                 }
 
@@ -170,13 +170,13 @@ jj100_print_page(gx_device_printer *pdev
                 out_beg -= (out_beg - out) % (bytes_per_column * 2);
 
                 /* Dot addressing */
-                gs_sprintf(prn_buf, "\033F%04d",
+                gs_snprintf(prn_buf, sizeof(prn_buf), "\033F%04d",
                         (out_beg - out) / bytes_per_column / 2);
                 gp_fputs(prn_buf, pdev->file);
 
                 /* Dot graphics */
                 size = out_end - out_beg + 1;
-                gs_sprintf(prn_buf, "\034bP,48,%04d.", size / bytes_per_column);
+                gs_snprintf(prn_buf, sizeof(prn_buf), "\034bP,48,%04d.", size / bytes_per_column);
                 gp_fputs(prn_buf, pdev->file);
                 gp_fwrite(out_beg, 1, size, pdev->file);
 
diff -pruN 9.55.0~dfsg-3/contrib/japanese/gdevlbp3.c 9.56.1~dfsg-1/contrib/japanese/gdevlbp3.c
--- 9.55.0~dfsg-3/contrib/japanese/gdevlbp3.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/japanese/gdevlbp3.c	2022-04-04 13:46:22.000000000 +0000
@@ -81,7 +81,7 @@ lbp310PrintPage(gx_device_printer *pDev,
         DataSize = CompressImage(pDev, &Box, fp, "\x1b[1;%d;%d;11;%d;.r");
 
         /* ----==== Set size ====---- */
-        gs_sprintf(Buf, "0%ld", DataSize);
+        gs_snprintf(Buf, sizeof(Buf), "0%ld", DataSize);
         i = (DataSize+strlen(Buf)+1)&1;
         /* ----==== escape to LIPS ====---- */
         gp_fprintf(fp, "\x80%s\x80\x80\x80\x80\x0c",Buf+i);
@@ -110,7 +110,7 @@ lbp320PrintPage(gx_device_printer *pDev,
         DataSize = CompressImage(pDev, &Box, fp, "\x1b[1;%d;%d;11;%d;.&r");
 
         /* ----==== Set size ====---- */
-        gs_sprintf(Buf, "000%ld", DataSize);
+        gs_snprintf(Buf, sizeof(Buf), "000%ld", DataSize);
         i = (DataSize+strlen(Buf)+1)&3;
         /* ----==== escape to LIPS ====---- */
         gp_fprintf(fp, "\x80%s\x80\x80\x80\x80\x0c",Buf+i);
diff -pruN 9.55.0~dfsg-3/contrib/japanese/gdevnpdl.c 9.56.1~dfsg-1/contrib/japanese/gdevnpdl.c
--- 9.55.0~dfsg-3/contrib/japanese/gdevnpdl.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/japanese/gdevnpdl.c	2022-04-04 13:46:22.000000000 +0000
@@ -600,31 +600,32 @@ npdl_print_page_copies(gx_device_printer
         /* Check paper size */
         switch (npdl_get_paper_size((gx_device *) pdev)) {
             case PAPER_SIZE_POSTCARD:
-                gs_sprintf(paper_command, "PC");
+                gs_snprintf(paper_command, sizeof(paper_command), "PC");
                 break;
             case PAPER_SIZE_A5:
-                gs_sprintf(paper_command, "A5");
+                gs_snprintf(paper_command, sizeof(paper_command), "A5");
                 break;
+            default:
             case PAPER_SIZE_A4:
-                gs_sprintf(paper_command, "A4");
+                gs_snprintf(paper_command, sizeof(paper_command), "A4");
                 break;
             case PAPER_SIZE_A3:
-                gs_sprintf(paper_command, "A3");
+                gs_snprintf(paper_command, sizeof(paper_command), "A3");
                 break;
             case PAPER_SIZE_B5:
-                gs_sprintf(paper_command, "B5");
+                gs_snprintf(paper_command, sizeof(paper_command), "B5");
                 break;
             case PAPER_SIZE_B4:
-                gs_sprintf(paper_command, "B4");
+                gs_snprintf(paper_command, sizeof(paper_command), "B4");
                 break;
             case PAPER_SIZE_LETTER:
-                gs_sprintf(paper_command, "LT");
+                gs_snprintf(paper_command, sizeof(paper_command), "LT");
                 break;
             case PAPER_SIZE_ENV4:
-                gs_sprintf(paper_command, "ENV4");
+                gs_snprintf(paper_command, sizeof(paper_command), "ENV4");
                 break;
             case PAPER_SIZE_BPOSTCARD:
-                gs_sprintf(paper_command, "UPPC");
+                gs_snprintf(paper_command, sizeof(paper_command), "UPPC");
                 break;
         }
 
diff -pruN 9.55.0~dfsg-3/contrib/lips4/gdevl4r.c 9.56.1~dfsg-1/contrib/lips4/gdevl4r.c
--- 9.55.0~dfsg-3/contrib/lips4/gdevl4r.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/lips4/gdevl4r.c	2022-04-04 13:46:22.000000000 +0000
@@ -804,9 +804,9 @@ lips2p_image_out(gx_device_printer * pde
     move_cap(pdev, prn_stream, x, y);
 
     Len = lips_mode3format_encode(lprn->TmpBuf, lprn->CompBuf, width / 8 * height);
-    gs_sprintf(raw_str, "%c%d;%d;%d.r", LIPS_CSI,
+    gs_snprintf(raw_str, sizeof(raw_str), "%c%d;%d;%d.r", LIPS_CSI,
             width / 8 * height, width / 8, (int)pdev->x_pixels_per_inch);
-    gs_sprintf(comp_str, "%c%d;%d;%d;9;%d.r", LIPS_CSI,
+    gs_snprintf(comp_str, sizeof(comp_str), "%c%d;%d;%d;9;%d.r", LIPS_CSI,
             Len, width / 8, (int)pdev->x_pixels_per_inch, height);
 
     if (Len < width / 8 * height - strlen(comp_str) + strlen(raw_str)) {
@@ -835,11 +835,11 @@ lips4_image_out(gx_device_printer * pdev
     Len = lips_packbits_encode(lprn->TmpBuf, lprn->CompBuf, width / 8 * height);
     Len_rle = lips_rle_encode(lprn->TmpBuf, lprn->CompBuf2, width / 8 * height);
 
-    gs_sprintf(raw_str, "%c%d;%d;%d.r", LIPS_CSI,
+    gs_snprintf(raw_str, sizeof(raw_str), "%c%d;%d;%d.r", LIPS_CSI,
             width / 8 * height, width / 8, (int)pdev->x_pixels_per_inch);
 
     if (Len < Len_rle) {
-        gs_sprintf(comp_str, "%c%d;%d;%d;11;%d.r", LIPS_CSI,
+        gs_snprintf(comp_str, sizeof(comp_str), "%c%d;%d;%d;11;%d.r", LIPS_CSI,
                 Len, width / 8, (int)pdev->x_pixels_per_inch, height);
         if (Len < width / 8 * height - strlen(comp_str) + strlen(raw_str)) {
             gp_fprintf(prn_stream, "%s", comp_str);
@@ -852,7 +852,7 @@ lips4_image_out(gx_device_printer * pdev
     } else {
         /* 2019-11-28: changed two occurrencies of 'Len' to 'Len_rle' here, but
         unable to test. */
-        gs_sprintf(comp_str, "%c%d;%d;%d;10;%d.r", LIPS_CSI,
+        gs_snprintf(comp_str, sizeof(comp_str), "%c%d;%d;%d;10;%d.r", LIPS_CSI,
                 Len_rle, width / 8, (int)pdev->x_pixels_per_inch, height);
         if (Len_rle < width / 8 * height - strlen(comp_str) + strlen(raw_str)) {
             gp_fprintf(prn_stream, "%s", comp_str);
diff -pruN 9.55.0~dfsg-3/contrib/lips4/gdevl4v.c 9.56.1~dfsg-1/contrib/lips4/gdevl4v.c
--- 9.55.0~dfsg-3/contrib/lips4/gdevl4v.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/lips4/gdevl4v.c	2022-04-04 13:46:22.000000000 +0000
@@ -405,17 +405,17 @@ lips4v_set_cap(gx_device * dev, int x, i
     int dy = y - pdev->prev_y;
 
     if (dx > 0) {
-        gs_sprintf(cap, "%c%da", LIPS_CSI, dx);
+        gs_snprintf(cap, sizeof(cap), "%c%da", LIPS_CSI, dx);
         lputs(s, cap);
     } else if (dx < 0) {
-        gs_sprintf(cap, "%c%dj", LIPS_CSI, -dx);
+        gs_snprintf(cap, sizeof(cap), "%c%dj", LIPS_CSI, -dx);
         lputs(s, cap);
     }
     if (dy > 0) {
-        gs_sprintf(cap, "%c%dk", LIPS_CSI, dy);
+        gs_snprintf(cap, sizeof(cap), "%c%dk", LIPS_CSI, dy);
         lputs(s, cap);
     } else if (dy < 0) {
-        gs_sprintf(cap, "%c%de", LIPS_CSI, -dy);
+        gs_snprintf(cap, sizeof(cap), "%c%de", LIPS_CSI, -dy);
         lputs(s, cap);
     }
     pdev->prev_x = x;
@@ -490,10 +490,10 @@ lips4v_copy_text_char(gx_device * dev, c
     if (download) {
         if (ccode % 128 == 0 && ccode == pdev->count) {
             /* Ê¸»ú¥»¥Ã¥ÈÅÐÏ¿Êä½õÌ¿Îá */
-            gs_sprintf(cset_sub, "%c%dx%c", LIPS_DCS, ccode / 128, LIPS_ST);
+            gs_snprintf(cset_sub, sizeof(cset_sub), "%c%dx%c", LIPS_DCS, ccode / 128, LIPS_ST);
             lputs(s, cset_sub);
             /* Ê¸»ú¥»¥Ã¥ÈÅÐÏ¿Ì¿Îá */
-            gs_sprintf(cset,
+            gs_snprintf(cset,sizeof(cset),
                     "%c%d;1;0;0;3840;8;400;100;0;0;200;%d;%d;0;0;;;;;%d.p",
                     LIPS_CSI,
                     size + 9, cell_length,	/* Cell Width */
@@ -502,7 +502,7 @@ lips4v_copy_text_char(gx_device * dev, c
             lputs(s, cset);
         } else {
             /* 1Ê¸»úÅÐÏ¿Ì¿Îá */
-            gs_sprintf(cset,
+            gs_snprintf(cset,sizeof(cset),
                     "%c%d;%d;8;%d.q", LIPS_CSI,
                     size + 9, ccode / 128, (int)dev->x_pixels_per_inch);
             lputs(s, cset);
@@ -520,7 +520,7 @@ lips4v_copy_text_char(gx_device * dev, c
     }
     /* Ê¸»ú¥»¥Ã¥È¡¦¥¢¥µ¥¤¥óÈÖ¹æÁªÂòÌ¿Îá2 */
     if (pdev->current_font != ccode / 128) {
-        gs_sprintf(cset_number, "%c%d%%v", LIPS_CSI, ccode / 128);
+        gs_snprintf(cset_number, sizeof(cset_number), "%c%d%%v", LIPS_CSI, ccode / 128);
         lputs(s, cset_number);
         pdev->current_font = ccode / 128;
     }
@@ -530,7 +530,7 @@ lips4v_copy_text_char(gx_device * dev, c
         if (pdev->color_info.depth == 8) {
             sputc(s, LIPS_CSI);
             lputs(s, "?10;2;");
-            gs_sprintf(text_color, "%d",
+            gs_snprintf(text_color, sizeof(text_color), "%d",
                     (int)(pdev->color_info.max_gray - pdev->current_color));
         } else {
             int r = (pdev->current_color >> 16) * 1000.0 / 255.0;
@@ -539,7 +539,7 @@ lips4v_copy_text_char(gx_device * dev, c
 
             sputc(s, LIPS_CSI);
             lputs(s, "?10;;");
-            gs_sprintf(text_color, "%d;%d;%d", r, g, b);
+            gs_snprintf(text_color, sizeof(text_color), "%d;%d;%d", r, g, b);
         }
         lputs(s, text_color);
         lputs(s, "%p");
@@ -655,16 +655,16 @@ lips4v_beginpage(gx_device_vector * vdev
                 lputs(s, "@PJL SET RESOLUTION = QUICK\n");
             lputs(s, l4v_file_header2);
             if (pdev->toner_density) {
-                gs_sprintf(toner_d, "@PJL SET TONER-DENSITY=%d\n",
+                gs_snprintf(toner_d, sizeof(toner_d), "@PJL SET TONER-DENSITY=%d\n",
                         pdev->toner_density);
                 lputs(s, toner_d);
             }
             if (pdev->toner_saving_set) {
                 lputs(s, "@PJL SET TONER-SAVING=");
                 if (pdev->toner_saving)
-                    gs_sprintf(toner_s, "ON\n");
+                    gs_snprintf(toner_s, sizeof(toner_s), "ON\n");
                 else
-                    gs_sprintf(toner_s, "OFF\n");
+                    gs_snprintf(toner_s, sizeof(toner_s), "OFF\n");
                 lputs(s, toner_s);
             }
             lputs(s, l4v_file_header3);
@@ -674,8 +674,8 @@ lips4v_beginpage(gx_device_vector * vdev
         if (dpi > 9999)
             return_error(gs_error_rangecheck);
 
-        /* set reaolution (dpi) */
-        gs_sprintf(dpi_char, "%d", dpi);
+        /* set resolution (dpi) */
+        gs_snprintf(dpi_char, sizeof(dpi_char), "%d", dpi);
         lputs(s, dpi_char);
 
         if (pdev->color_info.depth == 8)
@@ -684,7 +684,7 @@ lips4v_beginpage(gx_device_vector * vdev
             lputs(s, l4vcolor_file_header);
 
         /* username */
-        gs_sprintf(username, "%c2y%s%c", LIPS_DCS, pdev->Username, LIPS_ST);
+        gs_snprintf(username, sizeof(username), "%c2y%s%c", LIPS_DCS, pdev->Username, LIPS_ST);
         lputs(s, username);
     }
     if (strcmp(pdev->mediaType, "PlainPaper") == 0) {
@@ -711,13 +711,13 @@ lips4v_beginpage(gx_device_vector * vdev
          && strcmp(pdev->mediaType, LIPS_MEDIATYPE_DEFAULT) != 0)) {
         /* Use ManualFeed */
         if (pdev->prev_feed_mode != 10) {
-            gs_sprintf(feedmode, "%c10q", LIPS_CSI);
+            gs_snprintf(feedmode, sizeof(feedmode), "%c10q", LIPS_CSI);
             lputs(s, feedmode);
             pdev->prev_feed_mode = 10;
         }
     } else {
         if (pdev->prev_feed_mode != pdev->cassetFeed) {
-            gs_sprintf(feedmode, "%c%dq", LIPS_CSI, pdev->cassetFeed);
+            gs_snprintf(feedmode, sizeof(feedmode), "%c%dq", LIPS_CSI, pdev->cassetFeed);
             lputs(s, feedmode);
             pdev->prev_feed_mode = pdev->cassetFeed;
         }
@@ -729,10 +729,10 @@ lips4v_beginpage(gx_device_vector * vdev
     if (pdev->prev_paper_size != paper_size) {
         if (paper_size == USER_SIZE) {
             /* modified by shige 06/27 2003
-            gs_sprintf(paper, "%c80;%d;%dp", LIPS_CSI, width * 10, height * 10); */
+            gs_snprintf(paper, sizeof(paper), "%c80;%d;%dp", LIPS_CSI, width * 10, height * 10); */
             /* modified by shige 11/09 2003
-            gs_sprintf(paper, "%c80;%d;%dp", LIPS_CSI, height * 10, width * 10); */
-            gs_sprintf(paper, "%c80;%d;%dp", LIPS_CSI,
+            gs_snprintf(paper, sizeof(paper), "%c80;%d;%dp", LIPS_CSI, height * 10, width * 10); */
+            gs_snprintf(paper,sizeof(paper),  "%c80;%d;%dp", LIPS_CSI,
                     (height * 10 > LIPS_HEIGHT_MAX_720)?
                     LIPS_HEIGHT_MAX_720 : (height * 10),
                     (width * 10 > LIPS_WIDTH_MAX_720)?
@@ -740,77 +740,79 @@ lips4v_beginpage(gx_device_vector * vdev
             lputs(s, paper);
         } else if (paper_size == USER_SIZE + LANDSCAPE) {
             /* modified by shige 06/27 2003
-            gs_sprintf(paper, "%c81;%d;%dp", LIPS_CSI, height * 10, width * 10); */
+            gs_snprintf(paper, sizeof(paper), "%c81;%d;%dp", LIPS_CSI, height * 10, width * 10); */
             /* modified by shige 11/09 2003
-            gs_sprintf(paper, "%c81;%d;%dp", LIPS_CSI, width * 10, height * 10); */
-            gs_sprintf(paper, "%c80;%d;%dp", LIPS_CSI,
+            gs_snprintf(paper, sizeof(paper), "%c81;%d;%dp", LIPS_CSI, width * 10, height * 10); */
+            gs_snprintf(paper,sizeof(paper),  "%c80;%d;%dp", LIPS_CSI,
                     (width * 10 > LIPS_HEIGHT_MAX_720)?
                     LIPS_HEIGHT_MAX_720 : (width * 10),
                     (height * 10 > LIPS_WIDTH_MAX_720)?
                     LIPS_WIDTH_MAX_720 : (height * 10));
             lputs(s, paper);
         } else {
-            gs_sprintf(paper, "%c%dp", LIPS_CSI, paper_size);
+            gs_snprintf(paper, sizeof(paper), "%c%dp", LIPS_CSI, paper_size);
             lputs(s, paper);
         }
     } else if (paper_size == USER_SIZE) {
         if (pdev->prev_paper_width != width ||
-            pdev->prev_paper_height != height)
+            pdev->prev_paper_height != height) {
                 /* modified by shige 06/27 2003
-                gs_sprintf(paper, "%c80;%d;%dp", LIPS_CSI, width * 10, height * 10); */
+                gs_snprintf(paper, sizeof(paper), "%c80;%d;%dp", LIPS_CSI, width * 10, height * 10); */
                 /* modified by shige 11/09 2003
-                gs_sprintf(paper, "%c80;%d;%dp", LIPS_CSI, height * 10, width * 10); */
-                gs_sprintf(paper, "%c80;%d;%dp", LIPS_CSI,
+                gs_snprintf(paper, sizeof(paper), "%c80;%d;%dp", LIPS_CSI, height * 10, width * 10); */
+                gs_snprintf(paper, sizeof(paper), "%c80;%d;%dp", LIPS_CSI,
                     (height * 10 > LIPS_HEIGHT_MAX_720)?
                     LIPS_HEIGHT_MAX_720 : (height * 10),
                     (width * 10 > LIPS_WIDTH_MAX_720)?
                     LIPS_WIDTH_MAX_720 : (width * 10));
-        lputs(s, paper);
+                lputs(s, paper);
+        }
     } else if (paper_size == USER_SIZE + LANDSCAPE) {
         if (pdev->prev_paper_width != width ||
-            pdev->prev_paper_height != height)
+            pdev->prev_paper_height != height) {
                 /* modified by shige 06/27 2003
-                gs_sprintf(paper, "%c81;%d;%dp", LIPS_CSI, height * 10, width * 10); */
+                gs_snprintf(paper, sizeof(paper), "%c81;%d;%dp", LIPS_CSI, height * 10, width * 10); */
                 /* modified by shige 11/09 2003
-                gs_sprintf(paper, "%c81;%d;%dp", LIPS_CSI, width * 10, height * 10); */
-                gs_sprintf(paper, "%c80;%d;%dp", LIPS_CSI,
+                gs_snprintf(paper, sizeof(paper), "%c81;%d;%dp", LIPS_CSI, width * 10, height * 10); */
+                gs_snprintf(paper, sizeof(paper), "%c80;%d;%dp", LIPS_CSI,
                     (width * 10 > LIPS_HEIGHT_MAX_720)?
                     LIPS_HEIGHT_MAX_720 : (width * 10),
                     (height * 10 > LIPS_WIDTH_MAX_720)?
                     LIPS_WIDTH_MAX_720 : (height * 10));
-        lputs(s, paper);
+                lputs(s, paper);
+        }
     }
     pdev->prev_paper_size = paper_size;
     pdev->prev_paper_width = width;
     pdev->prev_paper_height = height;
 
     if (pdev->faceup) {
-        gs_sprintf(faceup_char, "%c11;12;12~", LIPS_CSI);
+        gs_snprintf(faceup_char, sizeof(faceup_char), "%c11;12;12~", LIPS_CSI);
         lputs(s, faceup_char);
     }
     /* N-up Printing Setting */
     if (pdev->first_page) {
         if (pdev->nup != 1) {
-            gs_sprintf(nup_char, "%c%d1;;%do", LIPS_CSI, pdev->nup, paper_size);
+            gs_snprintf(nup_char, sizeof(nup_char), "%c%d1;;%do", LIPS_CSI, pdev->nup, paper_size);
             lputs(s, nup_char);
         }
     }
     /* Duplex Setting */
     if (dupset && dup) {
         if (pdev->prev_duplex_mode == 0 || pdev->prev_duplex_mode == 1) {
-            gs_sprintf(duplex_char, "%c2;#x", LIPS_CSI);	/* duplex */
+            gs_snprintf(duplex_char, sizeof(duplex_char), "%c2;#x", LIPS_CSI);	/* duplex */
             lputs(s, duplex_char);
             if (!tum) {
                 /* long edge binding */
                 if (pdev->prev_duplex_mode != 2) {
-                    gs_sprintf(tumble_char, "%c0;#w", LIPS_CSI);
+                    gs_snprintf(tumble_char, sizeof(tumble_char), "%c0;#w", LIPS_CSI);
                     lputs(s, tumble_char);
                 }
                 pdev->prev_duplex_mode = 2;
             } else {
                 /* short edge binding */
                 if (pdev->prev_duplex_mode != 3) {
-                    gs_sprintf(tumble_char, "%c2;#w", LIPS_CSI);
+                    gs_snprintf(tumble_char, sizeof(tumble_char), "%c2;#w", LIPS_CSI);
                     lputs(s, tumble_char);
                 }
                 pdev->prev_duplex_mode = 3;
@@ -818,7 +820,7 @@ lips4v_beginpage(gx_device_vector * vdev
         }
     } else if (dupset && !dup) {
         if (pdev->prev_duplex_mode != 1) {
-            gs_sprintf(duplex_char, "%c0;#x", LIPS_CSI);	/* simplex */
+            gs_snprintf(duplex_char, sizeof(duplex_char), "%c0;#x", LIPS_CSI);	/* simplex */
             lputs(s, duplex_char);
         }
         pdev->prev_duplex_mode = 1;
@@ -831,9 +833,9 @@ lips4v_beginpage(gx_device_vector * vdev
     /* size unit (dpi) */
     sputc(s, LIPS_CSI);
     lputs(s, "11h");
-    gs_sprintf(unit, "%c?7;%d I", LIPS_CSI, (int)pdev->x_pixels_per_inch);
+    gs_snprintf(unit, sizeof(unit), "%c?7;%d I", LIPS_CSI, (int)pdev->x_pixels_per_inch);
     lputs(s, unit);
-    gs_sprintf(page_header, "%c[0&}#%c", LIPS_ESC, LIPS_IS2);
+    gs_snprintf(page_header, sizeof(page_header), "%c[0&}#%c", LIPS_ESC, LIPS_IS2);
     lputs(s, page_header);	/* vector mode */
 
     lputs(s, "!0");		/* size unit (dpi) */
@@ -842,10 +844,10 @@ lips4v_beginpage(gx_device_vector * vdev
     sputc(s, LIPS_IS2);
 
     if (pdev->color_info.depth == 8) {
-        gs_sprintf(l4vmono_page_header, "!13%c$%c", LIPS_IS2, LIPS_IS2);
+        gs_snprintf(l4vmono_page_header, sizeof(l4vmono_page_header), "!13%c$%c", LIPS_IS2, LIPS_IS2);
         lputs(s, l4vmono_page_header);
     } else {
-        gs_sprintf(l4vcolor_page_header, "!11%c$%c", LIPS_IS2, LIPS_IS2);
+        gs_snprintf(l4vcolor_page_header, sizeof(l4vcolor_page_header), "!11%c$%c", LIPS_IS2, LIPS_IS2);
         lputs(s, l4vcolor_page_header);
     }
 
@@ -949,7 +951,7 @@ lips4v_setlinecap(gx_device_vector * vde
         break;
     }
     /* ÀþÃ¼·Á¾õ»ØÄêÌ¿Îá */
-    gs_sprintf(c, "}E%d%c", line_cap, LIPS_IS2);
+    gs_snprintf(c, sizeof(c), "}E%d%c", line_cap, LIPS_IS2);
     lputs(s, c);
 
     pdev->linecap = cap;
@@ -990,7 +992,7 @@ lips4v_setlinejoin(gx_device_vector * vd
         break;
     }
 
-    gs_sprintf(c, "}F%d%c", lips_join, LIPS_IS2);
+    gs_snprintf(c, sizeof(c), "}F%d%c", lips_join, LIPS_IS2);
     lputs(s, c);
 
     return 0;
@@ -1411,7 +1413,7 @@ lips4v_output_page(gx_device * dev, int
     if (num_copies > 255)
         num_copies = 255;
     if (pdev->prev_num_copies != num_copies) {
-        gs_sprintf(str, "%c%dv", LIPS_CSI, num_copies);
+        gs_snprintf(str, sizeof(str), "%c%dv", LIPS_CSI, num_copies);
         lputs(s, str);
         pdev->prev_num_copies = num_copies;
     }
diff -pruN 9.55.0~dfsg-3/contrib/opvp/build_opv_harness.sh 9.56.1~dfsg-1/contrib/opvp/build_opv_harness.sh
--- 9.55.0~dfsg-3/contrib/opvp/build_opv_harness.sh	1970-01-01 00:00:00.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/opvp/build_opv_harness.sh	2022-04-04 13:46:22.000000000 +0000
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+gcc -Werror -Wall -c -fpic -g opvpharness.c
+gcc -shared -o libopv.so opvpharness.o
\ No newline at end of file
diff -pruN 9.55.0~dfsg-3/contrib/opvp/gdevopvp.c 9.56.1~dfsg-1/contrib/opvp/gdevopvp.c
--- 9.55.0~dfsg-3/contrib/opvp/gdevopvp.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/opvp/gdevopvp.c	2022-04-04 13:46:22.000000000 +0000
@@ -115,14 +115,62 @@ typedef struct {
 
 #define MAX_PATH_POINTS 1000
 
-/* driver */
-typedef struct  gx_device_opvp_s {
+/* To remove the existing globals from this device,
+   we place the current globals into a structure
+   and place them at the same offset location
+   for the opvp and oprp devices, allowing easy
+   access regardless of the device type. */
+
+typedef struct opXp_globals_s {
+    bool vector;
+    bool inkjet;
+    bool zoomAuto;
+    bool zooming;
+    bool beginPage;
+    float margins[4];
+    float zoom[2];
+    float shift[2];
+    opvp_int_t outputFD;
+    opvp_int_t nApiEntry;
+    opvp_dc_t printerContext;
+    opvp_cspace_t colorSpace;
+    opvp_cspace_t savedColorSpace;
+    char* vectorDriver;
+    char* printerModel;
+    void* handle;
+    opvp_brush_t* vectorFillColor;
+    opvp_int_t* ErrorNo;
+    opvp_api_procs_t* apiEntry;
+    OPVP_api_procs* apiEntry_0_2;
+    char* jobInfo;
+    char* docInfo;
+    opvp_dc_t(*OpenPrinter)(opvp_int_t, const opvp_char_t*,
+        const opvp_int_t[2], opvp_api_procs_t**);
+    int (*OpenPrinter_0_2)(int, char*, int*,
+        OPVP_api_procs**);
+    int (*GetLastError)(gx_device*);
+} opXp_globals;
+
+typedef struct base_opvp_s {
     gx_device_vector_common;
+} base_opvp;
+
+typedef struct base_oprp_s {
+    gx_device_common;
+    gx_prn_device_common;
+} base_oprp;
+
+typedef struct gx_device_opvp_s {
+    gx_device_vector_common;
+    char padding[1 + (sizeof(base_oprp) > sizeof(base_opvp) ? sizeof(base_oprp) - sizeof(base_opvp) : 0)];
+    opXp_globals globals;
 } gx_device_opvp;
 
-typedef struct  gx_device_oprp_s {
+typedef struct gx_device_oprp_s {
     gx_device_common;
     gx_prn_device_common;
+    char padding[1 + (sizeof(base_opvp) > sizeof(base_oprp) ? sizeof(base_opvp) - sizeof(base_oprp) : 0)];
+    opXp_globals globals;
 } gx_device_oprp;
 
 /* point (internal) */
@@ -135,15 +183,16 @@ typedef struct {
 
 /* Utilities */
 static  int opvp_startpage(gx_device *);
-static  int opvp_endpage(void);
+static  int opvp_endpage(gx_device*);
 static  char *opvp_alloc_string(char **, const char *);
 static  char *opvp_cat_string(char **, const char *);
 static  char *opvp_adjust_num_string(char *);
-static  char **opvp_gen_dynamic_lib_name(void);
+static  char **opvp_gen_dynamic_lib_name(gx_device*);
 static  char *opvp_to_utf8(char *);
-#define opvp_check_in_page(pdev)        \
-                ((beginPage) || (inkjet) ? 0 \
-                    : (*vdev_proc(pdev, beginpage))((gx_device_vector*)pdev))
+#define opvp_check_in_page(opdev)        \
+                 ((((gx_device_opvp*)(opdev))->globals.beginPage) || \
+                  (((gx_device_opvp*)(opdev))->globals.inkjet) ? 0  \
+                    : (*vdev_proc(opdev, beginpage))((gx_device_vector*)opdev))
 static  int opvp_get_papertable_index(gx_device *);
 static  char *opvp_get_sizestring(float, float);
 /* not used     static  const char *opvp_get_papersize_region(gx_device *);*/
@@ -160,8 +209,8 @@ static  int opvp_draw_image(gx_device_op
                             int, int, int, int, int, int, const byte *);
 
 /* load/unload vector driver */
-static  int opvp_load_vector_driver(void);
-static  int opvp_unload_vector_driver(void);
+static  int opvp_load_vector_driver(gx_device*);
+static  int opvp_unload_vector_driver(gx_device*);
 static  int prepare_open(gx_device *);
 
 /* driver procs */
@@ -177,10 +226,10 @@ static  int opvp_copy_mono(gx_device *,
                            gx_color_index, gx_color_index);
 static  int opvp_copy_color(gx_device *, const byte *, int, int,
                             gx_bitmap_id, int, int, int, int);
-static  int _get_params(gs_param_list *);
+static  int _get_params(gx_device*, gs_param_list *);
 static  int opvp_get_params(gx_device *, gs_param_list *);
 static  int oprp_get_params(gx_device *, gs_param_list *);
-static  int _put_params(gs_param_list *);
+static  int _put_params(gx_device*, gs_param_list *);
 static  int opvp_put_params(gx_device *, gs_param_list *);
 static  int oprp_put_params(gx_device *, gs_param_list *);
 static  int opvp_fill_path(gx_device *, const gs_gstate *, gx_path *,
@@ -295,12 +344,60 @@ gs_public_st_suffix_add0_final(
     NULL, /* *jobInfo */\
     NULL /* *docInfo */
 
+
+static int
+GetLastError_1_0(gx_device* dev)
+{
+    gx_device_opvp* pdev = (gx_device_opvp*)dev;
+
+    return *(pdev->globals.ErrorNo);
+}
+
+/* Initialize the globals */
+static void
+InitGlobals(gx_device* dev)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    opdev->globals.vector = true;
+    opdev->globals.inkjet = false;
+    opdev->globals.zoomAuto = false;
+    opdev->globals.zooming = false;
+    opdev->globals.beginPage = false;
+    opdev->globals.margins[0] = 0;
+    opdev->globals.margins[1] = 0;
+    opdev->globals.margins[2] = 0;
+    opdev->globals.margins[3] = 0;
+    opdev->globals.zoom[0] = 1;
+    opdev->globals.zoom[1] = 1;
+    opdev->globals.shift[0] = 0;
+    opdev->globals.shift[1] = 0;
+    opdev->globals.outputFD = -1;
+    opdev->globals.nApiEntry = 0;
+    opdev->globals.printerContext = -1;
+    opdev->globals.colorSpace = OPVP_CSPACE_STANDARDRGB;
+    opdev->globals.savedColorSpace = OPVP_CSPACE_STANDARDRGB;
+    opdev->globals.vectorDriver = NULL;
+    opdev->globals.printerModel = NULL;
+    opdev->globals.handle = NULL;
+    opdev->globals.vectorFillColor = NULL;
+    opdev->globals.ErrorNo = NULL;
+    opdev->globals.apiEntry = NULL;
+    opdev->globals.apiEntry_0_2 = NULL;
+    opdev->globals.jobInfo = NULL;
+    opdev->globals.docInfo = NULL;
+    opdev->globals.OpenPrinter = NULL;
+    opdev->globals.OpenPrinter_0_2 = NULL;
+    opdev->globals.GetLastError = GetLastError_1_0;
+}
+
 /* device procs */
 static void
 opvp_initialize_device_procs(gx_device *dev)
 {
     gdev_prn_initialize_device_procs(dev);
 
+    set_dev_proc(dev, initialize_device, gx_init_non_threadsafe_device);
     set_dev_proc(dev, open_device, opvp_open);
     set_dev_proc(dev, get_initial_matrix, opvp_get_initial_matrix);
     set_dev_proc(dev, output_page, opvp_output_page);
@@ -325,6 +422,9 @@ opvp_initialize_device_procs(gx_device *
      * by the system to the default. For compatibility we do the same. */
     set_dev_proc(dev, encode_color, NULL);
     set_dev_proc(dev, decode_color, NULL);
+
+    /* And the device globals */
+    InitGlobals(dev);
 }
 
 /* vector procs */
@@ -381,6 +481,7 @@ oprp_initialize_device_procs(gx_device *
 {
     gdev_prn_initialize_device_procs(dev);
 
+    set_dev_proc(dev, initialize_device, gx_init_non_threadsafe_device);
     set_dev_proc(dev, open_device, oprp_open);
     set_dev_proc(dev, output_page, opvp_output_page);
     set_dev_proc(dev, close_device, opvp_close);
@@ -394,6 +495,9 @@ oprp_initialize_device_procs(gx_device *
      * by the system to the default. For compatibility we do the same. */
     set_dev_proc(dev, encode_color, NULL);
     set_dev_proc(dev, decode_color, NULL);
+
+    /* And the device globals */
+    InitGlobals(dev);
 }
 
 const gx_device_oprp gs_oprp_device =
@@ -412,43 +516,6 @@ const gx_device_oprp gs_oprp_device =
     )
 };
 
-/* driver mode */
-static bool vector = true;
-static bool inkjet = false;
-static char *vectorDriver = NULL;
-static char *printerModel = NULL;
-static void *handle = NULL;
-static opvp_dc_t (*OpenPrinter)(opvp_int_t,const opvp_char_t*,
-                                  const opvp_int_t[2],
-                                  opvp_api_procs_t**) = NULL;
-static int (*OpenPrinter_0_2)(int,char*,int*,
-                                  OPVP_api_procs**) = NULL;
-static opvp_int_t *ErrorNo = NULL;
-static opvp_int_t outputFD = -1;
-static opvp_int_t nApiEntry = 0;
-static opvp_api_procs_t *apiEntry = NULL;
-static OPVP_api_procs *apiEntry_0_2 = NULL;
-static opvp_dc_t printerContext = -1;
-static char *jobInfo = NULL;
-static char *docInfo = NULL;
-static opvp_cspace_t colorSpace = OPVP_CSPACE_STANDARDRGB;
-static opvp_cspace_t savedColorSpace;
-static opvp_brush_t *vectorFillColor = NULL;
-static float margins[4] = {0, 0, 0, 0};
-static float zoom[2] = {1, 1};
-static float shift[2] = {0, 0};
-static bool zoomAuto = false;
-static bool zooming = false;
-static bool beginPage = false;
-
-static int
-GetLastError_1_0(void)
-{
-    return *ErrorNo;
-}
-
-static int (*GetLastError)(void) = GetLastError_1_0;
-
 /* Wrapper functions that keep compatible with 0.2 */
 
 /* color space mapping 0.2 to 1.0 */
@@ -496,9 +563,11 @@ static int colorDepth_0_2[] = {
 
 /* translate error code */
 static int
-GetLastError_0_2(void)
+GetLastError_0_2(gx_device* dev)
 {
-    switch(*ErrorNo) {
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
+
+    switch(*(opdev->globals.ErrorNo)) {
     case OPVP_FATALERROR_0_2:
         return OPVP_FATALERROR;
         break;
@@ -526,48 +595,51 @@ GetLastError_0_2(void)
 }
 
 static opvp_result_t
-StartPageWrapper(opvp_dc_t printerContext, const opvp_char_t *pageInfo)
+StartPageWrapper(gx_device *dev, opvp_dc_t printerContext, const opvp_char_t *pageInfo)
 {
     int r;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
-    if ((r = apiEntry_0_2->StartPage(printerContext,
+    if ((r = opdev->globals.apiEntry_0_2->StartPage(printerContext,
            /* discard const */(char *)pageInfo)) != OPVP_OK) {
           /* error */
         return r;
     }
     /* initialize ROP */
-    if (apiEntry_0_2->SetROP != NULL) {
-        apiEntry_0_2->SetROP(printerContext,
+    if (opdev->globals.apiEntry_0_2->SetROP != NULL) {
+        opdev->globals.apiEntry_0_2->SetROP(printerContext,
           OPVP_0_2_ROP_P);
     }
     return OPVP_OK;
 }
 
 static opvp_result_t
-InitGSWrapper(opvp_dc_t printerContext)
+InitGSWrapper(gx_device *dev, opvp_dc_t printerContext)
 {
     int r;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
-    if ((r = apiEntry_0_2->InitGS(printerContext)) != OPVP_OK) {
+    if ((r = opdev->globals.apiEntry_0_2->InitGS(printerContext)) != OPVP_OK) {
           /* error */
         return r;
     }
     /* initialize ROP */
-    if (apiEntry_0_2->SetROP != NULL) {
-        apiEntry_0_2->SetROP(printerContext,
+    if (opdev->globals.apiEntry_0_2->SetROP != NULL) {
+        opdev->globals.apiEntry_0_2->SetROP(printerContext,
           OPVP_0_2_ROP_P);
     }
     return OPVP_OK;
 }
 
 static opvp_result_t
-QueryColorSpaceWrapper( opvp_dc_t printerContext, opvp_int_t *pnum,
+QueryColorSpaceWrapper(gx_device *dev, opvp_dc_t printerContext, opvp_int_t *pnum,
     opvp_cspace_t *pcspace)
 {
     int r;
     int i;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
-    if ((r = apiEntry_0_2->QueryColorSpace(printerContext,
+    if ((r = opdev->globals.apiEntry_0_2->QueryColorSpace(printerContext,
          (OPVP_ColorSpace *)pcspace,pnum)) != OPVP_OK) {
         /* error */
         return r;
@@ -587,51 +659,54 @@ QueryColorSpaceWrapper( opvp_dc_t printe
 }
 
 static opvp_result_t
-SetColorSpaceWrapper(opvp_dc_t printerContext, opvp_cspace_t cspace)
+SetColorSpaceWrapper(gx_device *dev, opvp_dc_t printerContext, opvp_cspace_t cspace)
 {
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
+
     if (cspace == OPVP_CSPACE_DEVICEKRGB) {
         /* 0.2 doesn't have OPVP_CSPACE_DEVICEKRGB */
-        *ErrorNo = OPVP_NOTSUPPORTED_0_2;
+        *(opdev->globals.ErrorNo) = OPVP_NOTSUPPORTED_0_2;
         return -1;
     }
     if ((int)cspace
          >= sizeof(cspace_1_0_to_0_2)/sizeof(OPVP_ColorSpace)) {
         /* unknown color space */
-        *ErrorNo = OPVP_PARAMERROR_0_2;
+        *(opdev->globals.ErrorNo) = OPVP_PARAMERROR_0_2;
         return -1;
     }
-    return  apiEntry_0_2->SetColorSpace(printerContext,
+    return  opdev->globals.apiEntry_0_2->SetColorSpace(printerContext,
       cspace_1_0_to_0_2[cspace]);
 }
 
+/* Not used
 static opvp_result_t
-GetColorSpaceWrapper(opvp_dc_t printerContext, opvp_cspace_t *pcspace)
+GetColorSpaceWrapper(gx_device *dev, opvp_dc_t printerContext, opvp_cspace_t *pcspace)
 {
     int r;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
-    if ((r = apiEntry_0_2->GetColorSpace(printerContext,
+    if ((r = opdev->globals.apiEntry_0_2->GetColorSpace(printerContext,
       (OPVP_ColorSpace *)pcspace)) != OPVP_OK) {
-        /* error */
         return r;
     }
     if (*pcspace
          >= sizeof(cspace_0_2_to_1_0)/sizeof(opvp_cspace_t)) {
-        /* unknown color space */
-        /* set DEVICERGB instead */
         *pcspace = OPVP_CSPACE_DEVICERGB;
     } else {
         *pcspace = cspace_0_2_to_1_0[*pcspace];
     }
     return r;
 }
+*/
 
 static opvp_result_t
-SetStrokeColorWrapper(opvp_dc_t printerContext, const opvp_brush_t *brush)
+SetStrokeColorWrapper(gx_device *dev, opvp_dc_t printerContext, const opvp_brush_t *brush)
 {
     OPVP_Brush brush_0_2;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
     if (brush == NULL) {
-        *ErrorNo = OPVP_PARAMERROR_0_2;
+        *(opdev->globals.ErrorNo) = OPVP_PARAMERROR_0_2;
         return -1;
     }
     if (brush->colorSpace == OPVP_CSPACE_DEVICEKRGB) {
@@ -641,7 +716,7 @@ SetStrokeColorWrapper(opvp_dc_t printerC
     if ((int)brush->colorSpace
          >= sizeof(cspace_1_0_to_0_2)/sizeof(OPVP_ColorSpace)) {
         /* unknown color space */
-        *ErrorNo = OPVP_PARAMERROR_0_2;
+        *(opdev->globals.ErrorNo) = OPVP_PARAMERROR_0_2;
         return -1;
     }
     brush_0_2.colorSpace = cspace_1_0_to_0_2[brush->colorSpace];
@@ -649,16 +724,17 @@ SetStrokeColorWrapper(opvp_dc_t printerC
     brush_0_2.yorg = brush->yorg;
     brush_0_2.pbrush = (OPVP_BrushData *)brush->pbrush;
     memcpy(brush_0_2.color,brush->color,sizeof(brush_0_2.color));
-    return apiEntry_0_2->SetStrokeColor(printerContext,&brush_0_2);
+    return opdev->globals.apiEntry_0_2->SetStrokeColor(printerContext, &brush_0_2);
 }
 
 static opvp_result_t
-SetFillColorWrapper(opvp_dc_t printerContext, const opvp_brush_t *brush)
+SetFillColorWrapper(gx_device *dev, opvp_dc_t printerContext, const opvp_brush_t *brush)
 {
     OPVP_Brush brush_0_2;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
     if (brush == NULL) {
-        *ErrorNo = OPVP_PARAMERROR_0_2;
+        *(opdev->globals.ErrorNo) = OPVP_PARAMERROR_0_2;
         return -1;
     }
     if (brush->colorSpace == OPVP_CSPACE_DEVICEKRGB) {
@@ -668,7 +744,7 @@ SetFillColorWrapper(opvp_dc_t printerCon
     if ((int)brush->colorSpace
          >= sizeof(cspace_1_0_to_0_2)/sizeof(OPVP_ColorSpace)) {
         /* unknown color space */
-        *ErrorNo = OPVP_PARAMERROR_0_2;
+        *(opdev->globals.ErrorNo) = OPVP_PARAMERROR_0_2;
         return -1;
     }
     brush_0_2.colorSpace = cspace_1_0_to_0_2[brush->colorSpace];
@@ -676,27 +752,28 @@ SetFillColorWrapper(opvp_dc_t printerCon
     brush_0_2.yorg = brush->yorg;
     brush_0_2.pbrush = (OPVP_BrushData *)brush->pbrush;
     memcpy(brush_0_2.color,brush->color,sizeof(brush_0_2.color));
-    return apiEntry_0_2->SetFillColor(printerContext,&brush_0_2);
+    return opdev->globals.apiEntry_0_2->SetFillColor(printerContext, &brush_0_2);
 }
 
 static opvp_result_t
-SetBgColorWrapper(opvp_dc_t printerContext, const opvp_brush_t *brush)
+SetBgColorWrapper(gx_device *dev, opvp_dc_t printerContext, const opvp_brush_t *brush)
 {
     OPVP_Brush brush_0_2;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
     if (brush == NULL) {
-        *ErrorNo = OPVP_PARAMERROR_0_2;
+        *(opdev->globals.ErrorNo) = OPVP_PARAMERROR_0_2;
         return -1;
     }
     if (brush->colorSpace == OPVP_CSPACE_DEVICEKRGB) {
         /* 0.2 doesn't have OPVP_CSPACE_DEVICEKRGB */
-        *ErrorNo = OPVP_NOTSUPPORTED_0_2;
+        *(opdev->globals.ErrorNo) = OPVP_NOTSUPPORTED_0_2;
         return -1;
     }
     if ((int)brush->colorSpace
          >= sizeof(cspace_1_0_to_0_2)/sizeof(OPVP_ColorSpace)) {
         /* unknown color space */
-        *ErrorNo = OPVP_PARAMERROR_0_2;
+        *(opdev->globals.ErrorNo) = OPVP_PARAMERROR_0_2;
         return -1;
     }
     brush_0_2.colorSpace = cspace_1_0_to_0_2[brush->colorSpace];
@@ -704,11 +781,12 @@ SetBgColorWrapper(opvp_dc_t printerConte
     brush_0_2.yorg = brush->yorg;
     brush_0_2.pbrush = (OPVP_BrushData *)brush->pbrush;
     memcpy(brush_0_2.color,brush->color,sizeof(brush_0_2.color));
-    return apiEntry_0_2->SetBgColor(printerContext,&brush_0_2);
+    return opdev->globals.apiEntry_0_2->SetBgColor(printerContext, &brush_0_2);
 }
 
 static opvp_result_t
 DrawImageWrapper(
+    gx_device *dev,
     opvp_dc_t printerContext,
     opvp_int_t sourceWidth,
     opvp_int_t sourceHeight,
@@ -723,30 +801,31 @@ DrawImageWrapper(
     OPVP_ImageFormat iformat_0_2;
     OPVP_PaintMode paintmode_0_2 = OPVP_paintModeTransparent;
     int depth;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
     if (imageFormat == OPVP_IFORMAT_MASK) {
-        if (apiEntry_0_2->GetPaintMode != NULL) {
-            apiEntry_0_2->GetPaintMode(printerContext,
+        if (opdev->globals.apiEntry_0_2->GetPaintMode != NULL) {
+            opdev->globals.apiEntry_0_2->GetPaintMode(printerContext,
               &paintmode_0_2);
         }
         if (paintmode_0_2 != OPVP_paintModeTransparent) {
-            if (apiEntry_0_2->SetROP != NULL) {
-                apiEntry_0_2->SetROP(printerContext,
+            if (opdev->globals.apiEntry_0_2->SetROP != NULL) {
+                opdev->globals.apiEntry_0_2->SetROP(printerContext,
                     OPVP_0_2_ROP_S);
             }
         }
         else {
-            if (apiEntry_0_2->SetROP != NULL) {
-                apiEntry_0_2->SetROP(printerContext,
+            if (opdev->globals.apiEntry_0_2->SetROP != NULL) {
+                opdev->globals.apiEntry_0_2->SetROP(printerContext,
                     OPVP_0_2_ROP_OR);
             }
         }
         depth = 1;
     } else {
-        if (apiEntry_0_2->SetROP != NULL) {
-            apiEntry_0_2->SetROP(printerContext,OPVP_0_2_ROP_S);
+        if (opdev->globals.apiEntry_0_2->SetROP != NULL) {
+            opdev->globals.apiEntry_0_2->SetROP(printerContext,OPVP_0_2_ROP_S);
         }
-        depth = colorDepth_0_2[colorSpace];
+        depth = colorDepth_0_2[opdev->globals.colorSpace];
     }
 
     OPVP_I2FIX(0,rect.p0.x);
@@ -755,17 +834,17 @@ DrawImageWrapper(
     OPVP_I2FIX(destinationHeight,rect.p1.y);
     if (imageFormat >= sizeof(iformat_1_0_to_0_2)/sizeof(OPVP_ImageFormat)) {
         /* illegal image format */
-        *ErrorNo = OPVP_PARAMERROR_0_2;
+        *(opdev->globals.ErrorNo) = OPVP_PARAMERROR_0_2;
         return -1;
     }
     iformat_0_2 = iformat_1_0_to_0_2[imageFormat];
-    r = apiEntry_0_2->DrawImage(printerContext,sourceWidth,sourceHeight,
+    r = opdev->globals.apiEntry_0_2->DrawImage(printerContext,sourceWidth,sourceHeight,
             depth,iformat_0_2,rect,
             sourcePitch*sourceHeight,
             /* remove const */ (void *)imagedata);
 
-    if (apiEntry_0_2->SetROP != NULL) {
-        apiEntry_0_2->SetROP(printerContext,OPVP_0_2_ROP_P);
+    if (opdev->globals.apiEntry_0_2->SetROP != NULL) {
+        opdev->globals.apiEntry_0_2->SetROP(printerContext,OPVP_0_2_ROP_P);
     }
 
     return r;
@@ -773,6 +852,7 @@ DrawImageWrapper(
 
 static opvp_result_t
 StartDrawImageWrapper(
+    gx_device *dev,
     opvp_dc_t printerContext,
     opvp_int_t sourceWidth,
     opvp_int_t sourceHeight,
@@ -786,28 +866,29 @@ StartDrawImageWrapper(
     OPVP_ImageFormat iformat_0_2;
     OPVP_PaintMode paintmode_0_2 = OPVP_paintModeTransparent;
     int depth;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
     if (imageFormat == OPVP_IFORMAT_MASK) {
-        if (apiEntry_0_2->GetPaintMode != NULL) {
-            apiEntry_0_2->GetPaintMode(printerContext,
+        if (opdev->globals.apiEntry_0_2->GetPaintMode != NULL) {
+            opdev->globals.apiEntry_0_2->GetPaintMode(printerContext,
               &paintmode_0_2);
         }
         if (paintmode_0_2 != OPVP_paintModeTransparent) {
-            if (apiEntry_0_2->SetROP != NULL) {
-                apiEntry_0_2->SetROP(printerContext,OPVP_0_2_ROP_S);
+            if (opdev->globals.apiEntry_0_2->SetROP != NULL) {
+                opdev->globals.apiEntry_0_2->SetROP(printerContext,OPVP_0_2_ROP_S);
             }
         }
         else {
-            if (apiEntry_0_2->SetROP != NULL) {
-                apiEntry_0_2->SetROP(printerContext,OPVP_0_2_ROP_OR);
+            if (opdev->globals.apiEntry_0_2->SetROP != NULL) {
+                opdev->globals.apiEntry_0_2->SetROP(printerContext,OPVP_0_2_ROP_OR);
             }
         }
         depth = 1;
     } else {
-        if (apiEntry_0_2->SetROP != NULL) {
-            apiEntry_0_2->SetROP(printerContext,OPVP_0_2_ROP_S);
+        if (opdev->globals.apiEntry_0_2->SetROP != NULL) {
+            opdev->globals.apiEntry_0_2->SetROP(printerContext,OPVP_0_2_ROP_S);
         }
-        depth = colorDepth_0_2[colorSpace];
+        depth = colorDepth_0_2[opdev->globals.colorSpace];
     }
 
     OPVP_I2FIX(0,rect.p0.x);
@@ -816,310 +897,518 @@ StartDrawImageWrapper(
     OPVP_I2FIX(destinationHeight,rect.p1.y);
     if (imageFormat >= sizeof(iformat_1_0_to_0_2)/sizeof(OPVP_ImageFormat)) {
         /* illegal image format */
-        *ErrorNo = OPVP_PARAMERROR_0_2;
+        *(opdev->globals.ErrorNo) = OPVP_PARAMERROR_0_2;
         return -1;
     }
     iformat_0_2 = iformat_1_0_to_0_2[imageFormat];
-    r = apiEntry_0_2->StartDrawImage(printerContext,
-            sourceWidth,sourceHeight,
-            depth,iformat_0_2,rect);
+    r = opdev->globals.apiEntry_0_2->StartDrawImage(printerContext,
+            sourceWidth, sourceHeight,
+            depth, iformat_0_2, rect);
 
     return r;
 }
 
 static opvp_result_t
-EndDrawImageWrapper(opvp_dc_t printerContext)
+EndDrawImageWrapper(gx_device *dev, opvp_dc_t printerContext)
 {
     int r;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
-    r = apiEntry_0_2->EndDrawImage(printerContext);
+    r = opdev->globals.apiEntry_0_2->EndDrawImage(printerContext);
 
     /* make sure rop is pattern copy */
-    if (apiEntry_0_2->SetROP != NULL) {
-        apiEntry_0_2->SetROP(printerContext,OPVP_0_2_ROP_P);
+    if (opdev->globals.apiEntry_0_2->SetROP != NULL) {
+        opdev->globals.apiEntry_0_2->SetROP(printerContext,OPVP_0_2_ROP_P);
     }
 
     return r;
 }
 
+/* Not used
 static opvp_result_t
 QueryDeviceCapabilityWrapper(
+    gx_device *dev,
     opvp_dc_t printerContext,
     opvp_queryinfoflags_t queryflag,
     opvp_int_t *buflen,
     opvp_char_t *infoBuf)
 {
-    return apiEntry_0_2->QueryDeviceCapability(printerContext,queryflag,
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
+
+    return opdev->globals.apiEntry_0_2->QueryDeviceCapability(printerContext,queryflag,
       *buflen,(char *)infoBuf);
 }
 
 static opvp_result_t
 QueryDeviceInfoWrapper(
+    gx_device *dev,
     opvp_dc_t printerContext,
     opvp_queryinfoflags_t queryflag,
     opvp_int_t *buflen,
     opvp_char_t *infoBuf)
 {
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
+
     if (queryflag & OPVP_QF_MEDIACOPY) {
-        *ErrorNo = OPVP_NOTSUPPORTED;
+        *(opdev->globals.ErrorNo) = OPVP_NOTSUPPORTED;
         return -1;
     }
     if (queryflag & OPVP_QF_PRINTREGION) {
         queryflag &= ~OPVP_QF_PRINTREGION;
         queryflag |= 0x0020000;
     }
-    return apiEntry_0_2->QueryDeviceInfo(printerContext,queryflag,
-      *buflen,(char *)infoBuf);
+    return opdev->globals.apiEntry_0_2->QueryDeviceInfo(printerContext, queryflag,
+      *buflen, (char *)infoBuf);
 }
+*/
 
 static opvp_result_t
-SetLineDashWrapper(opvp_dc_t printerContext, opvp_int_t num,
+SetLineDashWrapper(gx_device *dev, opvp_dc_t printerContext, opvp_int_t num,
     const opvp_fix_t *pdash)
 {
-    return apiEntry_0_2->SetLineDash(printerContext,
-      /* remove const */ (OPVP_Fix *)pdash,num);
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
+
+    return opdev->globals.apiEntry_0_2->SetLineDash(printerContext, num,
+      /* remove const */ (OPVP_Fix *)pdash);
 }
 
+/* Not used
 static opvp_result_t
-GetLineDashWrapper(opvp_dc_t printerContext, opvp_int_t *pnum,
+GetLineDashWrapper(gx_device* dev, opvp_dc_t printerContext, opvp_int_t *pnum,
     opvp_fix_t *pdash)
 {
-    return apiEntry_0_2->GetLineDash(printerContext,
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
+
+    return opdev->globals.apiEntry_0_2->GetLineDash(printerContext,
       pdash,pnum);
 }
+*/
 
 static opvp_dc_t
 OpenPrinterWrapper(
+    gx_device *dev,
     opvp_int_t outputFD,
     const opvp_char_t *printerModel,
     const opvp_int_t apiVersion[2],
     opvp_api_procs_t **apiProcs)
 {
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
+
     opvp_dc_t dc = -1;
 
-    if (OpenPrinter != NULL) {
-        dc = (*OpenPrinter)(outputFD,printerModel,apiVersion,apiProcs);
+    if (opdev->globals.OpenPrinter != NULL) {
+        dc = (*(opdev->globals.OpenPrinter))(outputFD, printerModel, apiVersion, apiProcs);
     } else {
         /* try version 0.2 */
 
-        if (OpenPrinter_0_2 != NULL) {
+        if (opdev->globals.OpenPrinter_0_2 != NULL) {
             static opvp_api_procs_t tEntry;
-            int nApiEntry;
+            int nApiEntry;  /* Alias with prior global. Kept as is */
 
-            dc = (*OpenPrinter_0_2)(outputFD,
+            dc = (*(opdev->globals.OpenPrinter_0_2))(outputFD,
                     /* remove const */
                     (char *)printerModel,
-                    &nApiEntry,&apiEntry_0_2);
+                    &nApiEntry,
+                    &(opdev->globals.apiEntry_0_2));
             /* setting functions */
             tEntry.opvpClosePrinter
-                    = apiEntry_0_2->ClosePrinter;
+                    = opdev->globals.apiEntry_0_2->ClosePrinter;
             tEntry.opvpStartJob
                     = (opvp_result_t (*)(opvp_int_t,
                        const opvp_char_t*))
-                       apiEntry_0_2->StartJob;
-            tEntry.opvpEndJob = apiEntry_0_2->EndJob;
+                       opdev->globals.apiEntry_0_2->StartJob;
+            tEntry.opvpEndJob = opdev->globals.apiEntry_0_2->EndJob;
             tEntry.opvpAbortJob = NULL;
             tEntry.opvpStartDoc
                     = (opvp_result_t (*)(opvp_dc_t,
                        const opvp_char_t*))
-                       apiEntry_0_2->StartDoc;
-            tEntry.opvpEndDoc = apiEntry_0_2->EndDoc;
-            if (apiEntry_0_2->StartPage != NULL) {
-                tEntry.opvpStartPage = StartPageWrapper;
-            } else {
-                tEntry.opvpStartPage = NULL;
-            }
-            tEntry.opvpEndPage = apiEntry_0_2->EndPage;
-
-            if (apiEntry_0_2->QueryDeviceCapability != NULL) {
-                tEntry.opvpQueryDeviceCapability
-                  = QueryDeviceCapabilityWrapper;
-            } else {
-                tEntry.opvpQueryDeviceCapability = NULL;
-            }
-
-            if (apiEntry_0_2->QueryDeviceInfo != NULL) {
-                tEntry.opvpQueryDeviceInfo = QueryDeviceInfoWrapper;
-            } else {
-                tEntry.opvpQueryDeviceInfo = NULL;
-            }
-
-            tEntry.opvpResetCTM = apiEntry_0_2->ResetCTM;
+                       opdev->globals.apiEntry_0_2->StartDoc;
+            tEntry.opvpEndDoc = opdev->globals.apiEntry_0_2->EndDoc;
+            tEntry.opvpStartPage = NULL;
+            tEntry.opvpEndPage = opdev->globals.apiEntry_0_2->EndPage;
+            tEntry.opvpQueryDeviceCapability = NULL;
+            tEntry.opvpQueryDeviceInfo = NULL;
+            tEntry.opvpResetCTM = opdev->globals.apiEntry_0_2->ResetCTM;
             tEntry.opvpSetCTM = (opvp_result_t (*)(opvp_dc_t,
                        const opvp_ctm_t*))
-                       apiEntry_0_2->SetCTM;
+                       opdev->globals.apiEntry_0_2->SetCTM;
             tEntry.opvpGetCTM = (opvp_result_t (*)(opvp_dc_t,opvp_ctm_t*))
-                       apiEntry_0_2->GetCTM;
-            if (apiEntry_0_2->InitGS != NULL) {
-                tEntry.opvpInitGS = InitGSWrapper;
-            } else {
-                tEntry.opvpInitGS = NULL;
-            }
-            tEntry.opvpSaveGS = apiEntry_0_2->SaveGS;
-            tEntry.opvpRestoreGS = apiEntry_0_2->RestoreGS;
-            if (apiEntry_0_2->QueryColorSpace != NULL) {
-                tEntry.opvpQueryColorSpace = QueryColorSpaceWrapper;
-            } else {
-                tEntry.opvpQueryColorSpace = NULL;
-            }
-            if (apiEntry_0_2->SetColorSpace != NULL) {
-                tEntry.opvpSetColorSpace = SetColorSpaceWrapper;
-            } else {
-                tEntry.opvpSetColorSpace = NULL;
-            }
-            if (apiEntry_0_2->GetColorSpace != NULL) {
-                tEntry.opvpGetColorSpace = GetColorSpaceWrapper;
-            } else {
-                tEntry.opvpGetColorSpace = NULL;
-            }
+                opdev->globals.apiEntry_0_2->GetCTM;
+            tEntry.opvpInitGS = NULL;
+            tEntry.opvpSaveGS = opdev->globals.apiEntry_0_2->SaveGS;
+            tEntry.opvpRestoreGS = opdev->globals.apiEntry_0_2->RestoreGS;
+            tEntry.opvpQueryColorSpace = NULL;
+            tEntry.opvpSetColorSpace = NULL;
+            tEntry.opvpGetColorSpace = NULL;
+
             tEntry.opvpSetFillMode
                     = (opvp_result_t (*)(opvp_dc_t,opvp_fillmode_t))
-                       apiEntry_0_2->SetFillMode;
+                opdev->globals.apiEntry_0_2->SetFillMode;
             tEntry.opvpGetFillMode
                     = (opvp_result_t (*)(opvp_dc_t,opvp_fillmode_t*))
-                       apiEntry_0_2->GetFillMode;
-            tEntry.opvpSetAlphaConstant = apiEntry_0_2->SetAlphaConstant;
-            tEntry.opvpGetAlphaConstant = apiEntry_0_2->GetAlphaConstant;
-            tEntry.opvpSetLineWidth = apiEntry_0_2->SetLineWidth;
-            tEntry.opvpGetLineWidth = apiEntry_0_2->GetLineWidth;
-            if (apiEntry_0_2->SetLineDash != NULL) {
-                tEntry.opvpSetLineDash = SetLineDashWrapper;
-            } else {
-                tEntry.opvpSetLineDash = NULL;
-            }
-            if (apiEntry_0_2->GetLineDash != NULL) {
-                tEntry.opvpGetLineDash = GetLineDashWrapper;
-            } else {
-                tEntry.opvpGetLineDash = NULL;
-            }
+                opdev->globals.apiEntry_0_2->GetFillMode;
+            tEntry.opvpSetAlphaConstant = opdev->globals.apiEntry_0_2->SetAlphaConstant;
+            tEntry.opvpGetAlphaConstant = opdev->globals.apiEntry_0_2->GetAlphaConstant;
+            tEntry.opvpSetLineWidth = opdev->globals.apiEntry_0_2->SetLineWidth;
+            tEntry.opvpGetLineWidth = opdev->globals.apiEntry_0_2->GetLineWidth;
+            tEntry.opvpSetLineDash = NULL;
+            tEntry.opvpGetLineDash = NULL;
+
             tEntry.opvpSetLineDashOffset
-                    = apiEntry_0_2->SetLineDashOffset;
+                    = opdev->globals.apiEntry_0_2->SetLineDashOffset;
             tEntry.opvpGetLineDashOffset
-                    = apiEntry_0_2->GetLineDashOffset;
+                    = opdev->globals.apiEntry_0_2->GetLineDashOffset;
             tEntry.opvpSetLineStyle
                     = (opvp_result_t (*)(opvp_dc_t,opvp_linestyle_t))
-                       apiEntry_0_2->SetLineStyle;
+                opdev->globals.apiEntry_0_2->SetLineStyle;
             tEntry.opvpGetLineStyle
                     = (opvp_result_t (*)(opvp_dc_t,opvp_linestyle_t*))
-                       apiEntry_0_2->GetLineStyle;
+                opdev->globals.apiEntry_0_2->GetLineStyle;
             tEntry.opvpSetLineCap
                     = (opvp_result_t (*)(opvp_dc_t,opvp_linecap_t))
-                       apiEntry_0_2->SetLineCap;
+                opdev->globals.apiEntry_0_2->SetLineCap;
             tEntry.opvpGetLineCap
                     = (opvp_result_t (*)(opvp_dc_t,opvp_linecap_t*))
-                       apiEntry_0_2->GetLineCap;
+                opdev->globals.apiEntry_0_2->GetLineCap;
             tEntry.opvpSetLineJoin
                     = (opvp_result_t (*)(opvp_dc_t,opvp_linejoin_t))
-                       apiEntry_0_2->SetLineJoin;
+                opdev->globals.apiEntry_0_2->SetLineJoin;
             tEntry.opvpGetLineJoin
                     = (opvp_result_t (*)(opvp_dc_t,opvp_linejoin_t*))
-                       apiEntry_0_2->GetLineJoin;
-            tEntry.opvpSetMiterLimit = apiEntry_0_2->SetMiterLimit;
-            tEntry.opvpGetMiterLimit = apiEntry_0_2->GetMiterLimit;
+                opdev->globals.apiEntry_0_2->GetLineJoin;
+            tEntry.opvpSetMiterLimit = opdev->globals.apiEntry_0_2->SetMiterLimit;
+            tEntry.opvpGetMiterLimit = opdev->globals.apiEntry_0_2->GetMiterLimit;
             tEntry.opvpSetPaintMode
                     = (opvp_result_t (*)(opvp_dc_t,opvp_paintmode_t))
-                       apiEntry_0_2->SetPaintMode;
+                opdev->globals.apiEntry_0_2->SetPaintMode;
             tEntry.opvpGetPaintMode
                     = (opvp_result_t (*)(opvp_dc_t,opvp_paintmode_t*))
-                       apiEntry_0_2->GetPaintMode;
-            if (apiEntry_0_2->SetStrokeColor != NULL) {
-                tEntry.opvpSetStrokeColor = SetStrokeColorWrapper;
-            } else {
-                tEntry.opvpSetStrokeColor = NULL;
-            }
-            if (apiEntry_0_2->SetFillColor != NULL) {
-                tEntry.opvpSetFillColor = SetFillColorWrapper;
-            } else {
-                tEntry.opvpSetFillColor = NULL;
-            }
-            if (apiEntry_0_2->SetBgColor != NULL) {
-                tEntry.opvpSetBgColor = SetBgColorWrapper;
-            } else {
-                tEntry.opvpSetBgColor = NULL;
-            }
-            tEntry.opvpNewPath = apiEntry_0_2->NewPath;
-            tEntry.opvpEndPath = apiEntry_0_2->EndPath;
-            tEntry.opvpStrokePath = apiEntry_0_2->StrokePath;
-            tEntry.opvpFillPath = apiEntry_0_2->FillPath;
-            tEntry.opvpStrokeFillPath = apiEntry_0_2->StrokeFillPath;
+                opdev->globals.apiEntry_0_2->GetPaintMode;
+            tEntry.opvpSetStrokeColor = NULL;
+            tEntry.opvpSetFillColor = NULL;
+            tEntry.opvpSetBgColor = NULL;
+            tEntry.opvpNewPath = opdev->globals.apiEntry_0_2->NewPath;
+            tEntry.opvpEndPath = opdev->globals.apiEntry_0_2->EndPath;
+            tEntry.opvpStrokePath = opdev->globals.apiEntry_0_2->StrokePath;
+            tEntry.opvpFillPath = opdev->globals.apiEntry_0_2->FillPath;
+            tEntry.opvpStrokeFillPath = opdev->globals.apiEntry_0_2->StrokeFillPath;
             tEntry.opvpSetClipPath
                     = (opvp_result_t (*)(opvp_dc_t,opvp_cliprule_t))
-                       apiEntry_0_2->SetClipPath;
-            tEntry.opvpResetClipPath = apiEntry_0_2->ResetClipPath;
-            tEntry.opvpSetCurrentPoint = apiEntry_0_2->SetCurrentPoint;
+                opdev->globals.apiEntry_0_2->SetClipPath;
+            tEntry.opvpResetClipPath = opdev->globals.apiEntry_0_2->ResetClipPath;
+            tEntry.opvpSetCurrentPoint = opdev->globals.apiEntry_0_2->SetCurrentPoint;
             tEntry.opvpLinePath
                     = (opvp_result_t (*)(opvp_dc_t,
                        opvp_pathmode_t,opvp_int_t,
                        const opvp_point_t*))
-                       apiEntry_0_2->LinePath;
+                opdev->globals.apiEntry_0_2->LinePath;
             tEntry.opvpPolygonPath
                     = (opvp_result_t (*)(opvp_dc_t,opvp_int_t,
                        const opvp_int_t*,
                        const opvp_point_t*))
-                       apiEntry_0_2->PolygonPath;
+                opdev->globals.apiEntry_0_2->PolygonPath;
             tEntry.opvpRectanglePath
                     = (opvp_result_t (*)(opvp_dc_t,opvp_int_t,
                        const opvp_rectangle_t*))
-                       apiEntry_0_2->RectanglePath;
+                opdev->globals.apiEntry_0_2->RectanglePath;
             tEntry.opvpRoundRectanglePath
                     = (opvp_result_t (*)(opvp_dc_t,opvp_int_t,
                        const opvp_roundrectangle_t*))
-                       apiEntry_0_2->RoundRectanglePath;
+                opdev->globals.apiEntry_0_2->RoundRectanglePath;
             tEntry.opvpBezierPath
                     = (opvp_result_t (*)(opvp_dc_t,opvp_int_t,
                        const opvp_point_t*))
-                       apiEntry_0_2->BezierPath;
+                opdev->globals.apiEntry_0_2->BezierPath;
             tEntry.opvpArcPath
                     = (opvp_result_t (*)(opvp_dc_t,opvp_arcmode_t,
                        opvp_arcdir_t,opvp_fix_t,opvp_fix_t,opvp_fix_t,
                        opvp_fix_t,opvp_fix_t,opvp_fix_t,opvp_fix_t,
-                       opvp_fix_t))apiEntry_0_2->ArcPath;
-            if (apiEntry_0_2->DrawImage != NULL) {
-                tEntry.opvpDrawImage = DrawImageWrapper;
-            } else {
-                tEntry.opvpDrawImage = NULL;
-            }
-            if (apiEntry_0_2->StartDrawImage != NULL) {
-                tEntry.opvpStartDrawImage = StartDrawImageWrapper;
-            } else {
-                tEntry.opvpStartDrawImage = NULL;
-            }
+                       opvp_fix_t))opdev->globals.apiEntry_0_2->ArcPath;
+            tEntry.opvpDrawImage = NULL;
+            tEntry.opvpStartDrawImage = NULL;
             tEntry.opvpTransferDrawImage =
                (opvp_result_t (*)(opvp_dc_t,opvp_int_t,const void*))
-               apiEntry_0_2->TransferDrawImage;
-            if (apiEntry_0_2->EndDrawImage != NULL) {
-                tEntry.opvpEndDrawImage = EndDrawImageWrapper;
-            } else {
-                tEntry.opvpEndDrawImage = NULL;
-            }
-            tEntry.opvpStartScanline = apiEntry_0_2->StartScanline;
+                opdev->globals.apiEntry_0_2->TransferDrawImage;
+            tEntry.opvpEndDrawImage = NULL;
+            tEntry.opvpStartScanline = opdev->globals.apiEntry_0_2->StartScanline;
             tEntry.opvpScanline
                     = (opvp_result_t (*)(opvp_dc_t,opvp_int_t,
                        const opvp_int_t*))
-                       apiEntry_0_2->Scanline;
-            tEntry.opvpEndScanline = apiEntry_0_2->EndScanline;
-            tEntry.opvpStartRaster = apiEntry_0_2->StartRaster;
+                opdev->globals.apiEntry_0_2->Scanline;
+            tEntry.opvpEndScanline = opdev->globals.apiEntry_0_2->EndScanline;
+            tEntry.opvpStartRaster = opdev->globals.apiEntry_0_2->StartRaster;
             tEntry.opvpTransferRasterData
                     = (opvp_result_t (*)(opvp_dc_t,opvp_int_t,
                        const opvp_byte_t*))
-                       apiEntry_0_2->TransferRasterData;
-            tEntry.opvpSkipRaster = apiEntry_0_2->SkipRaster;
-            tEntry.opvpEndRaster = apiEntry_0_2->EndRaster;
-            tEntry.opvpStartStream = apiEntry_0_2->StartStream;
+                opdev->globals.apiEntry_0_2->TransferRasterData;
+            tEntry.opvpSkipRaster = opdev->globals.apiEntry_0_2->SkipRaster;
+            tEntry.opvpEndRaster = opdev->globals.apiEntry_0_2->EndRaster;
+            tEntry.opvpStartStream = opdev->globals.apiEntry_0_2->StartStream;
             tEntry.opvpTransferStreamData
                     = (opvp_result_t (*)(opvp_dc_t,opvp_int_t,
                        const void *))
-                       apiEntry_0_2->TransferStreamData;
-            tEntry.opvpEndStream = apiEntry_0_2->EndStream;
+                opdev->globals.apiEntry_0_2->TransferStreamData;
+            tEntry.opvpEndStream = opdev->globals.apiEntry_0_2->EndStream;
 
             *apiProcs = &tEntry;
 
-            GetLastError = GetLastError_0_2;
+            opdev->globals.GetLastError = GetLastError_0_2;
         }
     }
     return dc;
 }
 
+/* Set of methods to separate the 0.2 and 1.0 calls AND deal with the prior use of globals */
+static opvp_result_t
+gsopvpStartPage(gx_device* dev, opvp_dc_t printerContext, const opvp_char_t* pageInfo)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->StartPage) {
+        return StartPageWrapper(dev, printerContext, pageInfo);
+    } else if (opdev->globals.apiEntry->opvpStartPage) {
+        return opdev->globals.apiEntry->opvpStartPage(printerContext, pageInfo);
+    } else
+        return OPVP_FATALERROR;
+}
+
+/* Not used
+static opvp_result_t
+gsopvpQueryDeviceCapability(
+    gx_device* dev,
+    opvp_dc_t printerContext,
+    opvp_queryinfoflags_t queryflag,
+    opvp_int_t* buflen,
+    opvp_char_t* infoBuf)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->QueryDeviceCapability) {
+        return QueryDeviceCapabilityWrapper(dev, printerContext, queryflag, buflen, infoBuf);
+    } else if (opdev->globals.apiEntry->opvpQueryDeviceCapability) {
+        return opdev->globals.apiEntry->opvpQueryDeviceCapability(printerContext, queryflag, buflen, infoBuf);
+    } else
+        return OPVP_FATALERROR;
+}
+
+static opvp_result_t
+gsopvpQueryDeviceInfo(
+    gx_device* dev,
+    opvp_dc_t printerContext,
+    opvp_queryinfoflags_t queryflag,
+    opvp_int_t* buflen,
+    opvp_char_t* infoBuf)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->QueryDeviceCapability) {
+        return QueryDeviceInfoWrapper(dev, printerContext, queryflag, buflen, infoBuf);
+    } else if (opdev->globals.apiEntry->opvpQueryDeviceCapability) {
+        return opdev->globals.apiEntry->opvpQueryDeviceInfo(printerContext, queryflag, buflen, infoBuf);
+    } else
+        return OPVP_FATALERROR;
+}
+*/
+
+static opvp_result_t
+gsopvpInitGS(gx_device* dev, opvp_dc_t printerContext)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->InitGS) {
+        return InitGSWrapper(dev, printerContext);
+    } else if (opdev->globals.apiEntry->opvpInitGS) {
+        return opdev->globals.apiEntry->opvpInitGS(printerContext);
+    } else
+        return OPVP_FATALERROR;
+}
+
+static opvp_result_t
+gsopvpQueryColorSpace(gx_device* dev, opvp_dc_t printerContext, opvp_int_t* pnum,
+    opvp_cspace_t* pcspace)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->QueryColorSpace) {
+        return QueryColorSpaceWrapper(dev, printerContext, pnum, pcspace);
+    } else if (opdev->globals.apiEntry->opvpQueryColorSpace) {
+        return opdev->globals.apiEntry->opvpQueryColorSpace(printerContext, pnum, pcspace);
+    } else
+        return OPVP_FATALERROR;
+}
+
+static opvp_result_t
+gsopvpSetColorSpace(gx_device* dev, opvp_dc_t printerContext, opvp_cspace_t cspace)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->SetColorSpace) {
+        return SetColorSpaceWrapper(dev, printerContext, cspace);
+    } else if (opdev->globals.apiEntry->opvpQueryColorSpace) {
+        return opdev->globals.apiEntry->opvpSetColorSpace(printerContext, cspace);
+    } else
+        return OPVP_FATALERROR;
+}
+
+/* Not used
+static opvp_result_t
+gsopvpGetColorSpace(gx_device* dev, opvp_dc_t printerContext, opvp_cspace_t* pcspace)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->GetColorSpace) {
+        return GetColorSpaceWrapper(dev, printerContext, pcspace);
+    } else if (opdev->globals.apiEntry->opvpGetColorSpace) {
+        return opdev->globals.apiEntry->opvpGetColorSpace(printerContext, pcspace);
+    } else
+        return OPVP_FATALERROR;
+}
+*/
+
+static opvp_result_t
+gsopvpSetLineDash(gx_device* dev, opvp_dc_t printerContext, opvp_int_t num,
+    const opvp_fix_t* pdash)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->SetLineDash) {
+        return SetLineDashWrapper(dev, printerContext, num, pdash);
+    } else if (opdev->globals.apiEntry->opvpSetLineDash) {
+        return opdev->globals.apiEntry->opvpSetLineDash(printerContext, num, pdash);
+    } else
+        return OPVP_FATALERROR;
+}
+
+/* Not used
+static opvp_result_t
+gsopvpGetLineDash(gx_device* dev, opvp_dc_t printerContext, opvp_int_t* pnum,
+    opvp_fix_t* pdash)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->GetLineDash) {
+        return GetLineDashWrapper(dev, printerContext, pnum, pdash);
+    } else if (opdev->globals.apiEntry->opvpGetLineDash) {
+        return opdev->globals.apiEntry->opvpGetLineDash(printerContext, pnum, pdash);
+    } else
+        return OPVP_FATALERROR;
+}
+*/
+
+static opvp_result_t
+gsopvpSetStrokeColor(gx_device* dev, opvp_dc_t printerContext, const opvp_brush_t* brush)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->SetStrokeColor) {
+        return SetStrokeColorWrapper(dev, printerContext, brush);
+    } else if (opdev->globals.apiEntry->opvpSetStrokeColor) {
+        return opdev->globals.apiEntry->opvpSetStrokeColor(printerContext, brush);
+    } else
+        return OPVP_FATALERROR;
+}
+
+static opvp_result_t
+gsopvpSetFillColor(gx_device* dev, opvp_dc_t printerContext, const opvp_brush_t* brush)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->SetFillColor) {
+        return SetFillColorWrapper(dev, printerContext, brush);
+    } else if (opdev->globals.apiEntry->opvpSetFillColor) {
+        return opdev->globals.apiEntry->opvpSetFillColor(printerContext, brush);
+    } else
+        return OPVP_FATALERROR;
+}
+
+static opvp_result_t
+gsopvpSetBgColor(gx_device* dev, opvp_dc_t printerContext, const opvp_brush_t* brush)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->SetBgColor) {
+        return SetBgColorWrapper(dev, printerContext, brush);
+    } else if (opdev->globals.apiEntry->opvpSetBgColor) {
+        return opdev->globals.apiEntry->opvpSetBgColor(printerContext, brush);
+    } else
+        return OPVP_FATALERROR;
+}
+
+static opvp_result_t
+gsopvpDrawImage(
+    gx_device* dev,
+    opvp_dc_t printerContext,
+    opvp_int_t sourceWidth,
+    opvp_int_t sourceHeight,
+    opvp_int_t sourcePitch,
+    opvp_imageformat_t imageFormat,
+    opvp_int_t destinationWidth,
+    opvp_int_t destinationHeight,
+    const void* imagedata)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->DrawImage) {
+        return DrawImageWrapper(dev, printerContext, sourceWidth, sourceHeight,
+            sourcePitch, imageFormat, destinationWidth, destinationHeight, imagedata);
+    } else if (opdev->globals.apiEntry->opvpDrawImage) {
+        return opdev->globals.apiEntry->opvpDrawImage(printerContext, sourceWidth, sourceHeight,
+            sourcePitch, imageFormat, destinationWidth, destinationHeight, imagedata);
+    } else
+        return OPVP_FATALERROR;
+}
+
+static opvp_result_t
+gsopvpStartDrawImage(
+    gx_device* dev,
+    opvp_dc_t printerContext,
+    opvp_int_t sourceWidth,
+    opvp_int_t sourceHeight,
+    opvp_int_t sourcePitch,
+    opvp_imageformat_t imageFormat,
+    opvp_int_t destinationWidth,
+    opvp_int_t destinationHeight)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->StartDrawImage) {
+        return StartDrawImageWrapper(dev, printerContext, sourceWidth, sourceHeight,
+            sourcePitch, imageFormat, destinationWidth, destinationHeight);
+    } else if (opdev->globals.apiEntry->opvpStartDrawImage) {
+        return opdev->globals.apiEntry->opvpStartDrawImage(printerContext, sourceWidth, sourceHeight,
+            sourcePitch, imageFormat, destinationWidth, destinationHeight);
+    } else
+        return OPVP_FATALERROR;
+}
+
+static opvp_result_t
+gsopvpEndDrawImage(gx_device* dev, opvp_dc_t printerContext)
+{
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.apiEntry_0_2 != NULL &&
+        opdev->globals.apiEntry_0_2->EndDrawImage) {
+        return EndDrawImageWrapper(dev, printerContext);
+    } else if (opdev->globals.apiEntry->opvpEndDrawImage) {
+        return opdev->globals.apiEntry->opvpEndDrawImage(printerContext);
+    } else
+        return OPVP_FATALERROR;
+}
+
 /* for image */
 static  const
 gx_image_enum_procs_t opvp_image_enum_procs =
@@ -1167,27 +1456,28 @@ typedef struct bbox_image_enum_s {
 /* initialize Graphic State */
 /* No defaults in OPVP 1.0 */
 static int
-InitGS(void)
+InitGS(gx_device* dev)
 {
-    if (apiEntry->opvpInitGS != NULL) {
-        if (apiEntry->opvpInitGS(printerContext) != OPVP_OK) {
-            return -1;
-        }
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
+
+    if (gsopvpInitGS(dev, opdev->globals.printerContext) != OPVP_OK) {
+        return -1;
     }
-    if (apiEntry->opvpSetColorSpace != NULL) {
-        if (apiEntry->opvpSetColorSpace(printerContext,colorSpace)
-           != OPVP_OK) {
+
+    if (opdev->globals.apiEntry->opvpSetColorSpace != NULL) {
+        if (opdev->globals.apiEntry->opvpSetColorSpace(opdev->globals.printerContext,
+            opdev->globals.colorSpace) != OPVP_OK){
             return -1;
         }
     }
-    if (apiEntry->opvpSetPaintMode != NULL) {
-        if (apiEntry->opvpSetPaintMode(printerContext,
+    if (opdev->globals.apiEntry->opvpSetPaintMode != NULL) {
+        if (opdev->globals.apiEntry->opvpSetPaintMode(opdev->globals.printerContext,
             OPVP_PAINTMODE_TRANSPARENT) != OPVP_OK) {
             return -1;
         }
     }
-    if (apiEntry->opvpSetAlphaConstant != NULL) {
-        if (apiEntry->opvpSetAlphaConstant(printerContext,1.0)
+    if (opdev->globals.apiEntry->opvpSetAlphaConstant != NULL) {
+        if (opdev->globals.apiEntry->opvpSetAlphaConstant(opdev->globals.printerContext,1.0)
            != OPVP_OK) {
             return -1;
         }
@@ -1203,20 +1493,20 @@ opvp_startpage(gx_device *dev)
     int ecode = 0;
     opvp_result_t r = -1;
     static char *page_info = NULL;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
     /* page info */
     page_info = opvp_alloc_string(&page_info, OPVP_INFO_PREFIX);
     page_info = opvp_cat_string(&page_info, opvp_gen_page_info(dev));
 
     /* call StartPage */
-    if (printerContext != -1) {
-        if (apiEntry->opvpStartPage)
-            r = apiEntry->opvpStartPage(printerContext,
-                       (opvp_char_t *)opvp_to_utf8(page_info));
+    if (opdev->globals.printerContext != -1) {
+        r = gsopvpStartPage(dev, opdev->globals.printerContext,
+                (opvp_char_t*)opvp_to_utf8(page_info));
         if (r != OPVP_OK) {
             ecode = -1;
         } else {
-            ecode = InitGS();
+            ecode = InitGS(dev);
         }
     }
 
@@ -1224,15 +1514,16 @@ opvp_startpage(gx_device *dev)
 }
 
 static  int
-opvp_endpage(void)
+opvp_endpage(gx_device* dev)
 {
     int ecode = 0;
     opvp_result_t r = -1;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
     /* call EndPage */
-    if (printerContext != -1) {
-        if (apiEntry->opvpEndPage)
-            r = apiEntry->opvpEndPage(printerContext);
+    if (opdev->globals.printerContext != -1) {
+        if (opdev->globals.apiEntry->opvpEndPage)
+            r = opdev->globals.apiEntry->opvpEndPage(opdev->globals.printerContext);
         if (r != OPVP_OK) {
             ecode = -1;
         }
@@ -1304,32 +1595,33 @@ opvp_adjust_num_string(char *num_string)
 }
 
 static  char **
-opvp_gen_dynamic_lib_name(void)
+opvp_gen_dynamic_lib_name(gx_device* dev)
 {
     static char *buff[5] = {NULL,NULL,NULL,NULL,NULL};
     char tbuff[OPVP_BUFF_SIZE];
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
-    if (!vectorDriver) {
+    if (!(opdev->globals.vectorDriver)) {
         return NULL;
     }
 
     memset((void*)tbuff, 0, OPVP_BUFF_SIZE);
-    strncpy(tbuff, vectorDriver, OPVP_BUFF_SIZE - 1);
+    strncpy(tbuff, opdev->globals.vectorDriver, OPVP_BUFF_SIZE - 1);
     opvp_alloc_string(&(buff[0]), tbuff);
 
     memset((void*)tbuff, 0, OPVP_BUFF_SIZE);
-    strncpy(tbuff, vectorDriver, OPVP_BUFF_SIZE - 4);
+    strncpy(tbuff, opdev->globals.vectorDriver, OPVP_BUFF_SIZE - 4);
     strcat(tbuff, ".so");
     opvp_alloc_string(&(buff[1]), tbuff);
 
     memset((void*)tbuff, 0, OPVP_BUFF_SIZE);
-    strncpy(tbuff, vectorDriver, OPVP_BUFF_SIZE - 5);
+    strncpy(tbuff, opdev->globals.vectorDriver, OPVP_BUFF_SIZE - 5);
     strcat(tbuff, ".dll");
     opvp_alloc_string(&(buff[2]), tbuff);
 
     memset((void*)tbuff, 0, OPVP_BUFF_SIZE);
     strcpy(tbuff, "lib");
-    strncat(tbuff, vectorDriver, OPVP_BUFF_SIZE - 7);
+    strncat(tbuff, opdev->globals.vectorDriver, OPVP_BUFF_SIZE - 7);
     strcat(tbuff, ".so");
     opvp_alloc_string(&(buff[3]), tbuff);
 
@@ -1387,7 +1679,7 @@ opvp_to_utf8(char *string)
 }
 
 static float
-opvp_fabsf(float f)
+opvp_fabsf(gx_device* dev, float f)
 {
     return (float)fabs((double)f);
 }
@@ -1429,7 +1721,7 @@ opvp_get_papertable_index(gx_device *pde
                 paper = i;
                 match = true;
                 break;
-            } else if ((f = opvp_fabsf(height - paper_h)) < TOLERANCE) {
+            } else if ((f = opvp_fabsf(pdev, height - paper_h)) < TOLERANCE) {
                 if (f < h_delta) {
                     h_delta = f;
                     candidate = i;
@@ -1442,14 +1734,14 @@ opvp_get_papertable_index(gx_device *pde
         } else if (prev != paper_w) {
             prev = paper_w;
             if (paper_w < width) {
-                if ((f = opvp_fabsf(width - paper_w)) < TOLERANCE) {
+                if ((f = opvp_fabsf(pdev, width - paper_w)) < TOLERANCE) {
                     if (f < sw_delta) {
                         sw_delta = f;
                         smaller  = i;
                     }
                 }
             } else {
-                if ((f = opvp_fabsf(width - paper_w)) < TOLERANCE) {
+                if ((f = opvp_fabsf(pdev, width - paper_w)) < TOLERANCE) {
                     if (f < lw_delta) {
                         lw_delta = f;
                         larger   = i;
@@ -1468,7 +1760,7 @@ opvp_get_papertable_index(gx_device *pde
                     sh_delta = 0;
                     s_candi  = i;
                     break;
-                } else if ((f = opvp_fabsf(height - paper_h)) < TOLERANCE) {
+                } else if ((f = opvp_fabsf(pdev, height - paper_h)) < TOLERANCE) {
                     if (f < sh_delta) {
                         sh_delta = f;
                         s_candi  = i;
@@ -1484,7 +1776,7 @@ opvp_get_papertable_index(gx_device *pde
                     lh_delta = 0;
                     l_candi  = i;
                     break;
-                } else if ((f = opvp_fabsf(height - paper_h)) < TOLERANCE) {
+                } else if ((f = opvp_fabsf(pdev, height - paper_h)) < TOLERANCE) {
                     if (f < lh_delta) {
                         lh_delta = f;
                         l_candi  = i;
@@ -1560,8 +1852,8 @@ opvp_get_mediasize(gx_device *pdev)
            (strcmp(region, "oe"  ) == 0)) {
             unit    = "in";
         } else {
-            width  *= MMPI;
-            height *= MMPI;
+            width  *= (float) MMPI;
+            height *= (float) MMPI;
             unit    = "mm";
         }
     } else {
@@ -1591,9 +1883,10 @@ opvp_gen_page_info(gx_device *dev)
     int num_copies = 1;
     bool landscape;
     char tbuff[OPVP_BUFF_SIZE];
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
     /* copies */
-    if (!inkjet) {
+    if (!(opdev->globals.inkjet)) {
         if (dev->IgnoreNumCopies) {
             num_copies = 1;
         } else if (dev->NumCopies_set > 0) {
@@ -1642,14 +1935,13 @@ opvp_set_brush_color(gx_device_opvp *pde
         ecode = -1;
     } else {
 #if ENABLE_SIMPLE_MODE
-        brush->colorSpace = colorSpace;
+        brush->colorSpace = pdev->globals.colorSpace;
 #else
         opvp_result_t           r = -1;
         /* call GetColorSpace */
-        if (apiEntry->opvpGetColorSpace) {
-            r = apiEntry->opvpGetColorSpace(printerContext,
-                                   &(brush->colorSpace));
-        }
+
+        r = gsopvpGetColorSpace((gx_device*)pdev, printerContext,
+                                &(brush->colorSpace));
         if (r != OPVP_OK) {
             brush->colorSpace = OPVP_CSPACE_DEVICEKRGB;
         }
@@ -1667,7 +1959,7 @@ opvp_set_brush_color(gx_device_opvp *pde
 
 static  int
 opvp_draw_image(
-    gx_device_opvp *pdev,
+    gx_device_opvp *opdev,
     int depth,
     int sw,
     int sh,
@@ -1682,35 +1974,36 @@ opvp_draw_image(
     int                 count;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* image size */
     count = raster * sh;
 
     /* call DrawImage */
-    if (apiEntry->opvpDrawImage) {
-        r = apiEntry->opvpDrawImage(printerContext,
-               sw,sh,
-               raster,
-               mask ? OPVP_IFORMAT_MASK : OPVP_IFORMAT_RAW,
-               dw,dh,
-               /* discard 'const' qualifier */
-               (void *)data);
-    }
+    r = gsopvpDrawImage((gx_device*) opdev,
+            opdev->globals.printerContext,
+            sw,sh,
+            raster,
+            mask ? OPVP_IFORMAT_MASK : OPVP_IFORMAT_RAW,
+            dw,dh,
+            /* discard 'const' qualifier */
+            (void *)data);
+
     if (r != OPVP_OK) {
         /* call StartDrawImage */
-        if (apiEntry->opvpStartDrawImage) {
-            r = apiEntry->opvpStartDrawImage(printerContext,
-                    sw,sh,
-                    raster,
-                    mask ? OPVP_IFORMAT_MASK : OPVP_IFORMAT_RAW,
-                    dw,dh);
-        }
+        r = gsopvpStartDrawImage((gx_device*)opdev,
+                opdev->globals.printerContext,
+                sw,sh,
+                raster,
+                mask ? OPVP_IFORMAT_MASK : OPVP_IFORMAT_RAW,
+                dw,dh);
+
         if (r == OPVP_OK) {
             /* call TansferDrawImage */
-            if (apiEntry->opvpTransferDrawImage) {
-                r = apiEntry->opvpTransferDrawImage(
-                       printerContext,
+            if (opdev->globals.apiEntry->opvpTransferDrawImage) {
+                r = opdev->globals.apiEntry->opvpTransferDrawImage(
+                       opdev->globals.printerContext,
                        count,
                         /* discard 'const' qualifier */
                        (void *)data);
@@ -1718,9 +2011,8 @@ opvp_draw_image(
             if (r != OPVP_OK) ecode = -1;
 
             /* call EndDrawImage */
-            if (apiEntry->opvpEndDrawImage) {
-                apiEntry->opvpEndDrawImage(printerContext);
-            }
+            gsopvpEndDrawImage((gx_device*)opdev, opdev->globals.printerContext);
+
         } else {
             ecode = 0;  /* continue... */
         }
@@ -1735,48 +2027,49 @@ opvp_draw_image(
  * load vector-driver
  */
 static  int
-opvp_load_vector_driver(void)
+opvp_load_vector_driver(gx_device* dev)
 {
     char **list = NULL;
     int i;
     void *h;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
-    if (handle) {
-        opvp_unload_vector_driver();
+    if (opdev->globals.handle) {
+        opvp_unload_vector_driver(dev);
     }
 
-    if (vectorDriver) {
-        list = opvp_gen_dynamic_lib_name();
+    if (opdev->globals.vectorDriver) {
+        list = opvp_gen_dynamic_lib_name(dev);
     }
 
     if (list) {
         i = 0;
         while (list[i]) {
             if ((h = dlopen(list[i],RTLD_NOW))) {
-                OpenPrinter = dlsym(h,"opvpOpenPrinter");
-                ErrorNo = dlsym(h,"opvpErrorNo");
-                if (OpenPrinter && ErrorNo) {
-                    handle = h;
+                opdev->globals.OpenPrinter = dlsym(h,"opvpOpenPrinter");
+                opdev->globals.ErrorNo = dlsym(h,"opvpErrorNo");
+                if (opdev->globals.OpenPrinter && opdev->globals.ErrorNo) {
+                    opdev->globals.handle = h;
                     break;
                 }
-                OpenPrinter = NULL;
-                ErrorNo = NULL;
+                opdev->globals.OpenPrinter = NULL;
+                opdev->globals.ErrorNo = NULL;
                 /* try version 0.2 driver */
-                OpenPrinter_0_2 = dlsym(h,"OpenPrinter");
-                ErrorNo = dlsym(h,"errorno");
-                if (OpenPrinter_0_2 && ErrorNo) {
-                    handle = h;
+                opdev->globals.OpenPrinter_0_2 = dlsym(h,"OpenPrinter");
+                opdev->globals.ErrorNo = dlsym(h,"errorno");
+                if (opdev->globals.OpenPrinter_0_2 && opdev->globals.ErrorNo) {
+                    opdev->globals.handle = h;
                     break;
                 }
-                OpenPrinter_0_2 = NULL;
-                ErrorNo = NULL;
+                opdev->globals.OpenPrinter_0_2 = NULL;
+                opdev->globals.ErrorNo = NULL;
                 dlclose(h);
             }
             i++;
         }
     }
 
-    if (handle) {
+    if (opdev->globals.handle) {
         return 0;
     } else {
         return -1;
@@ -1787,13 +2080,15 @@ opvp_load_vector_driver(void)
  * unload vector-driver
  */
 static  int
-opvp_unload_vector_driver(void)
+opvp_unload_vector_driver(gx_device* dev)
 {
-    if (handle) {
-        dlclose(handle);
-        handle = NULL;
-        OpenPrinter = NULL;
-        ErrorNo = NULL;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
+
+    if (opdev->globals.handle) {
+        dlclose(opdev->globals.handle);
+        opdev->globals.handle = NULL;
+        opdev->globals.OpenPrinter = NULL;
+        opdev->globals.ErrorNo = NULL;
     }
     return 0;
 }
@@ -1810,6 +2105,7 @@ prepare_open(gx_device *dev)
     int dumFD = -1;
     opvp_dc_t dumContext = -1;
     opvp_cspace_t cspace = OPVP_CSPACE_STANDARDRGB;
+    gx_device_opvp *opdev = (gx_device_opvp*) dev;
 
     /* open dummy device */
     code = open("/dev/null", O_RDWR);
@@ -1818,19 +2114,19 @@ prepare_open(gx_device *dev)
 
     /* load vector driver */
     if (!ecode) {
-        if ((code = opvp_load_vector_driver())) {
+        if ((code = opvp_load_vector_driver(dev))) {
             ecode = code;
         }
     }
 
     /* prepare array of function pointer for PDAPI */
     if (!ecode) {
-        if (!apiEntry) {
-            if (!(apiEntry = calloc(sizeof(opvp_api_procs_t), 1))) {
+        if (!(opdev->globals.apiEntry)) {
+            if (!(opdev->globals.apiEntry = calloc(sizeof(opvp_api_procs_t), 1))) {
                 ecode = -1;
             }
         } else {
-            memset(apiEntry, 0, sizeof(opvp_api_procs_t));
+            memset(opdev->globals.apiEntry, 0, sizeof(opvp_api_procs_t));
         }
     }
 
@@ -1842,8 +2138,8 @@ prepare_open(gx_device *dev)
         /* require version 1.0 */
         apiVersion[0] = 1;
         apiVersion[1] = 0;
-        dc = OpenPrinterWrapper(dumFD, (opvp_char_t *)printerModel,
-          apiVersion,&api_entry);
+        dc = OpenPrinterWrapper(dev, dumFD, (opvp_char_t *)(opdev->globals.printerModel),
+          apiVersion, &api_entry);
         if (dc == -1) {
             ecode = -1;
         } else {
@@ -1853,33 +2149,33 @@ prepare_open(gx_device *dev)
 
     /* set apiEntry */
     if (!ecode) {
-        nApiEntry = sizeof(opvp_api_procs_t)/sizeof(void *);
-        memcpy(apiEntry, api_entry, nApiEntry*sizeof(void *));
+        opdev->globals.nApiEntry = sizeof(opvp_api_procs_t)/sizeof(void *);
+        memcpy(opdev->globals.apiEntry, api_entry, opdev->globals.nApiEntry*sizeof(void *));
     } else {
-        if (apiEntry) free(apiEntry);
-        apiEntry = NULL;
+        if (opdev->globals.apiEntry) free(opdev->globals.apiEntry);
+        opdev->globals.apiEntry = NULL;
     }
 
     /* check vector fucntion */
-    if (apiEntry) {
-        if (!inkjet) {
-            if (!(apiEntry->opvpNewPath) ||
-                !(apiEntry->opvpEndPath) ||
-                !(apiEntry->opvpStrokePath) ||
-                !(apiEntry->opvpSetCurrentPoint) ||
-                !(apiEntry->opvpLinePath) ||
-                !(apiEntry->opvpBezierPath)) {
+    if (opdev->globals.apiEntry) {
+        if (!(opdev->globals.inkjet)) {
+            if (!(opdev->globals.apiEntry->opvpNewPath) ||
+                !(opdev->globals.apiEntry->opvpEndPath) ||
+                !(opdev->globals.apiEntry->opvpStrokePath) ||
+                !(opdev->globals.apiEntry->opvpSetCurrentPoint) ||
+                !(opdev->globals.apiEntry->opvpLinePath) ||
+                !(opdev->globals.apiEntry->opvpBezierPath)) {
                 /* NOT avail vector drawing mode */
-                vector = false;
+                opdev->globals.vector = false;
             }
         }
         /* call GetColorSpace */
-        if (apiEntry->opvpGetColorSpace) {
-            (void)apiEntry->opvpGetColorSpace(dumContext, &cspace);
+        if (opdev->globals.apiEntry->opvpGetColorSpace) {
+            (void)(opdev->globals.apiEntry->opvpGetColorSpace)(dumContext, &cspace);
         }
         if (cspace == OPVP_CSPACE_BW) {
             /* mono-color */
-            colorSpace = cspace;
+            opdev->globals.colorSpace = cspace;
             dev->color_info.num_components = 1;
             dev->color_info.depth = 1;
             dev->color_info.max_gray = 0;
@@ -1888,7 +2184,7 @@ prepare_open(gx_device *dev)
             dev->color_info.dither_colors = 1;
         } else if (cspace == OPVP_CSPACE_DEVICEGRAY) {
             /* gray-scale */
-            colorSpace = cspace;
+            opdev->globals.colorSpace = cspace;
             dev->color_info.num_components = 1;
             dev->color_info.depth = 8;
             dev->color_info.max_gray = 255;
@@ -1897,7 +2193,7 @@ prepare_open(gx_device *dev)
             dev->color_info.dither_colors = 256;
         } else {
             /* rgb color */
-            colorSpace = OPVP_CSPACE_STANDARDRGB;
+            opdev->globals.colorSpace = OPVP_CSPACE_STANDARDRGB;
             dev->color_info.num_components = 3;
             dev->color_info.depth = 24;
             dev->color_info.max_gray = 255;
@@ -1913,8 +2209,8 @@ prepare_open(gx_device *dev)
     /* call Closerinter as dummy */
     if (dumContext != -1) {
         /* call ClosePrinter */
-        if (apiEntry->opvpClosePrinter) {
-            apiEntry->opvpClosePrinter(dumContext);
+        if (opdev->globals.apiEntry->opvpClosePrinter) {
+            opdev->globals.apiEntry->opvpClosePrinter(dumContext);
         }
         dumContext = -1;
     }
@@ -1926,7 +2222,7 @@ prepare_open(gx_device *dev)
     }
 
     /* un-load vector driver */
-    opvp_unload_vector_driver();
+    opvp_unload_vector_driver(dev);
 
     return ecode;
 }
@@ -1961,51 +2257,52 @@ opvp_open(gx_device *dev)
     }
 
     /* set margins */
-    if (zoomAuto) {
-        margin_width = (margins[0] + margins[2])
+    if (pdev->globals.zoomAuto) {
+        margin_width = (pdev->globals.margins[0] + pdev->globals.margins[2])
                      * dev->HWResolution[0];
-        margin_height = (margins[1] + margins[3])
+        margin_height = (pdev->globals.margins[1] + pdev->globals.margins[3])
                       * dev->HWResolution[1];
-        zoom[0] = (dev->width - margin_width) / dev->width;
-        zoom[1] = (dev->height - margin_height) / dev->height;
-        if (zoom[0] < zoom[1]) {
-            zoom[1] = zoom[0];
+        pdev->globals.zoom[0] = (dev->width - margin_width) / dev->width;
+        pdev->globals.zoom[1] = (dev->height - margin_height) / dev->height;
+        if (pdev->globals.zoom[0] < pdev->globals.zoom[1]) {
+            pdev->globals.zoom[1] = pdev->globals.zoom[0];
         } else {
-            zoom[0] = zoom[1];
+            pdev->globals.zoom[0] = pdev->globals.zoom[1];
         }
     }
-    if (inkjet) {
-        if ((margins[0] != 0) ||
-            (margins[1] != 0) || (margins[3] != 0)) {
-            shift[0] = margins[0] * dev->HWResolution[0];
-            shift[1] = (margins[1] + margins[3])
+    if (pdev->globals.inkjet) {
+        if ((pdev->globals.margins[0] != 0) ||
+            (pdev->globals.margins[1] != 0) || (pdev->globals.margins[3] != 0)) {
+            pdev->globals.shift[0] = pdev->globals.margins[0] * dev->HWResolution[0];
+            pdev->globals.shift[1] = (pdev->globals.margins[1] + pdev->globals.margins[3])
                      * dev->HWResolution[1];
-            zooming = true;
+            pdev->globals.zooming = true;
         }
-        dev->width -= margins[2] * dev->HWResolution[0];
-        dev->height -= margins[1] * dev->HWResolution[1];
+        dev->width -= (int) (pdev->globals.margins[2] * dev->HWResolution[0]);
+        dev->height -= (int) (pdev->globals.margins[1] * dev->HWResolution[1]);
     } else {
-            if ((margins[0] != 0) || (margins[1] != 0)) {
-                shift[0] = margins[0] * dev->HWResolution[0];
-                shift[1] = margins[3] * dev->HWResolution[1];
-                zooming = true;
+            if ((pdev->globals.margins[0] != 0) || (pdev->globals.margins[1] != 0)) {
+                pdev->globals.shift[0] = pdev->globals.margins[0] * dev->HWResolution[0];
+                pdev->globals.shift[1] = pdev->globals.margins[3] * dev->HWResolution[1];
+                pdev->globals.zooming = true;
             }
             adj_margins[0] = 0;
             adj_margins[3] = 0;
-            adj_margins[1] = dev->height * zoom[1] / dev->HWResolution[1]
+            adj_margins[1] = dev->height * pdev->globals.zoom[1] / dev->HWResolution[1]
                             - (dev->MediaSize[1] / PS_DPI
-                             - (margins[1] + margins[3]));
+                             - (pdev->globals.margins[1] + pdev->globals.margins[3]));
             if (adj_margins[1] < 0) adj_margins[0] = 0;
-            adj_margins[2] = dev->width * zoom[0] / dev->HWResolution[0]
+            adj_margins[2] = dev->width * pdev->globals.zoom[0] / dev->HWResolution[0]
                             - (dev->MediaSize[0] / PS_DPI
-                             - (margins[0] + margins[2]));
+                             - (pdev->globals.margins[0] + pdev->globals.margins[2]));
             if (adj_margins[2] < 0) adj_margins[2] = 0;
             gx_device_set_margins(dev, adj_margins, true);
     }
-    if ((zoom[0] != 1) || (zoom[1] != 1)) zooming = true;
+    if ((pdev->globals.zoom[0] != 1) || (pdev->globals.zoom[1] != 1))
+        pdev->globals.zooming = true;
 
     /* open file for output device */
-    if (!inkjet) {
+    if (!(pdev->globals.inkjet)) {
         pdev->v_memory = gs_memory_stable(pdev->memory);
         /* open output stream */
         code = gdev_vector_open_file_options((gx_device_vector*)dev,
@@ -2027,7 +2324,7 @@ opvp_open(gx_device *dev)
                 pdev->bbox_device->memory = gs_memory_stable(dev->memory);
             }
         }
-        outputFD = fileno(gp_get_file(pdev->file));
+        pdev->globals.outputFD = fileno(gp_get_file(pdev->file));
     } else {
         /* open printer device */
         code = gdev_prn_open(dev);
@@ -2044,13 +2341,13 @@ opvp_open(gx_device *dev)
         if (code < 0) {
             return code;
         }
-        outputFD = fileno(gp_get_file(rdev->file));
+        pdev->globals.outputFD = fileno(gp_get_file(rdev->file));
     }
-    if (outputFD < 0)
-        return outputFD;
+    if (pdev->globals.outputFD < 0)
+        return pdev->globals.outputFD;
 
     /* RE-load vector driver */
-    if ((code = opvp_load_vector_driver())) {
+    if ((code = opvp_load_vector_driver(dev))) {
         return code;
     }
 
@@ -2058,44 +2355,49 @@ opvp_open(gx_device *dev)
     /* require version 1.0 */
     apiVersion[0] = 1;
     apiVersion[1] = 0;
-    dc = OpenPrinterWrapper(outputFD,(opvp_char_t *)printerModel,
+    dc = OpenPrinterWrapper(dev, pdev->globals.outputFD, (opvp_char_t *)pdev->globals.printerModel,
       apiVersion,&api_entry);
-    if (!apiEntry) {
-        if (!(apiEntry = calloc(sizeof(opvp_api_procs_t), 1))) {
-            ecode = -1;
-        }
+
+    if (!(pdev->globals.apiEntry)) {
+        pdev->globals.apiEntry = calloc(sizeof(opvp_api_procs_t), 1);
     } else {
-        memset(apiEntry, 0, sizeof(opvp_api_procs_t));
+        memset(pdev->globals.apiEntry, 0, sizeof(opvp_api_procs_t));
     }
-    if (dc == -1) {
+
+    if (dc == -1 || pdev->globals.apiEntry == NULL) {
         ecode =  -1;
-        if (apiEntry) free(apiEntry);
-        apiEntry = NULL;
-        opvp_unload_vector_driver();
-        if (inkjet) gdev_prn_close(dev);
+        if (pdev->globals.apiEntry)
+            free(pdev->globals.apiEntry);
+        pdev->globals.apiEntry = NULL;
+        opvp_unload_vector_driver(dev);
+        if (pdev->globals.inkjet)
+            gdev_prn_close(dev);
         else gdev_vector_close_file((gx_device_vector *)pdev);
         return ecode;
     }
-    printerContext = dc;
-    nApiEntry = sizeof(opvp_api_procs_t)/sizeof(void *);
-    memcpy(apiEntry, api_entry, nApiEntry*sizeof(void *));
+
+    pdev->globals.printerContext = dc;
+    pdev->globals.nApiEntry = sizeof(opvp_api_procs_t)/sizeof(void *);
+    memcpy(pdev->globals.apiEntry, api_entry, pdev->globals.nApiEntry*sizeof(void *));
 
     /* initialize */
-    if ((!ecode) && (!inkjet)) {
+    if ((!ecode) && (!(pdev->globals.inkjet))) {
         pdev->vec_procs = &opvp_vector_procs;
-        if (vector) gdev_vector_init((gx_device_vector *)pdev);
+        if (pdev->globals.vector)
+            gdev_vector_init((gx_device_vector *)pdev);
     }
 
-    if (apiEntry->opvpQueryColorSpace) {
+    if (pdev->globals.apiEntry->opvpQueryColorSpace ||
+        pdev->globals.apiEntry_0_2->QueryColorSpace) {
         int n = sizeof(cspace_available);
         int nn = n;
         opvp_cspace_t *p = malloc(n*sizeof(opvp_cspace_t));
 
-        if ((r = apiEntry->opvpQueryColorSpace(printerContext,&nn,p))
+        if ((r = gsopvpQueryColorSpace(dev, pdev->globals.printerContext,&nn,p))
              == OPVP_PARAMERROR && nn > n) {
             /* realloc buffer and retry */
             p = realloc(p,nn*sizeof(opvp_cspace_t));
-            r = apiEntry->opvpQueryColorSpace(printerContext,&nn,p);
+            r = gsopvpQueryColorSpace(dev, pdev->globals.printerContext,&nn,p);
         }
         if (r == OPVP_OK) {
             int i;
@@ -2111,12 +2413,12 @@ opvp_open(gx_device *dev)
     /* start job */
     if (!ecode) {
         /* job info */
-        if (jobInfo) {
-            if (strlen(jobInfo) > 0) {
-                job_info = opvp_alloc_string(&job_info,jobInfo);
+        if (pdev->globals.jobInfo) {
+            if (strlen(pdev->globals.jobInfo) > 0) {
+                job_info = opvp_alloc_string(&job_info, pdev->globals.jobInfo);
             }
         }
-        tmp_info = opvp_alloc_string(&tmp_info,opvp_gen_job_info(dev));
+        tmp_info = opvp_alloc_string(&tmp_info, opvp_gen_job_info(dev));
         if (tmp_info) {
             if (strlen(tmp_info) > 0) {
                 if (job_info) {
@@ -2130,8 +2432,8 @@ opvp_open(gx_device *dev)
         }
 
         /* call StartJob */
-        if (apiEntry->opvpStartJob) {
-            r = apiEntry->opvpStartJob(printerContext,
+        if (pdev->globals.apiEntry->opvpStartJob) {
+            r = pdev->globals.apiEntry->opvpStartJob(pdev->globals.printerContext,
               (opvp_char_t *)opvp_to_utf8(job_info));
         }
         if (r != OPVP_OK) {
@@ -2142,9 +2444,9 @@ opvp_open(gx_device *dev)
     /* start doc */
     if (!ecode) {
         /* doc info */
-        if (docInfo) {
-            if (strlen(docInfo) > 0) {
-                doc_info = opvp_alloc_string(&doc_info,docInfo);
+        if (pdev->globals.docInfo) {
+            if (strlen(pdev->globals.docInfo) > 0) {
+                doc_info = opvp_alloc_string(&doc_info, pdev->globals.docInfo);
             }
         }
         tmp_info = opvp_alloc_string(&tmp_info, opvp_gen_doc_info(dev));
@@ -2161,8 +2463,8 @@ opvp_open(gx_device *dev)
         }
 
         /* call StartDoc */
-        if (apiEntry->opvpStartDoc) {
-            r = apiEntry->opvpStartDoc(printerContext,
+        if (pdev->globals.apiEntry->opvpStartDoc) {
+            r = pdev->globals.apiEntry->opvpStartDoc(pdev->globals.printerContext,
               (opvp_char_t *)opvp_to_utf8(doc_info));
         }
         if (r != OPVP_OK) {
@@ -2183,9 +2485,11 @@ opvp_open(gx_device *dev)
 static  int
 oprp_open(gx_device *dev)
 {
+    gx_device_opvp *opdev = (gx_device_opvp*) dev;
+
     /* set inkjet mode */
-    vector = false;
-    inkjet = true;
+    opdev->globals.vector = false;
+    opdev->globals.inkjet = true;
 
     /* matrix */
     dev->procs.get_initial_matrix = opvp_get_initial_matrix;
@@ -2198,24 +2502,24 @@ oprp_open(gx_device *dev)
 static  void
 opvp_get_initial_matrix(gx_device *dev, gs_matrix *pmat)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)dev;
+    gx_device_opvp * opdev = (gx_device_opvp *)dev;
     opvp_ctm_t omat;
 
     gx_default_get_initial_matrix(dev,pmat);
-    if (zooming) {
+    if (opdev->globals.zooming) {
         /* gs matrix */
-        pmat->xx *= zoom[0];
-        pmat->xy *= zoom[1];
-        pmat->yx *= zoom[0];
-        pmat->yy *= zoom[1];
-        pmat->tx = pmat->tx * zoom[0] + shift[0];
-        pmat->ty = pmat->ty * zoom[1] + shift[1];
+        pmat->xx *= opdev->globals.zoom[0];
+        pmat->xy *= opdev->globals.zoom[1];
+        pmat->yx *= opdev->globals.zoom[0];
+        pmat->yy *= opdev->globals.zoom[1];
+        pmat->tx = pmat->tx * opdev->globals.zoom[0] + opdev->globals.shift[0];
+        pmat->ty = pmat->ty * opdev->globals.zoom[1] + opdev->globals.shift[1];
     }
 
-    if (pdev->is_open) {
+    if (opdev->is_open) {
         /* call ResetCTM */
-        if (apiEntry->opvpResetCTM) {
-            apiEntry->opvpResetCTM(printerContext);
+        if (opdev->globals.apiEntry->opvpResetCTM) {
+            opdev->globals.apiEntry->opvpResetCTM(opdev->globals.printerContext);
         } else {
             /* call SetCTM */
             omat.a = 1;
@@ -2224,8 +2528,8 @@ opvp_get_initial_matrix(gx_device *dev,
             omat.d = 1;
             omat.e = 0;
             omat.f = 0;
-            if (apiEntry->opvpSetCTM) {
-                apiEntry->opvpSetCTM(printerContext, &omat);
+            if (opdev->globals.apiEntry->opvpSetCTM) {
+                opdev->globals.apiEntry->opvpSetCTM(opdev->globals.printerContext, &omat);
             }
         }
     }
@@ -2239,30 +2543,32 @@ opvp_get_initial_matrix(gx_device *dev,
 static  int
 opvp_output_page(gx_device *dev, int num_copies, int flush)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)dev;
+    gx_device_opvp *opdev = (gx_device_opvp *)dev;
     int ecode = 0;
     int code = -1;
 
-    if (inkjet) return gdev_prn_output_page(dev, num_copies, flush);
+    if (opdev->globals.inkjet)
+        return gdev_prn_output_page(dev, num_copies, flush);
 
 #ifdef OPVP_IGNORE_BLANK_PAGE
     if (pdev->in_page) {
 #else
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 #endif
         /* end page */
-        code = opvp_endpage();
+        code = opvp_endpage(dev);
         if (code) ecode = code;
 
-        pdev->in_page = false;
-        beginPage = false;
+        opdev->in_page = false;
+        opdev->globals.beginPage = false;
 #ifdef OPVP_IGNORE_BLANK_PAGE
     }
 #endif
 
-    if (vector) {
-        gdev_vector_reset((gx_device_vector *)pdev);
+    if (opdev->globals.vector) {
+        gdev_vector_reset((gx_device_vector *)dev);
     }
 
     code = gx_finish_output_page(dev, num_copies, flush);
@@ -2289,6 +2595,7 @@ oprp_print_page(gx_device_printer *pdev,
     int rasterWidth;
     bool start_page = false;
     bool start_raster = false;
+    gx_device_opvp *opdev = (gx_device_opvp*)pdev;
 #if ENABLE_SKIP_RASTER
     int i;
     byte check;
@@ -2317,8 +2624,8 @@ oprp_print_page(gx_device_printer *pdev,
 
     /* call StartRaster */
     if (!ecode) {
-        if (apiEntry->opvpStartRaster) {
-            r = apiEntry->opvpStartRaster(printerContext,rasterWidth);
+        if (opdev->globals.apiEntry->opvpStartRaster) {
+            r = opdev->globals.apiEntry->opvpStartRaster(opdev->globals.printerContext,rasterWidth);
         }
         if (r != OPVP_OK) {
             ecode = r;
@@ -2339,7 +2646,7 @@ oprp_print_page(gx_device_printer *pdev,
         }
 #if ENABLE_SKIP_RASTER
         /* check support SkipRaster */
-        if (apiEntry->opvpSkipRaster) {
+        if (opdev->globals.apiEntry->opvpSkipRaster) {
             /* check all white */
             if (pdev->color_info.depth > 8) {
                 for (check = 0xff, i = 0; i < raster_size; i++)
@@ -2349,7 +2656,7 @@ oprp_print_page(gx_device_printer *pdev,
                 }
                 /* if all white call SkipRaster */
                 if (check == 0xff) {
-                    r = apiEntry->opvpSkipRaster(printerContext, 1);
+                    r = opdev->globals.apiEntry->opvpSkipRaster(opdev->globals.printerContext, 1);
                     if (r == OPVP_OK) continue;
                 }
             } else {
@@ -2359,27 +2666,29 @@ oprp_print_page(gx_device_printer *pdev,
                 }
                 /* if all zero call SkipRaster */
                 if (check) {
-                    r = apiEntry->opvpSkipRaster(printerContext, 1);
-                    if (r == OPVP_OK) continue;
+                    r = opdev->globals.apiEntry->opvpSkipRaster(opdev->globals.printerContext, 1);
+                    if (r == OPVP_OK)
+                        continue;
                 }
             }
         }
 #endif
         /* call TransferRasterData */
         if (!ecode) {
-            if (apiEntry->opvpTransferRasterData) {
-                r = apiEntry->opvpTransferRasterData(printerContext,
+            if (opdev->globals.apiEntry->opvpTransferRasterData) {
+                r = opdev->globals.apiEntry->opvpTransferRasterData(opdev->globals.printerContext,
                                                 raster_size,
                                                 data);
             }
-            if (r != OPVP_OK) ecode = r;
+            if (r != OPVP_OK)
+                ecode = r;
         }
     }
 
     /* call EndRaster */
     if (start_raster) {
-        if (apiEntry->opvpEndRaster) {
-            r = apiEntry->opvpEndRaster(printerContext);
+        if (opdev->globals.apiEntry->opvpEndRaster) {
+            r = opdev->globals.apiEntry->opvpEndRaster(opdev->globals.printerContext);
         }
         if (r != OPVP_OK) ecode = r;
         start_raster = false;
@@ -2387,7 +2696,7 @@ oprp_print_page(gx_device_printer *pdev,
 
     /* end page */
     if (start_page) {
-        code = opvp_endpage();
+        code = opvp_endpage((gx_device*) pdev);
         if (code) ecode = code;
         start_page = false;
     }
@@ -2411,37 +2720,38 @@ opvp_close(gx_device *dev)
     int ecode = 0;
 
     /* finalize */
-    if (printerContext != -1) {
+    if (pdev->globals.printerContext != -1) {
         /* call EndDoc */
-        if (apiEntry->opvpEndDoc) {
-            apiEntry->opvpEndDoc(printerContext);
+        if (pdev->globals.apiEntry->opvpEndDoc) {
+            pdev->globals.apiEntry->opvpEndDoc(pdev->globals.printerContext);
         }
 
         /* call EndJob */
-        if (apiEntry->opvpEndJob) {
-            apiEntry->opvpEndJob(printerContext);
+        if (pdev->globals.apiEntry->opvpEndJob) {
+            pdev->globals.apiEntry->opvpEndJob(pdev->globals.printerContext);
         }
 
         /* call ClosePrinter */
-        if (apiEntry->opvpClosePrinter) {
-            apiEntry->opvpClosePrinter(printerContext);
+        if (pdev->globals.apiEntry->opvpClosePrinter) {
+            pdev->globals.apiEntry->opvpClosePrinter(pdev->globals.printerContext);
         }
-        printerContext = -1;
+        pdev->globals.printerContext = -1;
     }
 
     /* unload vector driver */
-    if (apiEntry) free(apiEntry);
-    apiEntry = NULL;
-    opvp_unload_vector_driver();
+    if (pdev->globals.apiEntry)
+        free(pdev->globals.apiEntry);
+    pdev->globals.apiEntry = NULL;
+    opvp_unload_vector_driver(dev);
 
-    if (inkjet) {
+    if (pdev->globals.inkjet) {
         /* close printer */
         gdev_prn_close(dev);
     } else {
         /* close output stream */
         gdev_vector_close_file((gx_device_vector *)pdev);
     }
-    outputFD = -1;
+    pdev->globals.outputFD = -1;
 
     return ecode;
 }
@@ -2455,6 +2765,7 @@ opvp_map_rgb_color(gx_device *dev,
 {
     opvp_cspace_t cs;
     uint c, m, y, k;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
 #if !(ENABLE_SIMPLE_MODE)
     gx_device_opvp *pdev;
@@ -2467,16 +2778,15 @@ opvp_map_rgb_color(gx_device *dev,
     b = prgb[2];
 
 #if ENABLE_SIMPLE_MODE
-    cs = colorSpace;
+    cs = opdev->globals.colorSpace;
 #else
     pdev = (gx_device_opvp *)dev;
     r = -1;
     cs = OPVP_CSPACE_STANDARDRGB;
     if (pdev->is_open) {
         /* call GetColorSpace */
-        if (apiEntry->opvpGetColorSpace) {
-            r = apiEntry->opvpGetColorSpace(printerContext, &cs);
-        }
+
+        r = gsopvpGetColorSpace(dev, opdev->globals.printerContext, &cs);
         if (r != OPVP_OK) {
             if (pdev->color_info.depth > 32) {
                     cs = OPVP_CSPACE_STANDARDRGB64;
@@ -2555,15 +2865,14 @@ opvp_map_color_rgb(gx_device *dev, gx_co
 #endif
     opvp_cspace_t cs = OPVP_CSPACE_STANDARDRGB;
     uint c, m, y, k;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
 #if ENABLE_SIMPLE_MODE
-    cs = colorSpace;
+    cs = opdev->globals.colorSpace;
 #else
     /* call GetColorSpace */
     if (pdev->is_open) {
-        if (apiEntry->opvpGetColorSpace) {
-            r = apiEntry->opvpGetColorSpace(printerContext, &cs);
-        }
+        r = gsopvpGetColorSpace(dev, opdev->globals.printerContext, &cs);
         if (r != OPVP_OK) {
             if (pdev->color_info.depth > 32) {
                 cs = OPVP_CSPACE_STANDARDRGB64;
@@ -2635,44 +2944,44 @@ opvp_fill_rectangle(
     int h,
     gx_color_index color)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)dev;
+    gx_device_opvp *opdev = (gx_device_opvp *)dev;
     byte data[8] = {0xC0, 0, 0, 0, 0xC0, 0, 0, 0};
     int code = -1;
     int ecode = 0;
     opvp_brush_t brush;
     opvp_point_t point;
 
-    if (vector) {
-        return gdev_vector_fill_rectangle( dev, x, y, w, h, color);
+    if (opdev->globals.vector) {
+        return gdev_vector_fill_rectangle(dev, x, y, w, h, color);
     }
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
 #if !(ENABLE_SIMPLE_MODE)
     /* call SaveGS */
-    if (apiEntry->opvpSaveGS) {
-        apiEntry->opvpSaveGS(printerContext);
+    if (pdev->globals.apiEntry->opvpSaveGS) {
+        pdev->globals.apiEntry->opvpSaveGS(printerContext);
     }
 #endif
 
     /* one-color */
-    opvp_set_brush_color(pdev, color, &brush);
+    opvp_set_brush_color(opdev, color, &brush);
 
     /* call SetFillColor */
-    if (apiEntry->opvpSetFillColor) {
-        apiEntry->opvpSetFillColor(printerContext, &brush);
-    }
+    gsopvpSetFillColor(dev, opdev->globals.printerContext, &brush);
+
 
     /* call SetCurrentPoint */
     OPVP_I2FIX(x, point.x);
     OPVP_I2FIX(y, point.y);
-    if (apiEntry->opvpSetCurrentPoint) {
-        apiEntry->opvpSetCurrentPoint(printerContext,point.x, point.y);
+    if (opdev->globals.apiEntry->opvpSetCurrentPoint) {
+        opdev->globals.apiEntry->opvpSetCurrentPoint(opdev->globals.printerContext,point.x, point.y);
     }
 
     /* draw image */
-    code = opvp_draw_image(pdev,
+    code = opvp_draw_image(opdev,
                            1,
                            2, 2,
                            w, h,
@@ -2684,17 +2993,15 @@ opvp_fill_rectangle(
     }
 
     /* restore fill color */
-    if (vectorFillColor) {
+    if (opdev->globals.vectorFillColor) {
         /* call SetFillColor */
-        if (apiEntry->opvpSetFillColor) {
-            apiEntry->opvpSetFillColor(printerContext,vectorFillColor);
-        }
+        gsopvpSetFillColor(dev, opdev->globals.printerContext,opdev->globals.vectorFillColor);
     }
 
 #if !(ENABLE_SIMPLE_MODE)
     /* call RestoreGS */
-    if (apiEntry->opvpRestoreGS) {
-        apiEntry->opvpRestoreGS(printerContext);
+    if (pdev->globals.apiEntry->opvpRestoreGS) {
+        pdev->globals.opdev->globals.apiEntry->opvpRestoreGS(printerContext);
     }
 #endif
 
@@ -2718,7 +3025,7 @@ opvp_copy_mono(
     gx_color_index zero,
     gx_color_index one)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)dev;
+    gx_device_opvp *opdev = (gx_device_opvp *)dev;
     int code = -1;
     int ecode = 0;
     opvp_brush_t brush;
@@ -2736,7 +3043,8 @@ opvp_copy_mono(
     bool reverse = false;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* data offset */
     if (data_x) {
@@ -2773,8 +3081,8 @@ opvp_copy_mono(
 
 #if !(ENABLE_SIMPLE_MODE)
     /* call SaveGS */
-    if (apiEntry->opvpSaveGS) {
-        apiEntry->opvpSaveGS(printerContext);
+    if (opdev->globals.apiEntry->opvpSaveGS) {
+        opdev->globals.apiEntry->opvpSaveGS(printerContext);
     }
 #endif
     if (one == gx_no_color_index) {
@@ -2789,25 +3097,22 @@ opvp_copy_mono(
     if (zero != gx_no_color_index) {
         /* not mask */
         /* Set PaintMode */
-        if (apiEntry->opvpSetPaintMode) {
-            apiEntry->opvpSetPaintMode(printerContext,OPVP_PAINTMODE_OPAQUE);
+        if (opdev->globals.apiEntry->opvpSetPaintMode) {
+            opdev->globals.apiEntry->opvpSetPaintMode(opdev->globals.printerContext, OPVP_PAINTMODE_OPAQUE);
         }
         /* zero-color */
-        opvp_set_brush_color(pdev, zero, &brush);
+        opvp_set_brush_color(opdev, zero, &brush);
 
         /* call SetBgColor */
-        if (apiEntry->opvpSetBgColor) {
-            apiEntry->opvpSetBgColor(printerContext, &brush);
-        }
+        gsopvpSetBgColor(dev, opdev->globals.printerContext, &brush);
+
     }
 
     /* one-color */
-    opvp_set_brush_color(pdev, one, &brush);
+    opvp_set_brush_color(opdev, one, &brush);
 
     /* call SetFillColor */
-    if (apiEntry->opvpSetFillColor) {
-        apiEntry->opvpSetFillColor(printerContext, &brush);
-    }
+    gsopvpSetFillColor(dev, opdev->globals.printerContext, &brush);
 
     if (reverse) {
         /* 0/1 reverse image */
@@ -2826,12 +3131,12 @@ opvp_copy_mono(
     /* call SetCurrentPoint */
     OPVP_I2FIX(x, point.x);
     OPVP_I2FIX(y, point.y);
-    if (apiEntry->opvpSetCurrentPoint) {
-        apiEntry->opvpSetCurrentPoint(printerContext,point.x, point.y);
+    if (opdev->globals.apiEntry->opvpSetCurrentPoint) {
+        opdev->globals.apiEntry->opvpSetCurrentPoint(opdev->globals.printerContext,point.x, point.y);
     }
 
     /* draw image */
-    code = opvp_draw_image(pdev,
+    code = opvp_draw_image(opdev,
                            1,
                            w, h,
                            w, h,
@@ -2844,23 +3149,21 @@ opvp_copy_mono(
 
     if (zero != gx_no_color_index) {
         /* restore PaintMode */
-        if (apiEntry->opvpSetPaintMode) {
-            apiEntry->opvpSetPaintMode(printerContext,
+        if (opdev->globals.apiEntry->opvpSetPaintMode) {
+            opdev->globals.apiEntry->opvpSetPaintMode(opdev->globals.printerContext,
               OPVP_PAINTMODE_TRANSPARENT);
         }
     }
     /* restore fill color */
-    if (vectorFillColor) {
+    if (opdev->globals.vectorFillColor) {
         /* call SetFillColor */
-        if (apiEntry->opvpSetFillColor) {
-            apiEntry->opvpSetFillColor(printerContext,vectorFillColor);
-        }
+        gsopvpSetFillColor(dev, opdev->globals.printerContext,opdev->globals.vectorFillColor);
     }
 
 #if !(ENABLE_SIMPLE_MODE)
     /* call RestoreGS */
-    if (apiEntry->opvpRestoreGS) {
-        apiEntry->opvpRestoreGS(printerContext);
+    if (opdev->globals.apiEntry->opvpRestoreGS) {
+        opdev->globals.apiEntry->opvpRestoreGS(opdev->globals.printerContext);
     }
 #endif
 
@@ -2887,7 +3190,7 @@ opvp_copy_color(
     int w,
     int h)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)dev;
+    gx_device_opvp *opdev = (gx_device_opvp *)dev;
     int code = -1;
     int ecode = 0;
     opvp_point_t point;
@@ -2902,11 +3205,12 @@ opvp_copy_color(
     int adj_raster = raster;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* data offset */
     if (data_x) {
-        depth = pdev->color_info.depth;
+        depth = opdev->color_info.depth;
         pixel = (depth + 7) >> 3;
         byte_length = pixel * w;
         adj_raster = ((byte_length + 3) >> 2) << 2;
@@ -2926,21 +3230,21 @@ opvp_copy_color(
 
 #if !(ENABLE_SIMPLE_MODE)
     /* call SaveGS */
-    if (apiEntry->opvpSaveGS) {
-        apiEntry->opvpSaveGS(printerContext);
+    if (opdev->globals.apiEntry->opvpSaveGS) {
+        opdev->globals.apiEntry->opvpSaveGS(opdev->globals.printerContext);
     }
 #endif
 
     /* call SetCurrentPoint */
     OPVP_I2FIX(x, point.x);
     OPVP_I2FIX(y, point.y);
-    if (apiEntry->opvpSetCurrentPoint) {
-        apiEntry->opvpSetCurrentPoint(printerContext, point.x, point.y);
+    if (opdev->globals.apiEntry->opvpSetCurrentPoint) {
+        opdev->globals.apiEntry->opvpSetCurrentPoint(opdev->globals.printerContext, point.x, point.y);
     }
 
     /* draw image */
-    code = opvp_draw_image(pdev,
-                           pdev->color_info.depth,
+    code = opvp_draw_image(opdev,
+                           opdev->color_info.depth,
                            w, h,
                            w, h,
                            adj_raster,
@@ -2952,8 +3256,8 @@ opvp_copy_color(
 
 #if !(ENABLE_SIMPLE_MODE)
     /* call RestoreGS */
-    if (apiEntry->opvpRestoreGS) {
-        apiEntry->opvpRestoreGS(printerContext);
+    if (opdev->globals.apiEntry->opvpRestoreGS) {
+        opdev->globals.apiEntry->opvpRestoreGS(opdev->globals.printerContext);
     }
 #endif
 
@@ -2969,7 +3273,7 @@ opvp_copy_color(
  * get params
  */
 static  int
-_get_params(gs_param_list *plist)
+_get_params(gx_device* dev, gs_param_list *plist)
 {
     int code;
     int ecode = 0;
@@ -2985,37 +3289,38 @@ _get_params(gs_param_list *plist)
     gs_param_string mbps;
     gs_param_string zmps;
     char buff[OPVP_BUFF_SIZE];
+    gx_device_opvp* opdev = (gx_device_opvp*)dev;
 
     /* get params */
 
     /* vector driver name */
     pname = "Driver";
-    vdps.data = (byte *)vectorDriver;
-    vdps.size = (vectorDriver ? strlen(vectorDriver) + 1 : 0);
+    vdps.data = (byte *)opdev->globals.vectorDriver;
+    vdps.size = (opdev->globals.vectorDriver ? strlen(opdev->globals.vectorDriver) + 1 : 0);
     vdps.persistent = false;
     code = param_write_string(plist, pname, &vdps);
     if (code) ecode = code;
 
     /* printer model name */
     pname = "Model";
-    pmps.data = (byte *)printerModel;
-    pmps.size = (printerModel ? strlen(printerModel) + 1 : 0);
+    pmps.data = (byte *)opdev->globals.printerModel;
+    pmps.size = (opdev->globals.printerModel ? strlen(opdev->globals.printerModel) + 1 : 0);
     pmps.persistent = false;
     code = param_write_string(plist, pname, &pmps);
     if (code) ecode = code;
 
     /* job info */
     pname = "JobInfo";
-    jips.data = (byte *)jobInfo;
-    jips.size = (jobInfo ? strlen(jobInfo) + 1 : 0);
+    jips.data = (byte *)opdev->globals.jobInfo;
+    jips.size = (opdev->globals.jobInfo ? strlen(opdev->globals.jobInfo) + 1 : 0);
     jips.persistent = false;
     code = param_write_string(plist, pname, &jips);
     if (code) ecode = code;
 
     /* doc info */
     pname = "DocInfo";
-    dips.data = (byte *)docInfo;
-    dips.size = (docInfo ? strlen(docInfo) + 1 : 0);
+    dips.data = (byte *)opdev->globals.docInfo;
+    dips.size = (opdev->globals.docInfo ? strlen(opdev->globals.docInfo) + 1 : 0);
     dips.persistent = false;
     code = param_write_string(plist, pname, &dips);
     if (code) ecode = code;
@@ -3052,28 +3357,28 @@ _get_params(gs_param_list *plist)
     /* margins */
     memset((void*)buff, 0, OPVP_BUFF_SIZE);
     pname = "MarginLeft";
-    snprintf(buff, OPVP_BUFF_SIZE - 1, "%f",margins[0]);
+    snprintf(buff, OPVP_BUFF_SIZE - 1, "%f",opdev->globals.margins[0]);
     mlps.data = (byte *)buff;
     mlps.size = strlen(buff) + 1;
     mlps.persistent = false;
     code = param_write_string(plist, pname, &mlps);
     if (code) ecode = code;
     pname = "MarginTop";
-    snprintf(buff, OPVP_BUFF_SIZE - 1, "%f",margins[3]);
+    snprintf(buff, OPVP_BUFF_SIZE - 1, "%f",opdev->globals.margins[3]);
     mtps.data = (byte *)buff;
     mtps.size = strlen(buff) + 1;
     mtps.persistent = false;
     code = param_write_string(plist, pname, &mtps);
     if (code) ecode = code;
     pname = "MarginRight";
-    snprintf(buff, OPVP_BUFF_SIZE - 1, "%f",margins[2]);
+    snprintf(buff, OPVP_BUFF_SIZE - 1, "%f",opdev->globals.margins[2]);
     mrps.data = (byte *)buff;
     mrps.size = strlen(buff) + 1;
     mrps.persistent = false;
     code = param_write_string(plist, pname, &mrps);
     if (code) ecode = code;
     pname = "MarginBottom";
-    snprintf(buff, OPVP_BUFF_SIZE - 1, "%f",margins[1]);
+    snprintf(buff, OPVP_BUFF_SIZE - 1, "%f",opdev->globals.margins[1]);
     mbps.data = (byte *)buff;
     mbps.size = strlen(buff) + 1;
     mbps.persistent = false;
@@ -3082,7 +3387,7 @@ _get_params(gs_param_list *plist)
 
     /* zoom */
     pname = "Zoom";
-    snprintf(buff, OPVP_BUFF_SIZE - 1, "%f",zoom[0]);
+    snprintf(buff, OPVP_BUFF_SIZE - 1, "%f",opdev->globals.zoom[0]);
     zmps.data = (byte *)buff;
     zmps.size = strlen(buff) + 1;
     zmps.persistent = false;
@@ -3105,7 +3410,7 @@ opvp_get_params(gx_device *dev, gs_param
     if (code) return code;
 
     /* get params */
-    return _get_params(plist);
+    return _get_params(dev, plist);
 }
 
 /*
@@ -3121,14 +3426,14 @@ oprp_get_params(gx_device *dev, gs_param
     if (code) return code;
 
     /* get params */
-    return _get_params(plist);
+    return _get_params(dev, plist);
 }
 
 /*
  * put params
  */
 static  int
-_put_params(gs_param_list *plist)
+_put_params(gx_device *dev, gs_param_list *plist)
 {
     int code;
     int ecode = 0;
@@ -3144,6 +3449,7 @@ _put_params(gs_param_list *plist)
     gs_param_string mrps;
     gs_param_string mbps;
     gs_param_string zmps;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
     /* vector driver name */
     pname = "Driver";
@@ -3153,10 +3459,10 @@ _put_params(gs_param_list *plist)
         buff = realloc(buff, vdps.size + 1);
         memcpy(buff, vdps.data, vdps.size);
         buff[vdps.size] = 0;
-        opvp_alloc_string(&vectorDriver, buff);
+        opvp_alloc_string(&(opdev->globals.vectorDriver), buff);
         break;
     case 1:
-        /* opvp_alloc_string(&vectorDriver, NULL);*/
+        /* opvp_alloc_string(&(opdev->globals.vectorDriver), NULL);*/
         break;
     default:
         ecode = code;
@@ -3171,10 +3477,10 @@ _put_params(gs_param_list *plist)
         buff = realloc(buff, pmps.size + 1);
         memcpy(buff, pmps.data, pmps.size);
         buff[pmps.size] = 0;
-        opvp_alloc_string(&printerModel, buff);
+        opvp_alloc_string(&(opdev->globals.printerModel), buff);
         break;
     case 1:
-        /*opvp_alloc_string(&printerModel, NULL);*/
+        /*opvp_alloc_string(&(opdev->globals.printerModel), NULL);*/
         break;
     default:
         ecode = code;
@@ -3189,10 +3495,10 @@ _put_params(gs_param_list *plist)
         buff = realloc(buff, jips.size + 1);
         memcpy(buff, jips.data, jips.size);
         buff[jips.size] = 0;
-        opvp_alloc_string(&jobInfo, buff);
+        opvp_alloc_string(&(opdev->globals.jobInfo), buff);
         break;
     case 1:
-        /*opvp_alloc_string(&jobInfo, NULL);*/
+        /*opvp_alloc_string(&(opdev->globals.jobInfo), NULL);*/
         break;
     default:
         ecode = code;
@@ -3207,10 +3513,10 @@ _put_params(gs_param_list *plist)
         buff = realloc(buff, dips.size + 1);
         memcpy(buff, dips.data, dips.size);
         buff[dips.size] = 0;
-        opvp_alloc_string(&docInfo, buff);
+        opvp_alloc_string(&(opdev->globals.docInfo), buff);
         break;
     case 1:
-        /*opvp_alloc_string(&docInfo, NULL);*/
+        /*opvp_alloc_string(&(opdev->globals.docInfo), NULL);*/
         break;
     default:
         ecode = code;
@@ -3256,7 +3562,7 @@ _put_params(gs_param_list *plist)
         buff = realloc(buff, mlps.size + 1);
         memcpy(buff, mlps.data, mlps.size);
         buff[mlps.size] = 0;
-        margins[0] = atof(buff);
+        opdev->globals.margins[0] = atof(buff);
         break;
     case 1:
         break;
@@ -3271,7 +3577,7 @@ _put_params(gs_param_list *plist)
         buff = realloc(buff, mtps.size + 1);
         memcpy(buff, mtps.data, mtps.size);
         buff[mtps.size] = 0;
-        margins[3] = atof(buff);
+        opdev->globals.margins[3] = atof(buff);
         break;
     case 1:
         break;
@@ -3286,7 +3592,7 @@ _put_params(gs_param_list *plist)
         buff = realloc(buff, mrps.size + 1);
         memcpy(buff, mrps.data, mrps.size);
         buff[mrps.size] = 0;
-        margins[2] = atof(buff);
+        opdev->globals.margins[2] = atof(buff);
         break;
     case 1:
         break;
@@ -3301,7 +3607,7 @@ _put_params(gs_param_list *plist)
         buff = realloc(buff, mbps.size + 1);
         memcpy(buff, mbps.data, mbps.size);
         buff[mbps.size] = 0;
-        margins[1] = atof(buff);
+        opdev->globals.margins[1] = atof(buff);
         break;
     case 1:
         break;
@@ -3319,15 +3625,15 @@ _put_params(gs_param_list *plist)
         memcpy(buff, zmps.data, zmps.size);
         buff[zmps.size] = 0;
         if (strncasecmp(buff, "Auto", 4)) {
-            zoom[0] = atof(buff);
-            if (zoom[0] > 0) {
-                zoom[1] = zoom[0];
+            opdev->globals.zoom[0] = atof(buff);
+            if (opdev->globals.zoom[0] > 0) {
+                opdev->globals.zoom[1] = opdev->globals.zoom[0];
             } else {
-                zoom[0] = zoom[1] = 1;
+                opdev->globals.zoom[0] = opdev->globals.zoom[1] = 1;
             }
         } else {
-            zoom[0] = zoom[1] = 1;
-            zoomAuto = true;
+            opdev->globals.zoom[0] = opdev->globals.zoom[1] = 1;
+            opdev->globals.zoomAuto = true;
         }
         break;
     case 1:
@@ -3351,7 +3657,7 @@ opvp_put_params(gx_device *dev, gs_param
     int code;
 
     /* put params */
-    code = _put_params(plist);
+    code = _put_params(dev, plist);
     if (code) return code;
 
     /* put default params */
@@ -3367,7 +3673,7 @@ oprp_put_params(gx_device *dev, gs_param
     int code;
 
     /* put params */
-    code = _put_params(plist);
+    code = _put_params(dev, plist);
     if (code) return code;
 
     /* put default params */
@@ -3444,13 +3750,14 @@ opvp_fill_path(
 {
     bool draw_image = false;
     gs_fixed_rect inner, outer;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
     /* check if paths are too complex */
     if (!checkPath(ppath) || !checkCPath(pxpath)) {
         return gx_default_fill_path(dev, pgs, ppath, params, pdevc, pxpath);
     }
     /* check clippath support */
-    if (!(apiEntry->opvpSetClipPath)) {
+    if (!(opdev->globals.apiEntry->opvpSetClipPath)) {
         /* get clipping box area */
         gx_cpath_inner_box(pxpath,&inner);
         gx_cpath_outer_box(pxpath,&outer);
@@ -3462,7 +3769,7 @@ opvp_fill_path(
         }
     }
 
-    if (!vector || draw_image) {
+    if (!(opdev->globals.vector) || draw_image) {
         return gx_default_fill_path(dev, pgs, ppath, params, pdevc, pxpath);
     }
 
@@ -3483,6 +3790,7 @@ opvp_stroke_path(
 {
     bool draw_image = false;
     gs_fixed_rect inner, outer;
+    gx_device_opvp *opdev = (gx_device_opvp *) dev;
 
     /* check if paths are too complex */
     if (!checkPath(ppath) || !checkCPath(pxpath)) {
@@ -3490,7 +3798,7 @@ opvp_stroke_path(
                                       params, pdcolor, pxpath);
     }
     /* check clippath support */
-    if (!(apiEntry->opvpSetClipPath)) {
+    if (!(opdev->globals.apiEntry->opvpSetClipPath)) {
         /* get clipping box area */
         gx_cpath_inner_box(pxpath,&inner);
         gx_cpath_outer_box(pxpath,&outer);
@@ -3502,7 +3810,7 @@ opvp_stroke_path(
         }
     }
 
-    if (!vector || draw_image) {
+    if (!(opdev->globals.vector) || draw_image) {
         return gx_default_stroke_path(dev, pgs, ppath,
                                       params, pdcolor, pxpath);
     }
@@ -3530,7 +3838,9 @@ opvp_fill_mask(
     gs_logical_operation_t lop,
     const gx_clip_path *pcpath)
 {
-    if (vector) {
+    gx_device_opvp *opdev = (gx_device_opvp*) dev;
+
+    if (opdev->globals.vector) {
         int code;
         code = gdev_vector_update_fill_color((gx_device_vector *)dev, NULL, pdcolor);
         if (code < 0)   return code;
@@ -3574,6 +3884,7 @@ opvp_begin_typed_image(
     int p;
     float mag[2] = {1, 1};
     const gs_color_space *pcs;
+    gx_device_opvp *opdev = (gx_device_opvp *)dev;
 
     /* check if paths are too complex */
     if (pic->type->index != 1 || !checkCPath(pcpath))
@@ -3757,7 +4068,7 @@ opvp_begin_typed_image(
                  * 3 planes 24 bits color image
                  * (8 bits per plane)
                  */
-                if (apiEntry->opvpStartDrawImage) {
+                if (opdev->globals.apiEntry->opvpStartDrawImage) {
                     draw_image = true;
                 }
             }
@@ -3768,54 +4079,51 @@ opvp_begin_typed_image(
         *pinfo = (gx_image_enum_common_t *)vinfo;
 
         if (!ecode) {
+            opvp_cspace_t ncspace;
+
             if (!pim->ImageMask) {
                 /* call SetPaintMode */
-                if (apiEntry->opvpSetPaintMode) {
-                    apiEntry->opvpSetPaintMode(printerContext,
+                if (opdev->globals.apiEntry->opvpSetPaintMode) {
+                    opdev->globals.apiEntry->opvpSetPaintMode(opdev->globals.printerContext,
                        OPVP_PAINTMODE_OPAQUE);
                     change_paint_mode = true;
                 }
                 /* set color space */
-                if (apiEntry->opvpSetColorSpace != NULL) {
-                    opvp_cspace_t ncspace;
-
-                    savedColorSpace = colorSpace;
-                    switch (bits_per_pixel) {
-                    case 1:
-                        ncspace = OPVP_CSPACE_DEVICEGRAY;
-                        bits_per_pixel = 8;
-                        if (!cspace_available[ncspace]) {
-                            ncspace = OPVP_CSPACE_STANDARDRGB;
-                            bits_per_pixel = 24;
-                        }
-                        break;
-                    case 8:
-                        ncspace = OPVP_CSPACE_DEVICEGRAY;
-                        if (!cspace_available[ncspace]) {
-                            ncspace = OPVP_CSPACE_STANDARDRGB;
-                            bits_per_pixel = 24;
-                        }
-                        break;
-                    case 24:
-                        ncspace = OPVP_CSPACE_DEVICERGB;
-                        if (!cspace_available[ncspace]) {
-                            ncspace = OPVP_CSPACE_STANDARDRGB;
-                        }
-                        break;
-                    default:
+                opdev->globals.savedColorSpace = opdev->globals.colorSpace;
+                switch (bits_per_pixel) {
+                case 1:
+                    ncspace = OPVP_CSPACE_DEVICEGRAY;
+                    bits_per_pixel = 8;
+                    if (!cspace_available[ncspace]) {
+                        ncspace = OPVP_CSPACE_STANDARDRGB;
+                        bits_per_pixel = 24;
+                    }
+                    break;
+                case 8:
+                    ncspace = OPVP_CSPACE_DEVICEGRAY;
+                    if (!cspace_available[ncspace]) {
+                        ncspace = OPVP_CSPACE_STANDARDRGB;
+                        bits_per_pixel = 24;
+                    }
+                    break;
+                case 24:
+                    ncspace = OPVP_CSPACE_DEVICERGB;
+                    if (!cspace_available[ncspace]) {
+                        ncspace = OPVP_CSPACE_STANDARDRGB;
+                    }
+                    break;
+                default:
+                    r = -1;
+                    goto fallthrough;
+                    break;
+                }
+                if (ncspace != opdev->globals.colorSpace) {
+                    if (gsopvpSetColorSpace(dev, opdev->globals.printerContext, ncspace) != OPVP_OK) {
                         r = -1;
                         goto fallthrough;
-                        break;
-                    }
-                    if (ncspace != colorSpace) {
-                        if (apiEntry->opvpSetColorSpace(printerContext,ncspace)
-                             != OPVP_OK) {
-                            r = -1;
-                            goto fallthrough;
-                        }
-                        colorSpace = ncspace;
-                        change_cspace = true;
                     }
+                    opdev->globals.colorSpace = ncspace;
+                    change_cspace = true;
                 }
             }
         }
@@ -3832,8 +4140,8 @@ opvp_begin_typed_image(
                 ctm.d = mtx.yy;
                 ctm.e = mtx.tx;
                 ctm.f = mtx.ty;
-                if (apiEntry->opvpSetCTM) {
-                    r = apiEntry->opvpSetCTM(printerContext, &ctm);
+                if (opdev->globals.apiEntry->opvpSetCTM) {
+                    r = opdev->globals.apiEntry->opvpSetCTM(opdev->globals.printerContext, &ctm);
                 }
                 else r = -1;
                 if (r != OPVP_OK) ecode = r;
@@ -3841,38 +4149,33 @@ opvp_begin_typed_image(
         }
         if (!ecode) {
             int dw,dh;
+            opvp_int_t adj_raster;
 
             /* image size */
             if (mag[0] != 1) {
-                dw = floor(vinfo->width * mag[0]+0.5);
+                dw = (int) floor(vinfo->width * mag[0]+0.5);
             } else {
                 dw = vinfo->width;
             }
             if (mag[1] != 1) {
-                dh = floor(vinfo->height * mag[1]+0.5);
+                dh = (int) floor(vinfo->height * mag[1]+0.5);
             } else {
                 dh = vinfo->height;
             }
             /* call StartDrawImage */
-            if (apiEntry->opvpStartDrawImage) {
-                opvp_int_t adj_raster;
-
-                adj_raster = bits_per_pixel*vinfo->width;
-                adj_raster = ((adj_raster+31) >> 5) << 2;
-                r = apiEntry->opvpStartDrawImage(
-                                        printerContext,
-                                        vinfo->width,
-                                        vinfo->height,
-                                        adj_raster,
-                                        pim->ImageMask ?
-                                          OPVP_IFORMAT_MASK:
-                                          OPVP_IFORMAT_RAW,
-                                        dw,dh);
-                if(r != OPVP_OK) {
-                    if (apiEntry->opvpEndDrawImage) {
-                        apiEntry->opvpEndDrawImage(printerContext);
-                    }
-                }
+            adj_raster = bits_per_pixel*vinfo->width;
+            adj_raster = ((adj_raster+31) >> 5) << 2;
+            r = gsopvpStartDrawImage(dev,
+                                    opdev->globals.printerContext,
+                                    vinfo->width,
+                                    vinfo->height,
+                                    adj_raster,
+                                    pim->ImageMask ?
+                                        OPVP_IFORMAT_MASK:
+                                        OPVP_IFORMAT_RAW,
+                                    dw,dh);
+            if(r != OPVP_OK) {
+                gsopvpEndDrawImage(dev, opdev->globals.printerContext);
             }
 
             /* bugfix for 32bit CMYK image print error */
@@ -3880,23 +4183,20 @@ fallthrough:
             if(r != OPVP_OK) {
                 if (change_paint_mode) {
                     /* restore paint mode */
-                    if (apiEntry->opvpSetPaintMode) {
-                        apiEntry->opvpSetPaintMode(printerContext,
+                    if (opdev->globals.apiEntry->opvpSetPaintMode) {
+                        opdev->globals.apiEntry->opvpSetPaintMode(opdev->globals.printerContext,
                            OPVP_PAINTMODE_TRANSPARENT);
                     }
                     change_paint_mode = false;
                 }
                 if (change_cspace) {
                     /* restore color space */
-                    colorSpace = savedColorSpace;
-                    if (apiEntry->opvpSetColorSpace) {
-                        apiEntry->opvpSetColorSpace(printerContext,
-                           colorSpace);
-                    }
+                    opdev->globals.colorSpace = opdev->globals.savedColorSpace;
+                    gsopvpSetColorSpace(dev, opdev->globals.printerContext, opdev->globals.colorSpace);
                     change_cspace = false;
                 }
-                if(apiEntry->opvpResetCTM) {
-                    apiEntry->opvpResetCTM(printerContext); /* reset CTM */
+                if(opdev->globals.apiEntry->opvpResetCTM) {
+                    opdev->globals.apiEntry->opvpResetCTM(opdev->globals.printerContext); /* reset CTM */
                 }
                 goto fallback;
             }
@@ -3942,6 +4242,7 @@ opvp_image_plane_data(
     bbox_image_enum *pbe;
     gx_image_enum *tinfo;
     const gs_gstate *pgs;
+    gx_device_opvp *opdev = (gx_device_opvp*)(info->dev);
 
     vinfo = (gdev_vector_image_enum_t *)info;
 
@@ -4037,7 +4338,7 @@ opvp_image_plane_data(
             if(color_index == gs_color_space_index_Indexed) {
                 if (base_color_index == gs_color_space_index_DeviceGray ||
                    base_color_index == gs_color_space_index_CIEA) {
-                    if (colorSpace == OPVP_CSPACE_DEVICEGRAY) {
+                    if (opdev->globals.colorSpace == OPVP_CSPACE_DEVICEGRAY) {
                         /* Convert indexed gray color -> Gray */
                         if (bits_per_pixel == 8) { /* 8bit image */
                             dst_bytes = data_bytes;
@@ -4193,8 +4494,8 @@ opvp_image_plane_data(
             /* Convert Gray */
             if(color_index == gs_color_space_index_DeviceGray ||
                color_index == gs_color_space_index_CIEA) {
-                if (colorSpace == OPVP_CSPACE_STANDARDRGB
-                  || colorSpace == OPVP_CSPACE_DEVICERGB) {
+                if (opdev->globals.colorSpace == OPVP_CSPACE_STANDARDRGB
+                  || opdev->globals.colorSpace == OPVP_CSPACE_DEVICERGB) {
                     /* convert to RGB */
                     if (bits_per_pixel == 8) { /* 8bit image */
                         dst_bytes = data_bytes * 3;
@@ -4206,7 +4507,7 @@ opvp_image_plane_data(
                                 src_ptr = buf + raster_length * i;
                                 dst_ptr = tmp_buf + dst_length * i;
                                 for (j = 0; j < data_bytes; j++) {
-                                    unsigned char d = floor(
+                                    unsigned char d = (unsigned char) floor(
                                       imageDecode[0]*255 + src_ptr[j]*
                                       (imageDecode[1]-imageDecode[0])+0.5);
 
@@ -4234,7 +4535,7 @@ opvp_image_plane_data(
                                 for (j = 0; j < vinfo->width; j++) {
                                     int o = ((src_ptr[j/8] & (1 << (7 - (j & 7))))
                                               != 0);
-                                    unsigned char d = floor(
+                                    unsigned char d =  (unsigned char) floor(
                                       imageDecode[0]*255 + o*
                                       (imageDecode[1]-imageDecode[0])*255+0.5);
                                     dst_ptr[j*3] = d; /* R */
@@ -4250,7 +4551,7 @@ opvp_image_plane_data(
                             vinfo->bits_per_pixel = 24;
                         }
                     }
-                } else if (colorSpace == OPVP_CSPACE_DEVICEGRAY) {
+                } else if (opdev->globals.colorSpace == OPVP_CSPACE_DEVICEGRAY) {
                     if (bits_per_pixel == 1) { /* 1bit image */
                         dst_bytes = vinfo->width;
                         dst_length = ((dst_bytes + 3) >> 2) << 2;
@@ -4263,7 +4564,7 @@ opvp_image_plane_data(
                                 for (j = 0; j < vinfo->width; j++) {
                                     int o = ((src_ptr[j/8] & (1 << (7 - (j & 7))))
                                               != 0);
-                                    unsigned char d = floor(
+                                    unsigned char d = (unsigned char) floor(
                                       imageDecode[0]*255 + o*
                                       (imageDecode[1]-imageDecode[0])*255+0.5);
                                     dst_ptr[j] = d; /* R */
@@ -4299,8 +4600,8 @@ opvp_image_plane_data(
         }
 
         /* call TansferDrawImage */
-        if (apiEntry->opvpTransferDrawImage) {
-            apiEntry->opvpTransferDrawImage(printerContext,
+        if (opdev->globals.apiEntry->opvpTransferDrawImage) {
+            opdev->globals.apiEntry->opvpTransferDrawImage(opdev->globals.printerContext,
                         raster_length * height, (void *)buf);
         }
     }
@@ -4325,21 +4626,20 @@ opvp_image_end_image(gx_image_enum_commo
     gx_device_vector *vdev = (gx_device_vector *)dev;
     gdev_vector_image_enum_t *vinfo;
     opvp_ctm_t ctm;
+    gx_device_opvp *opdev = (gx_device_opvp*)dev;
 
     vinfo = (gdev_vector_image_enum_t *)info;
 
     if (begin_image) {
         /* call EndDrawImage */
-        if (apiEntry->opvpEndDrawImage) {
-            apiEntry->opvpEndDrawImage(printerContext);
-        }
+        gsopvpEndDrawImage(dev, opdev->globals.printerContext);
 
         begin_image = false;
 
         if (FastImageMode != FastImageNoCTM) {
             /* call ResetCTM */
-            if (apiEntry->opvpResetCTM) {
-                apiEntry->opvpResetCTM(printerContext);
+            if (opdev->globals.apiEntry->opvpResetCTM) {
+                opdev->globals.apiEntry->opvpResetCTM(opdev->globals.printerContext);
             } else {
                 /* call SetCTM */
                 ctm.a = 1;
@@ -4348,25 +4648,24 @@ opvp_image_end_image(gx_image_enum_commo
                 ctm.d = 1;
                 ctm.e = 0;
                 ctm.f = 0;
-                if (apiEntry->opvpSetCTM) {
-                    apiEntry->opvpSetCTM(printerContext, &ctm);
+                if (opdev->globals.apiEntry->opvpSetCTM) {
+                    opdev->globals.apiEntry->opvpSetCTM(opdev->globals.printerContext, &ctm);
                 }
             }
         }
         if (change_paint_mode) {
             /* restore paint mode */
-            if (apiEntry->opvpSetPaintMode) {
-                apiEntry->opvpSetPaintMode(printerContext,
+            if (opdev->globals.apiEntry->opvpSetPaintMode) {
+                opdev->globals.apiEntry->opvpSetPaintMode(opdev->globals.printerContext,
                    OPVP_PAINTMODE_TRANSPARENT);
             }
             change_paint_mode = false;
         }
         if (change_cspace) {
             /* restore color space */
-            colorSpace = savedColorSpace;
-            if (apiEntry->opvpSetColorSpace) {
-                apiEntry->opvpSetColorSpace(printerContext,
-                   colorSpace);
+            opdev->globals.colorSpace = opdev->globals.savedColorSpace;
+            if (gsopvpSetColorSpace(dev, opdev->globals.printerContext, opdev->globals.colorSpace) != OPVP_OK) {
+                return -1;
             }
             change_cspace = false;
         }
@@ -4382,20 +4681,20 @@ opvp_image_end_image(gx_image_enum_commo
 static  int
 opvp_beginpage(gx_device_vector *vdev)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     int code = -1;
     int ecode = 0;
 
 #ifdef OPVP_IGNORE_BLANK_PAGE
-    if (pdev->in_page) return 0;
+    if (opdev->in_page) return 0;
 #endif
     /* start page */
-    code = opvp_startpage((gx_device *)pdev);
+    code = opvp_startpage((gx_device *)opdev);
     if (code) {
         ecode = code;
     } else {
-        pdev->in_page = true;   /* added '05.12.07 */
-        beginPage = true;
+        opdev->in_page = true;   /* added '05.12.07 */
+        opdev->globals.beginPage = true;
     }
 
     return ecode;
@@ -4407,18 +4706,19 @@ opvp_beginpage(gx_device_vector *vdev)
 static  int
 opvp_setlinewidth(gx_device_vector *vdev, double width)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int ecode = 0;
     opvp_fix_t w;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* call SetLineWidth */
     OPVP_F2FIX(width, w);
-    if (apiEntry->opvpSetLineWidth) {
-        r = apiEntry->opvpSetLineWidth(printerContext, w);
+    if (opdev->globals.apiEntry->opvpSetLineWidth) {
+        r = opdev->globals.apiEntry->opvpSetLineWidth(opdev->globals.printerContext, w);
     }
     if (r != OPVP_OK) {
         ecode = -1;
@@ -4433,13 +4733,14 @@ opvp_setlinewidth(gx_device_vector *vdev
 static  int
 opvp_setlinecap(gx_device_vector *vdev, gs_line_cap cap)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int ecode = 0;
     opvp_linecap_t linecap;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     switch (cap) {
     case gs_cap_butt:
@@ -4458,8 +4759,8 @@ opvp_setlinecap(gx_device_vector *vdev,
     }
 
     /* call SetLineCap */
-    if (apiEntry->opvpSetLineCap) {
-        r = apiEntry->opvpSetLineCap(printerContext, linecap);
+    if (opdev->globals.apiEntry->opvpSetLineCap) {
+        r = opdev->globals.apiEntry->opvpSetLineCap(opdev->globals.printerContext, linecap);
     }
     if (r != OPVP_OK) {
         ecode = -1;
@@ -4474,13 +4775,14 @@ opvp_setlinecap(gx_device_vector *vdev,
 static  int
 opvp_setlinejoin(gx_device_vector *vdev, gs_line_join join)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int ecode = 0;
     opvp_linejoin_t linejoin;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     switch (join) {
     case gs_join_miter:
@@ -4500,8 +4802,8 @@ opvp_setlinejoin(gx_device_vector *vdev,
     }
 
     /* call SetLineJoin */
-    if (apiEntry->opvpSetLineJoin) {
-        r = apiEntry->opvpSetLineJoin(printerContext, linejoin);
+    if (opdev->globals.apiEntry->opvpSetLineJoin) {
+        r = opdev->globals.apiEntry->opvpSetLineJoin(opdev->globals.printerContext, linejoin);
     }
     if (r != OPVP_OK) {
         ecode = -1;
@@ -4516,18 +4818,19 @@ opvp_setlinejoin(gx_device_vector *vdev,
 static  int
 opvp_setmiterlimit(gx_device_vector *vdev, double limit)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int ecode = 0;
     opvp_fix_t l;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* call SetMiterLimit */
     OPVP_F2FIX(limit, l);
-    if (apiEntry->opvpSetMiterLimit) {
-        r = apiEntry->opvpSetMiterLimit(printerContext, l);
+    if (opdev->globals.apiEntry->opvpSetMiterLimit) {
+        r = opdev->globals.apiEntry->opvpSetMiterLimit(opdev->globals.printerContext, l);
     }
     if (r != OPVP_OK) {
         ecode = -1;
@@ -4546,7 +4849,7 @@ opvp_setdash(
     uint count,
     double offset)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int ecode = 0;
     opvp_fix_t *p = NULL;
@@ -4554,7 +4857,8 @@ opvp_setdash(
     int i;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* pattern */
     if (count) {
@@ -4570,9 +4874,7 @@ opvp_setdash(
 
     /* call SetLineDash */
     if (!ecode) {
-        if (apiEntry->opvpSetLineDash) {
-            r = apiEntry->opvpSetLineDash(printerContext, count,p);
-        }
+        r = gsopvpSetLineDash((gx_device*) vdev, opdev->globals.printerContext, count,p);
         if (r != OPVP_OK) {
             ecode = -1;
         }
@@ -4581,8 +4883,8 @@ opvp_setdash(
     /* call SetLineDashOffset */
     if (!ecode) {
         OPVP_F2FIX(offset, o);
-        if (apiEntry->opvpSetLineDashOffset) {
-            r = apiEntry->opvpSetLineDashOffset(printerContext, o);
+        if (opdev->globals.apiEntry->opvpSetLineDashOffset) {
+            r = opdev->globals.apiEntry->opvpSetLineDashOffset(opdev->globals.printerContext, o);
         }
         if (r != OPVP_OK) {
             ecode = -1;
@@ -4591,8 +4893,8 @@ opvp_setdash(
 
     /* call SetLineStyle */
     if (!ecode) {
-        if (apiEntry->opvpSetLineStyle) {
-            r = apiEntry->opvpSetLineStyle(printerContext,
+        if (opdev->globals.apiEntry->opvpSetLineStyle) {
+            r = opdev->globals.apiEntry->opvpSetLineStyle(opdev->globals.printerContext,
                                   (count ?
                                    OPVP_LINESTYLE_DASH :
                                    OPVP_LINESTYLE_SOLID));
@@ -4613,11 +4915,12 @@ opvp_setdash(
 static  int
 opvp_setflat(gx_device_vector *vdev, double flatness)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     int ecode = 0;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* what to do ? */
 
@@ -4654,26 +4957,26 @@ opvp_setfillcolor(
     const gs_gstate *pgs, /* added for gs 8.15 */
     const gx_drawing_color *pdc)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int ecode = 0;
     gx_color_index color;
     static opvp_brush_t brush;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     if (!gx_dc_is_pure(pdc)) return_error(gs_error_rangecheck);
 
     /* color */
-    if (!vectorFillColor) vectorFillColor = &brush;
+    if (!opdev->globals.vectorFillColor)
+        opdev->globals.vectorFillColor = &brush;
     color = gx_dc_pure_color(pdc);
-    opvp_set_brush_color(pdev, color, vectorFillColor);
+    opvp_set_brush_color(opdev, color, opdev->globals.vectorFillColor);
 
     /* call SetFillColor */
-    if (apiEntry->opvpSetFillColor) {
-        r = apiEntry->opvpSetFillColor(printerContext, vectorFillColor);
-    }
+    r = gsopvpSetFillColor((gx_device*) vdev, opdev->globals.printerContext, opdev->globals.vectorFillColor);
     if (r != OPVP_OK) {
         ecode = -1;
     }
@@ -4690,25 +4993,24 @@ opvp_setstrokecolor(
     const gs_gstate *pgs, /* added for gs 8.15 */
     const gx_drawing_color *pdc)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int ecode = 0;
     gx_color_index color;
     opvp_brush_t brush;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     if (!gx_dc_is_pure(pdc)) return_error(gs_error_rangecheck);
 
     /* color */
     color = gx_dc_pure_color(pdc);
-    opvp_set_brush_color(pdev, color, &brush);
+    opvp_set_brush_color(opdev, color, &brush);
 
     /* call SetStrokeColor */
-    if (apiEntry->opvpSetStrokeColor) {
-        r = apiEntry->opvpSetStrokeColor(printerContext, &brush);
-    }
+    r = gsopvpSetStrokeColor((gx_device*) vdev, opdev->globals.printerContext, &brush);
     if (r != OPVP_OK) {
         ecode = -1;
     }
@@ -4728,7 +5030,7 @@ opvp_vector_dopath(
     gx_path_type_t type,
     const gs_matrix *pmat)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int code = -1;
     int ecode = 0;
@@ -4755,7 +5057,8 @@ opvp_vector_dopath(
     current.x = current.y = 0;
 #endif
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     if (gx_path_is_rectangle(ppath, &rect))
     return (*vdev_proc(vdev, dorect))(vdev,
@@ -4797,9 +5100,9 @@ opvp_vector_dopath(
             switch (pop) {
             case gs_pe_moveto:
                 /* call SetCurrentPoint */
-                if (apiEntry->opvpSetCurrentPoint) {
-                    r = apiEntry->opvpSetCurrentPoint(
-                       printerContext,
+                if (opdev->globals.apiEntry->opvpSetCurrentPoint) {
+                    r = opdev->globals.apiEntry->opvpSetCurrentPoint(
+                        opdev->globals.printerContext,
                        opvp_p[npoints-1].x,
                        opvp_p[npoints-1].y);
                 }
@@ -4807,9 +5110,9 @@ opvp_vector_dopath(
                 break;
             case gs_pe_lineto:
                 /* call LinePath */
-                if (apiEntry->opvpLinePath) {
-                    r = apiEntry->opvpLinePath(
-                       printerContext,
+                if (opdev->globals.apiEntry->opvpLinePath) {
+                    r = opdev->globals.apiEntry->opvpLinePath(
+                        opdev->globals.printerContext,
                        OPVP_PATHOPEN,
                        npoints - 1,
                        &(opvp_p[1]));
@@ -4819,9 +5122,9 @@ opvp_vector_dopath(
             case gs_pe_curveto:
                 /* npoints */
                 /* call BezierPath */
-                if (apiEntry->opvpBezierPath) {
-                    r = apiEntry->opvpBezierPath(
-                       printerContext,
+                if (opdev->globals.apiEntry->opvpBezierPath) {
+                    r = opdev->globals.apiEntry->opvpBezierPath(
+                        opdev->globals.printerContext,
                        npoints - 1,
                        &(opvp_p[1])
                        );
@@ -4950,8 +5253,8 @@ opvp_vector_dopath(
             break;
         default:
             /* error */
-            return_error(gs_error_unknownerror);
-            break;
+            ecode = gs_note_error(gs_error_unknownerror);
+            goto exit;
         }
 
 #ifdef  OPVP_OPT_MULTI_PATH
@@ -4963,6 +5266,7 @@ opvp_vector_dopath(
     code = (*vdev_proc(vdev, endpath))(vdev, type);
     if (code) ecode = code;
 
+exit:
 #ifdef  OPVP_OPT_MULTI_PATH
     if (points) free(points);
     if (opvp_p) free(opvp_p);
@@ -4982,7 +5286,7 @@ opvp_vector_dorect(
     fixed y1,
     gx_path_type_t type)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int code = -1;
     int ecode = 0;
@@ -4991,7 +5295,8 @@ opvp_vector_dorect(
     _fPoint p;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* begin path */
     code = (*vdev_proc(vdev, beginpath))(vdev, type);
@@ -5010,8 +5315,9 @@ opvp_vector_dorect(
         OPVP_F2FIX(p.y, rectangles[0].p1.y);
 
         /* call RectanglePath */
-        if (apiEntry->opvpRectanglePath) {
-            r = apiEntry->opvpRectanglePath(printerContext,
+        if (opdev->globals.apiEntry->opvpRectanglePath) {
+            r = opdev->globals.apiEntry->opvpRectanglePath(
+                                   opdev->globals.printerContext,
                                    1,
                                    rectangles);
         }
@@ -5038,22 +5344,23 @@ opvp_vector_dorect(
 static  int
 opvp_beginpath(gx_device_vector *vdev, gx_path_type_t type)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int ecode = 0;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* check clip-path */
     if (type & gx_path_type_clip) {
-        if (apiEntry->opvpResetClipPath)
-        apiEntry->opvpResetClipPath(printerContext);
+        if (opdev->globals.apiEntry->opvpResetClipPath)
+        opdev->globals.apiEntry->opvpResetClipPath(opdev->globals.printerContext);
     }
 
     /* call NewPath */
-    if (apiEntry->opvpNewPath) {
-        r = apiEntry->opvpNewPath(printerContext);
+    if (opdev->globals.apiEntry->opvpNewPath) {
+        r = opdev->globals.apiEntry->opvpNewPath(opdev->globals.printerContext);
     }
     if (r != OPVP_OK) {
         ecode = -1;
@@ -5074,19 +5381,20 @@ opvp_moveto(
     double y1,
     gx_path_type_t type)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int ecode = 0;
     opvp_point_t p;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* call SetCurrentPoint */
     OPVP_F2FIX(x1, p.x);
     OPVP_F2FIX(y1, p.y);
-    if (apiEntry->opvpSetCurrentPoint) {
-        r = apiEntry->opvpSetCurrentPoint(printerContext, p.x, p.y);
+    if (opdev->globals.apiEntry->opvpSetCurrentPoint) {
+        r = opdev->globals.apiEntry->opvpSetCurrentPoint(opdev->globals.printerContext, p.x, p.y);
     }
     if (r != OPVP_OK) {
         ecode = -1;
@@ -5107,21 +5415,23 @@ opvp_lineto(
     double y1,
     gx_path_type_t type)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int ecode = 0;
     opvp_point_t points[1];
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* point */
     OPVP_F2FIX(x1, points[0].x);
     OPVP_F2FIX(y1, points[0].y);
 
     /* call LinePath */
-    if (apiEntry->opvpLinePath) {
-        r = apiEntry->opvpLinePath(printerContext, OPVP_PATHOPEN, 1, points);
+    if (opdev->globals.apiEntry->opvpLinePath) {
+        r = opdev->globals.apiEntry->opvpLinePath(
+                  opdev->globals.printerContext, OPVP_PATHOPEN, 1, points);
     }
     if (r != OPVP_OK) {
         ecode = -1;
@@ -5146,13 +5456,14 @@ opvp_curveto(
     double y3,
     gx_path_type_t type)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int ecode = 0;
     opvp_point_t points[4];
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* points */
     OPVP_F2FIX(x0, points[0].x);
@@ -5165,8 +5476,9 @@ opvp_curveto(
     OPVP_F2FIX(y3, points[3].y);
 
     /* call BezierPath */
-    if (apiEntry->opvpBezierPath) {
-        r = apiEntry->opvpBezierPath(printerContext,
+    if (opdev->globals.apiEntry->opvpBezierPath) {
+        r = opdev->globals.apiEntry->opvpBezierPath(
+                            opdev->globals.printerContext,
                             3,
                             &(points[1])
                             );
@@ -5190,21 +5502,23 @@ opvp_closepath(
     double y_start,
     gx_path_type_t type)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int ecode = 0;
     opvp_point_t points[1];
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* point */
     OPVP_F2FIX(x_start, points[0].x);
     OPVP_F2FIX(y_start, points[0].y);
 
     /* call LinePath */
-    if (apiEntry->opvpLinePath) {
-        r = apiEntry->opvpLinePath(printerContext, OPVP_PATHCLOSE, 1, points);
+    if (opdev->globals.apiEntry->opvpLinePath) {
+        r = opdev->globals.apiEntry->opvpLinePath(
+                          opdev->globals.printerContext, OPVP_PATHCLOSE, 1, points);
     }
     if (r != OPVP_OK) {
         ecode = -1;
@@ -5219,16 +5533,17 @@ opvp_closepath(
 static  int
 opvp_endpath(gx_device_vector *vdev, gx_path_type_t type)
 {
-    gx_device_opvp *pdev = (gx_device_opvp *)vdev;
+    gx_device_opvp *opdev = (gx_device_opvp *)vdev;
     opvp_result_t r = -1;
     int ecode = 0;
 
     /* check page-in */
-    if (opvp_check_in_page(pdev)) return -1;
+    if (opvp_check_in_page(opdev))
+        return -1;
 
     /* call EndPath */
-    if (apiEntry->opvpEndPath) {
-        r = apiEntry->opvpEndPath(printerContext);
+    if (opdev->globals.apiEntry->opvpEndPath) {
+        r = opdev->globals.apiEntry->opvpEndPath(opdev->globals.printerContext);
     }
     if (r != OPVP_OK) {
         ecode = -1;
@@ -5238,9 +5553,9 @@ opvp_endpath(gx_device_vector *vdev, gx_
         /* fill mode */
         if (type & gx_path_type_even_odd) {
             /* call SetFillMode */
-            if (apiEntry->opvpSetFillMode) {
-                r = apiEntry->opvpSetFillMode(
-                   printerContext,
+            if (opdev->globals.apiEntry->opvpSetFillMode) {
+                r = opdev->globals.apiEntry->opvpSetFillMode(
+                   opdev->globals.printerContext,
                    OPVP_FILLMODE_EVENODD
                 );
             }
@@ -5249,9 +5564,9 @@ opvp_endpath(gx_device_vector *vdev, gx_
             }
         } else {
             /* call SetFillMode */
-            if (apiEntry->opvpSetFillMode) {
-                r = apiEntry->opvpSetFillMode(
-                   printerContext,
+            if (opdev->globals.apiEntry->opvpSetFillMode) {
+                r = opdev->globals.apiEntry->opvpSetFillMode(
+                   opdev->globals.printerContext,
                    OPVP_FILLMODE_WINDING
                 );
             }
@@ -5262,16 +5577,16 @@ opvp_endpath(gx_device_vector *vdev, gx_
 
         if (type & gx_path_type_stroke) {
             /* call StrokeFillPath */
-            if (apiEntry->opvpStrokeFillPath) {
-                r = apiEntry->opvpStrokeFillPath(printerContext);
+            if (opdev->globals.apiEntry->opvpStrokeFillPath) {
+                r = opdev->globals.apiEntry->opvpStrokeFillPath(opdev->globals.printerContext);
             }
             if (r != OPVP_OK) {
                 ecode = -1;
             }
         } else {
             /* call FillPath */
-            if (apiEntry->opvpFillPath) {
-                r = apiEntry->opvpFillPath(printerContext);
+            if (opdev->globals.apiEntry->opvpFillPath) {
+                r = opdev->globals.apiEntry->opvpFillPath(opdev->globals.printerContext);
             }
             if (r != OPVP_OK) {
                 ecode = -1;
@@ -5279,9 +5594,9 @@ opvp_endpath(gx_device_vector *vdev, gx_
         }
     } else if (type & gx_path_type_clip) {
         /* call SetClipPath */
-        if (apiEntry->opvpSetClipPath) {
-            r = apiEntry->opvpSetClipPath(
-               printerContext,
+        if (opdev->globals.apiEntry->opvpSetClipPath) {
+            r = opdev->globals.apiEntry->opvpSetClipPath(
+               opdev->globals.printerContext,
                (type & gx_path_type_even_odd
                             ? OPVP_CLIPRULE_EVENODD
                             : OPVP_CLIPRULE_WINDING));
@@ -5291,8 +5606,8 @@ opvp_endpath(gx_device_vector *vdev, gx_
         }
     } else if (type & gx_path_type_stroke) {
         /* call StrokePath */
-        if (apiEntry->opvpStrokePath) {
-            r = apiEntry->opvpStrokePath(printerContext);
+        if (opdev->globals.apiEntry->opvpStrokePath) {
+            r = opdev->globals.apiEntry->opvpStrokePath(opdev->globals.printerContext);
         }
         if (r != OPVP_OK) {
             ecode = -1;
diff -pruN 9.55.0~dfsg-3/contrib/opvp/opvp_0_2_0.h 9.56.1~dfsg-1/contrib/opvp/opvp_0_2_0.h
--- 9.55.0~dfsg-3/contrib/opvp/opvp_0_2_0.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/opvp/opvp_0_2_0.h	2022-04-04 13:46:22.000000000 +0000
@@ -210,7 +210,7 @@ typedef	struct	_OPVP_CTM {
 typedef	struct	_OPVP_api_procs {
         int	(*OpenPrinter)(int,char *,int *,struct _OPVP_api_procs **);
         int	(*ClosePrinter)(int);
-        int	(*StartJob)(int,char *);
+        int	(*StartJob)(int, char *);
         int	(*EndJob)(int);
         int	(*StartDoc)(int,char *);
         int	(*EndDoc)(int);
@@ -238,7 +238,7 @@ typedef	struct	_OPVP_api_procs {
         int	(*GetAlphaConstant)(int,float *);
         int	(*SetLineWidth)(int,OPVP_Fix);
         int	(*GetLineWidth)(int,OPVP_Fix *);
-        int	(*SetLineDash)(int,OPVP_Fix *,int);
+        int	(*SetLineDash)(int, int, OPVP_Fix *);
         int	(*GetLineDash)(int,OPVP_Fix *,int *);
         int	(*SetLineDashOffset)(int,OPVP_Fix);
         int	(*GetLineDashOffset)(int,OPVP_Fix *);
diff -pruN 9.55.0~dfsg-3/contrib/opvp/opvp.h 9.56.1~dfsg-1/contrib/opvp/opvp.h
--- 9.55.0~dfsg-3/contrib/opvp/opvp.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/opvp/opvp.h	2022-04-04 13:46:22.000000000 +0000
@@ -160,7 +160,7 @@ typedef struct _opvp_brushdata {
 #elif defined(__HP_cc)
         opvp_byte_t data[1];
 #else
-        opvp_byte_t data[];
+        opvp_byte_t data[1];
 #endif
 
 } opvp_brushdata_t;
@@ -222,8 +222,8 @@ typedef	struct _opvp_api_procs {
         opvp_result_t (*opvpEndDoc)(opvp_dc_t);
         opvp_result_t (*opvpStartPage)(opvp_dc_t,const opvp_char_t*);
         opvp_result_t (*opvpEndPage)(opvp_dc_t);
-        opvp_result_t (*opvpQueryDeviceCapability)(opvp_dc_t,opvp_flag_t,opvp_int_t*,opvp_byte_t*);
-        opvp_result_t (*opvpQueryDeviceInfo)(opvp_dc_t,opvp_flag_t,opvp_int_t*,opvp_char_t*);
+        opvp_result_t (*opvpQueryDeviceCapability)(opvp_dc_t, opvp_queryinfoflags_t,opvp_int_t*,opvp_byte_t*);
+        opvp_result_t (*opvpQueryDeviceInfo)(opvp_dc_t, opvp_queryinfoflags_t,opvp_int_t*,opvp_char_t*);
         opvp_result_t (*opvpResetCTM)(opvp_dc_t);
         opvp_result_t (*opvpSetCTM)(opvp_dc_t,const opvp_ctm_t*);
         opvp_result_t (*opvpGetCTM)(opvp_dc_t,opvp_ctm_t*);
@@ -287,8 +287,7 @@ typedef	struct _opvp_api_procs {
 } opvp_api_procs_t;
 
 /* Function prototype */
-opvp_dc_t opvpOpenPrinter(
-        opvp_int_t outputFD,
+extern opvp_dc_t opvpOpenPrinter(opvp_int_t outputFD,
         const opvp_char_t *printerModel,
         const opvp_int_t apiVersion[2],
         opvp_api_procs_t **apiProcs);
diff -pruN 9.55.0~dfsg-3/contrib/opvp/opvpharness.c 9.56.1~dfsg-1/contrib/opvp/opvpharness.c
--- 9.55.0~dfsg-3/contrib/opvp/opvpharness.c	1970-01-01 00:00:00.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/opvp/opvpharness.c	2022-04-04 13:46:22.000000000 +0000
@@ -0,0 +1,947 @@
+/* Test harness for checking results after
+   removal of globals in opvp device. Note,
+   0.2 API not tested. Assumption is OpenPrinter
+   is the first method called and ClosePrinter
+   will be the final method called. If not,
+   there will be problems. */
+
+#include "opvp.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+/* Yes globals, but here we don't care */
+FILE *pFile = NULL;
+
+/* Graphic state members of API that are set and get. */
+typedef struct opvp_gstate_s {
+    opvp_ctm_t ctm;
+    opvp_cspace_t colorspace;
+    opvp_fillmode_t fill_mode;
+    opvp_float_t alpha;
+    opvp_fix_t line_width;
+    opvp_int_t line_dash_n;
+    opvp_fix_t *line_dash_array;
+    opvp_fix_t dash_offset;
+    opvp_linestyle_t line_style;
+    opvp_linecap_t line_cap;
+    opvp_linejoin_t line_join;
+    opvp_fix_t miter_limit;
+    opvp_paintmode_t paintmode;
+} opvp_gstate_t;
+
+opvp_gstate_t opv_gstate;
+
+static opvp_result_t
+opvpClosePrinter(opvp_dc_t printerContext)
+{
+    fputs("opvpClosePrinter\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fclose(pFile);
+
+    if (opv_gstate.line_dash_array != NULL)
+        free(opv_gstate.line_dash_array);
+
+    return 0;
+}
+
+static opvp_result_t
+opvpStartJob(opvp_dc_t printerContext, const opvp_char_t *job_info)
+{
+    fputs("opvpStartJob\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tjob_info = %s\n", job_info);
+    return 0;
+}
+
+static opvp_result_t
+opvpEndJob(opvp_dc_t printerContext)
+{
+    fputs("opvpEndJob\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpAbortJob(opvp_dc_t printerContext)
+{
+    fputs("opvpAbortJob\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpStartDoc(opvp_dc_t printerContext, const opvp_char_t *docinfo)
+{
+    fputs("opvpStartDoc\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tjob_info = %s\n", docinfo);
+    return 0;
+}
+
+static opvp_result_t
+opvpEndDoc(opvp_dc_t printerContext)
+{
+    fputs("opvpEndDoc\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpStartPage(opvp_dc_t printerContext, const opvp_char_t *pageinfo)
+{
+    fputs("opvpStartPage\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tjob_info = %s\n", pageinfo);
+    return 0;
+}
+
+static opvp_result_t
+opvpEndPage(opvp_dc_t printerContext)
+{
+    fputs("opvpEndPage\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+/* Not used */
+static opvp_result_t
+opvpQueryDeviceCapability(opvp_dc_t printerContext, opvp_flag_t flag, opvp_int_t *a, opvp_byte_t *b)
+{
+    fputs("opvpQueryDeviceCapability\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+/* Not used */
+static opvp_result_t
+opvpQueryDeviceInfo(opvp_dc_t printerContext, opvp_flag_t flag, opvp_int_t *a, opvp_char_t *b)
+{
+    fputs("opvpQueryDeviceInfo\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpResetCTM(opvp_dc_t printerContext)
+{
+    fputs("opvpResetCTM\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    opv_gstate.ctm.a = 1.0;
+    opv_gstate.ctm.b = 0.0;
+    opv_gstate.ctm.c = 0.0;
+    opv_gstate.ctm.d = 1.0;
+    opv_gstate.ctm.e = 0.0;
+    opv_gstate.ctm.f = 0.0;
+    return 0;
+}
+
+static opvp_result_t
+opvpSetCTM(opvp_dc_t printerContext, const opvp_ctm_t *pctm)
+{
+    fputs("opvpSetCTM\n", pFile);
+    opv_gstate.ctm = *pctm;
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tctm.a = %f\n", pctm->a);
+    fprintf(pFile, "\tctm.b = %f\n", pctm->b);
+    fprintf(pFile, "\tctm.c = %f\n", pctm->c);
+    fprintf(pFile, "\tctm.d = %f\n", pctm->d);
+    fprintf(pFile, "\tctm.e = %f\n", pctm->e);
+    fprintf(pFile, "\tctm.f = %f\n", pctm->f);
+    return 0;
+}
+
+static opvp_result_t
+opvpGetCTM(opvp_dc_t printerContext, opvp_ctm_t *pctm)
+{
+    fputs("opvpGetCTM\n", pFile);
+    *pctm = opv_gstate.ctm;
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpInitGS(opvp_dc_t printerContext)
+{
+    fputs("opvpInitGS\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpSaveGS(opvp_dc_t printerContext)
+{
+    fputs("opvpSaveGS\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpRestoreGS(opvp_dc_t printerContext)
+{
+    fputs("opvpRestoreGS\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpQueryColorSpace(opvp_dc_t printerContext, opvp_int_t *n, opvp_cspace_t *colorspace)
+{
+    fputs("opvpQueryColorSpace\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    *n = 0;
+    *colorspace = opv_gstate.colorspace;
+    return 0;
+}
+
+static opvp_result_t
+opvpSetColorSpace(opvp_dc_t printerContext, opvp_cspace_t colorspace)
+{
+    fputs("opvpSetColorSpace\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tcolorspace = %d\n", colorspace);
+    opv_gstate.colorspace = colorspace;
+    return 0;
+}
+
+static opvp_result_t
+opvpGetColorSpace(opvp_dc_t printerContext, opvp_cspace_t *colorspace)
+{
+    fputs("opvpGetColorSpace\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    *colorspace = opv_gstate.colorspace;
+    return 0;
+}
+
+static opvp_result_t
+opvpSetFillMode(opvp_dc_t printerContext, opvp_fillmode_t fillmode)
+{
+    fputs("opvpSetFillMode\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tfillmode = %d\n", fillmode);
+    opv_gstate.fill_mode = fillmode;
+    return 0;
+}
+
+static opvp_result_t
+opvpGetFillMode(opvp_dc_t printerContext, opvp_fillmode_t *fillmode)
+{
+    fputs("opvpGetFillMode\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    *fillmode = opv_gstate.fill_mode;
+    return 0;
+}
+
+static opvp_result_t
+opvpSetAlphaConstant(opvp_dc_t printerContext, opvp_float_t alpha)
+{
+    fputs("opvpSetAlphaConstant\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\talpha = %f\n", alpha);
+    opv_gstate.alpha = alpha;
+    return 0;
+}
+
+static opvp_result_t
+opvpGetAlphaConstant(opvp_dc_t printerContext, opvp_float_t *alpha)
+{
+    fputs("opvpGetAlphaConstant\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    *alpha = opv_gstate.alpha;
+    return 0;
+}
+
+static opvp_result_t
+opvpSetLineWidth(opvp_dc_t printerContext, opvp_fix_t width)
+{
+    fputs("opvpSetLineWidth\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\twidth = %d\n", width);
+    opv_gstate.line_width = width;
+    return 0;
+}
+
+static opvp_result_t
+opvpGetLineWidth(opvp_dc_t printerContext, opvp_fix_t *width)
+{
+    fputs("opvpGetLineWidth\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    *width = opv_gstate.line_width;
+    return 0;
+}
+
+static opvp_result_t
+opvpSetLineDash(opvp_dc_t printerContext, opvp_int_t n, const opvp_fix_t *dash)
+{
+    int k;
+
+    fputs("opvpSetLineDash\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tn = %d\n", n);
+    opv_gstate.line_dash_n = n;
+
+    if (opv_gstate.line_dash_array != NULL)
+        free(opv_gstate.line_dash_array);
+
+    opv_gstate.line_dash_array = (opvp_fix_t*) malloc(n * sizeof(opvp_fix_t));
+    if (opv_gstate.line_dash_array == NULL) {
+        fprintf(pFile,"\t Error in dash array allocation.  Exiting.");
+        fclose(pFile);
+        exit(-1);
+    }
+
+    for (k = 0; k < n; k++) {
+        opv_gstate.line_dash_array[k] =  dash[k];
+        fprintf(pFile, "\tdash[%d] = %d\n", k, dash[k]);
+    }
+    return 0;
+}
+
+static opvp_result_t
+opvpGetLineDash(opvp_dc_t printerContext, opvp_int_t *n, opvp_fix_t *dash)
+{
+    fputs("opvpGetLineDash\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+
+    *n = opv_gstate.line_dash_n;
+    dash = opv_gstate.line_dash_array;
+
+    return 0;
+}
+
+static opvp_result_t
+opvpSetLineDashOffset(opvp_dc_t printerContext, opvp_fix_t offset)
+{
+    fputs("opvpSetLineDashOffset\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\toffset = %d\n", offset);
+    opv_gstate.dash_offset = offset;
+    return 0;
+}
+
+static opvp_result_t
+opvpGetLineDashOffset(opvp_dc_t printerContext, opvp_fix_t *offset)
+{
+    fputs("opvpGetLineDashOffset\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    *offset = opv_gstate.dash_offset;
+    return 0;
+}
+
+static opvp_result_t
+opvpSetLineStyle(opvp_dc_t printerContext, opvp_linestyle_t style)
+{
+    fputs("opvpSetLineStyle\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tstyle = %d\n", style);
+    opv_gstate.line_style = style;
+    return 0;
+}
+
+static opvp_result_t
+opvpGetLineStyle(opvp_dc_t printerContext, opvp_linestyle_t *style)
+{
+    fputs("opvpGetLineStyle\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    *style = opv_gstate.line_style;
+    return 0;
+}
+
+static opvp_result_t
+opvpSetLineCap(opvp_dc_t printerContext, opvp_linecap_t cap)
+{
+    fputs("opvpSetLineCap\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tcap = %d\n", cap);
+    opv_gstate.line_cap = cap;
+    return 0;
+}
+
+static opvp_result_t
+opvpGetLineCap(opvp_dc_t printerContext, opvp_linecap_t *cap)
+{
+    fputs("opvpGetLineCap\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    *cap = opv_gstate.line_cap;
+    return 0;
+}
+
+static opvp_result_t
+opvpSetLineJoin(opvp_dc_t printerContext, opvp_linejoin_t join)
+{
+    fputs("opvpSetLineJoin\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tjoin = %d\n", join);
+    opv_gstate.line_join = join;
+    return 0;
+}
+
+static opvp_result_t
+opvpGetLineJoin(opvp_dc_t printerContext, opvp_linejoin_t *join)
+{
+    fputs("opvpGetLineJoin\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    *join = opv_gstate.line_join;
+    return 0;
+}
+
+static opvp_result_t
+opvpSetMiterLimit(opvp_dc_t printerContext, opvp_fix_t miter)
+{
+    fputs("opvpSetMiterLimit\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tmiter = %d\n", miter);
+    opv_gstate.miter_limit = miter;
+    return 0;
+}
+
+static opvp_result_t
+opvpGetMiterLimit(opvp_dc_t printerContext, opvp_fix_t *miter)
+{
+    fputs("opvpGetMiterLimit\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    *miter = opv_gstate.miter_limit;
+    return 0;
+}
+
+static opvp_result_t
+opvpSetPaintMode(opvp_dc_t printerContext, opvp_paintmode_t paintmode)
+{
+    fputs("opvpSetPaintMode\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tpaintmode = %d\n", paintmode);
+    opv_gstate.paintmode = paintmode;
+    return 0;
+}
+
+static opvp_result_t
+opvpGetPaintMode(opvp_dc_t printerContext, opvp_paintmode_t *paintmode)
+{
+    fputs("opvpGetPaintMode\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    *paintmode = opv_gstate.paintmode;
+    return 0;
+}
+
+static opvp_result_t
+opvpSetStrokeColor(opvp_dc_t printerContext, const opvp_brush_t *strokecolor)
+{
+    int k;
+
+    fputs("opvpSetStrokeColor\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    if (strokecolor ==  NULL) {
+        fprintf(pFile, "\tstrokecolor is NULL\n");
+    } else {
+        fprintf(pFile, "\tstrokecolor.colorSpace = %d\n", strokecolor->colorSpace);
+
+        for (k = 0; k < 4; k++) {
+            fprintf(pFile, "\tstrokecolor.color[%d] = %d\n", k, strokecolor->color[k]);
+        }
+
+        fprintf(pFile, "\tstrokecolor.xorg = %d\n", strokecolor->xorg);
+        fprintf(pFile, "\tstrokecolor.yorg = %d\n", strokecolor->yorg);
+
+        if (strokecolor->pbrush ==  NULL) {
+            fprintf(pFile, "\tstrokecolor.pbrush is NULL\n");
+        } else {
+            fprintf(pFile, "\tstrokecolor.pbrush.type = %d\n", strokecolor->pbrush->type);
+            fprintf(pFile, "\tstrokecolor.pbrush.width = %d\n", strokecolor->pbrush->width);
+            fprintf(pFile, "\tstrokecolor.pbrush.height = %d\n", strokecolor->pbrush->height);
+            fprintf(pFile, "\tstrokecolor.pbrush.pitch = %d\n", strokecolor->pbrush->pitch);
+        }
+    }
+    return 0;
+}
+
+static opvp_result_t
+opvpSetFillColor(opvp_dc_t printerContext, const opvp_brush_t *fillcolor)
+{
+    int k;
+
+    fputs("opvpSetFillColor\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    if (fillcolor ==  NULL) {
+        fprintf(pFile, "\tfillcolor is NULL\n");
+    } else {
+        fprintf(pFile, "\tfillcolor.colorSpace = %d\n", fillcolor->colorSpace);
+
+        for (k = 0; k < 4; k++) {
+            fprintf(pFile, "\tfillcolor.color[%d] = %d\n", k, fillcolor->color[k]);
+        }
+
+        fprintf(pFile, "\tfillcolor.xorg = %d\n", fillcolor->xorg);
+        fprintf(pFile, "\tfillcolor.yorg = %d\n", fillcolor->yorg);
+
+        if (fillcolor->pbrush ==  NULL) {
+            fprintf(pFile, "\tfillcolor.pbrush is NULL\n");
+        } else {
+            fprintf(pFile, "\tfillcolor.pbrush.type = %d\n", fillcolor->pbrush->type);
+            fprintf(pFile, "\tfillcolor.pbrush.width = %d\n", fillcolor->pbrush->width);
+            fprintf(pFile, "\tfillcolor.pbrush.height = %d\n", fillcolor->pbrush->height);
+            fprintf(pFile, "\tfillcolor.pbrush.pitch = %d\n", fillcolor->pbrush->pitch);
+        }
+    }
+    return 0;
+}
+
+static opvp_result_t
+opvpSetBgColor(opvp_dc_t printerContext, const opvp_brush_t *bgcolor)
+{
+    int k;
+
+    fputs("opvpSetBgColor\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    if (bgcolor ==  NULL) {
+        fprintf(pFile, "\tfillcbgcolorolor is NULL\n");
+    } else {
+        fprintf(pFile, "\tbgcolor.colorSpace = %d\n", bgcolor->colorSpace);
+
+        for (k = 0; k < 4; k++) {
+            fprintf(pFile, "\tbgcolor.color[%d] = %d\n", k, bgcolor->color[k]);
+        }
+
+        fprintf(pFile, "\tbgcolor.xorg = %d\n", bgcolor->xorg);
+        fprintf(pFile, "\tbgcolor.yorg = %d\n", bgcolor->yorg);
+
+        if (bgcolor->pbrush ==  NULL) {
+            fprintf(pFile, "\tbgcolor.pbrush is NULL\n");
+        } else {
+            fprintf(pFile, "\tbgcolor.pbrush.type = %d\n", bgcolor->pbrush->type);
+            fprintf(pFile, "\tbgcolor.pbrush.width = %d\n", bgcolor->pbrush->width);
+            fprintf(pFile, "\tbgcolor.pbrush.height = %d\n", bgcolor->pbrush->height);
+            fprintf(pFile, "\tbgcolor.pbrush.pitch = %d\n", bgcolor->pbrush->pitch);
+        }
+    }
+    return 0;
+}
+
+static opvp_result_t
+opvpNewPath(opvp_dc_t printerContext)
+{
+    fputs("opvpNewPath\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpEndPath(opvp_dc_t printerContext)
+{
+    fputs("opvpEndPath\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpStrokePath(opvp_dc_t printerContext)
+{
+    fputs("opvpStrokePath\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpFillPath(opvp_dc_t printerContext)
+{
+    fputs("opvpFillPath\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpStrokeFillPath(opvp_dc_t printerContext)
+{
+    fputs("opvpStrokeFillPath\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpSetClipPath(opvp_dc_t printerContext, opvp_cliprule_t cliprule)
+{
+    fputs("opvpSetClipPath\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tcliprule = %d\n", cliprule);
+    return 0;
+}
+
+static opvp_result_t
+opvpResetClipPath(opvp_dc_t printerContext)
+{
+    fputs("opvpResetClipPath\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpSetCurrentPoint(opvp_dc_t printerContext, opvp_fix_t pointx, opvp_fix_t pointy)
+{
+    fputs("opvpSetCurrentPoint\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tpointx = %d\n", pointx);
+    fprintf(pFile, "\tpointy = %d\n", pointy);
+    return 0;
+}
+
+static opvp_result_t
+opvpLinePath(opvp_dc_t printerContext, opvp_pathmode_t pathmode, opvp_int_t npoints, const opvp_point_t *points)
+{
+    int k;
+
+    fputs("opvpLinePath\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tpathmode = %d\n", pathmode);
+    fprintf(pFile, "\tnpoints = %d\n", npoints);
+
+    for (k = 0; k < npoints; k++) {
+        fprintf(pFile, "\tpoints[%d].x = %d\n", k, points[k].x);
+        fprintf(pFile, "\tpoints[%d].y = %d\n", k, points[k].y);
+    }
+    return 0;
+}
+
+/* Not used */
+static opvp_result_t
+opvpPolygonPath(opvp_dc_t printerContext, opvp_int_t n, const opvp_int_t *m, const opvp_point_t *p)
+{
+    fputs("opvpPolygonPath\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpRectanglePath(opvp_dc_t printerContext, opvp_int_t n, const opvp_rectangle_t *rects)
+{
+    int k;
+
+    fputs("opvpRectanglePath\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tn = %d\n", n);
+
+    for (k = 0; k < n; k++) {
+        fprintf(pFile, "\trects[%d].p0.x = %d\n", k, rects[k].p0.x);
+        fprintf(pFile, "\trects[%d].p0.y = %d\n", k, rects[k].p0.y);
+        fprintf(pFile, "\trects[%d].p1.x = %d\n", k, rects[k].p1.x);
+        fprintf(pFile, "\trects[%d].p1.y = %d\n", k, rects[k].p1.y);
+    }
+    return 0;
+}
+
+static opvp_result_t
+opvpRoundRectanglePath(opvp_dc_t printerContext, opvp_int_t n, const opvp_roundrectangle_t *rects)
+{
+    int k;
+
+    fputs("opvpRoundRectanglePath\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tn = %d\n", n);
+
+    for (k = 0; k < n; k++) {
+        fprintf(pFile, "\trects[%d].p0.x = %d\n", k, rects[k].p0.x);
+        fprintf(pFile, "\trects[%d].p0.y = %d\n", k, rects[k].p0.y);
+        fprintf(pFile, "\trects[%d].p1.x = %d\n", k, rects[k].p1.x);
+        fprintf(pFile, "\trects[%d].p1.y = %d\n", k, rects[k].p1.y);
+        fprintf(pFile, "\trects[%d].xellipse = %d\n", k, rects[k].xellipse);
+        fprintf(pFile, "\trects[%d].yellipse = %d\n", k, rects[k].yellipse);
+    }
+    return 0;
+}
+
+static opvp_result_t
+opvpBezierPath(opvp_dc_t printerContext, opvp_int_t n, const opvp_point_t *points)
+{
+    int k;
+
+    fputs("opvpBezierPath\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tn = %d\n", n);
+
+    for (k = 0; k < n; k++) {
+        fprintf(pFile, "\tpoints[%d].x = %d\n", k, points[k].x);
+        fprintf(pFile, "\trects[%d].y = %d\n", k, points[k].y);
+
+    }
+    return 0;
+}
+
+/* Not used */
+static opvp_result_t
+opvpArcPath(opvp_dc_t printerContext, opvp_arcmode_t mode, opvp_arcdir_t dir,
+    opvp_fix_t a, opvp_fix_t b, opvp_fix_t c, opvp_fix_t d, opvp_fix_t e, opvp_fix_t f,
+    opvp_fix_t g, opvp_fix_t h)
+{
+    fputs("opvpArcPath\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpDrawImage(opvp_dc_t printerContext, opvp_int_t sw, opvp_int_t sh, opvp_int_t raster,
+    opvp_imageformat_t format, opvp_int_t dw, opvp_int_t dh, const void *data)
+{
+    int k;
+    unsigned char *data_char = (unsigned char*) data;
+
+    fputs("opvpDrawImage\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tsw = %d\n", sw);
+    fprintf(pFile, "\tsh = %d\n", sh);
+    fprintf(pFile, "\traster = %d\n", raster);
+    fprintf(pFile, "\tformat = %d\n", format);
+    fprintf(pFile, "\tdw = %d\n", dw);
+    fprintf(pFile, "\tdh = %d\n", dh);
+
+    /* No idea how big data is here... Try sw, as byte? */
+    for (k = 0; k < sw; k++) {
+        fprintf(pFile, "\tdata[%d] = %d\n", k, data_char[k]);
+    }
+
+    return 0;
+}
+
+static opvp_result_t
+opvpStartDrawImage(opvp_dc_t printerContext, opvp_int_t sw, opvp_int_t sh, opvp_int_t raster,
+    opvp_imageformat_t format, opvp_int_t dw, opvp_int_t dh)
+{
+    fputs("opvpStartDrawImage\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tsw = %d\n", sw);
+    fprintf(pFile, "\tsh = %d\n", sh);
+    fprintf(pFile, "\traster = %d\n", raster);
+    fprintf(pFile, "\tformat = %d\n", format);
+    fprintf(pFile, "\tdw = %d\n", dw);
+    fprintf(pFile, "\tdh = %d\n", dh);
+    return 0;
+}
+
+static opvp_result_t
+opvpTransferDrawImage(opvp_dc_t printerContext, opvp_int_t count, const void *data)
+{
+    int k;
+    unsigned char *data_char = (unsigned char*) data;
+
+    fputs("opvpTransferDrawImage\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\tcount = %d\n", count);
+
+    for (k = 0; k < count; k++) {
+        fprintf(pFile, "\tdata[%d] = %d\n", k, data_char[k]);
+    }
+    return 0;
+}
+
+static opvp_result_t
+opvpEndDrawImage(opvp_dc_t printerContext)
+{
+    fputs("opvpEndDrawImage\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+/* not used */
+static opvp_result_t
+opvpStartScanline(opvp_dc_t printerContext, opvp_int_t a)
+{
+    fputs("opvpStartScanline\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+/* not used */
+static opvp_result_t
+opvpScanline(opvp_dc_t printerContext, opvp_int_t a, const opvp_int_t *b)
+{
+    fputs("opvpScanline\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+/* not used */
+static opvp_result_t
+opvpEndScanline(opvp_dc_t printerContext)
+{
+    fputs("opvpEndScanline\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpStartRaster(opvp_dc_t printerContext, opvp_int_t width)
+{
+    fputs("opvpStartRaster\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\twidth = %d\n", width);
+    return 0;
+}
+
+static opvp_result_t
+opvpTransferRasterData(opvp_dc_t printerContext, opvp_int_t raster_size, const opvp_byte_t *data)
+{
+    int k;
+
+    fputs("opvpTransferRasterData\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\traster_size = %d\n", raster_size);
+
+    for (k = 0; k < raster_size; k++) {
+        fprintf(pFile, "\tdata[%d] = %d\n", k, data[k]);
+    }
+    return 0;
+}
+
+static opvp_result_t
+opvpSkipRaster(opvp_dc_t printerContext, opvp_int_t a)
+{
+    fputs("opvpSkipRaster\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    fprintf(pFile, "\ta = %d\n", a);
+    return 0;
+}
+
+static opvp_result_t
+opvpEndRaster(opvp_dc_t printerContext)
+{
+    fputs("opvpEndRaster\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpStartStream(opvp_dc_t printerContext)
+{
+    fputs("opvpStartStream\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+/* Not used */
+static opvp_result_t
+opvpTransferStreamData(opvp_dc_t printerContext, opvp_int_t a, const void *b)
+{
+    fputs("opvpTransferStreamData\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+static opvp_result_t
+opvpEndStream(opvp_dc_t printerContext)
+{
+    fputs("opvpEndStream\n", pFile);
+    fprintf(pFile, "\tContext = %d\n", printerContext);
+    return 0;
+}
+
+opvp_int_t opvpErrorNo;
+
+opvp_dc_t
+opvpOpenPrinter(opvp_int_t outputFD, const opvp_char_t *printerModel, const opvp_int_t apiVersion[2],
+        opvp_api_procs_t **apiProcs)
+{
+    static opvp_api_procs_t procs;
+    opvp_fix_t fixed_value;
+
+    procs.opvpEndStream = opvpEndStream;
+    procs.opvpClosePrinter = opvpClosePrinter;
+    procs.opvpStartJob = opvpStartJob;
+    procs.opvpEndJob = opvpEndJob;
+    procs.opvpAbortJob = opvpAbortJob;
+    procs.opvpStartDoc = opvpStartDoc;
+    procs.opvpEndDoc = opvpEndDoc;
+    procs.opvpStartPage = opvpStartPage;
+    procs.opvpEndPage = opvpEndPage;
+    procs.opvpQueryDeviceCapability = opvpQueryDeviceCapability;
+    procs.opvpQueryDeviceInfo = opvpQueryDeviceInfo;
+    procs.opvpResetCTM = opvpResetCTM;
+    procs.opvpSetCTM = opvpSetCTM;
+    procs.opvpGetCTM = opvpGetCTM;
+    procs.opvpInitGS = opvpInitGS;
+    procs.opvpSaveGS = opvpSaveGS;
+    procs.opvpRestoreGS = opvpRestoreGS;
+    procs.opvpQueryColorSpace = opvpQueryColorSpace;
+    procs.opvpSetColorSpace = opvpSetColorSpace;
+    procs.opvpGetColorSpace = opvpGetColorSpace;
+    procs.opvpSetFillMode = opvpSetFillMode;
+    procs.opvpGetFillMode = opvpGetFillMode;
+    procs.opvpSetAlphaConstant = opvpSetAlphaConstant;
+    procs.opvpGetAlphaConstant = opvpGetAlphaConstant;
+    procs.opvpSetLineWidth = opvpSetLineWidth;
+    procs.opvpGetLineWidth = opvpGetLineWidth;
+    procs.opvpSetLineDash = opvpSetLineDash;
+    procs.opvpSetLineDash = opvpSetLineDash;
+    procs.opvpGetLineDash = opvpGetLineDash;
+    procs.opvpSetLineDashOffset = opvpSetLineDashOffset;
+    procs.opvpGetLineDashOffset = opvpGetLineDashOffset;
+    procs.opvpSetLineStyle = opvpSetLineStyle;
+    procs.opvpGetLineStyle = opvpGetLineStyle;
+    procs.opvpSetLineCap = opvpSetLineCap;
+    procs.opvpGetLineCap = opvpGetLineCap;
+    procs.opvpSetLineJoin = opvpSetLineJoin;
+    procs.opvpGetLineJoin = opvpGetLineJoin;
+    procs.opvpSetMiterLimit = opvpSetMiterLimit;
+    procs.opvpGetMiterLimit = opvpGetMiterLimit;
+    procs.opvpSetPaintMode = opvpSetPaintMode;
+    procs.opvpGetPaintMode = opvpGetPaintMode;
+    procs.opvpSetStrokeColor = opvpSetStrokeColor;
+    procs.opvpSetFillColor = opvpSetFillColor;
+    procs.opvpSetBgColor = opvpSetBgColor;
+    procs.opvpNewPath = opvpNewPath;
+    procs.opvpEndPath = opvpEndPath;
+    procs.opvpStrokePath = opvpStrokePath;
+    procs.opvpFillPath = opvpFillPath;
+    procs.opvpStrokeFillPath = opvpStrokeFillPath;
+    procs.opvpSetClipPath = opvpSetClipPath;
+    procs.opvpResetClipPath = opvpResetClipPath;
+    procs.opvpSetCurrentPoint = opvpSetCurrentPoint;
+    procs.opvpLinePath = opvpLinePath;
+    procs.opvpPolygonPath = opvpPolygonPath;
+    procs.opvpRectanglePath = opvpRectanglePath;
+    procs.opvpRoundRectanglePath = opvpRoundRectanglePath;
+    procs.opvpBezierPath = opvpBezierPath;
+    procs.opvpArcPath = opvpArcPath;
+    procs.opvpDrawImage = opvpDrawImage;
+    procs.opvpStartDrawImage = opvpStartDrawImage;
+    procs.opvpTransferDrawImage = opvpTransferDrawImage;
+    procs.opvpEndDrawImage = opvpEndDrawImage;
+    procs.opvpStartScanline = opvpStartScanline;
+    procs.opvpScanline = opvpScanline;
+    procs.opvpEndScanline = opvpEndScanline;
+    procs.opvpStartRaster = opvpStartRaster;
+    procs.opvpTransferRasterData = opvpTransferRasterData;
+    procs.opvpSkipRaster = opvpSkipRaster;
+    procs.opvpEndRaster = opvpEndRaster;
+    procs.opvpStartStream = opvpStartStream;
+    procs.opvpTransferStreamData = opvpTransferStreamData;
+    procs.opvpEndStream = opvpEndStream;
+
+    *apiProcs = &procs;
+
+    OPVP_F2FIX(1.0, fixed_value);
+
+    opv_gstate.ctm.a = 1.0;
+    opv_gstate.ctm.b = 0.0;
+    opv_gstate.ctm.c = 0.0;
+    opv_gstate.ctm.d = 1.0;
+    opv_gstate.ctm.e = 0.0;
+    opv_gstate.ctm.f = 0.0;
+    opv_gstate.colorspace = OPVP_CSPACE_DEVICERGB;
+    opv_gstate.fill_mode = OPVP_FILLMODE_EVENODD;
+    opv_gstate.alpha = 1.0;
+    opv_gstate.line_width = fixed_value;
+    opv_gstate.line_dash_n = 0;
+    opv_gstate.line_dash_array = NULL;
+    opv_gstate.dash_offset = fixed_value;
+    opv_gstate.line_style = OPVP_LINESTYLE_SOLID;
+    opv_gstate.line_cap = OPVP_LINECAP_BUTT;
+    opv_gstate.line_join = OPVP_LINEJOIN_MITER;
+    opv_gstate.miter_limit = fixed_value;
+    opv_gstate.paintmode = OPVP_PAINTMODE_OPAQUE;
+    opv_gstate.colorspace = OPVP_CSPACE_DEVICERGB;
+
+    pFile = fopen("opvp_command_dump.txt","w");
+    if (pFile != NULL)
+    {
+        fputs("opvpOpenPrinter\n", pFile);
+        return 0;
+    }
+    return -1;
+}
diff -pruN 9.55.0~dfsg-3/contrib/opvp/README.txt 9.56.1~dfsg-1/contrib/opvp/README.txt
--- 9.55.0~dfsg-3/contrib/opvp/README.txt	1970-01-01 00:00:00.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/opvp/README.txt	2022-04-04 13:46:22.000000000 +0000
@@ -0,0 +1,18 @@
+In an effort to remove globals in devices
+that ship with Ghostscript, the opvp device was updated
+to place it's global values into the device structure.
+
+As part of that commit, a harness was added to enable
+testing of the opvp device client API to test for any
+issues. To build the harness use  ./build_opv_harness.sh
+This will create a debug version of the shared object
+libopv.so. The command line
+gs -sDEVICE=opvp -sDriver=./contrib/opvp/libopv.so -o output.txt -f ./examples/tiger.eps
+can then be used.
+
+This command should create a file called
+opvp_command_dump.txt that contains the calls and
+the parameters made to the client API. Note that the
+harness itself has to rely upon globals. To do otherwise
+would require a to change the client API, which we
+do not own.
\ No newline at end of file
diff -pruN 9.55.0~dfsg-3/contrib/pcl3/eprn/eprnparm.c 9.56.1~dfsg-1/contrib/pcl3/eprn/eprnparm.c
--- 9.55.0~dfsg-3/contrib/pcl3/eprn/eprnparm.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/pcl3/eprn/eprnparm.c	2022-04-04 13:46:22.000000000 +0000
@@ -390,7 +390,7 @@ static char *next_word(char *s)
 
 #define cleanup()       (free(list), gp_fclose(f))
 
-static int eprn_read_media_data(eprn_Eprn *eprn, gs_memory_t *memory)
+static int eprn_read_media_data(mediasize_table *tables, eprn_Eprn *eprn, gs_memory_t *memory)
 {
   char buffer[BUFFER_SIZE];
   const char
@@ -502,7 +502,7 @@ static int eprn_read_media_data(eprn_Epr
       t++;
     }
     {
-      ms_MediaCode code = ms_find_code_from_name(s, eprn->flag_desc);
+      ms_MediaCode code = ms_find_code_from_name(tables, s, eprn->flag_desc);
       if (code == ms_none) {
         eprintf5("%s" ERRPREF "Unknown media name (%s) in "
             "media configuration file\n%s  %s, line %d.\n",
@@ -664,7 +664,7 @@ int eprn_set_media_data(eprn_Device *dev
     else {
       strncpy(eprn->media_file, media_file, length);
       eprn->media_file[length] = '\0';
-      if ((rc = eprn_read_media_data(eprn, dev->memory->non_gc_memory)) != 0) {
+      if ((rc = eprn_read_media_data(&dev->eprn.table, eprn, dev->memory->non_gc_memory)) != 0) {
         gs_free(dev->memory->non_gc_memory, eprn->media_file, length + 1, sizeof(char),
           "eprn_set_media_data");
         eprn->media_file = NULL;
diff -pruN 9.55.0~dfsg-3/contrib/pcl3/eprn/gdeveprn.h 9.56.1~dfsg-1/contrib/pcl3/eprn/gdeveprn.h
--- 9.55.0~dfsg-3/contrib/pcl3/eprn/gdeveprn.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/pcl3/eprn/gdeveprn.h	2022-04-04 13:46:22.000000000 +0000
@@ -528,6 +528,7 @@ typedef struct s_eprn_Device {
     present in 'next_scan_line' with its device coordinate being "next_y - 1",
     unless 'next_y' is zero in which case we have finished. */
   gs_gstate * pgs;
+  mediasize_table table;
 } eprn_Eprn;
 
 /* Macro for device structure type definitions. Note that in contrast to
@@ -625,7 +626,8 @@ typedef struct {
     0,		/* octets_per_line */	\
     0,		/* output_planes */	\
     0,		/* next_y */		\
-    NULL        /* pgs    */            \
+    NULL,       /* pgs    */            \
+    { 0 }       /* table */             \
   }
 
 /*  For the calling conventions of the following functions consult the comments
diff -pruN 9.55.0~dfsg-3/contrib/pcl3/eprn/mediasize.c 9.56.1~dfsg-1/contrib/pcl3/eprn/mediasize.c
--- 9.55.0~dfsg-3/contrib/pcl3/eprn/mediasize.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/pcl3/eprn/mediasize.c	2022-04-04 13:46:22.000000000 +0000
@@ -149,7 +149,9 @@ static const ms_SizeDescription list[] =
 
 /*****************************************************************************/
 
-#if !defined(NDEBUG) && !defined(GS_THREADSAFE)
+#undef CHECK_CONSTRAINTS
+
+#ifdef CHECK_CONSTRAINTS
 static char checked = 0;
 
 /* Function to check constraints on table entries */
@@ -183,9 +185,9 @@ static void check(void)
 
 const ms_SizeDescription *ms_find_size_from_code(ms_MediaCode code)
 {
-#if !defined(NDEBUG) && !defined(GS_THREADSAFE)
+#ifdef CHECK_CONSTRAINTS
   if (!checked) check();
-#endif	/* !NDEBUG && !GS_THREADSAFE */
+#endif
   code = ms_without_flags(code);
   if (code < 1 || array_size(list) <= code) return NULL;
 
@@ -243,15 +245,21 @@ static const ms_Flag substrings[] = {
   {0, NULL}
 };
 
+/* If you get an error when compiling the following, then MAX_MEDIASIZES
+ * (defined in pcltables.h) must be increased. */
+typedef struct
+{
+        char compile_time_assert[array_size(list) <= MAX_MEDIASIZES ? 1 : -1];
+} compile_time_assert_for_list_length;
+
 /*****************************************************************************/
 
-ms_MediaCode ms_find_code_from_name(const char *name,
-  const ms_Flag *user_flag_list)
+ms_MediaCode ms_find_code_from_name(mediasize_table *tables,
+                                    const char *name,
+                                    const ms_Flag *user_flag_list)
 {
   const char *end;
   char stripped_name[LONGER_THAN_NAMES];
-  static const ms_SizeDescription *sorted_list[array_size(list) - 1];
-  static unsigned int entries = 0;
   ms_SizeDescription
     keydata,
     *key = &keydata;
@@ -261,13 +269,14 @@ ms_MediaCode ms_find_code_from_name(cons
 
   /* On the first use of this function, compile a table of pointers into the
      list which is sorted by the names of the sizes. */
-  if (entries == 0) {
-    while (entries < array_size(sorted_list)) {
-      sorted_list[entries] = list + entries + 1;	/* ignore 'ms_none' */
+  if (tables->mediasize_list_inited == 0) {
+    int entries = 1; /* ignore 'ms_none' */
+    while (entries < array_size(list)) {
+      tables->mediasize_list[entries] = list + entries;
       entries++;
     }
-    qsort(sorted_list, array_size(sorted_list), sizeof(ms_SizeDescription *),
-      &cmp_by_name);
+    qsort(tables->mediasize_list, array_size(list) - 1, sizeof(ms_SizeDescription *), &cmp_by_name);
+    tables->mediasize_list_inited = 1;
   }
 
   /* Prevent idiots (like myself) from crashing the routine */
@@ -325,7 +334,7 @@ ms_MediaCode ms_find_code_from_name(cons
   keydata.name = stripped_name;
 
   /* Search */
-  found = (const ms_SizeDescription **)bsearch(&key, sorted_list, entries,
+  found = (const ms_SizeDescription **)bsearch(&key, tables->mediasize_list, array_size(list) - 1,
     sizeof(ms_SizeDescription *), &cmp_by_name);
 
   return found == NULL? ms_none: ((*found)->size | flags);
diff -pruN 9.55.0~dfsg-3/contrib/pcl3/eprn/mediasize.h 9.56.1~dfsg-1/contrib/pcl3/eprn/mediasize.h
--- 9.55.0~dfsg-3/contrib/pcl3/eprn/mediasize.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/pcl3/eprn/mediasize.h	2022-04-04 13:46:22.000000000 +0000
@@ -150,6 +150,13 @@ typedef struct {
     */
 } ms_SizeDescription;
 
+#define MAX_MEDIASIZES 128
+
+typedef struct {
+	int mediasize_list_inited;
+	const ms_SizeDescription *mediasize_list[MAX_MEDIASIZES];
+} mediasize_table;
+
 /******************************************************************************
 
   Media codes
@@ -254,8 +261,9 @@ extern const ms_SizeDescription *ms_find
       and appearance as a qualifier or consider the order of appearance.
     - There is no support for serialization qualifiers.
 */
-extern ms_MediaCode ms_find_code_from_name(const char *name,
-  const ms_Flag *user_flag_list);
+extern ms_MediaCode ms_find_code_from_name(mediasize_table *tables,
+                                           const char *name,
+                                           const ms_Flag *user_flag_list);
 
 /*===========================================================================*/
 
diff -pruN 9.55.0~dfsg-3/contrib/pcl3/src/gdevpcl3.c 9.56.1~dfsg-1/contrib/pcl3/src/gdevpcl3.c
--- 9.55.0~dfsg-3/contrib/pcl3/src/gdevpcl3.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/pcl3/src/gdevpcl3.c	2022-04-04 13:46:22.000000000 +0000
@@ -82,6 +82,7 @@ typedef struct {
     configured,         /* Has the output file been configured? */
     configure_every_page;  /* Repeat the configuration for every page? */
   pcl_FileData file_data;
+  pcl3_sizetable table;
 } pcl3_Device;
 
 /*****************************************************************************/
@@ -305,9 +306,9 @@ static void get_string_for_int(int in_va
     out_value->persistent = true;
   }
   else {
-    static char buffer[22];     /* Must be sufficient for an 'int' */
+    char buffer[22];     /* Must be sufficient for an 'int' */
 
-    gs_sprintf(buffer, "%d", in_value);
+    gs_snprintf(buffer, sizeof(buffer), "%d", in_value);
     assert(strlen(buffer) < sizeof(buffer));
     out_value->data = (const byte *)buffer;
     out_value->size = strlen(buffer);
@@ -1326,7 +1327,7 @@ static int pcl3_open_device(gx_device *d
     unsigned int j;
 
     /* Media handling */
-    data->size = pcl3_page_size(dev->eprn.code);
+    data->size = pcl3_page_size(&dev->table, dev->eprn.code);
     if (data->size == pcl_ps_default) {
       /*  This is due to a media description using a media size code for which
           there is no PCL Page Size code. This is either an error in a builtin
diff -pruN 9.55.0~dfsg-3/contrib/pcl3/src/pcl3opts.c 9.56.1~dfsg-1/contrib/pcl3/src/pcl3opts.c
--- 9.55.0~dfsg-3/contrib/pcl3/src/pcl3opts.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/pcl3/src/pcl3opts.c	2022-04-04 13:46:22.000000000 +0000
@@ -144,6 +144,7 @@ static void print_result(CollectedInfo *
   int j;
   int ll;	/* line length */
   unsigned int min_hres, min_vres, black_levels, non_black_levels, start;
+  pcl3_sizetable sizetable = { 0 };
 
   if (ip->number_of_outputs > 0)
     imessage(ip->out, 10,
@@ -373,7 +374,7 @@ static void print_result(CollectedInfo *
   }
 
   if (ip->fdata.size != 0) {
-    ms_MediaCode media_code = pcl3_media_code(ip->fdata.size);
+    ms_MediaCode media_code = pcl3_media_code(&sizetable, ip->fdata.size);
     const ms_SizeDescription *size = ms_find_size_from_code(media_code);
     if (size == NULL)
       imessage(ip->out, 14,
diff -pruN 9.55.0~dfsg-3/contrib/pcl3/src/pclcap.c 9.56.1~dfsg-1/contrib/pcl3/src/pclcap.c
--- 9.55.0~dfsg-3/contrib/pcl3/src/pclcap.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/pcl3/src/pclcap.c	2022-04-04 13:46:22.000000000 +0000
@@ -783,9 +783,9 @@ const pcl_PrinterDescription pcl3_printe
 };
 
 /*****************************************************************************/
+#undef CHECK_CONSTRAINTS
 
-#if !defined(NDEBUG) && !defined(GS_THREADSAFE)
-
+#ifdef CHECK_CONSTRAINTS
 static int checked = 0;
 
 static void check(void)
@@ -800,8 +800,7 @@ static void check(void)
 
   return;
 }
-
-#endif	/* !NDEBUG && !GS_THREADSAFE */
+#endif
 
 /******************************************************************************
 
@@ -816,9 +815,9 @@ static void check(void)
 
 void pcl3_fill_defaults(pcl_Printer printer, pcl_FileData *data)
 {
-#if !defined(NDEBUG) && !defined(GS_THREADSAFE)
+#ifdef CHECK_CONSTRAINTS
   if (!checked) check();
-#endif	/* !NDEBUG */
+#endif
 
   /* Set everything to zero */
   memset(data, 0, sizeof(pcl_FileData));
diff -pruN 9.55.0~dfsg-3/contrib/pcl3/src/pclsize.c 9.56.1~dfsg-1/contrib/pcl3/src/pclsize.c
--- 9.55.0~dfsg-3/contrib/pcl3/src/pclsize.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/pcl3/src/pclsize.c	2022-04-04 13:46:22.000000000 +0000
@@ -36,16 +36,8 @@
     This list is used to find a PCL Page Size code for a particular media code.
     It will be sorted by media code before the first use.
 
-    This structure is based on the assumption that one needs only a single
-    Page Size code for each supported media code. See the discussion in
-    pclgen.h.
 */
 
-typedef struct {
-  ms_MediaCode mc;
-  pcl_PageSize ps;
-} CodeEntry;
-
 #define me(keyword)	{ms_##keyword, pcl_ps_##keyword}
 
 static CodeEntry code_map[] = {
@@ -84,6 +76,13 @@ static CodeEntry code_map[] = {
 
 #undef me
 
+/* If the following gives a compile error, then MAX_CODEENTRIES in pclsize.h
+ * must be increased. */
+typedef struct
+{
+        char compile_time_assert[array_size(code_map) <= MAX_CODEENTRIES ? 1 : -1];
+} compile_time_assert_for_codeentry;
+
 /******************************************************************************
 
   Function: cmp_by_size
@@ -108,20 +107,20 @@ static int cmp_by_size(const void *a, co
 
 ******************************************************************************/
 
-pcl_PageSize pcl3_page_size(ms_MediaCode code)
+pcl_PageSize pcl3_page_size(pcl3_sizetable *table, ms_MediaCode code)
 {
-  static pcl_bool initialized = FALSE;
   CodeEntry key;
   const CodeEntry *result;
 
   /* Sort the table if necessary */
-  if (!initialized) {
-    qsort(code_map, array_size(code_map), sizeof(CodeEntry), cmp_by_size);
-    initialized = TRUE;
+  if (!table->inited_code_map) {
+    memcpy(table->code_map, code_map, array_size(code_map) * sizeof(CodeEntry));
+    qsort(table->code_map, array_size(code_map), sizeof(CodeEntry), cmp_by_size);
+    table->inited_code_map = TRUE;
   }
 
   key.mc = ms_without_flags(code) |( code & PCL_CARD_FLAG);
-  result = (const CodeEntry *)bsearch(&key, code_map, array_size(code_map),
+  result = (const CodeEntry *)bsearch(&key, table->code_map, array_size(code_map),
     sizeof(CodeEntry), cmp_by_size);
 
   return result == NULL? pcl_ps_default: result->ps;
@@ -149,23 +148,21 @@ static int cmp_by_code(const void *a, co
 
 ******************************************************************************/
 
-ms_MediaCode pcl3_media_code(pcl_PageSize code)
+ms_MediaCode pcl3_media_code(pcl3_sizetable *table, pcl_PageSize code)
 {
-  static CodeEntry inverse_map[array_size(code_map)];
-  static pcl_bool initialized = FALSE;
   CodeEntry key;
   const CodeEntry *result;
 
   /* Construct the table if necessary */
-  if (!initialized) {
-    memcpy(&inverse_map, &code_map, sizeof(inverse_map));
-    qsort(inverse_map, array_size(inverse_map), sizeof(CodeEntry), cmp_by_code);
-    initialized = TRUE;
+  if (!table->inited_inverse_map) {
+    memcpy(&table->inverse_map, &code_map, sizeof(code_map));
+    qsort(table->inverse_map, array_size(code_map), sizeof(CodeEntry), cmp_by_code);
+    table->inited_inverse_map = TRUE;
   }
 
   key.ps = code;
-  result = (const CodeEntry *)bsearch(&key, inverse_map,
-    array_size(inverse_map), sizeof(CodeEntry), cmp_by_code);
+  result = (const CodeEntry *)bsearch(&key, table->inverse_map,
+    array_size(code_map), sizeof(CodeEntry), cmp_by_code);
   if (result == NULL) {
     key.ps = -code;
      /* Actually, this is a generalization on my part: I am assuming that any
@@ -173,8 +170,8 @@ ms_MediaCode pcl3_media_code(pcl_PageSiz
         same media extension irrespective of sheet orientation in raster space.
         I have found negative Page Size codes in HP documentation only for
         Env10 and EnvDL. */
-    result = (const CodeEntry *)bsearch(&key, inverse_map,
-      array_size(inverse_map), sizeof(CodeEntry), cmp_by_code);
+    result = (const CodeEntry *)bsearch(&key, table->inverse_map,
+      array_size(code_map), sizeof(CodeEntry), cmp_by_code);
   }
 
   return result == NULL? ms_none: result->mc;
@@ -189,7 +186,7 @@ ms_MediaCode pcl3_media_code(pcl_PageSiz
 
 ******************************************************************************/
 
-const ms_SizeDescription *pcl3_size_description(pcl_PageSize code)
+const ms_SizeDescription *pcl3_size_description(pcl3_sizetable *table, pcl_PageSize code)
 {
-  return ms_find_size_from_code(pcl3_media_code(code));
+  return ms_find_size_from_code(pcl3_media_code(table, code));
 }
diff -pruN 9.55.0~dfsg-3/contrib/pcl3/src/pclsize.h 9.56.1~dfsg-1/contrib/pcl3/src/pclsize.h
--- 9.55.0~dfsg-3/contrib/pcl3/src/pclsize.h	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/contrib/pcl3/src/pclsize.h	2022-04-04 13:46:22.000000000 +0000
@@ -24,9 +24,28 @@
 #define PCL_CARD_FLAG	MS_USER_FLAG_1
 #define PCL_CARD_STRING	"Card"
 
-extern pcl_PageSize pcl3_page_size(ms_MediaCode code);
-extern ms_MediaCode pcl3_media_code(pcl_PageSize code);
-extern const ms_SizeDescription *pcl3_size_description(pcl_PageSize size);
+/*
+    This structure is based on the assumption that one needs only a single
+    Page Size code for each supported media code. See the discussion in
+    pclgen.h.
+*/
+typedef struct {
+  ms_MediaCode mc;
+  pcl_PageSize ps;
+} CodeEntry;
+
+#define MAX_CODEENTRIES 64
+
+typedef struct {
+    int inited_code_map;
+    CodeEntry code_map[MAX_CODEENTRIES];
+    int inited_inverse_map;
+    CodeEntry inverse_map[MAX_CODEENTRIES];
+} pcl3_sizetable;
+
+extern pcl_PageSize pcl3_page_size(pcl3_sizetable *table, ms_MediaCode code);
+extern ms_MediaCode pcl3_media_code(pcl3_sizetable *table, pcl_PageSize code);
+extern const ms_SizeDescription *pcl3_size_description(pcl3_sizetable *table, pcl_PageSize size);
 
 /*****************************************************************************/
 
diff -pruN 9.55.0~dfsg-3/cups/gdevcups.c 9.56.1~dfsg-1/cups/gdevcups.c
--- 9.55.0~dfsg-3/cups/gdevcups.c	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/cups/gdevcups.c	2022-04-04 13:46:22.000000000 +0000
@@ -76,6 +76,12 @@
 
 #include <stdlib.h>
 #include <ctype.h>
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
 #include <cups/raster.h>
 #include <cups/ppd.h>
 #include <math.h>
@@ -461,6 +467,12 @@ cups_initialize_device_procs(gx_device *
 gx_device_cups	gs_cups_device = { gs_xxx_device("cups", "") };
 gx_device_cups	gs_pwgraster_device = { gs_xxx_device("pwgraster",
 						      "PwgRaster") };
+#if defined(CUPS_RASTER_HAVE_APPLERASTER)
+gx_device_cups	gs_appleraster_device = { gs_xxx_device("appleraster",
+							"PwgRaster") };
+gx_device_cups	gs_urf_device = { gs_xxx_device("urf",
+						"PwgRaster") };
+#endif
 
 /*
  * Local functions...
@@ -2947,7 +2959,14 @@ cups_print_pages(gx_device_printer *pdev
 #if defined(CUPS_RASTER_HAVE_PWGRASTER)
                                        (strcasecmp(cups->header.MediaClass,
 						   "PwgRaster") == 0 ?
+#if defined(CUPS_RASTER_HAVE_APPLERASTER)
+					(!strcmp(cups->dname, "appleraster") ||
+					 !strcmp(cups->dname, "urf") ?
+					 CUPS_RASTER_WRITE_APPLE :
+					 CUPS_RASTER_WRITE_PWG) :
+#else
 					CUPS_RASTER_WRITE_PWG :
+#endif
 					(cups->cupsRasterVersion == 3 ?
 					 CUPS_RASTER_WRITE :
 					 CUPS_RASTER_WRITE_COMPRESSED)))) ==
@@ -6080,5 +6099,8 @@ cups_spec_op(gx_device *dev_, int op, vo
     return gx_default_dev_spec_op(dev_, op, data, datasize);
 }
 
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
 /*
  */
diff -pruN 9.55.0~dfsg-3/debian/changelog 9.56.1~dfsg-1/debian/changelog
--- 9.55.0~dfsg-3/debian/changelog	2021-11-30 14:46:45.000000000 +0000
+++ 9.56.1~dfsg-1/debian/changelog	2022-04-20 20:47:35.000000000 +0000
@@ -1,3 +1,70 @@
+ghostscript (9.56.1~dfsg-1) unstable; urgency=medium
+
+  [ upstream ]
+  * new release
+    + fix text rendering mode 3 and pdfwrite;
+      closes: bug#1009680, thanks to Paul Gevers and others
+
+  [ Jonas Smedegaard ]
+  * fix watch file
+  * update symbols: 1 private symbol added
+
+ -- Jonas Smedegaard <dr@jones.dk>  Wed, 20 Apr 2022 22:47:35 +0200
+
+ghostscript (9.56.0~dfsg-1) unstable; urgency=medium
+
+  [ upstream ]
+  * new release
+
+  [ Jonas Smedegaard ]
+  * drop superfluous lintian overrides
+  * New upstream version 9.56.0~dfsg
+  * update symbols:
+    + 56 private symbols added
+    + 23 private symbols dropped
+  * use semantic newlines in long descriptions
+
+ -- Jonas Smedegaard <dr@jones.dk>  Wed, 30 Mar 2022 11:51:53 +0200
+
+ghostscript (9.56.0~~rc2~dfsg-1) experimental; urgency=medium
+
+  [ upstream ]
+  * new pre-release
+
+ -- Jonas Smedegaard <dr@jones.dk>  Mon, 21 Mar 2022 09:09:26 +0100
+
+ghostscript (9.56.0~~rc1~dfsg-1) experimental; urgency=medium
+
+  [ upstream ]
+  * new pre-release
+
+  [ Jonas Smedegaard ]
+  * update copyright info:
+    + add Reference and improve Comment
+      for files covered by project-wide terms
+    + fix interpret unversioned GPL/LGPL to mean any version
+    + use multiple separate License-Grant fields
+      (not multiple texts in one field, delimited by [...]
+      which is hard to distinguish when parsing by a machine)
+    + sort License sections alphabetically
+    + fix drop bogus Files section
+      (likely due to a false positive in older licensecheck
+      flagging the word Adobe as a license grant)
+    + fix avoid complex shell globbing in file listings
+      (leftover from pre-1.0 file format)
+    + update coverage
+  * update lintian overrides regarding license shortnames
+  * tighten lintian overrides
+  * drop patches cherry-picked upstream now applied
+  * drop patch 1003 adopted upstream
+  * drop patch 2009 obsoleted by upstream changes;
+    stop have ghostscript-doc depend on libjs-jquery
+  * update and unfuzz patches
+  * update Maintainer and Vcs-* fields, and drop Uploaders:
+    package now maintained in collaborative debian area of Salsa
+
+ -- Jonas Smedegaard <dr@jones.dk>  Mon, 07 Mar 2022 21:47:41 +0100
+
 ghostscript (9.55.0~dfsg-3) unstable; urgency=medium
 
   * add patch cherry-picked upstream
diff -pruN 9.55.0~dfsg-3/debian/control 9.56.1~dfsg-1/debian/control
--- 9.55.0~dfsg-3/debian/control	2021-11-19 16:58:39.000000000 +0000
+++ 9.56.1~dfsg-1/debian/control	2022-03-30 09:50:47.000000000 +0000
@@ -1,9 +1,7 @@
 Source: ghostscript
 Section: text
 Priority: optional
-Maintainer: Debian Printing Team <debian-printing@lists.debian.org>
-Uploaders:
- Jonas Smedegaard <dr@jones.dk>,
+Maintainer: Jonas Smedegaard <dr@jones.dk>
 Build-Depends:
  debhelper-compat (= 13),
  dh-linktree,
@@ -34,8 +32,8 @@ Build-Depends:
  zlib1g-dev:native,
 Standards-Version: 4.6.0
 Homepage: https://www.ghostscript.com/
-Vcs-Git: https://salsa.debian.org/printing-team/ghostscript.git
-Vcs-Browser: https://salsa.debian.org/printing-team/ghostscript
+Vcs-Git: https://salsa.debian.org/debian/ghostscript.git
+Vcs-Browser: https://salsa.debian.org/debian/ghostscript
 Rules-Requires-Root: no
 
 Package: ghostscript
@@ -51,12 +49,12 @@ Provides:
  postscript-viewer,
 Description: interpreter for the PostScript language and for PDF
  GPL Ghostscript is used for PostScript/PDF preview and printing.
- Usually as a back-end to a program such as ghostview, it can display
- PostScript and PDF documents in an X11 environment.
+ Usually as a back-end to a program such as ghostview,
+ it can display PostScript and PDF documents in an X11 environment.
  .
- Furthermore, it can render PostScript and PDF files as graphics to be
- printed on non-PostScript printers. Supported printers include common
- dot-matrix, inkjet and laser models.
+ Furthermore, it can render PostScript and PDF files as graphics
+ to be printed on non-PostScript printers.
+ Supported printers include common dot-matrix, inkjet and laser models.
 
 Package: ghostscript-x
 Architecture: any
@@ -66,8 +64,8 @@ Depends:
  ${shlibs:Depends},
 Description: interpreter for the PostScript language and for PDF - X11 support
  GPL Ghostscript is used for PostScript/PDF preview and printing.
- Usually as a back-end to a program such as ghostview, it can display
- PostScript and PDF documents in an X11 environment.
+ Usually as a back-end to a program such as ghostview,
+ it can display PostScript and PDF documents in an X11 environment.
  .
  This package contains the GPL Ghostscript output device for X11.
 
@@ -76,18 +74,17 @@ Section: doc
 Architecture: all
 Multi-Arch: foreign
 Depends:
- libjs-jquery,
  ${misc:Depends},
  ${shlibs:Depends},
 Suggests:
  ghostscript,
 Description: interpreter for the PostScript language and for PDF - Documentation
  GPL Ghostscript is used for PostScript/PDF preview and printing.
- Usually as a back-end to a program such as ghostview, it can display
- PostScript and PDF documents in an X11 environment.
+ Usually as a back-end to a program such as ghostview,
+ it can display PostScript and PDF documents in an X11 environment.
  .
- This package contains documentation for GPL Ghostscript, mainly
- targeted developers and advanced users.
+ This package contains documentation for GPL Ghostscript,
+ mainly targeted developers and advanced users.
 
 Package: libgs9
 Section: libs
@@ -102,11 +99,12 @@ Depends:
 Multi-Arch: same
 Description: interpreter for the PostScript language and for PDF - Library
  GPL Ghostscript is used for PostScript/PDF preview and printing.
- Usually as a back-end to a program such as ghostview, it can display
- PostScript and PDF documents in an X11 environment.
+ Usually as a back-end to a program such as ghostview,
+ it can display PostScript and PDF documents in an X11 environment.
  .
- This package provides the Ghostscript library which makes the
- facilities of GPL Ghostscript available to applications.
+ This package provides the Ghostscript library
+ which makes the facilities of GPL Ghostscript available
+ to applications.
 
 Package: libgs9-common
 Section: libs
@@ -118,16 +116,17 @@ Recommends:
 Multi-Arch: foreign
 Description: interpreter for the PostScript language and for PDF - common files
  GPL Ghostscript is used for PostScript/PDF preview and printing.
- Usually as a back-end to a program such as ghostview, it can display
- PostScript and PDF documents in an X11 environment.
+ Usually as a back-end to a program such as ghostview,
+ it can display PostScript and PDF documents in an X11 environment.
  .
- This package provides common architecture-independent files needed by
- the GPL Ghostscript library.
+ This package provides common architecture-independent files
+ needed by the GPL Ghostscript library.
  .
- By default, GPL Ghostscript uses a font from the fonts-droid package to
- approximate glyphs in PDFs for which the requested CJK TrueType font
- is missing.  If the fonts-droid package is not installed, these glyphs
- will be rendered as bullets.
+ By default, GPL Ghostscript uses a font from the fonts-droid package
+ to approximate glyphs in PDFs
+ for which the requested CJK TrueType font is missing.
+ If the fonts-droid package is not installed,
+ these glyphs will be rendered as bullets.
 
 Package: libgs-dev
 Section: libdevel
@@ -138,9 +137,10 @@ Depends:
 Multi-Arch: same
 Description: interpreter for the PostScript language and for PDF - Development Files
  GPL Ghostscript is used for PostScript/PDF preview and printing.
- Usually as a back-end to a program such as ghostview, it can display
- PostScript and PDF documents in an X11 environment.
+ Usually as a back-end to a program such as ghostview,
+ it can display PostScript and PDF documents in an X11 environment.
  .
- This package provides the development files for the GPL Ghostscript
- library which makes the facilities of GPL Ghostscript available to
- applications.
+ This package provides the development files
+ for the GPL Ghostscript library
+ which makes the facilities of GPL Ghostscript available
+ to applications.
diff -pruN 9.55.0~dfsg-3/debian/copyright 9.56.1~dfsg-1/debian/copyright
--- 9.55.0~dfsg-3/debian/copyright	2021-09-27 12:37:27.000000000 +0000
+++ 9.56.1~dfsg-1/debian/copyright	2022-03-16 12:52:52.000000000 +0000
@@ -60,7 +60,7 @@ Files:
 Copyright:
   1992-1996, 1999-2000  Aladdin Enterprises, Menlo Park, CA
   1991                  Apple Computer, Inc
-  2001-2021             Artifex Software, Inc.
+  2001-2022             Artifex Software, Inc.
   1989                  Berthold K.P. Horn
   1986                  Eric Gisin <egisin@waterloo.csnet>
   2001                  gs-cjk project
@@ -204,9 +204,14 @@ License-Grant:
  (415)492-9861,
  for further information.
 License: AGPL-3+
+Reference: LICENSE
 Comment:
  Files contain copyright statement and mention licensing,
  but contain no license nor concrete reference to license.
+ The files are located in directories covered by project-wide license
+ as described in file <LICENSE>,
+ but it is unclear whether or not the purpose of file-specific notes
+ is to state exceptions from general licensing.
  License presumed to be same as generally for the project.
 
 Files:
@@ -238,8 +243,11 @@ License-Grant:
  along with this software
  so you can know your rights and responsibilities.
  It should be in a file named COPYING.
-License: GPL
+License: GPL-1+
 Comment:
+ Version of license not mentioned.
+ License dictates that to mean any version of the license.
+ .
  The referenced file COPYING does not exist
  in folders belonging to same code.
 
@@ -256,8 +264,11 @@ License-Grant:
  along with this software
  so you can know your rights and responsibilities.
  It should be in a file named COPYING.
-License: GPL
+License: GPL-1+
 Comment:
+ Version of license not mentioned.
+ License dictates that to mean any version of the license.
+ .
  The referenced file COPYING does not exist
  in folders belonging to same code.
 
@@ -292,8 +303,11 @@ License-Grant:
  along with this software
  so you can know your rights and responsibilities.
  It should be in a file named COPYING.
-License: GPL
+License: GPL-1+
 Comment:
+ Version of license not mentioned.
+ License dictates that to mean any version of the license.
+ .
  The referenced file COPYING does not exist
  in folders belonging to same code.
 
@@ -311,8 +325,11 @@ License-Grant:
  along with this software
  so you can know your rights and responsibilities.
  It should be in a file named COPYING.
-License: GPL
+License: GPL-1+
 Comment:
+ Version of license not mentioned.
+ License dictates that to mean any version of the license.
+ .
  The referenced file COPYING does not exist
  in folders belonging to same code.
 
@@ -350,21 +367,26 @@ Copyright:
   1993-2006  Easy Software Products
 License-Grant:
  Distribution and use rights are outlined in the file "LICENSE.txt"
- which should have been included with this file. [...]
- .
+ which should have been included with this file.
+License-Grant:
  This code and any derivative of it may be used and distributed freely
  under the terms of the GNU General Public License
  when used with GNU Ghostscript or its derivatives.
  Use of the code (or any derivative of it)
  with software other than GNU GhostScript (or its derivatives)
  is governed by the CUPS license agreement.
-License: GPL
+License: GPL-1+
 Comment:
+ Version of license not mentioned.
+ License dictates that to mean any version of the license.
+ .
  The referenced file LICENSE.txt does not exist
  in folders belonging to same code.
 
 Files:
- contrib/gdevbjc[_a].[ch]
+ contrib/gdevbjc_.c
+ contrib/gdevbjc_.h
+ contrib/gdevbjca.c
 Copyright:
   1989, 2000  Aladdin Enterprises, Menlo Park, CA
   2000-2002   Gergely SzÃ¡sz <szaszg@hu.inter.net>
@@ -392,14 +414,6 @@ Copyright:
 License: BSD-3-Clause
 
 Files:
- base/gxfont1.h
- devices/gdevphex.c
-Copyright:
-  2001-2021  Artifex Software, Inc.
-License: Adobe-2006
- FIXME
-
-Files:
  base/aes.c
  base/aes.h
 Copyright:
@@ -487,6 +501,10 @@ License-Grant:
  as part of GNU Ghostscript and/or AFPL Ghostscript,
  under the same terms and conditions as Ghostscript.
 License: AGPL-3+
+Reference: LICENSE
+Comment:
+ Project-wide Ghostscript license terms
+ are described in file <LICENSE>.
 
 Files:
  config.guess
@@ -513,10 +531,12 @@ Files:
 Copyright:
   1998  Ivan Schreter
 License: AGPL-3+
+Reference: LICENSE
 Comment:
  File contain copyright statement and mentions licensing,
  but contains no license nor concrete reference to license.
- License presumed to be same as generally for the project.
+ License presumed to be same as generally for the project,
+ described in file <LICENSE>.
 
 Files:
  base/gsstrl.c
@@ -621,7 +641,8 @@ Copyright:
 License: MIT-Open-Group
 
 Files:
- contrib/eplaser/gdevescv.[ch]
+ contrib/eplaser/gdevescv.c
+ contrib/eplaser/gdevescv.h
 Copyright:
   1999-2000        EPSON SOFTWARE DEVELOPMENT LABORATORY, INC.
   2000-2006, 2009  SEIKO EPSON CORPORATION
@@ -634,8 +655,11 @@ License-Grant:
  along with this software
  so you can know your rights and responsibilities.
  It should be in a file named COPYING.
-License: GPL
+License: GPL-1+
 Comment:
+ Version of license not mentioned.
+ License dictates that to mean any version of the license.
+ .
  The referenced file COPYING does not exist
  in folders belonging to same code.
 
@@ -703,7 +727,7 @@ Comment:
 Files:
  debian/*
 Copyright:
-  2009-2021  Jonas Smedegaard <dr@jones.dk>
+  2009-2022  Jonas Smedegaard <dr@jones.dk>
   2004-2009  Masayuki Hatta (mhatta) <mhatta@debian.org>
 License-Grant:
  This packaging is free software;
@@ -725,8 +749,11 @@ Files:
  debian/update-gsfontmap
 Copyright:
   2010  Kenshi Muto <kmuto@debian.org>
-License: GPL
- FIXME
+License-Grant:
+ License: GPL
+License: GPL-1+
+ Version of license not mentioned.
+ License dictates that to mean any version of the license.
 
 License: AGPL-3
  GNU AFFERO GENERAL PUBLIC LICENSE
@@ -1580,138 +1607,9 @@ License: AGPL-3
  and how to apply and follow the GNU AGPL,
  see <http://www.gnu.org/licenses/>.
 
-License: GPL-3+
-Reference: /usr/share/common-licenses/GPL-3
-
-License: GPL-2+
-Reference: /usr/share/common-licenses/GPL-2
-
-License: GPL
-Reference: /usr/share/common-licenses/GPL
-
-License: LGPL-2.1
-Reference: /usr/share/common-licenses/LGPL-2.1
-
 License: Apache-2.0
 Reference: /usr/share/common-licenses/Apache-2.0
 
-License: GAP~configure
- This configure script is free software;
- the Free Software Foundation gives unlimited permission
- to copy, distribute and modify it.
-
-License: Expat
- Permission is hereby granted, free of charge,
- to any person obtaining a copy
- of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction,
- including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
- .
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
- .
- THE SOFTWARE IS PROVIDED "AS IS",
- WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO
- THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
- AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-License: Expat~SunSoft
- Permission is hereby granted, free of charge,
- to any person obtaining a copy
- of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction,
- including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
- .
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
- .
- THE SOFTWARE IS PROVIDED "AS IS",
- WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
- THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
- AND NONINFRINGEMENT.
- IN NO EVENT SHALL SUNSOFT, INC. OR ITS PARENT COMPANY BE LIABLE
- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-License: Expat~Ghostgum
- Permission is hereby granted, free of charge,
- to any person obtaining a copy of this file ("Software"),
- to deal in the Software without restriction,
- including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of this Software,
- and to permit persons to whom this file is furnished to do so,
- subject to the following conditions:
- .
- This Software is distributed with NO WARRANTY OF ANY KIND.
- No author or distributor accepts any responsibility
- for the consequences of using it,
- or for whether it serves any particular purpose or works at all,
- unless he or she says so in writing.
- .
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
-License: MIT-Open-Group
- Permission to use, copy, modify, distribute, and sell
- this software and its documentation
- for any purpose is hereby granted without fee,
- provided that the above copyright notice appear in all copies
- and that both that copyright notice and this permission notice appear
- in supporting documentation.
- .
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
- .
- THE SOFTWARE IS PROVIDED "AS IS",
- WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
- AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE OPEN GROUP BE LIABLE
- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-License: NTP~WSU
- Permission to use, copy, modify, and distribute
- this software and its documentation
- for any purpose and without fee is hereby granted.
- This software is provided "as is"
- without express or implied warranty.
-
-License: NTP~Lucent
- Permission to use, copy, modify, and distribute this software
- for any purpose without fee is hereby granted,
- provided that this entire notice is included in all copies
- of any software which is or includes
- a copy or modification of this software.
- .
- THIS SOFTWARE IS BEING PROVIDED "AS IS",
- WITHOUT ANY EXPRESS OR IMPLIED WARRANTY.
- IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
- ANY REPRESENTATION OR WARRANTY OF ANY KIND
- CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS
- FOR ANY PARTICULAR PURPOSE.
-
 License: BSD-3-Clause
  Redistribution and use in source and binary forms,
  with or without modification,
@@ -1785,6 +1683,75 @@ License: BSD-3-Clause~Adobe
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+License: Expat
+ Permission is hereby granted, free of charge,
+ to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction,
+ including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS",
+ WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO
+ THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+License: Expat~Ghostgum
+ Permission is hereby granted, free of charge,
+ to any person obtaining a copy of this file ("Software"),
+ to deal in the Software without restriction,
+ including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of this Software,
+ and to permit persons to whom this file is furnished to do so,
+ subject to the following conditions:
+ .
+ This Software is distributed with NO WARRANTY OF ANY KIND.
+ No author or distributor accepts any responsibility
+ for the consequences of using it,
+ or for whether it serves any particular purpose or works at all,
+ unless he or she says so in writing.
+ .
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+License: Expat~SunSoft
+ Permission is hereby granted, free of charge,
+ to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction,
+ including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS",
+ WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+ THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ AND NONINFRINGEMENT.
+ IN NO EVENT SHALL SUNSOFT, INC. OR ITS PARENT COMPANY BE LIABLE
+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
 License: FTL
  The FreeType Project LICENSE
  ----------------------------
@@ -1967,6 +1934,83 @@ License: FTL
  .
  Our home page can be found at https://www.freetype.org
 
+License: GAP~configure
+ This configure script is free software;
+ the Free Software Foundation gives unlimited permission
+ to copy, distribute and modify it.
+
+License: GPL-1+
+Reference: /usr/share/common-licenses/GPL-1
+
+License: GPL-2+
+Reference: /usr/share/common-licenses/GPL-2
+
+License: GPL-3+
+Reference: /usr/share/common-licenses/GPL-3
+
+License: ISC
+ Permission to use, copy, modify, and distribute this software
+ for any purpose with or without fee is hereby granted,
+ provided that the above copyright notice and this permission notice
+ appear in all copies.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS"
+ AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+ INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
+ OR ANY DAMAGES WHATSOEVER
+ RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+License: LGPL-2.1
+Reference: /usr/share/common-licenses/LGPL-2.1
+
+License: MIT-Open-Group
+ Permission to use, copy, modify, distribute, and sell
+ this software and its documentation
+ for any purpose is hereby granted without fee,
+ provided that the above copyright notice appear in all copies
+ and that both that copyright notice and this permission notice appear
+ in supporting documentation.
+ .
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS",
+ WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE OPEN GROUP BE LIABLE
+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+License: NTP~Lucent
+ Permission to use, copy, modify, and distribute this software
+ for any purpose without fee is hereby granted,
+ provided that this entire notice is included in all copies
+ of any software which is or includes
+ a copy or modification of this software.
+ .
+ THIS SOFTWARE IS BEING PROVIDED "AS IS",
+ WITHOUT ANY EXPRESS OR IMPLIED WARRANTY.
+ IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ ANY REPRESENTATION OR WARRANTY OF ANY KIND
+ CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS
+ FOR ANY PARTICULAR PURPOSE.
+
+License: NTP~WSU
+ Permission to use, copy, modify, and distribute
+ this software and its documentation
+ for any purpose and without fee is hereby granted.
+ This software is provided "as is"
+ without express or implied warranty.
+
 License: X11
  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation files
@@ -2018,20 +2062,3 @@ License: ZLIB
      and must not be misrepresented as being the original software.
   3. This notice may not be removed or altered
      from any source distribution.
-
-License: ISC
- Permission to use, copy, modify, and distribute this software
- for any purpose with or without fee is hereby granted,
- provided that the above copyright notice and this permission notice
- appear in all copies.
- .
- THE SOFTWARE IS PROVIDED "AS IS"
- AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
- INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE
- FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
- OR ANY DAMAGES WHATSOEVER
- RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- ARISING OUT OF OR IN CONNECTION WITH
- THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff -pruN 9.55.0~dfsg-3/debian/copyright_hints 9.56.1~dfsg-1/debian/copyright_hints
--- 9.55.0~dfsg-3/debian/copyright_hints	2021-11-19 17:01:32.000000000 +0000
+++ 9.56.1~dfsg-1/debian/copyright_hints	2022-04-20 20:01:27.000000000 +0000
@@ -31,6 +31,7 @@ Copyright:
   2000, Aladdin Enterprises, Menlo Park, CA
   2001-2008, Artifex Software, Inc.
   2001-2021, Artifex Software, Inc.
+  2001-2022, Artifex Software, Inc.
   2002-2021, Artifex, Inc.
   2003-2004, Copyright Hewlett-Packard Development Company, LP
   2003-2021, Artifex Software.
@@ -41,10 +42,14 @@ Copyright:
   2015-2021, Artifex Software, Inc.
   2017-2021, Artifex Software, Inc.
   2018-2021, Artifex Software, Inc.
+  2018-2022, Artifex Software, Inc.
   2019, Artifex Software, Inc.
   2019-2021, Artifex Software, Inc.
+  2019-2022, Artifex Software, Inc.
   2020-2021, Artifex Software, Inc.
+  2020-2022, Artifex Software, Inc.
   2021, Artifex Software, Inc.
+  2022, Artifex Software, Inc.
 License: AGPL-3+
  FIXME
 
@@ -146,7 +151,7 @@ Files:
 Copyright:
   1, k_fontinfodict | k_sid);
   12 0 SID -, FontInfo
-  2001-2021, Artifex Software, Inc.
+  2001-2022, Artifex Software, Inc.
   isFixedPitch,
 License: AGPL-3+
  FIXME
@@ -178,7 +183,7 @@ License: AGPL-3+
 Files:
  devices/gdevcdj.c
 Copyright:
-  2001-2021, Artifex Software, Inc.
+  2001-2022, Artifex Software, Inc.
   24 -> 32 bit (1-stage)
 License: AGPL-3+
  FIXME
@@ -186,7 +191,7 @@ License: AGPL-3+
 Files:
  base/gsicc_create.c
 Copyright:
-  2001-2021, Artifex Software, Inc.
+  2001-2022, Artifex Software, Inc.
   Artifex Software 2009-2021";
 License: AGPL-3+
  FIXME
@@ -260,7 +265,7 @@ License: AGPL-3+
 Files:
  devices/vector/gdevpsf1.c
 Copyright:
-  2001-2021, Artifex Software, Inc.
+  2001-2022, Artifex Software, Inc.
   info.members & FONT_INFO_COPYRIGHT);
 License: AGPL-3+
  FIXME
@@ -328,7 +333,7 @@ License: AGPL-3+
 Files:
  psi/iref.h
 Copyright:
-  2001-2021, Artifex Software, Inc.
+  2001-2022, Artifex Software, Inc.
   type of the structure.
 License: AGPL-3+
  FIXME
@@ -355,7 +360,8 @@ Files:
 Copyright:
   2001-2021, Artifex Software Inc.
   2001-2021, Artifex Software, Inc.
-  2009-2021, Artifex Software, Inc.
+  2001-2022, Artifex Software, Inc.
+  2009-2022, Artifex Software, Inc.
   2016-2021, Artifex Software, Inc.
 License: AGPL-3+
  FIXME
@@ -363,7 +369,7 @@ License: AGPL-3+
 Files:
  base/memento.c
 Copyright:
-  2009-2021, Artifex Software, Inc.
+  2009-2022, Artifex Software, Inc.
   doNestedDisplay(c, depth+1);
 License: AGPL-3+
  FIXME
@@ -377,20 +383,20 @@ Copyright:
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-  Copyright: Neither the name of Adobe Systems Incorporated nor the names
-  Copyright: Redistribution and use in source and binary forms, with or
-  Copyright: Redistributions in binary form must reproduce the above
-  Copyright: Redistributions of source code must retain the above
-  Copyright: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  Neither the name of Adobe Systems Incorporated nor the names
   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  Redistribution and use in source and binary forms, with or
+  Redistributions in binary form must reproduce the above
+  Redistributions of source code must retain the above
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
   disclaimer in the documentation and/or other materials
   disclaimer.
   following conditions are met:
@@ -459,7 +465,10 @@ Files:
  contrib/lxm3200-tweaked/README
  contrib/lxm3200-tweaked/Z12-Z31-QuickSetup
  contrib/md2k_md5k/README.jis
+ contrib/opvp/README.txt
+ contrib/opvp/build_opv_harness.sh
  contrib/opvp/opvp_media.def
+ contrib/opvp/opvpharness.c
  contrib/pcl3/doc/gs-pcl3.html
  contrib/pcl3/doc/notes.bbl
  contrib/pcl3/doc/pcl3opts.1
@@ -513,6 +522,8 @@ Files:
  demos/c/api_test.c
  demos/c/api_test.vcxproj
  demos/c/api_test.vcxproj.filters
+ demos/c/multi_test.vcxproj
+ demos/c/multi_test.vcxproj.filters
  demos/csharp/README.txt
  demos/csharp/linux/gtk_viewer/make.sh
  demos/csharp/windows/ghostnet.sln
@@ -579,8 +590,6 @@ Files:
  demos/java/gsviewer/build_darwin.sh
  demos/java/gsviewer/build_linux.sh
  demos/java/gsviewer/build_win32.bat
- demos/java/gsviewer/install_darwin.sh
- demos/java/gsviewer/install_linux.sh
  demos/java/gsviewer/src/com/artifex/gsviewer/DefaultUnhandledExceptionHandler.java
  demos/java/gsviewer/src/com/artifex/gsviewer/Document.java
  demos/java/gsviewer/src/com/artifex/gsviewer/GSFileFilter.java
@@ -613,8 +622,20 @@ Files:
  demos/java/jni/gs_jni/gs_jni.vcxproj
  demos/java/jni/gs_jni/gs_jni.vcxproj.filters
  demos/java/jni/gs_jni/install_linux.sh
+ demos/java/jni/gs_jni/instance_data.cpp
+ demos/java/jni/gs_jni/instance_data.h
  demos/java/jni/gs_jni/jni_util.cpp
  demos/java/jni/gs_jni/jni_util.h
+ demos/java/jni/gs_jni/settings.h
+ demos/java/mtdemo/Main.java
+ demos/java/mtdemo/README.txt
+ demos/java/mtdemo/Worker.java
+ demos/java/mtdemo/build_darwin.sh
+ demos/java/mtdemo/build_linux.sh
+ demos/java/mtdemo/build_win32.bat
+ demos/java/mtdemo/runmtd_darwin.sh
+ demos/java/mtdemo/runmtd_linux.sh
+ demos/java/mtdemo/runmtd_win32.bat
  demos/python/README.txt
  demos/python/examples.py
  devices/vector/doc_common.c
@@ -629,17 +650,18 @@ Files:
  doc/colormanage/figures/source_intent.pdf
  doc/colormanage/figures/source_profile.pdf
  doc/colormanage/figures/text_graph_image_cmyk_rgb.pdf
- doc/gs-style.css
+ doc/default.css
  doc/gs-vms.hlp
  doc/gsdoc.el
  doc/images/.DS_Store
  doc/images/Artifex_logo.png
  doc/images/favicon.png
+ doc/images/favicon.svg
  doc/images/ghostscript_logo.png
  doc/images/hamburger-light.png
+ doc/images/icon-search.png
  doc/images/x-light.png
  doc/index.html
- doc/index.js
  doc/language-bindings/c-sharp-ghost-api.html
  doc/language-bindings/c-sharp-ghost-mono.html
  doc/language-bindings/c-sharp-ghost-net.html
@@ -676,7 +698,7 @@ Files:
  doc/pclxps/README
  doc/pclxps/ghostpdl.pdf
  doc/pscet_status.txt
- doc/style.css
+ doc/site.js
  examples/alphabet.ps
  examples/annots.pdf
  examples/cjk/article9.ps
@@ -1508,9 +1530,6 @@ Files:
  DroidSansFallback.NOTICE
 Copyright:
   2005-2008, The Android Open Source Project
-  License. Subject to the terms and conditions of
-  license to reproduce, prepare Derivative Works of,
-  patent, trademark, and
 License: Apache-2.0
  FIXME
 
@@ -1837,11 +1856,3 @@ Copyright:
 License: GPL-3+
  FIXME
 
-Files:
- debian/patches/020211025~b4e8434.patch
-Copyright:
-  2001-2021, Artifex Software, Inc.) =
-  comments,
-License: UNKNOWN
- FIXME
-
diff -pruN 9.55.0~dfsg-3/debian/libgs9.symbols 9.56.1~dfsg-1/debian/libgs9.symbols
--- 9.55.0~dfsg-3/debian/libgs9.symbols	2021-11-19 15:51:06.000000000 +0000
+++ 9.56.1~dfsg-1/debian/libgs9.symbols	2022-04-20 20:46:29.000000000 +0000
@@ -1,4 +1,4 @@
-# SymbolsHelper-Confirmed: 9.55.0~dfsg amd64 arm64 armel armhf hppa hurd-i386 i386 ia64 m68k powerpc ppc64 ppc64el riscv64 s390x sh4 sparc64 x32
+# SymbolsHelper-Confirmed: 9.56.0~ amd64 arm64 armel armhf hppa hurd-i386 i386 ia64 m68k mips64el mipsel powerpc ppc64 ppc64el riscv64 s390x sh4 sparc64 x32
 libgs.so.9 libgs9 #MINVER#
  (optional=privatelib|arch-bits=32)Add64@Base 8.61.dfsg.1
  (optional=privatelib)BJL_command_set@Base 8.61.dfsg.1
@@ -25,6 +25,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)DecodeLMN_from_cache@Base 8.61.dfsg.1
  (optional=privatelib)Default_GraphicsState@Base 8.61.dfsg.1
  (optional=privatelib)DevCMYKOGComponents@Base 9.15~dfsg
+ (optional=privatelib)DevCMYKTComponents@Base 9.56.0~
  (optional=privatelib)DeviceCMYKComponents@Base 8.61.dfsg.1
  (optional=privatelib)DeviceGrayComponents@Base 9.27~dfsg
  (optional=privatelib)DeviceRGBComponents@Base 9.27~dfsg
@@ -76,12 +77,15 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)Memento_listBlockInfo@Base 9.19~~rc1~dfsg
  (optional=privatelib)Memento_listBlocks@Base 9.05~
  (optional=privatelib)Memento_listNewBlocks@Base 9.05~
+ (optional=privatelib)Memento_listPhasedBlocks@Base 9.56.0~
  (optional=privatelib)Memento_malloc@Base 9.04~dfsg
  (optional=privatelib)Memento_paranoidAt@Base 9.04~dfsg
  (optional=privatelib)Memento_realloc@Base 9.04~dfsg
  (optional=privatelib)Memento_reference@Base 9.20~dfsg
  (optional=privatelib)Memento_setMax@Base 9.06~dfsg
  (optional=privatelib)Memento_setParanoia@Base 9.04~dfsg
+ (optional=privatelib)Memento_setVerbose@Base 9.56.0~
+ (optional=privatelib)Memento_showHash@Base 9.56.0~
  (optional=privatelib)Memento_squeezing@Base 9.51~dfsg
  (optional=privatelib)Memento_startLeaking@Base 9.20~dfsg
  (optional=privatelib)Memento_stats@Base 9.06~dfsg
@@ -91,8 +95,8 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib|arch-bits=32)MulTo64@Base 8.61.dfsg.1
  (optional=privatelib|arch-bits=32)Order64@Base 8.61.dfsg.1
  (optional=privatelib)PCLm_close@Base 9.25~dfsg
- (optional=privatelib)PCLm_get_initial_matrix@Base 9.55.0~dfsg
  (optional=privatelib)PCLm_open@Base 9.25~dfsg
+ (optional=privatelib)PermittedKeys@Base 9.56.0~
  (optional=privatelib)QuadGlyphList@Base 9.05~
  (optional=privatelib)Range3_default@Base 8.61.dfsg.1
  (optional=privatelib)Range4_default@Base 8.61.dfsg.1
@@ -400,9 +404,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)clist_icc_writetable@Base 9.00~dfsg
  (optional=privatelib)clist_init_io_procs@Base 8.61.dfsg.1
  (optional=privatelib)clist_initialize_device_procs@Base 9.55.0~dfsg
- (optional=privatelib)clist_io_procs_file_global@Base 8.61.dfsg.1
  (optional=privatelib)clist_io_procs_memory@Base 8.61.dfsg.1
- (optional=privatelib)clist_io_procs_memory_global@Base 8.61.dfsg.1
  (optional=privatelib)clist_make_accum_device@Base 9.15~dfsg
  (optional=privatelib)clist_minimum_buffer@Base 9.53.0~dfsg
  (optional=privatelib)clist_mutate_to_clist@Base 9.54.0~dfsg
@@ -465,12 +467,15 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)cmd_put_enable_lop@Base 8.61.dfsg.1
  (optional=privatelib)cmd_put_frac31@Base 8.61.dfsg.1
  (optional=privatelib)cmd_put_halftone@Base 8.61.dfsg.1
+ (optional=privatelib)cmd_put_list_extended_op@Base 9.56.0~
  (optional=privatelib)cmd_put_list_op@Base 8.61.dfsg.1
  (optional=privatelib)cmd_put_params@Base 8.61.dfsg.1
  (optional=privatelib)cmd_put_range_op@Base 8.61.dfsg.1
  (optional=privatelib)cmd_put_w@Base 8.61.dfsg.1
  (optional=privatelib)cmd_read_matrix@Base 8.62.dfsg.1
  (optional=privatelib)cmd_set_lop@Base 8.61.dfsg.1
+ (optional=privatelib)cmd_set_screen_phase@Base 9.56.0~
+ (optional=privatelib)cmd_set_screen_phase_generic@Base 9.56.0~
  (optional=privatelib)cmd_set_tile_colors@Base 8.61.dfsg.1
  (optional=privatelib)cmd_set_tile_phase@Base 8.61.dfsg.1
  (optional=privatelib)cmd_set_tile_phase_generic@Base 8.63.dfsg.1
@@ -623,7 +628,6 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)debug_print_ref_packed@Base 8.61.dfsg.1
  (optional=privatelib)debug_print_string@Base 8.61.dfsg.1
  (optional=privatelib)debug_print_string_hex@Base 8.61.dfsg.1
- (optional=privatelib)debug_print_string_hex_nomem@Base 9.10~dfsg
  (optional=privatelib)default_defaultWidthX@Base 8.61.dfsg.1
  (optional=privatelib)default_subclass_begin_page@Base 9.55.0~dfsg
  (optional=privatelib)default_subclass_begin_transparency_group@Base 9.18~dfsg
@@ -691,6 +695,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)default_subclass_transform_pixel_region@Base 9.26~dfsg
  (optional=privatelib)default_subclass_update_spot_equivalent_colors@Base 9.18~dfsg
  (optional=privatelib)define_gs_font@Base 8.61.dfsg.1
+ (optional=privatelib)device_is_contone@Base 9.56.0~
  (optional=privatelib)devn_copy_params@Base 9.02~dfsg
  (optional=privatelib)devn_free_params@Base 9.02~dfsg
  (optional=privatelib)devn_get_color_comp_index@Base 8.61.dfsg.1
@@ -825,12 +830,10 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)eprn_set_page_layout@Base 8.61.dfsg.1
  (optional=privatelib)eprn_split_FS@Base 8.61.dfsg.1
  (optional=privatelib)errflush@Base 8.61.dfsg.1
- (optional=privatelib)errflush_nomem@Base 9.00~dfsg
  (optional=privatelib)errorexec_find@Base 9.25~dfsg-3~
  (optional=privatelib)errprintf@Base 8.61.dfsg.1
  (optional=privatelib)errprintf_nomem@Base 9.00~dfsg
  (optional=privatelib)errwrite@Base 8.61.dfsg.1
- (optional=privatelib)errwrite_nomem@Base 9.00~dfsg
  (optional=privatelib)ets_calloc@Base 9.21~dfsg
  (optional=privatelib)ets_create@Base 9.21~dfsg
  (optional=privatelib)ets_destroy@Base 9.21~dfsg
@@ -1135,11 +1138,15 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gp_fseek_impl@Base 9.28~~rc1~dfsg
  (optional=privatelib)gp_fseekable_impl@Base 9.28~~rc1~dfsg
  (optional=privatelib)gp_ftell_impl@Base 9.28~~rc1~dfsg
+ (optional=privatelib)gp_get_debug_mem_ptr@Base 9.56.0~
+ (optional=privatelib)gp_get_globals@Base 9.56.0~
  (optional=privatelib)gp_get_realtime@Base 8.61.dfsg.1
  (optional=privatelib)gp_get_usertime@Base 8.61.dfsg.1
  (optional=privatelib)gp_getenv@Base 8.61.dfsg.1
  (optional=privatelib)gp_getenv_display@Base 8.61.dfsg.1
  (optional=privatelib)gp_gettmpdir@Base 8.61.dfsg.1
+ (optional=privatelib)gp_global_lock@Base 9.56.0~
+ (optional=privatelib)gp_global_unlock@Base 9.56.0~
  (optional=privatelib)gp_init@Base 8.61.dfsg.1
  (optional=privatelib)gp_monitor_close@Base 8.61.dfsg.1
  (optional=privatelib)gp_monitor_enter@Base 8.61.dfsg.1
@@ -1169,6 +1176,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gp_semaphore_sizeof@Base 8.61.dfsg.1
  (optional=privatelib)gp_semaphore_wait@Base 8.61.dfsg.1
  (optional=privatelib)gp_serialnumber@Base 9.05~
+ (optional=privatelib)gp_set_debug_mem_ptr@Base 9.56.0~
  (optional=privatelib)gp_setmode_binary_impl@Base 9.28~~rc1~dfsg
  (optional=privatelib)gp_stat@Base 9.18~dfsg
  (optional=privatelib)gp_stat_impl@Base 9.18~dfsg
@@ -1184,6 +1192,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gp_xpsprint@Base 9.19~~rc1~dfsg
  (optional=privatelib)gray_cs_to_devn_cm@Base 8.61.dfsg.1
  (optional=privatelib)gray_cs_to_gray_cm@Base 8.61.dfsg.1
+ (optional=privatelib)gs_PCLm8_device@Base 9.56.1~dfsg-1
  (optional=privatelib)gs_PCLm_device@Base 9.25~dfsg
  (optional=privatelib)gs_abort@Base 8.61.dfsg.1
  (optional=privatelib)gs_abort_pdf14trans_device@Base 9.15~dfsg
@@ -1212,6 +1221,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gs_alloc_string_ref@Base 8.61.dfsg.1
  (optional=privatelib)gs_ap3250_device@Base 8.61.dfsg.1
  (optional=privatelib)gs_appledmp_device@Base 9.51~dfsg
+ (optional=privatelib)gs_appleraster_device@Base 9.56.0~
  (optional=privatelib)gs_arc@Base 8.61.dfsg.1
  (optional=privatelib)gs_arc_add@Base 8.61.dfsg.1
  (optional=privatelib)gs_arcn@Base 8.61.dfsg.1
@@ -1627,6 +1637,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gs_font_cid_system_info@Base 8.61.dfsg.1
  (optional=privatelib)gs_font_dir_alloc2@Base 8.61.dfsg.1
  (optional=privatelib)gs_font_dir_alloc2_limits@Base 8.61.dfsg.1
+ (optional=privatelib)gs_font_dir_free@Base 9.56.0~
  (optional=privatelib)gs_font_finalize@Base 8.61.dfsg.1
  (optional=privatelib)gs_font_find_similar@Base 8.61.dfsg.1
  (optional=privatelib)gs_font_glyph_is_notdef@Base 8.61.dfsg.1
@@ -1681,6 +1692,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gs_getstrokeconstantalpha@Base 9.22~dfsg
  (optional=privatelib)gs_gettextlinematrix@Base 9.22~dfsg
  (optional=privatelib)gs_gettextmatrix@Base 9.22~dfsg
+ (optional=privatelib)gs_globals_init@Base 9.56.0~
  (optional=privatelib)gs_glyph_cache__alloc@Base 8.61.dfsg.1
  (optional=privatelib)gs_glyph_cache__release@Base 8.61.dfsg.1
  (optional=privatelib)gs_glyph_data_free@Base 8.61.dfsg.1
@@ -1892,10 +1904,9 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gs_lib_ctx_get_cms_context@Base 9.10~dfsg
  (optional=privatelib)gs_lib_ctx_get_default_device_list@Base 9.15~dfsg
  (optional=privatelib)gs_lib_ctx_get_interp_instance@Base 9.00~dfsg
- (optional=privatelib)gs_lib_ctx_get_non_gc_memory_t@Base 8.61.dfsg.1
  (optional=privatelib)gs_lib_ctx_init@Base 8.61.dfsg.1
+ (optional=privatelib)gs_lib_ctx_nts_adjust@Base 9.56.0~
  (optional=privatelib)gs_lib_ctx_register_callout@Base 9.53.0~dfsg
- (optional=privatelib)gs_lib_ctx_set_cms_context@Base 9.10~dfsg
  (optional=privatelib)gs_lib_ctx_set_default_device_list@Base 9.15~dfsg
  (optional=privatelib)gs_lib_ctx_set_icc_directory@Base 9.04~dfsg
  (optional=privatelib)gs_lib_ctx_stash_exe@Base 9.28~~rc1~dfsg
@@ -2189,6 +2200,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gs_plibm_device@Base 9.16~dfsg
  (optional=privatelib)gs_png16_device@Base 8.61.dfsg.1
  (optional=privatelib)gs_png16m_device@Base 8.61.dfsg.1
+ (optional=privatelib)gs_png16malpha_device@Base 9.56.0~
  (optional=privatelib)gs_png256_device@Base 8.61.dfsg.1
  (optional=privatelib)gs_png48_device@Base 8.61.dfsg.1
  (optional=privatelib)gs_pngalpha_device@Base 8.61.dfsg.1
@@ -2223,6 +2235,8 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gs_psdcmyk16_device@Base 9.27~dfsg
  (optional=privatelib)gs_psdcmyk_device@Base 8.61.dfsg.1
  (optional=privatelib)gs_psdcmykog_device@Base 9.15~dfsg
+ (optional=privatelib)gs_psdcmyktags16_device@Base 9.56.0~
+ (optional=privatelib)gs_psdcmyktags_device@Base 9.56.0~
  (optional=privatelib)gs_psdrgb16_device@Base 9.27~dfsg
  (optional=privatelib)gs_psdrgb_device@Base 8.61.dfsg.1
  (optional=privatelib)gs_purge_control_paths@Base 9.28~~rc1~dfsg
@@ -2278,8 +2292,6 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gs_save_any_memory@Base 9.00~dfsg
  (optional=privatelib)gs_scale@Base 8.61.dfsg.1
  (optional=privatelib)gs_scalefont@Base 8.61.dfsg.1
- (optional=privatelib)gs_scan_comment_proc@Base 9.01~
- (optional=privatelib)gs_scan_dsc_proc@Base 9.01~
  (optional=privatelib)gs_scan_handle_refill@Base 9.01~
  (optional=privatelib)gs_scan_string_token_options@Base 9.01~
  (optional=privatelib)gs_scan_token@Base 9.01~
@@ -2548,6 +2560,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gs_update_trans_marking_params@Base 9.04~dfsg
  (optional=privatelib)gs_updatematrices@Base 9.55.0~dfsg
  (optional=privatelib)gs_upmergepath@Base 8.61.dfsg.1
+ (optional=privatelib)gs_urf_device@Base 9.56.0~
  (optional=privatelib)gs_vsnprintf@Base 9.10~dfsg
  (optional=privatelib)gs_vsprintf@Base 9.10~dfsg
  (optional=privatelib)gs_widthshow_begin@Base 8.61.dfsg.1
@@ -2905,6 +2918,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gx_default_fill_mask@Base 8.61.dfsg.1
  (optional=privatelib)gx_default_fill_parallelogram@Base 8.61.dfsg.1
  (optional=privatelib)gx_default_fill_path@Base 8.61.dfsg.1
+ (optional=privatelib)gx_default_fill_path_shading_or_pattern@Base 9.56.0~
  (optional=privatelib)gx_default_fill_rectangle_hl_color@Base 8.61.dfsg.1
  (optional=privatelib)gx_default_fill_stroke_path@Base 9.51~dfsg
  (optional=privatelib)gx_default_fill_trapezoid@Base 8.61.dfsg.1
@@ -2951,6 +2965,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gx_default_strip_tile_rect_devn@Base 9.06~dfsg
  (optional=privatelib)gx_default_strip_tile_rectangle@Base 8.61.dfsg.1
  (optional=privatelib)gx_default_stroke_path@Base 8.61.dfsg.1
+ (optional=privatelib)gx_default_stroke_path_shading_or_pattern@Base 9.56.0~
  (optional=privatelib)gx_default_sync_output@Base 8.61.dfsg.1
  (optional=privatelib)gx_default_text_begin@Base 8.61.dfsg.1
  (optional=privatelib)gx_default_text_release@Base 8.61.dfsg.1
@@ -3018,6 +3033,8 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gx_devn_prn_update_spot_equivalent_colors@Base 9.15~dfsg
  (optional=privatelib)gx_devn_reduce_colored_halftone@Base 8.61.dfsg.1
  (optional=privatelib)gx_downscaler_adjust_bandheight@Base 9.15~dfsg
+ (optional=privatelib)gx_downscaler_create_icc_link@Base 9.56.0~
+ (optional=privatelib)gx_downscaler_create_post_render_link@Base 9.56.0~
  (optional=privatelib)gx_downscaler_decode_factor@Base 9.53.0~dfsg
  (optional=privatelib)gx_downscaler_fin@Base 9.04~dfsg
  (optional=privatelib)gx_downscaler_get_bits_rectangle@Base 9.06~dfsg
@@ -3193,6 +3210,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gx_image_type_table@Base 8.61.dfsg.1
  (optional=privatelib)gx_image_type_table_count@Base 8.61.dfsg.1
  (optional=privatelib)gx_init_CIE@Base 8.61.dfsg.1
+ (optional=privatelib)gx_init_non_threadsafe_device@Base 9.56.0~
  (optional=privatelib)gx_init_paint_1@Base 8.61.dfsg.1
  (optional=privatelib)gx_init_paint_3@Base 8.61.dfsg.1
  (optional=privatelib)gx_init_paint_4@Base 8.61.dfsg.1
@@ -3419,7 +3437,6 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)gxht_thresh_planes@Base 9.05~
  (optional=privatelib)hc_put_code_proc@Base 8.61.dfsg.1
  (optional=privatelib)hc_put_last_bits_proc@Base 8.61.dfsg.1
- (optional=privatelib|arch=!sparc !sparc64)hdr_id@Base 9.16~dfsg
  (optional=privatelib)height@Base 8.61.dfsg.1
  (optional=privatelib)ht_order_procs_table@Base 8.61.dfsg.1
  (optional=privatelib)htsc_gen_ordered@Base 9.22~dfsg
@@ -3919,6 +3936,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdf_font_type3_alloc@Base 8.61.dfsg.1
  (optional=privatelib)pdf_font_used_glyph@Base 8.61.dfsg.1
  (optional=privatelib)pdf_fontfile_hash@Base 9.10~dfsg
+ (optional=privatelib)pdf_fontmap_lookup_cidfont@Base 9.56.0~
  (optional=privatelib)pdf_fontmap_lookup_font@Base 9.55.0~dfsg
  (optional=privatelib)pdf_forget_resource@Base 8.61.dfsg.1
  (optional=privatelib)pdf_free_charproc_ownership@Base 9.06~dfsg
@@ -4114,6 +4132,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_Ts@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_Tw@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_Tz@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_add_fontmapfiles@Base 9.56.0~
  (optional=privatelib)pdfi_add_initial_paths_to_search_paths@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_add_paths_to_search_paths@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_apply_AES_filter@Base 9.55.0~dfsg
@@ -4121,8 +4140,8 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_apply_SubFileDecode_filter@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_apply_imscale_filter@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_array_alloc@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_array_fetch@Base 9.56.0~
  (optional=privatelib)pdfi_array_from_stack@Base 9.55.0~dfsg
- (optional=privatelib)pdfi_array_get@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_array_get_int@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_array_get_no_deref@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_array_get_no_store_R@Base 9.55.0~dfsg
@@ -4167,13 +4186,15 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_create_icc_colorspace_from_stream@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_currentcolorspace@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_curveto@Base 9.55.0~dfsg
- (optional=privatelib)pdfi_custom_fmap_enties@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_custom_fmap_entries@Base 9.56.0~
  (optional=privatelib)pdfi_d0@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_d1@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_decode_glyph@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_decrypt_string@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_deref_loop_detect@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_deref_loop_detect_nocache@Base 9.56.0~
  (optional=privatelib)pdfi_dereference@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_dereference_nocache@Base 9.56.0~
  (optional=privatelib)pdfi_device_check_param@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_device_check_param_bool@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_device_check_param_exists@Base 9.55.0~dfsg
@@ -4189,10 +4210,10 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_dict_from_obj@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_dict_from_stack@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_dict_get2@Base 9.55.0~dfsg
- (optional=privatelib)pdfi_dict_get@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_dict_get_bool2@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_dict_get_bool@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_dict_get_by_key@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_dict_get_common@Base 9.56.0~
  (optional=privatelib)pdfi_dict_get_int2@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_dict_get_int@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_dict_get_int_def@Base 9.55.0~dfsg
@@ -4295,14 +4316,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_loop_detector_mark@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_make_float_array_from_dict@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_make_int_array_from_dict@Base 9.55.0~dfsg
- (optional=privatelib)pdfi_mark_dict@Base 9.55.0~dfsg
- (optional=privatelib)pdfi_mark_embed_filespec@Base 9.55.0~dfsg
- (optional=privatelib)pdfi_mark_from_dict@Base 9.55.0~dfsg
- (optional=privatelib)pdfi_mark_modA@Base 9.55.0~dfsg
- (optional=privatelib)pdfi_mark_modDest@Base 9.55.0~dfsg
- (optional=privatelib)pdfi_mark_object@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_mark_stack@Base 9.55.0~dfsg
- (optional=privatelib)pdfi_mark_stream@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_merge_dicts@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_moveto@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_name_alloc@Base 9.55.0~dfsg
@@ -4325,7 +4339,9 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_oc_is_off@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_op_BDC@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_op_BMC@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_op_DP@Base 9.56.0~
  (optional=privatelib)pdfi_op_EMC@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_op_MP@Base 9.56.0~
  (optional=privatelib)pdfi_op_Q@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_op_q@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_open_font_file@Base 9.55.0~dfsg
@@ -4342,6 +4358,16 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_pattern_cleanup@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_pattern_create@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_pattern_set@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_pdfmark_dict@Base 9.56.0~
+ (optional=privatelib)pdfi_pdfmark_embed_filespec@Base 9.56.0~
+ (optional=privatelib)pdfi_pdfmark_from_dict@Base 9.56.0~
+ (optional=privatelib)pdfi_pdfmark_from_objarray@Base 9.56.0~
+ (optional=privatelib)pdfi_pdfmark_modA@Base 9.56.0~
+ (optional=privatelib)pdfi_pdfmark_modDest@Base 9.56.0~
+ (optional=privatelib)pdfi_pdfmark_object@Base 9.56.0~
+ (optional=privatelib)pdfi_pdfmark_stream@Base 9.56.0~
+ (optional=privatelib)pdfi_pdfmark_write_boxes@Base 9.56.0~
+ (optional=privatelib)pdfi_pdfmark_write_docinfo@Base 9.56.0~
  (optional=privatelib)pdfi_pop@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_prep_collection@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_process_pdf_file@Base 9.55.0~dfsg
@@ -4354,6 +4380,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_read_Pages@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_read_Root@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_read_bare_object@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_read_byte@Base 9.56.0~
  (optional=privatelib)pdfi_read_bytes@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_read_cff_font@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_read_cidtype0_font@Base 9.55.0~dfsg
@@ -4370,6 +4397,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_read_xref@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_rectpath@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_repair_file@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_report_errors@Base 9.56.0~
  (optional=privatelib)pdfi_resolve_indirect@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_resolve_indirect_loop_detect@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_restore_DefaultQState@Base 9.55.0~dfsg
@@ -4379,9 +4407,11 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_separation_name_from_index@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_set_DefaultQState@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_set_ExtGState@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_set_error_var@Base 9.56.0~
  (optional=privatelib)pdfi_set_font_internal@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_set_input_stream@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_set_stream_parent@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_set_warning_var@Base 9.56.0~
  (optional=privatelib)pdfi_setcmykfill@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_setcmykstroke@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_setcolorN@Base 9.55.0~dfsg
@@ -4405,6 +4435,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_setrgbstroke@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_setstrokecolor@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_setstrokecolor_space@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_setup_DefaultSpaces@Base 9.56.0~
  (optional=privatelib)pdfi_shading@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_shading_build@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_shading_free@Base 9.55.0~dfsg
@@ -4423,6 +4454,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_stroke@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_t1_global_glyph_code@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_tell@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_tounicode_char_to_unicode@Base 9.56.0~
  (optional=privatelib)pdfi_trans_begin_form_group@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_trans_begin_isolated_group@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_trans_begin_page_group@Base 9.55.0~dfsg
@@ -4431,6 +4463,8 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_trans_end_isolated_group@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_trans_end_simple_group@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_trans_end_smask_notify@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_trans_okOPcs@Base 9.56.0~
+ (optional=privatelib)pdfi_trans_required@Base 9.56.0~
  (optional=privatelib)pdfi_trans_set_needs_OP@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_trans_set_params@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_trans_setup@Base 9.55.0~dfsg
@@ -4438,12 +4472,11 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)pdfi_trans_teardown@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_trans_teardown_text@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_unread@Base 9.55.0~dfsg
+ (optional=privatelib)pdfi_unread_byte@Base 9.56.0~
  (optional=privatelib)pdfi_unread_tell@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_v_curveto@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_verbose_error@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_verbose_warning@Base 9.55.0~dfsg
- (optional=privatelib)pdfi_write_boxes_pdfmark@Base 9.55.0~dfsg
- (optional=privatelib)pdfi_write_docinfo_pdfmark@Base 9.55.0~dfsg
  (optional=privatelib)pdfi_y_curveto@Base 9.55.0~dfsg
  (optional=privatelib)pdfmark_close_outline@Base 8.61.dfsg.1
  (optional=privatelib)pdfmark_end_pagelabels@Base 8.61.dfsg.1
@@ -4570,6 +4603,7 @@ libgs.so.9 libgs9 #MINVER#
  (optional=privatelib)psf_write_truetype_stripped@Base 8.61.dfsg.1
  (optional=privatelib)psf_write_type1_font@Base 8.61.dfsg.1
  (optional=privatelib)psf_write_type2_font@Base 8.61.dfsg.1
+ (optional=privatelib)psi_device_ref_finalize@Base 9.56.0~
  (optional=privatelib)ptr_const_string_procs@Base 8.61.dfsg.1
  (optional=privatelib)ptr_is_within_mem_clumps@Base 9.20~dfsg
  (optional=privatelib)ptr_name_index_procs@Base 9.06~dfsg
diff -pruN 9.55.0~dfsg-3/debian/patches/020211007~d9d8db2.patch 9.56.1~dfsg-1/debian/patches/020211007~d9d8db2.patch
--- 9.55.0~dfsg-3/debian/patches/020211007~d9d8db2.patch	2021-11-29 10:05:46.000000000 +0000
+++ 9.56.1~dfsg-1/debian/patches/020211007~d9d8db2.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,28 +0,0 @@
-Description: Fix gx_default_copy_alpha calling get_bits_rectangle
- In a previous commit
- I moved from using the (now removed) get_bits call
- to using get_bits_rectangle.
- In so doing, I had to choose a set of options
- to use when retrieving the bitmap data.
- .
- I chose GB_ALIGN_ANY for the alignment,
- when actually I should have picked GB_ALIGN_STANDARD.
- Correcting that solves this issue.
-Origin: upstream, https://git.ghostscript.com/?p=ghostpdl.git;h=d9d8db2
-Author: Robin Watts <Robin.Watts@artifex.com>
-Bug: https://bugs.ghostscript.com/show_bug.cgi?id=704543
-Bug-Debian: https://bugs.debian.org/1000710
-Last-Update: 2021-11-29
----
-This patch header follows DEP-3: https://dep.debian.net/deps/dep3/
---- a/base/gdevdbit.c
-+++ b/base/gdevdbit.c
-@@ -379,7 +379,7 @@
-             int l_xprev = x;
-             gs_get_bits_params_t params;
- 
--            params.options = (GB_ALIGN_ANY |
-+            params.options = (GB_ALIGN_STANDARD |
-                               (GB_RETURN_COPY | GB_RETURN_POINTER) |
-                               GB_OFFSET_0 |
-                               GB_RASTER_STANDARD | GB_PACKING_CHUNKY |
diff -pruN 9.55.0~dfsg-3/debian/patches/020211025~b4e8434.patch 9.56.1~dfsg-1/debian/patches/020211025~b4e8434.patch
--- 9.55.0~dfsg-3/debian/patches/020211025~b4e8434.patch	2021-11-19 16:43:46.000000000 +0000
+++ 9.56.1~dfsg-1/debian/patches/020211025~b4e8434.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,1258 +0,0 @@
-Description: regenerate pdfwrite gs_c_pdf_glyph_type array after Encoding changes
- Commit 4d91c6ad3e76e19f36d23a50dce253fbbc7d0560
- manually altered the 'known encoding'
- to add a number of missing strings.
- However this did not update an array
- which pdfwrite depends upon
- leading to incorrect ToUnicode entries being emitted.
- .
- This commit regenerates the files
- gscedata.c, gscedata.h, gdevpdtv.c and gdevpdtv.h
- using the program toolbin/encs2c.ps
- which is the correct method
- for updating these files (from the stored encodings).
- .
- In addition commit 87eed438b0cffba111371c25e1d169163f310465
- updated the encs2c.ps program
- by adding some information to the comments
- regarding using the program,
- specifically to include gs_css_e.ps.
- .
- This commit also updates the program still further
- to emit the current boilerplate for the copyright comments,
- stop writing the old CVS ID
- and adds a sterner warning to the comments in the .c and .h files
- about using the program to regenerate these files
- rather than manually modifying them.
-Origin: upstream, https://git.ghostscript.com/?p=ghostpdl.git;h=b4e8434
-Author: Ken Sharp <ken.sharp@artifex.com>
-Bug: https://bugs.ghostscript.com/show_bug.cgi?id=704478
-Bug-Debian: https://bugs.debian.org/998458
-Last-Update: 2021-11-19
----
-This patch header follows DEP-3: https://dep.debian.net/deps/dep3/
---- a/base/gscedata.c
-+++ b/base/gscedata.c
-@@ -12,7 +12,6 @@
-    Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
-    CA 94945, U.S.A., +1(415)492-9861, for further information.
- */
--
- /*
-  * This file contains substantial parts of toolbin/encs2c.ps,
-  * which generated the remainder of the file mechanically from
-@@ -23,6 +22,9 @@
-  * This source file is maintained manually under source code control,
-  * however its content should be regenerated by using encs2c.ps
-  * if changes are required.
-+ * You should not manually alter this file! If you regenerate it using
-+ * encs2c.ps you must regenerate all 4 files; base/gscedata.[c|h]
-+ * and devices/vector/gdevpdtv.[c|h]
-  */
- 
- #include "stdpre.h"
---- a/base/gscedata.h
-+++ b/base/gscedata.h
-@@ -12,7 +12,6 @@
-    Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
-    CA 94945, U.S.A., +1(415)492-9861, for further information.
- */
--
- /*
-  * This file contains substantial parts of toolbin/encs2c.ps,
-  * which generated the remainder of the file mechanically from
-@@ -23,13 +22,14 @@
-  * This source file is maintained manually under source code control,
-  * however its content should be regenerated by using encs2c.ps
-  * if changes are required.
-+ * You should not manually alter this file! If you regenerate it using
-+ * encs2c.ps you must regenerate all 4 files; base/gscedata.[c|h]
-+ * and devices/vector/gdevpdtv.[c|h]
-  */
- 
- #ifndef gscedata_INCLUDED
- #  define gscedata_INCLUDED
- 
--#include "stdpre.h"
--
- #define NUM_LEN_BITS 5
- 
- #define N(len,offset) (((offset) << NUM_LEN_BITS) + (len))
---- a/devices/vector/gdevpdtv.c
-+++ b/devices/vector/gdevpdtv.c
-@@ -12,7 +12,6 @@
-    Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
-    CA 94945, U.S.A., +1(415)492-9861, for further information.
- */
--
- /*
-  * This file contains substantial parts of toolbin/encs2c.ps,
-  * which generated the remainder of the file mechanically from
-@@ -23,6 +22,9 @@
-  * This source file is maintained manually under source code control,
-  * however its content should be regenerated by using encs2c.ps
-  * if changes are required.
-+ * You should not manually alter this file! If you regenerate it using
-+ * encs2c.ps you must regenerate all 4 files; base/gscedata.[c|h]
-+ * and devices/vector/gdevpdtv.[c|h]
-  */
- 
- #include "gdevpdtv.h"
-@@ -65,7 +67,7 @@
- 0,
- 0,
- 28,
--3,
-+0,
- 0,
- 0,
- 0,
-@@ -89,7 +91,7 @@
- 0,
- 0,
- 12,
--192,
-+0,
- 0,
- 0,
- 0,
-@@ -97,7 +99,7 @@
- 0,
- 0,
- 60,
--1,
-+0,
- 3,
- 0,
- 0,
-@@ -129,7 +131,7 @@
- 0,
- 0,
- 92,
--49,
-+51,
- 0,
- 0,
- 0,
-@@ -153,7 +155,7 @@
- 0,
- 0,
- 76,
--4,
-+0,
- 0,
- 192,
- 0,
-@@ -161,7 +163,7 @@
- 0,
- 0,
- 12,
--0,
-+1,
- 1,
- 0,
- 3,
-@@ -193,7 +195,7 @@
- 0,
- 0,
- 12,
--4,
-+5,
- 48,
- 0,
- 0,
-@@ -313,7 +315,7 @@
- 0,
- 0,
- 12,
--4,
-+192,
- 0,
- 0,
- 0,
-@@ -354,7 +356,7 @@
- 0,
- 60,
- 4,
--1,
-+0,
- 0,
- 0,
- 0,
-@@ -369,7 +371,7 @@
- 0,
- 0,
- 60,
--64,
-+0,
- 0,
- 48,
- 0,
-@@ -393,7 +395,7 @@
- 0,
- 0,
- 12,
--12,
-+0,
- 12,
- 0,
- 0,
-@@ -418,7 +420,7 @@
- 0,
- 28,
- 48,
--3,
-+1,
- 0,
- 3,
- 0,
-@@ -433,7 +435,7 @@
- 0,
- 0,
- 28,
--0,
-+4,
- 48,
- 0,
- 0,
-@@ -473,7 +475,7 @@
- 0,
- 0,
- 0,
--12,
-+4,
- 192,
- 0,
- 0,
-@@ -513,7 +515,8 @@
- 0,
- 0,
- 0,
--4,
-+12,
-+0,
- 0,
- 0,
- 0,
-@@ -537,7 +540,6 @@
- 0,
- 0,
- 0,
--192,
- 12,
- 0,
- 0,
-@@ -553,7 +555,7 @@
- 0,
- 0,
- 0,
--4,
-+0,
- 0,
- 4,
- 0,
-@@ -593,7 +595,7 @@
- 0,
- 0,
- 0,
--4,
-+76,
- 48,
- 48,
- 0,
-@@ -633,7 +635,8 @@
- 0,
- 0,
- 0,
--12,
-+4,
-+0,
- 0,
- 0,
- 0,
-@@ -649,7 +652,6 @@
- 0,
- 0,
- 0,
--64,
- 192,
- 0,
- 0,
-@@ -673,7 +675,7 @@
- 0,
- 0,
- 0,
--12,
-+4,
- 51,
- 0,
- 0,
-@@ -713,7 +715,7 @@
- 0,
- 0,
- 0,
--12,
-+4,
- 0,
- 0,
- 0,
-@@ -761,7 +763,7 @@
- 0,
- 0,
- 0,
--0,
-+192,
- 0,
- 4,
- 0,
-@@ -802,7 +804,7 @@
- 0,
- 0,
- 48,
--1,
-+3,
- 1,
- 0,
- 0,
-@@ -817,7 +819,7 @@
- 0,
- 0,
- 0,
--64,
-+0,
- 0,
- 48,
- 0,
-@@ -833,7 +835,7 @@
- 0,
- 0,
- 0,
--4,
-+12,
- 16,
- 0,
- 0,
-@@ -866,14 +868,14 @@
- 0,
- 0,
- 0,
--3,
-+1,
- 4,
- 0,
- 0,
- 0,
- 0,
- 0,
--12,
-+76,
- 0,
- 0,
- 0,
-@@ -930,7 +932,7 @@
- 0,
- 0,
- 0,
--1,
-+3,
- 16,
- 0,
- 0,
-@@ -953,7 +955,7 @@
- 0,
- 0,
- 0,
--0,
-+4,
- 0,
- 0,
- 0,
-@@ -985,7 +987,6 @@
- 0,
- 0,
- 0,
--64,
- 0,
- 0,
- 0,
-@@ -993,7 +994,8 @@
- 0,
- 0,
- 0,
--52,
-+0,
-+60,
- 17,
- 1,
- 0,
-@@ -1041,7 +1043,7 @@
- 0,
- 0,
- 0,
--112,
-+48,
- 12,
- 16,
- 0,
-@@ -1073,7 +1075,7 @@
- 0,
- 0,
- 0,
--4,
-+0,
- 48,
- 12,
- 0,
-@@ -1097,7 +1099,7 @@
- 0,
- 0,
- 0,
--192,
-+64,
- 0,
- 0,
- 0,
-@@ -1122,7 +1124,7 @@
- 0,
- 0,
- 0,
--3,
-+1,
- 0,
- 0,
- 0,
-@@ -1153,7 +1155,7 @@
- 0,
- 0,
- 0,
--68,
-+12,
- 48,
- 16,
- 0,
-@@ -1186,7 +1188,7 @@
- 0,
- 0,
- 48,
--13,
-+15,
- 1,
- 0,
- 0,
-@@ -1233,7 +1235,7 @@
- 0,
- 0,
- 0,
--12,
-+4,
- 16,
- 0,
- 0,
-@@ -1250,7 +1252,7 @@
- 0,
- 0,
- 0,
--3,
-+1,
- 0,
- 0,
- 0,
-@@ -1313,7 +1315,7 @@
- 0,
- 0,
- 0,
--12,
-+4,
- 51,
- 0,
- 0,
-@@ -1377,7 +1379,7 @@
- 0,
- 0,
- 0,
--48,
-+240,
- 3,
- 17,
- 0,
-@@ -1433,7 +1435,7 @@
- 0,
- 0,
- 0,
--4,
-+76,
- 0,
- 0,
- 0,
-@@ -1442,7 +1444,7 @@
- 0,
- 0,
- 0,
--192,
-+195,
- 0,
- 0,
- 0,
-@@ -1473,7 +1475,7 @@
- 0,
- 0,
- 0,
--60,
-+12,
- 60,
- 1,
- 0,
-@@ -1489,7 +1491,7 @@
- 0,
- 0,
- 0,
--64,
-+0,
- 0,
- 28,
- 0,
-@@ -1506,15 +1508,14 @@
- 0,
- 0,
- 0,
--3,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
--12,
- 0,
-+4,
- 0,
- 0,
- 0,
-@@ -1522,6 +1523,7 @@
- 0,
- 0,
- 0,
-+48,
- 0,
- 0,
- 0,
-@@ -1545,7 +1547,7 @@
- 0,
- 0,
- 0,
--192,
-+64,
- 12,
- 0,
- 0,
-@@ -1553,7 +1555,7 @@
- 0,
- 0,
- 0,
--12,
-+4,
- 48,
- 0,
- 0,
-@@ -1570,7 +1572,7 @@
- 0,
- 0,
- 0,
--1,
-+3,
- 3,
- 0,
- 0,
-@@ -1593,15 +1595,15 @@
- 0,
- 0,
- 0,
--4,
--0,
- 12,
- 0,
-+12,
- 0,
- 0,
- 0,
- 0,
- 0,
-+64,
- 0,
- 48,
- 0,
-@@ -1617,9 +1619,8 @@
- 0,
- 0,
- 0,
--48,
--204,
- 0,
-+204,
- 0,
- 0,
- 0,
-@@ -1634,7 +1635,8 @@
- 0,
- 0,
- 0,
--48,
-+12,
-+49,
- 0,
- 0,
- 0,
-@@ -1673,7 +1675,7 @@
- 0,
- 0,
- 0,
--0,
-+12,
- 0,
- 0,
- 0,
-@@ -1713,7 +1715,7 @@
- 0,
- 0,
- 0,
--112,
-+52,
- 16,
- 48,
- 0,
-@@ -1769,7 +1771,7 @@
- 0,
- 0,
- 0,
--0,
-+64,
- 0,
- 0,
- 0,
-@@ -1825,7 +1827,7 @@
- 0,
- 0,
- 0,
--0,
-+192,
- 0,
- 48,
- 0,
-@@ -1881,7 +1883,7 @@
- 0,
- 0,
- 0,
--64,
-+0,
- 192,
- 0,
- 0,
-@@ -1937,7 +1939,7 @@
- 0,
- 0,
- 0,
--192,
-+0,
- 0,
- 48,
- 0,
-@@ -1993,7 +1995,7 @@
- 0,
- 0,
- 0,
--192,
-+64,
- 0,
- 0,
- 0,
-@@ -2049,7 +2051,7 @@
- 0,
- 0,
- 0,
--64,
-+48,
- 12,
- 51,
- 0,
-@@ -2105,7 +2107,7 @@
- 0,
- 0,
- 0,
--64,
-+0,
- 0,
- 0,
- 0,
-@@ -2145,7 +2147,7 @@
- 0,
- 0,
- 192,
--48,
-+0,
- 192,
- 3,
- 0,
-@@ -2161,7 +2163,7 @@
- 0,
- 0,
- 0,
--192,
-+64,
- 0,
- 48,
- 0,
-@@ -2193,7 +2195,7 @@
- 0,
- 0,
- 192,
--0,
-+48,
- 52,
- 0,
- 0,
-@@ -2217,7 +2219,7 @@
- 0,
- 0,
- 0,
--64,
-+192,
- 0,
- 12,
- 0,
-@@ -2241,7 +2243,7 @@
- 0,
- 0,
- 192,
--48,
-+0,
- 0,
- 3,
- 0,
-@@ -2273,7 +2275,7 @@
- 0,
- 0,
- 0,
--0,
-+192,
- 48,
- 48,
- 0,
-@@ -2329,7 +2331,7 @@
- 0,
- 0,
- 0,
--0,
-+64,
- 0,
- 0,
- 0,
-@@ -2337,7 +2339,7 @@
- 0,
- 0,
- 192,
--0,
-+48,
- 12,
- 3,
- 0,
-@@ -2385,7 +2387,7 @@
- 0,
- 0,
- 192,
--112,
-+64,
- 0,
- 48,
- 0,
-@@ -2441,7 +2443,7 @@
- 0,
- 0,
- 0,
--0,
-+192,
- 0,
- 0,
- 0,
-@@ -2497,7 +2499,7 @@
- 0,
- 0,
- 0,
--0,
-+64,
- 192,
- 48,
- 0,
-@@ -2665,7 +2667,7 @@
- 0,
- 0,
- 0,
--0,
-+64,
- 0,
- 0,
- 0,
-@@ -2817,7 +2819,7 @@
- 0,
- 0,
- 0,
--0,
-+48,
- 0,
- 1,
- 0,
-@@ -2865,7 +2867,7 @@
- 0,
- 0,
- 0,
--48,
-+0,
- 0,
- 0,
- 0,
-@@ -3009,7 +3011,7 @@
- 0,
- 0,
- 0,
--0,
-+48,
- 0,
- 0,
- 0,
-@@ -3057,7 +3059,7 @@
- 0,
- 0,
- 0,
--48,
-+0,
- 12,
- 16,
- 0,
-@@ -3201,7 +3203,7 @@
- 0,
- 0,
- 0,
--0,
-+48,
- 68,
- 3,
- 0,
-@@ -3297,10 +3299,9 @@
- 0,
- 0,
- 0,
--48,
- 0,
--3,
- 0,
-+3,
- 0,
- 0,
- 0,
-@@ -3346,6 +3347,7 @@
- 0,
- 0,
- 0,
-+48,
- 12,
- 0,
- 0,
-@@ -3393,7 +3395,7 @@
- 0,
- 0,
- 0,
--1,
-+0,
- 0,
- 3,
- 0,
-@@ -3425,7 +3427,6 @@
- 0,
- 0,
- 0,
--3,
- 0,
- 0,
- 0,
-@@ -3441,7 +3442,8 @@
- 0,
- 0,
- 0,
--48,
-+0,
-+0,
- 0,
- 0,
- 0,
-@@ -3521,7 +3523,7 @@
- 0,
- 0,
- 0,
--3,
-+1,
- 0,
- 0,
- 0,
-@@ -3553,7 +3555,7 @@
- 0,
- 0,
- 0,
--1,
-+3,
- 192,
- 0,
- 0,
-@@ -3617,8 +3619,7 @@
- 0,
- 0,
- 0,
--3,
--0,
-+1,
- 0,
- 0,
- 0,
-@@ -3634,6 +3635,7 @@
- 0,
- 0,
- 0,
-+48,
- 12,
- 0,
- 0,
-@@ -3649,7 +3651,7 @@
- 0,
- 0,
- 0,
--1,
-+3,
- 0,
- 0,
- 0,
-@@ -3681,7 +3683,7 @@
- 0,
- 0,
- 0,
--51,
-+3,
- 0,
- 1,
- 0,
-@@ -3713,7 +3715,7 @@
- 0,
- 0,
- 0,
--3,
-+1,
- 0,
- 0,
- 0,
-@@ -3777,7 +3779,7 @@
- 0,
- 0,
- 0,
--49,
-+51,
- 12,
- 1,
- 0,
-@@ -3809,7 +3811,7 @@
- 0,
- 0,
- 0,
--0,
-+3,
- 0,
- 0,
- 0,
-@@ -3841,7 +3843,7 @@
- 0,
- 0,
- 0,
--0,
-+1,
- 0,
- 0,
- 0,
-@@ -3921,7 +3923,7 @@
- 0,
- 0,
- 0,
--0,
-+48,
- 4,
- 0,
- 0,
-@@ -3969,7 +3971,7 @@
- 0,
- 0,
- 0,
--48,
-+0,
- 0,
- 1,
- 0,
-@@ -4113,6 +4115,7 @@
- 0,
- 0,
- 0,
-+48,
- 0,
- 0,
- 0,
-@@ -4161,7 +4164,6 @@
- 0,
- 0,
- 0,
--16,
- 0,
- 3,
- 0,
-@@ -4209,6 +4211,7 @@
- 0,
- 0,
- 0,
-+16,
- 0,
- 0,
- 0,
-@@ -4257,7 +4260,6 @@
- 0,
- 0,
- 0,
--48,
- 192,
- 3,
- 0,
-@@ -4353,8 +4355,7 @@
- 0,
- 0,
- 0,
--16,
--0,
-+48,
- 0,
- 0,
- 0,
-@@ -4402,6 +4403,7 @@
- 0,
- 0,
- 0,
-+16,
- 0,
- 0,
- 0,
-@@ -4497,7 +4499,7 @@
- 0,
- 0,
- 0,
--48,
-+0,
- 0,
- 0,
- 0,
-@@ -4689,7 +4691,7 @@
- 0,
- 0,
- 0,
--16,
-+48,
- 0,
- 0,
- 0,
-@@ -4737,7 +4739,7 @@
- 0,
- 0,
- 0,
--48,
-+16,
- 0,
- 3,
- 0,
-@@ -4881,7 +4883,7 @@
- 0,
- 0,
- 0,
--0,
-+48,
- 0,
- 0,
- 0,
-@@ -4977,7 +4979,6 @@
- 0,
- 0,
- 0,
--48,
- 0,
- 0,
- 0,
-@@ -5026,6 +5027,7 @@
- 0,
- 0,
- 0,
-+48,
- 0,
- 3,
- 0,
-@@ -5073,7 +5075,6 @@
- 0,
- 0,
- 0,
--48,
- 0,
- 0,
- 0,
-@@ -5121,7 +5122,8 @@
- 0,
- 0,
- 0,
--16,
-+0,
-+48,
- 0,
- 3,
- 0,
-@@ -5217,12 +5219,60 @@
- 0,
- 0,
- 0,
--0,
-+16,
- 0,
- 3,
- 0,
- 0,
- 0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
-+0,
- 0,
- 0,
- 0,
---- a/devices/vector/gdevpdtv.h
-+++ b/devices/vector/gdevpdtv.h
-@@ -12,7 +12,6 @@
-    Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
-    CA 94945, U.S.A., +1(415)492-9861, for further information.
- */
--
- /*
-  * This file contains substantial parts of toolbin/encs2c.ps,
-  * which generated the remainder of the file mechanically from
-@@ -23,12 +22,15 @@
-  * This source file is maintained manually under source code control,
-  * however its content should be regenerated by using encs2c.ps
-  * if changes are required.
-+ * You should not manually alter this file! If you regenerate it using
-+ * encs2c.ps you must regenerate all 4 files; base/gscedata.[c|h]
-+ * and devices/vector/gdevpdtv.[c|h]
-  */
- 
- #ifndef gdevpdtv_INCLUDED
- #define gdevpdtv_INCLUDED
- 
--#define GS_C_PDF_MAX_GOOD_GLYPH 21894
-+#define GS_C_PDF_MAX_GOOD_GLYPH 22086
- #define GS_C_PDF_GOOD_GLYPH_MASK 1
- #define GS_C_PDF_GOOD_NON_SYMBOL_MASK 2
- 
---- a/toolbin/encs2c.ps
-+++ b/toolbin/encs2c.ps
-@@ -20,6 +20,11 @@
- % for reference.  It reads in the known Encodings and pseudo-Encodings
- % (see the definition of /encfiles below) and generates C files that
- % represent them in a compact format described in src/gscencs.c.
-+%
-+% If this file does need to be run again (and it has been in December 2020) you
-+% should generate all 4 files (gscedata.[c,h] and gdevpdtv.[c,h] by running this
-+% program 4 times, once for each output file.
-+%
- % The generated files are named gscedata.[ch] and are included in the
- % source distribution in the src directory.  The canonical invocation is
- %   gs -dNOSAFER -Ilib -dNODISPLAY -q -SO=gscedata.h toolbin/encs2c.ps > src/gscedata.h
-@@ -324,20 +329,24 @@
-   dup 1 eq { exit } if exch 1 add exch -1 bitshift
- } loop pop def
- 
-+
-+
- % Write the initial boilerplate.
- (/* Copyright (C) 2001-2021 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 that) =
--(   license.  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.) =
-+(   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.) =
-+
- (*/) =
--(/* $) print (Id: ) print O print ( $) print ( */) =
- (/*) =
- ( * This file contains substantial parts of toolbin/encs2c.ps,) =
- ( * which generated the remainder of the file mechanically from) =
-@@ -348,6 +357,9 @@
- ( * This source file is maintained manually under source code control,) =
- ( * however its content should be regenerated by using encs2c.ps) =
- ( * if changes are required.) =
-+( * You should not manually alter this file! If you regenerate it using) =
-+( * encs2c.ps you must regenerate all 4 files; base/gscedata.[c|h]) =
-+( * and devices/vector/gdevpdtv.[c|h]) =
- ( */) =
- () =
- 
diff -pruN 9.55.0~dfsg-3/debian/patches/020211029~8f62213.patch 9.56.1~dfsg-1/debian/patches/020211029~8f62213.patch
--- 9.55.0~dfsg-3/debian/patches/020211029~8f62213.patch	2021-11-19 16:46:51.000000000 +0000
+++ 9.56.1~dfsg-1/debian/patches/020211029~8f62213.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,41 +0,0 @@
-Description: correct the 2-byte Unicode test in pdfwrite
- Commit  b4e8434defb8e05ea05bb130b92217290efd2fba
- fixed the generation of the required tables, but....
- .
- The test against TwoByteToUnicode
- in pdf_simple_font_needs_ToUnicode() was inverted.
- If the font has a ToUnicode CMap and uses glyphs from that
- CMap which require more than 2 bytes to represent,
- then we should not emit a ToUnicode CMap,
- because we can't correctly do so.
- .
- However the code was returning 'true'
- which meant that it would try to write out a ToUnicode CMap;
- exactly the opposite of what was intended.
- .
- Fixing that resolves the problem
- when both quoteright and ffi or similar ligatures are used
- in the same file.
- .
- We still cannot preserve ToUnicode CMap information
- which requires more than 2 bytes to represent.
- But this is an enhancement, see bug #704674.
-Origin: upstream, https://git.ghostscript.com/?p=ghostpdl.git;h=8f62213
-Author: Ken Sharp <ken.sharp@artifex.com>
-Bug: https://bugs.ghostscript.com/show_bug.cgi?id=704478
-Bug: https://bugs.ghostscript.com/show_bug.cgi?id=704674
-Bug-Debian: https://bugs.debian.org/998461
-Last-Update: 2021-11-19
----
-This patch header follows DEP-3: https://dep.debian.net/deps/dep3/
---- a/devices/vector/gdevpdtw.c
-+++ b/devices/vector/gdevpdtw.c
-@@ -120,7 +120,7 @@
-         */
-         return true;
-     if (!pdfont->TwoByteToUnicode)
--        return true;
-+        return false;
- 
-     for (ch = 0; ch < 256; ++ch) {
-         pdf_encoding_element_t *pet = &pdfont->u.simple.Encoding[ch];
diff -pruN 9.55.0~dfsg-3/debian/patches/020211110~d468a90.patch 9.56.1~dfsg-1/debian/patches/020211110~d468a90.patch
--- 9.55.0~dfsg-3/debian/patches/020211110~d468a90.patch	2021-11-19 16:19:35.000000000 +0000
+++ 9.56.1~dfsg-1/debian/patches/020211110~d468a90.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,23 +0,0 @@
-Description: avoid freeing the background pixmap created by gv
- Because it might free a pixmap
- that was created by gv as a backing pixmap
- and might still be used by gv for further drawing.
-Origin: upstream, https://git.ghostscript.com/?p=ghostpdl.git;h=d468a90
-Author: Florian Lindemann <linde@bfw-online.de>
-Bug: https://bugs.ghostscript.com/show_bug.cgi?id=704709
-Bug-Debian: https://bugs.debian.org/998888
-Last-Update: 2021-11-19
----
-This patch header follows DEP-3: https://dep.debian.net/deps/dep3/
---- a/devices/gdevxini.c
-+++ b/devices/gdevxini.c
-@@ -984,7 +984,8 @@
-         xdev->gc = NULL;
-     }
-     if (xdev->dpy && xdev->bpixmap != (Pixmap)0) {
--        XFreePixmap(xdev->dpy, xdev->bpixmap);
-+        /* Only free the pixmap if we created it */
-+        if (xdev->ghostview != 0) XFreePixmap(xdev->dpy, xdev->bpixmap);
-         xdev->bpixmap = (Pixmap)0;
-         xdev->dest = (Pixmap)0;
-     }
diff -pruN 9.55.0~dfsg-3/debian/patches/020211129~30c7908.patch 9.56.1~dfsg-1/debian/patches/020211129~30c7908.patch
--- 9.55.0~dfsg-3/debian/patches/020211129~30c7908.patch	2021-11-30 14:45:28.000000000 +0000
+++ 9.56.1~dfsg-1/debian/patches/020211129~30c7908.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,23 +0,0 @@
-Description: Fix the logic for freeing X pixmap
- Correct my mistake applying the patch in:
- d468a903763147c0fcbeb4eef4df0158a605ff7e
- .
- Apologies to Florian Lindemann for messing up his fix.
-Origin: upstream, https://git.ghostscript.com/?p=ghostpdl.git;h=30c7908
-Author: Chris Liddell <chris.liddell@artifex.com>
-Bug: https://bugs.ghostscript.com/show_bug.cgi?id=704709
-Bug-Debian: https://bugs.debian.org/998888
-Last-Update: 2021-11-30
----
-This patch header follows DEP-3: https://dep.debian.net/deps/dep3/
---- a/devices/gdevxini.c
-+++ b/devices/gdevxini.c
-@@ -985,7 +985,7 @@
-     }
-     if (xdev->dpy && xdev->bpixmap != (Pixmap)0) {
-         /* Only free the pixmap if we created it */
--        if (xdev->ghostview != 0) XFreePixmap(xdev->dpy, xdev->bpixmap);
-+        if (xdev->ghostview == 0) XFreePixmap(xdev->dpy, xdev->bpixmap);
-         xdev->bpixmap = (Pixmap)0;
-         xdev->dest = (Pixmap)0;
-     }
diff -pruN 9.55.0~dfsg-3/debian/patches/1003_lcms2_typo.patch 9.56.1~dfsg-1/debian/patches/1003_lcms2_typo.patch
--- 9.55.0~dfsg-3/debian/patches/1003_lcms2_typo.patch	2021-09-18 12:29:48.000000000 +0000
+++ 9.56.1~dfsg-1/debian/patches/1003_lcms2_typo.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,17 +0,0 @@
-Description: Fix typo linking with lcms2
-Author: Jonas Smedegaard <dr@jones.dk>
-Bug: https://bugs.ghostscript.com/show_bug.cgi?id=704405
-Last-Update: 2021-09-18
----
-This patch header follows DEP-3: https://dep.debian.net/deps/dep3/
---- a/base/gsicc_lcms2.c
-+++ b/base/gsicc_lcms2.c
-@@ -462,7 +462,7 @@
- gscms_transform_color(gx_device *dev, gsicc_link_t *icclink, void *inputcolor,
-                              void *outputcolor, int num_bytes)
- {
--    return gscms_transformm_color_const(dev, icclink, inputcolor, outputcolor, num_bytes);
-+    return gscms_transform_color_const(dev, icclink, inputcolor, outputcolor, num_bytes);
- }
- 
- int
diff -pruN 9.55.0~dfsg-3/debian/patches/1004_enable_spot_devices.patch 9.56.1~dfsg-1/debian/patches/1004_enable_spot_devices.patch
--- 9.55.0~dfsg-3/debian/patches/1004_enable_spot_devices.patch	2021-09-18 12:04:10.000000000 +0000
+++ 9.56.1~dfsg-1/debian/patches/1004_enable_spot_devices.patch	2022-03-07 20:33:22.000000000 +0000
@@ -5,7 +5,7 @@ Last-Update: 2021-07-18
 This patch header follows DEP-3: https://dep.debian.net/deps/dep3/
 --- a/configure.ac
 +++ b/configure.ac
-@@ -2656,7 +2656,7 @@
+@@ -2642,7 +2642,7 @@
  AC_SUBST(EXTRACT_DIR)
  
  # the "display" device isn't an ideal fit in the list below, but it saves adding a "list" for just that one entry
diff -pruN 9.55.0~dfsg-3/debian/patches/2007_suggest_install_ghostscript-doc_in_code.patch 9.56.1~dfsg-1/debian/patches/2007_suggest_install_ghostscript-doc_in_code.patch
--- 9.55.0~dfsg-3/debian/patches/2007_suggest_install_ghostscript-doc_in_code.patch	2021-05-25 15:22:09.000000000 +0000
+++ 9.56.1~dfsg-1/debian/patches/2007_suggest_install_ghostscript-doc_in_code.patch	2022-03-07 20:33:30.000000000 +0000
@@ -5,7 +5,7 @@ Last-Update: 2012-07-24
 This patch header follows DEP-3: https://dep.debian.net/deps/dep3/
 --- a/psi/imainarg.c
 +++ b/psi/imainarg.c
-@@ -1260,6 +1260,7 @@
+@@ -1263,6 +1263,7 @@
  #endif
  static const char help_trailer[] = "\
  For more information, see %s.\n\
diff -pruN 9.55.0~dfsg-3/debian/patches/2009_use_system_javascript.patch 9.56.1~dfsg-1/debian/patches/2009_use_system_javascript.patch
--- 9.55.0~dfsg-3/debian/patches/2009_use_system_javascript.patch	2021-09-27 12:34:24.000000000 +0000
+++ 9.56.1~dfsg-1/debian/patches/2009_use_system_javascript.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,335 +0,0 @@
-Description: Use locally served jquery to avoid privacy breach
-Author: Jonas Smedegaard <dr@jones.dk>
-Last-Update: 2018-09-14
----
-This patch header follows DEP-3: https://dep.debian.net/deps/dep3/
---- a/doc/API.htm
-+++ b/doc/API.htm
-@@ -1804,7 +1804,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/C-style.htm
-+++ b/doc/C-style.htm
-@@ -1642,7 +1642,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Commprod.htm
-+++ b/doc/Commprod.htm
-@@ -312,7 +312,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/DLL.htm
-+++ b/doc/DLL.htm
-@@ -799,7 +799,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Deprecated.htm
-+++ b/doc/Deprecated.htm
-@@ -5796,7 +5796,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Develop.htm
-+++ b/doc/Develop.htm
-@@ -4788,7 +4788,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Devices.htm
-+++ b/doc/Devices.htm
-@@ -2204,7 +2204,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Drivers.htm
-+++ b/doc/Drivers.htm
-@@ -3406,7 +3406,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Fonts.htm
-+++ b/doc/Fonts.htm
-@@ -393,7 +393,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/History9.htm
-+++ b/doc/History9.htm
-@@ -294,7 +294,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Install.htm
-+++ b/doc/Install.htm
-@@ -463,7 +463,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Language.htm
-+++ b/doc/Language.htm
-@@ -2342,7 +2342,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Lib.htm
-+++ b/doc/Lib.htm
-@@ -1071,7 +1071,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Make.htm
-+++ b/doc/Make.htm
-@@ -1495,7 +1495,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/News.htm
-+++ b/doc/News.htm
-@@ -262,7 +262,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Ps-style.htm
-+++ b/doc/Ps-style.htm
-@@ -572,7 +572,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Ps2epsi.htm
-+++ b/doc/Ps2epsi.htm
-@@ -252,7 +252,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Psfiles.htm
-+++ b/doc/Psfiles.htm
-@@ -917,7 +917,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Readme.htm
-+++ b/doc/Readme.htm
-@@ -602,7 +602,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Release.htm
-+++ b/doc/Release.htm
-@@ -868,7 +868,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/SavedPages.htm
-+++ b/doc/SavedPages.htm
-@@ -340,7 +340,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Source.htm
-+++ b/doc/Source.htm
-@@ -489,7 +489,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Unix-lpr.htm
-+++ b/doc/Unix-lpr.htm
-@@ -328,7 +328,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Use.htm
-+++ b/doc/Use.htm
-@@ -5442,7 +5442,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/VectorDevices.htm
-+++ b/doc/VectorDevices.htm
-@@ -1518,7 +1518,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/WhatIsGS.htm
-+++ b/doc/WhatIsGS.htm
-@@ -209,7 +209,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/sample_downscale_device.htm
-+++ b/doc/sample_downscale_device.htm
-@@ -285,7 +285,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/subclass.htm
-+++ b/doc/subclass.htm
-@@ -773,7 +773,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/thirdparty.htm
-+++ b/doc/thirdparty.htm
-@@ -256,7 +256,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
---- a/doc/Internal.htm
-+++ b/doc/Internal.htm
-@@ -141,7 +141,7 @@
-           </div>
-     </div>
- 
--    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-+    <script src="jquery.min.js"></script>
-     <script src="index.js"></script>
- </body>
- </html>
diff -pruN 9.55.0~dfsg-3/debian/patches/2010_add_build_timestamp_setting.patch 9.56.1~dfsg-1/debian/patches/2010_add_build_timestamp_setting.patch
--- 9.55.0~dfsg-3/debian/patches/2010_add_build_timestamp_setting.patch	2021-09-16 14:32:27.000000000 +0000
+++ 9.56.1~dfsg-1/debian/patches/2010_add_build_timestamp_setting.patch	2022-03-07 20:36:40.000000000 +0000
@@ -96,7 +96,7 @@ This patch header follows DEP-3: https:/
 +    t = secs_ns[0];
      tms = *localtime(&t);
  #endif
-     gs_sprintf(buf1,
+     gs_snprintf(buf1, sizeof(buf1),
 --- a/devices/vector/gdevpsu.c
 +++ b/devices/vector/gdevpsu.c
 @@ -183,6 +183,7 @@
diff -pruN 9.55.0~dfsg-3/debian/patches/2011_avoid_remote_font.patch 9.56.1~dfsg-1/debian/patches/2011_avoid_remote_font.patch
--- 9.55.0~dfsg-3/debian/patches/2011_avoid_remote_font.patch	2021-05-25 15:22:09.000000000 +0000
+++ 9.56.1~dfsg-1/debian/patches/2011_avoid_remote_font.patch	2022-03-07 20:43:45.000000000 +0000
@@ -8,318 +8,450 @@ This patch header follows DEP-3: https:/
 +++ b/doc/API.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>The Ghostscript Interpreter Application Programming Interface (API)</title>
-     <!-- Supercedes the API in DLL.htm -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>The Ghostscript Interpreter Application Programming Interface (API)</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/C-style.htm
 +++ b/doc/C-style.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Ghostscript C Coding Guidelines</title>
-     <!-- Originally: c-style.txt -->
---- a/doc/Commprod.htm
-+++ b/doc/Commprod.htm
-@@ -3,7 +3,7 @@
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Conditions on Distributing Ghostscript in a Commercial Context</title>
-     <!-- Originally: commprod.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Ghostscript C Coding Guidelines</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/DLL.htm
 +++ b/doc/DLL.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>How to Use the Ghostscript Dynamic Link Library (DLL)</title>
-     <!-- Originally: dll.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>How to Use the Ghostscript Dynamic Link Library (DLL)</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Deprecated.htm
 +++ b/doc/Deprecated.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Deprecated Ghostscript Features</title>
- <link href="style.css" rel="stylesheet" type="text/css">
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Deprecated Ghostscript Features</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Develop.htm
 +++ b/doc/Develop.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Information for Ghostscript Developers</title>
- <link href="style.css" rel="stylesheet" type="text/css">
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Information for Ghostscript Developers</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Devices.htm
 +++ b/doc/Devices.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Details of Ghostscript Output Devices</title>
-     <!-- Originally: devices.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Details of Ghostscript Output Devices</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Drivers.htm
 +++ b/doc/Drivers.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>The Interface between Ghostscript and Device Drivers</title>
-     <!-- Originally: drivers.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>The Interface between Ghostscript and Device Drivers</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Fonts.htm
 +++ b/doc/Fonts.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Fonts and Font Facilities Supplied with Ghostscript</title>
-     <!-- Originally: fonts.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Fonts and Font Facilities Supplied with Ghostscript</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/History9.htm
 +++ b/doc/History9.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>History of Ghostscript versions 9.n</title>
-     <!--
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>History of Ghostscript versions 9.n</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Install.htm
 +++ b/doc/Install.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>How to Install Ghostscript</title>
-     <!-- Originally: install.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>How to Install Ghostscript</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Language.htm
 +++ b/doc/Language.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Ghostscript and the PostScript Language</title>
-     <!-- Originally: language.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Ghostscript and the PostScript Language</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Lib.htm
 +++ b/doc/Lib.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>The Ghostscript Library</title>
-     <!-- Originally: lib.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>The Ghostscript Library</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Make.htm
 +++ b/doc/Make.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>How to Build Ghostscript from Source Code</title>
-     <!-- Originally: make.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>How to Build Ghostscript from Source Code</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/News.htm
 +++ b/doc/News.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Recent Changes in Ghostscript</title>
-     <!-- Originally: NEWS -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Recent Changes in Ghostscript</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Ps-style.htm
 +++ b/doc/Ps-style.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Ghostscript PostScript Coding Guidelines</title>
- <link href="style.css" rel="stylesheet" type="text/css">
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Ghostscript PostScript Coding Guidelines</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Ps2epsi.htm
 +++ b/doc/Ps2epsi.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Convert PostScript to Encapsulated PostScript Interchange Format</title>
-     <!-- Originally: ps2epsi.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Convert PostScript to Encapsulated PostScript Interchange Format</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Psfiles.htm
 +++ b/doc/Psfiles.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>PostScript Files Distributed with Ghostscript</title>
-     <!-- Originally: psfiles.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>PostScript Files Distributed with Ghostscript</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Readme.htm
 +++ b/doc/Readme.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Overview of Ghostscript</title>
-     <!-- Originally: README -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Overview of Ghostscript</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Release.htm
 +++ b/doc/Release.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>How to Prepare a Ghostscript Release</title>
-     <!-- Originally: make.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>How to Prepare a Ghostscript Release</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/SavedPages.htm
 +++ b/doc/SavedPages.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Ghostscript: Sample CMYK 32-bit Device that Supports Post Rendering Processing</title>
- <link href="style.css" rel="stylesheet" type="text/css">
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Using Saved Pages ( --saved-pages=... )</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Source.htm
 +++ b/doc/Source.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Guide to Ghostscript Source Code</title>
-     <!-- Originally: source.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Guide to Ghostscript Source Code</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Unix-lpr.htm
 +++ b/doc/Unix-lpr.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Setting Up a Unix 1pr Filter for Ghostscript</title>
-     <!-- Originally: unix-lpr.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Setting Up a Unix 1pr Filter for Ghostscript</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/Use.htm
 +++ b/doc/Use.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>How to Use Ghostscript</title>
-     <!-- Originally: use.txt -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>How to Use Ghostscript</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/VectorDevices.htm
 +++ b/doc/VectorDevices.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>High Level Output Devices</title>
- <link href="style.css" rel="stylesheet" type="text/css">
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>High Level Output Devices</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/WhatIsGS.htm
 +++ b/doc/WhatIsGS.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Ghostscript: What Is Ghostscript?</title>
- <link href="style.css" rel="stylesheet" type="text/css">
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Ghostscript: What Is Ghostscript?</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/sample_downscale_device.htm
 +++ b/doc/sample_downscale_device.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Ghostscript: Sample CMYK 32-bit Device that Supports Post Rendering Processing</title>
- <link href="style.css" rel="stylesheet" type="text/css">
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Ghostscript: Sample CMYK 32-bit Device that Supports Post Rendering Processing</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/subclass.htm
 +++ b/doc/subclass.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Ghostscript: Device Subclassing</title>
- <link href="style.css" rel="stylesheet" type="text/css">
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Ghostscript: Device Subclassing</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
 --- a/doc/thirdparty.htm
 +++ b/doc/thirdparty.htm
 @@ -3,7 +3,7 @@
  <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
--<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
-+<!-- link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet" -->
- <link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
- <title>Third Party Libraries Used by Ghostscript and GhostPDL</title>
-     <!-- Originally: NEWS -->
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Third Party Libraries Used by Ghostscript and GhostPDL</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
+--- a/doc/GPDL.htm
++++ b/doc/GPDL.htm
+@@ -3,7 +3,7 @@
+ <head>
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>The GhostPDL Interpreter Framework</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
+--- a/doc/Internal.htm
++++ b/doc/Internal.htm
+@@ -3,7 +3,7 @@
+ <head>
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Ghostscript Internal Operators</title>
+     <link href="default.css" rel="stylesheet" type="text/css">
+--- a/doc/Search.htm
++++ b/doc/Search.htm
+@@ -3,7 +3,7 @@
+ <head>
+     <meta http-equiv="content-type" content="text/html; charset=utf-8">
+     <meta name="viewport" content="user-scalable=yes, initial-scale=1, width=device-width">
+-    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++    <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+     <link rel="shortcut icon" href="images/favicon.svg">
+     <title>Search Ghostscript.com</title>
+     <!-- note: we can't use default.css as some of the CSS there conflicts with Google's injected CSS -->
+--- a/doc/language-bindings/c-sharp-ghost-api.html
++++ b/doc/language-bindings/c-sharp-ghost-api.html
+@@ -3,7 +3,7 @@
+         <title>Ghostscript language bindings</title>
+         <meta http-equiv="content-type" content="text/html; charset=utf-8">
+         <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
+-        <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++        <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+         <link rel="shortcut icon" href="../images/favicon.svg">
+         <link href="css/default.css" rel="stylesheet" type="text/css" />
+         <script type="text/javascript" src="js/default.js"></script>
+--- a/doc/language-bindings/c-sharp-ghost-mono.html
++++ b/doc/language-bindings/c-sharp-ghost-mono.html
+@@ -3,7 +3,7 @@
+         <title>Ghostscript language bindings</title>
+         <meta http-equiv="content-type" content="text/html; charset=utf-8">
+         <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
+-        <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++        <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+         <link rel="shortcut icon" href="../images/favicon.svg">
+         <link href="css/default.css" rel="stylesheet" type="text/css" />
+         <script type="text/javascript" src="js/default.js"></script>
+--- a/doc/language-bindings/c-sharp-ghost-net.html
++++ b/doc/language-bindings/c-sharp-ghost-net.html
+@@ -3,7 +3,7 @@
+         <title>Ghostscript language bindings</title>
+         <meta http-equiv="content-type" content="text/html; charset=utf-8">
+         <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
+-        <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++        <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+         <link rel="shortcut icon" href="../images/favicon.svg">
+         <link href="css/default.css" rel="stylesheet" type="text/css" />
+         <script type="text/javascript" src="js/default.js"></script>
+--- a/doc/language-bindings/c-sharp-intro.html
++++ b/doc/language-bindings/c-sharp-intro.html
+@@ -3,7 +3,7 @@
+         <title>Ghostscript language bindings</title>
+         <meta http-equiv="content-type" content="text/html; charset=utf-8">
+         <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
+-        <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++        <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+         <link rel="shortcut icon" href="../images/favicon.svg">
+         <link href="css/default.css" rel="stylesheet" type="text/css" />
+         <script type="text/javascript" src="js/default.js"></script>
+--- a/doc/language-bindings/demo-code.html
++++ b/doc/language-bindings/demo-code.html
+@@ -3,7 +3,7 @@
+         <title>Ghostscript language bindings</title>
+         <meta http-equiv="content-type" content="text/html; charset=utf-8">
+         <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
+-        <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++        <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+         <link rel="shortcut icon" href="../images/favicon.svg">
+         <link href="css/default.css" rel="stylesheet" type="text/css" />
+         <script type="text/javascript" src="js/default.js"></script>
+--- a/doc/language-bindings/index.html
++++ b/doc/language-bindings/index.html
+@@ -3,7 +3,7 @@
+         <title>Ghostscript language bindings</title>
+         <meta http-equiv="content-type" content="text/html; charset=utf-8">
+         <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
+-        <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++        <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+         <link rel="shortcut icon" href="../images/favicon.svg">
+         <link href="css/default.css" rel="stylesheet" type="text/css" />
+         <script type="text/javascript" src="js/default.js"></script>
+--- a/doc/language-bindings/java-gsjavajar.html
++++ b/doc/language-bindings/java-gsjavajar.html
+@@ -3,7 +3,7 @@
+         <title>Ghostscript language bindings</title>
+         <meta http-equiv="content-type" content="text/html; charset=utf-8">
+         <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
+-        <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++        <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+         <link rel="shortcut icon" href="../images/favicon.svg">
+         <link href="css/default.css" rel="stylesheet" type="text/css" />
+         <script type="text/javascript" src="js/default.js"></script>
+--- a/doc/language-bindings/java-intro.html
++++ b/doc/language-bindings/java-intro.html
+@@ -3,7 +3,7 @@
+         <title>Ghostscript language bindings</title>
+         <meta http-equiv="content-type" content="text/html; charset=utf-8">
+         <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
+-        <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++        <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+         <link rel="shortcut icon" href="../images/favicon.svg">
+         <link href="css/default.css" rel="stylesheet" type="text/css" />
+         <script type="text/javascript" src="js/default.js"></script>
+--- a/doc/language-bindings/python-gsapi.html
++++ b/doc/language-bindings/python-gsapi.html
+@@ -3,7 +3,7 @@
+         <title>Ghostscript language bindings</title>
+         <meta http-equiv="content-type" content="text/html; charset=utf-8">
+         <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
+-        <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++        <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+         <link rel="shortcut icon" href="../images/favicon.svg">
+         <link href="css/default.css" rel="stylesheet" type="text/css" />
+         <script type="text/javascript" src="js/default.js"></script>
+--- a/doc/language-bindings/python-intro.html
++++ b/doc/language-bindings/python-intro.html
+@@ -3,7 +3,7 @@
+         <title>Ghostscript language bindings</title>
+         <meta http-equiv="content-type" content="text/html; charset=utf-8">
+         <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
+-        <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet">
++        <!-- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,200i,300,300i,400,400i,600,600i,700,700i,900,900i" rel="stylesheet"> -->
+         <link rel="shortcut icon" href="../images/favicon.svg">
+         <link href="css/default.css" rel="stylesheet" type="text/css" />
+         <script type="text/javascript" src="js/default.js"></script>
diff -pruN 9.55.0~dfsg-3/debian/patches/series 9.56.1~dfsg-1/debian/patches/series
--- 9.55.0~dfsg-3/debian/patches/series	2021-11-30 14:43:10.000000000 +0000
+++ 9.56.1~dfsg-1/debian/patches/series	2022-04-20 20:31:45.000000000 +0000
@@ -1,10 +1,4 @@
-020211007~d9d8db2.patch
-020211025~b4e8434.patch
-020211029~8f62213.patch
-020211110~d468a90.patch
-020211129~30c7908.patch
 1001_cross.patch
-1003_lcms2_typo.patch
 1004_enable_spot_devices.patch
 2001_docdir_fix_for_debian.patch
 2002_gs_man_fix_debian.patch
@@ -14,6 +8,5 @@
 2006_suggest_install_ghostscript-doc_in_docs.patch
 2007_suggest_install_ghostscript-doc_in_code.patch
 2008_mention_ghostscript-x_in_docs.patch
-2009_use_system_javascript.patch
 2010_add_build_timestamp_setting.patch
 2011_avoid_remote_font.patch
diff -pruN 9.55.0~dfsg-3/debian/source/lintian-overrides 9.56.1~dfsg-1/debian/source/lintian-overrides
--- 9.55.0~dfsg-3/debian/source/lintian-overrides	2021-09-27 12:41:03.000000000 +0000
+++ 9.56.1~dfsg-1/debian/source/lintian-overrides	2022-03-21 10:51:28.000000000 +0000
@@ -1,17 +1,15 @@
 # License is in Reference field (see bug#786450)
-missing-license-paragraph-in-dep5-copyright debian/copyright agpl-3\+ *
-missing-license-paragraph-in-dep5-copyright debian/copyright apache-2\.0 *
-missing-license-paragraph-in-dep5-copyright debian/copyright gpl *
-missing-license-paragraph-in-dep5-copyright debian/copyright gpl-2\+ *
-missing-license-paragraph-in-dep5-copyright debian/copyright gpl-3\+ *
-missing-license-paragraph-in-dep5-copyright debian/copyright lgpl-2\.1 *
-missing-license-paragraph-in-dep5-copyright debian/copyright lgpl-2\.1\+ *
-missing-license-text-in-dep5-copyright debian/copyright Apache-2\.0 *
-missing-license-text-in-dep5-copyright debian/copyright GPL *
-missing-license-text-in-dep5-copyright debian/copyright GPL-2\+ *
-missing-license-text-in-dep5-copyright debian/copyright GPL-3\+ *
-missing-license-text-in-dep5-copyright debian/copyright LGPL-2\.1 *
-missing-license-text-in-dep5-copyright debian/copyright LGPL-2\.1\+ *
+missing-license-paragraph-in-dep5-copyright agpl-3\+ *
+missing-license-paragraph-in-dep5-copyright apache-2\.0 *
+missing-license-paragraph-in-dep5-copyright gpl-1\+ *
+missing-license-paragraph-in-dep5-copyright gpl-2\+ *
+missing-license-paragraph-in-dep5-copyright gpl-3\+ *
+missing-license-paragraph-in-dep5-copyright lgpl-2\.1 *
+missing-license-text-in-dep5-copyright Apache-2\.0 *
+missing-license-text-in-dep5-copyright GPL-1\+ *
+missing-license-text-in-dep5-copyright GPL-2\+ *
+missing-license-text-in-dep5-copyright GPL-3\+ *
+missing-license-text-in-dep5-copyright LGPL-2\.1 *
 
 # License "none" are files without license (and not needing a license)
-missing-license-paragraph-in-dep5-copyright debian/copyright none *
+missing-license-paragraph-in-dep5-copyright none *
diff -pruN 9.55.0~dfsg-3/debian/watch 9.56.1~dfsg-1/debian/watch
--- 9.55.0~dfsg-3/debian/watch	2021-05-25 15:22:09.000000000 +0000
+++ 9.56.1~dfsg-1/debian/watch	2022-04-20 19:57:54.000000000 +0000
@@ -5,7 +5,9 @@ version=4
 # NB! below is unusual (see "man uscan" for common Github pattern)
 
 opts=\
-dversionmangle=auto,repacksuffix=~dfsg,uversionmangle=s/\d\K(rc)/~~$1/,\
-filenamemangle=s%.*?(@ANY_VERSION@@ARCHIVE_EXT@)%@PACKAGE@-$1% \
+uversionmangle=s/\d\K(rc)/~~$1/,\
+filenamemangle=s%.*?(@ANY_VERSION@@ARCHIVE_EXT@)%@PACKAGE@-$1%,\
+repacksuffix=~dfsg,\
+dversionmangle=auto \
 https://github.com/ArtifexSoftware/ghostpdl-downloads/releases \
 .*?ghostpdl@ANY_VERSION@@ARCHIVE_EXT@
diff -pruN 9.55.0~dfsg-3/demos/c/Makefile 9.56.1~dfsg-1/demos/c/Makefile
--- 9.55.0~dfsg-3/demos/c/Makefile	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/demos/c/Makefile	2022-04-04 13:46:22.000000000 +0000
@@ -10,3 +10,17 @@ run_api_test: api_test
 post_api_test:
 	md5sum apitest*
 	rm apitest*
+
+multi_test: multi_test.c
+	(cd ../.. && ./autogen.sh)
+	(cd ../.. && make so XCFLAGS="-DCLUSTER")
+	pwd
+	gcc -fPIC -I../.. multi_test.c -DGHOSTPDL=1 -lgpdl -lpthread -L../../sobin -o multi_test
+
+run_multi_test: multi_test
+	LD_LIBRARY_PATH=../../sobin ./multi_test
+
+post_multi_test:
+	md5sum multitest*
+	rm multitest*
+	rm multi_out*
diff -pruN 9.55.0~dfsg-3/demos/c/multi_test.c 9.56.1~dfsg-1/demos/c/multi_test.c
--- 9.55.0~dfsg-3/demos/c/multi_test.c	1970-01-01 00:00:00.000000000 +0000
+++ 9.56.1~dfsg-1/demos/c/multi_test.c	2022-04-04 13:46:22.000000000 +0000
@@ -0,0 +1,191 @@
+/* Copyright (C) 2018-2022 Artifex Software, Inc.
+   All Rights Reserved.
+
+   This software is provided AS-IS with no warranty, either express or
+   implied.
+
+   This software is distributed under license and may not be copied,
+   modified or distributed except as expressly authorized under the terms
+   of the license contained in the file LICENSE in this distribution.
+
+   Refer to licensing information at http://www.artifex.com or contact
+   Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
+   CA 94945, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Simple example file to demonstrate multi-instance use of
+ * the ghostscript/GPDL library. */
+
+#ifdef _WIN32
+/* Stop windows builds complaining about sprintf being insecure. */
+#define _CRT_SECURE_NO_WARNINGS
+#include <windows.h>
+#define WINDOWS
+#else
+#include <pthread.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <assert.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+/* This can work either with the Ghostscript or GhostPDL
+ * library. The code is the same for both. */
+#if GHOSTPDL
+#include "pcl/pl/plapi.h"   /* GSAPI - gpdf version */
+#else
+#include "psi/iapi.h"       /* GSAPI - ghostscript version */
+#endif
+
+/* We hold details of each threads working in a thread_data
+ * structure. */
+typedef struct
+{
+    /* What worker number are we ? */
+    int thread_num;
+    /* What input file? should this worker use? */
+    char *in_file;
+    /* Somewhere to store the thread id */
+#ifdef WINDOWS
+    HANDLE thread;
+#else
+    pthread_t thread;
+#endif
+    /* exit code for the thread */
+    int code;
+} thread_data;
+
+/* The function to perform the work of the thread.
+ * Starts a gs instance, runs a file, shuts it down.
+ */
+static
+#ifdef WINDOWS
+DWORD WINAPI
+#else
+void *
+#endif
+worker(void *td_)
+{
+    thread_data *td = (thread_data *)td_;
+    int code;
+    void *instance  = NULL;
+    char out[32];
+
+    /* Construct the argc/argv to pass to ghostscript. */
+    int argc = 0;
+    char *argv[10];
+
+    sprintf(out, "multi_out_%d_", td->thread_num);
+    strcat(out, "%d.png");
+    argv[argc++] = "gpdl";
+    argv[argc++] = "-sDEVICE=png16m";
+    argv[argc++] = "-o";
+    argv[argc++] = out;
+    argv[argc++] = "-r100";
+    argv[argc++] = td->in_file;
+
+    /* Create a GS instance. */
+    code = gsapi_new_instance(&instance, NULL);
+    if (code < 0) {
+        printf("Error %d in gsapi_new_instance\n", code);
+        goto failearly;
+    }
+
+    /* Run our test. */
+    code = gsapi_init_with_args(instance, argc, argv);
+    if (code < 0) {
+        printf("Error %d in gsapi_init_with_args\n", code);
+        goto fail;
+    }
+
+    /* Close the interpreter down (important, or we will leak!) */
+    code = gsapi_exit(instance);
+    if (code < 0) {
+        printf("Error %d in gsapi_exit\n", code);
+        goto fail;
+    }
+
+fail:
+    /* Delete the gs instance. */
+    gsapi_delete_instance(instance);
+
+failearly:
+    td->code = code;
+
+#ifdef WINDOWS
+    return 0;
+#else
+    return NULL;
+#endif
+}
+
+/* A list of input files to run. */
+char *in_files[] =
+{
+  "../../examples/tiger.eps",
+  "../../examples/golfer.eps",
+  "../../examples/escher.ps",
+  "../../examples/snowflak.ps"
+#ifdef GHOSTPDL
+  , "../../pcl/examples/grashopp.pcl"
+  , "../../pcl/examples/owl.pcl"
+  , "../../pcl/examples/tiger.xps"
+#endif
+};
+
+#define NUM_INPUTS (sizeof(in_files)/sizeof(*in_files))
+#define NUM_WORKERS (10)
+
+int main(int argc, char *argv[])
+{
+    int failed = 0;
+#ifndef WINDOWS
+    int code;
+#endif
+    int i;
+    thread_data td[NUM_WORKERS];
+
+    /* Start NUM_WORKERS threads */
+    for (i = 0; i < NUM_WORKERS; i++)
+    {
+        td[i].in_file = in_files[i % NUM_INPUTS];
+        td[i].thread_num = i;
+
+#ifdef WINDOWS
+        td[i].thread = CreateThread(NULL, 0, worker, &td[i], 0, NULL);
+#else
+        code = pthread_create(&td[i].thread, NULL, worker, &td[i]);
+        if (code != 0) {
+            fprintf(stderr, "Thread %d creation failed\n", i);
+            exit(1);
+        }
+#endif
+    }
+
+    /* Wait for them all to finish */
+    for (i = 0; i < NUM_WORKERS; i++)
+    {
+        void *status = NULL;
+
+#ifdef WINDOWS
+        WaitForSingleObject(td[i].thread, INFINITE);
+#else
+        code = pthread_join(td[i].thread, &status);
+        if (code != 0) {
+            fprintf(stderr, "Thread join %d failed\n", i);
+            exit(1);
+        }
+#endif
+        /* All the threads should return with 0 */
+        if (td[i].code != 0)
+            failed = 1;
+        fprintf(stderr, "Thread %d finished with %d\n", i, td[i].code);
+    }
+
+    return failed;
+}
diff -pruN 9.55.0~dfsg-3/demos/c/multi_test.vcxproj 9.56.1~dfsg-1/demos/c/multi_test.vcxproj
--- 9.55.0~dfsg-3/demos/c/multi_test.vcxproj	1970-01-01 00:00:00.000000000 +0000
+++ 9.56.1~dfsg-1/demos/c/multi_test.vcxproj	2022-04-04 13:46:22.000000000 +0000
@@ -0,0 +1,239 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Memento|Win32">
+      <Configuration>Memento</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Memento|x64">
+      <Configuration>Memento</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <Keyword>Win32Proj</Keyword>
+    <ProjectGuid>{6f11d63a-3533-47e5-8aa8-973d804443a0}</ProjectGuid>
+    <RootNamespace>multi_test</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Memento|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Memento|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Memento|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Memento|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(ProjectDir)..\..\debugbin\</OutDir>
+    <TargetName>$(ProjectName)</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Memento|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(ProjectDir)..\..\membin\</OutDir>
+    <TargetName>$(ProjectName)</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(ProjectDir)..\..\bin\</OutDir>
+    <TargetName>$(ProjectName)</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <TargetName>$(ProjectName)64</TargetName>
+    <OutDir>$(ProjectDir)..\..\debugbin\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Memento|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(ProjectDir)..\..\membin\</OutDir>
+    <TargetName>$(ProjectName)64</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(ProjectDir)..\..\bin\</OutDir>
+    <TargetName>$(ProjectName)64</TargetName>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>GHOSTPDL;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>gpdldll32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\..\debugbin</AdditionalLibraryDirectories>
+    </Link>
+    <PostBuildEvent>
+      <Command>
+      </Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Memento|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>GHOSTPDL;MEMENTO;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>gpdldll32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\..\membin</AdditionalLibraryDirectories>
+    </Link>
+    <PostBuildEvent>
+      <Command>
+      </Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>GHOSTPDL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>gpdldll32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\..\bin</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>gpdldll64.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\..\debugbin</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Memento|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>MEMENTO;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>gpdldll64.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\..\membin</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>..\..\bin</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="multi_test.c" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff -pruN 9.55.0~dfsg-3/demos/c/multi_test.vcxproj.filters 9.56.1~dfsg-1/demos/c/multi_test.vcxproj.filters
--- 9.55.0~dfsg-3/demos/c/multi_test.vcxproj.filters	1970-01-01 00:00:00.000000000 +0000
+++ 9.56.1~dfsg-1/demos/c/multi_test.vcxproj.filters	2022-04-04 13:46:22.000000000 +0000
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="multi_test.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
diff -pruN 9.55.0~dfsg-3/demos/c/ReadMe.txt 9.56.1~dfsg-1/demos/c/ReadMe.txt
--- 9.55.0~dfsg-3/demos/c/ReadMe.txt	2021-09-27 07:40:04.000000000 +0000
+++ 9.56.1~dfsg-1/demos/c/ReadMe.txt	2022-04-04 13:46:22.000000000 +0000
@@ -29,3 +29,43 @@ tested there. Some fiddling to load the
 Building with GHOSTPDL=0 will allow the Ghostscript DLL to be
 tested. The VS2019 project will need to be edited to use the
 appropriate .lib file too.
+
+On unix, the file can be built using:
+
+ make api_test
+
+run using:
+
+ make run_api_test
+
+and cleaned up using:
+
+ make post_api_test
+
+
+                            multi_test
+                            ~~~~~~~~~~
+
+This is a simple VS2019 project that loads the gpdl dll and drives
+multiple instances of it from the gsapi functions.
+
+It is intended as a simple demonstration of how to use multiple
+instances of the ghostscript library at once; in this case many
+ghostscript instances run in parallel within a single process
+to convert several different documents at a time.
+
+Bu