[cairo-commit] 2 commits - src/cairo.c src/cairo-device.c src/cairo-error-private.h src/cairo.h src/cairo-image-info.c src/cairo-image-info-private.h src/cairo-misc.c src/cairo-pdf-surface.c src/cairo-pdf-surface-private.h src/cairo-region.c src/cairo-spans.c src/cairo-surface.c test/global.jb2 test/image1.jb2 test/image2.jb2 test/mime-data.c test/reference

Adrian Johnson ajohnson at kemper.freedesktop.org
Sun Sep 15 05:08:08 PDT 2013


 src/cairo-device.c                           |    1 
 src/cairo-error-private.h                    |    1 
 src/cairo-image-info-private.h               |    5 
 src/cairo-image-info.c                       |  135 ++++++++++++++++++++
 src/cairo-misc.c                             |    2 
 src/cairo-pdf-surface-private.h              |    8 +
 src/cairo-pdf-surface.c                      |  182 +++++++++++++++++++++++++++
 src/cairo-region.c                           |    1 
 src/cairo-spans.c                            |    2 
 src/cairo-surface.c                          |    4 
 src/cairo.c                                  |    4 
 src/cairo.h                                  |    6 
 test/global.jb2                              |binary
 test/image1.jb2                              |binary
 test/image2.jb2                              |binary
 test/mime-data.c                             |   97 ++++++++++++++
 test/reference/mime-data.base.argb32.ref.png |binary
 test/reference/mime-data.base.rgb24.ref.png  |binary
 test/reference/mime-data.pdf.ref.png         |binary
 test/reference/mime-data.ps.ref.png          |binary
 test/reference/mime-data.ref.png             |binary
 test/reference/mime-data.script.ref.png      |binary
 test/reference/mime-data.svg.ref.png         |binary
 23 files changed, 445 insertions(+), 3 deletions(-)

New commits:
commit 412a4c34d9207c339fd16a99756ea96082dc993f
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sun Sep 15 21:27:50 2013 +0930

    test: update mime-data to test jbig2 mime types

diff --git a/test/global.jb2 b/test/global.jb2
new file mode 100644
index 0000000..5a15498
Binary files /dev/null and b/test/global.jb2 differ
diff --git a/test/image1.jb2 b/test/image1.jb2
new file mode 100644
index 0000000..882ca95
Binary files /dev/null and b/test/image1.jb2 differ
diff --git a/test/image2.jb2 b/test/image2.jb2
new file mode 100644
index 0000000..48e4146
Binary files /dev/null and b/test/image2.jb2 differ
diff --git a/test/mime-data.c b/test/mime-data.c
index b1074cd..c744f5c 100644
--- a/test/mime-data.c
+++ b/test/mime-data.c
@@ -116,6 +116,97 @@ paint_file (cairo_t *cr,
 }
 
 static cairo_test_status_t
+paint_jbig2_file (cairo_t *cr, int x, int y)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *image;
+    unsigned char *mime_data;
+    unsigned int mime_length;
+    cairo_status_t status;
+    const char jbig2_image1_filename[] = "image1.jb2";
+    const char jbig2_image2_filename[] = "image2.jb2";
+    const char jbig2_global_filename[] = "global.jb2";
+
+    /* Deliberately use a non-matching MIME images, so that we can identify
+     * when the MIME representation is used in preference to the plain image
+     * surface.
+     */
+
+    /* Image 1 */
+
+    status = read_file (ctx, jbig2_image1_filename, &mime_data, &mime_length);
+    if (status)
+	return cairo_test_status_from_status (ctx, status);
+
+    image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 50);
+
+    status = cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID,
+					  (unsigned char *)"global", 6, NULL, NULL);
+    if (status) {
+	cairo_surface_destroy (image);
+	return cairo_test_status_from_status (ctx, status);
+    }
+
+    status = cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_JBIG2,
+					  mime_data, mime_length,
+					  free, mime_data);
+    if (status) {
+	cairo_surface_destroy (image);
+	free (mime_data);
+	return cairo_test_status_from_status (ctx, status);
+    }
+
+    cairo_set_source_surface (cr, image, x, y);
+    cairo_surface_destroy (image);
+
+    cairo_paint (cr);
+
+    /* Image 2 */
+
+    status = read_file (ctx, jbig2_image2_filename, &mime_data, &mime_length);
+    if (status)
+	return cairo_test_status_from_status (ctx, status);
+
+    image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 50);
+
+    status = cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID,
+					  (unsigned char *)"global", 6, NULL, NULL);
+    if (status) {
+	cairo_surface_destroy (image);
+	return cairo_test_status_from_status (ctx, status);
+    }
+
+    status = cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_JBIG2,
+					  mime_data, mime_length,
+					  free, mime_data);
+    if (status) {
+	cairo_surface_destroy (image);
+	free (mime_data);
+	return cairo_test_status_from_status (ctx, status);
+    }
+
+    /* Set the global data */
+    status = read_file (ctx, jbig2_global_filename, &mime_data, &mime_length);
+    if (status)
+	return cairo_test_status_from_status (ctx, status);
+
+    status = cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_JBIG2_GLOBAL,
+					  mime_data, mime_length,
+					  free, mime_data);
+    if (status) {
+	cairo_surface_destroy (image);
+	free (mime_data);
+	return cairo_test_status_from_status (ctx, status);
+    }
+
+    cairo_set_source_surface (cr, image, x, y + 50);
+    cairo_surface_destroy (image);
+
+    cairo_paint (cr);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
 draw (cairo_t *cr, int width, int height)
 {
     const char jpg_filename[] = "jpeg.jpg";
@@ -135,6 +226,10 @@ draw (cairo_t *cr, int width, int height)
     if (status)
 	return status;
 
+    status = paint_jbig2_file (cr, 0, 150);
+    if (status)
+	return status;
+
     return CAIRO_TEST_SUCCESS;
 }
 
@@ -142,5 +237,5 @@ CAIRO_TEST (mime_data,
 	    "Check that the mime-data embedding works",
 	    "jpeg, api", /* keywords */
 	    NULL, /* requirements */
-	    200, 150,
+	    200, 250,
 	    NULL, draw)
diff --git a/test/reference/mime-data.base.argb32.ref.png b/test/reference/mime-data.base.argb32.ref.png
index 3a912c5..4bc007c 100644
Binary files a/test/reference/mime-data.base.argb32.ref.png and b/test/reference/mime-data.base.argb32.ref.png differ
diff --git a/test/reference/mime-data.base.rgb24.ref.png b/test/reference/mime-data.base.rgb24.ref.png
index 3a912c5..4bc007c 100644
Binary files a/test/reference/mime-data.base.rgb24.ref.png and b/test/reference/mime-data.base.rgb24.ref.png differ
diff --git a/test/reference/mime-data.pdf.ref.png b/test/reference/mime-data.pdf.ref.png
index 90a08f5..76c17f8 100644
Binary files a/test/reference/mime-data.pdf.ref.png and b/test/reference/mime-data.pdf.ref.png differ
diff --git a/test/reference/mime-data.ps.ref.png b/test/reference/mime-data.ps.ref.png
index 721fb9c..7ec7d9b 100644
Binary files a/test/reference/mime-data.ps.ref.png and b/test/reference/mime-data.ps.ref.png differ
diff --git a/test/reference/mime-data.ref.png b/test/reference/mime-data.ref.png
index 3a912c5..4bc007c 100644
Binary files a/test/reference/mime-data.ref.png and b/test/reference/mime-data.ref.png differ
diff --git a/test/reference/mime-data.script.ref.png b/test/reference/mime-data.script.ref.png
index 64d7bf3..07691b1 100644
Binary files a/test/reference/mime-data.script.ref.png and b/test/reference/mime-data.script.ref.png differ
diff --git a/test/reference/mime-data.svg.ref.png b/test/reference/mime-data.svg.ref.png
index 81fde72..a4bbb1b 100644
Binary files a/test/reference/mime-data.svg.ref.png and b/test/reference/mime-data.svg.ref.png differ
commit 5c0caa6f82374ec38a33d5f25a725f60bc121887
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Sep 14 20:59:56 2013 +0930

    pdf: support JBIG2 mime data
    
    JBIG2 images may have shared global data that is stored in a separate
    stream in PDF. The CAIRO_MIME_TYPE_JBIG2 mime type is for the JBIG2
    data for each image. All images that use global data must also set
    CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID to a unique identifier. One of the
    images must also set CAIRO_MIME_TYPE_JBIG2_GLOBAL to the global
    data. The global data will be shared by all JBIG2 images with the same
    CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID.

diff --git a/src/cairo-device.c b/src/cairo-device.c
index 098f856..585a9c1 100644
--- a/src/cairo-device.c
+++ b/src/cairo-device.c
@@ -158,6 +158,7 @@ _cairo_device_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_INVALID_CONTENT:
     case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
     case CAIRO_STATUS_DEVICE_FINISHED:
+    case CAIRO_STATUS_JBIG2_GLOBAL_MISSING:
     default:
 	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 	return (cairo_device_t *) &_nil_device;
diff --git a/src/cairo-error-private.h b/src/cairo-error-private.h
index ea9c2ea..2d170ef 100644
--- a/src/cairo-error-private.h
+++ b/src/cairo-error-private.h
@@ -90,6 +90,7 @@ enum _cairo_int_status {
     CAIRO_INT_STATUS_DEVICE_ERROR,
     CAIRO_INT_STATUS_INVALID_MESH_CONSTRUCTION,
     CAIRO_INT_STATUS_DEVICE_FINISHED,
+    CAIRO_INT_STATUS_JBIG2_GLOBAL_MISSING,
 
     CAIRO_INT_STATUS_LAST_STATUS,
 
diff --git a/src/cairo-image-info-private.h b/src/cairo-image-info-private.h
index 0d9ef84..e64928e 100644
--- a/src/cairo-image-info-private.h
+++ b/src/cairo-image-info-private.h
@@ -60,4 +60,9 @@ _cairo_image_info_get_png_info (cairo_image_info_t	*info,
 				const unsigned char     *data,
 				unsigned long            length);
 
+cairo_private cairo_int_status_t
+_cairo_image_info_get_jbig2_info (cairo_image_info_t	*info,
+				  const unsigned char	*data,
+				  unsigned long		 length);
+
 #endif /* CAIRO_IMAGE_INFO_PRIVATE_H */
diff --git a/src/cairo-image-info.c b/src/cairo-image-info.c
index 4489698..64053a2 100644
--- a/src/cairo-image-info.c
+++ b/src/cairo-image-info.c
@@ -1,3 +1,4 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
 /* cairo - a vector graphics library with display and print output
  *
  * Copyright © 2008 Adrian Johnson
@@ -290,3 +291,137 @@ _cairo_image_info_get_png_info (cairo_image_info_t     *info,
 
     return CAIRO_STATUS_SUCCESS;
 }
+
+static const unsigned char *
+_jbig2_find_data_end (const unsigned char *p,
+		      const unsigned char *end,
+		      int                  type)
+{
+    unsigned char end_seq[2];
+    int mmr;
+
+    /* Segments of type "Immediate generic region" may have an
+     * unspecified data length.  The JBIG2 specification specifies the
+     * method to find the end of the data for these segments. */
+    if (type == 36 || type == 38 || type == 39) {
+	if (p + 18 < end) {
+	    mmr = p[17] & 0x01;
+	    if (mmr) {
+		/* MMR encoding ends with 0x00, 0x00 */
+		end_seq[0] = 0x00;
+		end_seq[1] = 0x00;
+	    } else {
+		/* Template encoding ends with 0xff, 0xac */
+		end_seq[0] = 0xff;
+		end_seq[1] = 0xac;
+	    }
+	    p += 18;
+	    while (p < end) {
+		if (p[0] == end_seq[0] && p[1] == end_seq[1]) {
+		    /* Skip the 2 terminating bytes and the 4 byte row count that follows. */
+		    p += 6;
+		    if (p < end)
+			return p;
+		}
+		p++;
+	    }
+	}
+    }
+
+    return NULL;
+}
+
+static const unsigned char *
+_jbig2_get_next_segment (const unsigned char  *p,
+			 const unsigned char  *end,
+			 int                  *type,
+			 const unsigned char **data,
+			 unsigned long        *data_len)
+{
+    unsigned long seg_num;
+    cairo_bool_t big_page_size;
+    int num_segs;
+    int ref_seg_bytes;
+    int referred_size;
+
+    if (p + 6 >= end)
+	return NULL;
+
+    seg_num = _get_be32 (p);
+    *type = p[4] & 0x3f;
+    big_page_size = (p[4] & 0x40) != 0;
+    p += 5;
+
+    num_segs = p[0] >> 5;
+    if (num_segs == 7) {
+	num_segs = _get_be32 (p) & 0x1fffffff;
+	ref_seg_bytes = 4 + ((num_segs + 1)/8);
+    } else {
+	ref_seg_bytes = 1;
+    }
+    p += ref_seg_bytes;
+
+    if (seg_num <= 256)
+	referred_size = 1;
+    else if (seg_num <= 65536)
+	referred_size = 2;
+    else
+	referred_size = 4;
+
+    p += num_segs * referred_size;
+    p += big_page_size ? 4 : 1;
+    if (p + 4 >= end)
+	return NULL;
+
+    *data_len = _get_be32 (p);
+    p += 4;
+    *data = p;
+
+    if (*data_len == 0xffffffff) {
+	/* if data length is -1 we have to scan through the data to find the end */
+	p = _jbig2_find_data_end (*data, end, *type);
+	if (!p || p >= end)
+	    return NULL;
+
+	*data_len = p - *data;
+    } else {
+	p += *data_len;
+    }
+
+    if (p < end)
+	return p;
+    else
+	return NULL;
+}
+
+static void
+_jbig2_extract_info (cairo_image_info_t *info, const unsigned char *p)
+{
+    info->width = _get_be32 (p);
+    info->height = _get_be32 (p + 4);
+    info->num_components = 1;
+    info->bits_per_component = 1;
+}
+
+cairo_int_status_t
+_cairo_image_info_get_jbig2_info (cairo_image_info_t	*info,
+				  const unsigned char	*data,
+				  unsigned long		 length)
+{
+    const unsigned char *p = data;
+    const unsigned char *end = data + length;
+    int seg_type;
+    const unsigned char *seg_data;
+    unsigned long seg_data_len;
+
+    while (p && p < end) {
+	p = _jbig2_get_next_segment (p, end, &seg_type, &seg_data, &seg_data_len);
+	if (p && seg_type == 48 && seg_data_len > 8) {
+	    /* page information segment */
+	    _jbig2_extract_info (info, seg_data);
+	    return CAIRO_STATUS_SUCCESS;
+	}
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
diff --git a/src/cairo-misc.c b/src/cairo-misc.c
index bb37e1a..7575d42 100644
--- a/src/cairo-misc.c
+++ b/src/cairo-misc.c
@@ -156,6 +156,8 @@ cairo_status_to_string (cairo_status_t status)
 	return "invalid operation during mesh pattern construction";
     case CAIRO_STATUS_DEVICE_FINISHED:
 	return "the target device has been finished";
+    case CAIRO_STATUS_JBIG2_GLOBAL_MISSING:
+	return "CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID used but no CAIRO_MIME_TYPE_JBIG2_GLOBAL data provided";
     default:
     case CAIRO_STATUS_LAST_STATUS:
 	return "<unknown error status>";
diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h
index dfeb1aa..0032947 100644
--- a/src/cairo-pdf-surface-private.h
+++ b/src/cairo-pdf-surface-private.h
@@ -129,6 +129,13 @@ typedef struct _cairo_pdf_smask_group {
     cairo_scaled_font_t	 *scaled_font;
 } cairo_pdf_smask_group_t;
 
+typedef struct _cairo_pdf_jbig2_global {
+    unsigned char *id;
+    unsigned long id_length;
+    cairo_pdf_resource_t  res;
+    cairo_bool_t emitted;
+} cairo_pdf_jbig2_global_t;
+
 typedef struct _cairo_pdf_surface cairo_pdf_surface_t;
 
 struct _cairo_pdf_surface {
@@ -151,6 +158,7 @@ struct _cairo_pdf_surface {
     cairo_hash_table_t *all_surfaces;
     cairo_array_t smask_groups;
     cairo_array_t knockout_group;
+    cairo_array_t jbig2_global;
 
     cairo_scaled_font_subsets_t *font_subsets;
     cairo_array_t fonts;
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 92c614d..4c9dd5a 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -130,6 +130,23 @@
  *
  * The PDF surface is used to render cairo graphics to Adobe
  * PDF files and is a multi-page vector surface backend.
+ *
+ * The following mime types are supported: %CAIRO_MIME_TYPE_JPEG,
+ * %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_UNIQUE_ID,
+ * %CAIRO_MIME_TYPE_JBIG2, %CAIRO_MIME_TYPE_JBIG2_GLOBAL,
+ * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID.
+ *
+ * JBIG2 data in PDF must be in the embedded format as descibed in
+ * ISO/IEC 11544. Image specific JBIG2 data must be in
+ * %CAIRO_MIME_TYPE_JBIG2.  Any global segments in the JBIG2 data
+ * (segments with page association field set to 0) must be in
+ * %CAIRO_MIME_TYPE_JBIG2_GLOBAL. The global data may be shared by
+ * multiple images. All images sharing the same global data must set
+ * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID to a unique identifer. At least
+ * one of the images must provide the global data using
+ * %CAIRO_MIME_TYPE_JBIG2_GLOBAL. The global data will only be
+ * embedded once but shared by all JBIG2 images with the same
+ * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID.
  **/
 
 static cairo_bool_t
@@ -164,6 +181,9 @@ static const char *_cairo_pdf_supported_mime_types[] =
     CAIRO_MIME_TYPE_JPEG,
     CAIRO_MIME_TYPE_JP2,
     CAIRO_MIME_TYPE_UNIQUE_ID,
+    CAIRO_MIME_TYPE_JBIG2,
+    CAIRO_MIME_TYPE_JBIG2_GLOBAL,
+    CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID,
     NULL
 };
 
@@ -364,6 +384,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t	*output,
 
     _cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t));
     _cairo_array_init (&surface->page_surfaces, sizeof (cairo_pdf_source_surface_t));
+    _cairo_array_init (&surface->jbig2_global, sizeof (cairo_pdf_jbig2_global_t));
     surface->all_surfaces = _cairo_hash_table_create (_cairo_pdf_source_surface_equal);
     if (unlikely (surface->all_surfaces == NULL)) {
 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -1174,6 +1195,20 @@ _cairo_pdf_surface_release_source_image_from_pattern (cairo_pdf_surface_t
 }
 
 static cairo_int_status_t
+_get_jbig2_image_info (cairo_surface_t		 *source,
+		       cairo_image_info_t	 *info,
+		       const unsigned char	**mime_data,
+		       unsigned long		 *mime_data_length)
+{
+    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2,
+				 mime_data, mime_data_length);
+    if (*mime_data == NULL)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return _cairo_image_info_get_jbig2_info (info, *mime_data, *mime_data_length);
+}
+
+static cairo_int_status_t
 _get_jpx_image_info (cairo_surface_t		 *source,
 		     cairo_image_info_t		*info,
 		     const unsigned char	**mime_data,
@@ -1250,6 +1285,15 @@ _get_source_surface_size (cairo_surface_t         *source,
     extents->x = 0;
     extents->y = 0;
 
+    status = _get_jbig2_image_info (source, &info, &mime_data, &mime_data_length);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+	*width = info.width;
+	*height = info.height;
+	extents->width = info.width;
+	extents->height = info.height;
+	return status;
+    }
+
     status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length);
     if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
 	*width = info.width;
@@ -1969,6 +2013,8 @@ _cairo_pdf_surface_finish (void *abstract_surface)
     long offset;
     cairo_pdf_resource_t info, catalog;
     cairo_status_t status, status2;
+    int size, i;
+    cairo_pdf_jbig2_global_t *global;
 
     status = surface->base.status;
     if (status == CAIRO_STATUS_SUCCESS)
@@ -2056,6 +2102,17 @@ _cairo_pdf_surface_finish (void *abstract_surface)
 	surface->font_subsets = NULL;
     }
 
+    size = _cairo_array_num_elements (&surface->jbig2_global);
+    for (i = 0; i < size; i++) {
+	global = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, i);
+	free(global->id);
+	if (!global->emitted)
+	    return _cairo_error (CAIRO_STATUS_JBIG2_GLOBAL_MISSING);
+    }
+    _cairo_array_fini (&surface->jbig2_global);
+
+    _cairo_array_truncate (&surface->page_surfaces, 0);
+
     _cairo_surface_clipper_reset (&surface->clipper);
 
     return status;
@@ -2556,6 +2613,127 @@ CLEANUP:
 }
 
 static cairo_int_status_t
+_cairo_pdf_surface_lookup_jbig2_global (cairo_pdf_surface_t       *surface,
+					const unsigned char       *global_id,
+					unsigned long              global_id_length,
+					cairo_pdf_jbig2_global_t **entry)
+{
+    cairo_pdf_jbig2_global_t global;
+    int size, i;
+    cairo_int_status_t status;
+
+    size = _cairo_array_num_elements (&surface->jbig2_global);
+    for (i = 0; i < size; i++) {
+	*entry = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, i);
+	if ((*entry)->id && global_id && (*entry)->id_length == global_id_length
+	    && memcmp((*entry)->id, global_id, global_id_length) == 0) {
+	    return CAIRO_STATUS_SUCCESS;
+	}
+    }
+
+    global.id = malloc(global_id_length);
+    memcpy (global.id, global_id, global_id_length);
+    global.id_length = global_id_length;
+    global.res = _cairo_pdf_surface_new_object (surface);
+    if (global.res.id == 0)
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    global.emitted = FALSE;
+    status = _cairo_array_append (&surface->jbig2_global, &global);
+    if (unlikely(status))
+	return status;
+
+    size = _cairo_array_num_elements (&surface->jbig2_global);
+    *entry = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, size - 1);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_emit_jbig2_image (cairo_pdf_surface_t   *surface,
+				     cairo_surface_t	   *source,
+				     cairo_pdf_resource_t   res)
+{
+    cairo_int_status_t status;
+    const unsigned char *mime_data;
+    unsigned long mime_data_length;
+    cairo_image_info_t info;
+    const unsigned char *global_id;
+    unsigned long global_id_length;
+    const unsigned char *global_data;
+    unsigned long global_data_length;
+    cairo_pdf_jbig2_global_t *global_entry;
+
+    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2,
+				 &mime_data, &mime_data_length);
+    if (mime_data == NULL)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_image_info_get_jbig2_info (&info, mime_data, mime_data_length);
+    if (status)
+	return status;
+
+    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID,
+				 &global_id, &global_id_length);
+    if (global_id && global_id_length > 0) {
+	status = _cairo_pdf_surface_lookup_jbig2_global (surface, global_id, global_id_length, &global_entry);
+	if (unlikely(status))
+	    return status;
+
+	if (!global_entry->emitted) {
+	    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2_GLOBAL,
+					 &global_data, &global_data_length);
+	    if (global_data) {
+		status = _cairo_pdf_surface_open_stream (surface, &global_entry->res, FALSE, NULL);
+		if (unlikely(status))
+		    return status;
+
+		_cairo_output_stream_write (surface->output, global_data, global_data_length);
+		status = _cairo_pdf_surface_close_stream (surface);
+		if (unlikely(status))
+		    return status;
+
+		global_entry->emitted = TRUE;
+	    }
+	}
+
+	status = _cairo_pdf_surface_open_stream (surface,
+						 &res,
+						 FALSE,
+						 "   /Type /XObject\n"
+						 "   /Subtype /Image\n"
+						 "   /Width %d\n"
+						 "   /Height %d\n"
+						 "   /ColorSpace /DeviceGray\n"
+						 "   /BitsPerComponent 1\n"
+						 "   /Filter /JBIG2Decode\n"
+						 "   /DecodeParms << /JBIG2Globals %d 0 R >>\n",
+						 info.width,
+						 info.height,
+						 global_entry->res.id);
+    } else {
+	status = _cairo_pdf_surface_open_stream (surface,
+						 &res,
+						 FALSE,
+						 "   /Type /XObject\n"
+						 "   /Subtype /Image\n"
+						 "   /Width %d\n"
+						 "   /Height %d\n"
+						 "   /ColorSpace /DeviceGray\n"
+						 "   /BitsPerComponent 1\n"
+						 "   /Filter /JBIG2Decode\n",
+						 info.width,
+						 info.height);
+    }
+    if (unlikely(status))
+	return status;
+
+    _cairo_output_stream_write (surface->output, mime_data, mime_data_length);
+    status = _cairo_pdf_surface_close_stream (surface);
+
+    return status;
+}
+
+static cairo_int_status_t
 _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t   *surface,
 				   cairo_surface_t	 *source,
 				   cairo_pdf_resource_t   res)
@@ -2665,6 +2843,10 @@ _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t        *surface,
 
     if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
 	if (!source->hash_entry->stencil_mask) {
+	    status = _cairo_pdf_surface_emit_jbig2_image (surface, source->surface, source->hash_entry->surface_res);
+	    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+		return status;
+
 	    status = _cairo_pdf_surface_emit_jpx_image (surface, source->surface, source->hash_entry->surface_res);
 	    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 		return status;
diff --git a/src/cairo-region.c b/src/cairo-region.c
index a51e224..ceaf4c0 100644
--- a/src/cairo-region.c
+++ b/src/cairo-region.c
@@ -106,6 +106,7 @@ _cairo_region_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
     case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
     case CAIRO_STATUS_DEVICE_FINISHED:
+    case CAIRO_STATUS_JBIG2_GLOBAL_MISSING:
     default:
 	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 	return (cairo_region_t *) &_cairo_region_nil;
diff --git a/src/cairo-spans.c b/src/cairo-spans.c
index b8d4180..182390c 100644
--- a/src/cairo-spans.c
+++ b/src/cairo-spans.c
@@ -127,6 +127,7 @@ _cairo_scan_converter_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL;
     case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL;
     case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL;
+    case CAIRO_STATUS_JBIG2_GLOBAL_MISSING:
     default:
 	break;
     }
@@ -239,6 +240,7 @@ _cairo_span_renderer_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL;
     case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL;
     case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL;
+    case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: RETURN_NIL;
     default:
 	break;
     }
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index f2f2ef6..d550131 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -1285,7 +1285,8 @@ _cairo_mime_data_destroy (void *ptr)
  *
  * The recognized MIME types are the following: %CAIRO_MIME_TYPE_JPEG,
  * %CAIRO_MIME_TYPE_PNG, %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_URI,
- * %CAIRO_MIME_TYPE_UNIQUE_ID.
+ * %CAIRO_MIME_TYPE_UNIQUE_ID, %CAIRO_MIME_TYPE_JBIG2,
+ * %CAIRO_MIME_TYPE_JBIG2_GLOBAL, %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID.
  *
  * See corresponding backend surface docs for details about which MIME
  * types it can handle. Caution: the associated MIME data will be
@@ -2674,6 +2675,7 @@ _cairo_surface_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
     case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
     case CAIRO_STATUS_DEVICE_FINISHED:
+    case CAIRO_STATUS_JBIG2_GLOBAL_MISSING:
     default:
 	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 	return (cairo_surface_t *) &_cairo_surface_nil;
diff --git a/src/cairo.c b/src/cairo.c
index 82396d2..c7128ae 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -152,7 +152,9 @@ static const cairo_t _cairo_nil[] = {
     DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_TYPE_MISMATCH),
     DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_ERROR),
     DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_MESH_CONSTRUCTION),
-    DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_FINISHED)
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_FINISHED),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_JBIG2_GLOBAL_MISSING)
+
 };
 COMPILE_TIME_ASSERT (ARRAY_LENGTH (_cairo_nil) == CAIRO_STATUS_LAST_STATUS - 1);
 
diff --git a/src/cairo.h b/src/cairo.h
index de35126..2e69793 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -290,6 +290,8 @@ typedef struct _cairo_user_data_key {
  *   cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch()
  *   pair (Since 1.12)
  * @CAIRO_STATUS_DEVICE_FINISHED: target device has been finished (Since 1.12)
+ * @CAIRO_STATUS_JBIG2_GLOBAL_MISSING: %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID has been used on at least one image
+ *   but no image provided %CAIRO_MIME_TYPE_JBIG2_GLOBAL (Since 1.14)
  * @CAIRO_STATUS_LAST_STATUS: this is a special value indicating the number of
  *   status values defined in this enumeration.  When using this value, note
  *   that the version of cairo at run-time may have additional status values
@@ -345,6 +347,7 @@ typedef enum _cairo_status {
     CAIRO_STATUS_DEVICE_ERROR,
     CAIRO_STATUS_INVALID_MESH_CONSTRUCTION,
     CAIRO_STATUS_DEVICE_FINISHED,
+    CAIRO_STATUS_JBIG2_GLOBAL_MISSING,
 
     CAIRO_STATUS_LAST_STATUS
 } cairo_status_t;
@@ -2419,6 +2422,9 @@ cairo_surface_set_user_data (cairo_surface_t		 *surface,
 #define CAIRO_MIME_TYPE_JP2 "image/jp2"
 #define CAIRO_MIME_TYPE_URI "text/x-uri"
 #define CAIRO_MIME_TYPE_UNIQUE_ID "application/x-cairo.uuid"
+#define CAIRO_MIME_TYPE_JBIG2 "application/x-cairo.jbig2"
+#define CAIRO_MIME_TYPE_JBIG2_GLOBAL "application/x-cairo.jbig2-global"
+#define CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID "application/x-cairo.jbig2-global-id"
 
 cairo_public void
 cairo_surface_get_mime_data (cairo_surface_t		*surface,


More information about the cairo-commit mailing list