13ff48bf5SDavid du Colombier /* Copyright (C) 2000 Aladdin Enterprises. All rights reserved.
23ff48bf5SDavid du Colombier
3*593dc095SDavid du Colombier This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier implied.
53ff48bf5SDavid du Colombier
6*593dc095SDavid du Colombier This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier of the license contained in the file LICENSE in this distribution.
93ff48bf5SDavid du Colombier
10*593dc095SDavid du Colombier For more information about licensing, please refer to
11*593dc095SDavid du Colombier http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier San Rafael, CA 94903, U.S.A., +1(415)492-9861.
153ff48bf5SDavid du Colombier */
163ff48bf5SDavid du Colombier
17*593dc095SDavid du Colombier /* $Id: gdevpdfj.c,v 1.49 2005/09/06 13:47:10 leonardo Exp $ */
183ff48bf5SDavid du Colombier /* Image-writing utilities for pdfwrite driver */
193ff48bf5SDavid du Colombier #include "memory_.h"
203ff48bf5SDavid du Colombier #include "string_.h"
213ff48bf5SDavid du Colombier #include "gx.h"
223ff48bf5SDavid du Colombier #include "gserrors.h"
233ff48bf5SDavid du Colombier #include "gdevpdfx.h"
243ff48bf5SDavid du Colombier #include "gdevpdfg.h"
253ff48bf5SDavid du Colombier #include "gdevpdfo.h"
263ff48bf5SDavid du Colombier #include "gxcspace.h"
273ff48bf5SDavid du Colombier #include "gsiparm4.h"
28*593dc095SDavid du Colombier #include "gdevpsds.h"
29*593dc095SDavid du Colombier #include "spngpx.h"
303ff48bf5SDavid du Colombier
313ff48bf5SDavid du Colombier #define CHECK(expr)\
323ff48bf5SDavid du Colombier BEGIN if ((code = (expr)) < 0) return code; END
333ff48bf5SDavid du Colombier
343ff48bf5SDavid du Colombier /* GC descriptors */
353ff48bf5SDavid du Colombier public_st_pdf_image_writer();
36*593dc095SDavid du Colombier private ENUM_PTRS_WITH(pdf_image_writer_enum_ptrs, pdf_image_writer *piw)
37*593dc095SDavid du Colombier index -= 4;
38*593dc095SDavid du Colombier if (index < psdf_binary_writer_max_ptrs * piw->alt_writer_count) {
39*593dc095SDavid du Colombier gs_ptr_type_t ret =
40*593dc095SDavid du Colombier ENUM_USING(st_psdf_binary_writer, &piw->binary[index / psdf_binary_writer_max_ptrs],
41*593dc095SDavid du Colombier sizeof(psdf_binary_writer), index % psdf_binary_writer_max_ptrs);
42*593dc095SDavid du Colombier
43*593dc095SDavid du Colombier if (ret == 0) /* don't stop early */
44*593dc095SDavid du Colombier ENUM_RETURN(0);
45*593dc095SDavid du Colombier return ret;
46*593dc095SDavid du Colombier }
47*593dc095SDavid du Colombier return 0;
48*593dc095SDavid du Colombier case 0: ENUM_RETURN(piw->pres);
49*593dc095SDavid du Colombier case 1: ENUM_RETURN(piw->data);
50*593dc095SDavid du Colombier case 2: ENUM_RETURN(piw->named);
51*593dc095SDavid du Colombier case 3: ENUM_RETURN(piw->pres_mask);
52*593dc095SDavid du Colombier ENUM_PTRS_END
RELOC_PTRS_WITH(pdf_image_writer_reloc_ptrs,pdf_image_writer * piw)53*593dc095SDavid du Colombier private RELOC_PTRS_WITH(pdf_image_writer_reloc_ptrs, pdf_image_writer *piw)
54*593dc095SDavid du Colombier {
55*593dc095SDavid du Colombier int i;
56*593dc095SDavid du Colombier
57*593dc095SDavid du Colombier for (i = 0; i < piw->alt_writer_count; ++i)
58*593dc095SDavid du Colombier RELOC_USING(st_psdf_binary_writer, &piw->binary[i],
59*593dc095SDavid du Colombier sizeof(psdf_binary_writer));
60*593dc095SDavid du Colombier RELOC_VAR(piw->pres);
61*593dc095SDavid du Colombier RELOC_VAR(piw->data);
62*593dc095SDavid du Colombier RELOC_VAR(piw->named);
63*593dc095SDavid du Colombier RELOC_VAR(piw->pres_mask);
64*593dc095SDavid du Colombier }
65*593dc095SDavid du Colombier RELOC_PTRS_END
663ff48bf5SDavid du Colombier
673ff48bf5SDavid du Colombier /* ---------------- Image stream dictionaries ---------------- */
683ff48bf5SDavid du Colombier
693ff48bf5SDavid du Colombier const pdf_image_names_t pdf_image_names_full = {
703ff48bf5SDavid du Colombier { PDF_COLOR_SPACE_NAMES },
713ff48bf5SDavid du Colombier { PDF_FILTER_NAMES },
723ff48bf5SDavid du Colombier PDF_IMAGE_PARAM_NAMES
733ff48bf5SDavid du Colombier };
743ff48bf5SDavid du Colombier const pdf_image_names_t pdf_image_names_short = {
753ff48bf5SDavid du Colombier { PDF_COLOR_SPACE_NAMES_SHORT },
763ff48bf5SDavid du Colombier { PDF_FILTER_NAMES_SHORT },
773ff48bf5SDavid du Colombier PDF_IMAGE_PARAM_NAMES_SHORT
783ff48bf5SDavid du Colombier };
793ff48bf5SDavid du Colombier
803ff48bf5SDavid du Colombier /* Store the values of image parameters other than filters. */
813ff48bf5SDavid du Colombier /* pdev is used only for updating procsets. */
823ff48bf5SDavid du Colombier /* pcsvalue is not used for masks. */
833ff48bf5SDavid du Colombier private int
pdf_put_pixel_image_values(cos_dict_t * pcd,gx_device_pdf * pdev,const gs_pixel_image_t * pim,const gs_color_space * pcs,const pdf_image_names_t * pin,const cos_value_t * pcsvalue)843ff48bf5SDavid du Colombier pdf_put_pixel_image_values(cos_dict_t *pcd, gx_device_pdf *pdev,
853ff48bf5SDavid du Colombier const gs_pixel_image_t *pim,
863ff48bf5SDavid du Colombier const gs_color_space *pcs,
873ff48bf5SDavid du Colombier const pdf_image_names_t *pin,
883ff48bf5SDavid du Colombier const cos_value_t *pcsvalue)
893ff48bf5SDavid du Colombier {
903ff48bf5SDavid du Colombier int num_components;
913ff48bf5SDavid du Colombier float indexed_decode[2];
923ff48bf5SDavid du Colombier const float *default_decode = NULL;
933ff48bf5SDavid du Colombier int code;
943ff48bf5SDavid du Colombier
953ff48bf5SDavid du Colombier if (pcs) {
963ff48bf5SDavid du Colombier CHECK(cos_dict_put_c_key(pcd, pin->ColorSpace, pcsvalue));
973ff48bf5SDavid du Colombier pdf_color_space_procsets(pdev, pcs);
983ff48bf5SDavid du Colombier num_components = gs_color_space_num_components(pcs);
993ff48bf5SDavid du Colombier if (gs_color_space_get_index(pcs) == gs_color_space_index_Indexed) {
1003ff48bf5SDavid du Colombier indexed_decode[0] = 0;
101*593dc095SDavid du Colombier indexed_decode[1] = (float)((1 << pim->BitsPerComponent) - 1);
1023ff48bf5SDavid du Colombier default_decode = indexed_decode;
1033ff48bf5SDavid du Colombier }
1043ff48bf5SDavid du Colombier } else
1053ff48bf5SDavid du Colombier num_components = 1;
1063ff48bf5SDavid du Colombier CHECK(cos_dict_put_c_key_int(pcd, pin->Width, pim->Width));
1073ff48bf5SDavid du Colombier CHECK(cos_dict_put_c_key_int(pcd, pin->Height, pim->Height));
1083ff48bf5SDavid du Colombier CHECK(cos_dict_put_c_key_int(pcd, pin->BitsPerComponent,
1093ff48bf5SDavid du Colombier pim->BitsPerComponent));
1103ff48bf5SDavid du Colombier {
1113ff48bf5SDavid du Colombier int i;
1123ff48bf5SDavid du Colombier
113*593dc095SDavid du Colombier for (i = 0; i < num_components * 2; ++i) {
1143ff48bf5SDavid du Colombier if (pim->Decode[i] !=
1153ff48bf5SDavid du Colombier (default_decode ? default_decode[i] : i & 1)
1163ff48bf5SDavid du Colombier )
1173ff48bf5SDavid du Colombier break;
118*593dc095SDavid du Colombier }
1193ff48bf5SDavid du Colombier if (i < num_components * 2) {
1203ff48bf5SDavid du Colombier cos_array_t *pca =
1213ff48bf5SDavid du Colombier cos_array_alloc(pdev, "pdf_put_pixel_image_values(decode)");
1223ff48bf5SDavid du Colombier
1233ff48bf5SDavid du Colombier if (pca == 0)
1243ff48bf5SDavid du Colombier return_error(gs_error_VMerror);
125*593dc095SDavid du Colombier if (pcs == NULL) {
126*593dc095SDavid du Colombier /* 269-01.ps sets /Decode[0 100] with a mask image. */
127*593dc095SDavid du Colombier for (i = 0; i < num_components * 2; ++i)
128*593dc095SDavid du Colombier CHECK(cos_array_add_real(pca, min(pim->Decode[i], 1)));
129*593dc095SDavid du Colombier } else {
1303ff48bf5SDavid du Colombier for (i = 0; i < num_components * 2; ++i)
1313ff48bf5SDavid du Colombier CHECK(cos_array_add_real(pca, pim->Decode[i]));
132*593dc095SDavid du Colombier }
1333ff48bf5SDavid du Colombier CHECK(cos_dict_put_c_key_object(pcd, pin->Decode,
1343ff48bf5SDavid du Colombier COS_OBJECT(pca)));
1353ff48bf5SDavid du Colombier }
1363ff48bf5SDavid du Colombier }
1373ff48bf5SDavid du Colombier if (pim->Interpolate)
1383ff48bf5SDavid du Colombier CHECK(cos_dict_put_c_strings(pcd, pin->Interpolate, "true"));
1393ff48bf5SDavid du Colombier return 0;
1403ff48bf5SDavid du Colombier }
1413ff48bf5SDavid du Colombier int
pdf_put_image_values(cos_dict_t * pcd,gx_device_pdf * pdev,const gs_pixel_image_t * pic,const pdf_image_names_t * pin,const cos_value_t * pcsvalue)1423ff48bf5SDavid du Colombier pdf_put_image_values(cos_dict_t *pcd, gx_device_pdf *pdev,
1433ff48bf5SDavid du Colombier const gs_pixel_image_t *pic,
1443ff48bf5SDavid du Colombier const pdf_image_names_t *pin,
1453ff48bf5SDavid du Colombier const cos_value_t *pcsvalue)
1463ff48bf5SDavid du Colombier {
1473ff48bf5SDavid du Colombier const gs_color_space *pcs = pic->ColorSpace;
1483ff48bf5SDavid du Colombier int code;
1493ff48bf5SDavid du Colombier
1503ff48bf5SDavid du Colombier switch (pic->type->index) {
1513ff48bf5SDavid du Colombier case 1: {
1523ff48bf5SDavid du Colombier const gs_image1_t *pim = (const gs_image1_t *)pic;
1533ff48bf5SDavid du Colombier
1543ff48bf5SDavid du Colombier if (pim->ImageMask) {
1553ff48bf5SDavid du Colombier CHECK(cos_dict_put_c_strings(pcd, pin->ImageMask, "true"));
1563ff48bf5SDavid du Colombier pdev->procsets |= ImageB;
1573ff48bf5SDavid du Colombier pcs = NULL;
1583ff48bf5SDavid du Colombier }
1593ff48bf5SDavid du Colombier }
1603ff48bf5SDavid du Colombier break;
1613ff48bf5SDavid du Colombier case 3: {
1623ff48bf5SDavid du Colombier /*
1633ff48bf5SDavid du Colombier * Clients must treat this as a special case: they must call
1643ff48bf5SDavid du Colombier * pdf_put_image_values for the MaskDict separately, and must
1653ff48bf5SDavid du Colombier * add the Mask entry to the main image stream (dictionary).
1663ff48bf5SDavid du Colombier */
1673ff48bf5SDavid du Colombier /*const gs_image3_t *pim = (const gs_image3_t *)pic;*/
1683ff48bf5SDavid du Colombier
1693ff48bf5SDavid du Colombier /* Masked images are only supported starting in PDF 1.3. */
1703ff48bf5SDavid du Colombier if (pdev->CompatibilityLevel < 1.3)
1713ff48bf5SDavid du Colombier return_error(gs_error_rangecheck);
1723ff48bf5SDavid du Colombier }
1733ff48bf5SDavid du Colombier break;
1743ff48bf5SDavid du Colombier case 4: {
1753ff48bf5SDavid du Colombier const gs_image4_t *pim = (const gs_image4_t *)pic;
1763ff48bf5SDavid du Colombier int num_components = gs_color_space_num_components(pcs);
1773ff48bf5SDavid du Colombier cos_array_t *pca;
1783ff48bf5SDavid du Colombier int i;
1793ff48bf5SDavid du Colombier
1803ff48bf5SDavid du Colombier /* Masked images are only supported starting in PDF 1.3. */
1813ff48bf5SDavid du Colombier if (pdev->CompatibilityLevel < 1.3)
182*593dc095SDavid du Colombier break; /* Will convert into an imagemask with a pattern color. */
1833ff48bf5SDavid du Colombier pca = cos_array_alloc(pdev, "pdf_put_image_values(mask)");
1843ff48bf5SDavid du Colombier if (pca == 0)
1853ff48bf5SDavid du Colombier return_error(gs_error_VMerror);
1863ff48bf5SDavid du Colombier for (i = 0; i < num_components; ++i) {
1873ff48bf5SDavid du Colombier int lo, hi;
1883ff48bf5SDavid du Colombier
1893ff48bf5SDavid du Colombier if (pim->MaskColor_is_range)
1903ff48bf5SDavid du Colombier lo = pim->MaskColor[i * 2], hi = pim->MaskColor[i * 2 + 1];
1913ff48bf5SDavid du Colombier else
1923ff48bf5SDavid du Colombier lo = hi = pim->MaskColor[i];
1933ff48bf5SDavid du Colombier CHECK(cos_array_add_int(pca, lo));
1943ff48bf5SDavid du Colombier CHECK(cos_array_add_int(pca, hi));
1953ff48bf5SDavid du Colombier }
1963ff48bf5SDavid du Colombier CHECK(cos_dict_put_c_key_object(pcd, "/Mask", COS_OBJECT(pca)));
1973ff48bf5SDavid du Colombier }
1983ff48bf5SDavid du Colombier break;
1993ff48bf5SDavid du Colombier default:
2003ff48bf5SDavid du Colombier return_error(gs_error_rangecheck);
2013ff48bf5SDavid du Colombier }
2023ff48bf5SDavid du Colombier return pdf_put_pixel_image_values(pcd, pdev, pic, pcs, pin, pcsvalue);
2033ff48bf5SDavid du Colombier }
2043ff48bf5SDavid du Colombier
2053ff48bf5SDavid du Colombier /* Store filters for an image. */
2063ff48bf5SDavid du Colombier /* Currently this only saves parameters for CCITTFaxDecode. */
2073ff48bf5SDavid du Colombier int
pdf_put_image_filters(cos_dict_t * pcd,gx_device_pdf * pdev,const psdf_binary_writer * pbw,const pdf_image_names_t * pin)2083ff48bf5SDavid du Colombier pdf_put_image_filters(cos_dict_t *pcd, gx_device_pdf *pdev,
2093ff48bf5SDavid du Colombier const psdf_binary_writer * pbw,
2103ff48bf5SDavid du Colombier const pdf_image_names_t *pin)
2113ff48bf5SDavid du Colombier {
2123ff48bf5SDavid du Colombier return pdf_put_filters(pcd, pdev, pbw->strm, &pin->filter_names);
2133ff48bf5SDavid du Colombier }
2143ff48bf5SDavid du Colombier
2153ff48bf5SDavid du Colombier /* ---------------- Image writing ---------------- */
2163ff48bf5SDavid du Colombier
2173ff48bf5SDavid du Colombier /*
2183ff48bf5SDavid du Colombier * Fill in the image parameters for a device space bitmap.
2193ff48bf5SDavid du Colombier * PDF images are always specified top-to-bottom.
2203ff48bf5SDavid du Colombier * data_h is the actual number of data rows, which may be less than h.
2213ff48bf5SDavid du Colombier */
2223ff48bf5SDavid du Colombier void
pdf_make_bitmap_matrix(gs_matrix * pmat,int x,int y,int w,int h,int h_actual)2233ff48bf5SDavid du Colombier pdf_make_bitmap_matrix(gs_matrix * pmat, int x, int y, int w, int h,
2243ff48bf5SDavid du Colombier int h_actual)
2253ff48bf5SDavid du Colombier {
226*593dc095SDavid du Colombier pmat->xx = (float)w;
2273ff48bf5SDavid du Colombier pmat->xy = 0;
2283ff48bf5SDavid du Colombier pmat->yx = 0;
229*593dc095SDavid du Colombier pmat->yy = (float)(-h_actual);
230*593dc095SDavid du Colombier pmat->tx = (float)x;
231*593dc095SDavid du Colombier pmat->ty = (float)(y + h);
2323ff48bf5SDavid du Colombier }
2333ff48bf5SDavid du Colombier
2343ff48bf5SDavid du Colombier /*
2353ff48bf5SDavid du Colombier * Put out the gsave and matrix for an image. y_scale adjusts the matrix
2363ff48bf5SDavid du Colombier * for images that end prematurely.
2373ff48bf5SDavid du Colombier */
2383ff48bf5SDavid du Colombier void
pdf_put_image_matrix(gx_device_pdf * pdev,const gs_matrix * pmat,floatp y_scale)2393ff48bf5SDavid du Colombier pdf_put_image_matrix(gx_device_pdf * pdev, const gs_matrix * pmat,
2403ff48bf5SDavid du Colombier floatp y_scale)
2413ff48bf5SDavid du Colombier {
2423ff48bf5SDavid du Colombier gs_matrix imat;
2433ff48bf5SDavid du Colombier
2443ff48bf5SDavid du Colombier gs_matrix_translate(pmat, 0.0, 1.0 - y_scale, &imat);
2453ff48bf5SDavid du Colombier gs_matrix_scale(&imat, 1.0, y_scale, &imat);
2463ff48bf5SDavid du Colombier pdf_put_matrix(pdev, "q ", &imat, "cm\n");
2473ff48bf5SDavid du Colombier }
2483ff48bf5SDavid du Colombier
2493ff48bf5SDavid du Colombier /* Put out a reference to an image resource. */
2503ff48bf5SDavid du Colombier int
pdf_do_image_by_id(gx_device_pdf * pdev,double scale,const gs_matrix * pimat,bool in_contents,gs_id id)251*593dc095SDavid du Colombier pdf_do_image_by_id(gx_device_pdf * pdev, double scale,
252*593dc095SDavid du Colombier const gs_matrix * pimat, bool in_contents, gs_id id)
2533ff48bf5SDavid du Colombier {
254*593dc095SDavid du Colombier /* fixme : in_contents is always true (there are no calls with false). */
2553ff48bf5SDavid du Colombier if (in_contents) {
2563ff48bf5SDavid du Colombier int code = pdf_open_contents(pdev, PDF_IN_STREAM);
2573ff48bf5SDavid du Colombier
2583ff48bf5SDavid du Colombier if (code < 0)
2593ff48bf5SDavid du Colombier return code;
2603ff48bf5SDavid du Colombier }
261*593dc095SDavid du Colombier if (pimat)
262*593dc095SDavid du Colombier pdf_put_image_matrix(pdev, pimat, scale);
263*593dc095SDavid du Colombier pprintld1(pdev->strm, "/R%ld Do\nQ\n", id);
264*593dc095SDavid du Colombier return pdf_register_charproc_resource(pdev, id, resourceXObject);
265*593dc095SDavid du Colombier }
266*593dc095SDavid du Colombier int
pdf_do_image(gx_device_pdf * pdev,const pdf_resource_t * pres,const gs_matrix * pimat,bool in_contents)267*593dc095SDavid du Colombier pdf_do_image(gx_device_pdf * pdev, const pdf_resource_t * pres,
268*593dc095SDavid du Colombier const gs_matrix * pimat, bool in_contents)
269*593dc095SDavid du Colombier {
270*593dc095SDavid du Colombier /* fixme : call pdf_do_image_by_id when pimam == NULL. */
271*593dc095SDavid du Colombier double scale = 1;
272*593dc095SDavid du Colombier
2733ff48bf5SDavid du Colombier if (pimat) {
2743ff48bf5SDavid du Colombier /* Adjust the matrix to account for short images. */
2753ff48bf5SDavid du Colombier const pdf_x_object_t *const pxo = (const pdf_x_object_t *)pres;
276*593dc095SDavid du Colombier scale = (double)pxo->data_height / pxo->height;
2773ff48bf5SDavid du Colombier }
278*593dc095SDavid du Colombier return pdf_do_image_by_id(pdev, scale, pimat, in_contents, pdf_resource_id(pres));
2793ff48bf5SDavid du Colombier }
2803ff48bf5SDavid du Colombier
2813ff48bf5SDavid du Colombier /* ------ Begin / finish ------ */
2823ff48bf5SDavid du Colombier
283*593dc095SDavid du Colombier /* Initialize image writer. */
284*593dc095SDavid du Colombier void
pdf_image_writer_init(pdf_image_writer * piw)285*593dc095SDavid du Colombier pdf_image_writer_init(pdf_image_writer * piw)
286*593dc095SDavid du Colombier {
287*593dc095SDavid du Colombier memset(piw, 0, sizeof(*piw));
288*593dc095SDavid du Colombier piw->alt_writer_count = 1; /* Default. */
289*593dc095SDavid du Colombier }
290*593dc095SDavid du Colombier
2913ff48bf5SDavid du Colombier /*
292*593dc095SDavid du Colombier * Begin writing an image, creating the resource if not in-line, and setting
293*593dc095SDavid du Colombier * up the binary writer. If pnamed != 0, it is a stream object created by a
294*593dc095SDavid du Colombier * NI pdfmark.
2953ff48bf5SDavid du Colombier */
2963ff48bf5SDavid du Colombier int
pdf_begin_write_image(gx_device_pdf * pdev,pdf_image_writer * piw,gx_bitmap_id id,int w,int h,cos_dict_t * named,bool in_line)2973ff48bf5SDavid du Colombier pdf_begin_write_image(gx_device_pdf * pdev, pdf_image_writer * piw,
298*593dc095SDavid du Colombier gx_bitmap_id id, int w, int h, cos_dict_t *named,
2993ff48bf5SDavid du Colombier bool in_line)
3003ff48bf5SDavid du Colombier {
3013ff48bf5SDavid du Colombier /* Patch pdev->strm so the right stream gets into the writer. */
3023ff48bf5SDavid du Colombier stream *save_strm = pdev->strm;
303*593dc095SDavid du Colombier cos_stream_t *data;
304*593dc095SDavid du Colombier bool mask = (piw->data != NULL);
305*593dc095SDavid du Colombier int alt_stream_index = (!mask ? 0 : piw->alt_writer_count);
3063ff48bf5SDavid du Colombier int code;
3073ff48bf5SDavid du Colombier
3083ff48bf5SDavid du Colombier if (in_line) {
3093ff48bf5SDavid du Colombier piw->pres = 0;
3103ff48bf5SDavid du Colombier piw->pin = &pdf_image_names_short;
311*593dc095SDavid du Colombier data = cos_stream_alloc(pdev, "pdf_begin_image_data");
312*593dc095SDavid du Colombier if (data == 0)
3133ff48bf5SDavid du Colombier return_error(gs_error_VMerror);
3143ff48bf5SDavid du Colombier piw->end_string = " Q";
315*593dc095SDavid du Colombier piw->named = 0; /* must have named == 0 */
3163ff48bf5SDavid du Colombier } else {
3173ff48bf5SDavid du Colombier pdf_x_object_t *pxo;
3183ff48bf5SDavid du Colombier cos_stream_t *pcos;
319*593dc095SDavid du Colombier pdf_resource_t *pres;
3203ff48bf5SDavid du Colombier
321*593dc095SDavid du Colombier /*
322*593dc095SDavid du Colombier * Note that if named != 0, there are two objects with the same id
323*593dc095SDavid du Colombier * while the image is being accumulated: named, and pres->object.
324*593dc095SDavid du Colombier */
325*593dc095SDavid du Colombier code = pdf_alloc_resource(pdev, resourceXObject, id, &pres,
326*593dc095SDavid du Colombier (named ? named->id : -1L));
3273ff48bf5SDavid du Colombier if (code < 0)
3283ff48bf5SDavid du Colombier return code;
329*593dc095SDavid du Colombier *(mask ? &piw->pres_mask : &piw->pres) = pres;
330*593dc095SDavid du Colombier cos_become(pres->object, cos_type_stream);
331*593dc095SDavid du Colombier pres->rid = id;
3323ff48bf5SDavid du Colombier piw->pin = &pdf_image_names_full;
333*593dc095SDavid du Colombier pxo = (pdf_x_object_t *)pres;
3343ff48bf5SDavid du Colombier pcos = (cos_stream_t *)pxo->object;
3353ff48bf5SDavid du Colombier CHECK(cos_dict_put_c_strings(cos_stream_dict(pcos), "/Subtype",
3363ff48bf5SDavid du Colombier "/Image"));
3373ff48bf5SDavid du Colombier pxo->width = w;
3383ff48bf5SDavid du Colombier pxo->height = h;
3393ff48bf5SDavid du Colombier /* Initialize data_height for the benefit of copy_{mono,color}. */
3403ff48bf5SDavid du Colombier pxo->data_height = h;
341*593dc095SDavid du Colombier data = pcos;
342*593dc095SDavid du Colombier if (!mask)
343*593dc095SDavid du Colombier piw->named = named;
3443ff48bf5SDavid du Colombier }
3453ff48bf5SDavid du Colombier pdev->strm = pdev->streams.strm;
346*593dc095SDavid du Colombier pdev->strm = cos_write_stream_alloc(data, pdev, "pdf_begin_write_image");
347*593dc095SDavid du Colombier if (pdev->strm == 0)
348*593dc095SDavid du Colombier return_error(gs_error_VMerror);
349*593dc095SDavid du Colombier if (!mask)
350*593dc095SDavid du Colombier piw->data = data;
351*593dc095SDavid du Colombier piw->height = h;
352*593dc095SDavid du Colombier code = psdf_begin_binary((gx_device_psdf *) pdev, &piw->binary[alt_stream_index]);
353*593dc095SDavid du Colombier piw->binary[alt_stream_index].target = NULL; /* We don't need target with cos_write_stream. */
3543ff48bf5SDavid du Colombier pdev->strm = save_strm;
3553ff48bf5SDavid du Colombier return code;
3563ff48bf5SDavid du Colombier }
3573ff48bf5SDavid du Colombier
358*593dc095SDavid du Colombier /*
359*593dc095SDavid du Colombier * Make alternative stream for image compression choice.
360*593dc095SDavid du Colombier */
361*593dc095SDavid du Colombier int
pdf_make_alt_stream(gx_device_pdf * pdev,psdf_binary_writer * pbw)362*593dc095SDavid du Colombier pdf_make_alt_stream(gx_device_pdf * pdev, psdf_binary_writer * pbw)
363*593dc095SDavid du Colombier {
364*593dc095SDavid du Colombier stream *save_strm = pdev->strm;
365*593dc095SDavid du Colombier cos_stream_t *pcos = cos_stream_alloc(pdev, "pdf_make_alt_stream");
366*593dc095SDavid du Colombier int code;
367*593dc095SDavid du Colombier
368*593dc095SDavid du Colombier if (pcos == 0)
369*593dc095SDavid du Colombier return_error(gs_error_VMerror);
370*593dc095SDavid du Colombier pcos->id = 0;
371*593dc095SDavid du Colombier CHECK(cos_dict_put_c_strings(cos_stream_dict(pcos), "/Subtype", "/Image"));
372*593dc095SDavid du Colombier pbw->strm = cos_write_stream_alloc(pcos, pdev, "pdf_make_alt_stream");
373*593dc095SDavid du Colombier if (pbw->strm == 0)
374*593dc095SDavid du Colombier return_error(gs_error_VMerror);
375*593dc095SDavid du Colombier pbw->dev = (gx_device_psdf *)pdev;
376*593dc095SDavid du Colombier pbw->memory = pdev->pdf_memory;
377*593dc095SDavid du Colombier pdev->strm = pbw->strm;
378*593dc095SDavid du Colombier code = psdf_begin_binary((gx_device_psdf *) pdev, pbw);
379*593dc095SDavid du Colombier pdev->strm = save_strm;
380*593dc095SDavid du Colombier pbw->target = NULL; /* We don't need target with cos_write_stream. */
381*593dc095SDavid du Colombier return code;
382*593dc095SDavid du Colombier }
383*593dc095SDavid du Colombier
3843ff48bf5SDavid du Colombier /* Begin writing the image data, setting up the dictionary and filters. */
3853ff48bf5SDavid du Colombier int
pdf_begin_image_data(gx_device_pdf * pdev,pdf_image_writer * piw,const gs_pixel_image_t * pim,const cos_value_t * pcsvalue,int alt_writer_index)3863ff48bf5SDavid du Colombier pdf_begin_image_data(gx_device_pdf * pdev, pdf_image_writer * piw,
387*593dc095SDavid du Colombier const gs_pixel_image_t * pim, const cos_value_t *pcsvalue,
388*593dc095SDavid du Colombier int alt_writer_index)
3893ff48bf5SDavid du Colombier {
390*593dc095SDavid du Colombier
391*593dc095SDavid du Colombier cos_stream_t *s = cos_stream_from_pipeline(piw->binary[alt_writer_index].strm);
392*593dc095SDavid du Colombier cos_dict_t *pcd = cos_stream_dict(s);
3933ff48bf5SDavid du Colombier int code = pdf_put_image_values(pcd, pdev, pim, piw->pin, pcsvalue);
3943ff48bf5SDavid du Colombier
3953ff48bf5SDavid du Colombier if (code >= 0)
396*593dc095SDavid du Colombier code = pdf_put_image_filters(pcd, pdev, &piw->binary[alt_writer_index], piw->pin);
3973ff48bf5SDavid du Colombier if (code < 0) {
3983ff48bf5SDavid du Colombier if (!piw->pres)
3993ff48bf5SDavid du Colombier COS_FREE(piw->data, "pdf_begin_image_data");
4003ff48bf5SDavid du Colombier piw->data = 0;
4013ff48bf5SDavid du Colombier }
4023ff48bf5SDavid du Colombier return code;
4033ff48bf5SDavid du Colombier }
4043ff48bf5SDavid du Colombier
405*593dc095SDavid du Colombier /* Complete image data. */
406*593dc095SDavid du Colombier int
pdf_complete_image_data(gx_device_pdf * pdev,pdf_image_writer * piw,int data_h,int width,int bits_per_pixel)407*593dc095SDavid du Colombier pdf_complete_image_data(gx_device_pdf *pdev, pdf_image_writer *piw, int data_h,
408*593dc095SDavid du Colombier int width, int bits_per_pixel)
409*593dc095SDavid du Colombier {
410*593dc095SDavid du Colombier if (data_h != piw->height) {
411*593dc095SDavid du Colombier if (piw->binary[0].strm->procs.process == s_DCTE_template.process ||
412*593dc095SDavid du Colombier piw->binary[0].strm->procs.process == s_PNGPE_template.process ) {
413*593dc095SDavid du Colombier /* Since DCTE and PNGPE can't safely close with incomplete data,
414*593dc095SDavid du Colombier we add stub data to complete the stream.
415*593dc095SDavid du Colombier */
416*593dc095SDavid du Colombier int bytes_per_line = (width * bits_per_pixel + 7) / 8;
417*593dc095SDavid du Colombier int lines_left = piw->height - data_h;
418*593dc095SDavid du Colombier byte buf[256];
419*593dc095SDavid du Colombier const uint lb = sizeof(buf);
420*593dc095SDavid du Colombier int i, l, status;
421*593dc095SDavid du Colombier uint ignore;
422*593dc095SDavid du Colombier
423*593dc095SDavid du Colombier memset(buf, 128, lb);
424*593dc095SDavid du Colombier for (; lines_left; lines_left--)
425*593dc095SDavid du Colombier for (i = 0; i < piw->alt_writer_count; i++) {
426*593dc095SDavid du Colombier for (l = bytes_per_line; l > 0; l -= lb)
427*593dc095SDavid du Colombier if ((status = sputs(piw->binary[i].strm, buf, min(l, lb),
428*593dc095SDavid du Colombier &ignore)) < 0)
429*593dc095SDavid du Colombier return_error(gs_error_ioerror);
430*593dc095SDavid du Colombier }
431*593dc095SDavid du Colombier }
432*593dc095SDavid du Colombier }
433*593dc095SDavid du Colombier return 0;
434*593dc095SDavid du Colombier }
435*593dc095SDavid du Colombier
4363ff48bf5SDavid du Colombier /* Finish writing the binary image data. */
4373ff48bf5SDavid du Colombier int
pdf_end_image_binary(gx_device_pdf * pdev,pdf_image_writer * piw,int data_h)4383ff48bf5SDavid du Colombier pdf_end_image_binary(gx_device_pdf *pdev, pdf_image_writer *piw, int data_h)
4393ff48bf5SDavid du Colombier {
440*593dc095SDavid du Colombier int code, code1 = 0;
4413ff48bf5SDavid du Colombier
442*593dc095SDavid du Colombier if (piw->alt_writer_count > 2)
443*593dc095SDavid du Colombier code = pdf_choose_compression(piw, true);
444*593dc095SDavid du Colombier else
445*593dc095SDavid du Colombier code = psdf_end_binary(&piw->binary[0]);
4463ff48bf5SDavid du Colombier /* If the image ended prematurely, update the Height. */
4473ff48bf5SDavid du Colombier if (data_h != piw->height)
448*593dc095SDavid du Colombier code1 = cos_dict_put_c_key_int(cos_stream_dict(piw->data),
4493ff48bf5SDavid du Colombier piw->pin->Height, data_h);
450*593dc095SDavid du Colombier return code < 0 ? code : code1;
4513ff48bf5SDavid du Colombier }
4523ff48bf5SDavid du Colombier
4533ff48bf5SDavid du Colombier /*
4543ff48bf5SDavid du Colombier * Finish writing an image. If in-line, write the BI/dict/ID/data/EI and
4553ff48bf5SDavid du Colombier * return 1; if a resource, write the resource definition and return 0.
4563ff48bf5SDavid du Colombier */
4573ff48bf5SDavid du Colombier int
pdf_end_write_image(gx_device_pdf * pdev,pdf_image_writer * piw)4583ff48bf5SDavid du Colombier pdf_end_write_image(gx_device_pdf * pdev, pdf_image_writer * piw)
4593ff48bf5SDavid du Colombier {
4603ff48bf5SDavid du Colombier pdf_resource_t *pres = piw->pres;
4613ff48bf5SDavid du Colombier
4623ff48bf5SDavid du Colombier if (pres) { /* image resource */
463*593dc095SDavid du Colombier cos_object_t *const pco = pres->object;
464*593dc095SDavid du Colombier cos_stream_t *const pcs = (cos_stream_t *)pco;
465*593dc095SDavid du Colombier cos_dict_t *named = piw->named;
466*593dc095SDavid du Colombier int code;
467*593dc095SDavid du Colombier
468*593dc095SDavid du Colombier if (named) {
469*593dc095SDavid du Colombier if (pdev->ForOPDFRead) {
470*593dc095SDavid du Colombier code = cos_dict_put_c_key_bool(named, "/.Global", true);
471*593dc095SDavid du Colombier if (code < 0)
472*593dc095SDavid du Colombier return code;
4733ff48bf5SDavid du Colombier }
474*593dc095SDavid du Colombier /*
475*593dc095SDavid du Colombier * This image was named by NI. Copy any dictionary elements
476*593dc095SDavid du Colombier * from the named dictionary to the image stream, and then
477*593dc095SDavid du Colombier * associate the name with the stream.
478*593dc095SDavid du Colombier */
479*593dc095SDavid du Colombier code = cos_dict_move_all(cos_stream_dict(pcs), named);
480*593dc095SDavid du Colombier if (code < 0)
481*593dc095SDavid du Colombier return code;
482*593dc095SDavid du Colombier pres->named = true;
483*593dc095SDavid du Colombier /*
484*593dc095SDavid du Colombier * We need to make the entry in the name dictionary point to
485*593dc095SDavid du Colombier * the stream (pcs) rather than the object created by NI (named).
486*593dc095SDavid du Colombier * Unfortunately, we no longer know what dictionary to use.
487*593dc095SDavid du Colombier * Instead, overwrite the latter with the former's contents,
488*593dc095SDavid du Colombier * and change the only relevant pointer.
489*593dc095SDavid du Colombier */
490*593dc095SDavid du Colombier *(cos_object_t *)named = *pco;
491*593dc095SDavid du Colombier pres->object = COS_OBJECT(named);
492*593dc095SDavid du Colombier } else if (!pres->named) { /* named objects are written at the end */
493*593dc095SDavid du Colombier code = pdf_substitute_resource(pdev, &piw->pres, resourceXObject, NULL, false);
494*593dc095SDavid du Colombier if (code < 0)
495*593dc095SDavid du Colombier return code;
496*593dc095SDavid du Colombier /* Warning : If the substituted image used alternate streams,
497*593dc095SDavid du Colombier its space in the pdev->streams.strm file won't be released. */
498*593dc095SDavid du Colombier piw->pres->where_used |= pdev->used_mask;
499*593dc095SDavid du Colombier }
500*593dc095SDavid du Colombier code = pdf_add_resource(pdev, pdev->substream_Resources, "/XObject", piw->pres);
501*593dc095SDavid du Colombier if (code < 0)
502*593dc095SDavid du Colombier return code;
5033ff48bf5SDavid du Colombier return 0;
5043ff48bf5SDavid du Colombier } else { /* in-line image */
5053ff48bf5SDavid du Colombier stream *s = pdev->strm;
506*593dc095SDavid du Colombier uint KeyLength = pdev->KeyLength;
5073ff48bf5SDavid du Colombier
5083ff48bf5SDavid du Colombier stream_puts(s, "BI\n");
5093ff48bf5SDavid du Colombier cos_stream_elements_write(piw->data, pdev);
5103ff48bf5SDavid du Colombier stream_puts(s, (pdev->binary_ok ? "ID " : "ID\n"));
511*593dc095SDavid du Colombier pdev->KeyLength = 0; /* Disable encryption for the inline image. */
5123ff48bf5SDavid du Colombier cos_stream_contents_write(piw->data, pdev);
513*593dc095SDavid du Colombier pdev->KeyLength = KeyLength;
5143ff48bf5SDavid du Colombier pprints1(s, "\nEI%s\n", piw->end_string);
5153ff48bf5SDavid du Colombier COS_FREE(piw->data, "pdf_end_write_image");
5163ff48bf5SDavid du Colombier return 1;
5173ff48bf5SDavid du Colombier }
5183ff48bf5SDavid du Colombier }
5193ff48bf5SDavid du Colombier
5203ff48bf5SDavid du Colombier /* ------ Copy data ------ */
5213ff48bf5SDavid du Colombier
5223ff48bf5SDavid du Colombier /* Copy the data for a mask or monobit bitmap. */
5233ff48bf5SDavid du Colombier int
pdf_copy_mask_bits(stream * s,const byte * base,int sourcex,int raster,int w,int h,byte invert)5243ff48bf5SDavid du Colombier pdf_copy_mask_bits(stream *s, const byte *base, int sourcex, int raster,
5253ff48bf5SDavid du Colombier int w, int h, byte invert)
5263ff48bf5SDavid du Colombier {
5273ff48bf5SDavid du Colombier int yi;
5283ff48bf5SDavid du Colombier
5293ff48bf5SDavid du Colombier for (yi = 0; yi < h; ++yi) {
5303ff48bf5SDavid du Colombier const byte *data = base + yi * raster + (sourcex >> 3);
5313ff48bf5SDavid du Colombier int sbit = sourcex & 7;
5323ff48bf5SDavid du Colombier
5333ff48bf5SDavid du Colombier if (sbit == 0) {
5343ff48bf5SDavid du Colombier int nbytes = (w + 7) >> 3;
5353ff48bf5SDavid du Colombier int i;
5363ff48bf5SDavid du Colombier
5373ff48bf5SDavid du Colombier for (i = 0; i < nbytes; ++data, ++i)
538*593dc095SDavid du Colombier sputc(s, (byte)(*data ^ invert));
5393ff48bf5SDavid du Colombier } else {
5403ff48bf5SDavid du Colombier int wleft = w;
5413ff48bf5SDavid du Colombier int rbit = 8 - sbit;
5423ff48bf5SDavid du Colombier
5433ff48bf5SDavid du Colombier for (; wleft + sbit > 8; ++data, wleft -= 8)
544*593dc095SDavid du Colombier sputc(s, (byte)(((*data << sbit) + (data[1] >> rbit)) ^ invert));
5453ff48bf5SDavid du Colombier if (wleft > 0)
546*593dc095SDavid du Colombier sputc(s, (byte)(((*data << sbit) ^ invert) &
547*593dc095SDavid du Colombier (byte) (0xff00 >> wleft)));
5483ff48bf5SDavid du Colombier }
5493ff48bf5SDavid du Colombier }
5503ff48bf5SDavid du Colombier return 0;
5513ff48bf5SDavid du Colombier }
5523ff48bf5SDavid du Colombier
5533ff48bf5SDavid du Colombier /* Copy the data for a colored image (device pixels). */
5543ff48bf5SDavid du Colombier int
pdf_copy_color_bits(stream * s,const byte * base,int sourcex,int raster,int w,int h,int bytes_per_pixel)5553ff48bf5SDavid du Colombier pdf_copy_color_bits(stream *s, const byte *base, int sourcex, int raster,
5563ff48bf5SDavid du Colombier int w, int h, int bytes_per_pixel)
5573ff48bf5SDavid du Colombier {
5583ff48bf5SDavid du Colombier int yi;
5593ff48bf5SDavid du Colombier
5603ff48bf5SDavid du Colombier for (yi = 0; yi < h; ++yi) {
5613ff48bf5SDavid du Colombier uint ignore;
5623ff48bf5SDavid du Colombier
5633ff48bf5SDavid du Colombier sputs(s, base + sourcex * bytes_per_pixel + yi * raster,
5643ff48bf5SDavid du Colombier w * bytes_per_pixel, &ignore);
5653ff48bf5SDavid du Colombier }
5663ff48bf5SDavid du Colombier return 0;
5673ff48bf5SDavid du Colombier }
568*593dc095SDavid du Colombier
569*593dc095SDavid du Colombier /* Choose image compression - auxiliary procs */
much_bigger__DL(long l1,long l2)570*593dc095SDavid du Colombier private inline bool much_bigger__DL(long l1, long l2)
571*593dc095SDavid du Colombier {
572*593dc095SDavid du Colombier return l1 > 1024*1024 && l2 < l1 / 3;
573*593dc095SDavid du Colombier }
574*593dc095SDavid du Colombier private void
pdf_choose_compression_cos(pdf_image_writer * piw,cos_stream_t * s[2],bool force)575*593dc095SDavid du Colombier pdf_choose_compression_cos(pdf_image_writer *piw, cos_stream_t *s[2], bool force)
576*593dc095SDavid du Colombier { /* Assume s[0] is Flate, s[1] is DCT, s[2] is chooser. */
577*593dc095SDavid du Colombier long l0, l1;
578*593dc095SDavid du Colombier int k0, k1;
579*593dc095SDavid du Colombier
580*593dc095SDavid du Colombier l0 = cos_stream_length(s[0]);
581*593dc095SDavid du Colombier l1 = cos_stream_length(s[1]);
582*593dc095SDavid du Colombier
583*593dc095SDavid du Colombier if (force && l0 <= l1)
584*593dc095SDavid du Colombier k0 = 1; /* Use Flate if it is not longer. */
585*593dc095SDavid du Colombier else {
586*593dc095SDavid du Colombier k0 = s_compr_chooser__get_choice(
587*593dc095SDavid du Colombier (stream_compr_chooser_state *)piw->binary[2].strm->state, force);
588*593dc095SDavid du Colombier if (k0 && l0 > 0 && l1 > 0)
589*593dc095SDavid du Colombier k0--;
590*593dc095SDavid du Colombier else if (much_bigger__DL(l0, l1))
591*593dc095SDavid du Colombier k0 = 0;
592*593dc095SDavid du Colombier else if (much_bigger__DL(l1, l0) || force)
593*593dc095SDavid du Colombier k0 = 1;
594*593dc095SDavid du Colombier else
595*593dc095SDavid du Colombier return;
596*593dc095SDavid du Colombier }
597*593dc095SDavid du Colombier k1 = 1 - k0;
598*593dc095SDavid du Colombier s_close_filters(&piw->binary[k0].strm, piw->binary[k0].target);
599*593dc095SDavid du Colombier s[k0]->cos_procs->release((cos_object_t *)s[k0], "pdf_image_choose_filter");
600*593dc095SDavid du Colombier s[k0]->written = 1;
601*593dc095SDavid du Colombier piw->binary[0].strm = piw->binary[k1].strm;
602*593dc095SDavid du Colombier s_close_filters(&piw->binary[2].strm, piw->binary[2].target);
603*593dc095SDavid du Colombier piw->binary[1].strm = piw->binary[2].strm = 0; /* for GC */
604*593dc095SDavid du Colombier piw->binary[1].target = piw->binary[2].target = 0;
605*593dc095SDavid du Colombier s[k1]->id = piw->pres->object->id;
606*593dc095SDavid du Colombier piw->pres->object = (cos_object_t *)s[k1];
607*593dc095SDavid du Colombier piw->data = s[k1];
608*593dc095SDavid du Colombier if (piw->alt_writer_count > 3) {
609*593dc095SDavid du Colombier piw->binary[1] = piw->binary[3];
610*593dc095SDavid du Colombier piw->binary[3].strm = 0; /* for GC */
611*593dc095SDavid du Colombier piw->binary[3].target = 0;
612*593dc095SDavid du Colombier }
613*593dc095SDavid du Colombier piw->alt_writer_count -= 2;
614*593dc095SDavid du Colombier }
615*593dc095SDavid du Colombier
616*593dc095SDavid du Colombier /* End binary with choosing image compression. */
617*593dc095SDavid du Colombier int
pdf_choose_compression(pdf_image_writer * piw,bool end_binary)618*593dc095SDavid du Colombier pdf_choose_compression(pdf_image_writer * piw, bool end_binary)
619*593dc095SDavid du Colombier {
620*593dc095SDavid du Colombier cos_stream_t *s[2];
621*593dc095SDavid du Colombier s[0] = cos_stream_from_pipeline(piw->binary[0].strm);
622*593dc095SDavid du Colombier s[1] = cos_stream_from_pipeline(piw->binary[1].strm);
623*593dc095SDavid du Colombier if (end_binary) {
624*593dc095SDavid du Colombier int status;
625*593dc095SDavid du Colombier
626*593dc095SDavid du Colombier status = s_close_filters(&piw->binary[0].strm, piw->binary[0].target);
627*593dc095SDavid du Colombier if (status < 0)
628*593dc095SDavid du Colombier return status;
629*593dc095SDavid du Colombier status = s_close_filters(&piw->binary[1].strm, piw->binary[1].target);
630*593dc095SDavid du Colombier if (status < 0)
631*593dc095SDavid du Colombier return status;
632*593dc095SDavid du Colombier }
633*593dc095SDavid du Colombier pdf_choose_compression_cos(piw, s, end_binary);
634*593dc095SDavid du Colombier return 0;
635*593dc095SDavid du Colombier }
636