/* Copyright (C) 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevpdfj.c,v 1.7.2.1.2.1 2003/01/17 00:49:01 giles Exp $ */ /* Image-writing utilities for pdfwrite driver */ #include "memory_.h" #include "string_.h" #include "gx.h" #include "gserrors.h" #include "gdevpdfx.h" #include "gdevpdfg.h" #include "gdevpdfo.h" #include "gxcspace.h" #include "gsiparm4.h" #define CHECK(expr)\ BEGIN if ((code = (expr)) < 0) return code; END /* GC descriptors */ public_st_pdf_image_writer(); /* ---------------- Image stream dictionaries ---------------- */ const pdf_image_names_t pdf_image_names_full = { { PDF_COLOR_SPACE_NAMES }, { PDF_FILTER_NAMES }, PDF_IMAGE_PARAM_NAMES }; const pdf_image_names_t pdf_image_names_short = { { PDF_COLOR_SPACE_NAMES_SHORT }, { PDF_FILTER_NAMES_SHORT }, PDF_IMAGE_PARAM_NAMES_SHORT }; /* Store the values of image parameters other than filters. */ /* pdev is used only for updating procsets. */ /* pcsvalue is not used for masks. */ 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) { int num_components; float indexed_decode[2]; const float *default_decode = NULL; int code; if (pcs) { CHECK(cos_dict_put_c_key(pcd, pin->ColorSpace, pcsvalue)); pdf_color_space_procsets(pdev, pcs); num_components = gs_color_space_num_components(pcs); if (gs_color_space_get_index(pcs) == gs_color_space_index_Indexed) { indexed_decode[0] = 0; indexed_decode[1] = (1 << pim->BitsPerComponent) - 1; default_decode = indexed_decode; } } else num_components = 1; CHECK(cos_dict_put_c_key_int(pcd, pin->Width, pim->Width)); CHECK(cos_dict_put_c_key_int(pcd, pin->Height, pim->Height)); CHECK(cos_dict_put_c_key_int(pcd, pin->BitsPerComponent, pim->BitsPerComponent)); { int i; for (i = 0; i < num_components * 2; ++i) if (pim->Decode[i] != (default_decode ? default_decode[i] : i & 1) ) break; if (i < num_components * 2) { cos_array_t *pca = cos_array_alloc(pdev, "pdf_put_pixel_image_values(decode)"); if (pca == 0) return_error(gs_error_VMerror); for (i = 0; i < num_components * 2; ++i) CHECK(cos_array_add_real(pca, pim->Decode[i])); CHECK(cos_dict_put_c_key_object(pcd, pin->Decode, COS_OBJECT(pca))); } } if (pim->Interpolate) CHECK(cos_dict_put_c_strings(pcd, pin->Interpolate, "true")); return 0; } 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) { const gs_color_space *pcs = pic->ColorSpace; int code; switch (pic->type->index) { case 1: { const gs_image1_t *pim = (const gs_image1_t *)pic; if (pim->ImageMask) { CHECK(cos_dict_put_c_strings(pcd, pin->ImageMask, "true")); pdev->procsets |= ImageB; pcs = NULL; } } break; case 3: { /* * Clients must treat this as a special case: they must call * pdf_put_image_values for the MaskDict separately, and must * add the Mask entry to the main image stream (dictionary). */ /*const gs_image3_t *pim = (const gs_image3_t *)pic;*/ /* Masked images are only supported starting in PDF 1.3. */ if (pdev->CompatibilityLevel < 1.3) return_error(gs_error_rangecheck); } break; case 4: { const gs_image4_t *pim = (const gs_image4_t *)pic; int num_components = gs_color_space_num_components(pcs); cos_array_t *pca; int i; /* Masked images are only supported starting in PDF 1.3. */ if (pdev->CompatibilityLevel < 1.3) return_error(gs_error_rangecheck); pca = cos_array_alloc(pdev, "pdf_put_image_values(mask)"); if (pca == 0) return_error(gs_error_VMerror); for (i = 0; i < num_components; ++i) { int lo, hi; if (pim->MaskColor_is_range) lo = pim->MaskColor[i * 2], hi = pim->MaskColor[i * 2 + 1]; else lo = hi = pim->MaskColor[i]; CHECK(cos_array_add_int(pca, lo)); CHECK(cos_array_add_int(pca, hi)); } CHECK(cos_dict_put_c_key_object(pcd, "/Mask", COS_OBJECT(pca))); } break; default: return_error(gs_error_rangecheck); } return pdf_put_pixel_image_values(pcd, pdev, pic, pcs, pin, pcsvalue); } /* Store filters for an image. */ /* Currently this only saves parameters for CCITTFaxDecode. */ int pdf_put_image_filters(cos_dict_t *pcd, gx_device_pdf *pdev, const psdf_binary_writer * pbw, const pdf_image_names_t *pin) { return pdf_put_filters(pcd, pdev, pbw->strm, &pin->filter_names); } /* ---------------- Image writing ---------------- */ /* * Fill in the image parameters for a device space bitmap. * PDF images are always specified top-to-bottom. * data_h is the actual number of data rows, which may be less than h. */ void pdf_make_bitmap_matrix(gs_matrix * pmat, int x, int y, int w, int h, int h_actual) { pmat->xx = w; pmat->xy = 0; pmat->yx = 0; pmat->yy = -h_actual; pmat->tx = x; pmat->ty = y + h; } /* * Put out the gsave and matrix for an image. y_scale adjusts the matrix * for images that end prematurely. */ void pdf_put_image_matrix(gx_device_pdf * pdev, const gs_matrix * pmat, floatp y_scale) { gs_matrix imat; gs_matrix_translate(pmat, 0.0, 1.0 - y_scale, &imat); gs_matrix_scale(&imat, 1.0, y_scale, &imat); pdf_put_matrix(pdev, "q ", &imat, "cm\n"); } /* Put out a reference to an image resource. */ int pdf_do_image(gx_device_pdf * pdev, const pdf_resource_t * pres, const gs_matrix * pimat, bool in_contents) { if (in_contents) { int code = pdf_open_contents(pdev, PDF_IN_STREAM); if (code < 0) return code; } if (pimat) { /* Adjust the matrix to account for short images. */ const pdf_x_object_t *const pxo = (const pdf_x_object_t *)pres; double scale = (double)pxo->data_height / pxo->height; pdf_put_image_matrix(pdev, pimat, scale); } pprintld1(pdev->strm, "/R%ld Do\nQ\n", pdf_resource_id(pres)); return 0; } /* ------ Begin / finish ------ */ /* * Begin writing an image, creating the resource if not in-line and * pres == 0, and setting up the binary writer. */ int pdf_begin_write_image(gx_device_pdf * pdev, pdf_image_writer * piw, gx_bitmap_id id, int w, int h, pdf_resource_t *pres, bool in_line) { /* Patch pdev->strm so the right stream gets into the writer. */ stream *save_strm = pdev->strm; int code; if (in_line) { piw->pres = 0; piw->pin = &pdf_image_names_short; piw->data = cos_stream_alloc(pdev, "pdf_begin_image_data"); if (piw->data == 0) return_error(gs_error_VMerror); piw->end_string = " Q"; } else { pdf_x_object_t *pxo; cos_stream_t *pcos; if (pres == 0) { code = pdf_alloc_resource(pdev, resourceXObject, id, &piw->pres, 0L); if (code < 0) return code; cos_become(piw->pres->object, cos_type_stream); } else { /* Resource already allocated (Mask for ImageType 3 image). */ piw->pres = pres; } piw->pres->rid = id; piw->pin = &pdf_image_names_full; pxo = (pdf_x_object_t *)piw->pres; pcos = (cos_stream_t *)pxo->object; CHECK(cos_dict_put_c_strings(cos_stream_dict(pcos), "/Subtype", "/Image")); pxo->width = w; pxo->height = h; /* Initialize data_height for the benefit of copy_{mono,color}. */ pxo->data_height = h; piw->data = pcos; } piw->height = h; pdev->strm = pdev->streams.strm; code = psdf_begin_binary((gx_device_psdf *) pdev, &piw->binary); pdev->strm = save_strm; return code; } /* Begin writing the image data, setting up the dictionary and filters. */ int pdf_begin_image_data(gx_device_pdf * pdev, pdf_image_writer * piw, const gs_pixel_image_t * pim, const cos_value_t *pcsvalue) { cos_dict_t *pcd = cos_stream_dict(piw->data); int code = pdf_put_image_values(pcd, pdev, pim, piw->pin, pcsvalue); if (code >= 0) code = pdf_put_image_filters(pcd, pdev, &piw->binary, piw->pin); if (code < 0) { if (!piw->pres) COS_FREE(piw->data, "pdf_begin_image_data"); piw->data = 0; } return code; } /* Finish writing the binary image data. */ int pdf_end_image_binary(gx_device_pdf *pdev, pdf_image_writer *piw, int data_h) { long pos = stell(pdev->streams.strm); /* piw->binary.target */ int code; psdf_end_binary(&piw->binary); code = cos_stream_add_since(piw->data, pos); if (code < 0) return code; /* If the image ended prematurely, update the Height. */ if (data_h != piw->height) code = cos_dict_put_c_key_int(cos_stream_dict(piw->data), piw->pin->Height, data_h); return code; } /* * Finish writing an image. If in-line, write the BI/dict/ID/data/EI and * return 1; if a resource, write the resource definition and return 0. */ int pdf_end_write_image(gx_device_pdf * pdev, pdf_image_writer * piw) { pdf_resource_t *pres = piw->pres; if (pres) { /* image resource */ if (!pres->named) { /* named objects are written at the end */ cos_write_object(pres->object, pdev); cos_release(pres->object, "pdf_end_write_image"); } return 0; } else { /* in-line image */ stream *s = pdev->strm; stream_puts(s, "BI\n"); cos_stream_elements_write(piw->data, pdev); stream_puts(s, (pdev->binary_ok ? "ID " : "ID\n")); cos_stream_contents_write(piw->data, pdev); pprints1(s, "\nEI%s\n", piw->end_string); COS_FREE(piw->data, "pdf_end_write_image"); return 1; } } /* ------ Copy data ------ */ /* Copy the data for a mask or monobit bitmap. */ int pdf_copy_mask_bits(stream *s, const byte *base, int sourcex, int raster, int w, int h, byte invert) { int yi; for (yi = 0; yi < h; ++yi) { const byte *data = base + yi * raster + (sourcex >> 3); int sbit = sourcex & 7; if (sbit == 0) { int nbytes = (w + 7) >> 3; int i; for (i = 0; i < nbytes; ++data, ++i) sputc(s, *data ^ invert); } else { int wleft = w; int rbit = 8 - sbit; for (; wleft + sbit > 8; ++data, wleft -= 8) sputc(s, ((*data << sbit) + (data[1] >> rbit)) ^ invert); if (wleft > 0) sputc(s, ((*data << sbit) ^ invert) & (byte) (0xff00 >> wleft)); } } return 0; } /* Copy the data for a colored image (device pixels). */ int pdf_copy_color_bits(stream *s, const byte *base, int sourcex, int raster, int w, int h, int bytes_per_pixel) { int yi; for (yi = 0; yi < h; ++yi) { uint ignore; sputs(s, base + sourcex * bytes_per_pixel + yi * raster, w * bytes_per_pixel, &ignore); } return 0; }