diff options
author | Adrian Johnson <ajohnson@redneon.com> | 2017-10-22 08:07:49 +1030 |
---|---|---|
committer | Adrian Johnson <ajohnson@redneon.com> | 2017-10-22 08:42:36 +1030 |
commit | e1a02b180d804887980c111c1f9780bed44b96a6 (patch) | |
tree | cca69a484e92389b20ef60f31fb697e61b7ce774 | |
parent | 4ae7f411c865a25b577faea58e5fda6f4e9e1172 (diff) | |
download | cairo-e1a02b180d804887980c111c1f9780bed44b96a6.tar.gz |
Add CCITT_FAX mime type for PDF and PS surfaces
This completes the full set of PDF/PS image filters allowing image
data to be passed though without decompressing then recompresssing in
a less efficient format.
The difficulty with CCITT_FAX is it needs some decoding parameters
that are not stored inside the image data. This is achieved by using
an additional mime type CCITT_FAX_PARAMS that contains the params in
key=value format.
-rw-r--r-- | doc/public/cairo-sections.txt | 2 | ||||
-rw-r--r-- | src/Makefile.sources | 16 | ||||
-rw-r--r-- | src/cairo-pdf-surface-private.h | 2 | ||||
-rw-r--r-- | src/cairo-pdf-surface.c | 263 | ||||
-rw-r--r-- | src/cairo-ps-surface.c | 200 | ||||
-rw-r--r-- | src/cairo-surface.c | 21 | ||||
-rw-r--r-- | src/cairo-tag-attributes-private.h | 15 | ||||
-rw-r--r-- | src/cairo-tag-attributes.c | 80 | ||||
-rw-r--r-- | src/cairo.h | 2 | ||||
-rw-r--r-- | test/ccitt.g3 | 2 | ||||
-rw-r--r-- | test/mime-data.c | 56 | ||||
-rw-r--r-- | test/reference/mime-data.base.argb32.ref.png | bin | 243 -> 272 bytes | |||
-rw-r--r-- | test/reference/mime-data.base.rgb24.ref.png | bin | 243 -> 272 bytes | |||
-rw-r--r-- | test/reference/mime-data.pdf.ref.png | bin | 7563 -> 8205 bytes | |||
-rw-r--r-- | test/reference/mime-data.ps.ref.png | bin | 4705 -> 5386 bytes | |||
-rw-r--r-- | test/reference/mime-data.ref.png | bin | 243 -> 272 bytes | |||
-rw-r--r-- | test/reference/mime-data.script.ref.png | bin | 2130 -> 2175 bytes | |||
-rw-r--r-- | test/reference/mime-data.svg.ref.png | bin | 6437 -> 6513 bytes |
18 files changed, 609 insertions, 50 deletions
diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt index 7b04ae7b3..40c214834 100644 --- a/doc/public/cairo-sections.txt +++ b/doc/public/cairo-sections.txt @@ -242,6 +242,8 @@ cairo_device_observer_stroke_elapsed <SECTION> <FILE>cairo-surface</FILE> CAIRO_HAS_MIME_SURFACE +CAIRO_MIME_TYPE_CCITT_FAX +CAIRO_MIME_TYPE_CCITT_FAX_PARAMS CAIRO_MIME_TYPE_JBIG2 CAIRO_MIME_TYPE_JBIG2_GLOBAL CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID diff --git a/src/Makefile.sources b/src/Makefile.sources index 89417ac86..cc2194f3e 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -264,8 +264,16 @@ cairo_egl_sources = cairo_glx_sources = cairo_wgl_sources = -_cairo_pdf_operators_private = cairo-pdf-operators-private.h cairo-pdf-shading-private.h -_cairo_pdf_operators_sources = cairo-pdf-operators.c cairo-pdf-shading.c +_cairo_pdf_operators_private = \ + cairo-pdf-operators-private.h \ + cairo-pdf-shading-private.h \ + cairo-tag-attributes-private.h \ + $(NULL) +_cairo_pdf_operators_sources = \ + cairo-pdf-operators.c \ + cairo-pdf-shading.c \ + cairo-tag-attributes.c \ + $(NULL) cairo_private += $(_cairo_pdf_operators_private) cairo_sources += $(_cairo_pdf_operators_sources) @@ -279,8 +287,8 @@ _cairo_deflate_stream_sources = cairo-deflate-stream.c cairo_sources += $(_cairo_deflate_stream_sources) cairo_pdf_headers = cairo-pdf.h -cairo_pdf_private = cairo-pdf-surface-private.h cairo-tag-stack-private.h cairo-tag-attributes-private.h -cairo_pdf_sources = cairo-pdf-surface.c cairo-pdf-interchange.c cairo-tag-stack.c cairo-tag-attributes.c +cairo_pdf_private = cairo-pdf-surface-private.h cairo-tag-stack-private.h +cairo_pdf_sources = cairo-pdf-surface.c cairo-pdf-interchange.c cairo-tag-stack.c cairo_svg_headers = cairo-svg.h cairo_svg_private = cairo-svg-surface-private.h diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h index a84ea9573..98f0cfdda 100644 --- a/src/cairo-pdf-surface-private.h +++ b/src/cairo-pdf-surface-private.h @@ -84,7 +84,7 @@ typedef struct _cairo_pdf_source_surface_entry { cairo_bool_t bounded; cairo_rectangle_int_t extents; - /* Union of source extents requried for all operations using this source */ + /* Union of source extents required for all operations using this source */ cairo_rectangle_int_t required_extents; } cairo_pdf_source_surface_entry_t; diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 9e1547eac..cb887119f 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -134,19 +134,62 @@ * 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. + * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, + * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS. * + * # JBIG2 Images # * JBIG2 data in PDF must be in the embedded format as described 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 + * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID to a unique identifier. 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 + * embedded once and shared by all JBIG2 images with the same * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID. + * + * # CCITT Fax Images # {#ccitt} + * The %CAIRO_MIME_TYPE_CCITT_FAX mime data requires a number of decoding + * parameters These parameters are specified using %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS. + * + * %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS mime data must contain a string of the form + * "param1=value1 param2=value2 ...". + * + * @Columns: [required] An integer specifying the width of the image in pixels. + * + * @Rows: [required] An integer specifying the height of the image in scan lines. + * + * @K: [optional] An integer identifying the encoding scheme used. < 0 + * is 2 dimensional Group 4, = 0 is Group3 1 dimensional, > 0 is mixed 1 + * and 2 dimensional encoding. Default is 0. + * + * @EndOfLine: [optional] If true end-of-line bit patterns are present. Default is false. + * + * @EncodedByteAlign: [optional] If true the end of line is padded + * with 0 bits so the next line begins on a byte boundary. Default is false. + * + * @EndOfBlock: [optional] If true the data contains an end-of-block pattern. Default is true. + * + * @BlackIs1: [optional] If true 1 bits are black pixels. Default is false. + * + * @DamagedRowsBeforeError: [optional] An integer specifying the + * number of damages rows tolerated before an error occurs. Default is 0. + * + * Boolean values may be "true" or "false", or 1 or 0. + * + * These parameters are the same as the CCITTFaxDecode parameters in the + * [PostScript Language Reference](https://www.adobe.com/products/postscript/pdfs/PLRM.pdf) + * and [Portable Document Format (PDF)](https://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf). + * Refer to these documents for further details. + * + * An example %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS string is: + * + * <programlisting> + * "Columns=10230 Rows=40000 K=1 EndOfLine=true EncodedByteAlign=1 BlackIs1=false" + * </programlisting> + * **/ static cairo_bool_t @@ -184,6 +227,8 @@ static const char *_cairo_pdf_supported_mime_types[] = CAIRO_MIME_TYPE_JBIG2, CAIRO_MIME_TYPE_JBIG2_GLOBAL, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, + CAIRO_MIME_TYPE_CCITT_FAX, + CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, NULL }; @@ -1359,44 +1404,93 @@ _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_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) + &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); + 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, - unsigned long *mime_data_length) + cairo_image_info_t *info) { + const unsigned char *mime_data; + unsigned long mime_data_length; + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2, - mime_data, mime_data_length); - if (*mime_data == NULL) + &mime_data, &mime_data_length); + if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; - return _cairo_image_info_get_jpx_info (info, *mime_data, *mime_data_length); + return _cairo_image_info_get_jpx_info (info, mime_data, mime_data_length); } static cairo_int_status_t _get_jpeg_image_info (cairo_surface_t *source, - cairo_image_info_t *info, - const unsigned char **mime_data, - unsigned long *mime_data_length) + cairo_image_info_t *info) { + const unsigned char *mime_data; + unsigned long mime_data_length; + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, - mime_data, mime_data_length); - if (*mime_data == NULL) + &mime_data, &mime_data_length); + if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; - return _cairo_image_info_get_jpeg_info (info, *mime_data, *mime_data_length); + return _cairo_image_info_get_jpeg_info (info, mime_data, mime_data_length); +} + +static cairo_int_status_t +_get_ccitt_image_info (cairo_surface_t *source, + int *width, + int *height) +{ + cairo_status_t status; + const unsigned char *ccitt_data; + unsigned long ccitt_data_len; + const unsigned char *ccitt_params_string; + unsigned long ccitt_params_string_len; + char *params; + cairo_ccitt_params_t ccitt_params; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_CCITT_FAX, + &ccitt_data, &ccitt_data_len); + if (unlikely (source->status)) + return source->status; + if (ccitt_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, + &ccitt_params_string, &ccitt_params_string_len); + if (unlikely (source->status)) + return source->status; + if (ccitt_params_string == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* ensure params_string is null terminated */ + params = malloc (ccitt_params_string_len + 1); + memcpy (params, ccitt_params_string, ccitt_params_string_len); + params[ccitt_params_string_len] = 0; + status = _cairo_tag_parse_ccitt_params (params, &ccitt_params); + if (unlikely(status)) + return source->status; + + free (params); + + if (ccitt_params.columns <= 0 || ccitt_params.rows <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + *width = ccitt_params.columns; + *height = ccitt_params.rows; + + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t @@ -1407,8 +1501,7 @@ _get_source_surface_extents (cairo_surface_t *source, { cairo_int_status_t status; cairo_image_info_t info; - const unsigned char *mime_data; - unsigned long mime_data_length; + int width, height; *bounded = TRUE; *subsurface = FALSE; @@ -1444,27 +1537,34 @@ _get_source_surface_extents (cairo_surface_t *source, extents->x = 0; extents->y = 0; - status = _get_jbig2_image_info (source, &info, &mime_data, &mime_data_length); + status = _get_jbig2_image_info (source, &info); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { extents->width = info.width; extents->height = info.height; return status; } - status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length); + status = _get_jpx_image_info (source, &info); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { extents->width = info.width; extents->height = info.height; return status; } - status = _get_jpeg_image_info (source, &info, &mime_data, &mime_data_length); + status = _get_jpeg_image_info (source, &info); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { extents->width = info.width; extents->height = info.height; return status; } + status = _get_ccitt_image_info (source, &width, &height); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + extents->width = width; + extents->height = height; + return status; + } + if (! _cairo_surface_get_extents (source, extents)) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -3182,6 +3282,113 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, } static cairo_int_status_t +_cairo_pdf_surface_emit_ccitt_image (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_pdf_source_surface_entry_t *surface_entry) +{ + cairo_status_t status; + const unsigned char *ccitt_data; + unsigned long ccitt_data_len; + const unsigned char *ccitt_params_string; + unsigned long ccitt_params_string_len; + char *params, *p, *end; + cairo_ccitt_params_t ccitt_params; + char buf[300]; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_CCITT_FAX, + &ccitt_data, &ccitt_data_len); + if (unlikely (source->status)) + return source->status; + if (ccitt_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, + &ccitt_params_string, &ccitt_params_string_len); + if (unlikely (source->status)) + return source->status; + if (ccitt_params_string == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* ensure params_string is null terminated */ + params = malloc (ccitt_params_string_len + 1); + memcpy (params, ccitt_params_string, ccitt_params_string_len); + params[ccitt_params_string_len] = 0; + status = _cairo_tag_parse_ccitt_params (params, &ccitt_params); + if (unlikely(status)) + return source->status; + + free (params); + + p = buf; + *p = 0; + end = buf + sizeof(buf) - 1; + p += snprintf (p, end - p, "/Columns %d /Rows %d /K %d", + ccitt_params.columns, + ccitt_params.rows, + ccitt_params.k); + if (ccitt_params.end_of_line) + p += snprintf (p, end - p, " /EndOfLine true"); + + if (ccitt_params.encoded_byte_align) + p += snprintf (p, end - p, " /EncodedByteAlign true"); + + if (!ccitt_params.end_of_block) + p += snprintf (p, end - p, " /EndOfBlock false"); + + if (ccitt_params.black_is_1) + p += snprintf (p, end - p, " /BlackIs1 true"); + + if (ccitt_params.damaged_rows_before_error > 0) { + p += snprintf (p, end - p, " /DamagedRowsBeforeError %d", + ccitt_params.damaged_rows_before_error); + } + + if (surface_entry->stencil_mask) { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /ImageMask true\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent 1\n" + " /Decode [1 0]\n" + " /Filter /CCITTFaxDecode\n" + " /DecodeParms << %s >> ", + ccitt_params.columns, + ccitt_params.rows, + surface_entry->interpolate ? "true" : "false", + buf); + } else { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /ColorSpace /DeviceGray\n" + " /BitsPerComponent 1\n" + " /Interpolate %s\n" + " /Filter /CCITTFaxDecode\n" + " /DecodeParms << %s >> ", + ccitt_params.columns, + ccitt_params.rows, + surface_entry->interpolate ? "true" : "false", + buf); + } + if (unlikely (status)) + return status; + + _cairo_output_stream_write (surface->output, ccitt_data, ccitt_data_len); + status = _cairo_pdf_surface_close_stream (surface); + + return status; +} + +static cairo_int_status_t _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, cairo_pdf_source_surface_t *source) { @@ -3201,6 +3408,10 @@ _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_emit_jpeg_image (surface, source->surface, source->hash_entry); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; + + status = _cairo_pdf_surface_emit_ccitt_image (surface, source->surface, source->hash_entry); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; } if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index e34f656cf..62b19e4af 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -77,6 +77,7 @@ #include "cairo-output-stream-private.h" #include "cairo-type3-glyph-surface-private.h" #include "cairo-image-info-private.h" +#include "cairo-tag-attributes-private.h" #include <stdio.h> #include <ctype.h> @@ -105,6 +106,13 @@ * * The PostScript surface is used to render cairo graphics to Adobe * PostScript files and is a multi-page vector surface backend. + * + * The following mime types are supported: %CAIRO_MIME_TYPE_JPEG, + * %CAIRO_MIME_TYPE_UNIQUE_ID, + * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS. + * + * The %CAIRO_MIME_TYPE_CCITT_FAX and %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS mime types + * are documented in [CCITT Fax Images][ccitt]. **/ /** @@ -146,6 +154,8 @@ static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] = static const char *_cairo_ps_supported_mime_types[] = { CAIRO_MIME_TYPE_JPEG, + CAIRO_MIME_TYPE_CCITT_FAX, + CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, NULL }; @@ -2440,6 +2450,28 @@ _cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface, return status; } +static const char * +get_interpolate (cairo_filter_t filter) +{ + const char *interpolate; + + switch (filter) { + default: + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + interpolate = "true"; + break; + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_GAUSSIAN: + interpolate = "false"; + break; + } + + return interpolate; +} + static cairo_status_t _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, cairo_image_surface_t *image_surf, @@ -2493,20 +2525,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, goto bail0; } ps_image = image; - - switch (filter) { - default: - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - interpolate = "true"; - break; - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - case CAIRO_FILTER_GAUSSIAN: - interpolate = "false"; - break; - } + interpolate = get_interpolate (filter); if (stencil_mask) { use_mask = FALSE; @@ -2827,8 +2846,7 @@ bail0: static cairo_status_t _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, cairo_surface_t *source, - int width, - int height) + cairo_filter_t filter) { cairo_status_t status; const unsigned char *mime_data; @@ -2895,11 +2913,13 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, " /Width %d def\n" " /Height %d def\n" " /BitsPerComponent %d def\n" + " /Interpolate %s def\n" " /Decode [ %s ] def\n", colorspace, info.width, info.height, info.bits_per_component, + get_interpolate (filter), decode); if (surface->use_string_datasource) { @@ -2936,6 +2956,148 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, } static cairo_status_t +_cairo_ps_surface_emit_ccitt_image (cairo_ps_surface_t *surface, + cairo_surface_t *source, + cairo_filter_t filter, + cairo_bool_t stencil_mask) +{ + cairo_status_t status; + const unsigned char *ccitt_data; + unsigned long ccitt_data_len; + const unsigned char *ccitt_params_string; + unsigned long ccitt_params_string_len; + char *params; + cairo_ccitt_params_t ccitt_params; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_CCITT_FAX, + &ccitt_data, &ccitt_data_len); + if (unlikely (source->status)) + return source->status; + if (ccitt_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, + &ccitt_params_string, &ccitt_params_string_len); + if (unlikely (source->status)) + return source->status; + if (ccitt_params_string == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* ensure params_string is null terminated */ + params = malloc (ccitt_params_string_len + 1); + memcpy (params, ccitt_params_string, ccitt_params_string_len); + params[ccitt_params_string_len] = 0; + status = _cairo_tag_parse_ccitt_params (params, &ccitt_params); + if (unlikely(status)) + return source->status; + + free (params); + + if (ccitt_params.columns <= 0 || ccitt_params.rows <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface->use_string_datasource) { + /* Emit the image data as a base85-encoded string which will + * be used as the data source for the image operator later. */ + _cairo_output_stream_printf (surface->stream, + "/CairoImageData [\n"); + + status = _cairo_ps_surface_emit_base85_string (surface, + ccitt_data, + ccitt_data_len, + CAIRO_PS_COMPRESS_NONE, + TRUE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->stream, + "] def\n"); + _cairo_output_stream_printf (surface->stream, + "/CairoImageDataIndex 0 def\n"); + } else { + _cairo_output_stream_printf (surface->stream, + "/cairo_ascii85_file currentfile /ASCII85Decode filter def\n"); + } + + if (!stencil_mask) { + _cairo_output_stream_printf (surface->stream, + "/DeviceGray setcolorspace\n"); + } + + _cairo_output_stream_printf (surface->stream, + "<<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 1\n" + " /Interpolate %s\n" + " /Decode [ 0 1 ]\n", + ccitt_params.columns, + ccitt_params.rows, + get_interpolate (filter)); + + if (surface->use_string_datasource) { + _cairo_output_stream_printf (surface->stream, + " /DataSource {\n" + " CairoImageData CairoImageDataIndex get\n" + " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" + " CairoImageDataIndex CairoImageData length 1 sub gt\n" + " { /CairoImageDataIndex 0 def } if\n" + " } /ASCII85Decode filter"); + } else { + _cairo_output_stream_printf (surface->stream, + " /DataSource cairo_ascii85_file"); + } + + _cairo_output_stream_printf (surface->stream, + " << /Columns %d /Rows %d /K %d", + ccitt_params.columns, + ccitt_params.rows, + ccitt_params.k); + + if (ccitt_params.end_of_line) + _cairo_output_stream_printf (surface->stream, " /EndOfLine true"); + + if (ccitt_params.encoded_byte_align) + _cairo_output_stream_printf (surface->stream, " /EncodedByteAlign true"); + + if (!ccitt_params.end_of_block) + _cairo_output_stream_printf (surface->stream, " /EndOfBlock false"); + + if (ccitt_params.black_is_1) + _cairo_output_stream_printf (surface->stream, " /BlackIs1 true"); + + if (ccitt_params.damaged_rows_before_error > 0) { + _cairo_output_stream_printf (surface->stream, + " /DamagedRowsBeforeError %d", + ccitt_params.damaged_rows_before_error); + } + + _cairo_output_stream_printf (surface->stream, + " >> /CCITTFaxDecode filter\n"); + + _cairo_output_stream_printf (surface->stream, + " /ImageMatrix [ 1 0 0 -1 0 %d ]\n" + ">>\n" + "%s%s\n", + ccitt_params.rows, + surface->use_string_datasource ? "" : "cairo_", + stencil_mask ? "imagemask" : "image"); + + if (!surface->use_string_datasource) { + /* Emit the image data as a base85-encoded string which will + * be used as the data source for the image operator. */ + status = _cairo_ps_surface_emit_base85_string (surface, + ccitt_data, + ccitt_data_len, + CAIRO_PS_COMPRESS_NONE, + FALSE); + } + + return status; +} + +static cairo_status_t _cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, cairo_surface_t *recording_surface, const cairo_rectangle_int_t *recording_extents, @@ -3078,7 +3240,11 @@ _cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, { cairo_surface_t *surf = ((cairo_surface_pattern_t *) source_pattern)->surface; - status = _cairo_ps_surface_emit_jpeg_image (surface, surf, src_surface_extents->width, src_surface_extents->height); + status = _cairo_ps_surface_emit_jpeg_image (surface, surf, source_pattern->filter); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_ps_surface_emit_ccitt_image (surface, surf, source_pattern->filter, stencil_mask); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 35c63d7b8..459b84c9e 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -1225,6 +1225,24 @@ _cairo_mime_data_destroy (void *ptr) } /** + * CAIRO_MIME_TYPE_CCITT_FAX: + * + * Group 3 or Group 4 CCITT facsimile encoding (International + * Telecommunication Union, Recommendations T.4 and T.6.) + * + * Since: 1.16 + **/ + +/** + * CAIRO_MIME_TYPE_CCITT_FAX_PARAMS: + * + * Decode parameters for Group 3 or Group 4 CCITT facsimile encoding. + * See [CCITT Fax Images][ccitt]. + * + * Since: 1.16 + **/ + +/** * CAIRO_MIME_TYPE_JBIG2: * * Joint Bi-level Image Experts Group image coding standard (ISO/IEC 11544). @@ -1314,7 +1332,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_JBIG2, - * %CAIRO_MIME_TYPE_JBIG2_GLOBAL, %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID. + * %CAIRO_MIME_TYPE_JBIG2_GLOBAL, %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, + * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS. * * See corresponding backend surface docs for details about which MIME * types it can handle. Caution: the associated MIME data will be diff --git a/src/cairo-tag-attributes-private.h b/src/cairo-tag-attributes-private.h index a127abc97..30bb48ed9 100644 --- a/src/cairo-tag-attributes-private.h +++ b/src/cairo-tag-attributes-private.h @@ -68,10 +68,25 @@ typedef struct _cairo_dest_attrs { cairo_bool_t internal; } cairo_dest_attrs_t; +typedef struct _cairo_ccitt_params { + int columns; + int rows; + int k; + cairo_bool_t end_of_line; + cairo_bool_t encoded_byte_align; + cairo_bool_t end_of_block; + cairo_bool_t black_is_1; + int damaged_rows_before_error; +} cairo_ccitt_params_t; + + cairo_private cairo_int_status_t _cairo_tag_parse_link_attributes (const char *attributes, cairo_link_attrs_t *link_attrs); cairo_private cairo_int_status_t _cairo_tag_parse_dest_attributes (const char *attributes, cairo_dest_attrs_t *dest_attrs); +cairo_private cairo_int_status_t +_cairo_tag_parse_ccitt_params (const char *attributes, cairo_ccitt_params_t *dest_attrs); + #endif /* CAIRO_TAG_ATTRIBUTES_PRIVATE_H */ diff --git a/src/cairo-tag-attributes.c b/src/cairo-tag-attributes.c index b3d08f6f7..64173402d 100644 --- a/src/cairo-tag-attributes.c +++ b/src/cairo-tag-attributes.c @@ -113,6 +113,36 @@ static attribute_spec_t _link_attrib_spec[] = { NULL } }; +/* + * Required: + * Columns - width of the image in pixels. + * Rows - height of the image in scan lines. + * + * Optional: + * K - An integer identifying the encoding scheme used. < 0 is 2 dimensional + * Group 4, = 0 is Group3 1 dimensional, > 0 is mixed 1 and 2 dimensional + * encoding. Default: 0. + * EndOfLine - If true end-of-line bit patterns are present. Default: false. + * EncodedByteAlign - If true the end of line is padded with 0 bits so the next + * line begins on a byte boundary. Default: false. + * EndOfBlock - If true the data contains an end-of-block pattern. Default: true. + * BlackIs1 - If true 1 bits are black pixels. Default: false. + * DamagedRowsBeforeError - Number of damages rows tolerated before an error + * occurs. Default: 0. + */ +static attribute_spec_t _ccitt_params_spec[] = +{ + { "Columns", ATTRIBUTE_INT }, + { "Rows", ATTRIBUTE_INT }, + { "K", ATTRIBUTE_INT }, + { "EndOfLine", ATTRIBUTE_BOOL }, + { "EncodedByteAlign", ATTRIBUTE_BOOL }, + { "EndOfBlock", ATTRIBUTE_BOOL }, + { "BlackIs1", ATTRIBUTE_BOOL }, + { "DamagedRowsBeforeError", ATTRIBUTE_INT }, + { NULL } +}; + typedef union { cairo_bool_t b; int i; @@ -569,3 +599,53 @@ _cairo_tag_parse_dest_attributes (const char *attributes, cairo_dest_attrs_t *de return status; } + +cairo_int_status_t +_cairo_tag_parse_ccitt_params (const char *attributes, cairo_ccitt_params_t *ccitt_params) +{ + cairo_list_t list; + cairo_int_status_t status; + attribute_t *attr; + + ccitt_params->columns = -1; + ccitt_params->rows = -1; + + /* set defaults */ + ccitt_params->k = 0; + ccitt_params->end_of_line = FALSE; + ccitt_params->encoded_byte_align = FALSE; + ccitt_params->end_of_block = TRUE; + ccitt_params->black_is_1 = FALSE; + ccitt_params->damaged_rows_before_error = 0; + + cairo_list_init (&list); + status = parse_attributes (attributes, _ccitt_params_spec, &list); + if (unlikely (status)) + goto cleanup; + + cairo_list_foreach_entry (attr, attribute_t, &list, link) + { + if (strcmp (attr->name, "Columns") == 0) { + ccitt_params->columns = attr->scalar.i; + } else if (strcmp (attr->name, "Rows") == 0) { + ccitt_params->rows = attr->scalar.i; + } else if (strcmp (attr->name, "K") == 0) { + ccitt_params->k = attr->scalar.i; + } else if (strcmp (attr->name, "EndOfLine") == 0) { + ccitt_params->end_of_line = attr->scalar.b; + } else if (strcmp (attr->name, "EncodedByteAlign") == 0) { + ccitt_params->encoded_byte_align = attr->scalar.b; + } else if (strcmp (attr->name, "EndOfBlock") == 0) { + ccitt_params->end_of_block = attr->scalar.b; + } else if (strcmp (attr->name, "BlackIs1") == 0) { + ccitt_params->black_is_1 = attr->scalar.b; + } else if (strcmp (attr->name, "DamagedRowsBeforeError") == 0) { + ccitt_params->damaged_rows_before_error = attr->scalar.b; + } + } + + cleanup: + free_attributes_list (&list); + + return status; +} diff --git a/src/cairo.h b/src/cairo.h index 32fc88b17..671be5a89 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -2453,6 +2453,8 @@ cairo_surface_set_user_data (cairo_surface_t *surface, #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" +#define CAIRO_MIME_TYPE_CCITT_FAX "image/g3fax" +#define CAIRO_MIME_TYPE_CCITT_FAX_PARAMS "application/x-cairo.ccitt.params" cairo_public void cairo_surface_get_mime_data (cairo_surface_t *surface, diff --git a/test/ccitt.g3 b/test/ccitt.g3 new file mode 100644 index 000000000..b5ef99e3d --- /dev/null +++ b/test/ccitt.g3 @@ -0,0 +1,2 @@ +F#Z#І(ԍb#GG:#hҜ3Bд#B.1P1é":*ՈVx6o^ֿNZ7D{_Jd{kkf+
L j؏c_P(p2({(sxЂB}(} +(pDu(pQ
\ No newline at end of file diff --git a/test/mime-data.c b/test/mime-data.c index c744f5c9b..e6d1405bf 100644 --- a/test/mime-data.c +++ b/test/mime-data.c @@ -207,6 +207,56 @@ paint_jbig2_file (cairo_t *cr, int x, int y) } static cairo_test_status_t +paint_ccitt_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 *ccitt_image_filename = "ccitt.g3"; + const char *ccitt_image_params = "Columns=200 Rows=50 K=-1"; + + /* 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. + */ + + status = read_file (ctx, ccitt_image_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); + + /* Set the CCITT image data */ + status = cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_CCITT_FAX, + 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 CCITT image paramaters */ + status = cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, + (unsigned char *)ccitt_image_params, + strlen (ccitt_image_params), + NULL, NULL); + if (status) { + cairo_surface_destroy (image); + return cairo_test_status_from_status (ctx, status); + } + + cairo_set_source_surface (cr, image, x, y); + 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"; @@ -230,6 +280,10 @@ draw (cairo_t *cr, int width, int height) if (status) return status; + status = paint_ccitt_file (cr, 0, 250); + if (status) + return status; + return CAIRO_TEST_SUCCESS; } @@ -237,5 +291,5 @@ CAIRO_TEST (mime_data, "Check that the mime-data embedding works", "jpeg, api", /* keywords */ NULL, /* requirements */ - 200, 250, + 200, 300, NULL, draw) diff --git a/test/reference/mime-data.base.argb32.ref.png b/test/reference/mime-data.base.argb32.ref.png Binary files differindex 4bc007c8a..03a0247a3 100644 --- a/test/reference/mime-data.base.argb32.ref.png +++ b/test/reference/mime-data.base.argb32.ref.png diff --git a/test/reference/mime-data.base.rgb24.ref.png b/test/reference/mime-data.base.rgb24.ref.png Binary files differindex 4bc007c8a..03a0247a3 100644 --- a/test/reference/mime-data.base.rgb24.ref.png +++ b/test/reference/mime-data.base.rgb24.ref.png diff --git a/test/reference/mime-data.pdf.ref.png b/test/reference/mime-data.pdf.ref.png Binary files differindex 76c17f8de..a669c8f2b 100644 --- a/test/reference/mime-data.pdf.ref.png +++ b/test/reference/mime-data.pdf.ref.png diff --git a/test/reference/mime-data.ps.ref.png b/test/reference/mime-data.ps.ref.png Binary files differindex 7ec7d9b2c..bf834020a 100644 --- a/test/reference/mime-data.ps.ref.png +++ b/test/reference/mime-data.ps.ref.png diff --git a/test/reference/mime-data.ref.png b/test/reference/mime-data.ref.png Binary files differindex 4bc007c8a..03a0247a3 100644 --- a/test/reference/mime-data.ref.png +++ b/test/reference/mime-data.ref.png diff --git a/test/reference/mime-data.script.ref.png b/test/reference/mime-data.script.ref.png Binary files differindex 07691b101..e48618033 100644 --- a/test/reference/mime-data.script.ref.png +++ b/test/reference/mime-data.script.ref.png diff --git a/test/reference/mime-data.svg.ref.png b/test/reference/mime-data.svg.ref.png Binary files differindex a4bbb1b6c..1889c6e8d 100644 --- a/test/reference/mime-data.svg.ref.png +++ b/test/reference/mime-data.svg.ref.png |